/* l2xistmt.c LTX2X interpreter parsing routines for statements */ /* Written by: Peter Wilson, CUA pwilson@cme.nist.gov */ /* This code is partly based on algorithms presented by Ronald Mak in */ /* "Writing Compilers & Interpreters", John Wiley & Sons, 1991 */ #include #include "l2xicmon.h" #include "l2xierr.h" #include "l2xiscan.h" #include "l2xisymt.h" #include "l2xiprse.h" #include "l2xiexec.h" #include "l2xiidbg.h" #ifndef l2xicpr_h #include "l2xicpr.h" /* extern token code lists */ #endif /* EXTERNALS */ extern TOKEN_CODE token; extern char token_string[]; extern char word_string[]; extern LITERAL literal; extern SYMTAB_NODE_PTR symtab_display[]; extern int level; extern ICT *code_bufferp; extern SYMTAB_NODE_PTR true_idp, false_idp, unknown_idp; /* FORWARDS */ TOKEN_CODE case_branch(); /***************************************************************************/ /* statement() Call routines to process a statement, depending on the */ /* the first token of the statement. */ /* at entry, token is one of statement_start_list */ /* at exit, token is the closing (e.g., ; or END_ ) */ statement() { entry_debug("statement"); if (token != BEGIN) crunch_statement_marker(); /* call the proper routine from the first token */ switch (token) { case IDENTIFIER: { SYMTAB_NODE_PTR idp; /* assignment or procedure call ? */ search_and_find_all_symtab(idp); if (idp->defn.key == PROC_DEFN || idp->defn.key == FUNC_DEFN) { crunch_symtab_node_ptr(idp); get_token(); routine_call(idp, TRUE); } else assignment_statement(idp); break; } case REPEAT: { grepeat_statement(); break; } case IF: { if_statement(); break; } case CASE: { case_statement(); break; } case BEGIN: { compound_statement(); break; } case XRETURN: { return_statement(); break; } case XSKIP: { skip_statement(); break; } case XESCAPE: { escape_statement(); break; } } /* end switch */ /* sync. Only a semicolon, END, ELSE or UNTIL can follow a statement */ /* check for mssing semicolon */ synchronize(statement_end_list, statement_start_list, NULL); if (token_in(statement_start_list)) error(MISSING_SEMICOLON); exit_debug("statement"); return; } /* end statement */ /***************************************************************************/ TOKEN_CODE end_funproc_list[] = {XEND_FUNCTION, XEND_PROCEDURE, 0}; /***************************************************************************/ /* return_statement() Process EXPRESS return statement */ /* RETURN [ '(' ')' ] ';' */ /* at entry, token is RETURN */ /* at exit, token is ; or END_FUNPROC */ return_statement() { entry_debug("return_statement"); get_token(); switch (token) { case SEMICOLON : { exit_debug("return_statement"); return; } case LPAREN : { expression(); exit_debug("return_statement"); return; } default : { synchronize(end_funproc_list, NULL, NULL); exit_debug("return_statement"); return; } } /* end switch */ } /* end RETURN_STATEMENT */ /***************************************************************************/ /***************************************************************************/ /* assignment_statement(var_idp) Process an assignment statement */ /* := */ assignment_statement(var_idp) SYMTAB_NODE_PTR var_idp; /* target variable id */ { TYPE_STRUCT_PTR var_tp, expr_tp; /* types of var and expression */ entry_debug("assignment_statement"); var_tp = variable(var_idp, TARGET_USE); if_token_get_else_error(COLONEQUAL, MISSING_COLONEQUAL); expr_tp = expression(); if (!is_assign_type_compatible(var_tp, expr_tp)) { error(INCOMPATIBLE_ASSIGNMENT); } exit_debug("assignment_statement"); return; } /* end assignment_statement */ /***************************************************************************/ /***************************************************************************/ /* grepeat_statement() Process a REPEAT statement */ /* REPEAT [ ] [ ] [ ] ; */ /* END_REPEAT */ /* at entry: token is REPEAT */ /* at exit: token is after END_REPEAT; */ grepeat_statement() { TYPE_STRUCT_PTR expr_tp; ADDRESS loop_end_location; entry_debug("grepeat_statement"); get_token(); loop_end_location = crunch_address_marker(NULL); /* place holder */ /* controls */ if (token == IDENTIFIER) { /* increment control */ inc_control(); } while_control(); until_control(); if_token_get_else_error(SEMICOLON, MISSING_SEMICOLON); /* the list of statements */ do { statement(); while (token == SEMICOLON) get_token(); } while (token_in(statement_start_list)); /* if_token_get_else_error(SEMICOLON, MISSING_SEMICOLON); */ if_token_get_else_error(XEND_REPEAT, MISSING_END_REPEAT); /* finally set the address marker for after the END_REPEAT */ fixup_address_marker(loop_end_location); exit_debug("grepeat_statement"); return; } /* end GREPEAT_STATEMENT */ /***************************************************************************/ /***************************************************************************/ /* inc_control() Process an increment control */ /* var := TO [ BY ] */ /* at entry: token is var */ /* at exit: token is after */ inc_control() { SYMTAB_NODE_PTR by_np; SYMTAB_NODE_PTR for_idp; TYPE_STRUCT_PTR for_tp, expr_tp, by_tp; TOKEN_CODE save_tok; entry_debug("inc_control (l2xistmt.c)"); /* fake a FOR */ save_tok = token; change_crunched_token(FOR); crunch_extra_token(save_tok); search_and_find_all_symtab(for_idp); crunch_symtab_node_ptr(for_idp); if ((for_idp->level != level) || (for_idp->defn.key != VAR_DEFN)) { error(INVALID_INCREMENT_CONTROL); } for_tp = base_type(for_idp->typep); get_token(); if ((for_tp != integer_typep) ) { error(INCOMPATIBLE_TYPES); } if_token_get_else_error(COLONEQUAL, MISSING_COLONEQUAL); expr_tp = expression(); if (!is_assign_type_compatible(for_tp, expr_tp)) { error(INCOMPATIBLE_TYPES); } if (token == TO) { get_token(); } else { error(MISSING_TO); } expr_tp = expression(); if (!is_assign_type_compatible(for_tp, expr_tp)) { error(INCOMPATIBLE_TYPES); } if (token == XBY) { /* BY */ get_token(); expr_tp = expression(); if (!is_assign_type_compatible(for_tp, expr_tp)) { error(INCOMPATIBLE_TYPES); get_token(); } } else { /* fake a BY */ save_tok = token; change_crunched_token(XBY); crunch_extra_token(NUMBER_LITERAL); by_np = search_symtab("1", symtab_display[1]); if (by_np == NULL) { by_np = enter_symtab("1", symtab_display[1]); } by_tp = integer_typep; by_np->defn.info.constant.value.integer = 1; crunch_symtab_node_ptr(by_np); crunch_extra_token(save_tok); } exit_debug("inc_control"); } /* end INC_CONTROL */ /***************************************************************************/ /***************************************************************************/ /* while_control() Process a WHILE control */ /* WHILE */ /* at entry: token may be WHILE */ /* at exit: token is after */ while_control() { TYPE_STRUCT_PTR expr_tp; TOKEN_CODE save_tok; entry_debug("while_control (l2xistmt.c)"); if (token != WHILE) { /* fake a WHILE */ save_tok = token; change_crunched_token(WHILE); crunch_extra_token(IDENTIFIER); crunch_symtab_node_ptr(true_idp); crunch_extra_token(save_tok); exit_debug("while_control"); return; } get_token(); expr_tp = expression(); if (expr_tp != logical_typep) error(INCOMPATIBLE_TYPES); exit_debug("while_control"); return; } /* end WHILE_CONTROL */ /***************************************************************************/ /***************************************************************************/ /* until_control() Process an UNTIL control */ /* UNTIL */ /* at entry: token may be UNTIL */ /* at exit: token is after */ until_control() { TYPE_STRUCT_PTR expr_tp; TOKEN_CODE save_tok; entry_debug("until_control (l2xistmt.c)"); if (token != UNTIL) { /* fake an UNTIL */ save_tok = token; change_crunched_token(UNTIL); crunch_extra_token(IDENTIFIER); crunch_symtab_node_ptr(false_idp); crunch_extra_token(save_tok); exit_debug("until_control"); return; } get_token(); expr_tp = expression(); if (expr_tp != logical_typep) error(INCOMPATIBLE_TYPES); exit_debug("until_control"); return; } /* end UNTIL_CONTROL */ /***************************************************************************/ /***************************************************************************/ /* if_statement() Process an IF statement */ /* IF THEN END_IF or */ /* IF THEN ELSE END_IF */ if_statement() { TYPE_STRUCT_PTR expr_tp; ADDRESS if_end_location; ADDRESS false_location; entry_debug("if_statement"); get_token(); false_location = crunch_address_marker(NULL); expr_tp = expression(); if (expr_tp != logical_typep) error(INCOMPATIBLE_TYPES); if_token_get_else_error(THEN, MISSING_THEN); statements(); fixup_address_marker(false_location); /* the ELSE branch */ if (token == ELSE) { get_token(); if_end_location = crunch_address_marker(NULL); statements(); fixup_address_marker(if_end_location); } if_token_get_else_error(XEND_IF, MISSING_END_IF); exit_debug("if_statement"); return; } /* end if_statement */ /***************************************************************************/ /***************************************************************************/ /* case_statement() Process a CASE statement */ /* CASE OF */ /* */ /* END_CASE */ /* CASE globals */ typedef struct case_item { int label_value; ADDRESS branch_location; struct case_item *next; } CASE_ITEM, *CASE_ITEM_PTR; CASE_ITEM_PTR case_item_head, case_item_tail; int case_label_count; case_statement() { BOOLEAN another_branch; TYPE_STRUCT_PTR expr_tp; TYPE_STRUCT_PTR case_label(); CASE_ITEM_PTR case_itemp, next_case_itemp; ICT *branch_table_location; ICT *case_end_chain = NULL; TOKEN_CODE save_tok; entry_debug("case_statement"); /* initialisations for the branch table */ get_token(); branch_table_location = crunch_address_marker(NULL); case_item_head = case_item_tail = NULL; case_label_count = 0; expr_tp = expression(); if (((expr_tp->form != SCALAR_FORM) && (expr_tp->form != ENUM_FORM) && (expr_tp->form != SUBRANGE_FORM)) || (expr_tp == real_typep)) error(INCOMPATIBLE_TYPES); /* sync. Should be OF */ synchronize(follow_expr_list, case_label_start_list, NULL); if_token_get_else_error(OF, MISSING_OF); /* loop to process CASE branches */ another_branch = token_in(case_label_start_list); while (another_branch) { if (token_in(case_label_start_list)) case_branch(expr_tp); /* link another address marker at the end of the branch to point to */ /* the end of the CASE statement */ case_end_chain = crunch_address_marker(case_end_chain); if (token == SEMICOLON) { get_token(); another_branch = TRUE; } else if (token_in(case_label_start_list)) { /* error(MISSING_SEMICOLON); */ another_branch = TRUE; } else another_branch = FALSE; } /* end while */ debug_print("case_statement: finished case branches\n"); /* emit the branch table */ fixup_address_marker(branch_table_location); crunch_integer(case_label_count); case_itemp = case_item_head; debug_print("case_statement: starting loop over case_itemp\n"); while (case_itemp != NULL) { crunch_integer(case_itemp->label_value); crunch_offset(case_itemp->branch_location); next_case_itemp = case_itemp->next; free(case_itemp); case_itemp = next_case_itemp; } debug_print("case_statement: finished loop over case_itemp\n"); if_token_get_else_error(XEND_CASE, MISSING_END_CASE); /* fix up the branch address markers */ while (case_end_chain != NULL) { sprintf(dbuffer, "case statement: case_end_chain = %d\n", case_end_chain); debug_print(dbuffer); case_end_chain = fixup_address_marker(case_end_chain); } exit_debug("case_statement"); return; } /* end case_statement */ /***************************************************************************/ /***************************************************************************/ /* case_branch(expr_tp) Process a CASE branch */ /* : */ TOKEN_CODE case_branch(expr_tp) TYPE_STRUCT_PTR expr_tp; /* type of CASE expression */ { BOOLEAN another_label; TYPE_STRUCT_PTR label_tp; CASE_ITEM_PTR case_itemp; CASE_ITEM_PTR old_case_item_tail = case_item_tail; TYPE_STRUCT_PTR case_label(); TOKEN_CODE save_tok; entry_debug("case_branch"); /* process */ do { label_tp = case_label(); if (expr_tp != label_tp && label_tp != any_typep) error(INCOMPATIBLE_TYPES); get_token(); if (token == COMMA) { get_token(); if (token_in(case_label_start_list)) another_label = TRUE; else { error(MISSING_CONSTANT); another_label = FALSE; } } else another_label = FALSE; } while (another_label); /* end do over case label list */ /* sync Sholud be : */ synchronize(follow_case_label_list, statement_start_list, NULL); if_token_get_else_error(COLON, MISSING_COLON); /* loop to fill in the branch location field of each item for this branch */ case_itemp = old_case_item_tail == NULL ? case_item_head : old_case_item_tail->next; while (case_itemp != NULL) { case_itemp->branch_location = code_bufferp; case_itemp = case_itemp->next; } statement(); /* make a `fake' compound statement to enable multiple */ /* statements for a particular case */ /* * save_tok = token; * change_crunched_token(BEGIN); * crunch_extra_token(save_tok); * statements(); * save_tok = token; * change_crunched_token(END); * crunch_extra_token(SEMICOLON); * crunch_extra_token(save_tok); */ exit_debug("case_branch"); return(save_tok); } /* end case_branch */ /***************************************************************************/ /***************************************************************************/ /* case_label() Process a case label */ /* return a pointer to its type structure */ TYPE_STRUCT_PTR case_label() { TOKEN_CODE sign = PLUS; /* unary + or - */ BOOLEAN saw_sign = FALSE; /* TRUE iff unary sign */ TYPE_STRUCT_PTR label_tp; CASE_ITEM_PTR case_itemp = alloc_struct(CASE_ITEM); entry_debug("case_label"); /* link in case item for this label */ if (case_item_head != NULL) { case_item_tail->next = case_itemp; case_item_tail = case_itemp; } else { case_item_head = case_item_tail = case_itemp; } case_itemp->next = NULL; ++case_label_count; /* unary + or - */ if ((token == PLUS) || (token == MINUS)) { sign = token; saw_sign = TRUE; get_token(); } /* numeric constant --- integer only */ if (token == NUMBER_LITERAL) { SYMTAB_NODE_PTR np = search_symtab(token_string, symtab_display[1]); if (np == NULL) np = enter_symtab(token_string, symtab_display[1]); crunch_symtab_node_ptr(np); if (literal.type == INTEGER_LIT) case_itemp->label_value = sign == PLUS ? literal.value.integer : -literal.value.integer; else error(INVALID_CONSTANT); exit_debug("case_label"); return(integer_typep); } else if (token == XOTHERWISE) { /* default --- any type */ case_itemp->label_value = XOTHERWISE; exit_debug("case_label"); return(any_typep); } /* id constant: int, char, or enum only */ else if (token == IDENTIFIER) { SYMTAB_NODE_PTR idp; search_all_symtab(idp); crunch_symtab_node_ptr(idp); if (idp == NULL) { error(UNDEFINED_IDENTIFIER); exit_debug("case_label"); return(&dummy_type); } else if (idp->defn.key != CONST_DEFN) { error(NOT_A_CONSTANT_IDENTIFIER); exit_debug("case_label"); return(&dummy_type); } else if (idp->typep == integer_typep) { case_itemp->label_value = sign = PLUS ? idp->defn.info.constant.value.integer : -idp->defn.info.constant.value.integer; exit_debug("case_label"); return(integer_typep); } else if (idp->typep->form == ENUM_FORM) { if (saw_sign) error(INVALID_CONSTANT); case_itemp->label_value = idp->defn.info.constant.value.integer; exit_debug("case_label"); return(idp->typep); } else { exit_debug("case_label"); return(&dummy_type); } } else { error(INVALID_CONSTANT); exit_debug("case_label"); return(&dummy_type); } } /* end case_label */ /***************************************************************************/ /***************************************************************************/ /* compound_statement() Process a compound statement */ /* BEGIN END */ compound_statement() { entry_debug("compound_statement"); get_token(); do { statement(); while (token == SEMICOLON) get_token(); if (token == END) break; /* sync. Should be at the start of next statement */ synchronize(statement_start_list, NULL, NULL); } while (token_in(statement_start_list)); if_token_get_else_error(END, MISSING_END); exit_debug("compound_statement"); return; } /* end compound_statement */ /***************************************************************************/ /***************************************************************************/ /* skip_statement() Process a SKIP statement */ /* SKIP; */ /* at entry: token is SKIP */ /* at exit: token is after SKIP */ skip_statement() { entry_debug("skip_statement (l2xistmt.c)"); get_token(); exit_debug("skip_statement"); } /* end SKIP_STATEMENT */ /***************************************************************************/ /***************************************************************************/ /* escape_statement() Process a ESCAPE statement */ /* ESCAPE; */ /* at entry: token is ESCAPE */ /* at exit: token is after ESCAPE */ escape_statement() { entry_debug("escape_statement (l2xistmt.c)"); get_token(); exit_debug("escape_statement"); } /* end ESCAPE_STATEMENT */ /***************************************************************************/