Commit | Line | Data |
---|---|---|
2aa83563 VS |
1 | String extend [ |
2 | String >> loadRelative [ | |
3 | | scriptPath scriptDirectory | | |
999efc9f | 4 | scriptPath := thisContext currentFileName. |
2aa83563 VS |
5 | scriptDirectory := FilePath stripFileNameFor: scriptPath. |
6 | FileStream fileIn: (FilePath append: self to: scriptDirectory) | |
7 | ] | |
8 | ] | |
9 | ||
10 | 'readline.st' loadRelative. | |
11 | 'util.st' loadRelative. | |
12 | 'types.st' loadRelative. | |
13 | 'reader.st' loadRelative. | |
14 | 'printer.st' loadRelative. | |
1b18f359 VS |
15 | |
16 | Object subclass: MAL [ | |
17 | MAL class >> READ: input [ | |
18 | ^Reader readStr: input | |
19 | ] | |
20 | ||
21 | MAL class >> evalAst: sexp env: env [ | |
22 | sexp type = #symbol ifTrue: [ | |
23 | ^env at: sexp value ifAbsent: [ | |
24 | ^MALUnknownSymbol new signal: sexp value | |
25 | ]. | |
26 | ]. | |
27 | ||
28 | sexp type = #list ifTrue: [ | |
29 | ^self evalList: sexp env: env class: MALList | |
30 | ]. | |
31 | sexp type = #vector ifTrue: [ | |
32 | ^self evalList: sexp env: env class: MALVector | |
33 | ]. | |
34 | sexp type = #map ifTrue: [ | |
35 | ^self evalList: sexp env: env class: MALMap | |
36 | ]. | |
37 | ||
38 | ^sexp | |
39 | ] | |
40 | ||
41 | MAL class >> evalList: sexp env: env class: aClass [ | |
42 | | items | | |
43 | items := sexp value collect: | |
44 | [ :item | self EVAL: item env: env ]. | |
45 | ^aClass new: items | |
46 | ] | |
47 | ||
48 | MAL class >> EVAL: sexp env: env [ | |
49 | | forms function args | | |
50 | sexp type ~= #list ifTrue: [ | |
51 | ^self evalAst: sexp env: env | |
52 | ]. | |
53 | sexp value isEmpty ifTrue: [ | |
54 | ^sexp | |
55 | ]. | |
56 | ||
57 | forms := (self evalAst: sexp env: env) value. | |
58 | function := forms first. | |
59 | args := forms allButFirst asArray. | |
60 | ^function valueWithArguments: args | |
61 | ] | |
62 | ||
63 | MAL class >> PRINT: sexp [ | |
64 | ^Printer prStr: sexp printReadably: true | |
65 | ] | |
66 | ||
67 | MAL class >> rep: input env: env [ | |
68 | ^self PRINT: (self EVAL: (self READ: input) env: env) | |
69 | ] | |
70 | ] | |
71 | ||
72 | | input historyFile replEnv | | |
73 | ||
74 | historyFile := '.mal_history'. | |
75 | ReadLine readHistory: historyFile. | |
76 | replEnv := Dictionary from: | |
77 | { #+ -> [ :a :b | MALNumber new: a value + b value ]. | |
78 | #- -> [ :a :b | MALNumber new: a value - b value ]. | |
79 | #* -> [ :a :b | MALNumber new: a value * b value ]. | |
80 | #/ -> [ :a :b | MALNumber new: a value // b value ] }. | |
81 | ||
82 | [ input := ReadLine readLine: 'user> '. input isNil ] whileFalse: [ | |
83 | input isEmpty ifFalse: [ | |
84 | ReadLine addHistory: input. | |
85 | ReadLine writeHistory: historyFile. | |
86 | [ (MAL rep: input env: replEnv) displayNl ] | |
87 | on: MALEmptyInput do: [ #return ] | |
88 | on: MALError do: | |
89 | [ :err | ('error: ', err messageText) displayNl. #return ]. | |
90 | ] | |
91 | ] | |
92 | ||
93 | '' displayNl. |