Skip to content
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 4 additions & 4 deletions bin/ebnf.mjs → bin/ebnf.js
Original file line number Diff line number Diff line change
@@ -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"],
Expand All @@ -26,14 +26,14 @@ if (file_path === "-") {

function exit_help(exit_code) {
console.log(`
Usage: node --experimental-modules ebnf.mjs [OPTIONS] <FILE>
Usage: node -r esm ebnf.js [OPTIONS] <FILE>

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:

Expand Down
8 changes: 4 additions & 4 deletions bin/parse.mjs → bin/parse.js
Original file line number Diff line number Diff line change
@@ -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"],
Expand All @@ -26,14 +26,14 @@ if (file_path === "-") {

function exit_help(exit_code) {
console.log(`
Usage: node --experimental-modules parse.mjs [OPTIONS] <FILE>
Usage: node -r esm parse.js [OPTIONS] <FILE>

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:

Expand Down
14 changes: 7 additions & 7 deletions lib/README.md
Original file line number Diff line number Diff line change
@@ -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`**.
6 changes: 3 additions & 3 deletions lib/combinators.mjs → lib/combinators.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down
11 changes: 5 additions & 6 deletions lib/ebnf.mjs → lib/ebnf.js
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -16,4 +16,3 @@ function ebnf(source, min_name_length = 0) {
.map(rule => serialize(rule, state))
.join("");
}

2 changes: 1 addition & 1 deletion lib/mappers.mjs → lib/mappers.js
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
4 changes: 2 additions & 2 deletions lib/parser.mjs → lib/parser.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
7 changes: 6 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -41,6 +41,7 @@
"json-diff": "^0.5.2"
},
"dependencies": {
"esm": "^3.2.25",
"minimist": "^1.2.0"
}
}
6 changes: 3 additions & 3 deletions spec/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion spec/fluent.ebnf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions spec/valid.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions syntax/abstract.mjs → syntax/abstract.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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.
Expand Down
File renamed without changes.
10 changes: 5 additions & 5 deletions syntax/grammar.mjs → syntax/grammar.js
Original file line number Diff line number Diff line change
@@ -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. */
Expand Down Expand Up @@ -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,
Expand Down
16 changes: 8 additions & 8 deletions test/bench.mjs → test/bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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 => {
Expand Down
6 changes: 3 additions & 3 deletions test/ebnf.mjs → test/ebnf.js
Original file line number Diff line number Diff line change
@@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 "$< → $@"
7 changes: 3 additions & 4 deletions test/literals.mjs → test/literals.js
Original file line number Diff line number Diff line change
@@ -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);
}

Expand Down Expand Up @@ -71,4 +71,3 @@ suite(tester => {
test(NumberLiteral.run("-01.0300"), {value: -1.03, precision: 4});
};
});

6 changes: 3 additions & 3 deletions test/parser.mjs → test/parser.js
Original file line number Diff line number Diff line change
@@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion test/suite.mjs → test/suite.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
File renamed without changes.