@jkearl/pratt
Version:
Pratt parser builder (along with simple tokenizer)
118 lines • 3.48 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PARENTHESES_PARSELET = {
parse(parser) {
const expr = parser.parse();
if (!parser.match(")")) {
throw new Error("Parse error: expected `)`");
}
return expr;
},
};
class ParserBuilder {
constructor(tokenizer) {
this.tokenizer = tokenizer;
this.prefixParselets = {};
this.infixParselets = {};
}
registerInfix(tokenType, parselet) {
this.infixParselets[tokenType] = parselet;
return this;
}
registerPrefix(tokenType, parselet) {
this.prefixParselets[tokenType] = parselet;
return this;
}
prefix(tokenType, precedence, builder) {
this.prefixParselets[tokenType] = {
parse(parser, token) {
const right = parser.parse(precedence);
return builder(token, right);
},
};
return this;
}
postfix(tokenType, precedence, builder) {
this.infixParselets[tokenType] = {
parse(parser, left, token) {
return builder(left, token);
},
precedence,
};
return this;
}
infixLeft(tokenType, precedence, builder) {
this.infixParselets[tokenType] = {
parse(parser, left, token) {
const right = parser.parse(precedence);
return builder(left, token, right);
},
precedence,
};
return this;
}
infixRight(tokenType, precedence, builder) {
this.infixParselets[tokenType] = {
parse(parser, left, token) {
const right = parser.parse(precedence - 1);
return builder(left, token, right);
},
precedence,
};
return this;
}
construct() {
return (input) => new Parser(this.tokenizer(input), this.prefixParselets, this.infixParselets).parse();
}
}
exports.ParserBuilder = ParserBuilder;
class Parser {
constructor(tokens, prefixParselets, infixParselets) {
this.tokens = tokens;
this.prefixParselets = prefixParselets;
this.infixParselets = infixParselets;
}
match(expected) {
const token = this.look();
if (token.id !== expected) {
return false;
}
this.consume();
return true;
}
parse(precedence = 0) {
const token = this.consume();
const prefix = this.prefixParselets[token.id];
if (!prefix) {
throw Error(`Parse error at ${token.value}. No matching prefix parselet.`);
}
let left = prefix.parse(this, token);
while (precedence < this.getPrecedence()) {
const token = this.consume();
const infix = this.infixParselets[token.id];
left = infix.parse(this, left, token);
}
return left;
}
getPrecedence() {
const nextToken = this.look();
if (!nextToken) {
return 0;
}
const parser = this.infixParselets[nextToken.id];
if (parser) {
return parser.precedence;
}
return 0;
}
consume() {
if (!this.tokens.length) {
throw Error("Cant consume any more tokens.");
}
return this.tokens.shift();
}
look() {
return this.tokens[0];
}
}
//# sourceMappingURL=parse.js.map