Here are a couple of numeric expression parsers I wrote a while back. Both use all QB64 numeric/logical/boolean operators along with standard Basic precedence, as noted in the Microsoft BASIC manuals, namely:
Operator Precedence in BASIC
Highest: ^
+ - (unary negation)
* /
+ -
= <> < > <= >=
The first uses a form of Recursive Descent called Precedence Climbing, which I find easier than traditional Recursive Descent. The second uses traditional Recursive Descent. Both support the operators listed above, along with the following functions: abs, atn, cos, exp, fix, int, log, rnd, sgn, sin, sqr, tan.
Hopefully, the code is easy enough to follow. Feedback is of course appreciated.
Expression Parser using Precedence Climbing:
'Simple numeric calculator - uses BASIC precedence rules
'
'Supports all QB numeric/logical operators, plus the following functions:
'
'abs, atn, cos, exp, fix, int, log, rnd, sgn, sin, sqr, tan
'
'Uses Precedence Climbing algorithm
'
'Operator Precedence in BASIC
'
'Highest: ^
' + - (unary negation)
' * /
' \ (integer division)
' MOD
' + -
' = <> < > <= >=
' NOT (unary)
' AND
' OR
' XOR
' EQV
'Lowest: IMP
'
' Values for tok - also indexes into prec array - code below depends on this ordering
' populate the precedence array - these conicide with the constants above
' we use the operator constants (above) as indexes into the precedence array
'
prec(POWER) = 13
prec(NEGATE) = 12
prec(DIVIDE) = 11: prec(MULTIPLY) = 11
prec(INT_DIV) = 10
prec(MODULUS) = 9
prec(SUBTRACT) = 8: prec(ADD) = 8
prec(GTR_T_EQL)=7:prec(LSS_T_EQL)=7:prec(GTR_T)=7:prec(LSS_T)=7:prec(NOT_EQUAL)=7:prec(EQUAL)=7
prec(NOT_T) = 6
prec(AND_T) = 5
prec(OR_T) = 4
prec(XOR_T) = 3
prec(EQV_T) = 2
prec(IMP_T) = 1
input_st = "-1 - 2 - 3 - 4"
print input_st
+ " = "; eval#
input_st = "3 + 6 / 12 * 3 - 2"
print input_st
+ " = "; eval#
input_st = "4 ^ - 2"
print input_st
+ " = "; eval#
input_st = "4 ^ 3 ^ 2"
print input_st
+ " = "; eval#
input_st = "32 / 2 / 2"
print input_st
+ " = "; eval#
input_st = "atn(1) * 4"
print "PI = " + input_st
+ " = "; eval#
input "Enter a string to evaluate > "; input_st
print "Expression = "; eval#
?
next_tok
eval# = evalr#(0)
n = primary#
do while is_binary%
(tok
) 'need short circuit for this: and prec(tok) >= pr op = tok
next_tok
case IMP_T : n
= n
imp evalr#
(prec
(op
) + 1) case EQV_T : n
= n
eqv evalr#
(prec
(op
) + 1) case XOR_T : n
= n
xor evalr#
(prec
(op
) + 1) case OR_T : n
= n
or evalr#
(prec
(op
) + 1) case AND_T : n
= n
and evalr#
(prec
(op
) + 1) case GTR_T : n
= n
> evalr#
(prec
(op
) + 1) case GTR_T_EQL : n
= n
>= evalr#
(prec
(op
) + 1) case LSS_T_EQL : n
= n
<= evalr#
(prec
(op
) + 1) case LSS_T : n
= n
< evalr#
(prec
(op
) + 1) case NOT_EQUAL : n
= n
<> evalr#
(prec
(op
) + 1) case EQUAL : n
= n
= evalr#
(prec
(op
) + 1) case SUBTRACT : n
= n
- evalr#
(prec
(op
) + 1) case ADD : n
= n
+ evalr#
(prec
(op
) + 1) case MODULUS : n
= n
mod evalr#
(prec
(op
) + 1) case INT_DIV : n
= n \ evalr#
(prec
(op
) + 1) case DIVIDE : n
= n
/ evalr#
(prec
(op
) + 1) case MULTIPLY : n
= n
* evalr#
(prec
(op
) + 1) case POWER : n
= n
^ evalr#
(prec
(op
) + 1) if is_relational
(op
) and is_relational
(tok
) then print "consecutive relational operators not allowed" evalr# = 0
evalr# = n
is_binary% = false
is_relational% = false
if op
>= EQUAL
and op
<= GTR_T_EQL
then is_relational%
= true
case NUMBER: n
= val(id_name
): next_tok
case ADD: next_tok: n
= evalr#
(prec
(NEGATE
)) case SUBTRACT: next_tok: n
= -evalr#
(prec
(NEGATE
)) case NOT_T: next_tok: n
= not evalr#
(prec
(NOT_T
)) next_tok
n = evalr#(0)
fun = id_name
next_tok
n = evalr#(0)
n = builtin(fun, n)
primary# = n
builtin# = n
id_name = ""
tok = 0
' skip spaces
' pick off a few multichar operators
case ">=": input_st
= mid$(input_st
, 3): tok
= GTR_T_EQL
case "<=": input_st
= mid$(input_st
, 3): tok
= LSS_T_EQL
case "<>": input_st
= mid$(input_st
, 3): tok
= NOT_EQUAL
' now do the rest, based on first letter
id_name
= id_name
+ left$(input_st
, 1) input_st
= mid$(input_st
, 2) tok = NUMBER
id_name
= id_name
+ left$(input_st
, 1) input_st
= mid$(input_st
, 2) case "mod": tok
= MODULUS:
case "^": input_st
= mid$(input_st
, 2): tok
= POWER
case "*": input_st
= mid$(input_st
, 2): tok
= MULTIPLY
case "/": input_st
= mid$(input_st
, 2): tok
= DIVIDE
case "\": input_st
= mid$(input_st
, 2): tok
= INT_DIV
case "+": input_st
= mid$(input_st
, 2): tok
= ADD
case "-": input_st
= mid$(input_st
, 2): tok
= SUBTRACT
case "=": input_st
= mid$(input_st
, 2): tok
= EQUAL
case "(": input_st
= mid$(input_st
, 2): tok
= LPAREN
case ")": input_st
= mid$(input_st
, 2): tok
= RPAREN
case ">": input_st
= mid$(input_st
, 2): tok
= GTR_T
case "<": input_st
= mid$(input_st
, 2): tok
= LSS_T
Expression Parser using Recursive Descent:
'Simple numeric calculator - uses BASIC precedence rules
'
'Supports all QB numeric/logical operators, plus the following functions:
'
'abs, atn, cos, exp, fix, int, log, rnd, sgn, sin, sqr, tan
'
'Uses Recursive Descent algorithm
'This requires one function per precedence level
'
'Operator Precedence in BASIC
'
'Highest: ^
' + - (unary negation)
' * /
' \ (integer division)
' MOD
' + -
' = <> < > <= >=
' NOT (unary)
' AND
' OR
' XOR
' EQV
'Lowest: IMP
' Values for tok
input_st = "-1 - 2 - 3 - 4"
print input_st
+ " = "; eval#
input_st = "3 + 6 / 12 * 3 - 2"
print input_st
+ " = "; eval#
input_st = "4 ^ - 2"
print input_st
+ " = "; eval#
input_st = "4 ^ 3 ^ 2"
print input_st
+ " = "; eval#
input_st = "32 / 2 / 2"
print input_st
+ " = "; eval#
input_st = "atn(1) * 4"
print "PI = " + input_st
+ " = "; eval#
input "Enter a string to evaluate > "; input_st
print "Expression = "; eval#
?
next_tok
eval# = evalr#(0)
n = eval_eqv#(0)
next_tok: n
= n
imp eval_eqv#
(0) evalr# = n
n = eval_xor#(0)
next_tok: n
= n
eqv eval_xor#
(0) eval_eqv# = n
n = eval_or#(0)
next_tok: n
= n
xor eval_or#
(0) eval_xor# = n
n = eval_and#(0)
next_tok: n
= n
or eval_and#
(0) eval_or# = n
n = eval_relational#(0)
next_tok: n
= n
and eval_relational#
(0) eval_and# = n
n = eval_add#(0)
case GTR_T : next_tok: n
= n
> eval_add#
(0) case GTR_T_EQL: next_tok: n
= n
>= eval_add#
(0) case LSS_T_EQL: next_tok: n
= n
<= eval_add#
(0) case LSS_T : next_tok: n
= n
< eval_add#
(0) case NOT_EQUAL: next_tok: n
= n
<> eval_add#
(0) case EQUAL : next_tok: n
= n
= eval_add#
(0) eval_relational# = n
n = eval_mod#(0)
case ADD: next_tok: n
= n
+ eval_mod#
(0) case SUBTRACT: next_tok: n
= n
- eval_mod#
(0) eval_add# = n
n = eval_idiv#(0)
next_tok: n
= n
mod eval_idiv#
(0) eval_mod# = n
n = eval_mul#(0)
next_tok: n = n \ eval_mul#(0)
eval_idiv# = n
n = eval_pow#(0)
case MULTIPLY: next_tok: n
= n
* eval_pow#
(0) case DIVIDE: next_tok: n
= n
/ eval_pow#
(0) eval_mul# = n
n = factor#(0)
next_tok: n = n ^ factor#(0)
eval_pow# = n
case NUMBER: n
= val(id_name
): next_tok
case ADD: next_tok: n
= factor#
(0) case SUBTRACT: next_tok: n
= -factor#
(0) case NOT_T: next_tok: n
= not factor#
(0) next_tok
n = evalr#(0)
fun = id_name
next_tok
n = evalr#(0)
n = builtin(fun, n)
factor# = n
builtin# = n
id_name = ""
tok = 0
' skip spaces
' pick off a few multichar operators
case ">=": input_st
= mid$(input_st
, 3): tok
= GTR_T_EQL
case "<=": input_st
= mid$(input_st
, 3): tok
= LSS_T_EQL
case "<>": input_st
= mid$(input_st
, 3): tok
= NOT_EQUAL
' now do the rest, based on first letter
id_name
= id_name
+ left$(input_st
, 1) input_st
= mid$(input_st
, 2) tok = NUMBER
id_name
= id_name
+ left$(input_st
, 1) input_st
= mid$(input_st
, 2) case "mod": tok
= MODULUS:
case "^": input_st
= mid$(input_st
, 2): tok
= POWER
case "*": input_st
= mid$(input_st
, 2): tok
= MULTIPLY
case "/": input_st
= mid$(input_st
, 2): tok
= DIVIDE
case "\": input_st
= mid$(input_st
, 2): tok
= INT_DIV
case "+": input_st
= mid$(input_st
, 2): tok
= ADD
case "-": input_st
= mid$(input_st
, 2): tok
= SUBTRACT
case "=": input_st
= mid$(input_st
, 2): tok
= EQUAL
case "(": input_st
= mid$(input_st
, 2): tok
= LPAREN
case ")": input_st
= mid$(input_st
, 2): tok
= RPAREN
case ">": input_st
= mid$(input_st
, 2): tok
= GTR_T
case "<": input_st
= mid$(input_st
, 2): tok
= LSS_T