diff --git a/src/acme.c b/src/acme.c index 476beb3..faae636 100644 --- a/src/acme.c +++ b/src/acme.c @@ -417,7 +417,6 @@ static void do_actual_work(void) sanity.macro_recursions_left = config.sanity_limit; sanity.source_recursions_left = config.sanity_limit; - sanity.passes_left = config.sanity_limit; // init output system output_init(); @@ -433,7 +432,7 @@ static void do_actual_work(void) while ((pass.counters.undefineds && (pass.counters.undefineds < undefs_before)) || pass.counters.symbolchanges) { undefs_before = pass.counters.undefineds; perform_pass(0); - if (--sanity.passes_left < 0) { + if (pass.number > config.sanity_limit) { throw_serious_error("Exceeded maximum number of passes, please see docs."); // ...or maybe do one additional pass where all errors are reported, including "not defined" and "value has changed". } diff --git a/src/alu.c b/src/alu.c index 7242848..74c4a0f 100644 --- a/src/alu.c +++ b/src/alu.c @@ -35,7 +35,7 @@ // constants #define ERRORMSG_INITIALSIZE 256 // ad hoc -#define FUNCTION_INITIALSIZE 8 // enough for "arctan" +#define FUNCTION_INITIALSIZE 16 // enough for "is_string" #define HALF_INITIAL_STACK_SIZE 8 static const char exception_div_by_zero[] = "Division by zero."; static const char exception_no_value[] = "No value given."; @@ -75,6 +75,8 @@ enum op_id { OPID_ISNUMBER, // is_number(v) OPID_ISLIST, // is_list(v) OPID_ISSTRING, // is_string(v) + OPID_DEC, // dec(v) + OPID_HEX, // hex(v) // add CHR function to create 1-byte string? or rather add \xAB escape sequence? // dyadic operators: OPID_POWEROF, // v^w @@ -95,7 +97,7 @@ enum op_id { OPID_NOTEQUAL, // v!=w v<>w v>= V0_98__PATHS_AND_SYMBOLCHANGE) + throw_error("the \"EOR\" operator is obsolete; use \"XOR\" instead."); + else + throw_finalpass_warning("\"EOR\" is deprecated; use \"XOR\" instead."); +} + + // expression parser @@ -870,7 +888,7 @@ static void push_dyadic_and_check(struct expression *expression, struct op *op) } -// Expect argument or monadic operator (hopefully inlined) +// 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) static boolean expect_argument_or_monadic_operator(struct expression *expression) @@ -1011,16 +1029,16 @@ static boolean expect_argument_or_monadic_operator(struct expression *expression default: // all other characters if ((GotByte >= '0') && (GotByte <= '9')) { parse_number_literal(); - // Now GotByte = non-decimal char + // now GotByte = non-decimal char goto now_expect_dyadic_op; } if (BYTE_STARTS_KEYWORD(GotByte)) { register int length; - // Read global label (or "NOT") + // read global label (or "NOT") length = parser_read_keyword(); - // Now GotByte = illegal char + // now GotByte = illegal char // Check for NOT. Okay, it's hardcoded, // but so what? Sue me... if ((length == 3) @@ -1064,7 +1082,7 @@ static boolean expect_argument_or_monadic_operator(struct expression *expression get_byte_and_push_monadic: GetByte(); PUSH_OP(op); - // State doesn't change + // state doesn't change break; now_expect_dyadic_op: @@ -1078,7 +1096,7 @@ now_expect_dyadic_op: } -// Expect dyadic operator (hopefully inlined) +// expect dyadic operator (hopefully inlined) static void expect_dyadic_operator(struct expression *expression) { void *node_body; @@ -1086,7 +1104,7 @@ static void expect_dyadic_operator(struct expression *expression) SKIPSPACE(); switch (GotByte) { -// Single-character dyadic operators +// single-character dyadic operators case '^': // "to the power of" op = &ops_powerof; goto get_byte_and_push_dyadic; @@ -1119,7 +1137,7 @@ static void expect_dyadic_operator(struct expression *expression) op = &ops_or; goto get_byte_and_push_dyadic; -// This part is commented out because there is no XOR character defined +// this part is commented out because there is no XOR character defined // case ???: // bitwise exclusive OR // op = &ops_xor; // goto get_byte_and_push_dyadic; @@ -1143,7 +1161,7 @@ static void expect_dyadic_operator(struct expression *expression) PUSH_OP(&ops_subexpr_bracket); return; -// Multi-character dyadic operators +// multi-character dyadic operators case '!': // "!=" GetByte(); // eat '!' if (parser_expect('=')) { @@ -1202,7 +1220,7 @@ static void expect_dyadic_operator(struct expression *expression) // check string versions of operators if (BYTE_STARTS_KEYWORD(GotByte)) { parser_read_and_lower_keyword(); - // Now GotByte = illegal char + // now GotByte = illegal char // search for tree item if (tree_easy_scan(op_tree, &node_body, GlobalDynaBuf)) { op = node_body; @@ -1507,6 +1525,16 @@ static void undef_handle_monadic_operator(struct object *self, const struct op * self->u.number.flags &= ~NUMBER_FORCEBITS; self->u.number.addr_refs = 0; break; + case OPID_DEC: + case OPID_HEX: + // undefined number results in empty string: + // (so program grows in later passes but does not shrink) + string_prepare_string(self, 0); // replace self with zero-length string + // we just converted an undefined argument into a result that is + // defined, so the expression parser won't count it as undefined + // when returning to the caller. therefore, we count it ourself: + ++pass.counters.undefineds; + break; // add new monadic operators here // case OPID_: // break; @@ -1515,6 +1543,25 @@ static void undef_handle_monadic_operator(struct object *self, const struct op * } } +// int: +// helper function to replace int object with string version (either decimal or hexadecimal) +// (also see number_print() further down which does something similar but not identical) +#define NUMBUFSIZE 64 // large enough(tm) even for 64bit systems +static void int_to_string(struct object *self, const char formatstring[]) +{ + char buffer[NUMBUFSIZE]; + int length; + +#if _BSD_SOURCE || _XOPEN_SOURCE >= 500 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L + length = snprintf(buffer, NUMBUFSIZE, formatstring, (long) self->u.number.val.intval); +#else + length = sprintf(buffer, formatstring, (long) self->u.number.val.intval); +#endif + string_prepare_string(self, length); // create string object and put on arg stack + // (the fn above has already put a terminator at the correct position) + memcpy(self->u.string->payload, buffer, length); +} + // prototype for int/float passing static void float_handle_monadic_operator(struct object *self, const struct op *op); // int: @@ -1565,6 +1612,12 @@ static void int_handle_monadic_operator(struct object *self, const struct op *op self->u.number.flags |= NUMBER_FITS_BYTE; self->u.number.flags &= ~NUMBER_FORCEBITS; break; + case OPID_DEC: + int_to_string(self, "%ld"); // decimal format + break; + case OPID_HEX: + int_to_string(self, "%lx"); // hexadecimal format + break; // add new monadic operators here // case OPID_: // break; @@ -1797,7 +1850,7 @@ static void undef_handle_dyadic_operator(struct object *self, const struct op *o goto shared; case OPID_EOR: - throw_finalpass_warning("\"EOR\" is deprecated; use \"XOR\" instead."); // FIXME - change to error, at least for newest dialect! + eor_is_obsolete(); /*FALLTHROUGH*/ case OPID_XOR: case OPID_AND: @@ -1964,7 +2017,7 @@ static void int_handle_dyadic_operator(struct object *self, const struct op *op, refs = self->u.number.addr_refs + other->u.number.addr_refs; // add address references break; case OPID_EOR: - throw_finalpass_warning("\"EOR\" is deprecated; use \"XOR\" instead."); // FIXME - change to error, at least for newest dialect! + eor_is_obsolete(); /*FALLTHROUGH*/ case OPID_XOR: self->u.number.val.intval ^= other->u.number.val.intval; @@ -2311,7 +2364,7 @@ static void object_no_op(struct object *self) // int/float: // print value for user message -#define NUMBUFSIZE 64 // large enough(tm) even for 64bit systems +// (also see int_to_string() further up which does something similar but not identical) static void number_print(const struct object *self, struct dynabuf *db) { char buffer[NUMBUFSIZE]; diff --git a/src/config.h b/src/config.h index 24649f4..8fc71d2 100644 --- a/src/config.h +++ b/src/config.h @@ -66,8 +66,8 @@ struct object { } u; }; struct string { - int length; - int refs; // FIXME - either use correctly or remove altogether! + int length; + int refs; // FIXME - either use correctly or remove altogether! char payload[1]; // real structs are malloc'd to correct size }; struct listitem { diff --git a/src/global.h b/src/global.h index 789dda9..60c7179 100644 --- a/src/global.h +++ b/src/global.h @@ -108,7 +108,12 @@ struct pass { int number; // counts up from one struct { int undefineds; // counts undefined expression results (if this stops decreasing, next pass must list them as errors) - //int needvalue; // counts undefined expression results actually needed for output (when this hits zero, we're done) FIXME - use + //int needvalue; // counts undefined expression results actually needed for output (when this hits zero, we're done) +// okay, this "needvalue" idea is problematic: evaluating "dec(UNDEFINED)" gives +// a defined result, namely an empty string. so the evaluation has to increment +// *some* counter to make sure more passes are done. but it cannot increment the +// "needvalue" counter because the expression parser does not know whether the +// result is actually put into memory! int symbolchanges; // count symbol changes (if nonzero, another pass is needed) int errors; // if nonzero -> stop after this pass int warnings; // this is needed for showing macro call stack @@ -130,7 +135,6 @@ extern struct pass pass; struct sanity { int macro_recursions_left; // for macro calls int source_recursions_left; // for "!src" - int passes_left; }; extern struct sanity sanity; diff --git a/src/output.c b/src/output.c index 7a27eb6..e42f7d3 100644 --- a/src/output.c +++ b/src/output.c @@ -137,8 +137,15 @@ static void real_output(intval_t byte) if (report->fd) report_binary(byte & 0xff); // file for reporting // write byte to output buffer - if (out->buffer) + if (out->buffer) { + if (out->write_idx >= out->needed_bufsize) { + throw_serious_error("Output buffer overrun."); + // FIXME - change this to BUG and add code to make sure it does not happen! + // or maybe at least enlarge the buffer by 16 bytes and place a canary in + // it so we can do a sanity check at the end! + } out->buffer[out->write_idx] = (byte & 0xff) ^ out->xor; + } // advance pointer out->write_idx++; ++statement_size; // count this byte diff --git a/src/version.h b/src/version.h index ca68c39..366e754 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 "11 Aug" // update before release FIXME +#define CHANGE_DATE "12 Aug" // 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