public static PropExpression Parse(string source)
        {
            var lexer = new PropLogicLexer(source);

            lexer.NextToken();
            PropExpression result = MatchExpression(lexer);

            ExpectToken(lexer, LexerTokenType.End);
            return(result);
        }
 private static PropExpression MatchUnary(PropLogicLexer lexer)
 {
     if (lexer.CurrentTokenType == LexerTokenType.Not)
     {
         lexer.NextToken();
         PropExpression result = MatchAtom(lexer);
         return(new PropNegation(result));
     }
     return(MatchAtom(lexer));
 }
        private static PropExpression MatchAndTerm(PropLogicLexer lexer)
        {
            PropExpression result = MatchUnary(lexer);

            while (lexer.CurrentTokenType == LexerTokenType.And)
            {
                lexer.NextToken();
                PropExpression right = MatchUnary(lexer);
                result = new PropConjunction(result, right);
            }
            return(result);
        }
        private static PropExpression MatchAtom(PropLogicLexer lexer)
        {
            ExpectToken(lexer, LexerTokenType.Identifier, LexerTokenType.OpenParen);

            PropExpression result;

            if (lexer.CurrentTokenType == LexerTokenType.Identifier)
            {
                result = new PropIdentifier(lexer.CurrentTokenValue);
                lexer.NextToken();
            }
            else
            {
                lexer.NextToken();
                result = MatchExpression(lexer);
                ExpectToken(lexer, LexerTokenType.CloseParen);
                lexer.NextToken();
            }
            return(result);
        }
        private static void ExpectToken(PropLogicLexer lexer, params LexerTokenType[] tokens)
        {
            foreach (LexerTokenType token in tokens)
            {
                if (lexer.CurrentTokenType == token)
                {
                    return;
                }
            }

            var sb = new StringBuilder();

            foreach (LexerTokenType token in tokens)
            {
                if (sb.Length > 0)
                {
                    sb.Append(", ");
                }
                sb.Append(TokenTypeToString(token));

                throw new ArgumentException(string.Format("Unexpected token in expression, got {0}, expecting one of {1}", TokenTypeToString(lexer.CurrentTokenType), sb));
            }
        }
        // BNF(ish) for the language this parses
        // Expression ::== OrTerm
        // OrTerm ::== AndTerm ( '||' AndTerm)*
        // AndTerm ::== Unary ( '&&' Unary)*
        // Unary ::= '!' Atom | Atom
        // Atom ::= IDENTIFIER | '(' Expression ')'
        // IDENTIFIER ::= [A-Za-z_][A-Za-z0-9_]*
        private static PropExpression MatchExpression(PropLogicLexer lexer)
        {
            PropExpression result = MatchOrTerm(lexer);

            return(result);
        }