Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature language generator and Chomsky normal form #92

Draft
wants to merge 26 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9b64a3e
:unamused: chore(deps): Upgrade.
make-github-pseudonymous-again Aug 6, 2020
765facd
:art: style(test/grammar): Remove unused imports.
make-github-pseudonymous-again Aug 6, 2020
29522e6
:sparkles: feat(language/generate): First attempt.
make-github-pseudonymous-again Aug 6, 2020
92550bc
:mag: test(language/generate): More tests.
make-github-pseudonymous-again Aug 6, 2020
bbdde07
:sparkles: feat(cnf/is): Add test for Chomsky normal form.
make-github-pseudonymous-again Aug 6, 2020
8303e22
:sparkles: feat(grammar/iter): New productions generators.
make-github-pseudonymous-again Aug 11, 2020
42eb654
:sparkles: feat(cfg/nullable): Add method to compute nullable nonterm…
make-github-pseudonymous-again Aug 11, 2020
e2c0063
:unamused: chore(dev-deps): Upgrade.
make-github-pseudonymous-again Aug 11, 2020
818684f
:art: style: Sprinkle some assertions.
make-github-pseudonymous-again Aug 11, 2020
1ff9012
:art: style: Explicitly allow anonymous default exports in index files.
make-github-pseudonymous-again Aug 11, 2020
e666dae
:books: docs(cnf/is): Add reference from Wikipedia.
make-github-pseudonymous-again Aug 11, 2020
59e0a66
:art: style(grammar/expandobject): Remove useless import.
make-github-pseudonymous-again Aug 11, 2020
0e44997
:books: docs(language/_generate): Fix wording.
make-github-pseudonymous-again Aug 11, 2020
6c73f03
:sparkles: feat(toJSON): Implement conversion of grammar to JSON.
make-github-pseudonymous-again Aug 11, 2020
341bc6a
:sparkles: feat(cnf/from): First draft.
make-github-pseudonymous-again Aug 11, 2020
3a7fa3d
:recycle: refactor(cnf/from): Use some abstraction.
make-github-pseudonymous-again Aug 11, 2020
dc92543
:recycle: refactor(sets): Rename Pairs#right -> Pairs#rightOf.
make-github-pseudonymous-again Aug 15, 2020
0c9ed42
:recycle: refactor(cfg/nullable): Use some abstraction.
make-github-pseudonymous-again Aug 15, 2020
d011753
:recycle: refactor(transitive_closure): Extract reachability computat…
make-github-pseudonymous-again Aug 16, 2020
4372cde
:bug: fix(util/_mostAppropriateKeyHolder): Fix case where _keys is em…
make-github-pseudonymous-again Aug 16, 2020
7a23cde
:recycle: refactor(cnf/from): Extract some reusable functions.
make-github-pseudonymous-again Aug 16, 2020
a033331
:sparkles: feat(grammar/simplify): First draft.
make-github-pseudonymous-again Aug 16, 2020
387c489
:unamused: chore(dev-deps): Upgrade.
make-github-pseudonymous-again Sep 2, 2020
2c3589d
:mag: test: Configure and use additional scripts.
make-github-pseudonymous-again Sep 2, 2020
cfd73e0
:recycle: refactor(Pairs): Use package.
make-github-pseudonymous-again Sep 2, 2020
70e67b1
:unamused: chore(dev-deps): Upgrade.
make-github-pseudonymous-again Sep 3, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ before_script:
- ./cc-test-reporter before-build

script:
- npm run cover
- npm run travis

after_script:
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
Grammar compilation toolkit for JavaScript. Served with asynchronous goodness.
See [docs](https://aureooms.github.io/js-grammar/index.html).

> :warning: The code needs a ES2015+ polyfill to work (`regeneratorRuntime`),
> for instance [@babel/polyfill](https://babeljs.io/docs/usage/polyfill).
> :warning: The code requires `regeneratorRuntime` to be defined, for instance by importing
> [regenerator-runtime/runtime](https://www.npmjs.com/package/regenerator-runtime).

```js
const G = grammar.from( { root , start , eof , productions } ) ;
Expand Down
8 changes: 4 additions & 4 deletions doc/manual/usage.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Usage

> :warning: The code needs a ES2015+ polyfill to work (`regeneratorRuntime`),
> for instance [@babel/polyfill](https://babeljs.io/docs/usage/polyfill).
> :warning: The code requires `regeneratorRuntime` to be defined, for instance by importing
> [regenerator-runtime/runtime](https://www.npmjs.com/package/regenerator-runtime).

First, require the polyfill at the entry point of your application
```js
require( '@babel/polyfill' ) ;
require( 'regenerator-runtime/runtime' );
// or
import '@babel/polyfill' ;
import 'regenerator-runtime/runtime.js' ;
```

Then, import the library where needed
Expand Down
86 changes: 70 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"author": "aureooms",
"ava": {
"require": [
"@babel/polyfill",
"regenerator-runtime/runtime",
"@babel/register"
],
"files": [
Expand All @@ -17,11 +17,57 @@
"presets": [
"@babel/preset-env"
],
"plugins": [
"@babel/plugin-proposal-async-generator-functions"
],
"env": {
"test": {
"presets": [
"babel-preset-power-assert"
],
"plugins": [
[
"transform-remove-console",
{
"exclude": [
"log",
"error",
"warn"
]
}
]
],
"sourceMaps": "inline"
},
"development": {
"presets": [
"babel-preset-power-assert"
],
"plugins": [
[
"transform-remove-console",
{
"exclude": [
"log",
"error",
"warn"
]
}
]
],
"sourceMaps": "inline"
},
"production": {
"plugins": [
"babel-plugin-unassert",
[
"transform-remove-console",
{
"exclude": [
"log",
"error",
"warn"
]
}
]
],
"sourceMaps": "inline"
}
}
Expand All @@ -30,26 +76,32 @@
"url": "https://github.com/aureooms/js-grammar/issues"
},
"dependencies": {
"@aureooms/js-itertools": "^4.0.1"
"@aureooms/js-itertools": "^4.1.0",
"@aureooms/js-pairs": "^0.0.0",
"@aureooms/js-red-black-tree": "^2.0.7"
},
"devDependencies": {
"@aureooms/js-compare": "1.4.5",
"@aureooms/js-compare": "1.4.8",
"@aureooms/js-tape": "7.0.0",
"@babel/cli": "7.10.5",
"@babel/core": "7.11.0",
"@babel/cli": "7.11.6",
"@babel/core": "7.11.6",
"@babel/plugin-proposal-async-generator-functions": "7.10.5",
"@babel/polyfill": "7.10.4",
"@babel/preset-env": "7.11.0",
"@babel/register": "7.10.5",
"ava": "3.11.1",
"@babel/preset-env": "7.11.5",
"@babel/register": "7.11.5",
"ava": "3.12.1",
"babel-plugin-transform-remove-console": "6.9.4",
"babel-plugin-unassert": "3.0.1",
"babel-preset-power-assert": "3.0.0",
"coveralls": "3.1.0",
"esdoc": "1.1.0",
"esdoc-ecmascript-proposal-plugin": "1.0.0",
"esdoc-inject-script-plugin": "1.0.0",
"esdoc-inject-style-plugin": "1.0.0",
"esdoc-standard-plugin": "1.0.0",
"np": "6.3.2",
"nyc": "15.1.0"
"np": "6.5.0",
"nyc": "15.1.0",
"power-assert": "1.6.1",
"regenerator-runtime": "0.13.7"
},
"files": [
"lib"
Expand All @@ -67,12 +119,14 @@
"url": "https://github.com/aureooms/js-grammar"
},
"scripts": {
"build": "rm -rf lib && babel src -d lib",
"build": "babel --delete-dir-on-start --env-name production src -d lib",
"cover": "nyc --reporter=lcov npm test",
"dev": "npm run cover -- -- -st --fail-fast",
"esdoc": "esdoc",
"prepare": "npm run build",
"release": "np",
"test": "ava"
"test": "ava",
"travis": "npm run cover"
},
"sideEffects": false
}
3 changes: 2 additions & 1 deletion src/ast/flatten.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import assert from 'assert' ;
import { anyIterator } from '../util' ;

function getCount ( object ) {
Expand All @@ -7,7 +8,7 @@ function getCount ( object ) {

export default async function* flatten ( root ) {

// assert root.type === 'node'
assert(root.type === 'node');

const stack = [
{ // no need to use the exhaustive iterator since flatten exhausts the whole subtree
Expand Down
1 change: 1 addition & 0 deletions src/ast/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import materialize from './materialize' ;
import rmap from './rmap' ;
import transform from './transform' ;

/* eslint import/no-anonymous-default-export: [2, {"allowObject": true}] */
export default {
Children ,
_children_exhaust ,
Expand Down
3 changes: 2 additions & 1 deletion src/ast/materialize.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import assert from 'assert';
import { anyIterator } from '../util' ;

export default async function materialize ( root ) {

// assert root.type === 'node'
assert(root.type === 'node');

const parents = [
{
Expand Down
4 changes: 3 additions & 1 deletion src/ast/transform.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import assert from 'assert' ;

export default async function transform ( tree , match , ctx ) {
// assert tree.type === 'node'
assert(tree.type === 'node');
const { nonterminal , production } = tree ;
return await match[nonterminal][production]( tree , match , ctx ) ;
}
13 changes: 13 additions & 0 deletions src/cfg/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import nullable from './nullable' ;
import unitrules from './unitrules' ;

/* eslint import/no-anonymous-default-export: [2, {"allowObject": true}] */
export default {
nullable ,
unitrules ,
} ;

export {
nullable ,
unitrules ,
} ;
53 changes: 53 additions & 0 deletions src/cfg/nullable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import assert from 'assert' ;
import { list , any , map , all } from '@aureooms/js-itertools' ;
import { simplify_zol_system_dnf_no_negation } from '../math/logic' ;

/**
* Compute the set of nullable terminals from a list of productions.
*
* NB: this corresponds to computing the set of variables that are true given a
* system of equations in disjunctive normal form. Given the grammar
*
* A -> B | BB | C
* B -> CC | b
* C -> C | ε
*
* we get the following system of equations
*
* A = B or (B and B) or C
* B = (C and C) or FALSE
* C = C or TRUE
*
* where terminals have been replaced by FALSE and the empty word has been
* replaced by TRUE.
*
* The solution for this example is
*
* A = B = C = TRUE
*
* If some symbol is not defined (has no productions) then it is FALSE (empty
* disjunction).
*
* TODO Use solver for this kind of system instead of ad-hoc code.
*
* @param {Iterable} productions - A sequence of (nonterminal, rule) pairs.
* @return {Set} The set of nullable terminals.
*/
export default function nullable ( productions ) {

const system = nullable_zol_system_dnf_no_negation(productions);
return simplify_zol_system_dnf_no_negation(system);

}

function* nullable_zol_system_dnf_no_negation ( productions ) {

for ( const [ nonterminal , rule ] of productions ) {
assert(typeof nonterminal === 'string');
assert(rule instanceof Array);
if (any(map(x => x.type === 'leaf', rule))) continue ;
assert(all(map(x => x.type === 'node', rule))) ;
yield [ nonterminal , list(map(x => x.nonterminal, rule)) ] ;
}

}
11 changes: 11 additions & 0 deletions src/cfg/unitrules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function* unitrules ( productions ) {

for ( const [A, rule] of productions ) {
if ( rule.length === 1 && rule[0].type === 'node' ) {
// A -> B
const B = rule[0].nonterminal ;
yield [A, B];
}
}

}
Loading