196 lines
5.7 KiB
Plaintext
196 lines
5.7 KiB
Plaintext
'Arithmetic evaluation
|
|
'
|
|
'Create a program which parses and evaluates arithmetic expressions.
|
|
'
|
|
'Requirements
|
|
'
|
|
' * An abstract-syntax tree (AST) for the expression must be created from parsing the
|
|
' input.
|
|
' * The AST must be used in evaluation, also, so the input may not be directly evaluated
|
|
' (e.g. by calling eval or a similar language feature.)
|
|
' * The expression will be a string or list of symbols like "(1+3)*7".
|
|
' * The four symbols + - * / must be supported as binary operators with conventional
|
|
' precedence rules.
|
|
' * Precedence-control parentheses must also be supported.
|
|
'
|
|
'Standard mathematical precedence should be followed:
|
|
'
|
|
' Parentheses
|
|
' Multiplication/Division (left to right)
|
|
' Addition/Subtraction (left to right)
|
|
'
|
|
' test cases:
|
|
' 2*-3--4+-0.25 : returns -2.25
|
|
' 1 + 2 * (3 + (4 * 5 + 6 * 7 * 8) - 9) / 10 : returns 71
|
|
|
|
enum
|
|
false = 0
|
|
true = -1
|
|
end enum
|
|
|
|
enum Symbol
|
|
unknown_sym
|
|
minus_sym
|
|
plus_sym
|
|
lparen_sym
|
|
rparen_sym
|
|
number_sym
|
|
mul_sym
|
|
div_sym
|
|
unary_minus_sym
|
|
unary_plus_sym
|
|
done_sym
|
|
eof_sym
|
|
end enum
|
|
|
|
type Tree
|
|
as Tree ptr leftp, rightp
|
|
op as Symbol
|
|
value as double
|
|
end type
|
|
|
|
dim shared sym as Symbol
|
|
dim shared tokenval as double
|
|
dim shared usr_input as string
|
|
|
|
declare function expr(byval p as integer) as Tree ptr
|
|
|
|
function isdigit(byval ch as string) as long
|
|
return ch <> "" and Asc(ch) >= Asc("0") and Asc(ch) <= Asc("9")
|
|
end function
|
|
|
|
sub error_msg(byval msg as string)
|
|
print msg
|
|
system
|
|
end sub
|
|
|
|
' tokenize the input string
|
|
sub getsym()
|
|
do
|
|
if usr_input = "" then
|
|
line input usr_input
|
|
usr_input += chr(10)
|
|
endif
|
|
dim as string ch = mid(usr_input, 1, 1) ' get the next char
|
|
usr_input = mid(usr_input, 2) ' remove it from input
|
|
|
|
sym = unknown_sym
|
|
select case ch
|
|
case " ": continue do
|
|
case chr(10), "": sym = done_sym: return
|
|
case "+": sym = plus_sym: return
|
|
case "-": sym = minus_sym: return
|
|
case "*": sym = mul_sym: return
|
|
case "/": sym = div_sym: return
|
|
case "(": sym = lparen_sym: return
|
|
case ")": sym = rparen_sym: return
|
|
case else
|
|
if isdigit(ch) then
|
|
dim s as string = ""
|
|
dim dot as integer = 0
|
|
do
|
|
s += ch
|
|
if ch = "." then dot += 1
|
|
ch = mid(usr_input, 1, 1) ' get the next char
|
|
usr_input = mid(usr_input, 2) ' remove it from input
|
|
loop while isdigit(ch) orelse ch = "."
|
|
if ch = "." or dot > 1 then error_msg("bogus number")
|
|
usr_input = ch + usr_input ' prepend the char to input
|
|
tokenval = val(s)
|
|
sym = number_sym
|
|
end if
|
|
return
|
|
end select
|
|
loop
|
|
end sub
|
|
|
|
function make_node(byval op as Symbol, byval leftp as Tree ptr, byval rightp as Tree ptr) as Tree ptr
|
|
dim t as Tree ptr
|
|
|
|
t = callocate(len(Tree))
|
|
t->op = op
|
|
t->leftp = leftp
|
|
t->rightp = rightp
|
|
return t
|
|
end function
|
|
|
|
function is_binary(byval op as Symbol) as integer
|
|
select case op
|
|
case mul_sym, div_sym, plus_sym, minus_sym: return true
|
|
case else: return false
|
|
end select
|
|
end function
|
|
|
|
function prec(byval op as Symbol) as integer
|
|
select case op
|
|
case unary_minus_sym, unary_plus_sym: return 100
|
|
case mul_sym, div_sym: return 90
|
|
case plus_sym, minus_sym: return 80
|
|
case else: return 0
|
|
end select
|
|
end function
|
|
|
|
function primary as Tree ptr
|
|
dim t as Tree ptr = 0
|
|
|
|
select case sym
|
|
case minus_sym, plus_sym
|
|
dim op as Symbol = sym
|
|
getsym()
|
|
t = expr(prec(unary_minus_sym))
|
|
if op = minus_sym then return make_node(unary_minus_sym, t, 0)
|
|
if op = plus_sym then return make_node(unary_plus_sym, t, 0)
|
|
case lparen_sym
|
|
getsym()
|
|
t = expr(0)
|
|
if sym <> rparen_sym then error_msg("expecting rparen")
|
|
getsym()
|
|
return t
|
|
case number_sym
|
|
t = make_node(sym, 0, 0)
|
|
t->value = tokenval
|
|
getsym()
|
|
return t
|
|
case else: error_msg("expecting a primary")
|
|
end select
|
|
end function
|
|
|
|
function expr(byval p as integer) as Tree ptr
|
|
dim t as Tree ptr = primary()
|
|
|
|
while is_binary(sym) andalso prec(sym) >= p
|
|
dim t1 as Tree ptr
|
|
dim op as Symbol = sym
|
|
getsym()
|
|
t1 = expr(prec(op) + 1)
|
|
t = make_node(op, t, t1)
|
|
wend
|
|
return t
|
|
end function
|
|
|
|
function eval(byval t as Tree ptr) as double
|
|
if t <> 0 then
|
|
select case t->op
|
|
case minus_sym: return eval(t->leftp) - eval(t->rightp)
|
|
case plus_sym: return eval(t->leftp) + eval(t->rightp)
|
|
case mul_sym: return eval(t->leftp) * eval(t->rightp)
|
|
case div_sym: return eval(t->leftp) / eval(t->rightp)
|
|
case unary_minus_sym: return -eval(t->leftp)
|
|
case unary_plus_sym: return eval(t->leftp)
|
|
case number_sym: return t->value
|
|
case else: error_msg("unexpected tree node")
|
|
end select
|
|
end if
|
|
return 0
|
|
end function
|
|
|
|
do
|
|
getsym()
|
|
if sym = eof_sym then exit do
|
|
if sym = done_sym then continue do
|
|
dim t as Tree ptr = expr(0)
|
|
print"> "; eval(t)
|
|
if sym = eof_sym then exit do
|
|
if sym <> done_sym then error_msg("unexpected input")
|
|
loop
|