Skip to content

Commit

Permalink
fixes and coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
markw65 committed Sep 7, 2023
1 parent ef0a1d0 commit e59a378
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 22 deletions.
31 changes: 15 additions & 16 deletions lib/compiler/passes/generate-js.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ function wrapInSourceNode(prefix, chunk, location, suffix, name) {
*
* @typedef {PEG.SourceBuildOptions<PEG.SourceOutputs>} SourceBuildOptions
* @typedef {object} ExtraOptions
* @property {undefined} [dependencies]
* @property {undefined} [exportVar]
* @property {PEG.Dependencies} [dependencies]
* @property {string} [exportVar]
* @typedef {SourceBuildOptions & ExtraOptions} Options
*/
/**
Expand All @@ -101,12 +101,13 @@ function wrapInSourceNode(prefix, chunk, location, suffix, name) {
* @param {Options} options
*/
function generateJS(ast, options) {
if (!ast.literals || !ast.locations) {
if (!ast.literals || !ast.locations || !ast.classes
|| !ast.expectations || !ast.functions) {
throw new Error(
"generateJS: generate bytecode was not called."
);
}
const { literals, locations } = ast;
const { literals, locations, classes, expectations, functions } = ast;
if (!options.allowedStartRules) {
throw new Error(
"generateJS: options.allowedStartRules was not set."
Expand Down Expand Up @@ -191,7 +192,7 @@ function generateJS(ast, options) {
}
if (code instanceof SourceNode) {
inSourceNode++;
code.children = (helper(code.children));
code.children = helper(code.children);
inSourceNode--;
return code;
}
Expand Down Expand Up @@ -232,7 +233,7 @@ function generateJS(ast, options) {
return "\"" + stringEscape(literal) + "\"";
}

/** @param {PEG.ast.CharacterRange} cls */
/** @param {PEG.ast.GrammarCharacterClass} cls */
function buildRegexp(cls) {
return "/^["
+ (cls.inverted ? "^" : "")
Expand All @@ -244,7 +245,7 @@ function generateJS(ast, options) {
+ "]/" + (cls.ignoreCase ? "i" : "");
}

/** @param {PEG.ast.Expectation} e */
/** @param {PEG.ast.GrammarExpectation} e */
function buildExpectation(e) {
switch (e.type) {
case "rule": {
Expand Down Expand Up @@ -289,14 +290,14 @@ function generateJS(ast, options) {

return new SourceNode(
null, null, options.grammarSource, [
(ast.literals || []).map(
literals.map(
(c, i) => " var " + l(i) + " = " + buildLiteral(c) + ";"
).concat("", (ast.classes || []).map(
).concat("", classes.map(
(c, i) => " var " + r(i) + " = " + buildRegexp(c) + ";"
)).concat("", (ast.expectations || []).map(
)).concat("", expectations.map(
(c, i) => " var " + e(i) + " = " + buildExpectation(c) + ";"
)).concat("").join("\n"),
...(ast.functions || []).map(buildFunc),
...functions.map(buildFunc),
]
);
}
Expand Down Expand Up @@ -409,10 +410,8 @@ function generateJS(ast, options) {
function generateRuleFunction(rule) {
/** @type {SourceArray} */
const parts = [];
if (!rule.bytecode) {
return parts;
}
const stack = new Stack(rule.name, "s", "var", rule.bytecode);
const bytecode = /** @type {number[]} */(rule.bytecode);
const stack = new Stack(rule.name, "s", "var", bytecode);

/** @param {number[]} bc */
function compile(bc) {
Expand Down Expand Up @@ -798,7 +797,7 @@ function generateJS(ast, options) {
return parts;
}

const code = compile(rule.bytecode);
const code = compile(bytecode);

parts.push(wrapInSourceNode(
"function ",
Expand Down
10 changes: 5 additions & 5 deletions lib/peg.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,17 @@ declare namespace ast {
* Type of the classes field on a Grammar node. Not quite the same as
* CharacterClass (`parts` was renamed to `value`).
*/
interface CharacterRange {
interface GrammarCharacterClass {
value: (string[] | string)[];
inverted: boolean;
ignoreCase: boolean;
}

type Expectation =
type GrammarExpectation =
| { type: "any" }
| { type: "literal"; value: string; ignoreCase: boolean }
| { type: "rule"; value: string }
| CharacterRange & { type: "class" }
| GrammarCharacterClass & { type: "class" }
;

/** The main Peggy AST class returned by the parser. */
Expand All @@ -88,8 +88,8 @@ declare namespace ast {
* bytecodes to refer back to via index.
*/
literals?: string[];
classes?: CharacterRange[];
expectations?: Expectation[];
classes?: GrammarCharacterClass[];
expectations?: GrammarExpectation[];
functions?: FunctionConst[];
locations?: LocationRange[];
}
Expand Down
12 changes: 11 additions & 1 deletion test/types/peg.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ describe("peg.d.ts", () => {
it("creates an AST", () => {
const grammar = peggy.parser.parse(src);
expectType<peggy.ast.Grammar>(grammar);

const visited: { [typ: string]: number } = {};
function add(typ: string): void {
if (!visited[typ]) {
Expand All @@ -197,6 +196,17 @@ describe("peg.d.ts", () => {
);
expectType<peggy.ast.Initializer | undefined>(node.initializer);
expectType<peggy.ast.Rule[]>(node.rules);
expectType<string[] | undefined>(node.literals);
expectType<peggy.ast.GrammarCharacterClass[] | undefined>(node.classes);
expectType<peggy.ast.GrammarExpectation[] | undefined>(
node.expectations
);
expectType<peggy.ast.FunctionConst[] | undefined>(
node.functions
);
expectType<peggy.LocationRange[] | undefined>(
node.locations
);

if (node.topLevelInitializer) {
visit(node.topLevelInitializer);
Expand Down
56 changes: 56 additions & 0 deletions test/unit/compiler/passes/generate-js.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// @ts-check
"use strict";

const chai = require("chai");
const pass = require("../../../../lib/compiler/passes/generate-js");

const { expect } = chai;
/**
* @typedef {import("../../../../lib/peg")} PEG
*/

describe("compiler pass |generateJS|", () => {
describe("coverage", () => {
/** @type {PEG.ast.Grammar} */
const ast = {
type: "grammar",
rules: [],
location: {
source: "",
start: { line:1, column:1, offset:0 },
end: { line:1, column:1, offset:0 },
},
};
const options
= /** @type {PEG.SourceBuildOptions<PEG.SourceOutputs>} */({});
it("throws unless various grammar fields are set", () => {
expect(
() => pass(ast, options)
).to.throw(Error, "generateJS: generate bytecode was not called.");
ast.literals = [];
expect(
() => pass({ ...ast, literals:[] }, options)
).to.throw(Error, "generateJS: generate bytecode was not called.");
ast.locations = [];
expect(
() => pass({ ...ast, literals:[] }, options)
).to.throw(Error, "generateJS: generate bytecode was not called.");
ast.classes = [];
expect(
() => pass({ ...ast, literals:[] }, options)
).to.throw(Error, "generateJS: generate bytecode was not called.");
ast.expectations = [];
expect(
() => pass({ ...ast, literals:[] }, options)
).to.throw(Error, "generateJS: generate bytecode was not called.");
ast.functions = [];
expect(
() => pass(ast, options)
).to.throw(Error, "generateJS: options.allowedStartRules was not set.");
options.allowedStartRules = ["start"];
expect(
() => pass(ast, options)
).to.not.throw();
});
});
});

0 comments on commit e59a378

Please sign in to comment.