Пример #1
0
  /**
   * Modified error recovery: tries to do a reduce if no shift out can be found.
   *
   * @param debug
   * @return <code>true</code> if error recovery was successful
   * @throws Exception
   */
  @Override
  protected boolean error_recovery(boolean debug) throws Exception {
    /* the current action code */
    int act;

    /* the Symbol/stack element returned by a reduce */
    Symbol lhs_sym = null;

    /* information about production being reduced with */
    short handle_size, lhs_sym_num;

    for (_done_parsing = false; !_done_parsing; ) {
      act = get_action(((Symbol) stack.peek()).parse_state, error_sym());

      /* decode the action -- > 0 encodes shift */
      if (act > 0) {
        // stop
        break;
      }
      /* if its less than zero, then it encodes a reduce action */
      else if (act < 0) {
        /* perform the action for the reduce */
        lhs_sym = do_action((-act) - 1, this, stack, tos);

        /* look up information about the production */
        lhs_sym_num = production_tab[(-act) - 1][0];
        handle_size = production_tab[(-act) - 1][1];

        if (debug) debug_reduce((-act) - 1, lhs_sym_num, handle_size);

        /* pop the handle off the stack */
        for (int i = 0; i < handle_size; i++) {
          stack.pop();
          tos--;
        }

        /* look up the state to go to from the one popped back to */
        act = get_reduce(((Symbol) stack.peek()).parse_state, lhs_sym_num);
        if (debug)
          debug_message(
              "# Reduce rule: top state "
                  + ((Symbol) stack.peek()).parse_state
                  + ", lhs sym "
                  + lhs_sym_num
                  + " -> state "
                  + act);

        /* shift to that state */
        lhs_sym.parse_state = act;

        // not accessible from here, just forget it. Let's just assume
        // our lexer does what he should do...
        // lhs_sym.used_by_parser = true;

        stack.push(lhs_sym);
        tos++;

        if (debug) debug_message("# Goto state #" + act);
      }
      /* finally if the entry is zero, we have an error */
      else if (act == 0) {
        // stop and try normal recover
        break;
      }
    }

    return super.error_recovery(debug);
  }
