diff --git a/Core/EVIL.Grammar/Parsing/Statements/TopLevel/Parser.FnStatement.cs b/Core/EVIL.Grammar/Parsing/Statements/Parser.FnStatement.cs similarity index 84% rename from Core/EVIL.Grammar/Parsing/Statements/TopLevel/Parser.FnStatement.cs rename to Core/EVIL.Grammar/Parsing/Statements/Parser.FnStatement.cs index 95c139d6..0736edbf 100644 --- a/Core/EVIL.Grammar/Parsing/Statements/TopLevel/Parser.FnStatement.cs +++ b/Core/EVIL.Grammar/Parsing/Statements/Parser.FnStatement.cs @@ -18,6 +18,14 @@ private FnStatement FnStatement() } var (line, col) = Match(Token.Fn); + if (_functionDescent > 0) + { + throw new ParserException( + "Named function definitions may only appear outside of other functions.", + (line, col) + ); + } + var functionIdentifier = Identifier(); var parameterList = ParameterList(); diff --git a/VirtualMachine/Ceres/ExecutionEngine/Diagnostics/Script.cs b/VirtualMachine/Ceres/ExecutionEngine/Diagnostics/Script.cs deleted file mode 100644 index b65b81a2..00000000 --- a/VirtualMachine/Ceres/ExecutionEngine/Diagnostics/Script.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Ceres.ExecutionEngine.Collections; -using Ceres.ExecutionEngine.Marshaling; -using Ceres.ExecutionEngine.TypeSystem; - -namespace Ceres.ExecutionEngine.Diagnostics -{ - public sealed class Script : IDynamicValueProvider - { - private List _chunks = new(); - - public IReadOnlyList Chunks => _chunks; - - public Chunk? this[string name] - { - get - { - try - { - return FindChunkByName(name); - } - catch (InvalidOperationException) - { - return null; - } - } - } - - public Chunk? this[int id] - { - get - { - if (id < 0 || id >= Chunks.Count) - return null; - - return Chunks[id]; - } - } - - public Chunk CreateChunk(string name, out bool replacedExisting, out Chunk replacedChunk) - { - replacedExisting = false; - replacedChunk = null!; - - var chunk = new Chunk(name); - AddChunk(chunk, out replacedExisting, out replacedChunk); - return chunk; - } - - public void AddChunk(Chunk chunk, out bool replacedExisting, out Chunk replacedChunk) - { - replacedExisting = false; - replacedChunk = null!; - - if (TryFindChunkByName(chunk.Name, out replacedChunk)) - { - _chunks.Remove(replacedChunk); - replacedExisting = true; - } - - _chunks.Add(chunk); - } - - public bool RemoveChunk(string name) - { - if (!TryFindChunkByName(name, out var chunk)) - return false; - - return _chunks.Remove(chunk); - } - - public bool RemoveChunk(Chunk chunk) - => _chunks.Remove(chunk); - - public Chunk FindChunkByName(string name) - { - return Chunks.FirstOrDefault(x => x.Name == name) - ?? throw new InvalidOperationException($"Chunk {name} not found."); - } - - public bool ChunkExists(string name) - => Chunks.FirstOrDefault(x => x.Name == name) != null; - - public bool TryFindChunkByName(string name, out Chunk chunk) - { - chunk = null!; - - try - { - chunk = FindChunkByName(name); - return true; - } - catch - { - return false; - } - } - - public DynamicValue ToDynamicValue() - { - var chunks = new Table(); - - for (var i = 0; i < Chunks.Count; i++) - { - var chunk = Chunks[i]; - chunks[chunk.Name] = chunk; - } - - return new Table - { - { "chunks", chunks } - }; - } - } -} \ No newline at end of file diff --git a/VirtualMachine/Ceres/TranslationEngine/Compiler.Statement.Fn.cs b/VirtualMachine/Ceres/TranslationEngine/Compiler.Statement.Fn.cs index f39eafc0..503b9f2b 100644 --- a/VirtualMachine/Ceres/TranslationEngine/Compiler.Statement.Fn.cs +++ b/VirtualMachine/Ceres/TranslationEngine/Compiler.Statement.Fn.cs @@ -30,7 +30,8 @@ public override void Visit(FnStatement fnStatement) if (wasExistingReplaced) { Log.EmitWarning( - $"Redefining chunk '{replacedChunk.Name}' previously defined in {replacedChunk.DebugDatabase.DefinedInFile} on line {replacedChunk.DebugDatabase.DefinedOnLine}.", + $"Named function '{replacedChunk.Name}' defined on line {fnStatement.Line} is " + + $"re-defining a previously defined function of the same name in {replacedChunk.DebugDatabase.DefinedInFile} on line {fnStatement.Line}.", CurrentFileName, EvilMessageCode.FnStatementRedefinedExistingChunk, fnStatement.Line, diff --git a/VirtualMachine/Ceres/TranslationEngine/Compiler.cs b/VirtualMachine/Ceres/TranslationEngine/Compiler.cs index 94550340..e689adb1 100644 --- a/VirtualMachine/Ceres/TranslationEngine/Compiler.cs +++ b/VirtualMachine/Ceres/TranslationEngine/Compiler.cs @@ -22,7 +22,6 @@ public partial class Compiler : AstVisitor private readonly Stack _chunks = new(); private Dictionary> _attributeProcessors = new(); - private List _includeProcessors = new(); private int _blockDescent; private readonly Stack _loopDescent = new(); @@ -71,6 +70,7 @@ public Compiler(bool optimizeCodeGeneration = false) public Chunk Compile(string source, string fileName = "") { + CurrentFileName = fileName; var parser = new Parser(); try @@ -214,14 +214,6 @@ public void RegisterAttributeProcessor(string attributeName, AttributeProcessor list.Add(processor); } - public void RegisterIncludeProcessor(IncludeProcessor processor) - { - if (!_includeProcessors.Contains(processor)) - { - _includeProcessors.Add(processor); - } - } - private DynamicValue ExtractConstantValueFrom(AstNode valueNode) { if (valueNode is BooleanConstant boolConst) diff --git a/VirtualMachine/Ceres/TranslationEngine/Diagnostics/CompilerMessage.cs b/VirtualMachine/Ceres/TranslationEngine/Diagnostics/CompilerMessage.cs index 619444bc..5bbdcd9a 100644 --- a/VirtualMachine/Ceres/TranslationEngine/Diagnostics/CompilerMessage.cs +++ b/VirtualMachine/Ceres/TranslationEngine/Diagnostics/CompilerMessage.cs @@ -20,25 +20,23 @@ public override string ToString() sb.Append($"EV{MessageCode:D4}"); } - sb.Append($" :: {Severity} :: {Message}"); - - if (FileName != null) - { - sb.Append($" :: {FileName}"); - } - + sb.Append($" :: {Severity}"); if (Line > 0) { - sb.Append($" (line {Line}"); + sb.Append($" :: line {Line}"); if (Column > 0) { sb.Append($", column {Column}"); } - - sb.Append(")"); } + if (!string.IsNullOrEmpty(FileName)) + { + sb.Append($" :: {FileName}"); + } + + sb.Append($" :: {Message}"); return sb.ToString(); } } diff --git a/VirtualMachine/Ceres/TranslationEngine/IncludeProcessor.cs b/VirtualMachine/Ceres/TranslationEngine/IncludeProcessor.cs deleted file mode 100644 index 74a79b03..00000000 --- a/VirtualMachine/Ceres/TranslationEngine/IncludeProcessor.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using Ceres.ExecutionEngine.Diagnostics; - -namespace Ceres.TranslationEngine -{ - public delegate IEnumerable IncludeProcessor( - Compiler compiler, - Script script, - string includePath, - out bool wasAlreadyIncluded - ); -} \ No newline at end of file diff --git a/VirtualMachine/Tests/Ceres.LanguageTests/Ceres.LanguageTests.csproj b/VirtualMachine/Tests/Ceres.LanguageTests/Ceres.LanguageTests.csproj index a5add307..2d98f962 100644 --- a/VirtualMachine/Tests/Ceres.LanguageTests/Ceres.LanguageTests.csproj +++ b/VirtualMachine/Tests/Ceres.LanguageTests/Ceres.LanguageTests.csproj @@ -69,7 +69,7 @@ Always - + Always diff --git a/VirtualMachine/Tests/Ceres.LanguageTests/TestRunner.cs b/VirtualMachine/Tests/Ceres.LanguageTests/TestRunner.cs index 96c58fac..6b783818 100644 --- a/VirtualMachine/Tests/Ceres.LanguageTests/TestRunner.cs +++ b/VirtualMachine/Tests/Ceres.LanguageTests/TestRunner.cs @@ -23,7 +23,7 @@ public class TestRunner private TextWriter TextOut { get; } private EvilRuntime Runtime { get; } - private Dictionary TestScripts { get; } = new(); + private Dictionary TestRoots { get; } = new(); private Dictionary> FailureLog { get; } = new(); public TestRunner(string testDirectory, CeresVM vm, TextWriter? textOut = null) @@ -44,7 +44,6 @@ private void CompileTests() .ToList(); var compiler = new Compiler(); - compiler.RegisterIncludeProcessor(IncludeProcessor); compiler.RegisterAttributeProcessor("approximate", AttributeProcessors.ApproximateAttribute); compiler.RegisterAttributeProcessor("disasm", AttributeProcessors.DisasmAttribute); @@ -54,8 +53,8 @@ private void CompileTests() try { TextOut.Write($"Compiling test '{path}'..."); - var script = compiler.Compile(source, Path.GetFullPath(path)); - TestScripts.Add(path, script); + var rootChunk = compiler.Compile(source, Path.GetFullPath(path)); + TestRoots.Add(path, rootChunk); TextOut.WriteLine(" [ PASS ]"); if (compiler.Log.HasAnyMessages) @@ -74,71 +73,28 @@ private void CompileTests() TextOut.WriteLine(); } - private IEnumerable IncludeProcessor(Compiler compiler, Script script, string path, out bool isRedundantInclude) - { - isRedundantInclude = false; - - var fullPathToInclude = Path.Combine( - Path.GetDirectoryName(compiler.CurrentFileName)!, - path - ); - - if (!File.Exists(fullPathToInclude)) - { - throw new FileNotFoundException("Cannot find the included file.", fullPathToInclude); - } - - if (fullPathToInclude == compiler.CurrentFileName) - { - throw new InvalidOperationException($"Recursive include detected in '{compiler.CurrentFileName}."); - } - - if (_includeCache.TryGetValue(fullPathToInclude, out var chunks)) - { - isRedundantInclude = true; - return chunks; - } - - try - { - var includeCompiler = new Compiler(); - includeCompiler.RegisterIncludeProcessor(IncludeProcessor); - - var includedScript = includeCompiler.Compile(File.ReadAllText(fullPathToInclude), fullPathToInclude); - - - chunks = includedScript.Chunks; - _includeCache.Add(fullPathToInclude, chunks); - return chunks; - } - catch (CompilerException ce) - { - throw new TestBuildPhaseException( - $"Failed to compile the included file '{path}':\n{ce.Log}", ce - ); - } - } - public async Task RunTests() { - foreach (var testdesc in TestScripts) + foreach (var testRoot in TestRoots) { var passed = 0; var failed = 0; var ignored = 0; - var path = testdesc.Key; + var path = testRoot.Key; TextOut.WriteLine($"--- [TEST '{path}'] ---"); - var testScript = testdesc.Value; + var testRootChunk = testRoot.Value; + var testChunks = new List(); + foreach (var (name, id) in testRootChunk.NamedSubChunkLookup) + { + var chunk = testRootChunk.SubChunks[id]; + if (chunk.HasAttribute("test")) + { + testChunks.Add(chunk); + } - var onInitChunks = testScript.Chunks.Where( - x => x.HasAttribute("on_init") - ); - - var testChunks = testScript.Chunks.Where( - x => x.HasAttribute("test") - ).ToList(); + } if (testChunks.Count == 0) { @@ -155,21 +111,7 @@ public async Task RunTests() Runtime.RegisterBuiltInModules(); Runtime.RegisterModule(out _); - var nonTestChunks = testScript.Chunks.Where( - x => !x.HasAttribute("test") - && !x.HasAttribute("on_init") - ).ToList(); - - foreach (var chunk in nonTestChunks) - { - VM.Global.Set(chunk.Name, chunk); - } - - foreach (var onInitChunk in onInitChunks) - { - TextOut.WriteLine($"Running test set-up function '{onInitChunk.Name}'..."); - VM.MainFiber.Schedule(onInitChunk); - } + VM.MainFiber.Schedule(testRootChunk); VM.MainFiber.BlockUntilFinished(); for (var i = 0; i < testChunks.Count; i++) diff --git a/VirtualMachine/Tests/Ceres.LanguageTests/tests/019_include.vil b/VirtualMachine/Tests/Ceres.LanguageTests/tests/019_require.vil.disabled similarity index 100% rename from VirtualMachine/Tests/Ceres.LanguageTests/tests/019_include.vil rename to VirtualMachine/Tests/Ceres.LanguageTests/tests/019_require.vil.disabled diff --git a/VirtualMachine/Tests/Ceres.LanguageTests/tests/021_override.vil b/VirtualMachine/Tests/Ceres.LanguageTests/tests/021_override.vil index c1a3d8c0..cdeb5fc6 100644 --- a/VirtualMachine/Tests/Ceres.LanguageTests/tests/021_override.vil +++ b/VirtualMachine/Tests/Ceres.LanguageTests/tests/021_override.vil @@ -1,76 +1,71 @@ -#[on_init] -fn _on_init() { - - Box = { new: self::fn(value) { - val table = { - __value: value, - - set_value: self::fn(value) -> self.__value = value, - get_value: self::fn() -> self.__value - }; +Box = { new: self::fn(value) { + val table = { + __value: value, - override(table)::__add(a, b) -> Box::new(a::get_value() + b::get_value()); - override(table)::__sub(a, b) -> Box::new(a::get_value() - b::get_value()); - override(table)::__mul(a, b) -> Box::new(a::get_value() * b::get_value()); - override(table)::__div(a, b) -> Box::new(a::get_value() / b::get_value()); - override(table)::__mod(a, b) -> Box::new(a::get_value() % b::get_value()); - override(table)::__shl(a, b) -> Box::new(a::get_value() << b::get_value()); - override(table)::__shr(a, b) -> Box::new(a::get_value() >> b::get_value()); - override(table)::__aneg(a) -> Box::new(-a::get_value()); - override(table)::__inc(a) -> Box::new(a::get_value() + 1); - override(table)::__dec(a) -> Box::new(a::get_value() - 1); - override(table)::__lnot(a) -> Box::new(!!!a::get_value()); - override(table)::__lor(a, b) -> Box::new(a::get_value() || b::get_value()); - override(table)::__land(a, b) -> Box::new(a::get_value() && b::get_value()); - override(table)::__bor(a, b) -> Box::new(a::get_value() | b::get_value()); - override(table)::__bxor(a, b) -> Box::new(a::get_value() ^ b::get_value()); - override(table)::__band(a, b) -> Box::new(a::get_value() & b::get_value()); - override(table)::__bnot(a) -> Box::new(~a::get_value()); - override(table)::__deq(a, b) -> Box::new(a::get_value() <==> b::get_value()); - override(table)::__dne(a, b) -> Box::new(a::get_value() b::get_value()); - override(table)::__eq(a, b) -> Box::new(a::get_value() == b::get_value()); - override(table)::__ne(a, b) -> Box::new(a::get_value() != b::get_value()); - override(table)::__gt(a, b) -> Box::new(a::get_value() > b::get_value()); - override(table)::__lt(a, b) -> Box::new(a::get_value() < b::get_value()); - override(table)::__gte(a, b) -> Box::new(a::get_value() >= b::get_value()); - override(table)::__lte(a, b) -> Box::new(a::get_value() <= b::get_value()); - override(table)::__len(a) -> "lmao"; - override(table)::__tonum(a) -> 21.37; - override(table)::__tostr(a) -> "tostring result"; - - override(table)::__invoke(a, b, c) { - ret @self::get_value() + "-" + @a + "-" + @b + "-" + @c; + set_value: self::fn(value) -> self.__value = value, + get_value: self::fn() -> self.__value + }; + + override(table)::__add(a, b) -> Box::new(a::get_value() + b::get_value()); + override(table)::__sub(a, b) -> Box::new(a::get_value() - b::get_value()); + override(table)::__mul(a, b) -> Box::new(a::get_value() * b::get_value()); + override(table)::__div(a, b) -> Box::new(a::get_value() / b::get_value()); + override(table)::__mod(a, b) -> Box::new(a::get_value() % b::get_value()); + override(table)::__shl(a, b) -> Box::new(a::get_value() << b::get_value()); + override(table)::__shr(a, b) -> Box::new(a::get_value() >> b::get_value()); + override(table)::__aneg(a) -> Box::new(-a::get_value()); + override(table)::__inc(a) -> Box::new(a::get_value() + 1); + override(table)::__dec(a) -> Box::new(a::get_value() - 1); + override(table)::__lnot(a) -> Box::new(!!!a::get_value()); + override(table)::__lor(a, b) -> Box::new(a::get_value() || b::get_value()); + override(table)::__land(a, b) -> Box::new(a::get_value() && b::get_value()); + override(table)::__bor(a, b) -> Box::new(a::get_value() | b::get_value()); + override(table)::__bxor(a, b) -> Box::new(a::get_value() ^ b::get_value()); + override(table)::__band(a, b) -> Box::new(a::get_value() & b::get_value()); + override(table)::__bnot(a) -> Box::new(~a::get_value()); + override(table)::__deq(a, b) -> Box::new(a::get_value() <==> b::get_value()); + override(table)::__dne(a, b) -> Box::new(a::get_value() b::get_value()); + override(table)::__eq(a, b) -> Box::new(a::get_value() == b::get_value()); + override(table)::__ne(a, b) -> Box::new(a::get_value() != b::get_value()); + override(table)::__gt(a, b) -> Box::new(a::get_value() > b::get_value()); + override(table)::__lt(a, b) -> Box::new(a::get_value() < b::get_value()); + override(table)::__gte(a, b) -> Box::new(a::get_value() >= b::get_value()); + override(table)::__lte(a, b) -> Box::new(a::get_value() <= b::get_value()); + override(table)::__len(a) -> "lmao"; + override(table)::__tonum(a) -> 21.37; + override(table)::__tostr(a) -> "tostring result"; + + override(table)::__invoke(a, b, c) { + ret @self::get_value() + "-" + @a + "-" + @b + "-" + @c; + } + + override(table)::__set(k, v) { + if (k == "jp2") { + tbl.rawset(self, "jp2", "gmd"); + ret 21.37; } - - override(table)::__set(k, v) { - if (k == "jp2") { - tbl.rawset(self, "jp2", "gmd"); - ret 21.37; - } - - ret tbl.rawset(self, k, v); - } - - override(table)::__get(k) { - if (k == "jp2gmd") { - ret 21.37; - } - - ret tbl.rawget(self, k); + + ret tbl.rawset(self, k, v); + } + + override(table)::__get(k) { + if (k == "jp2gmd") { + ret 21.37; } - override(table)::__exists(k) { - if (k == "jp2gmd") { - ret 11.11; - } - - ret tbl.rawget(self, k) !is Nil; + ret tbl.rawget(self, k); + } + + override(table)::__exists(k) { + if (k == "jp2gmd") { + ret 11.11; } - ret table; - }}; + ret tbl.rawget(self, k) !is Nil; + } -} + ret table; +}}; #[test] fn add_override() {