Skip to content

Commit

Permalink
Add library mode, an internal-only interface for calling into rules i…
Browse files Browse the repository at this point in the history
…n other grammars.
  • Loading branch information
hildjj committed Dec 31, 2023
1 parent cbea181 commit 21a9a27
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 38 deletions.
7 changes: 7 additions & 0 deletions bin/peggy-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ class PeggyCLI extends Command {
.choices(MODULE_FORMATS)
.default("commonjs")
)
.addOption(new Option("--library").hideHelp(), "Run tests in library mode. Maintainers only, for now.")
.option("-o, --output <file>", "Output file for generated parser. Use '-' for stdout (the default is a file next to the input file with the extension change to '.js', unless a test is specified, in which case no parser is output without this option)")
.option(
"--plugin <module>",
Expand Down Expand Up @@ -218,6 +219,11 @@ class PeggyCLI extends Command {
this.inputFiles = inputFiles;
this.argv = opts;

if (this.argv.library) {
this.peg$library = true;
delete this.argv.library;
}

if ((typeof this.argv.startRule === "string")
&& !this.argv.allowedStartRules.includes(this.argv.startRule)) {
this.argv.allowedStartRules.push(this.argv.startRule);
Expand Down Expand Up @@ -620,6 +626,7 @@ class PeggyCLI extends Command {

const opts = {
grammarSource: this.testGrammarSource,
peg$library: this.peg$library,
};
if (typeof this.progOptions.startRule === "string") {
opts.startRule = this.progOptions.startRule;
Expand Down
2 changes: 1 addition & 1 deletion docs/js/benchmark-bundle.min.js

Large diffs are not rendered by default.

45 changes: 41 additions & 4 deletions docs/js/examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,12 @@ function peg$parse(input, options) {
var peg$f26 = function() { return location(); };
var peg$f27 = function(match, rest) { return {match, rest}; };
var peg$f28 = function(match, rest) { return {match, rest}; };
var peg$currPos = 0;
var peg$savedPos = 0;
var peg$currPos = options.peg$currPos | 0;
var peg$savedPos = peg$currPos;
var peg$posDetailsCache = [{ line: 1, column: 1 }];
var peg$maxFailPos = 0;
var peg$maxFailPos = peg$currPos;
var peg$maxFailExpected = [];
var peg$silentFails = 0;
var peg$silentFails = options.peg$silentFails | 0;

var peg$result;

Expand Down Expand Up @@ -397,6 +397,33 @@ function peg$parse(input, options) {
);
}

var peg$assign = Object.assign || function(t) {
var i, s;
for (i = 1; i < arguments.length; i++) {
s = arguments[i];
for (var p in s) {
if (Object.prototype.hasOwnProperty.call(s, p)) {
t[p] = s[p];
}
}
}
return t;
};

function peg$callLibrary(lib, startRule) {
const opts = peg$assign({}, options, {
startRule: startRule,
peg$currPos: peg$currPos,
peg$silentFails: peg$silentFails,
peg$library: true
});
const res = lib.parse(input, opts);
peg$currPos = res.peg$currPos;
peg$maxFailPos = res.peg$maxFailPos;
peg$maxFailExpected = res.peg$maxFailExpected;
return (res.peg$result === res.peg$FAILED) ? peg$FAILED : res.peg$result;
}

function peg$parseliteral() {
var s0, s1, s2;

Expand Down Expand Up @@ -1496,6 +1523,15 @@ function peg$parse(input, options) {

peg$result = peg$startRuleFunction();

if (options.peg$library) {
return /** @type {any} */ ({
peg$result,
peg$currPos,
peg$FAILED,
peg$maxFailExpected,
peg$maxFailPos
});
}
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
return peg$result;
} else {
Expand All @@ -1514,6 +1550,7 @@ function peg$parse(input, options) {
}

root.peggyExamples = {
StartRules: ["literal", "literal_i", "any", "class", "not_class_i", "rule", "child", "paren", "paren_pluck", "star", "plus", "repetition", "maybe", "posAssertion", "negAssertion", "posPredicate", "negPredicate", "dollar", "label", "pluck_1", "pluck_2", "sequence", "action", "alt", "rest"],
SyntaxError: peg$SyntaxError,
parse: peg$parse
};
Expand Down
2 changes: 1 addition & 1 deletion docs/js/test-bundle.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/vendor/peggy/peggy.min.js

Large diffs are not rendered by default.

106 changes: 84 additions & 22 deletions lib/compiler/passes/generate-js.js
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,12 @@ function generateJS(ast, options) {
const imps = [];
const codes = [];
for (const tli of topLevel) {
const [imports, code] = parseImport(tli.code);
const [
imports,
code,
] = /** @type {PEG.ast.TopLevelInitializer[]} */ (
parseImport(tli.code)
);
if (imports.code) {
imps.push(imports);
codes.push(code);
Expand Down Expand Up @@ -1123,12 +1128,12 @@ function generateJS(ast, options) {
"",
generateTables(),
"",
" var peg$currPos = 0;",
" var peg$savedPos = 0;",
" var peg$currPos = options.peg$currPos | 0;",
" var peg$savedPos = peg$currPos;",
" var peg$posDetailsCache = [{ line: 1, column: 1 }];",
" var peg$maxFailPos = 0;",
" var peg$maxFailPos = peg$currPos;",
" var peg$maxFailExpected = [];",
" var peg$silentFails = 0;", // 0 = report failures, > 0 = silence failures
" var peg$silentFails = options.peg$silentFails | 0;", // 0 = report failures, > 0 = silence failures
""
);

Expand Down Expand Up @@ -1301,6 +1306,34 @@ function generateJS(ast, options) {
" location",
" );",
" }",
"",
// This part can be conditional on using "import" one day
" var peg$assign = Object.assign || function(t) {",
" var i, s;",
" for (i = 1; i < arguments.length; i++) {",
" s = arguments[i];",
" for (var p in s) {",
" if (Object.prototype.hasOwnProperty.call(s, p)) {",
" t[p] = s[p];",
" }",
" }",
" }",
" return t;",
" };",
" ",
" function peg$callLibrary(lib, startRule) {",
" const opts = peg$assign({}, options, {",
" startRule: startRule,",
" peg$currPos: peg$currPos,",
" peg$silentFails: peg$silentFails,",
" peg$library: true",
" });",
" const res = lib.parse(input, opts);",
" peg$currPos = res.peg$currPos;",
" peg$maxFailPos = res.peg$maxFailPos;",
" peg$maxFailExpected = res.peg$maxFailExpected;",
" return (res.peg$result === res.peg$FAILED) ? peg$FAILED : res.peg$result;",
" }",
""
);

Expand All @@ -1324,6 +1357,16 @@ function generateJS(ast, options) {
parts.push(
" peg$result = peg$startRuleFunction();",
"",
" if (options.peg$library) {",
// Hide this from TypeScript. It's internal-only.
" return /** @type {any} */ ({",
" peg$result,",
" peg$currPos,",
" peg$FAILED,",
" peg$maxFailExpected,",
" peg$maxFailPos",
" });",
" }",
" if (peg$result !== peg$FAILED && peg$currPos === input.length) {",
" return peg$result;",
" } else {",
Expand Down Expand Up @@ -1362,20 +1405,22 @@ function generateJS(ast, options) {
}

function generateParserObject() {
return options.trace
? [
"{",
" SyntaxError: peg$SyntaxError,",
" DefaultTracer: peg$DefaultTracer,",
" parse: peg$parse",
"}",
].join("\n")
: [
"{",
" SyntaxError: peg$SyntaxError,",
" parse: peg$parse",
"}",
].join("\n");
const res = ["{"];
if (options.trace) {
res.push(" DefaultTracer: peg$DefaultTracer,");
}

if (options.allowedStartRules) {
res.push(" StartRules: [" + options.allowedStartRules.map(r => '"' + r + '"').join(", ") + "],");
}

res.push(
" SyntaxError: peg$SyntaxError,",
" parse: peg$parse"
);

res.push("}");
return res.join("\n");
}

const generators = {
Expand Down Expand Up @@ -1443,10 +1488,27 @@ function generateJS(ast, options) {

parts.push(
toplevelCode,
"",
"export {",
""
);

parts.push(
"const peg$allowedStartRules = [",
" " + (options.allowedStartRules ? options.allowedStartRules.map(r => '"' + r + '"').join(",\n ") : ""),
"];",
""
);

parts.push(
"export {"
);

if (options.trace) {
parts.push(" peg$DefaultTracer as DefaultTracer,");
}

parts.push(
" peg$allowedStartRules as StartRules,",
" peg$SyntaxError as SyntaxError,",
options.trace ? " peg$DefaultTracer as DefaultTracer," : "",
" peg$parse as parse",
"};"
);
Expand Down
45 changes: 41 additions & 4 deletions lib/compiler/passes/js-imports.js
Original file line number Diff line number Diff line change
Expand Up @@ -575,12 +575,12 @@ function peg$parse(input, options) {
var peg$f41 = function() { return { type: "any", location: location() }; };
var peg$f42 = function(code) { return [code, location()]; };
var peg$f43 = function(digits) { return parseInt(digits, 10); };
var peg$currPos = 0;
var peg$savedPos = 0;
var peg$currPos = options.peg$currPos | 0;
var peg$savedPos = peg$currPos;
var peg$posDetailsCache = [{ line: 1, column: 1 }];
var peg$maxFailPos = 0;
var peg$maxFailPos = peg$currPos;
var peg$maxFailExpected = [];
var peg$silentFails = 0;
var peg$silentFails = options.peg$silentFails | 0;

var peg$result;

Expand Down Expand Up @@ -737,6 +737,33 @@ function peg$parse(input, options) {
);
}

var peg$assign = Object.assign || function(t) {
var i, s;
for (i = 1; i < arguments.length; i++) {
s = arguments[i];
for (var p in s) {
if (Object.prototype.hasOwnProperty.call(s, p)) {
t[p] = s[p];
}
}
}
return t;
};

function peg$callLibrary(lib, startRule) {
const opts = peg$assign({}, options, {
startRule: startRule,
peg$currPos: peg$currPos,
peg$silentFails: peg$silentFails,
peg$library: true
});
const res = lib.parse(input, opts);
peg$currPos = res.peg$currPos;
peg$maxFailPos = res.peg$maxFailPos;
peg$maxFailExpected = res.peg$maxFailExpected;
return (res.peg$result === res.peg$FAILED) ? peg$FAILED : res.peg$result;
}

function peg$parseJSSource() {
var s0, s1, s2;

Expand Down Expand Up @@ -3882,6 +3909,15 @@ function peg$parse(input, options) {

peg$result = peg$startRuleFunction();

if (options.peg$library) {
return /** @type {any} */ ({
peg$result,
peg$currPos,
peg$FAILED,
peg$maxFailExpected,
peg$maxFailPos
});
}
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
return peg$result;
} else {
Expand All @@ -3900,6 +3936,7 @@ function peg$parse(input, options) {
}

module.exports = {
StartRules: ["JSSource"],
SyntaxError: peg$SyntaxError,
parse: peg$parse
};
Loading

0 comments on commit 21a9a27

Please sign in to comment.