119 lines
2.6 KiB
Plaintext
119 lines
2.6 KiB
Plaintext
type Expr = Bin(op, Expr left, Expr right) or Literal(Float val)
|
|
with Lookup
|
|
|
|
type Token(val, Char kind) with Lookup
|
|
func Token.ToString() => this.val.ToString()
|
|
|
|
func tokenize(str) {
|
|
func isSep(c) =>
|
|
c is '+' or '-' or '*' or '/' or ' ' or '\t' or '\n' or '\r' or '(' or ')' or '\0'
|
|
|
|
var idx = -1
|
|
let len = str.Length()
|
|
let tokens = []
|
|
|
|
func next() {
|
|
idx += 1
|
|
return '\0' when idx >= len
|
|
str[idx]
|
|
}
|
|
|
|
while true {
|
|
let c = next()
|
|
|
|
match c {
|
|
'\0' => { break },
|
|
'+' => tokens.Add(Token(c, '+')),
|
|
'-' => tokens.Add(Token(c, '-')),
|
|
'*' => tokens.Add(Token(c, '*')),
|
|
'/' => tokens.Add(Token(c, '/')),
|
|
'(' => tokens.Add(Token(c, '(')),
|
|
')' => tokens.Add(Token(c, ')')),
|
|
_ => {
|
|
let i = idx
|
|
while !isSep(next()) { }
|
|
idx -= 1
|
|
tokens.Add(Token(Float.Parse(str[i..idx]), 'F'))
|
|
}
|
|
}
|
|
}
|
|
|
|
tokens
|
|
}
|
|
|
|
func parse(tokens) {
|
|
var idx = -1
|
|
let len = tokens.Length()
|
|
let eol = Token(val: nil, kind: 'E')
|
|
func pop() {
|
|
idx += 1
|
|
return eol when idx == len
|
|
tokens[idx]
|
|
}
|
|
func peek() {
|
|
let t = pop()
|
|
idx -=1
|
|
t
|
|
}
|
|
func expect(kind) {
|
|
peek().kind == kind
|
|
}
|
|
var add_or_sub1
|
|
|
|
func literal() {
|
|
return false when !expect('F')
|
|
Expr.Literal(pop().val)
|
|
}
|
|
|
|
func group() {
|
|
return false when !expect('(')
|
|
pop()
|
|
var ret = add_or_sub1()
|
|
throw "Invalid group" when !expect(')')
|
|
pop()
|
|
ret
|
|
}
|
|
|
|
func mul_or_div() {
|
|
var fst = group()
|
|
fst = literal() when !fst
|
|
return fst when !expect('*') && !expect('/')
|
|
let op = pop().val
|
|
var snd = group()
|
|
snd = literal() when !snd
|
|
Expr.Bin(op, fst, snd)
|
|
}
|
|
|
|
func add_or_sub() {
|
|
let fst = mul_or_div()
|
|
return fst when !expect('+') && !expect('-')
|
|
let op = pop().val
|
|
let snd = mul_or_div()
|
|
Expr.Bin(op, fst, snd)
|
|
}
|
|
add_or_sub1 = add_or_sub
|
|
|
|
add_or_sub()
|
|
}
|
|
|
|
func exec(ast) {
|
|
match ast {
|
|
Bin(op, left, right) => {
|
|
return exec(left) + exec(right) when op == '+'
|
|
return exec(left) - exec(right) when op == '-'
|
|
return exec(left) * exec(right) when op == '*'
|
|
return exec(left) / exec(right) when op == '/'
|
|
},
|
|
Literal(value) => value
|
|
}
|
|
}
|
|
|
|
func eval(str) {
|
|
let tokens = tokenize(str)
|
|
let ast = parse(tokens)
|
|
exec(ast)
|
|
}
|
|
|
|
print( eval("(1+33.23)*7") )
|
|
print( eval("1+33.23*7") )
|