ebnf2railroad
Advanced tools
Comparing version 1.5.0 to 1.6.0
@@ -7,2 +7,13 @@ # Changelog | ||
## [1.6.0] - 2018-11-13 | ||
### Added | ||
- Formatting of text output in the document | ||
- Long sequences will wrap over multiple lines | ||
- Choice lists between 3 and 6 items will be displayed under eachother | ||
- Choice lists over 6 items will be displayed as a grid | ||
- Option `--no-text-formatting` to write all text on a single line | ||
- Option `--no-optimizations` to write diagrams as-is | ||
- Option `--no-target` to skip writing documentation | ||
- Option `--write-style` to 'prettify' source documents | ||
## [1.5.0] - 2018-11-10 | ||
@@ -9,0 +20,0 @@ ### Added |
{ | ||
"name": "ebnf2railroad", | ||
"version": "1.5.0", | ||
"version": "1.6.0", | ||
"description": "EBNF to Railroad diagram", | ||
@@ -34,3 +34,3 @@ "keywords": [ | ||
"commander": "^2.19.0", | ||
"railroad-diagrams": "https://github.com/tabatkins/railroad-diagrams", | ||
"railroad-diagrams": "https://github.com/tabatkins/railroad-diagrams#e9b1a12", | ||
"showdown": "^1.8.7" | ||
@@ -37,0 +37,0 @@ }, |
# EBNF 2 RailRoad | ||
[![npm](https://img.shields.io/npm/v/ebnf2railroad.svg)](http://npm.im/ebnf2railroad) | ||
[![Build | ||
Status](https://travis-ci.org/matthijsgroen/ebnf2railroad.svg?branch=master)](https://travis-ci.org/matthijsgroen/ebnf2railroad) | ||
[![travis](https://badgen.now.sh/travis/matthijsgroen/ebnf2railroad?icon=travis)](https://travis-ci.org/matthijsgroen/ebnf2railroad) | ||
[![npm](https://badgen.now.sh/npm/v/ebnf2railroad?icon=npm)](http://npm.im/ebnf2railroad) | ||
[![code style: prettier](https://badgen.now.sh/badge/code%20style/prettier/ff69b4)](https://github.com/prettier/prettier) | ||
[![publishsize](https://badgen.now.sh/packagephobia/publish/ebnf2railroad)](https://packagephobia.now.sh/result?p=ebnf2railroad) | ||
[![license](https://badgen.now.sh/github/license/matthijsgroen/ebnf2railroad)](https://github.com/matthijsgroen/ebnf2railroad) | ||
A command line tool to create nice documentation including railroad | ||
A command line tool to create great documentation including railroad | ||
diagrams based on the ISO/IEC 14977 specification | ||
## Features | ||
- Creates optimized visual syntax diagrams based on the EBNF syntax | ||
- Quick navigation using references of used declarations | ||
- Nice comment markup using markdown | ||
- Validates if document is complete and has no duplicate declarations | ||
- Shows pretty printed text syntax in the document | ||
- Pretty printing of the sourcefile | ||
## Installation | ||
@@ -25,9 +36,35 @@ | ||
-V, --version output the version number | ||
-q, --quiet suppress output to STDOUT | ||
-o, --target [target] output the file to target destination. | ||
-q, --quiet suppress output to STDOUT | ||
--validate exit with status code 2 if ebnf document has warnings | ||
--title [title] title to use for HTML document | ||
--no-target skip writing output HTML | ||
-t, --title [title] title to use for HTML document | ||
--lint exit with status code 2 if EBNF document has warnings | ||
--write-style rewrites the source document with styled text | ||
--no-optimizations does not try to optimize the diagrams | ||
--no-text-formatting does not format the output text version (becomes single line) | ||
-h, --help output usage information | ||
``` | ||
### Examples | ||
To generate HTML documentation of the EBNF file: | ||
``` | ||
ebnf2railroad --title 'My Title' inputfile.ebnf -o outputfile.html | ||
``` | ||
To only verify the EBNF file: | ||
``` | ||
ebnf2railroad --lint inputfile.ebnf --no-target | ||
``` | ||
To prettify the source EBNF file: | ||
``` | ||
ebnf2railroad --write-style inputfile.ebnf --no-target | ||
``` | ||
### Online examples | ||
Check the examples folder for an example input file and the generated result page. | ||
@@ -34,0 +71,0 @@ |
@@ -8,13 +8,22 @@ const program = require("commander"); | ||
const { version } = require("../package.json"); | ||
const { productionToEBNF } = require("./ebnf-builder"); | ||
program.version(version); | ||
program | ||
.version(version) | ||
program | ||
.usage("[options] <file>") | ||
.option("-o, --target [target]", "output the file to target destination.") | ||
.option("-q, --quiet", "suppress output to STDOUT") | ||
.option("--validate", "exit with status code 2 if ebnf document has warnings") | ||
.option("--title [title]", "title to use for HTML document") | ||
.description( | ||
"Converts an ISO/IEC 14977 EBNF file to a HTML file with SVG railroad diagrams" | ||
) | ||
.option("-o, --target [target]", "output the file to target destination.") | ||
.option("--no-target", "skip writing output HTML", null) | ||
.option("-t, --title [title]", "title to use for HTML document") | ||
.option("--lint", "exit with status code 2 if EBNF document has warnings") | ||
.option("--write-style", "rewrites the source document with styled text") | ||
.option("--no-optimizations", "does not try to optimize the diagrams") | ||
.option( | ||
"--no-text-formatting", | ||
"does not format the output text version (becomes single line)" | ||
); | ||
@@ -29,2 +38,4 @@ | ||
const allowOutput = !program.quiet; | ||
const optimizeDiagrams = program.optimizations; | ||
const textFormatting = program.textFormatting; | ||
const output = text => allowOutput && process.stdout.write(text + "\n"); | ||
@@ -41,2 +52,3 @@ const outputError = text => allowOutput && process.stderr.write(text + "\n"); | ||
const ebnf = await readFile(filename, "utf8"); | ||
const basename = filename | ||
@@ -49,3 +61,4 @@ .split(".") | ||
const targetFilename = program.target || defaultOutputFilename; | ||
const targetFilename = | ||
program.target === true ? defaultOutputFilename : program.target; | ||
@@ -59,8 +72,24 @@ const ast = parse(ebnf); | ||
const report = createDocumentation(ast, { | ||
title: documentTitle | ||
}); | ||
await writeFile(targetFilename, report, "utf8"); | ||
if (program.writeStyle) { | ||
const prettyOutput = | ||
ast | ||
.map(production => | ||
productionToEBNF(production, { markup: false, format: true }) | ||
) | ||
.join("\n\n") + "\n"; | ||
output(`📜 Document created at ${targetFilename}`); | ||
await writeFile(filename, prettyOutput, "utf8"); | ||
output(`💅 Source updated at ${filename}`); | ||
} | ||
if (targetFilename) { | ||
const report = createDocumentation(ast, { | ||
title: documentTitle, | ||
optimizeDiagrams, | ||
textFormatting | ||
}); | ||
await writeFile(targetFilename, report, "utf8"); | ||
output(`📜 Document created at ${targetFilename}`); | ||
} | ||
warnings.length > 0 && program.validate && process.exit(2); | ||
@@ -77,2 +106,4 @@ } catch (e) { | ||
outputError(e.message); | ||
output(""); | ||
output("use --help for usage information"); | ||
} | ||
@@ -79,0 +110,0 @@ process.exit(1); |
@@ -27,71 +27,5 @@ const { | ||
const { createAlphabeticalToc, createStructuralToc } = require("./toc"); | ||
const { productionToEBNF } = require("./ebnf-builder"); | ||
const dasherize = str => str.replace(/\s+/g, "-"); | ||
const sanitize = str => | ||
str | ||
.replace(/&/g, "&") | ||
.replace(/</g, "<") | ||
.replace(/>/g, ">"); | ||
const productionToEBNF = production => { | ||
if (production.identifier) { | ||
return `<span class="ebnf-identifier">${ | ||
production.identifier | ||
}</span> = ${productionToEBNF( | ||
production.definition | ||
)}<span class="ebnf-end">;</span>`; | ||
} | ||
if (production.terminal) { | ||
return production.terminal.indexOf('"') > -1 | ||
? `<span class="ebnf-terminal">'${sanitize(production.terminal)}'</span>` | ||
: `<span class="ebnf-terminal">"${sanitize(production.terminal)}"</span>`; | ||
} | ||
if (production.nonTerminal) { | ||
return `<a class="ebnf-non-terminal" href="#${dasherize( | ||
production.nonTerminal | ||
)}">${production.nonTerminal}</a>`; | ||
} | ||
if (production.choice) { | ||
return production.choice.map(productionToEBNF).join(" <wbr />| "); | ||
} | ||
if (production.sequence) { | ||
return production.sequence.map(productionToEBNF).join(" , "); | ||
} | ||
if (production.specialSequence) { | ||
return `<span class="ebnf-special-sequence">? ${ | ||
production.specialSequence | ||
} ?</span>`; | ||
} | ||
if (production.repetition && production.amount !== undefined) { | ||
return `<span class="ebnf-multiplier">${ | ||
production.amount | ||
} *</span> ${productionToEBNF(production.repetition)}`; | ||
} | ||
if (production.repetition) { | ||
return `<wbr />{ ${productionToEBNF(production.repetition)} }`; | ||
} | ||
if (production.comment) { | ||
return `${productionToEBNF( | ||
production.group | ||
)} <span class="ebnf-comment">(* ${sanitize(production.comment)} *)</span>`; | ||
} | ||
if (production.group) { | ||
return `<wbr />( ${productionToEBNF(production.group)} )`; | ||
} | ||
if (production.optional) { | ||
return `<wbr />[ ${productionToEBNF(production.optional)} ]`; | ||
} | ||
if (production.exceptNonTerminal) { | ||
return `${productionToEBNF({ | ||
nonTerminal: production.include | ||
})} - ${productionToEBNF({ nonTerminal: production.exceptNonTerminal })}`; | ||
} | ||
if (production.exceptTerminal) { | ||
return `${productionToEBNF({ | ||
nonTerminal: production.include | ||
})} - ${productionToEBNF({ terminal: production.exceptTerminal })}`; | ||
} | ||
return "unknown construct"; | ||
}; | ||
const SHRINK_CHOICE = 10; | ||
@@ -109,6 +43,5 @@ | ||
if (production.nonTerminal) { | ||
return NonTerminal( | ||
production.nonTerminal, | ||
`#${dasherize(production.nonTerminal)}` | ||
); | ||
return NonTerminal(production.nonTerminal, { | ||
href: `#${dasherize(production.nonTerminal)}` | ||
}); | ||
} | ||
@@ -119,3 +52,3 @@ if (production.skip) { | ||
if (production.specialSequence) { | ||
const sequence = NonTerminal(" " + production.specialSequence + " "); | ||
const sequence = NonTerminal(" " + production.specialSequence + " ", {}); | ||
sequence.attrs.class = "special-sequence"; | ||
@@ -158,3 +91,3 @@ return sequence; | ||
productionToDiagram(production.repetition), | ||
Comment(`${production.amount} ×`) | ||
Comment(`${production.amount} ×`, {}) | ||
); | ||
@@ -169,5 +102,5 @@ } | ||
productionToDiagram(production.group), | ||
Comment(production.comment) | ||
Comment(production.comment, {}) | ||
) | ||
: Comment(production.comment); | ||
: Comment(production.comment, {}); | ||
} | ||
@@ -179,7 +112,11 @@ if (production.group) { | ||
return NonTerminal( | ||
`${production.include} - ${production.exceptNonTerminal}` | ||
`${production.include} - ${production.exceptNonTerminal}`, | ||
{} | ||
); | ||
} | ||
if (production.exceptTerminal) { | ||
return NonTerminal(`${production.include} - ${production.exceptTerminal}`); | ||
return NonTerminal( | ||
`${production.include} - ${production.exceptTerminal}`, | ||
{} | ||
); | ||
} | ||
@@ -216,4 +153,9 @@ return "unknown construct"; | ||
); | ||
const renderProduction = | ||
options.optimizeDiagrams === false | ||
? production | ||
: optimizeProduction(production); | ||
const diagram = productionToDiagram({ | ||
...optimizeProduction(production), | ||
...renderProduction, | ||
complex: outgoingReferences.length > 0 | ||
@@ -223,3 +165,6 @@ }); | ||
identifier: production.identifier, | ||
ebnf: productionToEBNF(production), | ||
ebnf: productionToEBNF(production, { | ||
markup: true, | ||
format: options.textFormatting | ||
}), | ||
referencedBy: searchReferencesToIdentifier(production.identifier, ast), | ||
@@ -226,0 +171,0 @@ referencesTo: outgoingReferences, |
const { Converter } = require("showdown"); | ||
const { dedent } = require("./dedent"); | ||
const converter = new Converter({ | ||
simplifiedAutoLink: true | ||
simplifiedAutoLink: true, | ||
strikethrough: true, | ||
tasklists: true, | ||
tables: true | ||
}); | ||
@@ -73,4 +76,3 @@ | ||
code.ebnf { | ||
padding: 1em 1em 1em 3em; | ||
text-indent: -2em; | ||
padding: 1em 1em 1em 1em; | ||
background: rgb(255, 246, 209); | ||
@@ -82,2 +84,5 @@ font-weight: bold; | ||
} | ||
code.ebnf pre { | ||
margin: 0; | ||
} | ||
.ebnf-identifier { | ||
@@ -189,3 +194,3 @@ color: #990099; | ||
${diagram} </div> | ||
<code class="ebnf">${ebnf}</code>${(referencedBy.length > 0 | ||
<code class="ebnf"><pre>${ebnf}</pre></code>${(referencedBy.length > 0 | ||
? "\n " + referencesTemplate(identifier, referencedBy) | ||
@@ -192,0 +197,0 @@ : "") + |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
HTTP dependency
Supply chain riskContains a dependency which resolves to a remote HTTP URL which could be used to inject untrusted code and reduce overall package reliability.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
HTTP dependency
Supply chain riskContains a dependency which resolves to a remote HTTP URL which could be used to inject untrusted code and reduce overall package reliability.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
75629
16
1903
100