Added: new import resolver system which allows for custom user import resolving.

This commit is contained in:
arookas 2015-12-08 20:11:35 -05:00
parent 08864e5e1f
commit e08b2dc888
7 changed files with 213 additions and 129 deletions

View file

@ -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);
} }
} }

View file

@ -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; if (name == null)
{
throw new ArgumentNullException("name");
} }
if (output == null)
public sunCompilerResults Compile(string file, Stream output)
{ {
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

View file

@ -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)); {
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 parser = new sunParser();
var tree = parser.Parse(file); var tree = parser.Parse(file);
tree.Compile(this); tree.Compile(this);
OnExitFile(new sunFileArgs(file)); ImportResolver.ExitFile(file);
Imports.PopDir(); }
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;
} }
} }

View 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
View 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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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())
{
try
{ {
var parser = new __sunParser(input); var parser = new __sunParser(input);
var node = parser.Parse(); var node = parser.Parse();
return CreateAst(file, node); return CreateAst(file.Name, node);
}
catch (ParserLogException ex)
{
throw new sunParserException(file.Name, ex[0]);
}
} }
} }