// Search for label. Create if nonexistant. If created, give it flags "Flags". // The label name must be held in GlobalDynaBuf. struct label_t *Label_find(zone_t zone, int flags) { struct node_ra_t *node; struct label_t *label; int node_created, force_bits = flags & MVALUE_FORCEBITS; node_created = Tree_hard_scan(&node, Label_forest, zone, TRUE); // if node has just been created, create label as well if (node_created) { // Create new label structure label = safe_malloc(sizeof(*label)); // Finish empty label item label->result.flags = flags; if (flags & MVALUE_IS_FP) label->result.val.fpval = 0; else label->result.val.intval = 0; label->usage = 0; // usage count label->pass = pass_count; node->body = label; } else { label = node->body; } // make sure the force bits don't clash if ((node_created == FALSE) && force_bits) if ((label->result.flags & MVALUE_FORCEBITS) != force_bits) Throw_error("Too late for postfix."); return label; }
// Conditional assembly ("!ifdef"). Has to be re-entrant. static enum eos_t PO_ifdef(void) {// Now GotByte = illegal char node_ra_t* node; label_t* label; zone_t zone; bool defined = FALSE; if(Input_read_zone_and_keyword(&zone) == 0) // skips spaces before return(SKIP_REMAINDER); Tree_hard_scan(&node, Label_forest, zone, FALSE); if(node) { label = (label_t*) node->body; // in first pass, count usage if(pass_count == 0) label->usage++; if(label->result.flags & MVALUE_DEFINED) defined = TRUE; } SKIPSPACE(); if(GotByte == CHAR_SOB) parse_block_else_block(defined); else { if(defined) return(PARSE_REMAINDER); return(SKIP_REMAINDER); } return(ENSURE_EOS); }
// This function is called from both macro definition and macro call. // Terminate macro name and copy from internal_name to GlobalDynaBuf // (because that's where Tree_hard_scan() looks for the search string). // Then try to find macro and return whether it was created. static int search_for_macro(struct rwnode **result, zone_t zone, int create) { DynaBuf_append(internal_name, '\0'); // terminate macro name // now internal_name = macro_title SPC argument_specifiers NUL DYNABUF_CLEAR(GlobalDynaBuf); DynaBuf_add_string(GlobalDynaBuf, internal_name->buffer); DynaBuf_append(GlobalDynaBuf, '\0'); return Tree_hard_scan(result, macro_forest, zone, create); }
// Parse macro call ("+MACROTITLE"). Has to be re-entrant. void Macro_parse_call(void) // Now GotByte = dot or first char of macro name { char local_gotbyte; struct symbol *symbol; struct section new_section, *outer_section; struct input new_input, *outer_input; struct macro *actual_macro; struct rwnode *macro_node, *symbol_node; zone_t macro_zone, symbol_zone; int arg_count = 0; // Enter deeper nesting level // Quit program if recursion too deep. if (--macro_recursions_left < 0) Throw_serious_error("Too deeply nested. Recursive macro calls?"); macro_zone = get_zone_and_title(); // now GotByte = first non-space after title // internal_name = MacroTitle ARG_SEPARATOR (grows to signature) // Accept n>=0 comma-separated arguments before CHAR_EOS. // Valid argument formats are: // EXPRESSION (everything that does NOT start with '~' // ~.LOCAL_LABEL_BY_REFERENCE // ~GLOBAL_LABEL_BY_REFERENCE // now GotByte = non-space if (GotByte != CHAR_EOS) { // any at all? do { // if arg table cannot take another element, enlarge if (argtable_size <= arg_count) enlarge_arg_table(); // Decide whether call-by-reference or call-by-value // In both cases, GlobalDynaBuf may be used. if (GotByte == REFERENCE_CHAR) { // read call-by-reference arg DynaBuf_append(internal_name, ARGTYPE_NUM_REF); GetByte(); // skip '~' character Input_read_zone_and_keyword(&symbol_zone); // GotByte = illegal char arg_table[arg_count].symbol = symbol_find(symbol_zone, 0); } else { // read call-by-value arg DynaBuf_append(internal_name, ARGTYPE_NUM_VAL); ALU_any_result(&(arg_table[arg_count].result)); } ++arg_count; } while (Input_accept_comma()); } // now arg_table contains the arguments // now GlobalDynaBuf = unused // check for "unknown macro" // Search for macro. Do not create if not found. search_for_macro(¯o_node, macro_zone, FALSE); if (macro_node == NULL) { Throw_error("Macro not defined (or wrong signature)."); Input_skip_remainder(); } else { // make macro_node point to the macro struct actual_macro = macro_node->body; local_gotbyte = GotByte; // CAUTION - ugly kluge // set up new input new_input.original_filename = actual_macro->def_filename; new_input.line_number = actual_macro->def_line_number; new_input.source_is_ram = TRUE; new_input.state = INPUTSTATE_NORMAL; // FIXME - fix others! new_input.src.ram_ptr = actual_macro->parameter_list; // remember old input outer_input = Input_now; // activate new input Input_now = &new_input; // remember old section outer_section = Section_now; // start new section (with new zone) // FALSE = title mustn't be freed Section_new_zone(&new_section, "Macro", actual_macro->original_name, FALSE); GetByte(); // fetch first byte of parameter list // assign arguments if (GotByte != CHAR_EOS) { // any at all? arg_count = 0; do { // Decide whether call-by-reference // or call-by-value // In both cases, GlobalDynaBuf may be used. if (GotByte == REFERENCE_CHAR) { // assign call-by-reference arg GetByte(); // skip '~' character Input_read_zone_and_keyword(&symbol_zone); if ((Tree_hard_scan(&symbol_node, symbols_forest, symbol_zone, TRUE) == FALSE) && (pass_count == 0)) Throw_error("Macro parameter twice."); symbol_node->body = arg_table[arg_count].symbol; } else { // assign call-by-value arg Input_read_zone_and_keyword(&symbol_zone); symbol = symbol_find(symbol_zone, 0); // FIXME - add a possibility to symbol_find to make it possible to find out // whether symbol was just created. Then check for the same error message here // as above ("Macro parameter twice."). symbol->result = arg_table[arg_count].result; } ++arg_count; } while (Input_accept_comma()); } // and now, finally, parse the actual macro body Input_now->state = INPUTSTATE_NORMAL; // FIXME - fix others! // maybe call parse_ram_block(actual_macro->def_line_number, actual_macro->body) Input_now->src.ram_ptr = actual_macro->body; Parse_until_eob_or_eof(); if (GotByte != CHAR_EOB) Bug_found("IllegalBlockTerminator", GotByte); // end section (free title memory, if needed) Section_finalize(&new_section); // restore previous section Section_now = outer_section; // restore previous input: Input_now = outer_input; // restore old Gotbyte context GotByte = local_gotbyte; // CAUTION - ugly kluge Input_ensure_EOS(); } ++macro_recursions_left; // leave this nesting level }