From fd95e3cc79052d749c3ff7bb7cbae27813c0cae7 Mon Sep 17 00:00:00 2001 From: lofcz Date: Sat, 8 Oct 2022 14:40:28 +0200 Subject: [PATCH 1/9] Using, wip --- .../DataTypes/Module.cs | 37 ++++++ .../Execution/ScriptLoadingContext.cs | 1 + src/WattleScript.Interpreter/Script.cs | 2 + .../Tree/Fast_Interface/Loader_Fast.cs | 18 +-- .../Tree/Lexer/Lexer.cs | 49 ++++++- .../Tree/Lexer/Token.cs | 2 + .../Tree/Lexer/TokenType.cs | 2 + src/WattleScript.Interpreter/Tree/NodeBase.cs | 2 +- .../Tree/Using/Using.cs | 122 ++++++++++++++++++ .../CLike/SyntaxCLike/Using/1-using.lua | 3 + .../CLike/SyntaxCLike/Using/1-using.txt | 1 + .../Using/ws.tests.classTest.wtlib | 5 + .../EndToEnd/CLikeTestRunner.cs | 24 +++- .../EndToEnd/CSyntaxTests.cs | 6 +- 14 files changed, 252 insertions(+), 22 deletions(-) create mode 100644 src/WattleScript.Interpreter/DataTypes/Module.cs create mode 100644 src/WattleScript.Interpreter/Tree/Using/Using.cs create mode 100644 src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.lua create mode 100644 src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.txt create mode 100644 src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/ws.tests.classTest.wtlib diff --git a/src/WattleScript.Interpreter/DataTypes/Module.cs b/src/WattleScript.Interpreter/DataTypes/Module.cs new file mode 100644 index 00000000..2331d2cf --- /dev/null +++ b/src/WattleScript.Interpreter/DataTypes/Module.cs @@ -0,0 +1,37 @@ +using System.IO; + +namespace WattleScript.Interpreter +{ + public class Module + { + /// + /// Gets/sets the Wattlescript source code of this module + /// + public string Code { get; set; } + + /// + /// Gets/sets the Wattlescript bytecode of this module. If not null, this has priority over + /// + public byte[] Bytecode { get; set; } + + /// + /// Gets/sets the Stream with Wattlescript bytecode of this module. If not null, this has priority over + /// + public Stream Stream { get; set; } + + public Module(string code) + { + Code = code; + } + + public Module(byte[] bytecode) + { + Bytecode = bytecode; + } + + public Module(Stream stream) + { + Stream = stream; + } + } +} \ No newline at end of file diff --git a/src/WattleScript.Interpreter/Execution/ScriptLoadingContext.cs b/src/WattleScript.Interpreter/Execution/ScriptLoadingContext.cs index 237cf078..ad15e564 100644 --- a/src/WattleScript.Interpreter/Execution/ScriptLoadingContext.cs +++ b/src/WattleScript.Interpreter/Execution/ScriptLoadingContext.cs @@ -14,6 +14,7 @@ class ScriptLoadingContext public Lexer Lexer { get; set; } public ScriptSyntax Syntax { get; set; } + public Dictionary Modules { get; set; } //Compiler state internal List ChunkAnnotations { get; set; } = new List(); diff --git a/src/WattleScript.Interpreter/Script.cs b/src/WattleScript.Interpreter/Script.cs index a311d02c..d1f1d1fb 100755 --- a/src/WattleScript.Interpreter/Script.cs +++ b/src/WattleScript.Interpreter/Script.cs @@ -145,6 +145,8 @@ public Script(CoreModules coreModules) /// public Table Globals => m_GlobalTable; + public Func UsingHandler { get; set; } + /// /// Loads a string containing a Lua/WattleScript function. /// diff --git a/src/WattleScript.Interpreter/Tree/Fast_Interface/Loader_Fast.cs b/src/WattleScript.Interpreter/Tree/Fast_Interface/Loader_Fast.cs index e0123c35..83243966 100644 --- a/src/WattleScript.Interpreter/Tree/Fast_Interface/Loader_Fast.cs +++ b/src/WattleScript.Interpreter/Tree/Fast_Interface/Loader_Fast.cs @@ -36,15 +36,14 @@ internal static DynamicExprExpression LoadDynamicExpr(Script script, SourceCode } } - private static ScriptLoadingContext CreateLoadingContext(Script script, SourceCode source, - string preprocessedCode = null, - Dictionary defines = null) + internal static ScriptLoadingContext CreateLoadingContext(Script script, SourceCode source, string preprocessedCode = null, Dictionary defines = null, bool lexerAutoSkipComments = true, bool lexerKeepInsignificantChars = false, Dictionary staticModules = null) { return new ScriptLoadingContext(script) { Source = source, - Lexer = new Lexer(source.SourceID, preprocessedCode ?? source.Code, true, script.Options.Syntax, script.Options.Directives, defines), - Syntax = script.Options.Syntax + Lexer = new Lexer(source.SourceID, preprocessedCode ?? source.Code, lexerAutoSkipComments, script.Options.Syntax, script.Options.Directives, defines, lexerKeepInsignificantChars), + Syntax = script.Options.Syntax, + Modules = staticModules }; } @@ -62,10 +61,13 @@ internal static FunctionProto LoadChunk(Script script, SourceCode source) { if (script.Options.Syntax == ScriptSyntax.Wattle) { - var preprocess = new Preprocessor(script, source.SourceID, source.Code); + Preprocessor preprocess = new Preprocessor(script, source.SourceID, source.Code); preprocess.Process(); - lcontext = CreateLoadingContext(script, source, preprocess.ProcessedSource, - preprocess.Defines); + + Using us = new Using(script, source.SourceID, preprocess.ProcessedSource, preprocess.Defines); + us.Process(); + + lcontext = CreateLoadingContext(script, source, preprocess.ProcessedSource, preprocess.Defines, staticModules: us.ResolvedUsings); } else { diff --git a/src/WattleScript.Interpreter/Tree/Lexer/Lexer.cs b/src/WattleScript.Interpreter/Tree/Lexer/Lexer.cs index 631655a3..ea10fd5e 100755 --- a/src/WattleScript.Interpreter/Tree/Lexer/Lexer.cs +++ b/src/WattleScript.Interpreter/Tree/Lexer/Lexer.cs @@ -20,11 +20,19 @@ class Lexer private ScriptSyntax m_Syntax; private HashSet m_Directives; private Dictionary m_Defines; + private bool keepInsignificantChars; + private StringBuilder whitespaceStringBuilder; - public Lexer(int sourceID, string scriptContent, bool autoSkipComments, ScriptSyntax syntax, HashSet directives, Dictionary defines) + public Lexer(int sourceID, string scriptContent, bool autoSkipComments, ScriptSyntax syntax, HashSet directives, Dictionary defines, bool keepInsignificantChars = false) { m_Code = scriptContent; m_SourceId = sourceID; + this.keepInsignificantChars = keepInsignificantChars; + + if (keepInsignificantChars) + { + whitespaceStringBuilder = new StringBuilder(); + } // remove unicode BOM if any if (m_Code.Length > 0 && m_Code[0] == 0xFEFF) @@ -210,6 +218,11 @@ private char CursorCharNext() return CursorChar(); } + private char CursorCharPeekNext() + { + return m_Cursor + 1 < m_Code.Length ? m_Code[m_Cursor + 1] : '\0'; + } + private bool CursorMatches(string pattern) { for (int i = 0; i < pattern.Length; i++) @@ -233,6 +246,18 @@ private bool IsWhiteSpace(char c) { return char.IsWhiteSpace(c); } + + private bool IsWhiteSpace(char c, StringBuilder sb) + { + bool isWs = char.IsWhiteSpace(c); + + if (isWs) + { + sb.Append(c); + } + + return isWs; + } private void SkipWhiteSpace() { @@ -240,6 +265,15 @@ private void SkipWhiteSpace() { } } + + private void StashWhiteSpace() + { + whitespaceStringBuilder.Clear(); + + for (; CursorNotEof() && IsWhiteSpace(CursorChar(), whitespaceStringBuilder); CursorNext()) + { + } + } void ProcessLineDirective(string directive, int line, int col) { @@ -273,7 +307,14 @@ void ProcessLineDirective(string directive, int line, int col) private Token ReadToken() { - SkipWhiteSpace(); + if (keepInsignificantChars) + { + StashWhiteSpace(); + } + else + { + SkipWhiteSpace(); + } int fromLine = m_Line; int fromCol = m_Col; @@ -574,7 +615,7 @@ private Token ReadToken() case '#' when m_Syntax == ScriptSyntax.Wattle: if (m_Cursor == 0 && m_Code.Length > 1 && m_Code[1] == '!') return ReadHashBang(fromLine, fromCol); - else if (m_StartOfLine && CursorMatches("#line")) + else if (CursorMatches("#line")) // [todo] fix { //Read in line CursorNext(); @@ -990,7 +1031,7 @@ private Token ReadComment(int fromLine, int fromCol) text.Append(c); } } - + return CreateToken(TokenType.Comment, fromLine, fromCol, text.ToString()); } diff --git a/src/WattleScript.Interpreter/Tree/Lexer/Token.cs b/src/WattleScript.Interpreter/Tree/Lexer/Token.cs index 76e33b3f..90b74e38 100644 --- a/src/WattleScript.Interpreter/Tree/Lexer/Token.cs +++ b/src/WattleScript.Interpreter/Tree/Lexer/Token.cs @@ -64,6 +64,8 @@ public override string ToString() return TokenType.Mixin; case "static": return TokenType.Static; + case "using": + return TokenType.Using; } } diff --git a/src/WattleScript.Interpreter/Tree/Lexer/TokenType.cs b/src/WattleScript.Interpreter/Tree/Lexer/TokenType.cs index adfbfc34..5352c151 100644 --- a/src/WattleScript.Interpreter/Tree/Lexer/TokenType.cs +++ b/src/WattleScript.Interpreter/Tree/Lexer/TokenType.cs @@ -123,6 +123,8 @@ enum TokenType Static, + Using, + Preprocessor_Defined //Reserved only in preprocessor } diff --git a/src/WattleScript.Interpreter/Tree/NodeBase.cs b/src/WattleScript.Interpreter/Tree/NodeBase.cs index 028576fa..77adac8e 100644 --- a/src/WattleScript.Interpreter/Tree/NodeBase.cs +++ b/src/WattleScript.Interpreter/Tree/NodeBase.cs @@ -40,7 +40,7 @@ protected static Token CheckTokenTypeEx(ScriptLoadingContext lcontext, TokenType return CheckTokenType(lcontext, tokenType1); } - protected static Token CheckTokenType(ScriptLoadingContext lcontext, TokenType tokenType) + internal static Token CheckTokenType(ScriptLoadingContext lcontext, TokenType tokenType) { Token t = lcontext.Lexer.Current; if (t.Type != tokenType) diff --git a/src/WattleScript.Interpreter/Tree/Using/Using.cs b/src/WattleScript.Interpreter/Tree/Using/Using.cs new file mode 100644 index 00000000..660c5b75 --- /dev/null +++ b/src/WattleScript.Interpreter/Tree/Using/Using.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using WattleScript.Interpreter.Execution; +using WattleScript.Interpreter.Tree.Fast_Interface; +using static WattleScript.Interpreter.Tree.NodeBase; + +namespace WattleScript.Interpreter.Tree +{ + internal class Using + { + private StringBuilder output; + private Script script; + private int sourceIndex; + private bool outputChars = true; + private ScriptLoadingContext lcontext; + private bool firstUsingEncountered = false; + private bool anyNonUsingEncounterd = false; + private string text; + private StringBuilder usingIdent = new StringBuilder(); + + //public string ProcessedSource => output.ToString(); + public Dictionary ResolvedUsings = new Dictionary(); + + + public Using(Script script, int sourceIndex, string text, Dictionary defines = null) + { + this.script = script; + this.sourceIndex = sourceIndex; + this.text = text; + + output = new StringBuilder(); + lcontext = Loader_Fast.CreateLoadingContext(script, script.GetSourceCode(sourceIndex), text, defines, false, true); + } + + void PushToOutput(string str) + { + output.Append(str); + } + + int ProcessUsingStatement() + { + CheckTokenType(lcontext, TokenType.Using); + int currentLineFrom = lcontext.Lexer.Current.FromLine; + bool canBeDot = false; + int charTo = 0; + Token prev = null; + usingIdent.Clear(); + + while (lcontext.Lexer.PeekNext().Type != TokenType.Eof) + { + Token tkn = lcontext.Lexer.Current; + + if (tkn.FromLine != currentLineFrom) + { + break; + } + + prev = lcontext.Lexer.Current; + + canBeDot = canBeDot switch + { + false when tkn.Type != TokenType.Name => throw new SyntaxErrorException(tkn, $"unexpected token '{tkn.Text}' found in using statement"), + true when tkn.Type != TokenType.Dot => throw new SyntaxErrorException(tkn, $"unexpected token '{tkn.Text}' found in using statement"), + _ => !canBeDot + }; + + usingIdent.Append(tkn.Text); + lcontext.Lexer.Next(); + } + + string usingIdentStr = usingIdent.ToString(); + + if (ResolvedUsings.ContainsKey(usingIdentStr)) + { + throw new SyntaxErrorException(prev, $"duplicate using '{usingIdentStr}' found"); + } + + ResolvedUsings.Add(usingIdentStr, script.UsingHandler(usingIdentStr)); + + return prev?.CharIndexTo ?? 0; + } + + public void Process() + { + if (!text.Contains("using")) // heuristic, will trip on comments, variable names, etc. but might be worth it + { + // PushToOutput(text); + // return; + } + + int previousCharTo = 0; + + + while (lcontext.Lexer.PeekNext().Type != TokenType.Eof) + { + lcontext.Lexer.Next(); + afterUsingStatement: + Token tkn = lcontext.Lexer.Current; + + + switch (tkn.Type) + { + case TokenType.Using: + firstUsingEncountered = true; + previousCharTo = ProcessUsingStatement(); + goto afterUsingStatement; + default: + anyNonUsingEncounterd = true; + PushToOutput(text.Substring(previousCharTo, tkn.CharIndexTo - previousCharTo)); + break; + } + + previousCharTo = tkn.CharIndexTo; + } + + string str = output.ToString(); + int z = 0; + } + } +} \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.lua b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.lua new file mode 100644 index 00000000..f5bc50cb --- /dev/null +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.lua @@ -0,0 +1,3 @@ +using ws.tests.classTest + +new C().print("hello") \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.txt b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.txt new file mode 100644 index 00000000..b6fc4c62 --- /dev/null +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.txt @@ -0,0 +1 @@ +hello \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/ws.tests.classTest.wtlib b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/ws.tests.classTest.wtlib new file mode 100644 index 00000000..d1caf6ba --- /dev/null +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/ws.tests.classTest.wtlib @@ -0,0 +1,5 @@ +class C() { + function print(x) { + print(x) + } +} \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs b/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs index d32f67ef..d551b01a 100644 --- a/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs +++ b/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs @@ -34,7 +34,7 @@ public async Task RunReportErrors(string path) await RunCore(path, true); } - Script InitScript() + Script InitScript(string scriptFolderPath) { Script script = new Script(CoreModules.Preset_SoftSandboxWattle); script.Options.IndexTablesFrom = 0; @@ -46,6 +46,16 @@ Script InitScript() script.Options.AnnotationPolicy = new CustomPolicy(AnnotationValueParsingPolicy.ForceTable); script.Globals["CurrentLine"] = (ScriptExecutionContext c, CallbackArguments a) => c.CallingLocation.FromLine; script.Globals["CurrentColumn"] = (ScriptExecutionContext c, CallbackArguments a) => c.CallingLocation.FromChar; + script.UsingHandler = (path) => + { + if (File.Exists($"{scriptFolderPath}\\{path}.wtlib")) + { + string libText = File.ReadAllText($"{scriptFolderPath}\\{path}.wtlib"); + return new Module(libText); + } + + return null; + }; return script; } @@ -60,11 +70,13 @@ public async Task RunCore(string path, bool reportErrors = false) return; } + string rootPath = Path.GetFullPath(Path.Combine(outputPath.Replace(".lua", ""), "..\\")); string code = await File.ReadAllTextAsync(path); string output = await File.ReadAllTextAsync(outputPath); stdOut = new StringBuilder(); - Script script = InitScript(); + Script script = InitScript(rootPath); + if (path.Contains("flaky")) { Assert.Inconclusive($"Test {path} marked as flaky"); @@ -93,7 +105,7 @@ public async Task RunCore(string path, bool reportErrors = false) if (TEST_SOURCE_REFS_DUMP) { - Exception e = TestSourceRefsBinDump(code); + Exception e = TestSourceRefsBinDump(code, rootPath); if (e != null) { Assert.Fail($"Dumped source refs not equal to original.\n{e.Message}\n{e.StackTrace}"); @@ -128,16 +140,16 @@ public async Task RunCore(string path, bool reportErrors = false) } } - public Exception TestSourceRefsBinDump(string code) + public Exception TestSourceRefsBinDump(string code, string path) { - Script sc = InitScript(); + Script sc = InitScript(path); DynValue dv = sc.LoadString(code); IReadOnlyList originalSourceRefs = dv.Function.Function.SourceRefs; using MemoryStream ms = new MemoryStream(); sc.Dump(dv, ms); ms.Seek(0, SeekOrigin.Begin); - sc = InitScript(); + sc = InitScript(path); dv = sc.LoadStream(ms); dv.Function.Call(); diff --git a/src/WattleScript.Tests/EndToEnd/CSyntaxTests.cs b/src/WattleScript.Tests/EndToEnd/CSyntaxTests.cs index b964332e..f3d30860 100644 --- a/src/WattleScript.Tests/EndToEnd/CSyntaxTests.cs +++ b/src/WattleScript.Tests/EndToEnd/CSyntaxTests.cs @@ -144,10 +144,10 @@ public void UsingDirective() { var s = new Script(); s.Options.Syntax = ScriptSyntax.Wattle; - s.Options.Directives.Add("using"); - var chunk = s.LoadString("using a.b.c;"); + s.Options.Directives.Add("usingDirective"); + var chunk = s.LoadString("usingDirective a.b.c;"); var a = chunk.Function.Annotations[0]; - Assert.AreEqual("using", a.Name); + Assert.AreEqual("usingDirective", a.Name); Assert.AreEqual("a.b.c", a.Value.String); } From 757d56390669affb874da8969603039a88c125f9 Mon Sep 17 00:00:00 2001 From: lofcz Date: Tue, 11 Oct 2022 19:18:26 +0200 Subject: [PATCH 2/9] Using -> Linker --- .../DataTypes/Module.cs | 9 + .../Execution/ScriptLoadingContext.cs | 2 +- .../Loaders/IScriptLoader.cs | 6 + .../Loaders/InvalidScriptLoader.cs | 4 + .../Loaders/ScriptLoaderBase.cs | 6 + src/WattleScript.Interpreter/Script.cs | 5 +- src/WattleScript.Interpreter/ScriptOptions.cs | 5 + .../Tree/Fast_Interface/Loader_Fast.cs | 23 ++- .../Tree/IStaticallyImportableStatement.cs | 15 ++ .../Tree/Linker/Linker.cs | 191 ++++++++++++++++++ .../Tree/Statement.cs | 42 ++++ .../Tree/Statements/ChunkStatement.cs | 9 +- .../Statements/ClassDefinitionStatement.cs | 6 +- .../Tree/Statements/CompositeStatement.cs | 12 ++ .../Statements/EnumDefinitionStatement.cs | 6 +- .../Statements/MixinDefinitionStatement.cs | 6 +- .../Tree/Using/Using.cs | 122 ----------- .../CLike/SyntaxCLike/Using/1-using.lua | 7 +- .../CLike/SyntaxCLike/Using/1-using.txt | 2 +- .../Using/ws.tests.classTest.wtlib | 10 +- .../EndToEnd/CLikeTestRunner.cs | 7 +- 21 files changed, 346 insertions(+), 149 deletions(-) create mode 100644 src/WattleScript.Interpreter/Tree/IStaticallyImportableStatement.cs create mode 100644 src/WattleScript.Interpreter/Tree/Linker/Linker.cs delete mode 100644 src/WattleScript.Interpreter/Tree/Using/Using.cs diff --git a/src/WattleScript.Interpreter/DataTypes/Module.cs b/src/WattleScript.Interpreter/DataTypes/Module.cs index 2331d2cf..0188cb44 100644 --- a/src/WattleScript.Interpreter/DataTypes/Module.cs +++ b/src/WattleScript.Interpreter/DataTypes/Module.cs @@ -4,6 +4,8 @@ namespace WattleScript.Interpreter { public class Module { + public static Module LocalModule = new Module(true); + /// /// Gets/sets the Wattlescript source code of this module /// @@ -19,6 +21,13 @@ public class Module /// public Stream Stream { get; set; } + internal bool IsEntryRef { get; set; } + + internal Module(bool isEntryRef) + { + IsEntryRef = isEntryRef; + } + public Module(string code) { Code = code; diff --git a/src/WattleScript.Interpreter/Execution/ScriptLoadingContext.cs b/src/WattleScript.Interpreter/Execution/ScriptLoadingContext.cs index ad15e564..ff9c1205 100644 --- a/src/WattleScript.Interpreter/Execution/ScriptLoadingContext.cs +++ b/src/WattleScript.Interpreter/Execution/ScriptLoadingContext.cs @@ -14,7 +14,7 @@ class ScriptLoadingContext public Lexer Lexer { get; set; } public ScriptSyntax Syntax { get; set; } - public Dictionary Modules { get; set; } + public Linker Using { get; set; } //Compiler state internal List ChunkAnnotations { get; set; } = new List(); diff --git a/src/WattleScript.Interpreter/Loaders/IScriptLoader.cs b/src/WattleScript.Interpreter/Loaders/IScriptLoader.cs index 7df1e932..d1321222 100644 --- a/src/WattleScript.Interpreter/Loaders/IScriptLoader.cs +++ b/src/WattleScript.Interpreter/Loaders/IScriptLoader.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace WattleScript.Interpreter.Loaders { @@ -36,5 +37,10 @@ public interface IScriptLoader /// The global context. /// string ResolveModuleName(string modname, Table globalContext); + /// + /// Resolves the name of a module imported by "using" statement + /// + /// + public Func UsingResolver { get; set; } } } diff --git a/src/WattleScript.Interpreter/Loaders/InvalidScriptLoader.cs b/src/WattleScript.Interpreter/Loaders/InvalidScriptLoader.cs index a1ac4d76..3a0f88a1 100644 --- a/src/WattleScript.Interpreter/Loaders/InvalidScriptLoader.cs +++ b/src/WattleScript.Interpreter/Loaders/InvalidScriptLoader.cs @@ -15,6 +15,8 @@ internal InvalidScriptLoader(string frameworkname) @"Loading scripts from files is not automatically supported on {0}. Please implement your own IScriptLoader (possibly, extending ScriptLoaderBase for easier implementation), use a preexisting loader like EmbeddedResourcesScriptLoader or UnityAssetsScriptLoader or load scripts from strings.", frameworkname); + + UsingResolver = s => throw new PlatformNotSupportedException(m_Error); } public object LoadFile(string file, Table globalContext) @@ -31,5 +33,7 @@ public string ResolveModuleName(string modname, Table globalContext) { throw new PlatformNotSupportedException(m_Error); } + + public Func UsingResolver { get; set; } } } diff --git a/src/WattleScript.Interpreter/Loaders/ScriptLoaderBase.cs b/src/WattleScript.Interpreter/Loaders/ScriptLoaderBase.cs index 176c753c..52e287f4 100644 --- a/src/WattleScript.Interpreter/Loaders/ScriptLoaderBase.cs +++ b/src/WattleScript.Interpreter/Loaders/ScriptLoaderBase.cs @@ -75,6 +75,12 @@ public virtual string ResolveModuleName(string modname, Table globalContext) return ResolveModuleName(modname, this.ModulePaths); } + /// + /// Resolves the name of a module imported by "using" statement + /// + /// + public Func UsingResolver { get; set; } + /// /// Gets or sets the modules paths used by the "require" function. If null, the default paths are used (using /// environment variables etc.). diff --git a/src/WattleScript.Interpreter/Script.cs b/src/WattleScript.Interpreter/Script.cs index d1f1d1fb..ab563cea 100755 --- a/src/WattleScript.Interpreter/Script.cs +++ b/src/WattleScript.Interpreter/Script.cs @@ -109,7 +109,7 @@ public Script() /// The core modules to be pre-registered in the default global table. public Script(CoreModules coreModules) { - Options = new ScriptOptions(DefaultOptions); + Options = new ScriptOptions(DefaultOptions) { CoreModules = coreModules }; PerformanceStats = new PerformanceStatistics(); Registry = new Table(this); @@ -117,7 +117,6 @@ public Script(CoreModules coreModules) m_GlobalTable = new Table(this).RegisterCoreModules(coreModules); } - /// /// Gets or sets the script loader which will be used as the value of the /// ScriptLoader property for all newly created scripts. @@ -145,8 +144,6 @@ public Script(CoreModules coreModules) /// public Table Globals => m_GlobalTable; - public Func UsingHandler { get; set; } - /// /// Loads a string containing a Lua/WattleScript function. /// diff --git a/src/WattleScript.Interpreter/ScriptOptions.cs b/src/WattleScript.Interpreter/ScriptOptions.cs index 2536b7a6..3ec26d44 100644 --- a/src/WattleScript.Interpreter/ScriptOptions.cs +++ b/src/WattleScript.Interpreter/ScriptOptions.cs @@ -152,5 +152,10 @@ public enum ParserErrorModes /// If set to 0, this property is ignored (no limit is applied). /// public ulong InstructionLimit { get; set; } = 0; + + /// + /// Gets/sets CoreModules used in script. + /// + internal CoreModules CoreModules { get; set; } } } diff --git a/src/WattleScript.Interpreter/Tree/Fast_Interface/Loader_Fast.cs b/src/WattleScript.Interpreter/Tree/Fast_Interface/Loader_Fast.cs index 83243966..9dc1613b 100644 --- a/src/WattleScript.Interpreter/Tree/Fast_Interface/Loader_Fast.cs +++ b/src/WattleScript.Interpreter/Tree/Fast_Interface/Loader_Fast.cs @@ -36,44 +36,53 @@ internal static DynamicExprExpression LoadDynamicExpr(Script script, SourceCode } } - internal static ScriptLoadingContext CreateLoadingContext(Script script, SourceCode source, string preprocessedCode = null, Dictionary defines = null, bool lexerAutoSkipComments = true, bool lexerKeepInsignificantChars = false, Dictionary staticModules = null) + internal static ScriptLoadingContext CreateLoadingContext(Script script, SourceCode source, string preprocessedCode = null, Dictionary defines = null, bool lexerAutoSkipComments = true, bool lexerKeepInsignificantChars = false, Linker staticImport = null) { return new ScriptLoadingContext(script) { Source = source, Lexer = new Lexer(source.SourceID, preprocessedCode ?? source.Code, lexerAutoSkipComments, script.Options.Syntax, script.Options.Directives, defines, lexerKeepInsignificantChars), Syntax = script.Options.Syntax, - Modules = staticModules + Using = staticImport }; } - internal static FunctionProto LoadChunk(Script script, SourceCode source) + internal static FunctionProto LoadChunk(Script script, SourceCode source, Linker staticImport = null) { #if !DEBUG_PARSER try { #endif - ScriptLoadingContext lcontext; ChunkStatement stat; using (script.PerformanceStats.StartStopwatch(Diagnostics.PerformanceCounter.AstCreation)) { + ScriptLoadingContext lcontext; + bool staticImportIsNull = staticImport == null; + if (script.Options.Syntax == ScriptSyntax.Wattle) { Preprocessor preprocess = new Preprocessor(script, source.SourceID, source.Code); preprocess.Process(); - Using us = new Using(script, source.SourceID, preprocess.ProcessedSource, preprocess.Defines); - us.Process(); + staticImport ??= new Linker(script, source.SourceID, preprocess.ProcessedSource, preprocess.Defines); + staticImport.Process(); - lcontext = CreateLoadingContext(script, source, preprocess.ProcessedSource, preprocess.Defines, staticModules: us.ResolvedUsings); + lcontext = CreateLoadingContext(script, source, preprocess.ProcessedSource, preprocess.Defines, staticImport: staticImport); } else { lcontext = CreateLoadingContext(script, source); } + stat = new ChunkStatement(lcontext); + + if (script.Options.Syntax == ScriptSyntax.Wattle && staticImportIsNull) + { + stat.Block.InsertStatements(staticImport?.Export()); + } + lcontext.Scope = new BuildTimeScope(); stat.ResolveScope(lcontext); } diff --git a/src/WattleScript.Interpreter/Tree/IStaticallyImportableStatement.cs b/src/WattleScript.Interpreter/Tree/IStaticallyImportableStatement.cs new file mode 100644 index 00000000..9d31ab0a --- /dev/null +++ b/src/WattleScript.Interpreter/Tree/IStaticallyImportableStatement.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WattleScript.Interpreter.Execution; +using WattleScript.Interpreter.Tree.Expressions; +using WattleScript.Interpreter.Tree.Statements; + +namespace WattleScript.Interpreter.Tree +{ + interface IStaticallyImportableStatement + { + public Token NameToken { get; } + public string DefinitionType { get; } + } +} diff --git a/src/WattleScript.Interpreter/Tree/Linker/Linker.cs b/src/WattleScript.Interpreter/Tree/Linker/Linker.cs new file mode 100644 index 00000000..e974d9b1 --- /dev/null +++ b/src/WattleScript.Interpreter/Tree/Linker/Linker.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using WattleScript.Interpreter.Debugging; +using WattleScript.Interpreter.Execution; +using WattleScript.Interpreter.Tree.Fast_Interface; +using WattleScript.Interpreter.Tree.Statements; +using static WattleScript.Interpreter.Tree.NodeBase; + +namespace WattleScript.Interpreter.Tree +{ + class LinkerException : Exception + { + public LinkerException(string message) : base(message) { } + } + + internal class Linker + { + private Script script; + private int sourceIndex; + private bool outputChars = true; + private ScriptLoadingContext lcontextLocal; + private bool firstUsingEncountered = false; + private bool anyNonUsingEncounterd = false; + private string text; + private StringBuilder usingIdent = new StringBuilder(); + + //public string ProcessedSource => output.ToString(); + public Dictionary ResolvedUsings = new Dictionary(); + public Dictionary> ImportMap = new Dictionary>(); + + + public Linker(Script script, int sourceIndex, string text, Dictionary defines = null) + { + this.script = script; + this.sourceIndex = sourceIndex; + this.text = text; + + lcontextLocal = Loader_Fast.CreateLoadingContext(script, script.GetSourceCode(sourceIndex), text, defines, false, true, this); + } + + void ProcessUsingStatement(ScriptLoadingContext lcontext) + { + CheckTokenType(lcontext, TokenType.Using); + int currentLineFrom = lcontext.Lexer.Current.FromLine; + bool canBeDot = false; + int charTo = 0; + Token prev = null; + usingIdent.Clear(); + + while (lcontext.Lexer.PeekNext().Type != TokenType.Eof) + { + Token tkn = lcontext.Lexer.Current; + + if (tkn.FromLine != currentLineFrom) + { + break; + } + + prev = lcontext.Lexer.Current; + + canBeDot = canBeDot switch + { + false when tkn.Type != TokenType.Name => throw new SyntaxErrorException(tkn, $"unexpected token '{tkn.Text}' found in using statement"), + true when tkn.Type != TokenType.Dot => throw new SyntaxErrorException(tkn, $"unexpected token '{tkn.Text}' found in using statement"), + _ => !canBeDot + }; + + usingIdent.Append(tkn.Text); + lcontext.Lexer.Next(); + } + + string usingIdentStr = usingIdent.ToString(); + + if (ResolvedUsings.ContainsKey(usingIdentStr)) + { + throw new SyntaxErrorException(prev, $"duplicate using '{usingIdentStr}' found"); + } + + Module resolvedModule = script.Options.ScriptLoader.UsingResolver(usingIdentStr); + + if (resolvedModule == null) + { + throw new LinkerException($"module '{usingIdentStr}' failed to resolve"); + } + + if (resolvedModule.IsEntryRef) + { + return; + } + + ResolvedUsings.Add(usingIdentStr, resolvedModule); + Process(usingIdentStr, resolvedModule.Code); + } + + void EnlistNamespace(string nmspc) + { + ImportMap.GetOrCreate(nmspc, () => new Dictionary()); + } + + void EnlistMember(IStaticallyImportableStatement statement) + { + EnlistNamespace("test"); + + if (ImportMap["test"].TryGetValue(statement.NameToken.Text, out _)) + { + throw new SyntaxErrorException(statement.NameToken, $"Member '{statement.NameToken.Text}' is already defined as {statement.DefinitionType}"); + } + + ImportMap["test"].Add(statement.NameToken.Text, statement); + } + + public List Export() + { + List statements = new List(); + + foreach (KeyValuePair> nmspc in ImportMap) + { + foreach (KeyValuePair member in nmspc.Value) + { + statements.Add((Statement) member.Value); + } + } + + return statements; + } + + void Process(string nmspc, string code) + { + Script tmp = new Script(script.Options.CoreModules); + SourceCode source = new SourceCode($"linker - {nmspc}", code, 0, tmp); + Preprocessor preprocess = new Preprocessor(tmp, source.SourceID, source.Code); + preprocess.Process(); + + ScriptLoadingContext lcontextLib = Loader_Fast.CreateLoadingContext(script, source, preprocess.ProcessedSource, staticImport: this); + + Process(lcontextLib); + } + + void Process(ScriptLoadingContext lcontext) + { + if (!text.Contains("using")) // heuristic, will trip on comments, variable names, etc. but might be worth it + { + // PushToOutput(text); + // return; + } + + int previousCharTo = 0; + + + while (lcontext.Lexer.PeekNext().Type != TokenType.Eof) + { + lcontext.Lexer.Next(); + afterUsingStatement: + Token tkn = lcontext.Lexer.Current; + + + switch (tkn.Type) + { + case TokenType.Using: + firstUsingEncountered = true; + ProcessUsingStatement(lcontext); + goto afterUsingStatement; + case TokenType.Enum: + anyNonUsingEncounterd = true; + EnlistMember(new EnumDefinitionStatement(lcontext)); + break; + case TokenType.Class: + anyNonUsingEncounterd = true; + EnlistMember(new ClassDefinitionStatement(lcontext)); + break; + case TokenType.Mixin: + anyNonUsingEncounterd = true; + EnlistMember(new MixinDefinitionStatement(lcontext)); + break; + default: + anyNonUsingEncounterd = true; + break; + } + } + + int z = 0; + } + + public void Process() + { + Process(lcontextLocal); + } + } +} \ No newline at end of file diff --git a/src/WattleScript.Interpreter/Tree/Statement.cs b/src/WattleScript.Interpreter/Tree/Statement.cs index fe1a0a58..0018a597 100644 --- a/src/WattleScript.Interpreter/Tree/Statement.cs +++ b/src/WattleScript.Interpreter/Tree/Statement.cs @@ -25,6 +25,33 @@ protected IVariable CheckVar(ScriptLoadingContext lcontext, Expression firstExpr public abstract void ResolveScope(ScriptLoadingContext lcontext); + static void ProcessUsing(ScriptLoadingContext lcontext) + { + int line = lcontext.Lexer.Current.FromLine; + bool canBeDot = false; + + lcontext.Lexer.Next(); + + while (lcontext.Lexer.PeekNext().Type != TokenType.Eof) + { + Token tkn = lcontext.Lexer.Current; + + if (tkn.FromLine != line || tkn.ToLine != line) + { + break; + } + + canBeDot = canBeDot switch + { + false when tkn.Type != TokenType.Name => throw new SyntaxErrorException(tkn, $"unexpected token '{tkn.Text}' found in using statement"), + true when tkn.Type != TokenType.Dot => throw new SyntaxErrorException(tkn, $"unexpected token '{tkn.Text}' found in using statement"), + _ => !canBeDot + }; + + lcontext.Lexer.Next(); + } + } + static void ProcessDirective(ScriptLoadingContext lcontext) { var str = lcontext.Lexer.Current.Text; @@ -142,6 +169,21 @@ static void ProcessFunctionAnnotation(ScriptLoadingContext lcontext) } } + protected static void ParseStaticImports(ScriptLoadingContext lcontext) + { + while (true) + { + switch (lcontext.Lexer.Current.Type) + { + case TokenType.Using: + ProcessUsing(lcontext); + continue; + } + + break; + } + } + protected static void ParseAnnotations(ScriptLoadingContext lcontext) { //Process Annotations diff --git a/src/WattleScript.Interpreter/Tree/Statements/ChunkStatement.cs b/src/WattleScript.Interpreter/Tree/Statements/ChunkStatement.cs index 627e51a8..8057dc86 100644 --- a/src/WattleScript.Interpreter/Tree/Statements/ChunkStatement.cs +++ b/src/WattleScript.Interpreter/Tree/Statements/ChunkStatement.cs @@ -6,7 +6,8 @@ namespace WattleScript.Interpreter.Tree.Statements { class ChunkStatement : Statement, IClosureBuilder { - Statement m_Block; + public CompositeStatement Block { get; } + RuntimeScopeFrame m_StackFrame; SymbolRef m_Env; SymbolRef m_VarArgs; @@ -15,7 +16,7 @@ class ChunkStatement : Statement, IClosureBuilder public ChunkStatement(ScriptLoadingContext lcontext) : base(lcontext) { - m_Block = new CompositeStatement(lcontext, BlockEndType.Normal); + Block = new CompositeStatement(lcontext, BlockEndType.Normal); if (lcontext.Lexer.Current.Type != TokenType.Eof) throw new SyntaxErrorException(lcontext.Lexer.Current, " expected near '{0}'", lcontext.Lexer.Current.Text); @@ -30,7 +31,7 @@ public override void ResolveScope(ScriptLoadingContext lcontext) m_VarArgs = lcontext.Scope.DefineLocal(WellKnownSymbols.VARARGS); m_Env = lcontext.Scope.DefineLocal(WellKnownSymbols.ENV); - m_Block.ResolveScope(lcontext); + Block.ResolveScope(lcontext); m_StackFrame = lcontext.Scope.PopFunction(); } @@ -42,7 +43,7 @@ public FunctionProto CompileFunction(Script script) bc.Emit_Load(SymbolRef.Upvalue(WellKnownSymbols.ENV, 0)); bc.Emit_Store(m_Env, 0, 0); bc.Emit_Pop(); - m_Block.Compile(bc); + Block.Compile(bc); bc.Emit_Ret(0); var proto = bc.GetProto("", m_StackFrame); diff --git a/src/WattleScript.Interpreter/Tree/Statements/ClassDefinitionStatement.cs b/src/WattleScript.Interpreter/Tree/Statements/ClassDefinitionStatement.cs index f023c529..5c0cb2a6 100644 --- a/src/WattleScript.Interpreter/Tree/Statements/ClassDefinitionStatement.cs +++ b/src/WattleScript.Interpreter/Tree/Statements/ClassDefinitionStatement.cs @@ -8,8 +8,11 @@ namespace WattleScript.Interpreter.Tree.Statements { - class ClassDefinitionStatement : Statement + class ClassDefinitionStatement : Statement, IStaticallyImportableStatement { + public Token NameToken { get; } + public string DefinitionType => "class"; + //Class construction related private SymbolRefExpression classStoreGlobal; private SymbolRefExpression classStoreLocal; @@ -59,6 +62,7 @@ public ClassDefinitionStatement(ScriptLoadingContext lcontext) : base(lcontext) lcontext.Lexer.Next(); var nameToken = CheckTokenType(lcontext, TokenType.Name); + NameToken = nameToken; className = nameToken.Text; localName = $"$class:{className}"; //base class diff --git a/src/WattleScript.Interpreter/Tree/Statements/CompositeStatement.cs b/src/WattleScript.Interpreter/Tree/Statements/CompositeStatement.cs index 418efb17..fb8b0bec 100644 --- a/src/WattleScript.Interpreter/Tree/Statements/CompositeStatement.cs +++ b/src/WattleScript.Interpreter/Tree/Statements/CompositeStatement.cs @@ -16,6 +16,13 @@ class CompositeStatement : Statement { List m_Statements = new List(); + public void InsertStatements(List statements) + { + // push to front + statements.AddRange(m_Statements); + m_Statements = statements; + } + public CompositeStatement(ScriptLoadingContext lcontext, BlockEndType endType) : base(lcontext) { @@ -23,6 +30,7 @@ public CompositeStatement(ScriptLoadingContext lcontext, BlockEndType endType) { try { + ParseStaticImports(lcontext); ParseAnnotations(lcontext); Token t = lcontext.Lexer.Current; if (t.IsEndOfBlock()) break; @@ -90,6 +98,10 @@ private void Synchronize(ScriptLoadingContext lcontext) case TokenType.Directive: case TokenType.Do: case TokenType.If: + case TokenType.Switch: + case TokenType.Enum: + case TokenType.Class: + case TokenType.Mixin: { goto endSynchronize; } diff --git a/src/WattleScript.Interpreter/Tree/Statements/EnumDefinitionStatement.cs b/src/WattleScript.Interpreter/Tree/Statements/EnumDefinitionStatement.cs index 280b0143..4ce6bc31 100644 --- a/src/WattleScript.Interpreter/Tree/Statements/EnumDefinitionStatement.cs +++ b/src/WattleScript.Interpreter/Tree/Statements/EnumDefinitionStatement.cs @@ -5,8 +5,11 @@ namespace WattleScript.Interpreter.Tree.Statements { - class EnumDefinitionStatement : Statement + class EnumDefinitionStatement : Statement, IStaticallyImportableStatement { + public Token NameToken { get; } + public string DefinitionType => "enum"; + private string enumName; private SourceRef assignment; private SourceRef buildCode; @@ -24,6 +27,7 @@ public EnumDefinitionStatement(ScriptLoadingContext lcontext) var start = lcontext.Lexer.Current; lcontext.Lexer.Next(); var name = CheckTokenType(lcontext, TokenType.Name); + NameToken = name; enumName = name.Text; assignment = start.GetSourceRef(name); var buildStart = CheckTokenType(lcontext, TokenType.Brk_Open_Curly); diff --git a/src/WattleScript.Interpreter/Tree/Statements/MixinDefinitionStatement.cs b/src/WattleScript.Interpreter/Tree/Statements/MixinDefinitionStatement.cs index 354581cd..ceecb890 100644 --- a/src/WattleScript.Interpreter/Tree/Statements/MixinDefinitionStatement.cs +++ b/src/WattleScript.Interpreter/Tree/Statements/MixinDefinitionStatement.cs @@ -8,8 +8,11 @@ namespace WattleScript.Interpreter.Tree.Statements { - class MixinDefinitionStatement : Statement + class MixinDefinitionStatement : Statement, IStaticallyImportableStatement { + public Token NameToken { get; } + public string DefinitionType => "mixin"; + private SymbolRefExpression storeValue; private string name; private GeneratedClosure init; @@ -26,6 +29,7 @@ public MixinDefinitionStatement(ScriptLoadingContext lcontext) : base(lcontext) annotations = lcontext.FunctionAnnotations.ToArray(); lcontext.Lexer.Next(); var nameToken = CheckTokenType(lcontext, TokenType.Name); + NameToken = nameToken; name = nameToken.Text; sourceRef = nameToken.GetSourceRef(CheckTokenType(lcontext, TokenType.Brk_Open_Curly)); diff --git a/src/WattleScript.Interpreter/Tree/Using/Using.cs b/src/WattleScript.Interpreter/Tree/Using/Using.cs deleted file mode 100644 index 660c5b75..00000000 --- a/src/WattleScript.Interpreter/Tree/Using/Using.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using WattleScript.Interpreter.Execution; -using WattleScript.Interpreter.Tree.Fast_Interface; -using static WattleScript.Interpreter.Tree.NodeBase; - -namespace WattleScript.Interpreter.Tree -{ - internal class Using - { - private StringBuilder output; - private Script script; - private int sourceIndex; - private bool outputChars = true; - private ScriptLoadingContext lcontext; - private bool firstUsingEncountered = false; - private bool anyNonUsingEncounterd = false; - private string text; - private StringBuilder usingIdent = new StringBuilder(); - - //public string ProcessedSource => output.ToString(); - public Dictionary ResolvedUsings = new Dictionary(); - - - public Using(Script script, int sourceIndex, string text, Dictionary defines = null) - { - this.script = script; - this.sourceIndex = sourceIndex; - this.text = text; - - output = new StringBuilder(); - lcontext = Loader_Fast.CreateLoadingContext(script, script.GetSourceCode(sourceIndex), text, defines, false, true); - } - - void PushToOutput(string str) - { - output.Append(str); - } - - int ProcessUsingStatement() - { - CheckTokenType(lcontext, TokenType.Using); - int currentLineFrom = lcontext.Lexer.Current.FromLine; - bool canBeDot = false; - int charTo = 0; - Token prev = null; - usingIdent.Clear(); - - while (lcontext.Lexer.PeekNext().Type != TokenType.Eof) - { - Token tkn = lcontext.Lexer.Current; - - if (tkn.FromLine != currentLineFrom) - { - break; - } - - prev = lcontext.Lexer.Current; - - canBeDot = canBeDot switch - { - false when tkn.Type != TokenType.Name => throw new SyntaxErrorException(tkn, $"unexpected token '{tkn.Text}' found in using statement"), - true when tkn.Type != TokenType.Dot => throw new SyntaxErrorException(tkn, $"unexpected token '{tkn.Text}' found in using statement"), - _ => !canBeDot - }; - - usingIdent.Append(tkn.Text); - lcontext.Lexer.Next(); - } - - string usingIdentStr = usingIdent.ToString(); - - if (ResolvedUsings.ContainsKey(usingIdentStr)) - { - throw new SyntaxErrorException(prev, $"duplicate using '{usingIdentStr}' found"); - } - - ResolvedUsings.Add(usingIdentStr, script.UsingHandler(usingIdentStr)); - - return prev?.CharIndexTo ?? 0; - } - - public void Process() - { - if (!text.Contains("using")) // heuristic, will trip on comments, variable names, etc. but might be worth it - { - // PushToOutput(text); - // return; - } - - int previousCharTo = 0; - - - while (lcontext.Lexer.PeekNext().Type != TokenType.Eof) - { - lcontext.Lexer.Next(); - afterUsingStatement: - Token tkn = lcontext.Lexer.Current; - - - switch (tkn.Type) - { - case TokenType.Using: - firstUsingEncountered = true; - previousCharTo = ProcessUsingStatement(); - goto afterUsingStatement; - default: - anyNonUsingEncounterd = true; - PushToOutput(text.Substring(previousCharTo, tkn.CharIndexTo - previousCharTo)); - break; - } - - previousCharTo = tkn.CharIndexTo; - } - - string str = output.ToString(); - int z = 0; - } - } -} \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.lua b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.lua index f5bc50cb..75912187 100644 --- a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.lua +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.lua @@ -1,3 +1,8 @@ using ws.tests.classTest -new C().print("hello") \ No newline at end of file +print("hello world") + +class ClassA : ClassB {} +class ClassC {} + +a = new ClassA() \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.txt b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.txt index b6fc4c62..95d09f2b 100644 --- a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.txt +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.txt @@ -1 +1 @@ -hello \ No newline at end of file +hello world \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/ws.tests.classTest.wtlib b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/ws.tests.classTest.wtlib index d1caf6ba..8e281c8c 100644 --- a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/ws.tests.classTest.wtlib +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/ws.tests.classTest.wtlib @@ -1,5 +1,5 @@ -class C() { - function print(x) { - print(x) - } -} \ No newline at end of file +using caller + +print("test") + +class ClassB { cInst = nil; ClassB() { this.cInst = new ClassC() } } \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs b/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs index 767584f7..8c9a37e0 100644 --- a/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs +++ b/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs @@ -46,8 +46,13 @@ Script InitScript(string scriptFolderPath) script.Options.AnnotationPolicy = new CustomPolicy(AnnotationValueParsingPolicy.ForceTable); script.Globals["CurrentLine"] = (ScriptExecutionContext c, CallbackArguments a) => c.CallingLocation.FromLine; script.Globals["CurrentColumn"] = (ScriptExecutionContext c, CallbackArguments a) => c.CallingLocation.FromChar; - script.UsingHandler = (path) => + script.Options.ScriptLoader.UsingResolver = (path) => { + if (path == "caller") + { + return Module.LocalModule; + } + if (File.Exists($"{scriptFolderPath}\\{path}.wtlib")) { string libText = File.ReadAllText($"{scriptFolderPath}\\{path}.wtlib"); From dcc202d90421b543848aa54cc5403a88138de20b Mon Sep 17 00:00:00 2001 From: lofcz Date: Tue, 11 Oct 2022 19:29:59 +0200 Subject: [PATCH 3/9] Extend test using-1 --- .../EndToEnd/CLike/SyntaxCLike/Linker/1-using.lua | 8 ++++++++ .../EndToEnd/CLike/SyntaxCLike/Linker/1-using.txt | 2 ++ .../{Using => Linker}/ws.tests.classTest.wtlib | 0 .../EndToEnd/CLike/SyntaxCLike/Using/1-using.lua | 8 -------- .../EndToEnd/CLike/SyntaxCLike/Using/1-using.txt | 1 - 5 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/1-using.lua create mode 100644 src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/1-using.txt rename src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/{Using => Linker}/ws.tests.classTest.wtlib (100%) delete mode 100644 src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.lua delete mode 100644 src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.txt diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/1-using.lua b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/1-using.lua new file mode 100644 index 00000000..623a5b6b --- /dev/null +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/1-using.lua @@ -0,0 +1,8 @@ +using ws.tests.classTest + +print("hello world") + +class ClassA : ClassB { ClassA() { base() } } +class ClassC { ClassC() { print("hello world2") } } + +a = new ClassA() \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/1-using.txt b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/1-using.txt new file mode 100644 index 00000000..96b5e706 --- /dev/null +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/1-using.txt @@ -0,0 +1,2 @@ +hello world +hello world2 \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/ws.tests.classTest.wtlib b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/ws.tests.classTest.wtlib similarity index 100% rename from src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/ws.tests.classTest.wtlib rename to src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/ws.tests.classTest.wtlib diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.lua b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.lua deleted file mode 100644 index 75912187..00000000 --- a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.lua +++ /dev/null @@ -1,8 +0,0 @@ -using ws.tests.classTest - -print("hello world") - -class ClassA : ClassB {} -class ClassC {} - -a = new ClassA() \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.txt b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.txt deleted file mode 100644 index 95d09f2b..00000000 --- a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Using/1-using.txt +++ /dev/null @@ -1 +0,0 @@ -hello world \ No newline at end of file From b0887decf3c9e465dd60da0e846229b318b46af5 Mon Sep 17 00:00:00 2001 From: lofcz Date: Wed, 12 Oct 2022 12:02:28 +0200 Subject: [PATCH 4/9] Using -> Linker --- .../Execution/Scopes/BuildTimeScope.cs | 5 ++ .../Execution/Scopes/BuildTimeScopeBlock.cs | 12 +-- .../Execution/Scopes/BuildTimeScopeFrame.cs | 5 ++ .../Execution/ScriptLoadingContext.cs | 2 +- .../Tree/Fast_Interface/Loader_Fast.cs | 2 +- .../Tree/IStaticallyImportableStatement.cs | 1 + .../Tree/Lexer/Token.cs | 2 + .../Tree/Lexer/TokenType.cs | 1 + .../Tree/Linker/Linker.cs | 77 ++++++++++++++++--- .../Tree/Statement.cs | 2 + .../Statements/ClassDefinitionStatement.cs | 3 + .../Tree/Statements/CompositeStatement.cs | 4 + .../Statements/EnumDefinitionStatement.cs | 2 + .../Statements/MixinDefinitionStatement.cs | 2 + .../Tree/Statements/NamespaceStatement.cs | 66 ++++++++++++++++ .../CLike/SyntaxCLike/Linker/1-using.lua | 14 ++-- .../SyntaxCLike/Linker/2-namespace-empty.lua | 7 ++ .../SyntaxCLike/Linker/2-namespace-empty.txt | 0 .../Linker/ws.tests.classTest.wtlib | 7 +- 19 files changed, 187 insertions(+), 27 deletions(-) create mode 100644 src/WattleScript.Interpreter/Tree/Statements/NamespaceStatement.cs create mode 100644 src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.lua create mode 100644 src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.txt diff --git a/src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScope.cs b/src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScope.cs index c1236eeb..b4f21301 100644 --- a/src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScope.cs +++ b/src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScope.cs @@ -24,6 +24,11 @@ public void SetHasVarArgs() m_Frames.Last().HasVarArgs = true; } + public void PushBlock(string nmspc) + { + m_Frames.Last().PushBlock(nmspc); + } + public void PushBlock() { m_Frames.Last().PushBlock(); diff --git a/src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScopeBlock.cs b/src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScopeBlock.cs index d6a5318f..7ff6148a 100644 --- a/src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScopeBlock.cs +++ b/src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScopeBlock.cs @@ -10,8 +10,10 @@ internal class BuildTimeScopeBlock internal BuildTimeScopeBlock Parent { get; private set; } internal List ChildNodes { get; private set; } internal RuntimeScopeBlock ScopeBlock { get; private set; } + internal string Namespace { get; private set; } + Dictionary m_DefinedNames = new Dictionary(); - + internal void Rename(string name) { SymbolRef sref = m_DefinedNames[name]; @@ -19,17 +21,17 @@ internal void Rename(string name) m_DefinedNames.Add(string.Format("@{0}_{1}", name, Guid.NewGuid().ToString("N")), sref); } - internal BuildTimeScopeBlock(BuildTimeScopeBlock parent) + internal BuildTimeScopeBlock(BuildTimeScopeBlock parent, string nmspc = "") { Parent = parent; ChildNodes = new List(); ScopeBlock = new RuntimeScopeBlock(); + Namespace = nmspc; } - - internal BuildTimeScopeBlock AddChild() + internal BuildTimeScopeBlock AddChild(string nmspc = "") { - BuildTimeScopeBlock block = new BuildTimeScopeBlock(this); + BuildTimeScopeBlock block = new BuildTimeScopeBlock(this, nmspc); ChildNodes.Add(block); return block; } diff --git a/src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScopeFrame.cs b/src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScopeFrame.cs index 637965b1..68102d18 100644 --- a/src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScopeFrame.cs +++ b/src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScopeFrame.cs @@ -19,6 +19,11 @@ internal BuildTimeScopeFrame(bool isConstructor) m_ScopeTreeHead = m_ScopeTreeRoot = new BuildTimeScopeBlock(null); } + internal void PushBlock(string nmspc) + { + m_ScopeTreeHead = m_ScopeTreeHead.AddChild(nmspc); + } + internal void PushBlock() { m_ScopeTreeHead = m_ScopeTreeHead.AddChild(); diff --git a/src/WattleScript.Interpreter/Execution/ScriptLoadingContext.cs b/src/WattleScript.Interpreter/Execution/ScriptLoadingContext.cs index ff9c1205..97c826c2 100644 --- a/src/WattleScript.Interpreter/Execution/ScriptLoadingContext.cs +++ b/src/WattleScript.Interpreter/Execution/ScriptLoadingContext.cs @@ -14,7 +14,7 @@ class ScriptLoadingContext public Lexer Lexer { get; set; } public ScriptSyntax Syntax { get; set; } - public Linker Using { get; set; } + public Linker Linker { get; set; } //Compiler state internal List ChunkAnnotations { get; set; } = new List(); diff --git a/src/WattleScript.Interpreter/Tree/Fast_Interface/Loader_Fast.cs b/src/WattleScript.Interpreter/Tree/Fast_Interface/Loader_Fast.cs index 9dc1613b..6bca833b 100644 --- a/src/WattleScript.Interpreter/Tree/Fast_Interface/Loader_Fast.cs +++ b/src/WattleScript.Interpreter/Tree/Fast_Interface/Loader_Fast.cs @@ -43,7 +43,7 @@ internal static ScriptLoadingContext CreateLoadingContext(Script script, SourceC Source = source, Lexer = new Lexer(source.SourceID, preprocessedCode ?? source.Code, lexerAutoSkipComments, script.Options.Syntax, script.Options.Directives, defines, lexerKeepInsignificantChars), Syntax = script.Options.Syntax, - Using = staticImport + Linker = staticImport }; } diff --git a/src/WattleScript.Interpreter/Tree/IStaticallyImportableStatement.cs b/src/WattleScript.Interpreter/Tree/IStaticallyImportableStatement.cs index 9d31ab0a..440d7a47 100644 --- a/src/WattleScript.Interpreter/Tree/IStaticallyImportableStatement.cs +++ b/src/WattleScript.Interpreter/Tree/IStaticallyImportableStatement.cs @@ -11,5 +11,6 @@ interface IStaticallyImportableStatement { public Token NameToken { get; } public string DefinitionType { get; } + public string Namespace { get; } } } diff --git a/src/WattleScript.Interpreter/Tree/Lexer/Token.cs b/src/WattleScript.Interpreter/Tree/Lexer/Token.cs index 21736d32..c558b6ee 100644 --- a/src/WattleScript.Interpreter/Tree/Lexer/Token.cs +++ b/src/WattleScript.Interpreter/Tree/Lexer/Token.cs @@ -73,6 +73,8 @@ public override string ToString() return TokenType.Sealed; case "using": return TokenType.Using; + case "namespace": + return TokenType.Namespace; } } diff --git a/src/WattleScript.Interpreter/Tree/Lexer/TokenType.cs b/src/WattleScript.Interpreter/Tree/Lexer/TokenType.cs index 74e2e0b5..6a03076a 100644 --- a/src/WattleScript.Interpreter/Tree/Lexer/TokenType.cs +++ b/src/WattleScript.Interpreter/Tree/Lexer/TokenType.cs @@ -127,6 +127,7 @@ enum TokenType Sealed, Using, + Namespace, Preprocessor_Defined //Reserved only in preprocessor } diff --git a/src/WattleScript.Interpreter/Tree/Linker/Linker.cs b/src/WattleScript.Interpreter/Tree/Linker/Linker.cs index e974d9b1..2cf16099 100644 --- a/src/WattleScript.Interpreter/Tree/Linker/Linker.cs +++ b/src/WattleScript.Interpreter/Tree/Linker/Linker.cs @@ -17,6 +17,8 @@ public LinkerException(string message) : base(message) { } internal class Linker { + public string CurrentNamespace { get; set; } = ""; + private Script script; private int sourceIndex; private bool outputChars = true; @@ -25,6 +27,7 @@ internal class Linker private bool anyNonUsingEncounterd = false; private string text; private StringBuilder usingIdent = new StringBuilder(); + private string lastNamespace; //public string ProcessedSource => output.ToString(); public Dictionary ResolvedUsings = new Dictionary(); @@ -40,6 +43,52 @@ public Linker(Script script, int sourceIndex, string text, Dictionary "class"; + public string Namespace { get; } //Class construction related private SymbolRefExpression classStoreGlobal; @@ -53,6 +54,8 @@ class ClassDefinitionStatement : Statement, IStaticallyImportableStatement public ClassDefinitionStatement(ScriptLoadingContext lcontext) : base(lcontext) { + Namespace = lcontext.Linker.CurrentNamespace; + while (lcontext.Lexer.Current.IsMemberModifier()) { MemberUtilities.AddModifierFlag(ref flags, lcontext.Lexer.Current, WattleMemberType.Class); diff --git a/src/WattleScript.Interpreter/Tree/Statements/CompositeStatement.cs b/src/WattleScript.Interpreter/Tree/Statements/CompositeStatement.cs index fb8b0bec..0540c8c8 100644 --- a/src/WattleScript.Interpreter/Tree/Statements/CompositeStatement.cs +++ b/src/WattleScript.Interpreter/Tree/Statements/CompositeStatement.cs @@ -26,6 +26,8 @@ public void InsertStatements(List statements) public CompositeStatement(ScriptLoadingContext lcontext, BlockEndType endType) : base(lcontext) { + lcontext.Linker.StoreNamespace(); + while (true) { try @@ -70,6 +72,8 @@ public CompositeStatement(ScriptLoadingContext lcontext, BlockEndType endType) } } + lcontext.Linker.RestoreNamespace(); + // eat away all superfluos ';'s while (lcontext.Lexer.Current.Type == TokenType.SemiColon) lcontext.Lexer.Next(); diff --git a/src/WattleScript.Interpreter/Tree/Statements/EnumDefinitionStatement.cs b/src/WattleScript.Interpreter/Tree/Statements/EnumDefinitionStatement.cs index 4ce6bc31..25ed90eb 100644 --- a/src/WattleScript.Interpreter/Tree/Statements/EnumDefinitionStatement.cs +++ b/src/WattleScript.Interpreter/Tree/Statements/EnumDefinitionStatement.cs @@ -9,6 +9,7 @@ class EnumDefinitionStatement : Statement, IStaticallyImportableStatement { public Token NameToken { get; } public string DefinitionType => "enum"; + public string Namespace { get; } private string enumName; private SourceRef assignment; @@ -21,6 +22,7 @@ class EnumDefinitionStatement : Statement, IStaticallyImportableStatement public EnumDefinitionStatement(ScriptLoadingContext lcontext) : base(lcontext) { + Namespace = lcontext.Linker.CurrentNamespace; annotations = lcontext.FunctionAnnotations.ToArray(); lcontext.FunctionAnnotations = new List(); //lexer is at "enum" diff --git a/src/WattleScript.Interpreter/Tree/Statements/MixinDefinitionStatement.cs b/src/WattleScript.Interpreter/Tree/Statements/MixinDefinitionStatement.cs index ceecb890..06c1c07f 100644 --- a/src/WattleScript.Interpreter/Tree/Statements/MixinDefinitionStatement.cs +++ b/src/WattleScript.Interpreter/Tree/Statements/MixinDefinitionStatement.cs @@ -12,6 +12,7 @@ class MixinDefinitionStatement : Statement, IStaticallyImportableStatement { public Token NameToken { get; } public string DefinitionType => "mixin"; + public string Namespace { get; } private SymbolRefExpression storeValue; private string name; @@ -26,6 +27,7 @@ class MixinDefinitionStatement : Statement, IStaticallyImportableStatement public MixinDefinitionStatement(ScriptLoadingContext lcontext) : base(lcontext) { + Namespace = lcontext.Linker.CurrentNamespace; annotations = lcontext.FunctionAnnotations.ToArray(); lcontext.Lexer.Next(); var nameToken = CheckTokenType(lcontext, TokenType.Name); diff --git a/src/WattleScript.Interpreter/Tree/Statements/NamespaceStatement.cs b/src/WattleScript.Interpreter/Tree/Statements/NamespaceStatement.cs new file mode 100644 index 00000000..180ae650 --- /dev/null +++ b/src/WattleScript.Interpreter/Tree/Statements/NamespaceStatement.cs @@ -0,0 +1,66 @@ +using System.Text; +using WattleScript.Interpreter.Execution; +using WattleScript.Interpreter.Execution.VM; + +namespace WattleScript.Interpreter.Tree.Statements +{ + class NamespaceStatement : Statement + { + private CompositeStatement block; + private RuntimeScopeBlock stackFrame; + private string namespaceIdentStr; + + public NamespaceStatement(ScriptLoadingContext lcontext) : base(lcontext) + { + CheckTokenType(lcontext, TokenType.Namespace); + bool canBeDot = false; + StringBuilder namespaceIdent = new StringBuilder(); + + while (lcontext.Lexer.PeekNext().Type != TokenType.Eof) + { + Token tkn = lcontext.Lexer.Current; + + if (!canBeDot && tkn.Type != TokenType.Name) + { + break; + } + + if (canBeDot && tkn.Type != TokenType.Dot) + { + break; + } + + canBeDot = !canBeDot; + + namespaceIdent.Append(tkn.Text); + lcontext.Lexer.Next(); + } + + namespaceIdentStr = namespaceIdent.ToString(); + lcontext.Linker.CurrentNamespace = namespaceIdentStr; + + if (lcontext.Lexer.Current.Type == TokenType.Brk_Open_Curly) + { + block = new CompositeStatement(lcontext, BlockEndType.CloseCurly); + } + else + { + block = new CompositeStatement(lcontext, BlockEndType.Normal); + } + } + + public override void Compile(FunctionBuilder bc) + { + bc.Emit_Enter(stackFrame); + block.Compile(bc); + bc.Emit_Leave(stackFrame); + } + + public override void ResolveScope(ScriptLoadingContext lcontext) + { + lcontext.Scope.PushBlock(namespaceIdentStr); + block.ResolveScope(lcontext); + stackFrame = lcontext.Scope.PopBlock(); + } + } +} \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/1-using.lua b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/1-using.lua index 623a5b6b..4dc3a48e 100644 --- a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/1-using.lua +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/1-using.lua @@ -1,8 +1,10 @@ using ws.tests.classTest -print("hello world") - -class ClassA : ClassB { ClassA() { base() } } -class ClassC { ClassC() { print("hello world2") } } - -a = new ClassA() \ No newline at end of file +namespace ws.tests.entry { + print("hello world") + + class ClassA : ClassB { ClassA() { base() } } + class ClassC { ClassC() { print("hello world2") } } + + a = new ClassA() +} \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.lua b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.lua new file mode 100644 index 00000000..0b77ddce --- /dev/null +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.lua @@ -0,0 +1,7 @@ +namespace lib.a { + class test { + + } +} + +a = new lib.a.test() \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.txt b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/ws.tests.classTest.wtlib b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/ws.tests.classTest.wtlib index 8e281c8c..3b662044 100644 --- a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/ws.tests.classTest.wtlib +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/ws.tests.classTest.wtlib @@ -1,5 +1,6 @@ using caller -print("test") - -class ClassB { cInst = nil; ClassB() { this.cInst = new ClassC() } } \ No newline at end of file +namespace ws.tests.classTest { + print("test") + class ClassB { cInst = nil; ClassB() { this.cInst = new ClassC() } } +} \ No newline at end of file From ffe44cff00f69c30d6a8a7f1c39234ec430831a7 Mon Sep 17 00:00:00 2001 From: lofcz Date: Wed, 12 Oct 2022 18:40:39 +0200 Subject: [PATCH 5/9] Parse possible namespace in NewExpression --- .../Tree/Expressions/NewExpression.cs | 14 +++++++ src/WattleScript.Interpreter/Tree/NodeBase.cs | 39 ++++++++++++++++++- .../Tree/Statements/CompositeStatement.cs | 4 +- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/WattleScript.Interpreter/Tree/Expressions/NewExpression.cs b/src/WattleScript.Interpreter/Tree/Expressions/NewExpression.cs index f80c5fb8..77c070cf 100644 --- a/src/WattleScript.Interpreter/Tree/Expressions/NewExpression.cs +++ b/src/WattleScript.Interpreter/Tree/Expressions/NewExpression.cs @@ -18,6 +18,20 @@ public NewExpression(ScriptLoadingContext lcontext) : base(lcontext) { lcontext.Lexer.Next(); //lexer at "new" token var classTok = CheckTokenType(lcontext, TokenType.Name); + + if (lcontext.Lexer.Current.Type == TokenType.Dot) // possible namespace qualifier + { + List namespaceQualifier = ParseNamespace(lcontext, true); + namespaceQualifier.Insert(0, classTok); + + if (namespaceQualifier.Count < 2) // at least ident-dot + { + throw new SyntaxErrorException(namespaceQualifier[namespaceQualifier.Count - 1], $"Got unexpected token '{namespaceQualifier[namespaceQualifier.Count - 1].Text}' while parsing namespace"); + } + + classTok = CheckTokenType(lcontext, TokenType.Name); + } + className = classTok.Text; CheckTokenType(lcontext, TokenType.Brk_Open_Round); if (lcontext.Lexer.Current.Type == TokenType.Brk_Close_Round) diff --git a/src/WattleScript.Interpreter/Tree/NodeBase.cs b/src/WattleScript.Interpreter/Tree/NodeBase.cs index 2b24340c..8d49dee4 100644 --- a/src/WattleScript.Interpreter/Tree/NodeBase.cs +++ b/src/WattleScript.Interpreter/Tree/NodeBase.cs @@ -1,4 +1,6 @@ -using WattleScript.Interpreter.Execution; +using System.Collections.Generic; +using System.Linq; +using WattleScript.Interpreter.Execution; using WattleScript.Interpreter.Execution.VM; namespace WattleScript.Interpreter.Tree @@ -25,6 +27,41 @@ internal static Token UnexpectedTokenType(Token t) }; } + /// + /// Parses a sequence of tokens in form of (name->dot->name->..) + /// + /// Current ScriptLoadingContext + /// Whether the first token should be "name" or "dot" + /// A list of tokens representing the qualifier + protected static List ParseNamespace(ScriptLoadingContext lcontext, bool currentTokenShouldBeDot) + { + List tokens = new List(); + + while (lcontext.Lexer.PeekNext().Type != TokenType.Eof) + { + if (currentTokenShouldBeDot && lcontext.Lexer.PeekNext().Type != TokenType.Name) + { + break; + } + + if (!currentTokenShouldBeDot && lcontext.Lexer.PeekNext().Type != TokenType.Dot) + { + break; + } + + currentTokenShouldBeDot = !currentTokenShouldBeDot; + tokens.Add(lcontext.Lexer.Current); + lcontext.Lexer.Next(); + } + + if (tokens.Last().Type == TokenType.Dot) + { + tokens.Remove(tokens.Last()); + } + + return tokens; + } + protected static Token CheckTokenTypeEx(ScriptLoadingContext lcontext, TokenType tokenType1, TokenType tokenType2) { if (lcontext.Syntax != ScriptSyntax.Lua) diff --git a/src/WattleScript.Interpreter/Tree/Statements/CompositeStatement.cs b/src/WattleScript.Interpreter/Tree/Statements/CompositeStatement.cs index 0540c8c8..3d28088b 100644 --- a/src/WattleScript.Interpreter/Tree/Statements/CompositeStatement.cs +++ b/src/WattleScript.Interpreter/Tree/Statements/CompositeStatement.cs @@ -26,7 +26,7 @@ public void InsertStatements(List statements) public CompositeStatement(ScriptLoadingContext lcontext, BlockEndType endType) : base(lcontext) { - lcontext.Linker.StoreNamespace(); + lcontext.Linker?.StoreNamespace(); while (true) { @@ -72,7 +72,7 @@ public CompositeStatement(ScriptLoadingContext lcontext, BlockEndType endType) } } - lcontext.Linker.RestoreNamespace(); + lcontext.Linker?.RestoreNamespace(); // eat away all superfluos ';'s while (lcontext.Lexer.Current.Type == TokenType.SemiColon) From 008a294573b32019b1d8fa242f61d74995e7c58b Mon Sep 17 00:00:00 2001 From: lofcz Date: Thu, 13 Oct 2022 17:31:23 +0200 Subject: [PATCH 6/9] Prevent double compilation when statically including --- .../Execution/InstructionFieldUsage.cs | 1 + .../Execution/VM/FunctionBuilder.cs | 19 +++++ .../Execution/VM/OpCode.cs | 29 +++---- .../VM/Processor/Processor_InstructionLoop.cs | 11 +++ .../Tree/Expressions/NewExpression.cs | 24 +++--- .../Tree/Linker/Linker.cs | 81 +++++++++++-------- src/WattleScript.Interpreter/Tree/NodeBase.cs | 29 +++++-- .../Statements/ClassDefinitionStatement.cs | 15 ++++ .../SyntaxCLike/Linker/2-namespace-empty.lua | 5 +- .../SyntaxCLike/Linker/2-namespace-empty.txt | 1 + 10 files changed, 148 insertions(+), 67 deletions(-) diff --git a/src/WattleScript.Interpreter/Execution/InstructionFieldUsage.cs b/src/WattleScript.Interpreter/Execution/InstructionFieldUsage.cs index 495616ca..c3d61593 100644 --- a/src/WattleScript.Interpreter/Execution/InstructionFieldUsage.cs +++ b/src/WattleScript.Interpreter/Execution/InstructionFieldUsage.cs @@ -98,6 +98,7 @@ internal static InstructionFieldUsage GetFieldUsage(this OpCode op) case OpCode.PushInt: case OpCode.BaseChk: case OpCode.SetMetaTab: + case OpCode.PrepNmspc: return InstructionFieldUsage.NumVal; case OpCode.Call: case OpCode.ThisCall: diff --git a/src/WattleScript.Interpreter/Execution/VM/FunctionBuilder.cs b/src/WattleScript.Interpreter/Execution/VM/FunctionBuilder.cs index fa43129a..1cbbb039 100755 --- a/src/WattleScript.Interpreter/Execution/VM/FunctionBuilder.cs +++ b/src/WattleScript.Interpreter/Execution/VM/FunctionBuilder.cs @@ -8,6 +8,7 @@ using System.Text; using System.Xml.Schema; using WattleScript.Interpreter.Debugging; +using WattleScript.Interpreter.Tree; namespace WattleScript.Interpreter.Execution.VM { @@ -421,6 +422,24 @@ public int Emit_MixInit(string mixinName) return AppendInstruction(new Instruction(OpCode.MixInit, StringArg(mixinName))); } + public int Emit_PrepNmspc(List namespaceComponents) + { + if (namespaceComponents == null) + { + throw new InternalErrorException("List of namespace components cannot be null"); + } + + int parts = 0; + + foreach (Token token in namespaceComponents.Where(x => x.Type == TokenType.Name)) + { + parts++; + Emit_IndexSet(0, 0, token.Text, true); + } + + return AppendInstruction(new Instruction(OpCode.PrepNmspc, parts)); + } + public int Emit_NewCall(int argCount, string className) { return AppendInstruction(new Instruction(OpCode.NewCall, argCount) {NumValB = (uint) StringArg(className)}); diff --git a/src/WattleScript.Interpreter/Execution/VM/OpCode.cs b/src/WattleScript.Interpreter/Execution/VM/OpCode.cs index eb19a0f1..e23291ea 100644 --- a/src/WattleScript.Interpreter/Execution/VM/OpCode.cs +++ b/src/WattleScript.Interpreter/Execution/VM/OpCode.cs @@ -118,20 +118,21 @@ internal enum OpCode // OOP // AnnotX instructions, add annotation to table //NumValB = annotation name string - AnnotI, //NumVal = int - AnnotN, //NumVal = number - AnnotS, //NumVal = string or nil - AnnotB, //NumVal = bool - AnnotT, //pop table from v-stack - LoopChk, //Checks if local in NumVal is < threshold. If not, throw error using NumValB as the class name - BaseChk, //Checks if v-stack top is a class. If not, throw error using NumVal as the base class name - NewCall, //Calls the new() function stored in table at v-stack offset NumVal with NumVal arguments. - //Throws error using class name in NumValB if type check fails - MixInit, //Checks type of mixin on v-stack top, stores init to v-stack + 1, adds functions to v-stack + 2, pops top - //Error check uses NumVal for mixin name - SetFlags, //Sets WattleFieldsInfo for NumVal keys, using modifier in NumVal2 (pops NumVal Items) - MergeFlags, //Merges the WattleFieldsInfo of v-stack(NumVal) into v-stack(NumVal2) - CopyFlags, //Copies the WattleFieldsInfo of v-stack top into v-stack +1, pops 1 value + AnnotI, // NumVal = int + AnnotN, // NumVal = number + AnnotS, // NumVal = string or nil + AnnotB, // NumVal = bool + AnnotT, // pop table from v-stack + LoopChk, // Checks if local in NumVal is < threshold. If not, throw error using NumValB as the class name + BaseChk, // Checks if v-stack top is a class. If not, throw error using NumVal as the base class name + NewCall, // Calls the new() function stored in table at v-stack offset NumVal with NumVal arguments. + // Throws error using class name in NumValB if type check fails + MixInit, // Checks type of mixin on v-stack top, stores init to v-stack + 1, adds functions to v-stack + 2, pops top + // Error check uses NumVal for mixin name + SetFlags, // Sets WattleFieldsInfo for NumVal keys, using modifier in NumVal2 (pops NumVal Items) + MergeFlags, // Merges the WattleFieldsInfo of v-stack(NumVal) into v-stack(NumVal2) + CopyFlags, // Copies the WattleFieldsInfo of v-stack top into v-stack +1, pops 1 value + PrepNmspc, // Pop next NumVal values from v-stack (namespace components), next [todo] NewCall/../.. will use this for namespace resolution. This should cover static class access eg. val = myNamespace.myStaticClass.staticField or myNamespace.myStaticClass.staticMethod() // Meta Invalid, // Crashes the executor with an unrecoverable NotImplementedException. This MUST always be the last opcode in enum } diff --git a/src/WattleScript.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs b/src/WattleScript.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs index b8f0f468..64d69489 100644 --- a/src/WattleScript.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs +++ b/src/WattleScript.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs @@ -410,6 +410,9 @@ private DynValue Processing_Loop(int instructionPtr, bool canAwait = false) } break; } + case OpCode.PrepNmspc: + ExecPrepNamespace(i); + break; case OpCode.Invalid: throw new NotImplementedException($"Invalid opcode {i.OpCode}"); default: @@ -661,6 +664,14 @@ private void ExecMergeFlags(Instruction i) dest.Table.Members.Merge(src.Table.Members); } } + + private void ExecPrepNamespace(Instruction i) + { + for (int j = 0; j < i.NumVal; j++) + { + DynValue component = m_ValueStack.Pop(); + } + } private void ExecIterPrep() { diff --git a/src/WattleScript.Interpreter/Tree/Expressions/NewExpression.cs b/src/WattleScript.Interpreter/Tree/Expressions/NewExpression.cs index 77c070cf..2c475964 100644 --- a/src/WattleScript.Interpreter/Tree/Expressions/NewExpression.cs +++ b/src/WattleScript.Interpreter/Tree/Expressions/NewExpression.cs @@ -13,6 +13,8 @@ class NewExpression : Expression private SymbolRefExpression classRef; private List arguments; private string className; + private List namespaceQualifier = null; + private bool referencesNamespace => namespaceQualifier != null; public NewExpression(ScriptLoadingContext lcontext) : base(lcontext) { @@ -21,27 +23,20 @@ public NewExpression(ScriptLoadingContext lcontext) : base(lcontext) if (lcontext.Lexer.Current.Type == TokenType.Dot) // possible namespace qualifier { - List namespaceQualifier = ParseNamespace(lcontext, true); + namespaceQualifier = ParseNamespace(lcontext, true); namespaceQualifier.Insert(0, classTok); if (namespaceQualifier.Count < 2) // at least ident-dot { - throw new SyntaxErrorException(namespaceQualifier[namespaceQualifier.Count - 1], $"Got unexpected token '{namespaceQualifier[namespaceQualifier.Count - 1].Text}' while parsing namespace"); + throw new SyntaxErrorException(namespaceQualifier[namespaceQualifier.Count - 1], $"Unexpected token '{namespaceQualifier[namespaceQualifier.Count - 1].Text}' while parsing namespace in 'new' expresson"); } - + classTok = CheckTokenType(lcontext, TokenType.Name); } className = classTok.Text; CheckTokenType(lcontext, TokenType.Brk_Open_Round); - if (lcontext.Lexer.Current.Type == TokenType.Brk_Close_Round) - { - arguments = new List(); - } - else - { - arguments = ExprList(lcontext); - } + arguments = lcontext.Lexer.Current.Type == TokenType.Brk_Close_Round ? new List() : ExprList(lcontext); var end = CheckTokenType(lcontext, TokenType.Brk_Close_Round); SourceRef = classTok.GetSourceRef(end); } @@ -52,6 +47,13 @@ public override void Compile(FunctionBuilder bc) classRef.Compile(bc); foreach(var a in arguments) a.CompilePossibleLiteral(bc); + + if (referencesNamespace) + { + // [todo] update indexing for fully qualified access + // bc.Emit_PrepNmspc(namespaceQualifier); + } + bc.Emit_NewCall(arguments.Count, className); bc.PopSourceRef(); } diff --git a/src/WattleScript.Interpreter/Tree/Linker/Linker.cs b/src/WattleScript.Interpreter/Tree/Linker/Linker.cs index 2cf16099..78a67d20 100644 --- a/src/WattleScript.Interpreter/Tree/Linker/Linker.cs +++ b/src/WattleScript.Interpreter/Tree/Linker/Linker.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using WattleScript.Interpreter.Debugging; using WattleScript.Interpreter.Execution; @@ -29,9 +30,15 @@ internal class Linker private StringBuilder usingIdent = new StringBuilder(); private string lastNamespace; + internal class StatementInfo + { + public IStaticallyImportableStatement Statement { get; set; } + public bool Compiled { get; set; } + } + //public string ProcessedSource => output.ToString(); public Dictionary ResolvedUsings = new Dictionary(); - public Dictionary> ImportMap = new Dictionary>(); + public Dictionary> ImportMap = new Dictionary>(); public Linker(Script script, int sourceIndex, string text, Dictionary defines = null) @@ -57,33 +64,14 @@ void ProcessNamespaceStatement(ScriptLoadingContext lcontext) { CheckTokenType(lcontext, TokenType.Namespace); bool canBeDot = false; - StringBuilder namespaceIdent = new StringBuilder(); - - while (lcontext.Lexer.PeekNext().Type != TokenType.Eof) - { - Token tkn = lcontext.Lexer.Current; - - if (!canBeDot && tkn.Type != TokenType.Name) - { - break; - } + List namespaceTokens = ParseNamespace(lcontext, false, true); - if (canBeDot && tkn.Type != TokenType.Dot) - { - break; - } - - canBeDot = !canBeDot; - - namespaceIdent.Append(tkn.Text); - lcontext.Lexer.Next(); - } - - string namespaceIdentStr = namespaceIdent.ToString(); + string namespaceIdentStr = string.Join(string.Empty, namespaceTokens.Select(x => x.Text)).ToString(); lcontext.Linker.CurrentNamespace = namespaceIdentStr; - if (lcontext.Lexer.Current.Type == TokenType.Brk_Open_Curly) + if (lcontext.Lexer.PeekNext().Type == TokenType.Brk_Open_Curly) { + lcontext.Lexer.Next(); CheckTokenType(lcontext, TokenType.Brk_Open_Curly); Loop(lcontext, true); } @@ -145,30 +133,32 @@ void ProcessUsingStatement(ScriptLoadingContext lcontext) void EnlistNamespace(string nmspc) { - ImportMap.GetOrCreate(nmspc, () => new Dictionary()); + ImportMap.GetOrCreate(nmspc, () => new Dictionary()); } void EnlistMember(IStaticallyImportableStatement statement) { - EnlistNamespace("test"); + string nmspc = statement.Namespace ?? ""; + + EnlistNamespace(nmspc); - if (ImportMap["test"].TryGetValue(statement.NameToken.Text, out _)) + if (ImportMap[nmspc].TryGetValue(statement.NameToken.Text, out _)) { throw new SyntaxErrorException(statement.NameToken, $"Member '{statement.NameToken.Text}' is already defined as {statement.DefinitionType}"); } - ImportMap["test"].Add(statement.NameToken.Text, statement); + ImportMap[nmspc].Add(statement.NameToken.Text, new StatementInfo() {Compiled = false, Statement = statement}); } public List Export() { List statements = new List(); - foreach (KeyValuePair> nmspc in ImportMap) + foreach (KeyValuePair> nmspc in ImportMap) { - foreach (KeyValuePair member in nmspc.Value) + foreach (KeyValuePair member in nmspc.Value) { - statements.Add((Statement) member.Value); + statements.Add((Statement) member.Value.Statement); } } @@ -210,15 +200,15 @@ void Loop(ScriptLoadingContext lcontext, bool breakOnNextBlockEnd = false) case TokenType.Enum: anyNonUsingEncounterd = true; EnlistMember(new EnumDefinitionStatement(lcontext)); - break; + goto afterUsingStatement; case TokenType.Class: anyNonUsingEncounterd = true; EnlistMember(new ClassDefinitionStatement(lcontext)); - break; + goto afterUsingStatement; case TokenType.Mixin: anyNonUsingEncounterd = true; EnlistMember(new MixinDefinitionStatement(lcontext)); - break; + goto afterUsingStatement; default: anyNonUsingEncounterd = true; break; @@ -238,6 +228,29 @@ void Process(ScriptLoadingContext lcontext) int z = 0; } + public void Link(string nmspc) + { + if (ResolvedUsings.ContainsKey(nmspc)) + { + return; + } + + Module resolvedModule = script.Options.ScriptLoader.UsingResolver(nmspc); + + if (resolvedModule == null) + { + throw new LinkerException($"module '{nmspc}' failed to resolve"); + } + + if (resolvedModule.IsEntryRef) + { + return; + } + + ResolvedUsings.Add(nmspc, resolvedModule); + Process(nmspc, resolvedModule.Code); + } + public void Process() { Process(lcontextLocal); diff --git a/src/WattleScript.Interpreter/Tree/NodeBase.cs b/src/WattleScript.Interpreter/Tree/NodeBase.cs index 8d49dee4..b770cb6b 100644 --- a/src/WattleScript.Interpreter/Tree/NodeBase.cs +++ b/src/WattleScript.Interpreter/Tree/NodeBase.cs @@ -27,13 +27,22 @@ internal static Token UnexpectedTokenType(Token t) }; } + internal static void ParseSemicolons(ScriptLoadingContext lcontext) + { + while (lcontext.Lexer.PeekNext().Type == TokenType.SemiColon) + { + lcontext.Lexer.Next(); + } + } + /// /// Parses a sequence of tokens in form of (name->dot->name->..) /// /// Current ScriptLoadingContext /// Whether the first token should be "name" or "dot" + /// /// A list of tokens representing the qualifier - protected static List ParseNamespace(ScriptLoadingContext lcontext, bool currentTokenShouldBeDot) + internal static List ParseNamespace(ScriptLoadingContext lcontext, bool currentTokenShouldBeDot, bool includeLastToken = false) { List tokens = new List(); @@ -41,11 +50,19 @@ protected static List ParseNamespace(ScriptLoadingContext lcontext, bool { if (currentTokenShouldBeDot && lcontext.Lexer.PeekNext().Type != TokenType.Name) { + if (includeLastToken) + { + tokens.Add(lcontext.Lexer.Current); + } break; } if (!currentTokenShouldBeDot && lcontext.Lexer.PeekNext().Type != TokenType.Dot) { + if (includeLastToken) + { + tokens.Add(lcontext.Lexer.Current); + } break; } @@ -62,7 +79,7 @@ protected static List ParseNamespace(ScriptLoadingContext lcontext, bool return tokens; } - protected static Token CheckTokenTypeEx(ScriptLoadingContext lcontext, TokenType tokenType1, TokenType tokenType2) + internal static Token CheckTokenTypeEx(ScriptLoadingContext lcontext, TokenType tokenType1, TokenType tokenType2) { if (lcontext.Syntax != ScriptSyntax.Lua) { @@ -90,7 +107,7 @@ internal static Token CheckTokenType(ScriptLoadingContext lcontext, TokenType to - protected static Token CheckTokenType(ScriptLoadingContext lcontext, TokenType tokenType1, TokenType tokenType2) + internal static Token CheckTokenType(ScriptLoadingContext lcontext, TokenType tokenType1, TokenType tokenType2) { Token t = lcontext.Lexer.Current; if (t.Type != tokenType1 && t.Type != tokenType2) @@ -100,7 +117,7 @@ protected static Token CheckTokenType(ScriptLoadingContext lcontext, TokenType t return t; } - protected static Token CheckTokenType(ScriptLoadingContext lcontext, TokenType tokenType1, TokenType tokenType2, TokenType tokenType3) + internal static Token CheckTokenType(ScriptLoadingContext lcontext, TokenType tokenType1, TokenType tokenType2, TokenType tokenType3) { Token t = lcontext.Lexer.Current; if (t.Type != tokenType1 && t.Type != tokenType2 && t.Type != tokenType3) @@ -111,14 +128,14 @@ protected static Token CheckTokenType(ScriptLoadingContext lcontext, TokenType t return t; } - protected static void CheckTokenTypeNotNext(ScriptLoadingContext lcontext, TokenType tokenType) + internal static void CheckTokenTypeNotNext(ScriptLoadingContext lcontext, TokenType tokenType) { Token t = lcontext.Lexer.Current; if (t.Type != tokenType) UnexpectedTokenType(t); } - protected static Token CheckMatch(ScriptLoadingContext lcontext, Token originalToken, TokenType expectedTokenType, string expectedTokenText) + internal static Token CheckMatch(ScriptLoadingContext lcontext, Token originalToken, TokenType expectedTokenType, string expectedTokenText) { Token t = lcontext.Lexer.Current; if (t.Type != expectedTokenType) diff --git a/src/WattleScript.Interpreter/Tree/Statements/ClassDefinitionStatement.cs b/src/WattleScript.Interpreter/Tree/Statements/ClassDefinitionStatement.cs index 895cc608..939c230b 100644 --- a/src/WattleScript.Interpreter/Tree/Statements/ClassDefinitionStatement.cs +++ b/src/WattleScript.Interpreter/Tree/Statements/ClassDefinitionStatement.cs @@ -51,9 +51,11 @@ class ClassDefinitionStatement : Statement, IStaticallyImportableStatement private Dictionary mixinRefs = new Dictionary(); private MemberModifierFlags flags = MemberModifierFlags.None; + private ScriptLoadingContext lcontext; public ClassDefinitionStatement(ScriptLoadingContext lcontext) : base(lcontext) { + this.lcontext = lcontext; Namespace = lcontext.Linker.CurrentNamespace; while (lcontext.Lexer.Current.IsMemberModifier()) @@ -423,6 +425,19 @@ void CompileNew(FunctionBuilder parent) public override void Compile(FunctionBuilder bc) { + if (lcontext.Linker.ImportMap.ContainsKey(Namespace)) + { + if (lcontext.Linker.ImportMap[Namespace].ContainsKey(className)) + { + if (lcontext.Linker.ImportMap[Namespace][className].Compiled) + { + return; + } + + lcontext.Linker.ImportMap[Namespace][className].Compiled = true; + } + } + bc.PushSourceRef(defSource); bc.Emit_Enter(classBlock); //mixin names diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.lua b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.lua index 0b77ddce..17d32c5d 100644 --- a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.lua +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.lua @@ -1,7 +1,8 @@ namespace lib.a { class test { - + x = 10 } } -a = new lib.a.test() \ No newline at end of file +a = new lib.a.test() +print(a.x) \ No newline at end of file diff --git a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.txt b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.txt index e69de29b..9a037142 100644 --- a/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.txt +++ b/src/WattleScript.Tests/EndToEnd/CLike/SyntaxCLike/Linker/2-namespace-empty.txt @@ -0,0 +1 @@ +10 \ No newline at end of file From 093632e4474621aaf7aa84cbf89119b33f6b2e4e Mon Sep 17 00:00:00 2001 From: lofcz Date: Thu, 13 Oct 2022 17:34:21 +0200 Subject: [PATCH 7/9] Revert --- .../Tree/Statements/ClassDefinitionStatement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WattleScript.Interpreter/Tree/Statements/ClassDefinitionStatement.cs b/src/WattleScript.Interpreter/Tree/Statements/ClassDefinitionStatement.cs index 939c230b..62a37597 100644 --- a/src/WattleScript.Interpreter/Tree/Statements/ClassDefinitionStatement.cs +++ b/src/WattleScript.Interpreter/Tree/Statements/ClassDefinitionStatement.cs @@ -425,7 +425,7 @@ void CompileNew(FunctionBuilder parent) public override void Compile(FunctionBuilder bc) { - if (lcontext.Linker.ImportMap.ContainsKey(Namespace)) + if (false && lcontext.Linker.ImportMap.ContainsKey(Namespace)) { if (lcontext.Linker.ImportMap[Namespace].ContainsKey(className)) { From 9e82fb79f90cb511f7eea0cf01204f55d5662461 Mon Sep 17 00:00:00 2001 From: lofcz Date: Thu, 13 Oct 2022 17:38:12 +0200 Subject: [PATCH 8/9] Improve library resolution error --- src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs b/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs index 8c9a37e0..0460a066 100644 --- a/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs +++ b/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs @@ -58,8 +58,8 @@ Script InitScript(string scriptFolderPath) string libText = File.ReadAllText($"{scriptFolderPath}\\{path}.wtlib"); return new Module(libText); } - - return null; + + throw new ScriptRuntimeException($"Library file {scriptFolderPath}\\{path}.wtlib not found"); }; return script; From f5b17bc8f36be3f416d0998beea8cf864792481d Mon Sep 17 00:00:00 2001 From: lofcz Date: Thu, 13 Oct 2022 17:41:09 +0200 Subject: [PATCH 9/9] Update CLikeTestRunner.cs --- src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs b/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs index 0460a066..2c518bb0 100644 --- a/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs +++ b/src/WattleScript.Tests/EndToEnd/CLikeTestRunner.cs @@ -59,6 +59,7 @@ Script InitScript(string scriptFolderPath) return new Module(libText); } + Assert.Fail($"Library file {scriptFolderPath}\\{path}.wtlib not found"); throw new ScriptRuntimeException($"Library file {scriptFolderPath}\\{path}.wtlib not found"); };