Added: new import resolver system which allows for custom user import resolving.
This commit is contained in:
parent
08864e5e1f
commit
e08b2dc888
7 changed files with 213 additions and 129 deletions
|
@ -28,12 +28,12 @@
|
||||||
|
|
||||||
public override void Compile(sunContext context)
|
public override void Compile(sunContext context)
|
||||||
{
|
{
|
||||||
var file = context.Imports.ResolveImport(this);
|
var result = context.Import(ImportFile.Value);
|
||||||
if (file == null)
|
switch (result)
|
||||||
{
|
{
|
||||||
return; // the file has already been imported
|
case sunImportResult.Missing:
|
||||||
|
case sunImportResult.FailedToLoad: throw new sunMissingImportException(this);
|
||||||
}
|
}
|
||||||
context.Compile(file);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
45
compiler.cs
45
compiler.cs
|
@ -8,30 +8,34 @@ namespace arookas
|
||||||
{
|
{
|
||||||
public class sunCompiler
|
public class sunCompiler
|
||||||
{
|
{
|
||||||
Stack<string> files;
|
public sunCompilerResults Compile(string name, Stream output)
|
||||||
string defaultRootDir, rootDir;
|
|
||||||
public string RootDir
|
|
||||||
{
|
{
|
||||||
get { return rootDir ?? defaultRootDir; }
|
return Compile(name, output, sunImportResolver.Default);
|
||||||
set { rootDir = value; }
|
|
||||||
}
|
}
|
||||||
|
public sunCompilerResults Compile(string name, Stream output, sunImportResolver resolver)
|
||||||
public sunCompiler()
|
|
||||||
{
|
|
||||||
defaultRootDir = AppDomain.CurrentDomain.BaseDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sunCompilerResults Compile(string file, Stream output)
|
|
||||||
{
|
{
|
||||||
|
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 results = new sunCompilerResults();
|
||||||
var timer = Stopwatch.StartNew();
|
var timer = Stopwatch.StartNew();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
files = new Stack<string>(5);
|
sunContext context = new sunContext(output, resolver);
|
||||||
sunContext context = new sunContext(output, RootDir);
|
var result = context.Import(name);
|
||||||
context.EnterFile += EnterFile;
|
if (result != sunImportResult.Loaded)
|
||||||
context.ExitFile += ExitFile;
|
{
|
||||||
context.Compile(file);
|
throw new sunImportException(name, result);
|
||||||
|
}
|
||||||
context.Text.Terminate(); // NOTETOSELF: don't do this in sunScript because imported files will add this as well
|
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)
|
foreach (var function in context.SymbolTable.Functions)
|
||||||
{
|
{
|
||||||
|
@ -51,17 +55,10 @@ namespace arookas
|
||||||
{
|
{
|
||||||
results.Error = ex;
|
results.Error = ex;
|
||||||
}
|
}
|
||||||
catch (ParserLogException ex)
|
|
||||||
{
|
|
||||||
results.Error = new sunParserException(files.Peek(), ex[0]);
|
|
||||||
}
|
|
||||||
timer.Stop();
|
timer.Stop();
|
||||||
results.CompileTime = timer.Elapsed;
|
results.CompileTime = timer.Elapsed;
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnterFile(object sender, sunFileArgs e) { files.Push(e.File); }
|
|
||||||
void ExitFile(object sender, sunFileArgs e) { files.Pop(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class sunCompilerResults
|
public class sunCompilerResults
|
||||||
|
|
78
context.cs
78
context.cs
|
@ -16,21 +16,31 @@ namespace arookas
|
||||||
public sunSymbolTable SymbolTable { get; private set; }
|
public sunSymbolTable SymbolTable { get; private set; }
|
||||||
public sunScopeStack Scopes { get; private set; }
|
public sunScopeStack Scopes { get; private set; }
|
||||||
public sunLoopStack Loops { get; private set; }
|
public sunLoopStack Loops { get; private set; }
|
||||||
public sunImportTable Imports { get; private set; }
|
public sunImportResolver ImportResolver { get; private set; }
|
||||||
|
|
||||||
public event EventHandler<sunFileArgs> EnterFile;
|
|
||||||
public event EventHandler<sunFileArgs> ExitFile;
|
|
||||||
|
|
||||||
// open/close
|
// 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();
|
DataTable = new sunDataTable();
|
||||||
SymbolTable = new sunSymbolTable();
|
SymbolTable = new sunSymbolTable();
|
||||||
Scopes = new sunScopeStack();
|
Scopes = new sunScopeStack();
|
||||||
Loops = new sunLoopStack();
|
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);
|
Text = new sunWriter(writer);
|
||||||
writer.PushAnchor();
|
writer.PushAnchor();
|
||||||
|
|
||||||
|
@ -67,16 +77,24 @@ namespace arookas
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// imports
|
// imports/compilation
|
||||||
public void Compile(string file)
|
public sunImportResult Import(string name)
|
||||||
{
|
{
|
||||||
Imports.PushDir(file);
|
if (name == null)
|
||||||
OnEnterFile(new sunFileArgs(file));
|
{
|
||||||
var parser = new sunParser();
|
throw new ArgumentNullException("name");
|
||||||
var tree = parser.Parse(file);
|
}
|
||||||
tree.Compile(this);
|
sunScriptFile file;
|
||||||
OnExitFile(new sunFileArgs(file));
|
var result = ImportResolver.ResolveImport(name, out file);
|
||||||
Imports.PopDir();
|
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
|
// builtins
|
||||||
|
@ -218,32 +236,18 @@ namespace arookas
|
||||||
writer.WriteS32(SymbolTable.Count);
|
writer.WriteS32(SymbolTable.Count);
|
||||||
writer.WriteS32(Scopes.Root.VariableCount);
|
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;
|
File = file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
// wrapper around Grammatica exceptions
|
||||||
class sunParserException : sunScriptException
|
class sunParserException : sunScriptException
|
||||||
{
|
{
|
||||||
|
|
114
import resolver.cs
Normal file
114
import resolver.cs
Normal file
|
@ -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<sunScriptFile> imports = new List<sunScriptFile>(10);
|
||||||
|
Stack<sunScriptFile> current = new Stack<sunScriptFile>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,58 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace arookas
|
|
||||||
{
|
|
||||||
class sunImportTable
|
|
||||||
{
|
|
||||||
List<string> imports = new List<string>(10);
|
|
||||||
Stack<string> curDir = new Stack<string>(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
18
parser.cs
18
parser.cs
|
@ -1,5 +1,4 @@
|
||||||
using PerCederberg.Grammatica.Runtime;
|
using PerCederberg.Grammatica.Runtime;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace arookas
|
namespace arookas
|
||||||
|
@ -16,13 +15,20 @@ namespace arookas
|
||||||
"true", "false",
|
"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);
|
try
|
||||||
var node = parser.Parse();
|
{
|
||||||
return CreateAst(file, node);
|
var parser = new __sunParser(input);
|
||||||
|
var node = parser.Parse();
|
||||||
|
return CreateAst(file.Name, node);
|
||||||
|
}
|
||||||
|
catch (ParserLogException ex)
|
||||||
|
{
|
||||||
|
throw new sunParserException(file.Name, ex[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue