Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

fluent-syntax

Package Overview
Dependencies
Maintainers
3
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fluent-syntax - npm Package Compare versions

Comparing version 0.11.0 to 0.12.0

48

CHANGELOG.md
# Changelog
## fluent-syntax 0.12.0 (March 26, 2019)
This release of `fluent-syntax` brings support for version 0.9 of the Fluent
Syntax spec. The API remains unchanged. Files written in valid Syntax 0.8 may
parse differently in this release. See the compatibility note below. Consult
the full Syntax 0.9 [changelog][chlog0.9] for details.
[chlog0.9]: https://github.com/projectfluent/fluent/releases/tag/v0.9.0
- Flatten complex reference expressions.
Reference expressions which may take complex forms, such as a reference
to a message's attribute, or a parameterized reference to an attribute of
a term, are now stored in a simplified manner. Instead of nesting
multiple expression nodes (e.g. `CallExpression` of an
`AttributeExpression` of a `TermReference`), all information is available
directly in the reference expression.
This change affects the following AST nodes:
- `MessageReference` now has an optional `attribute` field,
- `FunctionReference` now has a required `arguments` field,
- `TermReference` now has an optional `attribute` field and an optional
`arguments` field.
- Remove `VariantLists`.
The `VariantLists` and the `VariantExpression` syntax and AST nodes were
deprecated in Syntax 0.9 and have now been removed.
- Rename `StringLiteral.raw` to `value`.
`StringLiteral.value` contains the exact contents of the string literal,
character-for-character. Escape sequences are stored verbatim without
processing. A new method, `Literal.parse`, can be used to process the raw
value of the literal into an unescaped form.
- Rename `args` to `arguments`.
The `args` field of `MessageReference`, `TermReference`,
`FunctionReference`, and `Annotation` has been renamed to `arguments`.
### Backward-incompatible changes:
- `VariantLists` are no longer valid syntax. A syntax error is reported
when a `VariantList` or a `VariantExpression` is found in the parsed file.
## fluent-syntax 0.11.0 (March 25, 2019)

@@ -4,0 +52,0 @@

576

compat.js

@@ -1,2 +0,2 @@

