File: parser.bs

package info (click to toggle)
storm-lang 0.7.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 52,004 kB
  • sloc: ansic: 261,462; cpp: 140,405; sh: 14,891; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (349 lines) | stat: -rw-r--r-- 8,782 bytes parent folder | download
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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
use core:lang;
use lang:bnf;
use lang:bs;
use lang:bs:macro;

/**
 * A declared parser.
 */
class Parser extends lang:bs:NamedDecl {
	// Override to create function body.
	FnBody createBody(BSRawFn fn, Bool binary, Scope scope, Rule start, NameSet[] include) : abstract;

	// Create.
	init(Scope scope, SStr name) {
		init {
			scope = scope;
			name = name;
		}
	}

	// Options for this function.
	private SFreeOptions? options;

	// Scope.
	private Scope scope;

	// Name.
	SStr name;

	// Syntax entered in the parser block.
	ParserSyntax? syntax;

	// Parse a binary buffer rather than a string.
	private Bool binary;

	// Create two parsers from the same grammar: one for strings and one for binary blobs.
	private Bool dual;

	// Called to make the parser into a binary parser.
	void makeBinary() {
		binary = true;
	}

	// Called to make the parser into a "dual" parser.
	void makeDual() {
		dual = true;
	}

	// Generate the overloads for this parser.
	MultiDecl parserOverloads() {
		if (dual & binary)
			throw SyntaxError(name.pos, "Can not use both 'binary' and 'text and binary' for the same parser.");

		TObject[] overloads;
		// Add ourselves.
		overloads << this << ParserOverload(this, binary);
		// If we were asked to be a dual parser, add that as well.
		if (dual) {
			BinaryWrapper binary(this);
			overloads << binary << ParserOverload(binary, true);
		}
		return MultiDecl(overloads);
	}

	// Set syntax. Called by the grammar.
	void setSyntax(ParserSyntax syntax) {
		this.syntax = syntax;
	}

	// Set options. Called by the grammar.
	void setOptions(SFreeOptions options) {
		this.options = options;
	}

	// Function class so that we can do lazy-loading to get load order right.
	class ParserFn extends BSRawFn {
		init(Parser parser, Bool binary, ParserSyntax syntax, SrcName startName, NameLookup scopeTop) {
			Package syntaxPkg = syntax.createPackage(parser.scope, scopeTop);

			// Add includes. Most importantly so that we may access both 'syntaxPkg' and the current package.
			// We add the new package as the topmost, so that things there take precedence.
			NameSet[] include;
			include << ScopeLookup:firstPkg(scopeTop);
			for (name in syntax.use) {
				unless (inc = parser.scope.find(name) as NameSet)
					throw SyntaxError(name.pos, "Unknown package: ${name}");
				include << inc;
			}

			// Also add core.io, otherwise we can't access core:io:cut (which is a non-member):
			if (binary)
				include << named{core:io};

			// Note that we change the top to the child package. That way things there take precedence.
			Scope s = addIncludes(parser.scope, include).child(syntaxPkg);
			Rule start = findRule(s, startName);

			// We also need the syntax package in 'includes', otherwise we don't include the grammar for it.
			include << syntaxPkg;

			// Now we can start creating the actual parser functions!
			SimpleName resultName;
			resultName.add("parser");

			ValParam[] params = start.params.clone;
			if (binary) {
				params.insert(0, ValParam(named{core:io:Buffer}, " input"));
				params.insert(1, ValParam(named{Nat}, " start"));
				resultName.add("BinaryResult", [start.result]);
			} else {
				params.insert(0, ValParam(named{Str}, " input"));
				params.insert(1, ValParam(named{Str:Iter}, " start"));
				resultName.add("Result", [start.result]);
			}

			var resultType = s.value(resultName, parser.name.pos);

			init(resultType, parser.name, params, null) {
				parser = parser;
				binary = binary;
				start = start;
				scope = s;
				include = include;
			}
		}

		// Data we need when creating the body.
		Parser? parser;
		Rule? start;
		Scope scope;
		NameSet[] include;
		Bool binary;

		FnBody createBody() : override {
			unless (parser)
				throw InternalError("Should not need to create the body more than once!");
			unless (start)
				throw InternalError("Should not need to create the body more than once!");

			parser.createBody(this, binary, scope.withPos(parser.name.pos), start, include);
		}

		void clearBody() : override {
			// Shed some weight.
			parser = null;
			start = null;
			scope = Scope();
			include.clear();
		}
	}

	// Create the parser, but provide 'binary' explicitly.
	private ParserFn createExplicit(Bool binary) {
		unless (syntax)
			throw InternalError("The syntax for the parser was not set properly.");
		unless (startName = syntax.start)
			throw SyntaxError(name.pos, "You need to specify the start rule with 'start = <name>;'.");
		unless (scopeTop = scope.top)
			throw InternalError("Expected to find a package in the scope.");

		ParserFn fn(this, binary, syntax, startName, scopeTop);

		// Apply options.
		if (thread)
			fn.thread(scope, thread);
		if (options)
			options.transform(scope, fn);

		fn;
	}

