-
Notifications
You must be signed in to change notification settings - Fork 1
/
lispcalc.sml
106 lines (98 loc) · 3.42 KB
/
lispcalc.sml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
fun say m = print (m ^ "\n")
structure AST =
struct
datatype symbol = Add | Subtract | Multiply | Divide
datatype atom = Number of int | Symbol of symbol
datatype sexp_element = A of atom | S of sexp
and sexp = Sexp of sexp_element list
end
local
fun evalSymbol (sym: AST.symbol, args: AST.atom list): AST.atom =
let
val numbers = List.map (fn AST.Number x => x) args
val (first, rest) = (hd numbers, tl numbers)
val operation = case sym
of AST.Add => op+
| AST.Subtract => (fn (elt, acc) => acc - elt)
| AST.Multiply => op*
| AST.Divide => (fn (elt, acc) => acc div elt)
in
AST.Number (List.foldl operation first rest)
end
fun evalSexpElement (element: AST.sexp_element): AST.atom =
case element
of AST.A atom => atom
| AST.S sexp => evalSexp sexp
and evalSexp (AST.Sexp elements: AST.sexp): AST.atom =
let
val reduced = List.map evalSexpElement elements
in
case reduced
of (AST.Symbol sym)::args => evalSymbol (sym, args)
end
in
fun eval (s: AST.sexp): int =
case evalSexp s
of AST.Number n => n
end
local
fun parseToken (token: string): AST.atom =
if token = "+"
then AST.Symbol AST.Add
else if token = "-"
then AST.Symbol AST.Subtract
else if token = "*"
then AST.Symbol AST.Multiply
else if token = "/"
then AST.Symbol AST.Divide
else AST.Number ((valOf o Int.fromString) token)
fun parseSexpElements (tokens: string list): AST.sexp_element list * string list =
case tokens
of ")"::tokens' => ([], tokens')
| "("::tokens' =>
let
val (subsexp, tokens'') = parseSexp tokens
val atom = AST.S subsexp
val (rest, tokens''') = parseSexpElements tokens''
in
(atom::rest, tokens''')
end
| token::tokens' =>
let
val atom = AST.A (parseToken token)
val (rest, tokens'') = parseSexpElements tokens'
in
(atom::rest, tokens'')
end
and parseSexp (tokens: string list): AST.sexp * string list =
case tokens
of [] => (AST.Sexp [], nil)
| "("::tokens' =>
let
val (elements, tokens'') = parseSexpElements tokens'
in
(AST.Sexp elements, tokens'')
end
in
fun parse (source: string): AST.sexp =
let
val spacified = String.translate (fn c =>
case c
of #"(" => " ( "
| #")" => " ) "
| _ => String.str c)
source
val tokens = String.tokens (Char.contains " \n") spacified
(* val _ = List.app say tokens *)
in
#1 (parseSexp tokens)
end
end
fun repl () =
let
val program = (parse o valOf o TextIO.inputLine) TextIO.stdIn
val _ = say ((Int.toString o eval) program)
in
repl ()
end
val _ = repl ()