/* fluent-syntax@0.11.0 */
/* fluent-syntax@0.12.0 */
(function (global, factory) {

@@ -8,40 +8,2 @@ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :

function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArrayLimit(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
/*

@@ -231,10 +193,2 @@ * Base class for all Fluent AST nodes.

}
class VariantList extends SyntaxNode {
constructor(variants) {
super();
this.type = "VariantList";
this.variants = variants;
}
}
class Pattern extends SyntaxNode {

@@ -273,25 +227,83 @@ constructor(elements) {

class Expression extends SyntaxNode {}
class StringLiteral extends Expression {
constructor(raw, value) {
super();
this.type = "StringLiteral";
this.raw = raw;
class Expression extends SyntaxNode {} // An abstract base class for Literals.
class Literal extends Expression {
constructor(value) {
super(); // The "value" field contains the exact contents of the literal,
// character-for-character.
this.value = value;
}
parse() {
return {
value: this.value
};
}
}
class NumberLiteral extends Expression {
class StringLiteral extends Literal {
constructor(value) {
super();
super(value);
this.type = "StringLiteral";
}
parse() {
// Backslash backslash, backslash double quote, uHHHH, UHHHHHH.
const KNOWN_ESCAPES = /(?:\\\\|\\"|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;
function from_escape_sequence(match, codepoint4, codepoint6) {
switch (match) {
case "\\\\":
return "\\";
case "\\\"":
return "\"";
default:
let codepoint = parseInt(codepoint4 || codepoint6, 16);
if (codepoint <= 0xD7FF || 0xE000 <= codepoint) {
// It's a Unicode scalar value.
return String.fromCodePoint(codepoint);
} // Escape sequences reresenting surrogate code points are
// well-formed but invalid in Fluent. Replace them with U+FFFD
// REPLACEMENT CHARACTER.
return "�";
}
}
let value = this.value.replace(KNOWN_ESCAPES, from_escape_sequence);
return {
value
};
}
}
class NumberLiteral extends Literal {
constructor(value) {
super(value);
this.type = "NumberLiteral";
this.value = value;
}
parse() {
let value = parseFloat(this.value);
let decimal_position = this.value.indexOf(".");
let precision = decimal_position > 0 ? this.value.length - decimal_position - 1 : 0;
return {
value,
precision
};
}
}
class MessageReference extends Expression {
constructor(id) {
let attribute = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
super();
this.type = "MessageReference";
this.id = id;
this.attribute = attribute;
}

@@ -302,5 +314,9 @@

constructor(id) {
let attribute = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
let args = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
super();
this.type = "TermReference";
this.id = id;
this.attribute = attribute;
this.arguments = args;
}

@@ -318,6 +334,7 @@

class FunctionReference extends Expression {
constructor(id) {
constructor(id, args) {
super();
this.type = "FunctionReference";
this.id = id;
this.arguments = args;
}

@@ -335,27 +352,8 @@

}
class AttributeExpression extends Expression {
constructor(ref, name) {
class CallArguments extends SyntaxNode {
constructor() {
let positional = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
let named = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
super();
this.type = "AttributeExpression";
this.ref = ref;
this.name = name;
}
}
class VariantExpression extends Expression {
constructor(ref, key) {
super();
this.type = "VariantExpression";
this.ref = ref;
this.key = key;
}
}
class CallExpression extends Expression {
constructor(callee) {
let positional = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
let named = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
super();
this.type = "CallExpression";
this.callee = callee;
this.type = "CallArguments";
this.positional = positional;

@@ -461,3 +459,3 @@ this.named = named;

this.code = code;
this.args = args;
this.arguments = args;
this.message = message;

@@ -468,2 +466,40 @@ }

function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArrayLimit(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
class ParseError extends Error {

@@ -532,3 +568,3 @@ constructor(code) {

case "E0009":
return "The key has to be a simple identifier";
return "The argument name has to be a simple identifier";

@@ -959,2 +995,3 @@ case "E0010":

/* eslint no-magic-numbers: [0] */
const trailingWSRe = /[ \t\n\r]+$/;

@@ -994,3 +1031,3 @@

const methodNames = ["getComment", "getMessage", "getTerm", "getAttribute", "getIdentifier", "getVariant", "getNumber", "getPattern", "getVariantList", "getTextElement", "getPlaceable", "getExpression", "getInlineExpression", "getCallArgument", "getString", "getSimpleExpression", "getLiteral"];
const methodNames = ["getComment", "getMessage", "getTerm", "getAttribute", "getIdentifier", "getVariant", "getNumber", "getPattern", "getTextElement", "getPlaceable", "getExpression", "getInlineExpression", "getCallArgument", "getCallArguments", "getString", "getLiteral"];

@@ -1204,7 +1241,5 @@ for (var _i = 0; _i < methodNames.length; _i++) {

ps.skipBlankInline();
ps.expectChar("="); // Syntax 0.8 compat: VariantLists are supported but deprecated. They can
// only be found as values of Terms. Nested VariantLists are not allowed.
ps.expectChar("=");
const value = this.maybeGetPattern(ps);
const value = this.maybeGetVariantList(ps) || this.maybeGetPattern(ps);
if (value === null) {

@@ -1347,18 +1382,17 @@ throw new ParseError("E0006", id.name);

getNumber(ps) {
let num = "";
let value = "";
if (ps.currentChar === "-") {
num += "-";
ps.next();
value += `-${this.getDigits(ps)}`;
} else {
value += this.getDigits(ps);
}
num = `${num}${this.getDigits(ps)}`;
if (ps.currentChar === ".") {
num += ".";
ps.next();
num = `${num}${this.getDigits(ps)}`;
value += `.${this.getDigits(ps)}`;
}
return new NumberLiteral(num);
return new NumberLiteral(value);
} // maybeGetPattern distinguishes between patterns which start on the same line

@@ -1392,37 +1426,4 @@ // as the identifier (a.k.a. inline signleline patterns and inline multiline

return null;
} // Deprecated in Syntax 0.8. VariantLists are only allowed as values of Terms.
// Values of Messages, Attributes and Variants must be Patterns. This method
// is only used in getTerm.
maybeGetVariantList(ps) {
ps.peekBlank();
if (ps.currentPeek === "{") {
const start = ps.peekOffset;
ps.peek();
ps.peekBlankInline();
if (ps.currentPeek === EOL) {
ps.peekBlank();
if (ps.isVariantStart()) {
ps.resetPeek(start);
ps.skipToPeek();
return this.getVariantList(ps);
}
}
}
ps.resetPeek();
return null;
}
getVariantList(ps) {
ps.expectChar("{");
var variants = this.getVariants(ps);
ps.expectChar("}");
return new VariantList(variants);
}
getPattern(ps, _ref3) {

@@ -1607,3 +1608,3 @@ let isBlock = _ref3.isBlock;

ps.next();
return [`\\${next}`, next];
return `\\${next}`;

@@ -1635,9 +1636,3 @@ case "u":

const codepoint = parseInt(sequence, 16);
const unescaped = codepoint <= 0xD7FF || 0xE000 <= codepoint // It's a Unicode scalar value.
? String.fromCodePoint(codepoint) // Escape sequences reresenting surrogate code points are well-formed
// but invalid in Fluent. Replace them with U+FFFD REPLACEMENT
// CHARACTER.
: "�";
return [`\\${u}${sequence}`, unescaped];
return `\\${u}${sequence}`;
}

@@ -1664,17 +1659,13 @@

if (selector.type === "MessageReference") {
throw new ParseError("E0016");
if (selector.attribute === null) {
throw new ParseError("E0016");
} else {
throw new ParseError("E0018");
}
}
if (selector.type === "AttributeExpression" && selector.ref.type === "MessageReference") {
throw new ParseError("E0018");
}
if (selector.type === "TermReference" || selector.type === "VariantExpression") {
if (selector.type === "TermReference" && selector.attribute === null) {
throw new ParseError("E0017");
}
if (selector.type === "CallExpression" && selector.callee.type === "TermReference") {
throw new ParseError("E0017");
}
ps.next();

@@ -1688,10 +1679,6 @@ ps.next();

if (selector.type === "AttributeExpression" && selector.ref.type === "TermReference") {
if (selector.type === "TermReference" && selector.attribute !== null) {
throw new ParseError("E0019");
}
if (selector.type === "CallExpression" && selector.callee.type === "AttributeExpression") {
throw new ParseError("E0019");
}
return selector;

@@ -1705,64 +1692,2 @@ }

let expr = this.getSimpleExpression(ps);
switch (expr.type) {
case "NumberLiteral":
case "StringLiteral":
case "VariableReference":
return expr;
case "MessageReference":
{
if (ps.currentChar === ".") {
ps.next();
const attr = this.getIdentifier(ps);
return new AttributeExpression(expr, attr);
}
if (ps.currentChar === "(") {
// It's a Function. Ensure it's all upper-case.
if (!/^[A-Z][A-Z_?-]*$/.test(expr.id.name)) {
throw new ParseError("E0008");
}
const func = new FunctionReference(expr.id);
if (this.withSpans) {
func.addSpan(expr.span.start, expr.span.end);
}
return new CallExpression(func, ...this.getCallArguments(ps));
}
return expr;
}
case "TermReference":
{
if (ps.currentChar === "[") {
ps.next();
const key = this.getVariantKey(ps);
ps.expectChar("]");
return new VariantExpression(expr, key);
}
if (ps.currentChar === ".") {
ps.next();
const attr = this.getIdentifier(ps);
expr = new AttributeExpression(expr, attr);
}
if (ps.currentChar === "(") {
return new CallExpression(expr, ...this.getCallArguments(ps));
}
return expr;
}
default:
throw new ParseError("E0028");
}
}
getSimpleExpression(ps) {
if (ps.isNumberStart()) {

@@ -1785,3 +1710,16 @@ return this.getNumber(ps);

const id = this.getIdentifier(ps);
return new TermReference(id);
let attr;
if (ps.currentChar === ".") {
ps.next();
attr = this.getIdentifier(ps);
}
let args;
if (ps.currentChar === "(") {
args = this.getCallArguments(ps);
}
return new TermReference(id, attr, args);
}

@@ -1791,3 +1729,21 @@

const id = this.getIdentifier(ps);
return new MessageReference(id);
if (ps.currentChar === "(") {
// It's a Function. Ensure it's all upper-case.
if (!/^[A-Z][A-Z0-9_-]*$/.test(id.name)) {
throw new ParseError("E0008");
}
let args = this.getCallArguments(ps);
return new FunctionReference(id, args);
}
let attr;
if (ps.currentChar === ".") {
ps.next();
attr = this.getIdentifier(ps);
}
return new MessageReference(id, attr);
}

@@ -1806,10 +1762,10 @@

if (exp.type !== "MessageReference") {
throw new ParseError("E0009");
if (exp.type === "MessageReference" && exp.attribute === null) {
ps.next();
ps.skipBlank();
const value = this.getLiteral(ps);
return new NamedArgument(exp.id, value);
}
ps.next();
ps.skipBlank();
const value = this.getLiteral(ps);
return new NamedArgument(exp.id, value);
throw new ParseError("E0009");
}

@@ -1856,9 +1812,8 @@

ps.expectChar(")");
return [positional, named];
return new CallArguments(positional, named);
}
getString(ps) {
let raw = "";
ps.expectChar("\"");
let value = "";
ps.expectChar("\"");
let ch;

@@ -1868,11 +1823,4 @@

if (ch === "\\") {
const _this$getEscapeSequen = this.getEscapeSequence(ps),
_this$getEscapeSequen2 = _slicedToArray(_this$getEscapeSequen, 2),
sequence = _this$getEscapeSequen2[0],
unescaped = _this$getEscapeSequen2[1];
raw += sequence;
value += unescaped;
value += this.getEscapeSequence(ps);
} else {
raw += ch;
value += ch;

@@ -1887,3 +1835,3 @@ }

ps.expectChar("\"");
return new StringLiteral(raw, value);
return new StringLiteral(value);
}

@@ -2030,3 +1978,3 @@

if (message.value) {
parts.push(serializeValue(message.value));
parts.push(serializePattern(message.value));
}

@@ -2070,3 +2018,3 @@

parts.push(`-${term.id.name} =`);
parts.push(serializeValue(term.value));
parts.push(serializePattern(term.value));
var _iteratorNormalCompletion3 = true;

@@ -2101,19 +2049,6 @@ var _didIteratorError3 = false;

function serializeAttribute(attribute) {
const value = indent(serializeValue(attribute.value));
const value = indent(serializePattern(attribute.value));
return `\n .${attribute.id.name} =${value}`;
}
function serializeValue(value) {
switch (value.type) {
case "Pattern":
return serializePattern(value);
case "VariantList":
return serializeVariantList(value);
default:
throw new Error(`Unknown value type: ${value.type}`);
}
}
function serializePattern(pattern) {

@@ -2130,18 +2065,2 @@ const content = pattern.elements.map(serializeElement).join("");

function serializeVariantList(varlist) {
const content = varlist.variants.map(serializeVariant).join("");
return `\n {${indent(content)}\n }`;
}
function serializeVariant(variant) {
const key = serializeVariantKey(variant.key);
const value = indent(serializeValue(variant.value));
if (variant.default) {
return `\n *[${key}]${value}`;
}
return `\n [${key}]${value}`;
}
function serializeElement(element) {

@@ -2170,3 +2089,3 @@ switch (element.type) {

// opening and the closing brace.
return `{ ${serializeSelectExpression(expr)}}`;
return `{ ${serializeExpression(expr)}}`;

@@ -2181,3 +2100,3 @@ default:

case "StringLiteral":
return `"${expr.raw}"`;
return `"${expr.value}"`;

@@ -2187,24 +2106,64 @@ case "NumberLiteral":

case "MessageReference":
case "FunctionReference":
return expr.id.name;
case "VariableReference":
return `$${expr.id.name}`;
case "TermReference":
return `-${expr.id.name}`;
{
let out = `-${expr.id.name}`;
case "VariableReference":
return `$${expr.id.name}`;
if (expr.attribute) {
out += `.${expr.attribute.name}`;
}
case "AttributeExpression":
return serializeAttributeExpression(expr);
if (expr.arguments) {
out += serializeCallArguments(expr.arguments);
}
case "VariantExpression":
return serializeVariantExpression(expr);
return out;
}
case "CallExpression":
return serializeCallExpression(expr);
case "MessageReference":
{
let out = expr.id.name;
if (expr.attribute) {
out += `.${expr.attribute.name}`;
}
return out;
}
case "FunctionReference":
return `${expr.id.name}${serializeCallArguments(expr.arguments)}`;
case "SelectExpression":
return serializeSelectExpression(expr);
{
let out = `${serializeExpression(expr.selector)} ->`;
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = expr.variants[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
let variant = _step4.value;
out += serializeVariant(variant);
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
return `${out}\n`;
}
case "Placeable":

@@ -2218,47 +2177,14 @@ return serializePlaceable(expr);

function serializeSelectExpression(expr) {
const parts = [];
const selector = `${serializeExpression(expr.selector)} ->`;
parts.push(selector);
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
function serializeVariant(variant) {
const key = serializeVariantKey(variant.key);
const value = indent(serializePattern(variant.value));
try {
for (var _iterator4 = expr.variants[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
const variant = _step4.value;
parts.push(serializeVariant(variant));
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
if (variant.default) {
return `\n *[${key}]${value}`;
}
parts.push("\n");
return parts.join("");
return `\n [${key}]${value}`;
}
function serializeAttributeExpression(expr) {
const ref = serializeExpression(expr.ref);
return `${ref}.${expr.name.name}`;
}
function serializeVariantExpression(expr) {
const ref = serializeExpression(expr.ref);
const key = serializeVariantKey(expr.key);
return `${ref}[${key}]`;
}
function serializeCallExpression(expr) {
const callee = serializeExpression(expr.callee);
function serializeCallArguments(expr) {
const positional = expr.positional.map(serializeExpression).join(", ");

@@ -2268,6 +2194,6 @@ const named = expr.named.map(serializeNamedArgument).join(", ");

if (expr.positional.length > 0 && expr.named.length > 0) {
return `${callee}(${positional}, ${named})`;
return `(${positional}, ${named})`;
}
return `${callee}(${positional || named})`;
return `(${positional || named})`;
}

@@ -2400,3 +2326,2 @@

exports.Term = Term;
exports.VariantList = VariantList;
exports.Pattern = Pattern;

@@ -2407,2 +2332,3 @@ exports.PatternElement = PatternElement;

exports.Expression = Expression;
exports.Literal = Literal;
exports.StringLiteral = StringLiteral;

@@ -2415,5 +2341,3 @@ exports.NumberLiteral = NumberLiteral;

exports.SelectExpression = SelectExpression;
exports.AttributeExpression = AttributeExpression;
exports.VariantExpression = VariantExpression;
exports.CallExpression = CallExpression;
exports.CallArguments = CallArguments;
exports.Attribute = Attribute;

@@ -2420,0 +2344,0 @@ exports.Variant = Variant;

@@ -1,2 +0,2 @@

/* fluent-syntax@0.11.0 */
/* fluent-syntax@0.12.0 */
(function (global, factory) {

@@ -124,10 +124,2 @@ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :

class VariantList extends SyntaxNode {
constructor(variants) {
super();
this.type = "VariantList";
this.variants = variants;
}
}
class Pattern extends SyntaxNode {

@@ -167,24 +159,73 @@ constructor(elements) {

class StringLiteral extends Expression {
constructor(raw, value) {
// An abstract base class for Literals.
class Literal extends Expression {
constructor(value) {
super();
this.type = "StringLiteral";
this.raw = raw;
// The "value" field contains the exact contents of the literal,
// character-for-character.
this.value = value;
}
parse() {
return {value: this.value};
}
}
class NumberLiteral extends Expression {
class StringLiteral extends Literal {
constructor(value) {
super();
super(value);
this.type = "StringLiteral";
}
parse() {
// Backslash backslash, backslash double quote, uHHHH, UHHHHHH.
const KNOWN_ESCAPES =
/(?:\\\\|\\"|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;
function from_escape_sequence(match, codepoint4, codepoint6) {
switch (match) {
case "\\\\":
return "\\";
case "\\\"":
return "\"";
default:
let codepoint = parseInt(codepoint4 || codepoint6, 16);
if (codepoint <= 0xD7FF || 0xE000 <= codepoint) {
// It's a Unicode scalar value.
return String.fromCodePoint(codepoint);
}
// Escape sequences reresenting surrogate code points are
// well-formed but invalid in Fluent. Replace them with U+FFFD
// REPLACEMENT CHARACTER.
return "�";
}
}
let value = this.value.replace(KNOWN_ESCAPES, from_escape_sequence);
return {value};
}
}
class NumberLiteral extends Literal {
constructor(value) {
super(value);
this.type = "NumberLiteral";
this.value = value;
}
parse() {
let value = parseFloat(this.value);
let decimal_position = this.value.indexOf(".");
let precision = decimal_position > 0
? this.value.length - decimal_position - 1
: 0;
return {value, precision};
}
}
class MessageReference extends Expression {
constructor(id) {
constructor(id, attribute = null) {
super();
this.type = "MessageReference";
this.id = id;
this.attribute = attribute;
}

@@ -194,6 +235,8 @@ }

class TermReference extends Expression {
constructor(id) {
constructor(id, attribute = null, args = null) {
super();
this.type = "TermReference";
this.id = id;
this.attribute = attribute;
this.arguments = args;
}

@@ -211,6 +254,7 @@ }

class FunctionReference extends Expression {
constructor(id) {
constructor(id, args) {
super();
this.type = "FunctionReference";
this.id = id;
this.arguments = args;
}

@@ -228,25 +272,6 @@ }

class AttributeExpression extends Expression {
constructor(ref, name) {
class CallArguments extends SyntaxNode {
constructor(positional = [], named = []) {
super();
this.type = "AttributeExpression";
this.ref = ref;
this.name = name;
}
}
class VariantExpression extends Expression {
constructor(ref, key) {
super();
this.type = "VariantExpression";
this.ref = ref;
this.key = key;
}
}
class CallExpression extends Expression {
constructor(callee, positional = [], named = []) {
super();
this.type = "CallExpression";
this.callee = callee;
this.type = "CallArguments";
this.positional = positional;

@@ -348,3 +373,3 @@ this.named = named;

this.code = code;
this.args = args;
this.arguments = args;
this.message = message;

@@ -391,3 +416,3 @@ }

case "E0009":
return "The key has to be a simple identifier";
return "The argument name has to be a simple identifier";
case "E0010":

@@ -812,6 +837,5 @@ return "Expected one of the variants to be marked as default (*)";

"getComment", "getMessage", "getTerm", "getAttribute", "getIdentifier",
"getVariant", "getNumber", "getPattern", "getVariantList",
"getTextElement", "getPlaceable", "getExpression",
"getInlineExpression", "getCallArgument", "getString",
"getSimpleExpression", "getLiteral",
"getVariant", "getNumber", "getPattern", "getTextElement",
"getPlaceable", "getExpression", "getInlineExpression",
"getCallArgument", "getCallArguments", "getString", "getLiteral",
];

@@ -1019,5 +1043,3 @@ for (const name of methodNames) {

// Syntax 0.8 compat: VariantLists are supported but deprecated. They can
// only be found as values of Terms. Nested VariantLists are not allowed.
const value = this.maybeGetVariantList(ps) || this.maybeGetPattern(ps);
const value = this.maybeGetPattern(ps);
if (value === null) {

@@ -1158,18 +1180,17 @@ throw new ParseError("E0006", id.name);

getNumber(ps) {
let num = "";
let value = "";
if (ps.currentChar === "-") {
num += "-";
ps.next();
value += `-${this.getDigits(ps)}`;
} else {
value += this.getDigits(ps);
}
num = `${num}${this.getDigits(ps)}`;
if (ps.currentChar === ".") {
num += ".";
ps.next();
num = `${num}${this.getDigits(ps)}`;
value += `.${this.getDigits(ps)}`;
}
return new NumberLiteral(num);
return new NumberLiteral(value);
}

@@ -1199,32 +1220,2 @@

// Deprecated in Syntax 0.8. VariantLists are only allowed as values of Terms.
// Values of Messages, Attributes and Variants must be Patterns. This method
// is only used in getTerm.
maybeGetVariantList(ps) {
ps.peekBlank();
if (ps.currentPeek === "{") {
const start = ps.peekOffset;
ps.peek();
ps.peekBlankInline();
if (ps.currentPeek === EOL) {
ps.peekBlank();
if (ps.isVariantStart()) {
ps.resetPeek(start);
ps.skipToPeek();
return this.getVariantList(ps);
}
}
}
ps.resetPeek();
return null;
}
getVariantList(ps) {
ps.expectChar("{");
var variants = this.getVariants(ps);
ps.expectChar("}");
return new VariantList(variants);
}
getPattern(ps, {isBlock}) {

@@ -1371,3 +1362,3 @@ const elements = [];

ps.next();
return [`\\${next}`, next];
return `\\${next}`;
case "u":

@@ -1397,11 +1388,3 @@ return this.getUnicodeEscapeSequence(ps, next, 4);

const codepoint = parseInt(sequence, 16);
const unescaped = codepoint <= 0xD7FF || 0xE000 <= codepoint
// It's a Unicode scalar value.
? String.fromCodePoint(codepoint)
// Escape sequences reresenting surrogate code points are well-formed
// but invalid in Fluent. Replace them with U+FFFD REPLACEMENT
// CHARACTER.
: "�";
return [`\\${u}${sequence}`, unescaped];
return `\\${u}${sequence}`;
}

@@ -1428,20 +1411,13 @@

if (selector.type === "MessageReference") {
throw new ParseError("E0016");
if (selector.attribute === null) {
throw new ParseError("E0016");
} else {
throw new ParseError("E0018");
}
}
if (selector.type === "AttributeExpression"
&& selector.ref.type === "MessageReference") {
throw new ParseError("E0018");
}
if (selector.type === "TermReference"
|| selector.type === "VariantExpression") {
if (selector.type === "TermReference" && selector.attribute === null) {
throw new ParseError("E0017");
}
if (selector.type === "CallExpression"
&& selector.callee.type === "TermReference") {
throw new ParseError("E0017");
}
ps.next();

@@ -1457,12 +1433,6 @@ ps.next();

if (selector.type === "AttributeExpression"
&& selector.ref.type === "TermReference") {
if (selector.type === "TermReference" && selector.attribute !== null) {
throw new ParseError("E0019");
}
if (selector.type === "CallExpression"
&& selector.callee.type === "AttributeExpression") {
throw new ParseError("E0019");
}
return selector;

@@ -1476,56 +1446,2 @@ }

let expr = this.getSimpleExpression(ps);
switch (expr.type) {
case "NumberLiteral":
case "StringLiteral":
case "VariableReference":
return expr;
case "MessageReference": {
if (ps.currentChar === ".") {
ps.next();
const attr = this.getIdentifier(ps);
return new AttributeExpression(expr, attr);
}
if (ps.currentChar === "(") {
// It's a Function. Ensure it's all upper-case.
if (!/^[A-Z][A-Z_?-]*$/.test(expr.id.name)) {
throw new ParseError("E0008");
}
const func = new FunctionReference(expr.id);
if (this.withSpans) {
func.addSpan(expr.span.start, expr.span.end);
}
return new CallExpression(func, ...this.getCallArguments(ps));
}
return expr;
}
case "TermReference": {
if (ps.currentChar === "[") {
ps.next();
const key = this.getVariantKey(ps);
ps.expectChar("]");
return new VariantExpression(expr, key);
}
if (ps.currentChar === ".") {
ps.next();
const attr = this.getIdentifier(ps);
expr = new AttributeExpression(expr, attr);
}
if (ps.currentChar === "(") {
return new CallExpression(expr, ...this.getCallArguments(ps));
}
return expr;
}
default:
throw new ParseError("E0028");
}
}
getSimpleExpression(ps) {
if (ps.isNumberStart()) {

@@ -1548,3 +1464,15 @@ return this.getNumber(ps);

const id = this.getIdentifier(ps);
return new TermReference(id);
let attr;
if (ps.currentChar === ".") {
ps.next();
attr = this.getIdentifier(ps);
}
let args;
if (ps.currentChar === "(") {
args = this.getCallArguments(ps);
}
return new TermReference(id, attr, args);
}

@@ -1554,5 +1482,23 @@

const id = this.getIdentifier(ps);
return new MessageReference(id);
if (ps.currentChar === "(") {
// It's a Function. Ensure it's all upper-case.
if (!/^[A-Z][A-Z0-9_-]*$/.test(id.name)) {
throw new ParseError("E0008");
}
let args = this.getCallArguments(ps);
return new FunctionReference(id, args);
}
let attr;
if (ps.currentChar === ".") {
ps.next();
attr = this.getIdentifier(ps);
}
return new MessageReference(id, attr);
}
throw new ParseError("E0028");

@@ -1570,11 +1516,11 @@ }

if (exp.type !== "MessageReference") {
throw new ParseError("E0009");
if (exp.type === "MessageReference" && exp.attribute === null) {
ps.next();
ps.skipBlank();
const value = this.getLiteral(ps);
return new NamedArgument(exp.id, value);
}
ps.next();
ps.skipBlank();
const value = this.getLiteral(ps);
return new NamedArgument(exp.id, value);
throw new ParseError("E0009");
}

@@ -1620,19 +1566,14 @@

ps.expectChar(")");
return [positional, named];
return new CallArguments(positional, named);
}
getString(ps) {
let raw = "";
ps.expectChar("\"");
let value = "";
ps.expectChar("\"");
let ch;
while ((ch = ps.takeChar(x => x !== '"' && x !== EOL))) {
if (ch === "\\") {
const [sequence, unescaped] = this.getEscapeSequence(ps);
raw += sequence;
value += unescaped;
value += this.getEscapeSequence(ps);
} else {
raw += ch;
value += ch;

@@ -1648,3 +1589,3 @@ }

return new StringLiteral(raw, value);
return new StringLiteral(value);
}

@@ -1759,3 +1700,3 @@

if (message.value) {
parts.push(serializeValue(message.value));
parts.push(serializePattern(message.value));
}

@@ -1780,3 +1721,3 @@

parts.push(`-${term.id.name} =`);
parts.push(serializeValue(term.value));
parts.push(serializePattern(term.value));

@@ -1793,3 +1734,3 @@ for (const attribute of term.attributes) {

function serializeAttribute(attribute) {
const value = indent(serializeValue(attribute.value));
const value = indent(serializePattern(attribute.value));
return `\n .${attribute.id.name} =${value}`;

@@ -1799,14 +1740,2 @@ }

function serializeValue(value) {
switch (value.type) {
case "Pattern":
return serializePattern(value);
case "VariantList":
return serializeVariantList(value);
default:
throw new Error(`Unknown value type: ${value.type}`);
}
}
function serializePattern(pattern) {

@@ -1826,20 +1755,2 @@ const content = pattern.elements.map(serializeElement).join("");

function serializeVariantList(varlist) {
const content = varlist.variants.map(serializeVariant).join("");
return `\n {${indent(content)}\n }`;
}
function serializeVariant(variant) {
const key = serializeVariantKey(variant.key);
const value = indent(serializeValue(variant.value));
if (variant.default) {
return `\n *[${key}]${value}`;
}
return `\n [${key}]${value}`;
}
function serializeElement(element) {

@@ -1859,3 +1770,2 @@ switch (element.type) {

const expr = placeable.expression;
switch (expr.type) {

@@ -1867,3 +1777,3 @@ case "Placeable":

// opening and the closing brace.
return `{ ${serializeSelectExpression(expr)}}`;
return `{ ${serializeExpression(expr)}}`;
default:

@@ -1878,20 +1788,33 @@ return `{ ${serializeExpression(expr)} }`;

case "StringLiteral":
return `"${expr.raw}"`;
return `"${expr.value}"`;
case "NumberLiteral":
return expr.value;
case "MessageReference":
case "FunctionReference":
return expr.id.name;
case "TermReference":
return `-${expr.id.name}`;
case "VariableReference":
return `$${expr.id.name}`;
case "AttributeExpression":
return serializeAttributeExpression(expr);
case "VariantExpression":
return serializeVariantExpression(expr);
case "CallExpression":
return serializeCallExpression(expr);
case "SelectExpression":
return serializeSelectExpression(expr);
case "TermReference": {
let out = `-${expr.id.name}`;
if (expr.attribute) {
out += `.${expr.attribute.name}`;
}
if (expr.arguments) {
out += serializeCallArguments(expr.arguments);
}
return out;
}
case "MessageReference": {
let out = expr.id.name;
if (expr.attribute) {
out += `.${expr.attribute.name}`;
}
return out;
}
case "FunctionReference":
return `${expr.id.name}${serializeCallArguments(expr.arguments)}`;
case "SelectExpression": {
let out = `${serializeExpression(expr.selector)} ->`;
for (let variant of expr.variants) {
out += serializeVariant(variant);
}
return `${out}\n`;
}
case "Placeable":

@@ -1905,37 +1828,21 @@ return serializePlaceable(expr);

function serializeSelectExpression(expr) {
const parts = [];
const selector = `${serializeExpression(expr.selector)} ->`;
parts.push(selector);
function serializeVariant(variant) {
const key = serializeVariantKey(variant.key);
const value = indent(serializePattern(variant.value));
for (const variant of expr.variants) {
parts.push(serializeVariant(variant));
if (variant.default) {
return `\n *[${key}]${value}`;
}
parts.push("\n");
return parts.join("");
return `\n [${key}]${value}`;
}
function serializeAttributeExpression(expr) {
const ref = serializeExpression(expr.ref);
return `${ref}.${expr.name.name}`;
}
function serializeVariantExpression(expr) {
const ref = serializeExpression(expr.ref);
const key = serializeVariantKey(expr.key);
return `${ref}[${key}]`;
}
function serializeCallExpression(expr) {
const callee = serializeExpression(expr.callee);
function serializeCallArguments(expr) {
const positional = expr.positional.map(serializeExpression).join(", ");
const named = expr.named.map(serializeNamedArgument).join(", ");
if (expr.positional.length > 0 && expr.named.length > 0) {
return `${callee}(${positional}, ${named})`;
return `(${positional}, ${named})`;
}
return `${callee}(${positional || named})`;
return `(${positional || named})`;
}

@@ -2058,3 +1965,2 @@

exports.Term = Term;
exports.VariantList = VariantList;
exports.Pattern = Pattern;

@@ -2065,2 +1971,3 @@ exports.PatternElement = PatternElement;

exports.Expression = Expression;
exports.Literal = Literal;
exports.StringLiteral = StringLiteral;

@@ -2073,5 +1980,3 @@ exports.NumberLiteral = NumberLiteral;

exports.SelectExpression = SelectExpression;
exports.AttributeExpression = AttributeExpression;
exports.VariantExpression = VariantExpression;
exports.CallExpression = CallExpression;
exports.CallArguments = CallArguments;
exports.Attribute = Attribute;

@@ -2078,0 +1983,0 @@ exports.Variant = Variant;

{
"name": "fluent-syntax",
"description": "AST and parser for Fluent",
"version": "0.11.0",
"version": "0.12.0",
"homepage": "http://projectfluent.org",

@@ -6,0 +6,0 @@ "author": "Mozilla <l10n-drivers@mozilla.org>",

@@ -117,10 +117,2 @@ /*

export class VariantList extends SyntaxNode {
constructor(variants) {
super();
this.type = "VariantList";
this.variants = variants;
}
}
export class Pattern extends SyntaxNode {

@@ -160,24 +152,73 @@ constructor(elements) {

export class StringLiteral extends Expression {
constructor(raw, value) {
// An abstract base class for Literals.
export class Literal extends Expression {
constructor(value) {
super();
this.type = "StringLiteral";
this.raw = raw;
// The "value" field contains the exact contents of the literal,
// character-for-character.
this.value = value;
}
parse() {
return {value: this.value};
}
}
export class NumberLiteral extends Expression {
export class StringLiteral extends Literal {
constructor(value) {
super();
super(value);
this.type = "StringLiteral";
}
parse() {
// Backslash backslash, backslash double quote, uHHHH, UHHHHHH.
const KNOWN_ESCAPES =
/(?:\\\\|\\"|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;
function from_escape_sequence(match, codepoint4, codepoint6) {
switch (match) {
case "\\\\":
return "\\";
case "\\\"":
return "\"";
default:
let codepoint = parseInt(codepoint4 || codepoint6, 16);
if (codepoint <= 0xD7FF || 0xE000 <= codepoint) {
// It's a Unicode scalar value.
return String.fromCodePoint(codepoint);
}
// Escape sequences reresenting surrogate code points are
// well-formed but invalid in Fluent. Replace them with U+FFFD
// REPLACEMENT CHARACTER.
return "�";
}
}
let value = this.value.replace(KNOWN_ESCAPES, from_escape_sequence);
return {value};
}
}
export class NumberLiteral extends Literal {
constructor(value) {
super(value);
this.type = "NumberLiteral";
this.value = value;
}
parse() {
let value = parseFloat(this.value);
let decimal_position = this.value.indexOf(".");
let precision = decimal_position > 0
? this.value.length - decimal_position - 1
: 0;
return {value, precision};
}
}
export class MessageReference extends Expression {
constructor(id) {
constructor(id, attribute = null) {
super();
this.type = "MessageReference";
this.id = id;
this.attribute = attribute;
}

@@ -187,6 +228,8 @@ }

export class TermReference extends Expression {
constructor(id) {
constructor(id, attribute = null, args = null) {
super();
this.type = "TermReference";
this.id = id;
this.attribute = attribute;
this.arguments = args;
}

@@ -204,6 +247,7 @@ }

export class FunctionReference extends Expression {
constructor(id) {
constructor(id, args) {
super();
this.type = "FunctionReference";
this.id = id;
this.arguments = args;
}

@@ -221,25 +265,6 @@ }

export class AttributeExpression extends Expression {
constructor(ref, name) {
export class CallArguments extends SyntaxNode {
constructor(positional = [], named = []) {
super();
this.type = "AttributeExpression";
this.ref = ref;
this.name = name;
}
}
export class VariantExpression extends Expression {
constructor(ref, key) {
super();
this.type = "VariantExpression";
this.ref = ref;
this.key = key;
}
}
export class CallExpression extends Expression {
constructor(callee, positional = [], named = []) {
super();
this.type = "CallExpression";
this.callee = callee;
this.type = "CallArguments";
this.positional = positional;

@@ -341,5 +366,5 @@ this.named = named;

this.code = code;
this.args = args;
this.arguments = args;
this.message = message;
}
}

@@ -38,3 +38,3 @@ export class ParseError extends Error {

case "E0009":
return "The key has to be a simple identifier";
return "The argument name has to be a simple identifier";
case "E0010":

@@ -41,0 +41,0 @@ return "Expected one of the variants to be marked as default (*)";

@@ -43,6 +43,5 @@ /* eslint no-magic-numbers: [0] */

"getComment", "getMessage", "getTerm", "getAttribute", "getIdentifier",
"getVariant", "getNumber", "getPattern", "getVariantList",
"getTextElement", "getPlaceable", "getExpression",
"getInlineExpression", "getCallArgument", "getString",
"getSimpleExpression", "getLiteral",
"getVariant", "getNumber", "getPattern", "getTextElement",
"getPlaceable", "getExpression", "getInlineExpression",
"getCallArgument", "getCallArguments", "getString", "getLiteral",
];

@@ -250,5 +249,3 @@ for (const name of methodNames) {

// Syntax 0.8 compat: VariantLists are supported but deprecated. They can
// only be found as values of Terms. Nested VariantLists are not allowed.
const value = this.maybeGetVariantList(ps) || this.maybeGetPattern(ps);
const value = this.maybeGetPattern(ps);
if (value === null) {

@@ -389,18 +386,17 @@ throw new ParseError("E0006", id.name);

getNumber(ps) {
let num = "";
let value = "";
if (ps.currentChar === "-") {
num += "-";
ps.next();
value += `-${this.getDigits(ps)}`;
} else {
value += this.getDigits(ps);
}
num = `${num}${this.getDigits(ps)}`;
if (ps.currentChar === ".") {
num += ".";
ps.next();
num = `${num}${this.getDigits(ps)}`;
value += `.${this.getDigits(ps)}`;
}
return new AST.NumberLiteral(num);
return new AST.NumberLiteral(value);
}

@@ -430,32 +426,2 @@

// Deprecated in Syntax 0.8. VariantLists are only allowed as values of Terms.
// Values of Messages, Attributes and Variants must be Patterns. This method
// is only used in getTerm.
maybeGetVariantList(ps) {
ps.peekBlank();
if (ps.currentPeek === "{") {
const start = ps.peekOffset;
ps.peek();
ps.peekBlankInline();
if (ps.currentPeek === EOL) {
ps.peekBlank();
if (ps.isVariantStart()) {
ps.resetPeek(start);
ps.skipToPeek();
return this.getVariantList(ps);
}
}
}
ps.resetPeek();
return null;
}
getVariantList(ps) {
ps.expectChar("{");
var variants = this.getVariants(ps);
ps.expectChar("}");
return new AST.VariantList(variants);
}
getPattern(ps, {isBlock}) {

@@ -602,3 +568,3 @@ const elements = [];

ps.next();
return [`\\${next}`, next];
return `\\${next}`;
case "u":

@@ -628,11 +594,3 @@ return this.getUnicodeEscapeSequence(ps, next, 4);

const codepoint = parseInt(sequence, 16);
const unescaped = codepoint <= 0xD7FF || 0xE000 <= codepoint
// It's a Unicode scalar value.
? String.fromCodePoint(codepoint)
// Escape sequences reresenting surrogate code points are well-formed
// but invalid in Fluent. Replace them with U+FFFD REPLACEMENT
// CHARACTER.
: "�";
return [`\\${u}${sequence}`, unescaped];
return `\\${u}${sequence}`;
}

@@ -659,20 +617,13 @@

if (selector.type === "MessageReference") {
throw new ParseError("E0016");
if (selector.attribute === null) {
throw new ParseError("E0016");
} else {
throw new ParseError("E0018");
}
}
if (selector.type === "AttributeExpression"
&& selector.ref.type === "MessageReference") {
throw new ParseError("E0018");
}
if (selector.type === "TermReference"
|| selector.type === "VariantExpression") {
if (selector.type === "TermReference" && selector.attribute === null) {
throw new ParseError("E0017");
}
if (selector.type === "CallExpression"
&& selector.callee.type === "TermReference") {
throw new ParseError("E0017");
}
ps.next();

@@ -688,12 +639,6 @@ ps.next();

if (selector.type === "AttributeExpression"
&& selector.ref.type === "TermReference") {
if (selector.type === "TermReference" && selector.attribute !== null) {
throw new ParseError("E0019");
}
if (selector.type === "CallExpression"
&& selector.callee.type === "AttributeExpression") {
throw new ParseError("E0019");
}
return selector;

@@ -707,56 +652,2 @@ }

let expr = this.getSimpleExpression(ps);
switch (expr.type) {
case "NumberLiteral":
case "StringLiteral":
case "VariableReference":
return expr;
case "MessageReference": {
if (ps.currentChar === ".") {
ps.next();
const attr = this.getIdentifier(ps);
return new AST.AttributeExpression(expr, attr);
}
if (ps.currentChar === "(") {
// It's a Function. Ensure it's all upper-case.
if (!/^[A-Z][A-Z_?-]*$/.test(expr.id.name)) {
throw new ParseError("E0008");
}
const func = new AST.FunctionReference(expr.id);
if (this.withSpans) {
func.addSpan(expr.span.start, expr.span.end);
}
return new AST.CallExpression(func, ...this.getCallArguments(ps));
}
return expr;
}
case "TermReference": {
if (ps.currentChar === "[") {
ps.next();
const key = this.getVariantKey(ps);
ps.expectChar("]");
return new AST.VariantExpression(expr, key);
}
if (ps.currentChar === ".") {
ps.next();
const attr = this.getIdentifier(ps);
expr = new AST.AttributeExpression(expr, attr);
}
if (ps.currentChar === "(") {
return new AST.CallExpression(expr, ...this.getCallArguments(ps));
}
return expr;
}
default:
throw new ParseError("E0028");
}
}
getSimpleExpression(ps) {
if (ps.isNumberStart()) {

@@ -779,3 +670,15 @@ return this.getNumber(ps);

const id = this.getIdentifier(ps);
return new AST.TermReference(id);
let attr;
if (ps.currentChar === ".") {
ps.next();
attr = this.getIdentifier(ps);
}
let args;
if (ps.currentChar === "(") {
args = this.getCallArguments(ps);
}
return new AST.TermReference(id, attr, args);
}

@@ -785,5 +688,23 @@

const id = this.getIdentifier(ps);
return new AST.MessageReference(id);
if (ps.currentChar === "(") {
// It's a Function. Ensure it's all upper-case.
if (!/^[A-Z][A-Z0-9_-]*$/.test(id.name)) {
throw new ParseError("E0008");
}
let args = this.getCallArguments(ps);
return new AST.FunctionReference(id, args);
}
let attr;
if (ps.currentChar === ".") {
ps.next();
attr = this.getIdentifier(ps);
}
return new AST.MessageReference(id, attr);
}
throw new ParseError("E0028");

@@ -801,11 +722,11 @@ }

if (exp.type !== "MessageReference") {
throw new ParseError("E0009");
if (exp.type === "MessageReference" && exp.attribute === null) {
ps.next();
ps.skipBlank();
const value = this.getLiteral(ps);
return new AST.NamedArgument(exp.id, value);
}
ps.next();
ps.skipBlank();
const value = this.getLiteral(ps);
return new AST.NamedArgument(exp.id, value);
throw new ParseError("E0009");
}

@@ -851,19 +772,14 @@

ps.expectChar(")");
return [positional, named];
return new AST.CallArguments(positional, named);
}
getString(ps) {
let raw = "";
ps.expectChar("\"");
let value = "";
ps.expectChar("\"");
let ch;
while ((ch = ps.takeChar(x => x !== '"' && x !== EOL))) {
if (ch === "\\") {
const [sequence, unescaped] = this.getEscapeSequence(ps);
raw += sequence;
value += unescaped;
value += this.getEscapeSequence(ps);
} else {
raw += ch;
value += ch;

@@ -879,3 +795,3 @@ }

return new AST.StringLiteral(raw, value);
return new AST.StringLiteral(value);
}

@@ -882,0 +798,0 @@

@@ -100,3 +100,3 @@ import { includes } from "./util";

if (message.value) {
parts.push(serializeValue(message.value));
parts.push(serializePattern(message.value));
}

@@ -121,3 +121,3 @@

parts.push(`-${term.id.name} =`);
parts.push(serializeValue(term.value));
parts.push(serializePattern(term.value));

@@ -134,3 +134,3 @@ for (const attribute of term.attributes) {

function serializeAttribute(attribute) {
const value = indent(serializeValue(attribute.value));
const value = indent(serializePattern(attribute.value));
return `\n .${attribute.id.name} =${value}`;

@@ -140,14 +140,2 @@ }

function serializeValue(value) {
switch (value.type) {
case "Pattern":
return serializePattern(value);
case "VariantList":
return serializeVariantList(value);
default:
throw new Error(`Unknown value type: ${value.type}`);
}
}
function serializePattern(pattern) {

@@ -167,20 +155,2 @@ const content = pattern.elements.map(serializeElement).join("");

function serializeVariantList(varlist) {
const content = varlist.variants.map(serializeVariant).join("");
return `\n {${indent(content)}\n }`;
}
function serializeVariant(variant) {
const key = serializeVariantKey(variant.key);
const value = indent(serializeValue(variant.value));
if (variant.default) {
return `\n *[${key}]${value}`;
}
return `\n [${key}]${value}`;
}
function serializeElement(element) {

@@ -200,3 +170,2 @@ switch (element.type) {

const expr = placeable.expression;
switch (expr.type) {

@@ -208,3 +177,3 @@ case "Placeable":

// opening and the closing brace.
return `{ ${serializeSelectExpression(expr)}}`;
return `{ ${serializeExpression(expr)}}`;
default:

@@ -220,20 +189,33 @@ return `{ ${serializeExpression(expr)} }`;

case "StringLiteral":
return `"${expr.raw}"`;
return `"${expr.value}"`;
case "NumberLiteral":
return expr.value;
case "MessageReference":
case "FunctionReference":
return expr.id.name;
case "TermReference":
return `-${expr.id.name}`;
case "VariableReference":
return `$${expr.id.name}`;
case "AttributeExpression":
return serializeAttributeExpression(expr);
case "VariantExpression":
return serializeVariantExpression(expr);
case "CallExpression":
return serializeCallExpression(expr);
case "SelectExpression":
return serializeSelectExpression(expr);
case "TermReference": {
let out = `-${expr.id.name}`;
if (expr.attribute) {
out += `.${expr.attribute.name}`;
}
if (expr.arguments) {
out += serializeCallArguments(expr.arguments);
}
return out;
}
case "MessageReference": {
let out = expr.id.name;
if (expr.attribute) {
out += `.${expr.attribute.name}`;
}
return out;
}
case "FunctionReference":
return `${expr.id.name}${serializeCallArguments(expr.arguments)}`;
case "SelectExpression": {
let out = `${serializeExpression(expr.selector)} ->`;
for (let variant of expr.variants) {
out += serializeVariant(variant);
}
return `${out}\n`;
}
case "Placeable":

@@ -247,37 +229,21 @@ return serializePlaceable(expr);

function serializeSelectExpression(expr) {
const parts = [];
const selector = `${serializeExpression(expr.selector)} ->`;
parts.push(selector);
function serializeVariant(variant) {
const key = serializeVariantKey(variant.key);
const value = indent(serializePattern(variant.value));
for (const variant of expr.variants) {
parts.push(serializeVariant(variant));
if (variant.default) {
return `\n *[${key}]${value}`;
}
parts.push("\n");
return parts.join("");
return `\n [${key}]${value}`;
}
function serializeAttributeExpression(expr) {
const ref = serializeExpression(expr.ref);
return `${ref}.${expr.name.name}`;
}
function serializeVariantExpression(expr) {
const ref = serializeExpression(expr.ref);
const key = serializeVariantKey(expr.key);
return `${ref}[${key}]`;
}
function serializeCallExpression(expr) {
const callee = serializeExpression(expr.callee);
function serializeCallArguments(expr) {
const positional = expr.positional.map(serializeExpression).join(", ");
const named = expr.named.map(serializeNamedArgument).join(", ");
if (expr.positional.length > 0 && expr.named.length > 0) {
return `${callee}(${positional}, ${named})`;
return `(${positional}, ${named})`;
}
return `${callee}(${positional || named})`;
return `(${positional || named})`;
}

@@ -284,0 +250,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc