/**
   * Parse a simple expression.
   *
   * @param token the initial token.
   * @return the root of the generated parse subtree.
   * @throws Exception if an error occurred.
   */
  private ICodeNode parseSimpleExpression(Token token) throws Exception {
    TokenType signType = null; // type of leading sign (if any)

    // Look for a leading + or - sign.
    TokenType tokenType = token.getType();
    if ((tokenType == PLUS) || (tokenType == MINUS)) {
      signType = tokenType;
      token = nextToken(); // consume the + or -
    }

    // Parse a term and make the root of its tree the root node.
    ICodeNode rootNode = parseTerm(token);

    // Was there a leading - sign?
    if (signType == MINUS) {

      // Create a NEGATE node and adopt the current tree
      // as its child. The NEGATE node becomes the new root node.
      ICodeNode negateNode = ICodeFactory.createICodeNode(NEGATE);
      negateNode.addChild(rootNode);
      rootNode = negateNode;
    }

    token = currentToken();
    tokenType = token.getType();

    // Loop over additive operators.
    while (ADD_OPS.contains(tokenType)) {

      // Create a new operator node and adopt the current tree
      // as its first child.
      ICodeNodeType nodeType = ADD_OPS_OPS_MAP.get(tokenType);
      ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
      opNode.addChild(rootNode);

      token = nextToken(); // consume the operator

      // Parse another term.  The operator node adopts
      // the term's tree as its second child.
      opNode.addChild(parseTerm(token));

      // The operator node becomes the new root node.
      rootNode = opNode;

      token = currentToken();
      tokenType = token.getType();
    }

    return rootNode;
  }
  /**
   * Parse a set.
   *
   * @param token the initial token.
   * @return the root of the generated parse subtree.
   * @throws Exception if an error occurred.
   */
  private ICodeNode parseSet(Token token) throws Exception {
    ICodeNode rootNode = ICodeFactory.createICodeNode(SETS);
    int tempInteger = 0;
    token = nextToken(); // consume left bracket

    while (token.getType() != RIGHT_BRACKET) {

      if (token.getType() == INTEGER) {
        tempInteger = (int) token.getValue();
        rootNode.addChild(parseTerm(token));
        token = currentToken();
      } else if (token.getType() == IDENTIFIER) {
        ICodeNode var = parseFactor(token);
        rootNode.addChild(var);
        token = currentToken();
      }
      if (token.getType() == COMMA) {
        token = nextToken(); // consume next token
        if (token.getType() == COMMA) {
          errorHandler.flag(token, EXTRA_COMMA, this);
        }
      } else if (token.getType() == RIGHT_BRACKET) { // do nothing
      } else if (token.getType() == SEMICOLON) {
        errorHandler.flag(token, MISSING_CLOSE_SQUARE_BRACKET, this);
        break;
      } else if (token.getType() == DOT_DOT) {
        token = nextToken(); // consume the integer.
        // Create a STRING_CONSTANT node as the root node.
        ICodeNode node = ICodeFactory.createICodeNode(SUBRANGE);
        // denotes the maximum value of the range.
        if (token.getType() != INTEGER) {
          errorHandler.flag(token, MISSING_MAX_VAL_SUBRANGE, this);
        } else {
          int value = (int) token.getValue();
          node.setAttribute(VALUE, value);
        }
        rootNode.addChild(node);
        token = nextToken();

      } else {
        errorHandler.flag(token, MISSING_COMMA, this);
      }
    }
    if (token.getType() != SEMICOLON) {
      token = nextToken(); // consume right bracket
    }
    return rootNode;
  }
  /**
   * Parse an expression.
   *
   * @param token the initial token.
   * @return the root of the generated parse subtree.
   * @throws Exception if an error occurred.
   */
  private ICodeNode parseExpression(Token token) throws Exception {
    // Parse a simple expression and make the root of its tree
    // the root node.
    ICodeNode rootNode = parseSimpleExpression(token);

    token = currentToken();
    TokenType tokenType = token.getType();

    // Look for a relational operator.
    if (REL_OPS.contains(tokenType)) {

      // Create a new operator node and adopt the current tree
      // as its first child.
      ICodeNodeType nodeType = REL_OPS_MAP.get(tokenType);
      ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
      opNode.addChild(rootNode);

      token = nextToken(); // consume the operator

      // Parse the second simple expression.  The operator node adopts
      // the simple expression's tree as its second child.
      opNode.addChild(parseSimpleExpression(token));

      // The operator node becomes the new root node.
      rootNode = opNode;
    }

    return rootNode;
  }
  /**
   * Parse a term.
   *
   * @param token the initial token.
   * @return the root of the generated parse subtree.
   * @throws Exception if an error occurred.
   */
  private ICodeNode parseTerm(Token token) throws Exception {
    // Parse a factor and make its node the root node.
    ICodeNode rootNode = parseFactor(token);

    token = currentToken();
    TokenType tokenType = token.getType();

    // Loop over multiplicative operators.
    while (MULT_OPS.contains(tokenType)) {

      // Create a new operator node and adopt the current tree
      // as its first child.
      ICodeNodeType nodeType = MULT_OPS_OPS_MAP.get(tokenType);
      ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
      opNode.addChild(rootNode);

      token = nextToken(); // consume the operator

      // Parse another factor.  The operator node adopts
      // the term's tree as its second child.
      opNode.addChild(parseFactor(token));

      // The operator node becomes the new root node.
      rootNode = opNode;

      token = currentToken();
      tokenType = token.getType();
    }

    return rootNode;
  }
  /**
   * Parse a term.
   *
   * @param token the initial token.
   * @return the root of the generated parse subtree.
   * @throws Exception if an error occurred.
   */
  private ICodeNode parseTerm(Token token) throws Exception {
    // Parse a factor and make its node the root node.
    ICodeNode rootNode = parseFactor(token);

    token = currentToken();
    TokenType tokenType = token.getType();

    if (rootNode.getType() == ICodeNodeTypeImpl.SET
        && (tokenType == PascalTokenType.OR || tokenType == PascalTokenType.AND)) {
      errorHandler.flag(token, INVALID_OPERATOR, this);
    }

    // Loop over multiplicative operators.
    while (MULT_OPS.contains(tokenType)) {

      // Create a new operator node and adopt the current tree
      // as its first child.
      ICodeNodeType nodeType = MULT_OPS_OPS_MAP.get(tokenType);
      ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
      opNode.addChild(rootNode);

      switch ((ICodeNodeTypeImpl) opNode.getType()) {
        case INTEGER_DIVIDE:
        case FLOAT_DIVIDE:
          if (rootNode.getType() == ICodeNodeTypeImpl.SET) {
            errorHandler.flag(token, INVALID_OPERATOR, this);
          }
      }

      token = nextToken(); // consume the operator

      // Parse another factor.  The operator node adopts
      // the term's tree as its second child.
      opNode.addChild(parseFactor(token));

      // The operator node becomes the new root node.
      rootNode = opNode;

      token = currentToken();
      tokenType = token.getType();
    }

    return rootNode;
  }
  /**
   * Parse an expression.
   *
   * @param token the initial token.
   * @return the root of the generated parse subtree.
   * @throws Exception if an error occurred.
   */
  private ICodeNode parseExpression(Token token) throws Exception {
    // Parse a simple expression and make the root of its tree
    // the root node.
    ICodeNode rootNode = parseSimpleExpression(token);

    token = currentToken();
    TokenType tokenType = token.getType();

    // Look for a relational operator.
    if (REL_OPS.contains(tokenType)) {

      // Create a new operator node and adopt the current tree
      // as its first child.
      ICodeNodeType nodeType = REL_OPS_MAP.get(tokenType);
      ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
      opNode.addChild(rootNode);

      ICodeNodeTypeImpl type = (ICodeNodeTypeImpl) opNode.getType();
      if (type == LT || type == GT || type == IN_SET) {
        if (rootNode.getType() == ICodeNodeTypeImpl.SET) {
          errorHandler.flag(token, INVALID_OPERATOR, this);
        }
      }

      Token previousToken = token;
      token = nextToken(); // consume the operator

      // Parse the second simple expression.  The operator node adopts
      // the simple expression's tree as its second child.
      ICodeNode rightNode = parseSimpleExpression(token);
      opNode.addChild(rightNode);

      if (opNode.getType() == IN_SET && rightNode.getType() == INTEGER_CONSTANT) {
        errorHandler.flag(previousToken, INVALID_OPERATOR, this);
      }

      // The operator node becomes the new root node.
      rootNode = opNode;
    }

    return rootNode;
  }
Beispiel #7
0
  /**
   * Parse an expression
   *
   * @param token the initial token.
   * @return the root of the generated parse subtree.
   * @throws Exception if an error occurred.
   */
  private ICodeNode parseExpression(Token token) throws Exception {
    // Parse a simple expression and make the root of its tree
    // the root node.
    ICodeNode rootNode = parseSimpleExpression(token);
    TypeSpec resultType = rootNode != null ? rootNode.getTypeSpec() : Predefined.undefinedType;

    token = currentToken();
    TokenType tokenType = token.getType();

    // Look for a relational operator.
    if (REL_OPS.contains(tokenType)) {
      // Create a new operator node and adopt the current tree as its first child
      ICodeNodeType nodeType = REL_OPS_MAP.get(tokenType);
      ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
      opNode.addChild(rootNode);

      token = nextToken(); // consume the operator

      // Parse the second simple expression.
      // The operator node adopts the simple expression's tree as its second child.
      ICodeNode simExprNode = parseSimpleExpression(token);
      opNode.addChild(simExprNode);

      // The operator node becomes the new root node.
      rootNode = opNode;

      // Type check: The operands must be comparison compatible.
      TypeSpec simExprType =
          simExprNode != null ? simExprNode.getTypeSpec() : Predefined.undefinedType;
      if (TypeChecker.areComparisionCompatible(resultType, simExprType))
        resultType = Predefined.booleanType;
      else {
        errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
        resultType = Predefined.undefinedType;
      }
    }

    if (rootNode != null) rootNode.setTypeSpec(resultType);
    return rootNode;
  }
  /**
   * Parse a factor.
   *
   * @param token the initial token.
   * @return the root of the generated parse subtree.
   * @throws Exception if an error occurred.
   */
  private ICodeNode parseFactor(Token token) throws Exception {
    TokenType tokenType = token.getType();
    ICodeNode rootNode = null;

    switch ((PascalTokenType) tokenType) {
      case IDENTIFIER:
        {
          // Look up the identifier in the symbol table stack.
          // Flag the identifier as undefined if it's not found.
          String name = token.getText().toLowerCase();
          SymTabEntry id = symTabStack.lookup(name);
          if (id == null) {
            errorHandler.flag(token, IDENTIFIER_UNDEFINED, this);
            id = symTabStack.enterLocal(name);
          }

          rootNode = ICodeFactory.createICodeNode(VARIABLE);
          rootNode.setAttribute(ID, id);
          id.appendLineNumber(token.getLineNumber());

          token = nextToken(); // consume the identifier
          break;
        }

      case INTEGER:
        {
          // Create an INTEGER_CONSTANT node as the root node.
          rootNode = ICodeFactory.createICodeNode(INTEGER_CONSTANT);
          rootNode.setAttribute(VALUE, token.getValue());

          token = nextToken(); // consume the number
          break;
        }

      case REAL:
        {
          // Create an REAL_CONSTANT node as the root node.
          rootNode = ICodeFactory.createICodeNode(REAL_CONSTANT);
          rootNode.setAttribute(VALUE, token.getValue());

          token = nextToken(); // consume the number
          break;
        }

      case STRING:
        {
          String value = (String) token.getValue();

          // Create a STRING_CONSTANT node as the root node.
          rootNode = ICodeFactory.createICodeNode(STRING_CONSTANT);
          rootNode.setAttribute(VALUE, value);

          token = nextToken(); // consume the string
          break;
        }

      case NOT:
        {
          token = nextToken(); // consume the NOT

          // Create a NOT node as the root node.
          rootNode = ICodeFactory.createICodeNode(ICodeNodeTypeImpl.NOT);

          // Parse the factor.  The NOT node adopts the
          // factor node as its child.
          rootNode.addChild(parseFactor(token));

          break;
        }

      case LEFT_PAREN:
        {
          token = nextToken(); // consume the (

          // Parse an expression and make its node the root node.
          rootNode = parseExpression(token);

          // Look for the matching ) token.
          token = currentToken();
          if (token.getType() == RIGHT_PAREN) {
            token = nextToken(); // consume the )
          } else {
            errorHandler.flag(token, MISSING_RIGHT_PAREN, this);
          }

          break;
        }
      case LEFT_BRACKET:
        {
          rootNode = parseSet(token);

          break;
        }

      default:
        {
          errorHandler.flag(token, UNEXPECTED_TOKEN, this);
          break;
        }
    }

    return rootNode;
  }
  /**
   * Parse a set.
   *
   * @param token the initial token.
   * @return the root of the generated parse subtree.
   * @throws Exception if an error occurred.
   */
  private ICodeNode parseSet(Token token) throws Exception {
    ICodeNode rootNode = ICodeFactory.createICodeNode(ICodeNodeTypeImpl.SET);
    HashSet<Integer> values = new HashSet<>();
    rootNode.setAttribute(VALUE, new HashSet<Integer>());
    boolean isFinished = false;

    while (token.getType() != RIGHT_BRACKET && token.getType() != ERROR && !isFinished) {
      ICodeNode leftNode = parseSimpleExpression(token);

      if (leftNode.getType() == INTEGER_CONSTANT
          && !values.add((Integer) leftNode.getAttribute(VALUE))) {
        errorHandler.flag(token, NON_UNIQUE_MEMBERS, this);
      }

      token = currentToken();

      switch ((PascalTokenType) token.getType()) {
        case RIGHT_BRACKET:
          rootNode.addChild(leftNode);
          break;
        case COMMA:
          rootNode.addChild(leftNode);
          token = nextToken(); // Consume the ,
          if (token.getType() == COMMA) {
            errorHandler.flag(token, EXTRA_COMMAS, this);
            token = nextToken(); // Consume the extra ,
          }
          break;
        case DOT_DOT:
          token = nextToken(); // Consume the ..
          if (token.getType() == COMMA) {
            errorHandler.flag(token, INVALID_SUBRANGE, this);
            token = nextToken(); // Consume the ,
            rootNode.addChild(leftNode);
          } else {
            ICodeNode rightNode = parseSimpleExpression(token);
            ICodeNode subrangeNode = ICodeFactory.createICodeNode(SUBRANGE);
            subrangeNode.addChild(leftNode);
            subrangeNode.addChild(rightNode);
            rootNode.addChild(subrangeNode);

            if (leftNode.getType() == INTEGER_CONSTANT && rightNode.getType() == INTEGER_CONSTANT) {
              boolean duplicateFound = false;
              Integer leftRange = (Integer) leftNode.getAttribute(VALUE) + 1;
              Integer rightRange = (Integer) rightNode.getAttribute(VALUE);

              while (leftRange <= rightRange) {
                if (!values.add(leftRange++) && !duplicateFound) {
                  errorHandler.flag(token, NON_UNIQUE_MEMBERS, this);
                  duplicateFound = true;
                }
              }
            }

            token = currentToken();
            if (token.getType() == COMMA) {
              token = nextToken(); // Consume the ,
            } else if (token.getType() != RIGHT_BRACKET) {
              errorHandler.flag(token, MISSING_COMMA, this);
            }
          }
          break;

        case INTEGER:
          errorHandler.flag(token, MISSING_COMMA, this);
          break;

        case SEMICOLON:
          isFinished = true;
          break;

        default:
          errorHandler.flag(token, UNEXPECTED_TOKEN, this);
          break;
      }
    }

    return rootNode;
  }
Beispiel #10
0
  private ICodeNode parseIdentifier(Token token) throws Exception {
    ICodeNode rootNode = null;

    // Look up the identifier in the symbol table stack.
    // Flag the identifier as undefined if it's not found.
    String name = token.getText().toLowerCase();
    SymTabEntry id = symTabStack.lookup(name);

    // Undefined.
    if (id == null) {
      errorHandler.flag(token, IDENTIFIER_UNDEFINED, this);
      id = symTabStack.enterLocal(name);
      id.setDefinition(UNDEFINED);
      id.setTypeSpec(Predefined.undefinedType);
    }

    Definition defnCode = id.getDefinition();
    switch ((DefinitionImpl) defnCode) {
      case CONSTANT:
        {
          Object value = id.getAttribute(CONSTANT_VALUE);
          TypeSpec type = id.getTypeSpec();

          if (value instanceof Integer) {
            rootNode = ICodeFactory.createICodeNode(INTEGER_CONSTANT);
            rootNode.setAttribute(VALUE, value);
          } else if (value instanceof Float) {
            rootNode = ICodeFactory.createICodeNode(REAL_CONSTANT);
            rootNode.setAttribute(VALUE, value);
          } else if (value instanceof String) {
            rootNode = ICodeFactory.createICodeNode(STRING_CONSTANT);
            rootNode.setAttribute(VALUE, value);
          }

          id.appendLineNumber(token.getLineNumber());
          token = nextToken(); // consume the constant identifier

          if (rootNode != null) {
            rootNode.setTypeSpec(type);
          }

          break;
        }

      case ENUMERATION_CONSTANT:
        {
          Object value = id.getAttribute(CONSTANT_VALUE);
          TypeSpec type = id.getTypeSpec();

          rootNode = ICodeFactory.createICodeNode(INTEGER_CONSTANT);
          rootNode.setAttribute(VALUE, value);

          id.appendLineNumber(token.getLineNumber());
          token = nextToken(); // consume the enum constant identifier

          rootNode.setTypeSpec(type);
          break;
        }

      case FUNCTION:
        {
          CallParser callParser = new CallParser(this);
          rootNode = callParser.parse(token);
          break;
        }
      default:
        {
          VariableParser variableParser = new VariableParser(this);
          rootNode = variableParser.parse(token, id);
          break;
        }
    }
    return rootNode;
  }
Beispiel #11
0
  /**
   * Parse a factor.
   *
   * @param token the initial token.
   * @return the root of the generated parse subtree.
   * @throws Exception if an error occurred.
   */
  private ICodeNode parseFactor(Token token) throws Exception {
    TokenType tokenType = token.getType();
    ICodeNode rootNode = null;

    switch ((PascalTokenType) tokenType) {
      case IDENTIFIER:
        {
          return parseIdentifier(token);
        }

      case INTEGER:
        {
          // Create an INTEGER_CONSTANT node as the root node
          rootNode = ICodeFactory.createICodeNode(INTEGER_CONSTANT);
          rootNode.setAttribute(VALUE, token.getValue());

          token = nextToken(); // consume the number
          rootNode.setTypeSpec(Predefined.integerType);
          break;
        }

      case REAL:
        {
          // Create an REAL_CONSTANT node as the root node.
          rootNode = ICodeFactory.createICodeNode(REAL_CONSTANT);
          rootNode.setAttribute(VALUE, token.getValue());

          token = nextToken(); // consume the number

          rootNode.setTypeSpec(Predefined.realType);
          break;
        }

      case STRING:
        {
          String value = (String) token.getValue();

          // Create a STRING_CONSTANT node as the root node.
          rootNode = ICodeFactory.createICodeNode(STRING_CONSTANT);
          rootNode.setAttribute(VALUE, value);

          TypeSpec resultType =
              value.length() == 1 ? Predefined.charType : TypeFactory.createStringType(value);

          token = nextToken(); // consume the string
          rootNode.setTypeSpec(resultType);
          break;
        }

      case NOT:
        {
          token = nextToken(); // consume the NOT

          // Create a NOT node as the root node.
          rootNode = ICodeFactory.createICodeNode(ICodeNodeTypeImpl.NOT);

          // Parse the factor. The NOT node adopts the factor node as its child.
          ICodeNode factorNode = parseFactor(token);
          rootNode.addChild(factorNode);

          // Type check: the factor must be boolean.
          TypeSpec factorType =
              factorNode != null ? factorNode.getTypeSpec() : Predefined.undefinedType;
          if (!TypeChecker.isBoolean(factorType))
            errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
          rootNode.setTypeSpec(Predefined.booleanType);
          break;
        }

      case LEFT_PAREN:
        {
          token = nextToken(); // consume the (

          // Parse an expression and make its node the root node.
          rootNode = parseExpression(token);
          TypeSpec resultType =
              rootNode != null ? rootNode.getTypeSpec() : Predefined.undefinedType;

          // Look for the matching ) token.
          token = currentToken();
          if (token.getType() == RIGHT_PAREN) token = nextToken(); // consume the )
          else errorHandler.flag(token, MISSING_RIGHT_PAREN, this);
          rootNode.setTypeSpec(resultType);
          break;
        }

      default:
        {
          errorHandler.flag(token, UNEXPECTED_TOKEN, this);
          break;
        }
    }
    return rootNode;
  }
