diff --git a/Upsilon/BaseTypes/Number/ScriptNumber.cs b/Upsilon/BaseTypes/Number/ScriptNumber.cs index 97014a0..edaa347 100644 --- a/Upsilon/BaseTypes/Number/ScriptNumber.cs +++ b/Upsilon/BaseTypes/Number/ScriptNumber.cs @@ -44,7 +44,7 @@ namespace Upsilon.BaseTypes.Number public static ScriptNumber operator / (ScriptNumber a, ScriptNumber b) { if (!a.IsFloat && !b.IsFloat) - return new ScriptNumberLong(((ScriptNumberLong) a).Value / ((ScriptNumberLong) b).Value); + return new ScriptNumberDouble(((ScriptNumberLong) a).Value / (double)((ScriptNumberLong) b).Value); if (a.IsFloat && b.IsFloat) return new ScriptNumberDouble(((ScriptNumberDouble) a).Value / ((ScriptNumberDouble) b).Value); if (a.IsFloat) diff --git a/Upsilon/BaseTypes/ScriptFunction/ScriptFunction.cs b/Upsilon/BaseTypes/ScriptFunction/ScriptFunction.cs index 9e91ec3..d73063f 100644 --- a/Upsilon/BaseTypes/ScriptFunction/ScriptFunction.cs +++ b/Upsilon/BaseTypes/ScriptFunction/ScriptFunction.cs @@ -15,6 +15,6 @@ namespace Upsilon.BaseTypes.ScriptFunction return null; } - public abstract ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script); + public abstract ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope); } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs b/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs index a010d8d..06f265f 100644 --- a/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs +++ b/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs @@ -9,12 +9,15 @@ namespace Upsilon.BaseTypes.ScriptFunction { internal class ScriptMethodInfoFunction : ScriptFunction { - public ScriptMethodInfoFunction(UserDataMethod method, object o, bool directTypeManipulation, bool passScriptReference = false) + public ScriptMethodInfoFunction(UserDataMethod method, object o, bool directTypeManipulation, + bool passScriptReference = false, bool passScopeReference = false) { _method = method; _object = o; _directTypeManipulation = directTypeManipulation; _passScriptReference = passScriptReference; + _passScopeReference = passScopeReference; + ReturnType = _method.ReturnType; } @@ -22,6 +25,7 @@ namespace Upsilon.BaseTypes.ScriptFunction private readonly object _object; private readonly bool _directTypeManipulation; private readonly bool _passScriptReference; + private readonly bool _passScopeReference; public System.Type ReturnType { get; } public IEnumerable GetParameterInfo() @@ -29,7 +33,7 @@ namespace Upsilon.BaseTypes.ScriptFunction return _method.GetMethods().First().Parameters; } - public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script) + public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope) { var types = _directTypeManipulation ? variables.Select(x => x.GetType()).ToArray() @@ -44,6 +48,8 @@ namespace Upsilon.BaseTypes.ScriptFunction var objects = new List(); if (_passScriptReference) objects.Add(script); + if (_passScopeReference) + objects.Add(scope); objects.AddRange(_directTypeManipulation ? variables.Select(x => (object) x).ToList() : variables.Select(x => x.ToCSharpObject()).ToList()); diff --git a/Upsilon/BaseTypes/ScriptFunction/ScriptRuntimeFunction.cs b/Upsilon/BaseTypes/ScriptFunction/ScriptRuntimeFunction.cs index 3050412..a52bfcd 100644 --- a/Upsilon/BaseTypes/ScriptFunction/ScriptRuntimeFunction.cs +++ b/Upsilon/BaseTypes/ScriptFunction/ScriptRuntimeFunction.cs @@ -19,7 +19,7 @@ namespace Upsilon.BaseTypes.ScriptFunction EvaluationScope = evaluationScope; } - public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script) + public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope) { var innerEvaluator = new Evaluator.Evaluator(diagnostics, EvaluationScope, script); for (var i = 0; i < Parameters.Length; i++) diff --git a/Upsilon/BaseTypes/TypeConversion.cs b/Upsilon/BaseTypes/TypeConversion.cs index a33cbb7..4bb6b10 100644 --- a/Upsilon/BaseTypes/TypeConversion.cs +++ b/Upsilon/BaseTypes/TypeConversion.cs @@ -100,9 +100,10 @@ namespace Upsilon.BaseTypes return Type.Table; if (t == typeof(IIterable)) return Type.Nil; + if (t == typeof(ScriptType)) + return Type.Unknown; - - throw new ArgumentException(t.ToString()); + return Type.UserData; } } diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index 7ad3ca8..3ec419f 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -267,10 +267,26 @@ namespace Upsilon.Binder if (indexerVariable.Type == Type.UserData) { var bDefProperty = ((UserDataBoundTypeDefinition) ((FunctionParameterSymbol) indexerVariable) - .BoundTypeDefinition).Properties[fullStopIndexExpression.Index]; + .BoundTypeDefinition).Properties[fullStopIndexExpression.Index.ToLowerInvariant()]; var boundDef = BoundTypeHandler.GetTypeDefinition(bDefProperty.ActualType); return new FunctionParameterSymbol(fullStopIndexExpression.Index, boundDef); } + + if (indexerVariable.Type == Type.Unknown) + { + if (indexerVariable is FunctionParameterSymbol funcSymbol) + { + var boundProp = funcSymbol.BoundTypeDefinition; + if (boundProp is UserDataBoundTypeDefinition bProp) + { + var property = bProp.Properties[fullStopIndexExpression.Index.ToLowerInvariant()]; + var boundDef = BoundTypeHandler.GetTypeDefinition(property.ActualType); + return new FunctionParameterSymbol(fullStopIndexExpression.Index, boundDef); + } + } + + return new VariableSymbol(fullStopIndexExpression.Index, Type.Unknown, true); + } } return null; } @@ -699,7 +715,7 @@ namespace Upsilon.Binder { var functionParameter = (FunctionParameterSymbol) variableSymbol; var udBoundDef = (UserDataBoundTypeDefinition)functionParameter.BoundTypeDefinition; - if (udBoundDef.Properties.TryGetValue(index, out var property)) + if (udBoundDef.Properties.TryGetValue(index.ToLowerInvariant(), out var property)) { return new BoundFullStopIndexExpression(expression, index, property.Type, e.Span); } diff --git a/Upsilon/Binder/BoundBinaryOperator.cs b/Upsilon/Binder/BoundBinaryOperator.cs index 599279b..f83cf75 100644 --- a/Upsilon/Binder/BoundBinaryOperator.cs +++ b/Upsilon/Binder/BoundBinaryOperator.cs @@ -67,6 +67,16 @@ namespace Upsilon.Binder // String equality new BoundBinaryOperator(OperatorKind.Equality, Type.String, Type.String, Type.Boolean), new BoundBinaryOperator(OperatorKind.Inequality, Type.String, Type.String, Type.Boolean), + + // general nil checks + new BoundBinaryOperator(OperatorKind.Equality, Type.String, Type.Nil, Type.Boolean), + new BoundBinaryOperator(OperatorKind.Inequality, Type.String, Type.Nil, Type.Boolean), + new BoundBinaryOperator(OperatorKind.Equality, Type.UserData, Type.Nil, Type.Boolean), + new BoundBinaryOperator(OperatorKind.Inequality, Type.UserData, Type.Nil, Type.Boolean), + new BoundBinaryOperator(OperatorKind.Equality, Type.Table, Type.Nil, Type.Boolean), + new BoundBinaryOperator(OperatorKind.Inequality, Type.Table, Type.Nil, Type.Boolean), + new BoundBinaryOperator(OperatorKind.Equality, Type.Unknown, Type.Nil, Type.Boolean), + new BoundBinaryOperator(OperatorKind.Inequality, Type.Unknown, Type.Nil, Type.Boolean), }; public static BoundBinaryOperator Bind(SyntaxKind operatorToken, Type left, Type right) diff --git a/Upsilon/BoundTypes/BoundTypeHandler.cs b/Upsilon/BoundTypes/BoundTypeHandler.cs index e4f5182..d63a05e 100644 --- a/Upsilon/BoundTypes/BoundTypeHandler.cs +++ b/Upsilon/BoundTypes/BoundTypeHandler.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Generic; +using System.Linq; using Upsilon.BaseTypes; namespace Upsilon.BoundTypes @@ -38,6 +39,10 @@ namespace Upsilon.BoundTypes } return null; } + public static BoundTypeDefinition GetTypeDefinition(System.Type type) + { + return _typeDefinitions.Values.FirstOrDefault(x => x.ValidInternalTypes.Contains(type)); + } public static void LoadUserDataTypeDefinition(UserDataBoundTypeDefinition def) { diff --git a/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs b/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs index 98b60ab..9343c95 100644 --- a/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs +++ b/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Upsilon.BaseTypes; namespace Upsilon.BoundTypes @@ -12,6 +13,38 @@ namespace Upsilon.BoundTypes : base(Type.UserData, backingType) { Name = backingType.Name; + Properties = new Dictionary(); + var fields = backingType.GetFields().Select(x => new UserDataBoundProperty() + { + Name = x.Name, + ActualType = x.FieldType.Name, + Type = x.FieldType.GetScriptType(), + }); + foreach (var f in fields) + { + Properties.Add(f.Name.ToLowerInvariant(), f); + } + var properties = backingType.GetProperties().Select(x => new UserDataBoundProperty() + { + Name = x.Name, + ActualType = x.PropertyType.Name, + Type = x.PropertyType.GetScriptType(), + }); + foreach (var f in properties) + { + Properties.Add(f.Name.ToLowerInvariant(), f); + } + var methods = backingType.GetMethods().Select(x => new UserDataBoundMethod() + { + Name = x.Name, + Type = Type.Function, + ResultType = x.ReturnType.GetScriptType() + }); + foreach (var f in methods) + { + Properties.Add(f.Name.ToLowerInvariant(), f); + } + } public UserDataBoundTypeDefinition(string name, Dictionary properties) @@ -26,7 +59,14 @@ namespace Upsilon.BoundTypes { public string Name { get; set; } public Type Type { get; set; } - public string ActualType { get; set; } + public virtual string ActualType { get; set; } public string Comment { get; set; } } + + public class UserDataBoundMethod: UserDataBoundProperty + { + public override string ActualType => "Function"; + public Type ResultType { get; set; } + } + } \ No newline at end of file diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 0ab8ad9..e8da4c4 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -484,7 +484,7 @@ namespace Upsilon.Evaluator ls.Add(evaluate); } - var val = function.Run(_diagnostics, ls.ToArray(), _script); + var val = function.Run(_diagnostics, ls.ToArray(), _script, Scope); return val; } diff --git a/Upsilon/Evaluator/Script.cs b/Upsilon/Evaluator/Script.cs index 6473ba6..83be4b3 100644 --- a/Upsilon/Evaluator/Script.cs +++ b/Upsilon/Evaluator/Script.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.Immutable; using Upsilon.BaseTypes; using Upsilon.Binder; @@ -39,14 +40,25 @@ namespace Upsilon.Evaluator private Script(string scriptString, Binder.Binder binder, Evaluator evaluator, ScriptOptions options) { - ScriptString = new SourceText(scriptString); - Options = options; + ScriptString = new SourceText(scriptString); + Options = options; _scriptString = scriptString; - Diagnostics = new Diagnostics(ScriptString, options.ThrowExceptionOnError); + Diagnostics = new Diagnostics(ScriptString, options.ThrowExceptionOnError); - Binder = Upsilon.Binder.Binder.CreateWithSetScope(Diagnostics, binder.Scope); - Evaluator = Evaluator.CreateWithSetScope(Diagnostics, evaluator.Scope, this); - Scope = Evaluator.Scope; + Binder = Upsilon.Binder.Binder.CreateWithSetScope(Diagnostics, binder.Scope); + Evaluator = Evaluator.CreateWithSetScope(Diagnostics, evaluator.Scope, this); + Scope = Evaluator.Scope; + } + + private Script(BoundScript boundScript, Binder.Binder binder, Evaluator evaluator, ScriptOptions options) + { + Options = options; + _bound = boundScript; + Diagnostics = new Diagnostics(ScriptString, options.ThrowExceptionOnError); + + Binder = Upsilon.Binder.Binder.CreateWithSetScope(Diagnostics, binder.Scope); + Evaluator = Evaluator.CreateWithSetScope(Diagnostics, evaluator.Scope, this); + Scope = Evaluator.Scope; } @@ -56,6 +68,12 @@ namespace Upsilon.Evaluator return s; } + internal static Script ContinueWith(Script previousScript, BoundScript boundScript) + { + var s = new Script(boundScript, previousScript.Binder, previousScript.Evaluator, previousScript.Options); + return s; + } + internal void Parse() { _parsed = Parser.Parser.Parse(_scriptString, Diagnostics, Options.SaveDataComments); @@ -103,6 +121,11 @@ namespace Upsilon.Evaluator return Convert(Evaluator.Evaluate(Bind(), functionName, parameters)); } + public Dictionary GetVariables() + { + return Evaluator.Scope.Variables; + } + public T GetVariable(string variableName) { if (!Scope.TryGet(variableName, out var variable)) diff --git a/Upsilon/Evaluator/ScriptLoader.cs b/Upsilon/Evaluator/ScriptLoader.cs new file mode 100644 index 0000000..2c8c075 --- /dev/null +++ b/Upsilon/Evaluator/ScriptLoader.cs @@ -0,0 +1,55 @@ +using System.IO; + +namespace Upsilon.Evaluator +{ + public class ScriptLoader + { + public virtual string FilePath { get; } = "./"; + public virtual string ModulesPath { get; } = "./modules/"; + + public virtual string LoadFile(string fileName) + { + var path = FilePath; + var resolvedName = ResolveFileName(FilePath, fileName); + if (File.Exists(resolvedName)) + { + return File.ReadAllText(resolvedName); + } + else if (File.Exists(resolvedName + ".yup")) + { + return File.ReadAllText(resolvedName + ".yup"); + } + else if (File.Exists(resolvedName + ".lua")) + { + return File.ReadAllText(resolvedName + ".lua"); + } + return null; + } + + public virtual string LoadModule(string fileName) + { + var resolvedName = ResolveFileName(ModulesPath, fileName); + string text = null; + if (File.Exists(resolvedName)) + { + text = File.ReadAllText(resolvedName); + } + else if (File.Exists(resolvedName + ".yup")) + { + text = File.ReadAllText(resolvedName + ".yup"); + } + else if (File.Exists(resolvedName + ".lua")) + { + text = File.ReadAllText(resolvedName + ".lua"); + } + + return text; + } + + + public virtual string ResolveFileName(string path, string fileName) + { + return Path.Combine(path, fileName); + } + } +} \ No newline at end of file diff --git a/Upsilon/Executor.cs b/Upsilon/Executor.cs index 446c8d9..325bfe1 100644 --- a/Upsilon/Executor.cs +++ b/Upsilon/Executor.cs @@ -1,3 +1,4 @@ +using Upsilon.Binder; using Upsilon.Evaluator; namespace Upsilon @@ -9,7 +10,16 @@ namespace Upsilon public static Script ParseInput(string input, ScriptOptions options = null) { if (options == null) options = DefaultOptions; - var script = new Script(input, options); + var script = new Script(input, options); + script.Parse(); + return script; + } + + public static Script ParseFile(string fileName, ScriptOptions options = null) + { + if (options == null) options = DefaultOptions; + var input = options.ScriptLoader.LoadFile(fileName); + var script = new Script(input, options); script.Parse(); return script; } @@ -23,6 +33,16 @@ namespace Upsilon return script; } + public static Script ParseFileAndEvaluate(string fileName, ScriptOptions options = null) + { + if (options == null) options = DefaultOptions; + var input = options.ScriptLoader.LoadFile(fileName); + var script = new Script(input, options); + script.Parse(); + script.Evaluate(); + return script; + } + public static Script ContinueWith(Script script, string input) { var s = Script.ContinueWith(script, input); @@ -30,6 +50,13 @@ namespace Upsilon return s; } + public static Script ContinueWith(Script script, BoundScript bound) + { + var s = Script.ContinueWith(script, bound); + s.Parse(); + return s; + } + public static T EvaluateScript(string input, ScriptOptions options = null) { var script = ParseInput(input, options); diff --git a/Upsilon/Parser/Parser.cs b/Upsilon/Parser/Parser.cs index 4504581..6f92282 100644 --- a/Upsilon/Parser/Parser.cs +++ b/Upsilon/Parser/Parser.cs @@ -70,10 +70,6 @@ namespace Upsilon.Parser { return ParseAssignmentExpression(); } - if (Current.Kind == SyntaxKind.Identifier && Next.Kind == SyntaxKind.Comma) - { - return ParseAssignmentExpression(); - } if (Current.Kind == SyntaxKind.IfKeyword) { return ParseIfStatement(SyntaxKind.IfKeyword); diff --git a/Upsilon/ScriptOptions.cs b/Upsilon/ScriptOptions.cs index e559045..fa9b05c 100644 --- a/Upsilon/ScriptOptions.cs +++ b/Upsilon/ScriptOptions.cs @@ -20,5 +20,6 @@ namespace Upsilon public BoundScope OverrideStaticBoundScope { get; set; } = null; public EvaluationScope OverrideStaticScope { get; set; } = null; + public ScriptLoader ScriptLoader { get; set; } = null; } } \ No newline at end of file diff --git a/Upsilon/StandardLibraries/BasicFunctions.cs b/Upsilon/StandardLibraries/BasicFunctions.cs index e1a54b1..cb7e7d1 100644 --- a/Upsilon/StandardLibraries/BasicFunctions.cs +++ b/Upsilon/StandardLibraries/BasicFunctions.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using Upsilon.BaseTypes; using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.ScriptTypeInterfaces; +using Upsilon.Binder; using Upsilon.Evaluator; // ReSharper disable UnusedMember.Global @@ -39,10 +41,29 @@ namespace Upsilon.StandardLibraries return new PairsScriptIterator(table); } - [StandardLibraryScriptFunction("require", "Loads a module from the module path, using the given script loader.", true)] - public void Require(Script script, string file) - { + private static readonly Dictionary CachedBoundModules = new Dictionary(); + [StandardLibraryScriptFunction("require", "Loads a module from the module path, using the given script loader.", + true)] + public ScriptType Require(Script script, string file) + { + var loader = script.Options.ScriptLoader; + if (loader != null) + { + if (!CachedBoundModules.TryGetValue(file, out var boundScript)) + { + var loadedScript = loader.LoadModule(file); + boundScript = Executor.ParseInput(loadedScript).Bind(); + CachedBoundModules.Add(file, boundScript); + } + var s = Executor.ContinueWith(script, boundScript); + var val = s.Evaluate(); + if (val != null) + { + return val.ToScriptType(); + } + } + return new ScriptNull(); } diff --git a/Upsilon/StandardLibraries/ScriptLibrary.cs b/Upsilon/StandardLibraries/ScriptLibrary.cs index ec20497..9104931 100644 --- a/Upsilon/StandardLibraries/ScriptLibrary.cs +++ b/Upsilon/StandardLibraries/ScriptLibrary.cs @@ -19,7 +19,8 @@ namespace Upsilon.StandardLibraries { dictionary.Add(attr.Name, new LoadedStandardFunction() { - MethodInfoFunction = new ScriptMethodInfoFunction(new UserDataMethod(methodInfo), this, true), + MethodInfoFunction = new ScriptMethodInfoFunction(new UserDataMethod(methodInfo), + this, true, attr.PassScriptReference, attr.PassScopeReference), CommentValue = attr.Comment }); } diff --git a/Upsilon/StandardLibraries/StandardLibraryScriptFunctionAttribute.cs b/Upsilon/StandardLibraries/StandardLibraryScriptFunctionAttribute.cs index 0426507..a049259 100644 --- a/Upsilon/StandardLibraries/StandardLibraryScriptFunctionAttribute.cs +++ b/Upsilon/StandardLibraries/StandardLibraryScriptFunctionAttribute.cs @@ -5,15 +5,18 @@ namespace Upsilon.StandardLibraries [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] public class StandardLibraryScriptFunctionAttribute : Attribute { - public StandardLibraryScriptFunctionAttribute(string name, string comment = null, bool passScriptReference = false) + public StandardLibraryScriptFunctionAttribute(string name, string comment = null, + bool passScriptReference = false, bool passScopeReference = false) { Name = name; Comment = comment; PassScriptReference = passScriptReference; + PassScopeReference = passScopeReference; } public string Name { get; } public string Comment { get; } public bool PassScriptReference { get; } + public bool PassScopeReference { get; } } } \ No newline at end of file diff --git a/Upsilon/StandardLibraries/StaticScope.cs b/Upsilon/StandardLibraries/StaticScope.cs index 3cd5b37..5180a47 100644 --- a/Upsilon/StandardLibraries/StaticScope.cs +++ b/Upsilon/StandardLibraries/StaticScope.cs @@ -6,6 +6,7 @@ using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.Binder; using Upsilon.Binder.VariableSymbols; +using Upsilon.BoundTypes; using Upsilon.Evaluator; using Type = Upsilon.BaseTypes.Type; @@ -73,7 +74,8 @@ namespace Upsilon.StandardLibraries public static void RegisterStaticVariable(string name, object value) { var luaVariable = value.ToScriptType(); - var varSymbol = new VariableSymbol(name, luaVariable.Type, false); + var ubDef = BoundTypeHandler.GetTypeDefinition(value.GetType()); + var varSymbol = new FunctionParameterSymbol(name, ubDef); BoundScope.AssignToNearest(varSymbol); Scope.AssignToNearest(varSymbol, luaVariable); } diff --git a/Upsilon/Text/SourceText.cs b/Upsilon/Text/SourceText.cs index 6e99de0..f5e0a29 100644 --- a/Upsilon/Text/SourceText.cs +++ b/Upsilon/Text/SourceText.cs @@ -10,6 +10,11 @@ namespace Upsilon.Text public SourceText(string text) { _text = text; + if (string.IsNullOrEmpty(text)) + { + _lines = new SourceTextLine[0]; + return; + } var lines = text.Split(new[] {Environment.NewLine}, StringSplitOptions.None); _lines = new SourceTextLine[lines.Length]; var linePos = 0;