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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
45
compiler.cs
45
compiler.cs
|
@ -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
|
||||
|
|
78
context.cs
78
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<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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
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 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue