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

View file

@ -8,30 +8,34 @@ namespace arookas
{
public class sunCompiler
{
Stack<string> 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<string>(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

View file

@ -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<sunFileArgs> EnterFile;
public event EventHandler<sunFileArgs> 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;
}
}

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