1 import { MalType, MalList, MalString, MalNumber, MalBoolean, MalNil, MalKeyword, MalSymbol, MalVector, MalHashMap } from "./types";
6 constructor(private tokens: string[]) { }
9 const ret = this.peek();
15 return this.tokens[this.position];
19 export function readStr(input: string): MalType {
20 const tokens = tokenizer(input);
21 const reader = new Reader(tokens);
22 return readForm(reader);
25 function tokenizer(input: string): string[] {
26 const regexp = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/g;
27 const tokens: string[] = [];
29 const matches = regexp.exec(input);
33 const match = matches[1];
37 if (match[0] !== ";") {
45 function readForm(reader: Reader): MalType {
46 const token = reader.peek();
49 return readList(reader);
51 return readVector(reader);
53 return readHashMap(reader);
55 return readSymbol("quote");
57 return readSymbol("quasiquote");
59 return readSymbol("unquote");
61 return readSymbol("splice-unquote");
63 return readSymbol("deref");
67 const sym = MalSymbol.get("with-meta");
68 const target = readForm(reader);
69 return new MalList([sym, readForm(reader), target]);
72 return readAtom(reader);
75 function readSymbol(name: string) {
77 const sym = MalSymbol.get(name);
78 const target = readForm(reader);
79 return new MalList([sym, target]);
83 function readList(reader: Reader): MalType {
84 return readParen(reader, MalList, "(", ")");
87 function readVector(reader: Reader): MalType {
88 return readParen(reader, MalVector, "[", "]");
91 function readHashMap(reader: Reader): MalType {
92 return readParen(reader, MalHashMap, "{", "}");
95 function readParen(reader: Reader, ctor: { new (list: MalType[]): MalType; }, open: string, close: string): MalType {
96 const token = reader.next(); // drop open paren
98 throw new Error(`unexpected token ${token}, expected ${open}`);
100 const list: MalType[] = [];
102 const next = reader.peek();
103 if (next === close) {
106 throw new Error("unexpected EOF");
108 list.push(readForm(reader));
110 reader.next(); // drop close paren
112 return new ctor(list);
115 function readAtom(reader: Reader): MalType {
116 const token = reader.next();
117 if (token.match(/^-?[0-9]+$/)) {
118 const v = parseInt(token, 10);
119 return new MalNumber(v);
121 if (token.match(/^-?[0-9]\.[0-9]+$/)) {
122 const v = parseFloat(token);
123 return new MalNumber(v);
125 if (token[0] === '"') {
126 const v = token.slice(1, token.length - 1)
127 .replace(/\\(.)/g, (_, c: string) => c == 'n' ? '\n' : c)
128 return new MalString(v);
130 if (token[0] === ":") {
131 return MalKeyword.get(token.substr(1));
135 return MalNil.instance;
137 return new MalBoolean(true);
139 return new MalBoolean(false);
142 return MalSymbol.get(token);