Beispiel #12
0
  /**
   * Parse a term.
   *
   * @param token the initial token.
   * @return the root of the generated parse subtree.
   * @throws Exception if an error occurred.
   */
  private ICodeNode parseTerm(Token token) throws Exception {
    // Parse a factor and makes its node the root node.
    ICodeNode rootNode = parseFactor(token);
    TypeSpec resultType = rootNode != null ? rootNode.getTypeSpec() : Predefined.undefinedType;

    token = currentToken();
    TokenType tokenType = token.getType();

    // Loop over multiplicative operators.
    while (MULT_OPS.contains(tokenType)) {
      TokenType operator = tokenType;

      // Create a new operator node and adopt the current tree as its first child.
      ICodeNodeType nodeType = MULT_OPS_OPS_MAP.get(operator);
      ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
      opNode.addChild(rootNode);

      token = nextToken(); // consume the operator

      // Parse another factor.
      // The operator node adopts the term's tree as its secomd child.
      ICodeNode factorNode = parseFactor(token);
      opNode.addChild(factorNode);
      TypeSpec factorType =
          factorNode != null ? factorNode.getTypeSpec() : Predefined.undefinedType;
      // The operator node becomes the new root node.
      rootNode = opNode;

      // Determine the result type.
      switch ((PascalTokenType) operator) {
        case STAR:
          {
            // Both operands integer ==> integer result.
            if (TypeChecker.areBothInteger(resultType, factorType)) {
              resultType = Predefined.integerType;
            }

            // Both real operands or one real and one integer operand
            // ==> real result.
            else if (TypeChecker.isAtLeastOneReal(resultType, factorType)) {
              resultType = Predefined.realType;
            } else {
              errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
            }

            break;
          }

        case SLASH:
          {
            // All integer and real operand combinations
            // ==> real result.
            if (TypeChecker.areBothInteger(resultType, factorType)
                || TypeChecker.isAtLeastOneReal(resultType, factorType)) {
              resultType = Predefined.realType;
            } else {
              errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
            }

            break;
          }

        case DIV:
        case MOD:
          {
            // Both operands integer ==> integer result.
            if (TypeChecker.areBothInteger(resultType, factorType)) {
              resultType = Predefined.integerType;
            } else {
              errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
            }

            break;
          }

        case AND:
          {
            // Both operands boolean ==> boolean result.
            if (TypeChecker.areBothBoolean(resultType, factorType)) {
              resultType = Predefined.booleanType;
            } else {
              errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
            }

            break;
          }
      }

      // The operator node becomes the new root node.
      rootNode.setTypeSpec(resultType);

      token = currentToken();
      tokenType = token.getType();
    }
    return rootNode;
  }
