Merge branch 'hashtables'
[jscl.git] / src / prelude.js
1 // This file is prepended to the result of compile jscl.lisp, and
2 // contain runtime code that jscl assumes to exist.
3
4 var window = this;
5 var nil;
6
7 var lisp = {};
8
9 globalEval = eval;  // Just an indirect eval
10
11 function pv (x) { return x==undefined? nil: x; }
12
13 function mv(){
14     var r = [].slice.call(arguments);
15     r['multiple-value'] = true;
16     return r;
17 }
18
19 function forcemv (x) {
20     return typeof x == 'object' && 'multiple-value' in x? x: mv(x);
21 }
22
23 // NOTE: Define VALUES to be MV for toplevel forms. It is because
24 // `eval' compiles the forms and execute the Javascript code at
25 // toplevel with `js-eval', so it is necessary to return multiple
26 // values from the eval function.
27 var values = mv;
28
29 function checkArgsAtLeast(args, n){
30     if (args < n) throw 'too few arguments';
31 }
32
33 function checkArgsAtMost(args, n){
34     if (args > n) throw 'too many arguments';
35 }
36
37 function checkArgs(args, n){
38     checkArgsAtLeast(args, n);
39     checkArgsAtMost(args, n);
40 }
41
42 // Improper list constructor (like LIST*)
43 function QIList(){
44     if (arguments.length == 1)
45         return arguments[0];
46     else {
47         var i = arguments.length-1;
48         var r = arguments[i--];
49         for (; i>=0; i--){
50             r = {car: arguments[i], cdr: r};
51         }
52         return r;
53     }
54 }
55
56 // Return a new Array of strings, each either length-1, or length-2 (a UTF-16 surrogate pair).
57 function codepoints(string) {
58     return string.split(/(?![\udc00-\udfff])/);
59 }
60
61 // Create and return a lisp string for the Javascript string STRING.
62 function make_lisp_string (string){
63     var array = codepoints(string);
64     array.stringp = 1
65     return array;
66 }
67
68 function char_to_codepoint(ch) {
69     if (ch.length == 1) {
70         return ch.charCodeAt(0);
71     } else {
72         var xh = ch.charCodeAt(0) - 0xD800;
73         var xl = ch.charCodeAt(1) - 0xDC00;
74         return 0x10000 + (xh << 10) + (xl);
75     }
76 }
77
78 function char_from_codepoint(x) {
79     if (x <= 0xFFFF) {
80         return String.fromCharCode(x);
81     } else {
82         x -= 0x10000;
83         var xh = x >> 10;
84         var xl = x & 0x3FF;
85         return String.fromCharCode(0xD800 + xh) + String.fromCharCode(0xDC00 + xl);
86     }
87 }
88
89 // if a char (JS string) has the same number of codepoints after .toUpperCase(), return that, else the original.
90 function safe_char_upcase(x) {
91     var xu = x.toUpperCase();
92     if (codepoints(xu).length == 1) {
93         return xu;
94     } else {
95         return x;
96     }
97 }
98 function safe_char_downcase(x) {
99     var xl = x.toLowerCase();
100     if (codepoints(xl).length == 1) {
101         return xl;
102     } else {
103         return x;
104     }
105 }
106
107 function xstring(x){ return x.join(''); }
108
109
110 function Symbol(name, package_name){
111     this.name = name;
112     if (package_name)
113         this['package'] = package_name;
114 }
115
116 function lisp_to_js (x) {
117     if (typeof x == 'object' && 'length' in x && x.stringp == 1)
118         return xstring(x);
119     else if (typeof x == 'function'){
120         // Trampoline calling the Lisp function
121         return (function(){
122             var args = Array.prototype.slice.call(arguments);
123             for (var i in args)
124                 args[i] = js_to_lisp(args[i]);
125             return lisp_to_js(x.apply(this, [pv, arguments.length].concat(args)));
126         });
127     }
128     else return x;
129 }
130
131 function js_to_lisp (x) {
132     if (typeof x == 'string')
133         return make_lisp_string(x);
134     else if (typeof x == 'function'){
135         // Trampoline calling the JS function
136         return (function(values, nargs){
137             var args = Array.prototype.slice.call(arguments, 2);
138             for (var i in args)
139                 args[i] = lisp_to_js(args[i]);
140             return values(js_to_lisp(x.apply(this, args)));
141         });
142     } else return x;
143 }