├── Dist
├── Examples
│ ├── Sudoku.lisp
│ └── fibonacci.lisp
├── Lisp.exe
└── LispEngine.dll
├── Lisp
├── .gitignore
├── Lisp.sln
├── Lisp
│ ├── Examples
│ │ ├── Sudoku.lisp
│ │ └── fibonacci.lisp
│ ├── Lisp.csproj
│ ├── Program.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ └── REPL.lisp
├── LispEngine
│ ├── Bootstrap
│ │ ├── Append.cs
│ │ ├── Arithmetic.cs
│ │ ├── Builtins.cs
│ │ ├── Builtins.lisp
│ │ ├── Builtins.lisp~
│ │ ├── IO.lisp
│ │ ├── Library.lisp
│ │ ├── Reader.cs
│ │ ├── StandardEnvironment.cs
│ │ └── SymbolFunctions.cs
│ ├── Core
│ │ ├── Apply.cs
│ │ ├── Begin.cs
│ │ ├── BinaryFunction.cs
│ │ ├── CallCC.cs
│ │ ├── CoreForms.cs
│ │ ├── DebugFunctions.cs
│ │ ├── Define.cs
│ │ ├── Env.cs
│ │ ├── EqualFunctions.cs
│ │ ├── Eval.cs
│ │ ├── ExecuteWithErrorTranslator.cs
│ │ ├── Lambda.cs
│ │ ├── LexicalBinder.cs
│ │ ├── Log.cs
│ │ ├── Macro.cs
│ │ ├── Quote.cs
│ │ ├── Set.cs
│ │ ├── UnaryFunction.cs
│ │ └── VectorFunctions.cs
│ ├── Datums
│ │ ├── AbstractVisitor.cs
│ │ ├── Atom.cs
│ │ ├── Datum.cs
│ │ ├── DatumExtensions.cs
│ │ ├── DatumHelpers.cs
│ │ ├── DatumVisitor.cs
│ │ ├── Null.cs
│ │ ├── Pair.cs
│ │ ├── Symbol.cs
│ │ └── Vector.cs
│ ├── Evaluation
│ │ ├── AbstractFExpression.cs
│ │ ├── AbstractStackFunction.cs
│ │ ├── Continuation.cs
│ │ ├── DelegateFunctions.cs
│ │ ├── DelegateTask.cs
│ │ ├── EvaluateFExpression.cs
│ │ ├── EvaluateTask.cs
│ │ ├── EvaluationException.cs
│ │ ├── Evaluator.cs
│ │ ├── FExpression.cs
│ │ ├── Function.cs
│ │ ├── FunctionExpression.cs
│ │ ├── IEnvironment.cs
│ │ ├── IStack.cs
│ │ ├── LexicalEnvironment.cs
│ │ ├── Stack.cs
│ │ ├── StackFunction.cs
│ │ ├── StackFunctionAdapter.cs
│ │ ├── Statistics.cs
│ │ └── Task.cs
│ ├── Lexing
│ │ ├── Scanner.cs
│ │ ├── Token.cs
│ │ └── TokenType.cs
│ ├── LispEngine.csproj
│ ├── Parsing
│ │ ├── ParseException.cs
│ │ └── Parser.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── ReflectionBinding
│ │ ├── ReflectionBuiltins.cs
│ │ └── ReflectionBuiltins.lisp
│ └── Util
│ │ └── ResourceLoader.cs
├── LispTests
│ ├── Datums
│ │ └── DatumTest.cs
│ ├── Evaluation
│ │ ├── AmbTests.lisp
│ │ ├── ArithmeticTests.lisp
│ │ ├── CallCCTests.lisp
│ │ ├── DotNetTests.lisp
│ │ ├── EvalTests.lisp
│ │ ├── EvaluatorTests.cs
│ │ ├── EvaluatorTests.lisp
│ │ ├── LibraryTests.lisp
│ │ ├── MacroBuiltinTests.lisp
│ │ ├── PatternMatchingTests.lisp
│ │ ├── QuasiquoteTests.lisp
│ │ ├── SudokuTests.lisp
│ │ ├── VectorTests.lisp
│ │ └── �
│ ├── LispTests.csproj
│ ├── Parsing
│ │ ├── Lexing
│ │ │ └── ScannerTest.cs
│ │ ├── MultilineFile.lisp
│ │ ├── MultilineFile.lisp~
│ │ └── ParserTest.cs
│ └── Properties
│ │ └── AssemblyInfo.cs
└── Packages
│ └── NUnit
│ ├── NUnit.nupkg
│ ├── lib
│ ├── nunit.framework.dll
│ └── nunit.framework.xml
│ └── license.txt
└── README.md
/Dist/Examples/Sudoku.lisp:
--------------------------------------------------------------------------------
1 | (define digits '(1 2 3 4 5 6 7 8 9))
2 |
3 | ; For efficiency, we'll use a single integer
4 | ; 'bit field' to represent which digits are
5 | ; available. 0 means a contradiction.
6 | ; We do this rather than implement
7 | ; lots of builtin string primitives, which
8 | ; would have been the thing to do if we'd
9 | ; followed Peter Norvig's Python program
10 | ; exactly.
11 | (define (sub1 x)
12 | (- x 1))
13 |
14 | (define (digit-bit digit)
15 | (bit-shift 1 (sub1 digit)))
16 |
17 | (define (add-digit ds d)
18 | (bit-or ds (digit-bit d)))
19 |
20 | (define solved-digit?
21 | (lambda (1) 1
22 | (2) 2
23 | (4) 3
24 | (8) 4
25 | (16) 5
26 | (32) 6
27 | (64) 7
28 | (128) 8
29 | (256) 9
30 | (_) #f))
31 |
32 | (define (digit-set . digits)
33 | (fold-loop d digits
34 | ds 0
35 | (add-digit ds d)))
36 |
37 | (define all-digits (apply digit-set digits))
38 | (define none (digit-set))
39 |
40 | (define zero? (curry eq? 0))
41 | (define none? zero?)
42 | (define not-zero? (compose2 not zero?))
43 |
44 | (define (remove-digit ds d)
45 | (bit-and ds (- all-digits (digit-bit d))))
46 |
47 | (define (has-digit? ds d)
48 | (not-zero? (bit-and (digit-bit d) ds)))
49 |
50 | ; Inverse function of digit-set constructor
51 | (define (show-digits ds)
52 | (filter-loop d digits
53 | (has-digit? ds d)))
54 |
55 | (define num-squares 81)
56 |
57 | (define (index (row . column))
58 | (+ (sub1 column) (* (sub1 row) 9)))
59 |
60 | ; Grid representation. Use a vector
61 | ; and row/column arithmetic.
62 | (define empty-grid
63 | (make-vector num-squares all-digits))
64 |
65 | (define get-square vector-ref)
66 |
67 | (define set-square! vector-set!)
68 |
69 | (define (get-digits grid s)
70 | (show-digits (get-square grid s)))
71 |
72 | (define copy-grid vector-copy)
73 | (define (new-grid)
74 | (copy-grid empty-grid))
75 |
76 | ; Use digits for rows and the columns
77 | (define rows digits)
78 | (define cols digits)
79 | (define cross cartesian)
80 | (define squares (mapcar index (cross rows cols)))
81 | (define divisions '((1 2 3) (4 5 6) (7 8 9)))
82 | (define unitlist
83 | (loop cons-unit
84 | (append
85 | (cartesian-map cross divisions divisions)
86 | (loop c cols (cross rows (list c)))
87 | (loop r rows (cross (list r) cols)))
88 | (mapcar index cons-unit)))
89 |
90 | (define (units-for-square s)
91 | (filter-loop unit unitlist (in s unit)))
92 |
93 | (define (peers-for-square s)
94 | (remove s (flatten (get-square units s))))
95 |
96 | (define (make-grid square-function)
97 | (let g (make-vector num-squares)
98 | (loop s squares
99 | (set-square! g s (square-function s)))
100 | g))
101 |
102 | (define units (make-grid units-for-square))
103 | (define peers (make-grid peers-for-square))
104 |
105 | (define (grid->lists grid)
106 | (loop r digits
107 | (loop c digits
108 | (show-digits (get-square grid (index (cons r c)))))))
109 |
110 | (define (string->list s)
111 | (define (convert char)
112 | (let schar (.ToString char)
113 | (if (System.Char/IsDigit char)
114 | (System.Convert/ToInt32 schar)
115 | (eq? "." schar)
116 | 'dot
117 | (string->symbol schar))))
118 | (with (array (.ToCharArray s)
119 | length (.get_Length s))
120 | (repeat (lambda (i)
121 | (convert (.GetValue array i))) length)))
122 |
123 | (define zip (curry map cons))
124 | (define (grid->values grid)
125 | (let values (filter-loop c (string->list grid)
126 | (or (in c digits) (in c '(0 dot))))
127 | (if (eq? (length values) num-squares)
128 | (zip squares values)
129 | (throw "Could not parse grid"))))
130 |
131 | (define amb (make-amb-function (curry throw "No solution")))
132 | (define fail amb)
133 |
134 | ; After removing d from s and its peers,
135 | ; does d now only appear in one place for the units
136 | ; of s? If so, "assign" to that place.
137 | (define (check-units! grid s d)
138 | (fold-loop u (get-square units s)
139 | g grid
140 | ; Find squares in this unit which have this digit
141 | (match (filter-loop s u (has-digit? (get-square g s) d))
142 | () (fail) ; d cannot appear anywhere in some unit => contradiction
143 | ; d only appears in one square =>
144 | ; assign d to square s in values
145 | (s) (assign! g s d )
146 | ; No inference possible: do nothing.
147 | _ g)))
148 |
149 | (define (eliminate-peers! grid s remaining)
150 | (match (solved-digit? remaining)
151 | #f grid
152 | d (fold-loop peer (get-square peers s)
153 | g grid
154 | (eliminate! g peer d))))
155 |
156 | (define (apply-rules grid s d remaining)
157 | (if (none? remaining)
158 | (fail)
159 | (begin
160 | (set-square! grid s remaining)
161 | (eliminate-peers! grid s remaining)
162 | (check-units! grid s d))))
163 |
164 | ; Eliminate digit d from square s
165 | (define (eliminate! grid s d)
166 | (define current (get-square grid s))
167 | ; This test required to terminate the
168 | ; recursion
169 | (if (has-digit? current d)
170 | (apply-rules grid s d (remove-digit current d))
171 | grid))
172 |
173 | ; Return the 'values' that results from
174 | ; assigning d to square s
175 | (define (assign! grid s d)
176 | (define others (remove-digit (get-square grid s) d))
177 | (fold-loop d (show-digits others)
178 | g grid
179 | (eliminate! g s d)))
180 |
181 | (define (digit? d)
182 | (in d digits))
183 |
184 | (define (parse-grid grid-string)
185 | (fold-loop (s . d) (grid->values grid-string)
186 | g (new-grid)
187 | (if (digit? d)
188 | (assign! g s d)
189 | g)))
190 |
191 | (define (solved? grid)
192 | (let-cc return
193 | (index-loop i num-squares
194 | (if (solved-digit? (vector-ref grid i))
195 | #t
196 | (return #f)))
197 | #t))
198 |
199 | (define two-through-9 (cdr digits))
200 |
201 | (define (square-to-try grid)
202 | (let-cc return
203 | (loop num-missing two-through-9
204 | (loop s squares
205 | (let possible (show-digits (get-square grid s))
206 | (if (eq? (length possible) num-missing)
207 | (return (cons s possible))
208 | #f))))
209 | return "None missing"))
210 |
211 | (define (solve grid)
212 | (if (solved? grid)
213 | grid
214 | (with* ((s . digits) (square-to-try grid)
215 | d (amb digits))
216 | (write-line "Assiging {0} to {1}" d s)
217 | (solve (assign! (copy-grid grid) s d)))))
218 |
219 | (define (display-grid grid)
220 | (loop row (grid->lists grid)
221 | (write-line "{0}" row))
222 | nil)
223 |
224 | (define grid1 "003020600900305001001806400008102900700000008006708200002609500800203009005010300")
225 | (define grid2 "4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......")
226 | ; Hardest puzzle from http://www.mirror.co.uk/news/weird-news/worlds-hardest-sudoku-can-you-242294
227 | (define inkala-hardest
228 | (System.String/Concat
229 | "..5 3.. ..."
230 | "8.. ... .2."
231 | ".7. .1. 5.."
232 | "4.. ..5 3.."
233 | ".1. .7. ..6"
234 | "..3 2.. .8."
235 | ".6. 5.. ..9"
236 | "..4 ... .3."
237 | "... ..9 7.."))
238 |
239 | (write-line "Solving grid1...")
240 | (define parsed1 (parse-grid grid1))
241 | (display-grid parsed1)
242 | (write-line "Solving grid2...")
243 | (define parsed2 (parse-grid grid2))
244 | (write-line "With rote deduction we only get to:")
245 | (display-grid parsed2)
246 | (write-line "Solving using non-deterministic search...")
247 | (define solution2 (solve parsed2))
248 | (write-line "Solution:")
249 | (display-grid solution2)
250 |
--------------------------------------------------------------------------------
/Dist/Examples/fibonacci.lisp:
--------------------------------------------------------------------------------
1 | ; The archetypical fibonacci program...
2 | (define (fib n)
3 | (match n
4 | 0 0
5 | 1 1
6 | n (+ (fib (- n 1)) (fib (- n 2)))))
7 | (display (fib 8))
8 |
--------------------------------------------------------------------------------
/Dist/Lisp.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Patient0/FirstClassLisp/261c7ed81cc75f399c4989814717ee474291b641/Dist/Lisp.exe
--------------------------------------------------------------------------------
/Dist/LispEngine.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Patient0/FirstClassLisp/261c7ed81cc75f399c4989814717ee474291b641/Dist/LispEngine.dll
--------------------------------------------------------------------------------
/Lisp/.gitignore:
--------------------------------------------------------------------------------
1 | #ignore thumbnails created by windows
2 | Thumbs.db
3 | #Ignore files build by Visual Studio
4 | *.obj
5 | *.exe
6 | *.pdb
7 | *.user
8 | *.aps
9 | *.pch
10 | *.vspscc
11 | *_i.c
12 | *_p.c
13 | *.ncb
14 | *.suo
15 | *.tlb
16 | *.tlh
17 | *.bak
18 | *.cache
19 | *.ilk
20 | *.log
21 | *.swp
22 | *~
23 | [Bb]in
24 | [Dd]ebug*/
25 | *.lib
26 | *.sbr
27 | obj/
28 | [Rr]elease*/
29 | _ReSharper*/
30 | [Tt]est[Rr]esult*
31 |
--------------------------------------------------------------------------------
/Lisp/Lisp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual Studio 2010
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lisp", "Lisp\Lisp.csproj", "{F4859844-A8FB-4CE4-9184-2DD789A07085}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LispTests", "LispTests\LispTests.csproj", "{2517BF60-4567-456F-AE2E-25B280AD24F2}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LispEngine", "LispEngine\LispEngine.csproj", "{81EE52DF-F912-4FE5-973C-262762CA3B99}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Debug|Mixed Platforms = Debug|Mixed Platforms
14 | Debug|x86 = Debug|x86
15 | Release|Any CPU = Release|Any CPU
16 | Release|Mixed Platforms = Release|Mixed Platforms
17 | Release|x86 = Release|x86
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {F4859844-A8FB-4CE4-9184-2DD789A07085}.Debug|Any CPU.ActiveCfg = Debug|x86
21 | {F4859844-A8FB-4CE4-9184-2DD789A07085}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
22 | {F4859844-A8FB-4CE4-9184-2DD789A07085}.Debug|Mixed Platforms.Build.0 = Debug|x86
23 | {F4859844-A8FB-4CE4-9184-2DD789A07085}.Debug|x86.ActiveCfg = Debug|x86
24 | {F4859844-A8FB-4CE4-9184-2DD789A07085}.Debug|x86.Build.0 = Debug|x86
25 | {F4859844-A8FB-4CE4-9184-2DD789A07085}.Release|Any CPU.ActiveCfg = Release|x86
26 | {F4859844-A8FB-4CE4-9184-2DD789A07085}.Release|Mixed Platforms.ActiveCfg = Release|x86
27 | {F4859844-A8FB-4CE4-9184-2DD789A07085}.Release|Mixed Platforms.Build.0 = Release|x86
28 | {F4859844-A8FB-4CE4-9184-2DD789A07085}.Release|x86.ActiveCfg = Release|x86
29 | {F4859844-A8FB-4CE4-9184-2DD789A07085}.Release|x86.Build.0 = Release|x86
30 | {2517BF60-4567-456F-AE2E-25B280AD24F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {2517BF60-4567-456F-AE2E-25B280AD24F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {2517BF60-4567-456F-AE2E-25B280AD24F2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
33 | {2517BF60-4567-456F-AE2E-25B280AD24F2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
34 | {2517BF60-4567-456F-AE2E-25B280AD24F2}.Debug|x86.ActiveCfg = Debug|Any CPU
35 | {2517BF60-4567-456F-AE2E-25B280AD24F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
36 | {2517BF60-4567-456F-AE2E-25B280AD24F2}.Release|Any CPU.Build.0 = Release|Any CPU
37 | {2517BF60-4567-456F-AE2E-25B280AD24F2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
38 | {2517BF60-4567-456F-AE2E-25B280AD24F2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
39 | {2517BF60-4567-456F-AE2E-25B280AD24F2}.Release|x86.ActiveCfg = Release|Any CPU
40 | {81EE52DF-F912-4FE5-973C-262762CA3B99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {81EE52DF-F912-4FE5-973C-262762CA3B99}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {81EE52DF-F912-4FE5-973C-262762CA3B99}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
43 | {81EE52DF-F912-4FE5-973C-262762CA3B99}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
44 | {81EE52DF-F912-4FE5-973C-262762CA3B99}.Debug|x86.ActiveCfg = Debug|Any CPU
45 | {81EE52DF-F912-4FE5-973C-262762CA3B99}.Release|Any CPU.ActiveCfg = Release|Any CPU
46 | {81EE52DF-F912-4FE5-973C-262762CA3B99}.Release|Any CPU.Build.0 = Release|Any CPU
47 | {81EE52DF-F912-4FE5-973C-262762CA3B99}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
48 | {81EE52DF-F912-4FE5-973C-262762CA3B99}.Release|Mixed Platforms.Build.0 = Release|Any CPU
49 | {81EE52DF-F912-4FE5-973C-262762CA3B99}.Release|x86.ActiveCfg = Release|Any CPU
50 | EndGlobalSection
51 | GlobalSection(SolutionProperties) = preSolution
52 | HideSolutionNode = FALSE
53 | EndGlobalSection
54 | EndGlobal
55 |
--------------------------------------------------------------------------------
/Lisp/Lisp/Examples/Sudoku.lisp:
--------------------------------------------------------------------------------
1 | (define digits '(1 2 3 4 5 6 7 8 9))
2 |
3 | ; For efficiency, we'll use a single integer
4 | ; 'bit field' to represent which digits are
5 | ; available. 0 means a contradiction.
6 | ; We do this rather than implement
7 | ; lots of builtin string primitives, which
8 | ; would have been the thing to do if we'd
9 | ; followed Peter Norvig's Python program
10 | ; exactly.
11 | (define (sub1 x)
12 | (- x 1))
13 |
14 | (define (digit-bit digit)
15 | (bit-shift 1 (sub1 digit)))
16 |
17 | (define (add-digit ds d)
18 | (bit-or ds (digit-bit d)))
19 |
20 | (define solved-digit?
21 | (lambda (1) 1
22 | (2) 2
23 | (4) 3
24 | (8) 4
25 | (16) 5
26 | (32) 6
27 | (64) 7
28 | (128) 8
29 | (256) 9
30 | (_) #f))
31 |
32 | (define (digit-set . digits)
33 | (fold-loop d digits
34 | ds 0
35 | (add-digit ds d)))
36 |
37 | (define all-digits (apply digit-set digits))
38 | (define none (digit-set))
39 |
40 | (define zero? (curry eq? 0))
41 | (define none? zero?)
42 | (define not-zero? (compose2 not zero?))
43 |
44 | (define (remove-digit ds d)
45 | (bit-and ds (- all-digits (digit-bit d))))
46 |
47 | (define (has-digit? ds d)
48 | (not-zero? (bit-and (digit-bit d) ds)))
49 |
50 | ; Inverse function of digit-set constructor
51 | (define (show-digits ds)
52 | (filter-loop d digits
53 | (has-digit? ds d)))
54 |
55 | (define num-squares 81)
56 |
57 | (define (index (row . column))
58 | (+ (sub1 column) (* (sub1 row) 9)))
59 |
60 | ; Grid representation. Use a vector
61 | ; and row/column arithmetic.
62 | (define empty-grid
63 | (make-vector num-squares all-digits))
64 |
65 | (define get-square vector-ref)
66 |
67 | (define set-square! vector-set!)
68 |
69 | (define (get-digits grid s)
70 | (show-digits (get-square grid s)))
71 |
72 | (define copy-grid vector-copy)
73 | (define (new-grid)
74 | (copy-grid empty-grid))
75 |
76 | ; Use digits for rows and the columns
77 | (define rows digits)
78 | (define cols digits)
79 | (define cross cartesian)
80 | (define squares (mapcar index (cross rows cols)))
81 | (define divisions '((1 2 3) (4 5 6) (7 8 9)))
82 | (define unitlist
83 | (loop cons-unit
84 | (append
85 | (cartesian-map cross divisions divisions)
86 | (loop c cols (cross rows (list c)))
87 | (loop r rows (cross (list r) cols)))
88 | (mapcar index cons-unit)))
89 |
90 | (define (units-for-square s)
91 | (filter-loop unit unitlist (in s unit)))
92 |
93 | (define (peers-for-square s)
94 | (remove s (flatten (get-square units s))))
95 |
96 | (define (make-grid square-function)
97 | (let g (make-vector num-squares)
98 | (loop s squares
99 | (set-square! g s (square-function s)))
100 | g))
101 |
102 | (define units (make-grid units-for-square))
103 | (define peers (make-grid peers-for-square))
104 |
105 | (define (grid->lists grid)
106 | (loop r digits
107 | (loop c digits
108 | (show-digits (get-square grid (index (cons r c)))))))
109 |
110 | (define (string->list s)
111 | (define (convert char)
112 | (let schar (.ToString char)
113 | (if (System.Char/IsDigit char)
114 | (System.Convert/ToInt32 schar)
115 | (eq? "." schar)
116 | 'dot
117 | (string->symbol schar))))
118 | (with (array (.ToCharArray s)
119 | length (.get_Length s))
120 | (repeat (lambda (i)
121 | (convert (.GetValue array i))) length)))
122 |
123 | (define zip (curry map cons))
124 | (define (grid->values grid)
125 | (let values (filter-loop c (string->list grid)
126 | (or (in c digits) (in c '(0 dot))))
127 | (if (eq? (length values) num-squares)
128 | (zip squares values)
129 | (throw "Could not parse grid"))))
130 |
131 | (define amb (make-amb-function (curry throw "No solution")))
132 | (define fail amb)
133 |
134 | ; After removing d from s and its peers,
135 | ; does d now only appear in one place for the units
136 | ; of s? If so, "assign" to that place.
137 | (define (check-units! grid s d)
138 | (fold-loop u (get-square units s)
139 | g grid
140 | ; Find squares in this unit which have this digit
141 | (match (filter-loop s u (has-digit? (get-square g s) d))
142 | () (fail) ; d cannot appear anywhere in some unit => contradiction
143 | ; d only appears in one square =>
144 | ; assign d to square s in values
145 | (s) (assign! g s d )
146 | ; No inference possible: do nothing.
147 | _ g)))
148 |
149 | (define (eliminate-peers! grid s remaining)
150 | (match (solved-digit? remaining)
151 | #f grid
152 | d (fold-loop peer (get-square peers s)
153 | g grid
154 | (eliminate! g peer d))))
155 |
156 | (define (apply-rules grid s d remaining)
157 | (if (none? remaining)
158 | (fail)
159 | (begin
160 | (set-square! grid s remaining)
161 | (eliminate-peers! grid s remaining)
162 | (check-units! grid s d))))
163 |
164 | ; Eliminate digit d from square s
165 | (define (eliminate! grid s d)
166 | (define current (get-square grid s))
167 | ; This test required to terminate the
168 | ; recursion
169 | (if (has-digit? current d)
170 | (apply-rules grid s d (remove-digit current d))
171 | grid))
172 |
173 | ; Return the 'values' that results from
174 | ; assigning d to square s
175 | (define (assign! grid s d)
176 | (define others (remove-digit (get-square grid s) d))
177 | (fold-loop d (show-digits others)
178 | g grid
179 | (eliminate! g s d)))
180 |
181 | (define (digit? d)
182 | (in d digits))
183 |
184 | (define (parse-grid grid-string)
185 | (fold-loop (s . d) (grid->values grid-string)
186 | g (new-grid)
187 | (if (digit? d)
188 | (assign! g s d)
189 | g)))
190 |
191 | (define (solved? grid)
192 | (let-cc return
193 | (index-loop i num-squares
194 | (if (solved-digit? (vector-ref grid i))
195 | #t
196 | (return #f)))
197 | #t))
198 |
199 | (define two-through-9 (cdr digits))
200 |
201 | (define (square-to-try grid)
202 | (let-cc return
203 | (loop num-missing two-through-9
204 | (loop s squares
205 | (let possible (show-digits (get-square grid s))
206 | (if (eq? (length possible) num-missing)
207 | (return (cons s possible))
208 | #f))))
209 | return "None missing"))
210 |
211 | (define (solve grid)
212 | (if (solved? grid)
213 | grid
214 | (with* ((s . digits) (square-to-try grid)
215 | d (amb digits))
216 | (write-line "Assiging {0} to {1}" d s)
217 | (solve (assign! (copy-grid grid) s d)))))
218 |
219 | (define (display-grid grid)
220 | (loop row (grid->lists grid)
221 | (write-line "{0}" row))
222 | nil)
223 |
224 | (define grid1 "003020600900305001001806400008102900700000008006708200002609500800203009005010300")
225 | (define grid2 "4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......")
226 | ; Hardest puzzle from http://www.mirror.co.uk/news/weird-news/worlds-hardest-sudoku-can-you-242294
227 | (define inkala-hardest
228 | (System.String/Concat
229 | "..5 3.. ..."
230 | "8.. ... .2."
231 | ".7. .1. 5.."
232 | "4.. ..5 3.."
233 | ".1. .7. ..6"
234 | "..3 2.. .8."
235 | ".6. 5.. ..9"
236 | "..4 ... .3."
237 | "... ..9 7.."))
238 |
239 | (write-line "Solving grid1...")
240 | (define parsed1 (parse-grid grid1))
241 | (display-grid parsed1)
242 | (write-line "Solving grid2...")
243 | (define parsed2 (parse-grid grid2))
244 | (write-line "With rote deduction we only get to:")
245 | (display-grid parsed2)
246 | (write-line "Solving using non-deterministic search...")
247 | (define solution2 (solve parsed2))
248 | (write-line "Solution:")
249 | (display-grid solution2)
250 |
--------------------------------------------------------------------------------
/Lisp/Lisp/Examples/fibonacci.lisp:
--------------------------------------------------------------------------------
1 | ; The archetypical fibonacci program...
2 | (define (fib n)
3 | (match n
4 | 0 0
5 | 1 1
6 | n (+ (fib (- n 1)) (fib (- n 2)))))
7 | (display (fib 8))
8 |
--------------------------------------------------------------------------------
/Lisp/Lisp/Lisp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 8.0.30703
7 | 2.0
8 | {F4859844-A8FB-4CE4-9184-2DD789A07085}
9 | Exe
10 | Properties
11 | Lisp
12 | Lisp
13 | v4.0
14 | Client
15 | 512
16 | publish\
17 | true
18 | Disk
19 | false
20 | Foreground
21 | 7
22 | Days
23 | false
24 | false
25 | true
26 | 0
27 | 1.0.0.%2a
28 | false
29 | false
30 | true
31 |
32 |
33 | x86
34 | true
35 | full
36 | false
37 | bin\Debug\
38 | DEBUG;TRACE
39 | prompt
40 | 4
41 |
42 |
43 | x86
44 | pdbonly
45 | true
46 | bin\Release\
47 | TRACE
48 | prompt
49 | 4
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | {81EE52DF-F912-4FE5-973C-262762CA3B99}
67 | LispEngine
68 |
69 |
70 |
71 |
72 | PreserveNewest
73 |
74 |
75 |
76 | PreserveNewest
77 |
78 |
79 |
80 |
81 | False
82 | Microsoft .NET Framework 4 Client Profile %28x86 and x64%29
83 | true
84 |
85 |
86 | False
87 | .NET Framework 3.5 SP1 Client Profile
88 | false
89 |
90 |
91 | False
92 | .NET Framework 3.5 SP1
93 | false
94 |
95 |
96 | False
97 | Windows Installer 3.1
98 | true
99 |
100 |
101 |
102 |
109 |
--------------------------------------------------------------------------------
/Lisp/Lisp/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using LispEngine.Bootstrap;
4 | using LispEngine.Datums;
5 | using LispEngine.Evaluation;
6 | using LispEngine.Util;
7 |
8 | namespace Lisp
9 | {
10 | class Program
11 | {
12 | static void Main(string[] args)
13 | {
14 | try
15 | {
16 | var env = StandardEnvironment.Create();
17 | env.Define("args", DatumHelpers.atomList(args));
18 | var statistics = new Statistics();
19 | env = statistics.AddTo(env);
20 | ResourceLoader.ExecuteResource(statistics, Assembly.GetExecutingAssembly(), env, "Lisp.REPL.lisp");
21 | }
22 | catch (Exception ex)
23 | {
24 | Console.Error.WriteLine("ERROR:\n{0}\n{1}\n", ex, ex.StackTrace);
25 | }
26 |
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Lisp/Lisp/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Lisp")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Lisp")]
13 | [assembly: AssemblyCopyright("Copyright © 2012")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("2a974d22-dbe6-41ee-a586-79728a428771")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Lisp/Lisp/REPL.lisp:
--------------------------------------------------------------------------------
1 | (define global-env (env))
2 | (define repl-run
3 | (let standard-run run
4 | (lambda
5 | (filename run-environment)
6 | (standard-run filename run-environment)
7 | (filename)
8 | (standard-run filename global-env))))
9 | ; Redefine 'run' to a user-friendly version
10 | ; that defaults to the repl-environment for
11 | ; evaluation
12 | (define run repl-run)
13 |
14 | (define prev-stats (!get-statistics))
15 | (define (log-steps elapsed)
16 | (with (stats (!get-statistics)
17 | delta (!get-statistics-delta prev-stats))
18 | (set! prev-stats stats)
19 | (write-line "{0} Elapsed: {1}" delta elapsed)))
20 |
21 | (define (display-error msg c)
22 | (define (indent-list continuation-fn)
23 | (loop f (continuation-fn c)
24 | (writeerr "\t{0}" f)))
25 | (writeerr "ERROR: {0}" msg)
26 | (writeerr "Tasks:")
27 | (indent-list task-descriptions)
28 | (writeerr "Results:")
29 | (indent-list pending-results)
30 | nil)
31 |
32 | ; Create a new environment with the specified definitions
33 | (define (extend e . definitions)
34 | (define (extend1 (symbol definition) e)
35 | (eval
36 | `(,begin
37 | (,define ,symbol ,definition)
38 | (,env))
39 | e))
40 | (fold-right extend1 e definitions))
41 |
42 | (define (make-debug-env (msg c))
43 | (extend (get-env c)
44 | `(trace ,(curry display-error msg c))
45 | `(resume ,c)))
46 |
47 | (define last-error nil)
48 | ; Rudimentary repl that lets you evaluate
49 | ; expressions within the context of "last-error".
50 | (define (debug)
51 | (if (nil? last-error)
52 | "Nothing to debug"
53 | (repl "debug> " (make-debug-env last-error))))
54 |
55 | (define Stopwatch System.Diagnostics.Stopwatch)
56 |
57 | (define clear System.Console/Clear)
58 |
59 | (define (repl prompt repl-env)
60 | (define prompt (curry write prompt))
61 | (let-cc return
62 | (define env-with-exit-and-debug
63 | (extend repl-env
64 | `(exit ,(curry return nil))
65 | `(debug ,debug)))
66 | (define (check-read)
67 | (let next (read console)
68 | (if (eof-object? next)
69 | (return nil)
70 | next)))
71 | (define (repl-eval expr)
72 | (eval expr env-with-exit-and-debug))
73 |
74 | (define (loop)
75 | (try
76 | (prompt)
77 | (with* (expr (check-read)
78 | stop-watch (Stopwatch/StartNew)
79 | result (repl-eval expr))
80 | (display result)
81 | (log-steps (.get_Elapsed stop-watch)))
82 | catch error
83 | (set! last-error error)
84 | (writeerr "ERROR: {0}" (car error))
85 | (writeerr "(debug) to enter debug repl"))
86 | (loop))
87 | (loop)))
88 |
89 | (repl "FCLisp> " global-env)
90 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Bootstrap/Append.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Bootstrap
9 | {
10 | class Append : Function
11 | {
12 | // We could implement this in pure lisp but I'm lazy
13 | // and it's trivial.
14 | public static readonly StackFunction Instance = new Append().ToStack();
15 | public Datum Evaluate(Datum args)
16 | {
17 | return args.Enumerate()
18 | .Aggregate(Enumerable.Empty(), (current, a) => current.Concat(a.Enumerate()))
19 | .ToList();
20 | }
21 |
22 | public override string ToString()
23 | {
24 | return ",append";
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Bootstrap/Arithmetic.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Core;
6 | using LispEngine.Datums;
7 | using LispEngine.Evaluation;
8 |
9 | namespace LispEngine.Bootstrap
10 | {
11 | class Arithmetic : DatumHelpers
12 | {
13 | private delegate object Op(int x, int y);
14 |
15 | class Operation : BinaryFunction
16 | {
17 | private readonly string name;
18 | private readonly Op op;
19 | public Operation(string name, Op op)
20 | {
21 | this.name = name;
22 | this.op = op;
23 | }
24 | protected override Datum eval(Datum arg1, Datum arg2)
25 | {
26 | return atom(op(castInt(arg1), castInt(arg2)));
27 | }
28 | public override string ToString()
29 | {
30 | return name;
31 | }
32 | }
33 |
34 | private static StackFunction makeOperation(string name, Op op)
35 | {
36 | return new Operation(name, op).ToStack();
37 | }
38 |
39 | public static LexicalEnvironment Extend(LexicalEnvironment env)
40 | {
41 | return env
42 | .Define("+", makeOperation("+", (x, y) => x + y))
43 | .Define("-", makeOperation("-", (x, y) => x - y))
44 | .Define("*", makeOperation("*", (x, y) => x*y))
45 | .Define("/", makeOperation("/", (x, y) => x/y))
46 | .Define("<", makeOperation("<", (x, y) => x < y))
47 | .Define(">", makeOperation(">", (x, y) => x > y))
48 | .Define("bit-and", makeOperation("bit-and", (x , y) => x & y))
49 | .Define("bit-or", makeOperation("bit-or", (x, y) => x | y))
50 | .Define("bit-shift", makeOperation("bit-shift", (x, y) => x << y));
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Bootstrap/Builtins.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Reflection;
4 | using LispEngine.Core;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 | using LispEngine.Lexing;
8 | using LispEngine.Parsing;
9 | using LispEngine.ReflectionBinding;
10 | using LispEngine.Util;
11 |
12 | namespace LispEngine.Bootstrap
13 | {
14 | /**
15 | * Here we define everything that can be defined in Lisp itself
16 | * from the Core. i.e. everything that is 'standard' but which
17 | * can be defined once the Core is defined.
18 | */
19 | public sealed class Builtins
20 | {
21 | public static LexicalEnvironment AddTo(LexicalEnvironment env)
22 | {
23 | env = Arithmetic.Extend(env);
24 | env.Define("append", Append.Instance);
25 | env = SymbolFunctions.Extend(env);
26 | ResourceLoader.ExecuteResource(env, "LispEngine.Bootstrap.Builtins.lisp");
27 | ResourceLoader.ExecuteResource(env, "LispEngine.Bootstrap.Library.lisp");
28 | env = Reader.AddTo(env);
29 | return env;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Bootstrap/Builtins.lisp:
--------------------------------------------------------------------------------
1 | (define nil '())
2 | (define list (lambda x x))
3 | (define car (lambda ((a . b)) a))
4 | (define cdr (lambda ((c . d)) d))
5 | (define nil? (lambda (()) #t _ #f))
6 | (define pair? (lambda ((_ . _)) #t _ #f))
7 | (define display log)
8 |
9 | ; Now, let's implement simple non-nested quasiquote in terms of Lisp itself
10 | ; We need it quite early because writing macros with*out quasiquote
11 | ; is extremely painful!
12 | ; Using the builtin pattern matching of our lambda primitive makes
13 | ; this significantly simpler to implement.
14 | (define expand-quasiquote
15 | (lambda
16 | (('unquote e))
17 | e
18 | ((('unquote-splicing x) . y))
19 | (list append x (expand-quasiquote y))
20 | ((x . y))
21 | (list cons (expand-quasiquote x) (expand-quasiquote y))
22 | x
23 | (cons quote x)))
24 | (define quasiquote
25 | (macro expand-quasiquote))
26 |
27 | ; We use define followed by macro and a lambda an awful lot - so
28 | ; let's define a macro to ease the overhead in
29 | ; writing macros!
30 | (define define-macro
31 | (macro (lambda (name args . exprs)
32 | `(,define ,name
33 | (,macro (,lambda ,args (,begin ,@exprs)))))))
34 |
35 |
36 | ; Our let macro is like the one in arc - just
37 | ; a single variable, single expression, and no
38 | ; nesting.
39 | ; We can define "with*" later as a macro that
40 | ; expands into individual sublets.
41 | (define-macro let (var value . body)
42 | `((,lambda (,var) (,begin ,@body)) ,value))
43 |
44 | ; Our pattern matching is powerful enough to define 'if'
45 | ; as a macro. In fact, it can even handle
46 | ; "multicase" if, by expanding the remaining
47 | ; clauses into itself.
48 | (define if (macro
49 | (lambda (condition true-case false-case)
50 | ; Base case. Anything not false is considered 'true'
51 | `((,lambda (#f) ,false-case
52 | _ ,true-case) ,condition)
53 | ; Multiple clauses. Expand into recursive if statements.
54 | (condition true-case . remainder)
55 | `(,if ,condition ,true-case
56 | (,if ,@remainder)))))
57 |
58 | ; Now add support for multiple sub-statements in define:
59 | ; Whenever we see
60 | ; (define x expr1 expr2 ...)
61 | ; we'll expand it to
62 | ; (raw-define x (begin expr1 expr2 ...))
63 | ; Also, whenever we see
64 | ; (define (name arg1 arg2) expr1 expr2)
65 | ; we'll expand it to
66 | ; (define name (lambda arg1 arg2) (begin expr1 expr2))
67 | ; This is the traditional syntax used in SICP et al.
68 | ; However, we can't support multiple bodies in our
69 | ; lambdas with*out screwing up our nice 'case lambda'
70 | ; syntax which I find more useful than being able to
71 | ; define multiple bodies in a lambda. You can aways
72 | ; use 'begin' explicitly if need be.
73 | (define define
74 | (macro
75 | ; Because define itself mutates the environment,
76 | ; we have to capture the original 'define' here
77 | ; before we 'hide' it behind our macro replacement.
78 | ; Otherwise, we go into an infinite loop when expanding.
79 | (let raw-define define
80 | (lambda
81 | ; Traditional function definition
82 | ((fn . args) . exprs)
83 | `(,raw-define ,fn (,lambda ,args (,begin ,@exprs)))
84 | (symbol . exprs)
85 | `(,raw-define ,symbol (,begin ,@exprs))))))
86 |
87 | (define (let-bindings bindings)
88 | (define splice
89 | (lambda (bindings ())
90 | bindings
91 | ((vars values) (var value . rest))
92 | (splice (list (cons var vars) (cons value values)) rest)))
93 | (let (vars values) (splice '(() ()) bindings)
94 | (list vars (cons list values))))
95 |
96 | ; 'with' macro allows multiple bindings - but the bindings
97 | ; cannot see each other. i.e.
98 | ; (with (x 5 y x) ... ) won't work.
99 | ; However, this version is probably more efficient than with* below.
100 | (define-macro with (bindings . body)
101 | `(let ,@(let-bindings bindings) ,@body))
102 |
103 | ; 'with*' macro decomposes recursively into nested 'let' statements
104 | ; So 'later' definitions can see earlier definitions.
105 | ; i.e.
106 | ; (with* (x 5 y x) ... ) will work.
107 | (define with* (macro
108 | (lambda (() body)
109 | body
110 | ((var . (expr . bindings)) . body)
111 | `(,let ,var ,expr (,with* ,bindings (,begin ,@body))))))
112 |
113 | ; We cannot use "/" as this is overloaded to allow static method
114 | ; access ala Clojure style.
115 | (define call-cc call-with-current-continuation)
116 |
117 | ; let-cc provides "escape" functionality
118 | (define-macro let-cc (var . body)
119 | `(,call-cc (,lambda (,var) (,begin ,@body))))
120 |
121 | ; Function that can be used to expand a (possibly)
122 | ; macro-ized expression. Useful for debugging
123 | ; macros. The 'trick' part is the "unmacro"
124 | ; builtin which is the inverse function of 'macro'.
125 | (define expand
126 | (lambda (env (fexpr . args))
127 | (with* (macro-expr (eval fexpr env)
128 | macro-fn (unmacro macro-expr))
129 | (if (nil? macro-fn)
130 | (cons fexpr args)
131 | (apply macro-fn args)))
132 | (env other) other))
133 |
134 | ; In the common case of only wanting to
135 | ; dispatch on the pattern of one variable,
136 | ; define a convenient macro to de-nest
137 | ; the arguments that would otherwise
138 | ; be required in a plain lambda expression
139 | (define-macro match (var . cases)
140 | (define de-nest
141 | (lambda
142 | (()) ()
143 | ((pattern . (body . remaining)))
144 | `(,(list pattern) ,body ,@(de-nest remaining))))
145 | `((,lambda ,@(de-nest cases)) ,var))
146 |
147 | (define (not expr)
148 | (match expr
149 | #f #t
150 | _ #f))
151 |
152 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Bootstrap/Builtins.lisp~:
--------------------------------------------------------------------------------
1 | (define list (lambda x x))
2 | (define car (lambda ((x . y)) x))
3 | (define cdr (lambda ((x . y)) y))
4 | (define nil? (lambda (()) #t _ #f))
5 | (define pair? (lambda ((_ . _)) #t _ #f))
6 | (define let (macro
7 | (lambda (var value body)
8 | (list (list lambda (list var) body) value))))
9 |
10 | ; b.add("list", "(lambda x x)");
11 | ; b.add("car", "(lambda ((x . y)) x)");
12 | ; b.add("cdr", "(lambda ((x . y)) y)");
13 | ; b.add("nil?", "(lambda (()) #t _ #f)");
14 | ; b.add("pair?", "(lambda ((_ . _)) #t _ #f)");
15 | ; b.add("letdef", "(lambda (var value body) (list (list lambda (list var) body) value))");
16 | ; b.add("let", "(macro letdef)");
17 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Bootstrap/IO.lisp:
--------------------------------------------------------------------------------
1 | ; Functions for input and output
2 | ; Defined using .Net bindings for the most part
3 |
4 | ; Read a file into a list of s-expressions
5 | ; Not very elegant until we build in some sort of RAII
6 | (define (read-file filename)
7 | (let-cc return
8 | (let file-stream (System.IO.File/OpenRead filename)
9 | (try
10 | (with* (text-reader (new System.IO.StreamReader file-stream)
11 | input (open-input-stream text-reader))
12 | (define (loop so-far)
13 | (let next (read input)
14 | (if (eof-object? next)
15 | (begin
16 | (.Dispose file-stream)
17 | (return (reverse so-far)))
18 | (loop (cons next so-far)))))
19 | (loop nil))
20 | catch (msg c)
21 | ; Not quite ideal. Ideally we'd "throw" the
22 | ; original continuation along with this.
23 | (.Dispose file-stream)
24 | throw msg))))
25 |
26 |
27 | (define pwd System.IO.Directory/GetCurrentDirectory)
28 |
29 | ; Load an execute a lisp file using the specified
30 | ; environment
31 | (define (run filename run-environment)
32 | (define last-result nil)
33 | (loop expr (read-file filename)
34 | (set! last-result (eval expr run-environment)))
35 | last-result)
36 |
37 | (define system-console System.Console)
38 | (define console (open-input-stream (system-console/get_In)))
39 | (define display (curry system-console/WriteLine "-> {0}"))
40 | (define writeerr (curry .WriteLine (system-console/get_Error)))
41 | (define write system-console/Write)
42 | (define write-line system-console/WriteLine)
43 |
44 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Bootstrap/Reader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using LispEngine.Core;
7 | using LispEngine.Datums;
8 | using LispEngine.Evaluation;
9 | using LispEngine.Lexing;
10 | using LispEngine.Parsing;
11 |
12 | namespace LispEngine.Bootstrap
13 | {
14 | class Reader
15 | {
16 | class Eof
17 | {
18 | public override string ToString()
19 | {
20 | return "#!eof";
21 | }
22 | }
23 |
24 | private static readonly Datum eof = new Eof().ToAtom();
25 |
26 | class Read : UnaryFunction
27 | {
28 | protected override Datum eval(Datum arg)
29 | {
30 | var parser = (Parser)arg.CastObject();
31 | return parser.parse() ?? eof;
32 | }
33 |
34 | public override string ToString()
35 | {
36 | return ",read";
37 | }
38 |
39 | public static readonly StackFunction Instance = new Read().ToStack();
40 | }
41 |
42 | class IsEof : UnaryFunction
43 | {
44 | protected override Datum eval(Datum arg)
45 | {
46 | return eof.Equals(arg).ToAtom();
47 | }
48 |
49 | public override string ToString()
50 | {
51 | return ",eof-object?";
52 | }
53 |
54 | public static readonly StackFunction Instance = new IsEof().ToStack();
55 | }
56 |
57 | class OpenInputString : UnaryFunction
58 | {
59 | protected override Datum eval(Datum arg)
60 | {
61 | return new Parser(Scanner.Create(arg.CastString())).ToAtom();
62 | }
63 |
64 | public override string ToString()
65 | {
66 | return ",open-input-string";
67 | }
68 |
69 | public static readonly StackFunction Instance = new OpenInputString().ToStack();
70 | }
71 |
72 | class OpenInputStream : UnaryFunction
73 | {
74 | protected override Datum eval(Datum arg)
75 | {
76 | return new Parser(new Scanner((TextReader) arg.CastObject())).ToAtom();
77 | }
78 |
79 | public override string ToString()
80 | {
81 | return ",open-input-stream";
82 | }
83 |
84 | public static readonly StackFunction Instance = new OpenInputStream().ToStack();
85 | }
86 |
87 | public static LexicalEnvironment AddTo(LexicalEnvironment env)
88 | {
89 | env.Define("read", Read.Instance);
90 | env.Define("open-input-string", OpenInputString.Instance);
91 | env.Define("eof-object?", IsEof.Instance);
92 | env.Define("open-input-stream", OpenInputStream.Instance);
93 | return env;
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Bootstrap/StandardEnvironment.cs:
--------------------------------------------------------------------------------
1 | using LispEngine.Core;
2 | using LispEngine.Evaluation;
3 | using LispEngine.ReflectionBinding;
4 | using LispEngine.Util;
5 |
6 | namespace LispEngine.Bootstrap
7 | {
8 | public class StandardEnvironment
9 | {
10 | public static LexicalEnvironment Create()
11 | {
12 | var env = CreateSandbox();
13 | // Adding reflection builtins enables a lisp engine
14 | // to execute any code.
15 | env = ReflectionBuiltins.AddTo(env);
16 | // Add functions for reading files, executing lisp programs
17 | // defined in files.
18 | ResourceLoader.ExecuteResource(env, "LispEngine.Bootstrap.IO.lisp");
19 | return env;
20 | }
21 |
22 | /**
23 | * Create a "sandbox" environment: the symbols defined in the
24 | * environment don't provide any way for lisp programs to execute
25 | * arbitrary code.
26 | */
27 | public static LexicalEnvironment CreateSandbox()
28 | {
29 | var env = LexicalEnvironment.Create();
30 | env = CoreForms.AddTo(env);
31 | env = Builtins.AddTo(env);
32 | return env;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Bootstrap/SymbolFunctions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Core;
6 | using LispEngine.Datums;
7 | using LispEngine.Evaluation;
8 |
9 | namespace LispEngine.Bootstrap
10 | {
11 | class SymbolFunctions
12 | {
13 | class SymbolToString : UnaryFunction
14 | {
15 | protected override Datum eval(Datum arg)
16 | {
17 | return arg.CastSymbol().Identifier.ToAtom();
18 | }
19 | }
20 |
21 | class StringToSymbol : UnaryFunction
22 | {
23 | protected override Datum eval(Datum arg)
24 | {
25 | return DatumHelpers.symbol(DatumHelpers.castString(arg));
26 | }
27 | }
28 |
29 | class GenSym : Function
30 | {
31 | public Datum Evaluate(Datum args)
32 | {
33 | if (!DatumHelpers.nil.Equals(args))
34 | throw DatumHelpers.error("gensym accepts no arguments");
35 | return Symbol.GenUnique();
36 | }
37 | }
38 |
39 | public static LexicalEnvironment Extend(LexicalEnvironment env)
40 | {
41 | env.Define("symbol->string", new SymbolToString().ToStack());
42 | env.Define("string->symbol", new StringToSymbol().ToStack());
43 | env.Define("gensym", new GenSym().ToStack());
44 | return env;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/Apply.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | class Apply : AbstractStackFunction
11 | {
12 | public static readonly StackFunction Instance = new Apply();
13 |
14 | private Apply()
15 | {
16 | }
17 |
18 | public override Continuation Evaluate(Continuation c, Datum args)
19 | {
20 | var datumArgs = args.ToArray();
21 | if (datumArgs.Length != 2)
22 | throw c.error("Apply expects 2 arguments. {0} passed", datumArgs.Length);
23 | var function = datumArgs[0] as StackFunction;
24 | if (function == null)
25 | throw c.error("'{0}' is not a function", datumArgs[0]);
26 | return function.Evaluate(c, datumArgs[1]);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/Begin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | class Begin : AbstractFExpression
11 | {
12 | public static readonly FExpression Instance = new Begin();
13 |
14 | private static Continuation popResult(Continuation c)
15 | {
16 | return c.PopResult();
17 | }
18 |
19 | public override Continuation Evaluate(Continuation c, LexicalEnvironment env, Datum args)
20 | {
21 | var argList = args.ToArray();
22 | if (argList.Length < 1)
23 | throw c.error("Expected at least 1 expression for begin. Got none.");
24 | // Scope any local definitions.
25 | var localEnv = env.NewFrame();
26 | var remaining = argList.Reverse().ToArray();
27 | for (var i = 0; i < remaining.Length; ++i)
28 | {
29 | if (i > 0)
30 | c = c.PushTask(popResult, "Discard result");
31 | c = c.Evaluate(localEnv, remaining[i]);
32 | }
33 | return c;
34 | }
35 |
36 | public override string ToString()
37 | {
38 | return ",begin";
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/BinaryFunction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | abstract class BinaryFunction : Function
11 | {
12 | public Datum Evaluate(Datum args)
13 | {
14 | var argDatums = args.ToArray();
15 | if (argDatums.Length != 2)
16 | throw DatumHelpers.error("Exactly 2 arguments expected");
17 | return eval(argDatums[0], argDatums[1]);
18 | }
19 |
20 | protected abstract Datum eval(Datum arg1, Datum arg2);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/CallCC.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | class CallCC : AbstractStackFunction
11 | {
12 | public class ContinuationFunction : AbstractStackFunction
13 | {
14 | private readonly Continuation c;
15 | public ContinuationFunction(Continuation c)
16 | {
17 | this.c = c;
18 | }
19 |
20 | public Continuation Continuation
21 | {
22 | get { return c; }
23 | }
24 |
25 | public override Continuation Evaluate(Continuation oldContinuation, Datum args)
26 | {
27 | // Replace the old continuation with the new continuation - but pass in the
28 | // supplied argument as the 'return value' of the new continuation.
29 | var argArray = args.ToArray();
30 | // We allow calling a "continuation" with 0 args. Such a continuation
31 | // only arises from the error function.
32 | // TODO: we should differentiate the two with an "expected args" member which we can error check.
33 | if (argArray.Length == 0)
34 | return c;
35 | return c.PushResult(argArray[0]);
36 | }
37 | }
38 |
39 | public static StackFunction MakeContinuationFunction(Continuation c)
40 | {
41 | return new ContinuationFunction(c);
42 | }
43 |
44 | public static readonly StackFunction Instance = new CallCC();
45 |
46 | public override Continuation Evaluate(Continuation c, Datum args)
47 | {
48 | var argArray = args.ToArray();
49 | if(argArray.Length != 1)
50 | throw DatumHelpers.error("call-cc: expect a single function as an argument. Got {0}", argArray.Length);
51 | var arg = argArray[0];
52 | var function = arg as StackFunction;
53 | if(function == null)
54 | throw DatumHelpers.error("call-cc: {0} must be a function", arg);
55 | return function.Evaluate(c, DatumHelpers.compound(MakeContinuationFunction(c)));
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/CoreForms.cs:
--------------------------------------------------------------------------------
1 | using LispEngine.Datums;
2 | using LispEngine.Evaluation;
3 | using LispEngine.ReflectionBinding;
4 |
5 | namespace LispEngine.Core
6 | {
7 | // Because we have implemented macros as first class objects,
8 | // *all* core forms can be simply be defined in the environment!
9 | public class CoreForms
10 | {
11 | public static LexicalEnvironment AddTo(LexicalEnvironment env)
12 | {
13 | env = env
14 | .Define("log", Log.Instance)
15 | .Define("lambda", Lambda.Instance)
16 | .Define("cons", DelegateFunctions.MakeDatumFunction(DatumHelpers.cons, ",cons"))
17 | .Define("set-car!", DelegateFunctions.MakeDatumFunction(DatumHelpers.setCar, ",set-car!"))
18 | .Define("set-cdr!", DelegateFunctions.MakeDatumFunction(DatumHelpers.setCdr, ",set-cdr!"))
19 | .Define("apply", Apply.Instance)
20 | .Define("eq?", EqualFunctions.Eq)
21 | .Define("equal?", EqualFunctions.Equal)
22 | .Define("quote", Quote.Instance)
23 | .Define("define", Define.Instance)
24 | .Define("set!", Set.Instance)
25 | .Define("begin", Begin.Instance)
26 | .Define("call-with-current-continuation", CallCC.Instance)
27 | .Define("eval", Eval.Instance)
28 | .Define("env", Env.Instance);
29 | env = DebugFunctions.AddTo(env);
30 | env = Macro.AddTo(env);
31 | env = VectorFunctions.AddTo(env);
32 | return env;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/DebugFunctions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | class DebugFunctions : DelegateFunctions
11 | {
12 | private static Continuation asContinuation(Datum arg)
13 | {
14 | var cfunction = arg as CallCC.ContinuationFunction;
15 | if (cfunction == null)
16 | throw DatumHelpers.error("'{0}' is not a continuation", arg);
17 | return cfunction.Continuation;
18 | }
19 |
20 | private static Datum getEnvironments(Datum arg)
21 | {
22 | var c = asContinuation(arg);
23 | var stack = DatumHelpers.nil;
24 | while (c.Env != null)
25 | {
26 | stack = DatumHelpers.cons(c.Env.ToAtom(), stack);
27 | c = c.PopEnv();
28 | }
29 | return stack;
30 | }
31 |
32 | private static Datum getTaskDescriptions(Datum arg)
33 | {
34 | var c = asContinuation(arg);
35 | var stack = DatumHelpers.nil;
36 | while (c.Task != null)
37 | {
38 | stack = DatumHelpers.cons(c.Task.ToString().ToAtom(), stack);
39 | c = c.PopTask();
40 | }
41 | return stack;
42 | }
43 |
44 | private static Datum getPendingResults(Datum arg)
45 | {
46 | var c = asContinuation(arg);
47 | var stack = DatumHelpers.nil;
48 | while (c.Result != null)
49 | {
50 | stack = DatumHelpers.cons(c.Result, stack);
51 | c = c.PopResult();
52 | }
53 | return stack;
54 | }
55 |
56 | private static Datum getEnv(Datum arg)
57 | {
58 | var c = asContinuation(arg);
59 | return c.Env.ToAtom();
60 | }
61 |
62 | private static Datum throwMsg(Datum arg)
63 | {
64 | var msg = (String)arg.CastObject();
65 | throw DatumHelpers.error(msg);
66 | }
67 |
68 | public static LexicalEnvironment AddTo(LexicalEnvironment env)
69 | {
70 | return env.Define("task-descriptions", MakeDatumFunction(getTaskDescriptions, ",task-descriptions"))
71 | .Define("execute-with-error-translator", ExecuteWithErrorTranslator.Instance)
72 | .Define("env-stack", MakeDatumFunction(getEnvironments, ",env-stack"))
73 | .Define("pending-results", MakeDatumFunction(getPendingResults, ",pending-results"))
74 | .Define("throw", MakeDatumFunction(throwMsg, ",throw"))
75 | .Define("get-env", MakeDatumFunction(getEnv, ",get-env"));
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/Define.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | class Define : AbstractFExpression
11 | {
12 | public static readonly FExpression Instance = new Define();
13 |
14 | public override Continuation Evaluate(Continuation c, LexicalEnvironment env, Datum args)
15 | {
16 | var argList = args.ToArray();
17 | if (argList.Length != 2)
18 | throw c.error("Expected 2 arguments: (define ). Got {0} instead", argList.Length);
19 | var name = argList[0].CastSymbol();
20 | var expression = argList[1];
21 | c = c.PushTask(
22 | tc => { env.Define(name, tc.Result);
23 | return tc;},
24 | "define '{0}'", name);
25 | return c.Evaluate(env, expression);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/Env.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | class Env : AbstractFExpression
11 | {
12 | public override Continuation Evaluate(Continuation c, LexicalEnvironment env, Datum args)
13 | {
14 | return c.PushResult(env.ToAtom());
15 | }
16 |
17 | public override string ToString()
18 | {
19 | return ",env";
20 | }
21 |
22 | public static FExpression Instance = new Env();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/EqualFunctions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | class EqualFunctions : BinaryFunction
11 | {
12 | public static readonly StackFunction Eq = new EqualFunctions(true).ToStack();
13 | public static readonly StackFunction Equal = new EqualFunctions(false).ToStack();
14 | private readonly bool shallow;
15 | public EqualFunctions(bool shallow)
16 | {
17 | this.shallow = shallow;
18 | }
19 | protected override Datum eval(Datum arg1, Datum arg2)
20 | {
21 | if (shallow &&
22 | ((arg1 as Pair) != null ||
23 | (arg2 as Pair) != null))
24 | return DatumHelpers.atom(ReferenceEquals(arg1, arg2));
25 | return DatumHelpers.atom(arg1.Equals(arg2));
26 | }
27 |
28 | public override string ToString()
29 | {
30 | return shallow ? ",eq?" : ",equal?";
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/Eval.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | class Eval : AbstractStackFunction
11 | {
12 | public override Continuation Evaluate(Continuation c, Datum args)
13 | {
14 | var argArray = args.ToArray();
15 | var expression = argArray[0];
16 | var environment = (LexicalEnvironment) argArray[1].CastObject();
17 | return c.Evaluate(environment, expression);
18 | }
19 |
20 | public override string ToString()
21 | {
22 | return ",eval";
23 | }
24 |
25 | public static readonly StackFunction Instance = new Eval();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/ExecuteWithErrorTranslator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | using ErrorHandler = Func;
11 |
12 | /**
13 | * This "builtin" function provides a way to install a
14 | * Lisp function as the handler for what to do when a .Net exception
15 | * is thrown.
16 | */
17 | class ExecuteWithErrorTranslator : AbstractStackFunction
18 | {
19 | private static ErrorHandler makeErrorHandler(ErrorHandler oldErrorHandler, StackFunction f)
20 | {
21 | // Report the "message" from the exception to the Lisp
22 | // error handling function.
23 | // Ensure that the original error handler is in scope before evaluating the error function -
24 | // otherwise we end up in an infinite loop if there's an error in the error function itself.
25 | return (c, ex) => f.Evaluate(c.PopTask().SetErrorHandler(oldErrorHandler), DatumHelpers.compound(ex.Message.ToAtom(), CallCC.MakeContinuationFunction(c)));
26 | }
27 |
28 | public override Continuation Evaluate(Continuation c, Datum args)
29 | {
30 | var argArray = args.ToArray();
31 | if (argArray.Length != 2)
32 | throw DatumHelpers.error("Invalid syntax. ArgCount ({0}) != 2. Usage: (execute-with-error-handler )", argArray.Length);
33 | var errorHandler = makeErrorHandler(c.ErrorHandler, (StackFunction)argArray[0]);
34 | var fn = (StackFunction)argArray[1];
35 | return fn.Evaluate(c.NewErrorHandler(errorHandler), DatumHelpers.compound());
36 | }
37 |
38 | public override string ToString()
39 | {
40 | return ",execute-with-error-translator";
41 | }
42 |
43 | public static readonly StackFunction Instance = new ExecuteWithErrorTranslator();
44 | }
45 | }
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/Lambda.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | using FrameBinder = Func>;
11 |
12 | internal sealed class Lambda : AbstractFExpression
13 | {
14 | public static readonly FExpression Instance = new Lambda();
15 |
16 | private Lambda()
17 | {
18 | }
19 |
20 | private sealed class ArgBody
21 | {
22 | public readonly Datum argDatum;
23 | public readonly FrameBinder binding;
24 | public readonly Datum body;
25 | public ArgBody(Datum argDatum, Datum body)
26 | {
27 | this.argDatum = argDatum;
28 | this.binding = LexicalBinder.Create(argDatum);
29 | this.body = body;
30 | }
31 |
32 | public override string ToString()
33 | {
34 | return string.Format("{0} {1}", argDatum, body);
35 | }
36 | }
37 |
38 | private sealed class Closure : AbstractStackFunction
39 | {
40 | private readonly LexicalEnvironment env;
41 | private readonly IEnumerable argBodies;
42 | public Closure(LexicalEnvironment env, IEnumerable argBodies)
43 | {
44 | this.env = env;
45 | this.argBodies = argBodies;
46 | }
47 |
48 | Exception bindError(Datum args)
49 | {
50 | return DatumHelpers.error("Could not bind '{0}' to '{1}'", argList(), args);
51 | }
52 |
53 | private string argList()
54 | {
55 | return string.Join(" or ", argBodies.Select(a => a.argDatum.ToString()));
56 | }
57 |
58 | public override string ToString()
59 | {
60 | return string.Format("(lambda {0})", string.Join(" ", argBodies.Select(x => x.ToString()).ToArray()));
61 | }
62 |
63 | public override Continuation Evaluate(Continuation c, Datum args)
64 | {
65 | foreach (var ab in argBodies)
66 | {
67 | var frameBindings = ab.binding(args);
68 | if (frameBindings == null)
69 | continue;
70 | var closureEnv = env.NewFrame(frameBindings);
71 | return c.Evaluate(closureEnv, ab.body);
72 | }
73 | throw bindError(args);
74 | }
75 | }
76 |
77 | private static Datum evaluate(Continuation c, LexicalEnvironment env, Datum args)
78 | {
79 | var macroArgs = args.ToArray();
80 | if (macroArgs.Length % 2 != 0)
81 | throw c.error("Invalid macro syntax for lambda. Argument count for '{0}' is not even ({1}). Syntax is (lambda [args body]+)", args, macroArgs.Length);
82 |
83 | var argBodies = new List();
84 | for (var i = 0; i < macroArgs.Length; i += 2)
85 | {
86 | var closureArgs = macroArgs[i];
87 | var body = macroArgs[i + 1];
88 | argBodies.Add(new ArgBody(closureArgs, body));
89 | }
90 | return new Closure(env, argBodies);
91 | }
92 |
93 | public override Continuation Evaluate(Continuation c, LexicalEnvironment env, Datum args)
94 | {
95 | return c.PushResult(evaluate(c, env, args));
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/LexicalBinder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | using FrameBindings = IStack;
11 | using FrameBinder = Func>;
12 |
13 | using Binder = Func, IStack>;
14 |
15 | class LexicalBinder : DatumHelpers
16 | {
17 | private static Binder matchExact(Datum d)
18 | {
19 | return (arg, frame) => d.Equals(arg) ? frame : null;
20 | }
21 |
22 | private static FrameBindings bindSymbol(Symbol symbol, Datum value, FrameBindings bindings)
23 | {
24 | return bindings.Push(new LexicalEnvironment.Binding(symbol, value));
25 | }
26 |
27 | private static Binder combine(Binder first, Binder second)
28 | {
29 | return (arg, frame) =>
30 | {
31 | var argPair = arg as Pair;
32 | if (argPair == null)
33 | return null;
34 | var e = first(argPair.First, frame);
35 | return e == null ? null : second(argPair.Second, e);
36 | };
37 | }
38 |
39 | class BinderPartFactory : AbstractVisitor
40 | {
41 | public override Binder defaultCase(Datum d)
42 | {
43 | throw error("'{0}' is not a valid argument list", d);
44 | }
45 |
46 | public override Binder visit(Null n)
47 | {
48 | return (arg, frame) => arg == nil ? frame : null;
49 | }
50 |
51 | public override Binder visit(Symbol s)
52 | {
53 | return (arg, frame) => bindSymbol(s, arg, frame);
54 | }
55 |
56 | public override Binder visit(Pair p)
57 | {
58 | // Quoted instances also form an exact match
59 | if (quote.Equals(p.First))
60 | {
61 | var quoted = p.Second as Pair;
62 | if (quoted != null)
63 | return matchExact(quoted.First);
64 | }
65 | return combine(p.First.accept(this), p.Second.accept(this));
66 | }
67 |
68 | public override Binder visit(Atom a)
69 | {
70 | return matchExact(a);
71 | }
72 | }
73 |
74 | public static readonly DatumVisitor Factory = new BinderPartFactory();
75 |
76 | private static Binder create(Datum d)
77 | {
78 | return d.accept(Factory);
79 | }
80 |
81 | public static FrameBinder Create(Datum argPattern)
82 | {
83 | var binder = create(argPattern);
84 | return arg => binder(arg, LexicalEnvironment.EmptyFrame);
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/Log.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | class Log : UnaryFunction
11 | {
12 | public static readonly StackFunction Instance = new Log().ToStack();
13 | protected override Datum eval(Datum arg)
14 | {
15 | Console.WriteLine("{0}", arg);
16 | return arg;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/Macro.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | /**
11 | * Turns a function into something that
12 | * can be used as a macro.
13 | */
14 | internal class Macro : DatumHelpers, Function
15 | {
16 | public static readonly StackFunction Instance = new Macro().ToStack();
17 | public static readonly StackFunction Unmacro = new UnMacro().ToStack();
18 |
19 | public static LexicalEnvironment AddTo(LexicalEnvironment env)
20 | {
21 | env.Define("unmacro", Unmacro);
22 | env.Define("macro", Instance);
23 | return env;
24 | }
25 |
26 | private sealed class UnMacro : UnaryFunction
27 | {
28 | protected override Datum eval(Datum arg)
29 | {
30 | var macro = arg as MacroClosure;
31 | return macro == null ? nil : macro.Function;
32 | }
33 | }
34 |
35 | private sealed class EvaluateExpansion : Task
36 | {
37 | private readonly MacroClosure macro;
38 | private readonly Pair macroDatum;
39 |
40 | public EvaluateExpansion(MacroClosure macro, Pair macroDatum)
41 | {
42 | this.macro = macro;
43 | this.macroDatum = macroDatum;
44 | }
45 |
46 | public Continuation Perform(Continuation c)
47 | {
48 | // The result datum may be a graph. This makes certain
49 | // optimizations risky.
50 | var expansion = c.Result;
51 | if(macroDatum != null)
52 | {
53 | // Cache macro expansions. In the extremely
54 | // common case of the same macro being used on the
55 | // same Datum, re-use the expansion.
56 | macroDatum.Cache = cons(macro, expansion);
57 | }
58 | return c.PopResult().PopEnv().Evaluate(c.Env, expansion);
59 | }
60 | }
61 |
62 | private class MacroClosure : AbstractFExpression
63 | {
64 | private readonly StackFunction argFunction;
65 |
66 | public StackFunction Function
67 | {
68 | get { return argFunction; }
69 | }
70 |
71 | public MacroClosure(StackFunction argFunction)
72 | {
73 | this.argFunction = argFunction;
74 | }
75 |
76 | public override Continuation Evaluate(Continuation c, LexicalEnvironment env, Datum args)
77 | {
78 | var p = args as Pair;
79 | c = c.PushEnv(env).PushTask(new EvaluateExpansion(this, p));
80 | // Optimization - if this macro has been expanded on this Datum before,
81 | // use the same expansion.
82 | // See "macro-cache-in-datum" unit test for a demonstration of why
83 | // we need to check against "this" also.
84 | if(p != null)
85 | {
86 | var cachedPair = p.Cache as Pair;
87 | if(cachedPair != null && ReferenceEquals(cachedPair.First, this))
88 | return c.PushResult(cachedPair.Second);
89 | }
90 | c.Statistics.Expansions++;
91 | return argFunction.Evaluate(c, args);
92 | }
93 |
94 | public override string ToString()
95 | {
96 | return string.Format("(,macro {0})", argFunction);
97 | }
98 | }
99 |
100 | public static FExpression ToMacro(StackFunction function)
101 | {
102 | return new MacroClosure(function);
103 | }
104 |
105 | public Datum Evaluate(Datum args)
106 | {
107 | var function = UnaryFunction.GetSingle(args) as StackFunction;
108 | if (function == null)
109 | throw error("Expected function argument");
110 | return ToMacro(function);
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/Quote.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | class Quote : AbstractFExpression
11 | {
12 | public static readonly FExpression Instance = new Quote();
13 |
14 | private static Datum evaluate(Continuation c, Datum args)
15 | {
16 | var argList = args.ToArray();
17 | if (argList.Length != 1)
18 | throw c.error("invalid syntax '{0}'", args);
19 | return argList[0];
20 | }
21 |
22 | public override Continuation Evaluate(Continuation c, LexicalEnvironment env, Datum args)
23 | {
24 | return c.PushResult(evaluate(c, args));
25 | }
26 |
27 | public override string ToString()
28 | {
29 | return ",quote";
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/Set.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Text;
4 | using LispEngine.Datums;
5 | using LispEngine.Evaluation;
6 |
7 | namespace LispEngine.Core
8 | {
9 | class Set : AbstractFExpression
10 | {
11 | public static readonly FExpression Instance = new Set();
12 |
13 | public override Continuation Evaluate(Continuation c, LexicalEnvironment env, Datum args)
14 | {
15 | var argList = args.ToArray();
16 | if (argList.Length != 2)
17 | throw c.error("Expected 2 arguments: (set! ). Got {0} instead", argList.Length);
18 | var name = argList[0].CastSymbol();
19 | var expression = argList[1];
20 | c = c.PushTask(s =>
21 | {
22 | env.Set(name, s.Result);
23 | return s;
24 | }, "set! '{0}'", name);
25 | return c.Evaluate(env, expression);
26 | }
27 |
28 | public override string ToString()
29 | {
30 | return ",set!";
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/UnaryFunction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | abstract class UnaryFunction : Function
11 | {
12 | public static Datum GetSingle(Datum args)
13 | {
14 | var argArray = args.ToArray();
15 | if (argArray.Length != 1)
16 | throw DatumHelpers.error("Expected a single argument. Got {0}", argArray.Length);
17 | return argArray[0];
18 |
19 | }
20 | public Datum Evaluate(Datum args)
21 | {
22 | return eval(GetSingle(args));
23 | }
24 |
25 | protected abstract Datum eval(Datum arg);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Core/VectorFunctions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Datums;
6 | using LispEngine.Evaluation;
7 |
8 | namespace LispEngine.Core
9 | {
10 | class VectorFunctions : DatumHelpers
11 | {
12 | private static readonly Datum zero = DatumHelpers.atom(0);
13 | class MakeVector : Function
14 | {
15 | public Datum Evaluate(Datum args)
16 | {
17 | var argArray = args.ToArray();
18 | if (argArray.Length != 1 && argArray.Length != 2)
19 | throw error("1 or 2 arguments for make-vector");
20 | var size = argArray[0].CastInt();
21 | var initial = argArray.Length == 1 ? zero : argArray[1];
22 | var array = new Datum[size];
23 | for (int i = 0; i < size; ++i)
24 | array[i] = initial;
25 | return vector(array);
26 | }
27 |
28 | public override string ToString()
29 | {
30 | return ",make-vector";
31 | }
32 | }
33 |
34 | class VectorConstructor : Function
35 | {
36 | public Datum Evaluate(Datum args)
37 | {
38 | var argArray = args.ToArray();
39 | return vector(argArray);
40 | }
41 |
42 | public override string ToString()
43 | {
44 | return ",vector";
45 | }
46 | }
47 |
48 | private static Datum isVector(Datum d)
49 | {
50 | return (d is Vector).ToAtom();
51 | }
52 |
53 | // Seems crazy that this method does exist somewhere
54 | // in .Net already... Perhaps it does. I could not find it.
55 | private static T[] CreateCopy(T[] elements)
56 | {
57 | var copy = new T[elements.Length];
58 | Array.Copy(elements, copy, elements.Length);
59 | return copy;
60 | }
61 |
62 | private static Datum vectorSet(Datum v, Datum index, Datum value)
63 | {
64 | return castVector(v).Elements[index.CastInt()] = value;
65 | }
66 |
67 | private static Datum vectorCopy(Datum d)
68 | {
69 | return vector(CreateCopy(castVector(d).Elements));
70 | }
71 |
72 | private static Datum vectorLength(Datum d)
73 | {
74 | return castVector(d).Elements.Length.ToAtom();
75 | }
76 |
77 | private static Datum vectorRef(Datum d, Datum index)
78 | {
79 | return castVector(d).Elements[index.CastInt()];
80 | }
81 |
82 | public static LexicalEnvironment AddTo(LexicalEnvironment env)
83 | {
84 | env.Define("make-vector", new MakeVector().ToStack());
85 | env.Define("vector", new VectorConstructor().ToStack());
86 | env.Define("vector?", DelegateFunctions.MakeDatumFunction(isVector, ",vector?"));
87 | env.Define("vector-copy", DelegateFunctions.MakeDatumFunction(vectorCopy, ",vector-copy"));
88 | env.Define("vector-set!", DelegateFunctions.MakeDatumFunction(vectorSet, ",vector-set!"));
89 | env.Define("vector-length", DelegateFunctions.MakeDatumFunction(vectorLength, ",vector-length"));
90 | env.Define("vector-ref", DelegateFunctions.MakeDatumFunction(vectorRef, ",vector-ref"));
91 | return env;
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Datums/AbstractVisitor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Evaluation;
6 |
7 | namespace LispEngine.Datums
8 | {
9 | abstract class AbstractVisitor : DatumVisitor
10 | {
11 | public abstract T defaultCase(Datum d);
12 |
13 | public virtual T visit(Pair p)
14 | {
15 | return defaultCase(p);
16 | }
17 |
18 | public virtual T visit(Atom a)
19 | {
20 | return defaultCase(a);
21 | }
22 |
23 | public virtual T visit(Symbol s)
24 | {
25 | return defaultCase(s);
26 | }
27 |
28 | public virtual T visit(StackFunction s)
29 | {
30 | return defaultCase(s);
31 | }
32 |
33 | public virtual T visit(FExpression s)
34 | {
35 | return defaultCase(s);
36 | }
37 |
38 | public virtual T visit(Null n)
39 | {
40 | return defaultCase(n);
41 | }
42 |
43 | public virtual T visit(Vector v)
44 | {
45 | return defaultCase(v);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Datums/Atom.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.CodeDom;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using Microsoft.CSharp;
9 |
10 | namespace LispEngine.Datums
11 | {
12 | public sealed class Atom : Datum
13 | {
14 | private readonly object value;
15 |
16 | public Atom(object value)
17 | {
18 | this.value = value;
19 | }
20 |
21 | public object Value
22 | {
23 | [DebuggerStepThrough]
24 | get { return value; }
25 | }
26 |
27 | public bool Equals(Atom other)
28 | {
29 | if (ReferenceEquals(null, other)) return false;
30 | if (ReferenceEquals(this, other)) return true;
31 | return Equals(other.value, value);
32 | }
33 |
34 | public override bool Equals(object obj)
35 | {
36 | if (ReferenceEquals(null, obj)) return false;
37 | if (ReferenceEquals(this, obj)) return true;
38 | if (obj.GetType() != typeof (Atom)) return false;
39 | return Equals((Atom) obj);
40 | }
41 |
42 | public override int GetHashCode()
43 | {
44 | return (value != null ? value.GetHashCode() : 0);
45 | }
46 |
47 | public T accept(DatumVisitor visitor)
48 | {
49 | return visitor.visit(this);
50 | }
51 |
52 | public static bool operator ==(Atom left, Atom right)
53 | {
54 | return Equals(left, right);
55 | }
56 |
57 | public static bool operator !=(Atom left, Atom right)
58 | {
59 | return !Equals(left, right);
60 | }
61 |
62 | // http://stackoverflow.com/questions/323640/can-i-convert-a-c-sharp-string-value-to-an-escaped-string-literal
63 | // Seems the simplest approach, although I have no idea how performant it is.
64 | private static readonly CSharpCodeProvider provider = new CSharpCodeProvider();
65 | private static string ToLiteral(string input)
66 | {
67 | var writer = new StringWriter();
68 | provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, null);
69 | return writer.GetStringBuilder().ToString();
70 | }
71 |
72 | public override string ToString()
73 | {
74 | if (true.Equals(value))
75 | return "#t";
76 | if (false.Equals(value))
77 | return "#f";
78 | var s = value as string;
79 | if (s != null)
80 | return ToLiteral(s);
81 | return string.Format("{0}", value);
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Datums/Datum.cs:
--------------------------------------------------------------------------------
1 | namespace LispEngine.Datums
2 | {
3 | public interface Datum
4 | {
5 | T accept(DatumVisitor visitor);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Datums/DatumExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Evaluation;
6 |
7 | namespace LispEngine.Datums
8 | {
9 | public static class DatumExtensions
10 | {
11 | public static IEnumerable Enumerate(this Datum list)
12 | {
13 | return DatumHelpers.enumerate(list);
14 | }
15 |
16 | public static Datum[] ToArray(this Datum list)
17 | {
18 | return list.Enumerate().ToArray();
19 | }
20 |
21 | public static Datum ToList(this IEnumerable datums)
22 | {
23 | return DatumHelpers.compound(datums.ToArray());
24 | }
25 |
26 | public static Datum ToAtom(this object o)
27 | {
28 | return DatumHelpers.atom(o);
29 | }
30 |
31 | public static Symbol ToSymbol(this string identifier)
32 | {
33 | return DatumHelpers.symbol(identifier);
34 | }
35 |
36 | public static Symbol CastSymbol(this Datum d)
37 | {
38 | return DatumHelpers.castSymbol(d);
39 | }
40 |
41 | public static string CastString(this Datum d)
42 | {
43 | return DatumHelpers.castString(d);
44 | }
45 |
46 | public static int CastInt(this Datum d)
47 | {
48 | return DatumHelpers.castInt(d);
49 | }
50 |
51 | public static object CastObject(this Datum d)
52 | {
53 | return DatumHelpers.castObject(d);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Lisp/LispEngine/Datums/DatumHelpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using LispEngine.Evaluation;
6 |
7 | namespace LispEngine.Datums
8 | {
9 | public class DatumHelpers
10 | {
11 | public static readonly Datum nil = Null.Instance;
12 |
13 | public static readonly Datum quote = symbol("quote");
14 | public static readonly Datum quasiquote = symbol("quasiquote");
15 | public static readonly Datum unquote = symbol("unquote");
16 | public static readonly Datum unquoteSplicing = symbol("unquote-splicing");
17 | public static readonly Datum dot = symbol("dot");
18 | public static readonly Datum slash = symbol("slash");
19 |
20 | public const string quoteAbbreviation = "'";
21 | public const string quasiquoteAbbreviation = "`";
22 | public const string splicingAbbreviation = ",@";
23 | public const string unquoteAbbreviation = ",";
24 |
25 | public static Datum isQuote(string abbreviation)
26 | {
27 | if (abbreviation == unquoteAbbreviation)
28 | return unquote;
29 | if (abbreviation == quasiquoteAbbreviation)
30 | return quasiquote;
31 | if (abbreviation == splicingAbbreviation)
32 | return unquoteSplicing;
33 | if (abbreviation == quoteAbbreviation)
34 | return quote;
35 | return null;
36 | }
37 |
38 | public static string isQuote(Datum d)
39 | {
40 | if (d.Equals(quote))
41 | return quoteAbbreviation;
42 | if (d.Equals(quasiquote))
43 | return quasiquoteAbbreviation;
44 | if (d.Equals(unquoteSplicing))
45 | return splicingAbbreviation;
46 | if (d.Equals(unquote))
47 | return unquoteAbbreviation;
48 | return null;
49 | }
50 |
51 | public static Exception error(string msg, params object[] args)
52 | {
53 | return new Exception(string.Format(msg, args));
54 | }
55 |
56 | public static Symbol castSymbol(Datum dt)
57 | {
58 | var symbol = dt as Symbol;
59 | if (symbol == null)
60 | throw new Exception(String.Format("'{0}' is not a symbol", dt));
61 | return symbol;
62 | }
63 |
64 | public static Symbol symbol(string identifier)
65 | {
66 | return Symbol.GetSymbol(identifier);
67 | }
68 |
69 | public static Pair cons(Datum first, Datum second)
70 | {
71 | return new Pair(first, second);
72 | }
73 |
74 | public static Pair setCar(Datum first, Datum val)
75 | {
76 | var p = castPair(first);
77 | p.First = val;
78 | return p;
79 | }
80 |
81 | public static Pair setCdr(Datum first, Datum val)
82 | {
83 | var p = castPair(first);
84 | p.Second = val;
85 | return p;
86 | }
87 |
88 | public static Datum atomList(params T[] e)
89 | {
90 | return compound(e.AsEnumerable().Select(atom).ToArray());
91 | }
92 |
93 | public static Datum compound(params Datum[] e)
94 | {
95 | var list = new List(e);
96 | list.Reverse();
97 | return list.Aggregate(nil, (current, l) => cons(l, current));
98 | }
99 |
100 | public static Vector vector(params Datum[] elements)
101 | {
102 | return new Vector(elements);
103 | }
104 |
105 | public static Atom atom(T value)
106 | {
107 | return new Atom(value);
108 | }
109 |
110 | private static Pair castPair(Datum d)
111 | {
112 | var pair = d as Pair;
113 | if (pair == null)
114 | throw error("'{0}' is not a pair", d);
115 | return pair;
116 | }
117 |
118 | public static Vector castVector(Datum d)
119 | {
120 | var v = d as Vector;
121 | if (v == null)
122 | throw error("Expected '{0}' to be a vector", d);
123 | return v;
124 | }
125 |
126 | public static Datum car(Datum d)
127 | {
128 | return castPair(d).First;
129 | }
130 |
131 | public static IEnumerable enumerate(Datum list)
132 | {
133 | var next = list;
134 | while(next != nil)
135 | {
136 | var pair = next as Pair;
137 | if(pair == null)
138 | throw new Exception("Not a list");
139 | next = pair.Second;
140 | yield return pair.First;
141 | }
142 | }
143 |
144 | public static object castObject(Datum d)
145 | {
146 | var a = d as Atom;
147 | if (a == null)
148 | throw error("Expected '{0}' to be an atom, but got '{1}' instead", d, d.GetType().Name);
149 | return a.Value;
150 | }
151 |
152 | public static int castInt(Datum d)
153 | {
154 | var value = castObject(d);
155 | return (int) value;
156 | }
157 |
158 | public static string castString(Datum datum)
159 | {
160 | return (string)castObject(datum);
161 | }
162 |
163 | public static IEnumerable