Skip to content

Commit 9ae9b39

Browse files
Refactor parser debugging
Allow specifying grammar sets per parser
1 parent c120483 commit 9ae9b39

File tree

3 files changed

+164
-82
lines changed

3 files changed

+164
-82
lines changed

script/debug_parser.ts

Lines changed: 5 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,10 @@
1-
// eslint no-console: "off"
2-
3-
import fs from 'fs';
41
import process from 'process';
5-
import puppeteer from 'puppeteer';
6-
import esbuild from 'esbuild';
7-
8-
const parserName = process.argv[2];
9-
const args = process.argv.slice(3);
10-
const includeChordGrammar = args.includes('--include-chord-grammar');
11-
const includeSectionsGrammar = args.includes('--include-sections-grammar');
12-
13-
const parserFolder = `./src/parser/${parserName}`;
14-
const grammarFile = `${parserFolder}/grammar.pegjs`;
15-
const helpersFile = `${parserFolder}/helpers.ts`;
16-
const chordGrammarFile = './src/parser/chord/base_grammar.pegjs';
17-
const chordSuffixGrammarFile = './src/parser/chord/suffix_grammar.pegjs';
18-
const whitespaceGrammarFile = './src/parser/whitespace_grammar.pegjs';
19-
const sectionsGrammarFile = './src/parser/chord_pro/sections_grammar.pegjs';
20-
const chordDefinitionGrammarFile = './src/parser/chord_definition/grammar.pegjs';
21-
22-
const parserGrammar = fs.readFileSync(grammarFile, 'utf8');
23-
const chordGrammar = includeChordGrammar ? fs.readFileSync(chordGrammarFile) : '';
24-
const chordSuffixGrammar = fs.readFileSync(chordSuffixGrammarFile);
25-
const whitespaceGrammar = fs.readFileSync(whitespaceGrammarFile);
26-
const sectionsGrammar = includeSectionsGrammar ? fs.readFileSync(sectionsGrammarFile) : '';
27-
const chordDefinitionGrammar = fs.readFileSync(chordDefinitionGrammarFile);
28-
29-
const result = esbuild.buildSync({
30-
bundle: true,
31-
entryPoints: [helpersFile],
32-
globalName: 'helpers',
33-
write: false,
34-
});
35-
36-
const transpiledHelpers = result.outputFiles[0].text;
37-
38-
const parserSource = [
39-
`{\n${transpiledHelpers}\n}`,
40-
parserGrammar,
41-
chordGrammar,
42-
chordSuffixGrammar,
43-
chordDefinitionGrammar,
44-
sectionsGrammar,
45-
whitespaceGrammar,
46-
].join('\n\n');
47-
48-
async function run() {
49-
const browser = await puppeteer.launch({
50-
args: ['--start-maximized'],
51-
defaultViewport: null,
52-
headless: false,
53-
});
54-
55-
async function shutdownHandler() {
56-
await browser.close();
57-
}
58-
59-
['exit', 'SIGINT', 'SIGUSR1', 'SIGUSR2', 'uncaughtException', 'SIGTERM'].forEach((event) => {
60-
process.on(event, shutdownHandler);
61-
});
62-
63-
const [page] = await browser.pages();
64-
await page.setViewport({ width: 0, height: 0 });
65-
await page.goto('https://peggyjs.org/online.html');
66-
67-
await page.evaluate((grammar) => {
68-
// eslint-disable-next-line no-undef
69-
const textarea = document.getElementById('grammar');
70-
if (!textarea) return;
71-
72-
const editorNode = textarea.nextSibling;
73-
if (!editorNode) return;
74-
75-
// @ts-expect-error There is no way to validate that the CodeMirror object is present
76-
const editor = editorNode.CodeMirror;
77-
editor.setValue(grammar);
78-
}, parserSource);
2+
import PeggyOnline from './helpers/peggy_online';
3+
import ParserBuilder from './helpers/parser_builder';
794

80-
while (true) {
81-
// Loop forever to allow for interactive debugging with the online Peggy parser
82-
}
83-
}
5+
const parserSource = new ParserBuilder(process.argv[2]).build();
846

85-
run()
7+
PeggyOnline
8+
.open(parserSource)
869
.then(() => console.log('Done'))
8710
.catch((e) => console.error(e));

