// Looping assembly ("!for"). Has to be re-entrant. static enum eos_t PO_for(void) {// Now GotByte = illegal char input_t loop_input, *outer_input; result_t loop_counter; intval_t maximum; char* loop_body;// pointer to loop's body block label_t* label; zone_t zone; int force_bit, loop_start;// line number of "!for" pseudo opcode if(Input_read_zone_and_keyword(&zone) == 0) // skips spaces before return(SKIP_REMAINDER); // Now GotByte = illegal char force_bit = Input_get_force_bit(); // skips spaces after label = Label_find(zone, force_bit); if(Input_accept_comma() == FALSE) { Throw_error(exception_syntax); return(SKIP_REMAINDER); } maximum = ALU_defined_int(); if(maximum < 0) Throw_serious_error("Loop count is negative."); if(GotByte != CHAR_SOB) Throw_serious_error(exception_no_left_brace); // remember line number of loop pseudo opcode loop_start = Input_now->line_number; // read loop body into DynaBuf and get copy loop_body = Input_skip_or_store_block(TRUE); // changes line number! // switching input makes us lose GotByte. But we know it's '}' anyway! // set up new input loop_input = *Input_now;// copy current input structure into new loop_input.source_is_ram = TRUE; // set new byte source // remember old input outer_input = Input_now; // activate new input // (not yet useable; pointer and line number are still missing) Input_now = &loop_input; // init counter loop_counter.flags = MVALUE_DEFINED | MVALUE_EXISTS; loop_counter.val.intval = 0; // if count == 0, skip loop if(maximum) { do { loop_counter.val.intval++;// increment counter Label_set_value(label, &loop_counter, TRUE); parse_ram_block(loop_start, loop_body); } while(loop_counter.val.intval < maximum); } else Label_set_value(label, &loop_counter, TRUE); // Free memory free(loop_body); // restore previous input: Input_now = outer_input; // GotByte of OuterInput would be '}' (if it would still exist) GetByte(); // fetch next byte return(ENSURE_EOS); }
// Parse or skip a block. Returns whether block's '}' terminator was missing. // Afterwards: GotByte = '}' static bool skip_or_parse_block(bool parse) { if(!parse) { Input_skip_or_store_block(FALSE); return(FALSE); } // if block was correctly terminated, return FALSE Parse_until_eob_or_eof(); // if block isn't correctly terminated, complain and exit if(GotByte != CHAR_EOB) Throw_serious_error(exception_no_right_brace); return(FALSE); }
// Looping assembly ("!do"). Has to be re-entrant. static enum eos_t PO_do(void) { // Now GotByte = illegal char loopcond_t condition1, condition2; input_t loop_input, *outer_input; char* loop_body; bool go_on; int loop_start;// line number of loop pseudo opcode // Read head condition to buffer SKIPSPACE(); store_condition(&condition1, CHAR_SOB); if(GotByte != CHAR_SOB) Throw_serious_error(exception_no_left_brace); // Remember line number of loop body, // then read block and get copy loop_start = Input_now->line_number; loop_body = Input_skip_or_store_block(TRUE); // changes line number! // now GotByte = '}' NEXTANDSKIPSPACE();// Now GotByte = first non-blank char after block // Read tail condition to buffer store_condition(&condition2, CHAR_EOS); // now GotByte = CHAR_EOS // set up new input loop_input = *Input_now;// copy current input structure into new loop_input.source_is_ram = TRUE; // set new byte source // remember old input outer_input = Input_now; // activate new input (not useable yet, as pointer and // line number are not yet set up) Input_now = &loop_input; do { // Check head condition go_on = check_condition(&condition1); if(go_on) { parse_ram_block(loop_start, loop_body); // Check tail condition go_on = check_condition(&condition2); } } while(go_on); // Free memory free(condition1.body); free(loop_body); free(condition2.body); // restore previous input: Input_now = outer_input; GotByte = CHAR_EOS; // CAUTION! Very ugly kluge. // But by switching input, we lost the outer input's GotByte. We know // it was CHAR_EOS. We could just call GetByte() to get real input, but // then the main loop could choke on unexpected bytes. So we pretend // that we got the outer input's GotByte value magically back. return(AT_EOS_ANYWAY); }
// Macro definition ("!macro"). static enum eos_t PO_macro(void) {// Now GotByte = illegal char // In first pass, parse. In all other passes, skip. if(pass_count == 0) Macro_parse_definition(); // now GotByte = '}' else { // skip until CHAR_SOB ('{') is found. // no need to check for end-of-statement, because such an // error would already have been detected in first pass. // for the same reason, there is no need to check for quotes. while(GotByte != CHAR_SOB) GetByte(); Input_skip_or_store_block(FALSE); // now GotByte = '}' } GetByte(); // Proceed with next character return(ENSURE_EOS); }
// This function is only called during the first pass, so there's no need to // check whether to skip the definition or not. // Return with GotByte = '}' void Macro_parse_definition(void) // Now GotByte = illegal char after "!macro" { char *formal_parameters; struct rwnode *macro_node; struct macro *new_macro; zone_t macro_zone = get_zone_and_title(); // now GotByte = first non-space after title DYNABUF_CLEAR(GlobalDynaBuf); // prepare to hold formal parameters // GlobalDynaBuf = "" (will hold formal parameter list) // user_macro_name = [LOCAL_PREFIX] MacroTitle NUL // internal_name = MacroTitle ARG_SEPARATOR (grows to signature) // Accept n>=0 comma-separated formal parameters before CHAR_SOB ('{'). // Valid argument formats are: // .LOCAL_LABEL_BY_VALUE // ~.LOCAL_LABEL_BY_REFERENCE // GLOBAL_LABEL_BY_VALUE global args are very uncommon, // ~GLOBAL_LABEL_BY_REFERENCE but not forbidden // now GotByte = non-space if (GotByte != CHAR_SOB) { // any at all? do { // handle call-by-reference character ('~') if (GotByte != REFERENCE_CHAR) { DynaBuf_append(internal_name, ARGTYPE_NUM_VAL); } else { DynaBuf_append(internal_name, ARGTYPE_NUM_REF); DynaBuf_append(GlobalDynaBuf, REFERENCE_CHAR); GetByte(); } // handle prefix for local symbols (LOCAL_PREFIX, normally '.') if (GotByte == LOCAL_PREFIX) { DynaBuf_append(GlobalDynaBuf, LOCAL_PREFIX); GetByte(); } // handle symbol name Input_append_keyword_to_global_dynabuf(); } while (pipe_comma()); // ensure CHAR_SOB ('{') if (GotByte != CHAR_SOB) Throw_serious_error(exception_no_left_brace); } DynaBuf_append(GlobalDynaBuf, CHAR_EOS); // terminate param list // now GlobalDynaBuf = comma-separated parameter list without spaces, // but terminated with CHAR_EOS. formal_parameters = DynaBuf_get_copy(GlobalDynaBuf); // now GlobalDynaBuf = unused // Reading the macro body would change the line number. To have correct // error messages, we're checking for "macro twice" *now*. // Search for macro. Create if not found. // But if found, complain (macro twice). if (search_for_macro(¯o_node, macro_zone, TRUE) == FALSE) report_redefinition(macro_node); // quits with serious error // Create new macro struct and set it up. Finally we'll read the body. new_macro = safe_malloc(sizeof(*new_macro)); new_macro->def_line_number = Input_now->line_number; new_macro->def_filename = get_string_copy(Input_now->original_filename); new_macro->original_name = get_string_copy(user_macro_name->buffer); new_macro->parameter_list = formal_parameters; new_macro->body = Input_skip_or_store_block(TRUE); // changes LineNumber macro_node->body = new_macro; // link macro struct to tree node // and that about sums it up }