├── OverScript ├── OS.ico ├── Expr.cs ├── Properties │ └── PublishProfiles │ │ └── FolderProfile.pubxml ├── OverScript.csproj ├── LocMark.cs ├── NullableObject.cs ├── LoopRange.cs ├── PressedKey.cs ├── CustomType.cs ├── ClassInstance.cs ├── CallStack.cs ├── PPDirective.cs ├── VarType.cs ├── CustomObject.cs ├── Executor.cs ├── TypeConverter.cs ├── Literals.cs ├── Script.cs ├── Program.cs ├── Variables.cs ├── EvalUnit.cs ├── CodeExecution.cs └── ScriptClass.cs ├── .gitignore ├── LICENSE ├── README.md └── OverScript.sln /OverScript/OS.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overscript-lang/OverScript/HEAD/OverScript/OS.ico -------------------------------------------------------------------------------- /OverScript/Expr.cs: -------------------------------------------------------------------------------- 1 | namespace OverScript 2 | { 3 | public class Expr 4 | { 5 | public EvalUnit EU; 6 | public EvalUnit OrigEU; 7 | public Expr(EvalUnit eu, EvalUnit orig = null) 8 | { 9 | EU = eu; 10 | OrigEU = orig; 11 | } 12 | public override string ToString() => EU.ToString(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | #Ignore thumbnails created by Windows 3 | Thumbs.db 4 | #Ignore files built by Visual Studio 5 | *.obj 6 | *.exe 7 | *.pdb 8 | *.user 9 | *.aps 10 | *.pch 11 | *.vspscc 12 | *_i.c 13 | *_p.c 14 | *.ncb 15 | *.suo 16 | *.tlb 17 | *.tlh 18 | *.bak 19 | *.cache 20 | *.ilk 21 | *.log 22 | [Bb]in 23 | [Dd]ebug*/ 24 | ExceptionsON*/ 25 | *.lib 26 | *.sbr 27 | obj/ 28 | [Rr]elease*/ 29 | _ReSharper*/ 30 | [Tt]est[Rr]esult* 31 | .vs/ 32 | #Nuget packages folder 33 | packages/ 34 | -------------------------------------------------------------------------------- /OverScript/Properties/PublishProfiles/FolderProfile.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Release 8 | Any CPU 9 | bin\Release\net6.0\publish\ 10 | FileSystem 11 | net6.0 12 | false 13 | 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Dmitry Trojan 4 | 5 | All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The OverScript Programming Language 2 | 3 | This is the source code repository for [OverScript]. This version is for .NET 6. Version for .NET Standard 2.1 [here]. 4 | OverScript is a simple and powerful C-like statically-typed language written in C# and is great for both embedding in .NET programs and building standalone applications. The project was developed from scratch without looking back at traditional approaches to creating languages. The unique approach allows the language to go beyond the standard features and have great potential for improvement. 5 | 6 | [OverScript]: https://overscript.org/ 7 | [here]: https://github.com/overscript-lang/OverScriptStandard 8 | 9 | Simple code example: 10 | ```cs 11 | Point[] arr = new Point[]{new Point(25, 77), new Point(122, 219)}; //creating an array of two instances 12 | int n; // 0 by default 13 | foreach(Point p in arr){ // iterating over all elements of an array 14 | n++; 15 | WriteLine($"{n}) {p.X}; {p.Y}"); // outputting values using string interpolation 16 | } 17 | //1) 25; 77 18 | //2) 122; 219 19 | ReadKey(); 20 | 21 | class Point{ 22 | public int X, Y; 23 | New(int x, int y){ // constructor 24 | X=x; 25 | Y=y; 26 | } 27 | } 28 | ``` 29 | 30 | 31 | -------------------------------------------------------------------------------- /OverScript/OverScript.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | Debug;Release;ExceptionsON 7 | OS.ico 8 | Copyright © 2022 Dmitry Trojan 9 | Dmitry Trojan 10 | 11 | https://overscript.org/ 12 | 1.0.1.0 13 | 1.0.1.0 14 | 1.0.1 15 | 16 | 17 | 18 | TRACE;EXON 19 | false 20 | true 21 | 22 | 23 | 24 | TRACE;EXON 25 | false 26 | 27 | 28 | 29 | TRACE 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /OverScript.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OverScript", "OverScript\OverScript.csproj", "{F9C16F74-1036-4D09-AC4A-6F12D6EC94C0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | ExceptionsON|Any CPU = ExceptionsON|Any CPU 12 | Release|Any CPU = Release|Any CPU 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {F9C16F74-1036-4D09-AC4A-6F12D6EC94C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 16 | {F9C16F74-1036-4D09-AC4A-6F12D6EC94C0}.Debug|Any CPU.Build.0 = Debug|Any CPU 17 | {F9C16F74-1036-4D09-AC4A-6F12D6EC94C0}.ExceptionsON|Any CPU.ActiveCfg = ExceptionsON|Any CPU 18 | {F9C16F74-1036-4D09-AC4A-6F12D6EC94C0}.ExceptionsON|Any CPU.Build.0 = ExceptionsON|Any CPU 19 | {F9C16F74-1036-4D09-AC4A-6F12D6EC94C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {F9C16F74-1036-4D09-AC4A-6F12D6EC94C0}.Release|Any CPU.Build.0 = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(SolutionProperties) = preSolution 23 | HideSolutionNode = FALSE 24 | EndGlobalSection 25 | GlobalSection(ExtensibilityGlobals) = postSolution 26 | SolutionGuid = {F33296E3-5C0C-40DC-9CAF-C2AC02807EC8} 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /OverScript/LocMark.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Linq; 3 | using static OverScript.ScriptClass; 4 | 5 | namespace OverScript 6 | { 7 | public class LocMark 8 | { 9 | public CodeFile CFile; 10 | public int Line; 11 | public int Line2; 12 | 13 | static char[] CharsToTrim = new char[] { '\t', '\r', ' ' }; 14 | const string LineBreak = @"\n"; 15 | public LocMark(CodeFile File, int line, int line2 = -1) 16 | { 17 | Line = line; 18 | Line2 = line; 19 | CFile = File; 20 | 21 | } 22 | public string Code 23 | { 24 | get 25 | { 26 | string code = ""; 27 | for (int i = Line; i <= Line2; i++) 28 | { 29 | string s = CFile.Lines[i]; 30 | 31 | RemoveComments(ref s); 32 | code += s.Trim(CharsToTrim) + LineBreak; 33 | 34 | } 35 | if (code.EndsWith(LineBreak)) code = code.Remove(code.Length - 2, 2); 36 | return code; 37 | } 38 | } 39 | public string File => CFile.File; 40 | 41 | public LocMark Simplify() => new LocMark(CFile, Line2 < 0 ? Line : Line2); 42 | 43 | public static CodeFile NumToCodeFile(int num, Script script) 44 | { 45 | return script.CodeFiles.Where(x => x.Num == num).FirstOrDefault(); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /OverScript/NullableObject.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | namespace OverScript 4 | { 5 | public struct NullableObject 6 | { 7 | 8 | public bool IsNull; 9 | public T Item; 10 | 11 | private NullableObject(T item, bool isNull) : this() 12 | { 13 | IsNull = isNull; 14 | Item = item; 15 | } 16 | 17 | public NullableObject(T item) : this(item, item == null) 18 | { 19 | } 20 | 21 | 22 | public static implicit operator T(NullableObject nobj) 23 | { 24 | return nobj.Item; 25 | } 26 | 27 | public static implicit operator NullableObject(T item) 28 | { 29 | return new NullableObject(item); 30 | } 31 | 32 | public override string ToString() => Item?.ToString(); 33 | 34 | public override bool Equals(object obj) 35 | { 36 | if (obj == null) 37 | return IsNull; 38 | 39 | if (!(obj is NullableObject nobj)) 40 | return false; 41 | else 42 | { 43 | 44 | 45 | if (IsNull) 46 | return nobj.IsNull; 47 | 48 | if (nobj.IsNull) 49 | return false; 50 | 51 | return Item.Equals(nobj.Item); 52 | } 53 | } 54 | 55 | public override int GetHashCode() 56 | { 57 | if (IsNull) 58 | return 0; 59 | 60 | var result = Item.GetHashCode(); 61 | 62 | if (result >= 0) 63 | result++; 64 | 65 | return result; 66 | } 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /OverScript/LoopRange.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace OverScript 4 | { 5 | struct LoopRange : IEnumerable 6 | { 7 | public int Start, Stop, Step; 8 | public LoopRange(int start, int stop, int step) 9 | { 10 | Start = start; 11 | Stop = stop; 12 | Step = step; 13 | } 14 | public IEnumerator GetEnumerator() 15 | { 16 | return new RangeEnumerator(Start, Stop, Step); 17 | } 18 | public class RangeEnumerator : IEnumerator 19 | { 20 | public int Start, Stop, Step, CurrentValue; 21 | bool Reseted = false; 22 | public RangeEnumerator(int start, int stop, int step) 23 | { 24 | Start = start; 25 | Stop = stop; 26 | Step = step; 27 | Reset(); 28 | } 29 | 30 | public bool MoveNext() 31 | { 32 | if (Reseted) 33 | { 34 | CurrentValue = Start; 35 | Reseted = false; 36 | } 37 | else 38 | CurrentValue += Step; 39 | 40 | return Step > 0 ? (CurrentValue < Stop) : (CurrentValue > Stop); 41 | } 42 | 43 | public void Reset() 44 | { 45 | CurrentValue = 0; 46 | Reseted = true; 47 | } 48 | 49 | object IEnumerator.Current 50 | { 51 | get 52 | { 53 | return Current; 54 | } 55 | } 56 | 57 | public int Current 58 | { 59 | get 60 | { 61 | return CurrentValue; 62 | 63 | } 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /OverScript/PressedKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OverScript 4 | { 5 | struct PressedKey : IConvertible 6 | { 7 | public ConsoleKeyInfo KeyInfo { set; get; } 8 | public override string ToString() => KeyInfo.Key.ToString(); 9 | public ConsoleKey Key { get { return KeyInfo.Key; } } 10 | public char KeyChar { get { return KeyInfo.KeyChar; } } 11 | public PressedKey(ConsoleKeyInfo k) 12 | { 13 | KeyInfo = k; 14 | } 15 | string IConvertible.ToString(IFormatProvider provider) => ToString(); 16 | TypeCode IConvertible.GetTypeCode() { throw new NotImplementedException(); } 17 | bool IConvertible.ToBoolean(IFormatProvider provider) { throw new NotImplementedException(); } 18 | byte IConvertible.ToByte(IFormatProvider provider) => (Byte)KeyInfo.Key; 19 | DateTime IConvertible.ToDateTime(IFormatProvider provider) { throw new NotImplementedException(); } 20 | decimal IConvertible.ToDecimal(IFormatProvider provider) => (Decimal)KeyInfo.Key; 21 | double IConvertible.ToDouble(IFormatProvider provider) => (Double)KeyInfo.Key; 22 | short IConvertible.ToInt16(IFormatProvider provider) => (Int16)KeyInfo.Key; 23 | int IConvertible.ToInt32(IFormatProvider provider) => (Int32)KeyInfo.Key; 24 | long IConvertible.ToInt64(IFormatProvider provider) => (Int64)KeyInfo.Key; 25 | sbyte IConvertible.ToSByte(IFormatProvider provider) => (SByte)KeyInfo.Key; 26 | float IConvertible.ToSingle(IFormatProvider provider) => (Single)KeyInfo.Key; 27 | object IConvertible.ToType(Type conversionType, IFormatProvider provider) { throw new NotImplementedException(); } 28 | ushort IConvertible.ToUInt16(IFormatProvider provider) => (UInt16)KeyInfo.Key; 29 | uint IConvertible.ToUInt32(IFormatProvider provider) => (UInt32)KeyInfo.Key; 30 | ulong IConvertible.ToUInt64(IFormatProvider provider) => (UInt64)KeyInfo.Key; 31 | char IConvertible.ToChar(IFormatProvider provider) => KeyInfo.KeyChar; 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /OverScript/CustomType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using static OverScript.ScriptClass; 4 | 5 | namespace OverScript 6 | { 7 | public class CustomType 8 | { 9 | public string Name, FullName; 10 | public ScriptClass Class; 11 | public bool IsArray; 12 | public Script CurScript; 13 | public CustomType(string name, ScriptClass c) 14 | { 15 | FullName = name; 16 | Class = c; 17 | Name = name.Substring(name.LastIndexOf('.') + 1); 18 | IsArray = name.EndsWith("[]"); 19 | 20 | } 21 | public bool IsOfType(object obj) 22 | { 23 | if (!(obj is CustomObject co)) return false; 24 | var ct = this; 25 | 26 | var t = co.Type; 27 | if (t == ct) return true; 28 | if (ct.IsArray != co.Type.IsArray) return false; 29 | 30 | return t.Class.Is(ct.Class); 31 | } 32 | public override string ToString() => FullName; 33 | public override bool Equals(object obj) 34 | { 35 | 36 | if (obj is CustomType) return obj == this; 37 | return base.Equals(obj); 38 | } 39 | 40 | static Dictionary Types = new Dictionary(); 41 | public static CustomType Get(ScriptClass c, bool isArray = false) 42 | { 43 | 44 | string name = c.ClassFullName; 45 | if (isArray) name += "[]"; 46 | CustomType t; 47 | if (Types.TryGetValue(name, out t)) return t; 48 | t = Types[name] = new CustomType(name, c); 49 | return t; 50 | } 51 | 52 | public override int GetHashCode() 53 | { 54 | return FullName.GetHashCode(); 55 | } 56 | 57 | public object Call(Executor exec, string fnName, params object[] args) 58 | { 59 | if (!IsArray) 60 | return DynFuncCall(exec.GetStaticInstance(Class), fnName, args); 61 | else 62 | throw new InvalidOperationException("Cannot call a function on this type."); 63 | 64 | } 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /OverScript/ClassInstance.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OverScript 4 | { 5 | public class ClassInstance 6 | { 7 | public int Scope; 8 | public ScriptClass Class; 9 | public bool IsStatic; 10 | public CustomObject ThisObj; 11 | public Executor Exec; 12 | 13 | private ClassInstance() 14 | { 15 | Scope = -1; 16 | Exec = Executor.GetConstExecutor(); 17 | } 18 | public static ClassInstance GetConstInst() => new ClassInstance(); 19 | 20 | public ClassInstance(Executor exec, ScriptClass scriptClass, EvalUnit[] args = null, ClassInstance srcInst = null, int baseScope = -1, bool isStatic = false, bool ignoreConstructor = false, object constrFn = null) 21 | { 22 | Class = scriptClass; 23 | Scope = exec.NewScope(); 24 | 25 | IsStatic = isStatic; 26 | Exec = exec; 27 | 28 | ClassInstance ci = this; 29 | 30 | CallStack cstack = null; 31 | if (!isStatic) 32 | { 33 | 34 | if (Class.InstanceFuncs != null) 35 | for (int i = 0; i < Class.InstanceFuncs.Length; i++) Class.InstanceFuncs[i](args, baseScope, srcInst, ci, cstack, null); 36 | 37 | if (!ignoreConstructor) 38 | { 39 | if (constrFn != null) 40 | { 41 | 42 | var fn = (ScriptClass.FuncToCall)constrFn; 43 | fn(args, baseScope, srcInst, ci, cstack, null); 44 | 45 | } 46 | else 47 | { 48 | if (Class.CtorFunc != null) 49 | { 50 | args = null; 51 | Class.CtorFunc(args, baseScope, srcInst, ci, cstack, null); 52 | 53 | } 54 | } 55 | 56 | } 57 | } 58 | else 59 | { 60 | Exec.SetStaticInstance(scriptClass, this); 61 | 62 | if (Class.StaticFuncs != null) 63 | for (int i = 0; i < Class.StaticFuncs.Length; i++) Class.StaticFuncs[i](args, baseScope, srcInst, ci, cstack, null); 64 | } 65 | } 66 | 67 | ~ClassInstance() 68 | { 69 | if (Exec.ForciblyCanceled) return; 70 | 71 | 72 | try 73 | { 74 | 75 | if (Class.FinalizeFuncs != null) 76 | for (int i = 0; i < Class.FinalizeFuncs.Length; i++) Class.FinalizeFuncs[i](null, -1, null, this, null, null); 77 | 78 | 79 | if (Class.ForCleaning != null) Exec.RemVars(Scope, Class.ForCleaning); 80 | Exec.FreeScope(Scope); 81 | } 82 | catch (Exception ex) 83 | { 84 | if (!Exec.IgnoreFinalizerExceptions) throw new FinalizationFailedException("An error occurred during finalization. ", ex); 85 | } 86 | } 87 | 88 | public override string ToString() 89 | { 90 | 91 | return $"Instance{(IsStatic ? " (static)" : "")} of {Class.ClassFullName}"; 92 | 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /OverScript/CallStack.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using static OverScript.ScriptClass; 5 | 6 | namespace OverScript 7 | { 8 | public class CallStack 9 | { 10 | 11 | 12 | 13 | public List Items = new List(); 14 | public void Push(Item item) { Items.Add(item); } 15 | public Item Peek() => Items.Last(); 16 | public Item Pop() { var item = Items.Last(); Items.RemoveAt(Items.Count - 1); return item; } 17 | public struct Item 18 | { 19 | public ScriptFunction Fn; 20 | public ScriptClass FnClass; 21 | 22 | public List Refs; 23 | public RefParam Ref; 24 | public string BasePath; 25 | 26 | public Exception ThrownException; 27 | 28 | public struct RefParam 29 | { 30 | public VarInfo VI; 31 | public int ParamVarID; 32 | public RefParam(int paramVarID, VarInfo vi) 33 | { 34 | VI = vi; 35 | ParamVarID = paramVarID; 36 | 37 | } 38 | 39 | } 40 | 41 | public Item(ScriptFunction f, ScriptClass c) 42 | { 43 | Refs = null; 44 | Ref = default(RefParam); 45 | 46 | Fn = f; 47 | FnClass = c; 48 | 49 | 50 | BasePath = f.BasePath; 51 | ThrownException = null; 52 | } 53 | 54 | } 55 | public Exception GetThrownException() 56 | { 57 | int n = Items.Count - 1; 58 | return Items[n].ThrownException; 59 | } 60 | public bool SetThrownException(Exception ex) 61 | { 62 | int n = Items.Count - 1; 63 | var newItem = Items[n]; 64 | bool upd = newItem.ThrownException != ex; 65 | if (upd) 66 | { 67 | newItem.ThrownException = ex; 68 | Items[n] = newItem; 69 | } 70 | return upd; 71 | } 72 | public void SetBasePath(string path) 73 | { 74 | int n = Items.Count - 1; 75 | var newItem = Items[n]; 76 | newItem.BasePath = path; 77 | Items[n] = newItem; 78 | } 79 | public string GetBasePath() => Items[Items.Count - 1].BasePath; 80 | 81 | public void CreateRefs(int c) 82 | { 83 | int n = Items.Count - 1; 84 | var newItem = Items[n]; 85 | newItem.Refs = new System.Collections.Generic.List(c); ; 86 | Items[n] = newItem; 87 | 88 | 89 | 90 | } 91 | public void CreateRef(Item.RefParam rf) 92 | { 93 | int n = Items.Count - 1; 94 | var newItem = Items[n]; 95 | newItem.Ref = rf; 96 | Items[n] = newItem; 97 | 98 | 99 | 100 | } 101 | public void AddRef(Item.RefParam rf) 102 | { 103 | int n = Items.Count - 1; 104 | Items[n].Refs.Add(rf); 105 | } 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /OverScript/PPDirective.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using static OverScript.Literals; 4 | using static OverScript.ScriptClass; 5 | 6 | namespace OverScript 7 | { 8 | public class PPDirective 9 | { 10 | public string Code; 11 | public string CodeFile; 12 | 13 | public string Name; 14 | public string Path; 15 | public string Data; 16 | 17 | public Dictionary Params; 18 | public static PPDirective Get(string str, string file, Script script) 19 | { 20 | if (!str.StartsWith("#")) return null; 21 | string baseDir = System.IO.Path.GetDirectoryName(file); 22 | var d = new PPDirective(); 23 | d.Code = str; 24 | d.CodeFile = file; 25 | string[] parts = str.Split(',').Select(x => x.Trim()).ToArray(); 26 | 27 | string[] p = parts[0].Split(' ').Select(x => x.Trim()).ToArray(); 28 | d.Name = p[0].Remove(0, 1); 29 | 30 | int dataIndex = 1; 31 | if (p.Length > 1) 32 | { 33 | string p1 = p[1]; 34 | if ((p1.StartsWith(LiteralMark) && GetLitTypeIdByStr(p1) == TypeID.String) || p1.StartsWith('<')) 35 | { 36 | char c = p1[0]; 37 | 38 | string path = p1.Substring(1, p1.Length - 2); 39 | if (c == '<') 40 | path = System.IO.Path.GetFullPath(path, Program.ModulesDir); 41 | else 42 | { 43 | path = GetLitByStr(path) as string ?? ""; 44 | if (path.Length > 0) 45 | { 46 | 47 | path = ExpandVars(path, script); 48 | if (!System.IO.Path.IsPathFullyQualified(path)) path = System.IO.Path.GetFullPath(path, baseDir); 49 | else path = System.IO.Path.GetFullPath(path); 50 | } 51 | } 52 | d.Path = path; 53 | dataIndex++; 54 | } 55 | } 56 | 57 | for (int n = dataIndex; n < p.Length; n++) 58 | d.Data += GetParamValue(p[n]) + " "; 59 | 60 | if (d.Data != null) d.Data = d.Data.TrimEnd(); 61 | 62 | if (parts.Length > 1) d.Params = new Dictionary(); 63 | for (int n = 1; n < parts.Length; n++) 64 | { 65 | string s = parts[n]; 66 | int i = s.IndexOf('='); 67 | if (i >= 0) 68 | { 69 | string key = s.Substring(0, i).Trim(); 70 | string value = s.Substring(i + 1).Trim(); 71 | 72 | d.Params.Add(key, GetParamValue(value)); 73 | } 74 | else 75 | d.Params.Add(s, ""); 76 | 77 | } 78 | return d; 79 | 80 | string GetParamValue(string value) => value.StartsWith(LiteralMark) ? GetLitByStr(value).ToString() : value; 81 | } 82 | 83 | public static (string str, int pos) CuteDirectiveLine(ref string code, string name, int start = 0, char endChar = '\n') 84 | { 85 | string drc = null; 86 | int i = code.IndexOf("#" + name, start); 87 | if (i >= 0) 88 | { 89 | int i2 = code.IndexOf(endChar, i); 90 | if (i2 < 0) i2 = code.Length; 91 | 92 | drc = code.Substring(i, i2 - i); 93 | drc = drc.TrimEnd('\r'); 94 | code = code.Remove(i, i2 - i); 95 | } 96 | return (drc, i); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /OverScript/VarType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static OverScript.ScriptClass; 3 | 4 | namespace OverScript 5 | { 6 | public struct VarType 7 | { 8 | 9 | public string Name { get { return GetName(); } } 10 | public TypeID ID; 11 | public readonly CustomType CType; 12 | public Type T; 13 | public TypeID SubTypeID; 14 | public Type TypeHint; 15 | public Type SubType; 16 | public bool IsEmpty; 17 | public bool IsNull; 18 | public bool IsHintArray; 19 | public bool IsHintType; 20 | public bool IsByRef; 21 | public Type GetAbsType() 22 | { 23 | Type type = null; 24 | if (ID != TypeID.Object) 25 | type = T; 26 | else if (SubTypeID != TypeID.None && SubTypeID != TypeID.CustomType) 27 | type = GetTypeByID(SubTypeIdToTypeId(SubTypeID)); 28 | else if (SubType != null) 29 | type = SubType; 30 | 31 | else if (TypeHint != null) 32 | type = TypeHint; 33 | else 34 | type = T; 35 | 36 | return type; 37 | } 38 | string GetName() 39 | { 40 | string name; 41 | if (ID == TypeID.Object) 42 | { 43 | if (SubTypeID != TypeID.None) 44 | { 45 | name = TypeStr.Object + "/type:"; 46 | if (CType == null) 47 | name = name + GetTypeName(SubTypeIdToTypeId(SubTypeID)); 48 | else 49 | name = name + CType.FullName; 50 | 51 | } 52 | else if (TypeHint != null) 53 | name = TypeStr.Object + "/hint:" + TypeHint.ToString(); 54 | else if (IsEmpty) 55 | name = TypeStr.Object + "/empty"; 56 | 57 | 58 | else 59 | name = TypeStr.Object; 60 | 61 | } 62 | else if (ID == TypeID.ObjectArray && TypeHint != null) 63 | { 64 | name = TypeStr.ObjectArray + "/hint:" + TypeHint.ToString(); 65 | } 66 | else 67 | { 68 | if (CType == null) 69 | name = GetTypeName(ID); 70 | else 71 | name = CType.FullName; 72 | 73 | } 74 | return IsByRef ? "ref*" + name : name; 75 | } 76 | public VarType(TypeID type, CustomType ct = null) 77 | { 78 | 79 | ID = type; 80 | T = Types[(int)ID]; 81 | CType = ct; 82 | SubTypeID = TypeID.None; 83 | TypeHint = null; 84 | IsEmpty = IsNull = false; 85 | SubType = null; 86 | IsHintArray = IsHintType = false; 87 | IsByRef = false; 88 | } 89 | public VarType SetType(TypeID type) 90 | { 91 | ID = type; 92 | T = Types[(int)ID]; 93 | return this; 94 | } 95 | public VarType MakeArrayTypeHint(bool withTypeHint) 96 | { 97 | if (SubType != null) SubType = SubType.MakeArrayType(); 98 | if (withTypeHint && TypeHint != null) TypeHint = TypeHint.MakeArrayType(); 99 | IsHintArray = true; 100 | return this; 101 | } 102 | 103 | public VarType AddHint(Type type) 104 | { 105 | TypeHint = type; 106 | return this; 107 | } 108 | 109 | public VarType AddSubType(Type type) 110 | { 111 | SubType = type; 112 | return this; 113 | } 114 | public VarType MakeEmpty() 115 | { 116 | IsEmpty = true; 117 | return this; 118 | } 119 | public VarType MakeNull() 120 | { 121 | IsNull = true; 122 | return this; 123 | } 124 | public VarType SetHintBySubType() 125 | { 126 | 127 | if (SubType != null || (SubTypeID != TypeID.None && SubTypeID != TypeID.CustomType)) 128 | { 129 | TypeHint = SubType ?? GetTypeByID(SubTypeIdToTypeId(SubTypeID)); 130 | SubType = null; 131 | SubTypeID = TypeID.None; 132 | } 133 | return this; 134 | } 135 | public static bool operator ==(VarType a, VarType b) 136 | { 137 | return a.ID == b.ID && a.CType == b.CType && (a.ID != TypeID.Object || a.TypeHint == b.TypeHint); 138 | } 139 | public static bool operator !=(VarType a, VarType b) 140 | { 141 | return !(a == b); 142 | } 143 | 144 | public override string ToString() 145 | { 146 | return Name; 147 | } 148 | 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /OverScript/CustomObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using static OverScript.ScriptClass; 4 | 5 | namespace OverScript 6 | { 7 | public class CustomObject : System.Collections.IEnumerable, ICollection, IComparable, IDisposable 8 | { 9 | 10 | public object Object; 11 | public CustomType Type; 12 | public Executor Exec; 13 | public bool ExecutorIsValid => Exec.IsValid; 14 | public CustomObject(Executor exec, object obj, ScriptClass type, bool isArray = false) 15 | { 16 | Object = obj; 17 | Type = CustomType.Get(type, isArray); 18 | Exec = exec; 19 | } 20 | 21 | public IEnumerator GetEnumerator() 22 | { 23 | try 24 | { 25 | return Type.IsArray ? ((CustomObject[])Object).GetEnumerator() : (IEnumerator)Type.Class.GetEnumeratorFunc(null, -1, null, (ClassInstance)Object, null, null); 26 | } 27 | catch (Exception ex) 28 | { 29 | throw new ScriptExecutionException("Failed to get enumerator. " + ex.Message); 30 | } 31 | } 32 | 33 | public int Count => ((CustomObject[])Object).Length; 34 | 35 | public bool IsSynchronized => throw new NotImplementedException(); 36 | 37 | public object SyncRoot => throw new NotImplementedException(); 38 | 39 | public void CopyTo(Array array, int index) 40 | { 41 | throw new NotImplementedException(); 42 | } 43 | 44 | public static explicit operator ClassInstance(CustomObject obj) => (ClassInstance)obj.Object; 45 | 46 | public static explicit operator CustomObject[](CustomObject obj) => (CustomObject[])obj.Object; 47 | public static explicit operator Array(CustomObject obj) => (CustomObject[])obj.Object; 48 | 49 | 50 | 51 | public override string ToString() 52 | { 53 | if (Type.IsArray) 54 | { 55 | if (Type.Class.ArrayToStringFunc != null) 56 | { 57 | EvalUnit arg = new EvalUnit(); 58 | arg.Type = new VarType(TypeID.CustomArray, Type); 59 | arg.SpecificValue = this; 60 | arg.Kind = EvalUnitKind.SpecificValue; 61 | 62 | EvalUnit[] args = { arg }; 63 | return Type.Class.ArrayToStringFunc(args, -1, null, Exec.GetStaticInstance(Type.Class), null, null); 64 | } 65 | 66 | return $"Array of {Type.Name.Replace("[]", "")}"; 67 | } 68 | if (Type.Class.ToStringFunc != null) 69 | return Type.Class.ToStringFunc(null, -1, null, (ClassInstance)Object, null, null); 70 | 71 | return $"Instance of {Type.Name}"; 72 | } 73 | public override int GetHashCode() 74 | { 75 | if (Type.Class.GetHashCodeFunc != null) 76 | return Type.Class.GetHashCodeFunc(null, -1, null, (ClassInstance)Object, null, null); 77 | else 78 | return Object.GetHashCode(); 79 | } 80 | public void Dispose() 81 | { 82 | if (Object is CustomObject[] arr) 83 | { 84 | for (int i = 0; i < arr.Length; i++) arr[i]?.Dispose(); 85 | } 86 | else 87 | { 88 | 89 | if (Type.Class.DisposeFunc != null && Object != null) 90 | Type.Class.DisposeFunc(null, -1, null, (ClassInstance)Object, null, null); 91 | 92 | } 93 | } 94 | 95 | public override bool Equals(object obj) 96 | { 97 | if (Type.Class.EqualsFunc != null) 98 | { 99 | EvalUnit arg = new EvalUnit(); 100 | arg.Type = GetVarTypeByID(TypeID.Object); 101 | arg.SpecificValue = obj; 102 | arg.Kind = EvalUnitKind.SpecificValue; 103 | 104 | EvalUnit[] args = { arg }; 105 | return Type.Class.EqualsFunc(args, -1, null, (ClassInstance)Object, null, null); 106 | } 107 | else if (obj is CustomObject co) 108 | return Object == co.Object; 109 | else 110 | return base.Equals(obj); 111 | } 112 | public int CompareTo(object obj) 113 | { 114 | if (Type.Class.CompareToFunc != null) 115 | { 116 | EvalUnit arg = new EvalUnit(); 117 | arg.Type = GetVarTypeByID(TypeID.Object); 118 | arg.SpecificValue = obj; 119 | arg.Kind = EvalUnitKind.SpecificValue; 120 | 121 | EvalUnit[] args = { arg }; 122 | return Type.Class.CompareToFunc(args, -1, null, (ClassInstance)Object, null, null); 123 | } 124 | else 125 | throw new ScriptExecutionException($"'{Type.Class.ClassName}' does not implement CompareTo method."); 126 | } 127 | public object Call(string fnName, params object[] args) 128 | { 129 | if (Object is ClassInstance inst) 130 | return DynFuncCall(inst, fnName, args); 131 | else 132 | throw new InvalidOperationException("Cannot call a function on this object."); 133 | 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /OverScript/Executor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace OverScript 6 | { 7 | public partial class Executor : IDisposable 8 | { 9 | internal int ExecID; 10 | public int ID => ExecID; 11 | internal Script ExecutedScript; 12 | public Script Script => ExecutedScript; 13 | internal string[] Args; 14 | internal ClassInstance[] StaticInstances; 15 | internal Stack FreeScopes; 16 | internal object FreeScopesLocker; 17 | internal int LastScope = 0; 18 | 19 | internal bool Disposed = false, Canceled = false, ForciblyCanceled = false; 20 | internal ClassInstance AppInstance; 21 | Dictionary GlobalProperties = new Dictionary(); 22 | internal object GlobalPropertiesLocker = new object(); 23 | internal ExecutionStatus ExecStatus = ExecutionStatus.None; 24 | public ExecutionStatus Status => ExecStatus; 25 | public bool IsValid => !Disposed && (Status == ExecutionStatus.Running || Status == ExecutionStatus.Completed); 26 | 27 | ManualResetEvent CancellationCompleted = new ManualResetEvent(false); 28 | public bool IgnoreFinalizerExceptions = false; 29 | 30 | public enum ExecutionStatus : byte { None, Running, Completed, Faulted, Canceled } 31 | public Executor(Script script) 32 | { 33 | ExecID = ExecPool.GetVacantID(this); 34 | ExecutedScript = script; 35 | 36 | 37 | 38 | SetVarStorage(); 39 | 40 | } 41 | private Executor() 42 | { 43 | ExecID = -1; 44 | ExecutedScript = null; 45 | } 46 | internal static Executor GetConstExecutor() => new Executor(); 47 | 48 | 49 | 50 | public void Dispose() 51 | { 52 | Disposed = Canceled = ForciblyCanceled = true; 53 | DropVarStorage(); 54 | 55 | AppInstance = null; 56 | StaticInstances = null; 57 | ExecutedScript = null; 58 | FreeScopes = null; 59 | FreeScopesLocker = null; 60 | Args = null; 61 | GlobalProperties = null; 62 | GlobalPropertiesLocker = null; 63 | ExecPool.Executors[ExecID] = null; 64 | ExecID = -1; 65 | 66 | } 67 | public void Cancel(bool force = false) 68 | { 69 | 70 | ForciblyCanceled = force; 71 | Canceled = true; 72 | 73 | if (ExecStatus == ExecutionStatus.Running) CancellationCompleted.WaitOne(); 74 | } 75 | public void Execute(string[] args = null) 76 | { 77 | if (ExecStatus != ExecutionStatus.None) throw new InvalidOperationException("Re-execution is not supported."); 78 | ExecStatus = ExecutionStatus.Running; 79 | Args = args; 80 | FreeScopes = new Stack(); 81 | FreeScopesLocker = new object(); 82 | StaticInstances = new ClassInstance[ExecutedScript.ClassCount]; 83 | try 84 | { 85 | AppInstance = new ClassInstance(this, ExecutedScript.RootClass); 86 | } 87 | catch 88 | { 89 | ExecStatus = Canceled && !Disposed ? ExecutionStatus.Canceled : ExecutionStatus.Faulted; 90 | CancellationCompleted.Set(); 91 | throw; 92 | } 93 | ExecStatus = ExecutionStatus.Completed; 94 | if (Canceled) CancellationCompleted.Set(); 95 | } 96 | internal ClassInstance GetStaticInstance(ScriptClass c) 97 | { 98 | var si = StaticInstances[c.ID]; 99 | if (si == null) 100 | StaticInstances[c.ID] = si = new ClassInstance(this, c, null, null, -1, true); 101 | 102 | return si; 103 | } 104 | internal void SetStaticInstance(ScriptClass c, ClassInstance si) 105 | { 106 | StaticInstances[c.ID] = si; 107 | 108 | } 109 | 110 | public object Call(string fnName, params object[] args) 111 | { 112 | if (AppInstance != null) 113 | return ScriptClass.DynFuncCall(AppInstance, fnName, args); 114 | else 115 | throw new InvalidOperationException($"Cannot call a function because the main app instance is null."); 116 | 117 | } 118 | 119 | public object GetGlobal(string key) 120 | { 121 | lock (GlobalPropertiesLocker) { return GlobalProperties[key]; } 122 | } 123 | public void SetGlobal(string key, object value) 124 | { 125 | lock (GlobalPropertiesLocker) { GlobalProperties[key] = value; } 126 | } 127 | public Dictionary.Enumerator GetGlobalPropertiesEnumerator() => GlobalProperties.GetEnumerator(); 128 | 129 | public void ClearGlobalProperties() 130 | { 131 | GlobalProperties.Clear(); 132 | } 133 | public void ClearRootVars() 134 | { 135 | ClearVarsInScope(AppInstance.Scope); 136 | var staticInst = StaticInstances[AppInstance.Class.ID]; 137 | if(staticInst!=null) ClearVarsInScope(staticInst.Scope); 138 | } 139 | 140 | public object this[string key] 141 | { 142 | get { return GetGlobal(key); } 143 | set { SetGlobal(key, value); } 144 | } 145 | 146 | 147 | } 148 | 149 | 150 | } 151 | -------------------------------------------------------------------------------- /OverScript/TypeConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OverScript 4 | { 5 | public partial class ScriptClass 6 | { 7 | 8 | public static class TypeConverter 9 | { 10 | 11 | struct CType 12 | { 13 | T Value; 14 | public CType(T v) => Value = v; 15 | 16 | public static explicit operator CType(T x) => new CType(x); 17 | 18 | public static implicit operator T2(CType x) 19 | { 20 | try 21 | { 22 | switch (x) 23 | { 24 | case CType y: return y.Value; 25 | case CType y: return y.Value == null ? (T2)(object)null : (T2)(object)y.Value.ToString(); 26 | case CType y: return (T2)(object)Convert.ToInt32(y.Value); 27 | case CType y: return (T2)(object)Convert.ToInt16(y.Value); 28 | case CType y: return (T2)(object)Convert.ToByte(y.Value); 29 | case CType y: return (T2)(object)Convert.ToInt64(y.Value); 30 | case CType y: return (T2)(object)Convert.ToDecimal(y.Value); 31 | case CType y: return (T2)(object)Convert.ToChar(y.Value); 32 | case CType y: return (T2)(object)Convert.ToBoolean(y.Value); 33 | case CType y: return (T2)(object)Convert.ToDateTime(y.Value); 34 | case CType y: return (T2)(object)Convert.ToSingle(y.Value); 35 | case CType y: return (T2)(object)Convert.ToDouble(y.Value); 36 | 37 | case CType y: return (T2)(object)(Array)y.Value; 38 | default: 39 | 40 | return (T2)(object)x.Value; 41 | 42 | } 43 | } 44 | catch (InvalidCastException) 45 | { 46 | var t = x.Value.GetType(); 47 | string n; 48 | try { n = x.Value is CustomObject co ? co.Type.FullName : GetTypeName(TypeIds[t]); } 49 | catch { n = t.FullName; } 50 | 51 | string toTypeName; 52 | TypeID toType; 53 | if (TypeIds.TryGetValue(typeof(T2), out toType)) toTypeName = GetTypeName(toType); else toTypeName = typeof(T2).FullName; 54 | 55 | throw new ScriptExecutionException($"Unable to cast to type '{toTypeName}'. Value is of type '{n}'."); 56 | } 57 | 58 | } 59 | } 60 | 61 | 62 | public static T2 ConvertValue(T1 x) => (CType)x; 63 | public static bool ConvertAbility(TypeID id, TypeID id2) 64 | { 65 | if (id == TypeID.Object || id2 == TypeID.Object || id2 == TypeID.String) return true; 66 | 67 | switch (id2) 68 | { 69 | case TypeID.Int: return ConvertAbility(id); 70 | case TypeID.String: return ConvertAbility(id); 71 | case TypeID.Char: return ConvertAbility(id); 72 | case TypeID.Double: return ConvertAbility(id); 73 | case TypeID.Float: return ConvertAbility(id); 74 | case TypeID.Long: return ConvertAbility(id); 75 | case TypeID.Decimal: return ConvertAbility(id); 76 | case TypeID.Bool: return ConvertAbility(id); 77 | case TypeID.Short: return ConvertAbility(id); 78 | case TypeID.Byte: return ConvertAbility(id); 79 | case TypeID.Date: return ConvertAbility(id); 80 | 81 | default: 82 | return id == id2; 83 | 84 | } 85 | 86 | } 87 | public static bool ConvertAbility(TypeID id) 88 | { 89 | try 90 | { 91 | switch (id) 92 | { 93 | case TypeID.Int: ConvertValue(default(int)); break; 94 | case TypeID.String: ConvertValue(default(string)); break; 95 | case TypeID.Char: ConvertValue(default(char)); break; 96 | case TypeID.Double: ConvertValue(default(double)); break; 97 | case TypeID.Float: ConvertValue(default(float)); break; 98 | case TypeID.Long: ConvertValue(default(long)); break; 99 | case TypeID.Decimal: ConvertValue(default(decimal)); break; 100 | case TypeID.Bool: ConvertValue(default(bool)); break; 101 | case TypeID.Short: ConvertValue(default(short)); break; 102 | case TypeID.Byte: ConvertValue(default(byte)); break; 103 | case TypeID.Date: ConvertValue(default(DateTime)); break; 104 | default: 105 | return id == TypeIds[typeof(T)]; 106 | 107 | } 108 | return true; 109 | } 110 | catch 111 | { 112 | return false; 113 | } 114 | 115 | } 116 | 117 | } 118 | 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /OverScript/Literals.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using static OverScript.ScriptClass; 5 | 6 | namespace OverScript 7 | { 8 | public static class Literals 9 | { 10 | 11 | static int litCount = 0; 12 | 13 | internal static int SetLiteral(T lit) 14 | { 15 | 16 | int n = Lit.Find(lit); 17 | 18 | if (n >= 0) return n; 19 | n = Lit.Add(++litCount, lit); 20 | LiteralTypes[n] = GetTypeID(typeof(T)); 21 | 22 | return n; 23 | } 24 | internal static int SetLiteral(object lit) 25 | { 26 | switch (lit) 27 | { 28 | case string v: return SetLiteral(v); 29 | case int v: return SetLiteral(v); 30 | case long v: return SetLiteral(v); 31 | case float v: return SetLiteral(v); 32 | case double v: return SetLiteral(v); 33 | case decimal v: return SetLiteral(v); 34 | case char v: return SetLiteral(v); 35 | 36 | default: return SetLiteral(lit); 37 | } 38 | } 39 | 40 | internal static void TrimLit() 41 | { 42 | Lit.TrimExcess(); 43 | Lit.TrimExcess(); 44 | Lit.TrimExcess(); 45 | Lit.TrimExcess(); 46 | Lit.TrimExcess(); 47 | Lit.TrimExcess(); 48 | Lit.TrimExcess(); 49 | Lit.TrimExcess(); 50 | 51 | } 52 | public static void Reset() 53 | { 54 | Lit.Reset(); 55 | Lit.Reset(); 56 | Lit.Reset(); 57 | Lit.Reset(); 58 | Lit.Reset(); 59 | Lit.Reset(); 60 | Lit.Reset(); 61 | Lit.Reset(); 62 | 63 | } 64 | 65 | internal const char LiteralMark = (char)2; 66 | internal static Dictionary LiteralTypes = new Dictionary(); 67 | internal static Dictionary LitTypeID = new Dictionary(){ 68 | {typeof(string),1 },{typeof(int),2 } ,{typeof(long),3}, 69 | {typeof(float),4 },{typeof(double),5 },{typeof(decimal),6}, 70 | {typeof(char),7},{typeof(object),8} 71 | }; 72 | 73 | 74 | static class Lit 75 | { 76 | const int StartCapacity = 16; 77 | public static T[] Literals = new T[StartCapacity]; 78 | static int Count = 0; 79 | public static void Reset() 80 | { 81 | Literals = new T[StartCapacity]; 82 | Count = 0; 83 | } 84 | public static int Find(T v) 85 | { 86 | int n = Array.IndexOf(Literals, v, 0, Count); 87 | if (n < 0) return -1; 88 | return GetID(typeof(T), n); 89 | } 90 | 91 | public static int Add(int id, T value) 92 | { 93 | Count++; 94 | if (Count > Literals.Length) Array.Resize(ref Literals, Count * 2); 95 | int n = Count - 1; 96 | Literals[n] = value; 97 | 98 | return GetID(typeof(T), n); 99 | } 100 | static int GetID(Type t, int n) => int.Parse(LitTypeID[t].ToString() + n.ToString()); 101 | public static void TrimExcess() 102 | { 103 | Array.Resize(ref Literals, Count); 104 | } 105 | 106 | } 107 | 108 | internal static object GetLitByStr(string id) 109 | { 110 | id = id.Trim(LiteralMark); 111 | TypeID typeId = GetLitTypeIdByStr(id, true); 112 | int litIndex = int.Parse(id.Substring(1)); 113 | return GetLiteral(litIndex, typeId); 114 | } 115 | internal static TypeID GetLitTypeIdByStr(string id, bool skipTrim = false) 116 | { 117 | if (!skipTrim) id = id.TrimStart(LiteralMark); 118 | int typeNum = int.Parse(id.Substring(0, 1)); 119 | var type = LitTypeID.Where(x => x.Value == typeNum).Select(x => x.Key).FirstOrDefault(); 120 | TypeID typeId = GetTypeID(type); 121 | return typeId; 122 | } 123 | 124 | internal static object GetLiteral(int id, TypeID type) 125 | { 126 | try 127 | { 128 | switch (type) 129 | { 130 | case TypeID.Int: return Lit.Literals[id]; 131 | case TypeID.Double: return Lit.Literals[id]; 132 | case TypeID.String: return Lit.Literals[id]; 133 | case TypeID.Float: return Lit.Literals[id]; 134 | case TypeID.Decimal: return Lit.Literals[id]; 135 | case TypeID.Long: return Lit.Literals[id]; 136 | case TypeID.Char: return Lit.Literals[id]; 137 | case TypeID.Object: return Lit.Literals[id]; 138 | default: throw new ScriptExecutionException($"Literal of type '{type}' not found."); 139 | } 140 | } 141 | catch (IndexOutOfRangeException) 142 | { 143 | throw new ScriptExecutionException($"Failed to get literal."); 144 | } 145 | } 146 | internal static T GetLiteral(int id) 147 | { 148 | try 149 | { 150 | return Lit.Literals[id]; 151 | } 152 | catch (IndexOutOfRangeException) 153 | { 154 | throw new ScriptExecutionException($"Failed to get literal."); 155 | } 156 | } 157 | 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /OverScript/Script.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using static OverScript.ScriptClass; 4 | 5 | namespace OverScript 6 | { 7 | public class Script 8 | { 9 | internal string ScriptDir; 10 | internal string ScriptFile; 11 | public string File => ScriptFile; 12 | public Dictionary AppInfo = new Dictionary() { { "Name", "App" } }; 13 | 14 | internal int LockNum = 0; 15 | internal int ChainItemNum = 0; 16 | internal int ClassCount = 0; 17 | 18 | internal List CodeFiles = new List(); 19 | internal int FnLayer; 20 | internal ScriptFunction CurrentBuildFn; 21 | internal ScriptClass FThis; 22 | internal CodeUnit CurrentBuildCU; 23 | internal List> AvtProcessed = new List>(); 24 | 25 | internal BasicFunctions BFuncs = new BasicFunctions(); 26 | 27 | internal Dictionary AllTypes; 28 | 29 | 30 | 31 | internal List[] VarNames = new List[MaxTypeId + 1]; 32 | internal ExceptionVarID ExVarID; 33 | 34 | internal ScriptClass RootClass; 35 | internal EventHandler OnProgress; 36 | public const int LoadingSteps = 5; 37 | public Script(string file, EventHandler onProgress = null) 38 | { 39 | OnProgress = onProgress; 40 | 41 | VarNames[(int)TypeID.IntArray] = new List(); 42 | VarNames[(int)TypeID.ObjectArray] = new List(); 43 | VarNames[(int)TypeID.LongArray] = new List(); 44 | VarNames[(int)TypeID.FloatArray] = new List(); 45 | VarNames[(int)TypeID.DoubleArray] = new List(); 46 | VarNames[(int)TypeID.DecimalArray] = new List(); 47 | VarNames[(int)TypeID.StringArray] = new List(); 48 | VarNames[(int)TypeID.CharArray] = new List(); 49 | VarNames[(int)TypeID.BoolArray] = new List(); 50 | VarNames[(int)TypeID.ShortArray] = new List(); 51 | VarNames[(int)TypeID.ByteArray] = new List(); 52 | VarNames[(int)TypeID.DateArray] = new List(); 53 | VarNames[(int)TypeID.CustomArray] = new List(); 54 | 55 | VarNames[(int)TypeID.Int] = new List(); 56 | VarNames[(int)TypeID.String] = new List(); 57 | VarNames[(int)TypeID.Char] = new List(); 58 | VarNames[(int)TypeID.Double] = new List(); 59 | VarNames[(int)TypeID.Float] = new List(); 60 | VarNames[(int)TypeID.Long] = new List(); 61 | VarNames[(int)TypeID.Decimal] = new List(); 62 | VarNames[(int)TypeID.Bool] = new List(); 63 | VarNames[(int)TypeID.Object] = new List(); 64 | VarNames[(int)TypeID.Short] = new List(); 65 | VarNames[(int)TypeID.Byte] = new List(); 66 | VarNames[(int)TypeID.Date] = new List(); 67 | VarNames[(int)TypeID.Custom] = new List(); 68 | 69 | ExVarID = new ExceptionVarID(this); 70 | AllTypes = new Dictionary(); 71 | foreach (var item in BasicTypes) AllTypes.Add(item.Key, item.Value); 72 | 73 | 74 | ScriptFile = file; 75 | ScriptDir = System.IO.Path.GetDirectoryName(file); 76 | string code = System.IO.File.ReadAllText(ScriptFile, System.Text.Encoding.UTF8); 77 | 78 | int i = code.IndexOf("#app "); 79 | 80 | if (i >= 0) 81 | { 82 | 83 | bool atStart = false; 84 | if (i > 0) 85 | { 86 | string preText = code.Substring(0, i).Replace("\r", "").Replace("\n", "").Replace("\t", "").Trim(); 87 | atStart = preText.Length == 0; 88 | } 89 | else atStart = true; 90 | 91 | if (atStart) 92 | { 93 | var d = PPDirective.CuteDirectiveLine(ref code, "app"); 94 | if (d.str != null) 95 | { 96 | string origDirective = d.str; 97 | try { MakePreCode(this, ref d.str, ScriptFile); } 98 | catch (Exception ex) { throw new ScriptLoadingException($"Invalid app directive. " + ex.Message); } 99 | 100 | d.str = d.str.TrimStart(); 101 | var ppd = PPDirective.Get(d.str, ScriptFile, this); 102 | if (ppd.Data == null || !CheckCharsInVarName(ppd.Data)) 103 | throw new ScriptLoadingException($"Invalid app name '{ppd.Data}'."); 104 | 105 | AppInfo["Name"] = ppd.Data; 106 | if (ppd.Params != null) 107 | { 108 | foreach (var item in ppd.Params) 109 | AppInfo[item.Key] = item.Value; 110 | } 111 | 112 | } 113 | } 114 | 115 | 116 | 117 | } 118 | 119 | SetLoadingProgress(0); 120 | RootClass = new ScriptClass(code, AppInfo["Name"], null, this); 121 | SetLoadingProgress(-1); 122 | 123 | } 124 | 125 | internal void SetLoadingProgress(int step) 126 | { 127 | if (OnProgress != null) OnProgress(this, step); 128 | } 129 | 130 | internal int GetVarID(string name) 131 | { 132 | TypeID tid = GetTypeID(typeof(T)); 133 | return GetVarID(name, tid); 134 | 135 | 136 | } 137 | internal int GetVarID(string name, TypeID tid) 138 | { 139 | if (tid == TypeID.CustomArray) tid = TypeID.Custom; 140 | var names = VarNames[(int)tid]; 141 | 142 | int i = names.IndexOf(name); 143 | if (i < 0) { names.Add(name); i = names.Count - 1; } 144 | return i; 145 | } 146 | internal string VarIdToName(int id) => VarNames[(int)GetTypeID(typeof(T))][id]; 147 | internal int VarNameCount() => VarNames[(int)GetTypeID(typeof(T))].Count; 148 | 149 | 150 | 151 | internal void ImportFunctions(ref string code, string file) 152 | { 153 | 154 | 155 | var d = PPDirective.CuteDirectiveLine(ref code, "import"); 156 | while (d.str != null) 157 | { 158 | var ppd = PPDirective.Get(d.str, file, this); 159 | BFuncs.Import(ppd); 160 | 161 | d = PPDirective.CuteDirectiveLine(ref code, "import", d.pos); 162 | } 163 | } 164 | 165 | 166 | 167 | 168 | 169 | public static implicit operator Executor(Script script) 170 | { 171 | return new Executor(script); 172 | } 173 | 174 | } 175 | 176 | 177 | } 178 | -------------------------------------------------------------------------------- /OverScript/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | 5 | namespace OverScript 6 | { 7 | class Program 8 | { 9 | 10 | static System.Reflection.Assembly ExecutingAssembly = System.Reflection.Assembly.GetExecutingAssembly(); 11 | static public string OverScriptDir = Path.GetDirectoryName(ExecutingAssembly.Location); 12 | static public string ModulesDir = Path.Combine(Program.OverScriptDir, "modules"); 13 | 14 | static int LoadingStatusCurPos = 0; 15 | 16 | 17 | static void Main(string[] args) 18 | { 19 | 20 | Console.Title = "OverScript"; 21 | AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhException); 22 | ExecPool.Capacity = 1; 23 | 24 | string scriptFile; 25 | string[] scriptArgs; 26 | 27 | if (args.Length > 0) 28 | { 29 | scriptFile = Path.GetFullPath(args[0]); 30 | scriptArgs = args.Skip(1).ToArray(); 31 | } 32 | else 33 | { 34 | Console.WriteLine("Welcome to OverScript v" + ExecutingAssembly.GetName().Version+ ". See more at overscript.org."); 35 | Console.Write("Enter app file: "); 36 | string cl = Console.ReadLine(); 37 | if (cl.Length < 1) throw new FileLoadException("App file not specified."); 38 | 39 | 40 | var scriptToRun = ScriptClass.GetFileAndArgs(cl); 41 | scriptFile = scriptToRun.file; 42 | scriptArgs = scriptToRun.args; 43 | } 44 | 45 | 46 | string code = System.IO.File.ReadAllText(scriptFile, System.Text.Encoding.UTF8); 47 | 48 | 49 | 50 | Console.CursorVisible = false; 51 | Console.Clear(); 52 | 53 | string loadingStr = $"{Path.GetFileName(scriptFile)} loading"; 54 | LoadingStatusCurPos = loadingStr.Length + 1; 55 | if (LoadingStatusCurPos >= Console.WindowWidth) LoadingStatusCurPos = Console.WindowWidth - 1; 56 | 57 | Console.Write(loadingStr + " ..."); 58 | 59 | 60 | Script script = new Script(scriptFile, LoadingProgressChanged); 61 | Console.Clear(); 62 | 63 | string scriptName = script.AppInfo["Name"]; 64 | Console.Title = scriptName; 65 | 66 | string currentCulture; 67 | script.AppInfo.TryGetValue("CurrentCulture", out currentCulture); 68 | if (currentCulture != null) 69 | System.Globalization.CultureInfo.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo(currentCulture); 70 | 71 | 72 | 73 | Console.CursorVisible = true; 74 | 75 | Executor exec = new Executor(script); 76 | exec.Execute(scriptArgs); 77 | 78 | } 79 | 80 | public static void LoadingProgressChanged(object sender, int step) 81 | { 82 | Console.SetCursorPosition(LoadingStatusCurPos, 0); 83 | 84 | Console.WriteLine(step < 0 ? "completed." : $"[{new string('#', step).PadRight(Script.LoadingSteps, '-')}]"); 85 | } 86 | 87 | 88 | static void UnhException(object sender, UnhandledExceptionEventArgs args) 89 | { 90 | Exception e = (Exception)args.ExceptionObject; 91 | string s = ""; 92 | 93 | if (e.Data.Contains(ExceptionVarName.TypeName)) 94 | s += (e.Data[ExceptionVarName.TypeName] ?? "Null_exception_name") + ": " + (e.Data[ExceptionVarName.Message] ?? "Null_exception_message") + Environment.NewLine; 95 | else 96 | { 97 | s = e.GetType().Name + ": " + e.Message; 98 | Exception ie = e.InnerException; 99 | while (ie != null) 100 | { 101 | s += " ---> " + ie.GetType().Name + ": " + ie.Message; 102 | ie = ie.InnerException; 103 | } 104 | s += Environment.NewLine; 105 | } 106 | if (e.Data.Contains(ExceptionVarName.StackTrace)) s += e.Data[ExceptionVarName.StackTrace] + Environment.NewLine; 107 | 108 | Console.WriteLine(s); 109 | Console.WriteLine("Interpreter stack trace:"); 110 | Console.WriteLine(e.StackTrace); 111 | Console.WriteLine(""); 112 | Console.WriteLine("Press any key to exit"); 113 | Console.ReadKey(); 114 | Environment.Exit(0); 115 | } 116 | } 117 | 118 | public enum TypeID : byte 119 | { 120 | None, Void, Bool, Byte, Short, Int, Long, Float, Double, Decimal, String, Char, Date, Object, Custom, 121 | BoolArray, ByteArray, ShortArray, IntArray, LongArray, FloatArray, DoubleArray, DecimalArray, StringArray, CharArray, DateArray, ObjectArray, CustomArray, 122 | 123 | Type, BasicType, CustomType, Array, OfRefType, Empty, HintType, HintArrayType, 124 | 125 | BoolType, ByteType, ShortType, IntType, LongType, FloatType, DoubleType, DecimalType, StringType, CharType, DateType, ObjectType, 126 | BoolArrayType, ByteArrayType, ShortArrayType, IntArrayType, LongArrayType, FloatArrayType, DoubleArrayType, DecimalArrayType, StringArrayType, CharArrayType, DateArrayType, ObjectArrayType 127 | } 128 | 129 | public static class ExecPool 130 | { 131 | public static int Capacity = 64; 132 | internal static Executor[] Executors = new Executor[Capacity]; 133 | internal static int LastID = -1; 134 | internal static int GetVacantID(Executor exec) 135 | { 136 | int n = LastID; 137 | bool fromStart = false; 138 | do 139 | { 140 | n++; 141 | if (n >= Capacity) { n = 0; fromStart = true; } 142 | if (n > LastID && fromStart) throw new InvalidOperationException($"The maximum number of executors is used ({Capacity})."); 143 | 144 | 145 | } while (Executors[n] != null); 146 | 147 | LastID = n; 148 | Executors[n] = exec; 149 | return n; 150 | } 151 | } 152 | 153 | 154 | public class CodeFile 155 | { 156 | public string File; 157 | public string[] Lines; 158 | public string Base; 159 | public int Num; 160 | public CodeFile(string file, int num, Script script) 161 | { 162 | File = file; 163 | Lines = new string[0]; 164 | Base = script.ScriptDir; 165 | Num = num; 166 | } 167 | } 168 | 169 | 170 | public static class StringExtension 171 | { 172 | 173 | public static int EIndexOf(this string text, string str, int pos, StringComparison stringComparison = StringComparison.InvariantCulture) 174 | { 175 | if (pos < 0 || pos >= text.Length) return -1; 176 | return text.IndexOf(str, pos, stringComparison); 177 | } 178 | public static int EIndexOf(this string text, char str, int pos) 179 | { 180 | if (pos < 0 || pos >= text.Length) return -1; 181 | return text.IndexOf(str, pos); 182 | } 183 | public static int ELastIndexOf(this string text, string str, int pos, StringComparison stringComparison = StringComparison.InvariantCulture) 184 | { 185 | if (pos < 0 || pos >= text.Length) return -1; 186 | return text.LastIndexOf(str, pos, stringComparison); 187 | } 188 | public static int ELastIndexOf(this string text, char str, int pos) 189 | { 190 | if (pos < 0 || pos >= text.Length) return -1; 191 | return text.LastIndexOf(str, pos); 192 | } 193 | 194 | 195 | } 196 | 197 | 198 | 199 | class ScriptExecutionException : Exception 200 | { 201 | public ScriptExecutionException(string message) : base(ScriptClass.RestoreCode(message)) { } 202 | 203 | } 204 | public class ScriptLoadingException : Exception 205 | { 206 | public ScriptLoadingException(string message) : base(ScriptClass.RestoreCode(message)) { } 207 | } 208 | class CustomThrownException : Exception 209 | { 210 | public CustomThrownException(string message) : base(message) { } 211 | public CustomThrownException() : base() { } 212 | } 213 | class InvalidByRefArgumentException : Exception 214 | { 215 | public InvalidByRefArgumentException(string message) : base(ScriptClass.RestoreCode(message)) { } 216 | } 217 | 218 | class ExecutingCanceledException : Exception 219 | { 220 | public ExecutingCanceledException() : base("Executing is canceled.") { } 221 | public ExecutingCanceledException(string message) : base(message) { } 222 | public static Exception GetCanceledException(bool forced, string msg = null) 223 | { 224 | return forced ? new ExecutingForciblyCanceledException(msg) : new ExecutingCanceledException(msg); 225 | } 226 | } 227 | class ExecutingForciblyCanceledException : ExecutingCanceledException 228 | { 229 | public ExecutingForciblyCanceledException() : base("Executing is forcibly canceled.") { } 230 | public ExecutingForciblyCanceledException(string message) : base(message) { } 231 | } 232 | class FinalizationFailedException : Exception 233 | { 234 | public FinalizationFailedException(string message, Exception inner) : base(message, inner) { } 235 | } 236 | 237 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 238 | public class ImportAttribute : System.Attribute { } 239 | 240 | 241 | 242 | 243 | 244 | public static class ExceptionVarName 245 | { 246 | public static string TypeName = "exName"; 247 | public static string Message = "exMessage"; 248 | public static string CustomExObj = "exObject"; 249 | public static string Object = "exception"; 250 | public static string StackTrace = "stackTrace"; 251 | 252 | public static string NameVarInCustomExClass = "Name"; 253 | public static string MessageVarInCustomExClass = "Message"; 254 | } 255 | 256 | 257 | } 258 | -------------------------------------------------------------------------------- /OverScript/Variables.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static OverScript.ScriptClass; 3 | 4 | namespace OverScript 5 | { 6 | 7 | public class Var 8 | { 9 | public string Name; 10 | public object Value; 11 | public Var(string name, object value) 12 | { 13 | Name = name; 14 | Value = value; 15 | } 16 | } 17 | 18 | public struct VarKey 19 | { 20 | public int id; 21 | public int scope; 22 | 23 | public VarKey(int id, int scope) 24 | { 25 | this.id = id; 26 | this.scope = scope; 27 | } 28 | } 29 | public class VarName 30 | { 31 | public string Name; 32 | public int Id; 33 | public VarType Type; 34 | public bool IsPublic; 35 | public VarName(Script script, string name, VarType type, bool pub = false) 36 | { 37 | Name = name; 38 | Type = type; 39 | Id = script.GetVarID(name, type.ID); 40 | IsPublic = pub; 41 | } 42 | 43 | } 44 | 45 | 46 | public partial class Executor 47 | { 48 | 49 | 50 | internal void SetVarStorage() 51 | { 52 | ScriptVars.BuildVarStorage(this); 53 | ScriptVars.BuildVarStorage(this); 54 | ScriptVars.BuildVarStorage(this); 55 | ScriptVars.BuildVarStorage(this); 56 | ScriptVars.BuildVarStorage(this); 57 | ScriptVars.BuildVarStorage(this); 58 | ScriptVars.BuildVarStorage(this); 59 | ScriptVars.BuildVarStorage(this); 60 | ScriptVars.BuildVarStorage(this); 61 | ScriptVars.BuildVarStorage(this); 62 | ScriptVars.BuildVarStorage(this); 63 | ScriptVars.BuildVarStorage(this); 64 | 65 | ScriptVars.BuildVarStorage(this); 66 | ScriptVars.BuildVarStorage(this); 67 | ScriptVars.BuildVarStorage(this); 68 | ScriptVars.BuildVarStorage(this); 69 | ScriptVars.BuildVarStorage(this); 70 | ScriptVars.BuildVarStorage(this); 71 | ScriptVars.BuildVarStorage(this); 72 | ScriptVars.BuildVarStorage(this); 73 | ScriptVars.BuildVarStorage(this); 74 | ScriptVars.BuildVarStorage(this); 75 | ScriptVars.BuildVarStorage(this); 76 | ScriptVars.BuildVarStorage(this); 77 | ScriptVars.BuildVarStorage(this); 78 | 79 | } 80 | internal void DropVarStorage() 81 | { 82 | ScriptVars.ResetVarStorage(this); 83 | ScriptVars.ResetVarStorage(this); 84 | ScriptVars.ResetVarStorage(this); 85 | ScriptVars.ResetVarStorage(this); 86 | ScriptVars.ResetVarStorage(this); 87 | ScriptVars.ResetVarStorage(this); 88 | ScriptVars.ResetVarStorage(this); 89 | ScriptVars.ResetVarStorage(this); 90 | ScriptVars.ResetVarStorage(this); 91 | ScriptVars.ResetVarStorage(this); 92 | ScriptVars.ResetVarStorage(this); 93 | ScriptVars.ResetVarStorage(this); 94 | 95 | ScriptVars.ResetVarStorage(this); 96 | ScriptVars.ResetVarStorage(this); 97 | ScriptVars.ResetVarStorage(this); 98 | ScriptVars.ResetVarStorage(this); 99 | ScriptVars.ResetVarStorage(this); 100 | ScriptVars.ResetVarStorage(this); 101 | ScriptVars.ResetVarStorage(this); 102 | ScriptVars.ResetVarStorage(this); 103 | ScriptVars.ResetVarStorage(this); 104 | ScriptVars.ResetVarStorage(this); 105 | ScriptVars.ResetVarStorage(this); 106 | ScriptVars.ResetVarStorage(this); 107 | ScriptVars.ResetVarStorage(this); 108 | 109 | } 110 | internal void ClearVarsInScope(int scope) 111 | { 112 | ScriptVars.ClearScope(ExecID, scope); 113 | ScriptVars.ClearScope(ExecID, scope); 114 | ScriptVars.ClearScope(ExecID, scope); 115 | ScriptVars.ClearScope(ExecID, scope); 116 | ScriptVars.ClearScope(ExecID, scope); 117 | ScriptVars.ClearScope(ExecID, scope); 118 | ScriptVars.ClearScope(ExecID, scope); 119 | ScriptVars.ClearScope(ExecID, scope); 120 | ScriptVars.ClearScope(ExecID, scope); 121 | ScriptVars.ClearScope(ExecID, scope); 122 | ScriptVars.ClearScope(ExecID, scope); 123 | ScriptVars.ClearScope(ExecID, scope); 124 | 125 | ScriptVars.ClearScope(ExecID, scope); 126 | ScriptVars.ClearScope(ExecID, scope); 127 | ScriptVars.ClearScope(ExecID, scope); 128 | ScriptVars.ClearScope(ExecID, scope); 129 | ScriptVars.ClearScope(ExecID, scope); 130 | ScriptVars.ClearScope(ExecID, scope); 131 | ScriptVars.ClearScope(ExecID, scope); 132 | ScriptVars.ClearScope(ExecID, scope); 133 | ScriptVars.ClearScope(ExecID, scope); 134 | ScriptVars.ClearScope(ExecID, scope); 135 | ScriptVars.ClearScope(ExecID, scope); 136 | ScriptVars.ClearScope(ExecID, scope); 137 | ScriptVars.ClearScope(ExecID, scope); 138 | 139 | } 140 | 141 | 142 | internal int NewScope() 143 | { 144 | lock (FreeScopesLocker) 145 | { 146 | 147 | return FreeScopes.Count > 0 ? FreeScopes.Pop() : ++LastScope; 148 | } 149 | } 150 | 151 | 152 | 153 | 154 | internal static object GetVarValue(Executor exec, TypeID type, int id, int scope) 155 | { 156 | switch (type) 157 | { 158 | case TypeID.IntArray: return ScriptVars.Get(exec, id, scope); 159 | case TypeID.ObjectArray: return ScriptVars.Get(exec, id, scope); 160 | case TypeID.LongArray: return ScriptVars.Get(exec, id, scope); 161 | case TypeID.FloatArray: return ScriptVars.Get(exec, id, scope); 162 | case TypeID.DoubleArray: return ScriptVars.Get(exec, id, scope); 163 | case TypeID.DecimalArray: return ScriptVars.Get(exec, id, scope); 164 | case TypeID.StringArray: return ScriptVars.Get(exec, id, scope); 165 | case TypeID.CharArray: return ScriptVars.Get(exec, id, scope); 166 | case TypeID.BoolArray: return ScriptVars.Get(exec, id, scope); 167 | case TypeID.ShortArray: return ScriptVars.Get(exec, id, scope); 168 | case TypeID.ByteArray: return ScriptVars.Get(exec, id, scope); 169 | case TypeID.DateArray: return ScriptVars.Get(exec, id, scope); 170 | 171 | case TypeID.Int: return ScriptVars.Get(exec, id, scope); 172 | case TypeID.String: return ScriptVars.Get(exec, id, scope); 173 | case TypeID.Char: return ScriptVars.Get(exec, id, scope); 174 | case TypeID.Double: return ScriptVars.Get(exec, id, scope); 175 | case TypeID.Float: return ScriptVars.Get(exec, id, scope); 176 | case TypeID.Long: return ScriptVars.Get(exec, id, scope); 177 | case TypeID.Decimal: return ScriptVars.Get(exec, id, scope); 178 | case TypeID.Bool: return ScriptVars.Get(exec, id, scope); 179 | case TypeID.Object: return ScriptVars.Get(exec, id, scope); 180 | case TypeID.Short: return ScriptVars.Get(exec, id, scope); 181 | case TypeID.Byte: return ScriptVars.Get(exec, id, scope); 182 | case TypeID.Date: return ScriptVars.Get(exec, id, scope); 183 | case TypeID.CustomArray: 184 | case TypeID.Custom: return ScriptVars.Get(exec, id, scope); 185 | default: throw new ScriptExecutionException($"Type '{type}' not supported."); 186 | } 187 | 188 | } 189 | 190 | internal static class ScriptVars 191 | { 192 | public static T[][][] VarStorage = new T[ExecPool.Capacity][][]; 193 | static object[][] VarStorageResizeLocker = new object[ExecPool.Capacity][]; 194 | public static void BuildVarStorage(Executor exec) 195 | { 196 | int execId = exec.ExecID; 197 | int c = exec.ExecutedScript.VarNameCount(); 198 | VarStorage[execId] = new T[c][]; 199 | VarStorageResizeLocker[execId] = new object[c]; 200 | 201 | for (int i = 0; i < VarStorage[execId].Length; i++) 202 | { 203 | Array.Resize(ref VarStorage[execId][i], 16); 204 | VarStorageResizeLocker[execId][i] = new object(); 205 | } 206 | 207 | } 208 | public static void ResetVarStorage(Executor exec) 209 | { 210 | VarStorage[exec.ExecID] = null; 211 | } 212 | public static T SetAndReturnPrev(Executor exec, int id, int scope, ref T value) 213 | { 214 | try 215 | { 216 | int execId = exec.ExecID; 217 | T prev = VarStorage[execId][id][scope]; 218 | VarStorage[execId][id][scope] = value; 219 | return prev; 220 | } 221 | catch (NullReferenceException) 222 | { 223 | throw new ScriptExecutionException(exec.Disposed ? "Executor is disposed." : $"Failed to set variable '{exec.ExecutedScript.VarIdToName(id)}' due to corruption of variable store."); 224 | } 225 | catch (IndexOutOfRangeException) 226 | { 227 | throw new ScriptExecutionException(exec.Disposed ? "Executor is disposed." : $"Variable '{exec.ExecutedScript.VarIdToName(id)}' not found."); 228 | } 229 | } 230 | public static void Set(Executor exec, int id, int scope, ref T value) 231 | { 232 | try 233 | { 234 | int execId = exec.ExecID; 235 | lock (VarStorageResizeLocker[execId][id]) 236 | { 237 | VarStorage[execId][id][scope] = value; 238 | } 239 | 240 | } 241 | catch (NullReferenceException) 242 | { 243 | throw new ScriptExecutionException(exec.Disposed ? "Executor is disposed." : $"Failed to set variable '{exec.ExecutedScript.VarIdToName(id)}' due to corruption of variable store."); 244 | } 245 | catch (IndexOutOfRangeException) 246 | { 247 | throw new ScriptExecutionException(exec.Disposed ? "Executor is disposed." : $"Variable '{exec.ExecutedScript.VarIdToName(id)}' not found."); 248 | } 249 | } 250 | 251 | 252 | 253 | 254 | 255 | public static void AddSet(Executor exec, int id, int scope, ref T value) 256 | { 257 | 258 | if (scope >= VarStorage[exec.ExecID][id].Length) Add(exec, id, scope, value); 259 | else Set(exec, id, scope, ref value); 260 | 261 | } 262 | public static void Add(Executor exec, int id, int scope, T defVal = default) 263 | { 264 | int execId = exec.ExecID; 265 | var vs = VarStorage[execId]; 266 | var v = vs[id]; 267 | if (scope >= v.Length) 268 | { 269 | lock (VarStorageResizeLocker[execId][id]) 270 | { 271 | if (scope >= v.Length) 272 | { 273 | 274 | Array.Resize(ref vs[id], scope * 2); 275 | 276 | } 277 | } 278 | v = vs[id]; 279 | } 280 | 281 | ref T var = ref v[scope]; 282 | var = defVal; 283 | 284 | } 285 | public static T Get(Executor exec, int id, int scope) 286 | { 287 | try 288 | { 289 | 290 | 291 | var v = VarStorage[exec.ExecID][id][scope]; 292 | 293 | return v; 294 | 295 | } 296 | catch (NullReferenceException) 297 | { 298 | throw new ScriptExecutionException(exec.Disposed ? "Executor is disposed." : $"Failed to get variable '{exec.ExecutedScript.VarIdToName(id)}' due to corruption of variable store."); 299 | } 300 | catch (IndexOutOfRangeException) 301 | { 302 | throw new ScriptExecutionException(exec.Disposed ? "Executor is disposed." : $"Variable '{exec.ExecutedScript.VarIdToName(id)}' not found."); 303 | } 304 | 305 | } 306 | 307 | 308 | public static void RemVarsByIds(int execId, int[] ids, int scope) 309 | { 310 | try 311 | { 312 | 313 | var vs = VarStorage[execId]; 314 | for (int j = 0; j < ids.Length; j++) 315 | { 316 | int i = ids[j]; 317 | 318 | if (vs[i].Length <= scope) continue; 319 | vs[i][scope] = default(T); 320 | 321 | } 322 | } 323 | catch (Exception ex) 324 | { 325 | if (ex is IndexOutOfRangeException || ex is NullReferenceException) 326 | throw new ScriptExecutionException("Failed to clear variables due to corruption of variable store."); 327 | 328 | throw; 329 | } 330 | 331 | } 332 | public static void ClearScope(int execId, int scope) 333 | { 334 | var vs = VarStorage[execId]; 335 | for (int i = 0; i < vs.Length; i++) 336 | { 337 | if (vs[i].Length <= scope) continue; 338 | vs[i][scope] = default(T); 339 | 340 | } 341 | 342 | } 343 | 344 | 345 | } 346 | 347 | 348 | 349 | public int DisposeStored(bool allowExecuting = false) 350 | { 351 | int c = 0; 352 | bool canceled = Canceled, forciblyCanceled = ForciblyCanceled; 353 | if (allowExecuting) Canceled = ForciblyCanceled = false; 354 | c += DisposeAll(); 355 | c += DisposeAll(); 356 | c += DisposeAll(); 357 | if (allowExecuting) { Canceled = canceled; ForciblyCanceled = forciblyCanceled; } 358 | 359 | return c; 360 | } 361 | internal int DisposeAll() 362 | { 363 | int c = 0; 364 | var vs = ScriptVars.VarStorage[ExecID]; 365 | foreach (var vars in vs) 366 | { 367 | foreach (var obj in vars) 368 | { 369 | if (obj is IDisposable d) 370 | { 371 | try { d.Dispose(); c++; } catch { } 372 | 373 | } 374 | else if (obj is Array arr) c += DisposeArray(arr); 375 | 376 | } 377 | 378 | } 379 | return c; 380 | } 381 | internal int DisposeArray(Array arr) 382 | { 383 | int c = 0; 384 | for (int i = 0; i < arr.Length; i++) 385 | { 386 | object v = arr.GetValue(i); 387 | if (v is IDisposable d) 388 | { 389 | try { d.Dispose(); c++; } catch { } 390 | 391 | } 392 | else if (v is Array) c += DisposeArray(arr); 393 | 394 | } 395 | return c; 396 | } 397 | internal void RemVars(int scope, VarsToClear forCleaning) 398 | { 399 | if (ForciblyCanceled) return; 400 | 401 | 402 | 403 | 404 | int flagCount = forCleaning.UsedTypeCount; 405 | 406 | if (forCleaning.StringUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.String], scope); if (--flagCount == 0) goto remEnd; } 407 | if (forCleaning.ObjectUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.Object], scope); if (--flagCount == 0) goto remEnd; } 408 | 409 | if (forCleaning.CustomUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.Custom], scope); if (--flagCount == 0) goto remEnd; } 410 | 411 | if (forCleaning.IntArrayUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.IntArray], scope); if (--flagCount == 0) goto remEnd; } 412 | if (forCleaning.StringArrayUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.StringArray], scope); if (--flagCount == 0) goto remEnd; } 413 | if (forCleaning.ByteArrayUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.ByteArray], scope); if (--flagCount == 0) goto remEnd; } 414 | if (forCleaning.ObjectArrayUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.ObjectArray], scope); if (--flagCount == 0) goto remEnd; } 415 | 416 | if (forCleaning.LongArrayUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.LongArray], scope); if (--flagCount == 0) goto remEnd; } 417 | if (forCleaning.DoubleArrayUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.DoubleArray], scope); if (--flagCount == 0) goto remEnd; } 418 | if (forCleaning.FloatArrayUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.FloatArray], scope); if (--flagCount == 0) goto remEnd; } 419 | if (forCleaning.BoolArrayUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.BoolArray], scope); if (--flagCount == 0) goto remEnd; } 420 | if (forCleaning.DecimalArrayUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.DecimalArray], scope); if (--flagCount == 0) goto remEnd; } 421 | if (forCleaning.CharArrayUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.CharArray], scope); if (--flagCount == 0) goto remEnd; } 422 | if (forCleaning.ShortArrayUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.ShortArray], scope); } 423 | if (forCleaning.DateArrayUsed) { ScriptVars.RemVarsByIds(ExecID, forCleaning.VarIDs[(int)TypeID.DateArray], scope); } 424 | 425 | 426 | 427 | remEnd: 428 | return; 429 | 430 | 431 | } 432 | internal void FreeScope(int scope) 433 | { 434 | lock (FreeScopesLocker) FreeScopes.Push(scope); 435 | } 436 | 437 | 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /OverScript/EvalUnit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using static OverScript.Executor; 4 | using static OverScript.Literals; 5 | using static OverScript.ScriptClass; 6 | 7 | namespace OverScript 8 | { 9 | 10 | public class EvalUnit 11 | { 12 | public bool IsAssignment; 13 | public EvalUnit Parent; 14 | public VarType Type; 15 | public OperationKind OpKind; 16 | public string Code; 17 | public EvalUnit Op1_Unit, Op2_Unit, Path_Unit; 18 | 19 | public ScriptClass ClassLink; 20 | public ArgBlocks[] Nested; 21 | 22 | public EvalUnitKind Kind; 23 | 24 | public int VarID = -1; 25 | 26 | 27 | public VarScopeKind ScopeKind = VarScopeKind.None; 28 | public object Func; 29 | 30 | public object SpecificValue; 31 | public byte RefParamNum = 0; 32 | public bool Postfix = false; 33 | 34 | 35 | public bool ScopeKindIsThisOrStatic = false; 36 | public bool Define = false; 37 | public bool IsArrayItem = false; 38 | public CodeUnit CU = null; 39 | public InvolveInfo InvolveData = null; 40 | 41 | public delegate void PU(int scope, ClassInstance inst, CallStack cstack); 42 | public PU ProcessUnit; 43 | 44 | public class FuncOnExData 45 | { 46 | public EvalUnit ValueEU; 47 | public EvalUnit ExConditionEU; 48 | public EvalUnit TriesEU; 49 | public EvalUnit[] RetryEU; 50 | public EvalUnit RetryWhileEU; 51 | public bool Retry; 52 | public FuncOnExData(EvalUnit value, EvalUnit exConditionEU, bool retry, EvalUnit triesEU, EvalUnit retryWhileEU, EvalUnit[] retryEU) 53 | { 54 | ValueEU = value; 55 | ExConditionEU = exConditionEU; 56 | TriesEU = triesEU; 57 | RetryEU = retryEU; 58 | RetryWhileEU = retryWhileEU; 59 | Retry = retry; 60 | } 61 | } 62 | 63 | public List FuncOnEx = null; 64 | 65 | 66 | public bool EvalBool(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.Bool, scope, inst, cstack); 67 | public byte EvalByte(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.Byte, scope, inst, cstack); 68 | public short EvalShort(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.Short, scope, inst, cstack); 69 | public int EvalInt(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.Int, scope, inst, cstack); 70 | public long EvalLong(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.Long, scope, inst, cstack); 71 | public float EvalFloat(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.Float, scope, inst, cstack); 72 | public double EvalDouble(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.Double, scope, inst, cstack); 73 | public decimal EvalDecimal(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.Decimal, scope, inst, cstack); 74 | public string EvalString(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.String, scope, inst, cstack); 75 | public char EvalChar(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.Char, scope, inst, cstack); 76 | public DateTime EvalDate(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.Date, scope, inst, cstack); 77 | public object EvalObject(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.Object, scope, inst, cstack); 78 | public CustomObject EvalCustom(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.Custom, scope, inst, cstack); 79 | 80 | public bool[] EvalBoolArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.BoolArray, scope, inst, cstack); 81 | public byte[] EvalByteArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.ByteArray, scope, inst, cstack); 82 | public short[] EvalShortArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.ShortArray, scope, inst, cstack); 83 | public int[] EvalIntArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.IntArray, scope, inst, cstack); 84 | public long[] EvalLongArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.LongArray, scope, inst, cstack); 85 | public float[] EvalFloatArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.FloatArray, scope, inst, cstack); 86 | public double[] EvalDoubleArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.DoubleArray, scope, inst, cstack); 87 | public decimal[] EvalDecimalArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.DecimalArray, scope, inst, cstack); 88 | public string[] EvalStringArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.StringArray, scope, inst, cstack); 89 | public char[] EvalCharArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.CharArray, scope, inst, cstack); 90 | public DateTime[] EvalDateArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.DateArray, scope, inst, cstack); 91 | public object[] EvalObjectArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.ObjectArray, scope, inst, cstack); 92 | 93 | public CustomObject EvalCustomArray(int scope, ClassInstance inst, CallStack cstack) => Ev(TypeID.CustomArray, scope, inst, cstack); 94 | public Array EvalArray(int scope, ClassInstance inst, CallStack cstack) => Ev(scope, inst, cstack); 95 | 96 | 97 | public T Ev(TypeID tid, int scope, ClassInstance inst, CallStack cstack) 98 | { 99 | 100 | if (Type.ID == tid) 101 | return Eval(scope, inst, cstack); 102 | else 103 | return Ev(scope, inst, cstack); 104 | 105 | } 106 | 107 | public T Ev(int scope, ClassInstance inst, CallStack cstack) 108 | { 109 | switch (Type.ID) 110 | { 111 | case TypeID.Int: return Eval(scope, inst, cstack); 112 | case TypeID.Bool: return Eval(scope, inst, cstack); 113 | case TypeID.String: return Eval(scope, inst, cstack); 114 | case TypeID.Char: return Eval(scope, inst, cstack); 115 | case TypeID.Decimal: return Eval(scope, inst, cstack); 116 | case TypeID.Long: return Eval(scope, inst, cstack); 117 | case TypeID.Void: 118 | case TypeID.Object: return Eval(scope, inst, cstack); 119 | case TypeID.Double: return Eval(scope, inst, cstack); 120 | case TypeID.Float: return Eval(scope, inst, cstack); 121 | case TypeID.Byte: return Eval(scope, inst, cstack); 122 | case TypeID.Short: return Eval(scope, inst, cstack); 123 | case TypeID.Date: return Eval(scope, inst, cstack); 124 | 125 | case TypeID.IntArray: return Eval(scope, inst, cstack); 126 | case TypeID.BoolArray: return Eval(scope, inst, cstack); 127 | case TypeID.StringArray: return Eval(scope, inst, cstack); 128 | case TypeID.CharArray: return Eval(scope, inst, cstack); 129 | case TypeID.DecimalArray: return Eval(scope, inst, cstack); 130 | case TypeID.LongArray: return Eval(scope, inst, cstack); 131 | case TypeID.ObjectArray: return Eval(scope, inst, cstack); 132 | case TypeID.DoubleArray: return Eval(scope, inst, cstack); 133 | case TypeID.FloatArray: return Eval(scope, inst, cstack); 134 | case TypeID.ByteArray: return Eval(scope, inst, cstack); 135 | case TypeID.ShortArray: return Eval(scope, inst, cstack); 136 | case TypeID.DateArray: return Eval(scope, inst, cstack); 137 | case TypeID.CustomArray: 138 | case TypeID.Custom: return Eval(scope, inst, cstack); 139 | default: 140 | throw new ScriptExecutionException($"Wrong type '{Type.Name}'."); 141 | } 142 | } 143 | private TR Eval(int scope, ClassInstance inst, CallStack cstack) => TypeConverter.ConvertValue(Eval(scope, inst, cstack)); 144 | 145 | void AddVar(int scope, Executor exec) 146 | { 147 | 148 | ScriptVars.Add(exec, VarID, scope); 149 | } 150 | public void DefineVar(int scope, Executor exec) 151 | { 152 | 153 | 154 | switch (Type.ID) 155 | { 156 | 157 | case TypeID.IntArray: AddVar(scope, exec); break; 158 | case TypeID.ObjectArray: AddVar(scope, exec); break; 159 | case TypeID.LongArray: AddVar(scope, exec); break; 160 | case TypeID.FloatArray: AddVar(scope, exec); break; 161 | case TypeID.DoubleArray: AddVar(scope, exec); break; 162 | case TypeID.DecimalArray: AddVar(scope, exec); break; 163 | case TypeID.StringArray: AddVar(scope, exec); break; 164 | case TypeID.CharArray: AddVar(scope, exec); break; 165 | case TypeID.BoolArray: AddVar(scope, exec); break; 166 | case TypeID.ShortArray: AddVar(scope, exec); break; 167 | case TypeID.ByteArray: AddVar(scope, exec); break; 168 | case TypeID.DateArray: AddVar(scope, exec); break; 169 | 170 | case TypeID.Int: AddVar(scope, exec); break; 171 | case TypeID.String: AddVar(scope, exec); break; 172 | case TypeID.Char: AddVar(scope, exec); break; 173 | case TypeID.Double: AddVar(scope, exec); break; 174 | case TypeID.Float: AddVar(scope, exec); break; 175 | case TypeID.Long: AddVar(scope, exec); break; 176 | case TypeID.Decimal: AddVar(scope, exec); break; 177 | case TypeID.Bool: AddVar(scope, exec); break; 178 | case TypeID.Object: AddVar(scope, exec); break; 179 | case TypeID.Short: AddVar(scope, exec); break; 180 | case TypeID.Byte: AddVar(scope, exec); break; 181 | case TypeID.Date: AddVar(scope, exec); break; 182 | case TypeID.CustomArray: 183 | case TypeID.Custom: AddVar(scope, exec); break; 184 | 185 | default: throw new ScriptExecutionException($"Type '{Type.Name}' not supported."); 186 | } 187 | } 188 | 189 | 190 | 191 | public T Eval(int scope, ClassInstance inst, CallStack cstack) 192 | { 193 | if (inst.Exec.ForciblyCanceled) throw ExecutingCanceledException.GetCanceledException(true); 194 | 195 | 196 | T v, v1, v2; 197 | v1 = v2 = default(T); 198 | switch (Kind) 199 | { 200 | case EvalUnitKind.Assignment: 201 | 202 | v2 = Op2_Unit.Ev(scope, inst, cstack); 203 | 204 | VarInfo varInfo; 205 | 206 | varInfo = Op1_Unit.GetVarInfo(scope, inst, cstack); 207 | if (!Op1_Unit.IsArrayItem) 208 | { 209 | if (Define) AddVar(scope, inst.Exec); 210 | 211 | v = ScriptVars.SetAndReturnPrev(inst.Exec, varInfo.ID, varInfo.Scope, ref v2); 212 | } 213 | else 214 | { 215 | int index = Op1_Unit.Nested[0].Args[0].EvalInt(scope, inst, cstack); 216 | T[] arr; 217 | if (Op1_Unit.Op1_Unit == null) 218 | arr = Type.ID != TypeID.Custom ? ScriptVars.Get(inst.Exec, varInfo.ID, varInfo.Scope) : (T[])(object)(CustomObject[])ScriptVars.Get(inst.Exec, varInfo.ID, varInfo.Scope); 219 | else 220 | arr = Type.ID != TypeID.Custom ? Op1_Unit.Op1_Unit.Eval(scope, inst, cstack) : (T[])(object)(CustomObject[])Op1_Unit.Op1_Unit.Eval(scope, inst, cstack); 221 | 222 | v = SetArrayItemAndReturnPrev(arr, index, ref v2); 223 | } 224 | 225 | if (Postfix) v2 = v; 226 | return v2; 227 | case EvalUnitKind.Literal: 228 | return GetLiteral(VarID); 229 | 230 | case EvalUnitKind.ArrayItem: 231 | { 232 | int index = Nested[0].Args[0].EvalInt(scope, inst, cstack); 233 | T[] arr; 234 | if (Op1_Unit == null) 235 | { 236 | varInfo = GetVarInfo(scope, inst, cstack); 237 | arr = Type.ID != TypeID.Custom ? ScriptVars.Get(inst.Exec, varInfo.ID, varInfo.Scope) : (T[])(object)(CustomObject[])ScriptVars.Get(inst.Exec, varInfo.ID, varInfo.Scope); 238 | 239 | 240 | } 241 | else 242 | arr = Type.ID != TypeID.Custom ? Op1_Unit.Eval(scope, inst, cstack) : (T[])(object)(CustomObject[])Op1_Unit.Eval(scope, inst, cstack); 243 | 244 | v = GetArrayItem(arr, index); 245 | return v; 246 | } 247 | case EvalUnitKind.Function: 248 | FuncToCall fn = (FuncToCall)Func; 249 | varInfo = GetVarInfo(scope, inst, cstack); 250 | 251 | 252 | try 253 | { 254 | v = fn(Nested[0].Args, scope, inst, varInfo.Inst, cstack, this); 255 | return v; 256 | } 257 | catch (ExecutingForciblyCanceledException) { throw; } 258 | catch (Exception ex) 259 | { 260 | if (FuncOnEx == null) throw; 261 | 262 | string exType, exMsg; 263 | object exObj; 264 | if (cstack.SetThrownException(ex)) 265 | { 266 | GetExInfo(ex, out exType, out exMsg, out exObj); 267 | SetExVars(inst.Exec, scope, ex, exType, exMsg, exObj); 268 | } 269 | 270 | int tryNum = 0; 271 | FuncOnExData tryFOE = null; 272 | 273 | findFOE: 274 | 275 | for (int n = 0; n < FuncOnEx.Count; n++) 276 | { 277 | var curFOE = FuncOnEx[n]; 278 | bool cond = curFOE.ExConditionEU == null ? true : curFOE.ExConditionEU.EvalBool(scope, inst, cstack); 279 | if (cond) 280 | { 281 | int triesNum = curFOE.TriesEU == null ? 0 : curFOE.TriesEU.EvalInt(scope, inst, cstack); 282 | bool untilCancel = triesNum == 0 && curFOE.Retry; 283 | 284 | if (tryFOE != curFOE) tryNum = 0; 285 | 286 | tryNum += 1; 287 | 288 | if (untilCancel || tryNum <= triesNum) 289 | 290 | { 291 | try 292 | { 293 | if (curFOE.RetryWhileEU == null || curFOE.RetryWhileEU.EvalBool(scope, inst, cstack)) 294 | { 295 | 296 | if (curFOE.RetryEU != null) 297 | { 298 | for (int actNum = 0; actNum < curFOE.RetryEU.Length; actNum++) 299 | curFOE.RetryEU[actNum].ProcessUnit(scope, inst, cstack); 300 | } 301 | 302 | v = fn(Nested[0].Args, scope, inst, varInfo.Inst, cstack, this); 303 | cstack.SetThrownException(null); 304 | SetExVars(inst.Exec, scope, null, null, null, null); 305 | return v; 306 | } 307 | } 308 | catch (ExecutingForciblyCanceledException) { throw; } 309 | catch (Exception tryEx) 310 | { 311 | 312 | if (cstack.SetThrownException(tryEx)) 313 | { 314 | GetExInfo(tryEx, out exType, out exMsg, out exObj); 315 | SetExVars(inst.Exec, scope, tryEx, exType, exMsg, exObj); 316 | } 317 | 318 | 319 | ex = tryEx; 320 | tryFOE = curFOE; 321 | goto findFOE; 322 | } 323 | } 324 | if (curFOE.ValueEU == null || curFOE.ValueEU.Kind == EvalUnitKind.Empty) throw ex; 325 | 326 | string st = ErrMsgWithLoc(null, CU.CodeLocation, RestoreCode(CU.Code), inst.Class, CU.Fn); 327 | ex.Data[ExceptionVarName.StackTrace] += st; 328 | 329 | return curFOE.ValueEU.Ev(Type.ID, scope, inst, cstack); 330 | } 331 | } 332 | throw ex; 333 | 334 | } 335 | 336 | case EvalUnitKind.Empty: 337 | return default(T); 338 | 339 | case EvalUnitKind.New: 340 | var obj = (T)NewObj(this, scope, inst, cstack); 341 | return obj; 342 | case EvalUnitKind.This: 343 | if (inst.ThisObj == null) 344 | inst.ThisObj = new CustomObject(inst.Exec, inst, inst.Class); 345 | 346 | 347 | return (T)(object)inst.ThisObj; 348 | 349 | case EvalUnitKind.Variable: 350 | if (Define) { AddVar(scope, inst.Exec); v = default(T); } 351 | else 352 | { 353 | varInfo = GetVarInfo(scope, inst, cstack); 354 | v = ScriptVars.Get(inst.Exec, varInfo.ID, varInfo.Scope); 355 | } 356 | return v; 357 | default: 358 | 359 | return (T)SpecificValue; 360 | 361 | } 362 | } 363 | 364 | 365 | 366 | public VarInfo GetVarInfo(int scope, ClassInstance inst, CallStack cstack, bool prevCall = false) 367 | { 368 | 369 | switch (ScopeKind) 370 | { 371 | case VarScopeKind.Local: 372 | return new VarInfo(scope, inst, VarID); 373 | case VarScopeKind.This: 374 | return new VarInfo(inst.Scope, inst, VarID); 375 | case VarScopeKind.Inst: 376 | ClassInstance ci = (ClassInstance)Path_Unit.EvalCustom(scope, inst, cstack); 377 | return new VarInfo(ci.Scope, ci, VarID); 378 | case VarScopeKind.Static: 379 | ci = inst.Exec.GetStaticInstance(ClassLink ?? inst.Class); 380 | return new VarInfo(ci.Scope, ci, VarID); 381 | case VarScopeKind.Ref: 382 | int n = cstack.Items.Count - (prevCall ? 2 : 1); 383 | if (n < 0) throw new ScriptExecutionException($"Failed to get variable '{Code}' by reference."); 384 | 385 | var cs = cstack.Items[n]; 386 | 387 | return cs.Refs != null ? cs.Refs[RefParamNum].VI : cs.Ref.VI; 388 | case VarScopeKind.Involved: 389 | 390 | 391 | 392 | return InvolveData.VI; 393 | 394 | default: 395 | throw new ScriptExecutionException($"Invalid ScopeKind '{ScopeKind}'."); 396 | } 397 | 398 | } 399 | public static EvalUnit GetEUWithSpecificValue(object v) 400 | { 401 | var vt = new VarType(TypeID.Object); 402 | return new EvalUnit() { Kind = EvalUnitKind.SpecificValue, SpecificValue = v, Type = vt, Code = "" }; 403 | } 404 | public static EvalUnit GetEUWithSpecificValue(object v, VarType vt) 405 | { 406 | 407 | return new EvalUnit() { Kind = EvalUnitKind.SpecificValue, SpecificValue = v, Type = vt, Code = "" }; 408 | } 409 | public EvalUnit ShallowCopy() 410 | { 411 | var eu = (EvalUnit)this.MemberwiseClone(); 412 | return eu; 413 | } 414 | public void ShallowCopyTo(EvalUnit eu) 415 | { 416 | 417 | eu.IsAssignment = IsAssignment; 418 | eu.Parent = Parent; 419 | eu.Type = Type; 420 | eu.OpKind = OpKind; 421 | eu.Code = Code; 422 | eu.Op1_Unit = Op1_Unit; 423 | eu.Op2_Unit = Op2_Unit; 424 | eu.Path_Unit = Path_Unit; 425 | eu.ClassLink = ClassLink; 426 | eu.Nested = Nested; 427 | eu.Kind = Kind; 428 | eu.VarID = VarID; 429 | eu.ScopeKind = ScopeKind; 430 | eu.Func = Func; 431 | eu.SpecificValue = SpecificValue; 432 | eu.RefParamNum = RefParamNum; 433 | eu.Postfix = Postfix; 434 | eu.ScopeKindIsThisOrStatic = ScopeKindIsThisOrStatic; 435 | eu.Define = Define; 436 | eu.IsArrayItem = IsArrayItem; 437 | eu.CU = CU; 438 | eu.InvolveData = InvolveData; 439 | eu.ProcessUnit = ProcessUnit; 440 | 441 | 442 | 443 | } 444 | public override string ToString() => RestoreCode(Code); 445 | 446 | 447 | static private T SetArrayItemAndReturnPrev(T[] array, int pos, ref T value) 448 | { 449 | try 450 | { 451 | T prev = array[pos]; 452 | array[pos] = value; 453 | return prev; 454 | } 455 | catch (IndexOutOfRangeException) 456 | { 457 | throw new IndexOutOfRangeException($"Array of size {array.Length} does not contain element with index {pos}."); 458 | } 459 | catch (NullReferenceException) 460 | { 461 | throw new NullReferenceException("Failed to set array element due to the array is null."); 462 | } 463 | } 464 | 465 | static private T GetArrayItem(T[] array, int pos) 466 | { 467 | try 468 | { 469 | return array[pos]; 470 | } 471 | catch (IndexOutOfRangeException) 472 | { 473 | throw new IndexOutOfRangeException($"Array of size {array.Length} does not contain element with index {pos}."); 474 | } 475 | catch (NullReferenceException) 476 | { 477 | throw new NullReferenceException("Failed to get array element due to the array is null."); 478 | } 479 | 480 | 481 | } 482 | 483 | 484 | } 485 | 486 | public class InvolveInfo 487 | { 488 | 489 | 490 | public VarScopeKind RealScopeKind; 491 | public VarInfo VI; 492 | 493 | public InvolveInfo(VarInfo vi, VarScopeKind kind = default) 494 | { 495 | VI = vi; 496 | RealScopeKind = kind; 497 | } 498 | 499 | } 500 | 501 | 502 | } 503 | 504 | -------------------------------------------------------------------------------- /OverScript/CodeExecution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using static OverScript.Executor; 6 | 7 | namespace OverScript 8 | { 9 | public partial class ScriptClass 10 | { 11 | 12 | public static void ResetProcessUnit(EvalUnit eu) 13 | { 14 | switch (eu.Type.ID) 15 | { 16 | case TypeID.Int: SetProcessUnit(eu); return; 17 | case TypeID.String: SetProcessUnit(eu); return; 18 | case TypeID.Char: SetProcessUnit(eu); return; 19 | case TypeID.Long: SetProcessUnit(eu); return; 20 | case TypeID.Float: SetProcessUnit(eu); return; 21 | case TypeID.Double: SetProcessUnit(eu); return; 22 | case TypeID.Decimal: SetProcessUnit(eu); return; 23 | case TypeID.Bool: SetProcessUnit(eu); return; 24 | case TypeID.Void: 25 | case TypeID.Object: SetProcessUnit(eu); return; 26 | case TypeID.Short: SetProcessUnit(eu); return; 27 | case TypeID.Byte: SetProcessUnit(eu); return; 28 | case TypeID.Date: SetProcessUnit(eu); return; 29 | 30 | case TypeID.ObjectArray: SetProcessUnit(eu); return; 31 | case TypeID.IntArray: SetProcessUnit(eu); return; 32 | case TypeID.LongArray: SetProcessUnit(eu); return; 33 | case TypeID.StringArray: SetProcessUnit(eu); return; 34 | case TypeID.CharArray: SetProcessUnit(eu); return; 35 | case TypeID.DoubleArray: SetProcessUnit(eu); return; 36 | case TypeID.FloatArray: SetProcessUnit(eu); return; 37 | case TypeID.BoolArray: SetProcessUnit(eu); return; 38 | case TypeID.DecimalArray: SetProcessUnit(eu); return; 39 | case TypeID.ShortArray: SetProcessUnit(eu); return; 40 | case TypeID.ByteArray: SetProcessUnit(eu); return; 41 | case TypeID.DateArray: SetProcessUnit(eu); return; 42 | case TypeID.CustomArray: 43 | case TypeID.Custom: SetProcessUnit(eu); return; 44 | default: throw new Exception($"Can't to process type '{eu.Type}'."); 45 | } 46 | } 47 | static void SetProcessUnit(EvalUnit eu) 48 | { 49 | eu.ProcessUnit = (int scope, ClassInstance inst, CallStack cstack) => eu.Eval(scope, inst, cstack); 50 | } 51 | private static void SetParamVar(ScriptFunction.FuncParam fnParam, EvalUnit fnArg, int scope, int baseScope, ClassInstance srcInst, ClassInstance inst, CallStack cstack) 52 | { 53 | if (srcInst == null) srcInst = inst; 54 | 55 | switch (fnParam.ParamType.ID) 56 | { 57 | case TypeID.Int: { var v = fnArg.EvalInt(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 58 | case TypeID.String: { var v = fnArg.EvalString(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 59 | case TypeID.Char: { var v = fnArg.EvalChar(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 60 | case TypeID.Long: { var v = fnArg.EvalLong(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 61 | case TypeID.Double: { var v = fnArg.EvalDouble(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 62 | case TypeID.Float: { var v = fnArg.EvalFloat(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 63 | case TypeID.Bool: { var v = fnArg.EvalBool(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 64 | case TypeID.Decimal: { var v = fnArg.EvalDecimal(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 65 | case TypeID.Object: { var v = fnArg.EvalObject(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 66 | case TypeID.Short: { var v = fnArg.EvalShort(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 67 | case TypeID.Byte: { var v = fnArg.EvalByte(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 68 | case TypeID.Date: { var v = fnArg.EvalDate(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 69 | 70 | case TypeID.IntArray: { var v = fnArg.EvalIntArray(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 71 | case TypeID.StringArray: { var v = fnArg.EvalStringArray(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 72 | case TypeID.CharArray: { var v = fnArg.EvalCharArray(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 73 | case TypeID.LongArray: { var v = fnArg.EvalLongArray(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 74 | case TypeID.DoubleArray: { var v = fnArg.EvalDoubleArray(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 75 | case TypeID.FloatArray: { var v = fnArg.EvalFloatArray(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 76 | case TypeID.BoolArray: { var v = fnArg.EvalBoolArray(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 77 | case TypeID.DecimalArray: { var v = fnArg.EvalDecimalArray(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 78 | case TypeID.ObjectArray: { var v = fnArg.EvalObjectArray(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 79 | case TypeID.ShortArray: { var v = fnArg.EvalShortArray(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 80 | case TypeID.ByteArray: { var v = fnArg.EvalByteArray(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 81 | case TypeID.DateArray: { var v = fnArg.EvalDateArray(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 82 | 83 | case TypeID.CustomArray: 84 | case TypeID.Custom: 85 | { var v = fnArg.EvalCustom(baseScope, srcInst, cstack); ScriptVars.AddSet(inst.Exec, fnParam.VarId, scope, ref v); break; } 86 | } 87 | 88 | } 89 | 90 | private static void SetParamsArray(ScriptFunction.FuncParam fnParam, EvalUnit[] fnArgs, int skip, int scope, int baseScope, ClassInstance srcInst, CallStack cstack) 91 | { 92 | 93 | switch (fnParam.ParamType.ID) 94 | { 95 | 96 | case TypeID.IntArray: { var v = GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 97 | case TypeID.StringArray: { var v = GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 98 | case TypeID.CharArray: { var v = GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 99 | case TypeID.LongArray: { var v = GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 100 | case TypeID.DoubleArray: { var v = GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 101 | case TypeID.FloatArray: { var v = GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 102 | case TypeID.BoolArray: { var v = GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 103 | case TypeID.DecimalArray: { var v = GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 104 | case TypeID.ObjectArray: { var v = GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 105 | case TypeID.ShortArray: { var v = GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 106 | case TypeID.ByteArray: { var v = GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 107 | case TypeID.DateArray: { var v = GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 108 | 109 | case TypeID.CustomArray: 110 | case TypeID.Custom: 111 | { var v = new CustomObject(srcInst.Exec, GetParamsVals(fnArgs, skip, baseScope, srcInst, cstack), fnParam.ParamType.CType.Class, true); ScriptVars.AddSet(srcInst.Exec, fnParam.VarId, scope, ref v); break; } 112 | } 113 | } 114 | 115 | static T[] GetParamsVals(EvalUnit[] args, int skip, int baseScope, ClassInstance srcInst, CallStack cstack) 116 | { 117 | if (args == null) return new T[0]; 118 | int c = args.Length - skip; 119 | if (c < 1) return new T[0]; 120 | 121 | T[] arr = new T[c]; 122 | int n = 0; 123 | TypeID tid = GetTypeID(typeof(T)); 124 | for (int i = skip; i < args.Length; i++) 125 | arr[n++] = args[i].Ev(tid, baseScope, srcInst, cstack); 126 | 127 | return arr; 128 | } 129 | 130 | class ForEachEnumeratorAndVarInfo 131 | { 132 | public System.Collections.IEnumerator Enm; 133 | public VarInfo VI; 134 | public ForEachEnumeratorAndVarInfo(System.Collections.IEnumerator enm, VarInfo vi) 135 | { 136 | Enm = enm; 137 | VI = vi; 138 | } 139 | } 140 | 141 | 142 | public static T ExecuteFunction(ScriptFunction fn, EvalUnit[] fnArgs, int baseScope, ClassInstance srcInst, ClassInstance inst, CallStack cstack, EvalUnit csrc) 143 | { 144 | if (cstack == null) cstack = new CallStack(); 145 | 146 | cstack.Push(new CallStack.Item(fn, inst.Class)); 147 | 148 | int scope = fn.IsInstanceFunc || fn.IsStaticFunc ? inst.Scope : inst.Exec.NewScope(); 149 | int currentUnit = 0; 150 | 151 | Executor exec = inst.Exec; 152 | T returnValue = default(T); 153 | CodeUnit u, prev; 154 | 155 | var units = fn.Units; 156 | int totalUnits = units.Length; 157 | 158 | if (fn.Params.Length > 0) 159 | { 160 | try 161 | { 162 | bool OneRef = fn.RefCount == 1; 163 | 164 | if (fn.RefCount > 1) 165 | cstack.CreateRefs(fn.RefCount); 166 | 167 | 168 | for (currentUnit = 0; currentUnit < fn.Params.Length; currentUnit++) 169 | { 170 | var prm = fn.Params[currentUnit]; 171 | if (prm.ByRef) 172 | { 173 | CallStack.Item.RefParam v; 174 | try { v = new CallStack.Item.RefParam(prm.VarId, fnArgs[currentUnit].GetVarInfo(baseScope, srcInst, cstack, true)); } 175 | catch { throw new InvalidByRefArgumentException($"Failed to execute function '{FormatFuncSign(fn.Signature)}'. Argument for parameter '{prm.ParamName}' cannot be used by reference."); } 176 | if (OneRef) 177 | cstack.CreateRef(v); 178 | else 179 | cstack.AddRef(v); 180 | 181 | continue; 182 | } 183 | u = units[currentUnit]; 184 | 185 | 186 | if (fnArgs == null || currentUnit >= fnArgs.Length || fnArgs[currentUnit].Kind == EvalUnitKind.Empty) 187 | { 188 | if (u.EU0.IsAssignment) 189 | u.EU0.ProcessUnit(scope, inst, cstack); 190 | else 191 | u.EU0.DefineVar(scope, inst.Exec); 192 | 193 | } 194 | } 195 | } 196 | catch (ExecutingForciblyCanceledException) { throw; } 197 | catch (Exception ex) 198 | { 199 | 200 | var lm = ex is InvalidByRefArgumentException ? csrc?.CU?.CodeLocation : fn.LocationMark; 201 | 202 | string st = ErrMsgWithLoc(null, lm, null, inst.Class, fn); 203 | ex.Data[ExceptionVarName.StackTrace] += st; 204 | 205 | 206 | ClearVars(); 207 | cstack.Pop(); throw; 208 | } 209 | 210 | int c = 0; 211 | if (fnArgs != null) 212 | { 213 | if (fn.HasParams) 214 | { 215 | c = fn.ParamsIndex; 216 | if (c > fnArgs.Length) c = fnArgs.Length; 217 | } 218 | else 219 | { 220 | c = fnArgs.Length; 221 | if (c > fn.Params.Length) c = fn.Params.Length; 222 | } 223 | 224 | for (int n = 0; n < c; n++) 225 | { 226 | if (fnArgs[n].Kind == EvalUnitKind.Empty || fn.Params[n].ByRef) continue; 227 | 228 | SetParamVar(fn.Params[n], fnArgs[n], scope, baseScope, srcInst, inst, cstack); 229 | } 230 | 231 | 232 | } 233 | if (fn.HasParams) 234 | { 235 | c = fnArgs.Length - 1; 236 | if (c == fn.ParamsIndex && fnArgs[c].Type.ID == fn.Params[c].ParamType.ID) 237 | SetParamVar(fn.Params[c], fnArgs[c], scope, baseScope, srcInst, inst, cstack); 238 | else 239 | SetParamsArray(fn.Params[fn.ParamsIndex], fnArgs, fn.ParamsIndex, scope, baseScope, srcInst, cstack); 240 | 241 | } 242 | 243 | } 244 | 245 | ForEachEnumeratorAndVarInfo[] ForEachInfo = null; 246 | EvalUnit[] ReapplyEU = null; 247 | 248 | u = currentUnit < units.Length ? units[currentUnit] : null; 249 | prev = null; 250 | while (u != null) 251 | { 252 | 253 | while (u.Forward) u = u.Next; 254 | 255 | #if EXON 256 | try 257 | { 258 | #endif 259 | if (inst.Exec.Canceled && (inst.Exec.ForciblyCanceled || (!u.InCatch && !u.InFinally && !fn.IsDisposeFunc && Thread.CurrentThread.Priority != ThreadPriority.Highest))) throw ExecutingCanceledException.GetCanceledException(inst.Exec.ForciblyCanceled); 260 | switch (u.Type) 261 | { 262 | case UnitType.Expression: 263 | 264 | u.EU0.ProcessUnit(scope, inst, cstack); 265 | u = u.TrueNext; 266 | 267 | break; 268 | 269 | case UnitType.ForEach: 270 | case UnitType.EndForEach: 271 | CodeUnit ForEachUnit; 272 | System.Collections.IEnumerator enumerator = null; 273 | ForEachEnumeratorAndVarInfo fei = default(ForEachEnumeratorAndVarInfo); 274 | 275 | if (u.Type == UnitType.ForEach) 276 | { 277 | ForEachUnit = u; 278 | 279 | System.Collections.IEnumerable en = (System.Collections.IEnumerable)u.EU1.EvalObject(scope, inst, cstack); 280 | 281 | enumerator = en.GetEnumerator(); 282 | if (ForEachInfo == null) ForEachInfo = new ForEachEnumeratorAndVarInfo[fn.ForEachCount]; 283 | ForEachInfo[u.Order] = fei = new ForEachEnumeratorAndVarInfo(enumerator, u.EU0.GetVarInfo(scope, inst, cstack)); 284 | if (u.EU0.Define) 285 | { 286 | 287 | int VScope = fei.VI.Scope; 288 | int VId = fei.VI.ID; 289 | DefineNonArray(exec, u.EU0.Type.ID, VId, VScope); 290 | 291 | } 292 | } 293 | else 294 | { 295 | ForEachUnit = u.TrueNext; 296 | 297 | try 298 | { 299 | fei = ForEachInfo[ForEachUnit.Order]; 300 | enumerator = fei.Enm; 301 | } 302 | catch (NullReferenceException) { throw new ScriptExecutionException("Loop error due to missing foreach enumerator."); } 303 | 304 | } 305 | if (enumerator.MoveNext()) 306 | { 307 | object curVal = enumerator.Current; 308 | int VScope = fei.VI.Scope; 309 | int VId = fei.VI.ID; 310 | switch (ForEachUnit.EU0.Type.ID) 311 | { 312 | case TypeID.Int: { int val = (int)curVal; ScriptVars.Set(exec, VId, VScope, ref val); } break; 313 | case TypeID.Long: { long val = (long)curVal; ScriptVars.Set(exec, VId, VScope, ref val); } break; 314 | case TypeID.Float: { float val = (float)curVal; ScriptVars.Set(exec, VId, VScope, ref val); } break; 315 | case TypeID.Double: { double val = (double)curVal; ScriptVars.Set(exec, VId, VScope, ref val); } break; 316 | case TypeID.Decimal: { decimal val = (decimal)curVal; ScriptVars.Set(exec, VId, VScope, ref val); } break; 317 | case TypeID.Bool: { bool val = (bool)curVal; ScriptVars.Set(exec, VId, VScope, ref val); } break; 318 | case TypeID.String: { string val = (string)curVal; ScriptVars.Set(exec, VId, VScope, ref val); } break; 319 | case TypeID.Char: { char val = (char)curVal; ScriptVars.Set(exec, VId, VScope, ref val); } break; 320 | case TypeID.Byte: { byte val = (byte)curVal; ScriptVars.Set(exec, VId, VScope, ref val); } break; 321 | case TypeID.Short: { short val = (short)curVal; ScriptVars.Set(exec, VId, VScope, ref val); } break; 322 | case TypeID.Date: { DateTime val = (DateTime)curVal; ScriptVars.Set(exec, VId, VScope, ref val); } break; 323 | case TypeID.CustomArray: 324 | case TypeID.Custom: { CustomObject val = (CustomObject)curVal; ScriptVars.Set(exec, VId, VScope, ref val); } break; 325 | case TypeID.Object: 326 | default: 327 | ScriptVars.Set(exec, VId, VScope, ref curVal); 328 | break; 329 | } 330 | u = u.Type == UnitType.ForEach ? u.TrueNext : u.TrueNext.TrueNext; 331 | } 332 | else { u = u.FalseNext; } 333 | 334 | break; 335 | 336 | case UnitType.For: 337 | u.EU0.ProcessUnit(scope, inst, cstack); 338 | if (u.EU1.EvalBool(scope, inst, cstack)) u = u.TrueNext; else u = u.FalseNext; 339 | 340 | break; 341 | 342 | case UnitType.EndFor: 343 | 344 | u.TrueNext.EU2.ProcessUnit(scope, inst, cstack); 345 | if (u.TrueNext.EU1.EvalBool(scope, inst, cstack)) u = u.TrueNext.TrueNext; else u = u.FalseNext; 346 | 347 | break; 348 | case UnitType.If: 349 | case UnitType.ElseIf: 350 | 351 | if (u.EU0.EvalBool(scope, inst, cstack)) u = u.TrueNext; 352 | else u = u.FalseNext; 353 | 354 | break; 355 | 356 | case UnitType.While: 357 | if (u.EU0.EvalBool(scope, inst, cstack)) u = u.TrueNext; 358 | else u = u.FalseNext; 359 | break; 360 | case UnitType.EndDo: 361 | if (u.EU0.EvalBool(scope, inst, cstack)) u = u.TrueNext; 362 | else u = u.FalseNext; 363 | break; 364 | 365 | case UnitType.Switch: 366 | object v = u.EU0.EvalObject(scope, inst, cstack); 367 | 368 | CodeUnit next; 369 | if (u.SwitchDict.TryGetValue(v, out next)) u = next; 370 | else u = u.FalseNext; 371 | break; 372 | 373 | case UnitType.Return: 374 | if (u.EU0 != null) returnValue = u.EU0.Ev(scope, inst, cstack); 375 | 376 | if (u.TrueNext != null) 377 | { 378 | prev = u; 379 | u = u.TrueNext; 380 | } 381 | else 382 | goto endFunc; 383 | 384 | break; 385 | case UnitType.Try: 386 | if (cstack.SetThrownException(null)) 387 | SetExVars(exec, scope, null, null, null, null); 388 | 389 | u = u.Next; 390 | break; 391 | case UnitType.EndTry: 392 | case UnitType.EndCatch: 393 | 394 | if (cstack.SetThrownException(null)) 395 | SetExVars(exec, scope, null, null, null, null); 396 | 397 | u = u.TrueNext; 398 | 399 | break; 400 | case UnitType.Break: 401 | case UnitType.Continue: 402 | case UnitType.GoTo: 403 | prev = u; 404 | u = u.TrueNext; 405 | 406 | break; 407 | 408 | case UnitType.EndFinally: 409 | var tex = cstack.GetThrownException(); 410 | if (tex != null) throw tex; 411 | 412 | if (prev != null) 413 | { 414 | if (prev.Type == UnitType.Return) goto endFunc; 415 | else u = prev.FalseNext; 416 | 417 | prev = null; 418 | } 419 | else 420 | u = u.TrueNext; 421 | 422 | break; 423 | case UnitType.Throw: 424 | if (u.EU0 != null) 425 | throw GetThrowEx(u.EU, scope, inst, cstack, exec); 426 | else 427 | throw cstack.GetThrownException(); 428 | 429 | case UnitType.Apply: 430 | 431 | var applyEU = GetApplyEU(u.EU0); 432 | try { applyEU.ProcessUnit(scope, inst, cstack); } 433 | catch (Exception ex) { throw new ScriptExecutionException($"Error on applying expression '{applyEU.Code}'. " + ex.Message); } 434 | u = u.TrueNext; 435 | break; 436 | case UnitType.Reapply: 437 | if (ReapplyEU == null) ReapplyEU = new EvalUnit[fn.ReapplyCount]; 438 | applyEU = ReapplyEU[u.Order]; 439 | if (applyEU == null) 440 | ReapplyEU[u.Order] = applyEU = GetApplyEU(u.EU0); 441 | 442 | try { applyEU.ProcessUnit(scope, inst, cstack); } 443 | catch (Exception ex) { throw new ScriptExecutionException($"Error on applying expression '{applyEU.Code}'. " + ex.Message); } 444 | 445 | u = u.TrueNext; 446 | break; 447 | default: 448 | 449 | if (u.TrueNext != null) throw new Exception($"UnitType '{u.Type}' not supported."); 450 | u = null; 451 | break; 452 | 453 | } 454 | 455 | #if EXON 456 | } 457 | #endif 458 | 459 | #if DEBUG 460 | try 461 | { 462 | 463 | } 464 | #endif 465 | catch (ExecutingForciblyCanceledException) { throw; } 466 | catch (Exception ex) 467 | { 468 | 469 | if (u.Type != UnitType.EndFinally && cstack.GetThrownException() != ex) 470 | { 471 | 472 | bool dataIsNull = ex.Data[ExceptionVarName.StackTrace] == null; 473 | string st = ErrMsgWithLoc(null, u.CodeLocation, dataIsNull ? RestoreCode(u.Code) : null, inst.Class, fn); 474 | ex.Data[ExceptionVarName.StackTrace] += st; 475 | 476 | } 477 | 478 | string exType = "", exMsg = ""; 479 | object exObj = null; 480 | bool re = false; 481 | doEx: 482 | if (u.Try == null) 483 | { 484 | ClearVars(); 485 | cstack.Pop(); throw; 486 | } 487 | 488 | if (!re) 489 | { 490 | if (cstack.SetThrownException(ex)) 491 | { 492 | GetExInfo(ex, out exType, out exMsg, out exObj); 493 | SetExVars(exec, scope, ex, exType, exMsg, exObj); 494 | } 495 | } 496 | 497 | bool caseNotFound = false; 498 | CodeUnit next; 499 | 500 | findNext: 501 | if (u.InCatch || caseNotFound) 502 | { 503 | if (u.Try.FalseNext == null) 504 | { 505 | u = units.Last(); 506 | re = true; 507 | goto doEx; 508 | } 509 | if (units[u.Try.FalseNext.Index - 1].Type == UnitType.Finally) u = u.Try.FalseNext; 510 | else 511 | { 512 | u = units[u.Try.FalseNext.Index - 1]; 513 | re = true; 514 | goto doEx; 515 | } 516 | } 517 | else if (FindEx(u.Try.CaseList, ex, out next) || (exType != null && u.Try.CaseDict.TryGetValue(exType, out next))) u = next; 518 | else if (u.Try.TrueNext != null) u = u.Try.TrueNext; 519 | else { caseNotFound = true; goto findNext; } 520 | 521 | } 522 | } 523 | 524 | endFunc: 525 | 526 | 527 | ClearVars(); 528 | cstack.Pop(); 529 | 530 | return returnValue; 531 | 532 | void ClearVars() 533 | { 534 | 535 | if (cstack.GetThrownException() != null) SetExVars(exec, scope, null, null, null, null); 536 | 537 | if (fn.ForCleaning != null) inst.Exec.RemVars(scope, fn.ForCleaning); 538 | if (!fn.IsInstanceOrStaticFunc) inst.Exec.FreeScope(scope); 539 | } 540 | EvalUnit GetApplyEU(EvalUnit eu) 541 | { 542 | var varInfo = eu.GetVarInfo(scope, inst, cstack); 543 | object euObj = ScriptVars.Get(exec, varInfo.ID, varInfo.Scope); 544 | if (euObj is Expr result) 545 | return result.EU; 546 | else throw new ScriptExecutionException("Argument is not an expression."); 547 | } 548 | } 549 | 550 | public static Exception GetThrowEx(EvalUnit[] args, int scope, ClassInstance inst, CallStack cstack, Executor exec) 551 | { 552 | Exception ex; 553 | object exVal = args[0].Ev(scope, inst, cstack); 554 | if (exVal is Exception exc) 555 | return exc; 556 | else if (exVal is CustomObject custExObj) 557 | { 558 | 559 | string name = "", msg = ""; 560 | GetExVarsFromCustomEx(exec, out name, out msg, ((ClassInstance)custExObj).Scope); 561 | 562 | ex = new CustomThrownException(); 563 | ex.Data[ExceptionVarName.TypeName] = name; 564 | ex.Data[ExceptionVarName.Message] = msg; 565 | ex.Data[ExceptionVarName.CustomExObj] = custExObj; 566 | return ex; 567 | 568 | } 569 | else if (exVal is string exStr) 570 | { 571 | ex = new CustomThrownException(); 572 | ex.Data[ExceptionVarName.TypeName] = exStr; 573 | ex.Data[ExceptionVarName.Message] = args.Length > 1 ? args[1].EvalString(scope, inst, cstack) : null; 574 | return ex; 575 | } 576 | else 577 | throw new ScriptExecutionException($"Invalid throw argument of type '{exVal.GetType()}'."); 578 | 579 | } 580 | static bool FindEx(List> caseList, Exception ex, out CodeUnit cu) 581 | { 582 | object exObj = ex is CustomThrownException && ex.Data.Contains(ExceptionVarName.CustomExObj) ? ex.Data[ExceptionVarName.CustomExObj] : ex; 583 | 584 | for (int i = 0; i < caseList.Count; i++) 585 | { 586 | var key = caseList[i].Key; 587 | var value = caseList[i].Value; 588 | 589 | bool ok = false; 590 | if (key is Type type) ok = type.IsInstanceOfType(ex); 591 | else if (key is CustomType ct) ok = ct.IsOfType(exObj); 592 | 593 | 594 | if (ok) 595 | { 596 | cu = value; 597 | return true; 598 | } 599 | 600 | } 601 | 602 | cu = null; 603 | return false; 604 | 605 | } 606 | 607 | public static void SetExVars(Executor exec, int scope, object ex, string exType, string exMsg, object exObj) 608 | { 609 | 610 | Script script = exec.ExecutedScript; 611 | ScriptVars.AddSet(exec, script.ExVarID.TypeName, scope, ref exType); 612 | ScriptVars.AddSet(exec, script.ExVarID.Message, scope, ref exMsg); 613 | 614 | ScriptVars.AddSet(exec, script.ExVarID.Object, scope, ref ex); 615 | ScriptVars.AddSet(exec, script.ExVarID.CustomExObj, scope, ref exObj); 616 | 617 | } 618 | static void GetExVarsFromCustomEx(Executor exec, out string exType, out string exMsg, int scope) 619 | { 620 | Script script = exec.ExecutedScript; 621 | 622 | exType = ScriptVars.Get(exec, script.ExVarID.NameVarInCustomExClass, scope); 623 | exMsg = ScriptVars.Get(exec, script.ExVarID.MessageVarInCustomExClass, scope); 624 | 625 | } 626 | public static void GetExInfo(Exception ex, out string exType, out string exMsg, out object exObj) 627 | { 628 | exType = ex.GetType().Name; 629 | exMsg = ex.Message; 630 | exObj = null; 631 | if (ex.Data.Contains(ExceptionVarName.TypeName)) exType = (string)ex.Data[ExceptionVarName.TypeName]; 632 | if (ex.Data.Contains(ExceptionVarName.Message)) exMsg = (string)ex.Data[ExceptionVarName.Message]; 633 | if (ex.Data.Contains(ExceptionVarName.CustomExObj)) exObj = ex.Data[ExceptionVarName.CustomExObj]; 634 | 635 | } 636 | 637 | public static CustomObject NewClassInstance(EvalUnit eu, int scope, ClassInstance inst, bool ignoreConstructor = false) 638 | { 639 | ClassInstance result = new ClassInstance(inst.Exec, eu.Type.CType.Class, eu.Nested[0].Args, inst, scope, false, ignoreConstructor, eu.Func); 640 | 641 | return new CustomObject(inst.Exec, result, eu.Type.CType.Class); 642 | } 643 | 644 | public static CustomObject NewClassInstance(ScriptClass sc, ArgBlocks[] nested, int scope, ClassInstance inst, bool ignoreConstructor = false) 645 | { 646 | ClassInstance result = new ClassInstance(inst.Exec, sc, nested[0].Args, inst, scope, false, ignoreConstructor, null); 647 | return new CustomObject(inst.Exec, result, sc); 648 | } 649 | 650 | private static object NewArray(TypeID arrType, ScriptClass custom, ArgBlocks[] nested, int scope, ClassInstance inst, CallStack cstack) 651 | { 652 | bool withInit = nested.Length > 1; 653 | int length = withInit ? nested[1].Args.Length : nested[0].Args[0].EvalInt(scope, inst, cstack); 654 | 655 | Type t = custom == null ? GetTypeByID(arrType).GetElementType() : TypeOfCustom; 656 | 657 | Array arr = Array.CreateInstance(t, length); 658 | if (withInit) 659 | InitArrayWithValues(arr, arrType, nested[1].Args, scope, inst, cstack); 660 | 661 | return custom != null ? new CustomObject(inst.Exec, arr, custom, true) : arr; 662 | } 663 | 664 | private static void InitArrayWithValues(Array arr, TypeID typeId, EvalUnit[] args, int scope, ClassInstance inst, CallStack cstack) 665 | { 666 | switch (typeId) 667 | { 668 | case TypeID.IntArray: FillWithArgs((int[])arr, args, scope, inst, cstack); break; 669 | case TypeID.StringArray: FillWithArgs((string[])arr, args, scope, inst, cstack); ; break; 670 | case TypeID.CharArray: FillWithArgs((char[])arr, args, scope, inst, cstack); break; 671 | case TypeID.LongArray: FillWithArgs((long[])arr, args, scope, inst, cstack); break; 672 | case TypeID.DoubleArray: FillWithArgs((double[])arr, args, scope, inst, cstack); break; 673 | case TypeID.FloatArray: FillWithArgs((float[])arr, args, scope, inst, cstack); break; 674 | case TypeID.ShortArray: FillWithArgs((short[])arr, args, scope, inst, cstack); break; 675 | case TypeID.ByteArray: FillWithArgs((byte[])arr, args, scope, inst, cstack); break; 676 | case TypeID.BoolArray: FillWithArgs((bool[])arr, args, scope, inst, cstack); break; 677 | case TypeID.DecimalArray: FillWithArgs((decimal[])arr, args, scope, inst, cstack); break; 678 | case TypeID.DateArray: FillWithArgs((DateTime[])arr, args, scope, inst, cstack); break; 679 | case TypeID.CustomArray: FillWithArgs((CustomObject[])arr, args, scope, inst, cstack); break; 680 | case TypeID.ObjectArray: FillWithArgs((object[])arr, args, scope, inst, cstack); break; 681 | default: throw new ScriptExecutionException($"Bad array type '{GetTypeName(typeId)}'."); 682 | } 683 | 684 | } 685 | 686 | private static void FillWithArgs(T[] arr, EvalUnit[] args, int scope, ClassInstance inst, CallStack cstack) 687 | { 688 | for (int i = 0; i < args.Length; i++) 689 | { 690 | T v = args[i].Ev(scope, inst, cstack); 691 | arr[i] = v; 692 | } 693 | } 694 | 695 | private static object NewBasic(TypeID type, ArgBlocks[] nested, int scope, ClassInstance inst, CallStack cstack, int length = -1) 696 | { 697 | 698 | if (nested[0].Args == null) return Activator.CreateInstance(GetTypeByID(type)); 699 | 700 | object[] prms = null; 701 | prms = new object[nested[0].Args.Length]; 702 | for (int i = 0; i < prms.Length; i++) 703 | prms[i] = nested[0].Args[i].EvalObject(scope, inst, cstack); 704 | 705 | return Activator.CreateInstance(GetTypeByID(type), prms); 706 | } 707 | public static object NewObj(EvalUnit eu, int scope, ClassInstance inst, CallStack cstack) 708 | { 709 | if (eu.Type.ID == TypeID.Custom) 710 | return NewClassInstance(eu, scope, inst); 711 | 712 | if (!TypeIsArray(eu.Type.ID)) 713 | return NewBasic(eu.Type.ID, eu.Nested, scope, inst, cstack); 714 | else 715 | return NewArray(eu.Type.ID, eu.Type.CType?.Class, eu.Nested, scope, inst, cstack); 716 | 717 | } 718 | 719 | private static void DefineNonArray(Executor exec, TypeID typeId, int varId, int scope) 720 | { 721 | switch (typeId) 722 | { 723 | case TypeID.Int: ScriptVars.Add(exec, varId, scope); break; 724 | case TypeID.Long: ScriptVars.Add(exec, varId, scope); break; 725 | case TypeID.Float: ScriptVars.Add(exec, varId, scope); break; 726 | case TypeID.Double: ScriptVars.Add(exec, varId, scope); break; 727 | case TypeID.Decimal: ScriptVars.Add(exec, varId, scope); break; 728 | case TypeID.Bool: ScriptVars.Add(exec, varId, scope); break; 729 | case TypeID.String: ScriptVars.Add(exec, varId, scope); break; 730 | case TypeID.Char: ScriptVars.Add(exec, varId, scope); break; 731 | case TypeID.Byte: ScriptVars.Add(exec, varId, scope); break; 732 | case TypeID.Short: ScriptVars.Add(exec, varId, scope); break; 733 | case TypeID.Date: ScriptVars.Add(exec, varId, scope); break; 734 | case TypeID.Custom: ScriptVars.Add(exec, varId, scope); break; 735 | case TypeID.Object: ScriptVars.Add(exec, varId, scope); break; 736 | default: throw new ScriptExecutionException("Non-array variable required."); 737 | } 738 | } 739 | 740 | public static object DynFuncCall(ClassInstance inst, string fnName, object[] args) 741 | { 742 | 743 | EvalUnit[] svArgs = null; 744 | int c = args == null ? 0 : args.Length; 745 | if (c > 0) 746 | { 747 | svArgs = new EvalUnit[args.Length]; 748 | for (int i = 0; i < c; i++) 749 | { 750 | var v = args[i]; 751 | var t = GetTypeID(v.GetType(), true); 752 | svArgs[i] = t == TypeID.None ? EvalUnit.GetEUWithSpecificValue(v) : EvalUnit.GetEUWithSpecificValue(v, GetVarTypeByID(t)); 753 | } 754 | } 755 | var fn = inst.Class.GetFunc(fnName, svArgs, true, null, inst.IsStatic ? true : null); 756 | if (fn == null) 757 | throw new ArgumentException($"{(inst.IsStatic ? "Static f" : "F")}unction '{FormatFuncSign(GetFuncSign(fnName, svArgs))}' not found at '{inst.Class.ClassFullName}'."); 758 | 759 | return ExecuteFunction(fn, svArgs, -1, null, inst, null, null); 760 | 761 | } 762 | 763 | public struct VarInfo 764 | { 765 | public ClassInstance Inst; 766 | 767 | public int Scope; 768 | public int ID; 769 | public VarInfo(int scope, ClassInstance inst, int id) 770 | { 771 | Inst = inst; 772 | 773 | Scope = scope; 774 | ID = id; 775 | } 776 | } 777 | 778 | } 779 | 780 | } -------------------------------------------------------------------------------- /OverScript/ScriptClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using static OverScript.Literals; 6 | 7 | namespace OverScript 8 | { 9 | 10 | public partial class ScriptClass 11 | { 12 | 13 | 14 | public enum EvalUnitKind : byte { Empty = 0, Expression, Variable, Literal, ArrayItem, Function, New, This, SpecificValue, BasicType, CustomType, Assignment } 15 | 16 | public struct ArgBlocks 17 | { 18 | public EvalUnit[] Args; 19 | 20 | } 21 | public delegate T FuncToCall(EvalUnit[] fnArgs, int baseScope, ClassInstance srcInst, ClassInstance inst, CallStack cstack, EvalUnit csrc); 22 | 23 | public const string InstanceFuncName = "Instance"; 24 | public const string StaticFuncName = "Static"; 25 | public const string CtorFuncName = "New"; 26 | public const string FinalizeFuncName = "Finalize"; 27 | public const string ToStringFuncName = "ToString"; 28 | public const string ArrayToStringFuncName = "ArrayToString"; 29 | public const string GetHashCodeFuncName = "GetHashCode"; 30 | public const string EqualsFuncName = "Equals"; 31 | public const string CompareToFuncName = "CompareTo"; 32 | public const string DisposeFuncName = "Dispose"; 33 | public const string GetEnumeratorFuncName = "GetEnumerator"; 34 | public const string EnumeratorMoveNextFuncName = "MoveNext"; 35 | public const string EnumeratorCurrentFuncName = "Current"; 36 | public const string EnumeratorResetFuncName = "Reset"; 37 | 38 | public const char BasicFunctionPrefix = '@'; 39 | public const char FunctionLayerPrefix = '$'; 40 | public const string OrLocalFunctionPrefix = "@@"; 41 | public const string OrBasicFunctionPrefix = "@@@"; 42 | public const string ClassSign = "class"; 43 | public const string NullStr = "null"; 44 | public const string TrueStr = "true"; 45 | public const string FalseStr = "false"; 46 | public const string ThisStr = "this"; 47 | public const char RedefinePrefix = '$'; 48 | public const string OperatorFunctionPrefix = "op_"; 49 | public const char AtRuntimeTypeMethodPrefix = '@'; 50 | public class CodeUnit 51 | { 52 | public EvalUnit EU0, EU1, EU2; 53 | public EvalUnit[] EU; 54 | public string[] Str; 55 | public int Nesting; 56 | public UnitType Type; 57 | public string Code; 58 | 59 | public bool InCatch, InFinally; 60 | public Dictionary, CodeUnit> SwitchDict; 61 | public Dictionary CaseDict; 62 | public List> CaseList; 63 | 64 | public CodeUnit Next, TrueNext, FalseNext, Try, Finally; 65 | public int Index; 66 | public LocMark CodeLocation; 67 | public ScriptFunction Fn; 68 | public int Order = -1; 69 | 70 | public bool Forward; 71 | public override string ToString() => RestoreCode(Code); 72 | 73 | } 74 | public enum VarScopeKind : byte 75 | { 76 | None = 0, 77 | Local = 1, 78 | This = 2, 79 | Inst = 3, 80 | Static = 4, 81 | Ref = 5, 82 | Involved = 6 83 | } 84 | 85 | public enum UnitType : ushort 86 | { 87 | Expression = 0, 88 | If = 1, 89 | For = 2, 90 | While = 3, 91 | ElseIf = 4, 92 | Switch = 5, 93 | Case = 6, 94 | Do = 7, 95 | Else = 8, 96 | Label = 9, 97 | EndIfTrueBlock = 12, 98 | EndIf = 11, 99 | EndFor = 21, 100 | EndWhile = 31, 101 | EndDo = 41, 102 | EndSwitch = 51, 103 | Break = 90, 104 | Continue = 91, 105 | DefaultCase = 92, 106 | GoTo = 93, 107 | Return = 94, 108 | Throw = 95, 109 | Apply = 96, 110 | Reapply = 97, 111 | 112 | Try = 100, 113 | EndTry = 1001, 114 | Catch = 101, 115 | EndCatch = 1011, 116 | Finally = 102, 117 | EndFinally = 1021, 118 | CatchByName = 103, 119 | EndCatchByName = 1031, 120 | ForEach = 200, 121 | EndForEach = 2001 122 | } 123 | static class UnitTypes 124 | { 125 | public static UnitType[] EndFor = new UnitType[] { UnitType.EndFor }; 126 | public static UnitType[] For = new UnitType[] { UnitType.For }; 127 | public static UnitType[] EndForEach = new UnitType[] { UnitType.EndForEach }; 128 | public static UnitType[] ForEach = new UnitType[] { UnitType.ForEach }; 129 | public static UnitType[] ElseOrEndif = new UnitType[] { UnitType.ElseIf, UnitType.Else, UnitType.EndIf }; 130 | public static UnitType[] Endif = new UnitType[] { UnitType.EndIf }; 131 | public static UnitType[] EndWhile = new UnitType[] { UnitType.EndWhile }; 132 | public static UnitType[] While = new UnitType[] { UnitType.While }; 133 | public static UnitType[] Do = new UnitType[] { UnitType.Do }; 134 | public static UnitType[] EndLoopOrEndSwitch = new UnitType[] { UnitType.EndDo, UnitType.EndWhile, UnitType.EndFor, UnitType.EndSwitch }; 135 | public static UnitType[] EndLoop = new UnitType[] { UnitType.EndDo, UnitType.EndWhile, UnitType.EndFor }; 136 | public static UnitType[] Case = new UnitType[] { UnitType.Case }; 137 | public static UnitType[] EndSwitch = new UnitType[] { UnitType.EndSwitch }; 138 | public static UnitType[] DefaultCase = new UnitType[] { UnitType.DefaultCase }; 139 | public static UnitType[] Switch = new UnitType[] { UnitType.Switch }; 140 | public static UnitType[] Label = new UnitType[] { UnitType.Label }; 141 | public static UnitType[] EndFinally = new UnitType[] { UnitType.EndFinally }; 142 | public static UnitType[] EndCatch = new UnitType[] { UnitType.EndCatch, UnitType.EndCatchByName }; 143 | public static UnitType[] Finally = new UnitType[] { UnitType.Finally }; 144 | public static UnitType[] Catch = new UnitType[] { UnitType.Catch }; 145 | public static UnitType[] CatchByName = new UnitType[] { UnitType.CatchByName }; 146 | public static UnitType[] Try = new UnitType[] { UnitType.Try }; 147 | public static UnitType[] TryCatchFinally = new UnitType[] { UnitType.Try, UnitType.Catch, UnitType.Finally }; 148 | public static UnitType[] EndTryEndCatchEndFinally = new UnitType[] { UnitType.EndTry, UnitType.EndCatch, UnitType.EndCatchByName, UnitType.EndFinally }; 149 | } 150 | public enum OperationKind : byte 151 | { 152 | None = 0, 153 | ForcedAssignment = 1, 154 | Assignment = 2, 155 | AdditionAssignment = 3, 156 | SubtractionAssignment = 4, 157 | MultiplyAssignment = 5, 158 | DivisionAssignment = 6, 159 | ModulusAssignment = 7, 160 | BitwiseAndAssignment = 8, 161 | ExclusiveOrAssignment = 9, 162 | BitwiseOrAssignment = 10, 163 | 164 | AdditionForcedAssignment = 11, 165 | SubtractionForcedAssignment = 12, 166 | MultiplyForcedAssignment = 13, 167 | DivisionForcedAssignment = 14, 168 | ModulusForcedAssignment = 15, 169 | BitwiseAndForcedAssignment = 16, 170 | ExclusiveOrForcedAssignment = 17, 171 | BitwiseOrForcedAssignment = 18, 172 | 173 | LogicalNot = 20, 174 | OnesComplement = 21, 175 | Casting = 22, 176 | 177 | Increment = 23, 178 | Decrement = 24, 179 | Addition = 25, 180 | Subtraction = 26, 181 | Multiply = 27, 182 | Division = 28, 183 | Modulus = 29, 184 | 185 | LeftShift = 31, 186 | RightShift = 32, 187 | 188 | BitwiseAnd = 34, 189 | ExclusiveOr = 35, 190 | BitwiseOr = 36, 191 | 192 | LessThan = 41, 193 | LessThanOrEqual = 42, 194 | GreaterThan = 43, 195 | GreaterThanOrEqual = 44, 196 | Equality = 45, 197 | Inequality = 46, 198 | 199 | LogicalAnd = 48, 200 | LogicalOr = 49, 201 | 202 | Coalescing = 50, 203 | 204 | } 205 | static bool OpIsAssignment(OperationKind opKind) 206 | { 207 | return opKind >= OperationKind.ForcedAssignment && opKind <= OperationKind.BitwiseOrForcedAssignment; 208 | } 209 | public enum OperandPosition : byte 210 | { 211 | None = 0, 212 | Left = 1, 213 | Right = 2 214 | } 215 | public static bool OpIsAnyAssign(OperationKind opKind) => (opKind > 0 && opKind <= OperationKind.BitwiseOrForcedAssignment) || opKind == OperationKind.Increment || opKind == OperationKind.Decrement; 216 | 217 | public static DateTime NotNullDate = default(DateTime).AddSeconds(1); 218 | 219 | public static Type TypeOfBool = typeof(bool); 220 | public static Type TypeOfByte = typeof(byte); 221 | public static Type TypeOfShort = typeof(short); 222 | public static Type TypeOfInt = typeof(int); 223 | public static Type TypeOfLong = typeof(long); 224 | public static Type TypeOfFloat = typeof(float); 225 | public static Type TypeOfDouble = typeof(double); 226 | public static Type TypeOfDecimal = typeof(decimal); 227 | public static Type TypeOfString = typeof(string); 228 | public static Type TypeOfChar = typeof(char); 229 | public static Type TypeOfObject = typeof(object); 230 | public static Type TypeOfDate = typeof(DateTime); 231 | public static Type TypeOfCustom = typeof(CustomObject); 232 | 233 | public static Type TypeOfBoolArray = typeof(bool[]); 234 | public static Type TypeOfByteArray = typeof(byte[]); 235 | public static Type TypeOfShortArray = typeof(short[]); 236 | public static Type TypeOfIntArray = typeof(int[]); 237 | public static Type TypeOfLongArray = typeof(long[]); 238 | public static Type TypeOfFloatArray = typeof(float[]); 239 | public static Type TypeOfDoubleArray = typeof(double[]); 240 | public static Type TypeOfDecimalArray = typeof(decimal[]); 241 | public static Type TypeOfStringArray = typeof(string[]); 242 | public static Type TypeOfCharArray = typeof(char[]); 243 | public static Type TypeOfObjectArray = typeof(object[]); 244 | public static Type TypeOfDateArray = typeof(DateTime[]); 245 | public static Type TypeOfCustomArray = typeof(CustomObject); 246 | public static Type TypeOfVoid = typeof(void); 247 | 248 | public static class TypeStr 249 | { 250 | public const string Int = "int"; 251 | public const string Long = "long"; 252 | public const string String = "string"; 253 | public const string Char = "char"; 254 | public const string Float = "float"; 255 | public const string Double = "double"; 256 | public const string Decimal = "decimal"; 257 | public const string Bool = "bool"; 258 | public const string Object = "object"; 259 | public const string Short = "short"; 260 | public const string Byte = "byte"; 261 | public const string DateTime = "date"; 262 | public const string Void = "void"; 263 | 264 | public const string IntArray = "int[]"; 265 | public const string LongArray = "long[]"; 266 | public const string StringArray = "string[]"; 267 | public const string CharArray = "char[]"; 268 | public const string FloatArray = "float[]"; 269 | public const string DoubleArray = "double[]"; 270 | public const string DecimalArray = "decimal[]"; 271 | public const string BoolArray = "bool[]"; 272 | public const string ObjectArray = "object[]"; 273 | public const string ShortArray = "short[]"; 274 | public const string ByteArray = "byte[]"; 275 | public const string DateTimeArray = "date[]"; 276 | 277 | } 278 | public static Dictionary TypeIds = new Dictionary() 279 | { 280 | { TypeOfBool,TypeID.Bool}, 281 | { TypeOfByte,TypeID.Byte}, 282 | { TypeOfShort,TypeID.Short}, 283 | { TypeOfInt,TypeID.Int}, 284 | { TypeOfLong,TypeID.Long}, 285 | { TypeOfFloat,TypeID.Float}, 286 | { TypeOfDouble,TypeID.Double}, 287 | { TypeOfDecimal,TypeID.Decimal}, 288 | { TypeOfString,TypeID.String}, 289 | { TypeOfChar,TypeID.Char}, 290 | { TypeOfObject,TypeID.Object}, 291 | { TypeOfDate,TypeID.Date}, 292 | { TypeOfCustom,TypeID.Custom}, 293 | 294 | { TypeOfBoolArray,TypeID.BoolArray}, 295 | { TypeOfByteArray,TypeID.ByteArray}, 296 | { TypeOfShortArray,TypeID.ShortArray}, 297 | { TypeOfIntArray,TypeID.IntArray}, 298 | { TypeOfLongArray,TypeID.LongArray}, 299 | { TypeOfFloatArray,TypeID.FloatArray}, 300 | { TypeOfDoubleArray,TypeID.DoubleArray}, 301 | { TypeOfDecimalArray,TypeID.DecimalArray}, 302 | { TypeOfStringArray,TypeID.StringArray}, 303 | { TypeOfCharArray,TypeID.CharArray}, 304 | { TypeOfObjectArray,TypeID.ObjectArray}, 305 | { TypeOfDateArray,TypeID.DateArray}, 306 | { typeof(CustomObject[]),TypeID.CustomArray}, 307 | 308 | { TypeOfVoid,TypeID.Void} 309 | 310 | }; 311 | 312 | [Flags] 313 | public enum TypeFlag 314 | { 315 | None = 0, 316 | Bool = 1 << 0, 317 | Byte = 1 << 1, 318 | Short = 1 << 2, 319 | Int = 1 << 3, 320 | Long = 1 << 4, 321 | Float = 1 << 5, 322 | Double = 1 << 6, 323 | Decimal = 1 << 7, 324 | String = 1 << 8, 325 | Char = 1 << 9, 326 | Object = 1 << 10, 327 | Date = 1 << 11, 328 | Custom = 1 << 12, 329 | 330 | BoolArray = 1 << 13, 331 | ByteArray = 1 << 14, 332 | ShortArray = 1 << 15, 333 | IntArray = 1 << 16, 334 | LongArray = 1 << 17, 335 | FloatArray = 1 << 18, 336 | DoubleArray = 1 << 19, 337 | DecimalArray = 1 << 20, 338 | StringArray = 1 << 21, 339 | CharArray = 1 << 22, 340 | ObjectArray = 1 << 23, 341 | DateArray = 1 << 24, 342 | CustomArray = 1 << 25 343 | } 344 | 345 | public static Type[] Types = ((TypeID[])Enum.GetValues(typeof(TypeID))).Select(x => TypeIds.FirstOrDefault(y => y.Value == x).Key).ToArray(); 346 | 347 | 348 | public static string GetTypeName(TypeID type) => type == TypeID.Custom || type == TypeID.CustomArray ? type.ToString() : BasicTypes.Where(x => x.Value.ID == type).FirstOrDefault().Key; 349 | 350 | 351 | 352 | public static VarType GetVarTypeByID(TypeID type) => type >= TypeID.Type || type == TypeID.Custom || type == TypeID.CustomArray ? new VarType(type) : BasicTypes.Where(x => x.Value.ID == type).FirstOrDefault().Value; 353 | 354 | public static Dictionary BasicTypes = new Dictionary() { 355 | { TypeStr.Int,new VarType( TypeID.Int) }, 356 | { TypeStr.Long, new VarType( TypeID.Long) }, 357 | { TypeStr.String, new VarType( TypeID.String) }, 358 | { TypeStr.Char,new VarType( TypeID.Char) }, 359 | { TypeStr.Float, new VarType(TypeID.Float) }, 360 | { TypeStr.Double, new VarType(TypeID.Double) }, 361 | { TypeStr.Decimal, new VarType( TypeID.Decimal)}, 362 | { TypeStr.Bool, new VarType(TypeID.Bool)}, 363 | { TypeStr.Object, new VarType(TypeID.Object)}, 364 | { TypeStr.Short, new VarType(TypeID.Short)}, 365 | { TypeStr.Byte, new VarType( TypeID.Byte)}, 366 | { TypeStr.DateTime, new VarType( TypeID.Date)}, 367 | 368 | { TypeStr.IntArray, new VarType( TypeID.IntArray)}, 369 | { TypeStr.ObjectArray, new VarType(TypeID.ObjectArray)}, 370 | { TypeStr.StringArray, new VarType( TypeID.StringArray)}, 371 | { TypeStr.CharArray, new VarType(TypeID.CharArray )}, 372 | { TypeStr.LongArray, new VarType(TypeID.LongArray)}, 373 | { TypeStr.DoubleArray, new VarType( TypeID.DoubleArray)}, 374 | { TypeStr.FloatArray, new VarType( TypeID.FloatArray)}, 375 | { TypeStr.DecimalArray, new VarType( TypeID.DecimalArray)}, 376 | { TypeStr.BoolArray, new VarType( TypeID.BoolArray)}, 377 | { TypeStr.ShortArray,new VarType( TypeID.ShortArray )}, 378 | { TypeStr.ByteArray, new VarType( TypeID.ByteArray )}, 379 | { TypeStr.DateTimeArray, new VarType( TypeID.DateArray)}, 380 | { TypeStr.Void, new VarType( TypeID.Void)}, 381 | 382 | }; 383 | 384 | public static string[] BasicTypeNames = BasicTypes.Keys.ToArray(); 385 | public static bool IsBasic(string str) => Array.IndexOf(BasicTypeNames, str) >= 0; 386 | 387 | public bool IsCustom(string str, out object obj) 388 | { 389 | bool isArray = false; 390 | if (str.EndsWith("[]")) { str = str.Remove(str.Length - 2, 2); isArray = true; } 391 | var c = GetClassByName(str, this); 392 | if (c != null) obj = CustomType.Get(c, isArray); else obj = null; 393 | return obj != null; 394 | 395 | } 396 | 397 | public static TypeID[] TypesByPriority = { TypeID.Object, TypeID.Bool, TypeID.Byte , TypeID.Short , TypeID.Char , TypeID.Int, TypeID.Long, 398 | TypeID.Float,TypeID.Double,TypeID.Decimal, TypeID.Date, TypeID.String, TypeID.Custom}; 399 | 400 | public static TypeID GetMaxType(TypeID t1, TypeID t2) => Array.IndexOf(TypesByPriority, t1) > Array.IndexOf(TypesByPriority, t2) ? t1 : t2; 401 | 402 | 403 | static char[] Brackets = { '[', '(', '{' }; 404 | 405 | static bool TypeIdIsType(TypeID id) => id >= TypeID.BoolType; 406 | public static bool TypeIsArray(TypeID t) => t >= TypeID.BoolArray; 407 | public static Type GetTypeByID(TypeID id) => Types[(byte)id]; 408 | public static TypeID GetTypeID(Type type, bool noException = false) 409 | { 410 | TypeID result; 411 | bool exists = TypeIds.TryGetValue(type, out result); 412 | if (!exists && !noException) 413 | throw new ScriptExecutionException($"Type '{type}' not found."); 414 | 415 | return result; 416 | } 417 | public static TypeID SubTypeIdToTypeId(TypeID tid) 418 | { 419 | string type = tid.ToString(); 420 | type = type.Remove(type.Length - 4); 421 | var en = Enum.Parse(typeof(TypeID), type); 422 | return (TypeID)en; 423 | } 424 | public static TypeID GetArrayTypeId(TypeID tid) 425 | { 426 | string type = tid.ToString() + "Array"; 427 | 428 | var en = Enum.Parse(typeof(TypeID), type); 429 | return (TypeID)en; 430 | } 431 | public static TypeID GetElementType(TypeID type) => GetTypeID(GetTypeByID(type).GetElementType()); 432 | public static bool TypeIsNumeric(TypeID type) => type == TypeID.Byte || type == TypeID.Short || type == TypeID.Int || type == TypeID.Long || type == TypeID.Decimal || type == TypeID.Float || type == TypeID.Double; 433 | 434 | public VarType GetElementVarType(VarType vt) 435 | { 436 | if (vt.ID == TypeID.CustomArray) 437 | vt = CurScript.AllTypes[vt.CType.Class.ClassFullName]; 438 | else 439 | { 440 | var hint = vt.TypeHint; 441 | vt = GetVarTypeByID(GetElementType(vt.ID)); 442 | 443 | if (hint != null) 444 | vt.AddHint(hint.GetElementType()); 445 | } 446 | return vt; 447 | } 448 | 449 | public static Dictionary TypeOrder = TypeIds.ToDictionary(x => x.Value, x => Array.IndexOf(TypesByPriority, x.Value)); 450 | 451 | 452 | 453 | public static string[] ExceptionVarNames = { ExceptionVarName.TypeName, ExceptionVarName.Message, ExceptionVarName.CustomExObj, ExceptionVarName.Object }; 454 | public class ExceptionVarID 455 | { 456 | public int TypeName; 457 | public int Message; 458 | public int CustomExObj; 459 | public int Object; 460 | 461 | 462 | public int NameVarInCustomExClass; 463 | public int MessageVarInCustomExClass; 464 | 465 | public ExceptionVarID(Script script) 466 | { 467 | TypeName = script.GetVarID(ExceptionVarName.TypeName); 468 | Message = script.GetVarID(ExceptionVarName.Message); 469 | CustomExObj = script.GetVarID(ExceptionVarName.CustomExObj); 470 | Object = script.GetVarID(ExceptionVarName.Object); 471 | 472 | 473 | NameVarInCustomExClass = script.GetVarID("Name"); 474 | MessageVarInCustomExClass = script.GetVarID("Message"); 475 | } 476 | } 477 | public class Enumerator : IEnumerator, IDisposable 478 | { 479 | Func MoveNextFn; 480 | Func CurrentFn; 481 | Action ResetFn, DisposeFn; 482 | 483 | public Enumerator(Func moveNextFn, Func currentFn, Action resetFn, Action disposeFn) 484 | { 485 | MoveNextFn = moveNextFn; 486 | CurrentFn = currentFn; 487 | ResetFn = resetFn; 488 | DisposeFn = disposeFn; 489 | } 490 | 491 | public object Current => CurrentFn(); 492 | public bool MoveNext() => MoveNextFn(); 493 | public void Reset() => ResetFn(); 494 | public void Dispose() 495 | { 496 | if (DisposeFn != null) DisposeFn(); 497 | } 498 | } 499 | 500 | 501 | 502 | 503 | private HashSet FuncNames, PublicNonStaticFuncNames, PublicStaticFuncNames; 504 | public List SubClasses = new List(); 505 | private List Functions = new List(); 506 | public string ClassName; 507 | public string ClassFullName; 508 | public ScriptClass OuterClass; 509 | 510 | 511 | public int ID; 512 | public FuncToCall[] InstanceFuncs; 513 | public FuncToCall CtorFunc; 514 | public FuncToCall ToStringFunc; 515 | public FuncToCall ArrayToStringFunc; 516 | public FuncToCall GetHashCodeFunc; 517 | public FuncToCall EqualsFunc; 518 | public FuncToCall CompareToFunc; 519 | public FuncToCall DisposeFunc; 520 | public FuncToCall GetEnumeratorFunc; 521 | public FuncToCall EnumeratorMoveNextFunc; 522 | public FuncToCall EnumeratorCurrentFunc; 523 | public FuncToCall EnumeratorResetFunc; 524 | 525 | public FuncToCall[] StaticFuncs; 526 | public FuncToCall[] FinalizeFuncs; 527 | public Dictionary FnLayers = new Dictionary(); 528 | public List InstVars = new List(); 529 | public List StatVars = new List(); 530 | 531 | public VarsToClear ForCleaning; 532 | public List InheritedClasses = new List(); 533 | 534 | public List PublicVars = new List(); 535 | public bool HasCtors; 536 | public bool IsPublic, IsStatic, IsSealed; 537 | public string Code; 538 | public static ScriptClass RootClass; 539 | public List<(string code, ScriptClass sc, bool? staticFunc)> FnCodes = new List<(string code, ScriptClass sc, bool? staticFunc)>(); 540 | 541 | public Script CurScript; 542 | 543 | 544 | public ScriptClass(string code, string name, ScriptClass outerClass, Script script) 545 | { 546 | bool isRoot = outerClass == null; 547 | OuterClass = outerClass; 548 | ID = script.ClassCount++; 549 | CurScript = script; 550 | 551 | #if EXON 552 | try 553 | { 554 | #endif 555 | 556 | if (isRoot) 557 | RootClass = this; 558 | 559 | string inhs = null; 560 | int i = name.IndexOf(':'); 561 | if (i > 0) 562 | { 563 | inhs = name.Substring(i + 2); 564 | name = name.Substring(0, i); 565 | 566 | } 567 | 568 | if (!CheckCharsInVarName(name)) 569 | throw new ScriptLoadingException($"Invalid class name '{name}'. A valid name starts with a letter or underscore, followed by any number of letters, numbers, or underscores."); 570 | 571 | ClassName = name; 572 | 573 | 574 | ClassFullName = GetFullName(); 575 | 576 | 577 | if (inhs != null) AddInhs(inhs); 578 | 579 | if (isRoot) 580 | { 581 | PrepareCode(ref code, CurScript); 582 | code = UnitSeparator + code; 583 | 584 | } 585 | 586 | GetSubClasses(ref code); 587 | Code = code; 588 | 589 | 590 | #if EXON 591 | } 592 | 593 | 594 | 595 | 596 | catch (Exception ex) 597 | { 598 | 599 | 600 | throw new ScriptLoadingException($"Class '{name}' loading error. " + ex.Message); 601 | } 602 | #endif 603 | if (isRoot) 604 | { 605 | 606 | GetTypeLiterals(); 607 | AddCustomTypes(); 608 | 609 | GetClassFuncCodes(); 610 | GetClassStaticFuncs(); 611 | GetClassConstantTypes(); 612 | GetConstants(); 613 | 614 | GetClassFuncs(); 615 | 616 | GetConstants(false, true); 617 | 618 | CopyInheritedConsts(); 619 | 620 | CopyInheritedFunctions(); 621 | CheckOverrides(); 622 | SetFnLayers(); 623 | GetFuncNames(); 624 | GetCtors(); 625 | 626 | 627 | 628 | ReverseClasses(); 629 | GetClassVarTypes(); 630 | 631 | BuildClass(); 632 | GetVarsToClear(); 633 | TrimLit(); 634 | 635 | 636 | } 637 | 638 | } 639 | 640 | public override string ToString() => $"ScriptClass {ClassFullName}"; 641 | string GetFullName() => OuterClass == null ? ClassName : OuterClass.ClassFullName + "." + ClassName; 642 | void AddInhs(string inhs) 643 | { 644 | string[] inhClasses = inhs.Split(','); 645 | ScriptClass inh; 646 | for (int j = 0; j < inhClasses.Length; j++) 647 | { 648 | 649 | inh = GetClassByName(inhClasses[j], OuterClass, true); 650 | if (inh == null) throw new ScriptLoadingException($"Couldn't find class '{inhClasses[j]}' to inherit."); 651 | if (inh.IsSealed) throw new ScriptLoadingException($"Can't inherit from sealed class '{inhClasses[j]}'."); 652 | InheritedClasses.Add(inh); 653 | AddSubClasses(inh.SubClasses); 654 | 655 | } 656 | 657 | InheritedClasses.Reverse(); 658 | } 659 | private void CopyInheritedConsts() 660 | { 661 | 662 | var d = new Dictionary(); 663 | foreach (var c in Constants) 664 | d.Add(c.Key, c.Value); 665 | 666 | Constants.Clear(); 667 | 668 | 669 | 670 | 671 | 672 | for (int i = InheritedClasses.Count - 1; i >= 0; i--) 673 | CopyConsts(InheritedClasses[i]); 674 | 675 | 676 | foreach (var c in d) 677 | Constants[c.Key] = c.Value; 678 | 679 | foreach (var c in SubClasses) 680 | c.CopyInheritedConsts(); 681 | 682 | } 683 | void CopyConsts(ScriptClass sc) 684 | { 685 | 686 | if (sc.Constants != null) 687 | { 688 | foreach (var c in sc.Constants) 689 | Constants[c.Key] = c.Value; 690 | 691 | } 692 | 693 | foreach (var c in sc.StaticVarTypes) 694 | StaticVarTypes[c.Key] = c.Value; 695 | 696 | foreach (var c in sc.PublicVars) 697 | if (!PublicVars.Contains(c)) PublicVars.Add(c); 698 | } 699 | 700 | 701 | public void GetTypeLiterals() 702 | { 703 | foreach (var c in SubClasses) 704 | c.GetTypeLiterals(); 705 | 706 | ExtractTypeLiterals(ref Code); 707 | 708 | } 709 | public void AddCustomTypes() 710 | { 711 | string className; 712 | 713 | className = ClassFullName; 714 | if (!CurScript.AllTypes.ContainsKey(className)) 715 | { 716 | CurScript.AllTypes.Add(className, new VarType(TypeID.Custom, CustomType.Get(this))); 717 | className += "[]"; 718 | CurScript.AllTypes.Add(className, new VarType(TypeID.CustomArray, CustomType.Get(this, true))); 719 | } 720 | 721 | foreach (var c in SubClasses) 722 | c.AddCustomTypes(); 723 | } 724 | private void GetCtors() 725 | { 726 | EvalUnit[] args = null; 727 | ScriptClass sc = this; 728 | HasCtors = sc.FuncNames.Contains("New"); 729 | InstanceFuncs = sc.GetServiceFuncs(InstanceFuncName, args); 730 | StaticFuncs = sc.GetServiceFuncs(StaticFuncName, args); 731 | CtorFunc = sc.GetFunc(CtorFuncName, args, true); 732 | FinalizeFuncs = sc.GetServiceFuncs(FinalizeFuncName, args); 733 | ToStringFunc = sc.GetFunc(ToStringFuncName, args, true, null, false, TypeID.String, 0); 734 | GetHashCodeFunc = sc.GetFunc(GetHashCodeFuncName, args, true, null, false, TypeID.Int, 0); 735 | DisposeFunc = sc.GetFunc(DisposeFuncName, args, true, null, false, TypeID.Void, 0); 736 | 737 | GetEnumeratorFunc = sc.GetFunc(GetEnumeratorFuncName, args, true, null, false, TypeID.Object, 0); 738 | EnumeratorMoveNextFunc = sc.GetFunc(EnumeratorMoveNextFuncName, args, true, null, false, TypeID.Bool, 0); 739 | EnumeratorResetFunc = sc.GetFunc(EnumeratorResetFuncName, args, true, null, false, TypeID.Void, 0); 740 | EnumeratorCurrentFunc = sc.GetFunc(EnumeratorCurrentFuncName, args, true, null, false, TypeID.Object, 0); 741 | 742 | EvalUnit arg = new EvalUnit(); 743 | arg.Type = GetVarTypeByID(TypeID.CustomArray); 744 | arg.Kind = EvalUnitKind.Variable; 745 | args = new EvalUnit[] { arg }; 746 | ArrayToStringFunc = sc.GetFunc(ArrayToStringFuncName, args, true, null, true, TypeID.String, 1); 747 | 748 | 749 | args[0].Type = GetVarTypeByID(TypeID.Object); 750 | CompareToFunc = sc.GetFunc(CompareToFuncName, args, true, null, false, TypeID.Int, 1); 751 | 752 | 753 | EqualsFunc = sc.GetFunc(EqualsFuncName, args, true, null, false, TypeID.Bool, 1); 754 | 755 | 756 | 757 | foreach (var c in SubClasses) c.GetCtors(); 758 | } 759 | private void AddFunctions(List funcs) 760 | { 761 | foreach (var f in funcs) 762 | { 763 | if (f.IsExclusive) continue; 764 | var fn = f.IsFixed ? f : f.ShallowCopy(); 765 | 766 | 767 | Functions.Add(fn); 768 | } 769 | } 770 | 771 | private void AddSubClasses(List classes) 772 | { 773 | foreach (var sc in classes) 774 | SubClasses.Add(sc); 775 | 776 | } 777 | private void GetConstants(bool withoutSubClasses = false, bool all = false) 778 | { 779 | if (all || Constants == null) 780 | { 781 | Constants = Constants ?? new Dictionary(); 782 | foreach (var fn in Functions) 783 | { 784 | 785 | bool isStatic = fn.IsStaticFunc; 786 | 787 | 788 | GetConsts(fn, isStatic ? StaticVarTypes : fn.VarTypes, isStatic ? Constants : fn.Constants, isStatic ? PublicVars : null); 789 | } 790 | } 791 | 792 | if (!withoutSubClasses) 793 | { 794 | foreach (var c in SubClasses) c.GetConstants(withoutSubClasses, all); 795 | } 796 | 797 | } 798 | private void GetClassConstantTypes() 799 | { 800 | 801 | foreach (var c in SubClasses) c.GetClassConstantTypes(); 802 | 803 | foreach (var fn in Functions) 804 | { 805 | if (fn.IsStaticFunc) 806 | GetConsts(fn, StaticVarTypes, Constants, PublicVars, true); 807 | 808 | 809 | } 810 | 811 | } 812 | 813 | 814 | 815 | 816 | private void GetFuncNames() 817 | { 818 | FuncNames = Functions.Select(x => x.Name).ToHashSet(); 819 | PublicNonStaticFuncNames = Functions.Where(x => x.IsPublic && !x.IsStatic).Select(x => x.Name).ToHashSet(); 820 | PublicStaticFuncNames = Functions.Where(x => x.IsPublic && x.IsStatic).Select(x => x.Name).ToHashSet(); 821 | 822 | 823 | foreach (var c in SubClasses) c.GetFuncNames(); 824 | } 825 | private void ReverseClasses() 826 | { 827 | 828 | SubClasses.Reverse(); 829 | 830 | foreach (var c in SubClasses) c.ReverseClasses(); 831 | } 832 | private void CopyInheritedFunctions() 833 | { 834 | foreach (var c in InheritedClasses) 835 | AddFunctions(c.Functions); 836 | 837 | foreach (var c in SubClasses) 838 | c.CopyInheritedFunctions(); 839 | } 840 | private void GetClassStaticFuncs() 841 | { 842 | foreach (var c in SubClasses) 843 | c.GetClassStaticFuncs(); 844 | 845 | GetFunctions(true); 846 | 847 | 848 | 849 | } 850 | private void GetClassFuncs() 851 | { 852 | foreach (var c in SubClasses) 853 | c.GetClassFuncs(); 854 | 855 | GetFunctions(false); 856 | 857 | Functions.Reverse(); 858 | foreach (var f in Functions) f.CheckForInconsistentAccessibility(); 859 | 860 | FnCodes = null; 861 | } 862 | private void GetClassFuncCodes() 863 | { 864 | foreach (var c in SubClasses) 865 | c.GetClassFuncCodes(); 866 | 867 | GetClassFuncCodes(ref Code); 868 | 869 | 870 | 871 | } 872 | void GetFunctions(bool staticFn = false) 873 | { 874 | ScriptFunction newFn; 875 | if (staticFn) 876 | { 877 | foreach (var fnCode in FnCodes.Where(x => x.staticFunc == true)) 878 | { 879 | newFn = new ScriptFunction($"{StaticFuncName}(){{{fnCode.code}}}", this); 880 | Functions.Add(newFn); 881 | } 882 | } 883 | else 884 | { 885 | 886 | foreach (var fnCode in FnCodes.Where(x => x.staticFunc == false)) 887 | { 888 | newFn = new ScriptFunction($"{InstanceFuncName}(){{{fnCode.code}}}", this); 889 | Functions.Add(newFn); 890 | } 891 | foreach (var fnCode in FnCodes.Where(x => x.staticFunc == null)) 892 | { 893 | newFn = new ScriptFunction(fnCode.code, this); 894 | if (IsStatic && !newFn.IsStatic) throw new ScriptLoadingException($"Static class '{ClassFullName}' can't contain an instance function '{newFn.Name}'."); 895 | Functions.Add(newFn); 896 | } 897 | } 898 | } 899 | 900 | private void GetClassVarTypes() 901 | { 902 | foreach (var c in SubClasses) 903 | c.GetClassVarTypes(); 904 | 905 | GetVarTypes(); 906 | } 907 | 908 | 909 | private void BuildClass() 910 | { 911 | 912 | 913 | foreach (var f in Functions) BuildFunc(f); 914 | 915 | ExprTypeCache = null; 916 | EUCache = null; 917 | 918 | foreach (var c in SubClasses) c.BuildClass(); 919 | 920 | } 921 | private void GetVarsToClear() 922 | { 923 | ForCleaning = GetForCleaning(VarTypes); 924 | foreach (var fn in Functions) 925 | if (!fn.IsInstanceOrStaticFunc) fn.ForCleaning = GetForCleaning(fn.VarTypes.Skip(ScriptFunction.DefaultVarTypeCount).Where(x => x.Key != ThisStr && fn.Params.Where(y => y.ByRef && y.ParamName == x.Key).FirstOrDefault() == null).ToDictionary(x => x.Key, x => x.Value)); 926 | 927 | foreach (var c in SubClasses) c.GetVarsToClear(); 928 | 929 | } 930 | private void GetSubClasses(ref string code) 931 | { 932 | 933 | int l = ClassSign.Length + 1; 934 | string cs = UnitSeparator + ClassSign + " "; 935 | int i2, i3, i = code.IndexOf(cs); 936 | string classCode = "", className = ""; 937 | while (i >= 0) 938 | { 939 | i2 = code.IndexOf('{', i); 940 | i3 = FindClosing(code, i, "{", "}"); 941 | classCode = code.Substring(i2 + 1, i3 - (i2 + 1)); 942 | classCode = classCode.Trim(UnitSeparator[0]); 943 | if (!classCode.StartsWith(CodeLocationChar)) 944 | { 945 | var lm = GetLocationMark(CurScript, code.Substring(0, i2)); 946 | if (lm != null) 947 | classCode = ToLocationMarkStr(lm.CFile.Num, lm.Line2) + classCode; 948 | } 949 | className = code.Substring(i + l, i2 - (i + l)); 950 | className = RemLocationMarks(className).TrimEnd(); 951 | classCode = UnitSeparator[0] + classCode; 952 | var newClass = new ScriptClass(classCode, className, this, CurScript); 953 | SubClasses.Add(newClass); 954 | 955 | i--; 956 | if (code[i] != UnitSeparator[0]) 957 | { 958 | i = code.LastIndexOf(UnitSeparator[0], i); 959 | string h = code.Substring(i + 1, i2 - (i + 1)); 960 | h = RemLocationMarks(h); 961 | bool isFixed = false, IsVirtual = false, IsOverride = false, IsExclusive = false, IsPrivate = false; 962 | GetModifiers(ref h, ref newClass.IsPublic, ref newClass.IsStatic, ref newClass.IsSealed, ref isFixed, ref IsVirtual, ref IsOverride, ref IsExclusive, ref IsPrivate); 963 | if (isFixed) throw new ScriptLoadingException("Modifier 'fixed' not supported for classes."); 964 | if (IsVirtual) throw new ScriptLoadingException("Modifier 'virtual' not supported for classes."); 965 | if (IsOverride) throw new ScriptLoadingException("Modifier 'override' not supported for classes."); 966 | if (IsExclusive) throw new ScriptLoadingException("Modifier 'exclusive' not supported for classes."); 967 | } 968 | 969 | code = code.Remove(i, i3 + 1 - i); 970 | 971 | i = code.IndexOf(cs, i); 972 | } 973 | } 974 | 975 | static string[] Modifiers = { "public ", "static ", "sealed ", "fixed ", "virtual ", "override ", "private ", "exclusive " }; 976 | static void GetModifiers(ref string h, ref bool IsPublic, ref bool IsStatic, ref bool IsSealed, ref bool IsFixed, ref bool IsVirtual, ref bool IsOverride, ref bool IsExclusive, ref bool IsPrivate) 977 | { 978 | bool redo; 979 | do 980 | { 981 | redo = false; 982 | for (int i = 0; i < Modifiers.Length; i++) 983 | { 984 | string m = Modifiers[i]; 985 | if (h.StartsWith(m)) 986 | { 987 | if (m == "public ") IsPublic = true; 988 | else if (m == "static ") IsStatic = true; 989 | else if (m == "sealed ") IsSealed = true; 990 | else if (m == "fixed ") IsFixed = true; 991 | else if (m == "virtual ") IsVirtual = true; 992 | else if (m == "override ") IsOverride = true; 993 | else if (m == "private ") { IsPublic = false; IsPrivate = true; } 994 | else if (m == "exclusive ") IsExclusive = true; 995 | 996 | h = h.Remove(0, m.Length); 997 | redo = true; 998 | } 999 | } 1000 | } while (redo); 1001 | 1002 | } 1003 | 1004 | public static bool CanCovert(Type fromType, Type toType) 1005 | { 1006 | var converter = System.ComponentModel.TypeDescriptor.GetConverter(fromType); 1007 | return converter.CanConvertTo(toType); 1008 | } 1009 | 1010 | 1011 | private void GetClassFuncCodes(ref string code) 1012 | { 1013 | 1014 | int i2, i3, i = code.IndexOf("){"); 1015 | string funcCode = "", funcHead = ""; 1016 | while (i >= 0) 1017 | { 1018 | i2 = code.LastIndexOf(UnitSeparator, i, StringComparison.Ordinal); 1019 | funcHead = code.Substring(i2 + 1, i + 1 - (i2 + 1)); 1020 | 1021 | if (!funcHead.StartsWith("if(") && !funcHead.StartsWith("elseif(") && !funcHead.StartsWith("for(") && !funcHead.StartsWith("foreach(") && !funcHead.StartsWith("while(") && !funcHead.StartsWith("switch(") && !funcHead.StartsWith("catch(") && !funcHead.StartsWith("catch$(") && !funcHead.StartsWith("lock(") && !funcHead.StartsWith("using(")) 1022 | { 1023 | i3 = FindClosing(code, i, "{", "}"); 1024 | funcCode = code.Substring(i2 + 1, i3 - i2); 1025 | 1026 | funcCode = funcCode.Trim(UnitSeparator[0]); 1027 | if (!funcCode.StartsWith(CodeLocationChar)) 1028 | { 1029 | var lm = GetLocationMark(CurScript, code.Substring(0, i2)); 1030 | 1031 | if (lm != null) 1032 | funcCode = ToLocationMarkStr(lm.CFile.Num, lm.Line2) + funcCode; 1033 | 1034 | } 1035 | else 1036 | 1037 | i2 = SkipLocationMarks(code, i2 + 1); 1038 | 1039 | FnCodes.Add((funcCode, this, null)); 1040 | 1041 | 1042 | code = code.Remove(i2, i3 + 1 - i2); 1043 | } 1044 | else { i2 = i + 1; } 1045 | 1046 | i = code.IndexOf("){", i2); 1047 | 1048 | } 1049 | 1050 | if (RemLocationMarks(code).Trim(UnitSeparator[0]).Length > 0) 1051 | { 1052 | int i0, l; 1053 | string staticCode = ""; 1054 | string[] find = { UnitSeparator + "const ", UnitSeparator + "static " }; 1055 | int n = 0; 1056 | foreach (string fs in find) 1057 | { 1058 | l = n++ == 0 ? 0 : fs.Length - 1; 1059 | i = code.IndexOf(fs); 1060 | 1061 | while (i >= 0) 1062 | { 1063 | 1064 | var pc = code[i - 1]; 1065 | if (pc == ' ' || pc == CodeLocationChar || pc == UnitSeparator[0]) 1066 | { 1067 | i2 = code.IndexOf(';', i); 1068 | if (i2 < 0) break; 1069 | i0 = i; 1070 | i--; 1071 | if (code[i] != UnitSeparator[0]) i = code.LastIndexOf(UnitSeparator[0], i); 1072 | string s = code.Substring(i, i0 - i) + code.Substring(i0 + l, i2 + 1 - (i0 + l)); 1073 | s = s.TrimStart(UnitSeparator[0]); 1074 | if (!s.StartsWith(CodeLocationChar)) 1075 | { 1076 | var lm = GetLocationMark(CurScript, code.Substring(0, i)); 1077 | if (lm != null) 1078 | s = ToLocationMarkStr(lm.CFile.Num, lm.Line2) + s; 1079 | 1080 | } 1081 | 1082 | staticCode += s + UnitSeparator; 1083 | 1084 | code = code.Remove(i, i2 + 1 - i); 1085 | 1086 | } 1087 | i = code.IndexOf(fs, i); 1088 | } 1089 | } 1090 | if (staticCode.Length > 0) 1091 | { 1092 | 1093 | 1094 | FnCodes.Add((staticCode, this, true)); 1095 | } 1096 | 1097 | if (RemLocationMarks(code).Trim(UnitSeparator[0]).Length > 0) 1098 | { 1099 | if (IsStatic) throw new ScriptLoadingException($"Static class '{ClassFullName}' can't contain an instance members."); 1100 | 1101 | 1102 | FnCodes.Add((code, this, false)); 1103 | } 1104 | } 1105 | 1106 | 1107 | 1108 | } 1109 | void GetVarTypes() 1110 | { 1111 | ScriptFunction[] f = Functions.Where(x => x.IsInstanceOrStaticFunc).ToArray(); 1112 | for (int j = f.Length - 1; j >= 0; j--) 1113 | { 1114 | if (f[j].Units == null) f[j].Units = ParseCode(f[j]); 1115 | 1116 | if (!f[j].IsStaticFunc) 1117 | AddVarTypes(f[j], VarTypes, Constants, PublicVars); 1118 | else 1119 | AddVarTypes(f[j], StaticVarTypes, Constants, PublicVars); 1120 | } 1121 | 1122 | 1123 | foreach (var v in VarTypes) 1124 | InstVars.Add(new VarName(CurScript, v.Key, v.Value, PublicVars.Contains(v.Key))); 1125 | 1126 | foreach (var v in StaticVarTypes) 1127 | StatVars.Add(new VarName(CurScript, v.Key, v.Value, PublicVars.Contains(v.Key))); 1128 | } 1129 | private void BuildFunc(ScriptFunction fn) 1130 | { 1131 | 1132 | if (fn.Compiled || (fn.IsFixed && fn.Class != this)) return; 1133 | 1134 | 1135 | if (!fn.IsInstanceOrStaticFunc) 1136 | { 1137 | fn.Units = ParseCode(fn); 1138 | AddVarTypes(fn, fn.VarTypes, fn.Constants); 1139 | 1140 | } 1141 | 1142 | BuildEvalUnits(fn); 1143 | CalcJumps(fn); 1144 | 1145 | fn.Compiled = true; 1146 | } 1147 | 1148 | void SetFnLayers() 1149 | { 1150 | foreach (var fn in Functions) 1151 | FnLayers[fn] = Array.IndexOf(Functions.Where(x => x.Signature == fn.Signature).ToArray(), fn); 1152 | 1153 | foreach (var c in SubClasses) 1154 | c.SetFnLayers(); 1155 | } 1156 | 1157 | 1158 | 1159 | public static (string file, string[] args) GetFileAndArgs(string str) 1160 | { 1161 | var strVals = new List(); 1162 | 1163 | char[] q = { '"', '\'' }; 1164 | int i = str.IndexOfAny(q); 1165 | while (i >= 0) 1166 | { 1167 | int i2 = FindEndOfLiteral(str, i, true, str[i]); 1168 | if (i2 < 0) throw new FormatException("Invalid command line format."); 1169 | string v = str.Substring(i + 1, i2 - (i + 1)); 1170 | 1171 | if (str[i] == '"') 1172 | v = System.Text.RegularExpressions.Regex.Unescape(v); 1173 | 1174 | 1175 | strVals.Add(v); 1176 | 1177 | str = str.Remove(i, i2 + 1 - i).Insert(i, $"{LiteralMark}{(strVals.Count - 1)}{LiteralMark}"); 1178 | 1179 | i = str.IndexOfAny(q, i); 1180 | } 1181 | 1182 | string[] s = str.Split(' ').Where(x => x.Length > 0).ToArray(); 1183 | for (i = 0; i < s.Length; i++) 1184 | { 1185 | string item = s[i]; 1186 | int j = item.IndexOf(LiteralMark); 1187 | while (j >= 0) 1188 | { 1189 | int j2 = item.IndexOf(LiteralMark, j + 1); 1190 | int n = int.Parse(item.Substring(j + 1, j2 - (j + 1))); 1191 | item = item.Remove(j, j2 + 1 - j).Insert(j, strVals[n]); 1192 | 1193 | j = item.IndexOf(LiteralMark, j); 1194 | } 1195 | s[i] = item; 1196 | } 1197 | 1198 | 1199 | string file = System.IO.Path.GetFullPath(s[0]); 1200 | string[] args = s.Skip(1).ToArray(); 1201 | return (file, args); 1202 | } 1203 | 1204 | 1205 | public Dictionary VarTypes = new Dictionary(); 1206 | public Dictionary StaticVarTypes = new Dictionary(); 1207 | public Dictionary Constants = null; 1208 | 1209 | 1210 | public const int MaxTypeId = (int)TypeID.CustomArray; 1211 | public class VarsToClear 1212 | { 1213 | public int[][] VarIDs = new int[MaxTypeId + 1][]; 1214 | 1215 | public TypeFlag UsedTypes = TypeFlag.None; 1216 | public int UsedTypeCount = 0; 1217 | 1218 | public bool StringUsed, ObjectUsed, CustomUsed, IntArrayUsed, StringArrayUsed, ByteArrayUsed, LongArrayUsed, DoubleArrayUsed, FloatArrayUsed, BoolArrayUsed, DecimalArrayUsed, CharArrayUsed, ShortArrayUsed, DateArrayUsed, ObjectArrayUsed; 1219 | public void SetUsedTypes() 1220 | { 1221 | 1222 | StringUsed = (UsedTypes & TypeFlag.String) != 0; 1223 | ObjectUsed = (UsedTypes & TypeFlag.Object) != 0; 1224 | CustomUsed = (UsedTypes & TypeFlag.Custom) != 0; 1225 | IntArrayUsed = (UsedTypes & TypeFlag.IntArray) != 0; 1226 | StringArrayUsed = (UsedTypes & TypeFlag.StringArray) != 0; 1227 | ByteArrayUsed = (UsedTypes & TypeFlag.ByteArray) != 0; 1228 | LongArrayUsed = (UsedTypes & TypeFlag.LongArray) != 0; 1229 | DoubleArrayUsed = (UsedTypes & TypeFlag.DoubleArray) != 0; 1230 | FloatArrayUsed = (UsedTypes & TypeFlag.FloatArray) != 0; 1231 | BoolArrayUsed = (UsedTypes & TypeFlag.BoolArray) != 0; 1232 | DecimalArrayUsed = (UsedTypes & TypeFlag.DecimalArray) != 0; 1233 | CharArrayUsed = (UsedTypes & TypeFlag.CharArray) != 0; 1234 | ShortArrayUsed = (UsedTypes & TypeFlag.ShortArray) != 0; 1235 | DateArrayUsed = (UsedTypes & TypeFlag.DateArray) != 0; 1236 | ObjectArrayUsed = (UsedTypes & TypeFlag.ObjectArray) != 0; 1237 | 1238 | UsedTypeCount = System.Numerics.BitOperations.PopCount((ulong)UsedTypes); 1239 | } 1240 | } 1241 | public class ScriptFunction 1242 | { 1243 | public string Name; 1244 | public VarType ReturnType; 1245 | public string TypeName; 1246 | public FuncParam[] Params; 1247 | public string Signature; 1248 | public CodeUnit[] Units; 1249 | public string Code; 1250 | public Dictionary VarTypes = new Dictionary(); 1251 | public Dictionary Constants = new Dictionary(); 1252 | 1253 | 1254 | public bool IsInstanceFunc, IsStaticFunc, IsFinalizeFunc, IsNewFunc, IsDisposeFunc, IsInstanceOrStaticFunc; 1255 | 1256 | public bool Compiled; 1257 | public ScriptClass Class; 1258 | public VarsToClear ForCleaning; 1259 | public LocMark LocationMark; 1260 | 1261 | public bool IsPublic, IsStatic, IsSealed, IsFixed, IsVirtual, IsOverride, IsExclusive; 1262 | 1263 | public int RefCount = 0; 1264 | public string CodeFile, BasePath; 1265 | public int ForEachCount = 0, ReapplyCount = 0; 1266 | public bool HasParams; 1267 | public int ParamsIndex = -1; 1268 | public VarType ParamsType; 1269 | public int MinArgNum = 0; 1270 | public ScriptFunction OrigFn; 1271 | 1272 | public class FuncParam 1273 | { 1274 | public string ParamName; 1275 | public string ParamTypeName; 1276 | public VarType ParamType; 1277 | public int VarId; 1278 | public bool ByRef; 1279 | public bool Optional; 1280 | public FuncParam(string name, string typeName, VarType type, bool byRef, bool optional, ScriptClass location) 1281 | { 1282 | ParamName = name; 1283 | ParamTypeName = typeName; 1284 | ParamType = type; 1285 | VarId = location.CurScript.GetVarID(name, type.ID); 1286 | ByRef = byRef; 1287 | Optional = optional; 1288 | } 1289 | 1290 | public FuncParam ShallowCopy() 1291 | { 1292 | var fp = (FuncParam)this.MemberwiseClone(); 1293 | return fp; 1294 | } 1295 | } 1296 | 1297 | public ScriptFunction ShallowCopy() 1298 | { 1299 | var f = (ScriptFunction)this.MemberwiseClone(); 1300 | 1301 | return f; 1302 | } 1303 | public void CheckForInconsistentAccessibility() 1304 | { 1305 | if (ReturnType.ID == TypeID.Custom || ReturnType.ID == TypeID.CustomArray) 1306 | { 1307 | string t = TypeName; 1308 | if (t.EndsWith("[]")) t = t.Remove(t.Length - 2, 2); 1309 | var c = GetClassByNameOrException(t, this.Class); 1310 | if (InconsistentAccessibility(this, c)) throw new ScriptLoadingException($"Inconsistent accessibility. Return type '{TypeName}' is less accessible than function '{Name}' at '{this.Class.ClassFullName}'."); 1311 | } 1312 | } 1313 | 1314 | public const int DefaultVarTypeCount = 4; 1315 | void AddDefaultVars() 1316 | { 1317 | 1318 | VarTypes.Clear(); 1319 | VarTypes.Add(ExceptionVarName.Object, new VarType(TypeID.Object)); 1320 | VarTypes.Add(ExceptionVarName.TypeName, new VarType(TypeID.String)); 1321 | VarTypes.Add(ExceptionVarName.Message, new VarType(TypeID.String)); 1322 | VarTypes.Add(ExceptionVarName.CustomExObj, new VarType(TypeID.Object)); 1323 | } 1324 | public override string ToString() => Signature; 1325 | 1326 | 1327 | 1328 | public ScriptFunction(string code, ScriptClass location) 1329 | { 1330 | string h = "", h2 = ""; 1331 | Script script = location.CurScript; 1332 | 1333 | #if EXON 1334 | try 1335 | { 1336 | #endif 1337 | OrigFn = this; 1338 | Class = location; 1339 | 1340 | if (code.StartsWith(CodeLocationChar)) 1341 | LocationMark = GetLocationMark(script, code, true); 1342 | 1343 | 1344 | int i = code.IndexOf('('); 1345 | 1346 | h = code.Substring(0, i); 1347 | h = RemLocationMarks(h); 1348 | 1349 | int i2 = FindClosing(code, i, "(", ")"); 1350 | h2 = code.Substring(i + 1, i2 - (i + 1)); 1351 | h2 = RemLocationMarks(h2); 1352 | bool isPrivate = false; 1353 | GetModifiers(ref h, ref IsPublic, ref IsStatic, ref IsSealed, ref IsFixed, ref IsVirtual, ref IsOverride, ref IsExclusive, ref isPrivate); 1354 | 1355 | 1356 | string[] p = h.Split(' '); 1357 | 1358 | if (p.Length > 1) 1359 | { 1360 | Name = p[1]; 1361 | TypeName = p[0]; 1362 | 1363 | ReturnType = Class.GetTypeFromDict(TypeName, Class); 1364 | } 1365 | else 1366 | { 1367 | Name = p[0]; 1368 | TypeName = TypeStr.Void; 1369 | ReturnType = script.AllTypes[TypeName]; 1370 | 1371 | } 1372 | 1373 | if (!CheckCharsInVarName(Name)) 1374 | throw new ScriptLoadingException($"Invalid function name '{Name}'. A valid name starts with a letter or underscore, followed by any number of letters, numbers, or underscores."); 1375 | 1376 | IsInstanceFunc = Name == InstanceFuncName; 1377 | IsStaticFunc = Name == StaticFuncName; 1378 | IsFinalizeFunc = Name == FinalizeFuncName; 1379 | IsNewFunc = Name == CtorFuncName; 1380 | IsDisposeFunc = Name == DisposeFuncName; 1381 | IsInstanceOrStaticFunc = IsInstanceFunc || IsStaticFunc; 1382 | if ((IsInstanceFunc || IsNewFunc || IsFinalizeFunc) && IsStatic) throw new ScriptLoadingException($"Function '{Name}' cannot be static."); 1383 | if (IsStaticFunc) IsStatic = true; 1384 | else if (IsNewFunc && !isPrivate) IsPublic = true; 1385 | AddDefaultVars(); 1386 | 1387 | Signature = Name + " "; 1388 | p = SmartSplit(h2); 1389 | 1390 | if (p.Length > 1 || p[0].Length > 0) 1391 | { 1392 | if (p.Length > byte.MaxValue) throw new ScriptLoadingException($"Too many parameters ({p.Length}). Limit is {byte.MaxValue}."); 1393 | string prmName, prmTypeName; 1394 | bool byRef; 1395 | HasParams = false; 1396 | VarType prmType; 1397 | Params = new FuncParam[p.Length]; 1398 | int opPos; 1399 | for (int n = 0; n < p.Length; n++) 1400 | { 1401 | if (HasParams) throw new ScriptLoadingException("A params parameter must be the last parameter."); 1402 | string prm = p[n]; 1403 | byRef = prm.StartsWith("ref "); 1404 | if (byRef) { p[n] = prm = prm.Substring(4); byRef = true; RefCount++; } 1405 | else if (prm.StartsWith("params ")) { p[n] = prm = prm.Substring(7); HasParams = true; ParamsIndex = n; } 1406 | 1407 | i = prm.IndexOf(' '); 1408 | if (i < 1) throw new ScriptLoadingException($"Invalid parameter at position {n + 1}."); 1409 | 1410 | prmName = prm.Substring(i + 1); 1411 | prmTypeName = prm.Substring(0, i); 1412 | 1413 | opPos = prmName.IndexOf(OperatorChar); 1414 | bool optional = opPos >= 0; 1415 | if (optional) 1416 | { 1417 | if (byRef) throw new ScriptLoadingException("Ref-parameter cannot have a default value."); 1418 | if (HasParams) throw new ScriptLoadingException("Params-parameter cannot have a default value."); 1419 | prmName = prmName.Substring(0, opPos); 1420 | 1421 | } 1422 | else if (!HasParams) MinArgNum = n + 1; 1423 | else optional = true; 1424 | 1425 | 1426 | 1427 | prmType = Class.GetTypeFromDict(prmTypeName, Class); 1428 | if (HasParams && !TypeIsArray(prmType.ID)) throw new ScriptLoadingException("When using the params keyword, must specify an array of the data type."); 1429 | Params[n] = new FuncParam(prmName, prmTypeName, prmType, byRef, optional, location); 1430 | 1431 | if (byRef) Signature += "ref*"; 1432 | else if (HasParams) Signature += "params*"; 1433 | else if (optional) Signature += "optional*"; 1434 | 1435 | Signature += prmType.Name + " "; 1436 | } 1437 | if (HasParams) 1438 | ParamsType = location.GetElementVarType(Params[ParamsIndex].ParamType); 1439 | 1440 | } 1441 | else Params = new FuncParam[0]; 1442 | 1443 | i = code.IndexOf("){"); 1444 | i = code.IndexOf('{', i); 1445 | i2 = code.LastIndexOf('}'); 1446 | Code = code.Substring(i + 1, i2 - (i + 1)); 1447 | Code = Code.Trim(UnitSeparator[0]); 1448 | if (!Code.StartsWith(CodeLocationChar)) 1449 | if (LocationMark != null) 1450 | Code = ToLocationMarkStr(LocationMark.CFile.Num, LocationMark.Line2) + Code; 1451 | 1452 | if (p[0].Length > 0) Code = string.Join(UnitSeparator, p) + UnitSeparator + Code; 1453 | 1454 | 1455 | 1456 | 1457 | if (!IsStatic) VarTypes.Add(ThisStr, new VarType(TypeID.Custom, CustomType.Get(Class))); 1458 | 1459 | var lm = LocationMark ?? GetLocationMark(script, code, true); 1460 | 1461 | if (lm != null) CodeFile = lm.File; 1462 | else throw new ScriptLoadingException($"Could not find a service label with data about the file in which the function '{Name}' is located."); 1463 | BasePath = lm.CFile.Base; 1464 | 1465 | var d = PPDirective.CuteDirectiveLine(ref Code, "base", 0, CodeLocationChar); 1466 | if (d.str != null) 1467 | { 1468 | 1469 | var ppd = PPDirective.Get(d.str, lm.File, script); 1470 | 1471 | BasePath = TrimLastSlash(ppd.Path) ?? System.IO.Path.GetDirectoryName(lm.File); 1472 | 1473 | 1474 | } 1475 | 1476 | #if EXON 1477 | } 1478 | catch (Exception ex) 1479 | { 1480 | var lm = GetLocationMark(script, code, true); 1481 | string msg = ErrMsgWithLoc($"Function '{h}' at '{Class.ClassFullName}' loading error. " + ex.Message, lm); 1482 | throw new ScriptLoadingException(msg); 1483 | } 1484 | #endif 1485 | } 1486 | 1487 | } 1488 | 1489 | static bool InconsistentAccessibility(ScriptFunction f, ScriptClass typeClass) 1490 | { 1491 | var fac = FuncAccessibilityClass(f); 1492 | var tac = AccessibilityClass(typeClass); 1493 | bool isNested = ClassIsNested(tac, fac); 1494 | return isNested; 1495 | } 1496 | static bool ClassIsNested(ScriptClass c, ScriptClass where) 1497 | { 1498 | if (c == where) return false; 1499 | while (c.OuterClass != null) 1500 | { 1501 | c = c.OuterClass; 1502 | if (c == where) return true; 1503 | 1504 | } 1505 | return false; 1506 | } 1507 | static ScriptClass AccessibilityClass(ScriptClass c) 1508 | { 1509 | 1510 | while (c.OuterClass != null) 1511 | { 1512 | bool pub = c.IsPublic; 1513 | c = c.OuterClass; 1514 | if (!pub) break; 1515 | } 1516 | return c; 1517 | } 1518 | static ScriptClass FuncAccessibilityClass(ScriptFunction f) 1519 | { 1520 | if (!f.IsPublic) return f.Class; 1521 | return AccessibilityClass(f.Class); 1522 | } 1523 | 1524 | 1525 | 1526 | public bool Is(ScriptClass f) 1527 | { 1528 | if (this == f || InheritedClasses.Contains(f)) return true; 1529 | for (int i = 0; i < InheritedClasses.Count; i++) 1530 | if (InheritedClasses[i].Is(f)) return true; 1531 | 1532 | return false; 1533 | } 1534 | 1535 | 1536 | } 1537 | 1538 | 1539 | 1540 | 1541 | 1542 | 1543 | } 1544 | --------------------------------------------------------------------------------