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