added dec() and hex() functions

git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@409 4df02467-bbd4-4a76-a152-e7ce94205b78
This commit is contained in:
marcobaye 2024-08-30 16:54:55 +00:00
parent a6a43015a8
commit 514eb38177
6 changed files with 98 additions and 35 deletions

View File

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

107
src/alu.c
View File

@ -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><w
OPID_AND, // v&w v AND w
OPID_OR, // v|w v OR w
OPID_EOR, // v EOR w v XOR w FIXME - remove
OPID_EOR, // v EOR w v XOR w deprecated!
OPID_XOR, // v XOR w
OPID_LIST_APPEND, // used internally when building list literal
OPID_ATINDEX, // v[w]
@ -163,6 +165,8 @@ static struct op ops_len = {42, OPGROUP_MONADIC, OPID_LEN, "len()" };
static struct op ops_isnumber = {42, OPGROUP_MONADIC, OPID_ISNUMBER, "is_number()" };
static struct op ops_islist = {42, OPGROUP_MONADIC, OPID_ISLIST, "is_list()" };
static struct op ops_isstring = {42, OPGROUP_MONADIC, OPID_ISSTRING, "is_string()" };
static struct op ops_dec = {42, OPGROUP_MONADIC, OPID_DEC, "dec()" };
static struct op ops_hex = {42, OPGROUP_MONADIC, OPID_HEX, "hex()" };
// variables
@ -209,6 +213,8 @@ static struct ronode function_tree[] = {
PREDEFNODE("is_number", &ops_isnumber),
PREDEFNODE("is_list", &ops_islist),
PREDEFNODE("is_string", &ops_isstring),
PREDEFNODE("dec", &ops_dec),
PREDEFNODE("hex", &ops_hex),
PREDEFNODE("arcsin", &ops_arcsin),
PREDEFNODE("arccos", &ops_arccos),
PREDEFNODE("arctan", &ops_arctan),
@ -381,8 +387,8 @@ static void get_symbol_value(scope_t scope, size_t name_length, unsigned int unp
}
// Parse program counter ('*')
static void parse_program_counter(unsigned int unpseudo_count) // Now GotByte = "*"
// parse program counter ('*')
static void parse_program_counter(unsigned int unpseudo_count) // now GotByte = "*"
{
struct number pc;
struct object *arg;
@ -449,7 +455,7 @@ static void parse_quoted(char closing_quote)
value = encoding_encode_char(GLOBALDYNABUF_CURRENT[0]);
PUSH_INT_ARG(value, 0, 0); // no flags, no addr refs
}
// Now GotByte = char following closing quote (or CHAR_EOS on error)
// now GotByte = char following closing quote (or CHAR_EOS on error)
return;
fail:
@ -461,7 +467,7 @@ fail:
// Parse binary value. Apart from '0' and '1', it also accepts the characters
// '.' and '#', this is much more readable. The current value is stored as soon
// as a character is read that is none of those given above.
static void parse_binary_literal(void) // Now GotByte = "%" or "b"
static void parse_binary_literal(void) // now GotByte = "%" or "b"
{
intval_t value = 0;
bits flags = 0;
@ -500,14 +506,14 @@ static void parse_binary_literal(void) // Now GotByte = "%" or "b"
}
}
PUSH_INT_ARG(value, flags, 0);
// Now GotByte = non-binary char
// now GotByte = non-binary char
}
// Parse hexadecimal value. It accepts "0" to "9", "a" to "f" and "A" to "F".
// The current value is stored as soon as a character is read that is none of
// those given above.
static void parse_hex_literal(void) // Now GotByte = "$" or "x"
static void parse_hex_literal(void) // now GotByte = "$" or "x"
{
char byte;
int digits = -1; // digit counter
@ -552,7 +558,7 @@ static void parse_hex_literal(void) // Now GotByte = "$" or "x"
// parse fractional part of a floating-point value
static void parse_frac_part(int integer_part) // Now GotByte = first digit after decimal point
static void parse_frac_part(int integer_part) // now GotByte = first digit after decimal point
{
double denominator = 1,
fpval = integer_part;
@ -582,7 +588,7 @@ static void parse_frac_part(int integer_part) // Now GotByte = first digit after
// point has been found, so don't expect "100000000000000000000" to work.
// CAUTION: "100000000000000000000.0" won't work either, because when the
// decimal point gets parsed, the integer value will have overflown already.
static void parse_number_literal(void) // Now GotByte = first digit
static void parse_number_literal(void) // now GotByte = first digit
{
intval_t intval = (GotByte & 15); // this works. it's ASCII.
@ -611,13 +617,13 @@ static void parse_number_literal(void) // Now GotByte = first digit
} else {
PUSH_INT_ARG(intval, 0, 0);
}
// Now GotByte = non-decimal char
// now GotByte = non-decimal char
}
// Parse octal value. It accepts "0" to "7". The current value is stored as
// soon as a character is read that is none of those given above.
static void parse_octal_literal(void) // Now GotByte = first octal digit
static void parse_octal_literal(void) // now GotByte = first octal digit
{
intval_t value = 0;
bits flags = 0;
@ -645,8 +651,8 @@ static void parse_octal_literal(void) // Now GotByte = first octal digit
}
// Parse function call (sin(), cos(), arctan(), ...)
static void parse_function_call(void)
// parse function call (sin(), cos(), arctan(), ...)
static void parse_function_call(void) // now GotByte = '('
{
void *node_body;
@ -738,6 +744,18 @@ static int parse_octal_or_unpseudo(void) // now GotByte = '&'
}
// helper function to tell user not to use the "EOR" operator anymore
// this gets called from two places, depending on whether the result
// would be defined or not - to make sure the user sees it.
static void eor_is_obsolete(void)
{
if (config.dialect >= 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];

View File

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

View File

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

View File

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

View File

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