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;
}
|