From 9e49ae687d56f33bb3d47e9f939265bafe44f4f4 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Wed, 10 Jul 2024 04:00:55 -0400 Subject: whichbas: add operator classifications (is_numeric_op() and friends), use them in INKEY$/semicolon logic (cmd 0x59). --- whichbas.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 160 insertions(+), 3 deletions(-) diff --git a/whichbas.c b/whichbas.c index da50998..57e7098 100644 --- a/whichbas.c +++ b/whichbas.c @@ -115,6 +115,149 @@ void print_result(void) { } } +/* return true if a token is numeric constant + (including TB/BXE/BXL hex) */ +int is_numconst_op(unsigned char tok) { + switch(tok) { + case OP_NUMCONST: + case OP_HEXCONST: + return 1; + default: + return 0; + } +} + +/* return true if a token is a function that *returns* + a numeric value (says nothing about the argument types, + though!) */ +int is_numeric_func(unsigned char tok) { + switch(tok) { + case OP_FUNC_USR: + case OP_FUNC_ASC: + case OP_FUNC_VAL: + case OP_FUNC_LEN: + case OP_FUNC_ADR: + case OP_FUNC_ATN: + case OP_FUNC_COS: + case OP_FUNC_PEEK: + case OP_FUNC_SIN: + case OP_FUNC_RND: + case OP_FUNC_FRE: + case OP_FUNC_EXP: + case OP_FUNC_LOG: + case OP_FUNC_CLOG: + case OP_FUNC_SQR: + case OP_FUNC_SGN: + case OP_FUNC_ABS: + case OP_FUNC_INT: + case OP_FUNC_PADDLE: + case OP_FUNC_STICK: + case OP_FUNC_PTRIG: + case OP_FUNC_STRIG: + return 1; + default: + return 0; + } +} + +/* return true if a token is an arithmetic operator */ +int is_arith_op(unsigned char tok) { + switch(tok) { + case OP_NUM_LE: + case OP_NUM_NE: + case OP_NUM_GE: + case OP_NUM_LT: + case OP_NUM_GT: + case OP_NUM_EQ: + case OP_POWER: + case OP_MULT: + case OP_PLUS: + case OP_MINUS: + case OP_DIVIDE: + case OP_NOT: + case OP_OR: + case OP_AND: + case OP_NUM_ASSIGN: + case OP_UPLUS: + case OP_UMINUS: + return 1; + default: + return 0; + } +} + +int is_numeric_var(unsigned char tok) { + int vartype; + + if(tok < 0x80) + return 0; + + vartype = get_vartype(tok); + return (vartype == TYPE_SCALAR || vartype == TYPE_ARRAY); +} + +/* return true if a token is: + - a numeric constant (including hex constants), + - a numeric variable (including arrays), + - a math operator (plus, minus, etc), + - a function that returns a numeric (e.g. ASC(), SIN()). + for now, only standard Atari BASIC tokens are considered. + */ +int is_numeric_op(unsigned char tok) { + return + is_numconst_op (tok) || + is_arith_op (tok) || + is_numeric_func (tok) || + is_numeric_var (tok) ; +} + +int is_string_var(unsigned char tok) { + return (tok >= 0x80 && (get_vartype(tok) == TYPE_STRING)); +} + +int is_string_const(unsigned char tok) { + return (tok == OP_STRCONST); +} + +int is_string_exp_op(unsigned char tok) { + switch(tok) { + case OP_STR_ASSIGN: + case OP_STR_LE: + case OP_STR_NE: + case OP_STR_GE: + case OP_STR_LT: + case OP_STR_GT: + case OP_STR_EQ: + return 1; + default: + return 0; + } +} + +int is_string_func(unsigned char tok) { + switch(tok) { + case OP_FUNC_STR: + case OP_FUNC_CHR: + return 1; + default: + return 0; + } +} + +/* return true if a token is: + - a string constant, + - a string variable, + - a string expression operator, like OP_STR_LE, + - a function that returns a string. +*/ +int is_string_op(unsigned char tok) { + return + is_string_const (tok) || + is_string_func (tok) || + is_string_exp_op (tok) || + is_string_var (tok) ; +} + void remove_type(int type) { bas_type &= ((~type) & 0x0f); @@ -525,8 +668,7 @@ CALLBACK(handle_op) { case 0x59: /* INKEY$ (0 arg pseudo-func) in TB, string array separator semicolon in BXL/BXE */ /* PARTIAL: ...but pretty good. we *can't* check nexttok == OP_GRP_RPAR, because - VAL(INKEY$) or ASC(INKEY$) are legit Turbo code. - This can fail to catch A$(X;Y) if X and Y are both complex expressions. */ + VAL(INKEY$) or ASC(INKEY$) are legit Turbo code. */ if(nexttok == OP_EOS || nexttok == OP_EOL) { /* the semicolon can't be the last token on the line (needs at least a right-paren), but INKEY$ can. */ @@ -535,6 +677,12 @@ CALLBACK(handle_op) { /* INKEY$ can be the first operator after the command, e.g if the command is IF. The semicolon cannot. */ remove_type(BT_BXL_BXE); + } else if(is_string_exp_op(last_op_tok) || is_string_exp_op(nexttok)) { + /* A$=INKEY$, IF INKEY$=A$, A$(LEN(A$)+1)=INKEY$, INKEY$<>"A"... */ + remove_type(BT_BXL_BXE); + } else if(is_numeric_op(last_op_tok) || is_numeric_op(nexttok)) { + remove_type(BT_TURBO); +#if 0 } else if(last_op_tok == OP_STR_ASSIGN) { /* catches A$=INKEY$, for what that's worth. */ remove_type(BT_BXL_BXE); @@ -547,6 +695,7 @@ CALLBACK(handle_op) { TODO: determine exactly what all it *can* be followed by, check for that. */ remove_type(BT_TURBO); +#endif } break; @@ -623,9 +772,17 @@ CALLBACK(handle_op) { if(nexttok == OP_STRCONST || nexttok >= 0x80) { /* %0 %1 %2 can't be followed by a string constant *or* a variable */ remove_type(BT_TURBO); - /* Can't do, due to LEFT$(HEX$("1234"), 1) (or STR$, etc) */ + /* Can't do, due to LEFT$(HEX$("1234"), 1) (or STR$, etc): */ /* } else { remove_type(BT_BXL_BXE); */ + /* ...but this stuff helps: */ + } else if(nexttok == OP_EOS || nexttok == OP_EOL) { + /* LEFT$ RIGHT$( MID$( can't occur at the end of a statement. */ + remove_type(BT_BXL_BXE); + } else if(pos == (last_cmd_pos + 2) && program[pos - 1] == OP_NUM_ASSIGN) { + /* LEFT$ RIGHT$( MID$( return strings, assignment would + be OP_STR_ASSIGN. */ + remove_type(BT_BXL_BXE); } break; -- cgit v1.2.3