1280 lines
48 KiB
Plaintext
Executable File
1280 lines
48 KiB
Plaintext
Executable File
"use strict";
|
|
var _a;
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.Parser = void 0;
|
|
var tslib_1 = require("tslib");
|
|
var error_1 = require("./error");
|
|
var types_1 = require("./types");
|
|
var regex_generated_1 = require("./regex.generated");
|
|
var icu_skeleton_parser_1 = require("@formatjs/icu-skeleton-parser");
|
|
var date_time_pattern_generator_1 = require("./date-time-pattern-generator");
|
|
var SPACE_SEPARATOR_START_REGEX = new RegExp("^".concat(regex_generated_1.SPACE_SEPARATOR_REGEX.source, "*"));
|
|
var SPACE_SEPARATOR_END_REGEX = new RegExp("".concat(regex_generated_1.SPACE_SEPARATOR_REGEX.source, "*$"));
|
|
function createLocation(start, end) {
|
|
return { start: start, end: end };
|
|
}
|
|
// #region Ponyfills
|
|
// Consolidate these variables up top for easier toggling during debugging
|
|
var hasNativeStartsWith = !!String.prototype.startsWith;
|
|
var hasNativeFromCodePoint = !!String.fromCodePoint;
|
|
var hasNativeFromEntries = !!Object.fromEntries;
|
|
var hasNativeCodePointAt = !!String.prototype.codePointAt;
|
|
var hasTrimStart = !!String.prototype.trimStart;
|
|
var hasTrimEnd = !!String.prototype.trimEnd;
|
|
var hasNativeIsSafeInteger = !!Number.isSafeInteger;
|
|
var isSafeInteger = hasNativeIsSafeInteger
|
|
? Number.isSafeInteger
|
|
: function (n) {
|
|
return (typeof n === 'number' &&
|
|
isFinite(n) &&
|
|
Math.floor(n) === n &&
|
|
Math.abs(n) <= 0x1fffffffffffff);
|
|
};
|
|
// IE11 does not support y and u.
|
|
var REGEX_SUPPORTS_U_AND_Y = true;
|
|
try {
|
|
var re = RE('([^\\p{White_Space}\\p{Pattern_Syntax}]*)', 'yu');
|
|
/**
|
|
* legacy Edge or Xbox One browser
|
|
* Unicode flag support: supported
|
|
* Pattern_Syntax support: not supported
|
|
* See https://github.com/formatjs/formatjs/issues/2822
|
|
*/
|
|
REGEX_SUPPORTS_U_AND_Y = ((_a = re.exec('a')) === null || _a === void 0 ? void 0 : _a[0]) === 'a';
|
|
}
|
|
catch (_) {
|
|
REGEX_SUPPORTS_U_AND_Y = false;
|
|
}
|
|
var startsWith = hasNativeStartsWith
|
|
? // Native
|
|
function startsWith(s, search, position) {
|
|
return s.startsWith(search, position);
|
|
}
|
|
: // For IE11
|
|
function startsWith(s, search, position) {
|
|
return s.slice(position, position + search.length) === search;
|
|
};
|
|
var fromCodePoint = hasNativeFromCodePoint
|
|
? String.fromCodePoint
|
|
: // IE11
|
|
function fromCodePoint() {
|
|
var codePoints = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
codePoints[_i] = arguments[_i];
|
|
}
|
|
var elements = '';
|
|
var length = codePoints.length;
|
|
var i = 0;
|
|
var code;
|
|
while (length > i) {
|
|
code = codePoints[i++];
|
|
if (code > 0x10ffff)
|
|
throw RangeError(code + ' is not a valid code point');
|
|
elements +=
|
|
code < 0x10000
|
|
? String.fromCharCode(code)
|
|
: String.fromCharCode(((code -= 0x10000) >> 10) + 0xd800, (code % 0x400) + 0xdc00);
|
|
}
|
|
return elements;
|
|
};
|
|
var fromEntries =
|
|
// native
|
|
hasNativeFromEntries
|
|
? Object.fromEntries
|
|
: // Ponyfill
|
|
function fromEntries(entries) {
|
|
var obj = {};
|
|
for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
|
|
var _a = entries_1[_i], k = _a[0], v = _a[1];
|
|
obj[k] = v;
|
|
}
|
|
return obj;
|
|
};
|
|
var codePointAt = hasNativeCodePointAt
|
|
? // Native
|
|
function codePointAt(s, index) {
|
|
return s.codePointAt(index);
|
|
}
|
|
: // IE 11
|
|
function codePointAt(s, index) {
|
|
var size = s.length;
|
|
if (index < 0 || index >= size) {
|
|
return undefined;
|
|
}
|
|
var first = s.charCodeAt(index);
|
|
var second;
|
|
return first < 0xd800 ||
|
|
first > 0xdbff ||
|
|
index + 1 === size ||
|
|
(second = s.charCodeAt(index + 1)) < 0xdc00 ||
|
|
second > 0xdfff
|
|
? first
|
|
: ((first - 0xd800) << 10) + (second - 0xdc00) + 0x10000;
|
|
};
|
|
var trimStart = hasTrimStart
|
|
? // Native
|
|
function trimStart(s) {
|
|
return s.trimStart();
|
|
}
|
|
: // Ponyfill
|
|
function trimStart(s) {
|
|
return s.replace(SPACE_SEPARATOR_START_REGEX, '');
|
|
};
|
|
var trimEnd = hasTrimEnd
|
|
? // Native
|
|
function trimEnd(s) {
|
|
return s.trimEnd();
|
|
}
|
|
: // Ponyfill
|
|
function trimEnd(s) {
|
|
return s.replace(SPACE_SEPARATOR_END_REGEX, '');
|
|
};
|
|
// Prevent minifier to translate new RegExp to literal form that might cause syntax error on IE11.
|
|
function RE(s, flag) {
|
|
return new RegExp(s, flag);
|
|
}
|
|
// #endregion
|
|
var matchIdentifierAtIndex;
|
|
if (REGEX_SUPPORTS_U_AND_Y) {
|
|
// Native
|
|
var IDENTIFIER_PREFIX_RE_1 = RE('([^\\p{White_Space}\\p{Pattern_Syntax}]*)', 'yu');
|
|
matchIdentifierAtIndex = function matchIdentifierAtIndex(s, index) {
|
|
var _a;
|
|
IDENTIFIER_PREFIX_RE_1.lastIndex = index;
|
|
var match = IDENTIFIER_PREFIX_RE_1.exec(s);
|
|
return (_a = match[1]) !== null && _a !== void 0 ? _a : '';
|
|
};
|
|
}
|
|
else {
|
|
// IE11
|
|
matchIdentifierAtIndex = function matchIdentifierAtIndex(s, index) {
|
|
var match = [];
|
|
while (true) {
|
|
var c = codePointAt(s, index);
|
|
if (c === undefined || _isWhiteSpace(c) || _isPatternSyntax(c)) {
|
|
break;
|
|
}
|
|
match.push(c);
|
|
index += c >= 0x10000 ? 2 : 1;
|
|
}
|
|
return fromCodePoint.apply(void 0, match);
|
|
};
|
|
}
|
|
var Parser = /** @class */ (function () {
|
|
function Parser(message, options) {
|
|
if (options === void 0) { options = {}; }
|
|
this.message = message;
|
|
this.position = { offset: 0, line: 1, column: 1 };
|
|
this.ignoreTag = !!options.ignoreTag;
|
|
this.locale = options.locale;
|
|
this.requiresOtherClause = !!options.requiresOtherClause;
|
|
this.shouldParseSkeletons = !!options.shouldParseSkeletons;
|
|
}
|
|
Parser.prototype.parse = function () {
|
|
if (this.offset() !== 0) {
|
|
throw Error('parser can only be used once');
|
|
}
|
|
return this.parseMessage(0, '', false);
|
|
};
|
|
Parser.prototype.parseMessage = function (nestingLevel, parentArgType, expectingCloseTag) {
|
|
var elements = [];
|
|
while (!this.isEOF()) {
|
|
var char = this.char();
|
|
if (char === 123 /* `{` */) {
|
|
var result = this.parseArgument(nestingLevel, expectingCloseTag);
|
|
if (result.err) {
|
|
return result;
|
|
}
|
|
elements.push(result.val);
|
|
}
|
|
else if (char === 125 /* `}` */ && nestingLevel > 0) {
|
|
break;
|
|
}
|
|
else if (char === 35 /* `#` */ &&
|
|
(parentArgType === 'plural' || parentArgType === 'selectordinal')) {
|
|
var position = this.clonePosition();
|
|
this.bump();
|
|
elements.push({
|
|
type: types_1.TYPE.pound,
|
|
location: createLocation(position, this.clonePosition()),
|
|
});
|
|
}
|
|
else if (char === 60 /* `<` */ &&
|
|
!this.ignoreTag &&
|
|
this.peek() === 47 // char code for '/'
|
|
) {
|
|
if (expectingCloseTag) {
|
|
break;
|
|
}
|
|
else {
|
|
return this.error(error_1.ErrorKind.UNMATCHED_CLOSING_TAG, createLocation(this.clonePosition(), this.clonePosition()));
|
|
}
|
|
}
|
|
else if (char === 60 /* `<` */ &&
|
|
!this.ignoreTag &&
|
|
_isAlpha(this.peek() || 0)) {
|
|
var result = this.parseTag(nestingLevel, parentArgType);
|
|
if (result.err) {
|
|
return result;
|
|
}
|
|
elements.push(result.val);
|
|
}
|
|
else {
|
|
var result = this.parseLiteral(nestingLevel, parentArgType);
|
|
if (result.err) {
|
|
return result;
|
|
}
|
|
elements.push(result.val);
|
|
}
|
|
}
|
|
return { val: elements, err: null };
|
|
};
|
|
/**
|
|
* A tag name must start with an ASCII lower/upper case letter. The grammar is based on the
|
|
* [custom element name][] except that a dash is NOT always mandatory and uppercase letters
|
|
* are accepted:
|
|
*
|
|
* ```
|
|
* tag ::= "<" tagName (whitespace)* "/>" | "<" tagName (whitespace)* ">" message "</" tagName (whitespace)* ">"
|
|
* tagName ::= [a-z] (PENChar)*
|
|
* PENChar ::=
|
|
* "-" | "." | [0-9] | "_" | [a-z] | [A-Z] | #xB7 | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x37D] |
|
|
* [#x37F-#x1FFF] | [#x200C-#x200D] | [#x203F-#x2040] | [#x2070-#x218F] | [#x2C00-#x2FEF] |
|
|
* [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
|
|
* ```
|
|
*
|
|
* [custom element name]: https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
|
|
* NOTE: We're a bit more lax here since HTML technically does not allow uppercase HTML element but we do
|
|
* since other tag-based engines like React allow it
|
|
*/
|
|
Parser.prototype.parseTag = function (nestingLevel, parentArgType) {
|
|
var startPosition = this.clonePosition();
|
|
this.bump(); // `<`
|
|
var tagName = this.parseTagName();
|
|
this.bumpSpace();
|
|
if (this.bumpIf('/>')) {
|
|
// Self closing tag
|
|
return {
|
|
val: {
|
|
type: types_1.TYPE.literal,
|
|
value: "<".concat(tagName, "/>"),
|
|
location: createLocation(startPosition, this.clonePosition()),
|
|
},
|
|
err: null,
|
|
};
|
|
}
|
|
else if (this.bumpIf('>')) {
|
|
var childrenResult = this.parseMessage(nestingLevel + 1, parentArgType, true);
|
|
if (childrenResult.err) {
|
|
return childrenResult;
|
|
}
|
|
var children = childrenResult.val;
|
|
// Expecting a close tag
|
|
var endTagStartPosition = this.clonePosition();
|
|
if (this.bumpIf('</')) {
|
|
if (this.isEOF() || !_isAlpha(this.char())) {
|
|
return this.error(error_1.ErrorKind.INVALID_TAG, createLocation(endTagStartPosition, this.clonePosition()));
|
|
}
|
|
var closingTagNameStartPosition = this.clonePosition();
|
|
var closingTagName = this.parseTagName();
|
|
if (tagName !== closingTagName) {
|
|
return this.error(error_1.ErrorKind.UNMATCHED_CLOSING_TAG, createLocation(closingTagNameStartPosition, this.clonePosition()));
|
|
}
|
|
this.bumpSpace();
|
|
if (!this.bumpIf('>')) {
|
|
return this.error(error_1.ErrorKind.INVALID_TAG, createLocation(endTagStartPosition, this.clonePosition()));
|
|
}
|
|
return {
|
|
val: {
|
|
type: types_1.TYPE.tag,
|
|
value: tagName,
|
|
children: children,
|
|
location: createLocation(startPosition, this.clonePosition()),
|
|
},
|
|
err: null,
|
|
};
|
|
}
|
|
else {
|
|
return this.error(error_1.ErrorKind.UNCLOSED_TAG, createLocation(startPosition, this.clonePosition()));
|
|
}
|
|
}
|
|
else {
|
|
return this.error(error_1.ErrorKind.INVALID_TAG, createLocation(startPosition, this.clonePosition()));
|
|
}
|
|
};
|
|
/**
|
|
* This method assumes that the caller has peeked ahead for the first tag character.
|
|
*/
|
|
Parser.prototype.parseTagName = function () {
|
|
var startOffset = this.offset();
|
|
this.bump(); // the first tag name character
|
|
while (!this.isEOF() && _isPotentialElementNameChar(this.char())) {
|
|
this.bump();
|
|
}
|
|
return this.message.slice(startOffset, this.offset());
|
|
};
|
|
Parser.prototype.parseLiteral = function (nestingLevel, parentArgType) {
|
|
var start = this.clonePosition();
|
|
var value = '';
|
|
while (true) {
|
|
var parseQuoteResult = this.tryParseQuote(parentArgType);
|
|
if (parseQuoteResult) {
|
|
value += parseQuoteResult;
|
|
continue;
|
|
}
|
|
var parseUnquotedResult = this.tryParseUnquoted(nestingLevel, parentArgType);
|
|
if (parseUnquotedResult) {
|
|
value += parseUnquotedResult;
|
|
continue;
|
|
}
|
|
var parseLeftAngleResult = this.tryParseLeftAngleBracket();
|
|
if (parseLeftAngleResult) {
|
|
value += parseLeftAngleResult;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
var location = createLocation(start, this.clonePosition());
|
|
return {
|
|
val: { type: types_1.TYPE.literal, value: value, location: location },
|
|
err: null,
|
|
};
|
|
};
|
|
Parser.prototype.tryParseLeftAngleBracket = function () {
|
|
if (!this.isEOF() &&
|
|
this.char() === 60 /* `<` */ &&
|
|
(this.ignoreTag ||
|
|
// If at the opening tag or closing tag position, bail.
|
|
!_isAlphaOrSlash(this.peek() || 0))) {
|
|
this.bump(); // `<`
|
|
return '<';
|
|
}
|
|
return null;
|
|
};
|
|
/**
|
|
* Starting with ICU 4.8, an ASCII apostrophe only starts quoted text if it immediately precedes
|
|
* a character that requires quoting (that is, "only where needed"), and works the same in
|
|
* nested messages as on the top level of the pattern. The new behavior is otherwise compatible.
|
|
*/
|
|
Parser.prototype.tryParseQuote = function (parentArgType) {
|
|
if (this.isEOF() || this.char() !== 39 /* `'` */) {
|
|
return null;
|
|
}
|
|
// Parse escaped char following the apostrophe, or early return if there is no escaped char.
|
|
// Check if is valid escaped character
|
|
switch (this.peek()) {
|
|
case 39 /* `'` */:
|
|
// double quote, should return as a single quote.
|
|
this.bump();
|
|
this.bump();
|
|
return "'";
|
|
// '{', '<', '>', '}'
|
|
case 123:
|
|
case 60:
|
|
case 62:
|
|
case 125:
|
|
break;
|
|
case 35: // '#'
|
|
if (parentArgType === 'plural' || parentArgType === 'selectordinal') {
|
|
break;
|
|
}
|
|
return null;
|
|
default:
|
|
return null;
|
|
}
|
|
this.bump(); // apostrophe
|
|
var codePoints = [this.char()]; // escaped char
|
|
this.bump();
|
|
// read chars until the optional closing apostrophe is found
|
|
while (!this.isEOF()) {
|
|
var ch = this.char();
|
|
if (ch === 39 /* `'` */) {
|
|
if (this.peek() === 39 /* `'` */) {
|
|
codePoints.push(39);
|
|
// Bump one more time because we need to skip 2 characters.
|
|
this.bump();
|
|
}
|
|
else {
|
|
// Optional closing apostrophe.
|
|
this.bump();
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
codePoints.push(ch);
|
|
}
|
|
this.bump();
|
|
}
|
|
return fromCodePoint.apply(void 0, codePoints);
|
|
};
|
|
Parser.prototype.tryParseUnquoted = function (nestingLevel, parentArgType) {
|
|
if (this.isEOF()) {
|
|
return null;
|
|
}
|
|
var ch = this.char();
|
|
if (ch === 60 /* `<` */ ||
|
|
ch === 123 /* `{` */ ||
|
|
(ch === 35 /* `#` */ &&
|
|
(parentArgType === 'plural' || parentArgType === 'selectordinal')) ||
|
|
(ch === 125 /* `}` */ && nestingLevel > 0)) {
|
|
return null;
|
|
}
|
|
else {
|
|
this.bump();
|
|
return fromCodePoint(ch);
|
|
}
|
|
};
|
|
Parser.prototype.parseArgument = function (nestingLevel, expectingCloseTag) {
|
|
var openingBracePosition = this.clonePosition();
|
|
this.bump(); // `{`
|
|
this.bumpSpace();
|
|
if (this.isEOF()) {
|
|
return this.error(error_1.ErrorKind.EXPECT_ARGUMENT_CLOSING_BRACE, createLocation(openingBracePosition, this.clonePosition()));
|
|
}
|
|
if (this.char() === 125 /* `}` */) {
|
|
this.bump();
|
|
return this.error(error_1.ErrorKind.EMPTY_ARGUMENT, createLocation(openingBracePosition, this.clonePosition()));
|
|
}
|
|
// argument name
|
|
var value = this.parseIdentifierIfPossible().value;
|
|
if (!value) {
|
|
return this.error(error_1.ErrorKind.MALFORMED_ARGUMENT, createLocation(openingBracePosition, this.clonePosition()));
|
|
}
|
|
this.bumpSpace();
|
|
if (this.isEOF()) {
|
|
return this.error(error_1.ErrorKind.EXPECT_ARGUMENT_CLOSING_BRACE, createLocation(openingBracePosition, this.clonePosition()));
|
|
}
|
|
switch (this.char()) {
|
|
// Simple argument: `{name}`
|
|
case 125 /* `}` */: {
|
|
this.bump(); // `}`
|
|
return {
|
|
val: {
|
|
type: types_1.TYPE.argument,
|
|
// value does not include the opening and closing braces.
|
|
value: value,
|
|
location: createLocation(openingBracePosition, this.clonePosition()),
|
|
},
|
|
err: null,
|
|
};
|
|
}
|
|
// Argument with options: `{name, format, ...}`
|
|
case 44 /* `,` */: {
|
|
this.bump(); // `,`
|
|
this.bumpSpace();
|
|
if (this.isEOF()) {
|
|
return this.error(error_1.ErrorKind.EXPECT_ARGUMENT_CLOSING_BRACE, createLocation(openingBracePosition, this.clonePosition()));
|
|
}
|
|
return this.parseArgumentOptions(nestingLevel, expectingCloseTag, value, openingBracePosition);
|
|
}
|
|
default:
|
|
return this.error(error_1.ErrorKind.MALFORMED_ARGUMENT, createLocation(openingBracePosition, this.clonePosition()));
|
|
}
|
|
};
|
|
/**
|
|
* Advance the parser until the end of the identifier, if it is currently on
|
|
* an identifier character. Return an empty string otherwise.
|
|
*/
|
|
Parser.prototype.parseIdentifierIfPossible = function () {
|
|
var startingPosition = this.clonePosition();
|
|
var startOffset = this.offset();
|
|
var value = matchIdentifierAtIndex(this.message, startOffset);
|
|
var endOffset = startOffset + value.length;
|
|
this.bumpTo(endOffset);
|
|
var endPosition = this.clonePosition();
|
|
var location = createLocation(startingPosition, endPosition);
|
|
return { value: value, location: location };
|
|
};
|
|
Parser.prototype.parseArgumentOptions = function (nestingLevel, expectingCloseTag, value, openingBracePosition) {
|
|
var _a;
|
|
// Parse this range:
|
|
// {name, type, style}
|
|
// ^---^
|
|
var typeStartPosition = this.clonePosition();
|
|
var argType = this.parseIdentifierIfPossible().value;
|
|
var typeEndPosition = this.clonePosition();
|
|
switch (argType) {
|
|
case '':
|
|
// Expecting a style string number, date, time, plural, selectordinal, or select.
|
|
return this.error(error_1.ErrorKind.EXPECT_ARGUMENT_TYPE, createLocation(typeStartPosition, typeEndPosition));
|
|
case 'number':
|
|
case 'date':
|
|
case 'time': {
|
|
// Parse this range:
|
|
// {name, number, style}
|
|
// ^-------^
|
|
this.bumpSpace();
|
|
var styleAndLocation = null;
|
|
if (this.bumpIf(',')) {
|
|
this.bumpSpace();
|
|
var styleStartPosition = this.clonePosition();
|
|
var result = this.parseSimpleArgStyleIfPossible();
|
|
if (result.err) {
|
|
return result;
|
|
}
|
|
var style = trimEnd(result.val);
|
|
if (style.length === 0) {
|
|
return this.error(error_1.ErrorKind.EXPECT_ARGUMENT_STYLE, createLocation(this.clonePosition(), this.clonePosition()));
|
|
}
|
|
var styleLocation = createLocation(styleStartPosition, this.clonePosition());
|
|
styleAndLocation = { style: style, styleLocation: styleLocation };
|
|
}
|
|
var argCloseResult = this.tryParseArgumentClose(openingBracePosition);
|
|
if (argCloseResult.err) {
|
|
return argCloseResult;
|
|
}
|
|
var location_1 = createLocation(openingBracePosition, this.clonePosition());
|
|
// Extract style or skeleton
|
|
if (styleAndLocation && startsWith(styleAndLocation === null || styleAndLocation === void 0 ? void 0 : styleAndLocation.style, '::', 0)) {
|
|
// Skeleton starts with `::`.
|
|
var skeleton = trimStart(styleAndLocation.style.slice(2));
|
|
if (argType === 'number') {
|
|
var result = this.parseNumberSkeletonFromString(skeleton, styleAndLocation.styleLocation);
|
|
if (result.err) {
|
|
return result;
|
|
}
|
|
return {
|
|
val: { type: types_1.TYPE.number, value: value, location: location_1, style: result.val },
|
|
err: null,
|
|
};
|
|
}
|
|
else {
|
|
if (skeleton.length === 0) {
|
|
return this.error(error_1.ErrorKind.EXPECT_DATE_TIME_SKELETON, location_1);
|
|
}
|
|
var dateTimePattern = skeleton;
|
|
// Get "best match" pattern only if locale is passed, if not, let it
|
|
// pass as-is where `parseDateTimeSkeleton()` will throw an error
|
|
// for unsupported patterns.
|
|
if (this.locale) {
|
|
dateTimePattern = (0, date_time_pattern_generator_1.getBestPattern)(skeleton, this.locale);
|
|
}
|
|
var style = {
|
|
type: types_1.SKELETON_TYPE.dateTime,
|
|
pattern: dateTimePattern,
|
|
location: styleAndLocation.styleLocation,
|
|
parsedOptions: this.shouldParseSkeletons
|
|
? (0, icu_skeleton_parser_1.parseDateTimeSkeleton)(dateTimePattern)
|
|
: {},
|
|
};
|
|
var type = argType === 'date' ? types_1.TYPE.date : types_1.TYPE.time;
|
|
return {
|
|
val: { type: type, value: value, location: location_1, style: style },
|
|
err: null,
|
|
};
|
|
}
|
|
}
|
|
// Regular style or no style.
|
|
return {
|
|
val: {
|
|
type: argType === 'number'
|
|
? types_1.TYPE.number
|
|
: argType === 'date'
|
|
? types_1.TYPE.date
|
|
: types_1.TYPE.time,
|
|
value: value,
|
|
location: location_1,
|
|
style: (_a = styleAndLocation === null || styleAndLocation === void 0 ? void 0 : styleAndLocation.style) !== null && _a !== void 0 ? _a : null,
|
|
},
|
|
err: null,
|
|
};
|
|
}
|
|
case 'plural':
|
|
case 'selectordinal':
|
|
case 'select': {
|
|
// Parse this range:
|
|
// {name, plural, options}
|
|
// ^---------^
|
|
var typeEndPosition_1 = this.clonePosition();
|
|
this.bumpSpace();
|
|
if (!this.bumpIf(',')) {
|
|
return this.error(error_1.ErrorKind.EXPECT_SELECT_ARGUMENT_OPTIONS, createLocation(typeEndPosition_1, (0, tslib_1.__assign)({}, typeEndPosition_1)));
|
|
}
|
|
this.bumpSpace();
|
|
// Parse offset:
|
|
// {name, plural, offset:1, options}
|
|
// ^-----^
|
|
//
|
|
// or the first option:
|
|
//
|
|
// {name, plural, one {...} other {...}}
|
|
// ^--^
|
|
var identifierAndLocation = this.parseIdentifierIfPossible();
|
|
var pluralOffset = 0;
|
|
if (argType !== 'select' && identifierAndLocation.value === 'offset') {
|
|
if (!this.bumpIf(':')) {
|
|
return this.error(error_1.ErrorKind.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE, createLocation(this.clonePosition(), this.clonePosition()));
|
|
}
|
|
this.bumpSpace();
|
|
var result = this.tryParseDecimalInteger(error_1.ErrorKind.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE, error_1.ErrorKind.INVALID_PLURAL_ARGUMENT_OFFSET_VALUE);
|
|
if (result.err) {
|
|
return result;
|
|
}
|
|
// Parse another identifier for option parsing
|
|
this.bumpSpace();
|
|
identifierAndLocation = this.parseIdentifierIfPossible();
|
|
pluralOffset = result.val;
|
|
}
|
|
var optionsResult = this.tryParsePluralOrSelectOptions(nestingLevel, argType, expectingCloseTag, identifierAndLocation);
|
|
if (optionsResult.err) {
|
|
return optionsResult;
|
|
}
|
|
var argCloseResult = this.tryParseArgumentClose(openingBracePosition);
|
|
if (argCloseResult.err) {
|
|
return argCloseResult;
|
|
}
|
|
var location_2 = createLocation(openingBracePosition, this.clonePosition());
|
|
if (argType === 'select') {
|
|
return {
|
|
val: {
|
|
type: types_1.TYPE.select,
|
|
value: value,
|
|
options: fromEntries(optionsResult.val),
|
|
location: location_2,
|
|
},
|
|
err: null,
|
|
};
|
|
}
|
|
else {
|
|
return {
|
|
val: {
|
|
type: types_1.TYPE.plural,
|
|
value: value,
|
|
options: fromEntries(optionsResult.val),
|
|
offset: pluralOffset,
|
|
pluralType: argType === 'plural' ? 'cardinal' : 'ordinal',
|
|
location: location_2,
|
|
},
|
|
err: null,
|
|
};
|
|
}
|
|
}
|
|
default:
|
|
return this.error(error_1.ErrorKind.INVALID_ARGUMENT_TYPE, createLocation(typeStartPosition, typeEndPosition));
|
|
}
|
|
};
|
|
Parser.prototype.tryParseArgumentClose = function (openingBracePosition) {
|
|
// Parse: {value, number, ::currency/GBP }
|
|
//
|
|
if (this.isEOF() || this.char() !== 125 /* `}` */) {
|
|
return this.error(error_1.ErrorKind.EXPECT_ARGUMENT_CLOSING_BRACE, createLocation(openingBracePosition, this.clonePosition()));
|
|
}
|
|
this.bump(); // `}`
|
|
return { val: true, err: null };
|
|
};
|
|
/**
|
|
* See: https://github.com/unicode-org/icu/blob/af7ed1f6d2298013dc303628438ec4abe1f16479/icu4c/source/common/messagepattern.cpp#L659
|
|
*/
|
|
Parser.prototype.parseSimpleArgStyleIfPossible = function () {
|
|
var nestedBraces = 0;
|
|
var startPosition = this.clonePosition();
|
|
while (!this.isEOF()) {
|
|
var ch = this.char();
|
|
switch (ch) {
|
|
case 39 /* `'` */: {
|
|
// Treat apostrophe as quoting but include it in the style part.
|
|
// Find the end of the quoted literal text.
|
|
this.bump();
|
|
var apostrophePosition = this.clonePosition();
|
|
if (!this.bumpUntil("'")) {
|
|
return this.error(error_1.ErrorKind.UNCLOSED_QUOTE_IN_ARGUMENT_STYLE, createLocation(apostrophePosition, this.clonePosition()));
|
|
}
|
|
this.bump();
|
|
break;
|
|
}
|
|
case 123 /* `{` */: {
|
|
nestedBraces += 1;
|
|
this.bump();
|
|
break;
|
|
}
|
|
case 125 /* `}` */: {
|
|
if (nestedBraces > 0) {
|
|
nestedBraces -= 1;
|
|
}
|
|
else {
|
|
return {
|
|
val: this.message.slice(startPosition.offset, this.offset()),
|
|
err: null,
|
|
};
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
this.bump();
|
|
break;
|
|
}
|
|
}
|
|
return {
|
|
val: this.message.slice(startPosition.offset, this.offset()),
|
|
err: null,
|
|
};
|
|
};
|
|
Parser.prototype.parseNumberSkeletonFromString = function (skeleton, location) {
|
|
var tokens = [];
|
|
try {
|
|
tokens = (0, icu_skeleton_parser_1.parseNumberSkeletonFromString)(skeleton);
|
|
}
|
|
catch (e) {
|
|
return this.error(error_1.ErrorKind.INVALID_NUMBER_SKELETON, location);
|
|
}
|
|
return {
|
|
val: {
|
|
type: types_1.SKELETON_TYPE.number,
|
|
tokens: tokens,
|
|
location: location,
|
|
parsedOptions: this.shouldParseSkeletons
|
|
? (0, icu_skeleton_parser_1.parseNumberSkeleton)(tokens)
|
|
: {},
|
|
},
|
|
err: null,
|
|
};
|
|
};
|
|
/**
|
|
* @param nesting_level The current nesting level of messages.
|
|
* This can be positive when parsing message fragment in select or plural argument options.
|
|
* @param parent_arg_type The parent argument's type.
|
|
* @param parsed_first_identifier If provided, this is the first identifier-like selector of
|
|
* the argument. It is a by-product of a previous parsing attempt.
|
|
* @param expecting_close_tag If true, this message is directly or indirectly nested inside
|
|
* between a pair of opening and closing tags. The nested message will not parse beyond
|
|
* the closing tag boundary.
|
|
*/
|
|
Parser.prototype.tryParsePluralOrSelectOptions = function (nestingLevel, parentArgType, expectCloseTag, parsedFirstIdentifier) {
|
|
var _a;
|
|
var hasOtherClause = false;
|
|
var options = [];
|
|
var parsedSelectors = new Set();
|
|
var selector = parsedFirstIdentifier.value, selectorLocation = parsedFirstIdentifier.location;
|
|
// Parse:
|
|
// one {one apple}
|
|
// ^--^
|
|
while (true) {
|
|
if (selector.length === 0) {
|
|
var startPosition = this.clonePosition();
|
|
if (parentArgType !== 'select' && this.bumpIf('=')) {
|
|
// Try parse `={number}` selector
|
|
var result = this.tryParseDecimalInteger(error_1.ErrorKind.EXPECT_PLURAL_ARGUMENT_SELECTOR, error_1.ErrorKind.INVALID_PLURAL_ARGUMENT_SELECTOR);
|
|
if (result.err) {
|
|
return result;
|
|
}
|
|
selectorLocation = createLocation(startPosition, this.clonePosition());
|
|
selector = this.message.slice(startPosition.offset, this.offset());
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
// Duplicate selector clauses
|
|
if (parsedSelectors.has(selector)) {
|
|
return this.error(parentArgType === 'select'
|
|
? error_1.ErrorKind.DUPLICATE_SELECT_ARGUMENT_SELECTOR
|
|
: error_1.ErrorKind.DUPLICATE_PLURAL_ARGUMENT_SELECTOR, selectorLocation);
|
|
}
|
|
if (selector === 'other') {
|
|
hasOtherClause = true;
|
|
}
|
|
// Parse:
|
|
// one {one apple}
|
|
// ^----------^
|
|
this.bumpSpace();
|
|
var openingBracePosition = this.clonePosition();
|
|
if (!this.bumpIf('{')) {
|
|
return this.error(parentArgType === 'select'
|
|
? error_1.ErrorKind.EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT
|
|
: error_1.ErrorKind.EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT, createLocation(this.clonePosition(), this.clonePosition()));
|
|
}
|
|
var fragmentResult = this.parseMessage(nestingLevel + 1, parentArgType, expectCloseTag);
|
|
if (fragmentResult.err) {
|
|
return fragmentResult;
|
|
}
|
|
var argCloseResult = this.tryParseArgumentClose(openingBracePosition);
|
|
if (argCloseResult.err) {
|
|
return argCloseResult;
|
|
}
|
|
options.push([
|
|
selector,
|
|
{
|
|
value: fragmentResult.val,
|
|
location: createLocation(openingBracePosition, this.clonePosition()),
|
|
},
|
|
]);
|
|
// Keep track of the existing selectors
|
|
parsedSelectors.add(selector);
|
|
// Prep next selector clause.
|
|
this.bumpSpace();
|
|
(_a = this.parseIdentifierIfPossible(), selector = _a.value, selectorLocation = _a.location);
|
|
}
|
|
if (options.length === 0) {
|
|
return this.error(parentArgType === 'select'
|
|
? error_1.ErrorKind.EXPECT_SELECT_ARGUMENT_SELECTOR
|
|
: error_1.ErrorKind.EXPECT_PLURAL_ARGUMENT_SELECTOR, createLocation(this.clonePosition(), this.clonePosition()));
|
|
}
|
|
if (this.requiresOtherClause && !hasOtherClause) {
|
|
return this.error(error_1.ErrorKind.MISSING_OTHER_CLAUSE, createLocation(this.clonePosition(), this.clonePosition()));
|
|
}
|
|
return { val: options, err: null };
|
|
};
|
|
Parser.prototype.tryParseDecimalInteger = function (expectNumberError, invalidNumberError) {
|
|
var sign = 1;
|
|
var startingPosition = this.clonePosition();
|
|
if (this.bumpIf('+')) {
|
|
}
|
|
else if (this.bumpIf('-')) {
|
|
sign = -1;
|
|
}
|
|
var hasDigits = false;
|
|
var decimal = 0;
|
|
while (!this.isEOF()) {
|
|
var ch = this.char();
|
|
if (ch >= 48 /* `0` */ && ch <= 57 /* `9` */) {
|
|
hasDigits = true;
|
|
decimal = decimal * 10 + (ch - 48);
|
|
this.bump();
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
var location = createLocation(startingPosition, this.clonePosition());
|
|
if (!hasDigits) {
|
|
return this.error(expectNumberError, location);
|
|
}
|
|
decimal *= sign;
|
|
if (!isSafeInteger(decimal)) {
|
|
return this.error(invalidNumberError, location);
|
|
}
|
|
return { val: decimal, err: null };
|
|
};
|
|
Parser.prototype.offset = function () {
|
|
return this.position.offset;
|
|
};
|
|
Parser.prototype.isEOF = function () {
|
|
return this.offset() === this.message.length;
|
|
};
|
|
Parser.prototype.clonePosition = function () {
|
|
// This is much faster than `Object.assign` or spread.
|
|
return {
|
|
offset: this.position.offset,
|
|
line: this.position.line,
|
|
column: this.position.column,
|
|
};
|
|
};
|
|
/**
|
|
* Return the code point at the current position of the parser.
|
|
* Throws if the index is out of bound.
|
|
*/
|
|
Parser.prototype.char = function () {
|
|
var offset = this.position.offset;
|
|
if (offset >= this.message.length) {
|
|
throw Error('out of bound');
|
|
}
|
|
var code = codePointAt(this.message, offset);
|
|
if (code === undefined) {
|
|
throw Error("Offset ".concat(offset, " is at invalid UTF-16 code unit boundary"));
|
|
}
|
|
return code;
|
|
};
|
|
Parser.prototype.error = function (kind, location) {
|
|
return {
|
|
val: null,
|
|
err: {
|
|
kind: kind,
|
|
message: this.message,
|
|
location: location,
|
|
},
|
|
};
|
|
};
|
|
/** Bump the parser to the next UTF-16 code unit. */
|
|
Parser.prototype.bump = function () {
|
|
if (this.isEOF()) {
|
|
return;
|
|
}
|
|
var code = this.char();
|
|
if (code === 10 /* '\n' */) {
|
|
this.position.line += 1;
|
|
this.position.column = 1;
|
|
this.position.offset += 1;
|
|
}
|
|
else {
|
|
this.position.column += 1;
|
|
// 0 ~ 0x10000 -> unicode BMP, otherwise skip the surrogate pair.
|
|
this.position.offset += code < 0x10000 ? 1 : 2;
|
|
}
|
|
};
|
|
/**
|
|
* If the substring starting at the current position of the parser has
|
|
* the given prefix, then bump the parser to the character immediately
|
|
* following the prefix and return true. Otherwise, don't bump the parser
|
|
* and return false.
|
|
*/
|
|
Parser.prototype.bumpIf = function (prefix) {
|
|
if (startsWith(this.message, prefix, this.offset())) {
|
|
for (var i = 0; i < prefix.length; i++) {
|
|
this.bump();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
/**
|
|
* Bump the parser until the pattern character is found and return `true`.
|
|
* Otherwise bump to the end of the file and return `false`.
|
|
*/
|
|
Parser.prototype.bumpUntil = function (pattern) {
|
|
var currentOffset = this.offset();
|
|
var index = this.message.indexOf(pattern, currentOffset);
|
|
if (index >= 0) {
|
|
this.bumpTo(index);
|
|
return true;
|
|
}
|
|
else {
|
|
this.bumpTo(this.message.length);
|
|
return false;
|
|
}
|
|
};
|
|
/**
|
|
* Bump the parser to the target offset.
|
|
* If target offset is beyond the end of the input, bump the parser to the end of the input.
|
|
*/
|
|
Parser.prototype.bumpTo = function (targetOffset) {
|
|
if (this.offset() > targetOffset) {
|
|
throw Error("targetOffset ".concat(targetOffset, " must be greater than or equal to the current offset ").concat(this.offset()));
|
|
}
|
|
targetOffset = Math.min(targetOffset, this.message.length);
|
|
while (true) {
|
|
var offset = this.offset();
|
|
if (offset === targetOffset) {
|
|
break;
|
|
}
|
|
if (offset > targetOffset) {
|
|
throw Error("targetOffset ".concat(targetOffset, " is at invalid UTF-16 code unit boundary"));
|
|
}
|
|
this.bump();
|
|
if (this.isEOF()) {
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
/** advance the parser through all whitespace to the next non-whitespace code unit. */
|
|
Parser.prototype.bumpSpace = function () {
|
|
while (!this.isEOF() && _isWhiteSpace(this.char())) {
|
|
this.bump();
|
|
}
|
|
};
|
|
/**
|
|
* Peek at the *next* Unicode codepoint in the input without advancing the parser.
|
|
* If the input has been exhausted, then this returns null.
|
|
*/
|
|
Parser.prototype.peek = function () {
|
|
if (this.isEOF()) {
|
|
return null;
|
|
}
|
|
var code = this.char();
|
|
var offset = this.offset();
|
|
var nextCode = this.message.charCodeAt(offset + (code >= 0x10000 ? 2 : 1));
|
|
return nextCode !== null && nextCode !== void 0 ? nextCode : null;
|
|
};
|
|
return Parser;
|
|
}());
|
|
exports.Parser = Parser;
|
|
/**
|
|
* This check if codepoint is alphabet (lower & uppercase)
|
|
* @param codepoint
|
|
* @returns
|
|
*/
|
|
function _isAlpha(codepoint) {
|
|
return ((codepoint >= 97 && codepoint <= 122) ||
|
|
(codepoint >= 65 && codepoint <= 90));
|
|
}
|
|
function _isAlphaOrSlash(codepoint) {
|
|
return _isAlpha(codepoint) || codepoint === 47; /* '/' */
|
|
}
|
|
/** See `parseTag` function docs. */
|
|
function _isPotentialElementNameChar(c) {
|
|
return (c === 45 /* '-' */ ||
|
|
c === 46 /* '.' */ ||
|
|
(c >= 48 && c <= 57) /* 0..9 */ ||
|
|
c === 95 /* '_' */ ||
|
|
(c >= 97 && c <= 122) /** a..z */ ||
|
|
(c >= 65 && c <= 90) /* A..Z */ ||
|
|
c == 0xb7 ||
|
|
(c >= 0xc0 && c <= 0xd6) ||
|
|
(c >= 0xd8 && c <= 0xf6) ||
|
|
(c >= 0xf8 && c <= 0x37d) ||
|
|
(c >= 0x37f && c <= 0x1fff) ||
|
|
(c >= 0x200c && c <= 0x200d) ||
|
|
(c >= 0x203f && c <= 0x2040) ||
|
|
(c >= 0x2070 && c <= 0x218f) ||
|
|
(c >= 0x2c00 && c <= 0x2fef) ||
|
|
(c >= 0x3001 && c <= 0xd7ff) ||
|
|
(c >= 0xf900 && c <= 0xfdcf) ||
|
|
(c >= 0xfdf0 && c <= 0xfffd) ||
|
|
(c >= 0x10000 && c <= 0xeffff));
|
|
}
|
|
/**
|
|
* Code point equivalent of regex `\p{White_Space}`.
|
|
* From: https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt
|
|
*/
|
|
function _isWhiteSpace(c) {
|
|
return ((c >= 0x0009 && c <= 0x000d) ||
|
|
c === 0x0020 ||
|
|
c === 0x0085 ||
|
|
(c >= 0x200e && c <= 0x200f) ||
|
|
c === 0x2028 ||
|
|
c === 0x2029);
|
|
}
|
|
/**
|
|
* Code point equivalent of regex `\p{Pattern_Syntax}`.
|
|
* See https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt
|
|
*/
|
|
function _isPatternSyntax(c) {
|
|
return ((c >= 0x0021 && c <= 0x0023) ||
|
|
c === 0x0024 ||
|
|
(c >= 0x0025 && c <= 0x0027) ||
|
|
c === 0x0028 ||
|
|
c === 0x0029 ||
|
|
c === 0x002a ||
|
|
c === 0x002b ||
|
|
c === 0x002c ||
|
|
c === 0x002d ||
|
|
(c >= 0x002e && c <= 0x002f) ||
|
|
(c >= 0x003a && c <= 0x003b) ||
|
|
(c >= 0x003c && c <= 0x003e) ||
|
|
(c >= 0x003f && c <= 0x0040) ||
|
|
c === 0x005b ||
|
|
c === 0x005c ||
|
|
c === 0x005d ||
|
|
c === 0x005e ||
|
|
c === 0x0060 ||
|
|
c === 0x007b ||
|
|
c === 0x007c ||
|
|
c === 0x007d ||
|
|
c === 0x007e ||
|
|
c === 0x00a1 ||
|
|
(c >= 0x00a2 && c <= 0x00a5) ||
|
|
c === 0x00a6 ||
|
|
c === 0x00a7 ||
|
|
c === 0x00a9 ||
|
|
c === 0x00ab ||
|
|
c === 0x00ac ||
|
|
c === 0x00ae ||
|
|
c === 0x00b0 ||
|
|
c === 0x00b1 ||
|
|
c === 0x00b6 ||
|
|
c === 0x00bb ||
|
|
c === 0x00bf ||
|
|
c === 0x00d7 ||
|
|
c === 0x00f7 ||
|
|
(c >= 0x2010 && c <= 0x2015) ||
|
|
(c >= 0x2016 && c <= 0x2017) ||
|
|
c === 0x2018 ||
|
|
c === 0x2019 ||
|
|
c === 0x201a ||
|
|
(c >= 0x201b && c <= 0x201c) ||
|
|
c === 0x201d ||
|
|
c === 0x201e ||
|
|
c === 0x201f ||
|
|
(c >= 0x2020 && c <= 0x2027) ||
|
|
(c >= 0x2030 && c <= 0x2038) ||
|
|
c === 0x2039 ||
|
|
c === 0x203a ||
|
|
(c >= 0x203b && c <= 0x203e) ||
|
|
(c >= 0x2041 && c <= 0x2043) ||
|
|
c === 0x2044 ||
|
|
c === 0x2045 ||
|
|
c === 0x2046 ||
|
|
(c >= 0x2047 && c <= 0x2051) ||
|
|
c === 0x2052 ||
|
|
c === 0x2053 ||
|
|
(c >= 0x2055 && c <= 0x205e) ||
|
|
(c >= 0x2190 && c <= 0x2194) ||
|
|
(c >= 0x2195 && c <= 0x2199) ||
|
|
(c >= 0x219a && c <= 0x219b) ||
|
|
(c >= 0x219c && c <= 0x219f) ||
|
|
c === 0x21a0 ||
|
|
(c >= 0x21a1 && c <= 0x21a2) ||
|
|
c === 0x21a3 ||
|
|
(c >= 0x21a4 && c <= 0x21a5) ||
|
|
c === 0x21a6 ||
|
|
(c >= 0x21a7 && c <= 0x21ad) ||
|
|
c === 0x21ae ||
|
|
(c >= 0x21af && c <= 0x21cd) ||
|
|
(c >= 0x21ce && c <= 0x21cf) ||
|
|
(c >= 0x21d0 && c <= 0x21d1) ||
|
|
c === 0x21d2 ||
|
|
c === 0x21d3 ||
|
|
c === 0x21d4 ||
|
|
(c >= 0x21d5 && c <= 0x21f3) ||
|
|
(c >= 0x21f4 && c <= 0x22ff) ||
|
|
(c >= 0x2300 && c <= 0x2307) ||
|
|
c === 0x2308 ||
|
|
c === 0x2309 ||
|
|
c === 0x230a ||
|
|
c === 0x230b ||
|
|
(c >= 0x230c && c <= 0x231f) ||
|
|
(c >= 0x2320 && c <= 0x2321) ||
|
|
(c >= 0x2322 && c <= 0x2328) ||
|
|
c === 0x2329 ||
|
|
c === 0x232a ||
|
|
(c >= 0x232b && c <= 0x237b) ||
|
|
c === 0x237c ||
|
|
(c >= 0x237d && c <= 0x239a) ||
|
|
(c >= 0x239b && c <= 0x23b3) ||
|
|
(c >= 0x23b4 && c <= 0x23db) ||
|
|
(c >= 0x23dc && c <= 0x23e1) ||
|
|
(c >= 0x23e2 && c <= 0x2426) ||
|
|
(c >= 0x2427 && c <= 0x243f) ||
|
|
(c >= 0x2440 && c <= 0x244a) ||
|
|
(c >= 0x244b && c <= 0x245f) ||
|
|
(c >= 0x2500 && c <= 0x25b6) ||
|
|
c === 0x25b7 ||
|
|
(c >= 0x25b8 && c <= 0x25c0) ||
|
|
c === 0x25c1 ||
|
|
(c >= 0x25c2 && c <= 0x25f7) ||
|
|
(c >= 0x25f8 && c <= 0x25ff) ||
|
|
(c >= 0x2600 && c <= 0x266e) ||
|
|
c === 0x266f ||
|
|
(c >= 0x2670 && c <= 0x2767) ||
|
|
c === 0x2768 ||
|
|
c === 0x2769 ||
|
|
c === 0x276a ||
|
|
c === 0x276b ||
|
|
c === 0x276c ||
|
|
c === 0x276d ||
|
|
c === 0x276e ||
|
|
c === 0x276f ||
|
|
c === 0x2770 ||
|
|
c === 0x2771 ||
|
|
c === 0x2772 ||
|
|
c === 0x2773 ||
|
|
c === 0x2774 ||
|
|
c === 0x2775 ||
|
|
(c >= 0x2794 && c <= 0x27bf) ||
|
|
(c >= 0x27c0 && c <= 0x27c4) ||
|
|
c === 0x27c5 ||
|
|
c === 0x27c6 ||
|
|
(c >= 0x27c7 && c <= 0x27e5) ||
|
|
c === 0x27e6 ||
|
|
c === 0x27e7 ||
|
|
c === 0x27e8 ||
|
|
c === 0x27e9 ||
|
|
c === 0x27ea ||
|
|
c === 0x27eb ||
|
|
c === 0x27ec ||
|
|
c === 0x27ed ||
|
|
c === 0x27ee ||
|
|
c === 0x27ef ||
|
|
(c >= 0x27f0 && c <= 0x27ff) ||
|
|
(c >= 0x2800 && c <= 0x28ff) ||
|
|
(c >= 0x2900 && c <= 0x2982) ||
|
|
c === 0x2983 ||
|
|
c === 0x2984 ||
|
|
c === 0x2985 ||
|
|
c === 0x2986 ||
|
|
c === 0x2987 ||
|
|
c === 0x2988 ||
|
|
c === 0x2989 ||
|
|
c === 0x298a ||
|
|
c === 0x298b ||
|
|
c === 0x298c ||
|
|
c === 0x298d ||
|
|
c === 0x298e ||
|
|
c === 0x298f ||
|
|
c === 0x2990 ||
|
|
c === 0x2991 ||
|
|
c === 0x2992 ||
|
|
c === 0x2993 ||
|
|
c === 0x2994 ||
|
|
c === 0x2995 ||
|
|
c === 0x2996 ||
|
|
c === 0x2997 ||
|
|
c === 0x2998 ||
|
|
(c >= 0x2999 && c <= 0x29d7) ||
|
|
c === 0x29d8 ||
|
|
c === 0x29d9 ||
|
|
c === 0x29da ||
|
|
c === 0x29db ||
|
|
(c >= 0x29dc && c <= 0x29fb) ||
|
|
c === 0x29fc ||
|
|
c === 0x29fd ||
|
|
(c >= 0x29fe && c <= 0x2aff) ||
|
|
(c >= 0x2b00 && c <= 0x2b2f) ||
|
|
(c >= 0x2b30 && c <= 0x2b44) ||
|
|
(c >= 0x2b45 && c <= 0x2b46) ||
|
|
(c >= 0x2b47 && c <= 0x2b4c) ||
|
|
(c >= 0x2b4d && c <= 0x2b73) ||
|
|
(c >= 0x2b74 && c <= 0x2b75) ||
|
|
(c >= 0x2b76 && c <= 0x2b95) ||
|
|
c === 0x2b96 ||
|
|
(c >= 0x2b97 && c <= 0x2bff) ||
|
|
(c >= 0x2e00 && c <= 0x2e01) ||
|
|
c === 0x2e02 ||
|
|
c === 0x2e03 ||
|
|
c === 0x2e04 ||
|
|
c === 0x2e05 ||
|
|
(c >= 0x2e06 && c <= 0x2e08) ||
|
|
c === 0x2e09 ||
|
|
c === 0x2e0a ||
|
|
c === 0x2e0b ||
|
|
c === 0x2e0c ||
|
|
c === 0x2e0d ||
|
|
(c >= 0x2e0e && c <= 0x2e16) ||
|
|
c === 0x2e17 ||
|
|
(c >= 0x2e18 && c <= 0x2e19) ||
|
|
c === 0x2e1a ||
|
|
c === 0x2e1b ||
|
|
c === 0x2e1c ||
|
|
c === 0x2e1d ||
|
|
(c >= 0x2e1e && c <= 0x2e1f) ||
|
|
c === 0x2e20 ||
|
|
c === 0x2e21 ||
|
|
c === 0x2e22 ||
|
|
c === 0x2e23 ||
|
|
c === 0x2e24 ||
|
|
c === 0x2e25 ||
|
|
c === 0x2e26 ||
|
|
c === 0x2e27 ||
|
|
c === 0x2e28 ||
|
|
c === 0x2e29 ||
|
|
(c >= 0x2e2a && c <= 0x2e2e) ||
|
|
c === 0x2e2f ||
|
|
(c >= 0x2e30 && c <= 0x2e39) ||
|
|
(c >= 0x2e3a && c <= 0x2e3b) ||
|
|
(c >= 0x2e3c && c <= 0x2e3f) ||
|
|
c === 0x2e40 ||
|
|
c === 0x2e41 ||
|
|
c === 0x2e42 ||
|
|
(c >= 0x2e43 && c <= 0x2e4f) ||
|
|
(c >= 0x2e50 && c <= 0x2e51) ||
|
|
c === 0x2e52 ||
|
|
(c >= 0x2e53 && c <= 0x2e7f) ||
|
|
(c >= 0x3001 && c <= 0x3003) ||
|
|
c === 0x3008 ||
|
|
c === 0x3009 ||
|
|
c === 0x300a ||
|
|
c === 0x300b ||
|
|
c === 0x300c ||
|
|
c === 0x300d ||
|
|
c === 0x300e ||
|
|
c === 0x300f ||
|
|
c === 0x3010 ||
|
|
c === 0x3011 ||
|
|
(c >= 0x3012 && c <= 0x3013) ||
|
|
c === 0x3014 ||
|
|
c === 0x3015 ||
|
|
c === 0x3016 ||
|
|
c === 0x3017 ||
|
|
c === 0x3018 ||
|
|
c === 0x3019 ||
|
|
c === 0x301a ||
|
|
c === 0x301b ||
|
|
c === 0x301c ||
|
|
c === 0x301d ||
|
|
(c >= 0x301e && c <= 0x301f) ||
|
|
c === 0x3020 ||
|
|
c === 0x3030 ||
|
|
c === 0xfd3e ||
|
|
c === 0xfd3f ||
|
|
(c >= 0xfe45 && c <= 0xfe46));
|
|
}
|