Пример #2
0
  public Symbol parse() {
    // Reset the errors count and set up problem requestor
    errorsDetected = 0;
    if (problemRequestor == null) problemRequestor = new AccumulatingSyntaxErrorRequestor();

    // Set up the parse tables for local access
    production_tab = production_table();
    action_tab = action_table();
    reduce_tab = reduce_table();

    // Set up the action object
    CUP$Parser$actions actionObject = new CUP$Parser$actions(this);

    // Set up the scouting stack to prevent any premature reductions in the presense of errors
    SlimParseStack scoutStack = new SlimParseStack();
    scoutStack.shiftStartState();

    // Set up the real parse stack
    boolean[] isNonTerminal =
        new boolean
            [INITIAL_STACK_SIZE]; // Set up an array to remember whether a given symbol on the stack
                                  // is a nonterminal
    Stack realStack = new Stack();
    realStack.push((new Symbol(0, 0 /* start state */)));
    int realStackTop = 0;
    isNonTerminal[0] = true;

    // The left most node that is artificial (i.e. in error -- created by recovery).  This is to
    // control
    // whether we should run parse actions or not
    // Value of 0 indicates that there are no error nodes on the stack
    int leftMostErrorNodeIndex = 0;

    // Perform the parsing
    for (_done_parsing = false; !_done_parsing; ) {
      // Obtain look ahead
      Terminal lookAhead = stream.getLookAhead();

      // Test if the current lookahead can be shifted on the scouting stack
      // If it can, then process all the actions associated with this lookahead on the real stack
      // Otherwise, reset the scouting stack and perform recovery
      if (scoutStack.canShift(lookAhead)) {
        // Repeat until all the actions associated with this lookahead is performed
        while (true) {
          Symbol lookAheadSymbol = stream.getLookAheadSymbol();
          int action = get_action(((Symbol) realStack.peek()).parse_state, lookAheadSymbol.sym);

          if (action > 0) {
            // Parse state of -2 on a terminal node indicates that it is artificially created by
            // error recovery
            boolean isErrorNode = lookAheadSymbol.parse_state == -2;

            // Perform the shift
            lookAheadSymbol.parse_state = action - 1;
            realStack.push(lookAheadSymbol);
            realStackTop++;

            try {
              isNonTerminal[realStackTop] = false;
            } catch (ArrayIndexOutOfBoundsException e) {
              isNonTerminal = enlargeStack(isNonTerminal);
              isNonTerminal[realStackTop] = false;
            }

            // Record the new error situation if applicable
            if (isErrorNode) {
              leftMostErrorNodeIndex =
                  (leftMostErrorNodeIndex == 0
                      ? realStackTop
                      : Math.min(leftMostErrorNodeIndex, realStackTop));
            }

            // After we have shifted, should break out of the action loop
            break;
          } else {
            // Information about the current rule
            int nonTerminalType = production_tab[(-action) - 1][0];
            int handleSize = production_tab[(-action) - 1][1];
            Symbol nonTerminal;

            // We run actions normally if there are no errors nodes on stack
            if (leftMostErrorNodeIndex == 0) {
              try {
                nonTerminal =
                    actionObject.CUP$Parser$do_action((-action) - 1, this, realStack, realStackTop);
              } catch (Exception e) {
                throw new RuntimeException("Parse action failed with exception", e);
              }
            } else {
              // Create the non-terminal symbol ourselves
              int left =
                  ((Symbol)
                          realStack.elementAt(
                              realStackTop - handleSize + (handleSize == 0 ? 0 : 1)))
                      .left;
              int right = ((Symbol) realStack.peek()).right;
              nonTerminal = new Symbol(nonTerminalType, left, right);

              // Remember whether we have really nulled out an error node
              boolean nullable = false;

              // We have to simulate the actions for sequence nodes and some special opt nodes
              if (scoutStack.isConstructPlus(nonTerminalType)) {
                nullable = true;
                if (handleSize == 1) {
                  nonTerminal.value = new ArrayList();
                } else {
                  Symbol elementAt = (Symbol) realStack.elementAt(realStackTop - handleSize + 1);
                  Object value = (elementAt).value;
                  if (Collections.EMPTY_LIST == value
                      && scoutStack.isConstructStar(elementAt.sym)) {
                    nonTerminal.value = new ArrayList();
                  } else if (value == null) {
                    nonTerminal.value = new ArrayList();
                  } else {
                    nonTerminal.value = value;
                  }
                }
              } else {
                nullable = true;

                switch (nonTerminalType) {
                  case NodeTypes.fieldsOpt:
                    nonTerminal.value = Collections.EMPTY_LIST;
                  case NodeTypes.returnsOpt:
                  case NodeTypes.partSubTypeOpt:
                  case NodeTypes.packageDeclarationOpt:
                    break;
                  default:
                    nullable = false;
                }
              }

              // If we have nulled out a nonterminal and there are no more error nodes on stack
              if (nullable && realStackTop - handleSize < leftMostErrorNodeIndex) {
                leftMostErrorNodeIndex = 0;
              }
            }

            // Pop the stack
            for (int i = 0; i < handleSize; i++) {
              realStack.pop();
              realStackTop--;
            }

            // Push the newly created nonterminal back to the stack
            nonTerminal.parse_state =
                get_reduce(((Symbol) realStack.peek()).parse_state, nonTerminalType);
            realStack.push(nonTerminal);
            realStackTop++;

            try {
              isNonTerminal[realStackTop] = true;
            } catch (ArrayIndexOutOfBoundsException e) {
              isNonTerminal = enlargeStack(isNonTerminal);
              isNonTerminal[realStackTop] = true;
            }

            // The left most error node may have been absorbed into this new node
            // So we have may have to adjust leftMostErrorNodeIndex
            if (realStackTop < leftMostErrorNodeIndex) {
              leftMostErrorNodeIndex = realStackTop;
            }

            // If we have reduced to $START, we should break out of the action loop
            if (nonTerminalType == NodeTypes.$START) break;
          }
        } // Repeat until all the actions associated with this lookahead is performed

        // Advance look ahead
        stream.advanceLookAhead();
      } else {
        // Recover from the error
        int errorNodeIndex = recover(realStack, isNonTerminal);

        // Reset the scout stack to be in sync with the real stack
        scoutStack.reset(realStack);

        // Check if leftMostErrorNodeIndex needs to be updated
        if (errorNodeIndex != 0) {
          leftMostErrorNodeIndex =
              (leftMostErrorNodeIndex == 0
                  ? errorNodeIndex
                  : Math.min(leftMostErrorNodeIndex, errorNodeIndex));
        }

        // Update the realStackTop because the error recovery may modify realStack but they don't
        // have access to this variable
        realStackTop = realStack.size() - 1;

        // Phrase recovery and panic recovery and dump symbols from stack, it may have dumped an
        // error node
        if (realStackTop < leftMostErrorNodeIndex) {
          leftMostErrorNodeIndex = 0;
        }
      }
    } // Perform the parsing

    // Record syntax errors
    File file = (File) ((Symbol) realStack.peek()).value;
    if (problemRequestor instanceof AccumulatingSyntaxErrorRequestor) {
      List errors = ((AccumulatingSyntaxErrorRequestor) problemRequestor).getSyntaxErrors();
      errors.addAll(stream.getLexerErrors());
      file.setSyntaxErrors(errors);
    }

    // Record whitespaces
    Scanner lexer = stream.getLexer();
    if (lexer instanceof Lexer) {
      Lexer eglLexer = (Lexer) lexer;
      file.blockComments = eglLexer.blockComments;
      file.lineBreaks = eglLexer.lineBreaks;
      file.lineComments = eglLexer.lineComments;
    }

    // Return the value
    return (Symbol) realStack.peek();
  }