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.8.1 to 0.9.0

.gitattributes

30

CHANGELOG.md
# Changelog
## fluent-syntax 0.9.0 (October 23, 2018)
This release of `fluent-syntax` brings support for version 0.7 of the
Fluent Syntax spec. The API remains unchanged. Files written in valid
Syntax 0.6 may not parse correctly in this release. See the summary of
backwards-incompatible changes below.
- Implement Fluent Syntax 0.7. (#287)
The major new feature of Syntax 0.7 is the relaxation of the indentation
requirement for all non-text elements of patterns. It's finally possible
to leave the closing brace of select expressions unindented:
emails = { $unread_email_count ->
[one] You have one unread email.
*[other] You have { $unread_email_count } unread emails.
}
Consult the [changelog](https://github.com/projectfluent/fluent/releases/tag/v0.7.0)
to learn about other changes in Syntax 0.7.
### Backward-incompatible changes:
- Variant keys can now be either `NumberLiterals` (as previously) or
`Identifiers`. The `VariantName` node class has been removed. Variant
keys with spaces in them produce syntax errors, e.g. `[New York]`.
- `CR` is not a valid EOL character anymore. Please use `LF` or `CRLF`.
- `Tab` is not recognized as syntax whitespace. It can only be used in
translation content.
## fluent-syntax 0.8.1 (August 1, 2018)

@@ -4,0 +34,0 @@

706

compat.js

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

/* fluent-syntax@0.8.1 */
/* fluent-syntax@0.9.0 */
(function (global, factory) {

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

}
class VariantName extends Identifier {
constructor(name) {
super(name);
this.type = "VariantName";
}
}
class BaseComment extends Entry {

@@ -309,131 +302,2 @@ constructor(content) {

class ParserStream {
constructor(string) {
this.string = string;
this.iter = string[Symbol.iterator]();
this.buf = [];
this.peekIndex = 0;
this.index = 0;
this.iterEnd = false;
this.peekEnd = false;
this.ch = this.iter.next().value;
}
next() {
if (this.iterEnd) {
return undefined;
}
if (this.buf.length === 0) {
this.ch = this.iter.next().value;
} else {
this.ch = this.buf.shift();
}
this.index++;
if (this.ch === undefined) {
this.iterEnd = true;
this.peekEnd = true;
}
this.peekIndex = this.index;
return this.ch;
}
current() {
return this.ch;
}
currentIs(ch) {
return this.ch === ch;
}
currentPeek() {
if (this.peekEnd) {
return undefined;
}
const diff = this.peekIndex - this.index;
if (diff === 0) {
return this.ch;
}
return this.buf[diff - 1];
}
currentPeekIs(ch) {
return this.currentPeek() === ch;
}
peek() {
if (this.peekEnd) {
return undefined;
}
this.peekIndex += 1;
const diff = this.peekIndex - this.index;
if (diff > this.buf.length) {
const ch = this.iter.next().value;
if (ch !== undefined) {
this.buf.push(ch);
} else {
this.peekEnd = true;
return undefined;
}
}
return this.buf[diff - 1];
}
getIndex() {
return this.index;
}
getPeekIndex() {
return this.peekIndex;
}
peekCharIs(ch) {
if (this.peekEnd) {
return false;
}
const ret = this.peek();
this.peekIndex -= 1;
return ret === ch;
}
resetPeek(pos) {
if (pos) {
if (pos < this.peekIndex) {
this.peekEnd = false;
}
this.peekIndex = pos;
} else {
this.peekIndex = this.index;
this.peekEnd = this.iterEnd;
}
}
skipToPeek() {
const diff = this.peekIndex - this.index;
for (let i = 0; i < diff; i++) {
this.ch = this.buf.shift();
}
this.index = this.peekIndex;
}
getSlice(start, end) {
return this.string.substring(start, end);
}
}
function _slicedToArray(arr, i) {

@@ -613,35 +477,84 @@ return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();

/* eslint no-magic-numbers: "off" */
const INLINE_WS = [" ", "\t"];
const SPECIAL_LINE_START_CHARS = ["}", ".", "[", "*"];
class FTLParserStream extends ParserStream {
skipInlineWS() {
while (this.ch) {
if (!includes(INLINE_WS, this.ch)) {
break;
}
class ParserStream {
constructor(string) {
this.string = string;
this.index = 0;
this.peekOffset = 0;
}
this.next();
charAt(offset) {
// When the cursor is at CRLF, return LF but don't move the cursor.
// The cursor still points to the EOL position, which in this case is the
// beginning of the compound CRLF sequence. This ensures slices of
// [inclusive, exclusive) continue to work properly.
if (this.string[offset] === "\r" && this.string[offset + 1] === "\n") {
return "\n";
}
return this.string[offset];
}
peekInlineWS() {
let ch = this.currentPeek();
get currentChar() {
return this.charAt(this.index);
}
while (ch) {
if (!includes(INLINE_WS, ch)) {
break;
}
get currentPeek() {
return this.charAt(this.index + this.peekOffset);
}
ch = this.peek();
next() {
this.peekOffset = 0; // Skip over the CRLF as if it was a single character.
if (this.string[this.index] === "\r" && this.string[this.index + 1] === "\n") {
this.index++;
}
this.index++;
return this.string[this.index];
}
skipBlankLines() {
peek() {
// Skip over the CRLF as if it was a single character.
if (this.string[this.index + this.peekOffset] === "\r" && this.string[this.index + this.peekOffset + 1] === "\n") {
this.peekOffset++;
}
this.peekOffset++;
return this.string[this.index + this.peekOffset];
}
resetPeek() {
let offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
this.peekOffset = offset;
}
skipToPeek() {
this.index += this.peekOffset;
this.peekOffset = 0;
}
}
const EOL = "\n";
const EOF = undefined;
const SPECIAL_LINE_START_CHARS = ["}", ".", "[", "*"];
class FluentParserStream extends ParserStream {
skipBlankInline() {
while (this.currentChar === " ") {
this.next();
}
}
peekBlankInline() {
while (this.currentPeek === " ") {
this.peek();
}
}
skipBlankBlock() {
let lineCount = 0;
while (true) {
this.peekInlineWS();
this.peekBlankInline();
if (this.currentPeekIs("\n")) {
this.skipToPeek();
if (this.currentPeek === EOL) {
this.next();

@@ -656,8 +569,8 @@ lineCount++;

peekBlankLines() {
peekBlankBlock() {
while (true) {
const lineStart = this.getPeekIndex();
this.peekInlineWS();
const lineStart = this.peekOffset;
this.peekBlankInline();
if (this.currentPeekIs("\n")) {
if (this.currentPeek === EOL) {
this.peek();

@@ -671,9 +584,16 @@ } else {

skipIndent() {
this.skipBlankLines();
this.skipInlineWS();
skipBlank() {
while (this.currentChar === " " || this.currentChar === EOL) {
this.next();
}
}
peekBlank() {
while (this.currentPeek === " " || this.currentPeek === EOL) {
this.peek();
}
}
expectChar(ch) {
if (this.ch === ch) {
if (this.currentChar === ch) {
this.next();

@@ -683,19 +603,7 @@ return true;

if (ch === "\n") {
// Unicode Character 'SYMBOL FOR NEWLINE' (U+2424)
throw new ParseError("E0003", "\u2424");
}
throw new ParseError("E0003", ch);
}
expectIndent() {
this.expectChar("\n");
this.skipBlankLines();
this.expectChar(" ");
this.skipInlineWS();
}
expectLineEnd() {
if (this.ch === undefined) {
if (this.currentChar === EOF) {
// EOF is a valid line end in Fluent.

@@ -705,9 +613,19 @@ return true;

return this.expectChar("\n");
if (this.currentChar === EOL) {
this.next();
return true;
} // Unicode Character 'SYMBOL FOR NEWLINE' (U+2424)
throw new ParseError("E0003", "\u2424");
}
takeChar(f) {
const ch = this.ch;
const ch = this.currentChar;
if (ch !== undefined && f(ch)) {
if (ch === EOF) {
return EOF;
}
if (f(ch)) {
this.next();

@@ -717,7 +635,7 @@ return ch;

return undefined;
return null;
}
isCharIDStart(ch) {
if (ch === undefined) {
if (ch === EOF) {
return false;

@@ -732,12 +650,9 @@ }

isIdentifierStart() {
const ch = this.currentPeek();
const isID = this.isCharIDStart(ch);
this.resetPeek();
return isID;
return this.isCharIDStart(this.currentPeek);
}
isNumberStart() {
const ch = this.currentIs("-") ? this.peek() : this.current();
const ch = this.currentChar === "-" ? this.peek() : this.currentChar;
if (ch === undefined) {
if (ch === EOF) {
this.resetPeek();

@@ -755,3 +670,3 @@ return false;

isCharPatternContinuation(ch) {
if (ch === undefined) {
if (ch === EOF) {
return false;

@@ -763,11 +678,17 @@ }

isPeekValueStart() {
this.peekInlineWS();
const ch = this.currentPeek(); // Inline Patterns may start with any char.
isValueStart(_ref) {
let _ref$skip = _ref.skip,
skip = _ref$skip === void 0 ? true : _ref$skip;
if (skip === false) throw new Error("Unimplemented");
this.peekBlankInline();
const ch = this.currentPeek; // Inline Patterns may start with any char.
if (ch !== undefined && ch !== "\n") {
if (ch !== EOF && ch !== EOL) {
this.skipToPeek();
return true;
}
return this.isPeekNextLineValue();
return this.isNextLineValue({
skip
});
} // -1 - any

@@ -779,6 +700,12 @@ // 0 - comment

isPeekNextLineComment() {
isNextLineComment() {
let level = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : -1;
if (!this.currentPeekIs("\n")) {
let _ref2 = arguments.length > 1 ? arguments[1] : undefined,
_ref2$skip = _ref2.skip,
skip = _ref2$skip === void 0 ? false : _ref2$skip;
if (skip === true) throw new Error("Unimplemented");
if (this.currentPeek !== EOL) {
return false;

@@ -790,5 +717,3 @@ }

while (i <= level || level === -1 && i < 3) {
this.peek();
if (!this.currentPeekIs("#")) {
if (this.peek() !== "#") {
if (i <= level && level !== -1) {

@@ -803,7 +728,8 @@ this.resetPeek();

i++;
}
} // The first char after #, ## or ###.
this.peek();
if ([" ", "\n"].includes(this.currentPeek())) {
const ch = this.peek();
if (ch === " " || ch === EOL) {
this.resetPeek();

@@ -817,22 +743,18 @@ return true;

isPeekNextLineVariantStart() {
if (!this.currentPeekIs("\n")) {
isNextLineVariantStart(_ref3) {
let _ref3$skip = _ref3.skip,
skip = _ref3$skip === void 0 ? false : _ref3$skip;
if (skip === true) throw new Error("Unimplemented");
if (this.currentPeek !== EOL) {
return false;
}
this.peek();
this.peekBlankLines();
const ptr = this.getPeekIndex();
this.peekInlineWS();
this.peekBlank();
if (this.getPeekIndex() - ptr === 0) {
this.resetPeek();
return false;
}
if (this.currentPeekIs("*")) {
if (this.currentPeek === "*") {
this.peek();
}
if (this.currentPeekIs("[") && !this.peekCharIs("[")) {
if (this.currentPeek === "[") {
this.resetPeek();

@@ -846,19 +768,10 @@ return true;

isPeekNextLineAttributeStart() {
if (!this.currentPeekIs("\n")) {
return false;
}
isNextLineAttributeStart(_ref4) {
let _ref4$skip = _ref4.skip,
skip = _ref4$skip === void 0 ? true : _ref4$skip;
if (skip === false) throw new Error("Unimplemented");
this.peekBlank();
this.peek();
this.peekBlankLines();
const ptr = this.getPeekIndex();
this.peekInlineWS();
if (this.getPeekIndex() - ptr === 0) {
this.resetPeek();
return false;
}
if (this.currentPeekIs(".")) {
this.resetPeek();
if (this.currentPeek === ".") {
this.skipToPeek();
return true;

@@ -871,37 +784,57 @@ }

isPeekNextLineValue() {
if (!this.currentPeekIs("\n")) {
isNextLineValue(_ref5) {
let _ref5$skip = _ref5.skip,
skip = _ref5$skip === void 0 ? true : _ref5$skip;
if (this.currentPeek !== EOL) {
return false;
}
this.peek();
this.peekBlankLines();
const ptr = this.getPeekIndex();
this.peekInlineWS();
this.peekBlankBlock();
const ptr = this.peekOffset;
this.peekBlankInline();
if (this.getPeekIndex() - ptr === 0) {
this.resetPeek();
return false;
if (this.currentPeek !== "{") {
if (this.peekOffset - ptr === 0) {
this.resetPeek();
return false;
}
if (!this.isCharPatternContinuation(this.currentPeek)) {
this.resetPeek();
return false;
}
}
if (!this.isCharPatternContinuation(this.currentPeek())) {
if (skip) {
this.skipToPeek();
} else {
this.resetPeek();
return false;
}
this.resetPeek();
return true;
}
skipToNextEntryStart() {
while (this.ch) {
if (this.currentIs("\n") && !this.peekCharIs("\n")) {
skipToNextEntryStart(junkStart) {
let lastNewline = this.string.lastIndexOf(EOL, this.index);
if (junkStart < lastNewline) {
// Last seen newline is _after_ the junk start. It's safe to rewind
// without the risk of resuming at the same broken entry.
this.index = lastNewline;
}
while (this.currentChar) {
// We're only interested in beginnings of line.
if (this.currentChar !== EOL) {
this.next();
continue;
} // Break if the first char in this line looks like an entry start.
if (this.ch === undefined || this.isIdentifierStart() || this.currentIs("-") || this.currentIs("#")) {
break;
}
const first = this.next();
if (this.isCharIDStart(first) || first === "-" || first === "#") {
break;
}
this.next();
}

@@ -911,4 +844,4 @@ }

takeIDStart() {
if (this.isCharIDStart(this.ch)) {
const ret = this.ch;
if (this.isCharIDStart(this.currentChar)) {
const ret = this.currentChar;
this.next();

@@ -933,14 +866,2 @@ return ret;

takeVariantNameChar() {
const closure = ch => {
const cc = ch.charCodeAt(0);
return cc >= 97 && cc <= 122 || // a-z
cc >= 65 && cc <= 90 || // A-Z
cc >= 48 && cc <= 57 || // 0-9
cc === 95 || cc === 45 || cc === 32; // _-<space>
};
return this.takeChar(closure);
}
takeDigit() {

@@ -981,3 +902,3 @@ const closure = ch => {

const start = ps.getIndex();
const start = ps.index;
const node = fn.call(this, ps, ...args); // Don't re-add the span if the node already has it. This may happen when

@@ -990,3 +911,3 @@ // one decorated function calls another decorated function.

const end = ps.getIndex();
const end = ps.index;
node.addSpan(start, end);

@@ -1005,3 +926,3 @@ return node;

const methodNames = ["getComment", "getMessage", "getTerm", "getAttribute", "getIdentifier", "getTermIdentifier", "getVariant", "getVariantName", "getNumber", "getValue", "getPattern", "getVariantList", "getTextElement", "getPlaceable", "getExpression", "getSelectorExpression", "getCallArg", "getString", "getLiteral", "getVariantList"];
const methodNames = ["getComment", "getMessage", "getTerm", "getAttribute", "getIdentifier", "getTermIdentifier", "getVariant", "getNumber", "getValue", "getPattern", "getVariantList", "getTextElement", "getPlaceable", "getExpression", "getSelectorExpression", "getCallArg", "getString", "getLiteral"];

@@ -1015,10 +936,10 @@ for (var _i = 0; _i < methodNames.length; _i++) {

parse(source) {
const ps = new FTLParserStream(source);
ps.skipBlankLines();
const ps = new FluentParserStream(source);
ps.skipBlankBlock();
const entries = [];
let lastComment = null;
while (ps.current()) {
while (ps.currentChar) {
const entry = this.getEntryOrJunk(ps);
const blankLines = ps.skipBlankLines(); // Regular Comments require special logic. Comments may be attached to
const blankLines = ps.skipBlankBlock(); // Regular Comments require special logic. Comments may be attached to
// Messages or Terms if they are followed immediately by them. However

@@ -1029,3 +950,3 @@ // they should parse as standalone when they're followed by Junk.

if (entry.type === "Comment" && blankLines === 0 && ps.current()) {
if (entry.type === "Comment" && blankLines === 0 && ps.currentChar) {
// Stash the comment and decide what to do with it in the next pass.

@@ -1058,3 +979,3 @@ lastComment = entry;

if (this.withSpans) {
res.addSpan(0, ps.getIndex());
res.addSpan(0, ps.index);
}

@@ -1076,6 +997,6 @@

parseEntry(source) {
const ps = new FTLParserStream(source);
ps.skipBlankLines();
const ps = new FluentParserStream(source);
ps.skipBlankBlock();
while (ps.currentIs("#")) {
while (ps.currentChar === "#") {
const skipped = this.getEntryOrJunk(ps);

@@ -1088,3 +1009,3 @@

ps.skipBlankLines();
ps.skipBlankBlock();
}

@@ -1096,3 +1017,3 @@

getEntryOrJunk(ps) {
const entryStartPos = ps.getIndex();
const entryStartPos = ps.index;

@@ -1108,7 +1029,13 @@ try {

const errorIndex = ps.getIndex();
ps.skipToNextEntryStart();
const nextEntryStart = ps.getIndex(); // Create a Junk instance
let errorIndex = ps.index;
ps.skipToNextEntryStart(entryStartPos);
const nextEntryStart = ps.index;
const slice = ps.getSlice(entryStartPos, nextEntryStart);
if (nextEntryStart < errorIndex) {
// The position of the error must be inside of the Junk's span.
errorIndex = nextEntryStart;
} // Create a Junk instance
const slice = ps.string.substring(entryStartPos, nextEntryStart);
const junk = new Junk(slice);

@@ -1128,7 +1055,7 @@

getEntry(ps) {
if (ps.currentIs("#")) {
if (ps.currentChar === "#") {
return this.getComment(ps);
}
if (ps.currentIs("-")) {
if (ps.currentChar === "-") {
return this.getTerm(ps);

@@ -1154,3 +1081,3 @@ }

while (ps.currentIs("#") && i < (level === -1 ? 2 : level)) {
while (ps.currentChar === "#" && i < (level === -1 ? 2 : level)) {
ps.next();

@@ -1164,7 +1091,7 @@ i++;

if (!ps.currentIs("\n")) {
if (ps.currentChar !== EOL) {
ps.expectChar(" ");
let ch;
while (ch = ps.takeChar(x => x !== "\n")) {
while (ch = ps.takeChar(x => x !== EOL)) {
content += ch;

@@ -1174,4 +1101,6 @@ }

if (ps.isPeekNextLineComment(level)) {
content += ps.current();
if (ps.isNextLineComment(level, {
skip: false
})) {
content += ps.currentChar;
ps.next();

@@ -1204,13 +1133,14 @@ } else {

const id = this.getIdentifier(ps);
ps.skipInlineWS();
ps.skipBlankInline();
ps.expectChar("=");
if (ps.isPeekValueStart()) {
ps.skipIndent();
if (ps.isValueStart({
skip: true
})) {
var pattern = this.getPattern(ps);
} else {
ps.skipInlineWS();
}
if (ps.isPeekNextLineAttributeStart()) {
if (ps.isNextLineAttributeStart({
skip: true
})) {
var attrs = this.getAttributes(ps);

@@ -1228,7 +1158,8 @@ }

const id = this.getTermIdentifier(ps);
ps.skipInlineWS();
ps.skipBlankInline();
ps.expectChar("=");
if (ps.isPeekValueStart()) {
ps.skipIndent();
if (ps.isValueStart({
skip: true
})) {
var value = this.getValue(ps);

@@ -1239,3 +1170,5 @@ } else {

if (ps.isPeekNextLineAttributeStart()) {
if (ps.isNextLineAttributeStart({
skip: true
})) {
var attrs = this.getAttributes(ps);

@@ -1250,7 +1183,8 @@ }

const key = this.getIdentifier(ps);
ps.skipInlineWS();
ps.skipBlankInline();
ps.expectChar("=");
if (ps.isPeekValueStart()) {
ps.skipIndent();
if (ps.isValueStart({
skip: true
})) {
const value = this.getPattern(ps);

@@ -1267,7 +1201,8 @@ return new Attribute(key, value);

while (true) {
ps.expectIndent();
const attr = this.getAttribute(ps);
attrs.push(attr);
if (!ps.isPeekNextLineAttributeStart()) {
if (!ps.isNextLineAttributeStart({
skip: true
})) {
break;

@@ -1298,5 +1233,5 @@ }

getVariantKey(ps) {
const ch = ps.current();
const ch = ps.currentChar;
if (!ch) {
if (ch === EOF) {
throw new ParseError("E0013");

@@ -1312,3 +1247,3 @@ }

return this.getVariantName(ps);
return this.getIdentifier(ps);
}

@@ -1319,3 +1254,3 @@

if (ps.currentIs("*")) {
if (ps.currentChar === "*") {
if (hasDefault) {

@@ -1331,7 +1266,10 @@ throw new ParseError("E0015");

ps.expectChar("[");
ps.skipBlank();
const key = this.getVariantKey(ps);
ps.skipBlank();
ps.expectChar("]");
if (ps.isPeekValueStart()) {
ps.skipIndent();
if (ps.isValueStart({
skip: true
})) {
const value = this.getValue(ps);

@@ -1349,3 +1287,2 @@ return new Variant(key, value, defaultIndex);

while (true) {
ps.expectIndent();
const variant = this.getVariant(ps, hasDefault);

@@ -1359,5 +1296,9 @@

if (!ps.isPeekNextLineVariantStart()) {
if (!ps.isNextLineVariantStart({
skip: false
})) {
break;
}
ps.skipBlank();
}

@@ -1372,18 +1313,2 @@

getVariantName(ps) {
let name = ps.takeIDStart();
while (true) {
const ch = ps.takeVariantNameChar();
if (ch) {
name += ch;
} else {
break;
}
}
return new VariantName(name.replace(trailingWSRe, ""));
}
getDigits(ps) {

@@ -1407,3 +1332,3 @@ let num = "";

if (ps.currentIs("-")) {
if (ps.currentChar === "-") {
num += "-";

@@ -1415,3 +1340,3 @@ ps.next();

if (ps.currentIs(".")) {
if (ps.currentChar === ".") {
num += ".";

@@ -1426,9 +1351,13 @@ ps.next();

getValue(ps) {
if (ps.currentIs("{")) {
if (ps.currentChar === "{") {
ps.peek();
ps.peekInlineWS();
ps.peekBlankInline();
if (ps.isPeekNextLineVariantStart()) {
if (ps.isNextLineVariantStart({
skip: false
})) {
return this.getVariantList(ps);
}
ps.resetPeek();
}

@@ -1441,5 +1370,8 @@

ps.expectChar("{");
ps.skipInlineWS();
ps.skipBlankInline();
ps.expectLineEnd();
ps.skipBlank();
const variants = this.getVariants(ps);
ps.expectIndent();
ps.expectLineEnd();
ps.skipBlank();
ps.expectChar("}");

@@ -1451,9 +1383,10 @@ return new VariantList(variants);

const elements = [];
ps.skipInlineWS();
let ch;
while (ch = ps.current()) {
while (ch = ps.currentChar) {
// The end condition for getPattern's while loop is a newline
// which is not followed by a valid pattern continuation.
if (ch === "\n" && !ps.isPeekNextLineValue()) {
if (ch === EOL && !ps.isNextLineValue({
skip: false
})) {
break;

@@ -1476,2 +1409,6 @@ }

lastElement.value = lastElement.value.replace(trailingWSRe, "");
if (lastElement.value === "") {
elements.pop();
}
}

@@ -1486,3 +1423,3 @@

while (ch = ps.current()) {
while (ch = ps.currentChar) {
if (ch === "{") {

@@ -1492,4 +1429,6 @@ return new TextElement(buffer);

if (ch === "\n") {
if (!ps.isPeekNextLineValue()) {
if (ch === EOL) {
if (!ps.isNextLineValue({
skip: false
})) {
return new TextElement(buffer);

@@ -1499,5 +1438,4 @@ }

ps.next();
ps.skipInlineWS(); // Add the new line to the buffer
buffer += ch;
ps.skipBlankInline();
buffer += EOL;
continue;

@@ -1509,6 +1447,7 @@ }

buffer += this.getEscapeSequence(ps);
} else {
buffer += ch;
ps.next();
continue;
}
buffer += ch;
ps.next();
}

@@ -1521,3 +1460,3 @@

let specials = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ["{", "\\"];
const next = ps.current();
const next = ps.currentChar;

@@ -1536,4 +1475,4 @@ if (specials.includes(next)) {

if (ch === undefined) {
throw new ParseError("E0026", sequence + ps.current());
if (!ch) {
throw new ParseError("E0026", sequence + ps.currentChar);
}

@@ -1558,10 +1497,8 @@

getExpression(ps) {
ps.skipInlineWS();
ps.skipBlank();
const selector = this.getSelectorExpression(ps);
ps.skipInlineWS();
ps.skipBlank();
if (ps.currentIs("-")) {
ps.peek();
if (!ps.currentPeekIs(">")) {
if (ps.currentChar === "-") {
if (ps.peek() !== ">") {
ps.resetPeek();

@@ -1585,4 +1522,7 @@ return selector;

ps.next();
ps.skipInlineWS();
ps.skipBlankInline();
ps.expectLineEnd();
ps.skipBlank();
const variants = this.getVariants(ps);
ps.skipBlank();

@@ -1598,3 +1538,2 @@ if (variants.length === 0) {

ps.expectIndent();
return new SelectExpression(selector, variants);

@@ -1605,2 +1544,3 @@ } else if (selector.type === "AttributeExpression" && selector.ref.type === "TermReference") {

ps.skipBlank();
return selector;

@@ -1610,3 +1550,3 @@ }

getSelectorExpression(ps) {
if (ps.currentIs("{")) {
if (ps.currentChar === "{") {
return this.getPlaceable(ps);

@@ -1621,3 +1561,3 @@ }

const ch = ps.current();
const ch = ps.currentChar;

@@ -1665,5 +1605,5 @@ if (ch === ".") {

const exp = this.getSelectorExpression(ps);
ps.skipInlineWS();
ps.skipBlank();
if (ps.current() !== ":") {
if (ps.currentChar !== ":") {
return exp;

@@ -1677,3 +1617,3 @@ }

ps.next();
ps.skipInlineWS();
ps.skipBlank();
const val = this.getArgVal(ps);

@@ -1687,7 +1627,6 @@ return new NamedArgument(exp.id, val);

const argumentNames = new Set();
ps.skipInlineWS();
ps.skipIndent();
ps.skipBlank();
while (true) {
if (ps.current() === ")") {
if (ps.currentChar === ")") {
break;

@@ -1711,9 +1650,7 @@ }

ps.skipInlineWS();
ps.skipIndent();
ps.skipBlank();
if (ps.current() === ",") {
if (ps.currentChar === ",") {
ps.next();
ps.skipInlineWS();
ps.skipIndent();
ps.skipBlank();
continue;

@@ -1734,3 +1671,3 @@ } else {

return this.getNumber(ps);
} else if (ps.currentIs('"')) {
} else if (ps.currentChar === '"') {
return this.getString(ps);

@@ -1744,6 +1681,6 @@ }

let val = "";
ps.expectChar('"');
ps.expectChar("\"");
let ch;
while (ch = ps.takeChar(x => x !== '"' && x !== "\n")) {
while (ch = ps.takeChar(x => x !== '"' && x !== EOL)) {
if (ch === "\\") {

@@ -1756,7 +1693,7 @@ val += this.getEscapeSequence(ps, ["{", "\\", "\""]);

if (ps.currentIs("\n")) {
if (ps.currentChar === EOL) {
throw new ParseError("E0020");
}
ps.next();
ps.expectChar("\"");
return new StringLiteral(val);

@@ -1766,5 +1703,5 @@ }

getLiteral(ps) {
const ch = ps.current();
const ch = ps.currentChar;
if (!ch) {
if (ch === EOF) {
throw new ParseError("E0014");

@@ -2172,10 +2109,6 @@ }

function serializeVariantName(VariantName) {
return VariantName.name;
}
function serializeVariantKey(key) {
switch (key.type) {
case "VariantName":
return serializeVariantName(key);
case "Identifier":
return serializeIdentifier(key);

@@ -2250,3 +2183,2 @@ case "NumberLiteral":

exports.Identifier = Identifier;
exports.VariantName = VariantName;
exports.BaseComment = BaseComment;

@@ -2253,0 +2185,0 @@ exports.Comment = Comment;

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

/* fluent-syntax@0.8.1 */
/* fluent-syntax@0.9.0 */
(function (global, factory) {

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

class VariantName extends Identifier {
constructor(name) {
super(name);
this.type = "VariantName";
}
}
class BaseComment extends Entry {

@@ -293,133 +286,2 @@ constructor(content) {

class ParserStream {
constructor(string) {
this.string = string;
this.iter = string[Symbol.iterator]();
this.buf = [];
this.peekIndex = 0;
this.index = 0;
this.iterEnd = false;
this.peekEnd = false;
this.ch = this.iter.next().value;
}
next() {
if (this.iterEnd) {
return undefined;
}
if (this.buf.length === 0) {
this.ch = this.iter.next().value;
} else {
this.ch = this.buf.shift();
}
this.index++;
if (this.ch === undefined) {
this.iterEnd = true;
this.peekEnd = true;
}
this.peekIndex = this.index;
return this.ch;
}
current() {
return this.ch;
}
currentIs(ch) {
return this.ch === ch;
}
currentPeek() {
if (this.peekEnd) {
return undefined;
}
const diff = this.peekIndex - this.index;
if (diff === 0) {
return this.ch;
}
return this.buf[diff - 1];
}
currentPeekIs(ch) {
return this.currentPeek() === ch;
}
peek() {
if (this.peekEnd) {
return undefined;
}
this.peekIndex += 1;
const diff = this.peekIndex - this.index;
if (diff > this.buf.length) {
const ch = this.iter.next().value;
if (ch !== undefined) {
this.buf.push(ch);
} else {
this.peekEnd = true;
return undefined;
}
}
return this.buf[diff - 1];
}
getIndex() {
return this.index;
}
getPeekIndex() {
return this.peekIndex;
}
peekCharIs(ch) {
if (this.peekEnd) {
return false;
}
const ret = this.peek();
this.peekIndex -= 1;
return ret === ch;
}
resetPeek(pos) {
if (pos) {
if (pos < this.peekIndex) {
this.peekEnd = false;
}
this.peekIndex = pos;
} else {
this.peekIndex = this.index;
this.peekEnd = this.iterEnd;
}
}
skipToPeek() {
const diff = this.peekIndex - this.index;
for (let i = 0; i < diff; i++) {
this.ch = this.buf.shift();
}
this.index = this.peekIndex;
}
getSlice(start, end) {
return this.string.substring(start, end);
}
}
class ParseError extends Error {

@@ -512,11 +374,68 @@ constructor(code, ...args) {

const INLINE_WS = [" ", "\t"];
class ParserStream {
constructor(string) {
this.string = string;
this.index = 0;
this.peekOffset = 0;
}
charAt(offset) {
// When the cursor is at CRLF, return LF but don't move the cursor.
// The cursor still points to the EOL position, which in this case is the
// beginning of the compound CRLF sequence. This ensures slices of
// [inclusive, exclusive) continue to work properly.
if (this.string[offset] === "\r"
&& this.string[offset + 1] === "\n") {
return "\n";
}
return this.string[offset];
}
get currentChar() {
return this.charAt(this.index);
}
get currentPeek() {
return this.charAt(this.index + this.peekOffset);
}
next() {
this.peekOffset = 0;
// Skip over the CRLF as if it was a single character.
if (this.string[this.index] === "\r"
&& this.string[this.index + 1] === "\n") {
this.index++;
}
this.index++;
return this.string[this.index];
}
peek() {
// Skip over the CRLF as if it was a single character.
if (this.string[this.index + this.peekOffset] === "\r"
&& this.string[this.index + this.peekOffset + 1] === "\n") {
this.peekOffset++;
}
this.peekOffset++;
return this.string[this.index + this.peekOffset];
}
resetPeek(offset = 0) {
this.peekOffset = offset;
}
skipToPeek() {
this.index += this.peekOffset;
this.peekOffset = 0;
}
}
const EOL = "\n";
const EOF = undefined;
const SPECIAL_LINE_START_CHARS = ["}", ".", "[", "*"];
class FTLParserStream extends ParserStream {
skipInlineWS() {
while (this.ch) {
if (!includes(INLINE_WS, this.ch)) {
break;
}
class FluentParserStream extends ParserStream {
skipBlankInline() {
while (this.currentChar === " ") {
this.next();

@@ -526,19 +445,14 @@ }

peekInlineWS() {
let ch = this.currentPeek();
while (ch) {
if (!includes(INLINE_WS, ch)) {
break;
}
ch = this.peek();
peekBlankInline() {
while (this.currentPeek === " ") {
this.peek();
}
}
skipBlankLines() {
skipBlankBlock() {
let lineCount = 0;
while (true) {
this.peekInlineWS();
this.peekBlankInline();
if (this.currentPeekIs("\n")) {
this.skipToPeek();
if (this.currentPeek === EOL) {
this.next();

@@ -553,9 +467,9 @@ lineCount++;

peekBlankLines() {
peekBlankBlock() {
while (true) {
const lineStart = this.getPeekIndex();
const lineStart = this.peekOffset;
this.peekInlineWS();
this.peekBlankInline();
if (this.currentPeekIs("\n")) {
if (this.currentPeek === EOL) {
this.peek();

@@ -569,9 +483,16 @@ } else {

skipIndent() {
this.skipBlankLines();
this.skipInlineWS();
skipBlank() {
while (this.currentChar === " " || this.currentChar === EOL) {
this.next();
}
}
peekBlank() {
while (this.currentPeek === " " || this.currentPeek === EOL) {
this.peek();
}
}
expectChar(ch) {
if (this.ch === ch) {
if (this.currentChar === ch) {
this.next();

@@ -581,19 +502,7 @@ return true;

if (ch === "\n") {
// Unicode Character 'SYMBOL FOR NEWLINE' (U+2424)
throw new ParseError("E0003", "\u2424");
}
throw new ParseError("E0003", ch);
}
expectIndent() {
this.expectChar("\n");
this.skipBlankLines();
this.expectChar(" ");
this.skipInlineWS();
}
expectLineEnd() {
if (this.ch === undefined) {
if (this.currentChar === EOF) {
// EOF is a valid line end in Fluent.

@@ -603,16 +512,25 @@ return true;

return this.expectChar("\n");
if (this.currentChar === EOL) {
this.next();
return true;
}
// Unicode Character 'SYMBOL FOR NEWLINE' (U+2424)
throw new ParseError("E0003", "\u2424");
}
takeChar(f) {
const ch = this.ch;
if (ch !== undefined && f(ch)) {
const ch = this.currentChar;
if (ch === EOF) {
return EOF;
}
if (f(ch)) {
this.next();
return ch;
}
return undefined;
return null;
}
isCharIDStart(ch) {
if (ch === undefined) {
if (ch === EOF) {
return false;

@@ -627,14 +545,11 @@ }

isIdentifierStart() {
const ch = this.currentPeek();
const isID = this.isCharIDStart(ch);
this.resetPeek();
return isID;
return this.isCharIDStart(this.currentPeek);
}
isNumberStart() {
const ch = this.currentIs("-")
const ch = this.currentChar === "-"
? this.peek()
: this.current();
: this.currentChar;
if (ch === undefined) {
if (ch === EOF) {
this.resetPeek();

@@ -651,3 +566,3 @@ return false;

isCharPatternContinuation(ch) {
if (ch === undefined) {
if (ch === EOF) {
return false;

@@ -659,12 +574,15 @@ }

isPeekValueStart() {
this.peekInlineWS();
const ch = this.currentPeek();
isValueStart({skip = true}) {
if (skip === false) throw new Error("Unimplemented");
this.peekBlankInline();
const ch = this.currentPeek;
// Inline Patterns may start with any char.
if (ch !== undefined && ch !== "\n") {
if (ch !== EOF && ch !== EOL) {
this.skipToPeek();
return true;
}
return this.isPeekNextLineValue();
return this.isNextLineValue({skip});
}

@@ -676,4 +594,6 @@

// 2 - resource comment
isPeekNextLineComment(level = -1) {
if (!this.currentPeekIs("\n")) {
isNextLineComment(level = -1, {skip = false}) {
if (skip === true) throw new Error("Unimplemented");
if (this.currentPeek !== EOL) {
return false;

@@ -685,4 +605,3 @@ }

while (i <= level || (level === -1 && i < 3)) {
this.peek();
if (!this.currentPeekIs("#")) {
if (this.peek() !== "#") {
if (i <= level && level !== -1) {

@@ -697,4 +616,5 @@ this.resetPeek();

this.peek();
if ([" ", "\n"].includes(this.currentPeek())) {
// The first char after #, ## or ###.
const ch = this.peek();
if (ch === " " || ch === EOL) {
this.resetPeek();

@@ -708,25 +628,16 @@ return true;

isPeekNextLineVariantStart() {
if (!this.currentPeekIs("\n")) {
isNextLineVariantStart({skip = false}) {
if (skip === true) throw new Error("Unimplemented");
if (this.currentPeek !== EOL) {
return false;
}
this.peek();
this.peekBlank();
this.peekBlankLines();
const ptr = this.getPeekIndex();
this.peekInlineWS();
if (this.getPeekIndex() - ptr === 0) {
this.resetPeek();
return false;
}
if (this.currentPeekIs("*")) {
if (this.currentPeek === "*") {
this.peek();
}
if (this.currentPeekIs("[") && !this.peekCharIs("[")) {
if (this.currentPeek === "[") {
this.resetPeek();

@@ -739,22 +650,9 @@ return true;

isPeekNextLineAttributeStart() {
if (!this.currentPeekIs("\n")) {
return false;
}
isNextLineAttributeStart({skip = true}) {
if (skip === false) throw new Error("Unimplemented");
this.peek();
this.peekBlank();
this.peekBlankLines();
const ptr = this.getPeekIndex();
this.peekInlineWS();
if (this.getPeekIndex() - ptr === 0) {
this.resetPeek();
return false;
}
if (this.currentPeekIs(".")) {
this.resetPeek();
if (this.currentPeek === ".") {
this.skipToPeek();
return true;

@@ -767,41 +665,52 @@ }

isPeekNextLineValue() {
if (!this.currentPeekIs("\n")) {
isNextLineValue({skip = true}) {
if (this.currentPeek !== EOL) {
return false;
}
this.peek();
this.peekBlankBlock();
this.peekBlankLines();
const ptr = this.peekOffset;
const ptr = this.getPeekIndex();
this.peekBlankInline();
this.peekInlineWS();
if (this.currentPeek !== "{") {
if (this.peekOffset - ptr === 0) {
this.resetPeek();
return false;
}
if (this.getPeekIndex() - ptr === 0) {
this.resetPeek();
return false;
if (!this.isCharPatternContinuation(this.currentPeek)) {
this.resetPeek();
return false;
}
}
if (!this.isCharPatternContinuation(this.currentPeek())) {
if (skip) {
this.skipToPeek();
} else {
this.resetPeek();
return false;
}
this.resetPeek();
return true;
}
skipToNextEntryStart() {
while (this.ch) {
if (this.currentIs("\n") && !this.peekCharIs("\n")) {
skipToNextEntryStart(junkStart) {
let lastNewline = this.string.lastIndexOf(EOL, this.index);
if (junkStart < lastNewline) {
// Last seen newline is _after_ the junk start. It's safe to rewind
// without the risk of resuming at the same broken entry.
this.index = lastNewline;
}
while (this.currentChar) {
// We're only interested in beginnings of line.
if (this.currentChar !== EOL) {
this.next();
if (this.ch === undefined ||
this.isIdentifierStart() ||
this.currentIs("-") ||
this.currentIs("#")) {
break;
}
continue;
}
this.next();
// Break if the first char in this line looks like an entry start.
const first = this.next();
if (this.isCharIDStart(first) || first === "-" || first === "#") {
break;
}
}

@@ -811,4 +720,4 @@ }

takeIDStart() {
if (this.isCharIDStart(this.ch)) {
const ret = this.ch;
if (this.isCharIDStart(this.currentChar)) {
const ret = this.currentChar;
this.next();

@@ -833,14 +742,2 @@ return ret;

takeVariantNameChar() {
const closure = ch => {
const cc = ch.charCodeAt(0);
return ((cc >= 97 && cc <= 122) || // a-z
(cc >= 65 && cc <= 90) || // A-Z
(cc >= 48 && cc <= 57) || // 0-9
cc === 95 || cc === 45 || cc === 32); // _-<space>
};
return this.takeChar(closure);
}
takeDigit() {

@@ -879,3 +776,3 @@ const closure = ch => {

const start = ps.getIndex();
const start = ps.index;
const node = fn.call(this, ps, ...args);

@@ -889,3 +786,3 @@

const end = ps.getIndex();
const end = ps.index;
node.addSpan(start, end);

@@ -906,6 +803,6 @@ return node;

"getComment", "getMessage", "getTerm", "getAttribute", "getIdentifier",
"getTermIdentifier", "getVariant", "getVariantName", "getNumber",
"getTermIdentifier", "getVariant", "getNumber",
"getValue", "getPattern", "getVariantList", "getTextElement",
"getPlaceable", "getExpression", "getSelectorExpression", "getCallArg",
"getString", "getLiteral", "getVariantList"
"getString", "getLiteral"
];

@@ -918,4 +815,4 @@ for (const name of methodNames) {

parse(source) {
const ps = new FTLParserStream(source);
ps.skipBlankLines();
const ps = new FluentParserStream(source);
ps.skipBlankBlock();

@@ -925,5 +822,5 @@ const entries = [];

while (ps.current()) {
while (ps.currentChar) {
const entry = this.getEntryOrJunk(ps);
const blankLines = ps.skipBlankLines();
const blankLines = ps.skipBlankBlock();

@@ -935,3 +832,3 @@ // Regular Comments require special logic. Comments may be attached to

// or the Term parsed successfully.
if (entry.type === "Comment" && blankLines === 0 && ps.current()) {
if (entry.type === "Comment" && blankLines === 0 && ps.currentChar) {
// Stash the comment and decide what to do with it in the next pass.

@@ -962,3 +859,3 @@ lastComment = entry;

if (this.withSpans) {
res.addSpan(0, ps.getIndex());
res.addSpan(0, ps.index);
}

@@ -979,6 +876,6 @@

parseEntry(source) {
const ps = new FTLParserStream(source);
ps.skipBlankLines();
const ps = new FluentParserStream(source);
ps.skipBlankBlock();
while (ps.currentIs("#")) {
while (ps.currentChar === "#") {
const skipped = this.getEntryOrJunk(ps);

@@ -989,3 +886,3 @@ if (skipped.type === "Junk") {

}
ps.skipBlankLines();
ps.skipBlankBlock();
}

@@ -997,3 +894,3 @@

getEntryOrJunk(ps) {
const entryStartPos = ps.getIndex();
const entryStartPos = ps.index;

@@ -1009,8 +906,12 @@ try {

const errorIndex = ps.getIndex();
ps.skipToNextEntryStart();
const nextEntryStart = ps.getIndex();
let errorIndex = ps.index;
ps.skipToNextEntryStart(entryStartPos);
const nextEntryStart = ps.index;
if (nextEntryStart < errorIndex) {
// The position of the error must be inside of the Junk's span.
errorIndex = nextEntryStart;
}
// Create a Junk instance
const slice = ps.getSlice(entryStartPos, nextEntryStart);
const slice = ps.string.substring(entryStartPos, nextEntryStart);
const junk = new Junk(slice);

@@ -1028,7 +929,7 @@ if (this.withSpans) {

getEntry(ps) {
if (ps.currentIs("#")) {
if (ps.currentChar === "#") {
return this.getComment(ps);
}
if (ps.currentIs("-")) {
if (ps.currentChar === "-") {
return this.getTerm(ps);

@@ -1053,3 +954,3 @@ }

let i = -1;
while (ps.currentIs("#") && (i < (level === -1 ? 2 : level))) {
while (ps.currentChar === "#" && (i < (level === -1 ? 2 : level))) {
ps.next();

@@ -1063,6 +964,6 @@ i++;

if (!ps.currentIs("\n")) {
if (ps.currentChar !== EOL) {
ps.expectChar(" ");
let ch;
while ((ch = ps.takeChar(x => x !== "\n"))) {
while ((ch = ps.takeChar(x => x !== EOL))) {
content += ch;

@@ -1072,4 +973,4 @@ }

if (ps.isPeekNextLineComment(level)) {
content += ps.current();
if (ps.isNextLineComment(level, {skip: false})) {
content += ps.currentChar;
ps.next();

@@ -1099,13 +1000,10 @@ } else {

ps.skipInlineWS();
ps.skipBlankInline();
ps.expectChar("=");
if (ps.isPeekValueStart()) {
ps.skipIndent();
if (ps.isValueStart({skip: true})) {
var pattern = this.getPattern(ps);
} else {
ps.skipInlineWS();
}
if (ps.isPeekNextLineAttributeStart()) {
if (ps.isNextLineAttributeStart({skip: true})) {
var attrs = this.getAttributes(ps);

@@ -1124,7 +1022,6 @@ }

ps.skipInlineWS();
ps.skipBlankInline();
ps.expectChar("=");
if (ps.isPeekValueStart()) {
ps.skipIndent();
if (ps.isValueStart({skip: true})) {
var value = this.getValue(ps);

@@ -1135,3 +1032,3 @@ } else {

if (ps.isPeekNextLineAttributeStart()) {
if (ps.isNextLineAttributeStart({skip: true})) {
var attrs = this.getAttributes(ps);

@@ -1148,7 +1045,6 @@ }

ps.skipInlineWS();
ps.skipBlankInline();
ps.expectChar("=");
if (ps.isPeekValueStart()) {
ps.skipIndent();
if (ps.isValueStart({skip: true})) {
const value = this.getPattern(ps);

@@ -1165,7 +1061,6 @@ return new Attribute(key, value);

while (true) {
ps.expectIndent();
const attr = this.getAttribute(ps);
attrs.push(attr);
if (!ps.isPeekNextLineAttributeStart()) {
if (!ps.isNextLineAttributeStart({skip: true})) {
break;

@@ -1196,5 +1091,5 @@ }

getVariantKey(ps) {
const ch = ps.current();
const ch = ps.currentChar;
if (!ch) {
if (ch === EOF) {
throw new ParseError("E0013");

@@ -1209,3 +1104,3 @@ }

return this.getVariantName(ps);
return this.getIdentifier(ps);
}

@@ -1216,3 +1111,3 @@

if (ps.currentIs("*")) {
if (ps.currentChar === "*") {
if (hasDefault) {

@@ -1228,8 +1123,11 @@ throw new ParseError("E0015");

ps.skipBlank();
const key = this.getVariantKey(ps);
ps.skipBlank();
ps.expectChar("]");
if (ps.isPeekValueStart()) {
ps.skipIndent();
if (ps.isValueStart({skip: true})) {
const value = this.getValue(ps);

@@ -1247,3 +1145,2 @@ return new Variant(key, value, defaultIndex);

while (true) {
ps.expectIndent();
const variant = this.getVariant(ps, hasDefault);

@@ -1257,5 +1154,6 @@

if (!ps.isPeekNextLineVariantStart()) {
if (!ps.isNextLineVariantStart({skip: false})) {
break;
}
ps.skipBlank();
}

@@ -1270,17 +1168,2 @@

getVariantName(ps) {
let name = ps.takeIDStart();
while (true) {
const ch = ps.takeVariantNameChar();
if (ch) {
name += ch;
} else {
break;
}
}
return new VariantName(name.replace(trailingWSRe, ""));
}
getDigits(ps) {

@@ -1304,3 +1187,3 @@ let num = "";

if (ps.currentIs("-")) {
if (ps.currentChar === "-") {
num += "-";

@@ -1312,3 +1195,3 @@ ps.next();

if (ps.currentIs(".")) {
if (ps.currentChar === ".") {
num += ".";

@@ -1323,8 +1206,10 @@ ps.next();

getValue(ps) {
if (ps.currentIs("{")) {
if (ps.currentChar === "{") {
ps.peek();
ps.peekInlineWS();
if (ps.isPeekNextLineVariantStart()) {
ps.peekBlankInline();
if (ps.isNextLineVariantStart({skip: false})) {
return this.getVariantList(ps);
}
ps.resetPeek();
}

@@ -1337,5 +1222,8 @@

ps.expectChar("{");
ps.skipInlineWS();
ps.skipBlankInline();
ps.expectLineEnd();
ps.skipBlank();
const variants = this.getVariants(ps);
ps.expectIndent();
ps.expectLineEnd();
ps.skipBlank();
ps.expectChar("}");

@@ -1347,10 +1235,9 @@ return new VariantList(variants);

const elements = [];
ps.skipInlineWS();
let ch;
while ((ch = ps.current())) {
while ((ch = ps.currentChar)) {
// The end condition for getPattern's while loop is a newline
// which is not followed by a valid pattern continuation.
if (ch === "\n" && !ps.isPeekNextLineValue()) {
if (ch === EOL && !ps.isNextLineValue({skip: false})) {
break;

@@ -1372,2 +1259,5 @@ }

lastElement.value = lastElement.value.replace(trailingWSRe, "");
if (lastElement.value === "") {
elements.pop();
}
}

@@ -1382,3 +1272,3 @@

let ch;
while ((ch = ps.current())) {
while ((ch = ps.currentChar)) {
if (ch === "{") {

@@ -1388,4 +1278,4 @@ return new TextElement(buffer);

if (ch === "\n") {
if (!ps.isPeekNextLineValue()) {
if (ch === EOL) {
if (!ps.isNextLineValue({skip: false})) {
return new TextElement(buffer);

@@ -1395,6 +1285,5 @@ }

ps.next();
ps.skipInlineWS();
ps.skipBlankInline();
// Add the new line to the buffer
buffer += ch;
buffer += EOL;
continue;

@@ -1406,6 +1295,7 @@ }

buffer += this.getEscapeSequence(ps);
} else {
buffer += ch;
ps.next();
continue;
}
buffer += ch;
ps.next();
}

@@ -1417,3 +1307,3 @@

getEscapeSequence(ps, specials = ["{", "\\"]) {
const next = ps.current();
const next = ps.currentChar;

@@ -1432,4 +1322,4 @@ if (specials.includes(next)) {

if (ch === undefined) {
throw new ParseError("E0026", sequence + ps.current());
if (!ch) {
throw new ParseError("E0026", sequence + ps.currentChar);
}

@@ -1454,12 +1344,11 @@

getExpression(ps) {
ps.skipInlineWS();
ps.skipBlank();
const selector = this.getSelectorExpression(ps);
ps.skipInlineWS();
ps.skipBlank();
if (ps.currentIs("-")) {
ps.peek();
if (ps.currentChar === "-") {
if (!ps.currentPeekIs(">")) {
if (ps.peek() !== ">") {
ps.resetPeek();

@@ -1485,5 +1374,8 @@ return selector;

ps.skipInlineWS();
ps.skipBlankInline();
ps.expectLineEnd();
ps.skipBlank();
const variants = this.getVariants(ps);
ps.skipBlank();

@@ -1499,4 +1391,2 @@ if (variants.length === 0) {

ps.expectIndent();
return new SelectExpression(selector, variants);

@@ -1508,2 +1398,4 @@ } else if (selector.type === "AttributeExpression" &&

ps.skipBlank();
return selector;

@@ -1513,3 +1405,3 @@ }

getSelectorExpression(ps) {
if (ps.currentIs("{")) {
if (ps.currentChar === "{") {
return this.getPlaceable(ps);

@@ -1524,3 +1416,3 @@ }

const ch = ps.current();
const ch = ps.currentChar;

@@ -1577,5 +1469,5 @@ if (ch === ".") {

ps.skipInlineWS();
ps.skipBlank();
if (ps.current() !== ":") {
if (ps.currentChar !== ":") {
return exp;

@@ -1589,3 +1481,3 @@ }

ps.next();
ps.skipInlineWS();
ps.skipBlank();

@@ -1602,7 +1494,6 @@ const val = this.getArgVal(ps);

ps.skipInlineWS();
ps.skipIndent();
ps.skipBlank();
while (true) {
if (ps.current() === ")") {
if (ps.currentChar === ")") {
break;

@@ -1624,9 +1515,7 @@ }

ps.skipInlineWS();
ps.skipIndent();
ps.skipBlank();
if (ps.current() === ",") {
if (ps.currentChar === ",") {
ps.next();
ps.skipInlineWS();
ps.skipIndent();
ps.skipBlank();
continue;

@@ -1646,3 +1535,3 @@ } else {

return this.getNumber(ps);
} else if (ps.currentIs('"')) {
} else if (ps.currentChar === '"') {
return this.getString(ps);

@@ -1656,6 +1545,6 @@ }

ps.expectChar('"');
ps.expectChar("\"");
let ch;
while ((ch = ps.takeChar(x => x !== '"' && x !== "\n"))) {
while ((ch = ps.takeChar(x => x !== '"' && x !== EOL))) {
if (ch === "\\") {

@@ -1668,7 +1557,7 @@ val += this.getEscapeSequence(ps, ["{", "\\", "\""]);

if (ps.currentIs("\n")) {
if (ps.currentChar === EOL) {
throw new ParseError("E0020");
}
ps.next();
ps.expectChar("\"");

@@ -1680,5 +1569,5 @@ return new StringLiteral(val);

getLiteral(ps) {
const ch = ps.current();
const ch = ps.currentChar;
if (!ch) {
if (ch === EOF) {
throw new ParseError("E0014");

@@ -2019,12 +1908,6 @@ }

function serializeVariantName(VariantName) {
return VariantName.name;
}
function serializeVariantKey(key) {
switch (key.type) {
case "VariantName":
return serializeVariantName(key);
case "Identifier":
return serializeIdentifier(key);
case "NumberLiteral":

@@ -2102,3 +1985,2 @@ return serializeNumberLiteral(key);

exports.Identifier = Identifier;
exports.VariantName = VariantName;
exports.BaseComment = BaseComment;

@@ -2105,0 +1987,0 @@ exports.Comment = Comment;

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

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

@@ -211,9 +211,2 @@ /*

export class VariantName extends Identifier {
constructor(name) {
super(name);
this.type = "VariantName";
}
}
export class BaseComment extends Entry {

@@ -220,0 +213,0 @@ constructor(content) {

/* eslint no-magic-numbers: [0] */
import * as AST from "./ast";
import { FTLParserStream } from "./ftlstream";
import { EOF, EOL, FluentParserStream } from "./stream";
import { ParseError } from "./errors";

@@ -17,3 +17,3 @@

const start = ps.getIndex();
const start = ps.index;
const node = fn.call(this, ps, ...args);

@@ -27,3 +27,3 @@

const end = ps.getIndex();
const end = ps.index;
node.addSpan(start, end);

@@ -44,6 +44,6 @@ return node;

"getComment", "getMessage", "getTerm", "getAttribute", "getIdentifier",
"getTermIdentifier", "getVariant", "getVariantName", "getNumber",
"getTermIdentifier", "getVariant", "getNumber",
"getValue", "getPattern", "getVariantList", "getTextElement",
"getPlaceable", "getExpression", "getSelectorExpression", "getCallArg",
"getString", "getLiteral", "getVariantList"
"getString", "getLiteral"
];

@@ -56,4 +56,4 @@ for (const name of methodNames) {

parse(source) {
const ps = new FTLParserStream(source);
ps.skipBlankLines();
const ps = new FluentParserStream(source);
ps.skipBlankBlock();

@@ -63,5 +63,5 @@ const entries = [];

while (ps.current()) {
while (ps.currentChar) {
const entry = this.getEntryOrJunk(ps);
const blankLines = ps.skipBlankLines();
const blankLines = ps.skipBlankBlock();

@@ -73,3 +73,3 @@ // Regular Comments require special logic. Comments may be attached to

// or the Term parsed successfully.
if (entry.type === "Comment" && blankLines === 0 && ps.current()) {
if (entry.type === "Comment" && blankLines === 0 && ps.currentChar) {
// Stash the comment and decide what to do with it in the next pass.

@@ -100,3 +100,3 @@ lastComment = entry;

if (this.withSpans) {
res.addSpan(0, ps.getIndex());
res.addSpan(0, ps.index);
}

@@ -117,6 +117,6 @@

parseEntry(source) {
const ps = new FTLParserStream(source);
ps.skipBlankLines();
const ps = new FluentParserStream(source);
ps.skipBlankBlock();
while (ps.currentIs("#")) {
while (ps.currentChar === "#") {
const skipped = this.getEntryOrJunk(ps);

@@ -127,3 +127,3 @@ if (skipped.type === "Junk") {

}
ps.skipBlankLines();
ps.skipBlankBlock();
}

@@ -135,3 +135,3 @@

getEntryOrJunk(ps) {
const entryStartPos = ps.getIndex();
const entryStartPos = ps.index;

@@ -147,8 +147,12 @@ try {

const errorIndex = ps.getIndex();
ps.skipToNextEntryStart();
const nextEntryStart = ps.getIndex();
let errorIndex = ps.index;
ps.skipToNextEntryStart(entryStartPos);
const nextEntryStart = ps.index;
if (nextEntryStart < errorIndex) {
// The position of the error must be inside of the Junk's span.
errorIndex = nextEntryStart;
}
// Create a Junk instance
const slice = ps.getSlice(entryStartPos, nextEntryStart);
const slice = ps.string.substring(entryStartPos, nextEntryStart);
const junk = new AST.Junk(slice);

@@ -166,7 +170,7 @@ if (this.withSpans) {

getEntry(ps) {
if (ps.currentIs("#")) {
if (ps.currentChar === "#") {
return this.getComment(ps);
}
if (ps.currentIs("-")) {
if (ps.currentChar === "-") {
return this.getTerm(ps);

@@ -191,3 +195,3 @@ }

let i = -1;
while (ps.currentIs("#") && (i < (level === -1 ? 2 : level))) {
while (ps.currentChar === "#" && (i < (level === -1 ? 2 : level))) {
ps.next();

@@ -201,6 +205,6 @@ i++;

if (!ps.currentIs("\n")) {
if (ps.currentChar !== EOL) {
ps.expectChar(" ");
let ch;
while ((ch = ps.takeChar(x => x !== "\n"))) {
while ((ch = ps.takeChar(x => x !== EOL))) {
content += ch;

@@ -210,4 +214,4 @@ }

if (ps.isPeekNextLineComment(level)) {
content += ps.current();
if (ps.isNextLineComment(level, {skip: false})) {
content += ps.currentChar;
ps.next();

@@ -237,13 +241,10 @@ } else {

ps.skipInlineWS();
ps.skipBlankInline();
ps.expectChar("=");
if (ps.isPeekValueStart()) {
ps.skipIndent();
if (ps.isValueStart({skip: true})) {
var pattern = this.getPattern(ps);
} else {
ps.skipInlineWS();
}
if (ps.isPeekNextLineAttributeStart()) {
if (ps.isNextLineAttributeStart({skip: true})) {
var attrs = this.getAttributes(ps);

@@ -262,7 +263,6 @@ }

ps.skipInlineWS();
ps.skipBlankInline();
ps.expectChar("=");
if (ps.isPeekValueStart()) {
ps.skipIndent();
if (ps.isValueStart({skip: true})) {
var value = this.getValue(ps);

@@ -273,3 +273,3 @@ } else {

if (ps.isPeekNextLineAttributeStart()) {
if (ps.isNextLineAttributeStart({skip: true})) {
var attrs = this.getAttributes(ps);

@@ -286,7 +286,6 @@ }

ps.skipInlineWS();
ps.skipBlankInline();
ps.expectChar("=");
if (ps.isPeekValueStart()) {
ps.skipIndent();
if (ps.isValueStart({skip: true})) {
const value = this.getPattern(ps);

@@ -303,7 +302,6 @@ return new AST.Attribute(key, value);

while (true) {
ps.expectIndent();
const attr = this.getAttribute(ps);
attrs.push(attr);
if (!ps.isPeekNextLineAttributeStart()) {
if (!ps.isNextLineAttributeStart({skip: true})) {
break;

@@ -334,5 +332,5 @@ }

getVariantKey(ps) {
const ch = ps.current();
const ch = ps.currentChar;
if (!ch) {
if (ch === EOF) {
throw new ParseError("E0013");

@@ -347,3 +345,3 @@ }

return this.getVariantName(ps);
return this.getIdentifier(ps);
}

@@ -354,3 +352,3 @@

if (ps.currentIs("*")) {
if (ps.currentChar === "*") {
if (hasDefault) {

@@ -366,8 +364,11 @@ throw new ParseError("E0015");

ps.skipBlank();
const key = this.getVariantKey(ps);
ps.skipBlank();
ps.expectChar("]");
if (ps.isPeekValueStart()) {
ps.skipIndent();
if (ps.isValueStart({skip: true})) {
const value = this.getValue(ps);

@@ -385,3 +386,2 @@ return new AST.Variant(key, value, defaultIndex);

while (true) {
ps.expectIndent();
const variant = this.getVariant(ps, hasDefault);

@@ -395,5 +395,6 @@

if (!ps.isPeekNextLineVariantStart()) {
if (!ps.isNextLineVariantStart({skip: false})) {
break;
}
ps.skipBlank();
}

@@ -408,17 +409,2 @@

getVariantName(ps) {
let name = ps.takeIDStart();
while (true) {
const ch = ps.takeVariantNameChar();
if (ch) {
name += ch;
} else {
break;
}
}
return new AST.VariantName(name.replace(trailingWSRe, ""));
}
getDigits(ps) {

@@ -442,3 +428,3 @@ let num = "";

if (ps.currentIs("-")) {
if (ps.currentChar === "-") {
num += "-";

@@ -450,3 +436,3 @@ ps.next();

if (ps.currentIs(".")) {
if (ps.currentChar === ".") {
num += ".";

@@ -461,8 +447,10 @@ ps.next();

getValue(ps) {
if (ps.currentIs("{")) {
if (ps.currentChar === "{") {
ps.peek();
ps.peekInlineWS();
if (ps.isPeekNextLineVariantStart()) {
ps.peekBlankInline();
if (ps.isNextLineVariantStart({skip: false})) {
return this.getVariantList(ps);
}
ps.resetPeek();
}

@@ -475,5 +463,8 @@

ps.expectChar("{");
ps.skipInlineWS();
ps.skipBlankInline();
ps.expectLineEnd();
ps.skipBlank();
const variants = this.getVariants(ps);
ps.expectIndent();
ps.expectLineEnd();
ps.skipBlank();
ps.expectChar("}");

@@ -485,10 +476,9 @@ return new AST.VariantList(variants);

const elements = [];
ps.skipInlineWS();
let ch;
while ((ch = ps.current())) {
while ((ch = ps.currentChar)) {
// The end condition for getPattern's while loop is a newline
// which is not followed by a valid pattern continuation.
if (ch === "\n" && !ps.isPeekNextLineValue()) {
if (ch === EOL && !ps.isNextLineValue({skip: false})) {
break;

@@ -510,2 +500,5 @@ }

lastElement.value = lastElement.value.replace(trailingWSRe, "");
if (lastElement.value === "") {
elements.pop();
}
}

@@ -520,3 +513,3 @@

let ch;
while ((ch = ps.current())) {
while ((ch = ps.currentChar)) {
if (ch === "{") {

@@ -526,4 +519,4 @@ return new AST.TextElement(buffer);

if (ch === "\n") {
if (!ps.isPeekNextLineValue()) {
if (ch === EOL) {
if (!ps.isNextLineValue({skip: false})) {
return new AST.TextElement(buffer);

@@ -533,6 +526,5 @@ }

ps.next();
ps.skipInlineWS();
ps.skipBlankInline();
// Add the new line to the buffer
buffer += ch;
buffer += EOL;
continue;

@@ -544,6 +536,7 @@ }

buffer += this.getEscapeSequence(ps);
} else {
buffer += ch;
ps.next();
continue;
}
buffer += ch;
ps.next();
}

@@ -555,3 +548,3 @@

getEscapeSequence(ps, specials = ["{", "\\"]) {
const next = ps.current();
const next = ps.currentChar;

@@ -570,4 +563,4 @@ if (specials.includes(next)) {

if (ch === undefined) {
throw new ParseError("E0026", sequence + ps.current());
if (!ch) {
throw new ParseError("E0026", sequence + ps.currentChar);
}

@@ -592,12 +585,11 @@

getExpression(ps) {
ps.skipInlineWS();
ps.skipBlank();
const selector = this.getSelectorExpression(ps);
ps.skipInlineWS();
ps.skipBlank();
if (ps.currentIs("-")) {
ps.peek();
if (ps.currentChar === "-") {
if (!ps.currentPeekIs(">")) {
if (ps.peek() !== ">") {
ps.resetPeek();

@@ -623,5 +615,8 @@ return selector;

ps.skipInlineWS();
ps.skipBlankInline();
ps.expectLineEnd();
ps.skipBlank();
const variants = this.getVariants(ps);
ps.skipBlank();

@@ -637,4 +632,2 @@ if (variants.length === 0) {

ps.expectIndent();
return new AST.SelectExpression(selector, variants);

@@ -646,2 +639,4 @@ } else if (selector.type === "AttributeExpression" &&

ps.skipBlank();
return selector;

@@ -651,3 +646,3 @@ }

getSelectorExpression(ps) {
if (ps.currentIs("{")) {
if (ps.currentChar === "{") {
return this.getPlaceable(ps);

@@ -662,3 +657,3 @@ }

const ch = ps.current();
const ch = ps.currentChar;

@@ -715,5 +710,5 @@ if (ch === ".") {

ps.skipInlineWS();
ps.skipBlank();
if (ps.current() !== ":") {
if (ps.currentChar !== ":") {
return exp;

@@ -727,3 +722,3 @@ }

ps.next();
ps.skipInlineWS();
ps.skipBlank();

@@ -740,7 +735,6 @@ const val = this.getArgVal(ps);

ps.skipInlineWS();
ps.skipIndent();
ps.skipBlank();
while (true) {
if (ps.current() === ")") {
if (ps.currentChar === ")") {
break;

@@ -762,9 +756,7 @@ }

ps.skipInlineWS();
ps.skipIndent();
ps.skipBlank();
if (ps.current() === ",") {
if (ps.currentChar === ",") {
ps.next();
ps.skipInlineWS();
ps.skipIndent();
ps.skipBlank();
continue;

@@ -784,3 +776,3 @@ } else {

return this.getNumber(ps);
} else if (ps.currentIs('"')) {
} else if (ps.currentChar === '"') {
return this.getString(ps);

@@ -794,6 +786,6 @@ }

ps.expectChar('"');
ps.expectChar("\"");
let ch;
while ((ch = ps.takeChar(x => x !== '"' && x !== "\n"))) {
while ((ch = ps.takeChar(x => x !== '"' && x !== EOL))) {
if (ch === "\\") {

@@ -806,7 +798,7 @@ val += this.getEscapeSequence(ps, ["{", "\\", "\""]);

if (ps.currentIs("\n")) {
if (ps.currentChar === EOL) {
throw new ParseError("E0020");
}
ps.next();
ps.expectChar("\"");

@@ -818,5 +810,5 @@ return new AST.StringLiteral(val);

getLiteral(ps) {
const ch = ps.current();
const ch = ps.currentChar;
if (!ch) {
if (ch === EOF) {
throw new ParseError("E0014");

@@ -823,0 +815,0 @@ }

@@ -307,12 +307,6 @@ import { includes } from "./util";

function serializeVariantName(VariantName) {
return VariantName.name;
}
function serializeVariantKey(key) {
switch (key.type) {
case "VariantName":
return serializeVariantName(key);
case "Identifier":
return serializeIdentifier(key);
case "NumberLiteral":

@@ -319,0 +313,0 @@ return serializeNumberLiteral(key);

@@ -0,130 +1,377 @@

/* eslint no-magic-numbers: "off" */
import { ParseError } from "./errors";
import { includes } from "./util";
export class ParserStream {
constructor(string) {
this.string = string;
this.iter = string[Symbol.iterator]();
this.buf = [];
this.peekIndex = 0;
this.index = 0;
this.peekOffset = 0;
}
this.iterEnd = false;
this.peekEnd = false;
charAt(offset) {
// When the cursor is at CRLF, return LF but don't move the cursor.
// The cursor still points to the EOL position, which in this case is the
// beginning of the compound CRLF sequence. This ensures slices of
// [inclusive, exclusive) continue to work properly.
if (this.string[offset] === "\r"
&& this.string[offset + 1] === "\n") {
return "\n";
}
this.ch = this.iter.next().value;
return this.string[offset];
}
get currentChar() {
return this.charAt(this.index);
}
get currentPeek() {
return this.charAt(this.index + this.peekOffset);
}
next() {
if (this.iterEnd) {
return undefined;
this.peekOffset = 0;
// Skip over the CRLF as if it was a single character.
if (this.string[this.index] === "\r"
&& this.string[this.index + 1] === "\n") {
this.index++;
}
this.index++;
return this.string[this.index];
}
if (this.buf.length === 0) {
this.ch = this.iter.next().value;
} else {
this.ch = this.buf.shift();
peek() {
// Skip over the CRLF as if it was a single character.
if (this.string[this.index + this.peekOffset] === "\r"
&& this.string[this.index + this.peekOffset + 1] === "\n") {
this.peekOffset++;
}
this.peekOffset++;
return this.string[this.index + this.peekOffset];
}
this.index++;
resetPeek(offset = 0) {
this.peekOffset = offset;
}
if (this.ch === undefined) {
this.iterEnd = true;
this.peekEnd = true;
skipToPeek() {
this.index += this.peekOffset;
this.peekOffset = 0;
}
}
export const EOL = "\n";
export const EOF = undefined;
const SPECIAL_LINE_START_CHARS = ["}", ".", "[", "*"];
export class FluentParserStream extends ParserStream {
skipBlankInline() {
while (this.currentChar === " ") {
this.next();
}
}
this.peekIndex = this.index;
peekBlankInline() {
while (this.currentPeek === " ") {
this.peek();
}
}
return this.ch;
skipBlankBlock() {
let lineCount = 0;
while (true) {
this.peekBlankInline();
if (this.currentPeek === EOL) {
this.next();
lineCount++;
} else {
this.resetPeek();
return lineCount;
}
}
}
current() {
return this.ch;
peekBlankBlock() {
while (true) {
const lineStart = this.peekOffset;
this.peekBlankInline();
if (this.currentPeek === EOL) {
this.peek();
} else {
this.resetPeek(lineStart);
break;
}
}
}
currentIs(ch) {
return this.ch === ch;
skipBlank() {
while (this.currentChar === " " || this.currentChar === EOL) {
this.next();
}
}
currentPeek() {
if (this.peekEnd) {
return undefined;
peekBlank() {
while (this.currentPeek === " " || this.currentPeek === EOL) {
this.peek();
}
}
const diff = this.peekIndex - this.index;
expectChar(ch) {
if (this.currentChar === ch) {
this.next();
return true;
}
if (diff === 0) {
return this.ch;
throw new ParseError("E0003", ch);
}
expectLineEnd() {
if (this.currentChar === EOF) {
// EOF is a valid line end in Fluent.
return true;
}
return this.buf[diff - 1];
if (this.currentChar === EOL) {
this.next();
return true;
}
// Unicode Character 'SYMBOL FOR NEWLINE' (U+2424)
throw new ParseError("E0003", "\u2424");
}
currentPeekIs(ch) {
return this.currentPeek() === ch;
takeChar(f) {
const ch = this.currentChar;
if (ch === EOF) {
return EOF;
}
if (f(ch)) {
this.next();
return ch;
}
return null;
}
peek() {
if (this.peekEnd) {
return undefined;
isCharIDStart(ch) {
if (ch === EOF) {
return false;
}
this.peekIndex += 1;
const cc = ch.charCodeAt(0);
return (cc >= 97 && cc <= 122) || // a-z
(cc >= 65 && cc <= 90); // A-Z
}
const diff = this.peekIndex - this.index;
isIdentifierStart() {
return this.isCharIDStart(this.currentPeek);
}
if (diff > this.buf.length) {
const ch = this.iter.next().value;
if (ch !== undefined) {
this.buf.push(ch);
} else {
this.peekEnd = true;
return undefined;
}
isNumberStart() {
const ch = this.currentChar === "-"
? this.peek()
: this.currentChar;
if (ch === EOF) {
this.resetPeek();
return false;
}
return this.buf[diff - 1];
const cc = ch.charCodeAt(0);
const isDigit = cc >= 48 && cc <= 57; // 0-9
this.resetPeek();
return isDigit;
}
getIndex() {
return this.index;
isCharPatternContinuation(ch) {
if (ch === EOF) {
return false;
}
return !includes(SPECIAL_LINE_START_CHARS, ch);
}
getPeekIndex() {
return this.peekIndex;
isValueStart({skip = true}) {
if (skip === false) throw new Error("Unimplemented");
this.peekBlankInline();
const ch = this.currentPeek;
// Inline Patterns may start with any char.
if (ch !== EOF && ch !== EOL) {
this.skipToPeek();
return true;
}
return this.isNextLineValue({skip});
}
peekCharIs(ch) {
if (this.peekEnd) {
// -1 - any
// 0 - comment
// 1 - group comment
// 2 - resource comment
isNextLineComment(level = -1, {skip = false}) {
if (skip === true) throw new Error("Unimplemented");
if (this.currentPeek !== EOL) {
return false;
}
const ret = this.peek();
let i = 0;
this.peekIndex -= 1;
while (i <= level || (level === -1 && i < 3)) {
if (this.peek() !== "#") {
if (i <= level && level !== -1) {
this.resetPeek();
return false;
}
break;
}
i++;
}
return ret === ch;
// The first char after #, ## or ###.
const ch = this.peek();
if (ch === " " || ch === EOL) {
this.resetPeek();
return true;
}
this.resetPeek();
return false;
}
resetPeek(pos) {
if (pos) {
if (pos < this.peekIndex) {
this.peekEnd = false;
isNextLineVariantStart({skip = false}) {
if (skip === true) throw new Error("Unimplemented");
if (this.currentPeek !== EOL) {
return false;
}
this.peekBlank();
if (this.currentPeek === "*") {
this.peek();
}
if (this.currentPeek === "[") {
this.resetPeek();
return true;
}
this.resetPeek();
return false;
}
isNextLineAttributeStart({skip = true}) {
if (skip === false) throw new Error("Unimplemented");
this.peekBlank();
if (this.currentPeek === ".") {
this.skipToPeek();
return true;
}
this.resetPeek();
return false;
}
isNextLineValue({skip = true}) {
if (this.currentPeek !== EOL) {
return false;
}
this.peekBlankBlock();
const ptr = this.peekOffset;
this.peekBlankInline();
if (this.currentPeek !== "{") {
if (this.peekOffset - ptr === 0) {
this.resetPeek();
return false;
}
this.peekIndex = pos;
if (!this.isCharPatternContinuation(this.currentPeek)) {
this.resetPeek();
return false;
}
}
if (skip) {
this.skipToPeek();
} else {
this.peekIndex = this.index;
this.peekEnd = this.iterEnd;
this.resetPeek();
}
return true;
}
skipToPeek() {
const diff = this.peekIndex - this.index;
skipToNextEntryStart(junkStart) {
let lastNewline = this.string.lastIndexOf(EOL, this.index);
if (junkStart < lastNewline) {
// Last seen newline is _after_ the junk start. It's safe to rewind
// without the risk of resuming at the same broken entry.
this.index = lastNewline;
}
while (this.currentChar) {
// We're only interested in beginnings of line.
if (this.currentChar !== EOL) {
this.next();
continue;
}
for (let i = 0; i < diff; i++) {
this.ch = this.buf.shift();
// Break if the first char in this line looks like an entry start.
const first = this.next();
if (this.isCharIDStart(first) || first === "-" || first === "#") {
break;
}
}
}
this.index = this.peekIndex;
takeIDStart() {
if (this.isCharIDStart(this.currentChar)) {
const ret = this.currentChar;
this.next();
return ret;
}
throw new ParseError("E0004", "a-zA-Z");
}
getSlice(start, end) {
return this.string.substring(start, end);
takeIDChar() {
const closure = ch => {
const cc = ch.charCodeAt(0);
return ((cc >= 97 && cc <= 122) || // a-z
(cc >= 65 && cc <= 90) || // A-Z
(cc >= 48 && cc <= 57) || // 0-9
cc === 95 || cc === 45); // _-
};
return this.takeChar(closure);
}
takeDigit() {
const closure = ch => {
const cc = ch.charCodeAt(0);
return (cc >= 48 && cc <= 57); // 0-9
};
return this.takeChar(closure);
}
takeHexDigit() {
const closure = ch => {
const cc = ch.charCodeAt(0);
return (cc >= 48 && cc <= 57) // 0-9
|| (cc >= 65 && cc <= 70) // A-F
|| (cc >= 97 && cc <= 102); // a-f
};
return this.takeChar(closure);
}
}
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