Package com.googlecode.aviator.parser

Source Code of com.googlecode.aviator.parser.ExpressionParser

/**
*  Copyright (C) 2010 dennis zhuang (killme2008@gmail.com)
*
*  This library is free software; you can redistribute it and/or modify
*  it under the terms of the GNU Lesser General Public License as published
*  by the Free Software Foundation; either version 2.1 of the License, or
*  (at your option) any later version.
*
*  This library is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
**/
package com.googlecode.aviator.parser;

import java.util.HashSet;
import java.util.Set;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.code.CodeGenerator;
import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
import com.googlecode.aviator.lexer.ExpressionLexer;
import com.googlecode.aviator.lexer.token.CharToken;
import com.googlecode.aviator.lexer.token.PatternToken;
import com.googlecode.aviator.lexer.token.Token;
import com.googlecode.aviator.lexer.token.Variable;
import com.googlecode.aviator.lexer.token.Token.TokenType;


/**
* Syntex parser for expression
*
* @author dennis
*
*/
public class ExpressionParser {
    private final ExpressionLexer lexer;

    static final Set<String> RESERVED_WORDS = new HashSet<String>();
    static {
        RESERVED_WORDS.add(Variable.TRUE.getLexeme());
        RESERVED_WORDS.add(Variable.FALSE.getLexeme());
        RESERVED_WORDS.add(Variable.NIL.getLexeme());
    }
    /*
     * Lookhead token
     */
    private Token<?> lookhead;

    private Token<?> prevToken;

    private final CodeGenerator codeGenerator;

    // Paren depth
    private int depth = 0;

    private boolean inPattern = false;


    public ExpressionParser(ExpressionLexer lexer, CodeGenerator codeGenerator) {
        super();
        this.lexer = lexer;
        this.lookhead = this.lexer.scan();
        if (this.lookhead == null) {
            throw new ExpressionSyntaxErrorException("Blank expression");
        }
        this.codeGenerator = codeGenerator;
    }


    public void ternary() {
        bool();
        if (lookhead == null || expectLexeme(":") || expectLexeme(",")) {
            return;
        }
        if (expectLexeme("?")) {
            move(true);
            this.codeGenerator.onTernaryBoolean(lookhead);
            ternary();
            if (expectLexeme(":")) {
                move(true);
                this.codeGenerator.onTernaryLeft(lookhead);
                ternary();
                this.codeGenerator.onTernaryRight(lookhead);
            }
            else {
                reportSyntaxError();
            }
        }
        else {
            if (expectLexeme(")")) {
                if (this.depth > 0) {
                    return;
                }
                else {
                    reportSyntaxError("Insert '(' to complete Expression");
                }
            }
            if (expectLexeme("]")) {
                if (this.depth > 0) {
                    return;
                }
                else {
                    reportSyntaxError("Insert '[' to complete Expression");
                }
            }
            if (!expectLexeme("(") && !expectLexeme("[")) {
                reportSyntaxError();
            }

        }
    }


    public void bool() {
        join();
        while (true) {
            if (isJoinToken()) {
                codeGenerator.onJoinLeft(lookhead);
                move(true);
                if (isJoinToken()) {
                    move(true);
                    join();
                    codeGenerator.onJoinRight(lookhead);
                }
                else {
                    reportSyntaxError();
                }
            }
            else {
                if (lookhead == null) {
                    break;
                }

                else {
                    break;
                }
            }

        }
    }


    private boolean isJoinToken() {
        return expectLexeme("|");
    }


    private boolean expectLexeme(String s) {
        if (lookhead == null) {
            return false;
        }
        return lookhead.getType() == TokenType.Char && ((CharToken) lookhead).getLexeme().equals(s);
    }


    private boolean isAndToken() {
        return expectLexeme("&");
    }


    public void join() {
        equality();
        while (true) {
            if (isAndToken()) {
                codeGenerator.onAndLeft(lookhead);
                move(true);
                if (isAndToken()) {
                    move(true);
                    equality();
                    codeGenerator.onAndRight(lookhead);
                }
                else {
                    reportSyntaxError();
                }
            }
            else {
                break;
            }
        }

    }


    public void equality() {
        rel();
        while (true) {
            if (expectLexeme("=")) {
                move(true);
                if (expectLexeme("=")) {
                    move(true);
                    rel();
                    codeGenerator.onEq(lookhead);
                }
                else if (expectLexeme("~")) {
                    // It is a regular expression
                    move(true);
                    rel();
                    codeGenerator.onMatch(lookhead);
                }
                else {
                    reportSyntaxError();
                }
            }
            else if (expectLexeme("!")) {
                move(true);
                if (expectLexeme("=")) {
                    move(true);
                    rel();
                    codeGenerator.onNeq(lookhead);
                }
                else {
                    reportSyntaxError();
                }
            }
            else {
                break;
            }
        }
    }


    public void rel() {
        expr();
        while (true) {
            if (expectLexeme("<")) {
                move(true);
                if (expectLexeme("=")) {
                    move(true);
                    expr();
                    codeGenerator.onLe(lookhead);
                }
                else {
                    expr();
                    codeGenerator.onLt(lookhead);
                }
            }
            else if (expectLexeme(">")) {
                move(true);
                if (expectLexeme("=")) {
                    move(true);
                    expr();
                    codeGenerator.onGe(lookhead);
                }
                else {
                    expr();
                    codeGenerator.onGt(lookhead);
                }
            }
            else {
                break;
            }
        }
    }


    public void expr() {
        term();
        while (true) {
            if (expectLexeme("+")) {
                move(true);
                term();
                codeGenerator.onAdd(lookhead);
            }
            else if (expectLexeme("-")) {
                move(true);
                term();
                codeGenerator.onSub(lookhead);
            }
            else {
                break;
            }
        }
    }


    public void term() {
        unary();
        while (true) {
            if (expectLexeme("*")) {
                move(true);
                unary();
                codeGenerator.onMult(lookhead);
            }
            else if (expectLexeme("/")) {
                move(true);
                unary();
                codeGenerator.onDiv(lookhead);
            }
            else if (expectLexeme("%")) {
                move(true);
                unary();
                codeGenerator.onMod(lookhead);
            }
            else {
                break;
            }
        }
    }


    public void unary() {
        if (expectLexeme("!")) {
            move(true);
            // check if it is a seq function call,"!" as variable
            if (expectLexeme(",") || expectLexeme(")")) {
                back();
                factor();
            }
            else {
                unary();
                this.codeGenerator.onNot(lookhead);
            }
        }
        else if (expectLexeme("-")) {
            move(true);
            // check if it is a seq function call,"!" as variable
            if (expectLexeme(",") || expectLexeme(")")) {
                back();
                factor();
            }
            else {
                unary();
                this.codeGenerator.onNeg(lookhead);
            }
        }
        else {
            factor();
        }
    }

    public static final CharToken LEFT_PAREN = new CharToken('(', -1);
    public static final CharToken RIGHT_PAREN = new CharToken(')', -1);


    public boolean isOPVariable(Token<?> token) {
        if (token.getType() != TokenType.Char) {
            return false;
        }
        CharToken charToken = (CharToken) token;

        move(true);
        if (expectLexeme(",") || expectLexeme(")")) {
            back();
            String lexeme = String.valueOf(charToken.getCh());
            if (lexeme.equals("-")) {
                lexeme = "-sub";
            }
            return AviatorEvaluator.FUNC_MAP.containsKey(lexeme);
        }
        else {
            back();
            return false;
        }
    }