	// Create the parser. The generated function takes (Str, Str:Iter, ...) as parameters, where
	// ... is the parameters to the root rule.
	protected Named doCreate() : override {
		return createExplicit(binary);
	}


	/**
	 * Wrapper of a parser that produces a binary of the other parser.
	 */
	private class BinaryWrapper extends lang:bs:NamedDecl {
		// Wrapped parser.
		Parser parser;

		// Create.
		init(Parser parser) {
			init { parser = parser; }
		}

		// Creation.
		protected Named doCreate() : override {
			return parser.createExplicit(true);
		}
	}
}

// Find a rule. Throws on error.
private Rule findRule(Scope scope, SrcName name) on Compiler {
	if (rule = scope.find(name) as Rule)
		rule;
	else
		throw SyntaxError(name.pos, "Unknown rule: ${name}");
}

// Add a set of NameSet:s to a Scope. Making sure not to modify the original scope.
private Scope addIncludes(Scope scope, NameSet[] add) on Compiler {
	if (lookup = scope.lookup) {
		lookup = lookup.clone;
		if (lookup as BSLookup) {
			for (x in add)
				if (x as Package)
					lookup.addInclude(x);
		} else if (lookup as ScopeExtra) {
			for (x in add)
				lookup.addExtra(x);
		}
		scope.lookup = lookup;
	}
	scope;
}


/**
 * Overload of a parser. Wraps the X(Str, Iter) with X(Str).
 */
class ParserOverload extends lang:bs:NamedDecl {
	init(lang:bs:NamedDecl parser, Bool binary) {
		init {
			wrap = parser;
			binary = binary;
		}
	}

	lang:bs:NamedDecl wrap;
	Bool binary;

	protected Named doCreate() : override {
		unless (wrapFn = wrap.create() as BSRawFn)
			throw InternalError("Expected a function.");

		ValParam[] params;
		for (i, x in wrapFn.docParams) {
			if (i != 1) // Remove the position parameter
				params << ValParam(x.type, x.name);
		}

		BSTreeFn fn(wrapFn.result, SStr(wrapFn.name, wrapFn.pos), params, wrapFn.declaredThread);

		// Apply thread options.
		if (thread = wrapFn.declaredThread)
			fn.runOn(thread);

		fn;
	}

	protected void doResolve(Named fn) : override {
		unless (wrapFn = wrap.create() as BSRawFn)
			throw InternalError("Expected a function.");

		unless (fn as BSTreeFn)
			throw InternalError("Expected a tree function.");

		Scope scope(wrapFn);

		FnBody body(fn, scope);
		fn.body = body;

		Actuals actuals;
		LocalVarAccess str(SrcPos(), body.parameters[0]); // Source string.
		actuals.add(str);
		if (binary)
			actuals.add(NumLiteral(SrcPos(), 0));
		else
			actuals.add(FnCall(SrcPos(), scope, named{Str:begin<Str>}, Actuals(str)));

		for (Nat i = 2; i < body.parameters.count; i++)
			actuals.add(LocalVarAccess(SrcPos(), body.parameters[i]));
		body.add(FnCall(SrcPos(), scope, wrapFn, actuals));
	}
}

class ParserSyntax extends lang:bnf:FileContents {
	// Name of the start rule.
	SrcName? start;

	void push(FileItem item) : override {
		if (item as StartDecl) {
			if (start)
				throw SyntaxError(item.name.pos, "You can only specify the start rule once.");
			start = item.name;
		} else {
			super:push(item);
		}
	}
}

// Create a package that contains the productions in here.
Package createPackage(lang:bnf:FileContents this, Scope scope, NameLookup parent) on Compiler {
	Package p("<anonymous>");
	p.parentLookup = parent;

	SyntaxLookup lookup;
	if (old = scope.lookup as ScopeExtra) {
		lookup.addExtra(parent);
		for (x in old.extra)
			lookup.addExtra(x, false);
	} else if (old = scope.lookup as BSLookup) {
		lookup.addExtra(parent);
		for (x in old.includes)
			lookup.addExtra(x, false);
	}

	// New includes.
	for (x in use) {
		unless (found = rootScope.find(x))
			throw SyntaxError(x.pos, "The package ${x} does not exist!");
		lookup.addExtra(found);
	}

	scope.lookup = lookup;
	scope.top = p;

	for (x in rules) {
		p.add(Rule(x, scope));
	}

	var delims = delimiters(scope);

	for (x in productions) {
		Str name = if (n = x.name) {
			n;
		} else {
			p.anonName();
		};
		p.add(ProductionType(name, x, delims, scope));
	}

	p;
}

// Declare what the start rule is.
class StartDecl extends lang:bnf:FileItem {
	init(SrcName name) {
		init { name = name; }
	}

	SrcName name;
}