Replace range-based tests for is_XYZ with flag-based tests

This gives us much more flexibility in rearranging IDs, and makes the
code overall much more readable.

Fixes: #234

Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
This commit is contained in:
Christophe de Dinechin 2023-07-29 15:27:27 +02:00
parent 684ab0fb7d
commit 7451edfccc
8 changed files with 217 additions and 89 deletions

View file

@ -119,7 +119,7 @@ fraction_p arithmetic::fraction_promotion(algebraic_g &x)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
{ {
id ty = x->type(); id ty = x->type();
if (ty >= FIRST_FRACTION_TYPE && ty <= LAST_FRACTION_TYPE) if (is_fraction(ty))
return fraction_g((fraction *) object_p(x)); return fraction_g((fraction *) object_p(x));
if (ty >= ID_integer && ty <= ID_neg_integer) if (ty >= ID_integer && ty <= ID_neg_integer)
{ {

View file

@ -294,7 +294,7 @@ inline size_t bignum::wordsize(id type)
// Return the word size for an bignum type in bits // Return the word size for an bignum type in bits
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
{ {
if (type >= FIRST_BASED_TYPE && type <= LAST_BASED_TYPE) if (is_based(type))
return Settings.wordsize; return Settings.wordsize;
return 0; return 0;
} }

View file

@ -48,10 +48,10 @@ MENU_BODY(Catalog)
} }
#define NUM_COMMANDS (object::LAST_COMMAND - object::FIRST_COMMAND + 1) static object::id sorted_ids[object::NUM_IDS];
static object::id sorted_ids[NUM_COMMANDS];
static uint NUM_COMMANDS = 0;
static int sort_ids(const void *left, const void *right) static int sort_ids(const void *left, const void *right)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -69,9 +69,12 @@ static void initialize_sorted_ids()
// Sort IDs alphabetically // Sort IDs alphabetically
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
{ {
for (uint i = 0; i < NUM_COMMANDS; i++) uint count = 0;
sorted_ids[i] = object::id(i + object::FIRST_COMMAND); for (uint i = 0; i < object::NUM_IDS; i++)
qsort(sorted_ids, NUM_COMMANDS, sizeof(sorted_ids[0]), sort_ids); if (object::is_command(object::id(i)))
sorted_ids[count++] = object::id(i);
qsort(sorted_ids, count, sizeof(sorted_ids[0]), sort_ids);
NUM_COMMANDS = count;
} }

View file

@ -60,7 +60,7 @@ PARSE_BODY(command)
bool eq = p.precedence; bool eq = p.precedence;
// If we are parsing an equation, only accept algebraic command // If we are parsing an equation, only accept algebraic command
if (eq && (i < FIRST_ALGEBRAIC || i > LAST_ALGEBRAIC)) if (eq && !is_algebraic_function(i))
return SKIP; return SKIP;
id found = id(0); id found = id(0);

View file

@ -48,16 +48,44 @@
#define ALIAS(op, name) #define ALIAS(op, name)
#endif #endif
#ifndef OPCODE
#define OPCODE(id) ID(id)
#endif
#ifndef MENU #ifndef MENU
#define MENU(id) ID(id) #define MENU(id) ID(id)
#endif #endif
#ifndef FLAGS
#define FLAGS(name, ...)
#endif
// ============================================================================
//
// Range of types for attributes
//
// ============================================================================
FLAGS(is_type, directory, decimal128)
FLAGS(is_integer, hex_integer, neg_integer)
FLAGS(is_based, hex_integer, based_integer, hex_bignum, based_bignum)
FLAGS(is_bignum, hex_bignum, neg_bignum)
FLAGS(is_fraction, fraction, neg_big_fraction)
FLAGS(is_decimal, decimal32, decimal128)
FLAGS(is_real, bignum, decimal128)
FLAGS(is_complex, rectangular, polar)
FLAGS(is_command, Drop, Unimplemented)
FLAGS(is_symbolic, local, equation, pi, ImaginaryUnit)
FLAGS(is_algebraic, add, det)
FLAGS(is_immediate, MainMenu, LastMenu)
// ============================================================================
//
// Definition of types
//
// ============================================================================
ID(object) // Value 0 is reserved for "not implemented" ID(object) // Value 0 is reserved for "not implemented"
ID(directory) ID(directory)
ID(text) ID(text)
ID(list) ID(list)
@ -72,53 +100,36 @@ ID(symbol)
ID(equation) ID(equation)
// Complex types must be parsed before numbers // Complex types must be parsed before numbers
#define FIRST_COMPLEX_TYPE ID_rectangular
ID(rectangular) ID(rectangular)
ID(polar) ID(polar)
#define LAST_COMPLEX_TYPE ID_polar
#define FIRST_INTEGER_TYPE ID_hex_integer
#define FIRST_BASED_TYPE ID_hex_integer
ID(hex_integer) ID(hex_integer)
ID(dec_integer) ID(dec_integer)
ID(oct_integer) ID(oct_integer)
ID(bin_integer) ID(bin_integer)
ID(based_integer) ID(based_integer)
#define FIRST_BIGNUM_TYPE ID_hex_bignum
ID(hex_bignum) ID(hex_bignum)
ID(dec_bignum) ID(dec_bignum)
ID(oct_bignum) ID(oct_bignum)
ID(bin_bignum) ID(bin_bignum)
ID(based_bignum) ID(based_bignum)
#define LAST_BASED_TYPE ID_based_bignum
#define FIRST_REAL_TYPE ID_bignum
#define FIRST_SYMBOLIC_TYPE ID_bignum
ID(bignum) ID(bignum)
ID(neg_bignum) ID(neg_bignum)
#define LAST_BIGNUM_TYPE ID_neg_bignum
ID(integer) ID(integer)
ID(neg_integer) ID(neg_integer)
#define LAST_INTEGER_TYPE ID_neg_integer
#define FIRST_FRACTION_TYPE ID_fraction
ID(fraction) ID(fraction)
ID(neg_fraction) ID(neg_fraction)
ID(big_fraction) ID(big_fraction)
ID(neg_big_fraction) ID(neg_big_fraction)
#define LAST_FRACTION_TYPE ID_neg_big_fraction
#define FIRST_DECIMAL_TYPE ID_decimal32
ID(decimal32) ID(decimal32)
ID(decimal64) ID(decimal64)
ID(decimal128) ID(decimal128)
#define LAST_DECIMAL_TYPE ID_decimal128
#define LAST_REAL_TYPE ID_decimal128
#define LAST_SYMBOLIC_TYPE ID_decimal128
#define FIRST_COMMAND ID_Drop
CMD(Drop) CMD(Drop)
CMD(Drop2) CMD(Drop2)
CMD(DropN) CMD(DropN)
@ -141,12 +152,8 @@ CMD(False)
NAMED(ToText, "→Text") NAMED(ToText, "→Text")
ALIAS(ToText, "→Str") ALIAS(ToText, "→Str")
#define FIRST_SYMBOLIC_CONSTANT ID_pi
NAMED(pi, "π") NAMED(pi, "π")
NAMED(ImaginaryUnit, "") NAMED(ImaginaryUnit, "")
#define LAST_SYMBOLIC_CONSTANT ID_ImaginaryUnit
#define FIRST_ALGEBRAIC ID_add
OP(add, "+") OP(add, "+")
OP(sub, "-") OP(sub, "-")
@ -231,7 +238,6 @@ CMD(arg)
CMD(conj) CMD(conj)
NAMED(det, "Determinant") NAMED(det, "Determinant")
#define LAST_ALGEBRAIC ID_det
// ============================================================================ // ============================================================================
@ -354,8 +360,6 @@ CMD(Version)
// //
// ============================================================================ // ============================================================================
#define FIRST_IMMEDIATE ID_MainMenu
MENU(MainMenu) MENU(MainMenu)
MENU(HomeMenu) MENU(HomeMenu)
@ -416,15 +420,15 @@ MENU(TextMenu)
CMD(VariablesMenu) CMD(VariablesMenu)
CMD(ToolsMenu) CMD(ToolsMenu)
CMD(LastMenu)
CMD(Catalog) CMD(Catalog)
CMD(LastMenu) // Last immediate command
NAMED(Off, "PowerOff") NAMED(Off, "PowerOff")
CMD(SaveState) CMD(SaveState)
CMD(SystemSetup) CMD(SystemSetup)
CMD(Help) CMD(Help)
CMD(Unimplemented) CMD(Unimplemented)
#define LAST_COMMAND ID_Unimplemented
// ============================================================================ // ============================================================================
@ -462,3 +466,4 @@ ID(dmcp_font)
#undef MENU #undef MENU
#undef NAMED #undef NAMED
#undef ALIAS #undef ALIAS
#undef FLAGS

View file

@ -73,6 +73,32 @@ RECORDER(object_errors, 16, "Runtime errors on objects");
RECORDER(assert_error, 16, "Assertion failures"); RECORDER(assert_error, 16, "Assertion failures");
template <typename first, typename last, typename ...rest>
struct handler_flag
{
static constexpr bool set(object::id id)
{
return (id >= first::static_id && id <= last::static_id)
|| handler_flag<rest...>::set(id);
}
};
template <typename first, typename last>
struct handler_flag<first, last>
{
static constexpr bool set(object::id id)
{
return id >= first::static_id && id <= last::static_id;
}
};
#define ID(id)
#define FLAGS(name, ...) \
static constexpr auto name = handler_flag<__VA_ARGS__>();
#include "ids.tbl"
const object::dispatch object::handler[NUM_IDS] = const object::dispatch object::handler[NUM_IDS] =
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Table of handlers for each object type // Table of handlers for each object type
@ -94,7 +120,19 @@ const object::dispatch object::handler[NUM_IDS] =
.menu = (menu_fn) id::do_menu, \ .menu = (menu_fn) id::do_menu, \
.menu_marker = (menu_marker_fn) id::do_menu_marker, \ .menu_marker = (menu_marker_fn) id::do_menu_marker, \
.arity = id::ARITY, \ .arity = id::ARITY, \
.precedence = id::PRECEDENCE \ .precedence = id::PRECEDENCE, \
.is_type = ::is_type.set(ID_##id), \
.is_integer = ::is_integer.set(ID_##id), \
.is_based = ::is_based.set(ID_##id), \
.is_bignum = ::is_bignum.set(ID_##id), \
.is_fraction = ::is_fraction.set(ID_##id), \
.is_real = ::is_real.set(ID_##id), \
.is_decimal = ::is_decimal.set(ID_##id), \
.is_complex = ::is_complex.set(ID_##id), \
.is_command = ::is_command.set(ID_##id), \
.is_symbolic = ::is_symbolic.set(ID_##id), \
.is_algebraic = ::is_algebraic.set(ID_##id), \
.is_immediate = ::is_immediate.set(ID_##id), \
}, },
#include "ids.tbl" #include "ids.tbl"
}; };

View file

@ -202,6 +202,18 @@ struct object
menu_marker_fn menu_marker; // Show marker menu_marker_fn menu_marker; // Show marker
uint arity; // Number of input arguments uint arity; // Number of input arguments
uint precedence; // Precedence in equations uint precedence; // Precedence in equations
bool is_type :1; // Is a data type
bool is_integer :1; // Is an integer type
bool is_based :1; // Is a based integer type
bool is_bignum :1; // Is a bignum type
bool is_fraction :1; // Is a fraction type
bool is_real :1; // Is a real type (excludes based ints)
bool is_decimal :1; // Is a decimal type
bool is_complex :1; // Is a complex (but not real) type
bool is_command :1; // Is an RPL command
bool is_symbolic :1; // Is a symbol or an equation
bool is_algebraic :1; // Algebraic functions (in equations)
bool is_immediate :1; // Commands that execute immediately
}; };
@ -460,12 +472,30 @@ struct object
// //
// ======================================================================== // ========================================================================
static bool is_type(id ty)
// -------------------------------------------------------------------------
// Check if a type is for an RPL data type
// -------------------------------------------------------------------------
{
return handler[ty].is_type;
}
bool is_type() const
// -------------------------------------------------------------------------
// Check if an object is an integer
// -------------------------------------------------------------------------
{
return is_type(type());
}
static bool is_integer(id ty) static bool is_integer(id ty)
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Check if a type is an integer // Check if a type is an integer
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
{ {
return ty >= FIRST_INTEGER_TYPE && ty <= LAST_INTEGER_TYPE; return handler[ty].is_integer;
} }
@ -478,12 +508,30 @@ struct object
} }
static bool is_based(id ty)
// -------------------------------------------------------------------------
// Check if a type is a based integer
// -------------------------------------------------------------------------
{
return handler[ty].is_based;
}
bool is_based() const
// -------------------------------------------------------------------------
// Check if an object is a based integer
// -------------------------------------------------------------------------
{
return is_based(type());
}
static bool is_bignum(id ty) static bool is_bignum(id ty)
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Check if a type is a big integer // Check if a type is a big integer
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
{ {
return ty >= FIRST_BIGNUM_TYPE && ty <= LAST_BIGNUM_TYPE; return handler[ty].is_bignum;
} }
@ -501,7 +549,7 @@ struct object
// Check if a type is a fraction // Check if a type is a fraction
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
{ {
return ty >= FIRST_FRACTION_TYPE && ty <= LAST_FRACTION_TYPE; return handler[ty].is_fraction;
} }
@ -514,6 +562,16 @@ struct object
} }
static bool is_fractionable(id ty)
// -------------------------------------------------------------------------
// Check if a type is a fraction or a non-based integer
// -------------------------------------------------------------------------
{
return handler[ty].is_fraction ||
(handler[ty].is_integer && handler[ty].is_real);
}
bool is_fractionable() const bool is_fractionable() const
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Check if an object is a fraction or an integer // Check if an object is a fraction or an integer
@ -523,21 +581,12 @@ struct object
} }
static bool is_fractionable(id ty)
// -------------------------------------------------------------------------
// Check if a type is a fraction or a non-based integer
// -------------------------------------------------------------------------
{
return ty >= FIRST_REAL_TYPE && ty <= LAST_FRACTION_TYPE;
}
static bool is_decimal(id ty) static bool is_decimal(id ty)
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Check if a type is a decimal // Check if a type is a decimal
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
{ {
return ty >= FIRST_DECIMAL_TYPE && ty <= LAST_DECIMAL_TYPE; return handler[ty].is_decimal;
} }
@ -555,7 +604,7 @@ struct object
// Check if a type is a real number // Check if a type is a real number
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
{ {
return ty >= FIRST_REAL_TYPE && ty <= LAST_REAL_TYPE; return handler[ty].is_real;
} }
@ -573,7 +622,7 @@ struct object
// Check if a type is a complex number // Check if a type is a complex number
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
{ {
return ty >= FIRST_COMPLEX_TYPE && ty <= LAST_COMPLEX_TYPE; return handler[ty].is_complex;
} }
@ -591,7 +640,7 @@ struct object
// Check if a type denotes a command // Check if a type denotes a command
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
{ {
return ty >= FIRST_COMMAND && ty <= LAST_COMMAND; return handler[ty].is_command;
} }
@ -604,15 +653,48 @@ struct object
} }
static bool is_immediate(id ty)
// ------------------------------------------------------------------------
// Check if a type denotes an immediate command (e.g. menus)
// ------------------------------------------------------------------------
{
return handler[ty].is_immediate;
}
bool is_immediate() const
// ------------------------------------------------------------------------
// Check if an object is an immediate command (e.g. menus)
// ------------------------------------------------------------------------
{
return is_immediate(type());
}
static bool is_algebraic_number(id ty)
// ------------------------------------------------------------------------
// Check if something is a number (real or complex)
// ------------------------------------------------------------------------
{
return is_real(ty) || is_complex(ty);
}
bool is_algebraic_number() const
// ------------------------------------------------------------------------
// Check if something is a number (real or complex)
// ------------------------------------------------------------------------
{
return is_algebraic_number(type());
}
static bool is_symbolic(id ty) static bool is_symbolic(id ty)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Check if a type denotes a symbolic argument (symbol, equation, number) // Check if a type denotes a symbolic argument (symbol, equation, number)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
{ {
return (is_strictly_symbolic(ty) || return handler[ty].is_symbolic || is_algebraic_number(ty);
(ty >= FIRST_SYMBOLIC_TYPE && ty <= LAST_SYMBOLIC_TYPE) ||
(ty >= FIRST_COMPLEX_TYPE && ty <= LAST_COMPLEX_TYPE) ||
(ty >= FIRST_SYMBOLIC_CONSTANT && ty <= LAST_SYMBOLIC_CONSTANT));
} }
@ -630,7 +712,7 @@ struct object
// Check if a type denotes a symbol or equation // Check if a type denotes a symbol or equation
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
{ {
return ty == ID_symbol || ty == ID_equation || ty == ID_local; return handler[ty].is_symbolic;
} }
@ -648,7 +730,7 @@ struct object
// Check if a type denotes an algebraic function // Check if a type denotes an algebraic function
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
{ {
return ty >= FIRST_ALGEBRAIC && ty <= LAST_ALGEBRAIC; return handler[ty].is_algebraic;
} }

View file

@ -3385,7 +3385,7 @@ bool user_interface::handle_functions(int key)
{ {
evaluating = key; evaluating = key;
object::id ty = obj->type(); object::id ty = obj->type();
bool imm = ty >= object::FIRST_IMMEDIATE && ty <= object::LAST_COMMAND; bool imm = object::is_immediate(ty);
if (rt.editing() && !imm) if (rt.editing() && !imm)
{ {
if (key == KEY_ENTER || key == KEY_BSP) if (key == KEY_ENTER || key == KEY_BSP)