diff --git a/README.md b/README.md index 590d7e2..15d3380 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ validate your work: npm test # Test the parser against JSON AST fixtures. npm run lint # Lint the parser code. - npm run generate:ebnf # Generate the EBNF from syntax/grammar.mjs. + npm run generate:ebnf # Generate the EBNF from syntax/grammar.js. npm run generate:fixtures # Generate test fixtures (FTL → JSON AST). npm run build:guide # Build the HTML version of the Guide. diff --git a/bin/ebnf.mjs b/bin/ebnf.js similarity index 83% rename from bin/ebnf.mjs rename to bin/ebnf.js index b755b70..34ef6ae 100644 --- a/bin/ebnf.mjs +++ b/bin/ebnf.js @@ -1,7 +1,7 @@ import fs from "fs"; import readline from "readline"; import parse_args from "minimist"; -import ebnf from "../lib/ebnf.mjs"; +import ebnf from "../lib/ebnf.js"; const argv = parse_args(process.argv.slice(2), { boolean: ["help"], @@ -26,14 +26,14 @@ if (file_path === "-") { function exit_help(exit_code) { console.log(` - Usage: node --experimental-modules ebnf.mjs [OPTIONS] + Usage: node -r esm ebnf.js [OPTIONS] When FILE is "-", read text from stdin. Examples: - node --experimental-modules ebnf.mjs path/to/grammar.mjs - cat path/to/grammar.mjs | node --experimental-modules ebnf.mjs - + node -r esm ebnf.js path/to/grammar.js + cat path/to/grammar.js | node -r esm ebnf.js - Options: diff --git a/bin/parse.mjs b/bin/parse.js similarity index 83% rename from bin/parse.mjs rename to bin/parse.js index e71b3a1..ba5ee1f 100644 --- a/bin/parse.mjs +++ b/bin/parse.js @@ -1,7 +1,7 @@ import fs from "fs"; import readline from "readline"; import parse_args from "minimist"; -import {Resource} from "../syntax/grammar.mjs"; +import {Resource} from "../syntax/grammar.js"; const argv = parse_args(process.argv.slice(2), { boolean: ["help"], @@ -26,14 +26,14 @@ if (file_path === "-") { function exit_help(exit_code) { console.log(` - Usage: node --experimental-modules parse.mjs [OPTIONS] + Usage: node -r esm parse.js [OPTIONS] When FILE is "-", read text from stdin. Examples: - node --experimental-modules parse.mjs path/to/file.ftl - cat path/to/file.ftl | node --experimental-modules parse.mjs - + node -r esm parse.js path/to/file.ftl + cat path/to/file.ftl | node -r esm parse.js - Options: diff --git a/lib/README.md b/lib/README.md index 0872778..0ce9faf 100644 --- a/lib/README.md +++ b/lib/README.md @@ -1,19 +1,19 @@ # Fluent Specification Support Code -This directory contains support code for the parser-combinator underlying the syntax code, as well as the code to transform `grammar.mjs` to `fluent.ebnf`. The combination allows for a succinct formulation of the syntax in `grammar.mjs` and a readable representation of that in `fluent.ebnf`. +This directory contains support code for the parser-combinator underlying the syntax code, as well as the code to transform `grammar.js` to `fluent.ebnf`. The combination allows for a succinct formulation of the syntax in `grammar.js` and a readable representation of that in `fluent.ebnf`. ## Parser-Combinator -**`parser.mjs`** is the base parser class, **`stream.mjs`** holds a iterator over the strings to be parsed. +**`parser.js`** is the base parser class, **`stream.js`** holds a iterator over the strings to be parsed. -**`combinators.mjs`** holds the actual basic grammar productions and logical combinations of grammar productions. +**`combinators.js`** holds the actual basic grammar productions and logical combinations of grammar productions. -Both use `Success` and `Failure` from **`result.mjs`** to pass success and failure conditions forward. +Both use `Success` and `Failure` from **`result.js`** to pass success and failure conditions forward. -After the definition of grammar productions, the utilities in **`mappers.mjs`** are used to concat, flatten, and extract the matched data to prepare it for AST generation. +After the definition of grammar productions, the utilities in **`mappers.js`** are used to concat, flatten, and extract the matched data to prepare it for AST generation. ## EBNF Generation -The `fluent.ebnf` is created by parsing the `grammar.mjs` and transpilation to an EBNF file. The `babylon` JavaScript parser is used to load a Babel AST. +The `fluent.ebnf` is created by parsing the `grammar.js` and transpilation to an EBNF file. The `babylon` JavaScript parser is used to load a Babel AST. -**`ebnf.mjs`** is the top-level entry point used by `bin/ebnf.mjs`. It uses the iteration logic from **`walker.mjs`** to go over the Babel AST and to extract the information relevant to the EBNF via the Visitor in **`visitor.mjs`**. The resulting data is then serialiazed to EBNF via **`serializer.mjs`**. +**`ebnf.js`** is the top-level entry point used by `bin/ebnf.js`. It uses the iteration logic from **`walker.js`** to go over the Babel AST and to extract the information relevant to the EBNF via the Visitor in **`visitor.js`**. The resulting data is then serialiazed to EBNF via **`serializer.js`**. diff --git a/lib/combinators.mjs b/lib/combinators.js similarity index 96% rename from lib/combinators.mjs rename to lib/combinators.js index c0da4b6..5f00fb9 100644 --- a/lib/combinators.mjs +++ b/lib/combinators.js @@ -1,6 +1,6 @@ -import Parser from "./parser.mjs"; -import {Success, Failure} from "./result.mjs"; -import {join} from "./mappers.mjs"; +import Parser from "./parser.js"; +import {Success, Failure} from "./result.js"; +import {join} from "./mappers.js"; export function defer(fn) { // Parsers may be defined as defer(() => parser) to avoid cyclic diff --git a/lib/ebnf.mjs b/lib/ebnf.js similarity index 60% rename from lib/ebnf.mjs rename to lib/ebnf.js index 9cbae73..5b92a4e 100644 --- a/lib/ebnf.mjs +++ b/lib/ebnf.js @@ -1,11 +1,11 @@ -import babylon from "babylon"; -import walk from "./walker.mjs"; -import visitor from "./visitor.mjs"; -import serialize from "./serializer.mjs"; +import {parse} from "babylon"; +import walk from "./walker.js"; +import visitor from "./visitor.js"; +import serialize from "./serializer.js"; export default function ebnf(source, min_name_length = 0) { - let grammar_ast = babylon.parse(source, {sourceType: "module"}); + let grammar_ast = parse(source, {sourceType: "module"}); let rules = walk(grammar_ast, visitor); let state = { max_name_length: Math.max( @@ -16,4 +16,3 @@ function ebnf(source, min_name_length = 0) { .map(rule => serialize(rule, state)) .join(""); } - diff --git a/lib/mappers.mjs b/lib/mappers.js similarity index 96% rename from lib/mappers.mjs rename to lib/mappers.js index b9945d7..6c923fb 100644 --- a/lib/mappers.mjs +++ b/lib/mappers.js @@ -1,4 +1,4 @@ -import {Abstract} from "./parser.mjs"; +import {Abstract} from "./parser.js"; // Flatten a list up to a given depth. // This is useful when a parser uses nested sequences and repeats. diff --git a/lib/parser.mjs b/lib/parser.js similarity index 92% rename from lib/parser.mjs rename to lib/parser.js index fed57d8..73f89e3 100644 --- a/lib/parser.mjs +++ b/lib/parser.js @@ -1,5 +1,5 @@ -import Stream from "./stream.mjs"; -import {Failure} from "./result.mjs"; +import Stream from "./stream.js"; +import {Failure} from "./result.js"; export class Abstract { constructor(value) { diff --git a/lib/result.mjs b/lib/result.js similarity index 100% rename from lib/result.mjs rename to lib/result.js diff --git a/lib/serializer.mjs b/lib/serializer.js similarity index 100% rename from lib/serializer.mjs rename to lib/serializer.js diff --git a/lib/stream.mjs b/lib/stream.js similarity index 100% rename from lib/stream.mjs rename to lib/stream.js diff --git a/lib/visitor.mjs b/lib/visitor.js similarity index 100% rename from lib/visitor.mjs rename to lib/visitor.js diff --git a/lib/walker.mjs b/lib/walker.js similarity index 100% rename from lib/walker.mjs rename to lib/walker.js diff --git a/package-lock.json b/package-lock.json index bd66aba..9769d6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "fluent-spec", - "version": "0.5.0", + "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -504,6 +504,11 @@ "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", "dev": true }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, "espree": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", diff --git a/package.json b/package.json index da30f18..9a90806 100644 --- a/package.json +++ b/package.json @@ -4,19 +4,19 @@ "version": "1.0.0", "private": true, "scripts": { - "bench": "node --experimental-modules --harmony-async-iteration ./test/bench.mjs ./test/benchmarks/gecko_strings.ftl", + "bench": "node -r esm --harmony-async-iteration ./test/bench.js ./test/benchmarks/gecko_strings.ftl", "build:guide": "gitbook build guide build/guide", "build": "npm run --silent build:guide", "ci": "npm run --silent lint && npm test && npm run --silent test:ebnf", "clean": "rm -rf build", "deploy": "gh-pages -d build", - "generate:ebnf": "node --experimental-modules bin/ebnf.mjs ./syntax/grammar.mjs > ./spec/fluent.ebnf", + "generate:ebnf": "node -r esm bin/ebnf.js ./syntax/grammar.js > ./spec/fluent.ebnf", "generate:fixtures": "make -sC test/fixtures", "generate": "npm run --silent generate:ebnf && npm run --silent generate:fixtures", - "lint": "eslint **/*.mjs", - "test:ebnf": "node --experimental-modules test/ebnf.mjs ./syntax/grammar.mjs ./spec/fluent.ebnf", - "test:fixtures": "node --experimental-modules test/parser.mjs ./test/fixtures", - "test:unit": "node --experimental-modules test/literals.mjs", + "lint": "eslint **/*.js", + "test:ebnf": "node -r esm test/ebnf.js ./syntax/grammar.js ./spec/fluent.ebnf", + "test:fixtures": "node -r esm test/parser.js ./test/fixtures", + "test:unit": "node -r esm test/literals.js", "test": "npm run --silent test:fixtures && npm run --silent test:unit" }, "homepage": "https://projectfluent.org", @@ -41,6 +41,7 @@ "json-diff": "^0.5.2" }, "dependencies": { + "esm": "^3.2.25", "minimist": "^1.2.0" } } diff --git a/spec/CHANGELOG.md b/spec/CHANGELOG.md index b4458a9..9e7d4d4 100644 --- a/spec/CHANGELOG.md +++ b/spec/CHANGELOG.md @@ -233,7 +233,7 @@ compatibility strategy for future releases. The `Function` production and its corresponding AST node have been removed. The logic validating that function names are all upper-case has - been moved to `abstract.mjs`. + been moved to `abstract.js`. ## 0.7.0 (October 15, 2018) @@ -282,11 +282,11 @@ compatibility strategy for future releases. correctness at a cost of reduced performance. The ASDL description of the AST has been removed in favor of - `syntax/ast.mjs` which defines the actual AST nodes returned by the + `syntax/ast.js` which defines the actual AST nodes returned by the reference parser. The EBNF is now auto-generated from the reference parser's - `syntax/grammar.mjs` file. It provides an easy to read overview of the + `syntax/grammar.js` file. It provides an easy to read overview of the grammar and will continue to be updated in the future. Going forward, all changes to the grammar will be implemented in the diff --git a/spec/fluent.ebnf b/spec/fluent.ebnf index abc12b1..ec29c8c 100644 --- a/spec/fluent.ebnf +++ b/spec/fluent.ebnf @@ -49,7 +49,7 @@ block_placeable ::= blank_block blank_inline? inline_placeable /* Rules for validating expressions in Placeables and as selectors of * SelectExpressions are documented in spec/valid.md and enforced in - * syntax/abstract.mjs. */ + * syntax/abstract.js. */ InlineExpression ::= StringLiteral | NumberLiteral | FunctionReference diff --git a/spec/valid.md b/spec/valid.md index f374dbd..c21aff9 100644 --- a/spec/valid.md +++ b/spec/valid.md @@ -4,12 +4,12 @@ Fluent Syntax distinguishes between well-formed and valid resources. - Well-formed Fluent resources conform to the Fluent grammar described by the Fluent EBNF (`spec/fluent.ebnf`). The EBNF is auto-generated from - `syntax/grammar.mjs`. + `syntax/grammar.js`. - Valid Fluent resources must be well-formed and are additionally checked for semantic correctness. The validation process may reject syntax which is well-formed. The validation rules are expressed in code in - `syntax/abstract.mjs`. + `syntax/abstract.js`. For example, the `message.attr(param: "value")` syntax is _well-formed_. `message.attr` is an `AttributeExpression` which may be the callee of a diff --git a/syntax/abstract.mjs b/syntax/abstract.js similarity index 96% rename from syntax/abstract.mjs rename to syntax/abstract.js index 708803e..1ca1de3 100644 --- a/syntax/abstract.mjs +++ b/syntax/abstract.js @@ -1,12 +1,12 @@ /* * AST Validation * - * The parse result of the grammar.mjs parser is a well-formed AST which is + * The parse result of the grammar.js parser is a well-formed AST which is * validated according to the rules documented in `spec/valid.md`. */ -import * as FTL from "./ast.mjs"; -import {always, never} from "../lib/combinators.mjs"; +import * as FTL from "./ast.js"; +import {always, never} from "../lib/combinators.js"; export function list_into(Type) { switch (Type) { @@ -155,7 +155,7 @@ function attach_comments(acc, cur) { } // Remove the largest common indentation from a list of elements of a Pattern. -// The indents are parsed in grammar.mjs and passed to abstract.mjs as string +// The indents are parsed in grammar.js and passed to abstract.js as string // primitives along with other PatternElements. function dedent(elements) { // Calculate the maximum common indent. diff --git a/syntax/ast.mjs b/syntax/ast.js similarity index 100% rename from syntax/ast.mjs rename to syntax/ast.js diff --git a/syntax/grammar.mjs b/syntax/grammar.js similarity index 98% rename from syntax/grammar.mjs rename to syntax/grammar.js index 4836eb8..6f94bda 100644 --- a/syntax/grammar.mjs +++ b/syntax/grammar.js @@ -1,12 +1,12 @@ -import * as FTL from "./ast.mjs"; -import {list_into, into} from "./abstract.mjs"; +import * as FTL from "./ast.js"; +import {list_into, into} from "./abstract.js"; import { always, and, charset, defer, either, eof, maybe, not, regex, repeat, repeat1, sequence, string -} from "../lib/combinators.mjs"; +} from "../lib/combinators.js"; import { element_at, flatten, join, keep_abstract, mutate, print, prune -} from "../lib/mappers.mjs"; +} from "../lib/mappers.js"; /* ----------------------------------------------------- */ /* An FTL file defines a Resource consisting of Entries. */ @@ -191,7 +191,7 @@ let block_placeable = defer(() => /* ------------------------------------------------------------------- */ /* Rules for validating expressions in Placeables and as selectors of * SelectExpressions are documented in spec/valid.md and enforced in - * syntax/abstract.mjs. */ + * syntax/abstract.js. */ let InlineExpression = defer(() => either( StringLiteral, diff --git a/test/bench.mjs b/test/bench.js similarity index 81% rename from test/bench.mjs rename to test/bench.js index b8c1da0..0a411c1 100644 --- a/test/bench.mjs +++ b/test/bench.js @@ -2,17 +2,17 @@ import fs from "fs"; import perf from "perf_hooks"; const {PerformanceObserver, performance} = perf; -import FluentSyntax from "fluent-syntax"; -import FluentRuntime from "fluent"; -import {Resource} from "../syntax/grammar.mjs"; -import {readfile} from "./util.mjs"; +import {parse} from "fluent-syntax"; +import {_parse} from "fluent"; +import {Resource} from "../syntax/grammar.js"; +import {readfile} from "./util.js"; let args = process.argv.slice(2); if (args.length < 1 || 2 < args.length) { console.error( - "Usage: node --experimental-modules --harmony-async-iteration " + - "bench.mjs FTL_FILE [SAMPLE SIZE = 30]"); + "Usage: node -r esm --harmony-async-iteration " + + "bench.js FTL_FILE [SAMPLE SIZE = 30]"); process.exit(1); } @@ -31,8 +31,8 @@ async function main(ftl_file, sample_size = 30) { let subjects = new Map([ ["Reference", new Subject("Reference", () => Resource.run(ftl))], - ["Tooling", new Subject("Tooling", () => FluentSyntax.parse(ftl))], - ["Runtime", new Subject("Runtime", () => FluentRuntime._parse(ftl))], + ["Tooling", new Subject("Tooling", () => parse(ftl))], + ["Runtime", new Subject("Runtime", () => _parse(ftl))], ]); new PerformanceObserver(items => { diff --git a/test/ebnf.mjs b/test/ebnf.js similarity index 89% rename from test/ebnf.mjs rename to test/ebnf.js index 19a1a60..2fcbd11 100644 --- a/test/ebnf.mjs +++ b/test/ebnf.js @@ -1,13 +1,13 @@ import color from "cli-color"; import difflib from "difflib"; -import ebnf from "../lib/ebnf.mjs"; -import {readfile, PASS, FAIL} from "./util.mjs"; +import ebnf from "../lib/ebnf.js"; +import {readfile, PASS, FAIL} from "./util.js"; let args = process.argv.slice(2); if (args.length !== 2) { console.error( - "Usage: node --experimental-modules ebnf.mjs " + + "Usage: node -r esm ebnf.js " + "GRAMMAR_FILE EXPECTED_EBNF"); process.exit(1); } diff --git a/test/fixtures/Makefile b/test/fixtures/Makefile index 49c98e7..3caf947 100644 --- a/test/fixtures/Makefile +++ b/test/fixtures/Makefile @@ -5,7 +5,7 @@ all: $(AST_FIXTURES) .PHONY: $(AST_FIXTURES) $(AST_FIXTURES): %.json: %.ftl - @node --experimental-modules ../../bin/parse.mjs $< \ + @node -r esm ../../bin/parse.js $< \ 2> /dev/null \ 1> $@; @echo "$< → $@" diff --git a/test/literals.mjs b/test/literals.js similarity index 94% rename from test/literals.mjs rename to test/literals.js index be8a580..7b0fd69 100644 --- a/test/literals.mjs +++ b/test/literals.js @@ -1,9 +1,9 @@ /* eslint quotes: "off" */ -import suite from "./suite.mjs"; -import {StringLiteral, NumberLiteral} from "../syntax/grammar.mjs"; +import suite from "./suite.js"; +import {StringLiteral, NumberLiteral} from "../syntax/grammar.js"; if (process.argv.length > 2) { - console.error("Usage: node --experimental-modules literals.mjs"); + console.error("Usage: node -r esm literals.js"); process.exit(1); } @@ -71,4 +71,3 @@ suite(tester => { test(NumberLiteral.run("-01.0300"), {value: -1.03, precision: 4}); }; }); - diff --git a/test/parser.mjs b/test/parser.js similarity index 94% rename from test/parser.mjs rename to test/parser.js index f71e7e2..e1e0bc1 100644 --- a/test/parser.mjs +++ b/test/parser.js @@ -1,13 +1,13 @@ import assert from "assert"; import path from "path"; -import {Resource} from "../syntax/grammar.mjs"; -import {readdir, readfile, diff, PASS, FAIL} from "./util.mjs"; +import {Resource} from "../syntax/grammar.js"; +import {readdir, readfile, diff, PASS, FAIL} from "./util.js"; const fixtures_dir = process.argv[2]; if (!fixtures_dir) { console.error( - "Usage: node --experimental-modules parser.mjs FIXTURE"); + "Usage: node -r esm parser.js FIXTURE"); process.exit(1); } diff --git a/test/suite.mjs b/test/suite.js similarity index 96% rename from test/suite.mjs rename to test/suite.js index 94de928..6743162 100644 --- a/test/suite.mjs +++ b/test/suite.js @@ -1,6 +1,6 @@ import assert from "assert"; import color from "cli-color"; -import {diff, PASS, FAIL} from "./util.mjs"; +import {diff, PASS, FAIL} from "./util.js"; export default function suite(fn) { diff --git a/test/util.mjs b/test/util.js similarity index 100% rename from test/util.mjs rename to test/util.js