├── LICENSE
├── Makefile
├── README
├── cl.cc
├── cl.h
├── fftest.cc
├── primops.cc
├── reader.cc
├── reader.h
├── rlstream.cc
├── rlstream.h
├── stopwatch.cc
├── stopwatch.h
└── testdata
/LICENSE:
--------------------------------------------------------------------------------
1 | Ciel - a Lisp-like language implemented in C++.
2 |
3 | Copyright (C) 2010 by Ron Garret, all rights reserved.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 2 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | # Makefile for Ciel
3 | #
4 | # NOTE: The -O2 optimized version of cl (called clo) dumps core on OS X.
5 | # Not sure why.
6 | #
7 |
8 | OS = $(shell sh -c 'uname')
9 |
10 | ifeq ($(OS), Linux)
11 | SHARED_LIBRARY_FLAGS = -shared -fPIC
12 | else ifeq ($(OS), Darwin)
13 | SHARED_LIBRARY_FLAGS = -bundle -undefined dynamic_lookup # go figure
14 | endif
15 |
16 | CC = g++ -m64 -Wall -rdynamic
17 | LIBS= -lcln -lgc -ldl -lreadline -ltermcap # or -lncurses, dealer choice
18 | OBJS = rlstream.o stopwatch.o reader.o primops.o
19 |
20 | cl: cl.cc cl.h ${OBJS}
21 | ${CC} cl.cc ${OBJS} -g -o cl ${LIBS}
22 |
23 | clo: cl.cc cl.h ${OBJS}
24 | ${CC} -O2 cl.cc ${OBJS} -o clo ${LIBS}
25 |
26 | primops.o: primops.cc cl.h
27 | ${CC} -O2 -c primops.cc
28 |
29 | reader.o: reader.cc reader.h cl.h rlstream.h
30 | ${CC} -O2 -c reader.cc
31 |
32 | rlstream.o: rlstream.cc rlstream.h
33 | ${CC} -O2 -c rlstream.cc -DUSE_GNU_READLINE
34 |
35 | stopwatch.o: stopwatch.cc stopwatch.h
36 | ${CC} -O2 -c stopwatch.cc
37 |
38 | fftest: fftest.cc cl.h
39 | ${CC} $(SHARED_LIBRARY_FLAGS) -o fftest fftest.cc
40 |
41 | test:
42 | time ./cl const string clString::name = "String";
25 |
26 | template<>
27 | void clString::print(ostream &os) { os << '"' << theThing << '"'; }
28 |
29 | ///////////////////////
30 | ///
31 | /// Characters
32 | ///
33 |
34 | template<> const string clChar::name = "Character";
35 |
36 | template<>
37 | void clChar::print(ostream &os) { os << "'" << theThing << "'"; }
38 |
39 | ///////////////////////
40 | ///
41 | /// Dictionaries
42 | ///
43 |
44 | template<>
45 | clObject& dictionary::ref(clObject& i) {
46 | gc_hash_map::iterator r = this->theMap.find(&i);
47 | if (r == this->theMap.end()) return nil;
48 | return *r->second;
49 | }
50 |
51 | template<>
52 | void dictionary::del(clObject& i) {
53 | gc_hash_map::iterator r = this->theMap.find(&i);
54 | if (r == this->theMap.end()) return;
55 | this->theMap.erase(r);
56 | }
57 |
58 | template<>
59 | void dictionary::setref(clObject &i, clObject &v) {
60 | this->theMap[&i] = &v;
61 | }
62 |
63 | template<>
64 | clObject& dictionary::size() {
65 | return *new clNumber(this->theMap.size());
66 | }
67 |
68 |
69 | /////////////////////////////////////
70 | //
71 | // Numbers
72 | //
73 |
74 | string type_of(cl_N n) {
75 | if (instanceof(n, cl_I_ring)) return "integer";
76 | else if (instanceof(n, cl_RA_ring)) return "rational";
77 | else if (instanceof(n, cl_R_ring)) return "float";
78 | else if (instanceof(n, cl_C_ring)) return "complex";
79 | else return "unknown";
80 | }
81 |
82 | //////////////////////////////
83 | ///
84 | /// Vectors
85 | ///
86 |
87 | int position(gcVector& v, clObject& o) {
88 | for (unsigned int i=0; itheVector.begin();
97 | gcVector::iterator end = this->theVector.end();
98 | if (i!=end) {
99 | s << **i;
100 | for (i++; i!=end; i++) s << ' ' << **i;
101 | }
102 | s << ']';
103 | }
104 |
105 |
106 | //////////////////////////
107 | ///
108 | /// Cons Cells
109 | ///
110 |
111 | consCell& cons(clObject &car, clObject &cdr) {
112 | consCell *c = new consCell();
113 | c->theCar = &car;
114 | c->theCdr = &cdr;
115 | return *c;
116 | }
117 |
118 | consCell& vec2list(gcVector& v, unsigned int n=0) {
119 | if (n==v.size()) return nil;
120 | else return cons(*v[n], vec2list(v,n+1));
121 | }
122 |
123 | void consCell::print(ostream &s) {
124 | s << '(' << car(this);
125 | clObject *o = this->theCdr;
126 | consCell *p;
127 | while((p = theCons(o)) && !null(p)) {
128 | s << ' ' << car(p);
129 | o = p->theCdr;
130 | }
131 | if (!null(o)) s << " . " << *o;
132 | s << ')';
133 | }
134 |
135 | clObject& consCell::operator[](int n) {
136 | return (n<=0) ? car(this) : theCons(cdr(this))[n-1];
137 | }
138 |
139 |
140 | int length(consCell& c) {
141 | return null(c) ? 0 : length(theCons(cdr(c)))+1;
142 | }
143 |
144 | int position(clObject& o, consCell& c, int i=0) {
145 | if (null(c)) return -1;
146 | return (&o == &(car(c)) ? i : position(o, theCons(cdr(c)), i+1));
147 | }
148 |
149 | gcVector& list2vec(consCell& c, gcVector& v = *new gcVector()) {
150 | // gcVector& v = *new gcVector();
151 | clObject* cp = &c;
152 | while (cp->typeOf()=="cons") {
153 | v.push_back(&car(cp));
154 | cp = &cdr(cp);
155 | }
156 | return v;
157 | }
158 |
159 |
160 | ////////////////////////////
161 | ///
162 | /// Error
163 | ///
164 | void error(string msg, clObject& arg) {
165 | throw &cons(*new clString(msg), arg);
166 | }
167 |
168 | clSymbol& intern(string s) {
169 | clSymbol* smb = symbolTable[s];
170 | if (smb == NULL) {
171 | smb = new clSymbol(s);
172 | symbolTable[s] = smb;
173 | }
174 | return *smb;
175 | }
176 |
177 | ////////////////////////
178 | ///
179 | /// Dynamic environments
180 | ///
181 |
182 | void dEnv::print (ostream& s) {
183 | for (gc_map::iterator i = m.begin(); i != m.end(); i++ ) {
184 | if (i->second == this) continue;
185 | s << *i->first << " : ";
186 | if (i->second) s << *i->second;
187 | else s << "";
188 | s << '\n';
189 | }
190 | }
191 |
192 | clObject& dEnv::lkup(clSymbol& s) {
193 | gc_map::iterator i = m.find(&s);
194 | if (i != m.end()) return *(i->second);
195 | if (!parent) { error("Unbound symbol", s); }
196 | return parent->lkup(s);
197 | }
198 |
199 |
200 | ////////////////////////
201 | ///
202 | /// Lexical environments
203 | ///
204 | class Env : public gc {
205 | public:
206 | gcVector& paramv;
207 | gcVector valuev;
208 | Env *parent;
209 |
210 | Env(clObject& params, gcVector& _paramv, gcVector& values, Env* _parent=0)
211 | : paramv(_paramv), valuev(values), parent(_parent)
212 | {
213 | if (valuev.size() != paramv.size())
214 | error("Wrong number of arguments", nil);
215 | }
216 |
217 | clObject& lkup(clSymbol& s) {
218 | int i = position(paramv, s);
219 | if (i>=0) return *valuev[i];
220 | if (parent) return parent->lkup(s);
221 | return TLE.lkup(s);
222 | }
223 |
224 | void set(clSymbol& s, clObject& o) {
225 | int i = position(paramv, s);
226 | if (i>=0) { valuev[i] = &o; return; }
227 | if (parent) { parent->set(s, o); return; }
228 | else TLE.set(s,o);
229 | }
230 | };
231 |
232 | // This really need to go somewhere else. There are other static
233 | // initializers in other files (e.g. primops) so we're playing Russion
234 | // roullette here.
235 | //
236 | GC_initter dummy; // Force GC init before other static initializers
237 | Env NLE(nil, *new gcVector(), *new gcVector()); // Null lexical environment
238 |
239 |
240 | ///////////////////////
241 | //
242 | // Closures
243 | //
244 |
245 | class clClosure : public clObject {
246 | public:
247 | virtual clObject& apply(clVector& args) =0;
248 | };
249 |
250 | class vecClosure : public clClosure {
251 | Env& env;
252 | gcVector& params;
253 | gcVector& body;
254 |
255 | vecClosure(gcVector& _params, gcVector& _body, Env& _env)
256 | : env(_env), params(_params), body(_body) {}
257 |
258 | virtual clObject& apply(clVector& args) {
259 | Env& e = *new Env(nil, params, args.theVector, &env);
260 | gcVector::iterator start = body.begin()+1;
261 | gcVector::iterator end = body.end();
262 | clObject* result = &nil;
263 | for (gcVector::iterator i = start; i != end; i++) {
264 | result = &((**i).eval(e));
265 | }
266 | return *result;
267 | }
268 | };
269 |
270 | class consClosure : public clClosure {
271 | public:
272 | Env* env;
273 | clObject* params; // The original formal parameters as specified by the user
274 | gcVector paramv; // The formal parameters converted to a vector
275 | int restarg; // Non-zero means the last formal is a restarg
276 | consCell* body;
277 |
278 | consClosure(clObject& _params, clObject& _body, Env& _env) {
279 | // Check for legal arglist
280 | try {
281 | paramv = dynamic_cast(_params).theVector;
282 | } catch(bad_cast) {
283 | clObject* p = &_params;
284 | consCell *p1;
285 | while ((p1 = theCons(p)) && !null(p1)) {
286 | if (!the(&car(p1))) error("Illegal argument", car(p1));
287 | paramv.push_back(&car(p1));
288 | p = &cdr(p1);
289 | }
290 | if (!null(p)) {
291 | p = the(p);
292 | if (!p) error("Illegal restarg", _params);
293 | paramv.push_back(p);
294 | restarg = (p ? 1 : 0);
295 | }
296 | }
297 |
298 | // Check for legal body
299 | clObject* p = &_body;
300 | consCell *p1;
301 | while ((p1 = theCons(p)) && !null(p1)) p = &cdr(p1);
302 | if (!null(p)) error("Illegal function body form", *p);
303 |
304 | params = &_params;
305 | body = theCons(&_body);
306 | env = &_env;
307 | }
308 |
309 |
310 | void print(ostream &os) { os << "cfn-" << *params << *body; }
311 | string typeOf() { return "consClosure"; }
312 |
313 | clObject& apply(clVector& args) {
314 | // Create a frame for the function arguments
315 | if (restarg) {
316 | int nargs = paramv.size();
317 | gcVector& restval = *new gcVector();
318 | for (Uint i = nargs-1; i'; }
357 | string typeOf() { return "Special form"; }
358 |
359 | clObject& apply(argsT& args, Env& e) { return (*applier)(args, e); }
360 | };
361 |
362 | clObject& quote_apply(argsT& args, Env& e) { return args[0]; }
363 |
364 | clObject& fn_apply(argsT& args, Env& e) {
365 | return *new consClosure(args[0], cdr(vec2list(args.theVector)), e);
366 | }
367 |
368 | clObject& set_apply(argsT& args, Env& e) {
369 | clSymbol& target = the(args[0]);
370 | clObject& value = args[1].eval(e);
371 | e.set(target, value);
372 | return value;
373 | }
374 |
375 | clObject& if_apply(argsT& args, Env& e) {
376 | clObject& condition = args[0].eval(e);
377 | if (!null(condition)) return args[1].eval(e);
378 | return args[2].eval(e);
379 | }
380 |
381 | clObject& try_apply(argsT& args, Env& e) {
382 | try {
383 | return args[0].eval(e);
384 | } catch (clObject* o) {
385 | // Not quite right -- should evalute to a continuation and
386 | // pass *o as an arg.
387 | return args[1].eval(e);
388 | }
389 | }
390 |
391 | clObject& dotimes_apply(argsT& args, Env& e) {
392 | cl_I n = theInteger(args[0].eval(e));
393 | clObject& form = args[1];
394 | for (int i=0; ieval(*e); }
418 |
419 | private:
420 | unwindFrame();
421 | unwindFrame(const unwindFrame&);
422 | };
423 |
424 | clObject& unwind_apply(argsT& args, Env& e) {
425 | unwindFrame f(args[1], e);
426 | return args[0].eval(e);
427 | }
428 |
429 | ////////////////////
430 | ///
431 | /// Ctrl-C handler
432 | ///
433 | #include
434 | int stopeval = 0;
435 | void ctrl_c_handler(int signum) { stopeval=1; }
436 |
437 | //////////////
438 | //
439 | // Stack guard
440 | //
441 | #define STACK_LIMIT 1000000
442 | void* stackbottom = 0;
443 |
444 |
445 | ////////////////////////
446 | ///
447 | /// Eval
448 |
449 | clObject& eval(clObject& o) {
450 | return o.eval(NLE);
451 | }
452 |
453 | clObject& clSymbol::eval(Env& e) { return e.lkup(*this); }
454 |
455 | clVector& evalArgs(argsT& args, Env &e) {
456 | clVector& result = *new clVector();
457 | for (gcVector::iterator i = args.theVector.begin();
458 | i != args.theVector.end();
459 | i++)
460 | vpe(result, (*i)->eval(e));
461 | return result;
462 | }
463 |
464 | clVector& evalArgs(consCell& args, Env &e) {
465 | clVector& result = *new clVector();
466 | clObject* p = &args;
467 | consCell *p1;
468 | while ((p1 = theCons(p)) && !null(p1)) {
469 | vpe(result, car(p1).eval(e));
470 | p = &cdr(p1);
471 | }
472 | return result;
473 | }
474 |
475 | void inline evalPrep() {
476 | // Check for ctrl-c
477 | if (stopeval) {
478 | stopeval=0;
479 | // insert debugger here some day
480 | throw &intern("Interrupt");
481 | }
482 | // Check for possible stack overflow
483 | int dummy;
484 | // Amazing that this doesn't generate a warning
485 | if (abs((long)stackbottom-(long)(&dummy)) > STACK_LIMIT) {
486 | throw &intern("Stack overflow");
487 | }
488 | }
489 |
490 | clObject& clVector::eval(Env& e) {
491 | evalPrep();
492 | argsT args;
493 |
494 | // Evaluate the operator
495 | clObject& op = (*this)[0].eval(e);
496 |
497 | // Special forms get unevaled arguments
498 | specialForm* sp = the(&op);
499 | if (sp) {
500 | for (Uint i=1; i < this->theVector.size(); i++)
501 | vpe(args, *(this->theVector[i]));
502 | return sp->apply(args, e);
503 | }
504 |
505 | // Primops and closures get evaluated arguments
506 | for (Uint i=1; itheVector.size(); i++)
507 | vpe(args, (this->theVector[i])->eval(e));
508 | primop* p = the(&op);
509 | if (p) return p->apply(args);
510 | clClosure* c = dynamic_cast(&op);
511 | if (c) return c->apply(args);
512 | // The operator was not callable
513 | throw &cons(intern("Not a function object"), op);
514 | return nil;
515 | }
516 |
517 | clObject& consCell::eval(Env& e) {
518 | evalPrep();
519 | // Evaluate the CAR
520 | clObject& op = car(this).eval(e);
521 | // Special forms get unevaled arguments
522 | specialForm* sp = the(&op);
523 | if (sp) {
524 | argsT args(list2vec(theCons(cdr(this))));
525 | return sp->apply(args, e);
526 | }
527 | // Primops and closures get evaluated arguments
528 | argsT& args = evalArgs(theCons(cdr(this)), e);
529 | primop* p = the(&op);
530 | if (p) return p->apply(args);
531 | clClosure* c = dynamic_cast(&op);
532 | if (c) return c->apply(args);
533 | // The CAR was not callable
534 | throw &cons(intern("Not a function object"), op);
535 | return nil;
536 | }
537 |
538 | void CL_init() { GC_init(); }
539 |
540 | int main(int argc, const char* argv[]) {
541 | GC_INIT();
542 | new specialForm("quote", "e_apply);
543 | new specialForm("set", &set_apply);
544 | new specialForm("fn", &fn_apply);
545 | new specialForm("if", &if_apply);
546 | new specialForm("try", &try_apply);
547 | new specialForm("dotimes", &dotimes_apply);
548 | new specialForm("time", &time_apply);
549 | new specialForm("unwind-protect", &unwind_apply);
550 |
551 | clSymbol& lastResult = intern("_");
552 |
553 | signal(SIGINT, &ctrl_c_handler);
554 | int dummy;
555 | stackbottom=&dummy;
556 | TLE.set(intern("tle"), TLE);
557 |
558 | while(1) {
559 | clObject& o = readObject();
560 | stopeval=0; // Clear latent interrupts
561 | try {
562 | clObject& result = eval(o);
563 | TLE.set(lastResult, result);
564 | cout << "--> " << result << '\n';
565 | } catch (clObject *o) {
566 | cout << "*** Uncaught exception: " << *o << '\n';
567 | } catch (bad_cast) {
568 | cout << "*** Wrong type argument\n";
569 | } catch (bad_alloc) {
570 | cout << "*** Out of memory\n";
571 | exit(-1);
572 | } catch (out_of_range) {
573 | cout << "*** Vector reference out of range\n";
574 | } catch (...) {
575 | cout << "*** Zowie! Weird exception thrown!\n";
576 | }
577 | }
578 | return 0;
579 | }
580 |
--------------------------------------------------------------------------------
/cl.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include