forked from lizascharf/ps2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexpressionLibrary.ml
More file actions
299 lines (263 loc) · 9.21 KB
/
expressionLibrary.ml
File metadata and controls
299 lines (263 loc) · 9.21 KB
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
(*
CS 51 Problem Set 2
A Language for Symbolic Mathematics
Library Code for Representing Expressions
Spring 2017
*)
(*......................................................................
This code is for your reference. You shouldn't need to change anything
in this file.
......................................................................*)
(* Binary operators. *)
type binop = Add | Sub | Mul | Div | Pow
(* Unary operators. *)
type unop = Sin | Cos | Ln | Neg
(* Expressions *)
type expression =
| Num of float
| Var
| Binop of binop * expression * expression
| Unop of unop * expression
exception ParseError of string
type token =
| NumT of float
| VarT
| BinopT of binop
| UnopT of unop
| LParen
| RParen
| LBrace
| RBrace
| EOF
let recognized_tokens = [|"x"; "sin"; "cos"; "ln"|] ;;
let token_expressions = [|VarT; UnopT Sin; UnopT Cos; UnopT Ln|] ;;
let string_to_char_list (s : string) : char list =
let rec string_to_char_list' (s : string) (acc : char list) (i : int) =
if i < 0 then acc else
let c = String.get s i in
string_to_char_list' s (c :: acc) (i-1)
in string_to_char_list' s [] (String.length s - 1)
;;
let is_digit (c : char) : bool =
let i = int_of_char c in
i >= 48 && i <= 57
;;
(* The precedence of a binary operator. Used in the parse_string and
to_string_smart functions. *)
let binop_precedence (b : binop) : int =
match b with
| Add -> 3
| Sub -> 3
| Mul -> 2
| Div -> 2
| Pow -> 1
;;
let unop_precedence (_ : unop) : int = 4 ;;
(* A strict upper bound on the precedence of any operator. *)
let prec_bound : int = 5 ;;
let binop_is_associative (b : binop) : bool =
match b with
| Add | Mul -> true
| Sub | Div | Pow -> false ;;
(* Pretty-printing functions for expressions *)
let binop_to_string (b : binop) : string =
match b with
| Add -> "+"
| Sub -> "-"
| Mul -> "*"
| Div -> "/"
| Pow -> "^"
;;
let unop_to_string (u : unop) : string =
match u with
| Sin -> "sin"
| Cos -> "cos"
| Ln -> "ln"
| Neg -> "~"
;;
let token_to_string (t : token) : string =
match t with
| NumT n -> string_of_float n
| VarT -> "x"
| BinopT b -> binop_to_string b
| UnopT u -> unop_to_string u
| LParen -> "("
| RParen -> ")"
| LBrace -> "{"
| RBrace -> "}"
| EOF -> "EOF"
;;
(* Only adds parentheses when needed to prevent ambiguity. *)
let to_string_smart (e : expression) : string =
let rec to_string_smart' e parent_precedence parent_associative =
match e with
| Num n ->
if n >= 0.0 then string_of_float n
else "~" ^ string_of_float (abs_float n)
| Var -> "x"
| Unop (u,e1) ->
unop_to_string u ^ "(" ^
to_string_smart' e1 (unop_precedence u) false ^ ")"
| Binop (b,e1,e2) ->
let prec = binop_precedence b in
let e_str =
(to_string_smart' e1 prec false ^ binop_to_string b
^ to_string_smart' e2 prec (binop_is_associative b)) in
if prec > parent_precedence ||
(prec = parent_precedence && not parent_associative)
then "(" ^ e_str ^ ")"
else e_str
in to_string_smart' e prec_bound false
;;
(* Always adds parentheses around all binary ops. Completely unambiguous;
however, often very hard to read... *)
let rec to_string (e : expression) : string =
match e with
| Num n ->
if n >= 0.0 then
string_of_float n
else "~" ^ string_of_float (abs_float n)
| Var -> "x"
| Unop (u, e1) -> "(" ^ unop_to_string u ^ "(" ^ to_string e1 ^ "))"
| Binop (b, e1, e2) ->
"(" ^ to_string e1 ^ binop_to_string b ^ to_string e2 ^ ")"
;;
(* Lexing functions (producing tokens from char lists) *)
let rec match_while (p : char -> bool) (l : char list) : string * char list =
match l with
| [] -> ("", [])
| c :: cs ->
if p c then
let (s_cs, l_cs) = match_while p cs in
(String.make 1 c ^ s_cs, l_cs)
else ("", l) ;;
let lex_number_string = match_while (fun c -> is_digit c || c = '.')
let lex_number (l : char list) : (token * char list) option =
let (s,l') = lex_number_string l in
try Some (NumT (float_of_string s), l')
with Failure "float_of_string" -> None
| Invalid_argument _ -> None ;;
let rec match_string (l : char list) (s : string) : char list option =
if s = "" then Some l else
match l with
| [] -> None
| h :: t ->
if h = String.get s 0 then
match_string t (String.sub s 1 (String.length s - 1))
else None ;;
let lex_multi_char_token (l : char list) : (token * char list) option =
let rec lex_multi_char_token' l i =
if i >= Array.length recognized_tokens then None
else
match match_string l (Array.get recognized_tokens i) with
| Some l' -> Some (Array.get token_expressions i, l')
| None -> lex_multi_char_token' l (i+1)
in lex_multi_char_token' l 0 ;;
let rec lex' (l : char list) : token list =
match l with
| [] -> []
| ' ' :: cs -> lex' cs
| c :: cs ->
let (token, l') =
(match c with
| '+' -> (BinopT Add, cs)
| '-' -> (BinopT Sub, cs)
| '*' -> (BinopT Mul, cs)
| '/' -> (BinopT Div, cs)
| '^' -> (BinopT Pow, cs)
| '~' -> (UnopT Neg, cs)
| '(' -> (LParen, cs)
| ')' -> (RParen, cs)
| '{' -> (LBrace, cs)
| '}' -> (RBrace, cs)
| _ ->
(match lex_number l with
| Some (t, l') -> (t, l')
| None ->
(match lex_multi_char_token l with
| Some (t, l') -> (t, l')
| None -> raise (ParseError "Unrecognized token"))))
in token :: lex' l' ;;
let lex (s : string) : token list =
lex' (string_to_char_list s) @ [EOF] ;;
let parse (s : string) : expression =
let rec parse_toplevel_expression (l : token list) : expression =
let (e, _, _) = parse_delimited_expression l EOF prec_bound in e
and parse_expression (l : token list) : expression * token list =
match l with
| [] -> raise (ParseError "Unexpected end of string")
| t :: ts ->
match t with
| LParen ->
let (e, l', _) = parse_delimited_expression ts RParen prec_bound in
(e, l')
| RParen -> raise (ParseError "Unexpected rparen")
| LBrace ->
let (e, l', _) = parse_delimited_expression ts RBrace prec_bound in
(e, l')
| RBrace -> raise (ParseError "Unexpected rbrace")
| UnopT u -> parse_unop ts u
| VarT -> (Var, ts)
| EOF -> raise (ParseError "Unexpected EOF")
| NumT n -> (Num n, ts)
| BinopT _ ->
raise (ParseError ("Unexpected Binop: " ^ token_to_string t))
and parse_binop (l : token list) (delim : token) (current_prec : int) eq
: expression * token list * bool =
match l with
| [] -> raise (ParseError "Unexpected end of string 2")
| t :: ts ->
if t = delim then
(eq, ts, true)
else match t with
| BinopT b -> let prec = binop_precedence b in
if current_prec <= prec then
(eq,l,false)
else
let (eq2, l', d) = parse_delimited_expression ts delim prec in
if d then (Binop (b, eq, eq2), l', true)
else parse_binop l' delim current_prec
(Binop (b, eq, eq2))
| _ ->
raise
(ParseError ("Expecting Binop, but found: " ^ token_to_string t))
and parse_delimited_expression (l:token list) (delim:token)
(current_prec:int) : expression * token list * bool =
match l with
| [] -> raise (ParseError "Unexpected end of string 3")
| t :: _ ->
if t = delim then
raise (ParseError ("Unexpected delim: " ^ token_to_string delim))
else
let (eq,l') = parse_expression l in
parse_binop l' delim current_prec eq
and parse_unop (tokens:token list) (u:unop) =
let (e,t) = parse_expression tokens in (Unop(u,e),t)
in parse_toplevel_expression (lex s)
;;
let () = Random.self_init ()
let rec rand_exp l =
let smaller l = if l <= 1 then 0 else Random.int (l-1) in
match l with
| 0 -> (match (Random.int 2) with
| 0 -> Num ((float (Random.int 100)) -. 50.)
| _ -> Var )
| _ -> (match (Random.int 8) with
| 0 -> Num ((float (Random.int 100)) -. 50.)
| 1 -> Var
| 2 | 3 | 4 | 5 ->
(let binexp = match (Random.int 5) with
| 0 -> Add
| 1 -> Sub
| 2 -> Mul
| 3 -> Div
| _ -> Pow in
Binop(binexp,(rand_exp (smaller l)),
(rand_exp (smaller l))))
| _ -> (let unexp = match (Random.int 4) with
| 0 -> Sin
| 1 -> Cos
| 2 -> Neg
| _ -> Ln in
Unop(unexp, (rand_exp (smaller l)))));;
let rand_exp_str l = to_string_smart (rand_exp l);;