script/helpers/parser_builder.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import fs from 'fs';
2+
import esbuild from 'esbuild';
3+
4+
class ParserBuilder {
5+
parserName: string;
6+
7+
chordDefinitionGrammarFile = './src/parser/chord_definition/grammar.pegjs';
8+
9+
chordGrammarFile = './src/parser/chord/base_grammar.pegjs';
10+
11+
chordSuffixGrammarFile = './src/parser/chord/suffix_grammar.pegjs';
12+
13+
chordSimpleSuffixGrammarFile = './src/parser/chord/simple_suffix_grammar.pegjs';
14+
15+
sectionsGrammarFile = './src/parser/chord_pro/sections_grammar.pegjs';
16+
17+
whitespaceGrammarFile = './src/parser/whitespace_grammar.pegjs';
18+
19+
constructor(parserName: string) {
20+
this.parserName = parserName;
21+
}
22+
23+
build(): string { return this.parserSource; }
24+
25+
get parserSource(): string {
26+
return [
27+
this.transpiledHelpers,
28+
...this.grammars,
29+
].join('\n\n');
30+
}
31+
32+
get grammars(): (string | Buffer)[] {
33+
switch (this.parserName) {
34+
case 'chord':
35+
return [this.chordGrammar, this.chordSimpleSuffixGrammar];
36+
case 'chord_pro':
37+
return [this.parserGrammar, this.chordDefinitionGrammar, this.sectionsGrammar, this.whitespaceGrammar];
38+
case 'chords_over_words':
39+
return [this.parserGrammar, this.chordGrammar, this.chordSuffixGrammar, this.whitespaceGrammar];
40+
default:
41+
throw new Error(`No configuration for parser ${this.parserName}`);
42+
}
43+
}
44+
45+
get parserFolder(): string { return `./src/parser/${this.parserName}`; }
46+
47+
get grammarFile(): string { return `${this.parserFolder}/grammar.pegjs`; }
48+
49+
get helpersFile(): string { return `${this.parserFolder}/helpers.ts`; }
50+
51+
get parserGrammar(): string | Buffer {
52+
return fs.readFileSync(this.grammarFile, 'utf8');
53+
}
54+
55+
get chordSuffixGrammar(): string | Buffer {
56+
return fs.readFileSync(this.chordSuffixGrammarFile);
57+
}
58+
59+
get chordSimpleSuffixGrammar(): string | Buffer {
60+
return fs.readFileSync(this.chordSimpleSuffixGrammarFile);
61+
}
62+
63+
get whitespaceGrammar(): string | Buffer {
64+
return fs.readFileSync(this.whitespaceGrammarFile);
65+
}
66+
67+
get chordDefinitionGrammar(): string | Buffer {
68+
return fs.readFileSync(this.chordDefinitionGrammarFile);
69+
}
70+
71+
get sectionsGrammar(): string | Buffer {
72+
return fs.readFileSync(this.sectionsGrammarFile);
73+
}
74+
75+
get chordGrammar(): string | Buffer {
76+
return fs.readFileSync(this.chordGrammarFile);
77+
}
78+
79+
get transpiledHelpers(): string {
80+
if (!fs.existsSync(this.helpersFile)) {
81+
return '';
82+
}
83+
84+
const result = esbuild.buildSync({
85+
bundle: true,
86+
entryPoints: [this.helpersFile],
87+
globalName: 'helpers',
88+
write: false,
89+
});
90+
91+
return `{\n${result.outputFiles[0].text}\n}`;
92+
}
93+
}
94+
95+
export default ParserBuilder;

script/helpers/peggy_online.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import puppeteer, { Browser, Page } from 'puppeteer';
2+
import process from 'process';
3+
4+
class PeggyOnline {
5+
parserSource: string;
6+
7+
static open(parserSource: string): Promise<void> {
8+
return new PeggyOnline(parserSource).open();
9+
}
10+
11+
constructor(parserSource: string) {
12+
this.parserSource = parserSource;
13+
}
14+
15+
async open(): Promise<void> {
16+
const browser = await this.launchBrowser();
17+
18+
async function shutdownHandler() {
19+
await browser.close();
20+
}
21+
22+
this.attachShutdownHandler(shutdownHandler);
23+
const page = await this.openPage(browser);
24+
await this.addGrammar(page);
25+
}
26+
27+
async launchBrowser() {
28+
return puppeteer.launch({
29+
args: ['--start-maximized'],
30+
defaultViewport: null,
31+
headless: false,
32+
});
33+
}
34+
35+
async openPage(browser: Browser): Promise<Page> {
36+
const [page] = await browser.pages();
37+
await page.setViewport({ width: 0, height: 0 });
38+
await page.goto('https://peggyjs.org/online.html');
39+
return page;
40+
}
41+
42+
async addGrammar(page: Page) {
43+
await page.evaluate((grammar) => {
44+
// eslint-disable-next-line no-undef
45+
const textarea = document.getElementById('grammar');
46+
if (!textarea) return;
47+
48+
const editorNode = textarea.nextSibling;
49+
if (!editorNode) return;
50+
51+
// @ts-expect-error There is no way to validate that the CodeMirror object is present
52+
const editor = editorNode.CodeMirror;
53+
editor.setValue(grammar);
54+
}, this.parserSource);
55+
}
56+
57+
attachShutdownHandler(shutdownHandler: (...args: any[]) => void) {
58+
['exit', 'SIGINT', 'SIGUSR1', 'SIGUSR2', 'uncaughtException', 'SIGTERM'].forEach((event) => {
59+
process.on(event, shutdownHandler);
60+
});
61+
}
62+
}
63+
64+
export default PeggyOnline;

0 commit comments

Comments
 (0)