1 // Node vs browser behavior
3 if (typeof module
!== 'undefined') {
4 var types
= require('./types');
9 function Reader(tokens
) {
11 this.tokens
= tokens
.map(function (a
) { return a
; });
14 Reader
.prototype.next = function() { return this.tokens
[this.position
++]; }
15 Reader
.prototype.peek = function() { return this.tokens
[this.position
]; }
17 function tokenize(str
) {
18 var re
= /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*)/g;
20 while ((match
= re
.exec(str
)[1]) != '') {
21 if (match
[0] === ';') { continue; }
27 function read_atom (reader
) {
28 var token
= reader
.next();
29 //console.log("read_atom:", token);
30 if (token
.match(/^-?[0-9]+$/)) {
31 return parseInt(token
,10) // integer
32 } else if (token
.match(/^-?[0-9][0-9.]*$/)) {
33 return parseFloat(token
,10); // float
34 } else if (token
.match(/^"(?:\\.|[^\\"])*"$/)) {
35 return token
.slice(1,token
.length
-1)
36 .replace(/\\(.)/g, function (_
, c
) { return c
=== "n" ? "\n" : c
})
37 } else if (token
[0] === "\"") {
38 throw new Error("expected '\"', got EOF");
39 } else if (token
[0] === ":") {
40 return types
._keyword(token
.slice(1));
41 } else if (token
=== "nil") {
43 } else if (token
=== "true") {
45 } else if (token
=== "false") {
48 return types
._symbol(token
); // symbol
52 // read list of tokens
53 function read_list(reader
, start
, end
) {
57 var token
= reader
.next();
58 if (token
!== start
) {
59 throw new Error("expected '" + start
+ "'");
61 while ((token
= reader
.peek()) !== end
) {
63 throw new Error("expected '" + end
+ "', got EOF");
65 ast
.push(read_form(reader
));
71 // read vector of tokens
72 function read_vector(reader
) {
73 var lst
= read_list(reader
, '[', ']');
74 return types
._vector
.apply(null, lst
);
77 // read hash-map key/value pairs
78 function read_hash_map(reader
) {
79 var lst
= read_list(reader
, '{', '}');
80 return types
._hash_map
.apply(null, lst
);
83 function read_form(reader
) {
84 var token
= reader
.peek();
86 // reader macros/transforms
87 case ';': return null; // Ignore comments
88 case '\'': reader
.next();
89 return [types
._symbol('quote'), read_form(reader
)];
90 case '`': reader
.next();
91 return [types
._symbol('quasiquote'), read_form(reader
)];
92 case '~': reader
.next();
93 return [types
._symbol('unquote'), read_form(reader
)];
94 case '~@': reader
.next();
95 return [types
._symbol('splice-unquote'), read_form(reader
)];
96 case '^': reader
.next();
97 var meta
= read_form(reader
);
98 return [types
._symbol('with-meta'), read_form(reader
), meta
];
99 case '@': reader
.next();
100 return [types
._symbol('deref'), read_form(reader
)];
103 case ')': throw new Error("unexpected ')'");
104 case '(': return read_list(reader
);
107 case ']': throw new Error("unexpected ']'");
108 case '[': return read_vector(reader
);
111 case '}': throw new Error("unexpected '}'");
112 case '{': return read_hash_map(reader
);
115 default: return read_atom(reader
);
119 function BlankException(msg
) {
122 function read_str(str
) {
123 var tokens
= tokenize(str
);
124 if (tokens
.length
=== 0) { throw new BlankException(); }
125 return read_form(new Reader(tokens
))
128 exports
.Reader
= reader
.Reader
= Reader
;
129 exports
.BlankException
= reader
.BlankException
= BlankException
;
130 exports
.tokenize
= reader
.tokenize
= tokenize
;
131 exports
.read_form
= reader
.read_form
= read_form
;
132 exports
.read_str
= reader
.read_str
= read_str
;