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
This commit is contained in:
marcobaye 2020-06-13 23:11:10 +00:00
parent 395dcf55f3
commit 4643e841f9
9 changed files with 224 additions and 131 deletions

122
src/alu.c
View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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 = <something>)
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);
}

View File

@ -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.

View File

@ -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;
}

View File

@ -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.");
}

View File

@ -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);

View File

@ -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