// $Id: parser.cpp,v 1.28 2004/03/25 13:32:28 ericb Exp $ // // This software is subject to the terms of the IBM Jikes Compiler // License Agreement available at the following URL: // http://ibm.com/developerworks/opensource/jikes. // Copyright (C) 1996, 2004 IBM Corporation and others. All Rights Reserved. // You must accept the terms of that agreement to use this software. // #include "parser.h" #include "ast.h" #include "stream.h" #ifdef HAVE_JIKES_NAMESPACE namespace Jikes { // Open namespace Jikes block #endif void Parser::ReallocateStacks() { int old_stack_length = stack_length; stack_length += STACK_INCREMENT; assert(stack_length <= SHRT_MAX); int* old_stack = stack; stack = (int*) memcpy(new int[stack_length], stack, old_stack_length * sizeof(int)); delete [] old_stack; Location* old_location_stack = location_stack; location_stack = (Location*) memcpy(new Location[stack_length], location_stack, old_stack_length * sizeof(Location)); delete [] old_location_stack; Ast** old_parse_stack = parse_stack; parse_stack = (Ast**) memcpy(new Ast*[stack_length], parse_stack, old_stack_length * sizeof(Ast*)); delete [] old_parse_stack; // The first time through, we initialize parse_stack[0] to NULL. parse_stack[old_stack_length] = NULL; int* old_temp_stack = temp_stack; temp_stack = (int*) memcpy(new int[stack_length], temp_stack, old_stack_length * sizeof(int)); delete [] old_temp_stack; } AstListNode *Parser::AllocateListNode() { AstListNode *p; if (free_list_nodes) { p = free_list_nodes; free_list_nodes = free_list_nodes -> next; } else p = list_node_pool -> NewListNode(); return p; } void Parser::FreeCircularList(AstListNode* tail) { if (tail) { AstListNode* root = tail -> next; tail -> next = free_list_nodes; free_list_nodes = root; } } AstPackageDeclaration* Parser::PackageHeaderParse(LexStream* lex_stream_, StoragePool* ast_pool_) { AstPackageDeclaration* package_declaration = NULL; lex_stream_ -> Reset(); if (lex_stream_ -> PackageToken()) { ast_pool = ast_pool_; list_node_pool = new StoragePool(lex_stream_ -> NumTokens()); free_list_nodes = NULL; parse_package_header_only = true; // We are parsing the whole input and not just a segment. end_token = LexStream::LEX_INFINITY; lex_stream = lex_stream_; Ast *ast = HeaderParse(); parse_package_header_only = false; if (ast) { AstCompilationUnit *compilation_unit = ast -> CompilationUnitCast(); if (compilation_unit && ! compilation_unit -> BadCompilationUnitCast()) { package_declaration = compilation_unit -> package_declaration_opt; } } delete list_node_pool; } return package_declaration; } AstCompilationUnit* Parser::HeaderParse(LexStream* lex_stream_, StoragePool* ast_pool_) { lex_stream_ -> Reset(); body_pool = new StoragePool(lex_stream_ -> NumTokens()); ast_pool = (ast_pool_ ? ast_pool_ : body_pool); list_node_pool = new StoragePool(lex_stream_ -> NumTokens()); free_list_nodes = NULL; AstCompilationUnit *compilation_unit = NULL; parse_header_only = true; // We are parsing the whole input and not just a segment. end_token = LexStream::LEX_INFINITY; lex_stream = lex_stream_; Ast *ast = HeaderParse(); parse_header_only = false; if (ast) { compilation_unit = ast -> CompilationUnitCast(); if (compilation_unit && ! compilation_unit -> BadCompilationUnitCast()) { if (compilation_unit -> NumTypeDeclarations() == 0) compilation_unit -> MarkEmpty(); } } // // If we succesfully parsed a compilation unit, allocate a storage pool // for it. Subtract the amount of space that's already been allocated for // the headers from the estimate for the bodies. // if (compilation_unit) compilation_unit -> ast_pool = body_pool; else delete body_pool; delete list_node_pool; // free the pool of list nodes return compilation_unit; } Ast* Parser::HeaderParse() { TokenObject curtok = lex_stream -> Gettoken(); int act = START_STATE, current_kind = lex_stream -> Kind(curtok); /*****************************************************************/ /* Start parsing. */ /*****************************************************************/ state_stack_top = -1; // // Process a terminal // while (true) { if (++state_stack_top >= stack_length) ReallocateStacks(); stack[state_stack_top] = act; location_stack[state_stack_top] = Loc(curtok); act = t_action(act, current_kind, lex_stream); if (act <= NUM_RULES) state_stack_top--; // make reduction look like a shift-reduce else if (act > ERROR_ACTION) { curtok = lex_stream -> Gettoken(); current_kind = lex_stream -> Kind(curtok); act -= ERROR_ACTION; } else if (act < ACCEPT_ACTION) { curtok = lex_stream -> Gettoken(); current_kind = lex_stream -> Kind(curtok); continue; } else break; // // Process a non_terminal // do { state_stack_top -= (rhs[act] - 1); (this ->* rule_action[act])(); act = nt_action(stack[state_stack_top], lhs[act]); } while (act <= NUM_RULES); } /* process_terminal */ if (act == ERROR_ACTION) { // // If any error is found in a package declaration, do not try to // repair it. // if (! parse_package_header_only) RepairParse(curtok); if (parse_stack[0] && parse_stack[0] -> CompilationUnitCast()) ((AstCompilationUnit*) parse_stack[0]) -> MarkBad(); else parse_stack[0] = NULL; } return parse_stack[0]; } bool Parser::BodyParse(LexStream* lex_stream_, AstClassBody* class_body) { assert(class_body -> UnparsedClassBodyCast()); lex_stream = lex_stream_; ast_pool = class_body -> pool; body_pool = class_body -> pool; list_node_pool = new StoragePool(lex_stream_ -> NumTokens()); free_list_nodes = NULL; bool success = Body(class_body); delete list_node_pool; // free the pool of list nodes class_body -> MarkParsed(); return success; } bool Parser::Body(AstClassBody* class_body) { bool errors_detected = false; unsigned i; for (i = 0; i < class_body -> NumConstructors(); i++) { AstConstructorDeclaration* constructor_decl = class_body -> Constructor(i); if (constructor_decl -> constructor_symbol) { AstMethodBody* block = constructor_decl -> constructor_body; end_token = block -> right_brace_token; // last token in the body AstMethodBody* new_body = ParseSegment(block -> left_brace_token); if (! new_body) errors_detected = true; else constructor_decl -> constructor_body = new_body; } } for (i = 0; i < class_body -> NumMethods(); i++) { AstMethodDeclaration* method_decl = class_body -> Method(i); if (method_decl -> method_symbol && method_decl -> method_body_opt) { AstMethodBody* block = method_decl -> method_body_opt; end_token = block -> right_brace_token; AstMethodBody* new_block = ParseSegment(block -> left_brace_token); if (! new_block) // a bad block ? errors_detected = true; else method_decl -> method_body_opt = new_block; } } for (i = 0; i < class_body -> NumNestedClasses(); i++) errors_detected = errors_detected || ! Body(class_body -> NestedClass(i) -> class_body); for (i = 0; i < class_body -> NumNestedInterfaces(); i++) errors_detected = errors_detected || ! Body(class_body -> NestedInterface(i) -> class_body); return ! errors_detected; } bool Parser::InitializerParse(LexStream* stream, AstClassBody* class_body) { lex_stream = stream; ast_pool = class_body -> pool; body_pool = class_body -> pool; list_node_pool = new StoragePool(stream -> NumTokens()); free_list_nodes = NULL; bool success = Initializer(class_body); delete list_node_pool; // free the pool of list nodes return success; } bool Parser::Initializer(AstClassBody* class_body) { bool errors_detected = false; unsigned i; for (i = 0; i < class_body -> NumStaticInitializers(); i++) { AstMethodBody* block = class_body -> StaticInitializer(i) -> block; end_token = block -> right_brace_token; // last token in the body class_body -> StaticInitializer(i) -> block = ParseSegment(block -> left_brace_token); if (! class_body -> StaticInitializer(i) -> block) { errors_detected = true; // Restore old empty block. class_body -> StaticInitializer(i) -> block = block; } } for (i = 0; i < class_body -> NumInstanceInitializers(); i++) { AstMethodBody* block = class_body -> InstanceInitializer(i) -> block; end_token = block -> right_brace_token; // last token in the body class_body -> InstanceInitializer(i) -> block = ParseSegment(block -> left_brace_token); if (! class_body -> InstanceInitializer(i) -> block) { errors_detected = true; // Restore old empty block. class_body -> InstanceInitializer(i) -> block = block; } } for (i = 0; i < class_body -> NumNestedClasses(); i++) errors_detected = errors_detected || ! Initializer(class_body -> NestedClass(i) -> class_body); for (i = 0; i < class_body -> NumNestedInterfaces(); i++) errors_detected = errors_detected || ! Initializer(class_body -> NestedInterface(i) -> class_body); return ! errors_detected; } AstMethodBody* Parser::ParseSegment(TokenObject start_token) { // // The next call to Gettoken will return the start_token. // However, we initialize curtok to start_token in order // to obtain a valid location for the BodyMarker. // lex_stream -> Reset(start_token); TokenObject curtok = start_token; // get the location of the start_token int act = START_STATE, current_kind = TK_BodyMarker; /*****************************************************************/ /* Start parsing. */ /*****************************************************************/ state_stack_top = -1; // // Process a terminal // while (true) { if (++state_stack_top >= stack_length) ReallocateStacks(); stack[state_stack_top] = act; location_stack[state_stack_top] = Loc(curtok); act = t_action(act, current_kind, lex_stream); if (act <= NUM_RULES) state_stack_top--; // make reduction look like a shift-reduce else if (act > ERROR_ACTION) { curtok = lex_stream -> Gettoken(end_token); current_kind = lex_stream -> Kind(curtok); act -= ERROR_ACTION; } else if (act < ACCEPT_ACTION) { curtok = lex_stream -> Gettoken(end_token); current_kind = lex_stream -> Kind(curtok); continue; } else break; // // Process a nonterminal // do { state_stack_top -= (rhs[act] - 1); (this ->* rule_action[act])(); act = nt_action(stack[state_stack_top], lhs[act]); } while (act <= NUM_RULES); } /* process_terminal */ if (act == ERROR_ACTION) { RepairParse(curtok); parse_stack[0] = NULL; } return DYNAMIC_CAST (parse_stack[0]); } void Parser::RepairParse(TokenObject curtok) { // // Repair an error // while (true) { // // Pop state stack up to first state that had an // action on the error token. The net effect is to // remove all default reductions on an empty rule // caused by the error token. // int k; for (k = state_stack_top - 1; k >= 0 && location_stack[k] == Loc(curtok); k--); k++; state_stack_top = k; ErrorRepair(curtok); curtok = lex_stream -> Gettoken(end_token); int act = stack[state_stack_top--]; int current_kind = lex_stream -> Kind(curtok); // // Process a terminal // while (true) { if (++state_stack_top >= stack_length) ReallocateStacks(); stack[state_stack_top] = act; location_stack[state_stack_top] = Loc(curtok); act = t_action(act, current_kind, lex_stream); if (act <= NUM_RULES) state_stack_top--; // make reduction look like a shift-reduce else if (act > ERROR_ACTION) { curtok = lex_stream -> Gettoken(end_token); current_kind = lex_stream -> Kind(curtok); act -= ERROR_ACTION; } else if (act < ACCEPT_ACTION) { curtok = lex_stream -> Gettoken(end_token); current_kind = lex_stream -> Kind(curtok); continue; } // // Since the null string is a valid Java program, this function // will always succeed even if it has to delete the whole input. // else if (act == ACCEPT_ACTION) return; else // loop around and keep trying until something is accepted. break; // // Process a nonterminal // do { state_stack_top -= (rhs[act] - 1); (this ->* rule_action[act])(); act = nt_action(stack[state_stack_top], lhs[act]); } while (act <= NUM_RULES); } /* process_terminal */ } } // // This routine is invoked when an error is encountered in a "repair" // parser. It will place the parser back into a working configuration // by adjusting the state stack, the current token and the buffer. // void Parser::ErrorRepair(TokenObject error_token) { SecondaryRepairInfo repair; repair.code = ERROR_CODE; do { repair.distance = 0; repair.num_deletions = state_stack_top + BUFF_UBOUND; buffer[1] = error_token; buffer[0] = lex_stream -> Previous(buffer[1]); for (int k = 2; k < BUFF_SIZE; k++) buffer[k] = lex_stream -> Next(buffer[k - 1]); int last_index; for (last_index = MAX_DISTANCE - 1; last_index >= 1 && lex_stream -> Kind(buffer[last_index]) == EOFT_SYMBOL; last_index--); last_index++; repair = ErrorSurgery(stack, state_stack_top, last_index, repair); error_token = buffer[MAX_DISTANCE - MIN_DISTANCE + 2]; } while (repair.code == 0); state_stack_top = repair.stack_position; lex_stream -> Reset(buffer[repair.buffer_position]); } // // Keep cutting "stuff" out around the error until a stable configuration // is found. // SecondaryRepairInfo Parser::ErrorSurgery (int stck[], int stack_top, int last_index, SecondaryRepairInfo repair) { int stack_deletions = 0; Location previous_loc = Loc(buffer[2]); for (int top = stack_top; top >= 0 && repair.num_deletions >= stack_deletions; top--) { if (location_stack[top] < previous_loc) stack_deletions++; previous_loc = location_stack[top]; for (int i = 1; i <= last_index && repair.num_deletions >= (stack_deletions + i - 1); i++) { int j = ParseCheck(stck, top, lex_stream -> Kind(buffer[i]), i + 1); if ((j - i + 1) > MIN_DISTANCE) { int k = stack_deletions + i - 1; if ((k < repair.num_deletions) || (j - k) > (repair.distance - repair.num_deletions)) { repair.code = DELETION_CODE; repair.distance = j; repair.stack_position = top; repair.buffer_position = i; repair.num_deletions = k; } } } } return repair; } /*****************************************************************/ /* Try to parse until first_token and all tokens in BUFFER have */ /* been consumed, or an error is encountered. Return the number */ /* of tokens that were expended before the parse blocked. */ /*****************************************************************/ int Parser::ParseCheck(int stck[], int stack_top, int first_token, int buffer_position) { int max_pos, indx, ct, act, lhs_symbol; /*****************************************************************/ /* Initialize pointer for temp_stack and initialize maximum */ /* position of state stack that is still useful. */ /*****************************************************************/ act = stck[stack_top]; if (first_token > NT_OFFSET) { temp_stack_top = stack_top; max_pos = stack_top; indx = buffer_position; ct = lex_stream -> Kind(buffer[indx]); lex_stream -> Reset(lex_stream -> Next(buffer[indx])); lhs_symbol = first_token - NT_OFFSET; act = nt_action(act, lhs_symbol); if (act <= NUM_RULES) goto process_non_terminal; } else { temp_stack_top = stack_top - 1; max_pos = temp_stack_top; indx = buffer_position - 1; ct = first_token; lex_stream -> Reset(buffer[buffer_position]); } process_terminal: while (true) { if (++temp_stack_top >= stack_length) /* Stack overflow!!! */ return indx; temp_stack[temp_stack_top] = act; act = t_action(act, ct, lex_stream); if (act <= NUM_RULES) /* reduce action */ temp_stack_top--; else if (act < ACCEPT_ACTION || /* shift action */ act > ERROR_ACTION) /*shift-reduce action*/ { if (indx == MAX_DISTANCE) return indx; indx++; ct = lex_stream -> Kind(buffer[indx]); lex_stream -> Reset(lex_stream -> Next(buffer[indx])); if (act > ERROR_ACTION) act -= ERROR_ACTION; else goto process_terminal; } else if (act == ACCEPT_ACTION) /* accept action */ return MAX_DISTANCE; else return indx; /* error action */ process_non_terminal: do { temp_stack_top -= (rhs[act]-1); lhs_symbol = lhs[act]; act = (temp_stack_top > max_pos ? temp_stack[temp_stack_top] : stck[temp_stack_top]); act = nt_action(act, lhs_symbol); } while (act <= NUM_RULES); max_pos = Min(max_pos, temp_stack_top); } // process_terminal; return 0; } #ifdef HAVE_JIKES_NAMESPACE } // Close namespace Jikes block #endif