From 4643e841f90c4c8af7ba9faf9608250bd1fac5b9 Mon Sep 17 00:00:00 2001 From: marcobaye Date: Sat, 13 Jun 2020 23:11:10 +0000 Subject: [PATCH] symbol assignment refactoring seems to be finished now git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@228 4df02467-bbd4-4a76-a152-e7ce94205b78 --- src/alu.c | 122 ++++++++++++++++++++++++++++++--- src/alu.h | 1 + src/flow.c | 15 ++++- src/global.c | 26 ++++--- src/global.h | 5 +- src/pseudoopcodes.c | 9 ++- src/symbol.c | 160 ++++++++++++++++---------------------------- src/symbol.h | 15 +++-- src/version.h | 2 +- 9 files changed, 224 insertions(+), 131 deletions(-) diff --git a/src/alu.c b/src/alu.c index 9678d9d..21b5db3 100644 --- a/src/alu.c +++ b/src/alu.c @@ -1161,6 +1161,112 @@ static boolean object_return_true(struct object *self) return TRUE; } +// int/float: +// helper function to check two values for equality +// in case of undefined value(s), "fallback" is returned +static inline boolean num_different(struct object *self, struct object *other, boolean fallback) +{ + if ((self->u.number.flags & NUMBER_IS_DEFINED) == 0) + return fallback; + + if ((other->u.number.flags & NUMBER_IS_DEFINED) == 0) + return fallback; + + if (self->type == &type_int) { + if (other->type == &type_int) + return self->u.number.val.intval != other->u.number.val.intval; + else + return self->u.number.val.intval != other->u.number.val.fpval; + } else { + if (other->type == &type_int) + return self->u.number.val.fpval != other->u.number.val.intval; + else + return self->u.number.val.fpval != other->u.number.val.fpval; + } +} + +// int/float: +// assign new value +static void number_assign(struct object *self, struct object *new_value, boolean accept_change) +{ + int own_flags = self->u.number.flags, + other_flags = new_value->u.number.flags; + // local copies of the flags are used because + // self->...flags might get overwritten when copying struct over, and + // new_value-> is const so shouldn't be touched. + + // accepting a different value is easily done by just forgetting the old one: + if (accept_change) { + own_flags &= ~(NUMBER_IS_DEFINED | NUMBER_FITS_BYTE); + } + + // copy struct over? + if (!(own_flags & NUMBER_IS_DEFINED)) { + // symbol is undefined OR redefinitions are allowed, so use new value: + *self = *new_value; // copy type and flags/value/addr_refs + // flags will be fixed, see below + } else { + // symbol is already defined, so compare new and old values + // if values differ, complain and return + if (num_different(self, new_value, FALSE)) { + Throw_error(exception_symbol_defined); + return; + } + // values are the same, so only fiddle with flags + } + + // if symbol has no force bits of its own, use the ones from new value: + if ((own_flags & NUMBER_FORCEBITS) == 0) + own_flags = (own_flags & ~NUMBER_FORCEBITS) | (other_flags & NUMBER_FORCEBITS); + + // tainted symbols without "fits byte" flag must never get that flag + if ((own_flags & (NUMBER_EVER_UNDEFINED | NUMBER_FITS_BYTE)) == NUMBER_EVER_UNDEFINED) + other_flags &= ~NUMBER_FITS_BYTE; + // now OR together "fits byte", "defined" and "tainted" + // (any hypothetical problems about "new value is later found out to + // _not_ fit byte" would be detected when assigning a different value + // raises an error in a later pass) + own_flags |= other_flags & (NUMBER_FITS_BYTE | NUMBER_IS_DEFINED | NUMBER_EVER_UNDEFINED); + + self->u.number.flags = own_flags; +} + + +// list: +// assign new value +static void list_assign(struct object *self, struct object *new_value, boolean accept_change) +{ + if (!accept_change) { + if (0/* TODO - compare old and new lists? */) { + Throw_error(exception_symbol_defined); + return; + } + } + *self = *new_value; +} + +// string: +// helper function, returns whether equal +static boolean string_equal(struct string *arthur, struct string *ford) +{ + if (arthur->length != ford->length) + return FALSE; + + return !memcmp(arthur->payload, ford->payload, arthur->length); +} +// string: +// assign new value +static void string_assign(struct object *self, struct object *new_value, boolean accept_change) +{ + if (!accept_change) { + if (!string_equal(self->u.string, new_value->u.string)) { + Throw_error(exception_symbol_defined); + return; + } + } + *self = *new_value; +} + // this gets called for LSR, AND, OR, XOR with float args // FIXME - warning is never seen if arguments are undefined in first pass! static void warn_float_to_int(void) @@ -1762,11 +1868,7 @@ static void string_handle_dyadic_operator(struct object *self, struct op *op, st break; // complain arthur = self->u.string; ford = other->u.string; - if (arthur->length != ford->length) { - int_create_byte(self, FALSE); - } else { - int_create_byte(self, !memcmp(arthur->payload, ford->payload, arthur->length)); - } + int_create_byte(self, string_equal(arthur, ford)); arthur->refs--; // FIXME - call a function for this... ford->refs--; // FIXME - call a function for this... return; @@ -1776,11 +1878,7 @@ static void string_handle_dyadic_operator(struct object *self, struct op *op, st break; // complain arthur = self->u.string; ford = other->u.string; - if (arthur->length != ford->length) { - int_create_byte(self, TRUE); - } else { - int_create_byte(self, !!memcmp(arthur->payload, ford->payload, arthur->length)); - } + int_create_byte(self, !string_equal(arthur, ford)); arthur->refs--; // FIXME - call a function for this... ford->refs--; // FIXME - call a function for this... return; @@ -1902,6 +2000,7 @@ static void string_print(struct object *self, struct dynabuf *db) struct type type_int = { "integer", number_is_defined, + number_assign, int_handle_monadic_operator, int_handle_dyadic_operator, int_fix_result, @@ -1910,6 +2009,7 @@ struct type type_int = { struct type type_float = { "float", number_is_defined, + number_assign, float_handle_monadic_operator, float_handle_dyadic_operator, float_fix_result, @@ -1918,6 +2018,7 @@ struct type type_float = { struct type type_list = { "list", object_return_true, // lists are always considered to be defined (even though they can hold undefined numbers...) + list_assign, list_handle_monadic_operator, list_handle_dyadic_operator, object_no_op, // no need to fix list results @@ -1926,6 +2027,7 @@ struct type type_list = { struct type type_string = { "string", object_return_true, // strings are always defined + string_assign, string_handle_monadic_operator, string_handle_dyadic_operator, object_no_op, // no need to fix string results diff --git a/src/alu.h b/src/alu.h index dcaffc1..174f25e 100644 --- a/src/alu.h +++ b/src/alu.h @@ -15,6 +15,7 @@ struct dynabuf; struct type { const char *name; boolean (*is_defined)(struct object *self); + void (*assign)(struct object *self, struct object *new_value, boolean accept_change); void (*monadic_op)(struct object *self, struct op *op); void (*dyadic_op)(struct object *self, struct op *op, struct object *other); void (*fix_result)(struct object *self); diff --git a/src/flow.c b/src/flow.c index ff98765..7becfb9 100644 --- a/src/flow.c +++ b/src/flow.c @@ -61,8 +61,21 @@ void flow_forloop(struct for_loop *loop) loop_counter.u.number.flags = NUMBER_IS_DEFINED; loop_counter.u.number.val.intval = loop->counter.first; loop_counter.u.number.addr_refs = loop->counter.addr_refs; - symbol_set_object2(loop->symbol, &loop_counter, loop->force_bit, TRUE); + // CAUTION: next line does not have power to change symbol type, but if + // "symbol already defined" error is thrown, the type will still have + // been changed. this was done so the code below has a counter var. + symbol_set_object(loop->symbol, &loop_counter, POWER_CHANGE_VALUE); + // TODO: in versions before 0.97, force bit handling was broken + // in both "!set" and "!for": + // trying to change a force bit correctly raised an error, but + // in any case, ALL FORCE BITS WERE CLEARED in symbol. only + // cases like !set N=N+1 worked, because the force bit was + // taken from result. + // maybe support this behaviour via --dialect? + if (loop->force_bit) + symbol_set_force_bit(loop->symbol, loop->force_bit); loop_counter = loop->symbol->object; // update local copy with force bit + loop->symbol->has_been_read = TRUE; // lock force bit if (loop->use_old_algo) { // old algo for old syntax: // if count == 0, skip loop diff --git a/src/global.c b/src/global.c index ce3561b..615ed6f 100644 --- a/src/global.c +++ b/src/global.c @@ -54,6 +54,7 @@ const char exception_no_right_brace[] = "Found end-of-file instead of '}'."; //const char exception_not_yet[] = "Sorry, feature not yet implemented."; const char exception_number_out_of_range[] = "Number out of range."; const char exception_pc_undefined[] = "Program counter undefined."; +const char exception_symbol_defined[] = "Symbol already defined."; const char exception_syntax[] = "Syntax error."; const char exception_value_not_defined[] = "Value not defined."; // default value for number of errors before exiting @@ -162,7 +163,8 @@ static int first_label_of_statement(int *statement_flags) // parse label definition (can be either global or local). // name must be held in GlobalDynaBuf. // called by parse_symbol_definition, parse_backward_anon_def, parse_forward_anon_def -static void set_label(scope_t scope, int stat_flags, int force_bit, boolean change_allowed) // "change_allowed" is used by backward anons! +// "powers" is used by backward anons to allow changes +static void set_label(scope_t scope, int stat_flags, int force_bit, int powers) { struct symbol *symbol; struct number pc; @@ -176,7 +178,9 @@ static void set_label(scope_t scope, int stat_flags, int force_bit, boolean chan result.u.number.flags = pc.flags & NUMBER_IS_DEFINED; result.u.number.val.intval = pc.val.intval; result.u.number.addr_refs = pc.addr_refs; - symbol_set_object2(symbol, &result, force_bit, change_allowed); + symbol_set_object(symbol, &result, powers); + if (force_bit) + symbol_set_force_bit(symbol, force_bit); symbol->pseudopc = pseudopc_get_context(); // global labels must open new scope for cheap locals if (scope == SCOPE_GLOBAL) @@ -185,8 +189,8 @@ static void set_label(scope_t scope, int stat_flags, int force_bit, boolean chan // call with symbol name in GlobalDynaBuf and GotByte == '=' -// "po_set" is for "!set" pseudo opcode, so changes are allowed -void parse_assignment(scope_t scope, int force_bit, boolean po_set) +// "powers" is for "!set" pseudo opcode so changes are allowed (see symbol.h for powers) +void parse_assignment(scope_t scope, int force_bit, int powers) { struct symbol *symbol; struct object result; @@ -201,7 +205,9 @@ void parse_assignment(scope_t scope, int force_bit, boolean po_set) || (result.type == &type_float)) result.u.number.addr_refs = 1; } - symbol_set_object3(symbol, &result, force_bit, po_set); + symbol_set_object(symbol, &result, powers); + if (force_bit) + symbol_set_force_bit(symbol, force_bit); } @@ -214,11 +220,11 @@ static void parse_symbol_definition(scope_t scope, int stat_flags) force_bit = Input_get_force_bit(); // skips spaces after (yes, force bit is allowed for label definitions) if (GotByte == '=') { // explicit symbol definition (symbol = ) - parse_assignment(scope, force_bit, FALSE); + parse_assignment(scope, force_bit, POWER_NONE); Input_ensure_EOS(); } else { // implicit symbol definition (label) - set_label(scope, stat_flags, force_bit, FALSE); + set_label(scope, stat_flags, force_bit, POWER_NONE); } } @@ -266,7 +272,8 @@ static void parse_backward_anon_def(int *statement_flags) DYNABUF_APPEND(GlobalDynaBuf, '-'); while (GetByte() == '-'); DynaBuf_append(GlobalDynaBuf, '\0'); - set_label(section_now->local_scope, *statement_flags, 0, TRUE); // this "TRUE" is the whole secret + // 0 = no force bit, power = backward anons change their value! + set_label(section_now->local_scope, *statement_flags, 0, POWER_CHANGE_VALUE); } @@ -285,7 +292,8 @@ static void parse_forward_anon_def(int *statement_flags) symbol_fix_forward_anon_name(TRUE); // TRUE: increment counter DynaBuf_append(GlobalDynaBuf, '\0'); //printf("[%d, %s]\n", section_now->local_scope, GlobalDynaBuf->buffer); - set_label(section_now->local_scope, *statement_flags, 0, FALSE); + // 0 = no force bit + set_label(section_now->local_scope, *statement_flags, 0, POWER_NONE); } diff --git a/src/global.h b/src/global.h index 2443424..4004dc2 100644 --- a/src/global.h +++ b/src/global.h @@ -44,6 +44,7 @@ extern const char exception_no_right_brace[]; //extern const char exception_not_yet[]; extern const char exception_number_out_of_range[]; extern const char exception_pc_undefined[]; +extern const char exception_symbol_defined[]; extern const char exception_syntax[]; extern const char exception_value_not_defined[]; // byte flags table @@ -135,8 +136,8 @@ extern void config_default(struct config *conf); // allocate memory and die if not available extern void *safe_malloc(size_t amount); // call with symbol name in GlobalDynaBuf and GotByte == '=' -// "po_set" is for "!set" pseudo opcode, so changes are allowed -extern void parse_assignment(scope_t scope, int force_bit, boolean po_set); +// "powers" is for "!set" pseudo opcode so changes are allowed (see symbol.h for powers) +extern void parse_assignment(scope_t scope, int force_bit, int powers); // Parse block, beginning with next byte. // End reason (either CHAR_EOB or CHAR_EOF) can be found in GotByte afterwards // Has to be re-entrant. diff --git a/src/pseudoopcodes.c b/src/pseudoopcodes.c index ad22fbd..f143abb 100644 --- a/src/pseudoopcodes.c +++ b/src/pseudoopcodes.c @@ -751,7 +751,14 @@ static enum eos po_set(void) // now GotByte = illegal char return SKIP_REMAINDER; } - parse_assignment(scope, force_bit, TRUE); + // TODO: in versions before 0.97, force bit handling was broken + // in both "!set" and "!for": + // trying to change a force bit correctly raised an error, but + // in any case, ALL FORCE BITS WERE CLEARED in symbol. only + // cases like !set N=N+1 worked, because the force bit was + // taken from result. + // maybe support this behaviour via --dialect? + parse_assignment(scope, force_bit, POWER_CHANGE_VALUE | POWER_CHANGE_OBJTYPE); return ENSURE_EOS; } diff --git a/src/symbol.c b/src/symbol.c index f54c7fb..417b964 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -135,119 +135,77 @@ struct symbol *symbol_find(scope_t scope) } -// FIXME - temporary helper function during refactoring -static void symbol_forcebit(struct symbol *symbol, int force_bit) -{ - // if symbol has no object assigned to it, make it an int - if (symbol->object.type == NULL) { - // finish empty symbol item - symbol->object.type = &type_int; - symbol->object.u.number.flags = force_bit; - symbol->object.u.number.addr_refs = 0; - symbol->object.u.number.val.intval = 0; - } else { - // make sure the force bits don't clash - if ((symbol->object.type == &type_int) - || (symbol->object.type == &type_float)) { - if (force_bit - && force_bit != (symbol->object.u.number.flags & NUMBER_FORCEBITS)) - Throw_error("Too late for postfix."); - } - } -} -// assign value to symbol. the function acts upon the symbol's flag bits and +// assign object to symbol. the function acts upon the symbol's flag bits and // produces an error if needed. -// TODO - split checks into two parts: first deal with object type. in case of number, then check value/flags/whatever -static void symbol_set_object(struct symbol *symbol, struct object *new_value, boolean change_allowed) // FIXME - does "change_allowed" refer to type change or number value change? +// using "power" bits, caller can state which changes are ok. +// called by: +// implicit label definitions (including anons, backward anons have POWER_CHANGE_VALUE) +// explicit symbol assignments +// explicit symbol assignments via "!set" (has all powers) +// loop counter var init via "!for" (has POWER_CHANGE_VALUE and POWER_CHANGE_NUMTYPE) +// CAUTION: actual incrementing of counter is then done directly without calls here! +void symbol_set_object(struct symbol *symbol, struct object *new_value, int powers) { - int flags; // for int/float re-definitions + struct type *symbol_class, // helper vars to group + *newval_class; // ints and floats - // any new type? - if (((symbol->object.type != &type_int) && (symbol->object.type != &type_float)) - || ((new_value->type != &type_int) && (new_value->type != &type_float))) { - // changing value is ok, changing type needs extra flag: - if (change_allowed || (symbol->object.type == new_value->type)) - symbol->object = *new_value; - else - Throw_error("Symbol already defined."); + // if symbol has no object assigned to it yet, fine: + if (symbol->object.type == NULL) { + symbol->object = *new_value; // copy whole struct including type + // as long as the symbol has not been read, the force bits can + // be changed, so the caller still has a chance to do that. return; } -// FIXME - force bits assigned via !for or !set are lost, because due to "change_allowed", the new object struct is copied and that's it! + // now we know symbol already has a type - // both old and new are either int or float, so keep old algo: - - // value stuff - flags = symbol->object.u.number.flags; - if (change_allowed || !(flags & NUMBER_IS_DEFINED)) { - // symbol is not defined yet OR redefinitions are allowed - symbol->object = *new_value; - } else { - // symbol is already defined, so compare new and old values - // if different type OR same type but different value, complain - if ((symbol->object.type != new_value->type) - || ((symbol->object.type == &type_float) ? (symbol->object.u.number.val.fpval != new_value->u.number.val.fpval) : (symbol->object.u.number.val.intval != new_value->u.number.val.intval))) - Throw_error("Symbol already defined."); + // compare types (CAUTION, ints and floats are grouped!) + symbol_class = symbol->object.type; + if (symbol_class == &type_float) + symbol_class = &type_int; + newval_class = new_value->type; + if (newval_class == &type_float) + newval_class = &type_int; + // if too different, needs power (or complains) + if (symbol_class != newval_class) { + if (!(powers & POWER_CHANGE_OBJTYPE)) + Throw_error(exception_symbol_defined); + // CAUTION: if above line throws error, we still go ahead and change type! + // this is to keep "!for" working, where the counter var is accessed. + symbol->object = *new_value; // copy whole struct including type + // clear flag so caller can adjust force bits: + symbol->has_been_read = FALSE; // it's basically a new symbol now + return; } - // flags stuff - // Ensure that "unsure" symbols without "isByte" state don't get that - if ((flags & (NUMBER_EVER_UNDEFINED | NUMBER_FITS_BYTE)) == NUMBER_EVER_UNDEFINED) - new_value->u.number.flags &= ~NUMBER_FITS_BYTE; - if (change_allowed) { - // take flags from new value, then OR EVER_UNDEFINED from old value - flags = (flags & NUMBER_EVER_UNDEFINED) | new_value->u.number.flags; - } else { - if ((flags & NUMBER_FORCEBITS) == 0) { - if ((flags & (NUMBER_EVER_UNDEFINED | NUMBER_IS_DEFINED)) == 0) { - // FIXME - this can't happen!? Yes, it can! - // if the symbol was created just now to be assigned a value, - // then both flags are clear before the assignment takes place. - // in future this should no longer happen, because creating a - // symbol will give it the NULL type, and flags will only - // matter if it then gets assigned an int or float value. - flags |= new_value->u.number.flags & NUMBER_FORCEBITS; - } - } - flags |= new_value->u.number.flags & ~NUMBER_FORCEBITS; - } - symbol->object.u.number.flags = flags; + // now we know symbol and new value have compatible types, so call handler: + symbol->object.type->assign(&symbol->object, new_value, !!(powers & POWER_CHANGE_VALUE)); } -// FIXME - temporary helper function during refactoring -// used for: -// (implicit!) label definitions, including anons (FIXME - anons cannot have force bits. handle them elsewhere? change backward anons directly, no questions asked?) -// setting up loop counter for "!for" (CAUTION: actual incrementing is then done directly without calling this function!) -// "change_allowed" is used by backward anons, but then force_bit is 0 -// "change_allowed" is also used by "!for", then force_bit may be nonzero -void symbol_set_object2(struct symbol *symbol, struct object *result, int force_bit, boolean change_allowed) + + +// set force bit of symbol. trying to change to a different one will raise error. +void symbol_set_force_bit(struct symbol *symbol, int force_bit) { - symbol_forcebit(symbol, force_bit); // TODO - "if NULL object, make int" and "if not int, complain" - symbol_set_object(symbol, result, change_allowed); // FIXME - "backward anon allows number redef" is different from "!set allows object redef"! -} -// FIXME - temporary helper function during refactoring -// used for: -// explicit assignments, including "!set" -// "po_set" means "!set", so changes are allowed -void symbol_set_object3(struct symbol *symbol, struct object *result, int force_bit, boolean po_set) -{ - // FIXME - force bit can only be used if result is number! check! - symbol_forcebit(symbol, force_bit); - // if this was called by !set, new force bit replaces old one: - if (po_set) { - // clear symbol's force bits and set new ones - // (but only do this for numbers!) - if (((symbol->object.type == &type_int) || (symbol->object.type == &type_float)) - && ((result->type == &type_int) || (result->type == &type_float))) { - // clear symbol's size flags, set new one, clear result's size flags - symbol->object.u.number.flags &= ~(NUMBER_FORCEBITS | NUMBER_FITS_BYTE); - if (force_bit) { - symbol->object.u.number.flags |= force_bit; - result->u.number.flags &= ~(NUMBER_FORCEBITS | NUMBER_FITS_BYTE); - } - } - // FIXME - take a good look at the flags handling above and in the fn called below and clean this up! + if (!force_bit) + Bug_found("ForceBitZero", 0); // FIXME - add to docs! + if (symbol->object.type == NULL) + Bug_found("NullObject", 0); // FIXME - add to docs! + + if ((symbol->object.type != &type_int) && (symbol->object.type != &type_float)) { + Throw_error("Force bits can only be given to numbers."); // FIXME - add to docs! + return; } - symbol_set_object(symbol, result, po_set); + + // if change is ok, change + if (!symbol->has_been_read) { + symbol->object.u.number.flags &= ~NUMBER_FORCEBITS; + symbol->object.u.number.flags |= force_bit; + return; // and be done with it + } + + // it's too late to change, so check if the wanted bit is actually different + if ((symbol->object.u.number.flags & NUMBER_FORCEBITS) != force_bit) + Throw_error("Too late for postfix."); } diff --git a/src/symbol.h b/src/symbol.h index ee72610..67d9d5f 100644 --- a/src/symbol.h +++ b/src/symbol.h @@ -29,15 +29,18 @@ struct symbol { extern struct rwnode *symbols_forest[]; // trees (because of 8-bit hash) -// function acts upon the symbol's flag bits and produces an error if needed. -//extern void symbol_set_object(struct symbol *symbol, struct object *new_obj, boolean change_allowed); -// FIXME - temporary helper function during refactoring -extern void symbol_set_object2(struct symbol *symbol, struct object *new_obj, int force_bit, boolean change_allowed); -// FIXME - temporary helper function during refactoring -extern void symbol_set_object3(struct symbol *symbol, struct object *new_obj, int force_bit, boolean change_allowed); // search for symbol. if it does not exist, create with NULL type object (CAUTION!). // the symbol name must be held in GlobalDynaBuf. extern struct symbol *symbol_find(scope_t scope); +// assign object to symbol. function acts upon the symbol's flag bits and +// produces an error if needed. +// using "power" bits, caller can state which changes are ok. +#define POWER_NONE 0 +#define POWER_CHANGE_VALUE (1u << 0) // e.g. change 3 to 5 or 2.71 +#define POWER_CHANGE_OBJTYPE (1u << 1) // e.g. change 3 to "somestring" +extern void symbol_set_object(struct symbol *symbol, struct object *new_obj, int powers); +// set force bit of symbol. trying to change to a different one will raise error. +extern void symbol_set_force_bit(struct symbol *symbol, int force_bit); // set global symbol to value, no questions asked (for "-D" switch) // name must be held in GlobalDynaBuf. extern void symbol_define(intval_t value); diff --git a/src/version.h b/src/version.h index 8d721f9..3d164ea 100644 --- a/src/version.h +++ b/src/version.h @@ -9,7 +9,7 @@ #define RELEASE "0.96.5" // update before release FIXME #define CODENAME "Fenchurch" // update before release -#define CHANGE_DATE "13 June" // update before release FIXME +#define CHANGE_DATE "14 June" // update before release FIXME #define CHANGE_YEAR "2020" // update before release //#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/" #define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME