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

Check grammar is LL(1) as part of Travis builds #458

Merged
merged 2 commits into from
Oct 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
index.ids
deploy_key
index.html
/node_modules/
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
language: generic
language: node_js
node_js:
- "node"

script:
- bash ./deploy.sh
Expand Down
50 changes: 50 additions & 0 deletions check-grammar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use strict";
const Grammar = require("syntax-cli").Grammar;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: "use strict";

const LLParsingTable = require("syntax-cli").LLParsingTable;
const jsdom = require("jsdom");
const fs = require("fs");

function getRulesFromDOM(window) {
let rules = window.document.querySelectorAll("pre.grammar[id]");
return [].map.call(rules, pre => pre.textContent);
}

function processRules(rules) {
const REGEXP = /(\s*:\n\s*)|\b(integer|float|identifier|string|whitespace|comment|other)\b|(\s*\n\s*)|(ε)/g;
return rules.map(rule => {
return rule.trim().replace(REGEXP, m => {
if (/^(integer|float|identifier|string|whitespace|comment|other)$/.test(m)) {
return m.toUpperCase();
}
if (/:\n/.test(m)) { return "\n : "; }
if (/\n/.test(m)) { return "\n | "; }
if (/ε/.test(m)) { return "/* epsilon */"; }
}) + "\n ;";
});
}

function toBNF(rules) {
return "\n%%\n\n" + processRules(rules).join("\n");
}

let path = process.argv[2];
let html = fs.readFileSync(path, "utf8");
let dom = new jsdom.JSDOM(html);
let rules = getRulesFromDOM(dom.window);
let bnf = toBNF(rules);
let data = Grammar.dataFromString(bnf, "bnf");
let grammar = Grammar.fromData(data, { mode: "LL1" });
let table = new LLParsingTable({ grammar: grammar });
let conflicts = table.getConflicts();
if (table.hasConflicts()) {
console.log("The WebIDL grammar is NOT LL(1) due to the following conflicts:");
Object.keys(conflicts).forEach((nt, i) => {
let conflict = conflicts[nt];
let str = Object.keys(conflict).map(k => ` * ${k} (${ conflict[k] })`).join("\n");
console.log(` ${i+1}. ${ nt }:\n${ str }`);
});
process.exit(1);
} else {
console.log("The WebIDL grammar is LL(1).");
process.exit(0);
}
1 change: 1 addition & 0 deletions deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ TARGET_BRANCH="gh-pages"

function doCompile {
curl https://api.csswg.org/bikeshed/ -F [email protected] > out/index.html
node ./check-grammar.js ./out/index.html
}

# Pull requests and commits to other branches shouldn't try to deploy, just build to verify
Expand Down
21 changes: 15 additions & 6 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -1974,8 +1974,13 @@ The following extended attributes are applicable to operations:

<pre class="grammar" id="prod-Argument">
Argument :
ExtendedAttributeList "optional" TypeWithExtendedAttributes ArgumentName Default
ExtendedAttributeList Type Ellipsis ArgumentName
ExtendedAttributeList ArgumentRest
</pre>

<pre class="grammar" id="prod-ArgumentRest">
ArgumentRest :
"optional" TypeWithExtendedAttributes ArgumentName Default
Type Ellipsis ArgumentName
</pre>

<pre class="grammar" id="prod-ArgumentName">
Expand Down Expand Up @@ -4232,8 +4237,13 @@ No [=extended attributes=] are applicable to dictionaries.

<pre class="grammar" id="prod-DictionaryMember">
DictionaryMember :
ExtendedAttributeList "required" TypeWithExtendedAttributes identifier Default ";"
ExtendedAttributeList Type identifier Default ";"
ExtendedAttributeList DictionaryMemberRest
</pre>

<pre class="grammar" id="prod-DictionaryMemberRest">
DictionaryMemberRest :
"required" TypeWithExtendedAttributes identifier Default ";"
Type identifier Default ";"
</pre>

<pre class="grammar" id="prod-PartialDictionary">
Expand Down Expand Up @@ -5083,8 +5093,7 @@ type.

<pre class="grammar" id="prod-TypeWithExtendedAttributes">
TypeWithExtendedAttributes :
ExtendedAttributeList SingleType
ExtendedAttributeList UnionType Null
ExtendedAttributeList Type
</pre>

<pre class="grammar" id="prod-SingleType">
Expand Down
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"private": true,
"description": "Checks that the WebIDL grammar is LL(1)",
"devDependencies": {
"jsdom": "^11.3.0",
"syntax-cli": "0.0.97"
}
}