2016-12-28 20:32:00 +00:00
|
|
|
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code.
|
2020-04-25 10:20:52 +00:00
|
|
|
// Copyright (C) 1998-2020 Marco Baye
|
2012-02-27 21:14:46 +00:00
|
|
|
// Have a look at "acme.c" for further info
|
|
|
|
//
|
|
|
|
// Arithmetic/logic unit
|
2020-05-09 20:58:08 +00:00
|
|
|
// 11 Oct 2006 Improved float reading in parse_number_literal()
|
2012-02-27 21:14:46 +00:00
|
|
|
// 24 Nov 2007 Now accepts floats starting with decimal point
|
|
|
|
// 31 Jul 2009 Changed ASR again, just to be on the safe side.
|
2014-01-16 18:16:24 +00:00
|
|
|
// 14 Jan 2014 Changed associativity of "power-of" operator,
|
|
|
|
// so a^b^c now means a^(b^c).
|
2014-05-07 09:40:50 +00:00
|
|
|
// 7 May 2014 C-style "==" operators are now recognized (but
|
|
|
|
// give a warning).
|
2014-05-31 00:46:55 +00:00
|
|
|
// 31 May 2014 Added "0b" binary number prefix as alternative to "%".
|
2015-06-14 23:16:23 +00:00
|
|
|
// 28 Apr 2015 Added symbol name output to "value not defined" error.
|
2020-04-26 16:24:34 +00:00
|
|
|
// 1 Feb 2019 Prepared to make "honor leading zeroes" optionally (now done)
|
2020-05-09 20:58:08 +00:00
|
|
|
|
|
|
|
// the words "operand"/"operator"/"operation" are too similar, so:
|
|
|
|
// "op" means operator/operation
|
|
|
|
// "arg" means argument (used instead of "operand")
|
|
|
|
|
2014-12-22 00:47:52 +00:00
|
|
|
#include "alu.h"
|
2012-02-27 21:14:46 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <math.h> // only for fp support
|
2020-05-19 20:41:12 +00:00
|
|
|
#include <string.h> // for memcpy()
|
2012-02-27 21:14:46 +00:00
|
|
|
#include "platform.h"
|
|
|
|
#include "dynabuf.h"
|
|
|
|
#include "encoding.h"
|
|
|
|
#include "global.h"
|
|
|
|
#include "input.h"
|
2014-03-11 14:22:32 +00:00
|
|
|
#include "output.h"
|
2012-02-27 21:14:46 +00:00
|
|
|
#include "section.h"
|
2014-06-07 00:12:10 +00:00
|
|
|
#include "symbol.h"
|
2012-02-27 21:14:46 +00:00
|
|
|
#include "tree.h"
|
|
|
|
|
|
|
|
|
|
|
|
// constants
|
|
|
|
|
2015-06-14 23:16:23 +00:00
|
|
|
#define ERRORMSG_DYNABUF_INITIALSIZE 256 // ad hoc
|
2012-02-27 21:14:46 +00:00
|
|
|
#define FUNCTION_DYNABUF_INITIALSIZE 8 // enough for "arctan"
|
|
|
|
#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.";
|
|
|
|
static const char exception_paren_open[] = "Too many '('.";
|
2020-05-24 21:16:50 +00:00
|
|
|
static const char exception_not_number[] = "Expression did not return a number.";
|
2020-05-09 19:26:40 +00:00
|
|
|
|
|
|
|
enum op_group {
|
|
|
|
OPGROUP_SPECIAL, // start/end of expression, and parentheses
|
|
|
|
OPGROUP_MONADIC, // {result} = {op} {arg}
|
|
|
|
OPGROUP_DYADIC // {result} = {arg1} {op} {arg2}
|
|
|
|
};
|
2020-05-14 10:19:21 +00:00
|
|
|
enum op_id {
|
2020-05-09 19:26:40 +00:00
|
|
|
// special (pseudo) operators:
|
2020-05-19 20:41:12 +00:00
|
|
|
OPID_END_EXPRESSION, // end of expression (quasi-dyadic)
|
|
|
|
OPID_START_EXPRESSION, // start of expression
|
|
|
|
OPID_LEFT_PARENTHESIS, // (v '(' starts subexpression (quasi-monadic)
|
|
|
|
OPID_START_LIST, // [1,2] '[' starts list literal (quasi-monadic)
|
|
|
|
OPID_START_INDEX, // v[ '[' starts subexpression (quasi-monadic, also see dyadic OPID_ATINDEX)
|
2020-05-09 19:26:40 +00:00
|
|
|
// monadic operators (including functions):
|
2020-05-14 14:01:30 +00:00
|
|
|
OPID_NOT, // !v NOT v bit-wise NOT
|
|
|
|
OPID_NEGATE, // -v negation
|
|
|
|
OPID_LOWBYTEOF, // <v low byte of
|
|
|
|
OPID_HIGHBYTEOF, // >v high byte of
|
|
|
|
OPID_BANKBYTEOF, // ^v bank byte of
|
2020-05-14 10:19:21 +00:00
|
|
|
OPID_ADDRESS, // addr(v) FIXME - add nonaddr()?
|
|
|
|
OPID_INT, // int(v)
|
|
|
|
OPID_FLOAT, // float(v)
|
|
|
|
OPID_SIN, // sin(v)
|
|
|
|
OPID_COS, // cos(v)
|
|
|
|
OPID_TAN, // tan(v)
|
|
|
|
OPID_ARCSIN, // arcsin(v)
|
|
|
|
OPID_ARCCOS, // arccos(v)
|
|
|
|
OPID_ARCTAN, // arctan(v)
|
2020-05-14 17:36:09 +00:00
|
|
|
OPID_LEN, // len(v)
|
2020-05-09 19:26:40 +00:00
|
|
|
// dyadic operators:
|
2020-05-14 10:19:21 +00:00
|
|
|
OPID_POWEROF, // v^w
|
|
|
|
OPID_MULTIPLY, // v*w
|
2020-05-14 14:01:30 +00:00
|
|
|
OPID_DIVIDE, // v/w division
|
|
|
|
OPID_INTDIV, // v/w v DIV w integer division
|
|
|
|
OPID_MODULO, // v%w v MOD w remainder
|
|
|
|
OPID_SHIFTLEFT, // v<<w v ASL w v LSL w shift left
|
|
|
|
OPID_ASR, // v>>w v ASR w arithmetic shift right
|
|
|
|
OPID_LSR, // v>>>w v LSR w logical shift right
|
2020-05-14 10:19:21 +00:00
|
|
|
OPID_ADD, // v+w
|
|
|
|
OPID_SUBTRACT, // v-w
|
|
|
|
OPID_EQUALS, // v=w
|
2020-05-14 14:01:30 +00:00
|
|
|
OPID_LESSOREQUAL, // v<=w
|
2020-05-14 10:19:21 +00:00
|
|
|
OPID_LESSTHAN, // v< w
|
2020-05-14 14:01:30 +00:00
|
|
|
OPID_GREATEROREQUAL, // v>=w
|
2020-05-14 10:19:21 +00:00
|
|
|
OPID_GREATERTHAN, // v> w
|
|
|
|
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_XOR, // v XOR w
|
2020-05-19 20:41:12 +00:00
|
|
|
OPID_LIST_APPEND, // used internally when building list literal
|
|
|
|
OPID_ATINDEX, // v[w]
|
2012-02-27 21:14:46 +00:00
|
|
|
};
|
2020-05-09 20:58:08 +00:00
|
|
|
struct op {
|
2020-05-24 20:19:19 +00:00
|
|
|
int priority;
|
2020-05-09 19:26:40 +00:00
|
|
|
enum op_group group;
|
2020-05-14 10:19:21 +00:00
|
|
|
enum op_id id;
|
2020-05-14 14:01:30 +00:00
|
|
|
const char *text_version;
|
2012-02-27 21:14:46 +00:00
|
|
|
};
|
2020-05-19 20:41:12 +00:00
|
|
|
static struct op ops_end_expression = {0, OPGROUP_SPECIAL, OPID_END_EXPRESSION, "end of expression" };
|
|
|
|
static struct op ops_start_expression = {2, OPGROUP_SPECIAL, OPID_START_EXPRESSION, "start of expression" };
|
|
|
|
static struct op ops_left_parenthesis = {4, OPGROUP_SPECIAL, OPID_LEFT_PARENTHESIS, "left parenthesis" };
|
|
|
|
static struct op ops_start_list = {6, OPGROUP_SPECIAL, OPID_START_LIST, "start list" };
|
|
|
|
static struct op ops_start_index = {8, OPGROUP_SPECIAL, OPID_START_INDEX, "open index" };
|
|
|
|
static struct op ops_list_append = {14, OPGROUP_DYADIC, OPID_LIST_APPEND, "append to list" };
|
2020-05-14 14:01:30 +00:00
|
|
|
static struct op ops_or = {16, OPGROUP_DYADIC, OPID_OR, "logical or" };
|
|
|
|
static struct op ops_eor = {18, OPGROUP_DYADIC, OPID_EOR, "exclusive or" }; // FIXME - remove
|
|
|
|
static struct op ops_xor = {18, OPGROUP_DYADIC, OPID_XOR, "exclusive or" };
|
|
|
|
static struct op ops_and = {20, OPGROUP_DYADIC, OPID_AND, "logical and" };
|
|
|
|
static struct op ops_equals = {22, OPGROUP_DYADIC, OPID_EQUALS, "test for equality" };
|
|
|
|
static struct op ops_not_equal = {24, OPGROUP_DYADIC, OPID_NOTEQUAL, "test for inequality" };
|
2020-05-09 19:26:40 +00:00
|
|
|
// same priority for all comparison operators
|
2020-05-14 14:01:30 +00:00
|
|
|
static struct op ops_less_or_equal = {26, OPGROUP_DYADIC, OPID_LESSOREQUAL, "less than or equal" };
|
|
|
|
static struct op ops_less_than = {26, OPGROUP_DYADIC, OPID_LESSTHAN, "less than" };
|
|
|
|
static struct op ops_greater_or_equal = {26, OPGROUP_DYADIC, OPID_GREATEROREQUAL, "greater than or equal" };
|
|
|
|
static struct op ops_greater_than = {26, OPGROUP_DYADIC, OPID_GREATERTHAN, "greater than" };
|
2012-02-27 21:14:46 +00:00
|
|
|
// same priority for all byte extraction operators
|
2020-05-14 14:01:30 +00:00
|
|
|
static struct op ops_low_byte_of = {28, OPGROUP_MONADIC, OPID_LOWBYTEOF, "low byte of" };
|
|
|
|
static struct op ops_high_byte_of = {28, OPGROUP_MONADIC, OPID_HIGHBYTEOF, "high byte of" };
|
|
|
|
static struct op ops_bank_byte_of = {28, OPGROUP_MONADIC, OPID_BANKBYTEOF, "bank byte of" };
|
2014-01-16 18:16:24 +00:00
|
|
|
// same priority for all shift operators (left-associative, though they could be argued to be made right-associative :))
|
2020-05-14 14:01:30 +00:00
|
|
|
static struct op ops_shift_left = {30, OPGROUP_DYADIC, OPID_SHIFTLEFT, "shift left" };
|
|
|
|
static struct op ops_asr = {30, OPGROUP_DYADIC, OPID_ASR, "arithmetic shift right" };
|
|
|
|
static struct op ops_lsr = {30, OPGROUP_DYADIC, OPID_LSR, "logical shift right" };
|
2020-05-09 19:26:40 +00:00
|
|
|
// same priority for "+" and "-"
|
2020-05-14 14:01:30 +00:00
|
|
|
static struct op ops_add = {32, OPGROUP_DYADIC, OPID_ADD, "addition" };
|
|
|
|
static struct op ops_subtract = {32, OPGROUP_DYADIC, OPID_SUBTRACT, "subtraction" };
|
2020-05-09 19:26:40 +00:00
|
|
|
// same priority for "*", "/" and "%"
|
2020-05-14 14:01:30 +00:00
|
|
|
static struct op ops_multiply = {34, OPGROUP_DYADIC, OPID_MULTIPLY, "multiplication" };
|
|
|
|
static struct op ops_divide = {34, OPGROUP_DYADIC, OPID_DIVIDE, "division" };
|
|
|
|
static struct op ops_intdiv = {34, OPGROUP_DYADIC, OPID_INTDIV, "integer division" };
|
|
|
|
static struct op ops_modulo = {34, OPGROUP_DYADIC, OPID_MODULO, "modulo" };
|
2012-02-27 21:14:46 +00:00
|
|
|
// highest "real" priorities
|
2020-05-14 14:01:30 +00:00
|
|
|
static struct op ops_negate = {36, OPGROUP_MONADIC, OPID_NEGATE, "negation" };
|
2020-05-24 20:19:19 +00:00
|
|
|
#define PRIO_POWEROF 37 // the single right-associative operator, so this gets checked explicitly
|
|
|
|
static struct op ops_powerof = {PRIO_POWEROF, OPGROUP_DYADIC, OPID_POWEROF, "power of" };
|
2020-05-14 14:01:30 +00:00
|
|
|
static struct op ops_not = {38, OPGROUP_MONADIC, OPID_NOT, "logical not" };
|
2020-05-19 20:41:12 +00:00
|
|
|
static struct op ops_atindex = {40, OPGROUP_DYADIC, OPID_ATINDEX, "indexing" };
|
2014-12-16 08:21:44 +00:00
|
|
|
// function calls act as if they were monadic operators.
|
|
|
|
// they need high priorities to make sure they are evaluated once the
|
|
|
|
// parentheses' content is known:
|
|
|
|
// "sin(3 + 4) DYADIC_OPERATOR 5" becomes "sin 7 DYADIC_OPERATOR 5",
|
|
|
|
// so function calls' priority must be higher than all dyadic operators.
|
2020-05-14 14:01:30 +00:00
|
|
|
static struct op ops_addr = {42, OPGROUP_MONADIC, OPID_ADDRESS, "address()" };
|
|
|
|
static struct op ops_int = {42, OPGROUP_MONADIC, OPID_INT, "int()" };
|
|
|
|
static struct op ops_float = {42, OPGROUP_MONADIC, OPID_FLOAT, "float()" };
|
|
|
|
static struct op ops_sin = {42, OPGROUP_MONADIC, OPID_SIN, "sin()" };
|
|
|
|
static struct op ops_cos = {42, OPGROUP_MONADIC, OPID_COS, "cos()" };
|
|
|
|
static struct op ops_tan = {42, OPGROUP_MONADIC, OPID_TAN, "tan()" };
|
|
|
|
static struct op ops_arcsin = {42, OPGROUP_MONADIC, OPID_ARCSIN, "arcsin()" };
|
|
|
|
static struct op ops_arccos = {42, OPGROUP_MONADIC, OPID_ARCCOS, "arccos()" };
|
|
|
|
static struct op ops_arctan = {42, OPGROUP_MONADIC, OPID_ARCTAN, "arctan()" };
|
2020-05-14 17:36:09 +00:00
|
|
|
static struct op ops_len = {42, OPGROUP_MONADIC, OPID_LEN, "len()" };
|
2020-05-19 20:41:12 +00:00
|
|
|
// CAUTION: when adding a function that returns something indexable, fix the code inserting ops_atindex!
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
// variables
|
2020-05-15 11:39:00 +00:00
|
|
|
static struct dynabuf *errormsg_dyna_buf; // dynamic buffer to build variable-length error messages
|
2014-03-11 12:07:11 +00:00
|
|
|
static struct dynabuf *function_dyna_buf; // dynamic buffer for fn names
|
2020-05-09 20:58:08 +00:00
|
|
|
// operator stack, current size and stack pointer:
|
|
|
|
static struct op **op_stack = NULL;
|
|
|
|
static int opstack_size = HALF_INITIAL_STACK_SIZE;
|
|
|
|
static int op_sp;
|
|
|
|
// argument stack, current size and stack pointer:
|
2020-05-13 23:26:40 +00:00
|
|
|
static struct object *arg_stack = NULL;
|
2020-05-09 20:58:08 +00:00
|
|
|
static int argstack_size = HALF_INITIAL_STACK_SIZE;
|
|
|
|
static int arg_sp;
|
2014-03-11 12:07:11 +00:00
|
|
|
enum alu_state {
|
2020-05-09 20:58:08 +00:00
|
|
|
STATE_EXPECT_ARG_OR_MONADIC_OP,
|
|
|
|
STATE_EXPECT_DYADIC_OP,
|
2012-02-27 21:14:46 +00:00
|
|
|
STATE_TRY_TO_REDUCE_STACKS,
|
|
|
|
STATE_MAX_GO_ON, // "border value" to find the stoppers:
|
2020-05-09 20:58:08 +00:00
|
|
|
STATE_ERROR, // error has occurred
|
2014-03-11 12:07:11 +00:00
|
|
|
STATE_END // standard end
|
2012-02-27 21:14:46 +00:00
|
|
|
};
|
2014-03-11 12:07:11 +00:00
|
|
|
static enum alu_state alu_state; // deterministic finite automaton
|
2012-02-27 21:14:46 +00:00
|
|
|
// predefined stuff
|
2020-05-09 20:58:08 +00:00
|
|
|
static struct ronode *op_tree = NULL; // tree to hold operators
|
|
|
|
static struct ronode op_list[] = {
|
2012-02-27 21:14:46 +00:00
|
|
|
PREDEFNODE(s_asr, &ops_asr),
|
|
|
|
PREDEFNODE(s_lsr, &ops_lsr),
|
2020-05-14 14:01:30 +00:00
|
|
|
PREDEFNODE(s_asl, &ops_shift_left),
|
|
|
|
PREDEFNODE("lsl", &ops_shift_left),
|
2012-02-27 21:14:46 +00:00
|
|
|
PREDEFNODE("div", &ops_intdiv),
|
|
|
|
PREDEFNODE("mod", &ops_modulo),
|
|
|
|
PREDEFNODE(s_and, &ops_and),
|
2020-05-26 12:55:14 +00:00
|
|
|
PREDEFNODE("or", &ops_or),
|
2020-05-10 21:35:50 +00:00
|
|
|
PREDEFNODE(s_eor, &ops_eor), // FIXME - remove
|
2020-05-26 12:55:14 +00:00
|
|
|
PREDEFLAST("xor", &ops_xor),
|
2012-02-27 21:14:46 +00:00
|
|
|
// ^^^^ this marks the last element
|
|
|
|
};
|
2014-11-24 14:52:05 +00:00
|
|
|
static struct ronode *function_tree = NULL; // tree to hold functions
|
|
|
|
static struct ronode function_list[] = {
|
2014-06-02 00:47:46 +00:00
|
|
|
PREDEFNODE("addr", &ops_addr),
|
2015-06-14 23:16:23 +00:00
|
|
|
PREDEFNODE("address", &ops_addr),
|
2012-02-27 21:14:46 +00:00
|
|
|
PREDEFNODE("int", &ops_int),
|
|
|
|
PREDEFNODE("float", &ops_float),
|
2020-05-15 11:39:00 +00:00
|
|
|
PREDEFNODE("len", &ops_len),
|
2020-05-26 12:55:14 +00:00
|
|
|
PREDEFNODE("arcsin", &ops_arcsin),
|
|
|
|
PREDEFNODE("arccos", &ops_arccos),
|
|
|
|
PREDEFNODE("arctan", &ops_arctan),
|
|
|
|
PREDEFNODE("sin", &ops_sin),
|
|
|
|
PREDEFNODE("cos", &ops_cos),
|
|
|
|
PREDEFLAST("tan", &ops_tan),
|
2012-02-27 21:14:46 +00:00
|
|
|
// ^^^^ this marks the last element
|
|
|
|
};
|
|
|
|
|
2020-05-19 20:41:12 +00:00
|
|
|
#define PUSH_OP(x) \
|
|
|
|
do { \
|
|
|
|
op_stack[op_sp] = (x); \
|
|
|
|
if (++op_sp >= opstack_size) \
|
|
|
|
enlarge_operator_stack(); \
|
|
|
|
} while (0)
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-13 23:26:40 +00:00
|
|
|
#define PUSH_INT_ARG(i, f, r) \
|
|
|
|
do { \
|
|
|
|
arg_stack[arg_sp].type = &type_int; \
|
|
|
|
arg_stack[arg_sp].u.number.flags = (f); \
|
|
|
|
arg_stack[arg_sp].u.number.val.intval = (i); \
|
|
|
|
arg_stack[arg_sp++].u.number.addr_refs = (r); \
|
2012-02-27 21:14:46 +00:00
|
|
|
} while (0)
|
2020-05-13 23:26:40 +00:00
|
|
|
#define PUSH_FP_ARG(fp, f) \
|
|
|
|
do { \
|
|
|
|
arg_stack[arg_sp].type = &type_float; \
|
|
|
|
arg_stack[arg_sp].u.number.flags = (f); \
|
|
|
|
arg_stack[arg_sp].u.number.val.fpval = (fp); \
|
|
|
|
arg_stack[arg_sp++].u.number.addr_refs = 0; \
|
2012-02-27 21:14:46 +00:00
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
2020-05-09 20:58:08 +00:00
|
|
|
// double the size of the operator stack
|
2012-02-27 21:14:46 +00:00
|
|
|
static void enlarge_operator_stack(void)
|
|
|
|
{
|
2020-05-09 20:58:08 +00:00
|
|
|
opstack_size *= 2;
|
|
|
|
op_stack = realloc(op_stack, opstack_size * sizeof(*op_stack));
|
|
|
|
if (op_stack == NULL)
|
2012-02-27 21:14:46 +00:00
|
|
|
Throw_serious_error(exception_no_memory_left);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-09 20:58:08 +00:00
|
|
|
// double the size of the argument stack
|
|
|
|
static void enlarge_argument_stack(void)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2020-05-09 20:58:08 +00:00
|
|
|
argstack_size *= 2;
|
|
|
|
arg_stack = realloc(arg_stack, argstack_size * sizeof(*arg_stack));
|
|
|
|
if (arg_stack == NULL)
|
2012-02-27 21:14:46 +00:00
|
|
|
Throw_serious_error(exception_no_memory_left);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-09 20:58:08 +00:00
|
|
|
// create dynamic buffer, operator/function trees and operator/argument stacks
|
2012-02-27 21:14:46 +00:00
|
|
|
void ALU_init(void)
|
|
|
|
{
|
2015-06-14 23:16:23 +00:00
|
|
|
errormsg_dyna_buf = DynaBuf_create(ERRORMSG_DYNABUF_INITIALSIZE);
|
2012-02-27 21:14:46 +00:00
|
|
|
function_dyna_buf = DynaBuf_create(FUNCTION_DYNABUF_INITIALSIZE);
|
2020-05-09 20:58:08 +00:00
|
|
|
Tree_add_table(&op_tree, op_list);
|
2012-02-27 21:14:46 +00:00
|
|
|
Tree_add_table(&function_tree, function_list);
|
|
|
|
enlarge_operator_stack();
|
2020-05-09 20:58:08 +00:00
|
|
|
enlarge_argument_stack();
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// not-so-braindead algorithm for calculating "to the power of" function for
|
2020-05-09 20:58:08 +00:00
|
|
|
// integer arguments.
|
2020-05-09 23:01:51 +00:00
|
|
|
// my_pow(whatever, 0) returns 1.
|
|
|
|
// my_pow(0, whatever_but_zero) returns 0.
|
2012-02-27 21:14:46 +00:00
|
|
|
static intval_t my_pow(intval_t mantissa, intval_t exponent)
|
|
|
|
{
|
|
|
|
intval_t result = 1;
|
|
|
|
|
|
|
|
while (exponent) {
|
|
|
|
// handle exponent's lowmost bit
|
|
|
|
if (exponent & 1)
|
|
|
|
result *= mantissa;
|
|
|
|
// square the mantissa, halve the exponent
|
|
|
|
mantissa *= mantissa;
|
|
|
|
exponent >>= 1;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// arithmetic shift right (works even if C compiler does not support it)
|
|
|
|
static intval_t my_asr(intval_t left, intval_t right)
|
|
|
|
{
|
2020-05-09 20:58:08 +00:00
|
|
|
// if first argument is positive or zero, ASR and LSR are equivalent,
|
2012-02-27 21:14:46 +00:00
|
|
|
// so just do it and return the result:
|
|
|
|
if (left >= 0)
|
|
|
|
return left >> right;
|
|
|
|
|
2020-05-09 20:58:08 +00:00
|
|
|
// However, if the first argument is negative, the result is
|
2012-02-27 21:14:46 +00:00
|
|
|
// implementation-defined: While most compilers will do ASR, some others
|
|
|
|
// might do LSR instead, and *theoretically*, it is even possible for a
|
|
|
|
// compiler to define silly stuff like "shifting a negative value to the
|
|
|
|
// right will always return -1".
|
2020-05-09 20:58:08 +00:00
|
|
|
// Therefore, in case of a negative argument, we'll use this quick and
|
2012-02-27 21:14:46 +00:00
|
|
|
// simple workaround:
|
|
|
|
return ~((~left) >> right);
|
|
|
|
}
|
|
|
|
|
2020-05-02 21:59:20 +00:00
|
|
|
|
2020-05-13 23:26:40 +00:00
|
|
|
// if wanted, throw "Value not defined" error
|
2020-05-02 21:59:20 +00:00
|
|
|
// This function is not allowed to change DynaBuf because the symbol's name
|
|
|
|
// might be stored there!
|
2020-05-13 23:26:40 +00:00
|
|
|
static void is_not_defined(struct symbol *optional_symbol, char optional_prefix_char, char *name, size_t length)
|
2015-06-14 23:16:23 +00:00
|
|
|
{
|
2020-05-02 21:59:20 +00:00
|
|
|
if (!pass.complain_about_undefined)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// only complain once per symbol
|
|
|
|
if (optional_symbol) {
|
|
|
|
if (optional_symbol->has_been_reported)
|
|
|
|
return;
|
|
|
|
|
|
|
|
optional_symbol->has_been_reported = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
DYNABUF_CLEAR(errormsg_dyna_buf);
|
|
|
|
DynaBuf_add_string(errormsg_dyna_buf, "Value not defined (");
|
|
|
|
length += errormsg_dyna_buf->size;
|
|
|
|
|
|
|
|
if (optional_prefix_char) {
|
|
|
|
DynaBuf_append(errormsg_dyna_buf, optional_prefix_char);
|
|
|
|
++length;
|
|
|
|
}
|
|
|
|
DynaBuf_add_string(errormsg_dyna_buf, name);
|
|
|
|
if (errormsg_dyna_buf->size < length) {
|
2020-05-24 21:16:50 +00:00
|
|
|
Bug_found("IllegalSymbolNameLength", errormsg_dyna_buf->size - length);
|
2020-05-02 21:59:20 +00:00
|
|
|
} else {
|
|
|
|
errormsg_dyna_buf->size = length;
|
2015-06-14 23:16:23 +00:00
|
|
|
}
|
2020-05-02 21:59:20 +00:00
|
|
|
DynaBuf_add_string(errormsg_dyna_buf, ").");
|
|
|
|
DynaBuf_append(errormsg_dyna_buf, '\0');
|
|
|
|
Throw_error(errormsg_dyna_buf->buffer);
|
2015-06-14 23:16:23 +00:00
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-02 21:59:20 +00:00
|
|
|
|
2014-06-07 00:12:10 +00:00
|
|
|
// Lookup (and create, if necessary) symbol tree item and return its value.
|
2016-08-05 09:59:07 +00:00
|
|
|
// DynaBuf holds the symbol's name and "scope" its scope.
|
2015-06-14 23:16:23 +00:00
|
|
|
// The name length must be given explicitly because of anonymous forward labels;
|
|
|
|
// their internal name is different (longer) than their displayed name.
|
2012-02-27 21:14:46 +00:00
|
|
|
// This function is not allowed to change DynaBuf because that's where the
|
2014-06-07 00:12:10 +00:00
|
|
|
// symbol name is stored!
|
2020-05-28 18:40:40 +00:00
|
|
|
static void get_symbol_value(scope_t scope, char optional_prefix_char, size_t name_length, unsigned int unpseudo_count)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2014-06-07 00:12:10 +00:00
|
|
|
struct symbol *symbol;
|
2020-05-28 18:40:40 +00:00
|
|
|
struct object *arg;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-06-05 01:11:51 +00:00
|
|
|
symbol = symbol_find(scope);
|
2020-06-08 17:01:17 +00:00
|
|
|
symbol->has_been_read = TRUE;
|
2020-06-05 01:11:51 +00:00
|
|
|
if (symbol->object.type == NULL) {
|
|
|
|
// finish symbol item by making it an undefined int
|
|
|
|
symbol->object.type = &type_int;
|
2020-06-08 17:01:17 +00:00
|
|
|
symbol->object.u.number.flags = NUMBER_EVER_UNDEFINED; // reading undefined taints it
|
2020-06-05 01:11:51 +00:00
|
|
|
symbol->object.u.number.addr_refs = 0;
|
|
|
|
symbol->object.u.number.val.intval = 0;
|
2020-06-08 17:01:17 +00:00
|
|
|
} else {
|
|
|
|
// FIXME - add sanity check for int/float where DEFINED is false and EVER_UNDEFINED is false -> Bug_found()!
|
|
|
|
// (because the only way to have DEFINED clear is the block above, and EVER_UNDEFINED taints everything it touches)
|
2020-06-05 01:11:51 +00:00
|
|
|
}
|
2020-05-28 18:40:40 +00:00
|
|
|
// first push on arg stack, so we have a local copy we can "unpseudopc"
|
|
|
|
arg = &arg_stack[arg_sp++];
|
2020-05-29 10:57:01 +00:00
|
|
|
*arg = symbol->object;
|
2020-05-28 18:40:40 +00:00
|
|
|
if (unpseudo_count) {
|
|
|
|
if (arg->type == &type_int) {
|
|
|
|
pseudopc_unpseudo(&arg->u.number, symbol->pseudopc, unpseudo_count);
|
|
|
|
// TODO - check return value and enter error state if nonzero?
|
|
|
|
} else {
|
2020-05-29 22:03:04 +00:00
|
|
|
Throw_error("Un-pseudopc operator '&' can only be applied to labels.");
|
2020-05-28 18:40:40 +00:00
|
|
|
// TODO - enter error state?
|
|
|
|
}
|
|
|
|
}
|
2020-05-02 21:59:20 +00:00
|
|
|
// if needed, output "value not defined" error
|
2020-05-28 18:40:40 +00:00
|
|
|
// FIXME - in case of unpseudopc, error message should include the correct amount of '&' characters
|
|
|
|
if (!(arg->type->is_defined(arg)))
|
2020-05-13 23:26:40 +00:00
|
|
|
is_not_defined(symbol, optional_prefix_char, GLOBALDYNABUF_CURRENT, name_length);
|
2020-05-19 20:41:12 +00:00
|
|
|
// FIXME - if arg is list, increment ref count!
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-02 11:28:15 +00:00
|
|
|
// Parse program counter ('*')
|
|
|
|
static void parse_program_counter(void) // Now GotByte = "*"
|
|
|
|
{
|
|
|
|
struct number pc;
|
|
|
|
|
|
|
|
GetByte();
|
|
|
|
vcpu_read_pc(&pc);
|
2020-05-02 21:59:20 +00:00
|
|
|
// if needed, output "value not defined" error
|
2020-05-13 23:26:40 +00:00
|
|
|
if (!(pc.flags & NUMBER_IS_DEFINED))
|
|
|
|
is_not_defined(NULL, 0, "*", 1);
|
2020-05-09 20:58:08 +00:00
|
|
|
PUSH_INT_ARG(pc.val.intval, pc.flags, pc.addr_refs);
|
2020-05-02 11:28:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-19 20:41:12 +00:00
|
|
|
// make new string object
|
2020-05-26 12:55:14 +00:00
|
|
|
static void string_prepare_string(struct object *self, int len)
|
2020-05-19 20:41:12 +00:00
|
|
|
{
|
|
|
|
self->type = &type_string;
|
|
|
|
self->u.string = safe_malloc(sizeof(*(self->u.string)) + len);
|
2020-05-25 23:12:19 +00:00
|
|
|
self->u.string->payload[len] = 0; // terminate, to facilitate string_print()
|
|
|
|
self->u.string->length = len; // length does not include the added terminator
|
2020-05-19 20:41:12 +00:00
|
|
|
self->u.string->refs = 1;
|
|
|
|
}
|
|
|
|
// parse string or character
|
|
|
|
// characters will be converted using the current encoding, strings are kept as-is.
|
|
|
|
static void parse_quoted(char closing_quote)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
|
|
|
intval_t value;
|
|
|
|
|
2020-05-19 11:09:46 +00:00
|
|
|
DYNABUF_CLEAR(GlobalDynaBuf);
|
|
|
|
if (Input_quoted_to_dynabuf(closing_quote))
|
|
|
|
goto fail; // unterminated or escaping error
|
2020-05-18 23:47:15 +00:00
|
|
|
|
2020-05-19 11:09:46 +00:00
|
|
|
// eat closing quote
|
|
|
|
GetByte();
|
|
|
|
// now convert to unescaped version
|
2020-05-19 16:28:36 +00:00
|
|
|
if (Input_unescape_dynabuf(0))
|
2020-05-19 11:09:46 +00:00
|
|
|
goto fail; // escaping error
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-19 20:41:12 +00:00
|
|
|
// without backslash escaping, both ' and " are used for single
|
|
|
|
// characters.
|
|
|
|
// with backslash escaping, ' is for characters and " is for strings:
|
2020-05-27 17:32:48 +00:00
|
|
|
if ((closing_quote == '"') && (config.wanted_version >= VER_BACKSLASHESCAPING)) {
|
2020-05-19 20:41:12 +00:00
|
|
|
// string //////////////////////////////////
|
2020-05-26 12:55:14 +00:00
|
|
|
string_prepare_string(&arg_stack[arg_sp], GlobalDynaBuf->size); // create string object and put on arg stack
|
|
|
|
memcpy(arg_stack[arg_sp].u.string->payload, GLOBALDYNABUF_CURRENT, GlobalDynaBuf->size); // copy payload
|
|
|
|
++arg_sp;
|
2020-05-19 20:41:12 +00:00
|
|
|
} else {
|
|
|
|
// single character ////////////////////////
|
|
|
|
// too short?
|
|
|
|
if (GlobalDynaBuf->size == 0) {
|
|
|
|
Throw_error(exception_missing_string);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
// too long?
|
|
|
|
if (GlobalDynaBuf->size != 1)
|
|
|
|
Throw_error("There's more than one character.");
|
|
|
|
// parse character
|
2020-05-19 21:04:57 +00:00
|
|
|
value = (intval_t) (unsigned char) encoding_encode_char(GLOBALDYNABUF_CURRENT[0]);
|
2020-05-19 20:41:12 +00:00
|
|
|
PUSH_INT_ARG(value, NUMBER_IS_DEFINED | NUMBER_FITS_BYTE, 0);
|
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
// Now GotByte = char following closing quote (or CHAR_EOS on error)
|
2020-05-19 11:09:46 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
PUSH_INT_ARG(0, NUMBER_IS_DEFINED | NUMBER_FITS_BYTE, 0); // dummy
|
|
|
|
alu_state = STATE_ERROR;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-31 00:46:55 +00:00
|
|
|
// 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.
|
2020-05-09 20:58:08 +00:00
|
|
|
static void parse_binary_literal(void) // Now GotByte = "%" or "b"
|
2014-05-31 00:46:55 +00:00
|
|
|
{
|
|
|
|
intval_t value = 0;
|
2020-04-28 16:02:09 +00:00
|
|
|
int flags = NUMBER_IS_DEFINED,
|
2014-05-31 00:46:55 +00:00
|
|
|
digits = -1; // digit counter
|
|
|
|
|
2020-04-28 11:18:22 +00:00
|
|
|
for (;;) {
|
2014-11-30 17:00:13 +00:00
|
|
|
++digits;
|
2014-05-31 00:46:55 +00:00
|
|
|
switch (GetByte()) {
|
|
|
|
case '0':
|
|
|
|
case '.':
|
|
|
|
value <<= 1;
|
2020-04-28 11:18:22 +00:00
|
|
|
continue;
|
2014-05-31 00:46:55 +00:00
|
|
|
case '1':
|
|
|
|
case '#':
|
|
|
|
value = (value << 1) | 1;
|
2020-04-28 11:18:22 +00:00
|
|
|
continue;
|
2014-05-31 00:46:55 +00:00
|
|
|
}
|
2020-04-28 11:18:22 +00:00
|
|
|
break; // found illegal character
|
|
|
|
}
|
2020-05-13 23:45:03 +00:00
|
|
|
if (!digits)
|
2020-05-24 21:16:50 +00:00
|
|
|
Throw_warning("Binary literal without any digits."); // FIXME - make into error!
|
2014-05-31 00:46:55 +00:00
|
|
|
// set force bits
|
2020-04-14 00:28:31 +00:00
|
|
|
if (config.honor_leading_zeroes) {
|
2019-02-01 11:23:28 +00:00
|
|
|
if (digits > 8) {
|
|
|
|
if (digits > 16) {
|
|
|
|
if (value < 65536)
|
2020-04-28 16:02:09 +00:00
|
|
|
flags |= NUMBER_FORCES_24;
|
2019-02-01 11:23:28 +00:00
|
|
|
} else {
|
|
|
|
if (value < 256)
|
2020-04-28 16:02:09 +00:00
|
|
|
flags |= NUMBER_FORCES_16;
|
2019-02-01 11:23:28 +00:00
|
|
|
}
|
2014-05-31 00:46:55 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-09 20:58:08 +00:00
|
|
|
PUSH_INT_ARG(value, flags, 0);
|
2014-05-31 00:46:55 +00:00
|
|
|
// Now GotByte = non-binary char
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
// 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.
|
2020-05-09 20:58:08 +00:00
|
|
|
static void parse_hex_literal(void) // Now GotByte = "$" or "x"
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
|
|
|
char byte;
|
2020-04-28 11:18:22 +00:00
|
|
|
int digits = -1, // digit counter
|
2020-04-28 16:02:09 +00:00
|
|
|
flags = NUMBER_IS_DEFINED;
|
2012-02-27 21:14:46 +00:00
|
|
|
intval_t value = 0;
|
|
|
|
|
2020-04-28 11:18:22 +00:00
|
|
|
for (;;) {
|
2014-11-30 17:00:13 +00:00
|
|
|
++digits;
|
2012-02-27 21:14:46 +00:00
|
|
|
byte = GetByte();
|
2020-04-28 11:18:22 +00:00
|
|
|
// if digit or legal character, add value
|
2012-02-27 21:14:46 +00:00
|
|
|
if ((byte >= '0') && (byte <= '9')) {
|
|
|
|
value = (value << 4) + (byte - '0');
|
2020-04-28 11:18:22 +00:00
|
|
|
continue;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
if ((byte >= 'a') && (byte <= 'f')) {
|
|
|
|
value = (value << 4) + (byte - 'a') + 10;
|
2020-04-28 11:18:22 +00:00
|
|
|
continue;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
2020-04-28 11:18:22 +00:00
|
|
|
if ((byte >= 'A') && (byte <= 'F')) {
|
|
|
|
value = (value << 4) + (byte - 'A') + 10;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break; // found illegal character
|
|
|
|
}
|
2020-05-13 23:45:03 +00:00
|
|
|
if (!digits)
|
2020-05-24 21:16:50 +00:00
|
|
|
Throw_warning("Hex literal without any digits."); // FIXME - make into error!
|
2012-02-27 21:14:46 +00:00
|
|
|
// set force bits
|
2020-04-14 00:28:31 +00:00
|
|
|
if (config.honor_leading_zeroes) {
|
2019-02-01 11:23:28 +00:00
|
|
|
if (digits > 2) {
|
|
|
|
if (digits > 4) {
|
|
|
|
if (value < 65536)
|
2020-04-28 16:02:09 +00:00
|
|
|
flags |= NUMBER_FORCES_24;
|
2019-02-01 11:23:28 +00:00
|
|
|
} else {
|
|
|
|
if (value < 256)
|
2020-04-28 16:02:09 +00:00
|
|
|
flags |= NUMBER_FORCES_16;
|
2019-02-01 11:23:28 +00:00
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-09 20:58:08 +00:00
|
|
|
PUSH_INT_ARG(value, flags, 0);
|
2012-02-27 21:14:46 +00:00
|
|
|
// Now GotByte = non-hexadecimal char
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// parse fractional part of a floating-point value
|
|
|
|
static void parse_frac_part(int integer_part) // Now GotByte = first digit after decimal point
|
|
|
|
{
|
|
|
|
double denominator = 1,
|
|
|
|
fpval = integer_part;
|
|
|
|
|
|
|
|
// parse digits until no more
|
|
|
|
while ((GotByte >= '0') && (GotByte <= '9')) {
|
|
|
|
fpval = 10 * fpval + (GotByte & 15); // this works. it's ASCII.
|
|
|
|
denominator *= 10;
|
|
|
|
GetByte();
|
|
|
|
}
|
|
|
|
// FIXME - add possibility to read 'e' and exponent!
|
2020-05-09 20:58:08 +00:00
|
|
|
PUSH_FP_ARG(fpval / denominator, NUMBER_IS_DEFINED);
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Parse a decimal value. As decimal values don't use any prefixes, this
|
2016-02-21 23:08:02 +00:00
|
|
|
// function expects the first digit to be read already.
|
|
|
|
// If the first two digits are "0x", this function branches to the one for
|
|
|
|
// parsing hexadecimal values.
|
|
|
|
// If the first two digits are "0b", this function branches to the one for
|
|
|
|
// parsing binary values.
|
|
|
|
// If a decimal point is read, this function branches to the one for parsing
|
|
|
|
// floating-point values.
|
2012-02-27 21:14:46 +00:00
|
|
|
// This function accepts '0' through '9' and one dot ('.') as the decimal
|
|
|
|
// point. The current value is stored as soon as a character is read that is
|
|
|
|
// none of those given above. Float usage is only activated when a decimal
|
|
|
|
// 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.
|
2020-05-09 20:58:08 +00:00
|
|
|
static void parse_number_literal(void) // Now GotByte = first digit
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
|
|
|
intval_t intval = (GotByte & 15); // this works. it's ASCII.
|
|
|
|
|
|
|
|
GetByte();
|
2014-05-31 00:46:55 +00:00
|
|
|
// check for "0b" (binary) and "0x" (hexadecimal) prefixes
|
|
|
|
if (intval == 0) {
|
|
|
|
if (GotByte == 'b') {
|
2020-05-09 20:58:08 +00:00
|
|
|
parse_binary_literal();
|
2014-05-31 00:46:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (GotByte == 'x') {
|
2020-05-09 20:58:08 +00:00
|
|
|
parse_hex_literal();
|
2014-05-31 00:46:55 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
// parse digits until no more
|
|
|
|
while ((GotByte >= '0') && (GotByte <= '9')) {
|
|
|
|
intval = 10 * intval + (GotByte & 15); // ASCII, see above
|
|
|
|
GetByte();
|
|
|
|
}
|
|
|
|
// check whether it's a float
|
|
|
|
if (GotByte == '.') {
|
|
|
|
// read fractional part
|
|
|
|
GetByte();
|
|
|
|
parse_frac_part(intval);
|
|
|
|
} else {
|
2020-05-09 20:58:08 +00:00
|
|
|
PUSH_INT_ARG(intval, NUMBER_IS_DEFINED, 0);
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
// 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.
|
2020-05-27 20:25:42 +00:00
|
|
|
static void parse_octal_literal(void) // Now GotByte = first octal digit
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
|
|
|
intval_t value = 0;
|
2020-04-28 16:02:09 +00:00
|
|
|
int flags = NUMBER_IS_DEFINED,
|
2012-02-27 21:14:46 +00:00
|
|
|
digits = 0; // digit counter
|
|
|
|
|
|
|
|
while ((GotByte >= '0') && (GotByte <= '7')) {
|
|
|
|
value = (value << 3) + (GotByte & 7); // this works. it's ASCII.
|
2014-11-30 17:00:13 +00:00
|
|
|
++digits;
|
2012-02-27 21:14:46 +00:00
|
|
|
GetByte();
|
|
|
|
}
|
|
|
|
// set force bits
|
2020-04-14 00:28:31 +00:00
|
|
|
if (config.honor_leading_zeroes) {
|
2019-02-01 11:23:28 +00:00
|
|
|
if (digits > 3) {
|
|
|
|
if (digits > 6) {
|
|
|
|
if (value < 65536)
|
2020-04-28 16:02:09 +00:00
|
|
|
flags |= NUMBER_FORCES_24;
|
2019-02-01 11:23:28 +00:00
|
|
|
} else {
|
|
|
|
if (value < 256)
|
2020-04-28 16:02:09 +00:00
|
|
|
flags |= NUMBER_FORCES_16;
|
2019-02-01 11:23:28 +00:00
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-09 20:58:08 +00:00
|
|
|
PUSH_INT_ARG(value, flags, 0);
|
2012-02-27 21:14:46 +00:00
|
|
|
// Now GotByte = non-octal char
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Parse function call (sin(), cos(), arctan(), ...)
|
|
|
|
static void parse_function_call(void)
|
|
|
|
{
|
|
|
|
void *node_body;
|
|
|
|
|
|
|
|
// make lower case version of name in local dynamic buffer
|
|
|
|
DynaBuf_to_lower(function_dyna_buf, GlobalDynaBuf);
|
|
|
|
// search for tree item
|
2017-10-29 23:29:07 +00:00
|
|
|
if (Tree_easy_scan(function_tree, &node_body, function_dyna_buf)) {
|
2020-05-09 20:58:08 +00:00
|
|
|
PUSH_OP((struct op *) node_body);
|
2017-10-29 23:29:07 +00:00
|
|
|
} else {
|
2012-02-27 21:14:46 +00:00
|
|
|
Throw_error("Unknown function.");
|
2017-10-29 23:29:07 +00:00
|
|
|
alu_state = STATE_ERROR;
|
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-19 20:41:12 +00:00
|
|
|
// make empty list
|
|
|
|
static void list_init_list(struct object *self)
|
|
|
|
{
|
|
|
|
self->type = &type_list;
|
|
|
|
self->u.listhead = safe_malloc(sizeof(*(self->u.listhead)));
|
|
|
|
self->u.listhead->next = self->u.listhead;
|
|
|
|
self->u.listhead->prev = self->u.listhead;
|
|
|
|
self->u.listhead->length = 0;
|
|
|
|
self->u.listhead->refs = 1;
|
|
|
|
}
|
2020-06-06 12:01:44 +00:00
|
|
|
// extend list by appending a single object
|
|
|
|
static void list_append_object(struct listitem *head, struct object *obj)
|
|
|
|
{
|
|
|
|
struct listitem *item;
|
|
|
|
|
|
|
|
item = safe_malloc(sizeof(*item));
|
|
|
|
item->payload = *obj;
|
|
|
|
item->next = head;
|
|
|
|
item->prev = head->prev;
|
|
|
|
item->next->prev = item;
|
|
|
|
item->prev->next = item;
|
|
|
|
head->length++;
|
|
|
|
}
|
|
|
|
// extend list by appending all items of a list
|
|
|
|
static void list_append_list(struct listitem *selfhead, struct listitem *otherhead)
|
|
|
|
{
|
|
|
|
struct listitem *item;
|
|
|
|
|
|
|
|
if (selfhead == otherhead)
|
|
|
|
Bug_found("ExtendingListWithItself", 0); // TODO - add to docs!
|
|
|
|
item = otherhead->next;
|
|
|
|
while (item != otherhead) {
|
|
|
|
list_append_object(selfhead, &item->payload);
|
|
|
|
item = item->next;
|
|
|
|
}
|
|
|
|
}
|
2020-05-19 20:41:12 +00:00
|
|
|
|
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// expression parser
|
|
|
|
|
|
|
|
|
2020-05-28 18:40:40 +00:00
|
|
|
// helper function for "monadic &" (either octal value or "unpseudo" operator)
|
|
|
|
// returns nonzero on error
|
|
|
|
static int parse_octal_or_unpseudo(void) // now GotByte = '&'
|
|
|
|
{
|
|
|
|
unsigned int unpseudo_count = 1;
|
|
|
|
|
|
|
|
while (GetByte() == '&')
|
|
|
|
++unpseudo_count;
|
|
|
|
if ((unpseudo_count == 1) && (GotByte >= '0') & (GotByte <= '7')) {
|
|
|
|
parse_octal_literal(); // now GotByte = non-octal char
|
|
|
|
return 0; // ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO - support anonymous labels as well?
|
|
|
|
if (GotByte == '.') {
|
|
|
|
GetByte();
|
|
|
|
if (Input_read_keyword() == 0) // now GotByte = illegal char
|
|
|
|
return 1; // error (no string given)
|
|
|
|
|
|
|
|
get_symbol_value(section_now->local_scope, LOCAL_PREFIX, GlobalDynaBuf->size - 1, unpseudo_count); // -1 to not count terminator
|
|
|
|
} else if (GotByte == CHEAP_PREFIX) {
|
|
|
|
GetByte();
|
|
|
|
if (Input_read_keyword() == 0) // now GotByte = illegal char
|
|
|
|
return 1; // error (no string given)
|
|
|
|
|
|
|
|
get_symbol_value(section_now->cheap_scope, CHEAP_PREFIX, GlobalDynaBuf->size - 1, unpseudo_count); // -1 to not count terminator
|
|
|
|
} else if (BYTE_STARTS_KEYWORD(GotByte)) {
|
|
|
|
Input_read_keyword(); // now GotByte = illegal char
|
|
|
|
get_symbol_value(SCOPE_GLOBAL, '\0', GlobalDynaBuf->size - 1, unpseudo_count); // no prefix, -1 to not count terminator
|
|
|
|
} else {
|
|
|
|
Throw_error(exception_missing_string);
|
|
|
|
return 1; // error
|
|
|
|
}
|
|
|
|
return 0; // ok
|
|
|
|
}
|
|
|
|
|
2020-05-09 20:58:08 +00:00
|
|
|
// Expect argument or monadic operator (hopefully inlined)
|
2020-04-26 22:26:05 +00:00
|
|
|
// returns TRUE if it ate any non-space (-> so expression isn't empty)
|
|
|
|
// returns FALSE if first non-space is delimiter (-> end of expression)
|
2020-05-09 20:58:08 +00:00
|
|
|
static boolean expect_argument_or_monadic_operator(void)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2020-05-09 20:58:08 +00:00
|
|
|
struct op *op;
|
2015-06-14 23:16:23 +00:00
|
|
|
int ugly_length_kluge;
|
2020-05-01 21:01:23 +00:00
|
|
|
boolean perform_negation;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
SKIPSPACE();
|
|
|
|
switch (GotByte) {
|
|
|
|
case '+': // anonymous forward label
|
|
|
|
// count plus signs to build name of anonymous label
|
|
|
|
DYNABUF_CLEAR(GlobalDynaBuf);
|
|
|
|
do
|
|
|
|
DYNABUF_APPEND(GlobalDynaBuf, '+');
|
|
|
|
while (GetByte() == '+');
|
2015-06-14 23:16:23 +00:00
|
|
|
ugly_length_kluge = GlobalDynaBuf->size; // FIXME - get rid of this!
|
2014-06-07 00:12:10 +00:00
|
|
|
symbol_fix_forward_anon_name(FALSE); // FALSE: do not increment counter
|
2020-05-28 18:40:40 +00:00
|
|
|
get_symbol_value(section_now->local_scope, '\0', ugly_length_kluge, 0); // no prefix, no unpseudo
|
2020-05-09 20:58:08 +00:00
|
|
|
goto now_expect_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
case '-': // NEGATION operator or anonymous backward label
|
|
|
|
// count minus signs in case it's an anonymous backward label
|
|
|
|
perform_negation = FALSE;
|
|
|
|
DYNABUF_CLEAR(GlobalDynaBuf);
|
|
|
|
do {
|
|
|
|
DYNABUF_APPEND(GlobalDynaBuf, '-');
|
|
|
|
perform_negation = !perform_negation;
|
|
|
|
} while (GetByte() == '-');
|
|
|
|
SKIPSPACE();
|
2020-04-28 11:18:22 +00:00
|
|
|
if (BYTE_FOLLOWS_ANON(GotByte)) {
|
2012-02-27 21:14:46 +00:00
|
|
|
DynaBuf_append(GlobalDynaBuf, '\0');
|
2020-05-28 18:40:40 +00:00
|
|
|
get_symbol_value(section_now->local_scope, '\0', GlobalDynaBuf->size - 1, 0); // no prefix, -1 to not count terminator, no unpseudo
|
2020-05-09 20:58:08 +00:00
|
|
|
goto now_expect_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (perform_negation)
|
2020-05-09 20:58:08 +00:00
|
|
|
PUSH_OP(&ops_negate);
|
2012-02-27 21:14:46 +00:00
|
|
|
// State doesn't change
|
2020-05-27 20:25:42 +00:00
|
|
|
break;//goto done;
|
2012-02-27 21:14:46 +00:00
|
|
|
// Real monadic operators (state doesn't change, still ExpectMonadic)
|
|
|
|
case '!': // NOT operator
|
2020-05-09 20:58:08 +00:00
|
|
|
op = &ops_not;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_monadic;
|
|
|
|
|
|
|
|
case '<': // LOWBYTE operator
|
2020-05-14 14:01:30 +00:00
|
|
|
op = &ops_low_byte_of;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_monadic;
|
|
|
|
|
|
|
|
case '>': // HIGHBYTE operator
|
2020-05-14 14:01:30 +00:00
|
|
|
op = &ops_high_byte_of;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_monadic;
|
|
|
|
|
|
|
|
case '^': // BANKBYTE operator
|
2020-05-14 14:01:30 +00:00
|
|
|
op = &ops_bank_byte_of;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_monadic;
|
|
|
|
|
2020-05-19 20:41:12 +00:00
|
|
|
// special operators
|
|
|
|
case '[': // start of list literal
|
|
|
|
list_init_list(&arg_stack[arg_sp++]); // put empty list on arg stack
|
|
|
|
NEXTANDSKIPSPACE();
|
|
|
|
if (GotByte == ']') {
|
|
|
|
// list literal is empty, so we're basically done
|
|
|
|
GetByte();
|
|
|
|
alu_state = STATE_EXPECT_DYADIC_OP;
|
|
|
|
} else {
|
|
|
|
// non-empty list literal
|
|
|
|
PUSH_OP(&ops_start_list); // quasi-monadic "start of list", makes sure earlier ops do not process empty list
|
|
|
|
PUSH_OP(&ops_list_append); // dyadic "append to list", so next arg will be appended to list
|
|
|
|
// no need to TRY_TO_REDUCE_STACKS, because we know the one pushed first has a lower priority anyway
|
|
|
|
//stay in STATE_EXPECT_ARG_OR_MONADIC_OP
|
|
|
|
}
|
2020-05-27 20:25:42 +00:00
|
|
|
break;//goto done;
|
2020-05-19 20:41:12 +00:00
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
case '(': // left parenthesis
|
2020-05-19 20:41:12 +00:00
|
|
|
op = &ops_left_parenthesis;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_monadic;
|
|
|
|
|
2020-05-09 20:58:08 +00:00
|
|
|
// arguments (state changes to ExpectDyadic)
|
2020-05-19 20:41:12 +00:00
|
|
|
case '"': // character (old) or string (new)
|
|
|
|
case '\'': // character
|
2012-02-27 21:14:46 +00:00
|
|
|
// Character will be converted using current encoding
|
2020-05-19 20:41:12 +00:00
|
|
|
parse_quoted(GotByte);
|
2012-02-27 21:14:46 +00:00
|
|
|
// Now GotByte = char following closing quote
|
2020-05-09 20:58:08 +00:00
|
|
|
goto now_expect_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
case '%': // Binary value
|
2020-05-09 20:58:08 +00:00
|
|
|
parse_binary_literal(); // Now GotByte = non-binary char
|
|
|
|
goto now_expect_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-28 18:40:40 +00:00
|
|
|
case '&': // octal value or "unpseudo" operator applied to label
|
|
|
|
if (parse_octal_or_unpseudo() == 0)
|
|
|
|
goto now_expect_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-28 18:40:40 +00:00
|
|
|
// if we're here, there was an error (like "no string given"):
|
|
|
|
alu_state = STATE_ERROR;
|
|
|
|
break;//goto done;
|
2012-02-27 21:14:46 +00:00
|
|
|
case '$': // Hexadecimal value
|
2020-05-09 20:58:08 +00:00
|
|
|
parse_hex_literal();
|
2012-02-27 21:14:46 +00:00
|
|
|
// Now GotByte = non-hexadecimal char
|
2020-05-09 20:58:08 +00:00
|
|
|
goto now_expect_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
case '*': // Program counter
|
2014-03-11 12:07:11 +00:00
|
|
|
parse_program_counter();
|
2012-02-27 21:14:46 +00:00
|
|
|
// Now GotByte = char after closing quote
|
2020-05-09 20:58:08 +00:00
|
|
|
goto now_expect_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2013-06-26 23:01:00 +00:00
|
|
|
// FIXME - find a way to tell decimal point and LOCAL_PREFIX apart!
|
2014-06-07 00:12:10 +00:00
|
|
|
case '.': // local symbol or fractional part of float value
|
2012-02-27 21:14:46 +00:00
|
|
|
GetByte(); // start after '.'
|
|
|
|
// check for fractional part of float value
|
|
|
|
if ((GotByte >= '0') && (GotByte <= '9')) {
|
2020-05-28 18:40:40 +00:00
|
|
|
parse_frac_part(0); // now GotByte = non-decimal char
|
2020-05-09 20:58:08 +00:00
|
|
|
goto now_expect_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 18:40:40 +00:00
|
|
|
if (Input_read_keyword()) { // now GotByte = illegal char
|
|
|
|
get_symbol_value(section_now->local_scope, LOCAL_PREFIX, GlobalDynaBuf->size - 1, 0); // -1 to not count terminator, no unpseudo
|
|
|
|
goto now_expect_dyadic_op; // ok
|
2017-10-29 23:29:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if we're here, Input_read_keyword() will have thrown an error (like "no string given"):
|
|
|
|
alu_state = STATE_ERROR;
|
2020-05-27 20:25:42 +00:00
|
|
|
break;//goto done;
|
2017-10-29 23:29:07 +00:00
|
|
|
case CHEAP_PREFIX: // cheap local symbol
|
|
|
|
//printf("looking in cheap scope %d\n", section_now->cheap_scope);
|
|
|
|
GetByte(); // start after '@'
|
2020-05-28 18:40:40 +00:00
|
|
|
if (Input_read_keyword()) { // now GotByte = illegal char
|
|
|
|
get_symbol_value(section_now->cheap_scope, CHEAP_PREFIX, GlobalDynaBuf->size - 1, 0); // -1 to not count terminator, no unpseudo
|
|
|
|
goto now_expect_dyadic_op; // ok
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
2017-10-29 23:29:07 +00:00
|
|
|
// if we're here, Input_read_keyword() will have thrown an error (like "no string given"):
|
2012-02-27 21:14:46 +00:00
|
|
|
alu_state = STATE_ERROR;
|
2020-05-27 20:25:42 +00:00
|
|
|
break;//goto done;
|
2014-06-07 00:12:10 +00:00
|
|
|
// decimal values and global symbols
|
2012-02-27 21:14:46 +00:00
|
|
|
default: // all other characters
|
|
|
|
if ((GotByte >= '0') && (GotByte <= '9')) {
|
2020-05-09 20:58:08 +00:00
|
|
|
parse_number_literal();
|
2012-02-27 21:14:46 +00:00
|
|
|
// Now GotByte = non-decimal char
|
2020-05-09 20:58:08 +00:00
|
|
|
goto now_expect_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
2020-04-28 11:18:22 +00:00
|
|
|
if (BYTE_STARTS_KEYWORD(GotByte)) {
|
2012-02-27 21:14:46 +00:00
|
|
|
register int length;
|
2016-02-21 23:08:02 +00:00
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
// Read global label (or "NOT")
|
|
|
|
length = Input_read_keyword();
|
|
|
|
// Now GotByte = illegal char
|
|
|
|
// Check for NOT. Okay, it's hardcoded,
|
|
|
|
// but so what? Sue me...
|
|
|
|
if ((length == 3)
|
|
|
|
&& ((GlobalDynaBuf->buffer[0] | 32) == 'n')
|
|
|
|
&& ((GlobalDynaBuf->buffer[1] | 32) == 'o')
|
|
|
|
&& ((GlobalDynaBuf->buffer[2] | 32) == 't')) {
|
2020-05-09 20:58:08 +00:00
|
|
|
PUSH_OP(&ops_not);
|
2012-02-27 21:14:46 +00:00
|
|
|
// state doesn't change
|
|
|
|
} else {
|
|
|
|
if (GotByte == '(') {
|
|
|
|
parse_function_call();
|
2014-12-16 08:21:44 +00:00
|
|
|
// i thought about making the parentheses optional, so you can write "a = sin b"
|
|
|
|
// just like "a = not b". but then each new function name would have to be made
|
|
|
|
// a reserved keyword, otherwise stuff like "a = sin * < b" would be ambiguous:
|
|
|
|
// it could mean either "compare sine of PC to b" or "multiply 'sin' by low byte
|
|
|
|
// of b".
|
|
|
|
// however, apart from that check above, function calls have nothing to do with
|
|
|
|
// parentheses: "sin(x+y)" gets parsed just like "not(x+y)".
|
2012-02-27 21:14:46 +00:00
|
|
|
} else {
|
2020-05-28 18:40:40 +00:00
|
|
|
get_symbol_value(SCOPE_GLOBAL, '\0', GlobalDynaBuf->size - 1, 0); // no prefix, -1 to not count terminator, no unpseudo
|
2020-05-09 20:58:08 +00:00
|
|
|
goto now_expect_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// illegal character read - so don't go on
|
2020-05-09 20:58:08 +00:00
|
|
|
// we found end-of-expression instead of an argument,
|
2017-10-21 19:59:56 +00:00
|
|
|
// that's either an empty expression or an erroneous one!
|
2020-05-19 20:41:12 +00:00
|
|
|
PUSH_INT_ARG(0, 0, 0); // push dummy argument so stack checking code won't bark
|
|
|
|
if (op_stack[op_sp - 1] == &ops_start_expression) {
|
|
|
|
PUSH_OP(&ops_end_expression);
|
2012-02-27 21:14:46 +00:00
|
|
|
alu_state = STATE_TRY_TO_REDUCE_STACKS;
|
|
|
|
} else {
|
|
|
|
Throw_error(exception_syntax);
|
|
|
|
alu_state = STATE_ERROR;
|
|
|
|
}
|
2020-04-26 22:26:05 +00:00
|
|
|
return FALSE; // found delimiter
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
2020-05-27 20:25:42 +00:00
|
|
|
break;//goto done;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
// no other possibilities, so here are the shared endings
|
|
|
|
|
|
|
|
get_byte_and_push_monadic:
|
|
|
|
GetByte();
|
2020-05-09 20:58:08 +00:00
|
|
|
PUSH_OP(op);
|
2012-02-27 21:14:46 +00:00
|
|
|
// State doesn't change
|
|
|
|
break;
|
|
|
|
|
2020-05-09 20:58:08 +00:00
|
|
|
now_expect_dyadic_op:
|
2017-10-29 23:29:07 +00:00
|
|
|
// bugfix: if in error state, do not change state back to valid one
|
|
|
|
if (alu_state < STATE_MAX_GO_ON)
|
2020-05-09 20:58:08 +00:00
|
|
|
alu_state = STATE_EXPECT_DYADIC_OP;
|
2012-02-27 21:14:46 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-05-27 20:25:42 +00:00
|
|
|
//done:
|
2020-04-26 22:26:05 +00:00
|
|
|
return TRUE; // parsed something
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Expect dyadic operator (hopefully inlined)
|
|
|
|
static void expect_dyadic_operator(void)
|
|
|
|
{
|
2014-03-11 12:07:11 +00:00
|
|
|
void *node_body;
|
2020-05-09 20:58:08 +00:00
|
|
|
struct op *op;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
SKIPSPACE();
|
|
|
|
switch (GotByte) {
|
|
|
|
// Single-character dyadic operators
|
|
|
|
case '^': // "to the power of"
|
2020-05-09 20:58:08 +00:00
|
|
|
op = &ops_powerof;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
case '+': // add
|
2020-05-09 20:58:08 +00:00
|
|
|
op = &ops_add;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
case '-': // subtract
|
2020-05-09 20:58:08 +00:00
|
|
|
op = &ops_subtract;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
case '*': // multiply
|
2020-05-09 20:58:08 +00:00
|
|
|
op = &ops_multiply;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
case '/': // divide
|
2020-05-09 20:58:08 +00:00
|
|
|
op = &ops_divide;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
case '%': // modulo
|
2020-05-09 20:58:08 +00:00
|
|
|
op = &ops_modulo;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
case '&': // bitwise AND
|
2020-05-09 20:58:08 +00:00
|
|
|
op = &ops_and;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
case '|': // bitwise OR
|
2020-05-09 20:58:08 +00:00
|
|
|
op = &ops_or;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
// This part is commented out because there is no XOR character defined
|
|
|
|
// case ???: // bitwise exclusive OR
|
2020-05-09 20:58:08 +00:00
|
|
|
// op = &ops_xor;
|
2012-02-27 21:14:46 +00:00
|
|
|
// goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
case '=': // is equal
|
2020-05-09 20:58:08 +00:00
|
|
|
op = &ops_equals;
|
2014-05-06 22:16:41 +00:00
|
|
|
// if it's "==", accept but warn
|
|
|
|
if (GetByte() == '=') {
|
2014-05-31 00:46:55 +00:00
|
|
|
Throw_first_pass_warning("C-style \"==\" comparison detected.");
|
2014-05-06 22:16:41 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
}
|
2020-05-09 20:58:08 +00:00
|
|
|
goto push_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-15 15:33:00 +00:00
|
|
|
case '[': // indexing operator
|
|
|
|
GetByte(); // eat char
|
|
|
|
PUSH_OP(&ops_atindex); // first put high-priority dyadic on stack,
|
2020-05-19 20:41:12 +00:00
|
|
|
PUSH_OP(&ops_start_index); // then low-priority special ops_start_index
|
|
|
|
// FIXME! this would work reliably if "atindex" had the highest priority.
|
|
|
|
// but function calls have higher priority than indexing:
|
|
|
|
// fn(a+b)[c] -> fn d [c] -> e [c], but the code above would return fn(d[c]) instead
|
|
|
|
// atm, it's not a problem, because all functions return numbers, and numbers cannot
|
|
|
|
// be indexed anyway, but in the long run, this must be fixed.
|
|
|
|
// maybe call "try_to_reduce_stacks" inbetween the two PUSH_OPs above?
|
|
|
|
// or maybe add a PUSH_DYADIC_AND_TRY_TO_REDUCE(op) macro?
|
2020-05-15 15:33:00 +00:00
|
|
|
alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP;
|
|
|
|
return;
|
2020-05-19 20:41:12 +00:00
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
// Multi-character dyadic operators
|
|
|
|
case '!': // "!="
|
|
|
|
if (GetByte() == '=') {
|
2020-05-14 14:01:30 +00:00
|
|
|
op = &ops_not_equal;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
}
|
|
|
|
|
|
|
|
Throw_error(exception_syntax);
|
|
|
|
alu_state = STATE_ERROR;
|
2020-05-27 20:25:42 +00:00
|
|
|
break;//goto end;
|
2012-02-27 21:14:46 +00:00
|
|
|
case '<': // "<", "<=", "<<" and "<>"
|
|
|
|
switch (GetByte()) {
|
|
|
|
case '=': // "<=", less or equal
|
2020-05-14 14:01:30 +00:00
|
|
|
op = &ops_less_or_equal;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
case '<': // "<<", shift left
|
2020-05-14 14:01:30 +00:00
|
|
|
op = &ops_shift_left;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
case '>': // "<>", not equal
|
2020-05-14 14:01:30 +00:00
|
|
|
op = &ops_not_equal;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
default: // "<", less than
|
2020-05-14 14:01:30 +00:00
|
|
|
op = &ops_less_than;
|
2020-05-09 20:58:08 +00:00
|
|
|
goto push_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
//break; unreachable
|
|
|
|
case '>': // ">", ">=", ">>", ">>>" and "><"
|
|
|
|
switch (GetByte()) {
|
|
|
|
case '=': // ">=", greater or equal
|
2020-05-14 14:01:30 +00:00
|
|
|
op = &ops_greater_or_equal;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
case '<': // "><", not equal
|
2020-05-14 14:01:30 +00:00
|
|
|
op = &ops_not_equal;
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
case '>': // ">>" or ">>>", shift right
|
2020-05-09 20:58:08 +00:00
|
|
|
op = &ops_asr; // arithmetic shift right
|
2012-02-27 21:14:46 +00:00
|
|
|
if (GetByte() != '>')
|
2020-05-09 20:58:08 +00:00
|
|
|
goto push_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-09 20:58:08 +00:00
|
|
|
op = &ops_lsr; // logical shift right
|
2012-02-27 21:14:46 +00:00
|
|
|
goto get_byte_and_push_dyadic;
|
|
|
|
|
|
|
|
default: // ">", greater than
|
2020-05-14 14:01:30 +00:00
|
|
|
op = &ops_greater_than;
|
2020-05-09 20:58:08 +00:00
|
|
|
goto push_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
//break; unreachable
|
|
|
|
// end of expression or text version of dyadic operator
|
|
|
|
default:
|
2014-06-02 00:47:46 +00:00
|
|
|
// check string versions of operators
|
2020-04-28 11:18:22 +00:00
|
|
|
if (BYTE_STARTS_KEYWORD(GotByte)) {
|
2012-02-27 21:14:46 +00:00
|
|
|
Input_read_and_lower_keyword();
|
|
|
|
// Now GotByte = illegal char
|
|
|
|
// search for tree item
|
2020-05-09 20:58:08 +00:00
|
|
|
if (Tree_easy_scan(op_tree, &node_body, GlobalDynaBuf)) {
|
|
|
|
op = node_body;
|
|
|
|
goto push_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Throw_error("Unknown operator.");
|
|
|
|
alu_state = STATE_ERROR;
|
2020-05-27 20:25:42 +00:00
|
|
|
//goto end;
|
2012-02-27 21:14:46 +00:00
|
|
|
} else {
|
2017-10-21 19:59:56 +00:00
|
|
|
// we found end-of-expression when expecting an operator, that's ok.
|
2020-05-19 20:41:12 +00:00
|
|
|
op = &ops_end_expression;
|
2020-05-09 20:58:08 +00:00
|
|
|
goto push_dyadic_op;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-27 20:25:42 +00:00
|
|
|
//end:
|
|
|
|
return; // TODO - change the two points that go here and add a Bug_found() instead
|
2012-02-27 21:14:46 +00:00
|
|
|
|
|
|
|
// shared endings
|
|
|
|
get_byte_and_push_dyadic:
|
|
|
|
GetByte();
|
2020-05-09 20:58:08 +00:00
|
|
|
push_dyadic_op:
|
|
|
|
PUSH_OP(op);
|
2012-02-27 21:14:46 +00:00
|
|
|
alu_state = STATE_TRY_TO_REDUCE_STACKS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-15 11:39:00 +00:00
|
|
|
// helper function: create and output error message about (argument/)operator/argument combination
|
|
|
|
static void unsupported_operation(struct object *optional, struct op *op, struct object *arg)
|
2020-05-14 14:01:30 +00:00
|
|
|
{
|
2020-05-15 11:39:00 +00:00
|
|
|
if (optional) {
|
|
|
|
if (op->group != OPGROUP_DYADIC)
|
2020-05-24 21:16:50 +00:00
|
|
|
Bug_found("OperatorIsNotDyadic", op->id);
|
2020-05-15 11:39:00 +00:00
|
|
|
} else {
|
|
|
|
if (op->group != OPGROUP_MONADIC)
|
2020-05-24 21:16:50 +00:00
|
|
|
Bug_found("OperatorIsNotMonadic", op->id);
|
2020-05-15 11:39:00 +00:00
|
|
|
}
|
|
|
|
DYNABUF_CLEAR(errormsg_dyna_buf);
|
2020-05-24 21:16:50 +00:00
|
|
|
DynaBuf_add_string(errormsg_dyna_buf, "Operation not supported: Cannot apply \"");
|
2020-05-15 11:39:00 +00:00
|
|
|
DynaBuf_add_string(errormsg_dyna_buf, op->text_version);
|
|
|
|
DynaBuf_add_string(errormsg_dyna_buf, "\" to \"");
|
|
|
|
if (optional) {
|
|
|
|
DynaBuf_add_string(errormsg_dyna_buf, optional->type->name);
|
|
|
|
DynaBuf_add_string(errormsg_dyna_buf, "\" and \"");
|
|
|
|
}
|
|
|
|
DynaBuf_add_string(errormsg_dyna_buf, arg->type->name);
|
|
|
|
DynaBuf_add_string(errormsg_dyna_buf, "\".");
|
|
|
|
DynaBuf_append(errormsg_dyna_buf, '\0');
|
|
|
|
Throw_error(errormsg_dyna_buf->buffer);
|
2020-05-14 14:01:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// int/float
|
|
|
|
|
2020-05-26 12:55:14 +00:00
|
|
|
// int:
|
|
|
|
// create byte-sized int object (for comparison results, converted characters, ...)
|
|
|
|
static void int_create_byte(struct object *self, intval_t byte)
|
|
|
|
{
|
|
|
|
self->type = &type_int;
|
|
|
|
self->u.number.flags = NUMBER_IS_DEFINED | NUMBER_FITS_BYTE;
|
|
|
|
self->u.number.val.intval = byte;
|
|
|
|
self->u.number.addr_refs = 0;
|
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// int:
|
|
|
|
// convert to float
|
2020-05-13 23:26:40 +00:00
|
|
|
inline static void int_to_float(struct object *self)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2020-05-13 23:26:40 +00:00
|
|
|
self->type = &type_float;
|
|
|
|
self->u.number.val.fpval = self->u.number.val.intval;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// float:
|
|
|
|
// convert to int
|
2020-05-13 23:26:40 +00:00
|
|
|
inline static void float_to_int(struct object *self)
|
2020-05-10 12:33:41 +00:00
|
|
|
{
|
2020-05-13 23:26:40 +00:00
|
|
|
self->type = &type_int;
|
|
|
|
self->u.number.val.intval = self->u.number.val.fpval;
|
2020-05-10 12:33:41 +00:00
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// int/float:
|
2020-05-13 23:26:40 +00:00
|
|
|
// return DEFINED flag
|
|
|
|
static boolean number_is_defined(struct object *self)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2020-05-13 23:26:40 +00:00
|
|
|
return !!(self->u.number.flags & NUMBER_IS_DEFINED);
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
2020-05-19 20:41:12 +00:00
|
|
|
// list/string:
|
|
|
|
// ...are always considered "defined"
|
|
|
|
static boolean object_return_true(struct object *self)
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
Throw_first_pass_warning("Converted to integer for binary logic operator.");
|
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// int:
|
2020-05-09 23:01:51 +00:00
|
|
|
// handle monadic operator (includes functions)
|
2020-05-14 00:04:20 +00:00
|
|
|
static void int_handle_monadic_operator(struct object *self, struct op *op)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2020-05-10 21:35:50 +00:00
|
|
|
int refs = 0; // default for "addr_refs", shortens this fn
|
|
|
|
|
2020-05-14 10:19:21 +00:00
|
|
|
switch (op->id) {
|
|
|
|
case OPID_ADDRESS:
|
2020-05-10 21:35:50 +00:00
|
|
|
refs = 1; // result now is an address
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_INT:
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_FLOAT:
|
2020-05-09 23:01:51 +00:00
|
|
|
int_to_float(self);
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_SIN:
|
|
|
|
case OPID_COS:
|
|
|
|
case OPID_TAN:
|
|
|
|
case OPID_ARCSIN:
|
|
|
|
case OPID_ARCCOS:
|
|
|
|
case OPID_ARCTAN:
|
2020-05-09 23:01:51 +00:00
|
|
|
// convert int to fp and ask fp handler to do the work
|
|
|
|
int_to_float(self);
|
2020-05-29 10:57:01 +00:00
|
|
|
type_float.monadic_op(self, op); // TODO - put recursion check around this?
|
2020-05-10 21:35:50 +00:00
|
|
|
return; // float handler has done everything
|
|
|
|
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_NOT:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = ~(self->u.number.val.intval);
|
|
|
|
self->u.number.flags &= ~NUMBER_FITS_BYTE;
|
|
|
|
refs = -(self->u.number.addr_refs); // negate address ref count
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_NEGATE:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = -(self->u.number.val.intval);
|
|
|
|
self->u.number.flags &= ~NUMBER_FITS_BYTE;
|
|
|
|
refs = -(self->u.number.addr_refs); // negate address ref count as well
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_LOWBYTEOF:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.intval) & 255;
|
|
|
|
self->u.number.flags |= NUMBER_FITS_BYTE;
|
|
|
|
self->u.number.flags &= ~NUMBER_FORCEBITS;
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_HIGHBYTEOF:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = ((self->u.number.val.intval) >> 8) & 255;
|
|
|
|
self->u.number.flags |= NUMBER_FITS_BYTE;
|
|
|
|
self->u.number.flags &= ~NUMBER_FORCEBITS;
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_BANKBYTEOF:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = ((self->u.number.val.intval) >> 16) & 255;
|
|
|
|
self->u.number.flags |= NUMBER_FITS_BYTE;
|
|
|
|
self->u.number.flags &= ~NUMBER_FORCEBITS;
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
|
|
|
// add new monadic operators here
|
2020-05-14 10:19:21 +00:00
|
|
|
// case OPID_:
|
2020-05-09 19:26:40 +00:00
|
|
|
// break;
|
|
|
|
default:
|
2020-05-15 11:39:00 +00:00
|
|
|
unsupported_operation(NULL, op, self);
|
2020-05-09 23:01:51 +00:00
|
|
|
}
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.addr_refs = refs; // update address refs with local copy
|
2020-05-09 23:01:51 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// float:
|
2020-05-09 23:01:51 +00:00
|
|
|
// helper function for asin/acos:
|
|
|
|
// make sure arg is in [-1, 1] range before calling function
|
2020-05-13 23:26:40 +00:00
|
|
|
static void float_ranged_fn(double (*fn)(double), struct object *self)
|
2020-05-09 23:01:51 +00:00
|
|
|
{
|
2020-05-13 23:26:40 +00:00
|
|
|
if ((self->u.number.val.fpval >= -1) && (self->u.number.val.fpval <= 1)) {
|
|
|
|
self->u.number.val.fpval = fn(self->u.number.val.fpval);
|
2020-05-09 23:01:51 +00:00
|
|
|
} else {
|
2020-05-13 23:26:40 +00:00
|
|
|
if (self->u.number.flags & NUMBER_IS_DEFINED)
|
2020-05-09 23:01:51 +00:00
|
|
|
Throw_error("Argument out of range."); // TODO - add number output to error message
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval = 0;
|
2020-05-09 23:01:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// float:
|
2020-05-09 23:01:51 +00:00
|
|
|
// handle monadic operator (includes functions)
|
2020-05-14 00:04:20 +00:00
|
|
|
static void float_handle_monadic_operator(struct object *self, struct op *op)
|
2020-05-09 23:01:51 +00:00
|
|
|
{
|
2020-05-10 21:35:50 +00:00
|
|
|
int refs = 0; // default for "addr_refs", shortens this fn
|
|
|
|
|
2020-05-14 10:19:21 +00:00
|
|
|
switch (op->id) {
|
|
|
|
case OPID_ADDRESS:
|
2020-05-10 21:35:50 +00:00
|
|
|
refs = 1; // result now is an address
|
2020-05-09 23:01:51 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_INT:
|
2020-05-09 23:01:51 +00:00
|
|
|
float_to_int(self);
|
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_FLOAT:
|
2020-05-09 23:01:51 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_SIN:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval = sin(self->u.number.val.fpval);
|
2020-05-09 23:01:51 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_COS:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval = cos(self->u.number.val.fpval);
|
2020-05-09 23:01:51 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_TAN:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval = tan(self->u.number.val.fpval);
|
2020-05-09 23:01:51 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_ARCSIN:
|
2020-05-09 23:01:51 +00:00
|
|
|
float_ranged_fn(asin, self);
|
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_ARCCOS:
|
2020-05-09 23:01:51 +00:00
|
|
|
float_ranged_fn(acos, self);
|
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_ARCTAN:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval = atan(self->u.number.val.fpval);
|
2020-05-09 23:01:51 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_NEGATE:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval = -(self->u.number.val.fpval);
|
|
|
|
self->u.number.flags &= ~NUMBER_FITS_BYTE;
|
|
|
|
refs = -(self->u.number.addr_refs); // negate address ref count as well
|
2020-05-09 23:01:51 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_NOT:
|
|
|
|
case OPID_LOWBYTEOF:
|
|
|
|
case OPID_HIGHBYTEOF:
|
|
|
|
case OPID_BANKBYTEOF:
|
2020-05-09 23:01:51 +00:00
|
|
|
// convert fp to int and ask int handler to do the work
|
|
|
|
float_to_int(self);
|
2020-05-29 10:57:01 +00:00
|
|
|
type_int.monadic_op(self, op); // TODO - put recursion check around this?
|
2020-05-10 21:35:50 +00:00
|
|
|
return; // int handler has done everything
|
|
|
|
|
2020-05-09 23:01:51 +00:00
|
|
|
// add new monadic operators here
|
2020-05-14 10:19:21 +00:00
|
|
|
// case OPID_:
|
2020-05-09 23:01:51 +00:00
|
|
|
// break;
|
|
|
|
default:
|
2020-05-15 11:39:00 +00:00
|
|
|
unsupported_operation(NULL, op, self);
|
2020-05-09 19:26:40 +00:00
|
|
|
}
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.addr_refs = refs; // update address refs with local copy
|
2020-05-09 19:26:40 +00:00
|
|
|
}
|
|
|
|
|
2020-05-19 20:41:12 +00:00
|
|
|
// list:
|
|
|
|
// handle monadic operator (includes functions)
|
|
|
|
static void list_handle_monadic_operator(struct object *self, struct op *op)
|
|
|
|
{
|
|
|
|
int length;
|
|
|
|
|
|
|
|
if (op->id == OPID_LEN) {
|
|
|
|
length = self->u.listhead->length;
|
|
|
|
self->u.listhead->refs--; // FIXME - call some list_decrement_refs() instead...
|
|
|
|
self->type = &type_int;
|
|
|
|
self->u.number.flags = NUMBER_IS_DEFINED;
|
|
|
|
self->u.number.val.intval = length;
|
|
|
|
self->u.number.addr_refs = 0;
|
|
|
|
} else {
|
|
|
|
unsupported_operation(NULL, op, self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// string:
|
|
|
|
// handle monadic operator (includes functions)
|
|
|
|
static void string_handle_monadic_operator(struct object *self, struct op *op)
|
|
|
|
{
|
|
|
|
int length;
|
|
|
|
|
|
|
|
if (op->id == OPID_LEN) {
|
|
|
|
length = self->u.string->length;
|
|
|
|
self->u.string->refs--; // FIXME - call some string_decrement_refs() instead...
|
|
|
|
self->type = &type_int;
|
|
|
|
self->u.number.flags = NUMBER_IS_DEFINED;
|
|
|
|
self->u.number.val.intval = length;
|
|
|
|
self->u.number.addr_refs = 0;
|
|
|
|
} else {
|
|
|
|
unsupported_operation(NULL, op, self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// int/float:
|
2020-05-10 12:33:41 +00:00
|
|
|
// merge result flags
|
|
|
|
// (used by both int and float handlers for dyadic operators)
|
2020-05-13 23:26:40 +00:00
|
|
|
static void number_fix_result_after_dyadic(struct object *self, struct object *other)
|
2020-05-10 12:33:41 +00:00
|
|
|
{
|
|
|
|
// EVER_UNDEFINED and FORCEBIT flags are ORd together
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.flags |= other->u.number.flags & (NUMBER_EVER_UNDEFINED | NUMBER_FORCEBITS);
|
2020-05-10 12:33:41 +00:00
|
|
|
// DEFINED flags are ANDed together
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.flags &= (other->u.number.flags | ~NUMBER_IS_DEFINED);
|
2020-05-10 12:33:41 +00:00
|
|
|
// FITS_BYTE is cleared
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.flags &= ~NUMBER_FITS_BYTE;
|
2020-05-10 12:33:41 +00:00
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-13 23:26:40 +00:00
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// int:
|
2020-05-10 12:33:41 +00:00
|
|
|
// handle dyadic operator
|
2020-05-14 00:04:20 +00:00
|
|
|
static void int_handle_dyadic_operator(struct object *self, struct op *op, struct object *other)
|
2020-05-10 12:33:41 +00:00
|
|
|
{
|
2020-05-10 21:35:50 +00:00
|
|
|
int refs = 0; // default for "addr_refs", shortens this fn
|
2020-05-10 12:33:41 +00:00
|
|
|
|
|
|
|
// first check type of second arg:
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->type == &type_int) {
|
|
|
|
// ok
|
|
|
|
} else if (other->type == &type_float) {
|
2020-05-10 12:33:41 +00:00
|
|
|
// handle according to operation
|
2020-05-14 10:19:21 +00:00
|
|
|
switch (op->id) {
|
|
|
|
case OPID_POWEROF:
|
|
|
|
case OPID_MULTIPLY:
|
|
|
|
case OPID_DIVIDE:
|
|
|
|
case OPID_INTDIV:
|
|
|
|
case OPID_ADD:
|
|
|
|
case OPID_SUBTRACT:
|
|
|
|
case OPID_EQUALS:
|
2020-05-14 14:01:30 +00:00
|
|
|
case OPID_LESSOREQUAL:
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_LESSTHAN:
|
2020-05-14 14:01:30 +00:00
|
|
|
case OPID_GREATEROREQUAL:
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_GREATERTHAN:
|
|
|
|
case OPID_NOTEQUAL:
|
2020-05-10 12:33:41 +00:00
|
|
|
// become float, delegate to float handler
|
|
|
|
int_to_float(self);
|
2020-05-29 10:57:01 +00:00
|
|
|
type_float.dyadic_op(self, op, other); // TODO - put recursion check around this?
|
2020-05-10 21:35:50 +00:00
|
|
|
return; // float handler has done everything
|
|
|
|
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_MODULO:
|
2020-05-14 14:01:30 +00:00
|
|
|
case OPID_SHIFTLEFT:
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_ASR:
|
2020-05-10 21:35:50 +00:00
|
|
|
// convert other to int
|
2020-05-10 12:33:41 +00:00
|
|
|
float_to_int(other);
|
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_LSR:
|
|
|
|
case OPID_AND:
|
|
|
|
case OPID_OR:
|
|
|
|
case OPID_EOR:
|
|
|
|
case OPID_XOR:
|
2020-05-10 21:35:50 +00:00
|
|
|
// convert other to int, warning user
|
2020-05-10 12:33:41 +00:00
|
|
|
float_to_int(other);
|
|
|
|
warn_float_to_int();
|
|
|
|
break;
|
2020-05-13 23:26:40 +00:00
|
|
|
// add new dyadic operators here:
|
2020-05-14 10:19:21 +00:00
|
|
|
// case OPID_:
|
2020-05-10 12:33:41 +00:00
|
|
|
// break;
|
|
|
|
default:
|
2020-05-15 11:39:00 +00:00
|
|
|
unsupported_operation(self, op, other);
|
2020-05-13 23:26:40 +00:00
|
|
|
return;
|
2020-05-10 12:33:41 +00:00
|
|
|
}
|
2020-05-13 23:26:40 +00:00
|
|
|
// add new types here:
|
|
|
|
// } else if (other->type == &type_) {
|
|
|
|
// ...
|
|
|
|
} else {
|
2020-05-15 11:39:00 +00:00
|
|
|
unsupported_operation(self, op, other);
|
2020-05-13 23:26:40 +00:00
|
|
|
return;
|
2020-05-10 12:33:41 +00:00
|
|
|
}
|
2020-05-13 23:26:40 +00:00
|
|
|
// maybe put this into an extra "int_dyadic_int" function?
|
|
|
|
// sanity check, now "other" must be an int
|
|
|
|
if (other->type != &type_int)
|
2020-05-24 21:16:50 +00:00
|
|
|
Bug_found("SecondArgIsNotAnInt", op->id);
|
2020-05-10 12:33:41 +00:00
|
|
|
|
2020-05-15 11:39:00 +00:00
|
|
|
// part 2: now we got rid of non-ints, perform actual operation:
|
2020-05-14 10:19:21 +00:00
|
|
|
switch (op->id) {
|
|
|
|
case OPID_POWEROF:
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->u.number.val.intval >= 0) {
|
|
|
|
self->u.number.val.intval = my_pow(self->u.number.val.intval, other->u.number.val.intval);
|
2020-05-10 12:33:41 +00:00
|
|
|
} else {
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->u.number.flags & NUMBER_IS_DEFINED)
|
2020-05-10 12:33:41 +00:00
|
|
|
Throw_error("Exponent is negative.");
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = 0;
|
2020-05-10 12:33:41 +00:00
|
|
|
}
|
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_MULTIPLY:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval *= other->u.number.val.intval;
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_DIVIDE:
|
|
|
|
case OPID_INTDIV:
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->u.number.val.intval) {
|
|
|
|
self->u.number.val.intval /= other->u.number.val.intval;
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
// "division by zero" output is below
|
|
|
|
/*FALLTHROUGH*/
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_MODULO:
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->u.number.val.intval) {
|
|
|
|
self->u.number.val.intval %= other->u.number.val.intval;
|
2020-05-10 12:33:41 +00:00
|
|
|
} else {
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->u.number.flags & NUMBER_IS_DEFINED)
|
2020-05-10 12:33:41 +00:00
|
|
|
Throw_error(exception_div_by_zero);
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = 0;
|
2020-05-10 12:33:41 +00:00
|
|
|
}
|
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_ADD:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval += other->u.number.val.intval;
|
|
|
|
refs = self->u.number.addr_refs + other->u.number.addr_refs; // add address references
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_SUBTRACT:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval -= other->u.number.val.intval;
|
|
|
|
refs = self->u.number.addr_refs - other->u.number.addr_refs; // subtract address references
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 14:01:30 +00:00
|
|
|
case OPID_SHIFTLEFT:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval <<= other->u.number.val.intval;
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_ASR:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = my_asr(self->u.number.val.intval, other->u.number.val.intval);
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_LSR:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = ((uintval_t) (self->u.number.val.intval)) >> other->u.number.val.intval;
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 14:01:30 +00:00
|
|
|
case OPID_LESSOREQUAL:
|
2020-06-09 18:52:49 +00:00
|
|
|
// FIXME - all comparison results should clear all force bits and set fits_byte! also for floats!
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.intval <= other->u.number.val.intval);
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_LESSTHAN:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.intval < other->u.number.val.intval);
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 14:01:30 +00:00
|
|
|
case OPID_GREATEROREQUAL:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.intval >= other->u.number.val.intval);
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_GREATERTHAN:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.intval > other->u.number.val.intval);
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_NOTEQUAL:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.intval != other->u.number.val.intval);
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_EQUALS:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.intval == other->u.number.val.intval);
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_AND:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval &= other->u.number.val.intval;
|
|
|
|
refs = self->u.number.addr_refs + other->u.number.addr_refs; // add address references
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_OR:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval |= other->u.number.val.intval;
|
|
|
|
refs = self->u.number.addr_refs + other->u.number.addr_refs; // add address references
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_EOR:
|
2020-05-10 12:33:41 +00:00
|
|
|
Throw_first_pass_warning("\"EOR\" is deprecated; use \"XOR\" instead.");
|
|
|
|
/*FALLTHROUGH*/
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_XOR:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval ^= other->u.number.val.intval;
|
|
|
|
refs = self->u.number.addr_refs + other->u.number.addr_refs; // add address references
|
2020-05-10 12:33:41 +00:00
|
|
|
break;
|
|
|
|
// add new dyadic operators here
|
2020-05-14 10:19:21 +00:00
|
|
|
// case OPID_:
|
2020-05-10 12:33:41 +00:00
|
|
|
// break;
|
|
|
|
default:
|
2020-05-15 11:39:00 +00:00
|
|
|
unsupported_operation(self, op, other);
|
|
|
|
return;
|
2020-05-10 12:33:41 +00:00
|
|
|
}
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.addr_refs = refs; // update address refs with local copy
|
2020-05-10 12:33:41 +00:00
|
|
|
number_fix_result_after_dyadic(self, other); // fix result flags
|
|
|
|
}
|
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// float:
|
2020-05-10 12:33:41 +00:00
|
|
|
// handle dyadic operator
|
2020-05-14 00:04:20 +00:00
|
|
|
static void float_handle_dyadic_operator(struct object *self, struct op *op, struct object *other)
|
2020-05-09 19:26:40 +00:00
|
|
|
{
|
2020-05-10 21:35:50 +00:00
|
|
|
int refs = 0; // default for "addr_refs", shortens this fn
|
|
|
|
|
|
|
|
// first check type of second arg:
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->type == &type_float) {
|
|
|
|
// ok
|
|
|
|
} else if (other->type == &type_int) {
|
2020-05-10 21:35:50 +00:00
|
|
|
// handle according to operation
|
2020-05-14 10:19:21 +00:00
|
|
|
switch (op->id) {
|
2020-05-10 21:35:50 +00:00
|
|
|
// these want two floats
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_POWEROF:
|
|
|
|
case OPID_MULTIPLY:
|
|
|
|
case OPID_DIVIDE:
|
|
|
|
case OPID_INTDIV:
|
|
|
|
case OPID_ADD:
|
|
|
|
case OPID_SUBTRACT:
|
2020-05-14 14:01:30 +00:00
|
|
|
case OPID_LESSOREQUAL:
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_LESSTHAN:
|
2020-05-14 14:01:30 +00:00
|
|
|
case OPID_GREATEROREQUAL:
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_GREATERTHAN:
|
|
|
|
case OPID_NOTEQUAL:
|
|
|
|
case OPID_EQUALS:
|
2020-05-10 21:35:50 +00:00
|
|
|
// convert other to float
|
|
|
|
int_to_float(other);
|
|
|
|
break;
|
|
|
|
// these jump to int handler anyway
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_MODULO:
|
|
|
|
case OPID_LSR:
|
|
|
|
case OPID_AND:
|
|
|
|
case OPID_OR:
|
|
|
|
case OPID_EOR:
|
|
|
|
case OPID_XOR:
|
2020-05-10 21:35:50 +00:00
|
|
|
// these actually want a float and an int
|
2020-05-14 14:01:30 +00:00
|
|
|
case OPID_SHIFTLEFT:
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_ASR:
|
2020-05-10 21:35:50 +00:00
|
|
|
break;
|
|
|
|
// add new dyadic operators here
|
2020-05-14 10:19:21 +00:00
|
|
|
// case OPID_:
|
2020-05-10 21:35:50 +00:00
|
|
|
// break;
|
|
|
|
default:
|
2020-05-15 11:39:00 +00:00
|
|
|
unsupported_operation(self, op, other);
|
2020-05-13 23:26:40 +00:00
|
|
|
return;
|
2020-05-10 21:35:50 +00:00
|
|
|
}
|
2020-05-13 23:26:40 +00:00
|
|
|
// add new types here
|
|
|
|
// } else if (other->type == &type_) {
|
|
|
|
// ...
|
|
|
|
} else {
|
2020-05-15 11:39:00 +00:00
|
|
|
unsupported_operation(self, op, other);
|
2020-05-13 23:26:40 +00:00
|
|
|
return;
|
2020-05-10 21:35:50 +00:00
|
|
|
}
|
|
|
|
|
2020-05-14 10:19:21 +00:00
|
|
|
switch (op->id) {
|
|
|
|
case OPID_POWEROF:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval = pow(self->u.number.val.fpval, other->u.number.val.fpval);
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_MULTIPLY:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval *= other->u.number.val.fpval;
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_DIVIDE:
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->u.number.val.fpval) {
|
|
|
|
self->u.number.val.fpval /= other->u.number.val.fpval;
|
2012-02-27 21:14:46 +00:00
|
|
|
} else {
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->u.number.flags & NUMBER_IS_DEFINED)
|
2020-05-10 21:35:50 +00:00
|
|
|
Throw_error(exception_div_by_zero);
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval = 0;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_INTDIV:
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->u.number.val.fpval) {
|
|
|
|
self->u.number.val.intval = self->u.number.val.fpval / other->u.number.val.fpval; // fp becomes int!
|
2012-02-27 21:14:46 +00:00
|
|
|
} else {
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->u.number.flags & NUMBER_IS_DEFINED)
|
2012-02-27 21:14:46 +00:00
|
|
|
Throw_error(exception_div_by_zero);
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = 0;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
2020-05-13 23:26:40 +00:00
|
|
|
self->type = &type_int; // result is int
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_LSR:
|
|
|
|
case OPID_AND:
|
|
|
|
case OPID_OR:
|
|
|
|
case OPID_EOR:
|
|
|
|
case OPID_XOR:
|
2020-05-10 21:35:50 +00:00
|
|
|
warn_float_to_int();
|
|
|
|
/*FALLTHROUGH*/
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_MODULO:
|
2020-05-10 21:35:50 +00:00
|
|
|
float_to_int(self);
|
|
|
|
// int handler will check other and, if needed, convert to int
|
2020-05-29 10:57:01 +00:00
|
|
|
type_int.dyadic_op(self, op, other); // TODO - put recursion check around this?
|
2020-05-10 21:35:50 +00:00
|
|
|
return; // int handler has done everything
|
|
|
|
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_ADD:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval += other->u.number.val.fpval;
|
|
|
|
refs = self->u.number.addr_refs + other->u.number.addr_refs; // add address references
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_SUBTRACT:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval -= other->u.number.val.fpval;
|
|
|
|
refs = self->u.number.addr_refs - other->u.number.addr_refs; // subtract address references
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 14:01:30 +00:00
|
|
|
case OPID_SHIFTLEFT:
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->type == &type_float)
|
2020-05-09 23:01:51 +00:00
|
|
|
float_to_int(other);
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval *= pow(2.0, other->u.number.val.intval);
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_ASR:
|
2020-05-13 23:26:40 +00:00
|
|
|
if (other->type == &type_float)
|
2020-05-09 23:01:51 +00:00
|
|
|
float_to_int(other);
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.fpval /= (1 << other->u.number.val.intval); // FIXME - why not use pow() as in SL above?
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 14:01:30 +00:00
|
|
|
case OPID_LESSOREQUAL:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.fpval <= other->u.number.val.fpval);
|
|
|
|
self->type = &type_int; // result is int
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_LESSTHAN:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.fpval < other->u.number.val.fpval);
|
|
|
|
self->type = &type_int; // result is int
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 14:01:30 +00:00
|
|
|
case OPID_GREATEROREQUAL:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.fpval >= other->u.number.val.fpval);
|
|
|
|
self->type = &type_int; // result is int
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_GREATERTHAN:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.fpval > other->u.number.val.fpval);
|
|
|
|
self->type = &type_int; // result is int
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_NOTEQUAL:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.fpval != other->u.number.val.fpval);
|
|
|
|
self->type = &type_int; // result is int
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_EQUALS:
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.val.intval = (self->u.number.val.fpval == other->u.number.val.fpval);
|
|
|
|
self->type = &type_int; // result is int
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
|
|
|
// add new dyadic operators here
|
2020-05-14 10:19:21 +00:00
|
|
|
// case OPID_:
|
2020-05-09 19:26:40 +00:00
|
|
|
// break;
|
|
|
|
default:
|
2020-05-15 11:39:00 +00:00
|
|
|
unsupported_operation(self, op, other);
|
|
|
|
return;
|
2020-05-09 19:26:40 +00:00
|
|
|
}
|
2020-05-13 23:26:40 +00:00
|
|
|
self->u.number.addr_refs = refs; // update address refs with local copy
|
2020-05-10 12:33:41 +00:00
|
|
|
number_fix_result_after_dyadic(self, other); // fix result flags
|
2020-05-09 19:26:40 +00:00
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-19 20:41:12 +00:00
|
|
|
|
|
|
|
// helper function for lists and strings, check index
|
|
|
|
// return zero on success, nonzero on error
|
|
|
|
static int get_valid_index(int *target, int length, struct object *self, struct op *op, struct object *other)
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
|
|
|
|
if (other->type == &type_float)
|
|
|
|
float_to_int(other);
|
|
|
|
if (other->type != &type_int) {
|
|
|
|
unsupported_operation(self, op, other);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (!(other->u.number.flags & NUMBER_IS_DEFINED)) {
|
2020-05-24 21:16:50 +00:00
|
|
|
Throw_error("Index is undefined.");
|
2020-05-19 20:41:12 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
index = other->u.number.val.intval;
|
|
|
|
// negative indices access from the end
|
|
|
|
if (index < 0)
|
|
|
|
index += length;
|
|
|
|
if ((index < 0) || (index >= length)) {
|
2020-05-29 22:03:04 +00:00
|
|
|
Throw_error("Index out of range.");
|
2020-05-19 20:41:12 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
*target = index;
|
|
|
|
return 0; // ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// list:
|
|
|
|
// handle dyadic operator
|
|
|
|
static void list_handle_dyadic_operator(struct object *self, struct op *op, struct object *other)
|
|
|
|
{
|
|
|
|
struct listitem *item;
|
|
|
|
int length;
|
|
|
|
int index;
|
|
|
|
|
|
|
|
length = self->u.listhead->length;
|
|
|
|
switch (op->id) {
|
|
|
|
case OPID_LIST_APPEND:
|
2020-06-06 12:01:44 +00:00
|
|
|
list_append_object(self->u.listhead, other);
|
2020-05-19 20:41:12 +00:00
|
|
|
// no need to check/update ref count of "other": it loses the ref on the stack and gains one in the list
|
|
|
|
break;
|
|
|
|
case OPID_ATINDEX:
|
|
|
|
if (get_valid_index(&index, length, self, op, other))
|
|
|
|
return; // error
|
|
|
|
|
|
|
|
item = self->u.listhead->next;
|
|
|
|
while (index) {
|
|
|
|
item = item->next;
|
|
|
|
--index;
|
|
|
|
}
|
2020-06-06 12:01:44 +00:00
|
|
|
self->u.listhead->refs--; // FIXME - call some fn for this (and do _after_ next line)
|
2020-05-19 20:41:12 +00:00
|
|
|
*self = item->payload; // FIXME - if item is a list, it would gain a ref by this...
|
|
|
|
break;
|
2020-06-06 12:01:44 +00:00
|
|
|
case OPID_ADD:
|
|
|
|
if (other->type != &type_list) {
|
|
|
|
unsupported_operation(self, op, other);
|
|
|
|
return; // error
|
|
|
|
}
|
|
|
|
item = self->u.listhead; // get ref to first list
|
|
|
|
list_init_list(self); // replace first list on arg stack with new one
|
|
|
|
list_append_list(self->u.listhead, item);
|
|
|
|
item->refs--; // FIXME - call a function for this...
|
|
|
|
item = other->u.listhead;
|
|
|
|
list_append_list(self->u.listhead, item);
|
|
|
|
item->refs--; // FIXME - call a function for this...
|
|
|
|
return;
|
|
|
|
|
2020-05-19 20:41:12 +00:00
|
|
|
default:
|
|
|
|
unsupported_operation(self, op, other);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// string:
|
|
|
|
// handle dyadic operator
|
|
|
|
static void string_handle_dyadic_operator(struct object *self, struct op *op, struct object *other)
|
|
|
|
{
|
|
|
|
int length;
|
|
|
|
int index;
|
|
|
|
intval_t character;
|
2020-05-26 12:55:14 +00:00
|
|
|
struct string *arthur,
|
|
|
|
*ford;
|
2020-05-19 20:41:12 +00:00
|
|
|
|
|
|
|
length = self->u.string->length;
|
|
|
|
switch (op->id) {
|
|
|
|
case OPID_ATINDEX:
|
|
|
|
if (get_valid_index(&index, length, self, op, other))
|
2020-05-26 12:55:14 +00:00
|
|
|
return; // error has already been reported
|
2020-05-19 20:41:12 +00:00
|
|
|
|
2020-05-19 21:04:57 +00:00
|
|
|
character = (intval_t) (unsigned char) encoding_encode_char(self->u.string->payload[index]);
|
2020-05-19 20:41:12 +00:00
|
|
|
self->u.string->refs--; // FIXME - call a function for this...
|
2020-05-26 12:55:14 +00:00
|
|
|
int_create_byte(self, character);
|
|
|
|
return; // ok
|
|
|
|
|
|
|
|
case OPID_ADD:
|
|
|
|
if (other->type != &type_string)
|
|
|
|
break; // complain
|
|
|
|
arthur = self->u.string;
|
|
|
|
ford = other->u.string;
|
|
|
|
string_prepare_string(self, arthur->length + ford->length); // create string object and put on arg stack
|
|
|
|
memcpy(self->u.string->payload, arthur->payload, arthur->length);
|
|
|
|
memcpy(self->u.string->payload + arthur->length, ford->payload, ford->length);
|
|
|
|
arthur->refs--; // FIXME - call a function for this...
|
|
|
|
ford->refs--; // FIXME - call a function for this...
|
|
|
|
return;
|
|
|
|
|
|
|
|
case OPID_EQUALS:
|
|
|
|
if (other->type != &type_string)
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
arthur->refs--; // FIXME - call a function for this...
|
|
|
|
ford->refs--; // FIXME - call a function for this...
|
|
|
|
return;
|
|
|
|
|
|
|
|
case OPID_NOTEQUAL:
|
|
|
|
if (other->type != &type_string)
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
arthur->refs--; // FIXME - call a function for this...
|
|
|
|
ford->refs--; // FIXME - call a function for this...
|
|
|
|
return;
|
|
|
|
|
|
|
|
//case ...: // maybe comparisons?
|
2020-05-19 20:41:12 +00:00
|
|
|
default:
|
2020-05-26 12:55:14 +00:00
|
|
|
break; // complain
|
2020-05-19 20:41:12 +00:00
|
|
|
}
|
2020-05-26 12:55:14 +00:00
|
|
|
unsupported_operation(self, op, other);
|
2020-05-19 20:41:12 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 23:26:40 +00:00
|
|
|
// int/float:
|
|
|
|
// set flags according to result
|
|
|
|
static void number_fix_result(struct object *self)
|
|
|
|
{
|
|
|
|
// only allow a single force bit
|
|
|
|
if (self->u.number.flags & NUMBER_FORCES_24)
|
|
|
|
self->u.number.flags &= ~(NUMBER_FORCES_16 | NUMBER_FORCES_8);
|
|
|
|
else if (self->u.number.flags & NUMBER_FORCES_16)
|
|
|
|
self->u.number.flags &= ~NUMBER_FORCES_8;
|
|
|
|
}
|
|
|
|
|
|
|
|
// int:
|
|
|
|
// set flags according to result
|
|
|
|
static void int_fix_result(struct object *self)
|
|
|
|
{
|
|
|
|
number_fix_result(self);
|
|
|
|
// if undefined, return zero
|
|
|
|
if (!(self->u.number.flags & NUMBER_IS_DEFINED))
|
|
|
|
self->u.number.val.intval = 0;
|
|
|
|
// if value is sure, check to set FITS BYTE
|
|
|
|
else if ((!(self->u.number.flags & NUMBER_EVER_UNDEFINED))
|
|
|
|
&& (self->u.number.val.intval <= 255)
|
|
|
|
&& (self->u.number.val.intval >= -128))
|
|
|
|
self->u.number.flags |= NUMBER_FITS_BYTE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// float:
|
|
|
|
// set flags according to result
|
|
|
|
static void float_fix_result(struct object *self)
|
|
|
|
{
|
|
|
|
number_fix_result(self);
|
|
|
|
// if undefined, return zero
|
|
|
|
if (!(self->u.number.flags & NUMBER_IS_DEFINED))
|
|
|
|
self->u.number.val.fpval = 0;
|
|
|
|
// if value is sure, check to set FITS BYTE
|
|
|
|
else if ((!(self->u.number.flags & NUMBER_EVER_UNDEFINED))
|
|
|
|
&& (self->u.number.val.fpval <= 255.0)
|
|
|
|
&& (self->u.number.val.fpval >= -128.0))
|
|
|
|
self->u.number.flags |= NUMBER_FITS_BYTE;
|
|
|
|
}
|
|
|
|
|
2020-05-19 20:41:12 +00:00
|
|
|
// list/string:
|
|
|
|
// no need to fix results
|
|
|
|
static void object_no_op(struct object *self)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-05-13 23:26:40 +00:00
|
|
|
// int:
|
|
|
|
// print value for user message
|
|
|
|
static void int_print(struct object *self, struct dynabuf *db)
|
|
|
|
{
|
|
|
|
char buffer[32]; // 11 for dec, 8 for hex
|
|
|
|
|
|
|
|
if (self->u.number.flags & NUMBER_IS_DEFINED) {
|
|
|
|
sprintf(buffer, "%ld (0x%lx)", (long) self->u.number.val.intval, (long) self->u.number.val.intval);
|
|
|
|
DynaBuf_add_string(db, buffer);
|
|
|
|
} else {
|
|
|
|
DynaBuf_add_string(db, "<UNDEFINED INT>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// float:
|
|
|
|
// print value for user message
|
|
|
|
static void float_print(struct object *self, struct dynabuf *db)
|
|
|
|
{
|
|
|
|
char buffer[40];
|
|
|
|
|
|
|
|
if (self->u.number.flags & NUMBER_IS_DEFINED) {
|
|
|
|
// write up to 30 significant characters.
|
|
|
|
// remaining 10 should suffice for sign,
|
|
|
|
// decimal point, exponent, terminator etc.
|
|
|
|
sprintf(buffer, "%.30g", self->u.number.val.fpval);
|
|
|
|
DynaBuf_add_string(db, buffer);
|
|
|
|
} else {
|
|
|
|
DynaBuf_add_string(db, "<UNDEFINED FLOAT>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-19 20:41:12 +00:00
|
|
|
// list:
|
|
|
|
// print value for user message
|
|
|
|
static void list_print(struct object *self, struct dynabuf *db)
|
|
|
|
{
|
2020-05-25 23:12:19 +00:00
|
|
|
struct listitem *item;
|
|
|
|
int length;
|
|
|
|
struct object *obj;
|
|
|
|
const char *prefix = ""; // first item does not get a prefix
|
2020-05-19 20:41:12 +00:00
|
|
|
|
2020-05-25 23:12:19 +00:00
|
|
|
DynaBuf_append(db, '[');
|
|
|
|
length = self->u.listhead->length;
|
|
|
|
item = self->u.listhead->next;
|
|
|
|
while (length--) {
|
|
|
|
obj = &item->payload;
|
|
|
|
DynaBuf_add_string(db, prefix);
|
|
|
|
obj->type->print(obj, db);
|
|
|
|
item = item->next;
|
|
|
|
prefix = ", "; // following items are prefixed
|
|
|
|
}
|
|
|
|
DynaBuf_append(db, ']');
|
2020-05-19 20:41:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// string:
|
|
|
|
// print value for user message
|
|
|
|
static void string_print(struct object *self, struct dynabuf *db)
|
|
|
|
{
|
2020-05-25 23:12:19 +00:00
|
|
|
DynaBuf_add_string(db, self->u.string->payload); // there is a terminator after the actual payload, so this works
|
2020-05-19 20:41:12 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 23:26:40 +00:00
|
|
|
struct type type_int = {
|
2020-05-14 14:01:30 +00:00
|
|
|
"integer",
|
2020-05-13 23:26:40 +00:00
|
|
|
number_is_defined,
|
|
|
|
int_handle_monadic_operator,
|
|
|
|
int_handle_dyadic_operator,
|
|
|
|
int_fix_result,
|
|
|
|
int_print
|
|
|
|
};
|
|
|
|
struct type type_float = {
|
2020-05-14 14:01:30 +00:00
|
|
|
"float",
|
2020-05-13 23:26:40 +00:00
|
|
|
number_is_defined,
|
|
|
|
float_handle_monadic_operator,
|
|
|
|
float_handle_dyadic_operator,
|
|
|
|
float_fix_result,
|
|
|
|
float_print
|
|
|
|
};
|
|
|
|
struct type type_list = {
|
2020-05-14 14:01:30 +00:00
|
|
|
"list",
|
2020-05-19 20:41:12 +00:00
|
|
|
object_return_true, // lists are always considered to be defined (even though they can hold undefined numbers...)
|
|
|
|
list_handle_monadic_operator,
|
|
|
|
list_handle_dyadic_operator,
|
|
|
|
object_no_op, // no need to fix list results
|
|
|
|
list_print
|
|
|
|
};
|
|
|
|
struct type type_string = {
|
|
|
|
"string",
|
|
|
|
object_return_true, // strings are always defined
|
|
|
|
string_handle_monadic_operator,
|
|
|
|
string_handle_dyadic_operator,
|
|
|
|
object_no_op, // no need to fix string results
|
|
|
|
string_print
|
2020-05-13 23:26:40 +00:00
|
|
|
};
|
|
|
|
|
2020-05-09 19:26:40 +00:00
|
|
|
|
|
|
|
// handler for special operators like parentheses and start/end of expression:
|
2020-05-19 20:41:12 +00:00
|
|
|
// returns whether caller can remove "previous" operator from stack
|
2020-05-14 10:19:21 +00:00
|
|
|
static boolean handle_special_operator(struct expression *expression, enum op_id previous, enum op_id current)
|
2020-05-09 19:26:40 +00:00
|
|
|
{
|
2020-05-15 13:46:17 +00:00
|
|
|
// when this gets called, "previous" is a special operator, and "current" has a lower priority, so it is also a special operator
|
2020-05-09 19:26:40 +00:00
|
|
|
switch (previous) {
|
2020-05-14 10:19:21 +00:00
|
|
|
case OPID_START_EXPRESSION:
|
2020-05-09 19:26:40 +00:00
|
|
|
// the only operator with a lower priority than this
|
|
|
|
// "start-of-expression" operator is "end-of-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
|
2020-05-19 20:41:12 +00:00
|
|
|
return TRUE; // caller can remove this operator (we are done, so not really needed, but there are sanity checks for stack pointers)
|
2020-05-15 13:46:17 +00:00
|
|
|
|
2020-05-19 20:41:12 +00:00
|
|
|
case OPID_LEFT_PARENTHESIS:
|
2020-05-15 11:39:00 +00:00
|
|
|
expression->is_parenthesized = TRUE; // found parentheses. if this is not the outermost level, the outermost level will fix this flag later on.
|
2020-05-19 20:41:12 +00:00
|
|
|
if (current != OPID_END_EXPRESSION)
|
|
|
|
Bug_found("StrangeParenthesis", current);
|
|
|
|
if (GotByte == ')') {
|
|
|
|
// matching parenthesis
|
|
|
|
GetByte(); // eat char
|
|
|
|
op_sp -= 2; // remove both operators
|
2020-05-09 20:58:08 +00:00
|
|
|
alu_state = STATE_EXPECT_DYADIC_OP;
|
2020-05-15 13:46:17 +00:00
|
|
|
return FALSE; // we fixed the stack ourselves, so caller shouldn't touch it
|
2020-05-09 19:26:40 +00:00
|
|
|
}
|
2020-05-19 20:41:12 +00:00
|
|
|
// unmatched parenthesis, as in "lda ($80,x)"
|
|
|
|
++(expression->open_parentheses); // count
|
|
|
|
return TRUE; // caller can remove "OPID_LEFT_PARENTHESIS" operator from stack
|
|
|
|
|
|
|
|
case OPID_START_LIST:
|
|
|
|
if (current != OPID_END_EXPRESSION)
|
2020-05-24 21:16:50 +00:00
|
|
|
Bug_found("StrangeListBracket", current);
|
2020-05-19 20:41:12 +00:00
|
|
|
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
|
2020-05-15 11:39:00 +00:00
|
|
|
}
|
2020-05-19 20:41:12 +00:00
|
|
|
if (GotByte == ']') {
|
|
|
|
GetByte(); // eat ']'
|
|
|
|
op_sp -= 2; // remove both START_LIST and END_EXPRESSION
|
|
|
|
alu_state = STATE_EXPECT_DYADIC_OP;
|
|
|
|
return FALSE; // we fixed the stack ourselves, so caller shouldn't touch it
|
2020-05-15 13:46:17 +00:00
|
|
|
}
|
2020-05-24 21:16:50 +00:00
|
|
|
Throw_error("Unterminated list.");
|
2020-05-19 20:41:12 +00:00
|
|
|
alu_state = STATE_ERROR;
|
|
|
|
return TRUE; // caller can remove LISTBUILDER operator from stack
|
|
|
|
|
|
|
|
case OPID_START_INDEX:
|
|
|
|
if (current != OPID_END_EXPRESSION)
|
2020-05-24 21:16:50 +00:00
|
|
|
Bug_found("StrangeIndexBracket", current);
|
2020-05-19 20:41:12 +00:00
|
|
|
if (GotByte == ']') {
|
|
|
|
GetByte(); // eat ']'
|
|
|
|
op_sp -= 2; // remove both OPENINDEX and END_EXPRESSION
|
|
|
|
alu_state = STATE_EXPECT_DYADIC_OP;
|
|
|
|
return FALSE; // we fixed the stack ourselves, so caller shouldn't touch it
|
|
|
|
}
|
2020-05-24 21:16:50 +00:00
|
|
|
Throw_error("Unterminated index spec.");
|
2020-05-19 20:41:12 +00:00
|
|
|
alu_state = STATE_ERROR;
|
|
|
|
return TRUE; // caller can remove START_INDEX operator from stack
|
|
|
|
|
|
|
|
default:
|
2020-05-24 21:16:50 +00:00
|
|
|
Bug_found("IllegalOperatorId", previous);
|
2020-05-09 19:26:40 +00:00
|
|
|
}
|
2020-05-15 13:46:17 +00:00
|
|
|
// this is unreachable
|
|
|
|
return FALSE; // stack is done, so caller shouldn't touch it
|
2020-05-09 19:26:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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 try_to_reduce_stacks(struct expression *expression)
|
|
|
|
{
|
2020-05-09 20:58:08 +00:00
|
|
|
struct op *previous_op;
|
|
|
|
struct op *current_op;
|
2020-05-09 19:26:40 +00:00
|
|
|
|
2020-05-09 20:58:08 +00:00
|
|
|
if (op_sp < 2) {
|
2020-05-15 11:39:00 +00:00
|
|
|
// 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
|
2020-05-09 20:58:08 +00:00
|
|
|
alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP;
|
2020-05-09 19:26:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-05-09 20:58:08 +00:00
|
|
|
previous_op = op_stack[op_sp - 2];
|
|
|
|
current_op = op_stack[op_sp - 1];
|
2020-05-09 19:26:40 +00:00
|
|
|
|
|
|
|
// previous operator has lower piority than current one? then do nothing.
|
2020-05-09 20:58:08 +00:00
|
|
|
if (previous_op->priority < current_op->priority) {
|
|
|
|
alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP;
|
2020-05-09 19:26:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// previous operator has same priority as current one? then check associativity
|
2020-05-09 20:58:08 +00:00
|
|
|
if ((previous_op->priority == current_op->priority)
|
2020-05-24 20:19:19 +00:00
|
|
|
&& (current_op->priority == PRIO_POWEROF)
|
2020-05-27 17:32:48 +00:00
|
|
|
&& (config.wanted_version >= VER_RIGHTASSOCIATIVEPOWEROF)) {
|
2020-05-09 20:58:08 +00:00
|
|
|
alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP;
|
2020-05-09 19:26:40 +00:00
|
|
|
return;
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
2020-05-09 19:26:40 +00:00
|
|
|
// 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!
|
2020-05-09 20:58:08 +00:00
|
|
|
#define ARG_PREV (arg_stack[arg_sp - 2])
|
|
|
|
#define ARG_NOW (arg_stack[arg_sp - 1])
|
|
|
|
switch (previous_op->group) {
|
2020-05-09 19:26:40 +00:00
|
|
|
case OPGROUP_MONADIC: // monadic operators
|
2020-05-19 09:41:54 +00:00
|
|
|
if (arg_sp < 1)
|
2020-05-24 21:16:50 +00:00
|
|
|
Bug_found("ArgStackEmpty", arg_sp);
|
2020-05-29 10:57:01 +00:00
|
|
|
ARG_NOW.type->monadic_op(&ARG_NOW, previous_op);
|
2020-05-09 19:26:40 +00:00
|
|
|
// operation was something other than parentheses
|
|
|
|
expression->is_parenthesized = FALSE;
|
|
|
|
break;
|
|
|
|
case OPGROUP_DYADIC: // dyadic operators
|
2020-05-19 09:41:54 +00:00
|
|
|
if (arg_sp < 2)
|
2020-05-24 21:16:50 +00:00
|
|
|
Bug_found("NotEnoughArgs", arg_sp);
|
2020-05-29 10:57:01 +00:00
|
|
|
ARG_PREV.type->dyadic_op(&ARG_PREV, previous_op, &ARG_NOW);
|
2020-05-10 12:33:41 +00:00
|
|
|
// decrement argument stack pointer because dyadic operator merged two arguments into one
|
2020-05-09 20:58:08 +00:00
|
|
|
--arg_sp;
|
2020-05-09 19:26:40 +00:00
|
|
|
// operation was something other than parentheses
|
|
|
|
expression->is_parenthesized = FALSE;
|
|
|
|
break;
|
|
|
|
case OPGROUP_SPECIAL: // special (pseudo) operators
|
2020-05-15 13:46:17 +00:00
|
|
|
if (!handle_special_operator(expression, previous_op->id, current_op->id))
|
2020-05-09 20:58:08 +00:00
|
|
|
return; // called fn has fixed the stack, so we return and don't touch it
|
2020-05-09 19:26:40 +00:00
|
|
|
|
2020-05-09 20:58:08 +00:00
|
|
|
// both monadics and dyadics clear "is_parenthesized", but here we don't touch it!
|
2020-05-09 19:26:40 +00:00
|
|
|
break;
|
|
|
|
default:
|
2020-05-24 21:16:50 +00:00
|
|
|
Bug_found("IllegalOperatorGroup", previous_op->group);
|
2020-05-09 19:26:40 +00:00
|
|
|
}
|
|
|
|
// shared endings for "we did the operation indicated by previous operator":
|
2020-05-09 20:58:08 +00:00
|
|
|
// fix stack:
|
2020-05-09 19:26:40 +00:00
|
|
|
// remove previous operator and shift down current one
|
2020-05-09 20:58:08 +00:00
|
|
|
// 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
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-10 21:35:50 +00:00
|
|
|
// this is what the exported functions call
|
2020-05-20 16:39:19 +00:00
|
|
|
// returns nonzero on parse error
|
|
|
|
static int parse_expression(struct expression *expression)
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2020-05-13 23:26:40 +00:00
|
|
|
struct object *result = &expression->result;
|
2020-04-26 18:53:14 +00:00
|
|
|
|
2020-04-26 20:14:39 +00:00
|
|
|
// init
|
2020-04-26 22:26:05 +00:00
|
|
|
expression->is_empty = TRUE; // becomes FALSE when first valid char gets parsed
|
2020-04-26 18:53:14 +00:00
|
|
|
expression->open_parentheses = 0;
|
2020-04-26 20:14:39 +00:00
|
|
|
expression->is_parenthesized = FALSE; // toplevel operator will set this: '(' to TRUE, all others to FALSE
|
|
|
|
//expression->number will be overwritten later, so no need to init
|
2012-02-27 21:14:46 +00:00
|
|
|
|
2020-05-09 20:58:08 +00:00
|
|
|
op_sp = 0; // operator stack pointer
|
|
|
|
arg_sp = 0; // argument stack pointer
|
|
|
|
// begin by reading an argument (or a monadic operator)
|
|
|
|
alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP;
|
2020-05-19 20:41:12 +00:00
|
|
|
PUSH_OP(&ops_start_expression);
|
2012-02-27 21:14:46 +00:00
|
|
|
do {
|
|
|
|
// check stack sizes. enlarge if needed
|
2020-05-09 20:58:08 +00:00
|
|
|
if (arg_sp >= argstack_size)
|
|
|
|
enlarge_argument_stack();
|
2012-02-27 21:14:46 +00:00
|
|
|
switch (alu_state) {
|
2020-05-09 20:58:08 +00:00
|
|
|
case STATE_EXPECT_ARG_OR_MONADIC_OP:
|
|
|
|
if (expect_argument_or_monadic_operator())
|
2020-04-26 22:26:05 +00:00
|
|
|
expression->is_empty = FALSE;
|
2012-02-27 21:14:46 +00:00
|
|
|
break;
|
2020-05-09 20:58:08 +00:00
|
|
|
case STATE_EXPECT_DYADIC_OP:
|
2012-02-27 21:14:46 +00:00
|
|
|
expect_dyadic_operator();
|
|
|
|
break; // no fallthrough; state might
|
|
|
|
// have been changed to END or ERROR
|
|
|
|
case STATE_TRY_TO_REDUCE_STACKS:
|
2020-04-26 20:14:39 +00:00
|
|
|
try_to_reduce_stacks(expression);
|
2012-02-27 21:14:46 +00:00
|
|
|
break;
|
|
|
|
case STATE_MAX_GO_ON: // suppress
|
|
|
|
case STATE_ERROR: // compiler
|
|
|
|
case STATE_END: // warnings
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (alu_state < STATE_MAX_GO_ON);
|
|
|
|
// done. check state.
|
|
|
|
if (alu_state == STATE_END) {
|
|
|
|
// check for bugs
|
2020-05-09 20:58:08 +00:00
|
|
|
if (arg_sp != 1)
|
2020-05-24 21:16:50 +00:00
|
|
|
Bug_found("ArgStackNotEmpty", arg_sp);
|
2020-05-09 20:58:08 +00:00
|
|
|
if (op_sp != 1)
|
|
|
|
Bug_found("OperatorStackNotEmpty", op_sp);
|
2012-02-27 21:14:46 +00:00
|
|
|
// copy result
|
2020-05-09 20:58:08 +00:00
|
|
|
*result = arg_stack[0];
|
2020-05-02 10:40:10 +00:00
|
|
|
// if there was nothing to parse, mark as undefined FIXME - change this! make "nothing" its own result type; only numbers may be undefined
|
2012-02-27 21:14:46 +00:00
|
|
|
// (so ALU_defined_int() can react)
|
2020-05-02 14:58:17 +00:00
|
|
|
if (expression->is_empty) {
|
2020-05-13 23:26:40 +00:00
|
|
|
result->type = &type_int;
|
|
|
|
result->u.number.flags = NUMBER_EVER_UNDEFINED; // ...and without NUMBER_IS_DEFINED!
|
|
|
|
result->u.number.val.intval = 0;
|
|
|
|
result->u.number.addr_refs = 0;
|
2020-05-02 14:58:17 +00:00
|
|
|
} else {
|
|
|
|
// not empty. undefined?
|
2020-05-13 23:26:40 +00:00
|
|
|
if (!(result->type->is_defined(result))) {
|
2020-05-02 14:58:17 +00:00
|
|
|
// then count (in all passes)
|
|
|
|
++pass.undefined_count;
|
|
|
|
}
|
|
|
|
}
|
2012-02-27 21:14:46 +00:00
|
|
|
// do some checks depending on int/float
|
2020-05-13 23:26:40 +00:00
|
|
|
result->type->fix_result(result);
|
2020-05-20 16:39:19 +00:00
|
|
|
return 0; // ok
|
2012-02-27 21:14:46 +00:00
|
|
|
} else {
|
2017-10-29 23:29:07 +00:00
|
|
|
// State is STATE_ERROR. Errors have already been reported,
|
|
|
|
// but we must make sure not to pass bogus data to caller.
|
2020-05-02 10:40:10 +00:00
|
|
|
// FIXME - just use the return value to indicate "there were errors, do not use result!"
|
2020-05-13 23:26:40 +00:00
|
|
|
result->type = &type_int;
|
|
|
|
result->u.number.flags = 0; // maybe set DEFINED flag to suppress follow-up errors?
|
|
|
|
result->u.number.val.intval = 0;
|
|
|
|
result->u.number.addr_refs = 0;
|
2017-10-29 23:29:07 +00:00
|
|
|
// make sure no additional (spurious) errors are reported:
|
|
|
|
Input_skip_remainder();
|
|
|
|
// FIXME - remove this when new function interface gets used:
|
|
|
|
// callers must decide for themselves what to do when expression parser returns error
|
|
|
|
// (currently LDA'' results in both "no string given" AND "illegal combination of command and addressing mode"!)
|
2020-05-20 16:39:19 +00:00
|
|
|
return 1; // error
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-23 23:38:13 +00:00
|
|
|
// store int value (if undefined, store zero)
|
2020-05-02 10:40:10 +00:00
|
|
|
// For empty expressions, an error is thrown.
|
2015-06-14 23:16:23 +00:00
|
|
|
// OPEN_PARENTHESIS: complain
|
|
|
|
// EMPTY: complain
|
|
|
|
// UNDEFINED: allow
|
|
|
|
// FLOAT: convert to int
|
2020-05-23 23:38:13 +00:00
|
|
|
void ALU_any_int(intval_t *target) // ACCEPT_UNDEFINED
|
2015-06-14 23:16:23 +00:00
|
|
|
{
|
2020-04-26 18:53:14 +00:00
|
|
|
struct expression expression;
|
2015-06-14 23:16:23 +00:00
|
|
|
|
2020-05-20 16:39:19 +00:00
|
|
|
parse_expression(&expression); // FIXME - check return value and pass to caller!
|
2020-04-26 18:53:14 +00:00
|
|
|
if (expression.open_parentheses)
|
2015-06-14 23:16:23 +00:00
|
|
|
Throw_error(exception_paren_open);
|
2020-04-26 22:26:05 +00:00
|
|
|
if (expression.is_empty)
|
2015-06-14 23:16:23 +00:00
|
|
|
Throw_error(exception_no_value);
|
2020-05-13 23:26:40 +00:00
|
|
|
if (expression.result.type == &type_int)
|
2020-05-23 23:38:13 +00:00
|
|
|
*target = expression.result.u.number.val.intval;
|
|
|
|
else if (expression.result.type == &type_float)
|
|
|
|
*target = expression.result.u.number.val.fpval;
|
|
|
|
else {
|
|
|
|
*target = 0;
|
2020-05-24 21:16:50 +00:00
|
|
|
Throw_error(exception_not_number);
|
2020-05-23 23:38:13 +00:00
|
|
|
}
|
2015-06-14 23:16:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// stores int value and flags (floats are transformed to int)
|
2020-05-02 10:40:10 +00:00
|
|
|
// if result is empty or undefined, serious error is thrown
|
2015-06-14 23:16:23 +00:00
|
|
|
// OPEN_PARENTHESIS: complain
|
2020-05-02 10:40:10 +00:00
|
|
|
// EMPTY: complain _seriously_
|
2015-06-14 23:16:23 +00:00
|
|
|
// UNDEFINED: complain _seriously_
|
|
|
|
// FLOAT: convert to int
|
2020-05-22 20:55:36 +00:00
|
|
|
// FIXME - only very few callers actually _need_ a serious error to be thrown,
|
|
|
|
// so throw a normal one here and pass ok/fail as return value, so caller can react.
|
2020-04-28 16:02:09 +00:00
|
|
|
void ALU_defined_int(struct number *intresult) // no ACCEPT constants?
|
2015-06-14 23:16:23 +00:00
|
|
|
{
|
2020-04-26 18:53:14 +00:00
|
|
|
struct expression expression;
|
2020-05-02 21:59:20 +00:00
|
|
|
boolean buf = pass.complain_about_undefined;
|
2020-04-26 18:53:14 +00:00
|
|
|
|
2020-05-02 21:59:20 +00:00
|
|
|
pass.complain_about_undefined = TRUE;
|
2020-05-20 16:39:19 +00:00
|
|
|
parse_expression(&expression); // FIXME - check return value and pass to caller!
|
2020-05-02 21:59:20 +00:00
|
|
|
pass.complain_about_undefined = buf;
|
2020-04-26 18:53:14 +00:00
|
|
|
if (expression.open_parentheses)
|
2015-06-14 23:16:23 +00:00
|
|
|
Throw_error(exception_paren_open);
|
2020-05-02 10:40:10 +00:00
|
|
|
if (expression.is_empty)
|
|
|
|
Throw_serious_error(exception_no_value);
|
2020-05-13 23:26:40 +00:00
|
|
|
if (expression.result.type == &type_int) {
|
|
|
|
// ok
|
|
|
|
} else if (expression.result.type == &type_float) {
|
|
|
|
float_to_int(&expression.result);
|
|
|
|
} else {
|
2020-05-24 21:16:50 +00:00
|
|
|
Throw_serious_error(exception_not_number);
|
2020-05-13 23:26:40 +00:00
|
|
|
}
|
|
|
|
if (!(expression.result.u.number.flags & NUMBER_IS_DEFINED))
|
2020-05-02 21:59:20 +00:00
|
|
|
Throw_serious_error(exception_value_not_defined);
|
2020-05-13 23:26:40 +00:00
|
|
|
*intresult = expression.result.u.number;
|
2015-06-14 23:16:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-27 21:14:46 +00:00
|
|
|
// Store int value and flags.
|
2020-05-07 23:32:06 +00:00
|
|
|
// This function allows for "paren" '(' too many. Needed when parsing indirect
|
2020-04-26 18:53:14 +00:00
|
|
|
// addressing modes where internal indices have to be possible.
|
2020-05-02 10:40:10 +00:00
|
|
|
// For empty expressions, an error is thrown.
|
2020-05-09 20:58:08 +00:00
|
|
|
// OPEN_PARENTHESIS: depends on arg
|
2015-06-14 23:16:23 +00:00
|
|
|
// UNDEFINED: allow
|
2020-04-26 16:24:34 +00:00
|
|
|
// EMPTY: complain
|
2015-06-14 23:16:23 +00:00
|
|
|
// FLOAT: convert to int
|
2020-05-07 23:32:06 +00:00
|
|
|
void ALU_addrmode_int(struct expression *expression, int paren) // ACCEPT_UNDEFINED | ACCEPT_OPENPARENTHESIS
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2020-05-20 16:39:19 +00:00
|
|
|
parse_expression(expression); // FIXME - check return value and pass to caller!
|
2020-05-08 00:34:46 +00:00
|
|
|
// convert float to int
|
2020-05-13 23:26:40 +00:00
|
|
|
if (expression->result.type == &type_float)
|
|
|
|
float_to_int(&(expression->result));
|
|
|
|
if (expression->result.type != &type_int)
|
2020-05-24 21:16:50 +00:00
|
|
|
Throw_error(exception_not_number);
|
2020-05-07 23:32:06 +00:00
|
|
|
if (expression->open_parentheses > paren) {
|
2020-04-26 18:53:14 +00:00
|
|
|
expression->open_parentheses = 0;
|
2012-02-27 21:14:46 +00:00
|
|
|
Throw_error(exception_paren_open);
|
|
|
|
}
|
2020-04-26 22:26:05 +00:00
|
|
|
if (expression->is_empty)
|
2020-04-26 16:24:34 +00:00
|
|
|
Throw_error(exception_no_value);
|
2012-02-27 21:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-20 16:39:19 +00:00
|
|
|
// Store resulting object
|
2020-05-02 10:40:10 +00:00
|
|
|
// For empty expressions, an error is thrown.
|
2015-06-14 23:16:23 +00:00
|
|
|
// OPEN_PARENTHESIS: complain
|
|
|
|
// EMPTY: complain
|
|
|
|
// UNDEFINED: allow
|
|
|
|
// FLOAT: keep
|
2020-05-13 23:26:40 +00:00
|
|
|
void ALU_any_result(struct object *result) // ACCEPT_UNDEFINED | ACCEPT_FLOAT
|
2012-02-27 21:14:46 +00:00
|
|
|
{
|
2020-04-26 18:53:14 +00:00
|
|
|
struct expression expression;
|
|
|
|
|
2020-05-20 16:39:19 +00:00
|
|
|
parse_expression(&expression); // FIXME - check return value and pass to caller!
|
2020-05-13 23:26:40 +00:00
|
|
|
*result = expression.result;
|
2020-04-26 18:53:14 +00:00
|
|
|
if (expression.open_parentheses)
|
2012-02-27 21:14:46 +00:00
|
|
|
Throw_error(exception_paren_open);
|
2020-04-26 22:26:05 +00:00
|
|
|
if (expression.is_empty)
|
2012-02-27 21:14:46 +00:00
|
|
|
Throw_error(exception_no_value);
|
|
|
|
}
|
2020-04-30 16:34:09 +00:00
|
|
|
|
2020-05-02 10:40:10 +00:00
|
|
|
|
2020-04-30 16:34:09 +00:00
|
|
|
/* TODO
|
|
|
|
|
2020-05-20 16:39:19 +00:00
|
|
|
maybe move
|
2020-05-08 13:22:15 +00:00
|
|
|
if (expression.is_empty)
|
|
|
|
Throw_error(exception_no_value);
|
|
|
|
to end of parse_expression()
|
|
|
|
|
|
|
|
|
2020-05-08 00:34:46 +00:00
|
|
|
// stores int value and flags, allowing for "paren" '(' too many (x-indirect addr).
|
2020-05-07 23:32:06 +00:00
|
|
|
void ALU_addrmode_int(struct expression *expression, int paren)
|
2020-04-30 16:34:09 +00:00
|
|
|
mnemo.c
|
2020-05-09 20:58:08 +00:00
|
|
|
when parsing addressing modes needvalue!
|
2020-04-30 16:34:09 +00:00
|
|
|
|
2020-05-20 16:39:19 +00:00
|
|
|
// store resulting object
|
2020-05-13 23:26:40 +00:00
|
|
|
void ALU_any_result(struct object *result)
|
2020-04-30 16:34:09 +00:00
|
|
|
macro.c
|
2020-05-02 14:58:17 +00:00
|
|
|
macro call, when parsing call-by-value arg don't care
|
2020-04-30 16:34:09 +00:00
|
|
|
pseudoopcodes.c
|
2020-05-02 14:58:17 +00:00
|
|
|
!set don't care
|
|
|
|
when throwing user-specified errors don't care
|
2020-04-30 16:34:09 +00:00
|
|
|
symbol.c
|
2020-05-02 14:58:17 +00:00
|
|
|
explicit symbol definition don't care
|
2020-04-30 16:34:09 +00:00
|
|
|
|
|
|
|
// stores int value and flags (floats are transformed to int)
|
|
|
|
// if result was undefined, serious error is thrown
|
|
|
|
void ALU_defined_int(struct number *intresult)
|
|
|
|
flow.c
|
2020-05-02 14:58:17 +00:00
|
|
|
when parsing loop conditions make bool serious
|
2020-04-30 16:34:09 +00:00
|
|
|
pseudoopcodes.c
|
2020-05-02 14:58:17 +00:00
|
|
|
*= (FIXME, allow undefined) needvalue!
|
2020-05-23 23:38:13 +00:00
|
|
|
!initmem error
|
2020-05-02 14:58:17 +00:00
|
|
|
!fill (1st arg) (maybe allow undefined?) needvalue!
|
|
|
|
!skip (maybe allow undefined?) needvalue!
|
|
|
|
!align (1st + 2nd arg) (maybe allow undefined?) needvalue!
|
|
|
|
!pseudopc (FIXME, allow undefined) needvalue!
|
|
|
|
!if make bool serious
|
|
|
|
twice in !for serious
|
|
|
|
twice for !binary (maybe allow undefined?) needvalue!
|
2020-04-30 16:34:09 +00:00
|
|
|
//!enum
|
|
|
|
|
2020-05-23 23:38:13 +00:00
|
|
|
// store int value (0 if result was undefined)
|
|
|
|
void ALU_any_int(intval_t *target)
|
2020-04-30 16:34:09 +00:00
|
|
|
pseudoopcodes.c
|
2020-05-02 14:58:17 +00:00
|
|
|
!xor needvalue!
|
|
|
|
iterator for !by, !wo, etc. needvalue!
|
|
|
|
byte values in !raw, !tx, etc. needvalue!
|
|
|
|
!scrxor needvalue!
|
|
|
|
!fill (2nd arg) needvalue!
|
|
|
|
!align (3rd arg) needvalue!
|
2020-04-30 16:34:09 +00:00
|
|
|
*/
|