Merge branch 'master' into issue_166_string_ops
[jackhill/mal.git] / es6 / reader.js
CommitLineData
afa79313 1import { _symbol, _keyword, _vector, _hash_map } from './types';
4eb71990
JM
2
3export class BlankException extends Error {}
4
5class 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
14function 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
25function 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
51function 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
70function read_vector(reader) {
71 return _vector(...read_list(reader, '[', ']'));
72}
73
74// read hash-map key/value pairs
75function read_hash_map(reader) {
76 return _hash_map(...read_list(reader, '{', '}'));
77}
78
4eb71990
JM
79function 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
115export 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