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