Added code so far.

This commit is contained in:
arookas 2015-12-06 23:15:02 -05:00
parent 12301380a2
commit 4f1e820006
22 changed files with 3397 additions and 0 deletions

16
AssemblyInfo.cs Normal file
View file

@ -0,0 +1,16 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("ssc")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ssc")]
[assembly: AssemblyCopyright("Copyright © 2015 arookas")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("5413c8e1-aefa-43bc-9fe5-c95f221da8c5")]
[assembly: AssemblyVersion("0.1.0.0")]
[assembly: AssemblyFileVersion("0.1.0.0")]

127
ast/nodes.cs Normal file
View file

@ -0,0 +1,127 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace arookas
{
public class sunSourceLocation
{
public string File { get; private set; }
public int Line { get; private set; }
public int Column { get; private set; }
public sunSourceLocation(string file, int line, int column)
{
File = file;
Line = line;
Column = column;
}
public override string ToString()
{
return String.Format("\"{0}\", ({1},{2})", File, Line, Column);
}
}
class sunNode : IEnumerable<sunNode>
{
List<sunNode> children;
public sunNode Parent { get; private set; }
public sunSourceLocation Location { get; private set; }
public int Count { get { return children.Count; } }
public sunNode this[int index] { get { return index >= 0 && index < Count ? children[index] : null; } }
public bool IsRoot { get { return Parent == null; } }
public bool IsBranch { get { return Count > 0; } }
public bool IsLeaf { get { return Count < 1; } }
public sunNode(sunSourceLocation location)
{
children = new List<sunNode>(5);
Location = location;
}
public void Add(sunNode node)
{
if (node == null)
{
throw new ArgumentNullException("node");
}
if (node.Parent != null)
{
node.Parent.Remove(node);
}
node.Parent = this;
children.Add(node);
}
public void Remove(sunNode node)
{
if (node == null)
{
throw new ArgumentNullException("node");
}
if (node.Parent == this)
{
children.Remove(node);
node.Parent = null;
}
}
public void Clear()
{
foreach (var child in this)
{
child.Parent = null;
}
children.Clear();
}
public virtual void Compile(sunContext context)
{
// Simply compile all children nodes by default. This is here for the transcient nodes' implementations
// (sunStatement, sunCompoundStatement, etc.) so I only have to type this once. sunExpression is careful
// to override this with the custom shunting-yard algorithm implementation.
foreach (var child in this)
{
child.Compile(context);
}
}
protected bool TryCompile(sunNode node, sunContext context)
{
if (node != null)
{
node.Compile(context);
return true;
}
return false;
}
public IEnumerator<sunNode> GetEnumerator() { return children.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
abstract class sunToken<TValue> : sunNode
{
public TValue Value { get; private set; }
protected sunToken(sunSourceLocation location, string token)
: base(location)
{
Value = ParseValue(token);
}
protected abstract TValue ParseValue(string token);
}
abstract class sunRawToken : sunToken<string>
{
protected sunRawToken(sunSourceLocation location, string token)
: base(location, token)
{
}
protected override string ParseValue(string token) { return token; }
}
}

122
ast/nodes.expressions.cs Normal file
View file

@ -0,0 +1,122 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace arookas
{
class sunExpression : sunNode
{
public sunExpression(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
Stack<sunOperator> operatorStack = new Stack<sunOperator>(32);
AnalyzeExpression(context, this, operatorStack);
}
void AnalyzeExpression(sunContext context, sunExpression expression, Stack<sunOperator> operatorStack)
{
// this implementation assumes that the expression production child list alternates between operand and operator
// we can safely assume this as the grammar "operand {binary_operator operand}" enforces it
int stackCount = operatorStack.Count;
foreach (var node in expression)
{
if (node is sunOperand)
{
var operand = node as sunOperand;
// term
var term = operand.Term;
if (term is sunExpression)
{
AnalyzeExpression(context, term as sunExpression, operatorStack);
}
else
{
term.Compile(context);
}
var unaryOperators = operand.UnaryOperators;
if (unaryOperators != null)
{
unaryOperators.Compile(context);
}
}
else if (node is sunOperator)
{
var operatorNode = node as sunOperator;
while (operatorStack.Count > stackCount &&
(operatorNode.IsLeftAssociative && operatorNode.Precedence <= operatorStack.Peek().Precedence) ||
(operatorNode.IsRightAssociative && operatorNode.Precedence < operatorStack.Peek().Precedence))
{
operatorStack.Pop().Compile(context);
}
operatorStack.Push(operatorNode);
}
}
while (operatorStack.Count > stackCount)
{
operatorStack.Pop().Compile(context);
}
}
}
class sunOperand : sunNode
{
public sunNode UnaryOperators { get { return Count > 1 ? this[0] : null; } }
public sunNode Term { get { return this[Count - 1]; } }
public sunOperand(sunSourceLocation location)
: base(location)
{
}
// operands are compiled in sunExpression.Compile
}
class sunUnaryOperatorList : sunNode
{
public sunUnaryOperatorList(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
foreach (var child in this.Reverse())
{
// compile unary operators in reverse order
child.Compile(context);
}
}
}
class sunTernaryOperator : sunNode
{
public sunExpression Condition { get { return this[0] as sunExpression; } }
public sunExpression TrueBody { get { return this[1] as sunExpression; } }
public sunExpression FalseBody { get { return this[2] as sunExpression; } }
public sunTernaryOperator(sunSourceLocation node)
: base(node)
{
}
public override void Compile(sunContext context)
{
Condition.Compile(context);
var falsePrologue = context.Text.GotoIfZero();
TrueBody.Compile(context);
var trueEpilogue = context.Text.Goto();
context.Text.ClosePoint(falsePrologue);
FalseBody.Compile(context);
context.Text.ClosePoint(trueEpilogue);
}
}
}

224
ast/nodes.flow.cs Normal file
View file

@ -0,0 +1,224 @@
using PerCederberg.Grammatica.Runtime;
using System.Linq;
namespace arookas
{
class sunIf : sunNode
{
public sunExpression Condition { get { return this[0] as sunExpression; } }
public sunNode TrueBody { get { return this[1]; } }
public sunNode FalseBody { get { return this[2]; } }
public sunIf(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
Condition.Compile(context);
var trueBodyEpilogue = context.Text.GotoIfZero();
TrueBody.Compile(context);
var falseBody = FalseBody;
if (falseBody != null)
{
var falseBodyEpilogue = context.Text.Goto();
context.Text.ClosePoint(trueBodyEpilogue);
falseBody.Compile(context);
context.Text.ClosePoint(falseBodyEpilogue);
}
else
{
context.Text.ClosePoint(trueBodyEpilogue);
}
}
}
abstract class sunLoop : sunNode
{
public bool IsNamed { get { return NameLabel != null; } }
public sunNameLabel NameLabel { get { return this[0] as sunNameLabel; } }
protected sunLoop(sunSourceLocation location)
: base(location)
{
}
}
class sunWhile : sunLoop
{
public sunExpression Condition { get { return this[Count - 2] as sunExpression; } }
public sunNode Body { get { return this[Count - 1]; } }
public sunWhile(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
context.Loops.Push(IsNamed ? NameLabel.Label.Value : null);
var bodyPrologue = context.Text.OpenPoint();
var continuePoint = context.Text.OpenPoint();
Condition.Compile(context);
var bodyEpilogue = context.Text.GotoIfZero();
Body.Compile(context);
context.Text.Goto(bodyPrologue);
context.Text.ClosePoint(bodyEpilogue);
var breakPoint = context.Text.OpenPoint();
context.Loops.Pop(context, breakPoint, continuePoint);
}
}
class sunDo : sunLoop
{
public sunNode Body { get { return this[Count - 2]; } }
public sunExpression Condition { get { return this[Count - 1] as sunExpression; } }
public sunDo(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
context.Loops.Push(IsNamed ? NameLabel.Label.Value : null);
var bodyPrologue = context.Text.OpenPoint();
Body.Compile(context);
var continuePoint = context.Text.OpenPoint();
Condition.Compile(context);
var bodyEpilogue = context.Text.GotoIfZero();
context.Text.Goto(bodyPrologue);
context.Text.ClosePoint(bodyEpilogue);
var breakPoint = context.Text.OpenPoint();
context.Loops.Pop(context, breakPoint, continuePoint);
}
}
class sunFor : sunLoop
{
public sunForDeclaration Declaration { get { return this.FirstOrDefault(i => i is sunForDeclaration) as sunForDeclaration; } }
public sunForCondition Condition { get { return this.FirstOrDefault(i => i is sunForCondition) as sunForCondition; } }
public sunForIteration Iteration { get { return this.FirstOrDefault(i => i is sunForIteration) as sunForIteration; } }
public sunNode Body { get { return this[Count - 1]; } }
public sunFor(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
context.Scopes.Push();
context.Loops.Push(IsNamed ? NameLabel.Label.Value : null);
TryCompile(Declaration, context);
var bodyPrologue = context.Text.OpenPoint();
TryCompile(Condition, context);
var bodyEpilogue = context.Text.GotoIfZero();
Body.Compile(context);
var continuePoint = context.Text.OpenPoint();
TryCompile(Iteration, context);
context.Text.Goto(bodyPrologue);
context.Text.ClosePoint(bodyEpilogue);
var breakPoint = context.Text.OpenPoint();
context.Loops.Pop(context, breakPoint, continuePoint);
context.Scopes.Pop();
}
}
class sunForDeclaration : sunNode
{
public sunForDeclaration(sunSourceLocation location)
: base(location)
{
}
}
class sunForCondition : sunNode
{
public sunForCondition(sunSourceLocation location)
: base(location)
{
}
}
class sunForIteration : sunNode
{
public sunForIteration(sunSourceLocation location)
: base(location)
{
}
}
class sunReturn : sunNode
{
public sunExpression Expression { get { return this[0] as sunExpression; } }
public sunReturn(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var expression = Expression;
if (expression != null)
{
expression.Compile(context);
context.Text.ReturnValue();
}
else
{
context.Text.ReturnVoid();
}
}
}
class sunBreak : sunNode
{
public bool IsNamed { get { return Count > 0; } }
public sunIdentifier NameLabel { get { return this[0] as sunIdentifier; } }
public sunBreak(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var point = context.Text.Goto();
if (!context.Loops.AddBreak(point, IsNamed ? NameLabel.Value : null))
{
throw new sunBreakException(this);
}
}
}
class sunContinue : sunNode
{
public bool IsNamed { get { return Count > 0; } }
public sunIdentifier NameLabel { get { return this[0] as sunIdentifier; } }
public sunContinue(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var point = context.Text.Goto();
if (!context.Loops.AddContinue(point, IsNamed ? NameLabel.Value : null))
{
throw new sunContinueException(this);
}
}
}
}

95
ast/nodes.functions.cs Normal file
View file

@ -0,0 +1,95 @@
using System.Collections.Generic;
using System.Linq;
namespace arookas
{
class sunBuiltinDeclaration : sunNode
{
public sunIdentifier Builtin { get { return this[0] as sunIdentifier; } }
public sunParameterList Parameters { get { return this[1] as sunParameterList; } }
public sunBuiltinDeclaration(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
context.DeclareBuiltin(this);
}
}
class sunFunctionDefinition : sunNode
{
public sunIdentifier Function { get { return this[0] as sunIdentifier; } }
public sunParameterList Parameters { get { return this[1] as sunParameterList; } }
public sunNode Body { get { return this[2]; } }
public sunFunctionDefinition(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
context.DefineFunction(this); // possibly counter intuitively, this defines the function in the context; it doesn't compile the definition body
}
}
class sunFunctionCall : sunNode
{
public sunIdentifier Function { get { return this[0] as sunIdentifier; } }
public sunNode Arguments { get { return this[1] as sunNode; } }
bool IsStatement { get { return !(Parent is sunOperand); } }
public sunFunctionCall(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var callableInfo = context.ResolveCallable(this);
if (!callableInfo.Parameters.ValidateArgumentCount(Arguments.Count))
{
throw new sunArgumentCountException(this, callableInfo);
}
Arguments.Compile(context);
callableInfo.OpenCallSite(context, Arguments.Count);
if (IsStatement)
{
context.Text.Pop();
}
}
}
class sunParameterList : sunNode
{
public IEnumerable<sunIdentifier> Parameters { get { return this.OfType<sunIdentifier>(); } }
public bool IsVariadic { get { return Count > 0 && this[Count - 1] is sunEllipsis; } }
public sunParameterInfo ParameterInfo { get { return new sunParameterInfo(Parameters, IsVariadic); } }
public sunParameterList(sunSourceLocation location)
: base(location)
{
int count = this.Count(i => i is sunEllipsis);
if (count > 1 || (count > 0 && !(this[Count - 1] is sunEllipsis)))
{
throw new sunVariadicParameterListException(this);
}
}
}
class sunEllipsis : sunNode
{
public sunEllipsis(sunSourceLocation location)
: base(location)
{
}
}
}

201
ast/nodes.literals.cs Normal file
View file

@ -0,0 +1,201 @@
using System;
using System.Globalization;
using System.Text;
namespace arookas
{
class sunIntLiteral : sunToken<int> // base-10 integer
{
public sunIntLiteral(sunSourceLocation location, string token)
: base(location, token)
{
}
protected override int ParseValue(string token) { return Int32.Parse(token); }
public override void Compile(sunContext context)
{
context.Text.PushInt(Value);
}
}
class sunHexLiteral : sunIntLiteral // base-16 integer
{
public sunHexLiteral(sunSourceLocation location, string token)
: base(location, token)
{
}
protected override int ParseValue(string token)
{
// because .NET's hex parsing is gay and doesn't support
// leading signs, manually detect negative literals
var neg = (token[0] == '-');
var trim = neg ? 3 : 2;
var digits = token.Substring(trim); // trim the '0x' prefix before parsing
var value = Int32.Parse(token.Substring(2), NumberStyles.AllowHexSpecifier);
if (neg)
{
value = -value;
}
return value;
}
}
class sunFloatLiteral : sunToken<float>
{
public sunFloatLiteral(sunSourceLocation location, string token)
: base(location, token)
{
}
protected override float ParseValue(string image) { return Single.Parse(image); }
public override void Compile(sunContext context)
{
context.Text.PushFloat(Value);
}
}
class sunStringLiteral : sunToken<string>
{
public sunStringLiteral(sunSourceLocation location, string token)
: base(location, token)
{
}
protected override string ParseValue(string image) { return UnescapeString(image.Substring(1, image.Length - 2)); } // remove enclosing quotes
public override void Compile(sunContext context)
{
context.Text.PushData(context.DataTable.Add(Value));
}
// string unescaping utility
string UnescapeString(string value)
{
// based on Hans Passant's code
StringBuilder sb = new StringBuilder(value.Length);
for (int i = 0; i < value.Length;)
{
int j = value.IndexOf('\\', i);
if (j < 0 || j >= value.Length - 1)
{
j = value.Length;
}
sb.Append(value, i, j - i);
if (j >= value.Length)
{
break;
}
switch (value[j + 1])
{
case '\'': sb.Append('\''); break;
case '"': sb.Append('"'); break;
case '\\': sb.Append('\\'); break;
case '0': sb.Append('\0'); break;
case 'a': sb.Append('\a'); break;
case 'b': sb.Append('\b'); break;
case 'f': sb.Append('\f'); break;
case 'n': sb.Append('n'); break;
case 't': sb.Append('\t'); break;
case 'v': sb.Append('\v'); break;
case 'x': sb.Append(UnescapeHex(value, j + 2, out i)); continue;
case 'u': sb.Append(UnescapeUnicodeCodeUnit(value, j + 2, out i)); continue;
case 'U': sb.Append(UnescapeUnicodeSurrogatePair(value, j + 2, out i)); continue;
default: throw new sunEscapeSequenceException(this);
}
i = j + 2;
}
return sb.ToString();
}
char UnescapeHex(string value, int start, out int end)
{
if (start > value.Length)
{
throw new sunEscapeSequenceException(this); // we need at least one digit
}
StringBuilder sb = new StringBuilder(4);
int digits = 0;
while (digits < 4 && start < value.Length && IsHexDigit(value[start]))
{
sb.Append(value[start]);
++digits;
++start;
}
end = start;
return (char)Int32.Parse(sb.ToString(), NumberStyles.AllowHexSpecifier);
}
char UnescapeUnicodeCodeUnit(string value, int start, out int end)
{
if (start >= value.Length - 4)
{
throw new sunEscapeSequenceException(this); // we need four digits
}
end = start + 4;
return (char)Int32.Parse(value.Substring(start, 4), NumberStyles.AllowHexSpecifier);
}
string UnescapeUnicodeSurrogatePair(string value, int start, out int end)
{
if (start >= value.Length - 8)
{
throw new sunEscapeSequenceException(this); // we need eight digits
}
char high = (char)Int32.Parse(value.Substring(start, 4), NumberStyles.AllowHexSpecifier);
char low = (char)Int32.Parse(value.Substring(start + 4, 4), NumberStyles.AllowHexSpecifier);
if (!Char.IsHighSurrogate(high) || !Char.IsLowSurrogate(low))
{
throw new sunEscapeSequenceException(this); // characters are not a surrogate pair
}
end = start + 8;
return String.Concat(high, low);
}
static bool IsHexDigit(char c)
{
return (c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'F') ||
(c >= 'a' && c <= 'f');
}
}
class sunIdentifier : sunRawToken
{
public sunIdentifier(sunSourceLocation location, string token)
: base(location, token)
{
// make sure it is a valid identifier name (i.e. not a keyword)
if (sunParser.IsKeyword(Value))
{
throw new sunIdentifierException(this);
}
}
// identifiers are compiled on a per-context basis (i.e. at a higher level)
}
class sunTrue : sunIntLiteral
{
public sunTrue(sunSourceLocation location, string token)
: base(location, token)
{
}
protected override int ParseValue(string token) { return 1; }
}
class sunFalse : sunIntLiteral
{
public sunFalse(sunSourceLocation location, string token)
: base(location, token)
{
}
protected override int ParseValue(string token) { return 0; }
}
}

453
ast/nodes.operators.cs Normal file
View file

@ -0,0 +1,453 @@
using PerCederberg.Grammatica.Runtime;
namespace arookas
{
enum Associativity
{
Left,
Right,
}
abstract class sunOperator : sunNode
{
public virtual Associativity Associativity { get { return Associativity.Left; } }
public abstract int Precedence { get; }
public bool IsLeftAssociative { get { return Associativity == Associativity.Left; } }
public bool IsRightAssociative { get { return Associativity == Associativity.Right; } }
protected sunOperator(sunSourceLocation location)
: base(location)
{
}
}
// precedence 0
class sunLogOR : sunOperator
{
public override int Precedence { get { return 0; } }
public sunLogOR(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.LogOR(); }
}
// precedence 1
class sunLogAND : sunOperator
{
public override int Precedence { get { return 1; } }
public sunLogAND(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.LogAND(); }
}
// precedence 2
class sunBitOR : sunOperator
{
public override int Precedence { get { return 2; } }
public sunBitOR(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.BitOR(); }
}
// precedence 3
class sunBitAND : sunOperator
{
public override int Precedence { get { return 3; } }
public sunBitAND(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.BitAND(); }
}
// precedence 4
class sunEq : sunOperator
{
public override int Precedence { get { return 4; } }
public sunEq(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.Eq(); }
}
class sunNtEq : sunOperator
{
public override int Precedence { get { return 4; } }
public sunNtEq(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.NtEq(); }
}
// precedence 5
class sunLt : sunOperator
{
public override int Precedence { get { return 5; } }
public sunLt(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.Lt(); }
}
class sunLtEq : sunOperator
{
public override int Precedence { get { return 5; } }
public sunLtEq(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.LtEq(); }
}
class sunGt : sunOperator
{
public override int Precedence { get { return 5; } }
public sunGt(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.Gt(); }
}
class sunGtEq : sunOperator
{
public override int Precedence { get { return 5; } }
public sunGtEq(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.GtEq(); }
}
// precedence 6
class sunBitLsh : sunOperator
{
public override int Precedence { get { return 6; } }
public sunBitLsh(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.ShL(); }
}
class sunBitRsh : sunOperator
{
public override int Precedence { get { return 6; } }
public sunBitRsh(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.ShR(); }
}
// precedence 7
class sunAdd : sunOperator
{
public override int Precedence { get { return 7; } }
public sunAdd(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.Add(); }
}
class sunSub : sunOperator
{
public override int Precedence { get { return 7; } }
public sunSub(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.Sub(); }
}
// precedence 8
class sunMul : sunOperator
{
public override int Precedence { get { return 8; } }
public sunMul(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.Mul(); }
}
class sunDiv : sunOperator
{
public override int Precedence { get { return 8; } }
public sunDiv(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.Div(); }
}
class sunMod : sunOperator
{
public override int Precedence { get { return 8; } }
public sunMod(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.Mod(); }
}
// precedence 9
class sunLogNOT : sunOperator
{
public override int Precedence { get { return 9; } }
public sunLogNOT(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.LogNOT(); }
}
class sunNeg : sunOperator
{
public override int Precedence { get { return 9; } }
public sunNeg(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context) { context.Text.Neg(); }
}
// assignment operators
class sunAssign : sunOperator
{
public override Associativity Associativity { get { return Associativity.Right; } }
public override int Precedence { get { return -1; } }
public sunAssign(sunSourceLocation location)
: base(location)
{
}
public virtual void Compile(sunContext context, sunVariableInfo variableInfo, sunExpression expression)
{
expression.Compile(context);
context.Text.StoreVariable(variableInfo);
}
}
class sunAssignAdd : sunAssign
{
public sunAssignAdd(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context, sunVariableInfo variableInfo, sunExpression expression)
{
context.Text.PushVariable(variableInfo);
expression.Compile(context);
context.Text.Add();
context.Text.StoreVariable(variableInfo);
}
}
class sunAssignSub : sunAssign
{
public sunAssignSub(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context, sunVariableInfo variableInfo, sunExpression expression)
{
context.Text.PushVariable(variableInfo);
expression.Compile(context);
context.Text.Sub();
context.Text.StoreVariable(variableInfo);
}
}
class sunAssignMul : sunAssign
{
public sunAssignMul(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context, sunVariableInfo variableInfo, sunExpression expression)
{
context.Text.PushVariable(variableInfo);
expression.Compile(context);
context.Text.Mul();
context.Text.StoreVariable(variableInfo);
}
}
class sunAssignDiv : sunAssign
{
public sunAssignDiv(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context, sunVariableInfo variableInfo, sunExpression expression)
{
context.Text.PushVariable(variableInfo);
expression.Compile(context);
context.Text.Div();
context.Text.StoreVariable(variableInfo);
}
}
class sunAssignMod : sunAssign
{
public sunAssignMod(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context, sunVariableInfo variableInfo, sunExpression expression)
{
context.Text.PushVariable(variableInfo);
expression.Compile(context);
context.Text.Mod();
context.Text.StoreVariable(variableInfo);
}
}
class sunAssignBitAND : sunAssign
{
public sunAssignBitAND(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context, sunVariableInfo variableInfo, sunExpression expression)
{
context.Text.PushVariable(variableInfo);
expression.Compile(context);
context.Text.BitAND();
context.Text.StoreVariable(variableInfo);
}
}
class sunAssignBitOR : sunAssign
{
public sunAssignBitOR(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context, sunVariableInfo variableInfo, sunExpression expression)
{
context.Text.PushVariable(variableInfo);
expression.Compile(context);
context.Text.BitOR();
context.Text.StoreVariable(variableInfo);
}
}
class sunAssignBitLsh : sunAssign
{
public sunAssignBitLsh(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context, sunVariableInfo variableInfo, sunExpression expression)
{
context.Text.PushVariable(variableInfo);
expression.Compile(context);
context.Text.ShL();
context.Text.StoreVariable(variableInfo);
}
}
class sunAssignBitRsh : sunAssign
{
public sunAssignBitRsh(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context, sunVariableInfo variableInfo, sunExpression expression)
{
context.Text.PushVariable(variableInfo);
expression.Compile(context);
context.Text.ShR();
context.Text.StoreVariable(variableInfo);
}
}
}

50
ast/nodes.statements.cs Normal file
View file

@ -0,0 +1,50 @@
namespace arookas
{
class sunStatementBlock : sunNode
{
public sunStatementBlock(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
context.Scopes.Push();
base.Compile(context);
context.Scopes.Pop();
}
}
class sunImport : sunNode
{
public sunStringLiteral ImportFile { get { return this[0] as sunStringLiteral; } }
public sunImport(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var file = context.Imports.ResolveImport(this);
if (file == null)
{
return; // the file has already been imported
}
context.Compile(file);
}
}
class sunNameLabel : sunNode
{
public sunIdentifier Label { get { return this[0] as sunIdentifier; } }
public sunNameLabel(sunSourceLocation location)
: base(location)
{
}
}
}

155
ast/nodes.system.cs Normal file
View file

@ -0,0 +1,155 @@
namespace arookas
{
class sunYield : sunNode
{
public sunYield(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var builtinInfo = context.ResolveSystemBuiltin("yield");
context.Text.CallBuiltin(builtinInfo.Index, 0);
context.Text.Pop();
}
}
class sunExit : sunNode
{
public sunExit(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var builtinInfo = context.ResolveSystemBuiltin("exit");
context.Text.CallBuiltin(builtinInfo.Index, 0);
context.Text.Pop();
}
}
class sunDump : sunNode
{
public sunDump(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var builtinInfo = context.ResolveSystemBuiltin("dump");
context.Text.CallBuiltin(builtinInfo.Index, 0);
context.Text.Pop();
}
}
class sunLock : sunNode
{
public sunLock(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var builtinInfo = context.ResolveSystemBuiltin("lock");
context.Text.CallBuiltin(builtinInfo.Index, 0);
context.Text.Pop();
}
}
class sunUnlock : sunNode
{
public sunUnlock(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var builtinInfo = context.ResolveSystemBuiltin("unlock");
context.Text.CallBuiltin(builtinInfo.Index, 0);
context.Text.Pop();
}
}
class sunIntCast : sunNode
{
public sunExpression Argument { get { return this[0] as sunExpression; } }
public sunIntCast(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var builtinInfo = context.DeclareSystemBuiltin("int", false, "x");
Argument.Compile(context);
context.Text.CallBuiltin(builtinInfo.Index, 1);
}
}
class sunFloatCast : sunNode
{
public sunExpression Argument { get { return this[0] as sunExpression; } }
public sunFloatCast(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var builtinInfo = context.ResolveSystemBuiltin("float");
Argument.Compile(context);
context.Text.CallBuiltin(builtinInfo.Index, 1);
}
}
class sunTypeofCast : sunNode
{
public sunExpression Argument { get { return this[0] as sunExpression; } }
public sunTypeofCast(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var builtinInfo = context.ResolveSystemBuiltin("typeof");
Argument.Compile(context);
context.Text.CallBuiltin(builtinInfo.Index, 1);
}
}
class sunPrint : sunNode
{
public sunNode ArgumentList { get { return this[0]; } }
public sunPrint(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var builtinInfo = context.ResolveSystemBuiltin("print");
ArgumentList.Compile(context);
context.Text.CallBuiltin(builtinInfo.Index, ArgumentList.Count);
context.Text.Pop();
}
}
}

96
ast/nodes.variables.cs Normal file
View file

@ -0,0 +1,96 @@
namespace arookas
{
class sunVariableReference : sunNode
{
public sunIdentifier Variable { get { return this[0] as sunIdentifier; } }
public sunVariableReference(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
sunVariableInfo variableInfo;
sunConstInfo constInfo;
context.ResolveVariableOrConstant(Variable, out variableInfo, out constInfo);
if (variableInfo != null)
{
context.Text.PushVariable(variableInfo);
}
if (constInfo != null)
{
constInfo.Expression.Compile(context);
}
}
}
class sunVariableDeclaration : sunNode
{
public sunIdentifier Variable { get { return this[0] as sunIdentifier; } }
public sunVariableDeclaration(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var variableInfo = context.DeclareVariable(Variable);
context.Text.DeclareLocal(1);
}
}
class sunVariableDefinition : sunVariableAssignment
{
public sunVariableDefinition(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var variableInfo = context.DeclareVariable(Variable);
context.Text.DeclareLocal(1);
base.Compile(context);
}
}
class sunVariableAssignment : sunVariableDeclaration
{
public sunAssign Operator { get { return this[1] as sunAssign; } }
public sunExpression Expression { get { return this[2] as sunExpression; } }
public sunVariableAssignment(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var variableInfo = context.ResolveVariable(Variable);
Operator.Compile(context, variableInfo, Expression);
}
}
class sunConstDefinition : sunNode
{
public sunIdentifier Constant { get { return this[0] as sunIdentifier; } }
public sunExpression Expression { get { return this[2] as sunExpression; } }
public sunConstDefinition(sunSourceLocation location)
: base(location)
{
}
public override void Compile(sunContext context)
{
var constInfo = context.DeclareConstant(Constant, Expression);
}
}
}

81
compiler.cs Normal file
View file

@ -0,0 +1,81 @@
using PerCederberg.Grammatica.Runtime;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace arookas
{
public class sunCompiler
{
Stack<string> files;
string defaultRootDir, rootDir;
public string RootDir
{
get { return rootDir ?? defaultRootDir; }
set { rootDir = value; }
}
public sunCompiler()
{
defaultRootDir = AppDomain.CurrentDomain.BaseDirectory;
}
public sunCompilerResults Compile(string file, Stream output)
{
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);
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)
{
function.Compile(context);
}
foreach (var function in context.SymbolTable.Functions)
{
function.CloseCallSites(context);
}
results.SymbolCount = context.SymbolTable.Count;
results.BuiltinCount = context.SymbolTable.BuiltinCount;
results.FunctionCount = context.SymbolTable.FunctionCount;
results.VariableCount = context.SymbolTable.VariableCount;
context.Dispose();
}
catch (sunCompilerException ex)
{
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
{
// success
public bool Success { get { return Error == null; } }
public sunCompilerException Error { get; internal set; }
// statistics
public int SymbolCount { get; internal set; }
public int BuiltinCount { get; internal set; }
public int FunctionCount { get; internal set; }
public int VariableCount { get; internal set; }
public TimeSpan CompileTime { get; internal set; }
}
}

250
context.cs Normal file
View file

@ -0,0 +1,250 @@
using arookas.IO.Binary;
using System;
using System.IO;
using System.Linq;
using System.Text;
namespace arookas
{
class sunContext : aDisposable
{
aBinaryWriter writer;
uint textOffset, dataOffset, symbolOffset;
public sunWriter Text { get; private set; }
public sunDataTable DataTable { get; private set; }
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;
// open/close
public sunContext(Stream stream, string rootDir)
{
DataTable = new sunDataTable();
SymbolTable = new sunSymbolTable();
Scopes = new sunScopeStack();
Loops = new sunLoopStack();
Imports = new sunImportTable(rootDir);
writer = new aBinaryWriter(stream, Endianness.Big, Encoding.GetEncoding(932));
Text = new sunWriter(writer);
writer.PushAnchor();
WriteHeader(); // dummy header
// begin text block
textOffset = (uint)writer.Position;
writer.PushAnchor(); // match code offsets and writer offsets
// add system builtins
DeclareSystemBuiltin("yield", false);
DeclareSystemBuiltin("exit", false);
DeclareSystemBuiltin("dump", false);
DeclareSystemBuiltin("lock", false);
DeclareSystemBuiltin("unlock", false);
DeclareSystemBuiltin("int", false, "x");
DeclareSystemBuiltin("float", false, "x");
DeclareSystemBuiltin("typeof", false, "x");
DeclareSystemBuiltin("print", true);
}
protected override bool Dispose(bool destructor)
{
if (!destructor)
{
writer.PopAnchor();
dataOffset = (uint)writer.Position;
DataTable.Write(writer);
symbolOffset = (uint)writer.Position;
SymbolTable.Write(writer);
writer.Goto(0);
WriteHeader();
return true; // don't dispose the writer so the stream doesn't get disposed
}
return false;
}
// imports
public void Compile(string file)
{
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();
}
// builtins
public sunBuiltinInfo DeclareBuiltin(sunBuiltinDeclaration node)
{
var symbolInfo = SymbolTable.Callables.FirstOrDefault(f => f.Name == node.Builtin.Value);
if (symbolInfo != null)
{
throw new sunRedeclaredBuiltinException(node);
}
var builtinInfo = new sunBuiltinInfo(node.Builtin.Value, node.Parameters.ParameterInfo, SymbolTable.Count);
SymbolTable.Add(builtinInfo);
return builtinInfo;
}
public sunBuiltinInfo DeclareSystemBuiltin(string name, bool variadic, params string[] parameters)
{
var builtinInfo = SymbolTable.Builtins.FirstOrDefault(f => f.Name == name);
if (builtinInfo == null)
{
builtinInfo = new sunBuiltinInfo(name, new sunParameterInfo(parameters, variadic), SymbolTable.Count);
SymbolTable.Add(builtinInfo);
}
return builtinInfo;
}
public sunBuiltinInfo ResolveSystemBuiltin(string name)
{
return SymbolTable.Builtins.FirstOrDefault(f => f.Name == name);
}
// functions
public sunFunctionInfo DefineFunction(sunFunctionDefinition node)
{
if (node.Parameters.IsVariadic)
{
throw new sunVariadicFunctionException(node);
}
var symbolInfo = SymbolTable.Callables.FirstOrDefault(f => f.Name == node.Function.Value);
if (symbolInfo != null)
{
throw new sunRedefinedFunctionException(node);
}
var functionInfo = new sunFunctionInfo(node.Function.Value, node.Parameters.ParameterInfo, node.Body);
SymbolTable.Add(functionInfo);
return functionInfo;
}
public sunCallableSymbolInfo ResolveCallable(sunFunctionCall node)
{
var symbolInfo = SymbolTable.Callables.FirstOrDefault(f => f.Name == node.Function.Value);
if (symbolInfo == null)
{
throw new sunUndefinedFunctionException(node);
}
return symbolInfo;
}
// variables
public sunVariableInfo DeclareVariable(sunIdentifier node)
{
// assert variable is not already declared in current scope
if (Scopes.Top.GetIsVariableDeclared(node.Value))
{
throw new sunRedeclaredVariableException(node);
}
var variableInfo = Scopes.Top.DeclareVariable(node.Value, Scopes.Count > 1 ? 1 : 0);
if (Scopes.IsRoot)
{
// global-scope variables are added to the symbol table
SymbolTable.Add(variableInfo);
}
return variableInfo;
}
public sunVariableInfo ResolveVariable(sunIdentifier node)
{
// walk the stack backwards to resolve to the variable's latest declaration
for (int i = Scopes.Count - 1; i >= 0; --i)
{
var variableInfo = Scopes[i].ResolveVariable(node.Value);
if (variableInfo != null)
{
return variableInfo;
}
}
throw new sunUndeclaredVariableException(node);
}
public sunVariableInfo DeclareParameter(string name) { return Scopes.Top.DeclareVariable(name, 1); }
// constants
public sunConstInfo DeclareConstant(sunIdentifier node, sunExpression expression)
{
if (Scopes.Top.GetIsConstantDeclared(node.Value))
{
throw new sunRedeclaredVariableException(node);
}
var constInfo = Scopes.Top.DeclareConstant(node.Value, expression);
return constInfo;
}
public sunConstInfo ResolveConstant(sunIdentifier node)
{
// walk the stack backwards to resolve to the variable's latest declaration
for (int i = Scopes.Count - 1; i >= 0; --i)
{
var constInfo = Scopes[i].ResolveConstant(node.Value);
if (constInfo != null)
{
return constInfo;
}
}
throw new sunUndeclaredVariableException(node);
}
public void ResolveVariableOrConstant(sunIdentifier node, out sunVariableInfo variableInfo, out sunConstInfo constInfo)
{
try
{
variableInfo = ResolveVariable(node);
}
catch
{
variableInfo = null;
}
try
{
constInfo = ResolveConstant(node);
}
catch
{
constInfo = null;
}
}
void WriteHeader()
{
writer.WriteString("SPCB");
writer.Write32(textOffset);
writer.Write32(dataOffset);
writer.WriteS32(DataTable.Count);
writer.Write32(symbolOffset);
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 string File { get; private set; }
public sunFileArgs(string file)
{
File = file;
}
}
}

42
data table.cs Normal file
View file

@ -0,0 +1,42 @@
using arookas.IO.Binary;
using System.Collections;
using System.Collections.Generic;
namespace arookas
{
class sunDataTable : IEnumerable<string>
{
List<string> data = new List<string>(10);
public int Count { get { return data.Count; } }
public int Add(string value)
{
int index = data.IndexOf(value);
if (index < 0)
{
index = data.Count;
data.Add(value);
}
return index;
}
public void Clear() { data.Clear(); }
public void Write(aBinaryWriter writer)
{
int ofs = 0;
foreach (var value in this)
{
writer.WriteS32(ofs);
ofs += value.Length + 1; // include terminator
}
foreach (var value in this)
{
writer.WriteString(value, aBinaryStringFormat.NullTerminated);
}
}
public IEnumerator<string> GetEnumerator() { return data.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
}

244
exceptions.cs Normal file
View file

@ -0,0 +1,244 @@
using PerCederberg.Grammatica.Runtime;
using System;
namespace arookas
{
// base exception type
public class sunCompilerException : Exception
{
public sunCompilerException()
{
}
public sunCompilerException(string format, params object[] args)
: base(String.Format(format, args))
{
}
}
// exceptions that have a location in the source
public abstract class sunScriptException : sunCompilerException
{
public abstract sunSourceLocation Location { get; }
public sunScriptException()
{
}
public sunScriptException(string format, params object[] args)
: base(format, args)
{
}
}
// wrapper around Grammatica exceptions
class sunParserException : sunScriptException
{
string file;
public ParseException Info { get; private set; }
public override string Message { get { return Info.ErrorMessage; } }
public override sunSourceLocation Location { get { return new sunSourceLocation(file, Info.Line, Info.Column); } }
public sunParserException(string file, ParseException info)
{
if (file == null)
{
throw new ArgumentNullException("file");
}
if (info == null)
{
throw new ArgumentNullException("info");
}
this.file = file;
Info = info;
}
}
// node exceptions
abstract class sunNodeException<TNode> : sunScriptException where TNode : sunNode
{
public TNode Node { get; private set; }
public override sunSourceLocation Location { get { return Node.Location; } }
protected sunNodeException(TNode node)
{
if (node == null)
{
throw new ArgumentNullException("node");
}
Node = node;
}
}
class sunRedeclaredBuiltinException : sunNodeException<sunBuiltinDeclaration>
{
public override string Message { get { return String.Format("Redeclared builtin '{0}'.", Node.Builtin.Value); } }
public sunRedeclaredBuiltinException(sunBuiltinDeclaration node)
: base(node)
{
}
}
class sunUndefinedFunctionException : sunNodeException<sunFunctionCall>
{
public override string Message { get { return String.Format("Undefined function or builtin '{0}'.", Node.Function.Value); } }
public sunUndefinedFunctionException(sunFunctionCall node)
: base(node)
{
}
}
class sunRedefinedFunctionException : sunNodeException<sunFunctionDefinition>
{
public override string Message { get { return String.Format("Redefined function '{0}'.", Node.Function.Value); } }
public sunRedefinedFunctionException(sunFunctionDefinition node)
: base(node)
{
}
}
class sunUndeclaredVariableException : sunNodeException<sunIdentifier>
{
public override string Message { get { return String.Format("Undeclared variable '{0}'.", Node.Value); } }
public sunUndeclaredVariableException(sunIdentifier node)
: base(node)
{
}
}
class sunRedeclaredVariableException : sunNodeException<sunIdentifier>
{
public override string Message { get { return String.Format("Redeclared variable '{0}'.", Node.Value); } }
public sunRedeclaredVariableException(sunIdentifier node)
: base(node)
{
}
}
class sunRedeclaredParameterException : sunNodeException<sunIdentifier>
{
public override string Message { get { return String.Format("Redeclared parameter '{0}'.", Node.Value); } }
public sunRedeclaredParameterException(sunIdentifier node)
: base(node)
{
}
}
class sunVariadicFunctionException : sunNodeException<sunFunctionDefinition>
{
public override string Message { get { return String.Format("Function '{0}' is defined as a variadic function (only builtins may be variadic).", Node.Function.Value); } }
public sunVariadicFunctionException(sunFunctionDefinition node)
: base(node)
{
}
}
class sunEscapeSequenceException : sunNodeException<sunStringLiteral>
{
public override string Message { get { return String.Format("Bad escape sequence in string."); } }
public sunEscapeSequenceException(sunStringLiteral node)
: base(node)
{
}
}
class sunVariadicParameterListException : sunNodeException<sunParameterList>
{
public override string Message { get { return String.Format("Bad variadic parameter list."); } }
public sunVariadicParameterListException(sunParameterList node)
: base(node)
{
}
}
class sunArgumentCountException : sunNodeException<sunFunctionCall>
{
public sunCallableSymbolInfo CalledSymbol { get; private set; }
public int ArgumentMinimum { get { return CalledSymbol.Parameters.Minimum; } }
public int ArgumentCount { get { return Node.Arguments.Count; } }
public override string Message
{
get
{
string format;
if (CalledSymbol.Parameters.IsVariadic)
{
// assuming to be missing because there's only a minimum
format = "Missing {0} argument(s) (expected at least {1}; got {2}).";
}
else if (Node.Arguments.Count < CalledSymbol.Parameters.Minimum)
{
format = "Missing {0} argument(s) (expected {1}; got {2})."; // missing arguments
}
else
{
format = "Too many arguments (expected {1}; got {2})."; // extra arguments
}
return String.Format(format, ArgumentMinimum - ArgumentCount, ArgumentMinimum, ArgumentCount);
}
}
public sunArgumentCountException(sunFunctionCall node, sunCallableSymbolInfo calledSymbol)
: base(node)
{
if (calledSymbol == null)
{
throw new ArgumentNullException("calledSymbol");
}
CalledSymbol = calledSymbol;
}
}
class sunIdentifierException : sunNodeException<sunIdentifier>
{
public override string Message { get { return String.Format("Invalid identifier '{0}'.", Node.Value); } }
public sunIdentifierException(sunIdentifier node)
: base(node)
{
}
}
class sunMissingImportException : sunNodeException<sunImport>
{
public override string Message { get { return String.Format("Could not find import file '{0}'.", Node.ImportFile.Value); } }
public sunMissingImportException(sunImport node)
: base(node)
{
}
}
class sunBreakException : sunNodeException<sunBreak>
{
public override string Message { get { return "Break statements must be placed within a loop statement."; } }
public sunBreakException(sunBreak node)
: base(node)
{
}
}
class sunContinueException : sunNodeException<sunContinue>
{
public override string Message { get { return "Continue statements must be placed within a loop statement."; } }
public sunContinueException(sunContinue node)
: base(node)
{
}
}
}

58
import table.cs Normal file
View file

@ -0,0 +1,58 @@
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;
}
}
}

79
loop stack.cs Normal file
View file

@ -0,0 +1,79 @@
using System.Collections.Generic;
using System.Linq;
namespace arookas
{
class sunLoopStack
{
Stack<sunLoop> loops = new Stack<sunLoop>(5);
sunLoop Top { get { return loops.Peek(); } }
sunLoop this[string name] { get { return loops.FirstOrDefault(i => i.Name == name); } }
public int Count { get { return loops.Count; } }
public void Push() { Push(null); }
public void Push(string name) { loops.Push(new sunLoop(name)); }
public void Pop(sunContext context, sunPoint breakPoint, sunPoint continuePoint)
{
foreach (var _break in Top.Breaks)
{
context.Text.ClosePoint(_break, breakPoint.Offset);
}
foreach (var _continue in Top.Continues)
{
context.Text.ClosePoint(_continue, continuePoint.Offset);
}
loops.Pop();
}
public bool AddBreak(sunPoint point) { return AddBreak(point, null); }
public bool AddContinue(sunPoint point) { return AddContinue(point, null); }
public bool AddBreak(sunPoint point, string name)
{
if (Count < 1)
{
return false;
}
var loop = name == null ? Top : this[name];
if (loop == null)
{
return false;
}
loop.Breaks.Add(point);
return true;
}
public bool AddContinue(sunPoint point, string name)
{
if (Count < 1)
{
return false;
}
var loop = name == null ? Top : this[name];
if (loop == null)
{
return false;
}
loop.Continues.Add(point);
return true;
}
class sunLoop
{
public string Name { get; private set; }
public List<sunPoint> Breaks { get; private set; }
public List<sunPoint> Continues { get; private set; }
public sunLoop()
: this(null)
{
}
public sunLoop(string name)
{
Name = name;
Breaks = new List<sunPoint>(5);
Continues = new List<sunPoint>(5);
}
}
}
}

70
main.cs Normal file
View file

@ -0,0 +1,70 @@
using System;
using System.IO;
namespace arookas
{
static class SSC
{
public static void Main(string[] args)
{
string inFile = args[0];
var compiler = new sunCompiler();
using (var output = OpenWrite(Path.ChangeExtension(inFile, ".sb")))
{
var results = compiler.Compile(inFile, output);
if (!results.Success)
{
if (results.Error is sunScriptException)
{
var error = results.Error as sunScriptException;
Console.WriteLine("ERROR:\n \"{0}\"\n pos ({1}, {2})\n{3}", error.Location.File, error.Location.Line, error.Location.Column, error.Message);
Console.ReadKey();
return;
}
else
{
var error = results.Error;
Console.WriteLine("ERROR:\n", error.Message);
Console.ReadKey();
return;
}
}
Console.WriteLine("Finished compiling in {0:F2}ms.", results.CompileTime.TotalMilliseconds);
Console.WriteLine("Symbol count: {0}", results.SymbolCount);
Console.WriteLine(" - builtins: {0}", results.BuiltinCount);
Console.WriteLine(" - functions: {0}", results.FunctionCount);
Console.WriteLine(" - variables: {0}", results.VariableCount);
Console.ReadKey();
}
}
static FileStream OpenRead(string path)
{
try
{
return File.OpenRead(path);
}
catch
{
Console.WriteLine("Failed to open the file '{0}'.\nPlease make sure the file exists and is not currently in use.", Path.GetFileName(path));
Console.ReadKey();
Environment.Exit(1);
return null;
}
}
static FileStream OpenWrite(string path)
{
try
{
return File.Create(path);
}
catch
{
Console.WriteLine("Failed to create the file '{0}'.", Path.GetFileName(path));
Console.ReadKey();
Environment.Exit(1);
return null;
}
}
}
}

322
parser.cs Normal file
View file

@ -0,0 +1,322 @@
using PerCederberg.Grammatica.Runtime;
using System;
using System.IO;
using System.Linq;
namespace arookas
{
class sunParser
{
static string[] keywords =
{
"import",
"builtin", "function", "var", "const",
"if", "while", "do", "for",
"return", "break", "continue",
"yield", "exit", "dump", "lock", "unlock", "int", "float", "typeof", "print",
"true", "false",
};
public sunNode Parse(string file)
{
using (var input = new StreamReader(file))
{
var parser = new __sunParser(input);
var node = parser.Parse();
return CreateAst(file, node);
}
}
static sunNode CreateAst(string file, Node node)
{
var ast = ConvertNode(file, node);
if (ast == null)
{
return null;
}
// children
if (node is Production)
{
var production = node as Production;
for (int i = 0; i < production.Count; ++i)
{
var child = CreateAst(file, production[i]);
if (child != null)
{
ast.Add(child);
}
}
}
// transcience
switch ((__sunConstants)node.Id)
{
case __sunConstants.STATEMENT:
case __sunConstants.COMPOUND_STATEMENT:
case __sunConstants.COMPOUND_STATEMENT_ITEM:
case __sunConstants.ASSIGNMENT_OPERATOR:
case __sunConstants.BINARY_OPERATOR:
case __sunConstants.UNARY_OPERATOR:
case __sunConstants.TERM:
case __sunConstants.PARAMETER:
case __sunConstants.ARGUMENT_LIST:
case __sunConstants.ARGUMENT:
{
return Transcient(ast);
}
}
return ast;
}
static sunNode ConvertNode(string file, Node node)
{
var location = new sunSourceLocation(file, node.StartLine, node.StartColumn);
string token = null;
if (node is Token)
{
token = (node as Token).Image;
}
// statements
switch (GetId(node))
{
case __sunConstants.SCRIPT: return new sunNode(location);
case __sunConstants.STATEMENT: return new sunNode(location);
case __sunConstants.STATEMENT_BLOCK: return new sunStatementBlock(location);
case __sunConstants.COMPOUND_STATEMENT: return new sunNode(location);
case __sunConstants.COMPOUND_STATEMENT_ITEM: return new sunNode(location);
case __sunConstants.IMPORT_STATEMENT: return new sunImport(location);
case __sunConstants.NAME_LABEL: return new sunNameLabel(location);
case __sunConstants.YIELD_STATEMENT: return new sunYield(location);
case __sunConstants.EXIT_STATEMENT: return new sunExit(location);
case __sunConstants.DUMP_STATEMENT: return new sunDump(location);
case __sunConstants.LOCK_STATEMENT: return new sunLock(location);
case __sunConstants.UNLOCK_STATEMENT: return new sunUnlock(location);
case __sunConstants.PRINT_STATEMENT: return new sunPrint(location);
}
// literals
switch (GetId(node))
{
case __sunConstants.INT_NUMBER: return new sunIntLiteral(location, token);
case __sunConstants.HEX_NUMBER: return new sunHexLiteral(location, token);
case __sunConstants.DEC_NUMBER: return new sunFloatLiteral(location, token);
case __sunConstants.STRING: return new sunStringLiteral(location, token);
case __sunConstants.IDENTIFIER: return new sunIdentifier(location, token);
case __sunConstants.ELLIPSIS: return new sunEllipsis(location);
case __sunConstants.TRUE: return new sunTrue(location, token);
case __sunConstants.FALSE: return new sunFalse(location, token);
}
// operators
switch (GetId(node))
{
case __sunConstants.ADD: return new sunAdd(location);
case __sunConstants.SUB:
{
if (GetId(node.Parent) == __sunConstants.UNARY_OPERATOR)
{
return new sunNeg(location);
}
return new sunSub(location);
}
case __sunConstants.MUL: return new sunMul(location);
case __sunConstants.DIV: return new sunDiv(location);
case __sunConstants.MOD: return new sunMod(location);
case __sunConstants.BIT_AND: return new sunBitAND(location);
case __sunConstants.BIT_OR: return new sunBitOR(location);
case __sunConstants.BIT_LSH: return new sunBitLsh(location);
case __sunConstants.BIT_RSH: return new sunBitRsh(location);
case __sunConstants.LOG_AND: return new sunLogAND(location);
case __sunConstants.LOG_OR: return new sunLogOR(location);
case __sunConstants.LOG_NOT: return new sunLogNOT(location);
case __sunConstants.EQ: return new sunEq(location);
case __sunConstants.NEQ: return new sunNtEq(location);
case __sunConstants.LT: return new sunLt(location);
case __sunConstants.GT: return new sunGt(location);
case __sunConstants.LTEQ: return new sunLtEq(location);
case __sunConstants.GTEQ: return new sunGtEq(location);
case __sunConstants.ASSIGN: return new sunAssign(location);
case __sunConstants.ASSIGN_ADD: return new sunAssignAdd(location);
case __sunConstants.ASSIGN_SUB: return new sunAssignSub(location);
case __sunConstants.ASSIGN_MUL: return new sunAssignMul(location);
case __sunConstants.ASSIGN_DIV: return new sunAssignDiv(location);
case __sunConstants.ASSIGN_MOD: return new sunAssignMod(location);
case __sunConstants.ASSIGN_BIT_AND: return new sunAssignBitAND(location);
case __sunConstants.ASSIGN_BIT_OR: return new sunAssignBitOR(location);
case __sunConstants.ASSIGN_BIT_LSH: return new sunAssignBitLsh(location);
case __sunConstants.ASSIGN_BIT_RSH: return new sunAssignBitRsh(location);
case __sunConstants.ASSIGNMENT_OPERATOR: return new sunNode(location);
case __sunConstants.TERNARY_OPERATOR: return new sunTernaryOperator(location);
case __sunConstants.BINARY_OPERATOR: return new sunNode(location);
case __sunConstants.UNARY_OPERATOR: return new sunNode(location);
}
// expressions
switch (GetId(node))
{
case __sunConstants.EXPRESSION: return new sunExpression(location);
case __sunConstants.OPERAND: return new sunOperand(location);
case __sunConstants.TERM: return new sunNode(location);
case __sunConstants.UNARY_OPERATOR_LIST: return new sunUnaryOperatorList(location);
case __sunConstants.INT_CAST: return new sunIntCast(location);
case __sunConstants.FLOAT_CAST: return new sunFloatCast(location);
case __sunConstants.TYPEOF_CAST: return new sunTypeofCast(location);
}
// builtins
switch (GetId(node))
{
case __sunConstants.BUILTIN_DECLARATION: return new sunBuiltinDeclaration(location);
}
// functions
switch (GetId(node))
{
case __sunConstants.FUNCTION_DEFINITION: return new sunFunctionDefinition(location);
case __sunConstants.FUNCTION_CALL: return new sunFunctionCall(location);
case __sunConstants.PARAMETER_LIST: return new sunParameterList(location);
case __sunConstants.PARAMETER: return new sunNode(location);
case __sunConstants.ARGUMENT_LIST: return new sunNode(location);
case __sunConstants.ARGUMENT: return new sunNode(location);
}
// variables
switch (GetId(node))
{
case __sunConstants.VARIABLE_REFERENCE: return new sunVariableReference(location);
case __sunConstants.VARIABLE_DECLARATION: return new sunVariableDeclaration(location);
case __sunConstants.VARIABLE_DEFINITION: return new sunVariableDefinition(location);
case __sunConstants.VARIABLE_ASSIGNMENT: return new sunVariableAssignment(location);
}
// constants
switch (GetId(node))
{
case __sunConstants.CONST_DEFINITION: return new sunConstDefinition(location);
}
// flow control
switch (GetId(node))
{
case __sunConstants.IF_STATEMENT: return new sunIf(location);
case __sunConstants.WHILE_STATEMENT: return new sunWhile(location);
case __sunConstants.DO_STATEMENT: return new sunDo(location);
case __sunConstants.FOR_STATEMENT: return new sunFor(location);
case __sunConstants.FOR_DECLARATION: return new sunForDeclaration(location);
case __sunConstants.FOR_CONDITION: return new sunForCondition(location);
case __sunConstants.FOR_ITERATION: return new sunForIteration(location);
case __sunConstants.RETURN_STATEMENT: return new sunReturn(location);
case __sunConstants.BREAK_STATEMENT: return new sunBreak(location);
case __sunConstants.CONTINUE_STATEMENT: return new sunContinue(location);
}
// cleanup keywords punctuation
switch (GetId(node))
{
// keywords
case __sunConstants.IMPORT:
case __sunConstants.BUILTIN:
case __sunConstants.FUNCTION:
case __sunConstants.VAR:
case __sunConstants.CONST:
case __sunConstants.IF:
case __sunConstants.ELSE:
case __sunConstants.DO:
case __sunConstants.WHILE:
case __sunConstants.FOR:
case __sunConstants.RETURN:
case __sunConstants.BREAK:
case __sunConstants.CONTINUE:
case __sunConstants.YIELD:
case __sunConstants.EXIT:
case __sunConstants.DUMP:
case __sunConstants.LOCK:
case __sunConstants.UNLOCK:
case __sunConstants.INT:
case __sunConstants.FLOAT:
case __sunConstants.TYPEOF:
case __sunConstants.PRINT:
case __sunConstants.TRUE:
case __sunConstants.FALSE:
// punctuation
case __sunConstants.L_BRACE:
case __sunConstants.R_BRACE:
case __sunConstants.L_PAREN:
case __sunConstants.R_PAREN:
case __sunConstants.L_BRACKET:
case __sunConstants.R_BRACKET:
case __sunConstants.COLON:
case __sunConstants.SEMICOLON:
case __sunConstants.COMMA:
case __sunConstants.DOT:
// case __sunConstants.ELLIPSIS: // do not exclude ellipsis for variadic parameters
case __sunConstants.QMARK:
{
return null;
}
}
// emergency fallback
return null;
}
static sunSourceLocation GetSourceLocation(string file, Node node)
{
if (file == null)
{
throw new ArgumentNullException("file");
}
if (node == null)
{
throw new ArgumentNullException("node");
}
if (node is Production)
{
var production = node as Production;
if (production.Count > 0)
{
return GetSourceLocation(file, production[0]);
}
throw new ArgumentException("node is a child-less production.", "node");
}
else if (node is Token)
{
var token = node as Token;
return new sunSourceLocation(file, token.StartLine, token.StartColumn);
}
throw new ArgumentException("node is an unsupported type.", "node");
}
static sunNode Transcient(sunNode node)
{
if (node == null)
{
throw new ArgumentNullException("node");
}
return node.Count == 1 ? node[0] : node;
}
static __sunConstants GetId(Node node)
{
return (__sunConstants)node.Id;
}
public static bool IsKeyword(string name)
{
return keywords.Contains(name);
}
}
}

93
scope stack.cs Normal file
View file

@ -0,0 +1,93 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace arookas
{
class sunScopeStack : IEnumerable<sunScope>
{
List<sunScope> stack = new List<sunScope>(8);
public int Count { get { return stack.Count; } }
public bool IsRoot { get { return Count == 1; } }
public sunScope Root { get { return this[0]; } }
public sunScope Top { get { return this[Count - 1]; } }
public sunScope this[int index] { get { return stack[index]; } }
public sunScopeStack()
{
Push(); // push global scope
}
public void Push()
{
stack.Add(new sunScope());
}
public void Pop()
{
if (!IsRoot)
{
stack.RemoveAt(Count - 1);
}
}
public void Clear()
{
stack.Clear();
Push(); // add global scope
}
public bool GetIsVariableDeclared(string name) { return stack.Any(i => i.GetIsVariableDeclared(name)); }
public IEnumerator<sunScope> GetEnumerator() { return stack.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
class sunScope
{
List<sunVariableInfo> variables = new List<sunVariableInfo>(10);
List<sunConstInfo> constants = new List<sunConstInfo>(10);
public int VariableCount { get { return variables.Count; } }
public int ConstantCount { get { return constants.Count; } }
public bool GetIsVariableDeclared(string name) { return variables.Any(v => v.Name == name); }
public sunVariableInfo DeclareVariable(string name, int display)
{
if (GetIsVariableDeclared(name) || GetIsConstantDeclared(name))
{
return null;
}
var variableInfo = new sunVariableInfo(name, display, variables.Count);
variables.Add(variableInfo);
return variableInfo;
}
public sunVariableInfo ResolveVariable(string name) { return variables.FirstOrDefault(v => v.Name == name); }
public bool GetIsConstantDeclared(string name) { return constants.Any(c => c.Name == name); }
public sunConstInfo DeclareConstant(string name, sunExpression expression)
{
if (GetIsVariableDeclared(name) || GetIsConstantDeclared(name))
{
return null;
}
var constInfo = new sunConstInfo(name, expression);
constants.Add(constInfo);
return constInfo;
}
public sunConstInfo ResolveConstant(string name) { return constants.FirstOrDefault(c => c.Name == name); }
}
class sunConstInfo
{
public string Name { get; private set; }
public sunExpression Expression { get; private set; }
public sunConstInfo(string name, sunExpression expression)
{
Name = name;
Expression = expression;
}
}
}

219
sunscript.grammar Normal file
View file

@ -0,0 +1,219 @@
%header%
GRAMMARTYPE = "LL"
DESCRIPTION = "A grammar for SunScript."
AUTHOR = "arookas"
VERSION = "1.1"
DATE = "2015/11/29"
LICENSE = "No license."
COPYRIGHT = "Copyright (c) 2015 arookas"
%tokens%
// whitespace
WHITESPACE = <<[ \t\r\n]+>> %ignore%
// comments
SINGLE_LINE_COMMENT = <<//.*>> %ignore%
MULTI_LINE_COMMENT = <</\*([^*]|\*+[^*/])*\*+/>> %ignore%
// keywords
IMPORT = "import"
BUILTIN = "builtin"
FUNCTION = "function"
VAR = "var"
CONST = "const"
IF = "if"
ELSE = "else"
DO = "do"
WHILE = "while"
FOR = "for"
RETURN = "return"
BREAK = "break"
CONTINUE = "continue"
YIELD = "yield"
EXIT = "exit"
DUMP = "dump"
LOCK = "lock"
UNLOCK = "unlock"
INT = "int"
FLOAT = "float"
TYPEOF = "typeof"
PRINT = "print"
TRUE = "true"
FALSE = "false"
// punctuation
L_BRACE = "{"
R_BRACE = "}"
L_PAREN = "("
R_PAREN = ")"
L_BRACKET = "["
R_BRACKET = "]"
COLON = ":"
SEMICOLON = ";"
COMMA = ","
DOT = "."
ELLIPSIS = "..."
QMARK = "?"
// operators
ADD = "+"
SUB = "-" // doubles as the negation operator
MUL = "*"
DIV = "/"
MOD = "%"
BIT_AND = "&"
BIT_OR = "|"
BIT_LSH = "<<"
BIT_RSH = ">>"
LOG_NOT = "!"
LOG_AND = "&&"
LOG_OR = "||"
EQ = "=="
NEQ = "!="
LT = "<"
LTEQ = "<="
GT = ">"
GTEQ = ">="
ASSIGN = "="
ASSIGN_ADD = "+="
ASSIGN_SUB = "-="
ASSIGN_MUL = "*="
ASSIGN_DIV = "/="
ASSIGN_MOD = "%="
ASSIGN_BIT_AND = "&="
ASSIGN_BIT_OR = "|="
ASSIGN_BIT_LSH = "<<="
ASSIGN_BIT_RSH = ">>="
// literals
IDENTIFIER = <<[_A-Za-z][_A-Za-z0-9]*>>
DEC_NUMBER = <<-?[0-9]+\.[0-9]+>>
HEX_NUMBER = <<-?0x[0-9A-Fa-f]+>>
INT_NUMBER = <<-?[0-9]+>>
STRING = <<"(\\.|[^"])*">>
%productions%
script = statement+;
// statements
statement =
import_statement SEMICOLON |
compound_statement SEMICOLON |
function_definition |
builtin_declaration SEMICOLON |
if_statement |
while_statement |
do_statement SEMICOLON |
for_statement |
return_statement SEMICOLON |
break_statement SEMICOLON |
continue_statement SEMICOLON |
yield_statement SEMICOLON |
exit_statement SEMICOLON |
dump_statement SEMICOLON |
lock_statement SEMICOLON |
unlock_statement SEMICOLON |
statement_block;
compound_statement = compound_statement_item {COMMA compound_statement_item};
compound_statement_item =
const_definition |
variable_definition |
variable_declaration |
variable_assignment |
print_statement |
function_call;
statement_block = L_BRACE {statement} R_BRACE;
import_statement = IMPORT STRING;
yield_statement = YIELD;
exit_statement = EXIT;
dump_statement = DUMP;
lock_statement = LOCK;
unlock_statement = UNLOCK;
print_statement = PRINT argument_list;
name_label = IDENTIFIER COLON;
// operators
assignment_operator = ASSIGN | ASSIGN_ADD | ASSIGN_SUB | ASSIGN_MUL | ASSIGN_DIV | ASSIGN_MOD | ASSIGN_BIT_AND | ASSIGN_BIT_OR | ASSIGN_BIT_LSH | ASSIGN_BIT_RSH;
ternary_operator = expression QMARK expression COLON expression;
binary_operator =
ADD | SUB | MUL | DIV | MOD | // arithmetic
BIT_AND | BIT_OR | BIT_LSH | BIT_RSH | // bitwise
EQ | NEQ | LT | LTEQ | GT | GTEQ | // comparison
LOG_AND | LOG_OR; // logical
unary_operator = LOG_NOT | SUB;
// expressions
expression = operand {binary_operator operand};
operand = [unary_operator_list] term;
term =
int_cast |
float_cast |
typeof_cast |
function_call |
variable_reference |
STRING |
DEC_NUMBER |
HEX_NUMBER |
INT_NUMBER |
TRUE |
FALSE |
L_PAREN expression R_PAREN |
L_BRACKET ternary_operator R_BRACKET; // HACK: the brackets remove ambiguity
unary_operator_list = unary_operator+;
int_cast = INT L_PAREN expression R_PAREN;
float_cast = FLOAT L_PAREN expression R_PAREN;
typeof_cast = TYPEOF L_PAREN expression R_PAREN;
// constants
const_definition = CONST IDENTIFIER ASSIGN expression;
// variables
variable_reference = IDENTIFIER; // used in expressions
variable_declaration = VAR IDENTIFIER;
variable_definition = VAR IDENTIFIER assignment_operator expression;
variable_assignment = IDENTIFIER assignment_operator expression;
// functions
function_definition = FUNCTION IDENTIFIER parameter_list statement_block;
function_call = IDENTIFIER argument_list;
parameter_list = L_PAREN [parameter {COMMA parameter}] R_PAREN; // e.g. (a, b, ...)
parameter = IDENTIFIER | ELLIPSIS;
argument_list = L_PAREN [argument {COMMA argument}] R_PAREN;
argument = expression;
// builtins
builtin_declaration = BUILTIN IDENTIFIER parameter_list;
// flow control
if_statement = [name_label] IF expression statement [ELSE statement];
while_statement = [name_label] WHILE expression statement;
do_statement = [name_label] DO statement WHILE expression;
for_statement = [name_label] FOR L_PAREN [for_declaration] SEMICOLON [for_condition] SEMICOLON [for_iteration] R_PAREN statement;
for_declaration = compound_statement;
for_condition = expression;
for_iteration = compound_statement;
return_statement = RETURN [expression];
break_statement = BREAK [IDENTIFIER];
continue_statement = CONTINUE [IDENTIFIER];

212
symbol table.cs Normal file
View file

@ -0,0 +1,212 @@
using arookas.Collections;
using arookas.IO.Binary;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace arookas
{
class sunSymbolTable : IEnumerable<sunSymbolInfo>
{
List<sunSymbolInfo> symbols = new List<sunSymbolInfo>(10);
public int Count { get { return symbols.Count; } }
public int BuiltinCount { get { return symbols.Count(sym => sym.Type == sunSymbolType.Builtin); } }
public int FunctionCount { get { return symbols.Count(sym => sym.Type == sunSymbolType.Function); } }
public int VariableCount { get { return symbols.Count(sym => sym.Type == sunSymbolType.Variable); } }
public IEnumerable<sunCallableSymbolInfo> Callables { get { return symbols.OfType<sunCallableSymbolInfo>(); } }
public IEnumerable<sunBuiltinInfo> Builtins { get { return symbols.Where(sym => sym.Type == sunSymbolType.Builtin).Cast<sunBuiltinInfo>(); } }
public IEnumerable<sunFunctionInfo> Functions { get { return symbols.Where(sym => sym.Type == sunSymbolType.Function).Cast<sunFunctionInfo>(); } }
public IEnumerable<sunVariableInfo> Variables { get { return symbols.Where(sym => sym.Type == sunSymbolType.Variable).Cast<sunVariableInfo>(); } }
public void Add(sunSymbolInfo symbol) { symbols.Add(symbol); }
public void Clear() { symbols.Clear(); }
public void Write(aBinaryWriter writer)
{
int ofs = 0;
foreach (var sym in this)
{
writer.WriteS32((int)sym.Type);
writer.WriteS32(ofs);
writer.Write32(sym.Data);
// runtime fields
writer.WriteS32(0);
writer.WriteS32(0);
ofs += sym.Name.Length + 1; // include null terminator
}
foreach (var sym in this)
{
writer.WriteString(sym.Name, aBinaryStringFormat.NullTerminated);
}
}
public IEnumerator<sunSymbolInfo> GetEnumerator() { return symbols.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
abstract class sunSymbolInfo
{
public string Name { get; private set; }
// symbol table
public abstract sunSymbolType Type { get; }
public abstract uint Data { get; }
protected sunSymbolInfo(string name)
{
Name = name;
}
}
abstract class sunCallableSymbolInfo : sunSymbolInfo
{
public sunParameterInfo Parameters { get; private set; }
protected List<sunPoint> CallSites { get; private set; }
public bool HasCallSites { get { return CallSites.Count > 0; } }
protected sunCallableSymbolInfo(string name, sunParameterInfo parameterInfo)
: base(name)
{
Parameters = parameterInfo;
CallSites = new List<sunPoint>(10);
}
public abstract void OpenCallSite(sunContext context, int argumentCount);
public abstract void CloseCallSites(sunContext context);
public abstract void Compile(sunContext context);
}
class sunBuiltinInfo : sunCallableSymbolInfo
{
public int Index { get; private set; }
// symbol table
public override sunSymbolType Type { get { return sunSymbolType.Builtin; } }
public override uint Data { get { return (uint)Index; } }
public sunBuiltinInfo(string name, sunParameterInfo parameters, int index)
: base(name, parameters)
{
Index = index;
}
public override void Compile(sunContext context)
{
throw new InvalidOperationException("Cannot compile builtins.");
}
public override void OpenCallSite(sunContext context, int argumentCount)
{
context.Text.CallBuiltin(Index, argumentCount);
}
public override void CloseCallSites(sunContext context)
{
// do nothing
}
}
class sunFunctionInfo : sunCallableSymbolInfo
{
sunNode Body { get; set; }
public uint Offset { get; private set; }
// symbol table
public override sunSymbolType Type { get { return sunSymbolType.Function; } }
public override uint Data { get { return (uint)Offset; } }
public sunFunctionInfo(string name, sunParameterInfo parameters, sunNode body)
: base(name, parameters)
{
Body = body;
}
public override void Compile(sunContext context)
{
Offset = context.Text.Offset;
context.Scopes.Push();
foreach (var parameter in Parameters)
{
context.DeclareParameter(parameter);
}
context.Text.StoreDisplay(1);
Body.Compile(context);
context.Text.ReturnVoid();
context.Scopes.Pop();
}
public override void OpenCallSite(sunContext context, int argumentCount)
{
var point = context.Text.CallFunction(argumentCount);
CallSites.Add(point);
}
public override void CloseCallSites(sunContext context)
{
foreach (var callSite in CallSites)
{
context.Text.ClosePoint(callSite, Offset);
}
}
}
class sunParameterInfo : IEnumerable<string>
{
string[] Parameters { get; set; }
public int Minimum { get { return Parameters.Length; } }
public bool IsVariadic { get; private set; }
public sunParameterInfo(IEnumerable<sunIdentifier> parameters, bool variadic)
{
// validate parameter names
var duplicate = parameters.FirstOrDefault(a => parameters.Count(b => a.Value == b.Value) > 1);
if (duplicate != null)
{
throw new sunRedeclaredParameterException(duplicate);
}
Parameters = parameters.Select(i => i.Value).ToArray();
IsVariadic = variadic;
}
public sunParameterInfo(IEnumerable<string> parameters, bool variadic)
{
// validate parameter names
Parameters = parameters.ToArray();
IsVariadic = variadic;
}
public bool ValidateArgumentCount(int count)
{
return IsVariadic ? count >= Minimum : count == Minimum;
}
public IEnumerator<string> GetEnumerator() { return Parameters.GetArrayEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
class sunVariableInfo : sunSymbolInfo
{
public int Display { get; private set; }
public int Index { get; private set; }
// symbol table
public override sunSymbolType Type { get { return sunSymbolType.Variable; } }
public override uint Data { get { return (uint)Index; } }
public sunVariableInfo(string name, int display, int index)
: base(name)
{
Display = display;
Index = index;
}
}
enum sunSymbolType
{
Builtin,
Function,
Variable,
}
}

188
writer.cs Normal file
View file

@ -0,0 +1,188 @@
using arookas.IO.Binary;
namespace arookas
{
class sunWriter
{
aBinaryWriter writer;
public uint Offset { get { return (uint)writer.Position; } }
public sunWriter(aBinaryWriter writer)
{
this.writer = writer;
}
public sunPoint OpenPoint() { return new sunPoint(Offset); }
public void ClosePoint(sunPoint point)
{
ClosePoint(point, (uint)writer.Position);
}
public void ClosePoint(sunPoint point, uint offset)
{
writer.Keep();
writer.Goto(point.Offset);
writer.Write32(offset);
writer.Back();
}
public void PushInt(int value)
{
switch (value) // shortcut commands
{
case 0: writer.Write8(0x25); return;
case 1: writer.Write8(0x26); return;
}
writer.Write8(0x00);
writer.WriteS32(value);
}
public void PushFloat(float value)
{
writer.Write8(0x01);
writer.WriteF32(value);
}
public void PushData(int dataIndex)
{
writer.Write8(0x02);
writer.WriteS32(dataIndex);
}
public void PushVariable(sunVariableInfo variableInfo)
{
PushVariable(variableInfo.Display, variableInfo.Index);
}
public void PushVariable(int display, int variableIndex)
{
writer.Write8(0x04);
writer.WriteS32(display);
writer.WriteS32(variableIndex);
}
public void IncVariable(sunVariableInfo variableInfo)
{
IncVariable(variableInfo.Display, variableInfo.Index);
}
public void DecVariable(sunVariableInfo variableInfo)
{
DecVariable(variableInfo.Display, variableInfo.Index);
}
public void IncVariable(int display, int variableIndex)
{
writer.Write8(0x06);
writer.WriteS32(display);
writer.WriteS32(variableIndex);
}
public void DecVariable(int display, int variableIndex)
{
writer.Write8(0x07);
writer.WriteS32(display);
writer.WriteS32(variableIndex);
}
public void Add() { writer.Write8(0x08); }
public void Sub() { writer.Write8(0x09); }
public void Mul() { writer.Write8(0x0A); }
public void Div() { writer.Write8(0x0B); }
public void Mod() { writer.Write8(0x0C); }
public void StoreVariable(sunVariableInfo variableInfo)
{
StoreVariable(variableInfo.Display, variableInfo.Index);
}
public void StoreVariable(int display, int variableIndex)
{
writer.Write8(0x0D);
writer.Write8(0x04); // unused (skipped over by TSpcInterp)
writer.WriteS32(display);
writer.WriteS32(variableIndex);
}
public void Eq() { writer.Write8(0x0E); }
public void NtEq() { writer.Write8(0x0F); }
public void Gt() { writer.Write8(0x10); }
public void Lt() { writer.Write8(0x11); }
public void GtEq() { writer.Write8(0x12); }
public void LtEq() { writer.Write8(0x13); }
public void Neg() { writer.Write8(0x14); }
public void LogNOT() { writer.Write8(0x15); }
public void LogAND() { writer.Write8(0x16); }
public void LogOR() { writer.Write8(0x17); }
public void BitAND() { writer.Write8(0x18); }
public void BitOR() { writer.Write8(0x19); }
public void ShL() { writer.Write8(0x1A); }
public void ShR() { writer.Write8(0x1B); }
public sunPoint CallFunction(int argumentCount)
{
writer.Write8(0x1C);
sunPoint point = OpenPoint();
writer.Write32(0); // dummy
writer.WriteS32(argumentCount);
return point;
}
public void CallFunction(sunPoint point, int argumentCount)
{
writer.Write8(0x1C);
writer.Write32(point.Offset);
writer.WriteS32(argumentCount);
}
public void CallBuiltin(int symbolIndex, int argumentCount)
{
writer.Write8(0x1D);
writer.WriteS32(symbolIndex);
writer.WriteS32(argumentCount);
}
public void DeclareLocal(int count)
{
writer.Write8(0x1E);
writer.WriteS32(count);
}
public void StoreDisplay(int display)
{
writer.Write8(0x1F);
writer.WriteS32(display);
}
public void ReturnValue() { writer.Write8(0x20); }
public void ReturnVoid() { writer.Write8(0x21); }
public sunPoint GotoIfZero()
{
writer.Write8(0x22);
sunPoint point = OpenPoint();
writer.Write32(0); // dummy
return point;
}
public sunPoint Goto()
{
writer.Write8(0x23);
sunPoint point = OpenPoint();
writer.Write32(0); // dummy
return point;
}
public void GotoIfZero(sunPoint point)
{
writer.Write8(0x22);
writer.Write32(point.Offset);
}
public void Goto(sunPoint point)
{
writer.Write8(0x23);
writer.Write32(point.Offset);
}
public void Pop() { writer.Write8(0x24); }
public void Terminate() { writer.Write8(0x27); }
}
struct sunPoint
{
readonly uint offset;
public uint Offset { get { return offset; } }
public sunPoint(uint offset)
{
this.offset = offset;
}
}
}