Beispiel #13
0
  /**
   * Parse a simple expression.
   *
   * @param token the initial token.
   * @return the root of the generated parse subtree.
   * @throws Exception if an error occurred.
   */
  private ICodeNode parseSimpleExpression(Token token) throws Exception {
    Token signToken = null;
    TokenType signType = null; // type of leading sign (if any)

    // Look for a leading + or - sign
    TokenType tokenType = token.getType();
    if ((tokenType == PLUS) || (tokenType == MINUS)) {
      signType = tokenType;
      signToken = token;
      token = nextToken(); // consume the + or -
    }

    // Parse a term and make the root of its tree the root node.
    ICodeNode rootNode = parseTerm(token);
    TypeSpec resultType = rootNode != null ? rootNode.getTypeSpec() : Predefined.undefinedType;

    // Type check: Leading sign.
    if ((signType != null) && (!TypeChecker.isIntegerOrReal(resultType)))
      errorHandler.flag(signToken, INCOMPATIBLE_TYPES, this);

    // Was there a leading - sign?
    if (signType == MINUS) {
      // Create a NEGATE node and adopt the current tree as its child.
      // The NEGATE node becomes the new root node.
      ICodeNode negateNode = ICodeFactory.createICodeNode(NEGATE);
      negateNode.addChild(rootNode);
      negateNode.setTypeSpec(rootNode.getTypeSpec());
      rootNode = negateNode;
    }

    token = currentToken();
    tokenType = token.getType();

    // Loop over additive operators.
    while (ADD_OPS.contains(tokenType)) {
      TokenType operator = tokenType;

      // Create a new operator node and adopt the current tree as its first child.
      ICodeNodeType nodeType = ADD_OPS_OPS_MAP.get(operator);
      ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
      opNode.addChild(rootNode);

      token = nextToken(); // consume the operator

      // Parse another term.
      // The operator node adopts the term's tree as its second child.
      ICodeNode termNode = parseTerm(token);
      opNode.addChild(termNode);
      TypeSpec termType = termNode != null ? termNode.getTypeSpec() : Predefined.undefinedType;

      // The operator node becomes the new root node.
      rootNode = opNode;

      switch ((PascalTokenType) operator) {
        case PLUS:
        case MINUS:
          {
            // Both operands integer => integer result.
            if (TypeChecker.areBothInteger(resultType, termType))
              resultType = Predefined.integerType;

            // Both real operands or one real and one integer operand => real result.
            else if (TypeChecker.isAtLeastOneReal(resultType, termType))
              resultType = Predefined.realType;
            else errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
            break;
          }

        case OR:
          {
            // Both operands boolean ==> boolean result.
            if (TypeChecker.areBothBoolean(resultType, termType))
              resultType = Predefined.booleanType;
            else errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
            break;
          }
      }

      rootNode.setTypeSpec(resultType);
      token = currentToken();
      tokenType = token.getType();
    }
    return rootNode;
  }