private static void ParsePostfixExpr(ParseState state) { // What we do is optional qualification of names: // PostfixExpr ::= PrimaryExpr // | PrimaryExpr "(" ArgumentList ")" // // ArgumentList ::= <empty> | Expr { "," Expr } // // How a full C-like grammar looks like: // PostfixExpr ::= PrimaryExpr // | PostfixExpr "." Name // | PostfixExpr "[" Expr "]" // | PostfixExpr "(" ArgumentList ")" // // And how that would be parsed: // ParsePrimaryExpr(state); // while (state.IsOp(".", "[", "(")) { // var op = (string) state.Value; // state.Advance(); // switch (op) { etc... } ParsePrimaryExpr(state); if (state.IsOp("(")) { // TODO Optimization: if invocation target is syntactically known: resolve now (and check param types) state.Advance(); int n = ParseArgumentList(state); state.Advance(")"); state.Target.EmitCall(n); } }
private static void ParseUnaryExpr(ParseState state) { if (state.IsOp("-")) { state.Advance(); ParseUnaryExpr(state); state.Target.EmitCode(OpCode.Neg); } else if (state.IsOp("+")) { state.Advance(); ParseUnaryExpr(state); state.Target.EmitCode(OpCode.Pos); } else if (state.IsOp("not")) { state.Advance(); ParseUnaryExpr(state); state.Target.EmitCode(OpCode.Not); } else { ParsePostfixExpr(state); } }
private static void ParsePredicateExpr(ParseState state) { ParseAdditiveExpr(state); if (state.IsOp("is")) { state.Advance(); ParseTypeCheck(state); } else if (state.IsOp("not")) { state.Advance(); if (state.IsOp("in")) { state.Advance(); ParseMemberCheck(state, true); } else { object o = state.IsSymbol ? state.Value : state.Token; throw SyntaxError( state.Position, "Expected 'not in', but got 'not {0}'", o); } } else if (state.IsOp("in")) { state.Advance(); ParseMemberCheck(state, false); } else if (state.IsOp(_relops)) { ParseRelationalExpr(state); } }
private static void ParseMemberCheck(ParseState state, bool negate) { // Syntax: ... [not] in (AdditiveExpr {, AdditiveExpr}) // The 'in' and optional 'not' is already consumed // Notice that A in (B, C) is just a convenient abbreviation // for A = B or A = C, so we can generate code like this: // B C // A A T/F A A T/F // A A A A A A A A A F T // A . Dup . B . Ceq . Jit@1 . Dup . C . Ceq . Jit@1 . Pop . False . Jmp@end . @1:Pop . True . // --------------------- --------------------- // and when negated ("not in") we emit the same code as above // but with the opcodes True and False exchanged. state.Advance("("); if (state.IsOp(")")) { // Special case: "foo in ()" is always false state.Advance(); state.Target.EmitCode(OpCode.Pop); state.Target.EmitValue(false); } else { var mid = state.Target.ObtainLabel(); var end = state.Target.ObtainLabel(); state.Target.EmitCode(OpCode.Dup); ParseAdditiveExpr(state); state.Target.EmitCode(OpCode.Ceq); state.Target.EmitJump(OpCode.Jit, mid); while (state.IsOp(",")) { state.Advance(); state.Target.EmitCode(OpCode.Dup); ParseAdditiveExpr(state); state.Target.EmitCode(OpCode.Ceq); state.Target.EmitJump(OpCode.Jit, mid); } state.Advance(")"); state.Target.EmitCode(OpCode.Pop); state.Target.EmitValue(negate); state.Target.EmitJump(OpCode.Jmp, end); state.Target.DefineLabel(mid); state.Target.EmitCode(OpCode.Pop); state.Target.EmitValue(!negate); state.Target.DefineLabel(end); } }
private static Program Parse(string clause) { var program = new Program(); var state = new ParseState(clause); state.Advance(); if (state.IsEnd) { // Special case: empty clause is always true: program.EmitValue(true); return(program); } ParseClause(state, program); if (!state.IsEnd) { throw SyntaxError(state.Index, "Expected end of input"); } program.SqueezeNops(); return(program); }
private static void ParseAdditiveExpr(ParseState state) { ParseMultiplicativeExpr(state); while (state.IsOp("+", "-")) { var op = (string)state.Value; state.Advance(); ParseMultiplicativeExpr(state); switch (op) { case "+": state.Target.EmitCode(OpCode.Add); break; case "-": state.Target.EmitCode(OpCode.Sub); break; default: throw ParserBug("Unexpected operator: {0}", op); } } }
private static void ParseMultiplicativeExpr(ParseState state) { ParseUnaryExpr(state); while (state.IsOp("*", "/", "%")) { var op = (string)state.Value; state.Advance(); ParseUnaryExpr(state); switch (op) { case "*": state.Target.EmitCode(OpCode.Mul); break; case "/": state.Target.EmitCode(OpCode.Div); break; case "%": state.Target.EmitCode(OpCode.Rem); break; default: throw ParserBug("Unexpected operator: {0}", op); } } }
private static void ParseRelationalExpr(ParseState state) { // We support chained comparison, eg, A < B = C <= D < E // (like Python, but different from C, JavaScript, et al.) // // Notice that A op B op C says nothing about how A relates to C. // // The code generated evaluates each operand at most once. // For example, in "1 < 1+1 < 1-1 < 2*3", the sum is evaluated // only once and the multiplication is not evaluated at all. // // Expression: A op B op C etc. // Stack: A A B B B B C C C // B A F/T C B F/T // B C // Code: A ; B ; Dup1 ; Cop ; Jif@1 ; C ; Dup1 ; Cop ; Jif@1 ; Pop ; True ; Jmp@end ; @1: Pop ; False // ------------------ ------------------ // // Notice that emitting Jop' in place of Cop+Jif where op' is the negation of op // (that is, ne for eq, ge for lt, etc.) is a valid optimization for 2VL, but // it is not correct for 3VL because the "excluded third" doesn't hold. // // The binary case "A op B " is simplified to: A ; B ; Cop (end) // // The initial AdditiveExpr has already been consumed, and // the current token is known to be a relop (see calling code). int operatorCount = 0; var falsum = state.Target.ObtainLabel(); var end = state.Target.ObtainLabel(); while (state.IsOp(_relops)) { operatorCount += 1; var op = (string)state.Value; state.Advance(); ParseAdditiveExpr(state); if (operatorCount == 1 && !state.IsOp(_relops)) { // Code for the (frequent) binary case: state.Target.EmitCode(GetComp(op)); return; } // Code for the (general) chained case: state.Target.EmitCode(OpCode.Dup1); state.Target.EmitCode(GetComp(op)); state.Target.EmitJump(OpCode.Jif, falsum); } state.Target.EmitCode(OpCode.Pop); state.Target.EmitValue(true); state.Target.EmitJump(OpCode.Jmp, end); state.Target.DefineLabel(falsum); state.Target.EmitCode(OpCode.Pop); state.Target.EmitValue(false); state.Target.DefineLabel(end); }
private static void Consume(string expected, ref ParseState <char> state) { var oldCol = state.ComputeSourcePos().Col; AssertEqual(expected.AsSpan(), state.LookAhead(expected.Length)); state.Advance(expected.Length); Assert.Equal(oldCol + expected.Length, state.ComputeSourcePos().Col); }
private static void ParsePrimary(ParseState state, Program program) { if (state.CurrentToken == TokenType.LParen) { state.Advance(); ParseClause(state, program); state.ExpectToken(TokenType.RParen); state.Advance(); } else { ParseExpression(state, program); if (state.IsCompOp) // expr op expr { var inst = GetInstruction(state.CurrentToken); state.Advance(); ParseExpression(state, program); program.Emit(inst); } else if (state.IsSymbol("NOT")) // expr NOT IN (expr, expr, etc) { state.Advance(); state.ExpectSymbol("IN"); ParseInValueList(state, program, true); } else if (state.IsSymbol("IN")) // expr IN (expr, expr, etc) { ParseInValueList(state, program, false); } else if (state.IsSymbol("IS")) // expr IS [NOT] NULL { state.Advance(); bool negate = false; if (state.IsSymbol("NOT")) { negate = true; state.Advance(); } state.ExpectSymbol("NULL"); state.Advance(); program.Emit(negate ? Instruction.NotNull : Instruction.IsNull); } else { throw SyntaxError(state.Index, "Expected expr, IN, or IS; got {0}", state.CurrentTokenString); } } }
private static void Consume(char expected, ref ParseState <char> state) { var oldCol = state.ComputeSourcePos().Col; Assert.True(state.HasCurrent); Assert.Equal(expected, state.Current); state.Advance(); Assert.Equal(oldCol + 1, state.ComputeSourcePos().Col); }
private static void ParseConditionalExpr(ParseState state) { ParseNullCoalesceExpr(state); if (state.IsOp("?")) { state.Advance(); // With 2VL, the code for P ? A : B is as simple as: // // [P] Jif@1 [A] Jmp@end [B] // // With 3VL we want null ? A : B to evaluate to null. // There are now three cases, and the code becomes: // // [P] Dup Jit@1 Jif@2 Null Jmp@end @1: Pop [A] Jmp@end @2: [B] var consequent = state.Target.ObtainLabel(); var alternative = state.Target.ObtainLabel(); var end = state.Target.ObtainLabel(); state.Target.EmitCode(OpCode.Dup); state.Target.EmitJump(OpCode.Jit, consequent); state.Target.EmitJump(OpCode.Jif, alternative); state.Target.EmitCode(OpCode.Null); state.Target.EmitJump(OpCode.Jmp, end); state.Target.DefineLabel(consequent); state.Target.EmitCode(OpCode.Pop); ParseConditionalExpr(state); // consequent state.Advance(":"); state.Target.EmitJump(OpCode.Jmp, end); state.Target.DefineLabel(alternative); ParseConditionalExpr(state); // alternative state.Target.DefineLabel(end); } }
private static void ParseTypeCheck(ParseState state) { // Syntax: ... is [not] type (the 'is' is already consumed) bool negate = false; if (state.IsOp("not")) { negate = true; state.Advance(); } if (state.IsOp("null")) { state.Advance(); state.Target.EmitCode(OpCode.Cin); if (negate) { state.Target.EmitCode(OpCode.Not); } } else if (state.IsName) { // TODO parse Name {'.' Name}, useful for: ... is ESRI.ArcGIS.Display.IColor var type = (string)state.Value; state.Advance(); state.Target.EmitValue(type); state.Target.EmitCode(OpCode.Is); if (negate) { state.Target.EmitCode(OpCode.Not); } } else { object o = state.IsSymbol ? state.Value : state.Token; throw SyntaxError( state.Position, "Expected 'is null' or 'is Type', but got 'is {0}'", o); } }
private static void ParseExpression(ParseState state, Program program) { // TODO Allow richer expressions: arithmetic switch (state.CurrentToken) { case TokenType.Symbol: if (state.IsSymbol("FALSE")) { program.EmitValue(false); } else if (state.IsSymbol("TRUE")) { program.EmitValue(true); } else if (state.IsSymbol("NULL")) { program.EmitValue(null); } else // column reference to be looked up { program.EmitValue(state.TokenValue); program.Emit(Instruction.Get); } state.Advance(); break; case TokenType.String: case TokenType.Number: program.EmitValue(state.TokenValue); state.Advance(); break; default: throw SyntaxError(state.Index, "Expected Symbol, String, or Number; got {0}", state.CurrentTokenString); } }
private static void ParseLogicalAndExpr(ParseState state) { ParsePredicateExpr(state); while (state.IsOp("and")) { state.Advance(); ParsePredicateExpr(state); state.Target.EmitCode(OpCode.And); } }
//private static void ParseLogicalOrExpr(ParseState state) //{ // // LogicalOrExpr ::= LogicalAndExpr { "||" LogicalAndExpr } // // // // Semantics: A || B = if A then A else B (like JavaScript) // // // // For the code generated: see ParseLogicalAndExpr and s/JIF/JIT/g // ParseLogicalAndExpr(state); // if (state.IsOp("||")) // Could drop this if; unused labels are harmless // { // var end = state.Target.ObtainLabel(); // while (state.IsOp("||")) // { // state.Advance(); // state.Target.EmitCode(OpCode.Dup); // state.Target.EmitJump(OpCode.Jit, end); // state.Target.EmitCode(OpCode.Pop); // ParseLogicalAndExpr(state); // } // state.Target.DefineLabel(end); // } //} //private static void ParseLogicalAndExpr(ParseState state) //{ // // LogicalAndExpr ::= RelationalExpr { "&&" RelationalExpr } // // // // Semantics: A && B = if A then B else A (like JavaScript) // // // // Expression: A && B // // Stack: A // A A // A // _ // B // * // // Code: A // DUP // JIF @1 // POP // B // @1: (end) // // -------------------- // // // // Expression: A && B && C // // Stack: A // A A // A // _ // B // B B // B // _ // C // * // // Code: A // DUP // JIF @1 // POP // B // DUP // JIF @1 // POP // C // @1: (end) // // -------------------- -------------------- // ParseRelationalExpr(state); // if (state.IsOp("&&")) // Could drop this if; unused labels are harmless // { // var end = state.Target.ObtainLabel(); // while (state.IsOp("&&")) // { // state.Advance(); // state.Target.EmitCode(OpCode.Dup); // state.Target.EmitJump(OpCode.Jif, end); // state.Target.EmitCode(OpCode.Pop); // ParseRelationalExpr(state); // } // state.Target.DefineLabel(end); // } //} #endregion private static void ParseLogicalOrExpr(ParseState state) { ParseLogicalAndExpr(state); while (state.IsOp("or")) { state.Advance(); ParseLogicalAndExpr(state); state.Target.EmitCode(OpCode.Or); } }
private static void ParseInValueList(ParseState state, Program program, bool negate) { // Notice: // "x IN (a, b, c)" is equivalent to "x = a OR x = b OR x = c"; // we compile this to: [expr Mark Exch {Dup expr IsEq Exch} Pop Or] // "x NOT IN (a,b,c)" is equivalent to "x <> a AND x <> b AND x <> c"; // we compile this to: [expr Mark Exch {Dup expr NotEq Exch} Pop And] state.Advance(); // eat the "IN" token state.ExpectToken(TokenType.LParen); state.Advance(); program.Emit(Instruction.Mark); program.Emit(Instruction.Exch); program.Emit(Instruction.Dup); ParseExpression(state, program); program.Emit(negate ? Instruction.NotEq : Instruction.IsEq); program.Emit(Instruction.Exch); while (state.IsToken(TokenType.Comma)) { state.Advance(); program.Emit(Instruction.Dup); ParseExpression(state, program); program.Emit(negate ? Instruction.NotEq : Instruction.IsEq); program.Emit(Instruction.Exch); } state.ExpectToken(TokenType.RParen); state.Advance(); program.Emit(Instruction.Pop); program.Emit(negate ? Instruction.And : Instruction.Or); }
private static EvaluatorEngine Compile(string text, int index, out int length, StringComparison comparison) { int anchor = index; var state = new ParseState(text, index, new EvaluatorEngine(), comparison); state.Advance(); ParseExpression(state); state.Target.EmitCode(OpCode.End); state.Target.Commit(); length = state.Position - anchor; return(state.Target); }
private static void ParseFactor(ParseState state, Program program) { bool negate = false; if (state.IsSymbol("NOT")) { negate = true; state.Advance(); } ParsePrimary(state, program); if (negate) { program.Emit(Instruction.Neg); } }
private static int ParseArgumentList(ParseState state) { if (state.IsOp(")")) { return(0); // the empty list } int count = 1; ParseExpression(state); while (state.IsOp(",")) { state.Advance(); count += 1; ParseExpression(state); } return(count); // #args in list }
private static void ParseTerm(ParseState state, Program program) { bool singleton = true; int pc = program.EmitNop(); ParseFactor(state, program); while (state.IsSymbol("AND")) { singleton = false; state.Advance(); ParseFactor(state, program); } if (!singleton) { program.Emit(pc, Instruction.Mark); program.Emit(Instruction.And); } }
private static void ParseNullCoalesceExpr(ParseState state) { ParseLogicalOrExpr(state); if (state.IsOp("??")) { var end = state.Target.ObtainLabel(); var alt = state.Target.ObtainLabel(); state.Advance(); state.Target.EmitCode(OpCode.Dup); state.Target.EmitJump(OpCode.Jin, alt); state.Target.EmitJump(OpCode.Jmp, end); state.Target.DefineLabel(alt); state.Target.EmitCode(OpCode.Pop); ParseUnaryExpr(state); state.Target.DefineLabel(end); } }
public override IMatched <Unit> ParseStatement(ParseState state, Token[] tokens) { var mutable = tokens[1].Text == "var"; var fieldName = tokens[3].Text; var assignment = fieldName.IsNotEmpty(); state.Colorize(tokens, Color.Keyword, Color.Whitespace, Color.Identifier, Color.Whitespace, Color.Structure, Color.Whitespace, Color.Keyword, Color.Whitespace); if (getExpression(state, ExpressionFlags.Standard).ValueOrCast <Unit>(out var expression, out var asUnit)) { var matchField = newLabel("match"); state.AddStatement(new AssignToNewField(false, matchField, expression)); state.Advance(); var caseParser = new CaseParser(fieldName, mutable, assignment, matchField, true, CaseType.Statement); if (caseParser.Scan(state).ValueOrOriginal(out _, out asUnit)) { var ifStatement = caseParser.If; addMatchElse(ifStatement); state.AddStatement(ifStatement); state.Regress(); return(Unit.Matched()); } else { state.Regress(); return(asUnit); } } else { return(asUnit); } }
public override IMatched <Unit> ParseStatement(ParseState state, Token[] tokens) { var className = tokens[3].Text; Module.Global.ForwardReference(className); state.Colorize(tokens, Color.Keyword, Color.Whitespace, Color.Class); state.SkipEndOfLine(); state.Advance(); var mixins = new List <Mixin>(); while (state.More) { var mixinIncludesParser = new MixinIncludesParser(mixins); if (mixinIncludesParser.Scan(state).If(out _, out var anyException)) { } else if (anyException.If(out var exception)) { state.Regress(); return(failedMatch <Unit>(exception)); } else { break; } } state.SkipEndOfLine(); state.Regress(); if (getBlock(state).ValueOrCast <Unit>(out var block, out var asUnit)) { var builder = new ClassBuilder(className, Parameters.Empty, "", new Expression[0], false, block, mixins); if (builder.Register().ValueOrOriginal(out _, out asUnit)) { var cls = new Class(builder); state.AddStatement(cls); var classItemsParser = new ClassItemsParser(builder); while (state.More) { if (classItemsParser.Scan(state).If(out _, out var anyException)) { } else if (anyException.If(out var exception)) { return(failedMatch <Unit>(exception)); } else { break; } } Module.Global.RegisterMixin(new Mixin(className)); return(Unit.Matched()); } else { return(asUnit); } } else { return(asUnit); } }
private static void ParsePrimaryExpr(ParseState state) { // PrimaryExpr ::= Number | String | Name | Name "." Name | "(" Expr ")" if (state.IsNumber) { var value = (double)state.Value; state.Advance(); state.Target.EmitValue(value); } else if (state.IsString) { var value = (string)state.Value; state.Advance(); state.Target.EmitValue(value); } else if (state.IsName) { var name = (string)state.Value; state.Advance(); if (string.Equals(name, "null", state.Comparison)) { state.Target.EmitValue(null); } else if (string.Equals(name, "false", state.Comparison)) { state.Target.EmitValue(false); } else if (string.Equals(name, "true", state.Comparison)) { state.Target.EmitValue(true); } else { if (state.IsOp(".")) { string qualifier = name; state.Advance(); if (!state.IsName) { var o = state.IsSymbol ? state.Value : state.Token; throw SyntaxError( state.Position, "Expected '{0}.Name', but got '{0}.{1}'", qualifier, o); } name = (string)state.Value; state.Advance(); state.Target.EmitValue(qualifier); state.Target.EmitValue(name); state.Target.EmitCode(OpCode.GetQualified); // Stack: ... qual name => value } else { state.Target.EmitValue(name); state.Target.EmitCode(OpCode.Get); // Stack: ... name => value } } } else if (state.IsOp("(")) { state.Advance(); ParseExpression(state); state.Advance(")"); } else { throw SyntaxError( state.Position, "Expected a number, a string, a name, or '(', but got '{0}'", state.Value); } }
public override IMatched <Unit> ParseStatement(ParseState state, Token[] tokens) { var className = tokens[3].Text; var hasParameters = tokens[4].Text == "("; state.Colorize(tokens, Color.Keyword, Color.Whitespace, Color.Class, Color.OpenParenthesis); Parameters parameters; if (hasParameters) { if (getParameters(state).ValueOrCast <Unit>(out parameters, out var asUnit)) { } else if (asUnit.IsNotMatched) { parameters = new Parameters(0); } else { return(asUnit); } } else { parameters = Parameters.Empty; } state.SkipEndOfLine(); state.Advance(); var parentClassParser = new ParentClassParser(); var parentClassName = ""; var initialize = false; var arguments = new Expression[0]; if (parentClassParser.Scan(state).If(out _, out var anyException)) { (parentClassName, initialize, arguments) = parentClassParser.Parent; } else if (anyException.If(out var exception)) { state.Regress(); return(failedMatch <Unit>(exception)); } var mixins = new List <Mixin>(); while (state.More) { var mixinIncludesParser = new MixinIncludesParser(mixins); if (mixinIncludesParser.Scan(state).If(out _, out anyException)) { } else if (anyException.If(out var exception)) { state.Regress(); return(failedMatch <Unit>(exception)); } else { break; } } state.SkipEndOfLine(); state.Regress(); Module.Global.ForwardReference(className); state.SkipEndOfLine(); if (getBlock(state).ValueOrCast <Unit>(out var block, out var asUnit2)) { var builder = new ClassBuilder(className, parameters, parentClassName, arguments, initialize, block, mixins); if (builder.Register().ValueOrOriginal(out _, out asUnit2)) { var cls = new Class(builder); state.AddStatement(cls); var classItemsParser = new ClassItemsParser(builder); while (state.More) { if (classItemsParser.Scan(state).If(out _, out anyException)) { } else if (anyException.If(out var exception)) { return(failedMatch <Unit>(exception)); } else { break; } } return(Unit.Matched()); } else { return(asUnit2); } } else { return(asUnit2); } }