// 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); }
// Try to read a condition into DynaBuf and store copy pointer in // given loopcond_t structure. // If no condition given, NULL is written to structure. // Call with GotByte = first interesting character static void store_condition(loopcond_t* condition, char terminator) { void* node_body; // write line number condition->line = Input_now->line_number; // Check for empty condition if(GotByte == terminator) { // Write NULL condition, then return condition->body = NULL; return; } // Seems as if there really *is* a condition. // Read UNTIL/WHILE keyword if(Input_read_and_lower_keyword()) { // Search for new tree item if(!Tree_easy_scan(condkey_tree, &node_body, GlobalDynaBuf)) { Throw_error(exception_syntax); condition->body = NULL; return; } condition->type = (enum cond_key_t) node_body; // Write given condition into buffer SKIPSPACE(); DYNABUF_CLEAR(GlobalDynaBuf); Input_until_terminator(terminator); DynaBuf_append(GlobalDynaBuf, CHAR_EOS);// ensure terminator condition->body = DynaBuf_get_copy(GlobalDynaBuf); } return; }
// Skip or store block (starting with next byte, so call directly after // reading opening brace). // If "Store" is TRUE, the block is read into GlobalDynaBuf, then a copy // is made and a pointer to that is returned. // If "Store" is FALSE, NULL is returned. // After calling this function, GotByte holds '}'. Unless EOF was found first, // but then a serious error would have been thrown. char* Input_skip_or_store_block(bool store) { char byte; int depth = 1; // to find matching block end // prepare global dynamic buffer DYNABUF_CLEAR(GlobalDynaBuf); do { byte = GetByte(); // if wanted, store if(store) DYNABUF_APPEND(GlobalDynaBuf, byte); // now check for some special characters switch(byte) { case CHAR_EOF: // End-of-file in block? Sorry, no way. Throw_serious_error(exception_no_right_brace); case '"': // Quotes? Okay, read quoted stuff. case '\'': do { GetQuotedByte(); // if wanted, store if(store) DYNABUF_APPEND(GlobalDynaBuf, GotByte); } while((GotByte != CHAR_EOS) && (GotByte != byte)); break; case CHAR_SOB: depth++; break; case CHAR_EOB: depth--; break; } } while(depth); // in case of skip, return now if(!store) return(NULL); // otherwise, prepare to return copy of block // add EOF, just to make sure block is never read too far DynaBuf_append(GlobalDynaBuf, CHAR_EOS); DynaBuf_append(GlobalDynaBuf, CHAR_EOF); // return pointer to copy return(DynaBuf_get_copy(GlobalDynaBuf)); }
// Clear dynamic buffer, then append to it until an illegal (for a keyword) // character is read. Zero-terminate the string. Return its length (without // terminator). // Zero lengths will produce a "missing string" error. int Input_read_keyword(void) { int length; DYNABUF_CLEAR(GlobalDynaBuf); length = Input_append_keyword_to_global_dynabuf(); // add terminator to buffer (increments buffer's length counter) DynaBuf_append(GlobalDynaBuf, '\0'); return(length); }
// Read macro zone and title. Title is read to GlobalDynaBuf and then copied // over to internal_name DynaBuf, where ARG_SEPARATOR is added. // In user_macro_name DynaBuf, the original name is reconstructed (even with // LOCAL_PREFIX) so a copy can be linked to the resulting macro struct. static zone_t get_zone_and_title(void) { zone_t macro_zone; Input_read_zone_and_keyword(¯o_zone); // skips spaces before // now GotByte = illegal character after title // copy macro title to private dynabuf and add separator character DYNABUF_CLEAR(user_macro_name); DYNABUF_CLEAR(internal_name); if (macro_zone != ZONE_GLOBAL) DynaBuf_append(user_macro_name, LOCAL_PREFIX); DynaBuf_add_string(user_macro_name, GLOBALDYNABUF_CURRENT); DynaBuf_add_string(internal_name, GLOBALDYNABUF_CURRENT); DynaBuf_append(user_macro_name, '\0'); DynaBuf_append(internal_name, ARG_SEPARATOR); SKIPSPACE(); // done here once so it's not necessary at two callers return macro_zone; }
// Try to read a file name. If "allow_library" is TRUE, library access by using // <...> quoting is possible as well. The file name given in the assembler // source code is converted from UNIX style to platform style. // Returns whether error occurred (TRUE on error). Filename in GlobalDynaBuf. // Errors are handled and reported, but caller should call // Input_skip_remainder() then. bool Input_read_filename(bool allow_library) { char *lib_prefix, end_quote; DYNABUF_CLEAR(GlobalDynaBuf); SKIPSPACE(); // check for library access if(GotByte == '<') { // if library access forbidden, complain if(allow_library == FALSE) { Throw_error("Writing to library not supported."); return(TRUE); } // read platform's lib prefix lib_prefix = PLATFORM_LIBPREFIX; #ifndef NO_NEED_FOR_ENV_VAR // if lib prefix not set, complain if(lib_prefix == NULL) { Throw_error("\"ACME\" environment variable not found."); return(TRUE); } #endif // copy lib path and set quoting char DynaBuf_add_string(GlobalDynaBuf, lib_prefix); end_quote = '>'; } else { if(GotByte == '"') end_quote = '"'; else { Throw_error("File name quotes not found (\"\" or <>)."); return(TRUE); } } // read first character, complain if closing quote if(GetQuotedByte() == end_quote) { Throw_error("No file name given."); return(TRUE); } // read characters until closing quote (or EOS) is reached // append platform-converted characters to current string while((GotByte != CHAR_EOS) && (GotByte != end_quote)) { DYNABUF_APPEND(GlobalDynaBuf, PLATFORM_CONVERTPATHCHAR(GotByte)); GetQuotedByte(); } // on error, return if(GotByte == CHAR_EOS) return(TRUE); GetByte(); // fetch next to forget closing quote // terminate string DynaBuf_append(GlobalDynaBuf, '\0'); // add terminator return(FALSE); // no error }
// fix name of anonymous forward label (held in DynaBuf, NOT TERMINATED!) so it // references the *next* anonymous forward label definition. The tricky bit is, // each name length would need its own counter. But hey, ACME's real quick in // finding labels, so I'll just abuse the label system to store those counters. struct label_t *Label_fix_forward_name(void) { struct label_t *counter_label; unsigned long number; // terminate name, find "counter" label and read value DynaBuf_append(GlobalDynaBuf, '\0'); counter_label = Label_find(Section_now->zone, 0); // make sure it gets reset to zero in each new pass if (counter_label->pass != pass_count) { counter_label->pass = pass_count; counter_label->result.val.intval = 0; } number = (unsigned long) counter_label->result.val.intval; // now append to the name to make it unique GlobalDynaBuf->size--; // forget terminator, we want to append do { DYNABUF_APPEND(GlobalDynaBuf, 'a' + (number & 15)); number >>= 4; } while (number); DynaBuf_append(GlobalDynaBuf, '\0'); return counter_label; }
// show user-defined message static enum eos_t throw_string(const char prefix[], void (*fn)(const char*)) { result_t result; DYNABUF_CLEAR(user_message); DynaBuf_add_string(user_message, prefix); do { if(GotByte == '"') { // parse string GetQuotedByte(); // read initial character // send characters until closing quote is reached while(GotByte && (GotByte != '"')) { DYNABUF_APPEND(user_message, GotByte); GetQuotedByte(); } if(GotByte == CHAR_EOS) return(AT_EOS_ANYWAY); // after closing quote, proceed with next char GetByte(); } else { // parse value ALU_any_result(&result); if(result.flags & MVALUE_IS_FP) { // floating point if(result.flags & MVALUE_DEFINED) DynaBuf_add_double( user_message, result.val.fpval); else DynaBuf_add_string( user_message, "<UNDEFINED FLOAT>"); } else { // integer if(result.flags & MVALUE_DEFINED) DynaBuf_add_signed_long( user_message, result.val.intval); else DynaBuf_add_string( user_message, "<UNDEFINED INT>"); } } } while(Input_accept_comma()); DynaBuf_append(user_message, '\0'); fn(user_message->buffer); return(ENSURE_EOS); }
// copy string to DynaBuf static void keyword_to_dynabuf(const char keyword[]) { DYNABUF_CLEAR(GlobalDynaBuf); DynaBuf_add_string(GlobalDynaBuf, keyword); DynaBuf_append(GlobalDynaBuf, '\0'); DynaBuf_to_lower(GlobalDynaBuf, GlobalDynaBuf);// convert to lower case }
// 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 }
// 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 }