├── IMPLEMENTATION-NOTES.md
├── LICENSE
├── README.md
├── arith.cs
├── examples
├── eval-eval-fib15.l
├── eval-fib15.l
├── fact100.l
├── fib15.l
├── interp_in_thread.cs
└── qsort.l
├── lisp.cs
└── lisp.csproj
/IMPLEMENTATION-NOTES.md:
--------------------------------------------------------------------------------
1 | # Implementation Notes
2 |
3 |
4 |
5 | ## 1. Overview
6 |
7 | The Lisp implementation of [lisp.cs](lisp.cs) is a translation of lisp.dart
8 | at [lisp-in-dart](https://github.com/nukata/lisp-in-dart) into C# 7.
9 | Below is an example of running lisp.cs with Mono 6.4.0 on macOS 10.14.6.
10 |
11 | ```
12 | $ csc -r:System.Numerics.dll arith.cs lisp.cs
13 | Microsoft (R) Visual C# Compiler version 3.3.1-beta4-19462-11 (66a912c9)
14 | Copyright (C) Microsoft Corporation. All rights reserved.
15 |
16 | $ mono lisp.exe
17 | > (+ 5 6)
18 | 11
19 | > `(a b ,(car '(c d)))
20 | (a b c)
21 | > (let ((x '(a b c d)))
22 | (setcar (cddr x) x)
23 | x)
24 | (a b (a b (a ...) d) d)
25 | > (princ "\t789\n")
26 | 789
27 | "\t789\n"
28 | > (dump)
29 | (dotimes dolist while nconc last nreverse _nreverse assoc assq member memq listp
30 | or mapcar and append _append letrec let when if equal /= <= >= > setcdr setcar
31 | null = identity print consp not cdddr cddar cdadr cdaar caddr cadar caadr caaar
32 | cddr cdar cadr caar defun defmacro *version* dump exit apply symbol-name intern
33 | make-symbol gensym *gensym-counter* terpri princ prin1 truncate / - * + mod % <
34 | eql numberp stringp length rplacd rplaca list eq atom cons cdr car t)
35 | > *version*
36 | (2.0 "C# 7" "Nukata Lisp")
37 | > let
38 | #) '#0:0:vars) (progn (setq #0:1:vals #)
42 | '#0:1:vals) (cons (cons 'lambda (cons (#0:0:vars #1:0:args) #1:1:body)) (#0:1:v
43 | als #1:0:args)))> nil nil))>
44 | > (exit 0)
45 | $
46 | ```
47 |
48 | Some features of lisp.cs and lisp.dart are
49 |
50 | - It is basically a subset of Emacs Lisp.
51 | However, it is a Lisp-1 with static scoping.
52 | In short, it is a _Common Lisp-like Lisp-1_.
53 |
54 | - It makes proper tail calls always.
55 |
56 | - A quasi-quotation with backquote will be expanded when macros are expanded.
57 |
58 | - A circular list is printed with `...` finitely.
59 |
60 | - As an escape sequence within strings, you can use any of
61 | `\"`, `\\`, `\n`, `\r`, `\f`, `\b`, `\t`, `\v`.
62 |
63 | - `(dump)` returns a list of all global variables.
64 | The list does not include special forms such as `lambda` and `setq`
65 | since they are not variables.
66 |
67 | - `*version*` is a three-element list:
68 | the version number, the implementing language,
69 | and the name of implementation.
70 |
71 | - (`macro` _args_ _body_) is a special form that evaluates to a sort of
72 | anonymous function, or _macro expression_.
73 | The global environment will be used whenever (`macro` ...) evaluates.
74 | When you apply the resultant macro expression to a list of actual arguments,
75 | the arguments will not be evaluated and the result of the application
76 | will be evaluated again.
77 | Thus a variable bound to a macro expression works as a _macro_.
78 |
79 | - `defmacro` is a macro which binds a variable to a macro expression.
80 |
81 | - `defun` is a macro which binds a variable to a lambda expression.
82 |
83 | - `let` is a macro which applies a lambda expression to a list of initial
84 | values of variables.
85 |
86 | - Macros are _partially hygienic_.
87 | Free symbols within a macro expression will not be captured when the
88 | expression is applied (i.e., when the macro is expanded).
89 |
90 |
91 | The macro `let` is defined in the prelude as follows.
92 |
93 | ```Lisp
94 | (defmacro let (args &rest body)
95 | ((lambda (vars vals)
96 | (defun vars (x)
97 | (cond (x (cons (if (atom (car x))
98 | (car x)
99 | (caar x))
100 | (vars (cdr x))))))
101 | (defun vals (x)
102 | (cond (x (cons (if (atom (car x))
103 | nil
104 | (cadar x))
105 | (vals (cdr x))))))
106 | `((lambda ,(vars args) ,@body) ,@(vals args)))
107 | nil nil))
108 | ```
109 |
110 |
111 | Being _partially hygienic_, macros can avoid variable captures,
112 | provided that you always use the result of `(gensym)` for any symbol
113 | newly introduced to the expansion result.
114 | For example:
115 |
116 | ```Lisp
117 | (defmacro while (test &rest body)
118 | (let ((loop (gensym)))
119 | `(letrec ((,loop (lambda () (cond (,test ,@body (,loop))))))
120 | (,loop))))
121 | ```
122 |
123 | See [lisp-in-dart/IMPLEMENTATION-NOTES §5](https://github.com/nukata/lisp-in-dart/blob/master/IMPLEMENTATION-NOTES.md#5) for details.
124 |
125 | ----------------------------------------
126 |
127 | **Note:**
128 | I believe partially hygienic macros have ideal usefulness.
129 | If you like, you can define
130 | [anaphoric macros (Japanese page)](http://www.asahi-net.or.jp/~kc7k-nd/onlispjhtml/anaphoricMacros.html)
131 | by introducing a symbol (`it` in the following example) to the
132 | expansion result intentionally without `(gensym)`.
133 |
134 | ```
135 | > (defmacro aif (test then else)
136 | `(let ((it ,test))
137 | (if it ,then ,else) ))
138 | aif
139 | > (aif (+ 7 8 9)
140 | (print it)
141 | (print "?"))
142 | 24
143 | 24
144 | >
145 | ```
146 |
147 | ----------------------------------------
148 |
149 |
150 |
151 | ## 2. Internal Data Representation
152 |
153 | To represent data of the implemented language (Lisp), native types of the
154 | implementing language (C#) are used as they are, if possible.
155 | They are all treated as `object` uniformly.
156 |
157 |
158 | | Lisp Expression | Internal Representation |
159 | |:------------------------------------|:---------------------------------------|
160 | | numbers `1`, `2.3` | `int`, `double`, `BigInteger` |
161 | | strings `"abc"`, `"hello!\n"` | `string` |
162 | | `t` | `Sym` (user-defined) |
163 | | `nil` | `null` |
164 | | symbols `x`, `+` | `Sym` (user-defined) |
165 | | keywords `lambda`, `cond` | `Keyword` (derived from `Sym`) |
166 | | lists `(x 1 "2")`, `(y . 3)` | `Cell` (user-defined) |
167 |
168 | Below is the definition of the `Cell` class.
169 |
170 | ```CS
171 | /// Cons cell
172 | public sealed class Cell {
173 | /// Head part of the cons cell
174 | public object Car;
175 | /// Tail part of the cons cell
176 | public object Cdr;
177 |
178 | /// Construct a cons cell with its head and tail.
179 | public Cell(object car, object cdr) {
180 | Car = car;
181 | Cdr = cdr;
182 | }
183 |
184 | /// Make a simple string representation.
185 | /// Do not invoke this for any circular list.
186 | public override string ToString() =>
187 | $"({Car ?? "null"} . {Cdr ?? "null"})";
188 |
189 | /// Length as a list
190 | public int Length => FoldL(0, this, (i, e) => i + 1);
191 | }
192 | ```
193 |
194 |
195 | Below is the definition of the `Sym` class.
196 |
197 | ```CS
198 | /// Lisp symbol
199 | public class Sym {
200 | /// The symbol's name
201 | public string Name { get; }
202 |
203 | /// Construct a symbol that is not interned.
204 | public Sym(string name) {
205 | Name = name;
206 | }
207 |
208 | /// Return the symbol's name
209 | public override string ToString() => Name;
210 | /// Return the hashcode of the symbol's name
211 | public override int GetHashCode() => Name.GetHashCode();
212 |
213 | /// Table of interned symbols
214 | protected static readonly Dictionary Table =
215 | new Dictionary();
216 |
217 | /// Return an interned symbol for the name.
218 | /// If the name is not interned yet, such a symbol
219 | /// will be constructed with .
220 | protected static Sym New(string name, Func make) {
221 | lock (Table) {
222 | if (! Table.TryGetValue(name, out Sym result)) {
223 | result = make(name);
224 | Table[name] = result;
225 | }
226 | return result;
227 | }
228 | }
229 |
230 | /// Construct an interned symbol.
231 | public static Sym New(string name) => New(name, s => new Sym(s));
232 |
233 | /// Is it interned?
234 | public bool IsInterned {
235 | get {
236 | lock (Table) {
237 | return Table.TryGetValue(Name, out Sym s) &&
238 | Object.ReferenceEquals(this, s);
239 | }
240 | }
241 | }
242 | }
243 | ```
244 |
245 | Keywords of Lisp are defined as follows.
246 |
247 | ```CS
248 | // Expression keyword
249 | sealed class Keyword: Sym {
250 | Keyword(string name): base(name) {}
251 | internal static new Sym New(string name)
252 | => New(name, s => new Keyword(s));
253 | }
254 |
255 | static readonly Sym CondSym = Keyword.New("cond");
256 | static readonly Sym LambdaSym = Keyword.New("lambda");
257 | static readonly Sym MacroSym = Keyword.New("macro");
258 | static readonly Sym PrognSym = Keyword.New("progn");
259 | static readonly Sym QuasiquoteSym = Keyword.New("quasiquote");
260 | static readonly Sym QuoteSym = Keyword.New("quote");
261 | static readonly Sym SetqSym = Keyword.New("setq");
262 | ```
263 |
264 | Now, if you call `Sym.New("cond")` in `ReadToken()`,
265 | you will get `CondSym`, an instance of the `Keyword` class.
266 |
267 |
268 |
269 | ## 3. Implementations of Lisp functions
270 |
271 | The `Interp` class implements the core of the Lisp interpreter.
272 | It has a map for global variables and standard out for built-in functions.
273 |
274 | ```CS
275 | /// Core of the Lisp interpreter
276 | public class Interp {
277 | /// Table of the global values of symbols
278 | protected readonly Dictionary Globals =
279 | new Dictionary();
280 |
281 | /// Standard out
282 | public TextWriter COut { get; set; } = Console.Out;
283 | ```
284 |
285 | Each built-in function is defined with the `Def` method below.
286 | The `carity` argument takes the arity of the function to be defined.
287 | If the function has `&rest`, the `carity`
288 | takes `-(`_number of fixed arguments_ ` + 1)`.
289 |
290 | ```CS
291 | /// Define a built-in function by a name, an arity,
292 | /// and a body.
293 | public void Def(string name, int carity, BuiltInFuncBody body) {
294 | Globals[Sym.New(name)] = new BuiltInFunc(name, carity, body);
295 | }
296 | ```
297 |
298 | Below is an excerpt of the constructor of `Interp`.
299 | It shows the implementation of five elementary functions of Lisp.
300 |
301 | ```CS
302 | /// Set each built-in function/variable as the global value
303 | /// of symbol.
304 | public Interp() {
305 | Globals[TSym] = TSym;
306 | Def("car", 1, a => (a[0] as Cell)?.Car);
307 | Def("cdr", 1, a => (a[0] as Cell)?.Cdr);
308 | Def("cons", 2, a => new Cell(a[0], a[1]));
309 | Def("atom", 1, a => (a[0] is Cell) ? null : TSym);
310 | Def("eq", 2, a => (a[0] == a[1]) ? TSym : null);
311 | ```
312 |
313 | The standard out `COut` is used as follows:
314 |
315 | ```CS
316 | Def("prin1", 1, a => {
317 | COut.Write(Str(a[0], true)); return a[0];
318 | });
319 | Def("princ", 1, a => {
320 | COut.Write(Str(a[0], false)); return a[0];
321 | });
322 | Def("terpri", 0, a => {
323 | COut.WriteLine(); return TSym;
324 | });
325 | ```
326 |
327 | The function `dump` takes no arguments and returns a list of all global
328 | variables.
329 | Internally it reads the keys from `Globals` and constructs a list of them.
330 |
331 | ```CS
332 | Def("dump", 0, a =>
333 | Globals.Keys.Aggregate((Cell) null, (x, y) => new Cell(y, x)));
334 | ```
335 |
336 | For an example of running `(dump)`, see [§1](#1).
337 |
338 |
339 | Several functions and macros of Lisp are defined in the initialization script
340 | `Prelude`, which runs in the task of `MakeInterp`.
341 |
342 | ```CS
343 | /// Make a Lisp interpreter initialized with Prelude.
344 | public static async Task MakeInterp() {
345 | var interp = new Interp();
346 | await Run(interp, new StringReader(Prelude));
347 | return interp;
348 | }
349 | ```
350 |
351 | Below is the head of `Prelude`.
352 |
353 | ```CS
354 | /// Lisp initialization script
355 | public static readonly string Prelude = @"
356 | (setq defmacro
357 | (macro (name args &rest body)
358 | `(progn (setq ,name (macro ,args ,@body))
359 | ',name)))
360 |
361 | (defmacro defun (name args &rest body)
362 | `(progn (setq ,name (lambda ,args ,@body))
363 | ',name))
364 |
365 | (defun caar (x) (car (car x)))
366 | (defun cadr (x) (car (cdr x)))
367 | (defun cdar (x) (cdr (car x)))
368 | (defun cddr (x) (cdr (cdr x)))
369 | ```
370 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 OKI Software Co., Ltd.
2 | Copyright (c) 2018 SUZUKI Hisao
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a
5 | copy of this software and associated documentation files (the "Software"),
6 | to deal in the Software without restriction, including without limitation
7 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | and/or sell copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in
12 | all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lisp in C# 7
2 |
3 | This is a Lisp interpreter compatible with
4 | [lisp-in-dart](https://github.com/nukata/lisp-in-dart)
5 | ~~except for numeric types: all numbers are `double` in C#~~.
6 | I wrote it in C# 6 and presented it under the MIT License at
7 | (broken link)
8 | until the spring of 2017 (H29).
9 | I slightly modified it to match C# 7 in 2018 (H30).
10 |
11 | Now in 2019 (R1),
12 | I implemented a mixed mode arithmetic of `int`, `double` and `BigInteger`
13 | in the same way as
14 | [little-scheme-in-cs](https://github.com/nukata/little-scheme-in-cs).
15 |
16 |
17 |
18 | The same as lisp-in-dart, [lisp-in-go](https://github.com/nukata/lisp-in-go)
19 | and [lisp-in-typescript](https://github.com/nukata/lisp-in-typescript),
20 | this is a Lisp-1 with tail call optimization
21 | and partially hygienic macros but being a subset of Common Lisp
22 | in a loose meaning.
23 | It is easy to write a nontrivial script which runs both in this and in
24 | Common Lisp (and also in Emacs Lisp).
25 | Examples are found in the [`examples`](examples) folder.
26 |
27 | See [IMPLEMENTATION-NOTES.md](IMPLEMENTATION-NOTES.md) for the implementation.
28 |
29 |
30 | ## How to run
31 |
32 | With [Mono](https://www.mono-project.com) 6.12.0:
33 |
34 | ```
35 | $ csc -o -r:System.Numerics.dll lisp.cs arith.cs
36 | ....
37 | $ mono lisp.exe
38 | > (+ 5 6)
39 | 11
40 | > (exit 0)
41 | $
42 | ```
43 |
44 | With [.NET](https://github.com/dotnet/core) 6.0:
45 |
46 | ```
47 | $ dotnet build -c Release
48 | ....
49 | $ ./bin/Release/net6.0/lisp
50 | > (+ 5 6)
51 | 11
52 | > (exit 0)
53 | $
54 | ```
55 |
56 | You can give it a file name of your Lisp script.
57 | If you put a "`-`" after the file name, it will
58 | begin an interactive session after running the file.
59 |
60 | ```
61 | $ cat examples/fib15.l
62 | (defun fib (n)
63 | (if (< n 2)
64 | 1
65 | (+ (fib (- n 1))
66 | (fib (- n 2)))))
67 | (print (fib 15))
68 | $ mono lisp.exe examples/fib15.l -
69 | 987
70 | > (fib 0)
71 | 1
72 | > (fib 15)
73 | 987
74 | > (fib 16)
75 | 1597
76 | > (exit 0)
77 | $
78 | ```
79 |
80 |
81 | ## Examples
82 |
83 | There are five files ending with `.l` under the `examples` folder.
84 | These run also in Emacs Lisp and Common Lisp.
85 |
86 | - [`qsort.l`](examples/qsort.l)
87 | performs a quick sort.
88 |
89 | ```
90 | $ mono lisp.exe examples/qsort.l
91 | (1 1 2 3 3 4 5 5 5 6 7 8 9 9 9)
92 | $
93 | ```
94 |
95 | ```
96 | $ emacs -batch -l examples/qsort.l
97 |
98 | (1 1 2 3 3 4 5 5 5 6 7 8 9 9 9)
99 | $
100 | ```
101 |
102 | ```
103 | $ clisp examples/qsort.l
104 |
105 | (1 1 2 3 3 4 5 5 5 6 7 8 9 9 9)
106 | $
107 | ```
108 |
109 |
110 | - [`fact100.l`](examples/fact100.l)
111 | calculates 100!.
112 |
113 | ```
114 | $ mono lisp.exe examples/fact100.l
115 | 93326215443944152681699238856266700490715968264381621468592963895217599993229915
116 | 608941463976156518286253697920827223758251185210916864000000000000000000000000
117 | $
118 | ```
119 |
120 | - [`fib15.l`](examples/fib15.l)
121 | calculates Fibonacci for 15.
122 |
123 | - [`eval-fib15.l`](examples/eval-fib15.l)
124 | calculates Fibonacci for 15 on a meta-circular Lisp evaluator.
125 |
126 | - [`eval-eval-fib15.l`](examples/eval-eval-fib15.l)
127 | calculates Fibonacci for 15 on a meta-circular Lisp evaluator
128 | on a meta-circular Lisp evaluator.
129 |
130 |
131 |
132 | There is one more example:
133 |
134 | - [`interp_in_thread.cs`](examples/interp_in_thread.cs)
135 | runs a Lisp interpreter in another thread.
136 | You can embed an interpreter within your application in the same way.
137 |
138 | ```
139 | $ cd examples
140 | $ csc -o -t:library -r:System.Numerics.dll ../lisp.cs ../arith.cs
141 | ....
142 | $ csc -r:lisp.dll interp_in_thread.cs
143 | ...
144 | $ mono interp_in_thread.exe
145 | => (1 . 2)
146 | Reiwa
147 | => Reiwa
148 | $
149 | ```
150 |
151 | The examples of `eval-fib15.l` and `eval-eval-fib15.l` are inspired
152 | by .
153 |
154 |
155 |
156 | ## Performance
157 |
158 | The following is a result of a benchmark test: the time to execute [`eval-eval-fib15.l`](examples/eval-eval-fib15.l).
159 | I used MacBook Pro (15-inch, 2016), 2.6GHz Core i7, 16GB 2133MHz LPDDR3, macOS Mojave 10.14.6.
160 |
161 | | Lisp | Compiled/Executed on | Executed in | Executes | Time [sec] | Rel. Speed |
162 | |:------------------------------------------------------------------------------|:----------------------------------------------------------------|:--------------|:------------|-----------:|------------:|
163 | | GNU CLISP 2.49 | | Mach-O | *.fas | 4.0 | 8.0
164 | | GNU Emacs Lisp 26.2 | | Mach-O | *.elc | 6.4 | 5.0
165 | | [l2lisp-in-java](https://github.com/nukata/l2lisp-in-java) 1.0.0-9.4 | [AdoptOpenJDK 11.0.5+10 HotSpot](http://adoptopenjdk.net/) | *.jar | source file | 12.8 | 2.5
166 | | GNU Emacs Lisp 26.2 | | Mach-O | source file | 16.2 | 2.0
167 | | [lisp-in-dart](https://github.com/nukata/lisp-in-dart) 1.0.1 | Dart VM 2.5.2 | snapshot | source file | 20.4 | 1.6
168 | | [lisp-in-dart](https://github.com/nukata/lisp-in-dart) 1.0.1 | /Dart VM 2.5.2 | source file | source file | 21.4 | 1.5
169 | | [lisp-in-typescript](https://github.com/nukata/lisp-in-typescript) 1.0.0-1.27 | TS 3.6.4/Node.js 12.12.0 | *.js (ESNEXT) | source file | 23.9 | 1.3
170 | | [lisp-in-typescript](https://github.com/nukata/lisp-in-typescript) 1.0.0-1.27 | TS 3.6.4/Node.js 12.12.0 | *.js (ES5) | source file | 25.4 | 1.3
171 | | lisp-in-cs 2.0.0 | .NET Core SDK 3.0.100 | *.dll (.NET) | source file | 31.8 | 1.0
172 | | [l2lisp-in-python](https://github.com/nukata/l2lisp-in-python) (7.2) | /PyPy 7.1.1(Python 3.6.1) | source file | source file | 37.8 | 0.8
173 | | [l2lisp-in-python](https://github.com/nukata/l2lisp-in-python) (7.2) | /PyPy 7.1.1(Python 2.7.13) | source file | source file | 41.7 | 0.8
174 | | lisp-in-cs 2.0.0 | Mono 6.4.0.198 | *.exe (.NET) | source file | 43.9 | 0.7
175 | | [lisp-in-go](https://github.com/nukata/lisp-in-go) 2.0.1 | Go 1.13.3/ | Mach-O | source file | 66.6 | 0.5
176 | | GNU CLISP 2.49 | | Mach-O | source file | 575.8 | 0.1
177 | | [l2lisp-in-python](https://github.com/nukata/l2lisp-in-python) (7.2) | /Python 3.7.4 | source file | source file | 1116.7 | 0.0
178 |
179 | I am sorry to say that the performance of this Lisp (lisp-in-cs) is rather mediocre.
180 | Note that l2lisp-in-java, lisp-in-dart, lisp-in-typescript, lisp-in-cs and lisp-in-go are all written in largely the same way; l2lisp-in-python is a little old-fashioned.
181 | Therefore, *roughly speaking*, their speeds shown above reflect those of their respective implementation languages: Java, Dart, TypeScript, C# and Go (and Python).
182 |
183 |
--------------------------------------------------------------------------------
/arith.cs:
--------------------------------------------------------------------------------
1 | // A little arithmetic in C# 7, R01.07.14/R01.10.27 by SUZUKI Hisao
2 | // derived from arith.cs at https://github.com/nukata/little-scheme-in-cs
3 | using System;
4 | using System.Numerics; // This implies -r:System.Numerics.dll
5 |
6 | // test: csc -d:TEST -o -r:System.Numerics.dll arith.cs && mono arith.exe
7 |
8 | namespace LittleArith {
9 |
10 | /// Mixed mode arithmetic of int, double and BigInteger
11 | /// For values of other types, the methods of
12 | /// this class will throw ArgumentException.
13 | public static class Arith {
14 |
15 | /// Convert a long into an int or a BigInteger.
16 | private static object Normalize(long x) {
17 | int i = (int) x;
18 | // NB: ((i == x) ? i : (BigInteger) x) will be a BigInteger.
19 | if (i == x) {
20 | return i;
21 | } else {
22 | return (BigInteger) x;
23 | }
24 | }
25 |
26 | /// Convert a BigInteger into an int if possible.
27 | private static object Normalize(BigInteger x) {
28 | try {
29 | return (int) x;
30 | } catch (OverflowException) {
31 | return x;
32 | }
33 | }
34 |
35 | /// Return true if x is a number.
36 | public static bool IsNumber(object x) {
37 | return x is int || x is double || x is BigInteger;
38 | }
39 |
40 | /// x + y
41 | public static object Add(object x, object y) {
42 | switch (x) {
43 | case int a:
44 | switch (y) {
45 | case int b:
46 | return Normalize((long) a + (long) b);
47 | case double b:
48 | return a + b;
49 | case BigInteger b:
50 | return Normalize(a + b);
51 | }
52 | break;
53 | case double a:
54 | switch (y) {
55 | case int b:
56 | return a + b;
57 | case double b:
58 | return a + b;
59 | case BigInteger b:
60 | return a + (double) b;
61 | }
62 | break;
63 | case BigInteger a:
64 | switch (y) {
65 | case int b:
66 | return Normalize(a + b);
67 | case double b:
68 | return (double) a + b;
69 | case BigInteger b:
70 | return Normalize(a + b);
71 | }
72 | break;
73 | }
74 | throw new ArgumentException($"{x}, {y}");
75 | }
76 |
77 | /// x - y
78 | public static object Subtract(object x, object y) {
79 | switch (x) {
80 | case int a:
81 | switch (y) {
82 | case int b:
83 | return Normalize((long) a - (long) b);
84 | case double b:
85 | return a - b;
86 | case BigInteger b:
87 | return Normalize(a - b);
88 | }
89 | break;
90 | case double a:
91 | switch (y) {
92 | case int b:
93 | return a - b;
94 | case double b:
95 | return a - b;
96 | case BigInteger b:
97 | return a - (double) b;
98 | }
99 | break;
100 | case BigInteger a:
101 | switch (y) {
102 | case int b:
103 | return Normalize(a - b);
104 | case double b:
105 | return (double) a - b;
106 | case BigInteger b:
107 | return Normalize(a - b);
108 | }
109 | break;
110 | }
111 | throw new ArgumentException($"{x}, {y}");
112 | }
113 |
114 | /// x * y
115 | public static object Multiply(object x, object y) {
116 | switch (x) {
117 | case int a:
118 | switch (y) {
119 | case int b:
120 | return Normalize((long) a * (long) b);
121 | case double b:
122 | return a * b;
123 | case BigInteger b:
124 | return Normalize(a * b);
125 | }
126 | break;
127 | case double a:
128 | switch (y) {
129 | case int b:
130 | return a * b;
131 | case double b:
132 | return a * b;
133 | case BigInteger b:
134 | return a * (double) b;
135 | }
136 | break;
137 | case BigInteger a:
138 | switch (y) {
139 | case int b:
140 | return Normalize(a * b);
141 | case double b:
142 | return (double) a * b;
143 | case BigInteger b:
144 | return Normalize(a * b);
145 | }
146 | break;
147 | }
148 | throw new ArgumentException($"{x}, {y}");
149 | }
150 |
151 | /// The rounded quotient of x and y.
152 | public static double RoundedQuotient(object x, object y) {
153 | switch (x) {
154 | case int a:
155 | switch (y) {
156 | case int b:
157 | return (double) a / (double) b;
158 | case double b:
159 | return (double) a / b;
160 | case BigInteger b:
161 | return (double) a / (double) b;
162 | }
163 | break;
164 | case double a:
165 | switch (y) {
166 | case int b:
167 | return a / (double) b;
168 | case double b:
169 | return a / b;
170 | case BigInteger b:
171 | return a / (double) b;
172 | }
173 | break;
174 | case BigInteger a:
175 | switch (y) {
176 | case int b:
177 | return (double) a / (double) b;
178 | case double b:
179 | return (double) a / b;
180 | case BigInteger b:
181 | return (double) a / (double) b;
182 | }
183 | break;
184 | }
185 | throw new ArgumentException($"{x}, {y}");
186 | }
187 |
188 | private static object Truncate(double a) {
189 | BigInteger b = new BigInteger(a);
190 | return Normalize(b);
191 | }
192 |
193 | /// The quotient of x and y.
194 | public static object Quotient(object x, object y) {
195 | switch (x) {
196 | case int a:
197 | switch (y) {
198 | case int b:
199 | return a / b;
200 | case double b:
201 | return Truncate((double) a / b);
202 | case BigInteger b:
203 | return Normalize(a / b);
204 | }
205 | break;
206 | case double a:
207 | switch (y) {
208 | case int b:
209 | return Truncate(a / (double) b);
210 | case double b:
211 | return Truncate(a / b);
212 | case BigInteger b:
213 | return Truncate(a / (double) b);
214 | }
215 | break;
216 | case BigInteger a:
217 | switch (y) {
218 | case int b:
219 | return Normalize(a / b);
220 | case double b:
221 | return Truncate((double) a / b);
222 | case BigInteger b:
223 | return Normalize(a / b);
224 | }
225 | break;
226 | }
227 | throw new ArgumentException($"{x}, {y}");
228 | }
229 |
230 | /// x % y
231 | public static object Remainder(object x, object y) {
232 | switch (x) {
233 | case int a:
234 | switch (y) {
235 | case int b:
236 | return a % b;
237 | case double b:
238 | return (double) a % b;
239 | case BigInteger b:
240 | return Normalize(a % b);
241 | }
242 | break;
243 | case double a:
244 | switch (y) {
245 | case int b:
246 | return a % (double) b;
247 | case double b:
248 | return a % b;
249 | case BigInteger b:
250 | return a % (double) b;
251 | }
252 | break;
253 | case BigInteger a:
254 | switch (y) {
255 | case int b:
256 | return Normalize(a % b);
257 | case double b:
258 | return (double) a % b;
259 | case BigInteger b:
260 | return Normalize(a % b);
261 | }
262 | break;
263 | }
264 | throw new ArgumentException($"{x}, {y}");
265 | }
266 |
267 | /// Compare x and y.
268 | /// -1, 0 or 1 as x is less than, equal to, or greater than y.
269 | ///
270 | public static int Compare(object x, object y) {
271 | switch (x) {
272 | case int a:
273 | switch (y) {
274 | case int b:
275 | return Math.Sign((long) a - (long) b);
276 | case double b:
277 | return Math.Sign(a - b);
278 | case BigInteger b:
279 | return (a - b).Sign;
280 | }
281 | break;
282 | case double a:
283 | switch (y) {
284 | case int b:
285 | return Math.Sign(a - b);
286 | case double b:
287 | return Math.Sign(a - b);
288 | case BigInteger b:
289 | return Math.Sign(a - (double) b);
290 | }
291 | break;
292 | case BigInteger a:
293 | switch (y) {
294 | case int b:
295 | return (a - b).Sign;
296 | case double b:
297 | return Math.Sign((double) a - b);
298 | case BigInteger b:
299 | return (a - b).Sign;
300 | }
301 | break;
302 | }
303 | throw new ArgumentException($"{x}, {y}");
304 | }
305 |
306 | /// Try to parse a string as an int, a BigInteger or a double.
307 | ///
308 | /// true if s was parsed successfully; otherwise, false.
309 | ///
310 | public static bool TryParse(string s, out object result) {
311 | if (int.TryParse(s, out int i)) {
312 | result = i;
313 | return true;
314 | } else if (BigInteger.TryParse(s, out BigInteger b)) {
315 | result = b;
316 | return true;
317 | } else if (double.TryParse(s, out double d)) {
318 | result = d;
319 | return true;
320 | } else {
321 | result = double.NaN;
322 | return false;
323 | }
324 | }
325 |
326 | #if TEST
327 | private static void Main() {
328 | object x = Normalize(3L);
329 | Console.WriteLine("{0}, {1}", x, x.GetType());
330 | // -> 3, System.Int32
331 | x = Normalize(555_000_555_000);
332 | Console.WriteLine("{0}, {1}", x, x.GetType());
333 | // -> 555000555000, System.Numerics.BigInteger
334 |
335 | x = Normalize((BigInteger) 3);
336 | Console.WriteLine("{0}, {1}", x, x.GetType());
337 | // -> 3, System.Int32
338 | x = Normalize((BigInteger) 555_000_555_000);
339 | Console.WriteLine("{0}, {1}", x, x.GetType());
340 | // -> 555000555000, System.Numerics.BigInteger
341 |
342 | try {
343 | x = Add("123", 4);
344 | } catch (ArgumentException ex) {
345 | Console.WriteLine(ex.Message); // -> 123, 4
346 | }
347 | x = Add(2, 7.89);
348 | Console.WriteLine("{0}, {1}", x, x.GetType());
349 | // -> 9.89, System.Double
350 | x = Add(2, (BigInteger) 12345678901234567890);
351 | Console.WriteLine("{0}, {1}", x, x.GetType());
352 | // -> 12345678901234567892, Sysmtem.Numerics.BigInteger
353 | x = Add((BigInteger) 12345678901234567890, 1.0);
354 | Console.WriteLine("{0}, {1}", x, x.GetType());
355 | // -> 1.23456789012346E+19, Sysmtem.Double
356 | x = Add(1_000_111_000, 2_000_222_000);
357 | Console.WriteLine("{0}, {1}", x, x.GetType());
358 | // -> 3000333000, Sysmtem.Numerics.BigInteger
359 | x = Add((BigInteger) 3_000_333_000, (BigInteger)(-2_000_222_000));
360 | Console.WriteLine("{0}, {1}", x, x.GetType());
361 | // -> 1000111000, Sysmtem.Int32
362 |
363 | x = Subtract((BigInteger)3_000_333_000, (BigInteger)2_000_222_000);
364 | Console.WriteLine("{0}, {1}", x, x.GetType());
365 | // -> 1000111000, Sysmtem.Int32
366 |
367 | x = Multiply(2.2, (BigInteger) 3);
368 | Console.WriteLine("{0}, {1}", x, x.GetType());
369 | // -> 6.6, Sysmtem.Double
370 |
371 | x = RoundedQuotient((BigInteger) 99, 3);
372 | Console.WriteLine("{0}, {1}", x, x.GetType());
373 | // -> 33, System.Double
374 |
375 | x = Quotient((BigInteger) 99, 3);
376 | Console.WriteLine("{0}, {1}", x, x.GetType());
377 | // -> 33, System.Int32
378 | x = Quotient(-99.9, 3);
379 | Console.WriteLine("{0}, {1}", x, x.GetType());
380 | // -> -33, System.Int32
381 |
382 | x = Remainder(101.0, 3);
383 | Console.WriteLine("{0}, {1}", x, x.GetType());
384 | // -> 2, System.Double
385 | x = Remainder((BigInteger) (-101), 3);
386 | Console.WriteLine("{0}, {1}", x, x.GetType());
387 | // -> -2, System.Int32
388 | x = Remainder(-101.0, 3);
389 | Console.WriteLine("{0}, {1}", x, x.GetType());
390 | // -> -2, System.Double
391 |
392 | Console.WriteLine("{0}", Compare(2.2, (BigInteger) 3));
393 | // -> -1
394 |
395 | bool b = TryParse("123", out x);
396 | Console.WriteLine("{0}, {1}", x, x.GetType());
397 | // -> 123, Sysmtem.Int32
398 | b = TryParse("123.4", out x);
399 | Console.WriteLine("{0}, {1}", x, x.GetType());
400 | // -> 123.4, Sysmtem.Double
401 | b = TryParse("-12345678901234567890", out x);
402 | Console.WriteLine("{0}, {1}", x, x.GetType());
403 | // -> -12345678901234567890, Sysmtem.Numerics.BigInteger
404 | }
405 | #endif
406 | }
407 | }
408 |
--------------------------------------------------------------------------------
/examples/eval-eval-fib15.l:
--------------------------------------------------------------------------------
1 | ;;; A circular Lisp interpreter in Common/Emacs/Nukata Lisp
2 | ;;; by SUZUKI Hisao on H28.8/10, H29.3/13
3 | ;;; cf. Zick Standard Lisp (https://github.com/zick/ZickStandardLisp)
4 |
5 | (progn
6 | ;; Expr: (EXPR environment (symbol...) expression...)
7 | ;; Subr: (SUBR . function)
8 | ;; Environment: ((symbol . value)...)
9 | ;; N.B. Expr has its own environment since this Lisp is lexically scoped.
10 |
11 | ;; Language-specific Hacks
12 | (setq funcall (lambda (f x) (f x))) ; for Nukata Lisp and this Lisp
13 | (setq max-lisp-eval-depth 10000) ; for Emacs Lisp
14 | (setq max-specpdl-size 7000) ; for Emacs Lisp
15 |
16 | ;; The global environment of this Lisp
17 | (setq global-env
18 | (list '(*version* . (1.2 "Lisp" "circlisp"))
19 | (cons 'car
20 | (cons 'SUBR (lambda (x) (car (car x)))))
21 | (cons 'cdr
22 | (cons 'SUBR (lambda (x) (cdr (car x)))))
23 | (cons 'cons
24 | (cons 'SUBR (lambda (x) (cons (car x) (cadr% x)))))
25 | (cons 'eq
26 | (cons 'SUBR (lambda (x) (eq (car x) (cadr% x)))))
27 | (cons 'atom
28 | (cons 'SUBR (lambda (x) (atom (car x)))))
29 | (cons 'rplaca
30 | (cons 'SUBR (lambda (x) (rplaca (car x) (cadr% x)))))
31 | (cons 'rplacd
32 | (cons 'SUBR (lambda (x) (rplacd (car x) (cadr% x)))))
33 | (cons 'list
34 | (cons 'SUBR (lambda (x) x)))
35 | (cons '+
36 | (cons 'SUBR (lambda (x) (+ (car x) (cadr% x)))))
37 | (cons '*
38 | (cons 'SUBR (lambda (x) (* (car x) (cadr% x)))))
39 | (cons '-
40 | (cons 'SUBR (lambda (x) (- (car x) (cadr% x)))))
41 | (cons 'truncate
42 | (cons 'SUBR (lambda (x) (truncate (car x) (cadr% x)))))
43 | (cons 'mod
44 | (cons 'SUBR (lambda (x) (mod (car x) (cadr% x)))))
45 | (cons '=
46 | (cons 'SUBR (lambda (x) (= (car x) (cadr% x)))))
47 | (cons '<
48 | (cons 'SUBR (lambda (x) (< (car x) (cadr% x)))))
49 | (cons 'print
50 | (cons 'SUBR (lambda (x) (print (car x)))))
51 | (cons 'apply
52 | (cons 'SUBR (lambda (x) (apply% (car x) (cadr% x)))))
53 | (cons 'eval
54 | (cons 'SUBR (lambda (x) (eval% (car x) global-env))))))
55 |
56 | (defun caar% (x) (car (car x)))
57 | (defun cadr% (x) (car (cdr x)))
58 | (defun cddr% (x) (cdr (cdr x)))
59 | (defun caddr% (x) (car (cdr (cdr x))))
60 | (defun cdddr% (x) (cdr (cdr (cdr x))))
61 | (defun cadddr% (x) (car (cdr (cdr (cdr x)))))
62 |
63 | (defun assq% (key alist) ; cf. Emacs/Nukata Lisp
64 | (if alist
65 | (if (eq key (caar% alist))
66 | (car alist)
67 | (assq% key (cdr alist)))
68 | nil))
69 |
70 | (defun pairlis% (keys data alist) ; cf. Common Lisp
71 | (if keys
72 | (cons (cons (car keys) (car data))
73 | (pairlis% (cdr keys) (cdr data) alist))
74 | alist))
75 |
76 | ;; Define symbol as value in the global environment.
77 | (defun global-def (sym val)
78 | (rplacd global-env
79 | (cons (car global-env)
80 | (cdr global-env)))
81 | (rplaca global-env
82 | (cons sym val)))
83 |
84 | (defun eval% (e env)
85 | (if (atom e)
86 | ((lambda (var)
87 | (if var
88 | (cdr var)
89 | e))
90 | (assq% e env))
91 | (if (eq (car e) 'quote) ; (quote e)
92 | (cadr% e)
93 | (if (eq (car e) 'if) ; (if e e e)
94 | (if (eval% (cadr% e) env)
95 | (eval% (caddr% e) env)
96 | (eval% (cadddr% e) env))
97 | (if (eq (car e) 'progn) ; (progn e...)
98 | (eval-progn (cdr e) env nil)
99 | (if (eq (car e) 'lambda) ; (lambda (v...) e...)
100 | (make-closure env (cdr e))
101 | (if (eq (car e) 'defun) ; (defun f (v...) e...)
102 | (global-def (cadr% e)
103 | (make-closure env (cddr% e)))
104 | (if (eq (car e) 'setq) ; (setq v e)
105 | ((lambda (var value)
106 | (if var
107 | (rplacd var value)
108 | (global-def (cadr% e) value))
109 | value)
110 | (assq% (cadr% e) env)
111 | (eval% (caddr% e) env))
112 | (apply% (eval% (car e) env) ; (f e...)
113 | (evlis (cdr e) env))))))))))
114 |
115 | ;; (make-closure env '((v...) e...)) => (EXPR env (v...) e...)
116 | (defun make-closure (env ve)
117 | (cons 'EXPR
118 | (cons env ve)))
119 |
120 | ;; (eval-progn '((+ 1 2) 3 (+ 4 5)) global-env nil) => 9
121 | (defun eval-progn (x env result)
122 | (if x
123 | (if (cdr x)
124 | (eval-progn (cdr x)
125 | env
126 | (eval% (car x) env))
127 | (eval% (car x) env))
128 | result))
129 |
130 | ;; (evlis '((+ 1 2) 3 (+ 4 5)) global-env) => (3 3 9)
131 | (defun evlis (x env)
132 | (if x
133 | (cons (eval% (car x) env)
134 | (evlis (cdr x) env))
135 | nil))
136 |
137 | (defun apply% (fun arg)
138 | (if (eq (car fun) 'EXPR) ; (EXPR env (v...) e...)
139 | (eval-progn (cdddr% fun)
140 | (pairlis% (caddr% fun)
141 | arg
142 | (cadr% fun))
143 | nil)
144 | (if (eq (car fun) 'SUBR) ; (SUBR . f)
145 | (funcall (cdr fun) arg)
146 | fun)))
147 |
148 | (defun global-eval (e)
149 | (eval% e global-env))
150 |
151 | (global-eval (quote
152 |
153 | ;; -- WRITE YOUR EXPRESSION HERE --
154 | ;;; A circular Lisp interpreter in Common/Emacs/Nukata Lisp
155 | ;;; by SUZUKI Hisao on H28.8/10, H29.3/13
156 | ;;; cf. Zick Standard Lisp (https://github.com/zick/ZickStandardLisp)
157 |
158 | (progn
159 | ;; Expr: (EXPR environment (symbol...) expression...)
160 | ;; Subr: (SUBR . function)
161 | ;; Environment: ((symbol . value)...)
162 | ;; N.B. Expr has its own environment since this Lisp is lexically scoped.
163 |
164 | ;; Language-specific Hacks
165 | (setq funcall (lambda (f x) (f x))) ; for Nukata Lisp and this Lisp
166 | (setq max-lisp-eval-depth 10000) ; for Emacs Lisp
167 | (setq max-specpdl-size 7000) ; for Emacs Lisp
168 |
169 | ;; The global environment of this Lisp
170 | (setq global-env
171 | (list '(*version* . (1.2 "Lisp" "circlisp"))
172 | (cons 'car
173 | (cons 'SUBR (lambda (x) (car (car x)))))
174 | (cons 'cdr
175 | (cons 'SUBR (lambda (x) (cdr (car x)))))
176 | (cons 'cons
177 | (cons 'SUBR (lambda (x) (cons (car x) (cadr% x)))))
178 | (cons 'eq
179 | (cons 'SUBR (lambda (x) (eq (car x) (cadr% x)))))
180 | (cons 'atom
181 | (cons 'SUBR (lambda (x) (atom (car x)))))
182 | (cons 'rplaca
183 | (cons 'SUBR (lambda (x) (rplaca (car x) (cadr% x)))))
184 | (cons 'rplacd
185 | (cons 'SUBR (lambda (x) (rplacd (car x) (cadr% x)))))
186 | (cons 'list
187 | (cons 'SUBR (lambda (x) x)))
188 | (cons '+
189 | (cons 'SUBR (lambda (x) (+ (car x) (cadr% x)))))
190 | (cons '*
191 | (cons 'SUBR (lambda (x) (* (car x) (cadr% x)))))
192 | (cons '-
193 | (cons 'SUBR (lambda (x) (- (car x) (cadr% x)))))
194 | (cons 'truncate
195 | (cons 'SUBR (lambda (x) (truncate (car x) (cadr% x)))))
196 | (cons 'mod
197 | (cons 'SUBR (lambda (x) (mod (car x) (cadr% x)))))
198 | (cons '=
199 | (cons 'SUBR (lambda (x) (= (car x) (cadr% x)))))
200 | (cons '<
201 | (cons 'SUBR (lambda (x) (< (car x) (cadr% x)))))
202 | (cons 'print
203 | (cons 'SUBR (lambda (x) (print (car x)))))
204 | (cons 'apply
205 | (cons 'SUBR (lambda (x) (apply% (car x) (cadr% x)))))
206 | (cons 'eval
207 | (cons 'SUBR (lambda (x) (eval% (car x) global-env))))))
208 |
209 | (defun caar% (x) (car (car x)))
210 | (defun cadr% (x) (car (cdr x)))
211 | (defun cddr% (x) (cdr (cdr x)))
212 | (defun caddr% (x) (car (cdr (cdr x))))
213 | (defun cdddr% (x) (cdr (cdr (cdr x))))
214 | (defun cadddr% (x) (car (cdr (cdr (cdr x)))))
215 |
216 | (defun assq% (key alist) ; cf. Emacs/Nukata Lisp
217 | (if alist
218 | (if (eq key (caar% alist))
219 | (car alist)
220 | (assq% key (cdr alist)))
221 | nil))
222 |
223 | (defun pairlis% (keys data alist) ; cf. Common Lisp
224 | (if keys
225 | (cons (cons (car keys) (car data))
226 | (pairlis% (cdr keys) (cdr data) alist))
227 | alist))
228 |
229 | ;; Define symbol as value in the global environment.
230 | (defun global-def (sym val)
231 | (rplacd global-env
232 | (cons (car global-env)
233 | (cdr global-env)))
234 | (rplaca global-env
235 | (cons sym val)))
236 |
237 | (defun eval% (e env)
238 | (if (atom e)
239 | ((lambda (var)
240 | (if var
241 | (cdr var)
242 | e))
243 | (assq% e env))
244 | (if (eq (car e) 'quote) ; (quote e)
245 | (cadr% e)
246 | (if (eq (car e) 'if) ; (if e e e)
247 | (if (eval% (cadr% e) env)
248 | (eval% (caddr% e) env)
249 | (eval% (cadddr% e) env))
250 | (if (eq (car e) 'progn) ; (progn e...)
251 | (eval-progn (cdr e) env nil)
252 | (if (eq (car e) 'lambda) ; (lambda (v...) e...)
253 | (make-closure env (cdr e))
254 | (if (eq (car e) 'defun) ; (defun f (v...) e...)
255 | (global-def (cadr% e)
256 | (make-closure env (cddr% e)))
257 | (if (eq (car e) 'setq) ; (setq v e)
258 | ((lambda (var value)
259 | (if var
260 | (rplacd var value)
261 | (global-def (cadr% e) value))
262 | value)
263 | (assq% (cadr% e) env)
264 | (eval% (caddr% e) env))
265 | (apply% (eval% (car e) env) ; (f e...)
266 | (evlis (cdr e) env))))))))))
267 |
268 | ;; (make-closure env '((v...) e...)) => (EXPR env (v...) e...)
269 | (defun make-closure (env ve)
270 | (cons 'EXPR
271 | (cons env ve)))
272 |
273 | ;; (eval-progn '((+ 1 2) 3 (+ 4 5)) global-env nil) => 9
274 | (defun eval-progn (x env result)
275 | (if x
276 | (if (cdr x)
277 | (eval-progn (cdr x)
278 | env
279 | (eval% (car x) env))
280 | (eval% (car x) env))
281 | result))
282 |
283 | ;; (evlis '((+ 1 2) 3 (+ 4 5)) global-env) => (3 3 9)
284 | (defun evlis (x env)
285 | (if x
286 | (cons (eval% (car x) env)
287 | (evlis (cdr x) env))
288 | nil))
289 |
290 | (defun apply% (fun arg)
291 | (if (eq (car fun) 'EXPR) ; (EXPR env (v...) e...)
292 | (eval-progn (cdddr% fun)
293 | (pairlis% (caddr% fun)
294 | arg
295 | (cadr% fun))
296 | nil)
297 | (if (eq (car fun) 'SUBR) ; (SUBR . f)
298 | (funcall (cdr fun) arg)
299 | fun)))
300 |
301 | (defun global-eval (e)
302 | (eval% e global-env))
303 |
304 | (global-eval (quote
305 |
306 | ;; -- WRITE YOUR EXPRESSION HERE --
307 | (progn
308 | (defun fib (n)
309 | (if (< n 2)
310 | 1
311 | (+ (fib (- n 1))
312 | (fib (- n 2)))))
313 | (print (fib 15)))
314 | ;; --------------------------------
315 | )))
316 | ;; --------------------------------
317 | )))
318 |
--------------------------------------------------------------------------------
/examples/eval-fib15.l:
--------------------------------------------------------------------------------
1 | ;;; A circular Lisp interpreter in Common/Emacs/Nukata Lisp
2 | ;;; by SUZUKI Hisao on H28.8/10, H29.3/13
3 | ;;; cf. Zick Standard Lisp (https://github.com/zick/ZickStandardLisp)
4 |
5 | (progn
6 | ;; Expr: (EXPR environment (symbol...) expression...)
7 | ;; Subr: (SUBR . function)
8 | ;; Environment: ((symbol . value)...)
9 | ;; N.B. Expr has its own environment since this Lisp is lexically scoped.
10 |
11 | ;; Language-specific Hacks
12 | (setq funcall (lambda (f x) (f x))) ; for Nukata Lisp and this Lisp
13 | (setq max-lisp-eval-depth 10000) ; for Emacs Lisp
14 | (setq max-specpdl-size 7000) ; for Emacs Lisp
15 |
16 | ;; The global environment of this Lisp
17 | (setq global-env
18 | (list '(*version* . (1.2 "Lisp" "circlisp"))
19 | (cons 'car
20 | (cons 'SUBR (lambda (x) (car (car x)))))
21 | (cons 'cdr
22 | (cons 'SUBR (lambda (x) (cdr (car x)))))
23 | (cons 'cons
24 | (cons 'SUBR (lambda (x) (cons (car x) (cadr% x)))))
25 | (cons 'eq
26 | (cons 'SUBR (lambda (x) (eq (car x) (cadr% x)))))
27 | (cons 'atom
28 | (cons 'SUBR (lambda (x) (atom (car x)))))
29 | (cons 'rplaca
30 | (cons 'SUBR (lambda (x) (rplaca (car x) (cadr% x)))))
31 | (cons 'rplacd
32 | (cons 'SUBR (lambda (x) (rplacd (car x) (cadr% x)))))
33 | (cons 'list
34 | (cons 'SUBR (lambda (x) x)))
35 | (cons '+
36 | (cons 'SUBR (lambda (x) (+ (car x) (cadr% x)))))
37 | (cons '*
38 | (cons 'SUBR (lambda (x) (* (car x) (cadr% x)))))
39 | (cons '-
40 | (cons 'SUBR (lambda (x) (- (car x) (cadr% x)))))
41 | (cons 'truncate
42 | (cons 'SUBR (lambda (x) (truncate (car x) (cadr% x)))))
43 | (cons 'mod
44 | (cons 'SUBR (lambda (x) (mod (car x) (cadr% x)))))
45 | (cons '=
46 | (cons 'SUBR (lambda (x) (= (car x) (cadr% x)))))
47 | (cons '<
48 | (cons 'SUBR (lambda (x) (< (car x) (cadr% x)))))
49 | (cons 'print
50 | (cons 'SUBR (lambda (x) (print (car x)))))
51 | (cons 'apply
52 | (cons 'SUBR (lambda (x) (apply% (car x) (cadr% x)))))
53 | (cons 'eval
54 | (cons 'SUBR (lambda (x) (eval% (car x) global-env))))))
55 |
56 | (defun caar% (x) (car (car x)))
57 | (defun cadr% (x) (car (cdr x)))
58 | (defun cddr% (x) (cdr (cdr x)))
59 | (defun caddr% (x) (car (cdr (cdr x))))
60 | (defun cdddr% (x) (cdr (cdr (cdr x))))
61 | (defun cadddr% (x) (car (cdr (cdr (cdr x)))))
62 |
63 | (defun assq% (key alist) ; cf. Emacs/Nukata Lisp
64 | (if alist
65 | (if (eq key (caar% alist))
66 | (car alist)
67 | (assq% key (cdr alist)))
68 | nil))
69 |
70 | (defun pairlis% (keys data alist) ; cf. Common Lisp
71 | (if keys
72 | (cons (cons (car keys) (car data))
73 | (pairlis% (cdr keys) (cdr data) alist))
74 | alist))
75 |
76 | ;; Define symbol as value in the global environment.
77 | (defun global-def (sym val)
78 | (rplacd global-env
79 | (cons (car global-env)
80 | (cdr global-env)))
81 | (rplaca global-env
82 | (cons sym val)))
83 |
84 | (defun eval% (e env)
85 | (if (atom e)
86 | ((lambda (var)
87 | (if var
88 | (cdr var)
89 | e))
90 | (assq% e env))
91 | (if (eq (car e) 'quote) ; (quote e)
92 | (cadr% e)
93 | (if (eq (car e) 'if) ; (if e e e)
94 | (if (eval% (cadr% e) env)
95 | (eval% (caddr% e) env)
96 | (eval% (cadddr% e) env))
97 | (if (eq (car e) 'progn) ; (progn e...)
98 | (eval-progn (cdr e) env nil)
99 | (if (eq (car e) 'lambda) ; (lambda (v...) e...)
100 | (make-closure env (cdr e))
101 | (if (eq (car e) 'defun) ; (defun f (v...) e...)
102 | (global-def (cadr% e)
103 | (make-closure env (cddr% e)))
104 | (if (eq (car e) 'setq) ; (setq v e)
105 | ((lambda (var value)
106 | (if var
107 | (rplacd var value)
108 | (global-def (cadr% e) value))
109 | value)
110 | (assq% (cadr% e) env)
111 | (eval% (caddr% e) env))
112 | (apply% (eval% (car e) env) ; (f e...)
113 | (evlis (cdr e) env))))))))))
114 |
115 | ;; (make-closure env '((v...) e...)) => (EXPR env (v...) e...)
116 | (defun make-closure (env ve)
117 | (cons 'EXPR
118 | (cons env ve)))
119 |
120 | ;; (eval-progn '((+ 1 2) 3 (+ 4 5)) global-env nil) => 9
121 | (defun eval-progn (x env result)
122 | (if x
123 | (if (cdr x)
124 | (eval-progn (cdr x)
125 | env
126 | (eval% (car x) env))
127 | (eval% (car x) env))
128 | result))
129 |
130 | ;; (evlis '((+ 1 2) 3 (+ 4 5)) global-env) => (3 3 9)
131 | (defun evlis (x env)
132 | (if x
133 | (cons (eval% (car x) env)
134 | (evlis (cdr x) env))
135 | nil))
136 |
137 | (defun apply% (fun arg)
138 | (if (eq (car fun) 'EXPR) ; (EXPR env (v...) e...)
139 | (eval-progn (cdddr% fun)
140 | (pairlis% (caddr% fun)
141 | arg
142 | (cadr% fun))
143 | nil)
144 | (if (eq (car fun) 'SUBR) ; (SUBR . f)
145 | (funcall (cdr fun) arg)
146 | fun)))
147 |
148 | (defun global-eval (e)
149 | (eval% e global-env))
150 |
151 | (global-eval (quote
152 |
153 | ;; -- WRITE YOUR EXPRESSION HERE --
154 | (progn
155 | (defun fib (n)
156 | (if (< n 2)
157 | 1
158 | (+ (fib (- n 1))
159 | (fib (- n 2)))))
160 | (print (fib 15)))
161 | ;; --------------------------------
162 | )))
163 |
--------------------------------------------------------------------------------
/examples/fact100.l:
--------------------------------------------------------------------------------
1 | (defun factorial (n)
2 | (if (= n 0)
3 | 1
4 | (* n (factorial (- n 1)))))
5 |
6 | (print (factorial 100))
7 |
--------------------------------------------------------------------------------
/examples/fib15.l:
--------------------------------------------------------------------------------
1 | (defun fib (n)
2 | (if (< n 2)
3 | 1
4 | (+ (fib (- n 1))
5 | (fib (- n 2)))))
6 | (print (fib 15))
7 |
--------------------------------------------------------------------------------
/examples/interp_in_thread.cs:
--------------------------------------------------------------------------------
1 | // An example of running Lisp in another thread
2 | using System;
3 | using System.Collections.Concurrent;
4 | using System.IO;
5 | using System.Text;
6 | using System.Threading;
7 |
8 | // csc -o -t:library -r:System.Numerics.dll ../lisp.cs ../arith.cs
9 | // csc -r:lisp.dll interp_in_thread.cs
10 | // mono interp_in_thread.exe
11 |
12 | // Expected output:
13 | // => (1 . 2)
14 | // Reiwa
15 | // => Reiwa
16 |
17 | public static class ThreadTest {
18 |
19 | // A simple substitute for Console.Out
20 | class SendOut: TextWriter {
21 | public BlockingCollection