/** * @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 }, { "APPEND", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_append }, { "ATN", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_atn }, { "AUTO", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_auto }, { "BACKUP", AKBASIC_TOK_COMMAND, -1, akbasic_parse_optional_arglist, akbasic_cmd_backup }, { "BEGIN", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_begin }, { "BEND", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_bend }, { "BLOAD", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_bload }, { "BOOT", AKBASIC_TOK_COMMAND, -1, akbasic_parse_optional_arglist, akbasic_cmd_boot }, { "BOX", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_box }, { "BSAVE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_bsave }, { "BUMP", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_bump }, { "CATALOG", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, akbasic_parse_optional_arglist, akbasic_cmd_directory }, { "CHAR", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_char }, { "CHR", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_chr }, { "CIRCLE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_circle }, { "CLR", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_clr }, { "COLLECT", AKBASIC_TOK_COMMAND, -1, akbasic_parse_optional_arglist, akbasic_cmd_collect }, { "COLLISION", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_collision }, { "COLOR", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_color }, { "CONCAT", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_concat }, { "CONT", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_cont }, { "COPY", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_copy }, { "COS", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_cos }, { "DATA", AKBASIC_TOK_COMMAND, -1, akbasic_parse_data, akbasic_cmd_data }, { "DCLEAR", AKBASIC_TOK_COMMAND, -1, akbasic_parse_optional_arglist, akbasic_cmd_dclear }, { "DCLOSE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_optional_arglist, akbasic_cmd_dclose }, { "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 }, { "DIRECTORY", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, akbasic_parse_optional_arglist, akbasic_cmd_directory }, { "DLOAD", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_dload }, { "DO", AKBASIC_TOK_COMMAND, -1, akbasic_parse_do, akbasic_cmd_do }, { "DOPEN", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_dopen }, { "DRAW", AKBASIC_TOK_COMMAND, -1, akbasic_parse_draw, akbasic_cmd_draw }, { "DSAVE", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_dsave }, { "DVERIFY", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, akbasic_parse_arglist, akbasic_cmd_dverify }, { "ELSE", AKBASIC_TOK_COMMAND, -1, NULL, NULL }, { "END", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_end }, { "ENVELOPE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_envelope }, { "ERR", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_err }, { "EXIT", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_exit }, { "FETCH", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_fetch }, { "FILTER", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_filter }, { "FOR", AKBASIC_TOK_COMMAND, -1, akbasic_parse_for, akbasic_cmd_for }, { "GET", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_get }, { "GETKEY", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_getkey }, { "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 }, { "HEADER", AKBASIC_TOK_COMMAND, -1, akbasic_parse_optional_arglist, akbasic_cmd_header }, { "HELP", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_help }, { "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 }, /* * `INPUT#` and `PRINT#` are never scanned as verb names -- the scanner reads * a `#` as a type suffix and refuses a verb carrying one -- so these rows are * reached only from akbasic_parse_input() and akbasic_parse_print(), which * build a leaf with the name directly. They still have to be here, and in * order, because dispatch is a bsearch on the leaf's name. */ { "INPUT#", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_input_channel }, { "INSTR", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_instr }, { "KEY", AKBASIC_TOK_COMMAND, -1, akbasic_parse_optional_arglist, akbasic_cmd_key }, { "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 }, { "LOAD", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_dload }, { "LOCATE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_locate }, { "LOG", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_log }, { "LOOP", AKBASIC_TOK_COMMAND, -1, akbasic_parse_loop, akbasic_cmd_loop }, { "MID", AKBASIC_TOK_FUNCTION, 3, NULL, akbasic_fn_mid }, { "MOD", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_mod }, { "MOVSPR", AKBASIC_TOK_COMMAND, -1, akbasic_parse_movspr, akbasic_cmd_movspr }, { "NEW", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_new }, { "NEXT", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_next }, { "NOT", AKBASIC_TOK_NOT, -1, NULL, NULL }, { "ON", AKBASIC_TOK_COMMAND, -1, akbasic_parse_on, akbasic_cmd_on }, { "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 }, { "PLAY", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_play }, { "POINT", AKBASIC_TOK_COMMAND, -1, akbasic_parse_point, akbasic_cmd_point }, { "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, akbasic_parse_print, akbasic_cmd_print }, { "PRINT#", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_print_channel }, { "PUDEF", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_pudef }, { "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 }, { "RECORD", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_record }, { "REM", AKBASIC_TOK_REM, -1, NULL, NULL }, { "RENAME", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_rename }, { "RENUMBER", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, akbasic_parse_optional_arglist, akbasic_cmd_renumber }, { "RESTORE", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_restore }, { "RESUME", AKBASIC_TOK_COMMAND, -1, akbasic_parse_resume, akbasic_cmd_resume }, { "RETURN", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_return }, { "RGR", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_rgr }, { "RIGHT", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_right }, { "RSPCOLOR", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_rspcolor }, { "RSPPOS", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_rsppos }, { "RSPRITE", AKBASIC_TOK_FUNCTION, 2, NULL, akbasic_fn_rsprite }, { "RUN", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_run }, { "SAVE", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, NULL, akbasic_cmd_dsave }, { "SCALE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_scale }, { "SCNCLR", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_scnclr }, { "SCRATCH", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_scratch }, { "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 }, { "SLEEP", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_sleep }, { "SOUND", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_sound }, { "SPC", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_spc }, { "SPRCOLOR", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_sprcolor }, { "SPRITE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_sprite }, { "SPRSAV", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_sprsav }, { "SSHAPE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_sshape }, { "STASH", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_stash }, { "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 }, { "SWAP", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_swap }, { "SYS", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_sys }, { "TAN", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_tan }, { "TEMPO", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_tempo }, { "THEN", AKBASIC_TOK_COMMAND, -1, NULL, NULL }, { "TO", AKBASIC_TOK_COMMAND, -1, NULL, NULL }, { "TRAP", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_trap }, { "TROFF", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_troff }, { "TRON", AKBASIC_TOK_COMMAND, -1, NULL, akbasic_cmd_tron }, { "TYPE", AKBASIC_TOK_COMMAND, -1, akbasic_parse_type, akbasic_cmd_type }, { "UNTIL", AKBASIC_TOK_COMMAND, -1, NULL, NULL }, { "USING", AKBASIC_TOK_COMMAND, -1, NULL, NULL }, { "VAL", AKBASIC_TOK_FUNCTION, 1, NULL, akbasic_fn_val }, { "VERIFY", AKBASIC_TOK_COMMAND_IMMEDIATE, -1, akbasic_parse_arglist, akbasic_cmd_dverify }, { "VOL", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_vol }, { "WAIT", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_wait }, { "WHILE", AKBASIC_TOK_COMMAND, -1, NULL, NULL }, { "WIDTH", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_width }, { "WINDOW", AKBASIC_TOK_COMMAND, -1, akbasic_parse_arglist, akbasic_cmd_window }, { "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); }