-
Notifications
You must be signed in to change notification settings - Fork 46
/
parse.bal
223 lines (201 loc) · 7.12 KB
/
parse.bal
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
// Parse one file in a module
import wso2/nballerina.comm.err;
import wso2/nballerina.comm.diagnostic as d;
public type FilePath record {|
string filename;
string? directory = ();
|};
public readonly class ScannedModulePart {
private TokenizerState tokState;
public ImportDecl[] importDecls;
int partIndex;
function init(ImportDecl[] decls, Tokenizer tok, int partIndex) {
self.importDecls = decls.cloneReadOnly();
self.tokState = tok.save();
self.partIndex = partIndex;
}
function tokenizer() returns Tokenizer {
Tokenizer tok = new(self.tokState.file);
tok.restore(self.tokState);
return tok;
}
public function sourceFile() returns SourceFile {
return self.tokState.file;
}
}
public function scanModulePart(SourceFile file, int partIndex) returns ScannedModulePart|err:Syntax {
Tokenizer tok = new (file);
check tok.advance();
return new(check parseImportDecls(tok, partIndex), tok, partIndex);
}
public function parseModulePart(ScannedModulePart scanned) returns ModulePart|err:Syntax {
Tokenizer tok = scanned.tokenizer();
ModuleLevelDefn[] defns = [];
ModulePart part = {
file: scanned.sourceFile(),
partIndex: scanned.partIndex,
defns,
importDecls: scanned.importDecls
};
while tok.current() != () {
defns.push(check parseModuleDecl(tok, part));
}
return part;
}
public function parseExpression(SourceFile file) returns Expr|err:Syntax {
Tokenizer tok = new (file);
check tok.advance();
Expr expr = check parseExpr(tok);
if tok.current() != () {
return parseError(tok, "unexpected input after expression");
}
return expr;
}
function parseImportDecls(Tokenizer tok, int partIndex) returns ImportDecl[]|err:Syntax {
ImportDecl[] imports = [];
Token? t = tok.current();
while t == "import" {
ImportDecl im = check parseImportDecl(tok, partIndex);
imports.push(im);
t = tok.current();
}
return imports;
}
function parseImportDecl(Tokenizer tok, int partIndex) returns ImportDecl|err:Syntax {
Position startPos = tok.currentStartPos();
check tok.advance();
Position namePos = tok.currentStartPos();
string firstModuleName = check validImportPart(tok);
string? org = ();
if tok.current() == "/" {
// we have an org
org = firstModuleName;
check tok.advance();
namePos = tok.currentStartPos();
firstModuleName = check validImportPart(tok);
}
[string, string...] names = [firstModuleName];
names.push(...check parseImportNamesRest(tok));
string? prefix = check parseImportPrefix(tok);
Position endPos = check tok.expectEnd(";");
return { startPos, endPos, org, names, prefix, namePos, partIndex };
}
function parseImportNamesRest(Tokenizer tok) returns string[]|err:Syntax {
string[] names = [];
while tok.current() == "." {
check tok.advance();
names.push(check validImportPart(tok));
}
return names;
}
function validImportPart(Tokenizer tok) returns string|err:Syntax {
string identifier = check tok.expectIdentifier();
string? prevChar = ();
foreach var ch in identifier {
if ch == "_" {
if prevChar == () {
return tok.err("identifier in an import must not have leading underscores");
} else if prevChar == "_" {
return tok.err("identifier in an import must not have consecutive underscores");
}
}
prevChar = ch;
}
if prevChar == "_" {
return tok.err("identifier in an import must not have tailing underscores");
}
return identifier;
}
function parseImportPrefix(Tokenizer tok) returns string?|err:Syntax {
Token? t = tok.current();
if t != "as" {
return ();
}
check tok.advance();
return tok.expectIdentifier();
}
function parseModuleDecl(Tokenizer tok, ModulePart part) returns ModuleLevelDefn|err:Syntax {
Token? t = tok.current();
Position startPos = tok.currentStartPos();
Visibility vis;
if t == "public" {
vis = t;
check tok.advance();
t = tok.current();
}
else {
vis = ();
}
match t {
"type" => {
return parseTypeDefinition(tok, part, vis, startPos);
}
"const" => {
return parseConstDefinition(tok, part, vis, startPos);
}
"function" => {
return parseFunctionDefinition(tok, part, vis, startPos);
}
}
return parseError(tok);
}
function parseTypeDefinition(Tokenizer tok, ModulePart part, Visibility vis, Position startPos) returns TypeDefn|err:Syntax {
check tok.advance();
Position namePos = tok.currentStartPos();
string name = check tok.expectIdentifier();
TypeDesc td = check parseTypeDesc(tok);
Position endPos = check tok.expectEnd(";");
return { startPos, endPos, name, td, namePos, vis, part };
}
function parseConstDefinition(Tokenizer tok, ModulePart part, Visibility vis, Position startPos) returns ConstDefn|err:Syntax {
check tok.advance();
Token? t = tok.current();
SubsetBuiltinTypeDesc? td = ();
if t is SubsetBuiltinTypeName {
Position tdStartPos = tok.currentStartPos();
Position tdEndPos = tok.currentEndPos();
check tok.advance();
td = { startPos: tdStartPos, endPos: tdEndPos, builtinTypeName: t };
}
Position namePos = tok.currentStartPos();
string name = check tok.expectIdentifier();
check tok.expect("=");
Expr expr = check parseExpr(tok);
Position endPos = check tok.expectEnd(";");
return { startPos, endPos, td, name, expr, namePos, vis, part };
}
function parseFunctionDefinition(Tokenizer tok, ModulePart part, Visibility vis, Position startPos) returns FunctionDefn|err:Syntax {
check tok.advance();
Position namePos = tok.currentStartPos();
string name = check tok.expectIdentifier();
FunctionParam [] params = [];
FunctionTypeDesc typeDesc = check parseFunctionTypeDesc(tok, params);
StmtBlock body = check parseStmtBlock(tok);
Position endPos = tok.previousEndPos();
FunctionDefn defn = { startPos, endPos, params, typeDesc, name, vis, namePos, body, part };
return defn;
}
function parseError(Tokenizer tok, string? detail = ()) returns err:Syntax {
string message = "parse error";
Token? t = tok.current();
if t is string {
// XXX should use err:Template here
message += " at '" + t + "'";
}
if detail != () {
message += ": " + detail;
}
return tok.err(message);
}
public function defnLocation(ModuleLevelDefn defn) returns d:Location {
return qNameLocationInDefn(defn, defn.namePos);
}
public function qNameLocationInDefn(ModuleLevelDefn defn, Position qnamePos) returns d:Location {
return locationInDefn(defn, defn.part.file.qNameRange(qnamePos));
}
public function locationInDefn(ModuleLevelDefn defn, Position|Range pos) returns d:Location {
return d:location(defn.part.file, pos);
}
public function range(record {| *PositionFields; any|error...; |} node) returns Range {
return { startPos: node.startPos, endPos: node.endPos };
}