Skip to content

Commit

Permalink
Merge pull request #56 from matthijsgroen/bugfix/use-proper-height-do…
Browse files Browse the repository at this point in the history
…wn-for-choice

Bugfix/use proper height down for choice
matthijsgroen authored Feb 19, 2023
2 parents ef07aed + 0ecf40a commit 495635b
Showing 39 changed files with 909 additions and 709 deletions.
53 changes: 53 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: CI

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

jobs:
lint:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [16.x, 18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: "yarn"
- name: Install Dependencies
run: yarn install --immutable
- name: Linting
run: yarn lint

test:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [16.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: "yarn"

- name: Install Dependencies
run: yarn install --immutable

- name: Run unit tests
run: yarn test --coverage
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.13.1] - 2023-02-19

### Fixes

- Fix height calculation of 'Choice' element. (Had to take over element from `railroad-diagrams` package)

## [1.13.0] - 2020-10-24

### Added
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ebnf2railroad",
"version": "1.13.0",
"version": "1.13.1",
"description": "EBNF to Railroad diagram",
"keywords": [
"ebnf",
@@ -28,10 +28,13 @@
"publish": ".travis/publish-site.sh"
},
"private": false,
"files": ["src/*", "bin/*"],
"files": [
"src/*",
"bin/*"
],
"dependencies": {
"commander": "^2.19.0",
"prettier": "^1.14.3",
"prettier": "2.8.4",
"railroad-diagrams": "https://github.com/tabatkins/railroad-diagrams#c7730b8fab6cb0fd55fc3c3b0a81ce355fdbf963",
"showdown": "^1.0.0",
"utf-railroad": "^1.0.1"
22 changes: 11 additions & 11 deletions src/ast/ebnf-transform.js
Original file line number Diff line number Diff line change
@@ -13,10 +13,10 @@ const NodeTypes = {
Repetition: 9,
Special: 10,
ExceptTerminal: 11,
ExceptNonTerminal: 12
ExceptNonTerminal: 12,
};

const identifyNode = node => {
const identifyNode = (node) => {
if (Array.isArray(node)) return NodeTypes.Root;
if (node.definition) return NodeTypes.Production;
if (node.choice) return NodeTypes.Choice;
@@ -37,33 +37,33 @@ const travelers = {
[NodeTypes.Root]: (node, next) => node.map(next),
[NodeTypes.Production]: (node, next) => ({
...node,
definition: next(node.definition)
definition: next(node.definition),
}),
[NodeTypes.Choice]: (node, next) => ({
...node,
choice: node.choice.map(next)
choice: node.choice.map(next),
}),
[NodeTypes.Group]: (node, next) => ({
...node,
group: next(node.group)
group: next(node.group),
}),
[NodeTypes.Sequence]: (node, next) => ({
...node,
sequence: node.sequence.map(next)
sequence: node.sequence.map(next),
}),
[NodeTypes.Optional]: (node, next) => ({
...node,
optional: next(node.optional)
optional: next(node.optional),
}),
[NodeTypes.Repetition]: (node, next) => ({
...node,
repetition: next(node.repetition)
})
repetition: next(node.repetition),
}),
};

const ebnfTransform = traverse(identifyNode)(travelers);

const ebnfOptimizer = transformers => ast => {
const ebnfOptimizer = (transformers) => (ast) => {
const optimize = ebnfTransform(transformers);
let current = ast;
let transformed = optimize(ast);
@@ -79,5 +79,5 @@ module.exports = {
ebnfOptimizer,
NodeTypes,
identifyNode,
travelers
travelers,
};
39 changes: 20 additions & 19 deletions src/ast/optimizers/choice-clustering.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,41 @@
const { NodeTypes } = require("../ebnf-transform");

const skipFirst = list =>
const skipFirst = (list) =>
[
list.some(e => e === "skip" || e.skip) && { skip: true },
...list.filter(e => e !== "skip" && !e.skip)
list.some((e) => e === "skip" || e.skip) && { skip: true },
...list.filter((e) => e !== "skip" && !e.skip),
].filter(Boolean);

const equalElements = (first, second) =>
JSON.stringify(first) === JSON.stringify(second);

module.exports = {
[NodeTypes.Choice]: current => {
[NodeTypes.Choice]: (current) => {
if (!current.choice) return current;

const isCertain = elem =>
const isCertain = (elem) =>
(elem.terminal && elem) || (elem.nonTerminal && elem);

const groupElements = elements => {
const allSet = elements.every(f => f);
const groupElements = (elements) => {
const allSet = elements.every((f) => f);
if (!allSet) return {};
return elements.reduce((acc, elem) => {
const key = JSON.stringify(elem);
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {});
};
const countSame = groupElements => {
const countSame = (groupElements) => {
const amounts = Object.values(groupElements);
return Math.max(...amounts);
};

const collectCertainFirstElements = current.choice.map(
elem => isCertain(elem) || (elem.sequence && isCertain(elem.sequence[0]))
(elem) =>
isCertain(elem) || (elem.sequence && isCertain(elem.sequence[0]))
);
const collectCertainLastElements = current.choice.map(
elem =>
(elem) =>
isCertain(elem) ||
(elem.sequence && isCertain(elem.sequence[elem.sequence.length - 1]))
);
@@ -80,11 +81,11 @@ module.exports = {
newChoices.length > 0 &&
hasEmpty && {
optional:
newChoices.length == 1 ? newChoices[0] : { choice: newChoices }
newChoices.length == 1 ? newChoices[0] : { choice: newChoices },
},
newChoices.length > 0 &&
!hasEmpty &&
(newChoices.length == 1 ? newChoices[0] : { choice: newChoices })
(newChoices.length == 1 ? newChoices[0] : { choice: newChoices }),
].filter(Boolean);
const replacementElement =
newElements.length > 1 ? { sequence: newElements } : newElements[0];
@@ -95,7 +96,7 @@ module.exports = {
choice: []
.concat(beforeChoices)
.concat(replacementElement)
.concat(afterChoices)
.concat(afterChoices),
}
: replacementElement;

@@ -132,12 +133,12 @@ module.exports = {
newChoices.length > 0 &&
hasEmpty && {
optional:
newChoices.length == 1 ? newChoices[0] : { choice: newChoices }
newChoices.length == 1 ? newChoices[0] : { choice: newChoices },
},
newChoices.length > 0 &&
!hasEmpty &&
(newChoices.length == 1 ? newChoices[0] : { choice: newChoices }),
JSON.parse(lastElement)
JSON.parse(lastElement),
].filter(Boolean);
const replacementElement =
newElements.length > 1 ? { sequence: newElements } : newElements[0];
@@ -148,7 +149,7 @@ module.exports = {
choice: []
.concat(beforeChoices)
.concat(replacementElement)
.concat(afterChoices)
.concat(afterChoices),
}
: replacementElement;

@@ -161,7 +162,7 @@ module.exports = {
...current,
choice: skipFirst(
current.choice
.map(item => {
.map((item) => {
const optimizedItem = item;
if (optimizedItem.choice) {
return optimizedItem.choice;
@@ -170,11 +171,11 @@ module.exports = {
}
})
.reduce((acc, item) => acc.concat(item), [])
)
),
};
if (equalElements(result, current)) {
return current;
}
return result;
}
},
};
24 changes: 13 additions & 11 deletions src/ast/optimizers/choice-with-skip.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,38 @@
const { NodeTypes } = require("../ebnf-transform");

module.exports = {
[NodeTypes.Optional]: current => {
[NodeTypes.Optional]: (current) => {
if (!current.optional || !current.optional.choice) {
return current;
}
return {
choice: [{ skip: true }].concat(
current.optional.choice
.filter(node => !node.skip)
.map(node => (node.repetition ? { ...node, skippable: false } : node))
)
.filter((node) => !node.skip)
.map((node) =>
node.repetition ? { ...node, skippable: false } : node
)
),
};
},
[NodeTypes.Choice]: current => {
[NodeTypes.Choice]: (current) => {
if (!current.choice) {
return current;
}
const hasSkippableRepetition = current.choice.some(
node => node.repetition && node.skippable
(node) => node.repetition && node.skippable
);
if (hasSkippableRepetition) {
return {
choice: [{ skip: true }].concat(
current.choice
.filter(node => !node.skip)
.map(
node => (node.repetition ? { ...node, skippable: false } : node)
.filter((node) => !node.skip)
.map((node) =>
node.repetition ? { ...node, skippable: false } : node
)
)
),
};
}
return current;
}
},
};
12 changes: 6 additions & 6 deletions src/ast/optimizers/deduplicate-choices.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
const { NodeTypes } = require("../ebnf-transform");

module.exports = {
[NodeTypes.Choice]: current => {
[NodeTypes.Choice]: (current) => {
if (!current.choice) {
return current;
}
const stringChoices = current.choice.map(item => JSON.stringify(item));
const stringChoices = current.choice.map((item) => JSON.stringify(item));
const uniqueDirectChoices = current.choice.filter(
(item, idx) => !(stringChoices.indexOf(JSON.stringify(item)) < idx)
);

const stringChoicesComments = current.choice.map(
item => (item.group && item.comment ? JSON.stringify(item.group) : null)
const stringChoicesComments = current.choice.map((item) =>
item.group && item.comment ? JSON.stringify(item.group) : null
);
const uniqueChoices = uniqueDirectChoices.filter(
item =>
(item) =>
(!item.comment &&
stringChoicesComments.indexOf(JSON.stringify(item)) === -1) ||
item.comment
@@ -27,5 +27,5 @@ module.exports = {
return { ...current, choice: uniqueChoices };
}
return current;
}
},
};
10 changes: 5 additions & 5 deletions src/ast/optimizers/optional-choices.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const { NodeTypes } = require("../ebnf-transform");

module.exports = {
[NodeTypes.Choice]: current => {
[NodeTypes.Choice]: (current) => {
if (!current.choice) {
return current;
}
const hasOptional = current.choice.some(node => node.optional);
const hasOptional = current.choice.some((node) => node.optional);
if (hasOptional) {
return {
optional: {
@@ -15,10 +15,10 @@ module.exports = {
? options.concat(option.optional)
: options.concat(option),
[]
)
}
),
},
};
}
return current;
}
},
};
Loading

0 comments on commit 495635b

Please sign in to comment.