Commit | Line | Data |
---|---|---|
afa79313 | 1 | import { _symbol, _keyword, _vector, _hash_map } from './types'; |
4eb71990 JM |
2 | |
3 | export class BlankException extends Error {} | |
4 | ||
5 | class Reader { | |
6 | constructor(tokens) { | |
7 | this.tokens = tokens; | |
8 | this.position = 0; | |
9 | } | |
10 | next() { return this.tokens[this.position++]; } | |
11 | peek() { return this.tokens[this.position]; } | |
12 | } | |
13 | ||
14 | function tokenize(str) { | |
15 | const re = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/g; | |
16 | let match = null; | |
17 | let results = []; | |
18 | while ((match = re.exec(str)[1]) != '') { | |
19 | if (match[0] === ';') { continue; } | |
20 | results.push(match); | |
21 | } | |
22 | return results; | |
23 | } | |
24 | ||
25 | function read_atom (reader) { | |
26 | const token = reader.next(); | |
27 | //console.log("read_atom:", token); | |
28 | if (token.match(/^-?[0-9]+$/)) { | |
29 | return parseInt(token,10) // integer | |
30 | } else if (token.match(/^-?[0-9][0-9.]*$/)) { | |
31 | return parseFloat(token,10); // float | |
32 | } else if (token[0] === "\"") { | |
33 | return token.slice(1,token.length-1) | |
34 | .replace(/\\"/g, '"') | |
8d78bc26 JM |
35 | .replace(/\\n/g, "\n") |
36 | .replace(/\\\\/g, "\\"); // string | |
4eb71990 | 37 | } else if (token[0] === ":") { |
afa79313 | 38 | return _keyword(token.slice(1)); |
4eb71990 JM |
39 | } else if (token === "nil") { |
40 | return null; | |
41 | } else if (token === "true") { | |
42 | return true; | |
43 | } else if (token === "false") { | |
44 | return false; | |
45 | } else { | |
afa79313 | 46 | return _symbol(token); // symbol |
4eb71990 JM |
47 | } |
48 | } | |
49 | ||
50 | // read list of tokens | |
51 | function read_list(reader, start, end) { | |
52 | start = start || '('; | |
53 | end = end || ')'; | |
54 | var ast = []; | |
55 | var token = reader.next(); | |
56 | if (token !== start) { | |
57 | throw new Error("expected '" + start + "'"); | |
58 | } | |
59 | while ((token = reader.peek()) !== end) { | |
60 | if (!token) { | |
61 | throw new Error("expected '" + end + "', got EOF"); | |
62 | } | |
63 | ast.push(read_form(reader)); | |
64 | } | |
65 | reader.next(); | |
66 | return ast; | |
67 | } | |
68 | ||
afa79313 JM |
69 | // read vector of tokens |
70 | function read_vector(reader) { | |
71 | return _vector(...read_list(reader, '[', ']')); | |
72 | } | |
73 | ||
74 | // read hash-map key/value pairs | |
75 | function read_hash_map(reader) { | |
76 | return _hash_map(...read_list(reader, '{', '}')); | |
77 | } | |
78 | ||
4eb71990 JM |
79 | function read_form(reader) { |
80 | var token = reader.peek(); | |
81 | switch (token) { | |
82 | // reader macros/transforms | |
83 | case ';': return null; // Ignore comments | |
84 | case '\'': reader.next(); | |
afa79313 | 85 | return [_symbol('quote'), read_form(reader)]; |
4eb71990 | 86 | case '`': reader.next(); |
afa79313 | 87 | return [_symbol('quasiquote'), read_form(reader)]; |
4eb71990 | 88 | case '~': reader.next(); |
afa79313 | 89 | return [_symbol('unquote'), read_form(reader)]; |
4eb71990 | 90 | case '~@': reader.next(); |
afa79313 | 91 | return [_symbol('splice-unquote'), read_form(reader)]; |
4eb71990 JM |
92 | case '^': reader.next(); |
93 | var meta = read_form(reader); | |
afa79313 | 94 | return [_symbol('with-meta'), read_form(reader), meta]; |
4eb71990 | 95 | case '@': reader.next(); |
afa79313 | 96 | return [_symbol('deref'), read_form(reader)]; |
4eb71990 JM |
97 | |
98 | // list | |
99 | case ')': throw new Error("unexpected ')'"); | |
100 | case '(': return read_list(reader); | |
101 | ||
afa79313 JM |
102 | // vector |
103 | case ']': throw new Error("unexpected ']'"); | |
104 | case '[': return read_vector(reader); | |
105 | ||
106 | // hash-map | |
107 | case '}': throw new Error("unexpected '}'"); | |
108 | case '{': return read_hash_map(reader); | |
109 | ||
4eb71990 JM |
110 | // atom |
111 | default: return read_atom(reader); | |
112 | } | |
113 | } | |
114 | ||
115 | export function read_str(str) { | |
116 | var tokens = tokenize(str); | |
117 | if (tokens.length === 0) { throw new BlankException(); } | |
118 | return read_form(new Reader(tokens)) | |
119 | } | |
120 |