-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparse.ts
120 lines (103 loc) · 2.52 KB
/
parse.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import {
Bool,
List,
Name,
Node,
Num,
ShouldNotGetHere,
Token,
} from './header.ts';
let log = console.log;
function tokenValue(tok: Token) {
return tok.source.slice(tok.start, tok.start + tok.len);
}
interface Parser {
tokens: Token[];
pos: number;
current: Token;
}
function next(p: Parser) {
p.pos++;
p.current = p.tokens[p.pos];
}
// S-Expression Grammar
//
// Node ::= Bool | Num | Name # Atoms
// | List # Compound data
//
// List ::= '(' Str Node* ')'
// | '[' Str Node* ']' # Clojure-like sugar
//
// Program ::= Node # Top level
// TODO:
// - What is the () value?
// (cons 1 (cons 2 ()) -> (1 2)
export function parseNode(p: Parser): Node {
switch (p.current.id) {
case 'eof':
throw { message: 'Unexpected end of file', loc: p.pos };
case 'lparen':
return parseList(p, 'rparen');
case 'lbrack':
return parseList(p, 'rbrack');
case 'rparen':
throw { message: 'Unexpected )', loc: p.pos };
case 'rbrack':
throw { message: 'Unexpected ]', loc: p.pos };
case 'bool': {
let value = p.current.source[p.current.start] === 't';
let b: Bool = { tag: 'Bool', value, loc: p.pos };
next(p);
return b;
}
case 'int': {
let value = parseInt(tokenValue(p.current));
let i: Num = { tag: 'Num', value, loc: p.pos };
next(p);
return i;
}
case 'name': {
let s: Name = { tag: 'Name', value: tokenValue(p.current), loc: p.pos };
next(p);
return s;
}
case 'BAD':
throw { message: 'Bad characters', loc: p.pos };
default:
//log('tok ' + JSON.stringify(p.current))
throw ShouldNotGetHere;
}
}
function parseList(p: Parser, end_id: string): List {
// Sometimes we want to point to (
// let paren_pos = p.pos;
next(p); // eat (
if (p.current.id !== 'name') {
throw { message: 'Expected name after (', loc: p.pos };
}
let list: List = {
tag: 'List',
name: tokenValue(p.current),
// loc: paren_pos,
loc: p.pos,
args: [],
};
next(p); // move past head
while (p.current.id !== end_id) {
list.args.push(parseNode(p));
}
next(p); // eat rparen / rbrack
return list;
}
export function parse(tokens: Token[]): Node {
let p = { tokens, pos: 0, current: tokens[0] };
let node = parseNode(p);
// We only parse one expression
if (p.current.id !== 'eof') {
throw {
message: `Extra token ${p.current.id} at position ${p.pos}`,
loc: p.pos,
};
}
return node;
}