diff --git a/Upsilon/BaseTypes/LuaFunction.cs b/Upsilon/BaseTypes/LuaFunction.cs index ec37d55..f5caff8 100644 --- a/Upsilon/BaseTypes/LuaFunction.cs +++ b/Upsilon/BaseTypes/LuaFunction.cs @@ -1,6 +1,7 @@ using System.Collections.Immutable; using System.Linq; using System.Reflection; +using Upsilon.BaseTypes.LuaTypeInterfaces; using Upsilon.BaseTypes.UserData; using Upsilon.Binder; using Upsilon.Evaluator; @@ -65,7 +66,15 @@ namespace Upsilon.BaseTypes { var types = variables.Select(x => x.GetCSharpType()); var method = _method.GetMethod(types.ToArray()); - var objects = variables.Select(x => x.ToCSharpObject()); + var objects = variables.Select(x => x.ToCSharpObject()).ToList(); + var pars = method.GetParameters(); + if (pars.Length != objects.Count) + { + for (var i = objects.Count; i < pars.Length; i++) + { + objects.Add(null); + } + } return method.Invoke(_object, objects.ToArray()).ToLuaType(); } } diff --git a/Upsilon/BaseTypes/LuaTable.cs b/Upsilon/BaseTypes/LuaTable.cs index 65de10f..9fd3779 100644 --- a/Upsilon/BaseTypes/LuaTable.cs +++ b/Upsilon/BaseTypes/LuaTable.cs @@ -1,12 +1,13 @@ using System.Collections.Generic; using System.Linq; +using Upsilon.BaseTypes.LuaTypeInterfaces; using Upsilon.Binder; using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.BaseTypes { - internal class LuaTable : LuaType, IIndexable, IScopeOwner + internal class LuaTable : LuaType, IIndexable, IScopeOwner, IIterable { public EvaluationScope EvaluationScope { get; } @@ -15,7 +16,6 @@ namespace Upsilon.BaseTypes EvaluationScope = scope; } - public override Type Type => Type.Table; public override object ToCSharpObject() { @@ -43,5 +43,28 @@ namespace Upsilon.BaseTypes var s = index.ToString(); EvaluationScope.Set(new VariableSymbol(s, value.Type, false), value); } + + + private IEnumerator> _enumerator; + + public bool Next(out LuaType key, out LuaType next) + { + if (_enumerator == null) _enumerator = EvaluationScope.Variables.GetEnumerator(); + if (_enumerator.MoveNext()) + { + key = _enumerator.Current.Key.ToLuaType(); + next = _enumerator.Current.Value; + return true; + } + key = null; + next = null; + Reset(); + return false; + } + + public void Reset() + { + _enumerator = null; + } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/LuaTypeInterfaces/IITerable.cs b/Upsilon/BaseTypes/LuaTypeInterfaces/IITerable.cs new file mode 100644 index 0000000..e3138d4 --- /dev/null +++ b/Upsilon/BaseTypes/LuaTypeInterfaces/IITerable.cs @@ -0,0 +1,8 @@ +namespace Upsilon.BaseTypes.LuaTypeInterfaces +{ + public interface IIterable + { + bool Next(out LuaType key, out LuaType next); + void Reset(); + } +} \ No newline at end of file diff --git a/Upsilon/BaseTypes/IIndexable.cs b/Upsilon/BaseTypes/LuaTypeInterfaces/IIndexable.cs similarity index 75% rename from Upsilon/BaseTypes/IIndexable.cs rename to Upsilon/BaseTypes/LuaTypeInterfaces/IIndexable.cs index aae4275..ef382fc 100644 --- a/Upsilon/BaseTypes/IIndexable.cs +++ b/Upsilon/BaseTypes/LuaTypeInterfaces/IIndexable.cs @@ -8,9 +8,4 @@ namespace Upsilon.BaseTypes LuaType Get(Diagnostics diagnostics, TextSpan span, LuaType index, EvaluationScope scope); void Set(Diagnostics diagnostics, TextSpan span, LuaType index, LuaType value); } - - internal interface IScopeOwner - { - EvaluationScope EvaluationScope { get; } - } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/LuaTypeInterfaces/IScopeOwner.cs b/Upsilon/BaseTypes/LuaTypeInterfaces/IScopeOwner.cs new file mode 100644 index 0000000..8460a3e --- /dev/null +++ b/Upsilon/BaseTypes/LuaTypeInterfaces/IScopeOwner.cs @@ -0,0 +1,9 @@ +using Upsilon.Evaluator; + +namespace Upsilon.BaseTypes.LuaTypeInterfaces +{ + internal interface IScopeOwner + { + EvaluationScope EvaluationScope { get; } + } +} \ No newline at end of file diff --git a/Upsilon/BaseTypes/SimpleLuaTable.cs b/Upsilon/BaseTypes/SimpleLuaTable.cs new file mode 100644 index 0000000..e9d021e --- /dev/null +++ b/Upsilon/BaseTypes/SimpleLuaTable.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using Upsilon.Evaluator; +using Upsilon.Text; + +namespace Upsilon.BaseTypes +{ + internal class SimpleLuaTable : LuaType, IIndexable + { + private readonly IList _objects; + + public SimpleLuaTable(IList objects) + { + _objects = objects; + } + + public LuaType Get(Diagnostics diagnostics, TextSpan span, LuaType index, EvaluationScope scope) + { + int i; + switch (index.Type) + { + case Type.String: + { + var str = (LuaString)index; + i = int.Parse(str.Value) - 1; + break; + } + case Type.Number: + { + var ind = (Number.NumberLong) index; + i = (int) ind.Value - 1; + break; + } + default: + return null; + } + + return _objects[i]; + } + + public void Set(Diagnostics diagnostics, TextSpan span, LuaType index, LuaType value) + { + throw new System.NotImplementedException(); + } + + public override Type Type => Type.Table; + public override object ToCSharpObject() + { + return _objects; + } + + public override System.Type GetCSharpType() + { + return _objects.GetType(); + } + } +} \ No newline at end of file diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index dc408c5..ea416b2 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -258,7 +258,7 @@ namespace Upsilon.Binder } else if (assignment.Type == Type.Unknown && assignment is BoundVariableExpression v) { - v.Variable.Type = variable.Type; + v.Variable.Type = v.Type; } else { diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 04c6a17..b464c55 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using Upsilon.BaseTypes; +using Upsilon.BaseTypes.LuaTypeInterfaces; using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.UserData; using Upsilon.Binder; @@ -11,10 +12,10 @@ namespace Upsilon.Evaluator { internal class Evaluator { - private readonly Diagnostics _diagnostics; + private Diagnostics _diagnostics; private LuaType _lastValue; private LuaType _returnValue; - internal EvaluationScope Scope { get; } + internal EvaluationScope Scope { get; private set; } private bool HasReturned { get; set; } internal Evaluator(Diagnostics diagnostics, Dictionary variables) @@ -29,6 +30,13 @@ namespace Upsilon.Evaluator Scope = new EvaluationScope(parentScope); } + private Evaluator(){} + + internal static Evaluator CreateWithSetScope(Diagnostics diagnostics, EvaluationScope scope) + { + return new Evaluator {_diagnostics = diagnostics, Scope = scope}; + } + public LuaType Evaluate(BoundScript e) { EvaluateNode(e.Statement); @@ -282,9 +290,8 @@ namespace Upsilon.Evaluator private void EvaluateMultiAssignmentStatement(BoundMultiAssignmentStatement e) { var val = EvaluateExpression(e.Assignment); - if (val.Type == Type.Table) + if (val is IIndexable table) { - var table = (LuaTable) val; for (var i = 0; i < e.Variables.Length; i++) { var variableSymbol = e.Variables[i]; diff --git a/Upsilon/Evaluator/Script.cs b/Upsilon/Evaluator/Script.cs index 8ebfcd3..1fa3cf4 100644 --- a/Upsilon/Evaluator/Script.cs +++ b/Upsilon/Evaluator/Script.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using Upsilon.BaseTypes; using Upsilon.Binder; using Upsilon.Parser; +using Upsilon.StandardLibraries; using Upsilon.Text; using Upsilon.Utilities; @@ -19,13 +20,35 @@ namespace Upsilon.Evaluator private Binder.Binder Binder { get; set; } private EvaluationScope Scope { get; } + private static BoundScope _staticBoundScope; + private static EvaluationScope _staticScope; + + private static EvaluationScope StaticScope + { + get + { + var scope = _staticScope; + if (scope != null) + { + return scope; + } + + var (evaluationScope, boundScope) = StandardLibrary.Create(); + _staticBoundScope = boundScope; + return (_staticScope = evaluationScope); + } + } + public Script(string scriptString) { ScriptString = new SourceText(scriptString); Diagnostics = new Diagnostics(ScriptString); _parsed = Parser.Parser.Parse(scriptString, Diagnostics); - Binder = new Binder.Binder(Diagnostics, new Dictionary()); - Evaluator = new Evaluator( Diagnostics, new Dictionary()); + + Scope = EvaluationScope.CreateWithGetOnlyParent(StaticScope); + Binder = new Binder.Binder(Diagnostics, _staticBoundScope.Variables); + + Evaluator = Evaluator.CreateWithSetScope(Diagnostics, Scope); Scope = Evaluator.Scope; } diff --git a/Upsilon/StandardLibraries/Global.cs b/Upsilon/StandardLibraries/Global.cs new file mode 100644 index 0000000..255e43f --- /dev/null +++ b/Upsilon/StandardLibraries/Global.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using Upsilon.BaseTypes; +using Upsilon.BaseTypes.LuaTypeInterfaces; + +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global + +namespace Upsilon.StandardLibraries +{ + internal class BasicFunctions : LuaLibrary + { + [LuaFunction("assert")] + public void Assert(bool boolean, string message = null) + { + if (!boolean) + { + Error(message ?? "assertion failed!"); + } + } + + [LuaFunction("error")] + public void Error(string message) + { + throw new Exception(message); + } + + [LuaFunction("ipairs")] + public LuaType Pairs(IIterable table) + { + if (!table.Next(out var key, out var next)) + { + return new LuaNull(); + } + + return new SimpleLuaTable(new List() {key, next}); + } + } +} \ No newline at end of file diff --git a/Upsilon/StandardLibraries/LuaFunctionAttribute.cs b/Upsilon/StandardLibraries/LuaFunctionAttribute.cs new file mode 100644 index 0000000..29613d6 --- /dev/null +++ b/Upsilon/StandardLibraries/LuaFunctionAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Upsilon.StandardLibraries +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] + public class LuaFunctionAttribute : Attribute + { + public LuaFunctionAttribute(string name) + { + Name = name; + } + + public string Name { get; } + } +} \ No newline at end of file diff --git a/Upsilon/StandardLibraries/LuaLibrary.cs b/Upsilon/StandardLibraries/LuaLibrary.cs new file mode 100644 index 0000000..ad57039 --- /dev/null +++ b/Upsilon/StandardLibraries/LuaLibrary.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Reflection; +using Upsilon.BaseTypes; +using Upsilon.BaseTypes.UserData; + +namespace Upsilon.StandardLibraries +{ + internal abstract class LuaLibrary + { + public Dictionary LoadMethods() + { + var dictionary = new Dictionary(); + var methods = GetType().GetMethods(); + foreach (var methodInfo in methods) + { + var attr = methodInfo.GetCustomAttribute(); + if (attr != null) + { + dictionary.Add(attr.Name, new LuaMethodInfoFunction(new UserDataMethod(methodInfo), this)); + } + } + + return dictionary; + } + + } +} \ No newline at end of file diff --git a/Upsilon/StandardLibraries/StandardLibrary.cs b/Upsilon/StandardLibraries/StandardLibrary.cs new file mode 100644 index 0000000..692a80a --- /dev/null +++ b/Upsilon/StandardLibraries/StandardLibrary.cs @@ -0,0 +1,23 @@ +using System.Collections.Immutable; +using System.Linq; +using Upsilon.BaseTypes; +using Upsilon.Binder; +using Upsilon.Evaluator; + +namespace Upsilon.StandardLibraries +{ + internal class StandardLibrary + { + public static (EvaluationScope, BoundScope) Create() + { + var basicFunctions = new BasicFunctions().LoadMethods(); + var scope = new EvaluationScope(basicFunctions.ToDictionary(x => x.Key, x => (LuaType)x.Value)); + var boundScope = + new BoundScope( + scope.Variables.ToDictionary(x => x.Key + , x => (VariableSymbol)new FunctionVariableSymbol(x.Key, x.Value.Type, false, ImmutableArray.Empty){IsBound = true}), + null); + return (scope, boundScope); + } + } +} \ No newline at end of file diff --git a/UpsilonTests/StandardLibraryTests/BasicFunctionsTests.cs b/UpsilonTests/StandardLibraryTests/BasicFunctionsTests.cs new file mode 100644 index 0000000..c9b51b6 --- /dev/null +++ b/UpsilonTests/StandardLibraryTests/BasicFunctionsTests.cs @@ -0,0 +1,14 @@ +using Upsilon.Evaluator; +using Xunit; + +namespace UpsilonTests.StandardLibraryTests +{ + public class BasicFunctionsTests + { + [Fact] + public void Assert() + { + new Script("assert(true)").Evaluate(); + } + } +} \ No newline at end of file