static bool IsOperator(PatternToken token)
 {
     if (token.Type != TokenType.Identifier)
     {
         return(false);
     }
     return(ops.ContainsKey(token.Value));
 }
 static bool IsFunction(PatternToken token)
 {
     if (token.Type != TokenType.Identifier)
     {
         return(false);
     }
     return(fns.ContainsKey(token.Value));
 }
        PatternToken ReadToken()
        {
            if (lookAhead == null)
            {
                throw UnexpectedEnd();
            }
            PatternToken ret = lookAhead.Value;

            lookAhead = tokenizer.NextToken();
            return(ret);
        }
 Exception BadArgCount(PatternToken token, int expected)
 {
     throw new InvalidPatternException(string.Format("Invalid argument count for '{0}' at position {1}. Expected {2}", token.Value, token.Position, expected));
 }
 static bool IsOperator(PatternToken token)
 {
     if (token.Type != TokenType.Identifier)
         return false;
     return ops.ContainsKey(token.Value);
 }
 static bool IsFunction(PatternToken token)
 {
     if (token.Type != TokenType.Identifier)
         return false;
     return fns.ContainsKey(token.Value);
 }
 Exception UnknownToken(PatternToken token)
 {
     throw new InvalidPatternException(string.Format("Unknown token '{0}' at position {1}.", token.Value, token.Position));
 }
 Exception UnexpectedToken(PatternToken token, char expect)
 {
     throw new InvalidPatternException(string.Format("Unexpected token '{0}' at position {1}. Expected '{2}'.", token.Value, token.Position, expect));
 }
 Exception UnknownToken(PatternToken token)
 {
     throw new InvalidPatternException(string.Format("Unknown token '{0}' at position {1}.", token.Value, token.Position));
 }
        PatternExpression ParseExpression(bool readBinOp = false)
        {
            PatternExpression ret;
            PatternToken      token = ReadToken();

            switch (token.Type)
            {
            case TokenType.Literal:
                ret = new LiteralExpression(token.Value);
                break;

            case TokenType.LParens: {
                ret = ParseExpression(true);
                PatternToken parens = ReadToken();
                if (parens.Type != TokenType.RParens)
                {
                    throw MismatchParens(token.Position.Value);
                }
            }
            break;

            case TokenType.Identifier:
                if (IsOperator(token))
                {
                    // unary operator
                    PatternOperator op = ops[token.Value]();
                    if (!op.IsUnary)
                    {
                        throw UnexpectedToken(token);
                    }
                    op.OperandA = ParseExpression();
                    ret         = op;
                }
                else if (IsFunction(token))
                {
                    // function
                    PatternFunction fn = fns[token.Value]();

                    PatternToken parens = ReadToken();
                    if (parens.Type != TokenType.LParens)
                    {
                        throw UnexpectedToken(parens, '(');
                    }

                    fn.Arguments = new List <PatternExpression>(fn.ArgumentCount);
                    for (int i = 0; i < fn.ArgumentCount; i++)
                    {
                        if (PeekToken() == null)
                        {
                            throw UnexpectedEnd();
                        }
                        if (PeekToken().Value.Type == TokenType.RParens)
                        {
                            throw BadArgCount(token, fn.ArgumentCount);
                        }
                        if (i != 0)
                        {
                            PatternToken comma = ReadToken();
                            if (comma.Type != TokenType.Comma)
                            {
                                throw UnexpectedToken(comma, ',');
                            }
                        }
                        fn.Arguments.Add(ParseExpression());
                    }

                    parens = ReadToken();
                    if (parens.Type == TokenType.Comma)
                    {
                        throw BadArgCount(token, fn.ArgumentCount);
                    }
                    if (parens.Type != TokenType.RParens)
                    {
                        throw MismatchParens(parens.Position.Value);
                    }

                    ret = fn;
                }
                else
                {
                    bool boolValue;
                    if (bool.TryParse(token.Value, out boolValue))
                    {
                        ret = new LiteralExpression(boolValue);
                    }
                    else
                    {
                        throw UnknownToken(token);
                    }
                }

                break;

            default:
                throw UnexpectedToken(token);
            }

            if (!readBinOp)
            {
                return(ret);
            }

            // binary operator
            PatternToken?peek = PeekToken();

            while (peek != null)
            {
                if (peek.Value.Type != TokenType.Identifier)
                {
                    break;
                }
                if (!IsOperator(peek.Value))
                {
                    break;
                }

                PatternToken    binOpToken = ReadToken();
                PatternOperator binOp      = ops[binOpToken.Value]();
                if (binOp.IsUnary)
                {
                    throw UnexpectedToken(binOpToken);
                }
                binOp.OperandA = ret;
                binOp.OperandB = ParseExpression();
                ret            = binOp;

                peek = PeekToken();
            }

            return(ret);
        }
 Exception BadArgCount(PatternToken token, int expected)
 {
     throw new InvalidPatternException(string.Format("Invalid argument count for '{0}' at position {1}. Expected {2}", token.Value, token.Position, expected));
 }
 Exception UnexpectedToken(PatternToken token, char expect)
 {
     throw new InvalidPatternException(string.Format("Unexpected token '{0}' at position {1}. Expected '{2}'.", token.Value, token.Position, expect));
 }