-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathparser.ts
237 lines (218 loc) · 6.17 KB
/
parser.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
import { Tokenizer, IToken } from '../tokenizer/tokenizer'
import { Stmt, DeclareStmt, Program, Function, Block, IfStmt, ReturnStmt } from './statement'
import { Expr, FunctionCallExpr, AssignExpr, Args } from './expression'
import { Terminal, Identifier, Numeral } from './terminal'
import { expressionParser } from './exprParser'
/**
* 自顶部向下递归+lookahead一个token的parser
* Program -> Stmts
* Stmts -> Stmt Stmts | ϵ
* Stmt -> AssignExpr | IfStmt | WhileStmt | Function | Block | ...
* xxStmt -> Expr | Stmt | Terminal
* Expr -> Terminal
*/
export class Parser {
public tokens: Array<IToken> //token流
public lookahead: IToken //下一个token
private index: number //第index个token
//语法分析函数
parse(sourceCode): Program {
//从词法分析器拿到tokens
this.tokens = new Tokenizer(sourceCode).tokens
// 增加一个哨兵,用于判断结尾
this.tokens.push({ type: 'eof', value: null })
this.index = 0
this.lookahead = this.tokens[this.index++]
const program: Program = this.parseProgram()
program.buildLexicalScope()
return program
}
//向前读一个token:封装index,避免危险操作
read(): void {
if (this.lookahead.type !== 'eof') {
this.lookahead = this.tokens[this.index++]
}
}
//校验token,并向前读一个
match(value): string {
if (this.lookahead.value === value) {
this.read()
return value
}
throw `syntax error @line ${this.lookahead.lineNo} : expect ${value} here but ${this.lookahead.value} found.`
}
//校验类型(静态语言),并向前读一个
matchType(type): void {
if (this.lookahead.type === type) {
this.read()
}
throw 'syntax error'
}
//解析表达式
parseExpr(): Expr | Terminal {
return new expressionParser(this).exprParser()
}
/**
* Stmts -> Stmt Stmts | ϵ
*/
parseStmts(): Array<Stmt | Expr | Terminal> {
const stmts = []
while (this.lookahead.type !== 'eof' && this.lookahead.value !== '}') {
stmts.push(this.parseStmt())
}
return stmts
}
/**
* Program -> Stmts
*/
parseProgram(): Program {
return new Program(this.parseStmts())
}
/**
* Stmt -> AssignExpr | IfStmt | WhileStmt | Function | Block | ...
* AssignExpr -> var <id> = Expr
* IfStmt -> if Expr Block else IfStmt | if Expr Block | Stmt
*/
parseStmt(): Stmt | Expr | Terminal {
if (this.lookahead.type === 'id' || this.lookahead.type === 'number') {
return this.parseExpr()
}
switch (this.lookahead.value) {
case 'var':
return this.parseDeclareStmt()
case 'function':
return this.parseFunctionStmt()
case 'if':
return this.parseIfStmt()
case 'return':
return this.parseReturnStmt()
default:
console.log(this.lookahead)
throw `syntax error @line ${this.lookahead.lineNo} : not impl. ${this.lookahead.value}`
}
}
parseBlock(): Block {
this.match('{')
const stmts = this.parseStmts()
this.match('}')
return new Block(stmts)
}
/**
* FunctionStmt -> function {id}(...ARGS) BLOCK
*/
parseFunctionStmt(): Function {
this.match('function')
if (this.lookahead.type !== 'id') {
throw 'syntax error'
}
const id = this.lookahead.value
this.match(id)
this.match('(')
const args = this.parseFuncArguments()
this.match(')')
const block: Block = this.parseBlock()
return new Function(new Identifier(id), args, block)
}
/**
* ReturnStmt -> return Expr
*/
parseReturnStmt(): ReturnStmt {
this.match('return')
const expr = this.parseExpr()
return new ReturnStmt(expr)
}
/**
* Args -> <id> | <id>,Args | ϵ
*/
parseFuncArguments(): Args {
let list = []
if (this.lookahead.type === 'id') {
const id = this.lookahead.value
this.match(id)
list.push(new Identifier(id))
if (this.lookahead.value === ',') {
this.match(',')
list = list.concat(this.parseFuncArguments())
}
}
// else {
// return []
// }
return new Args(list, 'function')
}
/**
* fn(args)函数调用的参数解析
*/
parseArguments(): Args {
let list = []
let expr: Expr | Terminal = null
while ((expr = this.parseExpr())) {
list.push(expr)
}
return new Args(list)
}
/**
* IfStmt -> if Expr Block | if Expr Block else IfStmt | if Expr Block else Block
*/
parseIfStmt(): IfStmt {
this.match('if')
const expr = this.parseExpr()
const ifBlock = this.parseBlock()
if (this.lookahead.value === 'else') {
this.match('else')
// @ts-ignore
if (this.lookahead.value === 'if') {
const ifStmt = this.parseIfStmt()
return new IfStmt(expr, ifBlock, ifStmt)
}
else {
const elseBlock = this.parseBlock()
return new IfStmt(expr, ifBlock, null, elseBlock)
}
} else {
return new IfStmt(expr, ifBlock)
}
}
/**
* DeclareStmt -> var id = expr
*/
parseDeclareStmt(): DeclareStmt {
this.match('var')
if (this.lookahead.type !== 'id') {
throw 'syntax error'
}
const id = new Identifier(this.lookahead.value)
this.match(this.lookahead.value)
this.match('=')
const right = this.parseExpr()
return new DeclareStmt(id, right)
}
/**
* factor -> number | string | id
*/
parseFactor(): Terminal | Expr {
if (this.lookahead.type === 'number') {
const value = this.match(this.lookahead.value)
return new Numeral(value)
}
else if (this.lookahead.type === 'id') {
const value = this.match(this.lookahead.value)
if (this.lookahead.value === '(') {
this.match('(')
const args = this.parseArguments()
this.match(')')
return new FunctionCallExpr(new Identifier(value), args)
}
else if (this.lookahead.value === '=') {
this.match('=')
const expr = this.parseExpr()
return new AssignExpr(new Identifier(value), expr)
}
return new Identifier(value)
} else if (this.lookahead.type === 'string') {
throw 'not impl.'
} else {
throw `syntax error, expect a factor but ${this.lookahead.value} found`
}
}
}