diff --git a/ast/nodes.statements.cs b/ast/nodes.statements.cs index e3bd7de..2a85f66 100644 --- a/ast/nodes.statements.cs +++ b/ast/nodes.statements.cs @@ -28,12 +28,12 @@ public override void Compile(sunContext context) { - var file = context.Imports.ResolveImport(this); - if (file == null) + var result = context.Import(ImportFile.Value); + switch (result) { - return; // the file has already been imported + case sunImportResult.Missing: + case sunImportResult.FailedToLoad: throw new sunMissingImportException(this); } - context.Compile(file); } } diff --git a/compiler.cs b/compiler.cs index 0e3ada6..32fa484 100644 --- a/compiler.cs +++ b/compiler.cs @@ -8,30 +8,34 @@ namespace arookas { public class sunCompiler { - Stack files; - string defaultRootDir, rootDir; - public string RootDir + public sunCompilerResults Compile(string name, Stream output) { - get { return rootDir ?? defaultRootDir; } - set { rootDir = value; } + return Compile(name, output, sunImportResolver.Default); } - - public sunCompiler() - { - defaultRootDir = AppDomain.CurrentDomain.BaseDirectory; - } - - public sunCompilerResults Compile(string file, Stream output) + public sunCompilerResults Compile(string name, Stream output, sunImportResolver resolver) { + if (name == null) + { + throw new ArgumentNullException("name"); + } + if (output == null) + { + throw new ArgumentNullException("output"); + } + if (resolver == null) + { + throw new ArgumentNullException("resolver"); + } var results = new sunCompilerResults(); var timer = Stopwatch.StartNew(); try { - files = new Stack(5); - sunContext context = new sunContext(output, RootDir); - context.EnterFile += EnterFile; - context.ExitFile += ExitFile; - context.Compile(file); + sunContext context = new sunContext(output, resolver); + var result = context.Import(name); + if (result != sunImportResult.Loaded) + { + throw new sunImportException(name, result); + } context.Text.Terminate(); // NOTETOSELF: don't do this in sunScript because imported files will add this as well foreach (var function in context.SymbolTable.Functions) { @@ -51,17 +55,10 @@ namespace arookas { results.Error = ex; } - catch (ParserLogException ex) - { - results.Error = new sunParserException(files.Peek(), ex[0]); - } timer.Stop(); results.CompileTime = timer.Elapsed; return results; } - - void EnterFile(object sender, sunFileArgs e) { files.Push(e.File); } - void ExitFile(object sender, sunFileArgs e) { files.Pop(); } } public class sunCompilerResults diff --git a/context.cs b/context.cs index 6ea4d24..f87c130 100644 --- a/context.cs +++ b/context.cs @@ -16,21 +16,31 @@ namespace arookas public sunSymbolTable SymbolTable { get; private set; } public sunScopeStack Scopes { get; private set; } public sunLoopStack Loops { get; private set; } - public sunImportTable Imports { get; private set; } - - public event EventHandler EnterFile; - public event EventHandler ExitFile; + public sunImportResolver ImportResolver { get; private set; } // open/close - public sunContext(Stream stream, string rootDir) + public sunContext(Stream output) + : this(output, sunImportResolver.Default) { + + } + public sunContext(Stream output, sunImportResolver importResolver) + { + if (output == null) + { + throw new ArgumentNullException("output"); + } + if (importResolver == null) + { + throw new ArgumentNullException("importResolver"); + } DataTable = new sunDataTable(); SymbolTable = new sunSymbolTable(); Scopes = new sunScopeStack(); Loops = new sunLoopStack(); - Imports = new sunImportTable(rootDir); + ImportResolver = importResolver; - writer = new aBinaryWriter(stream, Endianness.Big, Encoding.GetEncoding(932)); + writer = new aBinaryWriter(output, Endianness.Big, Encoding.GetEncoding(932)); Text = new sunWriter(writer); writer.PushAnchor(); @@ -67,16 +77,24 @@ namespace arookas return false; } - // imports - public void Compile(string file) + // imports/compilation + public sunImportResult Import(string name) { - Imports.PushDir(file); - OnEnterFile(new sunFileArgs(file)); - var parser = new sunParser(); - var tree = parser.Parse(file); - tree.Compile(this); - OnExitFile(new sunFileArgs(file)); - Imports.PopDir(); + if (name == null) + { + throw new ArgumentNullException("name"); + } + sunScriptFile file; + var result = ImportResolver.ResolveImport(name, out file); + if (result == sunImportResult.Loaded) + { + ImportResolver.EnterFile(file); + var parser = new sunParser(); + var tree = parser.Parse(file); + tree.Compile(this); + ImportResolver.ExitFile(file); + } + return result; } // builtins @@ -218,32 +236,18 @@ namespace arookas writer.WriteS32(SymbolTable.Count); writer.WriteS32(Scopes.Root.VariableCount); } - - // events - void OnEnterFile(sunFileArgs e) - { - var func = EnterFile; - if (func != null) - { - func(this, e); - } - } - void OnExitFile(sunFileArgs e) - { - var func = ExitFile; - if (func != null) - { - func(this, e); - } - } } - class sunFileArgs : EventArgs + public class sunFileArgs : EventArgs { - public string File { get; private set; } + public sunScriptFile File { get; private set; } - public sunFileArgs(string file) + public sunFileArgs(sunScriptFile file) { + if (file == null) + { + throw new ArgumentNullException("file"); + } File = file; } } diff --git a/exceptions.cs b/exceptions.cs index 6579e4f..ec05c86 100644 --- a/exceptions.cs +++ b/exceptions.cs @@ -33,6 +33,27 @@ namespace arookas } } + public class sunImportException : sunCompilerException + { + public string Name { get; private set; } + public sunImportResult Result { get; private set; } + public override string Message { get { return String.Format("Name: {0}, Result: {1}", Name, Result); } } + + public sunImportException(string name, sunImportResult result) + { + if (name == null) + { + throw new ArgumentNullException("name"); + } + if (!result.IsDefined()) + { + throw new ArgumentOutOfRangeException("name"); + } + Name = name; + Result = result; + } + } + // wrapper around Grammatica exceptions class sunParserException : sunScriptException { diff --git a/import resolver.cs b/import resolver.cs new file mode 100644 index 0000000..54d7600 --- /dev/null +++ b/import resolver.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace arookas +{ + public abstract class sunImportResolver + { + static sunImportResolver defaultResolver = new sunDefaultImportResolver(); + public static sunImportResolver Default { get { return defaultResolver; } } + + public abstract void EnterFile(sunScriptFile file); + public abstract void ExitFile(sunScriptFile file); + public abstract sunImportResult ResolveImport(string name, out sunScriptFile file); + + // default implementation + sealed class sunDefaultImportResolver : sunImportResolver + { + List imports = new List(10); + Stack current = new Stack(5); + string rootDirectory; + + public sunDefaultImportResolver() + { + rootDirectory = AppDomain.CurrentDomain.BaseDirectory; + } + + public override void EnterFile(sunScriptFile file) { current.Push(file); } + public override void ExitFile(sunScriptFile file) { current.Pop(); } + public override sunImportResult ResolveImport(string name, out sunScriptFile file) + { + file = null; + string fullPath; + if (Path.IsPathRooted(name)) + { + // if the path is absolute, just use it directly + fullPath = name; + if (!File.Exists(fullPath)) + { + return sunImportResult.Missing; + } + } + else + { + // check if the file exists relative to the current one; + // if it's not there, check the root directory + fullPath = Path.Combine(Path.GetDirectoryName(current.Peek().Name), name); + if (!File.Exists(fullPath)) + { + fullPath = Path.Combine(rootDirectory, name); + if (!File.Exists(fullPath)) + { + return sunImportResult.Missing; + } + } + } + // open the file + try + { + file = new sunScriptFile(name, File.OpenRead(fullPath)); + } + catch + { + return sunImportResult.FailedToLoad; + } + // make sure the file has not been imported yet + if (imports.Any(i => i.Name == fullPath)) + { + return sunImportResult.Skipped; + } + imports.Add(file); + return sunImportResult.Loaded; + } + } + } + + public enum sunImportResult + { + Loaded, + Skipped, + Missing, + FailedToLoad, + } + + public class sunScriptFile + { + public string Name { get; private set; } + public Stream Stream { get; private set; } + + public sunScriptFile(string name, Stream stream) + { + if (name == null) + { + throw new ArgumentNullException("name"); + } + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + if (!stream.CanRead) + { + throw new ArgumentException("Stream does not support reading.", "stream"); + } + Name = name; + Stream = stream; + } + + public TextReader GetReader() + { + return new StreamReader(Stream); + } + } +} diff --git a/import table.cs b/import table.cs deleted file mode 100644 index db48348..0000000 --- a/import table.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Collections.Generic; -using System.IO; - -namespace arookas -{ - class sunImportTable - { - List imports = new List(10); - Stack curDir = new Stack(5); - string RootDir { get; set; } - - public sunImportTable(string rootDir) - { - RootDir = rootDir; - } - - public void PushDir(string dir) { curDir.Push(Path.GetDirectoryName(dir)); } - public void PopDir() { curDir.Pop(); } - - public string ResolveImport(sunImport import) - { - string fullPath; - string file = import.ImportFile.Value; - if (Path.IsPathRooted(file)) - { - // if the path is absolute, just use it directly - fullPath = file; - if (!File.Exists(fullPath)) - { - // could not find file - throw new sunMissingImportException(import); - } - } - else - { - // check if the file exists relative to the current one; - // if it's not there, check the root directory - fullPath = Path.Combine(curDir.Peek(), file); - if (!File.Exists(fullPath)) - { - fullPath = Path.Combine(RootDir, file); - if (!File.Exists(fullPath)) - { - // could not find file - throw new sunMissingImportException(import); - } - } - } - // make sure the file has not been imported yet - if (imports.Contains(fullPath)) - { - return null; - } - imports.Add(fullPath); - return fullPath; - } - } -} diff --git a/parser.cs b/parser.cs index ae618b9..ca6c1b8 100644 --- a/parser.cs +++ b/parser.cs @@ -1,5 +1,4 @@ using PerCederberg.Grammatica.Runtime; -using System.IO; using System.Linq; namespace arookas @@ -16,13 +15,20 @@ namespace arookas "true", "false", }; - public sunNode Parse(string file) + public sunNode Parse(sunScriptFile file) { - using (var input = new StreamReader(file)) + using (var input = file.GetReader()) { - var parser = new __sunParser(input); - var node = parser.Parse(); - return CreateAst(file, node); + try + { + var parser = new __sunParser(input); + var node = parser.Parse(); + return CreateAst(file.Name, node); + } + catch (ParserLogException ex) + { + throw new sunParserException(file.Name, ex[0]); + } } }