diff --git a/src/alu.c b/src/alu.c index 8fbd15a..e387854 100644 --- a/src/alu.c +++ b/src/alu.c @@ -693,11 +693,6 @@ static void list_append_list(struct listitem *selfhead, struct listitem *otherhe } -// expression parser - -// prototype (FIXME, re-arrange code so this is no longer needed!) -static void push_dyadic_and_check(struct expression *expression, struct op *op); - // helper function for "monadic &" (either octal value or "unpseudo" operator) // returns nonzero on error static int parse_octal_or_unpseudo(void) // now GotByte = '&' @@ -736,6 +731,150 @@ static int parse_octal_or_unpseudo(void) // now GotByte = '&' return 0; // ok } + +// expression parser + + +// handler for special operators like parentheses and start/end of expression: +// returns whether caller can remove "previous" operator from stack +static boolean handle_special_operator(struct expression *expression, enum op_id previous) +{ + // when this gets called, "current" operator is OPID_TERMINATOR + switch (previous) { + case OPID_START_EXPRESSION: + // therefore we know we are done. + // don't touch "is_parenthesized", because start/end are obviously not "real" operators + alu_state = STATE_END; // done + return TRUE; // caller can remove this operator (we are done, so not really needed, but there are sanity checks for stack pointers) + + case OPID_SUBEXPR_PAREN: + expression->is_parenthesized = TRUE; // found parentheses. if this is not the outermost level, the outermost level will fix this flag later on. + if (GotByte == ')') { + // matching parenthesis + GetByte(); // eat char + op_sp -= 2; // remove both SUBEXPR_PAREN and TERMINATOR + alu_state = STATE_EXPECT_DYADIC_OP; + return FALSE; // we fixed the stack ourselves, so caller shouldn't touch it + } + // unmatched parenthesis, as in "lda ($80,x)" + ++(expression->open_parentheses); // count + return TRUE; // caller can remove "SUBEXPR_PAREN" operator from stack + + case OPID_START_LIST: + if (GotByte == ',') { + GetByte(); // eat ',' + op_stack[op_sp - 1] = &ops_list_append; // change "end of expression" to "append" + alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP; + return FALSE; // stack remains, so caller shouldn't touch it + } + if (GotByte == ']') { + GetByte(); // eat ']' + op_sp -= 2; // remove both START_LIST and TERMINATOR + alu_state = STATE_EXPECT_DYADIC_OP; + return FALSE; // we fixed the stack ourselves, so caller shouldn't touch it + } + Throw_error("Unterminated list."); + alu_state = STATE_ERROR; + return TRUE; // caller can remove START_LIST operator from stack + + case OPID_SUBEXPR_BRACKET: + if (GotByte == ']') { + GetByte(); // eat ']' + op_sp -= 2; // remove both SUBEXPR_BRACKET and TERMINATOR + alu_state = STATE_EXPECT_DYADIC_OP; + return FALSE; // we fixed the stack ourselves, so caller shouldn't touch it + } + Throw_error("Unterminated index spec."); + alu_state = STATE_ERROR; + return TRUE; // caller can remove SUBEXPR_BRACKET operator from stack + + default: + Bug_found("IllegalOperatorId", previous); + } + // this is unreachable + return FALSE; // stack is done, so caller shouldn't touch it +} + + +// Try to reduce stacks by performing high-priority operations +// (if the previous operator has a higher priority than the current one, do it) +static void push_dyadic_and_check(struct expression *expression, struct op *op) +{ + struct op *previous_op; + struct op *current_op; + + PUSH_OP(op); + if (alu_state < STATE_MAX_GO_ON) + alu_state = STATE_TRY_TO_REDUCE_STACKS; + while (alu_state == STATE_TRY_TO_REDUCE_STACKS) { + if (op_sp < 2) { + // we only have one operator, which must be "start of expression", + // so there isn't anything left to do, so go on trying to parse the expression + alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP; + return; + } + + previous_op = op_stack[op_sp - 2]; + current_op = op_stack[op_sp - 1]; + + // previous operator has lower piority than current one? then do nothing. + if (previous_op->priority < current_op->priority) { + alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP; + return; + } + + // previous operator has same priority as current one? then check associativity + if ((previous_op->priority == current_op->priority) + && (current_op->priority == PRIO_POWEROF) + && (config.wanted_version >= VER_RIGHTASSOCIATIVEPOWEROF)) { + alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP; + return; + } + + // we now know that either + // - the previous operator has higher priority, or + // - it has the same priority and is left-associative, + // so perform that operation! +#define ARG_PREV (arg_stack[arg_sp - 2]) +#define ARG_NOW (arg_stack[arg_sp - 1]) + switch (previous_op->group) { + case OPGROUP_MONADIC: // monadic operators + if (arg_sp < 1) + Bug_found("ArgStackEmpty", arg_sp); + ARG_NOW.type->monadic_op(&ARG_NOW, previous_op); + // operation was something other than parentheses + expression->is_parenthesized = FALSE; + break; + case OPGROUP_DYADIC: // dyadic operators + if (arg_sp < 2) + Bug_found("NotEnoughArgs", arg_sp); + ARG_PREV.type->dyadic_op(&ARG_PREV, previous_op, &ARG_NOW); + // decrement argument stack pointer because dyadic operator merged two arguments into one + --arg_sp; + // operation was something other than parentheses + expression->is_parenthesized = FALSE; + break; + case OPGROUP_SPECIAL: // special (pseudo) operators + if (current_op->id != OPID_TERMINATOR) + Bug_found("StrangeOperator", current_op->id); + if (!handle_special_operator(expression, previous_op->id)) + continue; // called fn has fixed the stack, so we don't touch it + + // both monadics and dyadics clear "is_parenthesized", but here we don't touch it! + break; + default: + Bug_found("IllegalOperatorGroup", previous_op->group); + } + // shared endings for "we did the operation indicated by previous operator": + // fix stack: + // remove previous operator and shift down current one + // CAUTION - fiddling with our local copies like "previous_op = current_op" is not enough... ;) + op_stack[op_sp - 2] = op_stack[op_sp - 1]; + --op_sp; // decrement operator stack pointer + } +} + + // Expect argument or monadic operator (hopefully inlined) // returns TRUE if it ate any non-space (-> so expression isn't empty) // returns FALSE if first non-space is delimiter (-> end of expression) @@ -2144,6 +2283,7 @@ static void string_print(const struct object *self, struct dynabuf *db) DynaBuf_add_string(db, self->u.string->payload); // there is a terminator after the actual payload, so this works } +// "class" definitions struct type type_number = { "number", number_is_defined, @@ -2176,146 +2316,6 @@ struct type type_string = { }; -// handler for special operators like parentheses and start/end of expression: -// returns whether caller can remove "previous" operator from stack -static boolean handle_special_operator(struct expression *expression, enum op_id previous) -{ - // when this gets called, "current" operator is OPID_TERMINATOR - switch (previous) { - case OPID_START_EXPRESSION: - // therefore we know we are done. - // don't touch "is_parenthesized", because start/end are obviously not "real" operators - alu_state = STATE_END; // done - return TRUE; // caller can remove this operator (we are done, so not really needed, but there are sanity checks for stack pointers) - - case OPID_SUBEXPR_PAREN: - expression->is_parenthesized = TRUE; // found parentheses. if this is not the outermost level, the outermost level will fix this flag later on. - if (GotByte == ')') { - // matching parenthesis - GetByte(); // eat char - op_sp -= 2; // remove both SUBEXPR_PAREN and TERMINATOR - alu_state = STATE_EXPECT_DYADIC_OP; - return FALSE; // we fixed the stack ourselves, so caller shouldn't touch it - } - // unmatched parenthesis, as in "lda ($80,x)" - ++(expression->open_parentheses); // count - return TRUE; // caller can remove "SUBEXPR_PAREN" operator from stack - - case OPID_START_LIST: - if (GotByte == ',') { - GetByte(); // eat ',' - op_stack[op_sp - 1] = &ops_list_append; // change "end of expression" to "append" - alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP; - return FALSE; // stack remains, so caller shouldn't touch it - } - if (GotByte == ']') { - GetByte(); // eat ']' - op_sp -= 2; // remove both START_LIST and TERMINATOR - alu_state = STATE_EXPECT_DYADIC_OP; - return FALSE; // we fixed the stack ourselves, so caller shouldn't touch it - } - Throw_error("Unterminated list."); - alu_state = STATE_ERROR; - return TRUE; // caller can remove START_LIST operator from stack - - case OPID_SUBEXPR_BRACKET: - if (GotByte == ']') { - GetByte(); // eat ']' - op_sp -= 2; // remove both SUBEXPR_BRACKET and TERMINATOR - alu_state = STATE_EXPECT_DYADIC_OP; - return FALSE; // we fixed the stack ourselves, so caller shouldn't touch it - } - Throw_error("Unterminated index spec."); - alu_state = STATE_ERROR; - return TRUE; // caller can remove SUBEXPR_BRACKET operator from stack - - default: - Bug_found("IllegalOperatorId", previous); - } - // this is unreachable - return FALSE; // stack is done, so caller shouldn't touch it -} - - -// Try to reduce stacks by performing high-priority operations -// (if the previous operator has a higher priority than the current one, do it) -static void push_dyadic_and_check(struct expression *expression, struct op *op) -{ - struct op *previous_op; - struct op *current_op; - - PUSH_OP(op); - if (alu_state < STATE_MAX_GO_ON) - alu_state = STATE_TRY_TO_REDUCE_STACKS; - while (alu_state == STATE_TRY_TO_REDUCE_STACKS) { - if (op_sp < 2) { - // we only have one operator, which must be "start of expression", - // so there isn't anything left to do, so go on trying to parse the expression - alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP; - return; - } - - previous_op = op_stack[op_sp - 2]; - current_op = op_stack[op_sp - 1]; - - // previous operator has lower piority than current one? then do nothing. - if (previous_op->priority < current_op->priority) { - alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP; - return; - } - - // previous operator has same priority as current one? then check associativity - if ((previous_op->priority == current_op->priority) - && (current_op->priority == PRIO_POWEROF) - && (config.wanted_version >= VER_RIGHTASSOCIATIVEPOWEROF)) { - alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP; - return; - } - - // we now know that either - // - the previous operator has higher priority, or - // - it has the same priority and is left-associative, - // so perform that operation! -#define ARG_PREV (arg_stack[arg_sp - 2]) -#define ARG_NOW (arg_stack[arg_sp - 1]) - switch (previous_op->group) { - case OPGROUP_MONADIC: // monadic operators - if (arg_sp < 1) - Bug_found("ArgStackEmpty", arg_sp); - ARG_NOW.type->monadic_op(&ARG_NOW, previous_op); - // operation was something other than parentheses - expression->is_parenthesized = FALSE; - break; - case OPGROUP_DYADIC: // dyadic operators - if (arg_sp < 2) - Bug_found("NotEnoughArgs", arg_sp); - ARG_PREV.type->dyadic_op(&ARG_PREV, previous_op, &ARG_NOW); - // decrement argument stack pointer because dyadic operator merged two arguments into one - --arg_sp; - // operation was something other than parentheses - expression->is_parenthesized = FALSE; - break; - case OPGROUP_SPECIAL: // special (pseudo) operators - if (current_op->id != OPID_TERMINATOR) - Bug_found("StrangeOperator", current_op->id); - if (!handle_special_operator(expression, previous_op->id)) - continue; // called fn has fixed the stack, so we don't touch it - - // both monadics and dyadics clear "is_parenthesized", but here we don't touch it! - break; - default: - Bug_found("IllegalOperatorGroup", previous_op->group); - } - // shared endings for "we did the operation indicated by previous operator": - // fix stack: - // remove previous operator and shift down current one - // CAUTION - fiddling with our local copies like "previous_op = current_op" is not enough... ;) - op_stack[op_sp - 2] = op_stack[op_sp - 1]; - --op_sp; // decrement operator stack pointer - } -} - - // this is what the exported functions call // returns nonzero on parse error static int parse_expression(struct expression *expression) diff --git a/src/flow.c b/src/flow.c index 622e6b7..b6f6c4d 100644 --- a/src/flow.c +++ b/src/flow.c @@ -24,7 +24,32 @@ #include "tree.h" -// helper functions for "!for" and "!do" +// helper functions for if/ifdef/ifndef/else/for/do/while + + +// parse symbol name and return if symbol has defined value (called by ifdef/ifndef) +boolean check_ifdef_condition(void) +{ + scope_t scope; + struct rwnode *node; + struct symbol *symbol; + + // read symbol name + if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before + return FALSE; // there was an error, it has been reported, so return value is more or less meaningless anway + + // look for it + Tree_hard_scan(&node, symbols_forest, scope, FALSE); + if (!node) + return FALSE; // not found -> no, not defined + + symbol = (struct symbol *) node->body; + symbol->has_been_read = TRUE; // we did not really read the symbol's value, but checking for its existence still counts as "used it" + if (symbol->object.type == NULL) + Bug_found("ObjectHasNullType", 0); // FIXME - add to docs! + return symbol->object.type->is_defined(&symbol->object); +} + // parse a loop body (TODO - also use for macro body?) static void parse_ram_block(struct block *block) diff --git a/src/flow.h b/src/flow.h index c89e3c3..67fbcc6 100644 --- a/src/flow.h +++ b/src/flow.h @@ -43,6 +43,8 @@ struct do_while { }; +// parse symbol name and return if symbol has defined value (called by ifdef/ifndef) +extern boolean check_ifdef_condition(void); // back end function for "!for" pseudo opcode extern void flow_forloop(struct for_loop *loop); // try to read a condition into DynaBuf and store pointer to copy in diff --git a/src/global.c b/src/global.c index be317b6..e8b219b 100644 --- a/src/global.c +++ b/src/global.c @@ -513,3 +513,91 @@ void output_object(struct object *object, struct iter_context *iter) Bug_found("IllegalObjectType9", 0); // FIXME - add to docs! } } + + +// output 8-bit value with range check +void output_8(intval_t value) +{ + if ((value <= 0xff) && (value >= -0x80)) + Output_byte(value); + else + Throw_error(exception_number_out_of_range); +} + + +// output 16-bit value with range check big-endian +void output_be16(intval_t value) +{ + if ((value <= 0xffff) && (value >= -0x8000)) { + Output_byte(value >> 8); + Output_byte(value); + } else { + Throw_error(exception_number_out_of_range); + } +} + + +// output 16-bit value with range check little-endian +void output_le16(intval_t value) +{ + if ((value <= 0xffff) && (value >= -0x8000)) { + Output_byte(value); + Output_byte(value >> 8); + } else { + Throw_error(exception_number_out_of_range); + } +} + + +// output 24-bit value with range check big-endian +void output_be24(intval_t value) +{ + if ((value <= 0xffffff) && (value >= -0x800000)) { + Output_byte(value >> 16); + Output_byte(value >> 8); + Output_byte(value); + } else { + Throw_error(exception_number_out_of_range); + } +} + + +// output 24-bit value with range check little-endian +void output_le24(intval_t value) +{ + if ((value <= 0xffffff) && (value >= -0x800000)) { + Output_byte(value); + Output_byte(value >> 8); + Output_byte(value >> 16); + } else { + Throw_error(exception_number_out_of_range); + } +} + + +// output 32-bit value (without range check) big-endian +void output_be32(intval_t value) +{ +// if ((Value <= 0x7fffffff) && (Value >= -0x80000000)) { + Output_byte(value >> 24); + Output_byte(value >> 16); + Output_byte(value >> 8); + Output_byte(value); +// } else { +// Throw_error(exception_number_out_of_range); +// } +} + + +// output 32-bit value (without range check) little-endian +void output_le32(intval_t value) +{ +// if ((Value <= 0x7fffffff) && (Value >= -0x80000000)) { + Output_byte(value); + Output_byte(value >> 8); + Output_byte(value >> 16); + Output_byte(value >> 24); +// } else { +// Throw_error(exception_number_out_of_range); +// } +} diff --git a/src/global.h b/src/global.h index cc7bec2..7e58e44 100644 --- a/src/global.h +++ b/src/global.h @@ -175,6 +175,21 @@ struct iter_context { char stringxor; // for !scrxor, 0 otherwise }; extern void output_object(struct object *object, struct iter_context *iter); +// output 8-bit value with range check +extern void output_8(intval_t value); +// output 16-bit value with range check big-endian +extern void output_be16(intval_t value); +// output 16-bit value with range check little-endian +extern void output_le16(intval_t value); +// output 24-bit value with range check big-endian +extern void output_be24(intval_t value); +// output 24-bit value with range check little-endian +extern void output_le24(intval_t value); +// output 32-bit value (without range check) big-endian +extern void output_be32(intval_t value); +// output 32-bit value (without range check) little-endian +extern void output_le32(intval_t value); + #endif diff --git a/src/output.c b/src/output.c index 1413cc2..be9b916 100644 --- a/src/output.c +++ b/src/output.c @@ -197,94 +197,6 @@ void output_skip(int size) } -// output 8-bit value with range check -void output_8(intval_t value) -{ - if ((value <= 0xff) && (value >= -0x80)) - Output_byte(value); - else - Throw_error(exception_number_out_of_range); -} - - -// output 16-bit value with range check big-endian -void output_be16(intval_t value) -{ - if ((value <= 0xffff) && (value >= -0x8000)) { - Output_byte(value >> 8); - Output_byte(value); - } else { - Throw_error(exception_number_out_of_range); - } -} - - -// output 16-bit value with range check little-endian -void output_le16(intval_t value) -{ - if ((value <= 0xffff) && (value >= -0x8000)) { - Output_byte(value); - Output_byte(value >> 8); - } else { - Throw_error(exception_number_out_of_range); - } -} - - -// output 24-bit value with range check big-endian -void output_be24(intval_t value) -{ - if ((value <= 0xffffff) && (value >= -0x800000)) { - Output_byte(value >> 16); - Output_byte(value >> 8); - Output_byte(value); - } else { - Throw_error(exception_number_out_of_range); - } -} - - -// output 24-bit value with range check little-endian -void output_le24(intval_t value) -{ - if ((value <= 0xffffff) && (value >= -0x800000)) { - Output_byte(value); - Output_byte(value >> 8); - Output_byte(value >> 16); - } else { - Throw_error(exception_number_out_of_range); - } -} - - -// output 32-bit value (without range check) big-endian -void output_be32(intval_t value) -{ -// if ((Value <= 0x7fffffff) && (Value >= -0x80000000)) { - Output_byte(value >> 24); - Output_byte(value >> 16); - Output_byte(value >> 8); - Output_byte(value); -// } else { -// Throw_error(exception_number_out_of_range); -// } -} - - -// output 32-bit value (without range check) little-endian -void output_le32(intval_t value) -{ -// if ((Value <= 0x7fffffff) && (Value >= -0x80000000)) { - Output_byte(value); - Output_byte(value >> 8); - Output_byte(value >> 16); - Output_byte(value >> 24); -// } else { -// Throw_error(exception_number_out_of_range); -// } -} - - // fill output buffer with given byte value static void fill_completely(char value) { diff --git a/src/output.h b/src/output.h index a7acd12..7a031c3 100644 --- a/src/output.h +++ b/src/output.h @@ -54,23 +54,6 @@ extern void (*Output_byte)(intval_t); // returns zero if ok, nonzero if already set extern int output_initmem(char content); -// move elsewhere: - -// Output 8-bit value with range check -extern void output_8(intval_t value); -// Output 16-bit value with range check big-endian -extern void output_be16(intval_t value); -// Output 16-bit value with range check little-endian -extern void output_le16(intval_t value); -// Output 24-bit value with range check big-endian -extern void output_be24(intval_t value); -// Output 24-bit value with range check little-endian -extern void output_le24(intval_t value); -// Output 32-bit value (without range check) big-endian -extern void output_be32(intval_t value); -// Output 32-bit value (without range check) little-endian -extern void output_le32(intval_t value); - // outfile stuff: // try to set output format held in DynaBuf. Returns zero on success. diff --git a/src/pseudoopcodes.c b/src/pseudoopcodes.c index 2a33829..43eebc9 100644 --- a/src/pseudoopcodes.c +++ b/src/pseudoopcodes.c @@ -892,36 +892,14 @@ static enum eos po_source(void) // now GotByte = illegal char return ENSURE_EOS; } -// FIXME - move this to flow.c! -static boolean check_ifdef_condition(void) -{ - scope_t scope; - struct rwnode *node; - struct symbol *symbol; - - // read symbol name - if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before - return FALSE; // there was an error, it has been reported, so return value is more or less meaningless anway - - // look for it - Tree_hard_scan(&node, symbols_forest, scope, FALSE); - if (!node) - return FALSE; // not found -> no, not defined - - symbol = (struct symbol *) node->body; - symbol->has_been_read = TRUE; // we did not really read the symbol's value, but checking for its existence still counts as "used it" - if (symbol->object.type == NULL) - Bug_found("ObjectHasNullType", 0); // FIXME - add to docs! - return symbol->object.type->is_defined(&symbol->object); -} -// if/ifdef/ifndef/else function, to be able to do ELSE IF +// if/ifdef/ifndef/else enum ifmode { IFMODE_IF, // parse expression, then block IFMODE_IFDEF, // check symbol, then parse block or line IFMODE_IFNDEF, // check symbol, then parse block or line IFMODE_ELSE // unconditional last block }; -// function for if/ifdef/ifndef/else. has to be re-entrant. +// has to be re-entrant static enum eos ifelse(enum ifmode mode) { boolean nothing_done = TRUE; // once a block gets executed, this becomes FALSE, so all others will be skipped even if condition met