package fc.parser.rdp;

import fc.lang.AssignementExpression;
import fc.lang.ChangeSignFunction;
import fc.lang.ConstantValueExpression;
import fc.lang.DifferenceFunction;
import fc.lang.DivisionFunction;
import fc.lang.Expression;
import fc.lang.FunctionExpression;
import fc.lang.MultiplicationFunction;
import fc.lang.SumFunction;
import fc.lang.VariableExpression;
import fc.lexer.Lexer;
import fc.lexer.Symbol;
import fc.lexer.Token;
import fc.parser.common.ParseException;

/**
 * Implementation of a recursive descent parser for the a simple calculator
 */
public class Parser implements fc.parser.common.Parser {
    private Token currentToken;
    private Lexer lexer;

    /**
     * Fetch next token from the lexical analyzer and store it for later
     * treatment and return the current token.
     * 
     * @return the current Token
     */
    public Token advance() {
        Token previousToken = currentToken;
        currentToken = lexer.nextToken();
        return previousToken;
    }

    /**
     * Test if the current token matches the given symbol. If yes, the current
     * token is returned and the parse position is advanced, otherwise null is
     * returned and no action is performed.
     * 
     * @return the current Token or null
     */
    public Token accept(Symbol symbol) {
        if (currentToken.getSymbol() == symbol) {
            return advance();
        }
        else {
            return null;
        }
    }

    /**
     * Test if the current token matches the given symbol. If yes, the current
     * token is returned and the parse position is advanced, otherwise a parsing
     * exception is thrown.
     * 
     * @return the current Token
     */
    public Token expect(Symbol symbol) throws ParseException {
        Token previousToken = accept(symbol);

        if (previousToken == null) {
            throw new ParseException("Expected symbol " + symbol + " but got "
                    + currentToken.getSymbol() + " instead");
        }

        return previousToken;
    }

    /**
     * Parse a factor
     * 
     * factor -> number | identifier | (expr)
     * 
     * @return
     * @throws ParseException
     */
    private Expression parseFactor() throws ParseException {
        Expression result;

        // first term
        if (accept(Symbol.MINUS) != null) {
            // parse unary minus if any
            Expression term = parseFactorUnsigned();
            result = new FunctionExpression(new ChangeSignFunction(), term);
            advance();
        }
        else {
            result = parseFactorUnsigned();
        }

        return result;
    }

    /**
     * Parse factor
     * @return
     * @throws ParseException
     */
    private Expression parseFactorUnsigned() throws ParseException {
        Expression result;

        Token token;
        if ((token = accept(Symbol.NUMBER)) != null) {
            result = new ConstantValueExpression(token.getNumberValue()
                    .doubleValue());
        }
        else if ((token = accept(Symbol.IDENTIFIER)) != null) {
            result = new VariableExpression(token.getStringValue());
        }
        else if (accept(Symbol.LPAREN) != null) {
            result = parseExpression();
            expect(Symbol.RPAREN);
        }
        else {
            throw new ParseException(
                    "Expected a number, identifier or a left parenthesis but got "
                            + currentToken.getSymbol() + " instead");
        }

        return result;
    }

    /**
     * Parse a term
     * 
     * term -> factor | factor [*|/] factor
     * 
     * @return
     * @throws ParseException
     */
    private Expression parseTerm() throws ParseException {
        Expression result;

        // parse one factor
        result = parseFactor();

        // parse additional factors
        while (true) {
            if (accept(Symbol.MULTIPLY) != null) {
                Expression factor = parseFactor();
                result = new FunctionExpression(new MultiplicationFunction(),
                        result, factor);
            }
            else if (accept(Symbol.DIVIDE) != null) {
                Expression factor = parseFactor();
                result = new FunctionExpression(new DivisionFunction(), result,
                        factor);
            }
            else {
                break;
            }
        }

        return result;
    }

    /**
     * Parse a sum expression
     *
     * expr -> -term | term | term [+|-] term
     *
     * @return
     * @throws ParseException
     */
    private Expression parseExpression() throws ParseException {
        Expression result;
        // left summand
        result = parseTerm();

        // parse additional summands if any
        while (true) {
            if (accept(Symbol.PLUS) != null) {
                Expression term = parseTerm();
                result = new FunctionExpression(new SumFunction(), result, term);
            }
            else if (accept(Symbol.MINUS) != null) {
                Expression term = parseTerm();
                result = new FunctionExpression(new DifferenceFunction(),
                        result, term);
            }
            else {
                break;
            }
        }

        return result;
    }

    /**
     * Parse a basic statement
     * 
     * stm -> let identifier = expr | expr
     *
     * @return resulting expression
     * @throws ParseException
     */
    private Expression parseStatement() throws ParseException {
        Expression result;

        if (accept(Symbol.LET) != null) {
            // parse assignment statement (let x = expr)
            String identifier = expect(Symbol.IDENTIFIER).getStringValue();
            expect(Symbol.EQUAL);
            Expression expression = parseExpression();
            result = new AssignementExpression(identifier, expression);
        }
        else {
            result = parseExpression();
        }

        return result;
    }

    @Override
    public Expression parse(String line) throws ParseException {
        lexer = new Lexer(line);

        advance();
        Expression result = parseStatement();
        expect(Symbol.END);

        return result;
    }
}

@@ -2062,24 +2062,59 @@
 rse a factor
+, possibly with leading unary minus
 %0A     * %0A   
@@ -2117,17 +2117,17 @@
 %0A     * 
-f
+F
 actor -%3E
@@ -2131,18 +2131,40 @@
  -%3E 
-number %7C i
+%5B '-' %5D ( '(' Expr ')' %7C Num %7C I
 dent
@@ -2173,15 +2173,8 @@
 ier 
-%7C (expr
 )%0A  
@@ -2190,45 +2190,73 @@
   * 
-@return%0A     * @throws ParseException
+Note: this function covers the first part of the EBNF rule above.
 %0A   
@@ -2749,53 +2749,185 @@
 ctor
-%0A     * @return%0A     * @throws ParseException
+ without leading unary minus%0A     *%0A     * Factor -%3E %5B '-' %5D ( '(' Expr ')' %7C Num %7C Identifier )%0A     *%0A     * Note: this function covers the second part of the EBNF rule above.
 %0A   
@@ -3795,44 +3795,43 @@
   * 
-t
+T
 erm -%3E 
-f
+F
 actor 
-%7C factor %5B*%7C/%5D f
+%7B ('*'%7C'/') F
 actor
+ %7D
 %0A   
@@ -4681,20 +4681,40 @@
  Parse a
- sum
+n addition or difference
  express
@@ -4735,46 +4735,39 @@
   * 
-e
+E
 xpr -%3E 
--t
+T
 erm 
-%7C term %7C term %5B+%7C-%5D t
+%7B ('+'%7C'-') T
 erm
+ %7D
 %0A   
@@ -5609,20 +5609,28 @@
   * 
-stm
+Statement
  -%3E 
+'
 let
- i
+' I
 dent
@@ -5639,18 +5639,20 @@
 ier 
-= e
+'=' E
 xpr %7C 
-e
+E
 xpr%0A

Update EBNF rules in comments