; A reverse-Polish calculator: a tokenizer, a data type for tokens, and an ; evaluator that signals on a bad program and lets the caller decide. data Token Num(n: i64) Op(c: u8) Word(w: [const u8]) End struct Underflow op: u8 struct Unknown word: [const u8] const max-depth = 16 struct Machine stack: [max-depth i64] depth: i32 ; Read the token that starts at pos; the position after it comes back too. fn next-token(src: [const u8], pos: Ptr(i32)) -> Token let n = length(src) while deref(pos) < n and space?(src[deref(pos)]) ++(deref(pos)) if deref(pos) >= n return Token.End let start = deref(pos) until deref(pos) >= n or space?(src[deref(pos)]) ++(deref(pos)) let text = slice(src, start, deref(pos)) let c = text[0] if digit?(c) or (c == \- and length(text) > 1) match parse-i64(text) Some(v) -> Token.Num{.n v} None -> Token.Word{.w text} elif length(text) == 1 and some?(index-of(bytes-view("+-*/%"), c)) Token.Op{.c c} else Token.Word{.w text} fn push!(m: Ptr(Machine), v: i64) -> () m.stack[m.depth] = v m.depth += 1 fn pop!(m: Ptr(Machine), op: u8) -> i64 if m.depth == 0 error(Underflow{.op op}) m.depth -= 1 m.stack[m.depth] fn apply(op: u8, a: i64, b: i64) -> i64 match op \+ -> a + b \- -> a - b \* -> a * b \/ -> if b == 0 then 0 else a / b _ -> a % b fn run(src: string) -> i64 let m: Machine = zeroed() let text = bytes-view(src) let pos = 0 while :tokens true match next-token(text, addr(pos)) End -> break :tokens Num(n) -> push!(addr(m), n) Op(c) -> let b = pop!(addr(m), c) let a = pop!(addr(m), c) push!(addr(m), apply(c, a, b)) Word(w) -> if bytes=?(w, bytes-view("dup")) let v = pop!(addr(m), \d) push!(addr(m), v) push!(addr(m), v) elif bytes=?(w, bytes-view("drop")) pop!(addr(m), \d) continue else error(Unknown{.word w}) pop!(addr(m), \=) fn show(src: string) -> () let r = handler-case run(src) on Underflow(u) println(src, "=> stack empty at", string-of-byte(u.op)) return on Unknown(u) println(src, "=> unknown word", string(u.word)) return println(src, "=>", r) fn string-of-byte(b: u8) -> string match b \+ -> "+" \- -> "-" \* -> "*" \/ -> "/" \d -> "dup" _ -> "?" fn main() -> i32 show("1 2 +") show("3 4 * 5 -") show("-7 2 /") show("10 dup *") show("1 2 drop 9 %") show("1 +") show("2 3 swap") show("8 0 /") 0