diff --git a/src/alu.c b/src/alu.c index a3e51d8..3d27658 100644 --- a/src/alu.c +++ b/src/alu.c @@ -727,9 +727,6 @@ static int parse_octal_or_unpseudo(void) // now GotByte = '&' if (input_read_scope_and_symbol_name(&scope)) // now GotByte = illegal char return 1; // error (no string given) - if ((GotByte == '?') && symbol_fix_dynamic_name()) - return 1; // error - get_symbol_value(scope, GlobalDynaBuf->size - 1, unpseudo_count); // -1 to not count terminator // } else if (...) { // // anonymous symbol @@ -994,10 +991,6 @@ static boolean expect_argument_or_monadic_operator(struct expression *expression // here we need to put '.' into GlobalDynaBuf even though we have already skipped it: if (input_read_scope_and_symbol_name_KLUGED(&scope) == 0) { // now GotByte = illegal char - if ((GotByte == '?') && symbol_fix_dynamic_name()) { - alu_state = STATE_ERROR; - break;//goto done; - } get_symbol_value(scope, GlobalDynaBuf->size - 1, 0); // -1 to not count terminator, no unpseudo goto now_expect_dyadic_op; // ok } @@ -1008,10 +1001,6 @@ static boolean expect_argument_or_monadic_operator(struct expression *expression case CHEAP_PREFIX: // cheap local symbol //printf("looking in cheap scope %d\n", section_now->cheap_scope); if (input_read_scope_and_symbol_name(&scope) == 0) { // now GotByte = illegal char - if ((GotByte == '?') && symbol_fix_dynamic_name()) { - alu_state = STATE_ERROR; - break;//goto done; - } get_symbol_value(scope, GlobalDynaBuf->size - 1, 0); // -1 to not count terminator, no unpseudo goto now_expect_dyadic_op; // ok } @@ -1052,8 +1041,6 @@ static boolean expect_argument_or_monadic_operator(struct expression *expression // however, apart from that check above, function calls have nothing to do with // parentheses: "sin(x+y)" gets parsed just like "not(x+y)". } else { - if (GotByte == '?') - symbol_fix_dynamic_name(); get_symbol_value(SCOPE_GLOBAL, GlobalDynaBuf->size - 1, 0); // no prefix, -1 to not count terminator, no unpseudo goto now_expect_dyadic_op; } diff --git a/src/global.c b/src/global.c index e62f811..bff9c2e 100644 --- a/src/global.c +++ b/src/global.c @@ -260,9 +260,6 @@ static void parse_symbol_definition(scope_t scope) { bits force_bit; - if (GotByte == '?') - symbol_fix_dynamic_name(); - force_bit = parser_get_force_bit(); // skips spaces after (yes, force bit is allowed for label definitions) if (GotByte == '=') { // explicit symbol definition (symbol = ) diff --git a/src/input.c b/src/input.c index ca3ff8a..d89b893 100644 --- a/src/input.c +++ b/src/input.c @@ -310,12 +310,122 @@ static char get_processed_from_file(void) } } +int subst_chars_left = 0; // number of bytes left in buffer +boolean subst_enabled = TRUE; // flag to disable substing (for example while substing, or when copying block to RAM) +const char *subst_read_ptr = NULL; // pointer to read bytes from +STRUCT_DYNABUF_REF(subst_buffer, 40); // buffer to hold substitution result + +// fetch next byte from buffer +static char subst_get_char(void) +{ + if (subst_chars_left-- == 0) + BUG("NoSubstLeft", 0); + return *(subst_read_ptr++); +} +// read symbol name from source and then setup substitution buffer with contents +// of symbol (string or integer). int is converted to decimal digits. +// example: +// if "somesymbol" is 42 and "endsymbol" is 47, +// basename?(somesymbol)middle?endsymbol=9 +// becomes +// basename42middle47=9 +// this would happen in two steps, +// the first result would be "42m" and the second would be "47=" +static void subst_substitute(void) // now GotByte = '?' +{ + scope_t tmp_scope; + struct symbol *tmp_symbol; + boolean parenthesized; + + if (GotByte != '?') + BUG("NotQuestionMark", GotByte); + + GetByte(); // eat '?' character + // handle parentheses + if (GotByte == '(') { + GetByte(); // eat '(' character + parenthesized = TRUE; + } else { + parenthesized = FALSE; + } + + // reading symbol name will clobber GlobalDynaBuf, so we'll use our own + // buffer as tmp storage: + dynabuf_clear(subst_buffer); + dynabuf_add_bytes(subst_buffer, GlobalDynaBuf->buffer, GlobalDynaBuf->size); + + // read symbol name into GlobalDynaBuf and get symbol ptr: + if (input_read_scope_and_symbol_name(&tmp_scope)) { + tmp_symbol = NULL; // remember failure (check after restoring GlobalDynaBuf) + } else { + tmp_symbol = symbol_find(tmp_scope); // reads name from GlobalDynaBuf + tmp_symbol->has_been_read = TRUE; + } + + // restore previous contents of GlobalDynaBuf: + dynabuf_clear(GlobalDynaBuf); + dynabuf_add_bytes(GlobalDynaBuf, subst_buffer->buffer, subst_buffer->size); + + // read symbol and put value into subst buffer + dynabuf_clear(subst_buffer); + if (tmp_symbol == NULL) { + goto fail; // input_read_scope_and_symbol_name will have thrown error + } + if (tmp_symbol->object.type == NULL) { + Throw_error("Substitution symbol is undefined."); + // FIXME - set type to undefined int, just to make sure later refs via type do not crash! + goto fail; + } else if (tmp_symbol->object.type == &type_number) { + if (tmp_symbol->object.u.number.ntype != NUMTYPE_INT) { + Throw_error("Substitution symbol is undefined or not integer."); + goto fail; + } + dynabuf_add_signed_long(subst_buffer, (long) tmp_symbol->object.u.number.val.intval); + } else if (tmp_symbol->object.type == &type_string) { + dynabuf_add_bytes(subst_buffer, tmp_symbol->object.u.string->payload, tmp_symbol->object.u.string->length); + } else { + Throw_error("Substitution symbol is neither number nor string."); + goto fail; + } + + // check for closing parenthesis + if (parenthesized) { + SKIPSPACE(); + if (GotByte == ')') { + GetByte(); // eat ')' + } else { + Throw_error("Substitution does not end with ')' character."); + goto fail; + } + } + // now append the delimiter character to the buffer, otherwise it would + // be lost because reading from the buffer keeps clobbering GotByte: + dynabuf_append(subst_buffer, GotByte); + subst_chars_left = subst_buffer->size; + subst_read_ptr = subst_buffer->buffer; + // FIXME: do we need checks for "buffer contains braces, colon, EOS, ..." + // or is it enough to add "user, do not try stupid things" to docs? + return; +fail: + dynabuf_clear(subst_buffer); + dynabuf_append(subst_buffer, GotByte); + subst_chars_left = subst_buffer->size; + subst_read_ptr = subst_buffer->buffer; +} + // This function delivers the next byte from the currently active byte source // in shortened high-level format. FIXME - use fn ptr? // When inside quotes, use input_quoted_to_dynabuf() instead! +// CAUTION, symbol substitutions cause this fn to be called recursively! char GetByte(void) { -// for (;;) { + // substitution buffer has priority, so if anything is in there, + // deliver that: + if (subst_chars_left) { + GotByte = subst_get_char(); + } else { + // otherwise get a byte from file/ram: + // If byte source is RAM, then no conversions are // necessary, because in RAM the source already has // high-level format @@ -333,14 +443,33 @@ char GetByte(void) default: BUG("IllegalInputSrc", current_input.srctype); } -// // if start-of-line was read, increment line counter and repeat -// if (GotByte != CHAR_SOL) -// return GotByte; -// current_input.location.line_number++; -// } + if (GotByte == CHAR_SOL) current_input.location.line_number++; - return GotByte; + } + // check for '?' substitutions + if ((GotByte == '?') && (subst_enabled) && (subst_chars_left == 0)) { + // start a new substitution +/* +the check for "subst_chars_left" is needed because characters from the buffer +are not allowed to start a new substitution, except for the very last char! +the last char wasn't part of the result, it was the original delimiter. example: + cd = "xyz" + !info ab?cd?ef +the first '?' causes a lookup for "cd" and will write the result "xyz" to the +buffer. then the second '?' (now GotByte because it was the keyword delimiter) +will be appended to the buffer because otherwise it would get lost during buffer +reads. +*/ + subst_enabled = FALSE; // no substitutions in substitutions! + subst_substitute(); // do the actual work + subst_enabled = TRUE; // back to previous state + // because the delimiter is put into the buffer, we know the + // buffer isn't empty, even if the substitution result was "". + // so this is allowed: + GotByte = subst_get_char(); + } + return GotByte; } // This function delivers the next byte from the currently active byte source @@ -350,6 +479,13 @@ static void get_quoted_byte(void) { int from_file; // must be an int to catch EOF + // substitution buffer has priority, so if anything is in there, + // deliver that: + if (subst_chars_left) { + GotByte = subst_get_char(); + return; + } + switch (current_input.srctype) { case INPUTSRC_RAM: // if byte source is RAM, then no conversion is necessary, @@ -518,6 +654,10 @@ static void block_to_dynabuf(void) { char byte; int depth = 1; // to find matching block end + boolean substflagbuf = subst_enabled; + + // make sure not to subst while reading block (should only be done while parsing) + subst_enabled = FALSE; // prepare global dynamic buffer dynabuf_clear(GlobalDynaBuf); @@ -543,6 +683,9 @@ static void block_to_dynabuf(void) break; } } while (depth); + + // restore subst state + subst_enabled = substflagbuf; } // Skip block (starting with next byte, so call directly after reading opening brace). // After calling this function, GotByte holds '}'. Unless EOF was found first, @@ -656,7 +799,7 @@ int parser_read_keyword(void) // Clear dynamic buffer, then append to it until an illegal (for a keyword) // character is read. Zero-terminate the string, then convert to lower case. // Return its length (without terminator). -// Zero lengths will produce a "missing string" error. +// Zero lengths will produce a "missing string" error. (FIXME - change error msg!) int parser_read_and_lower_keyword(void) { int length; diff --git a/src/symbol.c b/src/symbol.c index 0c965bc..c7d07b2 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -283,86 +283,3 @@ void symbol_fix_forward_anon_name(boolean increment) if (increment) counter_symbol->object.u.number.val.intval++; } - -// temporary buffer for base name while handling second part -STRUCT_DYNABUF_REF(dyn_sym_name, 40); - -// replace dynamic symbol name with its final version, for example: -// if "somesymbol" is 42 and "endsymbol" is 47, -// basename?(somesymbol)middle?endsymbol -// becomes -// basename42middle47 -// on entry: GlobalDynaBuf holds base name, GotByte holds '?' -// on exit: GlobalDynaBuf holds fixed name -// return whether there was an error. -int symbol_fix_dynamic_name(void) -{ - scope_t tmp_scope; - struct symbol *tmp_symbol; - boolean parenthesized; - - if (GotByte != '?') - BUG("NotQuestionMark", GotByte); - - // start with base name - // (reading the inner parts will clobber GlobalDynaBuf, so copy it now) - dynabuf_clear(dyn_sym_name); - dynabuf_add_string(dyn_sym_name, GLOBALDYNABUF_CURRENT); - - while (GotByte == '?') { - GetByte(); // eat '?' character - - if (GotByte == '(') { - GetByte(); // eat '(' character - parenthesized = TRUE; - } else { - parenthesized = FALSE; - } - - // read inner part - if (input_read_scope_and_symbol_name(&tmp_scope)) - return 1; - - tmp_symbol = symbol_find(tmp_scope); - tmp_symbol->has_been_read = TRUE; - if (tmp_symbol->object.type == &type_number) { - if (tmp_symbol->object.u.number.ntype != NUMTYPE_INT) { - Throw_error("Inner part of dynamic symbol name is undefined or not integer."); - return 1; - } - dynabuf_add_signed_long(dyn_sym_name, (long) tmp_symbol->object.u.number.val.intval); - } else if (tmp_symbol->object.type == &type_string) { - dynabuf_add_bytes(dyn_sym_name, tmp_symbol->object.u.string->payload, tmp_symbol->object.u.string->length); - } else { - Throw_error("Inner part of dynamic symbol name is neither number nor string."); - return 1; - } - - // check for closing parenthesis - if (parenthesized) { - SKIPSPACE(); - if (GotByte == ')') { - GetByte(); // eat ')' - } else { - Throw_error("Inner part of dynamic symbol name does not end with ')' character."); - return 1; - } - } - - // any more characters? - while (BYTE_CONTINUES_KEYWORD(GotByte)) { - DYNABUF_APPEND(dyn_sym_name, GotByte); - GetByte(); - } - } - dynabuf_append(dyn_sym_name, '\0'); - // FIXME: now the dynamically created symbol name should be checked for - // its validity, because a string subst could have put anything in there! - - // return basename and number in GlobalDynaBuf: - dynabuf_clear(GlobalDynaBuf); - dynabuf_add_string(GlobalDynaBuf, dyn_sym_name->buffer); - dynabuf_append(GlobalDynaBuf, '\0'); - //printf("created dynamic symbol name <%s>\n", GLOBALDYNABUF_CURRENT); - return 0; -} diff --git a/src/symbol.h b/src/symbol.h index 92c51d6..a5ec826 100644 --- a/src/symbol.h +++ b/src/symbol.h @@ -58,9 +58,5 @@ extern void symbols_vicelabels(FILE *fd); // so it references the *next* anonymous forward label definition. extern void symbol_fix_forward_anon_name(boolean increment); -// replace name of dynamic symbol with its final version. -// return whether there was an error. -extern int symbol_fix_dynamic_name(void); - #endif diff --git a/src/version.h b/src/version.h index c774940..16bbe42 100644 --- a/src/version.h +++ b/src/version.h @@ -9,7 +9,7 @@ #define RELEASE "0.97" // update before release FIXME #define CODENAME "Zem" // update before release -#define CHANGE_DATE "29 Jul" // update before release FIXME +#define CHANGE_DATE "30 Jul" // update before release FIXME #define CHANGE_YEAR "2024" // update before release //#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/" #define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME diff --git a/testing/errors/subst0.a b/testing/errors/subst0.a new file mode 100644 index 0000000..9522200 --- /dev/null +++ b/testing/errors/subst0.a @@ -0,0 +1,3 @@ + *=$1000 +base? ; -> "no string given" + nop diff --git a/testing/errors/subst1.a b/testing/errors/subst1.a index fae1e0e..5c4753c 100644 --- a/testing/errors/subst1.a +++ b/testing/errors/subst1.a @@ -1,3 +1,3 @@ *=$1000 ;sub= we do not define sub at all -base?sub nop ; -> "neither number nor string" +base?sub nop ; -> "Substitution symbol is undefined"