diff --git a/README.md b/README.md index 8d2c784..1c4dce0 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,9 @@ end. ### LANG and REPL compatibility -| LANG version | REPL version | -|--------------|--------------| -| 0.2.0 | 1.0.0+ | -| 0.1.0-0.1.1 | Unsupported | +| REPL version | LANG version | +|-----------------|-----------------| +| 1.0.0 | 0.2.0+ | ### Download -[![stable](https://img.shields.io/badge/REPL_stable-1.0.0-00cc00)](https://github.com/AIexandrKotov/SLThree/releases/tag/0.2.0) [![stable](https://img.shields.io/badge/LANG_exp-0.2.0-ccaa00)](https://github.com/AIexandrKotov/SLThree/releases/tag/0.2.0) \ No newline at end of file +[![stable](https://img.shields.io/badge/REPL_stable-1.0.0-00cc00)](https://github.com/AIexandrKotov/SLThree/releases/tag/0.2.0) [![stable](https://img.shields.io/badge/LANG_exp-0.3.0-ccaa00)](https://github.com/AIexandrKotov/SLThree/releases/tag/0.3.0) \ No newline at end of file diff --git a/SLThree/Embedding/EmbeddingExtensions.cs b/SLThree/Embedding/EmbeddingExtensions.cs index 2b45e49..b896056 100644 --- a/SLThree/Embedding/EmbeddingExtensions.cs +++ b/SLThree/Embedding/EmbeddingExtensions.cs @@ -20,6 +20,18 @@ public static ExecutionContext RunScript(this ExecutionContext context, string c { return Parser.This.RunScript(code, null, context); } - public static T Unwrap(this ExecutionContext context) where T : new() => Wrapper.Unwrap(context); + + public static T Unwrap(this ExecutionContext context) where T : new() => UnwrapperForInstances.Unwrap(context); + public static void UnwrapStatic(this ExecutionContext context) => Wrapper.UnwrapStatic(context); + public static ExecutionContext Wrap(this T obj) => Wrapper.Wrap(obj); + public static ExecutionContext WrapStatic() => Wrapper.WrapStatic(); + public static void UnwrapStaticClass(this ExecutionContext context, Type type) => UnwrapperForStaticClasses.Unwrap(type, context); + public static void UnwrapStaticClass(this Type type, ExecutionContext context) => UnwrapperForStaticClasses.Unwrap(type, context); + public static ExecutionContext WrapStaticClass(this Type type) => UnwrapperForStaticClasses.Wrap(type); + + public static T SafeUnwrap(this ExecutionContext context) where T : new() => UnwrapperForInstances.SafeUnwrap(context); + public static void SafeUnwrapStatic(this ExecutionContext context) => Wrapper.SafeUnwrapStatic(context); + public static void SafeUnwrapStaticClass(this ExecutionContext context, Type type) => UnwrapperForStaticClasses.SafeUnwrap(type, context); + public static void SafeUnwrapStaticClass(this Type type, ExecutionContext context) => UnwrapperForStaticClasses.SafeUnwrap(type, context); } } diff --git a/SLThree/Exceptions/OperatorError.cs b/SLThree/Exceptions/OperatorError.cs index e5922ed..336d22a 100644 --- a/SLThree/Exceptions/OperatorError.cs +++ b/SLThree/Exceptions/OperatorError.cs @@ -1,12 +1,15 @@ -using System; +using SLThree.Extensions; +using System; namespace SLThree { public class OperatorError : RuntimeError { public OperatorError(ExpressionUnary unary, Type left) - : base($"Operator {unary.Operator} not allow for {left?.Name ?? "null"}", unary.SourceContext) { } + : base($"Operator {unary.Operator} not allow for {left?.GetTypeString() ?? "null"}", unary.SourceContext) { } public OperatorError(ExpressionBinary binary, Type left, Type right) - : base($"Operator {binary.Operator} not allow for {left?.Name ?? "null"} and {right?.Name ?? "null"}", binary.SourceContext) { } + : base($"Operator {binary.Operator} not allow for {left?.GetTypeString() ?? "null"} and {right?.GetTypeString() ?? "null"}", binary.SourceContext) { } + public OperatorError(string op, Type cond, SourceContext context) + : base($"Operator {op} not allow for {cond?.GetTypeString() ?? "null"}", context) { } } } diff --git a/SLThree/Exceptions/SLTException.cs b/SLThree/Exceptions/SLTException.cs index 0abcd0b..1b2ca55 100644 --- a/SLThree/Exceptions/SLTException.cs +++ b/SLThree/Exceptions/SLTException.cs @@ -41,6 +41,9 @@ public SyntaxError() : base() { } public SyntaxError(SourceContext context) : base(context) { } public SyntaxError(string message, SourceContext context) : base(message, context) { } public SyntaxError(string message, Exception inner, SourceContext context) : base(message, inner, context) { } + public SyntaxError(Cursor cursor) : base(new SourceContext(cursor)) { } + public SyntaxError(string message, Cursor cursor) : base(message, new SourceContext(cursor)) { } + public SyntaxError(string message, Exception inner, Cursor cursor) : base(message, inner, new SourceContext(cursor)) { } } /* /// diff --git a/SLThree/ExecutionContext.cs b/SLThree/ExecutionContext.cs index c89d1cb..42e711c 100644 --- a/SLThree/ExecutionContext.cs +++ b/SLThree/ExecutionContext.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; @@ -12,7 +14,10 @@ public interface IExecutable object GetValue(ExecutionContext context); } - public bool ForbidImplicit = true; + /// + /// Запрещает implicit в контексте + /// + public bool fimp = false; public bool Returned; public bool Broken; @@ -20,16 +25,13 @@ public interface IExecutable public object ReturnedValue; - public static ContextWrap global = new ContextWrap(new ExecutionContext()); + public List Errors = new List(); + + public static ContextWrap global = new ContextWrap(new ExecutionContext() { fimp = true }); - private static bool and(bool a, bool b) => a && b; - private static bool or(bool a, bool b) => a || b; - private static bool xor(bool a, bool b) => a ^ b; static ExecutionContext() { - global.pred.LocalVariables.SetValue("and", Method.Create(and)); - global.pred.LocalVariables.SetValue("or", Method.Create(or)); - global.pred.LocalVariables.SetValue("xor", Method.Create(xor)); + } public class ContextWrap @@ -42,10 +44,11 @@ public ContextWrap(ExecutionContext pred) } internal ExecutionContext PreviousContext; public ContextWrap pred => new ContextWrap(PreviousContext); - public ContextWrap direct => new ContextWrap(this); + public ContextWrap wrap => new ContextWrap(this); private int cycles = 0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void StartCycle() { cycles += 1; @@ -54,6 +57,7 @@ public void StartCycle() public bool InCycle() => cycles > 1; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void EndCycle() { cycles -= 1; @@ -64,6 +68,7 @@ public void EndCycle() public void Return(object o) { Returned = true; ReturnedValue = o; } public void Break() { Broken = true; } public void Continue() { Continued = true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PrepareToInvoke() { Returned = Broken = Continued = false; } public void DefaultEnvironment() { diff --git a/SLThree/Extensions/GenericExtensions.cs b/SLThree/Extensions/GenericExtensions.cs index c3d7810..c759719 100644 --- a/SLThree/Extensions/GenericExtensions.cs +++ b/SLThree/Extensions/GenericExtensions.cs @@ -1,6 +1,8 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; @@ -20,6 +22,7 @@ public static string JoinIntoString(this IEnumerable e, string delim) return sb.ToString(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TOut[] ConvertAll(this TIn[] array, Converter func) => Array.ConvertAll(array, func); @@ -39,6 +42,12 @@ public static string ReplaceAll(this string str, IDictionary rep public static T ToEnum(this string s) where T : Enum => (T)Enum.Parse(typeof(T), s); + public static IEnumerable Enumerate(this IEnumerable enumerable) + { + foreach (var x in enumerable) + yield return x; + } + public static TOut Cast(this TIn o) where TOut: TIn => (TOut)o; public static T Cast(this object o) => (T)o; } diff --git a/SLThree/Extensions/SLTHelpers.cs b/SLThree/Extensions/SLTHelpers.cs index e1e2589..54aaa12 100644 --- a/SLThree/Extensions/SLTHelpers.cs +++ b/SLThree/Extensions/SLTHelpers.cs @@ -28,6 +28,8 @@ public static object CastToMax(this object o) public static string GetTypeString(this Type t) { + if (t.IsGenericType) + return $"{t.FullName.Substring(0, t.FullName.IndexOf('`'))}<{t.GetGenericArguments().ConvertAll(x => x.GetTypeString()).JoinIntoString(", ")}>"; if (t == type_object) return "object"; if (t == type_byte) return "u8"; if (t == type_sbyte) return "i8"; @@ -45,6 +47,18 @@ public static string GetTypeString(this Type t) else return t.FullName; } + + public static bool IsList(this Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == type_list; + } + public static bool IsDictionary(this Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == type_dict; + } + private static Type type_list = typeof(List<>); + private static Type type_dict = typeof(Dictionary<,>); + public static string GetTypeString(this string t) { if (t == "System.Object") return "object"; diff --git a/SLThree/Lexems/BaseLexem.cs b/SLThree/Lexems/BaseLexem.cs index 4cb4a27..3b745d6 100644 --- a/SLThree/Lexems/BaseLexem.cs +++ b/SLThree/Lexems/BaseLexem.cs @@ -1,5 +1,6 @@ using Pegasus.Common; using System.Diagnostics; +using System.Runtime.CompilerServices; namespace SLThree { diff --git a/SLThree/Lexems/CreatorArray.cs b/SLThree/Lexems/CreatorArray.cs new file mode 100644 index 0000000..2d0a721 --- /dev/null +++ b/SLThree/Lexems/CreatorArray.cs @@ -0,0 +1,23 @@ +using Pegasus.Common; +using SLThree.Extensions; +using System.Linq; + +namespace SLThree +{ + public class CreatorArray : BaseLexem + { + public BaseLexem[] Lexems; + + public CreatorArray(BaseLexem[] lexems, Cursor cursor) : base(cursor) + { + Lexems = lexems; + } + + public override object GetValue(ExecutionContext context) + { + return Lexems.ConvertAll(x => x.GetValue(context)).ToList(); + } + + public override string ToString() => $"[{Lexems.JoinIntoString(", ")}]"; + } +} diff --git a/SLThree/Lexems/CreatorDictionary.cs b/SLThree/Lexems/CreatorDictionary.cs new file mode 100644 index 0000000..2413d9a --- /dev/null +++ b/SLThree/Lexems/CreatorDictionary.cs @@ -0,0 +1,40 @@ +using Pegasus.Common; +using SLThree.Extensions; +using System.Collections.Generic; +using System.Linq; + +namespace SLThree +{ + public class CreatorDictionary : BaseLexem + { + public class Entry : BaseLexem + { + public BaseLexem Key; + public BaseLexem Value; + public Entry(BaseLexem key, BaseLexem value, Cursor cursor) : base(cursor) + { + Key = key; + Value = value; + } + public override object GetValue(ExecutionContext context) + { + return new KeyValuePair(Key.GetValue(context), Value.GetValue(context)); + } + public override string ToString() => $"{Key}: {Value}"; + } + + public Entry[] Entries; + + public CreatorDictionary(Entry[] entries, Cursor cursor) : base(cursor) + { + Entries = entries; + } + + public override object GetValue(ExecutionContext context) + { + return Entries.Select(x => (KeyValuePair)x.GetValue(context)).ToDictionary(x => x.Key, x => x.Value); + } + + public override string ToString() => $"{{{Entries.JoinIntoString(", ")}}}"; + } +} diff --git a/SLThree/Lexems/CreatorTuple.cs b/SLThree/Lexems/CreatorTuple.cs new file mode 100644 index 0000000..6e7fd9d --- /dev/null +++ b/SLThree/Lexems/CreatorTuple.cs @@ -0,0 +1,47 @@ +using Pegasus.Common; +using SLThree.Extensions; +using System; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace SLThree +{ + public class CreatorTuple : BaseLexem + { + public BaseLexem[] Lexems; + + public CreatorTuple(BaseLexem[] lexems, Cursor cursor) : base(cursor) + { + Lexems = lexems; + } + + public override object GetValue(ExecutionContext context) + { + return Create(Lexems.ConvertAll(x => x.GetValue(context))); + } + + public static ITuple Create(object[] objs, int index = 0) + { + switch (objs.Length - index) + { + case 0: throw new ArgumentException("Zero length array in objs"); + case 1: return new Tuple(objs[index]); + case 2: return new Tuple(objs[index], objs[index + 1]); + case 3: return new Tuple(objs[index + 0], objs[index + 1], objs[index + 2]); + case 4: return new Tuple(objs[index + 0], objs[index + 1], objs[index + 2], objs[index + 3]); + case 5: return new Tuple(objs[index + 0], objs[index + 1], objs[index + 2], objs[index + 3], objs[index + 4]); + case 6: return new Tuple(objs[index + 0], objs[index + 1], objs[index + 2], objs[index + 3], objs[index + 4], objs[index + 5]); + case 7: return new Tuple(objs[index + 0], objs[index + 1], objs[index + 2], objs[index + 3], objs[index + 4], objs[index + 5], objs[index + 6]); + default: return new Tuple(objs[index + 0], objs[index + 1], objs[index + 2], objs[index + 3], objs[index + 4], objs[index + 5], objs[index + 6], Create(objs, index + 7)); + } + } + public static object[] ToArray(ITuple tuple) + { + var ret = new object[tuple.Length]; + for (var i = 0; i < tuple.Length; i++) + ret[i] = tuple[i]; + return ret; + } + public override string ToString() => $"({Lexems.JoinIntoString(", ")})"; + } +} diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryAdd.cs b/SLThree/Lexems/Expressions/ExpressionBinaryAdd.cs index 3184e38..76151b1 100644 --- a/SLThree/Lexems/Expressions/ExpressionBinaryAdd.cs +++ b/SLThree/Lexems/Expressions/ExpressionBinaryAdd.cs @@ -13,7 +13,7 @@ public override object GetValue(ExecutionContext context) { object left; object right; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); right = Right.GetValue(context); @@ -25,8 +25,8 @@ public override object GetValue(ExecutionContext context) } if (left is long i1) { - if (right is double d2) return i1 + d2; if (right is long i2) return i1 + i2; + if (right is double d2) return i1 + d2; } else if (left is double d1) { @@ -36,10 +36,11 @@ public override object GetValue(ExecutionContext context) } else if (left is ulong u1) { - if (right is double d2) return u1 + d2; if (right is ulong u2) return u1 + u2; + if (right is double d2) return u1 + d2; } - throw new OperatorError(this, left?.GetType(), right?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryAnd.cs b/SLThree/Lexems/Expressions/ExpressionBinaryAnd.cs new file mode 100644 index 0000000..6770f3e --- /dev/null +++ b/SLThree/Lexems/Expressions/ExpressionBinaryAnd.cs @@ -0,0 +1,19 @@ +using Pegasus.Common; + +namespace SLThree +{ + public class ExpressionBinaryAnd : ExpressionBinary + { + public override string Operator => "&&"; + public ExpressionBinaryAnd(BaseLexem left, BaseLexem right, Cursor cursor) : base(left, right, cursor) { } + public ExpressionBinaryAnd() : base() { } + public override object GetValue(ExecutionContext context) + { + object left = Left.GetValue(context); + object right = Right.GetValue(context); + if (left is bool b1 && right is bool b2) return b1 && b2; + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; + } + } +} diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryAssign.cs b/SLThree/Lexems/Expressions/ExpressionBinaryAssign.cs index 653b770..1f97a6c 100644 --- a/SLThree/Lexems/Expressions/ExpressionBinaryAssign.cs +++ b/SLThree/Lexems/Expressions/ExpressionBinaryAssign.cs @@ -20,20 +20,26 @@ public override object GetValue(ExecutionContext context) } else { - if (Left is MemberAccess memberAccess) + if (Left is NameLexem nl) + { + variable_index = context.LocalVariables.SetValue(nl.Name, right); + is_namelexem = true; + counted_invoked = context; + return right; + } + else if (Left is MemberAccess memberAccess) { memberAccess.SetValue(context, right); return right; } - else if (Left is NameLexem nl) + else if (Left is IndexLexem indexLexem) { - variable_index = context.LocalVariables.SetValue(nl.Name, right); - is_namelexem = true; - counted_invoked = context; + indexLexem.SetValue(context, right); return right; } } - throw new OperatorError(this, Left?.GetType(), right?.GetType()); + context.Errors.Add(new OperatorError(this, Left?.GetType(), right?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryBitAnd.cs b/SLThree/Lexems/Expressions/ExpressionBinaryBitAnd.cs new file mode 100644 index 0000000..0b5974a --- /dev/null +++ b/SLThree/Lexems/Expressions/ExpressionBinaryBitAnd.cs @@ -0,0 +1,38 @@ +using Pegasus.Common; +using SLThree.Extensions; + +namespace SLThree +{ + public class ExpressionBinaryBitAnd : ExpressionBinary + { + public override string Operator => "&"; + public ExpressionBinaryBitAnd(BaseLexem left, BaseLexem right, Cursor cursor) : base(left, right, cursor) { } + public ExpressionBinaryBitAnd() : base() { } + public override object GetValue(ExecutionContext context) + { + object left; + object right; + if (context.fimp) + { + left = Left.GetValue(context); + right = Right.GetValue(context); + } + else + { + left = Left.GetValue(context).CastToMax(); + right = Right.GetValue(context).CastToMax(); + } + if (left is bool b1 && right is bool b2) return b1 & b2; + else if (left is long i1) + { + if (right is long i2) return i1 & i2; + } + else if (left is ulong u1) + { + if (right is ulong u2) return u1 & u2; + } + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; + } + } +} diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryBitOr.cs b/SLThree/Lexems/Expressions/ExpressionBinaryBitOr.cs new file mode 100644 index 0000000..76b7e65 --- /dev/null +++ b/SLThree/Lexems/Expressions/ExpressionBinaryBitOr.cs @@ -0,0 +1,38 @@ +using Pegasus.Common; +using SLThree.Extensions; + +namespace SLThree +{ + public class ExpressionBinaryBitOr : ExpressionBinary + { + public override string Operator => "|"; + public ExpressionBinaryBitOr(BaseLexem left, BaseLexem right, Cursor cursor) : base(left, right, cursor) { } + public ExpressionBinaryBitOr() : base() { } + public override object GetValue(ExecutionContext context) + { + object left; + object right; + if (context.fimp) + { + left = Left.GetValue(context); + right = Right.GetValue(context); + } + else + { + left = Left.GetValue(context).CastToMax(); + right = Right.GetValue(context).CastToMax(); + } + if (left is bool b1 && right is bool b2) return b1 | b2; + else if (left is long i1) + { + if (right is long i2) return i1 | i2; + } + else if (left is ulong u1) + { + if (right is ulong u2) return u1 | u2; + } + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; + } + } +} diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryBitXor.cs b/SLThree/Lexems/Expressions/ExpressionBinaryBitXor.cs new file mode 100644 index 0000000..5a047ea --- /dev/null +++ b/SLThree/Lexems/Expressions/ExpressionBinaryBitXor.cs @@ -0,0 +1,38 @@ +using Pegasus.Common; +using SLThree.Extensions; + +namespace SLThree +{ + public class ExpressionBinaryBitXor : ExpressionBinary + { + public override string Operator => "^"; + public ExpressionBinaryBitXor(BaseLexem left, BaseLexem right, Cursor cursor) : base(left, right, cursor) { } + public ExpressionBinaryBitXor() : base() { } + public override object GetValue(ExecutionContext context) + { + object left; + object right; + if (context.fimp) + { + left = Left.GetValue(context); + right = Right.GetValue(context); + } + else + { + left = Left.GetValue(context).CastToMax(); + right = Right.GetValue(context).CastToMax(); + } + if (left is bool b1 && right is bool b2) return b1 ^ b2; + else if (left is long i1) + { + if (right is long i2) return i1 ^ i2; + } + else if (left is ulong u1) + { + if (right is ulong u2) return u1 ^ u2; + } + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; + } + } +} diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryDivide.cs b/SLThree/Lexems/Expressions/ExpressionBinaryDivide.cs index c8ebb14..b226dad 100644 --- a/SLThree/Lexems/Expressions/ExpressionBinaryDivide.cs +++ b/SLThree/Lexems/Expressions/ExpressionBinaryDivide.cs @@ -13,7 +13,7 @@ public override object GetValue(ExecutionContext context) { object left; object right; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); right = Right.GetValue(context); @@ -25,8 +25,8 @@ public override object GetValue(ExecutionContext context) } if (left is long i1) { - if (right is double d2) return i1 / d2; if (right is long i2) return i1 / i2; + if (right is double d2) return i1 / d2; } else if (left is double d1) { @@ -36,10 +36,11 @@ public override object GetValue(ExecutionContext context) } else if (left is ulong u1) { - if (right is double d2) return u1 / d2; if (right is ulong u2) return u1 / u2; + if (right is double d2) return u1 / d2; } - throw new OperatorError(this, left?.GetType(), right?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryEquals.cs b/SLThree/Lexems/Expressions/ExpressionBinaryEquals.cs index a9fafb9..fcaaf6d 100644 --- a/SLThree/Lexems/Expressions/ExpressionBinaryEquals.cs +++ b/SLThree/Lexems/Expressions/ExpressionBinaryEquals.cs @@ -13,7 +13,7 @@ public override object GetValue(ExecutionContext context) { object left; object right; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); right = Right.GetValue(context); @@ -25,8 +25,8 @@ public override object GetValue(ExecutionContext context) } if (left is long i1) { - if (right is double d2) return i1 == d2; if (right is long i2) return i1 == i2; + if (right is double d2) return i1 == d2; } else if (left is double d1) { @@ -36,12 +36,13 @@ public override object GetValue(ExecutionContext context) } else if (left is ulong u1) { - if (right is double d2) return u1 == d2; if (right is ulong u2) return u1 == u2; + if (right is double d2) return u1 == d2; } - if (!context.ForbidImplicit) + if (!context.fimp) return (left as IComparable).CompareTo(right) == 0; - throw new OperatorError(this, left?.GetType(), right?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryGreaterThan.cs b/SLThree/Lexems/Expressions/ExpressionBinaryGreaterThan.cs index 8fc7eb3..9a84334 100644 --- a/SLThree/Lexems/Expressions/ExpressionBinaryGreaterThan.cs +++ b/SLThree/Lexems/Expressions/ExpressionBinaryGreaterThan.cs @@ -13,7 +13,7 @@ public override object GetValue(ExecutionContext context) { object left; object right; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); right = Right.GetValue(context); @@ -25,8 +25,8 @@ public override object GetValue(ExecutionContext context) } if (left is long i1) { - if (right is double d2) return i1 > d2; if (right is long i2) return i1 > i2; + if (right is double d2) return i1 > d2; } else if (left is double d1) { @@ -36,12 +36,13 @@ public override object GetValue(ExecutionContext context) } else if (left is ulong u1) { - if (right is double d2) return u1 > d2; if (right is ulong u2) return u1 > u2; + if (right is double d2) return u1 > d2; } - if (!context.ForbidImplicit) + if (!context.fimp) return (left as IComparable).CompareTo(right) > 0; - throw new OperatorError(this, left?.GetType(), right?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryGreaterThanEquals.cs b/SLThree/Lexems/Expressions/ExpressionBinaryGreaterThanEquals.cs index 33fd2a8..24fdaf8 100644 --- a/SLThree/Lexems/Expressions/ExpressionBinaryGreaterThanEquals.cs +++ b/SLThree/Lexems/Expressions/ExpressionBinaryGreaterThanEquals.cs @@ -13,7 +13,7 @@ public override object GetValue(ExecutionContext context) { object left; object right; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); right = Right.GetValue(context); @@ -25,8 +25,8 @@ public override object GetValue(ExecutionContext context) } if (left is long i1) { - if (right is double d2) return i1 >= d2; if (right is long i2) return i1 >= i2; + if (right is double d2) return i1 >= d2; } else if (left is double d1) { @@ -36,12 +36,13 @@ public override object GetValue(ExecutionContext context) } else if (left is ulong u1) { - if (right is double d2) return u1 >= d2; if (right is ulong u2) return u1 >= u2; + if (right is double d2) return u1 >= d2; } - if (!context.ForbidImplicit) + if (!context.fimp) return (left as IComparable).CompareTo(right) >= 0; - throw new OperatorError(this, left?.GetType(), right?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryLessThan.cs b/SLThree/Lexems/Expressions/ExpressionBinaryLessThan.cs index 0b8ec92..22fc436 100644 --- a/SLThree/Lexems/Expressions/ExpressionBinaryLessThan.cs +++ b/SLThree/Lexems/Expressions/ExpressionBinaryLessThan.cs @@ -13,7 +13,7 @@ public override object GetValue(ExecutionContext context) { object left; object right; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); right = Right.GetValue(context); @@ -25,8 +25,8 @@ public override object GetValue(ExecutionContext context) } if (left is long i1) { - if (right is double d2) return i1 < d2; if (right is long i2) return i1 < i2; + if (right is double d2) return i1 < d2; } else if (left is double d1) { @@ -36,12 +36,13 @@ public override object GetValue(ExecutionContext context) } else if (left is ulong u1) { - if (right is double d2) return u1 < d2; if (right is ulong u2) return u1 < u2; + if (right is double d2) return u1 < d2; } - if (!context.ForbidImplicit) + if (!context.fimp) return (left as IComparable).CompareTo(right) < 0; - throw new OperatorError(this, left?.GetType(), right?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryLessThanEquals.cs b/SLThree/Lexems/Expressions/ExpressionBinaryLessThanEquals.cs index e4de528..6c0930e 100644 --- a/SLThree/Lexems/Expressions/ExpressionBinaryLessThanEquals.cs +++ b/SLThree/Lexems/Expressions/ExpressionBinaryLessThanEquals.cs @@ -13,7 +13,7 @@ public override object GetValue(ExecutionContext context) { object left; object right; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); right = Right.GetValue(context); @@ -25,8 +25,8 @@ public override object GetValue(ExecutionContext context) } if (left is long i1) { - if (right is double d2) return i1 <= d2; if (right is long i2) return i1 <= i2; + if (right is double d2) return i1 <= d2; } else if (left is double d1) { @@ -36,12 +36,13 @@ public override object GetValue(ExecutionContext context) } else if (left is ulong u1) { - if (right is double d2) return u1 <= d2; if (right is ulong u2) return u1 <= u2; + if (right is double d2) return u1 <= d2; } - if (!context.ForbidImplicit) + if (!context.fimp) return (left as IComparable).CompareTo(right) <= 0; - throw new OperatorError(this, left?.GetType(), right?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryMod.cs b/SLThree/Lexems/Expressions/ExpressionBinaryMod.cs index cbe6cb7..f4f12e3 100644 --- a/SLThree/Lexems/Expressions/ExpressionBinaryMod.cs +++ b/SLThree/Lexems/Expressions/ExpressionBinaryMod.cs @@ -12,7 +12,7 @@ public override object GetValue(ExecutionContext context) { object left; object right; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); right = Right.GetValue(context); @@ -24,8 +24,8 @@ public override object GetValue(ExecutionContext context) } if (left is long i1) { - if (right is double d2) return i1 % d2; if (right is long i2) return i1 % i2; + if (right is double d2) return i1 % d2; } else if (left is double d1) { @@ -35,10 +35,11 @@ public override object GetValue(ExecutionContext context) } else if (left is ulong u1) { - if (right is double d2) return u1 % d2; if (right is ulong u2) return u1 % u2; + if (right is double d2) return u1 % d2; } - throw new OperatorError(this, left?.GetType(), right?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryMultiply.cs b/SLThree/Lexems/Expressions/ExpressionBinaryMultiply.cs index 486a973..1690592 100644 --- a/SLThree/Lexems/Expressions/ExpressionBinaryMultiply.cs +++ b/SLThree/Lexems/Expressions/ExpressionBinaryMultiply.cs @@ -13,7 +13,7 @@ public override object GetValue(ExecutionContext context) { object left; object right; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); right = Right.GetValue(context); @@ -25,8 +25,8 @@ public override object GetValue(ExecutionContext context) } if (left is long i1) { - if (right is double d2) return i1 * d2; if (right is long i2) return i1 * i2; + if (right is double d2) return i1 * d2; } else if (left is double d1) { @@ -36,10 +36,11 @@ public override object GetValue(ExecutionContext context) } else if (left is ulong u1) { - if (right is double d2) return u1 * d2; if (right is ulong u2) return u1 * u2; + if (right is double d2) return u1 * d2; } - throw new OperatorError(this, left?.GetType(), right?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryOr.cs b/SLThree/Lexems/Expressions/ExpressionBinaryOr.cs new file mode 100644 index 0000000..04bea9b --- /dev/null +++ b/SLThree/Lexems/Expressions/ExpressionBinaryOr.cs @@ -0,0 +1,19 @@ +using Pegasus.Common; + +namespace SLThree +{ + public class ExpressionBinaryOr: ExpressionBinary + { + public override string Operator => "||"; + public ExpressionBinaryOr(BaseLexem left, BaseLexem right, Cursor cursor) : base(left, right, cursor) { } + public ExpressionBinaryOr() : base() { } + public override object GetValue(ExecutionContext context) + { + object left = Left.GetValue(context); + object right = Right.GetValue(context); + if (left is bool b1 && right is bool b2) return b1 || b2; + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; + } + } +} diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryRem.cs b/SLThree/Lexems/Expressions/ExpressionBinaryRem.cs index df7a165..13e79ff 100644 --- a/SLThree/Lexems/Expressions/ExpressionBinaryRem.cs +++ b/SLThree/Lexems/Expressions/ExpressionBinaryRem.cs @@ -18,7 +18,7 @@ public override object GetValue(ExecutionContext context) { object left; object right; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); right = Right.GetValue(context); @@ -30,8 +30,8 @@ public override object GetValue(ExecutionContext context) } if (left is long i1) { - if (right is double d2) return i1 - d2; if (right is long i2) return i1 - i2; + if (right is double d2) return i1 - d2; } else if (left is double d1) { @@ -41,10 +41,11 @@ public override object GetValue(ExecutionContext context) } else if (left is ulong u1) { - if (right is double d2) return u1 - d2; if (right is ulong u2) return u1 - u2; + if (right is double d2) return u1 - d2; } - throw new OperatorError(this, left?.GetType(), right?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionBinaryUnequals.cs b/SLThree/Lexems/Expressions/ExpressionBinaryUnequals.cs index 439fb07..03d5931 100644 --- a/SLThree/Lexems/Expressions/ExpressionBinaryUnequals.cs +++ b/SLThree/Lexems/Expressions/ExpressionBinaryUnequals.cs @@ -13,7 +13,7 @@ public override object GetValue(ExecutionContext context) { object left; object right; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); right = Right.GetValue(context); @@ -25,8 +25,8 @@ public override object GetValue(ExecutionContext context) } if (left is long i1) { - if (right is double d2) return i1 != d2; if (right is long i2) return i1 != i2; + if (right is double d2) return i1 != d2; } else if (left is double d1) { @@ -36,12 +36,13 @@ public override object GetValue(ExecutionContext context) } else if (left is ulong u1) { - if (right is double d2) return u1 != d2; if (right is ulong u2) return u1 != u2; + if (right is double d2) return u1 != d2; } - if (!context.ForbidImplicit) + if (!context.fimp) return (left as IComparable).CompareTo(right) != 0; - throw new OperatorError(this, left?.GetType(), right?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType(), right?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionTernary.cs b/SLThree/Lexems/Expressions/ExpressionTernary.cs new file mode 100644 index 0000000..4444d2f --- /dev/null +++ b/SLThree/Lexems/Expressions/ExpressionTernary.cs @@ -0,0 +1,30 @@ +using Pegasus.Common; + +namespace SLThree +{ + public class ExpressionTernary : BaseLexem + { + public string Operator => "?:"; + + public BaseLexem Condition; + public BaseLexem Left; + public BaseLexem Right; + + public ExpressionTernary(BaseLexem cond, BaseLexem left, BaseLexem right, Cursor cursor) : base(cursor) + { + Condition = cond; + Left = left; + Right = right; + } + public ExpressionTernary() : base(default) { } + public override object GetValue(ExecutionContext context) + { + var cond = Condition.GetValue(context); + var left = Left.GetValue(context); + var right = Right.GetValue(context); + return (bool)cond ? left : right; + } + + public override string ToString() => $"{Condition} ? {Left} : {Right}"; + } +} diff --git a/SLThree/Lexems/Expressions/ExpressionUnaryAdd.cs b/SLThree/Lexems/Expressions/ExpressionUnaryAdd.cs index bb11dbe..5d9da2c 100644 --- a/SLThree/Lexems/Expressions/ExpressionUnaryAdd.cs +++ b/SLThree/Lexems/Expressions/ExpressionUnaryAdd.cs @@ -11,7 +11,7 @@ public ExpressionUnaryAdd() : base() { } public override object GetValue(ExecutionContext context) { object left; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); } @@ -25,7 +25,8 @@ public override object GetValue(ExecutionContext context) case ulong v: return +v; case double v: return +v; } - throw new OperatorError(this, left?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionUnaryBitNot.cs b/SLThree/Lexems/Expressions/ExpressionUnaryBitNot.cs index d2227f2..ab92954 100644 --- a/SLThree/Lexems/Expressions/ExpressionUnaryBitNot.cs +++ b/SLThree/Lexems/Expressions/ExpressionUnaryBitNot.cs @@ -11,7 +11,7 @@ public ExpressionUnaryBitNot() : base() { } public override object GetValue(ExecutionContext context) { object left; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); } @@ -24,7 +24,8 @@ public override object GetValue(ExecutionContext context) case long v: return ~v; case ulong v: return ~v; } - throw new OperatorError(this, left?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionUnaryNot.cs b/SLThree/Lexems/Expressions/ExpressionUnaryNot.cs index 291ed82..0165add 100644 --- a/SLThree/Lexems/Expressions/ExpressionUnaryNot.cs +++ b/SLThree/Lexems/Expressions/ExpressionUnaryNot.cs @@ -11,7 +11,7 @@ public ExpressionUnaryNot() : base() { } public override object GetValue(ExecutionContext context) { object left; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); } @@ -23,7 +23,8 @@ public override object GetValue(ExecutionContext context) { case bool b: return !b; } - throw new OperatorError(this, left?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/ExpressionUnaryRem.cs b/SLThree/Lexems/Expressions/ExpressionUnaryRem.cs index a8fe4d2..2999122 100644 --- a/SLThree/Lexems/Expressions/ExpressionUnaryRem.cs +++ b/SLThree/Lexems/Expressions/ExpressionUnaryRem.cs @@ -11,7 +11,7 @@ public ExpressionUnaryRem() : base() { } public override object GetValue(ExecutionContext context) { object left; - if (context.ForbidImplicit) + if (context.fimp) { left = Left.GetValue(context); } @@ -24,7 +24,8 @@ public override object GetValue(ExecutionContext context) case long v: return -v; case double v: return -v; } - throw new OperatorError(this, left?.GetType()); + context.Errors.Add(new OperatorError(this, left?.GetType())); + return null; } } } diff --git a/SLThree/Lexems/Expressions/MemberAccess.cs b/SLThree/Lexems/Expressions/MemberAccess.cs index eda3d5c..fc079af 100644 --- a/SLThree/Lexems/Expressions/MemberAccess.cs +++ b/SLThree/Lexems/Expressions/MemberAccess.cs @@ -16,6 +16,7 @@ public ClassAccess(Type name) { Name = name; } + public override string ToString() => $"access to {Name.GetTypeString()}"; } public override string Operator => "."; @@ -42,6 +43,9 @@ public object Create(ExecutionContext context) private bool counted_contextwrapcache; private string variable_name; + + private bool counted_contextwrapcache2; + private bool is_unwrap; public override object GetValue(ExecutionContext context) { var left = Left.GetValue(context); @@ -50,6 +54,11 @@ public override object GetValue(ExecutionContext context) { return (left as ExecutionContext.ContextWrap).pred.LocalVariables.GetValue(variable_name).Item1; } + else if (counted_contextwrapcache2) + { + if (is_unwrap) return (left as ExecutionContext.ContextWrap).pred; + else return (Right as InvokeLexem).GetValue((left as ExecutionContext.ContextWrap).pred, (Right as InvokeLexem).Arguments.ConvertAll(x => x.GetValue(context))); + } if (left != null) { @@ -63,6 +72,12 @@ public override object GetValue(ExecutionContext context) } else if (Right is InvokeLexem invokeLexem) { + counted_contextwrapcache2 = true; + if (invokeLexem.Name?.Cast()?.Name == "unwrap" && invokeLexem.Arguments.Length == 0) + { + is_unwrap = true; + return pred.pred; + } return invokeLexem.GetValue(pred.pred, invokeLexem.Arguments.Select(x => x.GetValue(context)).ToArray()); } } @@ -80,7 +95,7 @@ public override object GetValue(ExecutionContext context) if (prop != null) return prop.GetValue(left); nest_type = type.GetNestedType(nameLexem.Name); if (nest_type != null) return new ClassAccess(nest_type); - throw new RuntimeError($"Name {nameLexem.Name} not found in {type.Name.GetTypeString()}", SourceContext); + throw new RuntimeError($"Name \"{nameLexem.Name}\" not found in {type.GetTypeString()}", SourceContext); } else if (Right is InvokeLexem invokeLexem) { diff --git a/SLThree/Lexems/IndexLexem.cs b/SLThree/Lexems/IndexLexem.cs new file mode 100644 index 0000000..898838b --- /dev/null +++ b/SLThree/Lexems/IndexLexem.cs @@ -0,0 +1,103 @@ +using Pegasus.Common; +using SLThree.Extensions; +using System.Collections; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace SLThree +{ + public class IndexLexem : BaseLexem + { + public BaseLexem Lexem; + public BaseLexem[] Arguments; + + public IndexLexem(BaseLexem lexem, BaseLexem[] arguments, Cursor cursor) : base(cursor) + { + Lexem = lexem; + Arguments = arguments; + } + + private int Mode = 0; // 1 - array, 2 - list, 3 - tuple, 4 - any + private PropertyInfo PropertyInfo; + + private void CalcMode(object o) + { + var type = o.GetType(); + Mode = type.IsArray ? 1 : 0; + if (Mode == 0) + { + Mode = type.IsList() ? 1 : 0; + if (Mode == 0) + { + Mode = type.GetInterfaces().Any(x => x == typeof(ITuple)) ? 3 : 0; + if (Mode == 0) + { + while (PropertyInfo == null && type != null) + { + PropertyInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(x => x.GetIndexParameters().Length == Arguments.Length); + if (PropertyInfo == null) type = type.BaseType; + } + if (PropertyInfo == null) + { + Mode = int.MaxValue; + } + else Mode = 4; + } + } + } + } + + public override object GetValue(ExecutionContext context) + { + var o = Lexem.GetValue(context); + + if (o == null) return null; + + if (Mode == 0) + { + CalcMode(o); + } + + switch (Mode) + { + case 1: + { + return o.Cast()[context.fimp ? Arguments[0].GetValue(context).Cast() : Arguments[0].GetValue(context).CastToType(typeof(int)).Cast()]; + } + case 3: + { + return o.Cast()[context.fimp ? Arguments[0].GetValue(context).Cast() : Arguments[0].GetValue(context).CastToType(typeof(int)).Cast()]; + } + case 4: return PropertyInfo.GetValue(o, Arguments.ConvertAll(x => x.GetValue(context))); + } + return null; + } + + public object SetValue(ExecutionContext context, object value) + { + var o = Lexem.GetValue(context); + + if (o == null) return null; + + if (Mode == 0) + { + CalcMode(o); + } + + switch (Mode) + { + case 1: + { + return o.Cast()[context.fimp ? Arguments[0].GetValue(context).Cast() : Arguments[0].GetValue(context).CastToType(typeof(int)).Cast()] = value; + } + case 4: + PropertyInfo.SetValue(o, value, Arguments.ConvertAll(x => x.GetValue(context))); + break; + } + return value; + } + + public override string ToString() => $"{Lexem}[{Arguments.JoinIntoString(", ")}]"; + } +} diff --git a/SLThree/Lexems/InvokeLexem.cs b/SLThree/Lexems/InvokeLexem.cs index 7aa9b60..46051aa 100644 --- a/SLThree/Lexems/InvokeLexem.cs +++ b/SLThree/Lexems/InvokeLexem.cs @@ -29,21 +29,22 @@ public object GetValue(ExecutionContext context, object[] args) get_name = Name.ToString().Replace(" ", ""); get_counted_name = true; } + var o = context.LocalVariables.GetValue(get_name).Item1; if (o == null) throw new RuntimeError($"Method {get_name}(_) not found", SourceContext); - if (o is BaseLexem bl) return bl.GetValue(context); + if (o is Method method) + { + if (method.ParamNames.Length != args.Length) throw new RuntimeError("Call with wrong arguments count", SourceContext); + return method.GetValue(context, args); + } else if (o is MethodInfo mi) { if (!mi.IsStatic) return mi.Invoke(args[0], args.Skip(1).ToArray()); else return mi.Invoke(null, args); } - else if (o is Method method) - { - if (method.ParamNames.Length != args.Length) throw new RuntimeError("Call with wrong arguments count", SourceContext); - return method.GetValue(context, args); - } + else if (o is BaseLexem bl) return bl.GetValue(context); else { var type = o.GetType(); @@ -60,15 +61,20 @@ public override object GetValue(ExecutionContext context) return GetValue(context, Arguments.ConvertAll(x => x.GetValue(context))); } + private bool cached_1; + private MethodInfo founded; public object GetValue(ExecutionContext context, object obj) { var key = Name.ToString().Replace(" ", ""); + if (cached_1) return founded.Invoke(null, Arguments.ConvertAll(x => x.GetValue(context))); + if (obj is MemberAccess.ClassAccess ca) { - return ca.Name.GetMethods(BindingFlags.Public | BindingFlags.Static) - .FirstOrDefault(x => x.Name == key && x.GetParameters().Length == Arguments.Length) - .Invoke(null, Arguments.ConvertAll(x => x.GetValue(context))); + founded = ca.Name.GetMethods(BindingFlags.Public | BindingFlags.Static) + .FirstOrDefault(x => x.Name == key && x.GetParameters().Length == Arguments.Length); + cached_1 = true; + return founded.Invoke(null, Arguments.ConvertAll(x => x.GetValue(context))); } else if (obj != null) { diff --git a/SLThree/Lexems/LambdaLexem.cs b/SLThree/Lexems/LambdaLexem.cs index 811e552..5b4f796 100644 --- a/SLThree/Lexems/LambdaLexem.cs +++ b/SLThree/Lexems/LambdaLexem.cs @@ -15,6 +15,8 @@ public LambdaLexem(InvokeLexem invokeLexem, StatementListStatement statements, I Left = invokeLexem; Right = statements; Modificators = modificators; + var many = Modificators.GroupBy(x => x).FirstOrDefault(x => x.Count() > 1); + if (many != null) throw new SyntaxError($"Repeated modifier \"{many.First()}\"", cursor); } public override string ToString() => $"{Left} => {Right}"; @@ -29,12 +31,10 @@ public override object GetValue(ExecutionContext context) Name = "anon_method", ParamNames = Left.Arguments.Select(x => (x as NameLexem).Name).ToArray(), Statements = Right, - IsImplicit = Modificators.Contains("implicit") + imp = Modificators.Contains("implicit") }; } return method; } - - public string Operator => "=>"; } } diff --git a/SLThree/Lexems/Literals/LinqLiteral.cs b/SLThree/Lexems/Literals/LinqLiteral.cs new file mode 100644 index 0000000..58c96df --- /dev/null +++ b/SLThree/Lexems/Literals/LinqLiteral.cs @@ -0,0 +1,13 @@ +using Pegasus.Common; + +namespace SLThree +{ + public class LinqLiteral : BaseLexem + { + public LinqLiteral(Cursor cursor) : base(cursor) { } + + public override string ToString() => "linq"; + + public override object GetValue(ExecutionContext context) => Linq.Linq.LinqAccess; + } +} diff --git a/SLThree/Lexems/Literals/Literal.cs b/SLThree/Lexems/Literals/Literal.cs index da12d64..fa3131d 100644 --- a/SLThree/Lexems/Literals/Literal.cs +++ b/SLThree/Lexems/Literals/Literal.cs @@ -3,9 +3,9 @@ namespace SLThree { - public partial class Literal : BaseLexem + public class Literal : BaseLexem { - public T Value; + public object Value; public Literal() : this(default, default) { } public Literal(T value, Cursor cursor) : base(cursor) { diff --git a/SLThree/Linq/Linq.cs b/SLThree/Linq/Linq.cs new file mode 100644 index 0000000..0febcc4 --- /dev/null +++ b/SLThree/Linq/Linq.cs @@ -0,0 +1,76 @@ +using SLThree.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Remoting.Messaging; +using System.Text; +using System.Threading.Tasks; + +namespace SLThree.Linq +{ + public static class Linq + { + internal static MemberAccess.ClassAccess LinqAccess = new MemberAccess.ClassAccess(typeof(Linq)); +#pragma warning disable IDE1006 // Стили именования + public static IEnumerable range(long end) + { + for (var i = 0; i < end; i += 1) + yield return i; + } + public static IEnumerable range(long start, long end) + { + for (var i = start; i < end; i += 1) + yield return i; + } + public static IEnumerable range(long start, long end, long step) + { + for (var i = start; i < end; i += step) + yield return i; + } + + public static object max(IEnumerable objects, Method method, ExecutionContext context) + { + return objects.Max(x => method.GetValue(context, new object[] { x })); + } + public static object max(IEnumerable objects, Method method) + { + return objects.Max(x => method.GetValue(new object[] { x })); + } + public static object max(IEnumerable objects) => objects.Max(); + + public static object min(IEnumerable objects, Method method, ExecutionContext context) + { + return objects.Min(x => method.GetValue(context, new object[] { x })); + } + public static object min(IEnumerable objects, Method method) + { + return objects.Min(x => method.GetValue(new object[] { x })); + } + public static object min(IEnumerable objects) => objects.Min(); + + public static IEnumerable map(IEnumerable objects, Method method, ExecutionContext context) + { + return objects.Select(x => method.GetValue(context, new object[] { x })); + } + public static IEnumerable map(IEnumerable objects, Method method) + { + return objects.Select(x => method.GetValue(new object[] { x })); + } + + public static IEnumerable filter(IEnumerable objects, Method method, ExecutionContext context) + { + return objects.Where(x => method.GetValue(context, new object[] { x }).Cast()); + } + public static IEnumerable filter(IEnumerable objects, Method method) + { + return objects.Where(x => method.GetValue(new object[] { x }).Cast()); + } + + public static List tolist(IEnumerable objects) => objects.ToList(); + public static object[] toarray(IEnumerable objects) => objects.ToArray(); + + public static string jts(IEnumerable objects, string str) => objects.JoinIntoString(str); + public static string jts(IEnumerable objects) => objects.JoinIntoString(" "); +#pragma warning restore IDE1006 // Стили именования + } +} diff --git a/SLThree/LocalVariablesContainer.cs b/SLThree/LocalVariablesContainer.cs index 93d7845..bab6cc7 100644 --- a/SLThree/LocalVariablesContainer.cs +++ b/SLThree/LocalVariablesContainer.cs @@ -11,6 +11,7 @@ public LocalVariablesContainer() } + internal int current = 0; internal object[] Variables = new object[8]; public Dictionary NamedIdenificators = new Dictionary(); @@ -33,6 +34,7 @@ private void Expand() public void FillArguments(Method method, object[] args) { if (Variables.Length <= args.Length) Variables = new object[8 + args.Length + Variables.Length]; + if (current <= args.Length) current = args.Length; args.CopyTo(Variables, 0); for (var i = 0; i < args.Length; i++) NamedIdenificators[method.ParamNames[i]] = i; @@ -48,11 +50,10 @@ public int SetValue(string name, object value) } else { - if (NamedIdenificators.Count >= Variables.Length) Expand(); - var count = NamedIdenificators.Count; - Variables[count] = value; - NamedIdenificators[name] = count; - return count; + if (current >= Variables.Length) Expand(); + Variables[current] = value; + NamedIdenificators[name] = current; + return current++; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/SLThree/Method.cs b/SLThree/Method.cs index b5f46fe..600695b 100644 --- a/SLThree/Method.cs +++ b/SLThree/Method.cs @@ -14,7 +14,7 @@ public class Method public string[] ParamNames; public StatementListStatement Statements; - public bool IsImplicit = false; + public bool imp = false; public override string ToString() => $"_ {Name}({ParamNames.ConvertAll(x => "_").JoinIntoString(", ")})"; @@ -33,7 +33,7 @@ public ExecutionContext GetExecutionContext(object[] arguments, ExecutionContext } ret.PreviousContext = context; ret.LocalVariables.FillArguments(this, arguments); - ret.ForbidImplicit = !IsImplicit; + ret.fimp = !imp; return ret; } diff --git a/SLThree/Properties/AssemblyInfo.cs b/SLThree/Properties/AssemblyInfo.cs index d9f628a..5565a91 100644 --- a/SLThree/Properties/AssemblyInfo.cs +++ b/SLThree/Properties/AssemblyInfo.cs @@ -41,10 +41,10 @@ public static class SLTVersion { public const string Major = "0"; //vh - public const string Minor = "2"; //vh + public const string Minor = "3"; //vh public const string Build = "0"; //vh - public const string Revision = "592"; //vh - public const long LastUpdate = 638340102431703149; //vh + public const string Revision = "845"; //vh + public const long LastUpdate = 638348783847449980; //vh public const string Version = Major + "." + Minor + "." + Build + "." + Revision; public const string VersionWithoutRevision = Major + "." + Minor + "." + Build; @@ -78,7 +78,7 @@ static SLTVersion() } } - public static string Edition { get; } = "Unwrap the wrap"; + public static string Edition { get; } = "Third time is lucky"; public static string GetTitle() { diff --git a/SLThree/SLThree.csproj b/SLThree/SLThree.csproj index ec17448..d95dba1 100644 --- a/SLThree/SLThree.csproj +++ b/SLThree/SLThree.csproj @@ -50,8 +50,16 @@ + + + + + + + + @@ -73,6 +81,7 @@ + @@ -82,6 +91,7 @@ + @@ -92,6 +102,8 @@ + + @@ -103,6 +115,7 @@ + @@ -111,12 +124,9 @@ - + - - - @@ -125,6 +135,9 @@ + + + diff --git a/SLThree/SourceContext.cs b/SLThree/SourceContext.cs index 701da6f..f7b15f4 100644 --- a/SLThree/SourceContext.cs +++ b/SLThree/SourceContext.cs @@ -20,6 +20,6 @@ public SourceContext(Cursor cursor) Filename = cursor.FileName; } - public override string ToString() => $"{Line}:{Column}{(string.IsNullOrEmpty(Filename) ? "" : $"in {Filename}")}"; + public override string ToString() => $"{Line}:{Column}{(string.IsNullOrEmpty(Filename) ? "" : $" in {Filename}")}"; } } diff --git a/SLThree/Statements/ConditionStatement.cs b/SLThree/Statements/ConditionStatement.cs index 8462cca..5c65380 100644 --- a/SLThree/Statements/ConditionStatement.cs +++ b/SLThree/Statements/ConditionStatement.cs @@ -2,6 +2,7 @@ using SLThree.Extensions; using System; using System.Collections.Generic; +using System.Diagnostics.PerformanceData; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -11,21 +12,34 @@ namespace SLThree public class ConditionStatement : BaseStatement { public BaseLexem Condition { get; set; } - public BaseStatement TrueBlock { get; set; } - public BaseStatement FalseBlock { get; set; } + public BaseStatement[] Body { get; set; } - public ConditionStatement(BaseLexem condition, BaseStatement trueBlock, BaseStatement falseBlock, Cursor cursor) : base(cursor) + public ConditionStatement(BaseLexem condition, StatementListStatement trueBlock, StatementListStatement falseBlock, Cursor cursor) : base(cursor) { Condition = condition; - TrueBlock = trueBlock; - FalseBlock = falseBlock; + count = trueBlock.Statements.Count + falseBlock.Statements.Count; + Body = new BaseStatement[count]; + trueBlock.Statements.CopyTo(Body, 0); + falsestart = trueBlock.Statements.Count; + falseBlock.Statements.CopyTo(Body, falsestart); } + private int count; + private int falsestart; - public override string ToString() => $"if ({Condition}) {{{TrueBlock}}}{(FalseBlock)}"; + public override string ToString() => $"if ({Condition}) {{{Body}}}"; public override object GetValue(ExecutionContext context) - => Condition.GetValue(context).Cast() - ? TrueBlock.GetValue(context) - : FalseBlock.GetValue(context); + { + var ret = default(object); + var cond = (bool)Condition.GetValue(context); + var start = cond ? 0 : falsestart; + var end = cond ? falsestart : count; + for (var i = start; i < end; i++) + { + ret = Body[i].GetValue(context); + if (context.Returned || context.Broken || context.Continued) break; + } + return ret; + } } } diff --git a/SLThree/Statements/ForeachLoopStatement.cs b/SLThree/Statements/ForeachLoopStatement.cs new file mode 100644 index 0000000..fe4e1ff --- /dev/null +++ b/SLThree/Statements/ForeachLoopStatement.cs @@ -0,0 +1,51 @@ +using Pegasus.Common; +using SLThree.Extensions; +using System.Collections; +using System.Linq; + +namespace SLThree +{ + public class ForeachLoopStatement : BaseStatement + { + public NameLexem Name { get; set; } + public BaseLexem Iterator { get; set; } + public BaseStatement[] CycleBody { get; set; } + + public ForeachLoopStatement(NameLexem name, BaseLexem iterator, StatementListStatement cycleBody, Cursor cursor) : base(cursor) + { + Name = name; + Iterator = iterator; + CycleBody = cycleBody.Statements.ToArray(); + count = CycleBody.Length; + } + + private ExecutionContext last_context; + private int variable_index; + private int count; + public override object GetValue(ExecutionContext context) + { + var iterator = Iterator.GetValue(context).Cast(); + if (context != last_context) + { + last_context = context; + variable_index = context.LocalVariables.SetValue(Name.Name, null); + } + var ret = default(object); + context.StartCycle(); + foreach (var x in iterator) + { + context.LocalVariables.SetValue(variable_index, x); + for (var i = 0; i < count; i++) + { + ret = CycleBody[i].GetValue(context); + if (context.Returned || context.Broken) break; + if (context.Continued) continue; + } + } + context.EndCycle(); + return ret; + } + + public override string ToString() => $"foreach ({Name} in {Iterator}) {{{CycleBody}}}"; + } +} diff --git a/SLThree/Statements/StatementListStatement.cs b/SLThree/Statements/StatementListStatement.cs index a3d6b22..09d502b 100644 --- a/SLThree/Statements/StatementListStatement.cs +++ b/SLThree/Statements/StatementListStatement.cs @@ -12,10 +12,12 @@ namespace SLThree public class StatementListStatement : BaseStatement { public IList Statements; + private int count; public StatementListStatement(IList statements, Cursor cursor) : base(cursor) { Statements = statements; + count = statements.Count; } public override string ToString() => $"{Statements.Count} statements"; @@ -23,10 +25,10 @@ public StatementListStatement(IList statements, Cursor cursor) : public override object GetValue(ExecutionContext context) { var ret = default(object); - for (var i = 0; i < Statements.Count; i++) + for (var i = 0; i < count; i++) { ret = Statements[i].GetValue(context); - if (context.Returned || (context.InCycle() && (context.Broken || context.Continued))) break; + if (context.Returned || context.Broken || context.Continued) break; } return ret; } diff --git a/SLThree/Statements/WhileCycleStatement.cs b/SLThree/Statements/WhileCycleStatement.cs deleted file mode 100644 index f8ee000..0000000 --- a/SLThree/Statements/WhileCycleStatement.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Pegasus.Common; -using SLThree.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SLThree -{ - public class WhileCycleStatement : BaseStatement - { - public BaseLexem Condition { get; set; } - public BaseStatement CycleBody { get; set; } - - public WhileCycleStatement(BaseLexem condition, BaseStatement cycleBody, Cursor cursor) : base(cursor) - { - Condition = condition; - CycleBody = cycleBody; - } - - public override string ToString() => $"while ({Condition}) {{{CycleBody}}}"; - - public override object GetValue(ExecutionContext context) - { - var ret = default(object); - context.StartCycle(); - while (Condition.GetValue(context).Cast()) - { - ret = CycleBody.GetValue(context); - if (context.Returned || context.Broken) return ret; - if (context.Continued) continue; - } - context.EndCycle(); - return ret; - } - } -} diff --git a/SLThree/Statements/WhileLoopStatement.cs b/SLThree/Statements/WhileLoopStatement.cs new file mode 100644 index 0000000..c0d3732 --- /dev/null +++ b/SLThree/Statements/WhileLoopStatement.cs @@ -0,0 +1,43 @@ +using Pegasus.Common; +using SLThree.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SLThree +{ + public class WhileLoopStatement : BaseStatement + { + public BaseLexem Condition { get; set; } + public BaseStatement[] CycleBody { get; set; } + + public WhileLoopStatement(BaseLexem condition, StatementListStatement cycleBody, Cursor cursor) : base(cursor) + { + Condition = condition; + CycleBody = cycleBody.Statements.ToArray(); + count = CycleBody.Length; + } + + public override string ToString() => $"while ({Condition}) {{{CycleBody}}}"; + + private int count; + public override object GetValue(ExecutionContext context) + { + var ret = default(object); + context.StartCycle(); + while ((bool)Condition.GetValue(context)) + { + for (var i = 0; i < count; i++) + { + ret = CycleBody[i].GetValue(context); + if (context.Returned || context.Broken) break; + if (context.Continued) continue; + } + } + context.EndCycle(); + return ret; + } + } +} diff --git a/SLThree/Wrapper.cs b/SLThree/Wrapper.cs index bc6b1c1..f9212f3 100644 --- a/SLThree/Wrapper.cs +++ b/SLThree/Wrapper.cs @@ -5,16 +5,19 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.Remoting.Contexts; using System.Security.Claims; using System.Text; using System.Threading.Tasks; namespace SLThree { - public static class Wrapper where T : new() + public abstract class Wrapper { - private static Dictionary Properties = new Dictionary(); - private static Dictionary Fields = new Dictionary(); + protected static readonly Dictionary Properties = new Dictionary(); + protected static readonly Dictionary Fields = new Dictionary(); + protected static readonly Dictionary StaticProperties = new Dictionary(); + protected static readonly Dictionary StaticFields = new Dictionary(); private static PropertyInfo InjectClassname = null; #region Type Setting static Wrapper() @@ -24,7 +27,6 @@ static Wrapper() foreach (var property in props) { if (Attribute.IsDefined(property, typeof(WrapperSkipAttribute))) continue; - else if (property.SetMethod == null) continue; //else if (Attribute.IsDefined(property, typeof(WrappingInjectClassname))) InjectClassname = property; else Properties[property.Name] = property; } @@ -34,16 +36,24 @@ static Wrapper() if (Attribute.IsDefined(field, typeof(WrapperSkipAttribute))) continue; else Fields[field.Name] = field; } + var static_props = type.GetProperties(BindingFlags.Static | BindingFlags.Public); + foreach (var property in static_props) + { + if (Attribute.IsDefined(property, typeof(WrapperSkipAttribute))) continue; + //else if (Attribute.IsDefined(property, typeof(WrappingInjectClassname))) InjectClassname = property; + else StaticProperties[property.Name] = property; + } + var static_fields = type.GetFields(BindingFlags.Static | BindingFlags.Public); + foreach (var field in static_fields) + { + if (Attribute.IsDefined(field, typeof(WrapperSkipAttribute))) continue; + else StaticFields[field.Name] = field; + } } + protected internal Wrapper() { } private static Type generic_list = typeof(List).GetGenericTypeDefinition(); private static Type generic_dict = typeof(Dictionary).GetGenericTypeDefinition(); private static Type type_ituple = typeof(ITuple); - /// - /// - /// - /// Пераметр NET-типа - /// Параметр BQS-типа - /// private static bool HasRecast(Type type) { if (type.IsArray) return true; @@ -64,7 +74,7 @@ private static object[] TupleToArray(ITuple tuple) ret[i] = tuple[i]; return ret; } - private static object Cast(Type type_to_cast, object o) + protected static object UnwrapCast(Type type_to_cast, object o) { if (type_to_cast.IsArray && o is object[] obj_array) { @@ -73,7 +83,7 @@ private static object Cast(Type type_to_cast, object o) if (HasRecast(gga)) { for (var i = 0; i < obj_array.Length; i++) - ret.SetValue(Cast(gga, obj_array[i]), i); + ret.SetValue(UnwrapCast(gga, obj_array[i]), i); } else { @@ -89,7 +99,7 @@ private static object Cast(Type type_to_cast, object o) if (HasRecast(gga)) { for (var i = 0; i < obj_list.Count; i++) - ret.SetValue(Cast(gga, obj_list[i]), i); + ret.SetValue(UnwrapCast(gga, obj_list[i]), i); } else { @@ -108,7 +118,7 @@ private static object Cast(Type type_to_cast, object o) if (HasRecast(gga[0])) { for (var i = 0; i < obj_list.Count; i++) - ret.Add(Cast(gga[0], obj_list[i])); + ret.Add(UnwrapCast(gga[0], obj_list[i])); } else { @@ -124,7 +134,7 @@ private static object Cast(Type type_to_cast, object o) if (HasRecast(gga[0]) || HasRecast(gga[1])) { foreach (var x in obj_dict) - ret[Cast(gga[0], x.Key)] = Cast(gga[1], x.Value); + ret[UnwrapCast(gga[0], x.Key)] = UnwrapCast(gga[1], x.Value); } else { @@ -140,7 +150,7 @@ private static object Cast(Type type_to_cast, object o) var gga = type_to_cast.GetGenericArguments(); if (gga.Any(x => HasRecast(x))) { - var ret = type_to_cast.GetConstructor(gga).Invoke(TupleToArray(tuple).Select((x, i) => Cast(gga[0], x)).ToArray()); + var ret = type_to_cast.GetConstructor(gga).Invoke(TupleToArray(tuple).Select((x, i) => UnwrapCast(gga[0], x)).ToArray()); return ret; } else @@ -151,14 +161,217 @@ private static object Cast(Type type_to_cast, object o) } return o; } + private static object WrapCast(object o) + { + if (o == null) return null; + var type = o.GetType(); + if (type.IsArray) + { + var arr = o as Array; + var ret = new object[arr.Length]; + if (HasRecast(type.GetElementType())) + { + for (var i = 0; i < arr.Length; i++) + ret[i] = WrapCast(arr.GetValue(i)); + } + else + { + for (var i = 0; i < arr.Length; i++) + ret[i] = arr.GetValue(i); + } + return ret; + } + else if (type.IsGenericType) + { + var gt = type.GetGenericTypeDefinition(); + if (gt == generic_list) + { + var lst = o as IList; + var ret = new List(lst.Count); + var gga = gt.GetGenericArguments(); + if (HasRecast(gga[0])) + { + for (var i = 0; i < lst.Count; i++) + ret[i] = WrapCast(lst[i]); + } + else + { + for (var i = 0; i < lst.Count; i++) + ret[i] = lst[i]; + } + return ret; + + } + else if (gt == generic_dict) + { + var dct = o as IDictionary; + var ret = new Dictionary(); + var gga = gt.GetGenericArguments(); + if (HasRecast(gga[0]) || HasRecast(gga[1])) + { + foreach (var x in dct.Keys) + ret[WrapCast(x)] = WrapCast(dct[x]); + } + else + { + foreach (var x in dct.Keys) + ret[x] = dct[x]; + } + return ret; + } + } + else if (type.GetInterfaces().Contains(type_ituple)) + { + //todo supporting any-size tuples + } + return o; + } #endregion + public static ExecutionContext Wrap(T obj) + { + var ret = new ExecutionContext(); + foreach (var x in Properties) + ret.LocalVariables.SetValue(x.Key, WrapCast(x.Value.GetValue(obj))); + foreach (var x in Fields) + ret.LocalVariables.SetValue(x.Key, WrapCast(x.Value.GetValue(obj))); + return ret; + } + public static ExecutionContext WrapStatic() + { + var ret = new ExecutionContext(); + foreach (var x in StaticProperties) + ret.LocalVariables.SetValue(x.Key, WrapCast(x.Value.GetValue(null))); + foreach (var x in StaticFields) + ret.LocalVariables.SetValue(x.Key, WrapCast(x.Value.GetValue(null))); + return ret; + } + public static void SafeUnwrapStatic(ExecutionContext context) + { + foreach (var name in context.LocalVariables.GetAsDictionary()) + { + try + { + if (Properties.ContainsKey(name.Key) && Properties[name.Key].SetMethod != null) + Properties[name.Key].SetValue(null, UnwrapCast(Properties[name.Key].PropertyType, name.Value)); + else if (Fields.ContainsKey(name.Key)) Fields[name.Key].SetValue(null, UnwrapCast(Fields[name.Key].FieldType, name.Value)); + } + catch (Exception e) + { + context.Errors.Add(e); + } + } + } + public static void UnwrapStatic(ExecutionContext context) + { + foreach (var name in context.LocalVariables.GetAsDictionary()) + { + if (Properties.ContainsKey(name.Key) && Properties[name.Key].SetMethod != null) + Properties[name.Key].SetValue(null, UnwrapCast(Properties[name.Key].PropertyType, name.Value)); + else if (Fields.ContainsKey(name.Key)) Fields[name.Key].SetValue(null, UnwrapCast(Fields[name.Key].FieldType, name.Value)); + } + } + } + + public sealed class UnwrapperForStaticClasses : Wrapper + { + private static Dictionary Unwrappers = new Dictionary(); + private new readonly Dictionary StaticProperties = new Dictionary(); + private new readonly Dictionary StaticFields = new Dictionary(); + private UnwrapperForStaticClasses(Type type) + { + if (!type.IsAbstract || !type.IsSealed) throw new ArgumentException($"Type {type.Name} is not static class!"); + var static_props = type.GetProperties(BindingFlags.Static | BindingFlags.Public); + foreach (var property in static_props) + { + if (Attribute.IsDefined(property, typeof(WrapperSkipAttribute))) continue; + //else if (Attribute.IsDefined(property, typeof(WrappingInjectClassname))) InjectClassname = property; + else StaticProperties[property.Name] = property; + } + var static_fields = type.GetFields(BindingFlags.Static | BindingFlags.Public); + foreach (var field in static_fields) + { + if (Attribute.IsDefined(field, typeof(WrapperSkipAttribute))) continue; + else StaticFields[field.Name] = field; + } + } + + public static ExecutionContext Wrap(Type type) + { + var ret = new ExecutionContext(); + if (!Unwrappers.ContainsKey(type)) Unwrappers.Add(type, new UnwrapperForStaticClasses(type)); + var props = Unwrappers[type].StaticProperties; + var fields = Unwrappers[type].StaticFields; + foreach (var x in props) + ret.LocalVariables.SetValue(x.Key, x.Value.GetValue(null)); + foreach (var x in fields) + ret.LocalVariables.SetValue(x.Key, x.Value.GetValue(null)); + return ret; + } + + public static void Unwrap(Type type, ExecutionContext context) + { + if (!Unwrappers.ContainsKey(type)) Unwrappers.Add(type, new UnwrapperForStaticClasses(type)); + var props = Unwrappers[type].StaticProperties; + var fields = Unwrappers[type].StaticFields; + foreach (var name in context.LocalVariables.GetAsDictionary()) + { + if (props.ContainsKey(name.Key) && props[name.Key].SetMethod != null) + props[name.Key].SetValue(null, UnwrapCast(props[name.Key].PropertyType, name.Value)); + else if (fields.ContainsKey(name.Key)) fields[name.Key].SetValue(null, UnwrapCast(fields[name.Key].FieldType, name.Value)); + } + } + + public static void SafeUnwrap(Type type, ExecutionContext context) + { + if (!Unwrappers.ContainsKey(type)) Unwrappers.Add(type, new UnwrapperForStaticClasses(type)); + var props = Unwrappers[type].StaticProperties; + var fields = Unwrappers[type].StaticFields; + foreach (var name in context.LocalVariables.GetAsDictionary()) + { + try + { + if (props.ContainsKey(name.Key) && props[name.Key].SetMethod != null) + props[name.Key].SetValue(null, UnwrapCast(props[name.Key].PropertyType, name.Value)); + else if (fields.ContainsKey(name.Key)) fields[name.Key].SetValue(null, UnwrapCast(fields[name.Key].FieldType, name.Value)); + } + catch (Exception e) + { + context.Errors.Add(e); + } + } + } + } + + public abstract class UnwrapperForInstances : Wrapper where T: new() + { + private UnwrapperForInstances() { } public static T Unwrap(ExecutionContext context) { var ret = new T(); foreach (var name in context.LocalVariables.GetAsDictionary()) { - if (Properties.ContainsKey(name.Key)) Properties[name.Key].SetValue(ret, Cast(Properties[name.Key].PropertyType, name.Value)); - else if (Fields.ContainsKey(name.Key)) Fields[name.Key].SetValue(ret, Cast(Fields[name.Key].FieldType, name.Value)); + if (Properties.ContainsKey(name.Key) && Properties[name.Key].SetMethod != null) + Properties[name.Key].SetValue(ret, UnwrapCast(Properties[name.Key].PropertyType, name.Value)); + else if (Fields.ContainsKey(name.Key)) Fields[name.Key].SetValue(ret, UnwrapCast(Fields[name.Key].FieldType, name.Value)); + } + return ret; + } + + public static T SafeUnwrap(ExecutionContext context) + { + var ret = new T(); + foreach (var name in context.LocalVariables.GetAsDictionary()) + { + try + { + if (Properties.ContainsKey(name.Key) && Properties[name.Key].SetMethod != null) + Properties[name.Key].SetValue(ret, UnwrapCast(Properties[name.Key].PropertyType, name.Value)); + else if (Fields.ContainsKey(name.Key)) Fields[name.Key].SetValue(ret, UnwrapCast(Fields[name.Key].FieldType, name.Value)); + } + catch (Exception e) + { + context.Errors.Add(e); + } } return ret; } @@ -167,7 +380,7 @@ public static T Unwrap(ExecutionContext context) /// /// Это свойство будет пропущено при разворачивании /// - [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)] public sealed class WrapperSkipAttribute : Attribute { public WrapperSkipAttribute() { } diff --git a/SLThree/docs/specification b/SLThree/docs/specification index 27778d8..5e9e327 100644 --- a/SLThree/docs/specification +++ b/SLThree/docs/specification @@ -1,17 +1,22 @@ --=== SLT SPECIFICATION ===-- Nums (default 64 bit), chars, strings (+interpolated) and bools like in C# Suffixes for nums determine type like in Rust (1u32 = uint, 1f32 = float) -Operators (priority like in C#): - Binary: + - * / % == != > >= < <= = += -= *= /= %= => as +Operators (priority like in C#, exclude bit operators): + Ternary: ?: + Binary: + - * / % == != > >= < <= = += -= *= /= %= => as, &&, ||, &, |, ^ Unary: + - ! ~ - Primary: x.y, x(), new x() + Primary: x.y, x(), new x(), x[y] Statements like in C#: - if and while - return, break, continue Almost like in C#: + - foreach ( in ) - using ; // not namespace - switch (supports non-const in case and break not needed) - [implicit] - implicit supports more types (slower) SLThree features: - getting lexems ( as is) and call it ---=== END SPECIFICATON ===-- \ No newline at end of file + - list, tuple and dictionary initializers +--=== END SPECIFICATON ===-- +For more information: + https://github.com/AIexandrKotov/SLThree/wiki/Language \ No newline at end of file diff --git a/SLThree/docs/versions/0.3.0 b/SLThree/docs/versions/0.3.0 new file mode 100644 index 0000000..636899a --- /dev/null +++ b/SLThree/docs/versions/0.3.0 @@ -0,0 +1,14 @@ +------ 0.3.0 Third time is lucky ------ [06.11.23] +Language: + - Foreach cycle + - linq context (linq.range, linq.max etc.) + - list, tuples and dictionary initializers + - Indexators + - Logical operators (&&, ||) and binary bit operators (&, |, ^) + - Ternary operator + - Hexadecimal and binary literals, suppoting _ in nums +Embedding: + - Wrapping classes in context, wrapping static members and classes + - Safe unwrapping +Optimization: + - >40% (collatz_conjecture 14 s => 8 s) \ No newline at end of file diff --git a/SLThree/syntax.peg b/SLThree/syntax.peg index 12402f8..5e0f03d 100644 --- a/SLThree/syntax.peg +++ b/SLThree/syntax.peg @@ -14,6 +14,7 @@ statement_list statement_ws = _ st:statement _ ";" { st } / _ st:switch_statement _ { st } + / _ st:foreach_statement _ { st } / _ st:while_statement _ { st } / _ st:condition_statement _ { st } / _ st:block_statement _ { st } @@ -26,13 +27,14 @@ statement / _ st:using_statement _ { st } / _ st:condition_statement _ { st } / _ st:switch_statement _ { st } + / _ st:foreach_statement _ { st } / _ st:while_statement _ { st } / _ st:block_statement _ { st } / _ st:expr_statement _ { st } using_statement - = "using" _ ex: as_expr_right _ "as" _ n:name { new UsingStatement(ex, n.Name, state) } - / "using" _ ex: as_expr_right { new UsingStatement(ex, state) } + = "using" _required_ ex: as_expr_right _required_ "as" _required_ n:name { new UsingStatement(ex, n.Name, state) } + / "using" _required_ ex: as_expr_right { new UsingStatement(ex, state) } return_statement = "return" _ lex:lexem { new ReturnStatement(lex, state) } @@ -55,11 +57,14 @@ case_node / _ "case" _ lex:lexem _ ":" _ st:statement _ ";" _ { new SwitchStatement.Node(lex, st, false) } / _ "case" _ lex:lexem _ ":" _ { new SwitchStatement.Node(lex, null, true) } -block_statement +block_statement = "{" _ st:statement_list _ "}" { st } while_statement - = "while" _ "(" cond:lexem ")" _ body:block_statement { new WhileCycleStatement(cond, body, state) } + = "while" _ "(" _ cond:lexem _ ")" _ body:block_statement { new WhileLoopStatement(cond, body, state) } + +foreach_statement + = "foreach" _ "(" _ n:name _ "in" _ iter:lexem _ ")" _ body:block_statement { new ForeachLoopStatement(n, iter, body, state) } condition_statement = "if" _ "(" cond:lexem ")" _ t:block_statement _ "else" _ f:block_statement { new ConditionStatement(cond, t, f, state) } @@ -69,54 +74,69 @@ expr_statement = value:lexem { new ExpressionStatement(value, state) } lexem - = binary_4 + = binary_9 + +binary_9 -memoize + = left:binary_7 _ "=" _ right:binary_9 { new ExpressionBinaryAssign(left, right, state) } + / left:binary_7 _ "+=" _ right:binary_9 { new ExpressionBinaryAssign(left, new ExpressionBinaryAdd(left, right, state), state) } + / left:binary_7 _ "-=" _ right:binary_9 { new ExpressionBinaryAssign(left, new ExpressionBinaryRem(left, right, state), state) } + / left:binary_7 _ "*=" _ right:binary_9 { new ExpressionBinaryAssign(left, new ExpressionBinaryMultiply(left, right, state), state) } + / left:binary_7 _ "/=" _ right:binary_9 { new ExpressionBinaryAssign(left, new ExpressionBinaryDivide(left, right, state), state) } + / left:binary_7 _ "%=" _ right:binary_9 { new ExpressionBinaryAssign(left, new ExpressionBinaryMod(left, right, state), state) } + / left:binary_7 _ "&=" _ right:binary_9 { new ExpressionBinaryAssign(left, new ExpressionBinaryBitAnd(left, right, state), state) } + / left:binary_7 _ "|=" _ right:binary_9 { new ExpressionBinaryAssign(left, new ExpressionBinaryBitOr(left, right, state), state) } + / left:binary_7 _ "^=" _ right:binary_9 { new ExpressionBinaryAssign(left, new ExpressionBinaryBitXor(left, right, state), state) } + / left:lambda_left _ "=>" _ right:lambda_right _ { new LambdaLexem(new InvokeLexem(null, left.Item2, state), right, left.Item1, state) } + / left:ternary_0 { left } + +ternary_0 -memoize + = cond:ternary_0 _ "?" _ t:lexem _ ":" _ f:lexem { new ExpressionTernary(cond, t, f, state) } + / binary_7 + +binary_7 -memoize + = left:binary_7 _ "||" _ right:binary_6 { new ExpressionBinaryOr(left, right, state) } + / binary_6 + +binary_6 -memoize + = left:binary_6 _ "&&" _ right:binary_5 { new ExpressionBinaryAnd(left, right, state) } + / binary_5 + +binary_5 -memoize + = left:binary_5 _ "&" _ right:binary_4 { new ExpressionBinaryBitAnd(left, right, state) } + / left:binary_5 _ "^" _ right:binary_4 { new ExpressionBinaryBitXor(left, right, state) } + / left:binary_5 _ "|" _ right:binary_4 { new ExpressionBinaryBitOr(left, right, state) } + / binary_4 binary_4 -memoize - = left:binary_3 _ "=" _ right:binary_4 { new ExpressionBinaryAssign(left, right, state) } - / left:binary_3 _ "+=" _ right:binary_4 { new ExpressionBinaryAssign(left, new ExpressionBinaryAdd(left, right, state), state) } - / left:binary_3 _ "-=" _ right:binary_4 { new ExpressionBinaryAssign(left, new ExpressionBinaryRem(left, right, state), state) } - / left:binary_3 _ "*=" _ right:binary_4 { new ExpressionBinaryAssign(left, new ExpressionBinaryMultiply(left, right, state), state) } - / left:binary_3 _ "/=" _ right:binary_4 { new ExpressionBinaryAssign(left, new ExpressionBinaryDivide(left, right, state), state) } - / left:binary_3 _ "%=" _ right:binary_4 { new ExpressionBinaryAssign(left, new ExpressionBinaryMod(left, right, state), state) } - / mods:method_modificator* _ "(" _ args:names_arguments_list _ ")" _ "=>" _ "{" _ st:statement_list _ "}" { - new LambdaLexem(new InvokeLexem(null, args.ToArray(), state), st, mods, state) - } - / mods:method_modificator* _ "(" _ args:names_arguments_list _ ")" _ "=>" _ st:expr_statement _ { - new LambdaLexem( - new InvokeLexem(null, args.ToArray(), state), - new StatementListStatement( - new BaseStatement[1] { new ReturnStatement(st.Lexem, state) } - , state), mods, state) - } - / mods:method_modificator* _ a:name _ "=>" _ "{" _ st:statement_list _ "}" { - new LambdaLexem(new InvokeLexem(null, new BaseLexem[1] { a }, state), st, mods, state) - } - / mods:method_modificator* _ a:name _ "=>" _ st:expr_statement _ { - new LambdaLexem( - new InvokeLexem(null, new BaseLexem[1] { a }, state), - new StatementListStatement( - new BaseStatement[1] { new ReturnStatement(st.Lexem, state) } - , state), mods, state) - } - / mods:method_modificator* _ ("(" _ ")") _ "=>" _ "{" _ st:statement_list _ "}" { - new LambdaLexem(new InvokeLexem(null, new BaseLexem[0], state), st, mods, state) - } - / mods:method_modificator* _ ("(" _ ")") _ "=>" _ st:expr_statement _ { - new LambdaLexem( - new InvokeLexem(null, new BaseLexem[0], state), - new StatementListStatement( - new BaseStatement[1] { new ReturnStatement(st.Lexem, state) } - , state), mods, state) - } - / left:binary_3 { left } + = left:binary_4 _ "==" _ right:binary_3 { new ExpressionBinaryEquals(left, right, state) } + / left:binary_4 _ "!=" _ right:binary_3 { new ExpressionBinaryUnequals(left, right, state) } + / binary_3 + +lambda_left , BaseLexem[]>> + = mods:method_modificator_list? _ "(" _ ")" { new ValueTuple, BaseLexem[]>(mods.Count == 0 ? new string[0] : mods[0], new BaseLexem[0]) } + / mods:method_modificator_list? _ "(" _ args:names_arguments_list _ ")" { new ValueTuple, BaseLexem[]>(mods.Count == 0 ? new string[0] : mods[0], args.ToArray()) } + / mods:method_modificator_list _required_ a:name { new ValueTuple, BaseLexem[]>(mods, new BaseLexem[1] { a } ) } + / a:name { new ValueTuple, BaseLexem[]>(new string[0], new BaseLexem[1] { a } ) } + +lambda_right + = st:expr_statement { new StatementListStatement(new BaseStatement[1] { new ReturnStatement(st.Lexem, state) }, state) } + / "{" _ st:statement_list _ "}" { st } + +method_modificator_list > + = m1:method_modificator m2p:other_modificators { m2p.AddAndRet(m1) } + / m1:method_modificator { new string[1] { m1 } } + +other_modificators > + = _required_ m2p:method_modificator+ { m2p } method_modificator - = _ s:"implicit" _ { s } + = implicit_keyword + +implicit_keyword + = s:"implicit" { s } binary_3 -memoize - = left:binary_3 _ "==" _ right:binary_2 { new ExpressionBinaryEquals(left, right, state) } - / left:binary_3 _ "as" _ right:as_expr_right { new CastLexem(left, right, state) } - / left:binary_3 _ "!=" _ right:binary_2 { new ExpressionBinaryUnequals(left, right, state) } + = left:binary_3 _ "as" _required_ right:as_expr_right { new CastLexem(left, right, state) } / left:binary_3 _ ">=" _ right:binary_2 { new ExpressionBinaryGreaterThanEquals(left, right, state) } / left:binary_3 _ "<=" _ right:binary_2 { new ExpressionBinaryLessThanEquals(left, right, state) } / left:binary_3 _ ">" _ right:binary_2 { new ExpressionBinaryGreaterThan(left, right, state) } @@ -138,8 +158,8 @@ binary_0 = _ left:binary_min _ { left } binary_min - = "(" _ bin:binary_4 _ ")" { bin } - / _ u:unary _ { u } + //= "(" _ bin:binary_9 _ ")" { bin } + = _ u:unary _ { u } unary = "+" _ left:binary_min { new ExpressionUnaryAdd(left, state) } @@ -157,14 +177,39 @@ as_expr_right -memoize / right:name { right } primary -memoize - = left:primary _ "." _ right:invoke_lexem { new MemberAccess(left, right, state) } + = "(" _ left:lexem _ ")" _ "." _ right:invoke_lexem { new MemberAccess(left, right, state) } + / left:primary _ "." _ right:invoke_lexem { new MemberAccess(left, right, state) } / left:primary _ "." _ right:name { new MemberAccess(left, right, state) } / "new" _ right:invoke_lexem { new NewLexem(right as InvokeLexem, state) } / "new" _ right:primary { new NewLexem(right as MemberAccess, state) } + / left:primary _ "[" _ args:arguments_list _ "]" { new IndexLexem(left, args.ToArray(), state) } / "typeof" _ "(" _ left:primary _ ")" { new TypeofLexem(left, state) } / invoke_lexem - / literal + / "(" _ x:lexem _ ")" { x } + / tuple_creator + / dictionary_creator + / list_creator + / special / name + / literal + +dictionary_creator + = "{" _ etrs:dictionary_entries _ "}" { new CreatorDictionary(etrs.ToArray(), state) } + +dictionary_entries > + = first:dictionary_entry _ oth:other_entry* { oth.AddAndRet(first) } + +other_entry = ("," _ a:dictionary_entry _ ) { a } + +dictionary_entry + = left:primary _ ":" _ right:lexem { new CreatorDictionary.Entry(left, right, state) } + +tuple_creator + = "(" _ args:arguments_list _ ")" { new CreatorTuple(args.ToArray(), state) } + +list_creator + = "[" _ args:arguments_list _ "]" { new CreatorArray(args.ToArray(), state) } + / "[" _ "]" { new CreatorArray(new BaseLexem[0], state) } invoke_lexem = left:identifier _ "(" _ args:arguments_list _ ")" { new InvokeLexem(left, args.ToArray(), state) } @@ -184,22 +229,45 @@ other_arg_name = ("," _ a:arg_name _ ) { a } arg_name = name -literal - = f32:([0-9]+ ("." [0-9]+)? ("f32")) { new FloatLiteral(float.Parse(f32.Replace("f32", ""), CultureInfo.InvariantCulture), state) } - / f64:([0-9]+ ("." [0-9]+) ("f64")?) { new DoubleLiteral(double.Parse(f64.Replace("f64", ""), CultureInfo.InvariantCulture), state) } - / f64:([0-9]+ ("f64")) { new DoubleLiteral(double.Parse(f64.Replace("f64", ""), CultureInfo.InvariantCulture), state) } - / i8:([0-9]+ ("i8")) { new SByteLiteral(sbyte.Parse(i8.Replace("i8", ""), CultureInfo.InvariantCulture), state) } - / u8:([0-9]+ ("u8")) { new ByteLiteral(byte.Parse(u8.Replace("u8", ""), CultureInfo.InvariantCulture), state) } - / i16:([0-9]+ ("i16")) { new ShortLiteral(short.Parse(i16.Replace("i16", ""), CultureInfo.InvariantCulture), state) } - / u16:([0-9]+ ("u16")) { new UShortLiteral(ushort.Parse(u16.Replace("u16", ""), CultureInfo.InvariantCulture), state) } - / i32:([0-9]+ ("i32")) { new IntLiteral(int.Parse(i32.Replace("i32", ""), CultureInfo.InvariantCulture), state) } - / u32:([0-9]+ ("u32")) { new UIntLiteral(uint.Parse(u32.Replace("u32", ""), CultureInfo.InvariantCulture), state) } - / u64:([0-9]+ ("u64")) { new ULongLiteral(ulong.Parse(u64.Replace("u64", ""), CultureInfo.InvariantCulture), state) } - / i64:([0-9]+ ("i64")?) { new LongLiteral(long.Parse(i64.Replace("i64", ""), CultureInfo.InvariantCulture), state) } - / b:("true"/"false") { new BoolLiteral(bool.Parse(b), state) } +special + = b:("true"/"false") { new BoolLiteral(bool.Parse(b), state) } + / "linq" { new LinqLiteral(state) } / "global" { new GlobalLiteral(state) } / "null" { new NullLiteral(state) } / "self" { new SelfLiteral(state) } + +literal + = f32:([0-9_]+ ("." [0-9_]+)? ("f32")) { new FloatLiteral(float.Parse(f32.Replace("f32", "").Replace("_", ""), CultureInfo.InvariantCulture), state) } + / f64:([0-9_]+ ("." [0-9_]+) ("f64")?) { new DoubleLiteral(double.Parse(f64.Replace("f64", "").Replace("_", ""), CultureInfo.InvariantCulture), state) } + / f64:([0-9_]+ ("f64")) { new DoubleLiteral(double.Parse(f64.Replace("f64", "").Replace("_", ""), CultureInfo.InvariantCulture), state) } + + / "0b" i8:([01_]+ ("i8")) { new SByteLiteral (Convert.ToSByte (i8.Replace("i8", "").Replace("_", ""), 2), state) } + / "0b" u8:([01_]+ ("u8")) { new ByteLiteral (Convert.ToByte (u8.Replace("u8", "").Replace("_", ""), 2), state) } + / "0b" i16:([01_]+ ("i16")) { new ShortLiteral (Convert.ToInt16 (i16.Replace("i16", "").Replace("_", ""), 2), state) } + / "0b" u16:([01_]+ ("u16")) { new UShortLiteral (Convert.ToUInt16 (u16.Replace("u16", "").Replace("_", ""), 2), state) } + / "0b" i32:([01_]+ ("i32")) { new IntLiteral (Convert.ToInt32 (i32.Replace("i32", "").Replace("_", ""), 2), state) } + / "0b" u32:([01_]+ ("u32")) { new UIntLiteral (Convert.ToUInt32 (u32.Replace("u32", "").Replace("_", ""), 2), state) } + / "0b" u64:([01_]+ ("u64")) { new ULongLiteral (Convert.ToUInt64 (u64.Replace("u64", "").Replace("_", ""), 2), state) } + / "0b" i64:([01_]+ ("i64")?) { new LongLiteral (Convert.ToInt64 (i64.Replace("i64", "").Replace("_", ""), 2), state) } + + / "0x" i8:([0-9A-F_]+ ("i8")) { new SByteLiteral (Convert.ToSByte (i8.Replace("i8", "").Replace("_", ""), 16), state) } + / "0x" u8:([0-9A-F_]+ ("u8")) { new ByteLiteral (Convert.ToByte (u8.Replace("u8", "").Replace("_", ""), 16), state) } + / "0x" i16:([0-9A-F_]+ ("i16")) { new ShortLiteral (Convert.ToInt16 (i16.Replace("i16", "").Replace("_", ""), 16), state) } + / "0x" u16:([0-9A-F_]+ ("u16")) { new UShortLiteral (Convert.ToUInt16 (u16.Replace("u16", "").Replace("_", ""), 16), state) } + / "0x" i32:([0-9A-F_]+ ("i32")) { new IntLiteral (Convert.ToInt32 (i32.Replace("i32", "").Replace("_", ""), 16), state) } + / "0x" u32:([0-9A-F_]+ ("u32")) { new UIntLiteral (Convert.ToUInt32 (u32.Replace("u32", "").Replace("_", ""), 16), state) } + / "0x" u64:([0-9A-F_]+ ("u64")) { new ULongLiteral (Convert.ToUInt64 (u64.Replace("u64", "").Replace("_", ""), 16), state) } + / "0x" i64:([0-9A-F_]+ ("i64")?) { new LongLiteral (Convert.ToInt64 (i64.Replace("i64", "").Replace("_", ""), 16), state) } + + / i8:([0-9_]+ ("i8")) { new SByteLiteral (sbyte.Parse (i8.Replace("i8", "").Replace("_", ""), CultureInfo.InvariantCulture), state) } + / u8:([0-9_]+ ("u8")) { new ByteLiteral (byte.Parse (u8.Replace("u8", "").Replace("_", ""), CultureInfo.InvariantCulture), state) } + / i16:([0-9_]+ ("i16")) { new ShortLiteral (short.Parse (i16.Replace("i16", "").Replace("_", ""), CultureInfo.InvariantCulture), state) } + / u16:([0-9_]+ ("u16")) { new UShortLiteral (ushort.Parse (u16.Replace("u16", "").Replace("_", ""), CultureInfo.InvariantCulture), state) } + / i32:([0-9_]+ ("i32")) { new IntLiteral (int.Parse (i32.Replace("i32", "").Replace("_", ""), CultureInfo.InvariantCulture), state) } + / u32:([0-9_]+ ("u32")) { new UIntLiteral (uint.Parse (u32.Replace("u32", "").Replace("_", ""), CultureInfo.InvariantCulture), state) } + / u64:([0-9_]+ ("u64")) { new ULongLiteral (ulong.Parse (u64.Replace("u64", "").Replace("_", ""), CultureInfo.InvariantCulture), state) } + / i64:([0-9_]+ ("i64")?) { new LongLiteral (long.Parse (i64.Replace("i64", "").Replace("_", ""), CultureInfo.InvariantCulture), state) } + / interpolated_string / string_literal / char_literal @@ -261,6 +329,8 @@ available_name > comment = _ "//" [^\r\n]* +_required_ = [ \t\r\n]+ + _ = [ \t\r\n]* EOF = !. / c:. #ERROR{ "Unexpected '" + c + "'" } \ No newline at end of file diff --git a/examples/char_based_interface.slt b/examples/char_based_interface.slt new file mode 100644 index 0000000..0442206 --- /dev/null +++ b/examples/char_based_interface.slt @@ -0,0 +1,50 @@ +out_help = () => +{ + global.println("0 - Выйти"); + global.println("h - Вывести ещё раз"); + global.println("+ - Вывести a + b"); + global.println("* - Вывести a * b"); + global.println("% - Вывести a % b"); + global.println($"a - Заполнить a (Сейчас: {self.pred.a})"); + global.println($"b - Заполнить b (Сейчас: {self.pred.b})"); +}; +read = str => +{ + global.print(str); + return global.readln().Trim() as i64; +}; + +a = b = 2; +out_help(); +while (true) +{ + global.print(">>> "); + input = global.readln().Trim(); + switch (input) + { + case "0": + break; + case "h": + out_help(); + case "+": + { + global.println($"Вывод: {a + b}"); + } + case "*": + { + global.println($"Вывод: {a * b}"); + } + case "%": + { + global.println($"Вывод: {a % b}"); + } + case "a": + { + a = read("Введите a: "); + } + case "b": + { + b = read("Введите b: "); + } + } +} \ No newline at end of file