    public void factor() {
        if (lookhead == null) {
            reportSyntaxError();
        }
        if (expectLexeme("(")) {
            this.depth++;
            move(true);
            ternary();
            if (!expectLexeme(")")) {
                reportSyntaxError("insert ')' to complete Expression");
            }
            else {
                move(true);
            }
            this.depth--;
        }
        else if (lookhead.getType() == TokenType.Number || lookhead.getType() == TokenType.String
                || lookhead.getType() == TokenType.Variable || lookhead == Variable.TRUE || lookhead == Variable.FALSE
                || isOPVariable(lookhead)) {
            if (lookhead.getType() == TokenType.Variable) {
                checkVariableName();
            }
            // binary operation as variable for seq functions
            if (lookhead.getType() == TokenType.Char) {
                CharToken charToken = (CharToken) lookhead;
                // make it as variable
                lookhead = new Variable(charToken.getLexeme(), charToken.getStartIndex());
            }
            move(true);
            // function
            if (prevToken.getType() == TokenType.Variable && expectLexeme("(")) {
                method();
            }
            else if (prevToken.getType() == TokenType.Variable && expectLexeme("[")) {
                this.depth++;
                if (RESERVED_WORDS.contains(prevToken.getLexeme())) {
                    throw new ExpressionSyntaxErrorException(prevToken.getLexeme() + " could not use [] operator");
                }
                this.codeGenerator.onElementStart(prevToken);

                move(true);
                ternary();
                if (!expectLexeme("]")) {
                    reportSyntaxError("insert ']' to complete Expression");
                }
                else {
                    this.depth--;
                    move(true);
                    this.codeGenerator.onElementEnd(lookhead);
                }
            }
            else {
                codeGenerator.onConstant(prevToken);
            }
        }
        else if (expectLexeme("/")) {
            pattern();
        }
        else {
            reportSyntaxError();
        }

    }


    private void checkVariableName() {
        String[] names = lookhead.getLexeme().split("\\.");
        for (String name : names) {
            if (!isJavaIdentifier(name)) {
                reportSyntaxError("Illegal identifier " + name + ",index=" + lookhead.getStartIndex());
            }
        }
    }


    private void method() {
        this.depth++;
        this.codeGenerator.onMethodName(prevToken);
        move(true);
        if (!expectLexeme(")")) {
            ternary();
            this.codeGenerator.onMethodParameter(lookhead);
            while (expectLexeme(",")) {
                move(true);
                ternary();
                this.codeGenerator.onMethodParameter(lookhead);
            }
        }
        if (!expectLexeme(")")) {
            reportSyntaxError("insert ')' to complete Expression");
        }
        else {
            this.depth--;
            move(true);
            this.codeGenerator.onMethodInvoke(lookhead);
        }
    }


    /**
     * Test whether a given string is a valid Java identifier.
     *
     * @param id
     *            string which should be checked
     * @return <code>true</code> if a valid identifier
     */
    public static final boolean isJavaIdentifier(String id) {
        if (id == null) {
            return false;
        }

        if (id.equals("")) {
            return false;
        }

        if (!(java.lang.Character.isJavaIdentifierStart(id.charAt(0)))) {
            return false;
        }

        for (int i = 1; i < id.length(); i++) {
            if (!(java.lang.Character.isJavaIdentifierPart(id.charAt(i)))) {
                return false;
            }
        }
        if (id.equals("null")) {
            return false;
        }
        return true;
    }


    private void pattern() {
        // It is a pattern
        int startIndex = this.lookhead.getStartIndex();
        move(true);
        this.inPattern = true;
        StringBuffer sb = new StringBuffer();
        while (lookhead != null) {
            while (!expectLexeme("/")) {
                sb.append(lookhead.getLexeme());
                move(false);
            }
            if (prevToken.getType() == TokenType.Char && ((CharToken) prevToken).getLexeme().equals("\\")) {
                sb.append("/");
                move(false);
                continue;
            }
            this.inPattern = false;
            break;
        }
        if (this.inPattern) {
            reportSyntaxError();
        }
        codeGenerator.onConstant(new PatternToken(sb.toString(), startIndex));
        move(true);
    }


    private void reportSyntaxError() {
        throw new ExpressionSyntaxErrorException("Syntax error:prev=" + (prevToken != null ? prevToken : "")
                + ",current=" + lookhead);
    }


    private void reportSyntaxError(String message) {
        throw new ExpressionSyntaxErrorException("Syntax error:" + message);
    }


    public void move(boolean analyse) {
        if (lookhead != null) {
            prevToken = lookhead;
            lookhead = lexer.scan(analyse);
        }
        else {
            reportSyntaxError();
        }

    }


    public void back() {
        this.lexer.pushback(lookhead);
        lookhead = prevToken;
    }


    public Expression parse() {
        ternary();
        if (this.depth > 0) {
            reportSyntaxError("insert ')' to complete Expression");
        }
        return codeGenerator.getResult();
    }

}
TOP

Related Classes of com.googlecode.aviator.parser.ExpressionParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.