306 lines
14 KiB
C#
306 lines
14 KiB
C#
using System.Globalization;
|
|
using Qrakhen.Qamp.Core.Execution;
|
|
using Qrakhen.Qamp.Core.Tokenization;
|
|
using Qrakhen.Qamp.Core.Values;
|
|
|
|
namespace Qrakhen.Qamp.Core.Compilation;
|
|
|
|
public static class ExpressionParser
|
|
{
|
|
private static readonly Dictionary<TokenType, Rule> _rules = new();
|
|
|
|
public static Rule Get(TokenType type) => _rules[type];
|
|
|
|
public static void And(Digester digester, bool canAssign)
|
|
{
|
|
long endJump = digester.EmitJump(OpCode.JumpIfFalse);
|
|
|
|
digester.Emit(OpCode.Pop);
|
|
digester.WeightedDigest(Weight.And);
|
|
|
|
digester.PatchJump(endJump);
|
|
}
|
|
|
|
static void Binary(Digester digester, bool canAssign)
|
|
{
|
|
TokenType operatorType = digester.Previous.Type;
|
|
Rule rule = Get(operatorType);
|
|
digester.WeightedDigest(rule.Weight + 1);
|
|
|
|
switch (operatorType) {
|
|
case TokenType.BangEqual: digester.Emit(OpCode.Equal, OpCode.Not); break;
|
|
case TokenType.EqualEqual: digester.Emit(OpCode.Equal); break;
|
|
case TokenType.Greater: digester.Emit(OpCode.Greater); break;
|
|
case TokenType.GreaterEqual: digester.Emit(OpCode.Less, OpCode.Not); break;
|
|
case TokenType.Less: digester.Emit(OpCode.Less); break;
|
|
case TokenType.LessEqual: digester.Emit(OpCode.Greater, OpCode.Not); break;
|
|
case TokenType.Plus: digester.Emit(OpCode.Add); break;
|
|
case TokenType.Minus: digester.Emit(OpCode.Subtract); break;
|
|
case TokenType.Star: digester.Emit(OpCode.Multiply); break;
|
|
case TokenType.Slash: digester.Emit(OpCode.Divide); break;
|
|
case TokenType.BitwiseAnd: digester.Emit(OpCode.BitwiseAnd); break;
|
|
case TokenType.BitwiseOr: digester.Emit(OpCode.BitwiseOr); break;
|
|
case TokenType.BitwiseXor: digester.Emit(OpCode.BitwiseXor); break;
|
|
case TokenType.BitwiseNot: digester.Emit(OpCode.BitwiseNot); break;
|
|
case TokenType.BitwiseLeft: digester.Emit(OpCode.BitwiseLeft); break;
|
|
case TokenType.BitwiseRight: digester.Emit(OpCode.BitwiseRight); break;
|
|
case TokenType.Increment: digester.Emit(OpCode.Increment); break;
|
|
case TokenType.Decrement: digester.Emit(OpCode.Decrement); break;
|
|
default: return;
|
|
}
|
|
}
|
|
|
|
static void Call(Digester digester, bool canAssign)
|
|
{
|
|
byte count = digester.Arguments();
|
|
digester.Emit(OpCode.Invoke, count);
|
|
}
|
|
|
|
static void Dot(Digester digester, bool canAssign)
|
|
{
|
|
digester.Consume(TokenType.Identifier, "Expected property name after '.'.");
|
|
long name = digester.IdentifierConstant(digester.Previous.Value);
|
|
|
|
if (canAssign && digester.Match(TokenType.Equal)) {
|
|
digester.ParseExpression();
|
|
digester.EmitDynamic(OpCode.SetMember, name);
|
|
} else if (digester.Match(TokenType.GroupOpen)) {
|
|
byte argCount = digester.Arguments();
|
|
digester.EmitDynamic(OpCode.InvokeMember, name);
|
|
digester.Emit(argCount);
|
|
} else {
|
|
digester.EmitDynamic(OpCode.GetMember, name);
|
|
}
|
|
}
|
|
|
|
static void Literal(Digester digester, bool canAssign)
|
|
{
|
|
switch (digester.Previous.Type) {
|
|
case TokenType.False:
|
|
digester.Emit(OpCode.False);
|
|
break;
|
|
case TokenType.Null:
|
|
digester.Emit(OpCode.Null);
|
|
break;
|
|
case TokenType.True:
|
|
digester.Emit(OpCode.True);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void Group(Digester digester, bool canAssign)
|
|
{
|
|
digester.ParseExpression();
|
|
digester.Consume(TokenType.GroupClose, "Expected ')' after expression.");
|
|
}
|
|
|
|
static void Array(Digester digester, bool canAssign)
|
|
{
|
|
int length = 0;
|
|
while (!digester.Check(TokenType.ArrayClose)) {
|
|
digester.ParseExpression();
|
|
length++;
|
|
if (digester.Check(TokenType.Comma))
|
|
digester.Next();
|
|
else
|
|
break;
|
|
}
|
|
digester.Consume(TokenType.ArrayClose, "Expected ']' after array definition");
|
|
digester.EmitDynamic(OpCode.Array, digester.MakeConstant(new Value((long)length)));
|
|
}
|
|
|
|
static void ArrayAdd(Digester digester, bool canAssign)
|
|
{
|
|
digester.ParseExpression();
|
|
digester.Emit(OpCode.ArrayAdd);
|
|
}
|
|
|
|
static void Index(Digester digester, bool canAssign)
|
|
{
|
|
digester.ParseExpression();
|
|
digester.Consume(TokenType.ArrayClose, "expected ] after array accessor");
|
|
if (canAssign && digester.Match(TokenType.ArrayAdd)) {
|
|
digester.ParseExpression();
|
|
digester.Emit(OpCode.ArrayAdd);
|
|
} else if (canAssign && digester.Match(TokenType.Equal)) {
|
|
digester.ParseExpression();
|
|
digester.Emit(OpCode.ArraySet);
|
|
} else {
|
|
digester.Emit(OpCode.ArrayGet);
|
|
}
|
|
}
|
|
|
|
static void Number(Digester digester, bool canAssign)
|
|
{
|
|
Token token = digester.Previous;
|
|
string number = token.Value ?? "";
|
|
TokenType type = token.Type;
|
|
Value value = default;
|
|
if (type == TokenType.Hexadecimal)
|
|
value = new Value(long.Parse(number, NumberStyles.HexNumber));
|
|
else if (type == TokenType.Decimal)
|
|
value = new Value(double.Parse(number, NumberStyles.Float, CultureInfo.InvariantCulture));
|
|
else if (type == TokenType.Integer)
|
|
value = new Value(long.Parse(number, NumberStyles.Integer));
|
|
else
|
|
digester.ErrorAt(token, $"Could not parse number {number}");
|
|
digester.EmitConstant(value);
|
|
}
|
|
|
|
static void Or(Digester digester, bool canAssign)
|
|
{
|
|
long elseJump = digester.EmitJump(OpCode.JumpIfFalse);
|
|
long endJump = digester.EmitJump(OpCode.Jump);
|
|
|
|
digester.PatchJump(elseJump);
|
|
digester.Emit(OpCode.Pop);
|
|
|
|
digester.WeightedDigest(Weight.Or);
|
|
digester.PatchJump(endJump);
|
|
}
|
|
|
|
static void String(Digester digester, bool canAssign)
|
|
{
|
|
digester.EmitConstant(Values.Objects.String.Make(digester.Previous.Value));
|
|
}
|
|
|
|
public static void Variable(Digester digester, string name, bool canAssign)
|
|
{
|
|
OpCode Get, Set;
|
|
long variable = digester.ResolveLocal(digester.Builder, name);
|
|
if (variable > -1) {
|
|
Get = OpCode.GetLocal;
|
|
Set = OpCode.SetLocal;
|
|
} else if ((variable = digester.ResolveOuter(digester.Builder, name)) > -1) {
|
|
Get = OpCode.GetOuter;
|
|
Set = OpCode.SetOuter;
|
|
} else {
|
|
variable = digester.IdentifierConstant(name);
|
|
Get = OpCode.GetGlobal;
|
|
Set = OpCode.SetGlobal;
|
|
}
|
|
|
|
if (canAssign && digester.Match(TokenType.Equal)) {
|
|
digester.ParseExpression();
|
|
digester.Emit(Set);
|
|
digester.EmitDynamic(variable);
|
|
} else {
|
|
digester.Emit(Get);
|
|
digester.EmitDynamic(variable);
|
|
}
|
|
}
|
|
|
|
public static void Variable(Digester digester, bool canAssign)
|
|
{
|
|
Variable(digester, digester.Previous.Value!, canAssign);
|
|
}
|
|
|
|
static void Base(Digester digester, bool canAssign)
|
|
{
|
|
if (digester.ClassBuilder == null)
|
|
digester.ErrorAtPrevious("We're currently not in a class. 'base' only works inside of a class context.");
|
|
else if (digester.ClassBuilder.Outer == null)
|
|
digester.ErrorAtPrevious($"{digester.ClassBuilder.Name} is not inherited from any class, so the 'base' keyword points to nothing.");
|
|
else
|
|
{
|
|
digester.Consume(TokenType.Dot, "Expected '.' after 'base' keyword.");
|
|
digester.Consume(TokenType.Identifier, "Expected an identifier after 'base' keyword.");
|
|
long name = digester.IdentifierConstant(digester.Previous.Value);
|
|
Variable(digester, "this", false);
|
|
if (digester.Match(TokenType.GroupOpen))
|
|
{
|
|
byte arguments = digester.Arguments();
|
|
Variable(digester, "base", false);
|
|
digester.Emit(OpCode.InvokeBase);
|
|
digester.Emit(arguments);
|
|
} else {
|
|
Variable(digester, "base", false);
|
|
digester.Emit(OpCode.Base);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void This(Digester digester, bool canAssign)
|
|
{
|
|
if (digester.ClassBuilder == null)
|
|
digester.ErrorAtPrevious("We're currently not in a class. 'this' only refers to the current instance of one.");
|
|
else
|
|
Variable(digester, false);
|
|
}
|
|
|
|
static void Modifier(Digester digester, bool canAssign)
|
|
{
|
|
Token token = digester.Previous;
|
|
digester.WeightedDigest(Weight.Modifier);
|
|
switch (token.Type) {
|
|
case TokenType.Bang: digester.Emit(OpCode.Not); break;
|
|
case TokenType.Minus: digester.Emit(OpCode.Negate); break;
|
|
case TokenType.BitwiseNot: digester.Emit(OpCode.BitwiseNot); break;
|
|
}
|
|
}
|
|
|
|
static ExpressionParser()
|
|
{
|
|
_rules[TokenType.GroupOpen] = new Rule(Group, Call, Weight.Call);
|
|
_rules[TokenType.GroupClose] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.ContextOpen] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.ContextClose] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.ArrayOpen] = new Rule(Array, Index, Weight.Call);
|
|
_rules[TokenType.ArrayClose] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.ArrayAdd] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.ArrayRemove] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.Colon] = new Rule(null, Dot, Weight.Call);
|
|
_rules[TokenType.Comma] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.Dot] = new Rule(null, Dot, Weight.Call);
|
|
_rules[TokenType.Minus] = new Rule(Modifier, Binary, Weight.Term);
|
|
_rules[TokenType.Plus] = new Rule(null, Binary, Weight.Term);
|
|
_rules[TokenType.Increment] = new Rule(Binary, Binary, Weight.Call);
|
|
_rules[TokenType.Decrement] = new Rule(Binary, Binary, Weight.Call);
|
|
_rules[TokenType.BitwiseAnd] = new Rule(Modifier, Binary, Weight.Term);
|
|
_rules[TokenType.BitwiseOr] = new Rule(null, Binary, Weight.Term);
|
|
_rules[TokenType.BitwiseXor] = new Rule(null, Binary, Weight.Term);
|
|
_rules[TokenType.BitwiseLeft] = new Rule(null, Binary, Weight.Term);
|
|
_rules[TokenType.BitwiseRight] = new Rule(null, Binary, Weight.Term);
|
|
_rules[TokenType.BitwiseNot] = new Rule(Modifier, null, Weight.Term);
|
|
_rules[TokenType.Semicolon] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.Slash] = new Rule(null, Binary, Weight.Factor);
|
|
_rules[TokenType.Star] = new Rule(null, Binary, Weight.Factor);
|
|
_rules[TokenType.Bang] = new Rule(Modifier, null, Weight.None);
|
|
_rules[TokenType.BangEqual] = new Rule(null, Binary, Weight.Equal);
|
|
_rules[TokenType.Equal] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.EqualEqual] = new Rule(null, Binary, Weight.Equal);
|
|
_rules[TokenType.Greater] = new Rule(null, Binary, Weight.Compare);
|
|
_rules[TokenType.GreaterEqual] = new Rule(null, Binary, Weight.Compare);
|
|
_rules[TokenType.Less] = new Rule(null, Binary, Weight.Compare);
|
|
_rules[TokenType.LessEqual] = new Rule(null, Binary, Weight.Compare);
|
|
_rules[TokenType.Identifier] = new Rule(Variable, null, Weight.None);
|
|
_rules[TokenType.String] = new Rule(String, null, Weight.None);
|
|
_rules[TokenType.Integer] = new Rule(Number, null, Weight.None);
|
|
_rules[TokenType.Hexadecimal] = new Rule(Number, null, Weight.None);
|
|
_rules[TokenType.Decimal] = new Rule(Number, null, Weight.None);
|
|
_rules[TokenType.And] = new Rule(null, And, Weight.And);
|
|
_rules[TokenType.Class] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.Else] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.False] = new Rule(Literal, null, Weight.None);
|
|
_rules[TokenType.For] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.Function] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.If] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.Null] = new Rule(Literal, null, Weight.None);
|
|
_rules[TokenType.Or] = new Rule(null, Or, Weight.Or);
|
|
_rules[TokenType.Print] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.TypeOf] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.Export] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.Import] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.Return] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.Base] = new Rule(Base, null, Weight.None);
|
|
_rules[TokenType.This] = new Rule(This, null, Weight.None);
|
|
_rules[TokenType.True] = new Rule(Literal, null, Weight.None);
|
|
_rules[TokenType.Var] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.While] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.Error] = new Rule(null, null, Weight.None);
|
|
_rules[TokenType.Eof] = new Rule(null, null, Weight.None);
|
|
}
|
|
}
|