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 _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, length); // 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 void TypeOf(Digester digester, bool canAssign) { digester.TypeOf(); } 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(TypeOf, 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); } }