/** * @file verbs.c * @brief The dispatch table: every verb, function and reserved word in one place. */ #include #include #include #include #include #include #include "verbs.h" /* * Sorted by name so bsearch() is valid -- tests/verbs_table.c asserts the * ordering, because a mis-sorted row silently becomes an unfindable verb rather * than a compile error. * * A NULL parse handler means the rval parses as a plain expression. A NULL exec * handler means the token is consumed by some other verb's parse handler and is * never evaluated on its own: ELSE, STEP, THEN and TO are all in that class, as * are the four reserved words, which exist here only so the scanner can give * them their token type. * * The reference bootstraps MOD, SPC and STR by running a BASIC program of DEF * statements through the interpreter at startup and keeping their expressions * (basicruntime_functions.go:14). That hack is not reproduced: they are ordinary * native handlers here, which removes the need to run the interpreter before the * interpreter is ready. */ static const akbasic_Verb VERBS[] = { /* name token type arity parse handler exec handler */ { "ABS", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_abs }, { "AND", AKBASIC_TOK_AND, -1, NULL, NULL }, { "ATN", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_atn }, { "AUTO", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_auto }, { "BOX", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_box }, { "CHR", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_chr }, { "CIRCLE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_circle }, { "COLOR", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_color }, { "COS", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_cos }, { "DATA", AKBASIC_TOK_COMMAND, -1, akbasic_parse_data, akbasic_cmd_data }, { "DEF", AKBASIC_TOK_COMMAND, -1, akbasic_parse_def, akbasic_cmd_def }, { "DELETE", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_delete }, { "DIM", AKBASIC_TOK_COMMAND, -1, akbasic_parse_dim, akbasic_cmd_dim }, { "DLOAD", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_dload }, { "DRAW", AKBASIC_TOK_COMMAND, -1, akbasic_parse_draw, akbasic_cmd_draw }, { "DSAVE", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_dsave }, { "ELSE", AKBASIC_TOK_COMMAND, -1, NULL, NULL }, { "EXIT", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_exit }, { "FOR", AKBASIC_TOK_COMMAND, -1, akbasic_parse_for, akbasic_cmd_for }, { "GOSUB", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_gosub }, { "GOTO", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_goto }, { "GRAPHIC", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_graphic }, { "GSHAPE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_gshape }, { "HEX", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_hex }, { "IF", AKBASIC_TOK_COMMAND, -1, akbasic_parse_if, akbasic_cmd_if }, { "INPUT", AKBASIC_TOK_COMMAND, -1, akbasic_parse_input, akbasic_cmd_input }, { "INSTR", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_instr }, { "LABEL", AKBASIC_TOK_COMMAND, -1, akbasic_parse_label, akbasic_cmd_label }, { "LEFT", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_left }, { "LEN", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_len }, { "LET", AKBASIC_TOK_COMMAND, -1, akbasic_parse_let, akbasic_cmd_let }, { "LIST", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_list }, { "LOCATE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_locate }, { "LOG", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_log }, { "MID", AKBASIC_TOK_FUNCTION, 3, NULL, akbasic_fn_mid }, { "MOD", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_mod }, { "NEXT", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_next }, { "NOT", AKBASIC_TOK_NOT, -1, NULL, NULL }, { "OR", AKBASIC_TOK_OR, -1, NULL, NULL }, { "PAINT", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_paint }, { "PEEK", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_peek }, { "POINTER", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_pointer }, { "POINTERVAR", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_pointervar }, { "POKE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_poke, akbasic_cmd_poke }, { "PRINT", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_print }, { "QUIT", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_quit }, { "RAD", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_rad }, { "READ", AKBASIC_TOK_COMMAND, -1, akbasic_parse_read, akbasic_cmd_read }, { "REM", AKBASIC_TOK_REM, -1, NULL, NULL }, { "RETURN", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_return }, { "RIGHT", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_right }, { "RUN", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_run }, { "SCALE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_scale }, { "SGN", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_sgn }, { "SHL", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_shl }, { "SHR", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_shr }, { "SIN", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_sin }, { "SPC", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_spc }, { "SSHAPE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_sshape }, { "STEP", AKBASIC_TOK_COMMAND, -1, NULL, NULL }, { "STOP", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_stop }, { "STR", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_str }, { "TAN", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_tan }, { "THEN", AKBASIC_TOK_COMMAND, -1, NULL, NULL }, { "TO", AKBASIC_TOK_COMMAND, -1, NULL, NULL }, { "VAL", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_val }, { "XOR", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_xor }, }; #define VERB_COUNT ((int)(sizeof(VERBS) / sizeof(VERBS[0]))) static int verb_compare(const void *key, const void *element) { const akbasic_Verb *verb = (const akbasic_Verb *)element; return strcmp((const char *)key, verb->name); } const akbasic_Verb *akbasic_verb_table(int *count) { if ( count != NULL ) { *count = VERB_COUNT; } return VERBS; } akerr_ErrorContext *akbasic_verb_lookup(const char *name, const akbasic_Verb **dest) { PREPARE_ERROR(errctx); char upper[AKBASIC_MAX_STRING_LENGTH]; size_t i = 0; size_t len = 0; FAIL_ZERO_RETURN(errctx, (name != NULL), AKERR_NULLPOINTER, "NULL name in verb lookup"); FAIL_ZERO_RETURN(errctx, (dest != NULL), AKERR_NULLPOINTER, "NULL destination in verb lookup"); *dest = NULL; len = strlen(name); if ( len == 0 || len >= sizeof(upper) ) { /* Too long to be any verb we know. Not an error, just a miss. */ SUCCEED_RETURN(errctx); } for ( i = 0; i < len; i++ ) { upper[i] = (char)toupper((unsigned char)name[i]); } upper[len] = '\0'; *dest = (const akbasic_Verb *)bsearch(upper, VERBS, VERB_COUNT, sizeof(VERBS[0]), verb_compare); SUCCEED_RETURN(errctx); }