├── README ├── arclite.arc ├── buffer.js ├── build.sh ├── eval.js ├── index.html ├── jquery.js ├── primitives.js ├── reader.js ├── styles.css ├── test ├── test_buffer.html ├── test_eval.html ├── test_primitives.html ├── test_reader.html ├── test_suite.html ├── test_tutorial.html └── test_types.html ├── types.js └── utils.js /README: -------------------------------------------------------------------------------- 1 | This is a JavaScript port of Paul Graham's Arc programming language. It is nearly-fully-conforming, in the sense of being able to run arc.arc: it lacks continuations and some of the I/O primitives, which is why there's a special arclite.arc with the broken forms stripped out. Firefox (and other browsers which support __proto__) only; does not work in IE. 2 | 3 | The source code is written as a series of modules; build.sh takes care of concatenating them together to a single arclite.js file. The index.html file provides a host for this file, letting you evaluate arc forms in your browser. There are also JSUnit tests in /test. 4 | -------------------------------------------------------------------------------- /arclite.arc: -------------------------------------------------------------------------------- 1 | ;;; Fixups to the standard library to get around incompatibilities in primitives, 2 | ;;; mostly related to the lack of mutable strings 3 | 4 | (def map (f . seqs) 5 | (if (some [isa _ 'string] seqs) 6 | ; Doesn't even work in reference implementation...I wonder why I bother ;-) 7 | (withs (n (apply min (map len seqs)) 8 | lists (map [coerce _ 'cons] seqs)) 9 | (apply f lists)) 10 | (no (cdr seqs)) 11 | (map1 f (car seqs)) 12 | ((afn (seqs) 13 | (if (some no seqs) 14 | nil 15 | (cons (apply f (map1 car seqs)) 16 | (self (map1 cdr seqs))))) 17 | seqs))) 18 | 19 | (def subseq (seq start (o end (len seq))) 20 | (if (isa seq 'string) 21 | (let s2 "" 22 | (for i start (- end 1) 23 | (= s2 (+ s2 (seq i)))) 24 | s2) 25 | (firstn (- end start) (nthcdr start seq)))) 26 | 27 | (def copy (x) 28 | (case (type x) 29 | sym x 30 | cons (apply (fn args args) x) 31 | string (subseq x 0) 32 | table (let new (table) 33 | (ontable k v x 34 | (= (new k) v)) 35 | new) 36 | (err "Can't copy " x))) 37 | -------------------------------------------------------------------------------- /buffer.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | if(typeof window.Buffer != 'undefined') { 4 | var _Buffer = window.Buffer; 5 | } 6 | 7 | var Buffer = window.Buffer = function(args) { 8 | if(this instanceof arguments.callee) { 9 | this.init.apply(this, args && args.callee ? args : arguments); 10 | } else { 11 | return new Buffer(arguments); 12 | } 13 | }; 14 | 15 | Buffer.no_conflict = function() { 16 | window.Buffer = _Buffer; 17 | return Buffer; 18 | }; 19 | 20 | Buffer.prototype = { 21 | 22 | init: function(text) { 23 | this._text = text; 24 | this._pos = 0; 25 | }, 26 | 27 | read: function() { 28 | var c = this.peek(); 29 | if(c === false) { 30 | return false; 31 | } 32 | 33 | this.eat(); 34 | return c; 35 | }, 36 | 37 | read_until: function(pred) { 38 | var start = this._pos 39 | while(this.peek() !== false && !pred(this.peek())) { 40 | this.eat(); 41 | }; 42 | return this._text.substring(start, this._pos); 43 | }, 44 | 45 | eat: function() { 46 | ++this._pos; 47 | if(this._pos > this._text.length) { 48 | throw new Error('Unexpected end of file at position ' + this._pos); 49 | } 50 | }, 51 | 52 | peek: function() { 53 | if(this._pos >= this._text.length) { 54 | return false; 55 | } 56 | 57 | return this._text.charAt(this._pos); 58 | }, 59 | 60 | get: function(index) { 61 | return this._text.charAt(index != undefined ? index : this._pos); 62 | } 63 | 64 | }; 65 | 66 | })(); 67 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cat utils.js buffer.js types.js reader.js primitives.js eval.js > arclite.js 3 | -------------------------------------------------------------------------------- /eval.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | if(typeof window.Eval != 'undefined') { 4 | var _Eval = window.val; 5 | } 6 | 7 | function is_literal(expr) { 8 | switch(expr.__type().__to_js()) { 9 | case 'char': 10 | case 'string': 11 | case 'int': 12 | case 'num': 13 | return true; 14 | default: 15 | return false; 16 | } 17 | }; 18 | 19 | function xcar(expr) { 20 | return expr.__teq('cons') ? expr._car : false; 21 | }; 22 | 23 | function car_eq(expr, val) { 24 | var car = xcar(expr); 25 | return car && car.__eq(val); 26 | }; 27 | 28 | function cadr(expr) { 29 | return expr._cdr._car; 30 | }; 31 | 32 | function apply(fn, args) { 33 | Utils.assert('Null function object passed', fn); 34 | while(fn._tagged) { 35 | fn = fn._val; 36 | } 37 | if(fn.__call == 'native') { 38 | return apply_fn(fn, args); 39 | } else { 40 | return fn.__call(args); 41 | } 42 | }; 43 | 44 | function apply_fn(fn, args) { 45 | var new_env = {}; 46 | new_env.__proto__ = fn._env; 47 | args = Types.list(args); 48 | 49 | function bind_var(fn_arg, call_arg) { 50 | var recurse = true; 51 | if(fn_arg.__teq('cons')) { 52 | // Not the end of the list... 53 | var sym = fn_arg._car; 54 | if(sym.__teq('sym')) { 55 | // Normal binding 56 | if(call_arg.__teq('cons')) { 57 | new_env[sym] = call_arg._car; 58 | bind_var(fn_arg._cdr, call_arg._cdr); 59 | } else { 60 | Utils.error('Wrong number of args for function: expected ' + 61 | fn._args.__print() + ', found ' + args.__print()); 62 | } 63 | } else if(car_eq(sym, 'o')) { 64 | // Optional parameters 65 | var default_expr = cadr(sym._cdr); 66 | new_env[cadr(sym)] = call_arg._car || 67 | (default_expr ? eval(default_expr, new_env) : Types.NIL); 68 | bind_var(fn_arg._cdr, call_arg._cdr || call_arg); 69 | } else if(sym.__teq('cons')) { 70 | // Destructuring 71 | bind_var(sym, call_arg._car); 72 | } else { 73 | Utils.error('Unknown object type in arg list ' + fn._args.__print()); 74 | } 75 | } else if(fn_arg == Types.NIL && call_arg == Types.NIL) { 76 | // End of the arg list; do nothing 77 | } else if(fn_arg.__teq('sym')) { 78 | // Rest parameter 79 | new_env[fn_arg] = call_arg; 80 | } else { 81 | Utils.error("Couldn't bind parameters " + args + ' to list ' + fn._args); 82 | } 83 | }; 84 | 85 | bind_var(fn._args, args); 86 | var last_result = Types.NIL; 87 | fn._body.__each(function(expr) { 88 | last_result = eval(expr, new_env); 89 | }); 90 | return last_result; 91 | }; 92 | 93 | function eval_quasiquote(expr, env) { 94 | var accum = []; 95 | do { 96 | var current = expr._car; 97 | if(car_eq(current, 'unquote')) { 98 | accum.push(eval(cadr(current), env)); 99 | } else if(car_eq(current, 'unquote-splicing')) { 100 | var results = eval(cadr(current), env).__to_list(); 101 | for(var i = 0, len = results.length; i < len; ++i) { 102 | accum.push(results[i]); 103 | } 104 | } else if(current.__teq('cons')) { 105 | accum.push(eval_quasiquote(current, env)); 106 | } else { 107 | accum.push(current); 108 | } 109 | expr = expr._cdr; 110 | } while(expr.__teq('cons')); 111 | return Types.list(accum); 112 | }; 113 | 114 | function eval_symbol(expr, env) { 115 | if(!is_symbol_macro(expr)) { 116 | var result = env[expr]; 117 | if(!result) { 118 | Utils.error('Unbound variable ' + expr); 119 | } 120 | return result; 121 | } else { 122 | return eval(symbol_expand(expr), env); 123 | } 124 | }; 125 | 126 | function eval_if(expr, env) { 127 | Utils.assert('No test for conditional', expr._car); 128 | var test = eval(expr._car, env); 129 | 130 | if(expr._cdr == Types.NIL) { 131 | return test; 132 | } 133 | 134 | if(test == Types.NIL) { 135 | var alternative = expr._cdr._cdr; 136 | return alternative != Types.NIL ? eval_if(alternative, env) : Types.NIL; 137 | } else { 138 | var consequent = cadr(expr); 139 | Utils.assert('No if-true clause', consequent); 140 | return eval(consequent, env); 141 | } 142 | }; 143 | 144 | function eval_fn(expr, env) { 145 | return Types.fn(expr._car, expr._cdr, env); 146 | }; 147 | 148 | function eval_symbol_macro(expr, env, macrodefs) { 149 | var name = cadr(expr), 150 | def = Types.fn(Types.list(Types.sym('expr')), expr._cdr._cdr, env); 151 | return macrodefs[name] = env[name] = def; 152 | }; 153 | 154 | function eval_set(expr, env) { 155 | function set_value(varname, value, env_segment) { 156 | if(!env_segment) { 157 | eval.global_env[varname] = value; 158 | return; 159 | } 160 | 161 | // hasOwnProperty requires a JS string, for some reason, even 162 | // though the actual key is a symbol string 163 | if(env_segment.hasOwnProperty(varname.__to_js())) { 164 | env_segment[varname] = value; 165 | } else { 166 | set_value(varname, value, env_segment.__proto__); 167 | } 168 | }; 169 | 170 | var varname, value; 171 | while(expr != Types.NIL) { 172 | varname = expr._car; 173 | value = eval(cadr(expr), env); 174 | set_value(varname, value, env); 175 | expr = expr._cdr._cdr; 176 | }; 177 | return value; 178 | }; 179 | 180 | function is_macro(expr, env) { 181 | env = env || eval.global_env; 182 | var sym = expr._car; 183 | return sym && env[sym] && env[sym].__teq('mac'); 184 | }; 185 | 186 | function eval_call(expr, env) { 187 | if(is_macro(expr, env)) { 188 | return eval_macro_call(expr, env); 189 | } else { 190 | return eval_fn_call(expr, env); 191 | } 192 | }; 193 | 194 | function is_symbol_macro(expr) { 195 | return expr.indexOf('~') != -1 || expr.indexOf(':') != -1; 196 | }; 197 | 198 | function symbol_expand(expr, env) { 199 | function complement(section) { 200 | return section.charAt(0) == '~' 201 | ? Types.list(Types.sym('complement'), Types.sym(section.substr(1))) 202 | : Types.sym(section); 203 | }; 204 | var sections = expr.split(':'); 205 | return sections.length > 1 206 | ? Types.list([Types.sym('compose')].concat(Utils.map(sections, complement))) 207 | : complement(sections[0]); 208 | }; 209 | 210 | function macro_expand(expr, env) { 211 | // Assumes that we've already checked and made sure the call is a macro 212 | env = env || eval.global_env; 213 | var macro_def = env[expr._car], 214 | macro_result = apply(macro_def, expr._cdr.__to_list()); 215 | Utils.assert('Macro call has no return value', macro_result); 216 | return macro_result; 217 | }; 218 | 219 | function eval_macro_call(expr, env) { 220 | return eval(macro_expand(expr, env), env); 221 | }; 222 | 223 | function eval_fn_call(expr, env) { 224 | var form = expr.__to_list(); 225 | for(var i = 0, len = form.length; i < len; ++i) { 226 | var result = eval(form[i], env); 227 | Utils.assert("Eval of " + form[i].__print() + " yielded undefined", result); 228 | form[i] = result; 229 | } 230 | return apply(form[0], form.slice(1)); 231 | }; 232 | 233 | var eval = window.Eval = function(expr, env) { 234 | env = env || eval.global_env; 235 | 236 | try { 237 | if(is_literal(expr)) { 238 | return expr; 239 | } else if(expr.__teq('sym')) { 240 | return eval_symbol(expr, env); 241 | } else if(car_eq(expr, 'quote')) { 242 | return cadr(expr); 243 | } else if(car_eq(expr, 'quasiquote')) { 244 | return eval_quasiquote(cadr(expr), env); 245 | } else if(car_eq(expr, 'if')) { 246 | return eval_if(expr._cdr, env); 247 | } else if(car_eq(expr, 'fn')) { 248 | return eval_fn(expr._cdr, env); 249 | } else if(car_eq(expr, 'set')) { 250 | return eval_set(expr._cdr, env); 251 | } else if(expr.__teq('cons')) { 252 | return eval_call(expr, env); 253 | } else { 254 | Utils.error('Unknown form type ' + expr); 255 | } 256 | } catch(e) { 257 | if(!(e instanceof Utils.ArcError)) { 258 | Utils.error('Internal error: ' + e.message, expr); 259 | } else { 260 | e.expr = expr; 261 | throw e; 262 | } 263 | } 264 | }; 265 | 266 | var exposed_interpreter_functions = { 267 | 268 | bound: function(name) { 269 | return eval.global_env[name] || Types.NIL; 270 | }, 271 | 272 | apply: Primitives._varargs(function() { 273 | // The behavior of apply is to flatten the last element into the arg list 274 | // if it is itself a list. I have no idea why it does this; it leads to 275 | // odd behavior like (apply + 1 2 '(3 4)) => 10 but 276 | // (apply + 1 '(2 3) 4) => error. But I need to do it for compatibility. 277 | var args = [].slice.call(arguments, 1), 278 | last_elem = args[args.length - 1]; 279 | if(last_elem.__teq('cons')) { 280 | args = args.slice(0, -1).concat(last_elem.__to_list()); 281 | } 282 | return apply(arguments[0], args); 283 | }, 1), 284 | 285 | 'atomic-invoke': function(f) { return apply(f, []); }, 286 | 287 | ssyntax: function(expr) { return Types.bool(is_symbol_macro(expr)); }, 288 | ssexpand: function(expr) { return symbol_expand(expr, eval.global_env); }, 289 | macex1: function(expr) { return is_macro(expr) ? macro_expand(expr) : expr; }, 290 | macex: function(expr) { 291 | while(is_macro(expr)) { 292 | expr = macro_expand(expr); 293 | } 294 | return expr; 295 | }, 296 | 297 | // Fake I/O: 298 | stdout: function() { return Types.T; }, 299 | stdin: function() { return Types.T; }, 300 | sread: function(arc_string) { return Read(arc_string.__to_js()); }, 301 | disp: Primitives._varargs(function() { 302 | for(var i = 0, len = arguments.length; i < len; ++i) { 303 | eval.stdout += arguments[i]; 304 | } 305 | return Types.NIL; 306 | }), 307 | 308 | writec: Primitives._varargs(function(c) { 309 | eval.stdout += c; 310 | return Types.NIL; 311 | }, 1) 312 | 313 | }; 314 | 315 | function init_globals() { 316 | var env = {}; 317 | function add_primitive(namespace, name) { 318 | var prim = namespace[name]; 319 | if(typeof prim == 'function') { 320 | env[name] = Types.primitive(prim, name); 321 | } else if(name.charAt(0) != '_') { 322 | env[name] = prim; 323 | } 324 | }; 325 | for(var prop in Primitives) { 326 | add_primitive(Primitives, prop); 327 | } 328 | for(var prop in exposed_interpreter_functions) { 329 | add_primitive(exposed_interpreter_functions, prop); 330 | } 331 | return env; 332 | }; 333 | 334 | Utils.extend(eval, { 335 | 336 | no_conflict: function() { 337 | window.Eval = _eval; 338 | return eval; 339 | }, 340 | 341 | init_globals: init_globals, 342 | global_env: init_globals(), 343 | character_macros: {}, 344 | symbol_macros: {}, 345 | 346 | eval_line: function(text) { 347 | return eval(Read(text)); 348 | }, 349 | 350 | eval_text: function(text) { 351 | var last_result, 352 | buf = Buffer(text); 353 | while(buf.peek() !== false) { 354 | var text = Read(buf); 355 | if(text === false) break; // Last token is zero-length 356 | last_result = eval(text); 357 | buf.read_until(Read.is_not_whitespace); 358 | } 359 | return last_result; 360 | }, 361 | 362 | stdout: '', 363 | stdin: '', 364 | clear_stdout: function() { 365 | eval.stdout = ''; 366 | } 367 | }); 368 | 369 | })(); 370 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |So, Arc came out about a week ago, and I tried to stay away, but it was just too tempting. I ended up spending a week porting it to JavaScript. You can download the implementation as either a single file or as a tarball. The unit tests assume you have JSUnit installed and accessible.
77 |Notes & deviations from the reference implementation:
Happy hacking!
93 |-- Jonathan Tang (Feb 6, 2008)
94 |2 | ; Main Arc lib. Ported to Scheme version Jul 06. 3 | 4 | ; optimize ~foo in functional position in ac, like compose 5 | ; rename: string, into-string (shorter). could call intos string, 6 | ; but then what to call string? 7 | ; get hold of error types within arc 8 | ; why is macex defined in scheme instead of using def below? 9 | ; write disp, read, write in arc 10 | ; could prob write rmfile and dir in terms of system 11 | ; could I get all of macros up into lib.arc? 12 | 13 | ; any logical reason I can't say (push x (if foo y z)) ? 14 | ; eval would have to always ret 2 things, the val and where it came from 15 | ; idea: implicit tables of tables; setf empty field, becomes table 16 | ; or should setf on a table just take n args? 17 | ; idea: permanent objs that live on disk and are updated when modified 18 | 19 | ; compromises in this implementation: 20 | ; no objs in code 21 | ; (mac testlit args (listtab args)) breaks when called 22 | ; separate string type 23 | ; (= (cdr (cdr str)) "foo") couldn't work because no way to get str tail 24 | 25 | 26 | (set do (annotate 'mac 27 | (fn args `((fn () ,@args))))) 28 | 29 | (set safeset (annotate 'mac 30 | (fn (var val) 31 | `(do (if (bound ',var) 32 | (do (disp "*** redefining ") 33 | (disp ',var) 34 | (writec #\newline))) 35 | (set ,var ,val))))) 36 | 37 | (set def (annotate 'mac 38 | (fn (name parms . body) 39 | `(do (sref sig ',parms ',name) 40 | (safeset ,name (fn ,parms ,@body)))))) 41 | 42 | (def caar (xs) (car (car xs))) 43 | (def cadr (xs) (car (cdr xs))) 44 | (def cddr (xs) (cdr (cdr xs))) 45 | 46 | (def no (x) (is x nil)) 47 | 48 | (def acons (x) (is (type x) 'cons)) 49 | 50 | (def atom (x) (no (acons x))) 51 | 52 | (def list args args) 53 | 54 | (def idfn (x) x) 55 | 56 | ; Maybe later make this internal. 57 | 58 | (def map1 (f xs) 59 | (if (no xs) 60 | nil 61 | (cons (f (car xs)) (map1 f (cdr xs))))) 62 | 63 | (def pair (xs (o f list)) 64 | (if (no xs) 65 | nil 66 | (no (cdr xs)) 67 | (list (list (car xs))) 68 | (cons (f (car xs) (cadr xs)) 69 | (pair (cddr xs) f)))) 70 | 71 | (set mac (annotate 'mac 72 | (fn (name parms . body) 73 | `(do (sref sig ',parms ',name) 74 | (safeset ,name (annotate 'mac (fn ,parms ,@body))))))) 75 | 76 | (mac and args 77 | (if args 78 | (if (cdr args) 79 | `(if ,(car args) (and ,@(cdr args))) 80 | (car args)) 81 | 't)) 82 | 83 | (def assoc (key al) 84 | (if (atom al) 85 | nil 86 | (and (acons (car al)) (is (caar al) key)) 87 | (car al) 88 | (assoc key (cdr al)))) 89 | 90 | (def alref (al key) (cadr (assoc key al))) 91 | 92 | (mac with (parms . body) 93 | `((fn ,(map1 car (pair parms)) 94 | ,@body) 95 | ,@(map1 cadr (pair parms)))) 96 | 97 | (mac let (var val . body) 98 | `(with (,var ,val) ,@body)) 99 | 100 | (mac withs (parms . body) 101 | (if (no parms) 102 | `(do ,@body) 103 | `(let ,(car parms) ,(cadr parms) 104 | (withs ,(cddr parms) ,@body)))) 105 | 106 | ; Rtm prefers to overload + to do this 107 | 108 | (def join args 109 | (if (no args) 110 | nil 111 | (let a (car args) 112 | (if (no a) 113 | (apply join (cdr args)) 114 | (cons (car a) (apply join (cdr a) (cdr args))))))) 115 | 116 | ; Need rfn for use in macro expansions. 117 | 118 | (mac rfn (name parms . body) 119 | `(let ,name nil 120 | (set ,name (fn ,parms ,@body)))) 121 | 122 | (mac afn (parms . body) 123 | `(let self nil 124 | (set self (fn ,parms ,@body)))) 125 | 126 | ; Ac expands x:y:z into (compose x y z), ~x into (complement x) 127 | 128 | ; Only used when the call to compose doesn't occur in functional position. 129 | ; Composes in functional position are transformed away by ac. 130 | 131 | (mac compose args 132 | (let g (uniq) 133 | `(fn ,g 134 | ,((afn (fs) 135 | (if (cdr fs) 136 | (list (car fs) (self (cdr fs))) 137 | `(apply ,(if (car fs) (car fs) 'idfn) ,g))) 138 | args)))) 139 | 140 | (mac complement (f) 141 | (let g (uniq) 142 | `(fn ,g (no (apply ,f ,g))))) 143 | 144 | (def rev (xs) 145 | ((afn (xs acc) 146 | (if (no xs) 147 | acc 148 | (self (cdr xs) (cons (car xs) acc)))) 149 | xs nil)) 150 | 151 | (def isnt (x y) (no (is x y))) 152 | 153 | (mac w/uniq (names . body) 154 | (if (acons names) 155 | `(with ,(apply + nil (map1 (fn (n) (list n '(uniq))) 156 | names)) 157 | ,@body) 158 | `(let ,names (uniq) ,@body))) 159 | 160 | (mac or args 161 | (and args 162 | (w/uniq g 163 | `(let ,g ,(car args) 164 | (if ,g ,g (or ,@(cdr args))))))) 165 | 166 | (def alist (x) (or (no x) (is (type x) 'cons))) 167 | 168 | (mac in (x . choices) 169 | (w/uniq g 170 | `(let ,g ,x 171 | (or ,@(map1 (fn (c) `(is ,g ,c)) choices))))) 172 | 173 | ; should take n args 174 | 175 | (def iso (x y) 176 | (or (is x y) 177 | (and (acons x) 178 | (acons y) 179 | (iso (car x) (car y)) 180 | (iso (cdr x) (cdr y))))) 181 | 182 | (mac when (test . body) 183 | `(if ,test (do ,@body))) 184 | 185 | (mac unless (test . body) 186 | `(if (no ,test) (do ,@body))) 187 | 188 | (mac while (test . body) 189 | (w/uniq (gf gp) 190 | `((rfn ,gf (,gp) 191 | (when ,gp ,@body (,gf ,test))) 192 | ,test))) 193 | 194 | (def empty (seq) 195 | (or (no seq) 196 | (and (no (acons seq)) (is (len seq) 0)))) 197 | 198 | (def reclist (f xs) 199 | (and xs (or (f xs) (reclist f (cdr xs))))) 200 | 201 | (def recstring (test s (o start 0)) 202 | (let n (len s) 203 | ((afn (i) 204 | (and (< i (len s)) 205 | (or (test i) 206 | (self (+ i 1))))) 207 | start))) 208 | 209 | (def testify (x) 210 | (if (isa x 'fn) x [is _ x])) 211 | 212 | (def some (test seq) 213 | (let f (testify test) 214 | (if (alist seq) 215 | (reclist f:car seq) 216 | (recstring f:seq seq)))) 217 | 218 | (def all (test seq) 219 | (~some (complement (testify test)) seq)) 220 | 221 | (def mem (test seq) 222 | (let f (testify test) 223 | (reclist [if (f:car _) _] seq))) 224 | 225 | (def find (test seq) 226 | (let f (testify test) 227 | (if (alist seq) 228 | (reclist [if (f:car _) (car _)] seq) 229 | (recstring [if (f:seq _) (seq _)] seq)))) 230 | 231 | (def isa (x y) (is (type x) y)) 232 | 233 | ; Possible to write map without map1, but makes News 3x slower. 234 | 235 | ;(def map (f . seqs) 236 | ; (if (some1 no seqs) 237 | ; nil 238 | ; (no (cdr seqs)) 239 | ; (let s1 (car seqs) 240 | ; (cons (f (car s1)) 241 | ; (map f (cdr s1)))) 242 | ; (cons (apply f (map car seqs)) 243 | ; (apply map f (map cdr seqs))))) 244 | 245 | 246 | (def map (f . seqs) 247 | (if (some [isa _ 'string] seqs) 248 | (withs (n (apply min (map len seqs)) 249 | new (newstring n)) 250 | ((afn (i) 251 | (if (is i n) 252 | new 253 | (do (sref new (apply f (map [_ i] seqs)) i) 254 | (self (+ i 1))))) 255 | 0)) 256 | (no (cdr seqs)) 257 | (map1 f (car seqs)) 258 | ((afn (seqs) 259 | (if (some no seqs) 260 | nil 261 | (cons (apply f (map1 car seqs)) 262 | (self (map1 cdr seqs))))) 263 | seqs))) 264 | 265 | (def mappend (f . args) 266 | (apply + nil (apply map f args))) 267 | 268 | (def firstn (n xs) 269 | (if (and (> n 0) xs) 270 | (cons (car xs) (firstn (- n 1) (cdr xs))) 271 | nil)) 272 | 273 | (def nthcdr (n xs) 274 | (if (> n 0) 275 | (nthcdr (- n 1) (cdr xs)) 276 | xs)) 277 | 278 | ; Generalization of pair: (tuples x) = (pair x) 279 | 280 | (def tuples (xs (o n 2)) 281 | (if (no xs) 282 | nil 283 | (cons (firstn n xs) 284 | (tuples (nthcdr n xs) n)))) 285 | 286 | (def caris (x val) (and (acons x) (is (car x) val))) 287 | 288 | (def warn (msg . args) 289 | (disp (+ "Warning: " msg ". ")) 290 | (map [do (write _) (disp " ")] args) 291 | (disp #\newline)) 292 | 293 | (mac atomic body 294 | `(atomic-invoke (fn () ,@body))) 295 | 296 | (mac atlet args 297 | `(atomic (let ,@args))) 298 | 299 | (mac atwith args 300 | `(atomic (with ,@args))) 301 | 302 | (mac atwiths args 303 | `(atomic (withs ,@args))) 304 | 305 | ; setforms returns (vars get set) for a place based on car of an expr 306 | ; vars is a list of gensyms alternating with expressions whose vals they 307 | ; should be bound to, suitable for use as first arg to withs 308 | ; get is an expression returning the current value in the place 309 | ; set is an expression representing a function of one argument 310 | ; that stores a new value in the place 311 | 312 | ; A bit gross that it works based on the *name* in the car, but maybe 313 | ; wrong to worry. Macros live in expression land. 314 | 315 | ; seems meaningful to e.g. (push 1 (pop x)) if (car x) is a cons. 316 | ; can't in cl though. could I define a setter for push or pop? 317 | 318 | (set setter (table)) 319 | 320 | (mac defset (name parms . body) 321 | (w/uniq gexpr 322 | `(sref setter 323 | (fn (,gexpr) 324 | (let ,parms (cdr ,gexpr) 325 | ,@body)) 326 | ',name))) 327 | 328 | (defset car (x) 329 | (w/uniq g 330 | (list (list g x) 331 | `(car ,g) 332 | `(fn (val) (scar ,g val))))) 333 | 334 | (defset cdr (x) 335 | (w/uniq g 336 | (list (list g x) 337 | `(cdr ,g) 338 | `(fn (val) (scdr ,g val))))) 339 | 340 | (defset caar (x) 341 | (w/uniq g 342 | (list (list g x) 343 | `(caar ,g) 344 | `(fn (val) (scar (car ,g) val))))) 345 | 346 | (defset cadr (x) 347 | (w/uniq g 348 | (list (list g x) 349 | `(cadr ,g) 350 | `(fn (val) (scar (cdr ,g) val))))) 351 | 352 | (defset cddr (x) 353 | (w/uniq g 354 | (list (list g x) 355 | `(cddr ,g) 356 | `(fn (val) (scdr (cdr ,g) val))))) 357 | 358 | ; Note: if expr0 macroexpands into any expression whose car doesn't 359 | ; have a setter, setforms assumes it's a data structure in functional 360 | ; position. Such bugs will be seen only when the code is executed, when 361 | ; sref complains it can't set a reference to a function. 362 | 363 | (def setforms (expr0) 364 | (let expr (macex expr0) 365 | (if (isa expr 'sym) 366 | (w/uniq (g h) 367 | (list (list g expr) 368 | g 369 | `(fn (,h) (set ,expr ,h)))) 370 | ; make it also work for uncompressed calls to compose 371 | (and (acons expr) (metafn (car expr))) 372 | (setforms (expand-metafn-call (ssexpand (car expr)) (cdr expr))) 373 | (let f (setter (car expr)) 374 | (if f 375 | (f expr) 376 | ; assumed to be data structure in fn position 377 | (do (when (caris (car expr) 'fn) 378 | (warn "Inverting what looks like a function call" 379 | expr0 expr)) 380 | (w/uniq (g h) 381 | (let argsyms (map [uniq] (cdr expr)) 382 | (list (+ (list g (car expr)) 383 | (mappend list argsyms (cdr expr))) 384 | `(,g ,@argsyms) 385 | `(fn (,h) (sref ,g ,h ,@argsyms))))))))))) 386 | 387 | (def metafn (x) 388 | (or (ssyntax x) 389 | (and (acons x) (in (car x) 'compose 'complement)))) 390 | 391 | (def expand-metafn-call (f args) 392 | (if (is (car f) 'compose) 393 | ((afn (fs) 394 | (if (caris (car fs) 'compose) ; nested compose 395 | (self (join (cdr (car fs)) (cdr fs))) 396 | (cdr fs) 397 | (list (car fs) (self (cdr fs))) 398 | (cons (car fs) args))) 399 | (cdr f)) 400 | (err "Can't invert " (cons f args)))) 401 | 402 | (def expand= (place val) 403 | (if (isa place 'sym) 404 | `(set ,place ,val) 405 | (let (vars prev setter) (setforms place) 406 | (w/uniq g 407 | `(atwith ,(+ vars (list g val)) 408 | (,setter ,g)))))) 409 | 410 | (def expand=list (terms) 411 | `(do ,@(map (fn ((p v)) (expand= p v)) ; [apply expand= _] 412 | (pair terms)))) 413 | 414 | (mac = args 415 | (expand=list args)) 416 | 417 | (mac loop (start test update . body) 418 | (w/uniq (gfn gparm) 419 | `(do ,start 420 | ((rfn ,gfn (,gparm) 421 | (if ,gparm 422 | (do ,@body ,update (,gfn ,test)))) 423 | ,test)))) 424 | 425 | (mac for (v init max . body) 426 | (w/uniq (gi gm) 427 | `(with (,v nil ,gi ,init ,gm (+ ,max 1)) 428 | (loop (set ,v ,gi) (< ,v ,gm) (set ,v (+ ,v 1)) 429 | ,@body)))) 430 | 431 | (mac repeat (n . body) 432 | `(for ,(uniq) 1 ,n ,@body)) 433 | 434 | ; could bind index instead of gensym 435 | 436 | (mac each (var expr . body) 437 | (w/uniq (gseq g) 438 | `(let ,gseq ,expr 439 | (if (alist ,gseq) 440 | ((afn (,g) 441 | (when (acons ,g) 442 | (let ,var (car ,g) ,@body) 443 | (self (cdr ,g)))) 444 | ,gseq) 445 | (isa ,gseq 'table) 446 | (maptable (fn (,g ,var) ,@body) 447 | ,gseq) 448 | (for ,g 0 (- (len ,gseq) 1) 449 | (let ,var (,gseq ,g) ,@body)))))) 450 | 451 | ; (nthcdr x y) = (subseq y x). 452 | 453 | (def subseq (seq start (o end (len seq))) 454 | (if (isa seq 'string) 455 | (let s2 (newstring (- end start)) 456 | (for i 0 (- end start 1) 457 | (= (s2 i) (seq (+ start i)))) 458 | s2) 459 | (firstn (- end start) (nthcdr start seq)))) 460 | 461 | (mac ontable (k v h . body) 462 | `(maptable (fn (,k ,v) ,@body) ,h)) 463 | 464 | (mac whilet (var test . body) 465 | (w/uniq (gf gp) 466 | `((rfn ,gf (,gp) 467 | (let ,var ,gp 468 | (when ,var ,@body (,gf ,test)))) 469 | ,test))) 470 | 471 | (def last (seq) 472 | (if (no (cdr seq)) 473 | (car seq) 474 | (last (cdr seq)))) 475 | 476 | (def rem (test seq) 477 | (let f (testify test) 478 | (if (alist seq) 479 | ((afn (s) 480 | (if (no s) nil 481 | (f (car s)) (self (cdr s)) 482 | (cons (car s) (self (cdr s))))) 483 | seq) 484 | (coerce (rem test (coerce seq 'cons)) 'string)))) 485 | 486 | (def keep (test seq) 487 | (rem (complement (testify test)) seq)) 488 | 489 | (def trues (f seq) (rem nil (map f seq))) 490 | 491 | (mac do1 args 492 | (w/uniq g 493 | `(let ,g ,(car args) 494 | ,@(cdr args) 495 | ,g))) 496 | 497 | ; Would like to write a faster case based on table generated by a macro, 498 | ; but can't insert objects into expansions in Mzscheme. 499 | 500 | (mac caselet (var expr . args) 501 | (let ex (afn (args) 502 | (if (no (cdr args)) 503 | (car args) 504 | `(if (is ,var ',(car args)) 505 | ,(cadr args) 506 | ,(self (cddr args))))) 507 | `(let ,var ,expr ,(ex args)))) 508 | 509 | (mac case (expr . args) 510 | `(caselet ,(uniq) ,expr ,@args)) 511 | 512 | (mac push (x place) 513 | (w/uniq gx 514 | (let (binds val setter) (setforms place) 515 | `(let ,gx ,x 516 | (atwiths ,binds 517 | (,setter (cons ,gx ,val))))))) 518 | 519 | (mac swap (place1 place2) 520 | (w/uniq (g1 g2) 521 | (with ((binds1 val1 setter1) (setforms place1) 522 | (binds2 val2 setter2) (setforms place2)) 523 | `(atwiths ,(+ binds1 (list g1 val1) binds2 (list g2 val2)) 524 | (,setter1 ,g2) 525 | (,setter2 ,g1))))) 526 | 527 | (mac rotate places 528 | (with (vars (map [uniq] places) 529 | forms (map setforms places)) 530 | `(atwiths ,(mappend (fn (g (binds val setter)) 531 | (+ binds (list g val))) 532 | vars 533 | forms) 534 | ,@(map (fn (g (binds val setter)) 535 | (list setter g)) 536 | (+ (cdr vars) (list (car vars))) 537 | forms)))) 538 | 539 | (mac pop (place) 540 | (w/uniq g 541 | (let (binds val setter) (setforms place) 542 | `(atwiths ,(+ binds (list g val)) 543 | (do1 (car ,g) 544 | (,setter (cdr ,g))))))) 545 | 546 | (def adjoin (x xs (o test iso)) 547 | (if (some [test x _] xs) 548 | xs 549 | (cons x xs))) 550 | 551 | (mac pushnew (x place . args) 552 | (w/uniq gx 553 | (let (binds val setter) (setforms place) 554 | `(atwiths ,(+ (list gx x) binds) 555 | (,setter (adjoin ,gx ,val ,@args)))))) 556 | 557 | (mac pull (test place) 558 | (w/uniq g 559 | (let (binds val setter) (setforms place) 560 | `(atwiths ,(+ (list g test) binds) 561 | (,setter (rem ,g ,val)))))) 562 | 563 | (mac ++ (place (o i 1)) 564 | (if (isa place 'sym) 565 | `(= ,place (+ ,place ,i)) 566 | (w/uniq gi 567 | (let (binds val setter) (setforms place) 568 | `(atwiths ,(+ binds (list gi i)) 569 | (,setter (+ ,val ,gi))))))) 570 | 571 | (mac -- (place (o i 1)) 572 | (if (isa place 'sym) 573 | `(= ,place (- ,place ,i)) 574 | (w/uniq gi 575 | (let (binds val setter) (setforms place) 576 | `(atwiths ,(+ binds (list gi i)) 577 | (,setter (- ,val ,gi))))))) 578 | 579 | ; E.g. (inc x) equiv to (zap + x 1) 580 | 581 | (mac zap (op place . args) 582 | (with (gop (uniq) 583 | gargs (map [uniq] args) 584 | mix (afn seqs 585 | (if (some no seqs) 586 | nil 587 | (+ (map car seqs) 588 | (apply self (map cdr seqs)))))) 589 | (let (binds val setter) (setforms place) 590 | `(atwiths ,(+ binds (list gop op) (mix gargs args)) 591 | (,setter (,gop ,val ,@gargs)))))) 592 | 593 | ; Can't simply mod pr to print strings represented as lists of chars, 594 | ; because empty string will get printed as nil. Would need to rep strings 595 | ; as lists of chars annotated with 'string, and modify car and cdr to get 596 | ; the rep of these. That would also require hacking the reader. 597 | 598 | ;(def pr args 599 | ; (if (isa (car args) 'output) 600 | ; (do (error "stream arg!" args) 601 | ; (map1 [disp _ (car args)] (cdr args)) 602 | ; (cadr args)) 603 | ; (do (map1 disp args) 604 | ; (car args)))) 605 | 606 | (def pr args 607 | (map1 disp args) 608 | (car args)) 609 | 610 | ; Rtm says this version should make the server 20% faster because map1 611 | ; generates so much garbage; in fact makes slower; maybe rewrite map1? 612 | 613 | ;(def newpr args 614 | ; (if (isa (car args) 'output) 615 | ; (do (each a (cdr args) (disp a (car args))) 616 | ; (cadr args)) 617 | ; (do (each a args (disp a)) 618 | ; (car args)))) 619 | 620 | (def prn args 621 | (do1 (apply pr args) 622 | (writec #\newline 623 | (if (isa (car args) 'output) (car args) (stdout))))) 624 | 625 | (mac nil! args 626 | `(do ,@(map (fn (a) `(= ,a nil)) args))) 627 | 628 | (mac t! args 629 | `(do ,@(map (fn (a) `(= ,a t)) args))) 630 | 631 | ; Destructing means ambiguity: are pat vars bound in else? (no) 632 | 633 | (mac iflet (var expr then . rest) 634 | (w/uniq gv 635 | `(let ,gv ,expr 636 | (if ,gv (let ,var ,gv ,then) ,@rest)))) 637 | 638 | (mac whenlet (var expr . body) 639 | `(iflet ,var ,expr (do ,@body))) 640 | 641 | (mac aif (expr . body) 642 | `(let it ,expr (if it ,@body))) 643 | 644 | (mac awhen (expr . body) 645 | `(let it ,expr (if it (do ,@body)))) 646 | 647 | (mac aand args 648 | (if (no args) 649 | 't 650 | (no (cdr args)) 651 | (car args) 652 | `(let it ,(car args) (and it (aand ,@(cdr args)))))) 653 | 654 | (mac accum (accfn . body) 655 | (w/uniq gacc 656 | `(withs (,gacc nil ,accfn [push _ ,gacc]) 657 | ,@body 658 | ,gacc))) 659 | 660 | ; Repeatedly evaluates its body till it returns nil, then returns vals. 661 | 662 | (mac drain (expr (o eof nil)) 663 | (w/uniq (gacc gdone gres) 664 | `(with (,gacc nil ,gdone nil) 665 | (while (no ,gdone) 666 | (let ,gres ,expr 667 | (if (is ,gres ,eof) 668 | (= ,gdone t) 669 | (push ,gres ,gacc)))) 670 | (rev ,gacc)))) 671 | 672 | ; For the common C idiom while x = snarfdata != stopval. 673 | ; Rename this if use it often. 674 | 675 | (mac whiler (var expr endval . body) 676 | (w/uniq gf 677 | `((rfn ,gf (,var) 678 | (when (and ,var (no (is ,var ,endval))) 679 | ,@body 680 | (,gf ,expr))) 681 | ,expr))) 682 | 683 | ;(def macex (e) 684 | ; (if (atom e) 685 | ; e 686 | ; (let op (and (atom (car e)) (eval (car e))) 687 | ; (if (isa op 'mac) 688 | ; (apply (rep op) (cdr e)) 689 | ; e)))) 690 | 691 | (def consif (x y) (if x (cons x y) y)) 692 | 693 | (def string args 694 | (apply + "" (map [coerce _ 'string] args))) 695 | 696 | (def flat (x (o stringstoo)) 697 | ((rfn f (x acc) 698 | (if (or (no x) (and stringstoo (is x ""))) 699 | acc 700 | (and (atom x) (no (and stringstoo (isa x 'string)))) 701 | (cons x acc) 702 | (f (car x) (f (cdr x) acc)))) 703 | x nil)) 704 | 705 | ; Perhaps not the final idea, or at least final name 706 | 707 | (mac default (x test alt) 708 | (w/uniq gx 709 | `(let ,gx ,x 710 | (if (,test ,gx) ,gx ,alt)))) 711 | 712 | (def pos (test seq (o start 0)) 713 | (let f (testify test) 714 | (if (alist seq) 715 | ((afn (seq n) 716 | (if (no seq) 717 | nil 718 | (f (car seq)) 719 | n 720 | (self (cdr seq) (+ n 1)))) 721 | (nthcdr start seq) 722 | start) 723 | (recstring [if (f (seq _)) _] seq start)))) 724 | 725 | (def even (n) (is (mod n 2) 0)) 726 | 727 | (def odd (n) (no (even n))) 728 | 729 | (mac after (x . ys) 730 | `(protect (fn () ,x) (fn () ,@ys))) 731 | 732 | (let expander 733 | (fn (f var name body) 734 | `(let ,var (,f ,name) 735 | (after (do ,@body) (close ,var)))) 736 | 737 | (mac w/infile (var name . body) 738 | (expander 'infile var name body)) 739 | 740 | (mac w/outfile (var name . body) 741 | (expander 'outfile var name body)) 742 | 743 | (mac w/instring (var str . body) 744 | (expander 'instring var str body)) 745 | ) 746 | 747 | (mac w/outstring (var . body) 748 | `(let ,var (outstring) ,@body)) 749 | 750 | (mac w/appendfile (var name . body) 751 | `(let ,var (outfile ,name 'append) 752 | (after (do ,@body) (close ,var)))) 753 | 754 | ; rename this simply "to"? - prob not; rarely use 755 | 756 | (mac w/stdout (str . body) 757 | `(call-w/stdout ,str (fn () ,@body))) 758 | 759 | (mac w/stdin (str . body) 760 | `(call-w/stdin ,str (fn () ,@body))) 761 | 762 | (mac tostring body 763 | (w/uniq gv 764 | `(w/outstring ,gv 765 | (w/stdout ,gv ,@body) 766 | (inside ,gv)))) 767 | 768 | (mac fromstring (str . body) 769 | (w/uniq gv 770 | `(w/instring ,gv ,str 771 | (w/stdin ,gv ,@body)))) 772 | 773 | (def readstring1 (s (o eof nil)) (w/instring i s (read i eof))) 774 | 775 | (def read ((o x (stdin)) (o eof nil)) 776 | (if (isa x 'string) (readstring1 x eof) (sread x eof))) 777 | 778 | (def readfile (name) (w/infile s name (drain (read s)))) 779 | 780 | (def readfile1 (name) (w/infile s name (read s))) 781 | 782 | (def writefile1 (val name) (w/outfile s name (write val s)) val) 783 | 784 | (def readall (src (o eof nil)) 785 | ((afn (i) 786 | (let x (read i eof) 787 | (if (is x eof) 788 | nil 789 | (cons x (self i))))) 790 | (if (isa src 'string) (instring src) src))) 791 | 792 | (def sym (x) (coerce x 'sym)) 793 | 794 | (mac rand-choice exprs 795 | `(case (rand ,(len exprs)) 796 | ,@(let key -1 797 | (mappend [list (++ key) _] 798 | exprs)))) 799 | 800 | (mac n-of (n expr) 801 | (w/uniq ga 802 | `(let ,ga nil 803 | (repeat ,n (push ,expr ,ga)) 804 | (rev ,ga)))) 805 | 806 | (def rand-string (n) 807 | (with (cap (fn () (+ 65 (rand 26))) 808 | sm (fn () (+ 97 (rand 26))) 809 | dig (fn () (+ 48 (rand 10)))) 810 | (coerce (map [coerce _ 'char] 811 | (cons (rand-choice (cap) (sm)) 812 | (n-of (- n 1) (rand-choice (cap) (sm) (dig))))) 813 | 'string))) 814 | 815 | (mac forlen (var s . body) 816 | `(for ,var 0 (- (len ,s) 1) ,@body)) 817 | 818 | (mac on (var s . body) 819 | (if (is var 'index) 820 | (err "Can't use index as first arg to on.") 821 | (w/uniq gs 822 | `(let ,gs ,s 823 | (forlen index ,gs 824 | (let ,var (,gs index) 825 | ,@body)))))) 826 | 827 | (def best (f seq) 828 | (if (no seq) 829 | nil 830 | (let wins (car seq) 831 | (each elt (cdr seq) 832 | (if (f elt wins) (= wins elt))) 833 | wins))) 834 | 835 | (def max args (best > args)) 836 | (def min args (best < args)) 837 | 838 | ; (mac max2 (x y) 839 | ; (w/uniq (a b) 840 | ; `(with (,a ,x ,b ,y) (if (> ,a ,b) ,a ,b)))) 841 | 842 | (def most (f seq) 843 | (unless (no seq) 844 | (withs (wins (car seq) topscore (f wins)) 845 | (each elt (cdr seq) 846 | (let score (f elt) 847 | (if (> score topscore) (= wins elt topscore score)))) 848 | wins))) 849 | 850 | ; Insert so that list remains sorted. Don't really want to expose 851 | ; these but seem to have to because can't include a fn obj in a 852 | ; macroexpansion. 853 | 854 | (def insert-sorted (test elt seq) 855 | (if (no seq) 856 | (list elt) 857 | (test elt (car seq)) 858 | (cons elt seq) 859 | (cons (car seq) (insert-sorted test elt (cdr seq))))) 860 | 861 | (mac insort (test elt seq) 862 | `(zap [insert-sorted ,test ,elt _] ,seq)) 863 | 864 | (def reinsert-sorted (test elt seq) 865 | (if (no seq) 866 | (list elt) 867 | (is elt (car seq)) 868 | (reinsert-sorted test elt (cdr seq)) 869 | (test elt (car seq)) 870 | (cons elt (rem elt seq)) 871 | (cons (car seq) (reinsert-sorted test elt (cdr seq))))) 872 | 873 | (mac insortnew (test elt seq) 874 | `(zap [reinsert-sorted ,test ,elt _] ,seq)) 875 | 876 | ; Could make this look at the sig of f and return a fn that took the 877 | ; right no of args and didn't have to call apply (or list if 1 arg). 878 | 879 | (def memo (f) 880 | (let cache (table) 881 | (fn args 882 | (or (cache args) 883 | (= (cache args) (apply f args)))))) 884 | 885 | (mac defmemo (name parms . body) 886 | `(safeset ,name (memo (fn ,parms ,@body)))) 887 | 888 | (def <= args 889 | (or (no args) 890 | (no (cdr args)) 891 | (and (no (> (car args) (cadr args))) 892 | (apply <= (cdr args))))) 893 | 894 | (def >= args 895 | (or (no args) 896 | (no (cdr args)) 897 | (and (no (< (car args) (cadr args))) 898 | (apply >= (cdr args))))) 899 | 900 | (def whitec (c) 901 | (in c #\space #\newline #\tab #\return)) 902 | 903 | (def nonwhite (c) (no (whitec c))) 904 | 905 | (def alphadig (c) 906 | (or (<= #\a c #\z) (<= #\A c #\Z) (<= #\0 c #\9))) 907 | 908 | (def punc (c) 909 | (in c #\. #\, #\; #\: #\! #\?)) 910 | 911 | (def readline ((o str (stdin))) 912 | (awhen (readc str) 913 | (tostring 914 | (writec it) 915 | (whiler c (readc str) #\newline 916 | (writec c))))) 917 | 918 | ; Don't currently use this but suspect some code could. 919 | 920 | (mac summing (sumfn . body) 921 | (w/uniq (gc gt) 922 | `(let ,gc 0 923 | (let ,sumfn (fn (,gt) (if ,gt (++ ,gc))) 924 | ,@body) 925 | ,gc))) 926 | 927 | (def trav (f base tree) 928 | (if (atom tree) 929 | (base tree) 930 | (f (trav f base (car tree)) (trav f base (cdr tree))))) 931 | 932 | (def carif (x) (if (atom x) x (car x))) 933 | 934 | ; Could prob be generalized beyond printing. 935 | 936 | (def prall (elts (o init "") (o sep ", ")) 937 | (when elts 938 | (pr init (car elts)) 939 | (map [pr sep _] (cdr elts)) 940 | elts)) 941 | 942 | (def prs args 943 | (prall args "" #\space)) 944 | 945 | (def tree-subst (old new tree) 946 | (if (is tree old) 947 | new 948 | (atom tree) 949 | tree 950 | (cons (tree-subst old new (car tree)) 951 | (tree-subst old new (cdr tree))))) 952 | 953 | (def ontree (f tree) 954 | (f tree) 955 | (unless (atom tree) 956 | (ontree f (car tree)) 957 | (ontree f (cdr tree)))) 958 | 959 | (def dotted (x) 960 | (if (atom x) 961 | nil 962 | (and (cdr x) (or (atom (cdr x)) 963 | (dotted (cdr x)))))) 964 | 965 | (def fill-table (table data) 966 | (each (k v) (pair data) (= (table k) v)) 967 | table) 968 | 969 | (mac obj args 970 | (w/uniq g 971 | `(let ,g (table) 972 | ,@(map (fn ((k v)) `(= (,g ',k) ,v)) 973 | (pair args)) 974 | ,g))) 975 | 976 | (def keys (h) 977 | (accum a (ontable k v h (a k)))) 978 | 979 | (def vals (h) 980 | (accum a (ontable k v h (a v)))) 981 | 982 | ; These two should really be done by coerce. Wrap coerce? 983 | 984 | (def tablist (h) 985 | (accum a (maptable (fn args (a args)) h))) 986 | 987 | (def listtab (al) 988 | (let h (table) 989 | (map (fn ((k v)) (= (h k) v)) 990 | al) 991 | h)) 992 | 993 | (def load-table (file (o eof)) 994 | (w/infile i file (read-table i eof))) 995 | 996 | (def read-table ((o i (stdin)) (o eof)) 997 | (let e (read i eof) 998 | (if (alist e) (listtab e) e))) 999 | 1000 | (def load-tables (file) 1001 | (w/infile i file 1002 | (w/uniq eof 1003 | (drain (read-table i eof) eof)))) 1004 | 1005 | (def save-table (h file) 1006 | (w/outfile o file (write-table h o))) 1007 | 1008 | (def write-table (h (o o (stdout))) 1009 | (write (tablist h) o)) 1010 | 1011 | (def copy (x) 1012 | (case (type x) 1013 | sym x 1014 | cons (apply (fn args args) x) 1015 | string (let new (newstring (len x)) 1016 | (forlen i x 1017 | (= (new i) (x i))) 1018 | new) 1019 | table (let new (table) 1020 | (ontable k v x 1021 | (= (new k) v)) 1022 | new) 1023 | (err "Can't copy " x))) 1024 | 1025 | (def abs (n) 1026 | (if (< n 0) (- n) n)) 1027 | 1028 | ; The problem with returning a list instead of multiple values is that 1029 | ; you can't act as if the fn didn't return multiple vals in cases where 1030 | ; you only want the first. Not a big problem. 1031 | 1032 | (def round (n) 1033 | (withs (base (truncate n) rem (abs (- n base))) 1034 | (if (> rem 1/2) ((if (> n 0) + -) base 1) 1035 | (< rem 1/2) base 1036 | (odd base) ((if (> n 0) + -) base 1) 1037 | base))) 1038 | 1039 | (def roundup (n) 1040 | (withs (base (truncate n) rem (abs (- n base))) 1041 | (if (>= rem 1/2) 1042 | ((if (> n 0) + -) base 1) 1043 | base))) 1044 | 1045 | (def to-nearest (n quantum) 1046 | (* (roundup (/ n quantum)) quantum)) 1047 | 1048 | (def avg (ns) (/ (apply + ns) (len ns))) 1049 | 1050 | ; Use mergesort on assumption that mostly sorting mostly sorted lists 1051 | ; benchmark: (let td (n-of 10000 (rand 100)) (time (sort < td)) 1) 1052 | 1053 | (def sort (test seq) 1054 | (if (alist seq) 1055 | (mergesort test (copy seq)) 1056 | (coerce (mergesort test (coerce seq 'cons)) (type seq)))) 1057 | 1058 | ; Destructive stable merge-sort, adapted from slib and improved 1059 | ; by Eli Barzilay for MzLib; re-written in Arc. 1060 | 1061 | (def mergesort (less? lst) 1062 | (with (n (len lst)) 1063 | (if (<= n 1) lst 1064 | ; ; check if the list is already sorted 1065 | ; ; (which can be a common case, eg, directory lists). 1066 | ; (let loop ([last (car lst)] [next (cdr lst)]) 1067 | ; (or (null? next) 1068 | ; (and (not (less? (car next) last)) 1069 | ; (loop (car next) (cdr next))))) 1070 | ; lst 1071 | ((afn (n) 1072 | (if (> n 2) 1073 | ; needs to evaluate L->R 1074 | (withs (j (/ (if (even n) n (- n 1)) 2) ; faster than round 1075 | a (self j) 1076 | b (self (- n j))) 1077 | (merge less? a b)) 1078 | ; the following case just inlines the length 2 case, 1079 | ; it can be removed (and use the above case for n>1) 1080 | ; and the code still works, except a little slower 1081 | (is n 2) 1082 | (with (x (car lst) y (cadr lst) p lst) 1083 | (= lst (cddr lst)) 1084 | (when (less? y x) (scar p y) (scar (cdr p) x)) 1085 | (scdr (cdr p) nil) 1086 | p) 1087 | (is n 1) 1088 | (with (p lst) 1089 | (= lst (cdr lst)) 1090 | (scdr p nil) 1091 | p) 1092 | nil)) 1093 | n)))) 1094 | 1095 | ; Also by Eli. 1096 | 1097 | (def merge (less? x y) 1098 | (if (no x) y 1099 | (no y) x 1100 | (let lup nil 1101 | (set lup 1102 | (fn (r x y r-x?) ; r-x? for optimization -- is r connected to x? 1103 | (if (less? (car y) (car x)) 1104 | (do (if r-x? (scdr r y)) 1105 | (if (cdr y) (lup y x (cdr y) nil) (scdr y x))) 1106 | ; (car x) <= (car y) 1107 | (do (if (no r-x?) (scdr r x)) 1108 | (if (cdr x) (lup x (cdr x) y t) (scdr x y)))))) 1109 | (if (less? (car y) (car x)) 1110 | (do (if (cdr y) (lup y x (cdr y) nil) (scdr y x)) 1111 | y) 1112 | ; (car x) <= (car y) 1113 | (do (if (cdr x) (lup x (cdr x) y t) (scdr x y)) 1114 | x))))) 1115 | 1116 | (def bestn (n f seq) 1117 | (firstn n (sort f seq))) 1118 | 1119 | (def split (seq pos) 1120 | (withs (mid (nthcdr (- pos 1) seq) 1121 | s2 (cdr mid)) 1122 | (nil! (cdr mid)) 1123 | (list seq s2))) 1124 | 1125 | (mac time (expr) 1126 | (w/uniq (t1 t2) 1127 | `(let ,t1 (msec) 1128 | (do1 ,expr 1129 | (let ,t2 (msec) 1130 | (prn "time: " (- ,t2 ,t1) " msec.")))))) 1131 | 1132 | (mac jtime (expr) 1133 | `(do1 'ok (time ,expr))) 1134 | 1135 | (mac time10 (expr) 1136 | `(time (repeat 10 ,expr))) 1137 | 1138 | (= templates* (table)) 1139 | 1140 | (def maps (fn . args) 1141 | (apply join (apply map fn args))) 1142 | 1143 | (mac deftem (tem . fields) 1144 | (withs (name (carif tem) includes (if (acons tem) (cdr tem))) 1145 | `(= (templates* ',name) 1146 | (+ (maps templates* ',(rev includes)) 1147 | (list ,@(map (fn ((k v)) `(list ',k (fn () ,v))) 1148 | (pair fields))))))) 1149 | 1150 | (def inst (tem . args) 1151 | (let x (table) 1152 | (each (k v) (templates* tem) 1153 | (unless (no v) (= (x k) (v)))) 1154 | (each (k v) (pair args) 1155 | (= (x k) v)) 1156 | x)) 1157 | 1158 | ; To write something to be read by temread, (write (tablist x)) 1159 | 1160 | (def temread (tem (o str (stdin))) 1161 | (templatize tem (read str))) 1162 | 1163 | ; Converts alist to inst; ugly; maybe should make this part of coerce. 1164 | ; Note: discards fields not defined by the template. 1165 | 1166 | (def templatize (tem raw) 1167 | (with (x (inst tem) fields (templates* tem)) 1168 | (each (k v) raw 1169 | (when (assoc k fields) 1170 | (= (x k) v))) 1171 | x)) 1172 | 1173 | (def temload (tem file) 1174 | (w/infile i file (temread tem i))) 1175 | 1176 | (def temloadall (tem file) 1177 | (map (fn (pairs) (templatize tem pairs)) 1178 | (w/infile in file (readall in)))) 1179 | 1180 | 1181 | (def number (n) (in (type n) 'int 'num)) 1182 | 1183 | (def cache (timef valf) 1184 | (with (cached nil gentime nil) 1185 | (fn () 1186 | (unless (and cached (< (- (seconds) gentime) (timef))) 1187 | (= cached (valf) 1188 | gentime (seconds))) 1189 | cached))) 1190 | 1191 | (mac errsafe (expr) 1192 | `(on-err (fn (c) nil) 1193 | (fn () ,expr))) 1194 | 1195 | (def saferead (arg) (errsafe (read arg))) 1196 | 1197 | (def safe-load-table (filename) 1198 | (or (errsafe (load-table filename)) 1199 | (table))) 1200 | 1201 | (def ensure-dir (path) 1202 | (unless (dir-exists path) 1203 | (system (string "mkdir " path)))) 1204 | 1205 | (def date ((o time (seconds))) 1206 | (let val (tostring (system (string "date -u -r " time " \"+%Y-%m-%d\""))) 1207 | (subseq val 0 (- (len val) 1)))) 1208 | 1209 | (def since (t1) (- (seconds) t1)) 1210 | 1211 | (def count (test x) 1212 | (with (n 0 testf (testify test)) 1213 | (each elt x 1214 | (if (testf elt) (++ n))) 1215 | n)) 1216 | 1217 | (def ellipsize (str (o limit 80)) 1218 | (if (<= (len str) limit) 1219 | str 1220 | (+ (subseq str 0 limit) "..."))) 1221 | 1222 | (def random-elt (seq) (seq (rand (len seq)))) 1223 | 1224 | (mac until (test . body) 1225 | `(while (no ,test) ,@body)) 1226 | 1227 | (def before (x y seq (o i 0)) 1228 | (with (xp (pos x seq i) yp (pos y seq i)) 1229 | (and xp (or (no yp) (< xp yp))))) 1230 | 1231 | (def orf fns 1232 | (fn (x) (some [_ x] fns))) 1233 | 1234 | (def andf fns 1235 | (fn (x) (all [_ x] fns))) 1236 | 1237 | (def atend (i s) 1238 | (>= i (- (len s) 1))) 1239 | 1240 | (def multiple (x y) 1241 | (is 0 (mod x y))) 1242 | 1243 | (mac nor args `(no (or ,@args))) 1244 | 1245 | ; Consider making the default sort fn take compare's two args (when do 1246 | ; you ever have to sort mere lists of numbers?) and rename current sort 1247 | ; as prim-sort or something. 1248 | 1249 | ; Could simply modify e.g. > so that (> len) returned the same thing 1250 | ; as (compare > len). 1251 | 1252 | (def compare (comparer scorer) 1253 | (fn (x y) (comparer (scorer x) (scorer y)))) 1254 | 1255 | ; Cleaner thus, but may only ever need in 2 arg case. 1256 | 1257 | ;(def compare (comparer scorer) 1258 | ; (fn args (apply comparer map scorer args))) 1259 | 1260 | (def only (f g . args) 1261 | (aif (apply g args) (f it))) 1262 | 1263 | (mac conswhen (f x y) 1264 | (w/uniq (gf gx) 1265 | `(with (,gf ,f ,gx ,x) 1266 | (if (,gf ,gx) (cons ,gx ,y) ,y)))) 1267 | 1268 | ; Could rename this get, but don't unless it's frequently used. 1269 | 1270 | (def firstn-that (n f xs) 1271 | (if (or (<= n 0) (no xs)) 1272 | nil 1273 | (f (car xs)) 1274 | (cons (car xs) (firstn-that (- n 1) f (cdr xs))) 1275 | (firstn-that n f (cdr xs)))) 1276 | 1277 | (def dedup (xs) 1278 | (with (h (table) acc nil) 1279 | (each x xs 1280 | (unless (h x) 1281 | (push x acc) 1282 | (t! (h x)))) 1283 | (rev acc))) 1284 | 1285 | (def single (x) (and (acons x) (no (cdr x)))) 1286 | 1287 | (def plural (n str) 1288 | (if (or (is n 1) (single n)) 1289 | str 1290 | (string str "s"))) 1291 | 1292 | (def intersperse (x ys) 1293 | (cons (car ys) 1294 | (mappend [list x _] (cdr ys)))) 1295 | 1296 | (def counts (seq (o c (table))) 1297 | (if (no seq) 1298 | c 1299 | (do (zap [if _ (+ _ 1) 1] (c (car seq))) 1300 | (counts (cdr seq) c)))) 1301 | 1302 | (def commonest (seq) 1303 | (with (winner nil n 0) 1304 | (ontable k v (counts seq) 1305 | (when (> v n) (= winner k n v))) 1306 | (list winner n))) 1307 | 1308 | (def splitn (n xs) 1309 | (let acc nil 1310 | ((afn (n xs) 1311 | (if (or (no xs) (<= n 0)) 1312 | (list (rev acc) xs) 1313 | (do (push (car xs) acc) 1314 | (self (- n 1) (cdr xs))))) 1315 | n xs))) 1316 | 1317 | (def reduce (f xs) 1318 | (if (cddr xs) 1319 | (reduce f (cons (f (car xs) (cadr xs)) (cddr xs))) 1320 | (apply f xs))) 1321 | 1322 | (def rreduce (f xs) 1323 | (if (cddr xs) 1324 | (f (car xs) (rreduce f (cdr xs))) 1325 | (apply f xs))) 1326 | 1327 | (let argsym (uniq) 1328 | 1329 | (def parse-format (str) 1330 | (rev (accum a 1331 | (with (chars nil i -1) 1332 | (w/instring s str 1333 | (whilet c (readc s) 1334 | (case c 1335 | #\# (do (a (coerce (rev chars) 'string)) 1336 | (nil! chars) 1337 | (a (read s))) 1338 | #\~ (do (a (coerce (rev chars) 'string)) 1339 | (nil! chars) 1340 | (readc s) 1341 | (a (list argsym (++ i)))) 1342 | (push c chars)))) 1343 | (when chars 1344 | (a (coerce (rev chars) 'string))))))) 1345 | 1346 | (mac prf (str . args) 1347 | `(let ,argsym (list ,@args) 1348 | (pr ,@(parse-format str)))) 1349 | ) 1350 | 1351 | (def load (file) 1352 | (w/infile f file 1353 | (whilet e (read f) 1354 | (eval e)))) 1355 | 1356 | (def positive (x) 1357 | (and (number x) (> x 0))) 1358 | 1359 | (mac w/table (var . body) 1360 | `(let ,var (table) ,@body ,var)) 1361 | 1362 | (def ero args 1363 | (each a args 1364 | (write a (stderr)) 1365 | (writec #\space (stderr)))) 1366 | 1367 | (def queue () (list nil nil 0)) 1368 | 1369 | ; Despite call to atomic, once had some sign this wasn't thread-safe. 1370 | 1371 | (def enq (obj q) 1372 | (atomic 1373 | (++ (q 2)) 1374 | (if (no (car q)) 1375 | (= (cadr q) (= (car q) (list obj))) 1376 | (= (cdr (cadr q)) (list obj) 1377 | (cadr q) (cdr (cadr q)))) 1378 | (car q))) 1379 | 1380 | (def deq (q) 1381 | (atomic (unless (is (q 2) 0) (-- (q 2))) 1382 | (pop (car q)))) 1383 | 1384 | ; Should redef len to do this, and make queues lists annotated queue. 1385 | 1386 | (def qlen (q) (q 2)) 1387 | 1388 | (def qlist (q) (car q)) 1389 | 1390 | (def enq-limit (val q (o limit 1000)) 1391 | (atomic 1392 | (unless (< (qlen q) limit) 1393 | (deq q)) 1394 | (enq val q))) 1395 | 1396 | (def median (ns) 1397 | ((sort > ns) (truncate (/ (len ns) 2)))) 1398 | 1399 | (mac noisy-each (n var val . body) 1400 | (w/uniq (gn gc) 1401 | `(with (,gn ,n ,gc 0) 1402 | (each ,var ,val 1403 | (when (multiple (++ ,gc) ,gn) 1404 | (pr ".") 1405 | ;(flushout) 1406 | ) 1407 | ,@body) 1408 | (prn) 1409 | ;(flushout) 1410 | ))) 1411 | 1412 | (mac point (name . body) 1413 | (w/uniq g 1414 | `(ccc (fn (,g) 1415 | (let ,name [,g _] 1416 | ,@body))))) 1417 | 1418 | (mac catch body 1419 | `(point throw ,@body)) 1420 | 1421 | (def downcase (x) 1422 | (let downc (fn (c) 1423 | (let n (coerce c 'int) 1424 | (if (or (< 64 n 91) (< 191 n 215) (< 215 n 223)) 1425 | (coerce (+ n 32) 'char) 1426 | c))) 1427 | (case (type x) 1428 | string (map downc x) 1429 | char (downc x) 1430 | sym (sym (map downc (coerce x 'string))) 1431 | (err "Can't downcase" x)))) 1432 | 1433 | (def upcase (x) 1434 | (let upc (fn (c) 1435 | (let n (coerce c 'int) 1436 | (if (or (< 96 n 123) (< 223 n 247) (< 247 n 255)) 1437 | (coerce (- n 32) 'char) 1438 | c))) 1439 | (case (type x) 1440 | string (map upc x) 1441 | char (upc x) 1442 | sym (sym (map upc (coerce x 'string))) 1443 | (err "Can't upcase" x)))) 1444 | 1445 | (def range (start end) 1446 | (if (> start end) 1447 | nil 1448 | (cons start (range (+ start 1) end)))) 1449 | 1450 | (def mismatch (s1 s2) 1451 | (catch 1452 | (on c s1 1453 | (when (isnt c (s2 index)) 1454 | (throw index))))) 1455 | 1456 | (def memtable (ks) 1457 | (let h (table) 1458 | (each k ks (t! (h k))) 1459 | h)) 1460 | 1461 | (= bar* " | ") 1462 | 1463 | (mac w/bars body 1464 | (w/uniq (out needbars) 1465 | `(let ,needbars nil 1466 | (do ,@(map (fn (e) 1467 | `(let ,out (tostring ,e) 1468 | (unless (is ,out "") 1469 | (if ,needbars 1470 | (pr bar* ,out) 1471 | (do (t! ,needbars) 1472 | (pr ,out)))))) 1473 | body))))) 1474 | 1475 | 1476 | ; Lower priority ideas 1477 | 1478 | ; solution to the "problem" of improper lists: allow any atom as a list 1479 | ; terminator, not just nil. means list recursion should terminate on 1480 | ; atom rather than nil, (def empty (x) (or (atom x) (is x ""))) 1481 | ; table should be able to take an optional initial-value. handle in sref. 1482 | ; warn about code of form (if (= )) -- probably mean is 1483 | ; warn when a fn has a parm that's already defined as a macro. 1484 | ; (def foo (after) (after)) 1485 | ; idea: a fn (nothing) that returns a special gensym which is ignored 1486 | ; by map, so can use map in cases when don't want all the vals 1487 | ; idea: anaph macro so instead of (aand x y) say (anaph and x y) 1488 | ; idea: foo.bar!baz as an abbrev for (foo bar 'baz) 1489 | ; could uniq be (def uniq () (annotate 'symbol (list 'u))) again? 1490 | ; idea: use x- for (car x) and -x for (cdr x) (but what about math -?) 1491 | ; idea: get rid of strings and just use symbols 1492 | ; could a string be (#\a #\b . "") ? 1493 | ; better err msg when , outside of a bq 1494 | ; idea: parameter (p foo) means in body foo is (pair arg) 1495 | ; idea: make ('string x) equiv to (coerce x 'string) ? or isa? 1496 | ; quoted atoms in car valuable unused semantic space 1497 | 1498 |1499 |
1500 | ;;; Fixups to the standard library to get around incompatibilities in primitives, 1501 | ;;; mostly related to the lack of mutable strings 1502 | 1503 | (def map (f . seqs) 1504 | (if (some [isa _ 'string] seqs) 1505 | ; Doesn't even work in reference implementation...I wonder why I bother ;-) 1506 | (withs (n (apply min (map len seqs)) 1507 | lists (map [coerce _ 'cons] seqs)) 1508 | (apply f lists)) 1509 | (no (cdr seqs)) 1510 | (map1 f (car seqs)) 1511 | ((afn (seqs) 1512 | (if (some no seqs) 1513 | nil 1514 | (cons (apply f (map1 car seqs)) 1515 | (self (map1 cdr seqs))))) 1516 | seqs))) 1517 | 1518 | (def subseq (seq start (o end (len seq))) 1519 | (if (isa seq 'string) 1520 | (let s2 "" 1521 | (for i start (- end 1) 1522 | (= s2 (+ s2 (seq i)))) 1523 | s2) 1524 | (firstn (- end start) (nthcdr start seq)))) 1525 | 1526 | (def copy (x) 1527 | (case (type x) 1528 | sym x 1529 | cons (apply (fn args args) x) 1530 | string (subseq x 0) 1531 | table (let new (table) 1532 | (ontable k v x 1533 | (= (new k) v)) 1534 | new) 1535 | (err "Can't copy " x))) 1536 | 1537 |1538 | 1539 | 1540 | 1541 | 1542 | 1543 | 1544 | 1545 | 1623 | -------------------------------------------------------------------------------- /test/test_types.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 66 | -------------------------------------------------------------------------------- /types.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | /** 3 | * A representation for primitive types. Arc types inherit from a common 4 | * base class that provides defaults (all of which just raise an error) 5 | * for some of the basic operations that the interpreter performs. 6 | * 7 | * @dependency utils.js 8 | */ 9 | 10 | if(typeof window.Types != 'undefined') { 11 | var _Types = window.Types; 12 | } 13 | 14 | var ArcBase = { 15 | 16 | _not_defined: function(method) { 17 | $.error('' + method + ' not defined on ' + this._type); 18 | }, 19 | _cant_be_coerced: function(type) { 20 | $.error(this._type + " object can't be coerced to " + type); 21 | }, 22 | 23 | __type: function() { return this._type; }, 24 | __call: function(args) { return Types.bool(this.__veq(args[0])); }, 25 | __set: function(index, val) { this._not_defined('__set'); }, 26 | __coerce: function(type) { this._not_defined('coerce'); }, 27 | __to_js: function() { return this; }, 28 | __eq: function(js_obj) { return this.__to_js() == js_obj; }, 29 | __teq: function(js_obj) { return this.__type().__eq(js_obj); }, 30 | __veq: function(arc_obj) { return this.__eq(arc_obj.__to_js()); }, 31 | __print: function() { return Types.str(this.toString()); } 32 | }; 33 | 34 | function new_arc_type(base_proto, init, methods) { 35 | $.extend(base_proto, ArcBase); 36 | $.extend(base_proto, methods); 37 | return $.class_decorator(init, base_proto); 38 | }; 39 | 40 | function copy_number(val) { 41 | return new Number(val); 42 | }; 43 | 44 | function copy_string(val) { 45 | return new String(val); 46 | }; 47 | 48 | function make_tagged(type, val) { 49 | return { _val: val, _type: type, _tagged: true }; 50 | }; 51 | 52 | function make_cons(car, cdr) { 53 | $.assert('Passed a non-arc object as car of a list', car && car.__type); 54 | $.assert('Passed a non-arc object as cdr of a list', cdr && cdr.__type); 55 | return { _car: car, _cdr: cdr }; 56 | }; 57 | 58 | function make_table(val) { 59 | return val || {}; 60 | }; 61 | 62 | function make_function(args, body, env) { 63 | return { 64 | _args: args, 65 | _body: body, 66 | _env: env 67 | } 68 | }; 69 | 70 | function decorate_primitive(fn, name) { 71 | fn._name = name || 'unnamed'; 72 | return fn; 73 | }; 74 | 75 | // Must be declared outside because it's referenced in the definition of Types 76 | var sym = new_arc_type(new String(), copy_string, {}); 77 | $.extend(sym.__fake_proto, { 78 | _type: sym('sym'), 79 | __coerce: function(type) { 80 | if(type.__eq('string')) { 81 | return Types.str(this == Types.NIL ? '' : this.__to_js()) 82 | } else { 83 | return this._not_defined(type); 84 | } 85 | }, 86 | __to_list: function() { return []; }, // For nil 87 | __to_js: function() { 88 | var retval = this.toString(); 89 | if(retval == 't') return true; 90 | if(retval == 'nil') return false; 91 | return this.toString(); 92 | } 93 | }); 94 | 95 | var Types = window.Types = { 96 | 97 | no_conflict: function() { 98 | window.Types = _Types; 99 | return Types; 100 | }, 101 | 102 | tagged: new_arc_type(new Object(), make_tagged, { 103 | __call: function(args) { return this._val.__call.call(this._val, args); }, 104 | __set: function(index, val) { return this._val.__set.call(this._val, index, val); }, 105 | __print: function() { 106 | return Types.str('(tagged ' + this._type + ' ' + this._val.__print() + ')'); 107 | } 108 | }), 109 | 110 | sym: sym, 111 | NIL: sym('nil'), 112 | T: sym('t'), 113 | bool: function(val) { 114 | return val ? Types.T : Types.NIL; 115 | }, 116 | 117 | chr: new_arc_type(new String(), copy_string, { 118 | _type: sym('char'), 119 | __to_js: function() { return this.toString(); }, 120 | __coerce: function(type) { 121 | switch(type.__to_js()) { 122 | case 'int': return Types.int_(this.charCodeAt(0)); 123 | case 'string': return Types.str(this); 124 | case 'sym': return Types.sym(this); 125 | default: return this._cant_be_coerced(type); 126 | } 127 | }, 128 | __print: function() { return Types.str('#\\' + this); } 129 | }), 130 | 131 | str: new_arc_type(new String(), copy_string, { 132 | _type: sym('string'), 133 | __call: function(args) { 134 | return Types.chr(this.charAt(args[0])); 135 | }, 136 | __set: function(index, new_val) { 137 | // The spec says to mutate the string, but JavaScript strings are 138 | // immutable. Instead we return a copy with the specified character 139 | // changed, and hope that nobody was depending upon mutation 140 | return Types.str(this.substr(0, index) + new_val.toString() 141 | + this.substr(index + 1)); 142 | }, 143 | __coerce: function(type) { 144 | switch(type.__to_js()) { 145 | case 'sym': return Types.sym(this); 146 | case 'cons': 147 | var retval = []; 148 | for(var i = 0, len = this.length; i < len; ++i) { 149 | retval.push(Types.chr(this.charAt(i))); 150 | } 151 | return Types.list(retval); 152 | case 'int': 153 | var parsed = parseInt(this); 154 | if(isNaN(parsed)) { 155 | $.error("Can't coerce " + this + ' to int'); 156 | } 157 | return Types.int_(parsed); 158 | default: return this._cant_be_coerced(type); 159 | } 160 | }, 161 | __to_js: function() { return this.toString(); }, 162 | __print: function() { return Types.str('"' + this + '"'); } 163 | }), 164 | 165 | int_: new_arc_type(new Number(), copy_number, { 166 | _type: sym('int'), 167 | __coerce: function(type) { 168 | switch(type.__to_js()) { 169 | case 'char': return Types.chr(String.fromCharCode(this)); 170 | case 'string': return Types.str(this.toString()); 171 | default: return this._cant_be_coerced(type); 172 | } 173 | }, 174 | __to_js: function() { return parseInt(this.toString()); } 175 | }), 176 | 177 | num: new_arc_type(new Number(), copy_number, { 178 | _type: sym('num'), 179 | __coerce: function(type) { 180 | switch(type.__to_js()) { 181 | case 'int': return Types.int_(Math.round(this)); 182 | case 'char': return Types.chr(String.fromCharCode(Math.round(this))); 183 | case 'string': return Types.str(this.toString()); 184 | default: return this._cant_be_coerced(type); 185 | } 186 | }, 187 | __to_js: function() { return parseFloat(this.toString()); } 188 | }), 189 | 190 | cons: new_arc_type(new Object(), make_cons, { 191 | _type: sym('cons'), 192 | __index: function(index) { 193 | var current = this; 194 | for(var i = 0; i < index; ++i) { 195 | current = current._cdr; 196 | if(!current.__type().__eq('cons')) { 197 | $.error('Index out of range: ' + index); 198 | } 199 | } 200 | return current; 201 | }, 202 | __call: function(args) { 203 | return this.__index(args[0])._car; 204 | }, 205 | __set: function(index, new_val) { 206 | this.__index(index)._car = new_val; 207 | return new_val; 208 | }, 209 | __each: function(fn) { 210 | var current = this; 211 | while(current.__teq('cons')) { 212 | fn(current._car); 213 | current = current._cdr; 214 | } 215 | return current; 216 | }, 217 | __coerce: function(type) { 218 | if(type.__to_js() == 'string') { 219 | var accum = ''; 220 | function add_char(chr) { 221 | accum += String.fromCharCode(chr.__to_js()); 222 | }; 223 | var last = this.__each(add_char); 224 | if(!last == Types.NIL) { 225 | add_char(last); 226 | } 227 | return Types.str(accum); 228 | } else { 229 | return this._cant_be_coerced(type); 230 | } 231 | }, 232 | __to_list: function() { 233 | var retval = []; 234 | var last = this.__each(function(elem) { retval.push(elem); }); 235 | if(last != Types.NIL) { 236 | retval.push(last); 237 | } 238 | return retval; 239 | }, 240 | __print: function() { 241 | var segments = []; 242 | var last = this.__each(function(elem) { 243 | segments.push(elem.__print().toString()); 244 | }); 245 | if(last != Types.NIL) { 246 | segments.push('.'); 247 | segments.push(last.__print().toString()); 248 | } 249 | return Types.str('(' + segments.join(' ') + ')'); 250 | }, 251 | __to_js: function() { return this.__to_list(); } 252 | }), 253 | 254 | list: function(arr, improper) { 255 | if(arr.length == 0) { 256 | return Types.NIL; 257 | } 258 | 259 | var i = arr.length - 1; 260 | for(var current = improper ? arr[i--] : Types.NIL; i >= 0; --i) { 261 | current = Types.cons(arr[i], current); 262 | } 263 | return current; 264 | }, 265 | 266 | table: new_arc_type(new Object(), make_table, { 267 | _type: sym('table'), 268 | __call: function(args) { 269 | return this[args[0].toString()] || Types.NIL; 270 | }, 271 | __set: function(index, new_val) { 272 | if(new_val == Types.NIL) { 273 | delete this[index]; 274 | } else { 275 | this[index] = new_val; 276 | } 277 | return new_val; 278 | }, 279 | __length: function() { 280 | var len = 0; 281 | Utils.prop_map(this, function() { len++; }); 282 | return len; 283 | }, 284 | __print: function() { 285 | var text = '#hash('; 286 | Utils.prop_map(this, function(v, key) { text += '(' + key + ' . ' + ')'; }); 287 | return Types.str(text + ')'); 288 | }, 289 | __to_js: function() { 290 | return Utils.extend({}, this); 291 | } 292 | }), 293 | 294 | fn: new_arc_type(new Object(), make_function, { 295 | _type: sym('fn'), 296 | __call: 'native', 297 | __print: function() { 298 | var argstr = this._args.__teq('cons') 299 | ? '(' + this._args.__to_list().join(' ') + ')' 300 | : this._args.toString(); 301 | return Types.str('#procedure: ' + argstr); 302 | } 303 | }), 304 | 305 | primitive: new_arc_type(new Function(), decorate_primitive, { 306 | _type: sym('fn'), 307 | __call: function(args) { 308 | if(this._varargs === undefined && args.length != this.length) { 309 | Utils.error(this._name + ' takes ' + this.length + 310 | ' arguments, found ' + Types.list(args).__print()); 311 | } 312 | if(this._varargs !== undefined && args.length < this._varargs) { 313 | Utils.error(this._name + ' takes at least ' + this.length + 314 | ' arguments, found ' + Types.list(args).__print()); 315 | 316 | } 317 | var result = this.apply(null, args); 318 | Utils.assert(this._name + ' did not return a value.', result); 319 | return result; 320 | }, 321 | __print: function() { 322 | return Types.str('#primitive procedure: ' + this._name); 323 | } 324 | }), 325 | 326 | to_js: function(arc_obj) { 327 | var result = arc_obj.__to_js(); 328 | return Utils.prop_map(result, Types.to_js); 329 | }, 330 | 331 | to_arc: function(js_obj) { 332 | switch(typeof js_obj) { 333 | case 'number': return Types.wrap_num(js_obj); 334 | case 'string': return Types.str(js_obj); 335 | case 'boolean': return Types.bool(js_obj); 336 | case 'function': return Types.primitive(js_obj); 337 | case 'undefined': return Types.NIL; 338 | default: 339 | if(js_obj instanceof Array) { 340 | return Types.list(Utils.map(js_obj, Types.to_arc)); 341 | } else { 342 | return Types.table(Utils.prop_map(Utils.extend({}, js_obj), 343 | Types._to_arc)); 344 | } 345 | } 346 | }, 347 | 348 | /** Detects the type of the result and wraps it with a num or int_ */ 349 | wrap_num: function(result) { 350 | var constr = result == Math.round(result) ? Types.int_ : Types.num; 351 | return constr(result); 352 | } 353 | 354 | }; 355 | 356 | })(Utils); 357 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | /** 3 | * Some basic utility functions. I want to avoid coupling this to large 4 | * JavaScript libraries. 5 | */ 6 | if(typeof window.Utils != 'undefined') { 7 | var _Utils = window.Utils; 8 | } 9 | 10 | function ArcError(msg, expr) { 11 | this.message = msg; 12 | this.expr = expr; 13 | }; 14 | ArcError.prototype = new Error(); 15 | ArcError.prototype.name = 'ArcError'; 16 | 17 | var Utils = window.Utils = { 18 | 19 | no_conflict: function() { 20 | window.Utils = _Utils; 21 | return Utils; 22 | }, 23 | 24 | extend: function(dest, src) { 25 | for(var prop in src) { 26 | if(src.hasOwnProperty(prop)) { 27 | dest[prop] = src[prop]; 28 | } 29 | } 30 | return dest; 31 | }, 32 | 33 | class_decorator: function(init, new_proto) { 34 | new_proto.__init = init; 35 | var constr = function() { 36 | var self = init.apply(this, arguments); 37 | var args = [self.__proto__].concat([].slice.call(arguments, 1)); 38 | self.__proto__ = new_proto; 39 | return self; 40 | }; 41 | constr.__fake_proto = new_proto; 42 | return constr; 43 | }, 44 | 45 | invoke: function(collection, method) { 46 | var accum = [], 47 | args = [].slice.call(arguments, 2); 48 | for(var i = 0, len = collection.length; i < len; ++i) { 49 | var item = collection[i]; 50 | accum.push(item[method].apply(item, args)); 51 | } 52 | return accum; 53 | }, 54 | 55 | map: function(collection, fn) { 56 | var accum = []; 57 | for(var i = 0, len = collection.length; i < len; ++i) { 58 | accum.push(fn(collection[i], i)); 59 | } 60 | return accum; 61 | }, 62 | 63 | prop_map: function(collection, fn) { 64 | for(var prop in collection) { 65 | if(collection.hasOwnProperty(prop)) { 66 | collection[prop] = fn(collection[prop], prop); 67 | } 68 | } 69 | return collection; 70 | }, 71 | 72 | ensure_fn: function(fn) { 73 | if(typeof fn == 'string') { 74 | fn = eval('false || function(x, y) { return ' + fn + '; }'); 75 | }; 76 | return fn; 77 | }, 78 | 79 | all: function(args, pred) { 80 | pred = Utils.ensure_fn(pred); 81 | for(var i = 0, len = args.length; i < len; ++i) { 82 | if(!pred(args[i])) { 83 | return false; 84 | } 85 | } 86 | return true; 87 | }, 88 | 89 | pairwise: function(args, base, pred) { 90 | if(args.length == 0) { 91 | return base; 92 | } 93 | pred = Utils.ensure_fn(pred); 94 | 95 | for(var i = 0, len = args.length - 1; i < len; ++i) { 96 | if(!pred(args[i], args[i + 1])) { 97 | return false; 98 | } 99 | } 100 | return true; 101 | }, 102 | 103 | fold1: function(args, fn) { 104 | if(args.length == 0) { 105 | return 0; 106 | } else if(args.length == 1) { 107 | return args[0]; 108 | } 109 | fn = Utils.ensure_fn(fn); 110 | 111 | var base = args[0]; 112 | for(var i = 1, len = args.length; i < len; ++i) { 113 | base = fn(base, args[i]); 114 | } 115 | return base; 116 | }, 117 | 118 | assert: function(msg, condition) { 119 | if(!condition) { 120 | Utils.error('Assertion failure: ' + msg); 121 | } 122 | }, 123 | 124 | error: function(msg, expr) { 125 | throw new ArcError(msg, expr); 126 | }, 127 | 128 | id: function(val) { 129 | return val; 130 | }, 131 | 132 | ArcError: ArcError 133 | 134 | }; 135 | 136 | })(); 137 | --------------------------------------------------------------------------------