diff --git a/.gitignore b/.gitignore index 772f60631..20bd5ece8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ index.ids deploy_key index.html +/node_modules/ diff --git a/.travis.yml b/.travis.yml index dd244e651..0e777890a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ -language: generic +language: node_js +node_js: + - "node" script: - bash ./deploy.sh diff --git a/check-grammar.js b/check-grammar.js new file mode 100644 index 000000000..ad5c68052 --- /dev/null +++ b/check-grammar.js @@ -0,0 +1,48 @@ +const Grammar = require("syntax-cli").Grammar; +const LLParsingTable = require("syntax-cli").LLParsingTable; +const jsdom = require("jsdom"); + +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 = require("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); +} diff --git a/deploy.sh b/deploy.sh index 1d0fbc32a..8c7666b2b 100755 --- a/deploy.sh +++ b/deploy.sh @@ -13,7 +13,8 @@ if [[ "$TRAVIS_PULL_REQUEST" != "false" || "$TRAVIS_BRANCH" != "$SOURCE_BRANCH" echo "Skipping deploy; just doing a build." mkdir out doCompile - exit 0 + node ./check-grammar.js ./out/index.html + exit $? fi # Save some useful information diff --git a/package.json b/package.json new file mode 100644 index 000000000..854dafcb0 --- /dev/null +++ b/package.json @@ -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" + } +}