Skip to content

Commit 6d29446

Browse files
committed
Introduce the calculations property
1 parent c7fcbb8 commit 6d29446

File tree

96 files changed

+18279
-16226
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+18279
-16226
lines changed

CHANGELOG.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Changelog
2+
3+
* Correct the example outputs for the LR(1) automaton calculation. These actually had the output of the LALR(1) automaton calculation, which takes as input the LR(1) automaton, and incorrectly overwrites parts of it. This bug is fixed in a later commit.
4+
5+
* Clean up tests, and add test cases (beyond the example outputs) for several calculations.
6+
7+
* Introduce the `calculations` property on `Grammar` instances. This replaces the `getCalculation` method, which took a string identifying a calculation and dispatched to the correct calculation function, memoizing the result. The new property returns an object with individual memoized properties, which can be destructured in calculation functions, like this:
8+
9+
```js
10+
export default function({ symbols, nonterminals }) {
11+
// ...
12+
}
13+
```
14+
15+
This avoids passing the grammar instance to calculation functions, which aren't really supposed to modify it or depend on it other than asking for calculations, and removes the dotted calculation names like "grammar.terminals".

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"scripts": {
2424
"generate-lezer-parser": "lezer-generator --output src/parser/rules.js src/parser/rules.grammar",
2525
"lint": "eslint src test",
26-
"test": "mocha --recursive --ignore \"test/fixtures/*\"",
26+
"test": "mocha --recursive --extension \".test.js\"",
2727
"build": "parcel build --no-autoinstall --no-cache",
2828
"serve": "parcel serve --no-autoinstall --no-cache",
2929
"serve-gallery": "parcel serve --no-autoinstall --no-cache gallery/index.html"

src/components/analysis/nonterminals_component.jsx

+6-10
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,8 @@ import { formatSymbol, formatSymbolList, listSymbols } from "../helpers.js";
33
export const ID = "nonterminals";
44
export const TITLE = "Nonterminals";
55

6-
export default function({ getCalculation }) {
7-
const nullable = getCalculation("grammar.nullable");
8-
const endable = getCalculation("grammar.endable");
9-
const first = getCalculation("grammar.first");
10-
const follow = getCalculation("grammar.follow");
11-
const info = getCalculation("grammar.symbolInfo");
6+
export default function({ grammar }) {
7+
const { nullable, endable, first, follow, symbolInfo } = grammar.calculations;
128

139
return (
1410
<section id={ID} className="analysis">
@@ -27,17 +23,17 @@ export default function({ getCalculation }) {
2723

2824
<tbody>
2925
{
30-
info.productionOrder.map(function(symbol) {
26+
symbolInfo.productionOrder.map(function(symbol) {
3127
const firstSymbols = first.get(symbol);
3228
const followSymbols = follow.get(symbol);
3329

3430
return (
3531
<tr key={symbol}>
36-
<td>{formatSymbol(symbol, info)}</td>
32+
<td>{formatSymbol(symbol, symbolInfo)}</td>
3733
<td>{nullable.has(symbol) ? "Nullable" : ""}</td>
3834
<td>{endable.has(symbol) ? "Endable" : ""}</td>
39-
<td>{formatSymbolList(listSymbols(firstSymbols, info.terminalOrder), info)}</td>
40-
<td>{formatSymbolList(listSymbols(followSymbols, info.terminalOrder), info)}</td>
35+
<td>{formatSymbolList(listSymbols(firstSymbols, symbolInfo.terminalOrder), symbolInfo)}</td>
36+
<td>{formatSymbolList(listSymbols(followSymbols, symbolInfo.terminalOrder), symbolInfo)}</td>
4137
</tr>
4238
);
4339
})

src/components/analysis/parsing/abstract_lr1_table_component.jsx

+12-14
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,33 @@
11
import { fillArray, formatSymbol, formatProduction } from "../../helpers.js";
22
import { END } from "../../../grammar/symbols.js";
33

4-
export default function({ getCalculation, tableCalculation }) {
5-
const info = getCalculation("grammar.symbolInfo");
6-
const table = getCalculation(tableCalculation);
7-
const productions = getCalculation("grammar.productions");
4+
export default function({ grammar, table }) {
5+
const { productions, symbolInfo } = grammar.calculations;
86

97
return (
108
<table className="symbols lr1-table">
119
<colgroup>
1210
<col />
1311
</colgroup>
1412
<colgroup className="t">
15-
{fillArray(info.terminals.size + 1, (index) => <col key={index} />)}
13+
{fillArray(symbolInfo.terminals.size + 1, (index) => <col key={index} />)}
1614
</colgroup>
1715
<colgroup className="nt">
18-
{fillArray(info.nonterminals.size, (index) => <col key={index} />)}
16+
{fillArray(symbolInfo.nonterminals.size, (index) => <col key={index} />)}
1917
</colgroup>
2018

2119
<thead>
2220
<tr>
2321
<th>State</th>
2422
{
25-
info.terminalOrder.map(function(symbol, index) {
26-
return <th key={"t"+index}>{formatSymbol(symbol, info)}</th>;
23+
symbolInfo.terminalOrder.map(function(symbol, index) {
24+
return <th key={"t"+index}>{formatSymbol(symbol, symbolInfo)}</th>;
2725
})
2826
}
29-
<th>{formatSymbol(END, info)}</th>
27+
<th>{formatSymbol(END, symbolInfo)}</th>
3028
{
31-
info.nonterminalOrder.map(function(symbol, index) {
32-
return <th key={"nt"+index}>{formatSymbol(symbol, info)}</th>;
29+
symbolInfo.nonterminalOrder.map(function(symbol, index) {
30+
return <th key={"nt"+index}>{formatSymbol(symbol, symbolInfo)}</th>;
3331
})
3432
}
3533
</tr>
@@ -42,7 +40,7 @@ export default function({ getCalculation, tableCalculation }) {
4240
<tr key={index}>
4341
<th scope="row">{index}</th>
4442
{
45-
info.terminalOrder.concat(END).map(function(s, index) {
43+
symbolInfo.terminalOrder.concat(END).map(function(s, index) {
4644
if (typeof state[s] === "undefined") {
4745
return <td key={"t"+index} />;
4846
} else {
@@ -60,7 +58,7 @@ export default function({ getCalculation, tableCalculation }) {
6058
actions.push(
6159
<li key={"r"+index}>
6260
{"reduce("}
63-
{formatProduction(productions[p], info)}
61+
{formatProduction(productions[p], symbolInfo)}
6462
{")"}
6563
</li>
6664
);
@@ -79,7 +77,7 @@ export default function({ getCalculation, tableCalculation }) {
7977
})
8078
}
8179
{
82-
info.nonterminalOrder.map(function(s, index) {
80+
symbolInfo.nonterminalOrder.map(function(s, index) {
8381
if (typeof state[s] === "undefined") {
8482
return <td key={"nt"+index} />;
8583
} else if (typeof state[s].shift === "undefined") {

src/components/analysis/parsing/abstract_lr_automaton_component.jsx

+8-8
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ function render(src) {
1616
});
1717
}
1818

19-
export default function({ getCalculation, automatonCalculation, title }) {
20-
const containerRef = useRef(null);
21-
19+
export default function({ grammar, automaton, title }) {
20+
const { productions, symbolInfo, start } = grammar.calculations;
2221
const src = template({
23-
info: getCalculation("grammar.symbolInfo"),
24-
automaton: getCalculation(automatonCalculation),
25-
productions: getCalculation("grammar.productions"),
26-
start: getCalculation("grammar.start"),
27-
title: title
22+
symbolInfo,
23+
automaton,
24+
productions,
25+
start,
26+
title
2827
});
28+
const containerRef = useRef(null);
2929

3030
useEffect(() => {
3131
render(src)

src/components/analysis/parsing/lalr1_automaton_component.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import AbstractLRAutomatonComponent from "./abstract_lr_automaton_component.jsx"
33
export const ID = "lalr1_automaton";
44
export const TITLE = "LALR(1) Automaton";
55

6-
export default function({ getCalculation }) {
6+
export default function({ grammar }) {
77
return (
88
<section id={ID} className="analysis">
99
<h2>{TITLE}</h2>
10-
<AbstractLRAutomatonComponent getCalculation={getCalculation} automatonCalculation="parsing.lr.lalr1_automaton" title={TITLE} />
10+
<AbstractLRAutomatonComponent grammar={grammar} automaton={grammar.calculations.lalr1Automaton} title={TITLE} />
1111
</section>
1212
);
1313
}

src/components/analysis/parsing/lalr1_table_component.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import AbstractLR1TableComponent from "./abstract_lr1_table_component.jsx";
33
export const ID = "lalr1_table";
44
export const TITLE = "LALR(1) Parsing Table";
55

6-
export default function({ getCalculation }) {
6+
export default function({ grammar }) {
77
return (
88
<section id={ID} className="analysis">
99
<h2>{TITLE}</h2>
10-
<AbstractLR1TableComponent getCalculation={getCalculation} tableCalculation="parsing.lr.lalr1_table" />
10+
<AbstractLR1TableComponent grammar={grammar} table={grammar.calculations.lalr1Table} />
1111
</section>
1212
);
1313
}

src/components/analysis/parsing/ll1_table_component.jsx

+10-12
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ import { END } from "../../../grammar/symbols.js";
44
export const ID = "ll1_table";
55
export const TITLE = "LL(1) Parsing Table";
66

7-
export default function({ getCalculation }) {
8-
const info = getCalculation("grammar.symbolInfo");
9-
const table = getCalculation("parsing.ll.ll1_table");
10-
const productions = getCalculation("grammar.productions");
7+
export default function({ grammar }) {
8+
const { symbolInfo, ll1Table: table, productions } = grammar.calculations;
119

1210
return (
1311
<section id={ID} className="analysis">
@@ -18,36 +16,36 @@ export default function({ getCalculation }) {
1816
<col />
1917
</colgroup>
2018
<colgroup className="t">
21-
{fillArray(info.terminals.size + 1, (index) => <col key={index} />)}
19+
{fillArray(symbolInfo.terminals.size + 1, (index) => <col key={index} />)}
2220
</colgroup>
2321

2422
<thead>
2523
<tr>
2624
<th />
2725
{
28-
info.terminalOrder.map(function(symbol, index) {
29-
return <th key={index}>{formatSymbol(symbol, info)}</th>;
26+
symbolInfo.terminalOrder.map(function(symbol, index) {
27+
return <th key={index}>{formatSymbol(symbol, symbolInfo)}</th>;
3028
})
3129
}
32-
<th>{formatSymbol(END, info)}</th>
30+
<th>{formatSymbol(END, symbolInfo)}</th>
3331
</tr>
3432
</thead>
3533

3634
<tbody>
3735
{
38-
info.productionOrder.map(function(nt, index) {
36+
symbolInfo.productionOrder.map(function(nt, index) {
3937
return (
4038
<tr key={index}>
41-
<th scope="row">{formatSymbol(nt, info)}</th>
39+
<th scope="row">{formatSymbol(nt, symbolInfo)}</th>
4240
{
43-
info.terminalOrder.concat(END).map(function(t, index) {
41+
symbolInfo.terminalOrder.concat(END).map(function(t, index) {
4442
if (typeof table[nt][t] !== "undefined") {
4543
return (
4644
<td key={index} className={table[nt][t].length > 1 ? "conflict" : ""}>
4745
<ul>
4846
{
4947
table[nt][t].map(function(p, index) {
50-
return <li key={index}>{formatProduction(productions[p], info)}</li>;
48+
return <li key={index}>{formatProduction(productions[p], symbolInfo)}</li>;
5149
})
5250
}
5351
</ul>

src/components/analysis/parsing/lr0_automaton_component.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import AbstractLRAutomatonComponent from "./abstract_lr_automaton_component.jsx"
33
export const ID = "lr0_automaton";
44
export const TITLE = "LR(0) Automaton";
55

6-
export default function({ getCalculation }) {
6+
export default function({ grammar }) {
77
return (
88
<section id={ID} className="analysis">
99
<h2>{TITLE}</h2>
10-
<AbstractLRAutomatonComponent getCalculation={getCalculation} automatonCalculation="parsing.lr.lr0_automaton" title={TITLE} />
10+
<AbstractLRAutomatonComponent grammar={grammar} automaton={grammar.calculations.lr0Automaton} title={TITLE} />
1111
</section>
1212
);
1313
}

src/components/analysis/parsing/lr0_table_component.jsx

+11-13
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ import { fillArray, formatSymbol, formatProduction } from "../../helpers.js";
33
export const ID = "lr0_table";
44
export const TITLE = "LR(0) Parsing Table";
55

6-
export default function({ getCalculation }) {
7-
const info = getCalculation("grammar.symbolInfo");
8-
const table = getCalculation("parsing.lr.lr0_table");
9-
const productions = getCalculation("grammar.productions");
6+
export default function({ grammar }) {
7+
const { symbolInfo, lr0Table: table, productions } = grammar.calculations;
108

119
return (
1210
<section id={ID} className="analysis">
@@ -17,23 +15,23 @@ export default function({ getCalculation }) {
1715
<col />
1816
</colgroup>
1917
<colgroup className="t">
20-
{fillArray(info.terminals.size, (index) => <col key={index} />)}
18+
{fillArray(symbolInfo.terminals.size, (index) => <col key={index} />)}
2119
</colgroup>
2220
<colgroup className="nt">
23-
{fillArray(info.nonterminals.size, (index) => <col key={index} />)}
21+
{fillArray(symbolInfo.nonterminals.size, (index) => <col key={index} />)}
2422
</colgroup>
2523

2624
<thead>
2725
<tr>
2826
<th>State</th>
2927
{
30-
info.terminalOrder.map(function(symbol, index) {
31-
return <th key={"t"+index}>{formatSymbol(symbol, info)}</th>;
28+
symbolInfo.terminalOrder.map(function(symbol, index) {
29+
return <th key={"t"+index}>{formatSymbol(symbol, symbolInfo)}</th>;
3230
})
3331
}
3432
{
35-
info.nonterminalOrder.map(function(symbol, index) {
36-
return <th key={"nt"+index}>{formatSymbol(symbol, info)}</th>;
33+
symbolInfo.nonterminalOrder.map(function(symbol, index) {
34+
return <th key={"nt"+index}>{formatSymbol(symbol, symbolInfo)}</th>;
3735
})
3836
}
3937
</tr>
@@ -46,7 +44,7 @@ export default function({ getCalculation }) {
4644
<tr key={index}>
4745
<th scope="row">{index}</th>
4846
{
49-
info.terminalOrder.map(function(s, index) {
47+
symbolInfo.terminalOrder.map(function(s, index) {
5048
let actions = [];
5149

5250
if (typeof state.shift[s] !== "undefined") {
@@ -60,7 +58,7 @@ export default function({ getCalculation }) {
6058
actions.push(
6159
<li key={"r"+index}>
6260
{"reduce("}
63-
{formatProduction(productions[p], info)}
61+
{formatProduction(productions[p], symbolInfo)}
6462
{")"}
6563
</li>
6664
);
@@ -78,7 +76,7 @@ export default function({ getCalculation }) {
7876
}
7977

8078
{
81-
info.nonterminalOrder.map(function(s, index) {
79+
symbolInfo.nonterminalOrder.map(function(s, index) {
8280
return (
8381
<td key={"nt"+index}>
8482
<ul>{typeof state.shift[s] !== "undefined" ? <li>{state.shift[s]}</li> : null}</ul>

src/components/analysis/parsing/lr1_automaton_component.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import AbstractLRAutomatonComponent from "./abstract_lr_automaton_component.jsx"
33
export const ID = "lr1_automaton";
44
export const TITLE = "LR(1) Automaton";
55

6-
export default function({ getCalculation }) {
6+
export default function({ grammar }) {
77
return (
88
<section id={ID} className="analysis">
99
<h2>{TITLE}</h2>
10-
<AbstractLRAutomatonComponent getCalculation={getCalculation} automatonCalculation="parsing.lr.lr1_automaton" title={TITLE} />
10+
<AbstractLRAutomatonComponent grammar={grammar} automaton={grammar.calculations.lr1Automaton} title={TITLE} />
1111
</section>
1212
);
1313
}

src/components/analysis/parsing/lr1_table_component.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import AbstractLR1TableComponent from "./abstract_lr1_table_component.jsx";
33
export const ID = "lr1_table";
44
export const TITLE = "LR(1) Parsing Table";
55

6-
export default function({ getCalculation }) {
6+
export default function({ grammar }) {
77
return (
88
<section id={ID} className="analysis">
99
<h2>{TITLE}</h2>
10-
<AbstractLR1TableComponent getCalculation={getCalculation} tableCalculation="parsing.lr.lr1_table" />
10+
<AbstractLR1TableComponent grammar={grammar} table={grammar.calculations.lr1Table} />
1111
</section>
1212
);
1313
}

src/components/analysis/parsing/lr_automaton_graph.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { bareFormatItem, bareFormatSymbol } from "../../helpers.js";
22

3-
export default function({ info, automaton, productions, start, title }) {
3+
export default function({ symbolInfo, automaton, productions, start, title }) {
44
let result = [];
55

66
result.push(
@@ -16,7 +16,7 @@ export default function({ info, automaton, productions, start, title }) {
1616
" [label=\"",
1717
index,
1818
" | ",
19-
state.items.map(function(item) { return bareFormatItem(item, start, productions, info); }).join("\\n"),
19+
state.items.map(function(item) { return bareFormatItem(item, start, productions, symbolInfo); }).join("\\n"),
2020
"\"];\n"
2121
);
2222
});
@@ -30,7 +30,7 @@ export default function({ info, automaton, productions, start, title }) {
3030
" -> s",
3131
state.transitions[s],
3232
" [label=\"",
33-
bareFormatSymbol(s, info),
33+
bareFormatSymbol(s, symbolInfo),
3434
"\"];\n"
3535
);
3636
}

src/components/analysis/parsing/slr1_table_component.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import AbstractLR1TableComponent from "./abstract_lr1_table_component.jsx";
33
export const ID = "slr1_table";
44
export const TITLE = "SLR(1) Parsing Table";
55

6-
export default function({ getCalculation }) {
6+
export default function({ grammar }) {
77
return (
88
<section id={ID} className="analysis">
99
<h2>{TITLE}</h2>
10-
<AbstractLR1TableComponent getCalculation={getCalculation} tableCalculation="parsing.lr.slr1_table" />
10+
<AbstractLR1TableComponent grammar={grammar} table={grammar.calculations.slr1Table} />
1111
</section>
1212
);
1313
}

0 commit comments

Comments
 (0)