qamp/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs

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