ember-source
Version:
A JavaScript framework for creating ambitious web applications
9,831 lines • 284 kB
JavaScript
import '../@ember/debug/index.js';
import { assert as debugAssert, isPresentArray, assertNever, getFirst, getLast, assign, dict, unwrap, asPresentArray, assertPresentArray, deprecate, expect } from '../@glimmer/util/index.js';
import { SexpOpcodes as opcodes } from '../@glimmer/wire-format/index.js';
import { isDevelopingApp } from '@embroider/macros';
import calculateLocationDisplay from '../@ember/template-compiler/lib/system/calculate-location-display.js';
import { isPath, trackLocals, isStringLiteral } from '../@ember/template-compiler/lib/plugins/utils.js';
import { assert } from '../@ember/debug/lib/assert.js';
var errorProps = ['description', 'fileName', 'lineNumber', 'endLineNumber', 'message', 'name', 'number', 'stack'];
function Exception(message, node) {
var loc = node && node.loc,
line,
endLineNumber,
column,
endColumn;
if (loc) {
line = loc.start.line;
endLineNumber = loc.end.line;
column = loc.start.column;
endColumn = loc.end.column;
message += ' - ' + line + ':' + column;
}
var tmp = Error.prototype.constructor.call(this, message);
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
for (var idx = 0; idx < errorProps.length; idx++) {
this[errorProps[idx]] = tmp[errorProps[idx]];
}
/* istanbul ignore else */
if (Error.captureStackTrace) {
Error.captureStackTrace(this, Exception);
}
try {
if (loc) {
this.lineNumber = line;
this.endLineNumber = endLineNumber;
// Work around issue under safari where we can't directly set the column value
/* istanbul ignore next */
if (Object.defineProperty) {
Object.defineProperty(this, 'column', {
value: column,
enumerable: true
});
Object.defineProperty(this, 'endColumn', {
value: endColumn,
enumerable: true
});
} else {
this.column = column;
this.endColumn = endColumn;
}
}
} catch (nop) {
/* Ignore if the browser is very particular */
}
}
Exception.prototype = new Error();
function Visitor() {
this.parents = [];
}
Visitor.prototype = {
constructor: Visitor,
mutating: false,
// Visits a given value. If mutating, will replace the value if necessary.
acceptKey: function (node, name) {
var value = this.accept(node[name]);
if (this.mutating) {
// Hacky sanity check: This may have a few false positives for type for the helper
// methods but will generally do the right thing without a lot of overhead.
if (value && !Visitor.prototype[value.type]) {
throw new Exception('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type);
}
node[name] = value;
}
},
// Performs an accept operation with added sanity check to ensure
// required keys are not removed.
acceptRequired: function (node, name) {
this.acceptKey(node, name);
if (!node[name]) {
throw new Exception(node.type + ' requires ' + name);
}
},
// Traverses a given array. If mutating, empty respnses will be removed
// for child elements.
acceptArray: function (array) {
for (var i = 0, l = array.length; i < l; i++) {
this.acceptKey(array, i);
if (!array[i]) {
array.splice(i, 1);
i--;
l--;
}
}
},
accept: function (object) {
if (!object) {
return;
}
/* istanbul ignore next: Sanity code */
if (!this[object.type]) {
throw new Exception('Unknown type: ' + object.type, object);
}
if (this.current) {
this.parents.unshift(this.current);
}
this.current = object;
var ret = this[object.type](object);
this.current = this.parents.shift();
if (!this.mutating || ret) {
return ret;
} else if (ret !== false) {
return object;
}
},
Program: function (program) {
this.acceptArray(program.body);
},
MustacheStatement: visitSubExpression,
Decorator: visitSubExpression,
BlockStatement: visitBlock,
DecoratorBlock: visitBlock,
PartialStatement: visitPartial,
PartialBlockStatement: function (partial) {
visitPartial.call(this, partial);
this.acceptKey(partial, 'program');
},
ContentStatement: function /* content */ () {},
CommentStatement: function /* comment */ () {},
SubExpression: visitSubExpression,
PathExpression: function /* path */ () {},
StringLiteral: function /* string */ () {},
NumberLiteral: function /* number */ () {},
BooleanLiteral: function /* bool */ () {},
UndefinedLiteral: function /* literal */ () {},
NullLiteral: function /* literal */ () {},
Hash: function (hash) {
this.acceptArray(hash.pairs);
},
HashPair: function (pair) {
this.acceptRequired(pair, 'value');
}
};
function visitSubExpression(mustache) {
this.acceptRequired(mustache, 'path');
this.acceptArray(mustache.params);
this.acceptKey(mustache, 'hash');
}
function visitBlock(block) {
visitSubExpression.call(this, block);
this.acceptKey(block, 'program');
this.acceptKey(block, 'inverse');
}
function visitPartial(partial) {
this.acceptRequired(partial, 'name');
this.acceptArray(partial.params);
this.acceptKey(partial, 'hash');
}
function WhitespaceControl(options) {
if (options === void 0) {
options = {};
}
this.options = options;
}
WhitespaceControl.prototype = new Visitor();
WhitespaceControl.prototype.Program = function (program) {
var doStandalone = !this.options.ignoreStandalone;
var isRoot = !this.isRootSeen;
this.isRootSeen = true;
var body = program.body;
for (var i = 0, l = body.length; i < l; i++) {
var current = body[i],
strip = this.accept(current);
if (!strip) {
continue;
}
var _isPrevWhitespace = isPrevWhitespace(body, i, isRoot),
_isNextWhitespace = isNextWhitespace(body, i, isRoot),
openStandalone = strip.openStandalone && _isPrevWhitespace,
closeStandalone = strip.closeStandalone && _isNextWhitespace,
inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
if (strip.close) {
omitRight(body, i, true);
}
if (strip.open) {
omitLeft(body, i, true);
}
if (doStandalone && inlineStandalone) {
omitRight(body, i);
if (omitLeft(body, i)) {
// If we are on a standalone node, save the indent info for partials
if (current.type === 'PartialStatement') {
// Pull out the whitespace from the final line
current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1];
}
}
}
if (doStandalone && openStandalone) {
omitRight((current.program || current.inverse).body);
// Strip out the previous content node if it's whitespace only
omitLeft(body, i);
}
if (doStandalone && closeStandalone) {
// Always strip the next node
omitRight(body, i);
omitLeft((current.inverse || current.program).body);
}
}
return program;
};
WhitespaceControl.prototype.BlockStatement = WhitespaceControl.prototype.DecoratorBlock = WhitespaceControl.prototype.PartialBlockStatement = function (block) {
this.accept(block.program);
this.accept(block.inverse);
// Find the inverse program that is involed with whitespace stripping.
var program = block.program || block.inverse,
inverse = block.program && block.inverse,
firstInverse = inverse,
lastInverse = inverse;
if (inverse && inverse.chained) {
firstInverse = inverse.body[0].program;
// Walk the inverse chain to find the last inverse that is actually in the chain.
while (lastInverse.chained) {
lastInverse = lastInverse.body[lastInverse.body.length - 1].program;
}
}
var strip = {
open: block.openStrip.open,
close: block.closeStrip.close,
// Determine the standalone candiacy. Basically flag our content as being possibly standalone
// so our parent can determine if we actually are standalone
openStandalone: isNextWhitespace(program.body),
closeStandalone: isPrevWhitespace((firstInverse || program).body)
};
if (block.openStrip.close) {
omitRight(program.body, null, true);
}
if (inverse) {
var inverseStrip = block.inverseStrip;
if (inverseStrip.open) {
omitLeft(program.body, null, true);
}
if (inverseStrip.close) {
omitRight(firstInverse.body, null, true);
}
if (block.closeStrip.open) {
omitLeft(lastInverse.body, null, true);
}
// Find standalone else statments
if (!this.options.ignoreStandalone && isPrevWhitespace(program.body) && isNextWhitespace(firstInverse.body)) {
omitLeft(program.body);
omitRight(firstInverse.body);
}
} else if (block.closeStrip.open) {
omitLeft(program.body, null, true);
}
return strip;
};
WhitespaceControl.prototype.Decorator = WhitespaceControl.prototype.MustacheStatement = function (mustache) {
return mustache.strip;
};
WhitespaceControl.prototype.PartialStatement = WhitespaceControl.prototype.CommentStatement = function (node) {
/* istanbul ignore next */
var strip = node.strip || {};
return {
inlineStandalone: true,
open: strip.open,
close: strip.close
};
};
function isPrevWhitespace(body, i, isRoot) {
if (i === undefined) {
i = body.length;
}
// Nodes that end with newlines are considered whitespace (but are special
// cased for strip operations)
var prev = body[i - 1],
sibling = body[i - 2];
if (!prev) {
return isRoot;
}
if (prev.type === 'ContentStatement') {
return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test(prev.original);
}
}
function isNextWhitespace(body, i, isRoot) {
if (i === undefined) {
i = -1;
}
var next = body[i + 1],
sibling = body[i + 2];
if (!next) {
return isRoot;
}
if (next.type === 'ContentStatement') {
return (sibling || !isRoot ? /^\s*?\r?\n/ : /^\s*?(\r?\n|$)/).test(next.original);
}
}
// Marks the node to the right of the position as omitted.
// I.e. {{foo}}' ' will mark the ' ' node as omitted.
//
// If i is undefined, then the first child will be marked as such.
//
// If multiple is truthy then all whitespace will be stripped out until non-whitespace
// content is met.
function omitRight(body, i, multiple) {
var current = body[i == null ? 0 : i + 1];
if (!current || current.type !== 'ContentStatement' || !multiple && current.rightStripped) {
return;
}
var original = current.value;
current.value = current.value.replace(multiple ? /^\s+/ : /^[ \t]*\r?\n?/, '');
current.rightStripped = current.value !== original;
}
// Marks the node to the left of the position as omitted.
// I.e. ' '{{foo}} will mark the ' ' node as omitted.
//
// If i is undefined then the last child will be marked as such.
//
// If multiple is truthy then all whitespace will be stripped out until non-whitespace
// content is met.
function omitLeft(body, i, multiple) {
var current = body[i == null ? body.length - 1 : i - 1];
if (!current || current.type !== 'ContentStatement' || !multiple && current.leftStripped) {
return;
}
// We omit the last node if it's whitespace only and not preceded by a non-content node.
var original = current.value;
current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, '');
current.leftStripped = current.value !== original;
return current.leftStripped;
}
/* parser generated by jison 0.4.18 */
/*
Returns a Parser object of the following structure:
Parser: {
yy: {}
}
Parser.prototype: {
yy: {},
trace: function(),
symbols_: {associative list: name ==> number},
terminals_: {associative list: number ==> name},
productions_: [...],
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
table: [...],
defaultActions: {...},
parseError: function(str, hash),
parse: function(input),
lexer: {
EOF: 1,
parseError: function(str, hash),
setInput: function(input),
input: function(),
unput: function(str),
more: function(),
less: function(n),
pastInput: function(),
upcomingInput: function(),
showPosition: function(),
test_match: function(regex_match_array, rule_index),
next: function(),
lex: function(),
begin: function(condition),
popState: function(),
_currentRules: function(),
topState: function(),
pushState: function(condition),
options: {
ranges: boolean (optional: true ==> token location info will include a .range[] member)
flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
},
performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
rules: [...],
conditions: {associative list: name ==> set},
}
}
token location info (@$, _$, etc.): {
first_line: n,
last_line: n,
first_column: n,
last_column: n,
range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based)
}
the parseError function receives a 'hash' object with these members for lexer and parser errors: {
text: (matched text)
token: (the produced terminal token, if any)
line: (yylineno)
}
while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
loc: (yylloc)
expected: (string describing the set of expected tokens)
recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
}
*/
var parser = function () {
var o = function (k, v, o, l) {
for (o = o || {}, l = k.length; l--; o[k[l]] = v);
return o;
},
$V0 = [2, 44],
$V1 = [1, 20],
$V2 = [5, 14, 15, 19, 29, 34, 39, 44, 47, 48, 52, 56, 60],
$V3 = [1, 35],
$V4 = [1, 38],
$V5 = [1, 30],
$V6 = [1, 31],
$V7 = [1, 32],
$V8 = [1, 33],
$V9 = [1, 34],
$Va = [1, 37],
$Vb = [14, 15, 19, 29, 34, 39, 44, 47, 48, 52, 56, 60],
$Vc = [14, 15, 19, 29, 34, 44, 47, 48, 52, 56, 60],
$Vd = [15, 18],
$Ve = [14, 15, 19, 29, 34, 47, 48, 52, 56, 60],
$Vf = [33, 64, 71, 79, 80, 81, 82, 83, 84],
$Vg = [23, 33, 55, 64, 67, 71, 74, 79, 80, 81, 82, 83, 84],
$Vh = [1, 51],
$Vi = [23, 33, 55, 64, 67, 71, 74, 79, 80, 81, 82, 83, 84, 86],
$Vj = [2, 43],
$Vk = [55, 64, 71, 79, 80, 81, 82, 83, 84],
$Vl = [1, 58],
$Vm = [1, 59],
$Vn = [1, 66],
$Vo = [33, 64, 71, 74, 79, 80, 81, 82, 83, 84],
$Vp = [23, 64, 71, 79, 80, 81, 82, 83, 84],
$Vq = [1, 76],
$Vr = [64, 67, 71, 79, 80, 81, 82, 83, 84],
$Vs = [33, 74],
$Vt = [23, 33, 55, 67, 71, 74],
$Vu = [1, 106],
$Vv = [1, 118],
$Vw = [71, 76];
var parser = {
trace: function trace() {},
yy: {},
symbols_: {
"error": 2,
"root": 3,
"program": 4,
"EOF": 5,
"program_repetition0": 6,
"statement": 7,
"mustache": 8,
"block": 9,
"rawBlock": 10,
"partial": 11,
"partialBlock": 12,
"content": 13,
"COMMENT": 14,
"CONTENT": 15,
"openRawBlock": 16,
"rawBlock_repetition0": 17,
"END_RAW_BLOCK": 18,
"OPEN_RAW_BLOCK": 19,
"helperName": 20,
"openRawBlock_repetition0": 21,
"openRawBlock_option0": 22,
"CLOSE_RAW_BLOCK": 23,
"openBlock": 24,
"block_option0": 25,
"closeBlock": 26,
"openInverse": 27,
"block_option1": 28,
"OPEN_BLOCK": 29,
"openBlock_repetition0": 30,
"openBlock_option0": 31,
"openBlock_option1": 32,
"CLOSE": 33,
"OPEN_INVERSE": 34,
"openInverse_repetition0": 35,
"openInverse_option0": 36,
"openInverse_option1": 37,
"openInverseChain": 38,
"OPEN_INVERSE_CHAIN": 39,
"openInverseChain_repetition0": 40,
"openInverseChain_option0": 41,
"openInverseChain_option1": 42,
"inverseAndProgram": 43,
"INVERSE": 44,
"inverseChain": 45,
"inverseChain_option0": 46,
"OPEN_ENDBLOCK": 47,
"OPEN": 48,
"expr": 49,
"mustache_repetition0": 50,
"mustache_option0": 51,
"OPEN_UNESCAPED": 52,
"mustache_repetition1": 53,
"mustache_option1": 54,
"CLOSE_UNESCAPED": 55,
"OPEN_PARTIAL": 56,
"partial_repetition0": 57,
"partial_option0": 58,
"openPartialBlock": 59,
"OPEN_PARTIAL_BLOCK": 60,
"openPartialBlock_repetition0": 61,
"openPartialBlock_option0": 62,
"sexpr": 63,
"OPEN_SEXPR": 64,
"sexpr_repetition0": 65,
"sexpr_option0": 66,
"CLOSE_SEXPR": 67,
"hash": 68,
"hash_repetition_plus0": 69,
"hashSegment": 70,
"ID": 71,
"EQUALS": 72,
"blockParams": 73,
"OPEN_BLOCK_PARAMS": 74,
"blockParams_repetition_plus0": 75,
"CLOSE_BLOCK_PARAMS": 76,
"path": 77,
"dataName": 78,
"STRING": 79,
"NUMBER": 80,
"BOOLEAN": 81,
"UNDEFINED": 82,
"NULL": 83,
"DATA": 84,
"pathSegments": 85,
"SEP": 86,
"$accept": 0,
"$end": 1
},
terminals_: {
2: "error",
5: "EOF",
14: "COMMENT",
15: "CONTENT",
18: "END_RAW_BLOCK",
19: "OPEN_RAW_BLOCK",
23: "CLOSE_RAW_BLOCK",
29: "OPEN_BLOCK",
33: "CLOSE",
34: "OPEN_INVERSE",
39: "OPEN_INVERSE_CHAIN",
44: "INVERSE",
47: "OPEN_ENDBLOCK",
48: "OPEN",
52: "OPEN_UNESCAPED",
55: "CLOSE_UNESCAPED",
56: "OPEN_PARTIAL",
60: "OPEN_PARTIAL_BLOCK",
64: "OPEN_SEXPR",
67: "CLOSE_SEXPR",
71: "ID",
72: "EQUALS",
74: "OPEN_BLOCK_PARAMS",
76: "CLOSE_BLOCK_PARAMS",
79: "STRING",
80: "NUMBER",
81: "BOOLEAN",
82: "UNDEFINED",
83: "NULL",
84: "DATA",
86: "SEP"
},
productions_: [0, [3, 2], [4, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [13, 1], [10, 3], [16, 5], [9, 4], [9, 4], [24, 6], [27, 6], [38, 6], [43, 2], [45, 3], [45, 1], [26, 3], [8, 5], [8, 5], [11, 5], [12, 3], [59, 5], [49, 1], [49, 1], [63, 5], [68, 1], [70, 3], [73, 3], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [78, 2], [77, 1], [85, 3], [85, 1], [6, 0], [6, 2], [17, 0], [17, 2], [21, 0], [21, 2], [22, 0], [22, 1], [25, 0], [25, 1], [28, 0], [28, 1], [30, 0], [30, 2], [31, 0], [31, 1], [32, 0], [32, 1], [35, 0], [35, 2], [36, 0], [36, 1], [37, 0], [37, 1], [40, 0], [40, 2], [41, 0], [41, 1], [42, 0], [42, 1], [46, 0], [46, 1], [50, 0], [50, 2], [51, 0], [51, 1], [53, 0], [53, 2], [54, 0], [54, 1], [57, 0], [57, 2], [58, 0], [58, 1], [61, 0], [61, 2], [62, 0], [62, 1], [65, 0], [65, 2], [66, 0], [66, 1], [69, 1], [69, 2], [75, 1], [75, 2]],
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
/* this == yyval */
var $0 = $$.length - 1;
switch (yystate) {
case 1:
return $$[$0 - 1];
case 2:
this.$ = yy.prepareProgram($$[$0]);
break;
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 20:
case 27:
case 28:
case 33:
case 34:
this.$ = $$[$0];
break;
case 9:
this.$ = {
type: 'CommentStatement',
value: yy.stripComment($$[$0]),
strip: yy.stripFlags($$[$0], $$[$0]),
loc: yy.locInfo(this._$)
};
break;
case 10:
this.$ = {
type: 'ContentStatement',
original: $$[$0],
value: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 11:
this.$ = yy.prepareRawBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$);
break;
case 12:
this.$ = {
path: $$[$0 - 3],
params: $$[$0 - 2],
hash: $$[$0 - 1]
};
break;
case 13:
this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], false, this._$);
break;
case 14:
this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], true, this._$);
break;
case 15:
this.$ = {
open: $$[$0 - 5],
path: $$[$0 - 4],
params: $$[$0 - 3],
hash: $$[$0 - 2],
blockParams: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 5], $$[$0])
};
break;
case 16:
case 17:
this.$ = {
path: $$[$0 - 4],
params: $$[$0 - 3],
hash: $$[$0 - 2],
blockParams: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 5], $$[$0])
};
break;
case 18:
this.$ = {
strip: yy.stripFlags($$[$0 - 1], $$[$0 - 1]),
program: $$[$0]
};
break;
case 19:
var inverse = yy.prepareBlock($$[$0 - 2], $$[$0 - 1], $$[$0], $$[$0], false, this._$),
program = yy.prepareProgram([inverse], $$[$0 - 1].loc);
program.chained = true;
this.$ = {
strip: $$[$0 - 2].strip,
program: program,
chain: true
};
break;
case 21:
this.$ = {
path: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 2], $$[$0])
};
break;
case 22:
case 23:
this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$);
break;
case 24:
this.$ = {
type: 'PartialStatement',
name: $$[$0 - 3],
params: $$[$0 - 2],
hash: $$[$0 - 1],
indent: '',
strip: yy.stripFlags($$[$0 - 4], $$[$0]),
loc: yy.locInfo(this._$)
};
break;
case 25:
this.$ = yy.preparePartialBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$);
break;
case 26:
this.$ = {
path: $$[$0 - 3],
params: $$[$0 - 2],
hash: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 4], $$[$0])
};
break;
case 29:
this.$ = {
type: 'SubExpression',
path: $$[$0 - 3],
params: $$[$0 - 2],
hash: $$[$0 - 1],
loc: yy.locInfo(this._$)
};
break;
case 30:
this.$ = {
type: 'Hash',
pairs: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 31:
this.$ = {
type: 'HashPair',
key: yy.id($$[$0 - 2]),
value: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 32:
this.$ = yy.id($$[$0 - 1]);
break;
case 35:
this.$ = {
type: 'StringLiteral',
value: $$[$0],
original: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 36:
this.$ = {
type: 'NumberLiteral',
value: Number($$[$0]),
original: Number($$[$0]),
loc: yy.locInfo(this._$)
};
break;
case 37:
this.$ = {
type: 'BooleanLiteral',
value: $$[$0] === 'true',
original: $$[$0] === 'true',
loc: yy.locInfo(this._$)
};
break;
case 38:
this.$ = {
type: 'UndefinedLiteral',
original: undefined,
value: undefined,
loc: yy.locInfo(this._$)
};
break;
case 39:
this.$ = {
type: 'NullLiteral',
original: null,
value: null,
loc: yy.locInfo(this._$)
};
break;
case 40:
this.$ = yy.preparePath(true, $$[$0], this._$);
break;
case 41:
this.$ = yy.preparePath(false, $$[$0], this._$);
break;
case 42:
$$[$0 - 2].push({
part: yy.id($$[$0]),
original: $$[$0],
separator: $$[$0 - 1]
});
this.$ = $$[$0 - 2];
break;
case 43:
this.$ = [{
part: yy.id($$[$0]),
original: $$[$0]
}];
break;
case 44:
case 46:
case 48:
case 56:
case 62:
case 68:
case 76:
case 80:
case 84:
case 88:
case 92:
this.$ = [];
break;
case 45:
case 47:
case 49:
case 57:
case 63:
case 69:
case 77:
case 81:
case 85:
case 89:
case 93:
case 97:
case 99:
$$[$0 - 1].push($$[$0]);
break;
case 96:
case 98:
this.$ = [$$[$0]];
break;
}
},
table: [o([5, 14, 15, 19, 29, 34, 48, 52, 56, 60], $V0, {
3: 1,
4: 2,
6: 3
}), {
1: [3]
}, {
5: [1, 4]
}, o([5, 39, 44, 47], [2, 2], {
7: 5,
8: 6,
9: 7,
10: 8,
11: 9,
12: 10,
13: 11,
24: 15,
27: 16,
16: 17,
59: 19,
14: [1, 12],
15: $V1,
19: [1, 23],
29: [1, 21],
34: [1, 22],
48: [1, 13],
52: [1, 14],
56: [1, 18],
60: [1, 24]
}), {
1: [2, 1]
}, o($V2, [2, 45]), o($V2, [2, 3]), o($V2, [2, 4]), o($V2, [2, 5]), o($V2, [2, 6]), o($V2, [2, 7]), o($V2, [2, 8]), o($V2, [2, 9]), {
20: 26,
49: 25,
63: 27,
64: $V3,
71: $V4,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, {
20: 26,
49: 39,
63: 27,
64: $V3,
71: $V4,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, o($Vb, $V0, {
6: 3,
4: 40
}), o($Vc, $V0, {
6: 3,
4: 41
}), o($Vd, [2, 46], {
17: 42
}), {
20: 26,
49: 43,
63: 27,
64: $V3,
71: $V4,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, o($Ve, $V0, {
6: 3,
4: 44
}), o([5, 14, 15, 18, 19, 29, 34, 39, 44, 47, 48, 52, 56, 60], [2, 10]), {
20: 45,
71: $V4,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, {
20: 46,
71: $V4,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, {
20: 47,
71: $V4,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, {
20: 26,
49: 48,
63: 27,
64: $V3,
71: $V4,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, o($Vf, [2, 76], {
50: 49
}), o($Vg, [2, 27]), o($Vg, [2, 28]), o($Vg, [2, 33]), o($Vg, [2, 34]), o($Vg, [2, 35]), o($Vg, [2, 36]), o($Vg, [2, 37]), o($Vg, [2, 38]), o($Vg, [2, 39]), {
20: 26,
49: 50,
63: 27,
64: $V3,
71: $V4,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, o($Vg, [2, 41], {
86: $Vh
}), {
71: $V4,
85: 52
}, o($Vi, $Vj), o($Vk, [2, 80], {
53: 53
}), {
25: 54,
38: 56,
39: $Vl,
43: 57,
44: $Vm,
45: 55,
47: [2, 52]
}, {
28: 60,
43: 61,
44: $Vm,
47: [2, 54]
}, {
13: 63,
15: $V1,
18: [1, 62]
}, o($Vf, [2, 84], {
57: 64
}), {
26: 65,
47: $Vn
}, o($Vo, [2, 56], {
30: 67
}), o($Vo, [2, 62], {
35: 68
}), o($Vp, [2, 48], {
21: 69
}), o($Vf, [2, 88], {
61: 70
}), {
20: 26,
33: [2, 78],
49: 72,
51: 71,
63: 27,
64: $V3,
68: 73,
69: 74,
70: 75,
71: $Vq,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, o($Vr, [2, 92], {
65: 77
}), {
71: [1, 78]
}, o($Vg, [2, 40], {
86: $Vh
}), {
20: 26,
49: 80,
54: 79,
55: [2, 82],
63: 27,
64: $V3,
68: 81,
69: 74,
70: 75,
71: $Vq,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, {
26: 82,
47: $Vn
}, {
47: [2, 53]
}, o($Vb, $V0, {
6: 3,
4: 83
}), {
47: [2, 20]
}, {
20: 84,
71: $V4,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, o($Ve, $V0, {
6: 3,
4: 85
}), {
26: 86,
47: $Vn
}, {
47: [2, 55]
}, o($V2, [2, 11]), o($Vd, [2, 47]), {
20: 26,
33: [2, 86],
49: 88,
58: 87,
63: 27,
64: $V3,
68: 89,
69: 74,
70: 75,
71: $Vq,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, o($V2, [2, 25]), {
20: 90,
71: $V4,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, o($Vs, [2, 58], {
20: 26,
63: 27,
77: 28,
78: 29,
85: 36,
69: 74,
70: 75,
31: 91,
49: 92,
68: 93,
64: $V3,
71: $Vq,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va
}), o($Vs, [2, 64], {
20: 26,
63: 27,
77: 28,
78: 29,
85: 36,
69: 74,
70: 75,
36: 94,
49: 95,
68: 96,
64: $V3,
71: $Vq,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va
}), {
20: 26,
22: 97,
23: [2, 50],
49: 98,
63: 27,
64: $V3,
68: 99,
69: 74,
70: 75,
71: $Vq,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, {
20: 26,
33: [2, 90],
49: 101,
62: 100,
63: 27,
64: $V3,
68: 102,
69: 74,
70: 75,
71: $Vq,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, {
33: [1, 103]
}, o($Vf, [2, 77]), {
33: [2, 79]
}, o([23, 33, 55, 67, 74], [2, 30], {
70: 104,
71: [1, 105]
}), o($Vt, [2, 96]), o($Vi, $Vj, {
72: $Vu
}), {
20: 26,
49: 108,
63: 27,
64: $V3,
66: 107,
67: [2, 94],
68: 109,
69: 74,
70: 75,
71: $Vq,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, o($Vi, [2, 42]), {
55: [1, 110]
}, o($Vk, [2, 81]), {
55: [2, 83]
}, o($V2, [2, 13]), {
38: 56,
39: $Vl,
43: 57,
44: $Vm,
45: 112,
46: 111,
47: [2, 74]
}, o($Vo, [2, 68], {
40: 113
}), {
47: [2, 18]
}, o($V2, [2, 14]), {
33: [1, 114]
}, o($Vf, [2, 85]), {
33: [2, 87]
}, {
33: [1, 115]
}, {
32: 116,
33: [2, 60],
73: 117,
74: $Vv
}, o($Vo, [2, 57]), o($Vs, [2, 59]), {
33: [2, 66],
37: 119,
73: 120,
74: $Vv
}, o($Vo, [2, 63]), o($Vs, [2, 65]), {
23: [1, 121]
}, o($Vp, [2, 49]), {
23: [2, 51]
}, {
33: [1, 122]
}, o($Vf, [2, 89]), {
33: [2, 91]
}, o($V2, [2, 22]), o($Vt, [2, 97]), {
72: $Vu
}, {
20: 26,
49: 123,
63: 27,
64: $V3,
71: $V4,
77: 28,
78: 29,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va,
85: 36
}, {
67: [1, 124]
}, o($Vr, [2, 93]), {
67: [2, 95]
}, o($V2, [2, 23]), {
47: [2, 19]
}, {
47: [2, 75]
}, o($Vs, [2, 70], {
20: 26,
63: 27,
77: 28,
78: 29,
85: 36,
69: 74,
70: 75,
41: 125,
49: 126,
68: 127,
64: $V3,
71: $Vq,
79: $V5,
80: $V6,
81: $V7,
82: $V8,
83: $V9,
84: $Va
}), o($V2, [2, 24]), o($V2, [2, 21]), {
33: [1, 128]
}, {
33: [2, 61]
}, {
71: [1, 130],
75: 129
}, {
33: [1, 131]
}, {
33: [2, 67]
}, o($Vd, [2, 12]), o($Ve, [2, 26]), o($Vt, [2, 31]), o($Vg, [2, 29]), {
33: [2, 72],
42: 132,
73: 133,
74: $Vv
}, o($Vo, [2, 69]), o($Vs, [2, 71]), o($Vb, [2, 15]), {
71: [1, 135],
76: [1, 134]
}, o($Vw, [2, 98]), o($Vc, [2, 16]), {
33: [1, 136]
}, {
33: [2, 73]
}, {
33: [2, 32]
}, o($Vw, [2, 99]), o($Vb, [2, 17])],
defaultActions: {
4: [2, 1],
55: [2, 53],
57: [2, 20],
61: [2, 55],
73: [2, 79],
81: [2, 83],
85: [2, 18],
89: [2, 87],
99: [2, 51],
102: [2, 91],
109: [2, 95],
111: [2, 19],
112: [2, 75],
117: [2, 61],
120: [2, 67],
133: [2, 73],
134: [2, 32]
},
parseError: function parseError(str, hash) {
if (hash.recoverable) {
this.trace(str);
} else {
var error = new Error(str);
error.hash = hash;
throw error;
}
},
parse: function parse(input) {
var self = this,
stack = [0],
vstack = [null],
lstack = [],
table = this.table,
yytext = '',
yylineno = 0,
yyleng = 0,
TERROR = 2,
EOF = 1;
var args = lstack.slice.call(arguments, 1);
var lexer = Object.create(this.lexer);
var sharedState = {
yy: {}
};
for (var k in this.yy) {
if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
sharedState.yy[k] = this.yy[k];
}
}
lexer.setInput(input, sharedState.yy);
sharedState.yy.lexer = lexer;
sharedState.yy.parser = this;
if (typeof lexer.yylloc == 'undefined') {
lexer.yylloc = {};
}
var yyloc = lexer.yylloc;
lstack.push(yyloc);
var ranges = lexer.options && lexer.options.ranges;
if (typeof sharedState.yy.parseError === 'function') {
this.parseError = sharedState.yy.parseError;
} else {
this.parseError = Object.getPrototypeOf(this).parseError;
}
var lex = function () {
var token;
token = lexer.lex() || EOF;
if (typeof token !== 'number') {
token = self.symbols_[token] || token;
}
return token;
};
var symbol,
state,
action,
r,
yyval = {},
p,
len,
newState,
expected;
while (true) {
state = stack[stack.length - 1];
if (this.defaultActions[state]) {
action = this.defaultActions[state];
} else {
if (symbol === null || typeof symbol == 'undefined') {
symbol = lex();
}
action = table[state] && table[state][symbol];
}
if (typeof action === 'undefined' || !action.length || !action[0]) {
var errStr = '';
expected = [];
for (p in table[state]) {
if (this.terminals_[p] && p > TERROR) {
expected.push('\'' + this.terminals_[p] + '\'');
}
}
if (lexer.showPosition) {
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
} else {
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
}
this.parseError(errStr, {
text: lexer.match,
token: this.terminals_[symbol] || symbol,
line: lexer.yylineno,
loc: yyloc,
expected: expected
});
}
if (action[0] instanceof Array && action.length > 1) {
throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
}
switch (action[0]) {
case 1:
stack.push(symbol);
vstack.push(lexer.yytext);
lstack.push(lexer.yylloc);
stack.push(action[1]);
symbol = null;
{
yyleng = lexer.yyleng;
yytext = lexer.yytext;
yylineno = lexer.yylineno;
yyloc = lexer.yylloc;
}
break;
case 2:
len = this.productions_[action[1]][1];
yyval.$ = vstack[vstack.length - len];
yyval._$ = {
first_line: lstack[lstack.length - (len || 1)].first_line,
last_line: lstack[lstack.length - 1].last_line,
first_column: lstack[lstack.length - (len || 1)].first_column,
last_column: lstack[lstack.length - 1].last_column
};
if (ranges) {
yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
}
r = this.performAction.apply(yyval, [yytext, yyleng, yylineno, sharedState.yy, action[1], vstack, lstack].concat(args));
if (typeof r !== 'undefined') {
return r;
}
if (len) {
stack = stack.slice(0, -1 * len * 2);
vstack = vstack.slice(0, -1 * len);
lstack = lstack.slice(0, -1 * len);
}
stack.push(this.productions_[action[1]][0]);
vstack.push(yyval.$);
lstack.push(yyval._$);
newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
stack.push(newState);
break;
case 3:
return true;
}
}
return true;
}
};
/* generated by jison-lex 0.3.4 */
var lexer = function () {
var lexer = {
EOF: 1,
parseError: function parseError(str, hash) {
if (this.yy.parser) {
this.yy.parser.parseError(str, hash);
} else {
throw new Error(str);
}
},
// resets the lexer, sets new input
setInput: function (input, yy) {
this.yy = yy || this.yy || {};
this._input = input;
this._more = this._backtrack = this.done = false;
this.yylineno = this.yyleng = 0;
this.yytext = this.matched = this.match = '';
this.conditionStack = ['INITIAL'];
this.yylloc = {
first_line: 1,
first_column: 0,
last_line: 1,
last_column: 0
};
if (this.options.ranges) {
this.yylloc.range = [0, 0];
}
this.offset = 0;
return this;
},
// consumes and returns one char from the input
input: function () {
var ch = this._input[0];
this.yytext += ch;
this.yyleng++;
this.offset++;
this.match += ch;
this.matched += ch;
var lines = ch.match(/(?:\r\n?|\n).*/g);
if (lines) {
this.yylineno++;
this.yylloc.last_line++;
} else {
this.yylloc.last_column++;
}
if (this.options.ranges) {
this.yylloc.range[1]++;
}
this._input = this._input.slice(1);
return ch;
},
// unshifts one char (or a string) into the input
unput: function (ch) {
var len = ch.length;
var lines = ch.split(/(?:\r\n?|\n)/g);
this._input = ch + this._input;
this.yytext = this.yytext.substr(0, this.yytext.length - len);
//this.yyleng -= len;
this.offset -= len;
var oldLines = this.match.split(/(?:\r\n?|\n)/g);
this.match = this.match.substr(0, this.match.length - 1);
this.matched = this.matched.substr(0, this.matched.length - 1);
if (lines.length - 1) {
this.yylineno -= lines.length - 1;
}
var r = this.yylloc.range;
this.yylloc = {
first_line: this.yylloc.first_line,
last_line: this.yylineno + 1,
first_column: this.yylloc.first_column,
last_column: lines ? (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length : this.yylloc.first_column - len
};
if (this.options.ranges) {
this.yylloc.range = [r[0], r[0] + this.yyleng - len];
}
this.yyleng = this.yytext.length;
return this;
},
// When called from action, caches matched text and appends it on next action
more: function () {
this._more = true;
return this;
},
// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
reject: function () {
if (this.options.backtrack_lexer) {
this._backtrack = true;
} else {
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
text: "",
token: null,
line: this.yylineno
});
}
return this;
},
// retain first n characters of the match
less: function (n) {
this.unput(this.match.slice(n));
},
// displays already matched input, i.e. for error messages
pastInput: function () {
var past = this.matched.substr(0, this.matched.length - this.match.length);
return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, "");
},
// displays upcoming input, i.e. for error messages
upcomingInput: function () {
var next = this.match;
if (next.length < 20) {
next += this._input.substr(0, 20 - next.length);
}
return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
},
// displays the character position where the lexing error occurred, i.e. for error messages
showPosition: function () {
var pre = this.pastInput();
var c = new Array(pre.length + 1).join("-");
return pre + this.upcomingInput() + "\n" + c + "^";
},
// test the lexed token: return FALSE when not a match, otherwise return token
test_match: function (match, indexed_rule) {
var token, lines, backup;
if (this.options.backtrack_lexer) {
// save context
backup = {
yylineno: this.yylineno,
yylloc: {
first_line: this.yylloc.first_line,
last_line: this.last_line,
first_column: this.yylloc.first_column,
last_column: this.yylloc.last_column
},
yytext: this.yytext,
match: this.match,
matches: this.matches,
matched: this.matched,
yyleng: this.yyleng,
offset: this.offset,
_more: this._more,
_input: this._input,
yy: this.yy,
conditionStack: this.conditionStack.slice(0),
done: this.done
};
if (this.options.ranges) {
backup.yylloc.range = this.yylloc.range.slice(0);
}
}
lines = match[0].match(/(?:\r\n?|\n).*/g);
if (lines) {
this.yylineno += lines.length;
}
this.yylloc = {
first_line: this.yylloc.last_line,
last_line: this.yylineno + 1,
first_column: this.yylloc.last_column,
last_column: lines ? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length
};
this.yytext += match[0];
this.match += match[0];
this.matches = match;
this.yyleng = this.yytext.length;
if (this.options.ranges) {
this.yylloc.range = [this.offset, this.offset += this.yyleng];
}
this._more = false;
this._backtrack = false;
this._input = this._input.slice(match[0].length);
this.matched += match[0];
token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
if (this.done && this._input) {
this.done = false;
}
if (token) {
return token;
} else if (this._backtrack) {
// recover context
for (var k in backup) {
this[k] = backup[k];
}
return false; // rule action called reject() implying the next rule should be tested instead.
}
return false;
},
// return next match in input
next: function () {
if (this.done) {
return this.EOF;
}
if (!this._input) {
this.done = true;
}
var token, match, tempMatch, index;
if (!this._more) {
this.yytext = '';
this.match = '';
}
var rules = this._currentRules();
for (var i = 0; i < rules.length; i++) {
tempMatch = this._input.match(this.rules[rules[i]]);
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
match = tempMatch;
index = i;
if (this.options.backtrack_lexer) {
token = this.test_match(tempMatch, rules[i]);
if (token !== false) {
return token;
} else if (this._backtrack) {
match = false;
continue; // rule action called reject() implying a rule MISmatch.
} else {
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
return false;
}
} else if (!this.options.flex) {
break;
}
}
}
if (match) {
token = this.test_match(match, rules[index]);
if (token !== false) {
return token;
}
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
return false;
}
if (this._input === "") {
return this.EOF;
} else {
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
text: "",
token: null,
line: this.yylineno
});
}
},
// return next match that has a token
lex: function lex() {
var r = this.next();
if (r) {
return r;
} else {
return this.lex();
}
},
// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
begin: function begin(condition) {
this.conditionStack.push(condition);
},
// pop the previously active lexer condition state off the condition stack
popState: function popState() {
var n = this.conditionStack.length - 1;
if (n > 0) {
return this.conditionStack.pop();
} else {
return this.conditionStack[0];
}
},
// produce the lexer rule set which is active for the currently active lexer condition state
_currentRules: function _currentRules() {
if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
} else {
return this.conditions["INITIAL"].rules;
}
},
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
topState: function topState(n) {
n = this.conditionStack.length - 1 - Math.abs(n || 0);
if (n >= 0) {
return this.conditionStack[n];
} else {
return "INITIAL";
}
},
// alias for begin(condition)
pushState: function pushState(condition) {
this.begin(condition);
},
// return the number of states currently on the stack
stateStackSize: function stateStackSize() {
return this.conditionStack.length;
},
options: {},
performAction: function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) {
function strip(start, end) {
return yy_.yytext = yy_.yytext.substring(start, yy_.yyleng - end + start);
}
switch ($avoiding_name_collisions) {
case 0:
if (yy_.yytext.slice(-2) === "\\\\") {
strip(0, 1);
this.begin("mu");
} else if (yy_.yytext.slice(-1) === "\\") {
strip(0, 1);
this.begin("emu");
} else {
this.begin("mu");
}
if (yy_.yytext) return 15;
break;
case 1:
return 15;
case 2:
this.popState();
return 15;
case 3:
this.begin('raw');
return 15;
case 4:
this.popState();
// Should be using `this.topState()` below, but it currently
// returns the second top instead of the first top. Opened an
// issue about it at https://github.com/zaach/jison/issues/291
if (this.conditionStack[this.conditionStack.length - 1] === 'raw') {
return 15;
} else {
strip(5, 9);
return 18;
}
case 5:
return 15;
case 6:
this.popState();
return 14;
case 7:
return 64;
case 8:
return 67;
case 9:
return 19;
case 10:
this.popState();
this.begin('raw');
return 23;
case 11:
return 56;
case 12:
return 60;
case 13:
return 29;
case 14:
return 47;
case 15:
this.popState();
return 44;
case 16:
this.popState();
return 44;
case 17:
return 34;
case 18:
return 39;
case 19:
return 52;
case 20:
return 48;
case 21:
this.unput(yy_.yytext);
this.popState();
this.begin('com');
break;
case 22:
this.popState();
return 14;
case 23:
return 48;
case 24:
return 72;
case 25:
return 71;
case 26:
return 71;
case 27:
return 86;
case 28:
// ignore whitespace
break;
case 29:
this.popState();
return 55;
case 30:
this.popState();
return 33;
case 31:
yy_.yytext = strip(1, 2).replace(/\\"/g, '"');
return 79;
case 32:
yy_.yytext = strip(1, 2).replace(/\\'/g, "'");
return 79;
case 33:
return 84;
case 34:
return 81;
case 35:
return 81;
case 36:
return 82;
case 37:
return 83;
case 38:
return 80;
case 39:
return 74;
case 40:
return 76;
case 41:
return 71;
case 42:
yy_.yytext = yy_.yytext.replace(/\\([\\\]])/g, '$1');
return 71;
case 43:
return 'INVALID';
case 44:
return 5;
}
},
rules: [/^(?:[^\x00]*?(?=(\{\{)))/, /^(?:[^\x00]+)/, /^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/, /^(?:\{\{\{\{(?=[^/]))/, /^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/, /^(?:[^\x00]+?(?=(\{\{\{\{)))/, /^(?:[\s\S]*?--(~)?\}\})/, /^(?:\()/, /^(?:\))/, /^(?:\{\{\{\{)/, /^(?:\}\}\}\})/, /^(?:\{\{(~)?>)/, /^(?:\{\{(~)?#>)/, /^(?:\{\{(~)?#\*?)/, /^(?:\{\{(~)?\/)/, /^(?:\{\{(~)?\^\s*(~)?\}\})/, /^(?:\{\{(~)?\s*else\s*(~)?\}\})/, /^(?:\{\{(~)?\^)/, /^(?:\{\{(~)?\s*else\b)/, /^(?:\{\{(~)?\{)/, /^(?:\{\{(~)?&)/, /^(?:\{\{(~)?!--)/, /^(?:\{\{(~)?![\s\S]*?\}\})/, /^(?:\{\{(~)?\*?)/, /^(?:=)/, /^(?:\.\.)/, /^(?:\.(?=([=~}\s\/.)|])))/, /^(?:[\/.])/, /^(?:\s+)/, /^(?:\}(~)?\}\})/, /^(?:(~)?\}\})/, /^(?:"(\\["]|[^"])*")/, /^(?:'(\\[']|[^'])*')/, /^(?:@)/, /^(?:true(?=([~}\s)])))/, /^(?:false(?=([~}\s)])))/, /^(?:undefined(?=([~}\s)])))/, /^(?:null(?=([~}\s)])))/, /^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/, /^(?:as\s+\|)/, /^(?:\|)/, /^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/, /^(?:\[(\\\]|[^\]])*\])/, /^(?:.)/, /^(?:$)/],
conditions: {
"mu": {
"rules": [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44],
"inclusive": false
},
"emu": {
"rules": [2],
"inclusive": false
},
"com": {
"rules": [6],
"inclusive": false
},
"raw": {
"rules": [3, 4, 5],
"inclusive": false
},
"INITIAL": {
"rules": [0, 1, 44],
"inclusive": true
}
}
};
return lexer;
}();
parser.lexer = lexer;
function Parser() {
this.yy = {};
}
Parser.prototype = parser;
parser.Parser = Parser;
return new Parser();
}();
/* eslint-disable new-cap */
function PrintVisitor() {
this.padding = 0;
}
PrintVisitor.prototype = new Visitor();
PrintVisitor.prototype.pad = function (string) {
var out = '';
for (var i = 0, l = this.padding; i < l; i++) {
out += ' ';
}
out += string + '\n';
return out;
};
PrintVisitor.prototype.Program = function (program) {
var out = '',
body = program.body,
i,
l;
if (program.blockParams) {
var blockParams = 'BLOCK PARAMS: [';
for (i = 0, l = program.blockParams.length; i < l; i++) {
blockParams += ' ' + program.blockParams[i];
}
blockParams += ' ]';
out += this.pad(blockParams);
}
for (i = 0, l = body.length; i < l; i++) {
out += this.accept(body[i]);
}
this.padding--;
return out;
};
PrintVisitor.prototype.MustacheStatement = function (mustache) {
return this.pad('{{ ' + this.SubExpression(mustache) + ' }}');
};
PrintVisitor.prototype.Decorator = function (mustache) {
return this.pad('{{ DIRECTIVE ' + this.SubExpression(mustache) + ' }}');
};
PrintVisitor.prototype.BlockStatement = PrintVisitor.prototype.DecoratorBlock = function (block) {
var out = '';
out += this.pad((block.type === 'DecoratorBlock' ? 'DIRECTIVE ' : '') + 'BLOCK:');
this.padding++;
out += this.pad(this.SubExpression(block));
if (block.program) {
out += this.pad('PROGRAM:');
this.padding++;
out += this.accept(block.program);
this.padding--;
}
if (block.inverse) {
if (block.program) {
this.padding++;
}
out += this.pad('{{^}}');
this.padding++;
out += this.accept(block.inverse);
this.padding--;
if (block.program) {
this.padding--;
}
}
this.padding--;
return out;
};
PrintVisitor.prototype.PartialStatement = function (partial) {
var content = 'PARTIAL:' + partial.name.original;
if (partial.params[0]) {
content += ' ' + this.accept(partial.params[0]);
}
if (partial.hash) {
content += ' ' + this.accept(partial.hash);
}
return this.pad('{{> ' + content + ' }}');
};
PrintVisitor.prototype.PartialBlockStatement = function (partial) {
var content = 'PARTIAL BLOCK:' + partial.name.original;
if (partial.params[0]) {
content += ' ' + this.accept(partial.params[0]);
}
if (partial.hash) {
content += ' ' + this.accept(partial.hash);
}
content += ' ' + this.pad('PROGRAM:');
this.padding++;
content += this.accept(partial.program);
this.padding--;
return this.pad('{{> ' + content + ' }}');
};
PrintVisitor.prototype.ContentStatement = function (content) {
return this.pad("CONTENT[ '" + content.value + "' ]");
};
PrintVisitor.prototype.CommentStatement = function (comment) {
return this.pad("{{! '" + comment.value + "' }}");
};
PrintVisitor.prototype.SubExpression = function (sexpr) {
var params = sexpr.params,
paramStrings = [],
hash;
for (var i = 0, l = params.length; i < l; i++) {
paramStrings.push(this.accept(params[i]));
}
params = '[' + paramStrings.join(', ') + ']';
hash = sexpr.hash ? ' ' + this.accept(sexpr.hash) : '';
return this.accept(sexpr.path) + ' ' + params + hash;
};
PrintVisitor.prototype.PathExpression = function (id) {
var path = id.parts.join('/');
return (id.data ? '@' : '') + 'PATH:' + path;
};
PrintVisitor.prototype.StringLiteral = function (string) {
return '"' + string.value + '"';
};
PrintVisitor.prototype.NumberLiteral = function (number) {
return 'NUMBER{' + number.value + '}';
};
PrintVisitor.prototype.BooleanLiteral = function (bool) {
return 'BOOLEAN{' + bool.value + '}';
};
PrintVisitor.prototype.UndefinedLiteral = function () {
return 'UNDEFINED';
};
PrintVisitor.prototype.NullLiteral = function () {
return 'NULL';
};
PrintVisitor.prototype.Hash = function (hash) {
var pairs = hash.pairs,
joinedPairs = [];
for (var i = 0, l = pairs.length; i < l; i++) {
joinedPairs.push(this.accept(pairs[i]));
}
return 'HASH{' + joinedPairs.join(', ') + '}';
};
PrintVisitor.prototype.HashPair = function (pair) {
return pair.key + '=' + this.accept(pair.value);
};
/* eslint-enable new-cap */
function validateClose(open, close) {
close = close.path ? close.path.original : close;
if (open.path.original !== close) {
var errorNode = {
loc: open.path.loc
};
throw new Exception(open.path.original + " doesn't match " + close, errorNode);
}
}
function SourceLocation(source, locInfo) {
this.source = source;
this.start = {
line: locInfo.first_line,
column: locInfo.first_column
};
this.end = {
line: locInfo.last_line,
column: locInfo.last_column
};
}
function id(token) {
if (/^\[.*\]$/.test(token)) {
return token.substring(1, token.length - 1);
} else {
return token;
}
}
function stripFlags(open, close) {
return {
open: open.charAt(2) === '~',
close: close.charAt(close.length - 3) === '~'
};
}
function stripComment(comment) {
return comment.replace(/^\{\{~?!-?-?/, '').replace(/-?-?~?\}\}$/, '');
}
function preparePath(data, parts, loc) {
loc = this.locInfo(loc);
var original = data ? '@' : '',
dig = [],
depth = 0;
for (var i = 0, l = parts.length; i < l; i++) {
var part = parts[i].part,
// If we have [] syntax then we do not treat path references as operators,
// i.e. foo.[this] resolves to approximately context.foo['this']
isLiteral = parts[i].original !== part;
original += (parts[i].separator || '') + part;
if (!isLiteral && (part === '..' || part === '.' || part === 'this')) {
if (dig.length > 0) {
throw new Exception('Invalid path: ' + original, {
loc: loc
});
} else if (part === '..') {
depth++;
}
} else {
dig.push(part);
}
}
return {
type: 'PathExpression',
data: data,
depth: depth,
parts: dig,
original: original,
loc: loc
};
}
function prepareMustache(path, params, hash, open, strip, locInfo) {
// Must use charAt to support IE pre-10
var escapeFlag = open.charAt(3) || open.charAt(2),
escaped = escapeFlag !== '{' && escapeFlag !== '&';
var decorator = /\*/.test(open);
return {
type: decorator ? 'Decorator' : 'MustacheStatement',
path: path,
params: params,
hash: hash,
escaped: escaped,
strip: strip,
loc: this.locInfo(locInfo)
};
}
function prepareRawBlock(openRawBlock, contents, close, locInfo) {
validateClose(openRawBlock, close);
locInfo = this.locInfo(locInfo);
var program = {
type: 'Program',
body: contents,
strip: {},
loc: locInfo
};
return {
type: 'BlockStatement',
path: openRawBlock.path,
params: openRawBlock.params,
hash: openRawBlock.hash,
program: program,
openStrip: {},
inverseStrip: {},
closeStrip: {},
loc: locInfo
};
}
function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) {
if (close && close.path) {
validateClose(openBlock, close);
}
var decorator = /\*/.test(openBlock.open);
program.blockParams = openBlock.blockParams;
var inverse, inverseStrip;
if (inverseAndProgram) {
if (decorator) {
throw new Exception('Unexpected inverse block on decorator', inverseAndProgram);
}
if (inverseAndProgram.chain) {
inverseAndProgram.program.body[0].closeStrip = close.strip;
}
inverseStrip = inverseAndProgram.strip;
inverse = inverseAndProgram.program;
}
if (inverted) {
inverted = inverse;
inverse = program;
program = inverted;
}
return {
type: decorator ? 'DecoratorBlock' : 'BlockStatement',
path: openBlock.path,
params: openBlock.params,
hash: openBlock.hash,
program: program,
inverse: inverse,
openStrip: openBlock.strip,
inverseStrip: inverseStrip,
closeStrip: close && close.strip,
loc: this.locInfo(locInfo)
};
}
function prepareProgram(statements, loc) {
if (!loc && statements.length) {
var firstLoc = statements[0].loc,
lastLoc = statements[statements.length - 1].loc;
/* istanbul ignore else */
if (firstLoc && lastLoc) {
loc = {
source: firstLoc.source,
start: {
line: firstLoc.start.line,
column: firstLoc.start.column
},
end: {
line: lastLoc.end.line,
column: lastLoc.end.column
}
};
}
}
return {
type: 'Program',
body: statements,
strip: {},
loc: loc
};
}
function preparePartialBlock(open, program, close, locInfo) {
validateClose(open, close);
return {
type: 'PartialBlockStatement',
name: open.path,
params: open.params,
hash: open.hash,
program: program,
openStrip: open.strip,
closeStrip: close && close.strip,
loc: this.locInfo(locInfo)
};
}
const Helpers = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
__proto__: null,
SourceLocation,
id,
prepareBlock,
prepareMustache,
preparePartialBlock,
preparePath,
prepareProgram,
prepareRawBlock,
stripComment,
stripFlags
}, Symbol.toStringTag, { value: 'Module' }));
var baseHelpers = {};
for (var helper in Helpers) {
if (Object.prototype.hasOwnProperty.call(Helpers, helper)) {
baseHelpers[helper] = Helpers[helper];
}
}
function parseWithoutProcessing(input, options) {
// Just return if an already-compiled AST was passed in.
if (input.type === 'Program') {
return input;
}
parser.yy = baseHelpers;
// Altering the shared object here, but this is ok as parser is a sync operation
parser.yy.locInfo = function (locInfo) {
return new SourceLocation(options && options.srcName, locInfo);
};
var ast = parser.parse(input);
return ast;
}
function parse(input, options) {
var ast = parseWithoutProcessing(input, options);
var strip = new WhitespaceControl(options);
return strip.accept(ast);
}
/**
* generated from https://raw.githubusercontent.com/w3c/html/26b5126f96f736f796b9e29718138919dd513744/entities.json
* do not edit
*/
var namedCharRefs = {
Aacute: "Á",
aacute: "á",
Abreve: "Ă",
abreve: "ă",
ac: "∾",
acd: "∿",
acE: "∾̳",
Acirc: "Â",
acirc: "â",
acute: "´",
Acy: "А",
acy: "а",
AElig: "Æ",
aelig: "æ",
af: "\u2061",
Afr: "𝔄",
afr: "𝔞",
Agrave: "À",
agrave: "à",
alefsym: "ℵ",
aleph: "ℵ",
Alpha: "Α",
alpha: "α",
Amacr: "Ā",
amacr: "ā",
amalg: "⨿",
amp: "&",
AMP: "&",
andand: "⩕",
And: "⩓",
and: "∧",
andd: "⩜",
andslope: "⩘",
andv: "⩚",
ang: "∠",
ange: "⦤",
angle: "∠",
angmsdaa: "⦨",
angmsdab: "⦩",
angmsdac: "⦪",
angmsdad: "⦫",
angmsdae: "⦬",
angmsdaf: "⦭",
angmsdag: "⦮",
angmsdah: "⦯",
angmsd: "∡",
angrt: "∟",
angrtvb: "⊾",
angrtvbd: "⦝",
angsph: "∢",
angst: "Å",
angzarr: "⍼",
Aogon: "Ą",
aogon: "ą",
Aopf: "𝔸",
aopf: "𝕒",
apacir: "⩯",
ap: "≈",
apE: "⩰",
ape: "≊",
apid: "≋",
apos: "'",
ApplyFunction: "\u2061",
approx: "≈",
approxeq: "≊",
Aring: "Å",
aring: "å",
Ascr: "𝒜",
ascr: "𝒶",
Assign: "≔",
ast: "*",
asymp: "≈",
asympeq: "≍",
Atilde: "Ã",
atilde: "ã",
Auml: "Ä",
auml: "ä",
awconint: "∳",
awint: "⨑",
backcong: "≌",
backepsilon: "϶",
backprime: "‵",
backsim: "∽",
backsimeq: "⋍",
Backslash: "∖",
Barv: "⫧",
barvee: "⊽",
barwed: "⌅",
Barwed: "⌆",
barwedge: "⌅",
bbrk: "⎵",
bbrktbrk: "⎶",
bcong: "≌",
Bcy: "Б",
bcy: "б",
bdquo: "„",
becaus: "∵",
because: "∵",
Because: "∵",
bemptyv: "⦰",
bepsi: "϶",
bernou: "ℬ",
Bernoullis: "ℬ",
Beta: "Β",
beta: "β",
beth: "ℶ",
between: "≬",
Bfr: "𝔅",
bfr: "𝔟",
bigcap: "⋂",
bigcirc: "◯",
bigcup: "⋃",
bigodot: "⨀",
bigoplus: "⨁",
bigotimes: "⨂",
bigsqcup: "⨆",
bigstar: "★",
bigtriangledown: "▽",
bigtriangleup: "△",
biguplus: "⨄",
bigvee: "⋁",
bigwedge: "⋀",
bkarow: "⤍",
blacklozenge: "⧫",
blacksquare: "▪",
blacktriangle: "▴",
blacktriangledown: "▾",
blacktriangleleft: "◂",
blacktriangleright: "▸",
blank: "␣",
blk12: "▒",
blk14: "░",
blk34: "▓",
block: "█",
bne: "=⃥",
bnequiv: "≡⃥",
bNot: "⫭",
bnot: "⌐",
Bopf: "𝔹",
bopf: "𝕓",
bot: "⊥",
bottom: "⊥",
bowtie: "⋈",
boxbox: "⧉",
boxdl: "┐",
boxdL: "╕",
boxDl: "╖",
boxDL: "╗",
boxdr: "┌",
boxdR: "╒",
boxDr: "╓",
boxDR: "╔",
boxh: "─",
boxH: "═",
boxhd: "┬",
boxHd: "╤",
boxhD: "╥",
boxHD: "╦",
boxhu: "┴",
boxHu: "╧",
boxhU: "╨",
boxHU: "╩",
boxminus: "⊟",
boxplus: "⊞",
boxtimes: "⊠",
boxul: "┘",
boxuL: "╛",
boxUl: "╜",
boxUL: "╝",
boxur: "└",
boxuR: "╘",
boxUr: "╙",
boxUR: "╚",
boxv: "│",
boxV: "║",
boxvh: "┼",
boxvH: "╪",
boxVh: "╫",
boxVH: "╬",
boxvl: "┤",
boxvL: "╡",
boxVl: "╢",
boxVL: "╣",
boxvr: "├",
boxvR: "╞",
boxVr: "╟",
boxVR: "╠",
bprime: "‵",
breve: "˘",
Breve: "˘",
brvbar: "¦",
bscr: "𝒷",
Bscr: "ℬ",
bsemi: "⁏",
bsim: "∽",
bsime: "⋍",
bsolb: "⧅",
bsol: "\\",
bsolhsub: "⟈",
bull: "•",
bullet: "•",
bump: "≎",
bumpE: "⪮",
bumpe: "≏",
Bumpeq: "≎",
bumpeq: "≏",
Cacute: "Ć",
cacute: "ć",
capand: "⩄",
capbrcup: "⩉",
capcap: "⩋",
cap: "∩",
Cap: "⋒",
capcup: "⩇",
capdot: "⩀",
CapitalDifferentialD: "ⅅ",
caps: "∩︀",
caret: "⁁",
caron: "ˇ",
Cayleys: "ℭ",
ccaps: "⩍",
Ccaron: "Č",
ccaron: "č",
Ccedil: "Ç",
ccedil: "ç",
Ccirc: "Ĉ",
ccirc: "ĉ",
Cconint: "∰",
ccups: "⩌",
ccupssm: "⩐",
Cdot: "Ċ",
cdot: "ċ",
cedil: "¸",
Cedilla: "¸",
cemptyv: "⦲",
cent: "¢",
centerdot: "·",
CenterDot: "·",
cfr: "𝔠",
Cfr: "ℭ",
CHcy: "Ч",
chcy: "ч",
check: "✓",
checkmark: "✓",
Chi: "Χ",
chi: "χ",
circ: "ˆ",
circeq: "≗",
circlearrowleft: "↺",
circlearrowright: "↻",
circledast: "⊛",
circledcirc: "⊚",
circleddash: "⊝",
CircleDot: "⊙",
circledR: "®",
circledS: "Ⓢ",
CircleMinus: "⊖",
CirclePlus: "⊕",
CircleTimes: "⊗",
cir: "○",
cirE: "⧃",
cire: "≗",
cirfnint: "⨐",
cirmid: "⫯",
cirscir: "⧂",
ClockwiseContourIntegral: "∲",
CloseCurlyDoubleQuote: "”",
CloseCurlyQuote: "’",
clubs: "♣",
clubsuit: "♣",
colon: ":",
Colon: "∷",
Colone: "⩴",
colone: "≔",
coloneq: "≔",
comma: ",",
commat: "@",
comp: "∁",
compfn: "∘",
complement: "∁",
complexes: "ℂ",
cong: "≅",
congdot: "⩭",
Congruent: "≡",
conint: "∮",
Conint: "∯",
ContourIntegral: "∮",
copf: "𝕔",
Copf: "ℂ",
coprod: "∐",
Coproduct: "∐",
copy: "©",
COPY: "©",
copysr: "℗",
CounterClockwiseContourIntegral: "∳",
crarr: "↵",
cross: "✗",
Cross: "⨯",
Cscr: "𝒞",
cscr: "𝒸",
csub: "⫏",
csube: "⫑",
csup: "⫐",
csupe: "⫒",
ctdot: "⋯",
cudarrl: "⤸",
cudarrr: "⤵",
cuepr: "⋞",
cuesc: "⋟",
cularr: "↶",
cularrp: "⤽",
cupbrcap: "⩈",
cupcap: "⩆",
CupCap: "≍",
cup: "∪",
Cup: "⋓",
cupcup: "⩊",
cupdot: "⊍",
cupor: "⩅",
cups: "∪︀",
curarr: "↷",
curarrm: "⤼",
curlyeqprec: "⋞",
curlyeqsucc: "⋟",
curlyvee: "⋎",
curlywedge: "⋏",
curren: "¤",
curvearrowleft: "↶",
curvearrowright: "↷",
cuvee: "⋎",
cuwed: "⋏",
cwconint: "∲",
cwint: "∱",
cylcty: "⌭",
dagger: "†",
Dagger: "‡",
daleth: "ℸ",
darr: "↓",
Darr: "↡",
dArr: "⇓",
dash: "‐",
Dashv: "⫤",
dashv: "⊣",
dbkarow: "⤏",
dblac: "˝",
Dcaron: "Ď",
dcaron: "ď",
Dcy: "Д",
dcy: "д",
ddagger: "‡",
ddarr: "⇊",
DD: "ⅅ",
dd: "ⅆ",
DDotrahd: "⤑",
ddotseq: "⩷",
deg: "°",
Del: "∇",
Delta: "Δ",
delta: "δ",
demptyv: "⦱",
dfisht: "⥿",
Dfr: "𝔇",
dfr: "𝔡",
dHar: "⥥",
dharl: "⇃",
dharr: "⇂",
DiacriticalAcute: "´",
DiacriticalDot: "˙",
DiacriticalDoubleAcute: "˝",
DiacriticalGrave: "`",
DiacriticalTilde: "˜",
diam: "⋄",
diamond: "⋄",
Diamond: "⋄",
diamondsuit: "♦",
diams: "♦",
die: "¨",
DifferentialD: "ⅆ",
digamma: "ϝ",
disin: "⋲",
div: "÷",
divide: "÷",
divideontimes: "⋇",
divonx: "⋇",
DJcy: "Ђ",
djcy: "ђ",
dlcorn: "⌞",
dlcrop: "⌍",
dollar: "$",
Dopf: "𝔻",
dopf: "𝕕",
Dot: "¨",
dot: "˙",
DotDot: "⃜",
doteq: "≐",
doteqdot: "≑",
DotEqual: "≐",
dotminus: "∸",
dotplus: "∔",
dotsquare: "⊡",
doublebarwedge: "⌆",
DoubleContourIntegral: "∯",
DoubleDot: "¨",
DoubleDownArrow: "⇓",
DoubleLeftArrow: "⇐",
DoubleLeftRightArrow: "⇔",
DoubleLeftTee: "⫤",
DoubleLongLeftArrow: "⟸",
DoubleLongLeftRightArrow: "⟺",
DoubleLongRightArrow: "⟹",
DoubleRightArrow: "⇒",
DoubleRightTee: "⊨",
DoubleUpArrow: "⇑",
DoubleUpDownArrow: "⇕",
DoubleVerticalBar: "∥",
DownArrowBar: "⤓",
downarrow: "↓",
DownArrow: "↓",
Downarrow: "⇓",
DownArrowUpArrow: "⇵",
DownBreve: "̑",
downdownarrows: "⇊",
downharpoonleft: "⇃",
downharpoonright: "⇂",
DownLeftRightVector: "⥐",
DownLeftTeeVector: "⥞",
DownLeftVectorBar: "⥖",
DownLeftVector: "↽",
DownRightTeeVector: "⥟",
DownRightVectorBar: "⥗",
DownRightVector: "⇁",
DownTeeArrow: "↧",
DownTee: "⊤",
drbkarow: "⤐",
drcorn: "⌟",
drcrop: "⌌",
Dscr: "𝒟",
dscr: "𝒹",
DScy: "Ѕ",
dscy: "ѕ",
dsol: "⧶",
Dstrok: "Đ",
dstrok: "đ",
dtdot: "⋱",
dtri: "▿",
dtrif: "▾",
duarr: "⇵",
duhar: "⥯",
dwangle: "⦦",
DZcy: "Џ",
dzcy: "џ",
dzigrarr: "⟿",
Eacute: "É",
eacute: "é",
easter: "⩮",
Ecaron: "Ě",
ecaron: "ě",
Ecirc: "Ê",
ecirc: "ê",
ecir: "≖",
ecolon: "≕",
Ecy: "Э",
ecy: "э",
eDDot: "⩷",
Edot: "Ė",
edot: "ė",
eDot: "≑",
ee: "ⅇ",
efDot: "≒",
Efr: "𝔈",
efr: "𝔢",
eg: "⪚",
Egrave: "È",
egrave: "è",
egs: "⪖",
egsdot: "⪘",
el: "⪙",
Element: "∈",
elinters: "⏧",
ell: "ℓ",
els: "⪕",
elsdot: "⪗",
Emacr: "Ē",
emacr: "ē",
empty: "∅",
emptyset: "∅",
EmptySmallSquare: "◻",
emptyv: "∅",
EmptyVerySmallSquare: "▫",
emsp13: " ",
emsp14: " ",
emsp: " ",
ENG: "Ŋ",
eng: "ŋ",
ensp: " ",
Eogon: "Ę",
eogon: "ę",
Eopf: "𝔼",
eopf: "𝕖",
epar: "⋕",
eparsl: "⧣",
eplus: "⩱",
epsi: "ε",
Epsilon: "Ε",
epsilon: "ε",
epsiv: "ϵ",
eqcirc: "≖",
eqcolon: "≕",
eqsim: "≂",
eqslantgtr: "⪖",
eqslantless: "⪕",
Equal: "⩵",
equals: "=",
EqualTilde: "≂",
equest: "≟",
Equilibrium: "⇌",
equiv: "≡",
equivDD: "⩸",
eqvparsl: "⧥",
erarr: "⥱",
erDot: "≓",
escr: "ℯ",
Escr: "ℰ",
esdot: "≐",
Esim: "⩳",
esim: "≂",
Eta: "Η",
eta: "η",
ETH: "Ð",
eth: "ð",
Euml: "Ë",
euml: "ë",
euro: "€",
excl: "!",
exist: "∃",
Exists: "∃",
expectation: "ℰ",
exponentiale: "ⅇ",
ExponentialE: "ⅇ",
fallingdotseq: "≒",
Fcy: "Ф",
fcy: "ф",
female: "♀",
ffilig: "ffi",
fflig: "ff",
ffllig: "ffl",
Ffr: "𝔉",
ffr: "𝔣",
filig: "fi",
FilledSmallSquare: "◼",
FilledVerySmallSquare: "▪",
fjlig: "fj",
flat: "♭",
fllig: "fl",
fltns: "▱",
fnof: "ƒ",
Fopf: "𝔽",
fopf: "𝕗",
forall: "∀",
ForAll: "∀",
fork: "⋔",
forkv: "⫙",
Fouriertrf: "ℱ",
fpartint: "⨍",
frac12: "½",
frac13: "⅓",
frac14: "¼",
frac15: "⅕",
frac16: "⅙",
frac18: "⅛",
frac23: "⅔",
frac25: "⅖",
frac34: "¾",
frac35: "⅗",
frac38: "⅜",
frac45: "⅘",
frac56: "⅚",
frac58: "⅝",
frac78: "⅞",
frasl: "⁄",
frown: "⌢",
fscr: "𝒻",
Fscr: "ℱ",
gacute: "ǵ",
Gamma: "Γ",
gamma: "γ",
Gammad: "Ϝ",
gammad: "ϝ",
gap: "⪆",
Gbreve: "Ğ",
gbreve: "ğ",
Gcedil: "Ģ",
Gcirc: "Ĝ",
gcirc: "ĝ",
Gcy: "Г",
gcy: "г",
Gdot: "Ġ",
gdot: "ġ",
ge: "≥",
gE: "≧",
gEl: "⪌",
gel: "⋛",
geq: "≥",
geqq: "≧",
geqslant: "⩾",
gescc: "⪩",
ges: "⩾",
gesdot: "⪀",
gesdoto: "⪂",
gesdotol: "⪄",
gesl: "⋛︀",
gesles: "⪔",
Gfr: "𝔊",
gfr: "𝔤",
gg: "≫",
Gg: "⋙",
ggg: "⋙",
gimel: "ℷ",
GJcy: "Ѓ",
gjcy: "ѓ",
gla: "⪥",
gl: "≷",
glE: "⪒",
glj: "⪤",
gnap: "⪊",
gnapprox: "⪊",
gne: "⪈",
gnE: "≩",
gneq: "⪈",
gneqq: "≩",
gnsim: "⋧",
Gopf: "𝔾",
gopf: "𝕘",
grave: "`",
GreaterEqual: "≥",
GreaterEqualLess: "⋛",
GreaterFullEqual: "≧",
GreaterGreater: "⪢",
GreaterLess: "≷",
GreaterSlantEqual: "⩾",
GreaterTilde: "≳",
Gscr: "𝒢",
gscr: "ℊ",
gsim: "≳",
gsime: "⪎",
gsiml: "⪐",
gtcc: "⪧",
gtcir: "⩺",
gt: ">",
GT: ">",
Gt: "≫",
gtdot: "⋗",
gtlPar: "⦕",
gtquest: "⩼",
gtrapprox: "⪆",
gtrarr: "⥸",
gtrdot: "⋗",
gtreqless: "⋛",
gtreqqless: "⪌",
gtrless: "≷",
gtrsim: "≳",
gvertneqq: "≩︀",
gvnE: "≩︀",
Hacek: "ˇ",
hairsp: " ",
half: "½",
hamilt: "ℋ",
HARDcy: "Ъ",
hardcy: "ъ",
harrcir: "⥈",
harr: "↔",
hArr: "⇔",
harrw: "↭",
Hat: "^",
hbar: "ℏ",
Hcirc: "Ĥ",
hcirc: "ĥ",
hearts: "♥",
heartsuit: "♥",
hellip: "…",
hercon: "⊹",
hfr: "𝔥",
Hfr: "ℌ",
HilbertSpace: "ℋ",
hksearow: "⤥",
hkswarow: "⤦",
hoarr: "⇿",
homtht: "∻",
hookleftarrow: "↩",
hookrightarrow: "↪",
hopf: "𝕙",
Hopf: "ℍ",
horbar: "―",
HorizontalLine: "─",
hscr: "𝒽",
Hscr: "ℋ",
hslash: "ℏ",
Hstrok: "Ħ",
hstrok: "ħ",
HumpDownHump: "≎",
HumpEqual: "≏",
hybull: "⁃",
hyphen: "‐",
Iacute: "Í",
iacute: "í",
ic: "\u2063",
Icirc: "Î",
icirc: "î",
Icy: "И",
icy: "и",
Idot: "İ",
IEcy: "Е",
iecy: "е",
iexcl: "¡",
iff: "⇔",
ifr: "𝔦",
Ifr: "ℑ",
Igrave: "Ì",
igrave: "ì",
ii: "ⅈ",
iiiint: "⨌",
iiint: "∭",
iinfin: "⧜",
iiota: "℩",
IJlig: "IJ",
ijlig: "ij",
Imacr: "Ī",
imacr: "ī",
image: "ℑ",
ImaginaryI: "ⅈ",
imagline: "ℐ",
imagpart: "ℑ",
imath: "ı",
Im: "ℑ",
imof: "⊷",
imped: "Ƶ",
Implies: "⇒",
incare: "℅",
in: "∈",
infin: "∞",
infintie: "⧝",
inodot: "ı",
intcal: "⊺",
int: "∫",
Int: "∬",
integers: "ℤ",
Integral: "∫",
intercal: "⊺",
Intersection: "⋂",
intlarhk: "⨗",
intprod: "⨼",
InvisibleComma: "\u2063",
InvisibleTimes: "\u2062",
IOcy: "Ё",
iocy: "ё",
Iogon: "Į",
iogon: "į",
Iopf: "𝕀",
iopf: "𝕚",
Iota: "Ι",
iota: "ι",
iprod: "⨼",
iquest: "¿",
iscr: "𝒾",
Iscr: "ℐ",
isin: "∈",
isindot: "⋵",
isinE: "⋹",
isins: "⋴",
isinsv: "⋳",
isinv: "∈",
it: "\u2062",
Itilde: "Ĩ",
itilde: "ĩ",
Iukcy: "І",
iukcy: "і",
Iuml: "Ï",
iuml: "ï",
Jcirc: "Ĵ",
jcirc: "ĵ",
Jcy: "Й",
jcy: "й",
Jfr: "𝔍",
jfr: "𝔧",
jmath: "ȷ",
Jopf: "𝕁",
jopf: "𝕛",
Jscr: "𝒥",
jscr: "𝒿",
Jsercy: "Ј",
jsercy: "ј",
Jukcy: "Є",
jukcy: "є",
Kappa: "Κ",
kappa: "κ",
kappav: "ϰ",
Kcedil: "Ķ",
kcedil: "ķ",
Kcy: "К",
kcy: "к",
Kfr: "𝔎",
kfr: "𝔨",
kgreen: "ĸ",
KHcy: "Х",
khcy: "х",
KJcy: "Ќ",
kjcy: "ќ",
Kopf: "𝕂",
kopf: "𝕜",
Kscr: "𝒦",
kscr: "𝓀",
lAarr: "⇚",
Lacute: "Ĺ",
lacute: "ĺ",
laemptyv: "⦴",
lagran: "ℒ",
Lambda: "Λ",
lambda: "λ",
lang: "⟨",
Lang: "⟪",
langd: "⦑",
langle: "⟨",
lap: "⪅",
Laplacetrf: "ℒ",
laquo: "«",
larrb: "⇤",
larrbfs: "⤟",
larr: "←",
Larr: "↞",
lArr: "⇐",
larrfs: "⤝",
larrhk: "↩",
larrlp: "↫",
larrpl: "⤹",
larrsim: "⥳",
larrtl: "↢",
latail: "⤙",
lAtail: "⤛",
lat: "⪫",
late: "⪭",
lates: "⪭︀",
lbarr: "⤌",
lBarr: "⤎",
lbbrk: "❲",
lbrace: "{",
lbrack: "[",
lbrke: "⦋",
lbrksld: "⦏",
lbrkslu: "⦍",
Lcaron: "Ľ",
lcaron: "ľ",
Lcedil: "Ļ",
lcedil: "ļ",
lceil: "⌈",
lcub: "{",
Lcy: "Л",
lcy: "л",
ldca: "⤶",
ldquo: "“",
ldquor: "„",
ldrdhar: "⥧",
ldrushar: "⥋",
ldsh: "↲",
le: "≤",
lE: "≦",
LeftAngleBracket: "⟨",
LeftArrowBar: "⇤",
leftarrow: "←",
LeftArrow: "←",
Leftarrow: "⇐",
LeftArrowRightArrow: "⇆",
leftarrowtail: "↢",
LeftCeiling: "⌈",
LeftDoubleBracket: "⟦",
LeftDownTeeVector: "⥡",
LeftDownVectorBar: "⥙",
LeftDownVector: "⇃",
LeftFloor: "⌊",
leftharpoondown: "↽",
leftharpoonup: "↼",
leftleftarrows: "⇇",
leftrightarrow: "↔",
LeftRightArrow: "↔",
Leftrightarrow: "⇔",
leftrightarrows: "⇆",
leftrightharpoons: "⇋",
leftrightsquigarrow: "↭",
LeftRightVector: "⥎",
LeftTeeArrow: "↤",
LeftTee: "⊣",
LeftTeeVector: "⥚",
leftthreetimes: "⋋",
LeftTriangleBar: "⧏",
LeftTriangle: "⊲",
LeftTriangleEqual: "⊴",
LeftUpDownVector: "⥑",
LeftUpTeeVector: "⥠",
LeftUpVectorBar: "⥘",
LeftUpVector: "↿",
LeftVectorBar: "⥒",
LeftVector: "↼",
lEg: "⪋",
leg: "⋚",
leq: "≤",
leqq: "≦",
leqslant: "⩽",
lescc: "⪨",
les: "⩽",
lesdot: "⩿",
lesdoto: "⪁",
lesdotor: "⪃",
lesg: "⋚︀",
lesges: "⪓",
lessapprox: "⪅",
lessdot: "⋖",
lesseqgtr: "⋚",
lesseqqgtr: "⪋",
LessEqualGreater: "⋚",
LessFullEqual: "≦",
LessGreater: "≶",
lessgtr: "≶",
LessLess: "⪡",
lesssim: "≲",
LessSlantEqual: "⩽",
LessTilde: "≲",
lfisht: "⥼",
lfloor: "⌊",
Lfr: "𝔏",
lfr: "𝔩",
lg: "≶",
lgE: "⪑",
lHar: "⥢",
lhard: "↽",
lharu: "↼",
lharul: "⥪",
lhblk: "▄",
LJcy: "Љ",
ljcy: "љ",
llarr: "⇇",
ll: "≪",
Ll: "⋘",
llcorner: "⌞",
Lleftarrow: "⇚",
llhard: "⥫",
lltri: "◺",
Lmidot: "Ŀ",
lmidot: "ŀ",
lmoustache: "⎰",
lmoust: "⎰",
lnap: "⪉",
lnapprox: "⪉",
lne: "⪇",
lnE: "≨",
lneq: "⪇",
lneqq: "≨",
lnsim: "⋦",
loang: "⟬",
loarr: "⇽",
lobrk: "⟦",
longleftarrow: "⟵",
LongLeftArrow: "⟵",
Longleftarrow: "⟸",
longleftrightarrow: "⟷",
LongLeftRightArrow: "⟷",
Longleftrightarrow: "⟺",
longmapsto: "⟼",
longrightarrow: "⟶",
LongRightArrow: "⟶",
Longrightarrow: "⟹",
looparrowleft: "↫",
looparrowright: "↬",
lopar: "⦅",
Lopf: "𝕃",
lopf: "𝕝",
loplus: "⨭",
lotimes: "⨴",
lowast: "∗",
lowbar: "_",
LowerLeftArrow: "↙",
LowerRightArrow: "↘",
loz: "◊",
lozenge: "◊",
lozf: "⧫",
lpar: "(",
lparlt: "⦓",
lrarr: "⇆",
lrcorner: "⌟",
lrhar: "⇋",
lrhard: "⥭",
lrm: "\u200e",
lrtri: "⊿",
lsaquo: "‹",
lscr: "𝓁",
Lscr: "ℒ",
lsh: "↰",
Lsh: "↰",
lsim: "≲",
lsime: "⪍",
lsimg: "⪏",
lsqb: "[",
lsquo: "‘",
lsquor: "‚",
Lstrok: "Ł",
lstrok: "ł",
ltcc: "⪦",
ltcir: "⩹",
lt: "<",
LT: "<",
Lt: "≪",
ltdot: "⋖",
lthree: "⋋",
ltimes: "⋉",
ltlarr: "⥶",
ltquest: "⩻",
ltri: "◃",
ltrie: "⊴",
ltrif: "◂",
ltrPar: "⦖",
lurdshar: "⥊",
luruhar: "⥦",
lvertneqq: "≨︀",
lvnE: "≨︀",
macr: "¯",
male: "♂",
malt: "✠",
maltese: "✠",
Map: "⤅",
map: "↦",
mapsto: "↦",
mapstodown: "↧",
mapstoleft: "↤",
mapstoup: "↥",
marker: "▮",
mcomma: "⨩",
Mcy: "М",
mcy: "м",
mdash: "—",
mDDot: "∺",
measuredangle: "∡",
MediumSpace: " ",
Mellintrf: "ℳ",
Mfr: "𝔐",
mfr: "𝔪",
mho: "℧",
micro: "µ",
midast: "*",
midcir: "⫰",
mid: "∣",
middot: "·",
minusb: "⊟",
minus: "−",
minusd: "∸",
minusdu: "⨪",
MinusPlus: "∓",
mlcp: "⫛",
mldr: "…",
mnplus: "∓",
models: "⊧",
Mopf: "𝕄",
mopf: "𝕞",
mp: "∓",
mscr: "𝓂",
Mscr: "ℳ",
mstpos: "∾",
Mu: "Μ",
mu: "μ",
multimap: "⊸",
mumap: "⊸",
nabla: "∇",
Nacute: "Ń",
nacute: "ń",
nang: "∠⃒",
nap: "≉",
napE: "⩰̸",
napid: "≋̸",
napos: "ʼn",
napprox: "≉",
natural: "♮",
naturals: "ℕ",
natur: "♮",
nbsp: " ",
nbump: "≎̸",
nbumpe: "≏̸",
ncap: "⩃",
Ncaron: "Ň",
ncaron: "ň",
Ncedil: "Ņ",
ncedil: "ņ",
ncong: "≇",
ncongdot: "⩭̸",
ncup: "⩂",
Ncy: "Н",
ncy: "н",
ndash: "–",
nearhk: "⤤",
nearr: "↗",
neArr: "⇗",
nearrow: "↗",
ne: "≠",
nedot: "≐̸",
NegativeMediumSpace: "",
NegativeThickSpace: "",
NegativeThinSpace: "",
NegativeVeryThinSpace: "",
nequiv: "≢",
nesear: "⤨",
nesim: "≂̸",
NestedGreaterGreater: "≫",
NestedLessLess: "≪",
NewLine: "\u000a",
nexist: "∄",
nexists: "∄",
Nfr: "𝔑",
nfr: "𝔫",
ngE: "≧̸",
nge: "≱",
ngeq: "≱",
ngeqq: "≧̸",
ngeqslant: "⩾̸",
nges: "⩾̸",
nGg: "⋙̸",
ngsim: "≵",
nGt: "≫⃒",
ngt: "≯",
ngtr: "≯",
nGtv: "≫̸",
nharr: "↮",
nhArr: "⇎",
nhpar: "⫲",
ni: "∋",
nis: "⋼",
nisd: "⋺",
niv: "∋",
NJcy: "Њ",
njcy: "њ",
nlarr: "↚",
nlArr: "⇍",
nldr: "‥",
nlE: "≦̸",
nle: "≰",
nleftarrow: "↚",
nLeftarrow: "⇍",
nleftrightarrow: "↮",
nLeftrightarrow: "⇎",
nleq: "≰",
nleqq: "≦̸",
nleqslant: "⩽̸",
nles: "⩽̸",
nless: "≮",
nLl: "⋘̸",
nlsim: "≴",
nLt: "≪⃒",
nlt: "≮",
nltri: "⋪",
nltrie: "⋬",
nLtv: "≪̸",
nmid: "∤",
NoBreak: "\u2060",
NonBreakingSpace: " ",
nopf: "𝕟",
Nopf: "ℕ",
Not: "⫬",
not: "¬",
NotCongruent: "≢",
NotCupCap: "≭",
NotDoubleVerticalBar: "∦",
NotElement: "∉",
NotEqual: "≠",
NotEqualTilde: "≂̸",
NotExists: "∄",
NotGreater: "≯",
NotGreaterEqual: "≱",
NotGreaterFullEqual: "≧̸",
NotGreaterGreater: "≫̸",
NotGreaterLess: "≹",
NotGreaterSlantEqual: "⩾̸",
NotGreaterTilde: "≵",
NotHumpDownHump: "≎̸",
NotHumpEqual: "≏̸",
notin: "∉",
notindot: "⋵̸",
notinE: "⋹̸",
notinva: "∉",
notinvb: "⋷",
notinvc: "⋶",
NotLeftTriangleBar: "⧏̸",
NotLeftTriangle: "⋪",
NotLeftTriangleEqual: "⋬",
NotLess: "≮",
NotLessEqual: "≰",
NotLessGreater: "≸",
NotLessLess: "≪̸",
NotLessSlantEqual: "⩽̸",
NotLessTilde: "≴",
NotNestedGreaterGreater: "⪢̸",
NotNestedLessLess: "⪡̸",
notni: "∌",
notniva: "∌",
notnivb: "⋾",
notnivc: "⋽",
NotPrecedes: "⊀",
NotPrecedesEqual: "⪯̸",
NotPrecedesSlantEqual: "⋠",
NotReverseElement: "∌",
NotRightTriangleBar: "⧐̸",
NotRightTriangle: "⋫",
NotRightTriangleEqual: "⋭",
NotSquareSubset: "⊏̸",
NotSquareSubsetEqual: "⋢",
NotSquareSuperset: "⊐̸",
NotSquareSupersetEqual: "⋣",
NotSubset: "⊂⃒",
NotSubsetEqual: "⊈",
NotSucceeds: "⊁",
NotSucceedsEqual: "⪰̸",
NotSucceedsSlantEqual: "⋡",
NotSucceedsTilde: "≿̸",
NotSuperset: "⊃⃒",
NotSupersetEqual: "⊉",
NotTilde: "≁",
NotTildeEqual: "≄",
NotTildeFullEqual: "≇",
NotTildeTilde: "≉",
NotVerticalBar: "∤",
nparallel: "∦",
npar: "∦",
nparsl: "⫽⃥",
npart: "∂̸",
npolint: "⨔",
npr: "⊀",
nprcue: "⋠",
nprec: "⊀",
npreceq: "⪯̸",
npre: "⪯̸",
nrarrc: "⤳̸",
nrarr: "↛",
nrArr: "⇏",
nrarrw: "↝̸",
nrightarrow: "↛",
nRightarrow: "⇏",
nrtri: "⋫",
nrtrie: "⋭",
nsc: "⊁",
nsccue: "⋡",
nsce: "⪰̸",
Nscr: "𝒩",
nscr: "𝓃",
nshortmid: "∤",
nshortparallel: "∦",
nsim: "≁",
nsime: "≄",
nsimeq: "≄",
nsmid: "∤",
nspar: "∦",
nsqsube: "⋢",
nsqsupe: "⋣",
nsub: "⊄",
nsubE: "⫅̸",
nsube: "⊈",
nsubset: "⊂⃒",
nsubseteq: "⊈",
nsubseteqq: "⫅̸",
nsucc: "⊁",
nsucceq: "⪰̸",
nsup: "⊅",
nsupE: "⫆̸",
nsupe: "⊉",
nsupset: "⊃⃒",
nsupseteq: "⊉",
nsupseteqq: "⫆̸",
ntgl: "≹",
Ntilde: "Ñ",
ntilde: "ñ",
ntlg: "≸",
ntriangleleft: "⋪",
ntrianglelefteq: "⋬",
ntriangleright: "⋫",
ntrianglerighteq: "⋭",
Nu: "Ν",
nu: "ν",
num: "#",
numero: "№",
numsp: " ",
nvap: "≍⃒",
nvdash: "⊬",
nvDash: "⊭",
nVdash: "⊮",
nVDash: "⊯",
nvge: "≥⃒",
nvgt: ">⃒",
nvHarr: "⤄",
nvinfin: "⧞",
nvlArr: "⤂",
nvle: "≤⃒",
nvlt: "<⃒",
nvltrie: "⊴⃒",
nvrArr: "⤃",
nvrtrie: "⊵⃒",
nvsim: "∼⃒",
nwarhk: "⤣",
nwarr: "↖",
nwArr: "⇖",
nwarrow: "↖",
nwnear: "⤧",
Oacute: "Ó",
oacute: "ó",
oast: "⊛",
Ocirc: "Ô",
ocirc: "ô",
ocir: "⊚",
Ocy: "О",
ocy: "о",
odash: "⊝",
Odblac: "Ő",
odblac: "ő",
odiv: "⨸",
odot: "⊙",
odsold: "⦼",
OElig: "Œ",
oelig: "œ",
ofcir: "⦿",
Ofr: "𝔒",
ofr: "𝔬",
ogon: "˛",
Ograve: "Ò",
ograve: "ò",
ogt: "⧁",
ohbar: "⦵",
ohm: "Ω",
oint: "∮",
olarr: "↺",
olcir: "⦾",
olcross: "⦻",
oline: "‾",
olt: "⧀",
Omacr: "Ō",
omacr: "ō",
Omega: "Ω",
omega: "ω",
Omicron: "Ο",
omicron: "ο",
omid: "⦶",
ominus: "⊖",
Oopf: "𝕆",
oopf: "𝕠",
opar: "⦷",
OpenCurlyDoubleQuote: "“",
OpenCurlyQuote: "‘",
operp: "⦹",
oplus: "⊕",
orarr: "↻",
Or: "⩔",
or: "∨",
ord: "⩝",
order: "ℴ",
orderof: "ℴ",
ordf: "ª",
ordm: "º",
origof: "⊶",
oror: "⩖",
orslope: "⩗",
orv: "⩛",
oS: "Ⓢ",
Oscr: "𝒪",
oscr: "ℴ",
Oslash: "Ø",
oslash: "ø",
osol: "⊘",
Otilde: "Õ",
otilde: "õ",
otimesas: "⨶",
Otimes: "⨷",
otimes: "⊗",
Ouml: "Ö",
ouml: "ö",
ovbar: "⌽",
OverBar: "‾",
OverBrace: "⏞",
OverBracket: "⎴",
OverParenthesis: "⏜",
para: "¶",
parallel: "∥",
par: "∥",
parsim: "⫳",
parsl: "⫽",
part: "∂",
PartialD: "∂",
Pcy: "П",
pcy: "п",
percnt: "%",
period: ".",
permil: "‰",
perp: "⊥",
pertenk: "‱",
Pfr: "𝔓",
pfr: "𝔭",
Phi: "Φ",
phi: "φ",
phiv: "ϕ",
phmmat: "ℳ",
phone: "☎",
Pi: "Π",
pi: "π",
pitchfork: "⋔",
piv: "ϖ",
planck: "ℏ",
planckh: "ℎ",
plankv: "ℏ",
plusacir: "⨣",
plusb: "⊞",
pluscir: "⨢",
plus: "+",
plusdo: "∔",
plusdu: "⨥",
pluse: "⩲",
PlusMinus: "±",
plusmn: "±",
plussim: "⨦",
plustwo: "⨧",
pm: "±",
Poincareplane: "ℌ",
pointint: "⨕",
popf: "𝕡",
Popf: "ℙ",
pound: "£",
prap: "⪷",
Pr: "⪻",
pr: "≺",
prcue: "≼",
precapprox: "⪷",
prec: "≺",
preccurlyeq: "≼",
Precedes: "≺",
PrecedesEqual: "⪯",
PrecedesSlantEqual: "≼",
PrecedesTilde: "≾",
preceq: "⪯",
precnapprox: "⪹",
precneqq: "⪵",
precnsim: "⋨",
pre: "⪯",
prE: "⪳",
precsim: "≾",
prime: "′",
Prime: "″",
primes: "ℙ",
prnap: "⪹",
prnE: "⪵",
prnsim: "⋨",
prod: "∏",
Product: "∏",
profalar: "⌮",
profline: "⌒",
profsurf: "⌓",
prop: "∝",
Proportional: "∝",
Proportion: "∷",
propto: "∝",
prsim: "≾",
prurel: "⊰",
Pscr: "𝒫",
pscr: "𝓅",
Psi: "Ψ",
psi: "ψ",
puncsp: " ",
Qfr: "𝔔",
qfr: "𝔮",
qint: "⨌",
qopf: "𝕢",
Qopf: "ℚ",
qprime: "⁗",
Qscr: "𝒬",
qscr: "𝓆",
quaternions: "ℍ",
quatint: "⨖",
quest: "?",
questeq: "≟",
quot: "\"",
QUOT: "\"",
rAarr: "⇛",
race: "∽̱",
Racute: "Ŕ",
racute: "ŕ",
radic: "√",
raemptyv: "⦳",
rang: "⟩",
Rang: "⟫",
rangd: "⦒",
range: "⦥",
rangle: "⟩",
raquo: "»",
rarrap: "⥵",
rarrb: "⇥",
rarrbfs: "⤠",
rarrc: "⤳",
rarr: "→",
Rarr: "↠",
rArr: "⇒",
rarrfs: "⤞",
rarrhk: "↪",
rarrlp: "↬",
rarrpl: "⥅",
rarrsim: "⥴",
Rarrtl: "⤖",
rarrtl: "↣",
rarrw: "↝",
ratail: "⤚",
rAtail: "⤜",
ratio: "∶",
rationals: "ℚ",
rbarr: "⤍",
rBarr: "⤏",
RBarr: "⤐",
rbbrk: "❳",
rbrace: "}",
rbrack: "]",
rbrke: "⦌",
rbrksld: "⦎",
rbrkslu: "⦐",
Rcaron: "Ř",
rcaron: "ř",
Rcedil: "Ŗ",
rcedil: "ŗ",
rceil: "⌉",
rcub: "}",
Rcy: "Р",
rcy: "р",
rdca: "⤷",
rdldhar: "⥩",
rdquo: "”",
rdquor: "”",
rdsh: "↳",
real: "ℜ",
realine: "ℛ",
realpart: "ℜ",
reals: "ℝ",
Re: "ℜ",
rect: "▭",
reg: "®",
REG: "®",
ReverseElement: "∋",
ReverseEquilibrium: "⇋",
ReverseUpEquilibrium: "⥯",
rfisht: "⥽",
rfloor: "⌋",
rfr: "𝔯",
Rfr: "ℜ",
rHar: "⥤",
rhard: "⇁",
rharu: "⇀",
rharul: "⥬",
Rho: "Ρ",
rho: "ρ",
rhov: "ϱ",
RightAngleBracket: "⟩",
RightArrowBar: "⇥",
rightarrow: "→",
RightArrow: "→",
Rightarrow: "⇒",
RightArrowLeftArrow: "⇄",
rightarrowtail: "↣",
RightCeiling: "⌉",
RightDoubleBracket: "⟧",
RightDownTeeVector: "⥝",
RightDownVectorBar: "⥕",
RightDownVector: "⇂",
RightFloor: "⌋",
rightharpoondown: "⇁",
rightharpoonup: "⇀",
rightleftarrows: "⇄",
rightleftharpoons: "⇌",
rightrightarrows: "⇉",
rightsquigarrow: "↝",
RightTeeArrow: "↦",
RightTee: "⊢",
RightTeeVector: "⥛",
rightthreetimes: "⋌",
RightTriangleBar: "⧐",
RightTriangle: "⊳",
RightTriangleEqual: "⊵",
RightUpDownVector: "⥏",
RightUpTeeVector: "⥜",
RightUpVectorBar: "⥔",
RightUpVector: "↾",
RightVectorBar: "⥓",
RightVector: "⇀",
ring: "˚",
risingdotseq: "≓",
rlarr: "⇄",
rlhar: "⇌",
rlm: "\u200f",
rmoustache: "⎱",
rmoust: "⎱",
rnmid: "⫮",
roang: "⟭",
roarr: "⇾",
robrk: "⟧",
ropar: "⦆",
ropf: "𝕣",
Ropf: "ℝ",
roplus: "⨮",
rotimes: "⨵",
RoundImplies: "⥰",
rpar: ")",
rpargt: "⦔",
rppolint: "⨒",
rrarr: "⇉",
Rrightarrow: "⇛",
rsaquo: "›",
rscr: "𝓇",
Rscr: "ℛ",
rsh: "↱",
Rsh: "↱",
rsqb: "]",
rsquo: "’",
rsquor: "’",
rthree: "⋌",
rtimes: "⋊",
rtri: "▹",
rtrie: "⊵",
rtrif: "▸",
rtriltri: "⧎",
RuleDelayed: "⧴",
ruluhar: "⥨",
rx: "℞",
Sacute: "Ś",
sacute: "ś",
sbquo: "‚",
scap: "⪸",
Scaron: "Š",
scaron: "š",
Sc: "⪼",
sc: "≻",
sccue: "≽",
sce: "⪰",
scE: "⪴",
Scedil: "Ş",
scedil: "ş",
Scirc: "Ŝ",
scirc: "ŝ",
scnap: "⪺",
scnE: "⪶",
scnsim: "⋩",
scpolint: "⨓",
scsim: "≿",
Scy: "С",
scy: "с",
sdotb: "⊡",
sdot: "⋅",
sdote: "⩦",
searhk: "⤥",
searr: "↘",
seArr: "⇘",
searrow: "↘",
sect: "§",
semi: ";",
seswar: "⤩",
setminus: "∖",
setmn: "∖",
sext: "✶",
Sfr: "𝔖",
sfr: "𝔰",
sfrown: "⌢",
sharp: "♯",
SHCHcy: "Щ",
shchcy: "щ",
SHcy: "Ш",
shcy: "ш",
ShortDownArrow: "↓",
ShortLeftArrow: "←",
shortmid: "∣",
shortparallel: "∥",
ShortRightArrow: "→",
ShortUpArrow: "↑",
shy: "\u00ad",
Sigma: "Σ",
sigma: "σ",
sigmaf: "ς",
sigmav: "ς",
sim: "∼",
simdot: "⩪",
sime: "≃",
simeq: "≃",
simg: "⪞",
simgE: "⪠",
siml: "⪝",
simlE: "⪟",
simne: "≆",
simplus: "⨤",
simrarr: "⥲",
slarr: "←",
SmallCircle: "∘",
smallsetminus: "∖",
smashp: "⨳",
smeparsl: "⧤",
smid: "∣",
smile: "⌣",
smt: "⪪",
smte: "⪬",
smtes: "⪬︀",
SOFTcy: "Ь",
softcy: "ь",
solbar: "⌿",
solb: "⧄",
sol: "/",
Sopf: "𝕊",
sopf: "𝕤",
spades: "♠",
spadesuit: "♠",
spar: "∥",
sqcap: "⊓",
sqcaps: "⊓︀",
sqcup: "⊔",
sqcups: "⊔︀",
Sqrt: "√",
sqsub: "⊏",
sqsube: "⊑",
sqsubset: "⊏",
sqsubseteq: "⊑",
sqsup: "⊐",
sqsupe: "⊒",
sqsupset: "⊐",
sqsupseteq: "⊒",
square: "□",
Square: "□",
SquareIntersection: "⊓",
SquareSubset: "⊏",
SquareSubsetEqual: "⊑",
SquareSuperset: "⊐",
SquareSupersetEqual: "⊒",
SquareUnion: "⊔",
squarf: "▪",
squ: "□",
squf: "▪",
srarr: "→",
Sscr: "𝒮",
sscr: "𝓈",
ssetmn: "∖",
ssmile: "⌣",
sstarf: "⋆",
Star: "⋆",
star: "☆",
starf: "★",
straightepsilon: "ϵ",
straightphi: "ϕ",
strns: "¯",
sub: "⊂",
Sub: "⋐",
subdot: "⪽",
subE: "⫅",
sube: "⊆",
subedot: "⫃",
submult: "⫁",
subnE: "⫋",
subne: "⊊",
subplus: "⪿",
subrarr: "⥹",
subset: "⊂",
Subset: "⋐",
subseteq: "⊆",
subseteqq: "⫅",
SubsetEqual: "⊆",
subsetneq: "⊊",
subsetneqq: "⫋",
subsim: "⫇",
subsub: "⫕",
subsup: "⫓",
succapprox: "⪸",
succ: "≻",
succcurlyeq: "≽",
Succeeds: "≻",
SucceedsEqual: "⪰",
SucceedsSlantEqual: "≽",
SucceedsTilde: "≿",
succeq: "⪰",
succnapprox: "⪺",
succneqq: "⪶",
succnsim: "⋩",
succsim: "≿",
SuchThat: "∋",
sum: "∑",
Sum: "∑",
sung: "♪",
sup1: "¹",
sup2: "²",
sup3: "³",
sup: "⊃",
Sup: "⋑",
supdot: "⪾",
supdsub: "⫘",
supE: "⫆",
supe: "⊇",
supedot: "⫄",
Superset: "⊃",
SupersetEqual: "⊇",
suphsol: "⟉",
suphsub: "⫗",
suplarr: "⥻",
supmult: "⫂",
supnE: "⫌",
supne: "⊋",
supplus: "⫀",
supset: "⊃",
Supset: "⋑",
supseteq: "⊇",
supseteqq: "⫆",
supsetneq: "⊋",
supsetneqq: "⫌",
supsim: "⫈",
supsub: "⫔",
supsup: "⫖",
swarhk: "⤦",
swarr: "↙",
swArr: "⇙",
swarrow: "↙",
swnwar: "⤪",
szlig: "ß",
Tab: "\u0009",
target: "⌖",
Tau: "Τ",
tau: "τ",
tbrk: "⎴",
Tcaron: "Ť",
tcaron: "ť",
Tcedil: "Ţ",
tcedil: "ţ",
Tcy: "Т",
tcy: "т",
tdot: "⃛",
telrec: "⌕",
Tfr: "𝔗",
tfr: "𝔱",
there4: "∴",
therefore: "∴",
Therefore: "∴",
Theta: "Θ",
theta: "θ",
thetasym: "ϑ",
thetav: "ϑ",
thickapprox: "≈",
thicksim: "∼",
ThickSpace: " ",
ThinSpace: " ",
thinsp: " ",
thkap: "≈",
thksim: "∼",
THORN: "Þ",
thorn: "þ",
tilde: "˜",
Tilde: "∼",
TildeEqual: "≃",
TildeFullEqual: "≅",
TildeTilde: "≈",
timesbar: "⨱",
timesb: "⊠",
times: "×",
timesd: "⨰",
tint: "∭",
toea: "⤨",
topbot: "⌶",
topcir: "⫱",
top: "⊤",
Topf: "𝕋",
topf: "𝕥",
topfork: "⫚",
tosa: "⤩",
tprime: "‴",
trade: "™",
TRADE: "™",
triangle: "▵",
triangledown: "▿",
triangleleft: "◃",
trianglelefteq: "⊴",
triangleq: "≜",
triangleright: "▹",
trianglerighteq: "⊵",
tridot: "◬",
trie: "≜",
triminus: "⨺",
TripleDot: "⃛",
triplus: "⨹",
trisb: "⧍",
tritime: "⨻",
trpezium: "⏢",
Tscr: "𝒯",
tscr: "𝓉",
TScy: "Ц",
tscy: "ц",
TSHcy: "Ћ",
tshcy: "ћ",
Tstrok: "Ŧ",
tstrok: "ŧ",
twixt: "≬",
twoheadleftarrow: "↞",
twoheadrightarrow: "↠",
Uacute: "Ú",
uacute: "ú",
uarr: "↑",
Uarr: "↟",
uArr: "⇑",
Uarrocir: "⥉",
Ubrcy: "Ў",
ubrcy: "ў",
Ubreve: "Ŭ",
ubreve: "ŭ",
Ucirc: "Û",
ucirc: "û",
Ucy: "У",
ucy: "у",
udarr: "⇅",
Udblac: "Ű",
udblac: "ű",
udhar: "⥮",
ufisht: "⥾",
Ufr: "𝔘",
ufr: "𝔲",
Ugrave: "Ù",
ugrave: "ù",
uHar: "⥣",
uharl: "↿",
uharr: "↾",
uhblk: "▀",
ulcorn: "⌜",
ulcorner: "⌜",
ulcrop: "⌏",
ultri: "◸",
Umacr: "Ū",
umacr: "ū",
uml: "¨",
UnderBar: "_",
UnderBrace: "⏟",
UnderBracket: "⎵",
UnderParenthesis: "⏝",
Union: "⋃",
UnionPlus: "⊎",
Uogon: "Ų",
uogon: "ų",
Uopf: "𝕌",
uopf: "𝕦",
UpArrowBar: "⤒",
uparrow: "↑",
UpArrow: "↑",
Uparrow: "⇑",
UpArrowDownArrow: "⇅",
updownarrow: "↕",
UpDownArrow: "↕",
Updownarrow: "⇕",
UpEquilibrium: "⥮",
upharpoonleft: "↿",
upharpoonright: "↾",
uplus: "⊎",
UpperLeftArrow: "↖",
UpperRightArrow: "↗",
upsi: "υ",
Upsi: "ϒ",
upsih: "ϒ",
Upsilon: "Υ",
upsilon: "υ",
UpTeeArrow: "↥",
UpTee: "⊥",
upuparrows: "⇈",
urcorn: "⌝",
urcorner: "⌝",
urcrop: "⌎",
Uring: "Ů",
uring: "ů",
urtri: "◹",
Uscr: "𝒰",
uscr: "𝓊",
utdot: "⋰",
Utilde: "Ũ",
utilde: "ũ",
utri: "▵",
utrif: "▴",
uuarr: "⇈",
Uuml: "Ü",
uuml: "ü",
uwangle: "⦧",
vangrt: "⦜",
varepsilon: "ϵ",
varkappa: "ϰ",
varnothing: "∅",
varphi: "ϕ",
varpi: "ϖ",
varpropto: "∝",
varr: "↕",
vArr: "⇕",
varrho: "ϱ",
varsigma: "ς",
varsubsetneq: "⊊︀",
varsubsetneqq: "⫋︀",
varsupsetneq: "⊋︀",
varsupsetneqq: "⫌︀",
vartheta: "ϑ",
vartriangleleft: "⊲",
vartriangleright: "⊳",
vBar: "⫨",
Vbar: "⫫",
vBarv: "⫩",
Vcy: "В",
vcy: "в",
vdash: "⊢",
vDash: "⊨",
Vdash: "⊩",
VDash: "⊫",
Vdashl: "⫦",
veebar: "⊻",
vee: "∨",
Vee: "⋁",
veeeq: "≚",
vellip: "⋮",
verbar: "|",
Verbar: "‖",
vert: "|",
Vert: "‖",
VerticalBar: "∣",
VerticalLine: "|",
VerticalSeparator: "❘",
VerticalTilde: "≀",
VeryThinSpace: " ",
Vfr: "𝔙",
vfr: "𝔳",
vltri: "⊲",
vnsub: "⊂⃒",
vnsup: "⊃⃒",
Vopf: "𝕍",
vopf: "𝕧",
vprop: "∝",
vrtri: "⊳",
Vscr: "𝒱",
vscr: "𝓋",
vsubnE: "⫋︀",
vsubne: "⊊︀",
vsupnE: "⫌︀",
vsupne: "⊋︀",
Vvdash: "⊪",
vzigzag: "⦚",
Wcirc: "Ŵ",
wcirc: "ŵ",
wedbar: "⩟",
wedge: "∧",
Wedge: "⋀",
wedgeq: "≙",
weierp: "℘",
Wfr: "𝔚",
wfr: "𝔴",
Wopf: "𝕎",
wopf: "𝕨",
wp: "℘",
wr: "≀",
wreath: "≀",
Wscr: "𝒲",
wscr: "𝓌",
xcap: "⋂",
xcirc: "◯",
xcup: "⋃",
xdtri: "▽",
Xfr: "𝔛",
xfr: "𝔵",
xharr: "⟷",
xhArr: "⟺",
Xi: "Ξ",
xi: "ξ",
xlarr: "⟵",
xlArr: "⟸",
xmap: "⟼",
xnis: "⋻",
xodot: "⨀",
Xopf: "𝕏",
xopf: "𝕩",
xoplus: "⨁",
xotime: "⨂",
xrarr: "⟶",
xrArr: "⟹",
Xscr: "𝒳",
xscr: "𝓍",
xsqcup: "⨆",
xuplus: "⨄",
xutri: "△",
xvee: "⋁",
xwedge: "⋀",
Yacute: "Ý",
yacute: "ý",
YAcy: "Я",
yacy: "я",
Ycirc: "Ŷ",
ycirc: "ŷ",
Ycy: "Ы",
ycy: "ы",
yen: "¥",
Yfr: "𝔜",
yfr: "𝔶",
YIcy: "Ї",
yicy: "ї",
Yopf: "𝕐",
yopf: "𝕪",
Yscr: "𝒴",
yscr: "𝓎",
YUcy: "Ю",
yucy: "ю",
yuml: "ÿ",
Yuml: "Ÿ",
Zacute: "Ź",
zacute: "ź",
Zcaron: "Ž",
zcaron: "ž",
Zcy: "З",
zcy: "з",
Zdot: "Ż",
zdot: "ż",
zeetrf: "ℨ",
ZeroWidthSpace: "",
Zeta: "Ζ",
zeta: "ζ",
zfr: "𝔷",
Zfr: "ℨ",
ZHcy: "Ж",
zhcy: "ж",
zigrarr: "⇝",
zopf: "𝕫",
Zopf: "ℤ",
Zscr: "𝒵",
zscr: "𝓏",
zwj: "\u200d",
zwnj: "\u200c"
};
var HEXCHARCODE = /^#[xX]([A-Fa-f0-9]+)$/;
var CHARCODE = /^#([0-9]+)$/;
var NAMED = /^([A-Za-z0-9]+)$/;
var EntityParser = /** @class */function () {
function EntityParser(named) {
this.named = named;
}
EntityParser.prototype.parse = function (entity) {
if (!entity) {
return;
}
var matches = entity.match(HEXCHARCODE);
if (matches) {
return String.fromCharCode(parseInt(matches[1], 16));
}
matches = entity.match(CHARCODE);
if (matches) {
return String.fromCharCode(parseInt(matches[1], 10));
}
matches = entity.match(NAMED);
if (matches) {
return this.named[matches[1]];
}
};
return EntityParser;
}();
var WSP = /[\t\n\f ]/;
var ALPHA = /[A-Za-z]/;
var CRLF = /\r\n?/g;
function isSpace$1(char) {
return WSP.test(char);
}
function isAlpha(char) {
return ALPHA.test(char);
}
function preprocessInput(input) {
return input.replace(CRLF, '\n');
}
var EventedTokenizer = /** @class */function () {
function EventedTokenizer(delegate, entityParser, mode) {
if (mode === void 0) {
mode = 'precompile';
}
this.delegate = delegate;
this.entityParser = entityParser;
this.mode = mode;
this.state = "beforeData" /* beforeData */;
this.line = -1;
this.column = -1;
this.input = '';
this.index = -1;
this.tagNameBuffer = '';
this.states = {
beforeData: function () {
var char = this.peek();
if (char === '<' && !this.isIgnoredEndTag()) {
this.transitionTo("tagOpen" /* tagOpen */);
this.markTagStart();
this.consume();
} else {
if (this.mode === 'precompile' && char === '\n') {
var tag = this.tagNameBuffer.toLowerCase();
if (tag === 'pre' || tag === 'textarea') {
this.consume();
}
}
this.transitionTo("data" /* data */);
this.delegate.beginData();
}
},
data: function () {
var char = this.peek();
var tag = this.tagNameBuffer;
if (char === '<' && !this.isIgnoredEndTag()) {
this.delegate.finishData();
this.transitionTo("tagOpen" /* tagOpen */);
this.markTagStart();
this.consume();
} else if (char === '&' && tag !== 'script' && tag !== 'style') {
this.consume();
this.delegate.appendToData(this.consumeCharRef() || '&');
} else {
this.consume();
this.delegate.appendToData(char);
}
},
tagOpen: function () {
var char = this.consume();
if (char === '!') {
this.transitionTo("markupDeclarationOpen" /* markupDeclarationOpen */);
} else if (char === '/') {
this.transitionTo("endTagOpen" /* endTagOpen */);
} else if (char === '@' || char === ':' || isAlpha(char)) {
this.transitionTo("tagName" /* tagName */);
this.tagNameBuffer = '';
this.delegate.beginStartTag();
this.appendToTagName(char);
}
},
markupDeclarationOpen: function () {
var char = this.consume();
if (char === '-' && this.peek() === '-') {
this.consume();
this.transitionTo("commentStart" /* commentStart */);
this.delegate.beginComment();
} else {
var maybeDoctype = char.toUpperCase() + this.input.substring(this.index, this.index + 6).toUpperCase();
if (maybeDoctype === 'DOCTYPE') {
this.consume();
this.consume();
this.consume();
this.consume();
this.consume();
this.consume();
this.transitionTo("doctype" /* doctype */);
if (this.delegate.beginDoctype) this.delegate.beginDoctype();
}
}
},
doctype: function () {
var char = this.consume();
if (isSpace$1(char)) {
this.transitionTo("beforeDoctypeName" /* beforeDoctypeName */);
}
},
beforeDoctypeName: function () {
var char = this.consume();
if (isSpace$1(char)) {
return;
} else {
this.transitionTo("doctypeName" /* doctypeName */);
if (this.delegate.appendToDoctypeName) this.delegate.appendToDoctypeName(char.toLowerCase());
}
},
doctypeName: function () {
var char = this.consume();
if (isSpace$1(char)) {
this.transitionTo("afterDoctypeName" /* afterDoctypeName */);
} else if (char === '>') {
if (this.delegate.endDoctype) this.delegate.endDoctype();
this.transitionTo("beforeData" /* beforeData */);
} else {
if (this.delegate.appendToDoctypeName) this.delegate.appendToDoctypeName(char.toLowerCase());
}
},
afterDoctypeName: function () {
var char = this.consume();
if (isSpace$1(char)) {
return;
} else if (char === '>') {
if (this.delegate.endDoctype) this.delegate.endDoctype();
this.transitionTo("beforeData" /* beforeData */);
} else {
var nextSixChars = char.toUpperCase() + this.input.substring(this.index, this.index + 5).toUpperCase();
var isPublic = nextSixChars.toUpperCase() === 'PUBLIC';
var isSystem = nextSixChars.toUpperCase() === 'SYSTEM';
if (isPublic || isSystem) {
this.consume();
this.consume();
this.consume();
this.consume();
this.consume();
this.consume();
}
if (isPublic) {
this.transitionTo("afterDoctypePublicKeyword" /* afterDoctypePublicKeyword */);
} else if (isSystem) {
this.transitionTo("afterDoctypeSystemKeyword" /* afterDoctypeSystemKeyword */);
}
}
},
afterDoctypePublicKeyword: function () {
var char = this.peek();
if (isSpace$1(char)) {
this.transitionTo("beforeDoctypePublicIdentifier" /* beforeDoctypePublicIdentifier */);
this.consume();
} else if (char === '"') {
this.transitionTo("doctypePublicIdentifierDoubleQuoted" /* doctypePublicIdentifierDoubleQuoted */);
this.consume();
} else if (char === "'") {
this.transitionTo("doctypePublicIdentifierSingleQuoted" /* doctypePublicIdentifierSingleQuoted */);
this.consume();
} else if (char === '>') {
this.consume();
if (this.delegate.endDoctype) this.delegate.endDoctype();
this.transitionTo("beforeData" /* beforeData */);
}
},
doctypePublicIdentifierDoubleQuoted: function () {
var char = this.consume();
if (char === '"') {
this.transitionTo("afterDoctypePublicIdentifier" /* afterDoctypePublicIdentifier */);
} else if (char === '>') {
if (this.delegate.endDoctype) this.delegate.endDoctype();
this.transitionTo("beforeData" /* beforeData */);
} else {
if (this.delegate.appendToDoctypePublicIdentifier) this.delegate.appendToDoctypePublicIdentifier(char);
}
},
doctypePublicIdentifierSingleQuoted: function () {
var char = this.consume();
if (char === "'") {
this.transitionTo("afterDoctypePublicIdentifier" /* afterDoctypePublicIdentifier */);
} else if (char === '>') {
if (this.delegate.endDoctype) this.delegate.endDoctype();
this.transitionTo("beforeData" /* beforeData */);
} else {
if (this.delegate.appendToDoctypePublicIdentifier) this.delegate.appendToDoctypePublicIdentifier(char);
}
},
afterDoctypePublicIdentifier: function () {
var char = this.consume();
if (isSpace$1(char)) {
this.transitionTo("betweenDoctypePublicAndSystemIdentifiers" /* betweenDoctypePublicAndSystemIdentifiers */);
} else if (char === '>') {
if (this.delegate.endDoctype) this.delegate.endDoctype();
this.transitionTo("beforeData" /* beforeData */);
} else if (char === '"') {
this.transitionTo("doctypeSystemIdentifierDoubleQuoted" /* doctypeSystemIdentifierDoubleQuoted */);
} else if (char === "'") {
this.transitionTo("doctypeSystemIdentifierSingleQuoted" /* doctypeSystemIdentifierSingleQuoted */);
}
},
betweenDoctypePublicAndSystemIdentifiers: function () {
var char = this.consume();
if (isSpace$1(char)) {
return;
} else if (char === '>') {
if (this.delegate.endDoctype) this.delegate.endDoctype();
this.transitionTo("beforeData" /* beforeData */);
} else if (char === '"') {
this.transitionTo("doctypeSystemIdentifierDoubleQuoted" /* doctypeSystemIdentifierDoubleQuoted */);
} else if (char === "'") {
this.transitionTo("doctypeSystemIdentifierSingleQuoted" /* doctypeSystemIdentifierSingleQuoted */);
}
},
doctypeSystemIdentifierDoubleQuoted: function () {
var char = this.consume();
if (char === '"') {
this.transitionTo("afterDoctypeSystemIdentifier" /* afterDoctypeSystemIdentifier */);
} else if (char === '>') {
if (this.delegate.endDoctype) this.delegate.endDoctype();
this.transitionTo("beforeData" /* beforeData */);
} else {
if (this.delegate.appendToDoctypeSystemIdentifier) this.delegate.appendToDoctypeSystemIdentifier(char);
}
},
doctypeSystemIdentifierSingleQuoted: function () {
var char = this.consume();
if (char === "'") {
this.transitionTo("afterDoctypeSystemIdentifier" /* afterDoctypeSystemIdentifier */);
} else if (char === '>') {
if (this.delegate.endDoctype) this.delegate.endDoctype();
this.transitionTo("beforeData" /* beforeData */);
} else {
if (this.delegate.appendToDoctypeSystemIdentifier) this.delegate.appendToDoctypeSystemIdentifier(char);
}
},
afterDoctypeSystemIdentifier: function () {
var char = this.consume();
if (isSpace$1(char)) {
return;
} else if (char === '>') {
if (this.delegate.endDoctype) this.delegate.endDoctype();
this.transitionTo("beforeData" /* beforeData */);
}
},
commentStart: function () {
var char = this.consume();
if (char === '-') {
this.transitionTo("commentStartDash" /* commentStartDash */);
} else if (char === '>') {
this.delegate.finishComment();
this.transitionTo("beforeData" /* beforeData */);
} else {
this.delegate.appendToCommentData(char);
this.transitionTo("comment" /* comment */);
}
},
commentStartDash: function () {
var char = this.consume();
if (char === '-') {
this.transitionTo("commentEnd" /* commentEnd */);
} else if (char === '>') {
this.delegate.finishComment();
this.transitionTo("beforeData" /* beforeData */);
} else {
this.delegate.appendToCommentData('-');
this.transitionTo("comment" /* comment */);
}
},
comment: function () {
var char = this.consume();
if (char === '-') {
this.transitionTo("commentEndDash" /* commentEndDash */);
} else {
this.delegate.appendToCommentData(char);
}
},
commentEndDash: function () {
var char = this.consume();
if (char === '-') {
this.transitionTo("commentEnd" /* commentEnd */);
} else {
this.delegate.appendToCommentData('-' + char);
this.transitionTo("comment" /* comment */);
}
},
commentEnd: function () {
var char = this.consume();
if (char === '>') {
this.delegate.finishComment();
this.transitionTo("beforeData" /* beforeData */);
} else {
this.delegate.appendToCommentData('--' + char);
this.transitionTo("comment" /* comment */);
}
},
tagName: function () {
var char = this.consume();
if (isSpace$1(char)) {
this.transitionTo("beforeAttributeName" /* beforeAttributeName */);
} else if (char === '/') {
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
} else if (char === '>') {
this.delegate.finishTag();
this.transitionTo("beforeData" /* beforeData */);
} else {
this.appendToTagName(char);
}
},
endTagName: function () {
var char = this.consume();
if (isSpace$1(char)) {
this.transitionTo("beforeAttributeName" /* beforeAttributeName */);
this.tagNameBuffer = '';
} else if (char === '/') {
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
this.tagNameBuffer = '';
} else if (char === '>') {
this.delegate.finishTag();
this.transitionTo("beforeData" /* beforeData */);
this.tagNameBuffer = '';
} else {
this.appendToTagName(char);
}
},
beforeAttributeName: function () {
var char = this.peek();
if (isSpace$1(char)) {
this.consume();
return;
} else if (char === '/') {
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
this.consume();
} else if (char === '>') {
this.consume();
this.delegate.finishTag();
this.transitionTo("beforeData" /* beforeData */);
} else if (char === '=') {
this.delegate.reportSyntaxError('attribute name cannot start with equals sign');
this.transitionTo("attributeName" /* attributeName */);
this.delegate.beginAttribute();
this.consume();
this.delegate.appendToAttributeName(char);
} else {
this.transitionTo("attributeName" /* attributeName */);
this.delegate.beginAttribute();
}
},
attributeName: function () {
var char = this.peek();
if (isSpace$1(char)) {
this.transitionTo("afterAttributeName" /* afterAttributeName */);
this.consume();
} else if (char === '/') {
this.delegate.beginAttributeValue(false);
this.delegate.finishAttributeValue();
this.consume();
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
} else if (char === '=') {
this.transitionTo("beforeAttributeValue" /* beforeAttributeValue */);
this.consume();
} else if (char === '>') {
this.delegate.beginAttributeValue(false);
this.delegate.finishAttributeValue();
this.consume();
this.delegate.finishTag();
this.transitionTo("beforeData" /* beforeData */);
} else if (char === '"' || char === "'" || char === '<') {
this.delegate.reportSyntaxError(char + ' is not a valid character within attribute names');
this.consume();
this.delegate.appendToAttributeName(char);
} else {
this.consume();
this.delegate.appendToAttributeName(char);
}
},
afterAttributeName: function () {
var char = this.peek();
if (isSpace$1(char)) {
this.consume();
return;
} else if (char === '/') {
this.delegate.beginAttributeValue(false);
this.delegate.finishAttributeValue();
this.consume();
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
} else if (char === '=') {
this.consume();
this.transitionTo("beforeAttributeValue" /* beforeAttributeValue */);
} else if (char === '>') {
this.delegate.beginAttributeValue(false);
this.delegate.finishAttributeValue();
this.consume();
this.delegate.finishTag();
this.transitionTo("beforeData" /* beforeData */);
} else {
this.delegate.beginAttributeValue(false);
this.delegate.finishAttributeValue();
this.transitionTo("attributeName" /* attributeName */);
this.delegate.beginAttribute();
this.consume();
this.delegate.appendToAttributeName(char);
}
},
beforeAttributeValue: function () {
var char = this.peek();
if (isSpace$1(char)) {
this.consume();
} else if (char === '"') {
this.transitionTo("attributeValueDoubleQuoted" /* attributeValueDoubleQuoted */);
this.delegate.beginAttributeValue(true);
this.consume();
} else if (char === "'") {
this.transitionTo("attributeValueSingleQuoted" /* attributeValueSingleQuoted */);
this.delegate.beginAttributeValue(true);
this.consume();
} else if (char === '>') {
this.delegate.beginAttributeValue(false);
this.delegate.finishAttributeValue();
this.consume();
this.delegate.finishTag();
this.transitionTo("beforeData" /* beforeData */);
} else {
this.transitionTo("attributeValueUnquoted" /* attributeValueUnquoted */);
this.delegate.beginAttributeValue(false);
this.consume();
this.delegate.appendToAttributeValue(char);
}
},
attributeValueDoubleQuoted: function () {
var char = this.consume();
if (char === '"') {
this.delegate.finishAttributeValue();
this.transitionTo("afterAttributeValueQuoted" /* afterAttributeValueQuoted */);
} else if (char === '&') {
this.delegate.appendToAttributeValue(this.consumeCharRef() || '&');
} else {
this.delegate.appendToAttributeValue(char);
}
},
attributeValueSingleQuoted: function () {
var char = this.consume();
if (char === "'") {
this.delegate.finishAttributeValue();
this.transitionTo("afterAttributeValueQuoted" /* afterAttributeValueQuoted */);
} else if (char === '&') {
this.delegate.appendToAttributeValue(this.consumeCharRef() || '&');
} else {
this.delegate.appendToAttributeValue(char);
}
},
attributeValueUnquoted: function () {
var char = this.peek();
if (isSpace$1(char)) {
this.delegate.finishAttributeValue();
this.consume();
this.transitionTo("beforeAttributeName" /* beforeAttributeName */);
} else if (char === '/') {
this.delegate.finishAttributeValue();
this.consume();
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
} else if (char === '&') {
this.consume();
this.delegate.appendToAttributeValue(this.consumeCharRef() || '&');
} else if (char === '>') {
this.delegate.finishAttributeValue();
this.consume();
this.delegate.finishTag();
this.transitionTo("beforeData" /* beforeData */);
} else {
this.consume();
this.delegate.appendToAttributeValue(char);
}
},
afterAttributeValueQuoted: function () {
var char = this.peek();
if (isSpace$1(char)) {
this.consume();
this.transitionTo("beforeAttributeName" /* beforeAttributeName */);
} else if (char === '/') {
this.consume();
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
} else if (char === '>') {
this.consume();
this.delegate.finishTag();
this.transitionTo("beforeData" /* beforeData */);
} else {
this.transitionTo("beforeAttributeName" /* beforeAttributeName */);
}
},
selfClosingStartTag: function () {
var char = this.peek();
if (char === '>') {
this.consume();
this.delegate.markTagAsSelfClosing();
this.delegate.finishTag();
this.transitionTo("beforeData" /* beforeData */);
} else {
this.transitionTo("beforeAttributeName" /* beforeAttributeName */);
}
},
endTagOpen: function () {
var char = this.consume();
if (char === '@' || char === ':' || isAlpha(char)) {
this.transitionTo("endTagName" /* endTagName */);
this.tagNameBuffer = '';
this.delegate.beginEndTag();
this.appendToTagName(char);
}
}
};
this.reset();
}
EventedTokenizer.prototype.reset = function () {
this.transitionTo("beforeData" /* beforeData */);
this.input = '';
this.tagNameBuffer = '';
this.index = 0;
this.line = 1;
this.column = 0;
this.delegate.reset();
};
EventedTokenizer.prototype.transitionTo = function (state) {
this.state = state;
};
EventedTokenizer.prototype.tokenize = function (input) {
this.reset();
this.tokenizePart(input);
this.tokenizeEOF();
};
EventedTokenizer.prototype.tokenizePart = function (input) {
this.input += preprocessInput(input);
while (this.index < this.input.length) {
var handler = this.states[this.state];
if (handler !== undefined) {
handler.call(this);
} else {
throw new Error("unhandled state " + this.state);
}
}
};
EventedTokenizer.prototype.tokenizeEOF = function () {
this.flushData();
};
EventedTokenizer.prototype.flushData = function () {
if (this.state === 'data') {
this.delegate.finishData();
this.transitionTo("beforeData" /* beforeData */);
}
};
EventedTokenizer.prototype.peek = function () {
return this.input.charAt(this.index);
};
EventedTokenizer.prototype.consume = function () {
var char = this.peek();
this.index++;
if (char === '\n') {
this.line++;
this.column = 0;
} else {
this.column++;
}
return char;
};
EventedTokenizer.prototype.consumeCharRef = function () {
var endIndex = this.input.indexOf(';', this.index);
if (endIndex === -1) {
return;
}
var entity = this.input.slice(this.index, endIndex);
var chars = this.entityParser.parse(entity);
if (chars) {
var count = entity.length;
// consume the entity chars
while (count) {
this.consume();
count--;
}
// consume the `;`
this.consume();
return chars;
}
};
EventedTokenizer.prototype.markTagStart = function () {
this.delegate.tagOpen();
};
EventedTokenizer.prototype.appendToTagName = function (char) {
this.tagNameBuffer += char;
this.delegate.appendToTagName(char);
};
EventedTokenizer.prototype.isIgnoredEndTag = function () {
var tag = this.tagNameBuffer;
return tag === 'title' && this.input.substring(this.index, this.index + 8) !== '</title>' || tag === 'style' && this.input.substring(this.index, this.index + 8) !== '</style>' || tag === 'script' && this.input.substring(this.index, this.index + 9) !== '</script>';
};
return EventedTokenizer;
}();
var Char = function (Char) {
return Char[Char.NBSP = 160] = "NBSP", Char[Char.QUOT = 34] = "QUOT", Char[Char.LT = 60] = "LT", Char[Char.GT = 62] = "GT", Char[Char.AMP = 38] = "AMP", Char;
}(Char || {});
const ATTR_VALUE_REGEX_TEST = /["&\xA0]/u,
ATTR_VALUE_REGEX_REPLACE = new RegExp(ATTR_VALUE_REGEX_TEST.source, "gu"),
TEXT_REGEX_TEST = /[&<>\xA0]/u,
TEXT_REGEX_REPLACE = new RegExp(TEXT_REGEX_TEST.source, "gu");
function attrValueReplacer(char) {
switch (char.charCodeAt(0)) {
case Char.NBSP:
return " ";
case Char.QUOT:
return """;
case Char.AMP:
return "&";
default:
return char;
}
}
function textReplacer(char) {
switch (char.charCodeAt(0)) {
case Char.NBSP:
return " ";
case Char.AMP:
return "&";
case Char.LT:
return "<";
case Char.GT:
return ">";
default:
return char;
}
}
function sortByLoc(a, b) {
// If either is invisible, don't try to order them
return a.loc.isInvisible || b.loc.isInvisible ? 0 : a.loc.startPosition.line < b.loc.startPosition.line || a.loc.startPosition.line === b.loc.startPosition.line && a.loc.startPosition.column < b.loc.startPosition.column ? -1 : a.loc.startPosition.line === b.loc.startPosition.line && a.loc.startPosition.column === b.loc.startPosition.column ? 0 : 1;
}
const voidMap = new Set(["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]);
const NON_WHITESPACE = /^\S/u;
/**
* Examples when true:
* - link
* - liNK
*
* Examples when false:
* - Link (component)
*/
function isVoidTag(tag) {
return voidMap.has(tag.toLowerCase()) && tag[0]?.toLowerCase() === tag[0];
}
class Printer {
buffer = "";
options;
constructor(options) {
this.options = options;
}
/*
This is used by _all_ methods on this Printer class that add to `this.buffer`,
it allows consumers of the printer to use alternate string representations for
a given node.
The primary use case for this are things like source -> source codemod utilities.
For example, ember-template-recast attempts to always preserve the original string
formatting in each AST node if no modifications are made to it.
*/
handledByOverride(node, ensureLeadingWhitespace = !1) {
if (void 0 !== this.options.override) {
let result = this.options.override(node, this.options);
if ("string" == typeof result) return ensureLeadingWhitespace && NON_WHITESPACE.test(result) && (result = ` ${result}`), this.buffer += result, !0;
}
return !1;
}
Node(node) {
switch (node.type) {
case "MustacheStatement":
case "BlockStatement":
case "MustacheCommentStatement":
case "CommentStatement":
case "TextNode":
case "ElementNode":
case "AttrNode":
case "Block":
case "Template":
return this.TopLevelStatement(node);
case "StringLiteral":
case "BooleanLiteral":
case "NumberLiteral":
case "UndefinedLiteral":
case "NullLiteral":
case "PathExpression":
case "SubExpression":
return this.Expression(node);
case "ConcatStatement":
// should have an AttrNode parent
return this.ConcatStatement(node);
case "Hash":
return this.Hash(node);
case "HashPair":
return this.HashPair(node);
case "ElementModifierStatement":
return this.ElementModifierStatement(node);
}
}
Expression(expression) {
switch (expression.type) {
case "StringLiteral":
case "BooleanLiteral":
case "NumberLiteral":
case "UndefinedLiteral":
case "NullLiteral":
return this.Literal(expression);
case "PathExpression":
return this.PathExpression(expression);
case "SubExpression":
return this.SubExpression(expression);
}
}
Literal(literal) {
switch (literal.type) {
case "StringLiteral":
return this.StringLiteral(literal);
case "BooleanLiteral":
return this.BooleanLiteral(literal);
case "NumberLiteral":
return this.NumberLiteral(literal);
case "UndefinedLiteral":
return this.UndefinedLiteral(literal);
case "NullLiteral":
return this.NullLiteral(literal);
}
}
TopLevelStatement(statement) {
switch (statement.type) {
case "MustacheStatement":
return this.MustacheStatement(statement);
case "BlockStatement":
return this.BlockStatement(statement);
case "MustacheCommentStatement":
return this.MustacheCommentStatement(statement);
case "CommentStatement":
return this.CommentStatement(statement);
case "TextNode":
return this.TextNode(statement);
case "ElementNode":
return this.ElementNode(statement);
case "Block":
return this.Block(statement);
case "Template":
return this.Template(statement);
case "AttrNode":
// should have element
return this.AttrNode(statement);
}
}
Template(template) {
this.TopLevelStatements(template.body);
}
Block(block) {
/*
When processing a template like:
```hbs
{{#if whatever}}
whatever
{{else if somethingElse}}
something else
{{else}}
fallback
{{/if}}
```
The AST still _effectively_ looks like:
```hbs
{{#if whatever}}
whatever
{{else}}{{#if somethingElse}}
something else
{{else}}
fallback
{{/if}}{{/if}}
```
The only way we can tell if that is the case is by checking for
`block.chained`, but unfortunately when the actual statements are
processed the `block.body[0]` node (which will always be a
`BlockStatement`) has no clue that its ancestor `Block` node was
chained.
This "forwards" the `chained` setting so that we can check
it later when processing the `BlockStatement`.
*/
block.chained && (block.body[0].chained = !0), this.handledByOverride(block) || this.TopLevelStatements(block.body);
}
TopLevelStatements(statements) {
statements.forEach(statement => this.TopLevelStatement(statement));
}
ElementNode(el) {
this.handledByOverride(el) || (this.OpenElementNode(el), this.TopLevelStatements(el.children), this.CloseElementNode(el));
}
OpenElementNode(el) {
this.buffer += `<${el.tag}`;
const parts = [...el.attributes, ...el.modifiers, ...el.comments].sort(sortByLoc);
for (const part of parts) switch (this.buffer += " ", part.type) {
case "AttrNode":
this.AttrNode(part);
break;
case "ElementModifierStatement":
this.ElementModifierStatement(part);
break;
case "MustacheCommentStatement":
this.MustacheCommentStatement(part);
}
el.blockParams.length && this.BlockParams(el.blockParams), el.selfClosing && (this.buffer += " /"), this.buffer += ">";
}
CloseElementNode(el) {
el.selfClosing || isVoidTag(el.tag) || (this.buffer += `</${el.tag}>`);
}
AttrNode(attr) {
if (this.handledByOverride(attr)) return;
let {
name: name,
value: value
} = attr;
this.buffer += name, ("TextNode" !== value.type || value.chars.length > 0) && (this.buffer += "=", this.AttrNodeValue(value));
}
AttrNodeValue(value) {
"TextNode" === value.type ? (this.buffer += '"', this.TextNode(value, !0), this.buffer += '"') : this.Node(value);
}
TextNode(text, isAttr) {
var attrValue;
this.handledByOverride(text) || ("raw" === this.options.entityEncoding ? this.buffer += text.chars : this.buffer += isAttr ? (attrValue = text.chars, ATTR_VALUE_REGEX_TEST.test(attrValue) ? attrValue.replace(ATTR_VALUE_REGEX_REPLACE, attrValueReplacer) : attrValue) : function (text) {
return TEXT_REGEX_TEST.test(text) ? text.replace(TEXT_REGEX_REPLACE, textReplacer) : text;
}(text.chars));
}
MustacheStatement(mustache) {
this.handledByOverride(mustache) || (this.buffer += mustache.trusting ? "{{{" : "{{", mustache.strip.open && (this.buffer += "~"), this.Expression(mustache.path), this.Params(mustache.params), this.Hash(mustache.hash), mustache.strip.close && (this.buffer += "~"), this.buffer += mustache.trusting ? "}}}" : "}}");
}
BlockStatement(block) {
this.handledByOverride(block) || (block.chained ? (this.buffer += block.inverseStrip.open ? "{{~" : "{{", this.buffer += "else ") : this.buffer += block.openStrip.open ? "{{~#" : "{{#", this.Expression(block.path), this.Params(block.params), this.Hash(block.hash), block.program.blockParams.length && this.BlockParams(block.program.blockParams), block.chained ? this.buffer += block.inverseStrip.close ? "~}}" : "}}" : this.buffer += block.openStrip.close ? "~}}" : "}}", this.Block(block.program), block.inverse && (block.inverse.chained || (this.buffer += block.inverseStrip.open ? "{{~" : "{{", this.buffer += "else", this.buffer += block.inverseStrip.close ? "~}}" : "}}"), this.Block(block.inverse)), block.chained || (this.buffer += block.closeStrip.open ? "{{~/" : "{{/", this.Expression(block.path), this.buffer += block.closeStrip.close ? "~}}" : "}}"));
}
BlockParams(blockParams) {
this.buffer += ` as |${blockParams.join(" ")}|`;
}
ConcatStatement(concat) {
this.handledByOverride(concat) || (this.buffer += '"', concat.parts.forEach(part => {
"TextNode" === part.type ? this.TextNode(part, !0) : this.Node(part);
}), this.buffer += '"');
}
MustacheCommentStatement(comment) {
this.handledByOverride(comment) || (this.buffer += `{{!--${comment.value}--}}`);
}
ElementModifierStatement(mod) {
this.handledByOverride(mod) || (this.buffer += "{{", this.Expression(mod.path), this.Params(mod.params), this.Hash(mod.hash), this.buffer += "}}");
}
CommentStatement(comment) {
this.handledByOverride(comment) || (this.buffer += `\x3c!--${comment.value}--\x3e`);
}
PathExpression(path) {
this.handledByOverride(path) || (this.buffer += path.original);
}
SubExpression(sexp) {
this.handledByOverride(sexp) || (this.buffer += "(", this.Expression(sexp.path), this.Params(sexp.params), this.Hash(sexp.hash), this.buffer += ")");
}
Params(params) {
// TODO: implement a top level Params AST node (just like the Hash object)
// so that this can also be overridden
params.length && params.forEach(param => {
this.buffer += " ", this.Expression(param);
});
}
Hash(hash) {
this.handledByOverride(hash, !0) || hash.pairs.forEach(pair => {
this.buffer += " ", this.HashPair(pair);
});
}
HashPair(pair) {
this.handledByOverride(pair) || (this.buffer += pair.key, this.buffer += "=", this.Node(pair.value));
}
StringLiteral(str) {
this.handledByOverride(str) || (this.buffer += JSON.stringify(str.value));
}
BooleanLiteral(bool) {
this.handledByOverride(bool) || (this.buffer += bool.value);
}
NumberLiteral(number) {
this.handledByOverride(number) || (this.buffer += number.value);
}
UndefinedLiteral(node) {
this.handledByOverride(node) || (this.buffer += "undefined");
}
NullLiteral(node) {
this.handledByOverride(node) || (this.buffer += "null");
}
print(node) {
let {
options: options
} = this;
if (options.override) {
let result = options.override(node, options);
if (void 0 !== result) return result;
}
return this.buffer = "", this.Node(node), this.buffer;
}
}
function build(ast, options = {
entityEncoding: "transformed"
}) {
return ast ? new Printer(options).print(ast) : "";
}
function isKeyword(word, type) {
return word in KEYWORDS_TYPES && (void 0 === type || KEYWORDS_TYPES[word].includes(type));
}
/**
* This includes the full list of keywords currently in use in the template
* language, and where their valid usages are.
*/
const KEYWORDS_TYPES = {
action: ["Call", "Modifier"],
component: ["Call", "Append", "Block"],
debugger: ["Append"],
"each-in": ["Block"],
each: ["Block"],
"has-block-params": ["Call", "Append"],
"has-block": ["Call", "Append"],
helper: ["Call", "Append"],
if: ["Call", "Append", "Block"],
"in-element": ["Block"],
let: ["Block"],
log: ["Call", "Append"],
modifier: ["Call", "Modifier"],
mount: ["Append"],
mut: ["Call", "Append"],
outlet: ["Append"],
readonly: ["Call", "Append"],
unbound: ["Call", "Append"],
unless: ["Call", "Append", "Block"],
yield: ["Append"]
},
UNKNOWN_POSITION = Object.freeze({
line: 1,
column: 0
}),
SYNTHETIC_LOCATION = Object.freeze({
source: "(synthetic)",
start: UNKNOWN_POSITION,
end: UNKNOWN_POSITION
}),
NON_EXISTENT_LOCATION = Object.freeze({
source: "(nonexistent)",
start: UNKNOWN_POSITION,
end: UNKNOWN_POSITION
}),
BROKEN_LOCATION = Object.freeze({
source: "(broken)",
start: UNKNOWN_POSITION,
end: UNKNOWN_POSITION
});
let OffsetKind = function (OffsetKind) {
return OffsetKind.CharPosition = "CharPosition", OffsetKind.HbsPosition = "HbsPosition", OffsetKind.InternalsSynthetic = "InternalsSynthetic", OffsetKind.NonExistent = "NonExistent", OffsetKind.Broken = "Broken", OffsetKind;
}({});
/**
* This file implements the DSL used by span and offset in places where they need to exhaustively
* consider all combinations of states (Handlebars offsets, character offsets and invisible/broken
* offsets).
*
* It's probably overkill, but it makes the code that uses it clear. It could be refactored or
* removed.
*/
class WhenList {
_whens;
constructor(whens) {
this._whens = whens;
}
first(kind) {
for (const when of this._whens) {
const value = when.match(kind);
if (isPresentArray(value)) return value[0];
}
return null;
}
}
class When {
_map = new Map();
get(pattern, or) {
let value = this._map.get(pattern);
return value || (value = or(), this._map.set(pattern, value), value);
}
add(pattern, out) {
this._map.set(pattern, out);
}
match(kind) {
const pattern = function (kind) {
switch (kind) {
case OffsetKind.Broken:
case OffsetKind.InternalsSynthetic:
case OffsetKind.NonExistent:
return "IS_INVISIBLE";
default:
return kind;
}
}(kind),
out = [],
exact = this._map.get(pattern),
fallback = this._map.get("MATCH_ANY");
return exact && out.push(exact), fallback && out.push(fallback), out;
}
}
function match(callback) {
return callback(new Matcher()).check();
}
class Matcher {
_whens = new When();
/**
* You didn't exhaustively match all possibilities.
*/
check() {
return (left, right) => this.matchFor(left.kind, right.kind)(left, right);
}
matchFor(left, right) {
const nesteds = this._whens.match(left);
debugAssert(isPresentArray(nesteds), `no match defined for (${left}, ${right}) and no AnyMatch defined either`);
const callback = new WhenList(nesteds).first(right);
return debugAssert(null !== callback, `no match defined for (${left}, ${right}) and no AnyMatch defined either`), callback;
}
// This big block is the bulk of the heavy lifting in this file. It facilitates exhaustiveness
// checking so that matchers can ensure they've actually covered all the cases (and TypeScript
// will treat it as an exhaustive match).
when(left, right, callback) {
return this._whens.get(left, () => new When()).add(right, callback), this;
}
}
class SourceSlice {
static synthetic(chars) {
let offsets = SourceSpan.synthetic(chars);
return new SourceSlice({
loc: offsets,
chars: chars
});
}
static load(source, slice) {
return new SourceSlice({
loc: SourceSpan.load(source, slice[1]),
chars: slice[0]
});
}
chars;
loc;
constructor(options) {
this.loc = options.loc, this.chars = options.chars;
}
getString() {
return this.chars;
}
serialize() {
return [this.chars, this.loc.serialize()];
}
}
/**
* All spans have these details in common.
*/
/**
* A `SourceSpan` object represents a span of characters inside of a template source.
*
* There are three kinds of `SourceSpan` objects:
*
* - `ConcreteSourceSpan`, which contains byte offsets
* - `LazySourceSpan`, which contains `SourceLocation`s from the Handlebars AST, which can be
* converted to byte offsets on demand.
* - `InvisibleSourceSpan`, which represent source strings that aren't present in the source,
* because:
* - they were created synthetically
* - their location is nonsensical (the span is broken)
* - they represent nothing in the source (this currently happens only when a bug in the
* upstream Handlebars parser fails to assign a location to empty blocks)
*
* At a high level, all `SourceSpan` objects provide:
*
* - byte offsets
* - source in column and line format
*
* And you can do these operations on `SourceSpan`s:
*
* - collapse it to a `SourceSpan` representing its starting or ending position
* - slice out some characters, optionally skipping some characters at the beginning or end
* - create a new `SourceSpan` with a different starting or ending offset
*
* All SourceSpan objects implement `SourceLocation`, for compatibility. All SourceSpan
* objects have a `toJSON` that emits `SourceLocation`, also for compatibility.
*
* For compatibility, subclasses of `AbstractSourceSpan` must implement `locDidUpdate`, which
* happens when an AST plugin attempts to modify the `start` or `end` of a span directly.
*
* The goal is to avoid creating any problems for use-cases like AST Explorer.
*/
class SourceSpan {
static get NON_EXISTENT() {
return new InvisibleSpan(OffsetKind.NonExistent, NON_EXISTENT_LOCATION).wrap();
}
static load(source, serialized) {
return "number" == typeof serialized ? SourceSpan.forCharPositions(source, serialized, serialized) : "string" == typeof serialized ? SourceSpan.synthetic(serialized) : Array.isArray(serialized) ? SourceSpan.forCharPositions(source, serialized[0], serialized[1]) : serialized === OffsetKind.NonExistent ? SourceSpan.NON_EXISTENT : serialized === OffsetKind.Broken ? SourceSpan.broken(BROKEN_LOCATION) : void assertNever(serialized);
}
static forHbsLoc(source, loc) {
const start = new HbsPosition(source, loc.start),
end = new HbsPosition(source, loc.end);
return new HbsSpan(source, {
start: start,
end: end
}, loc).wrap();
}
static forCharPositions(source, startPos, endPos) {
const start = new CharPosition(source, startPos),
end = new CharPosition(source, endPos);
return new CharPositionSpan(source, {
start: start,
end: end
}).wrap();
}
static synthetic(chars) {
return new InvisibleSpan(OffsetKind.InternalsSynthetic, NON_EXISTENT_LOCATION, chars).wrap();
}
static broken(pos = BROKEN_LOCATION) {
return new InvisibleSpan(OffsetKind.Broken, pos).wrap();
}
isInvisible;
constructor(data) {
this.data = data, this.isInvisible = data.kind !== OffsetKind.CharPosition && data.kind !== OffsetKind.HbsPosition;
}
getStart() {
return this.data.getStart().wrap();
}
getEnd() {
return this.data.getEnd().wrap();
}
get loc() {
const span = this.data.toHbsSpan();
return null === span ? BROKEN_LOCATION : span.toHbsLoc();
}
get module() {
return this.data.getModule();
}
/**
* Get the starting `SourcePosition` for this `SourceSpan`, lazily computing it if needed.
*/
get startPosition() {
return this.loc.start;
}
/**
* Get the ending `SourcePosition` for this `SourceSpan`, lazily computing it if needed.
*/
get endPosition() {
return this.loc.end;
}
/**
* Support converting ASTv1 nodes into a serialized format using JSON.stringify.
*/
toJSON() {
return this.loc;
}
/**
* Create a new span with the current span's end and a new beginning.
*/
withStart(other) {
return span(other.data, this.data.getEnd());
}
/**
* Create a new span with the current span's beginning and a new ending.
*/
withEnd(other) {
return span(this.data.getStart(), other.data);
}
asString() {
return this.data.asString();
}
/**
* Convert this `SourceSpan` into a `SourceSlice`. In debug mode, this method optionally checks
* that the byte offsets represented by this `SourceSpan` actually correspond to the expected
* string.
*/
toSlice(expected) {
const chars = this.data.asString();
return isDevelopingApp() && void 0 !== expected && chars !== expected &&
// eslint-disable-next-line no-console
console.warn(`unexpectedly found ${JSON.stringify(chars)} when slicing source, but expected ${JSON.stringify(expected)}`), new SourceSlice({
loc: this,
chars: expected || chars
});
}
/**
* For compatibility with SourceLocation in AST plugins
*
* @deprecated use startPosition instead
*/
get start() {
return this.loc.start;
}
/**
* For compatibility with SourceLocation in AST plugins
*
* @deprecated use withStart instead
*/
set start(position) {
this.data.locDidUpdate({
start: position
});
}
/**
* For compatibility with SourceLocation in AST plugins
*
* @deprecated use endPosition instead
*/
get end() {
return this.loc.end;
}
/**
* For compatibility with SourceLocation in AST plugins
*
* @deprecated use withEnd instead
*/
set end(position) {
this.data.locDidUpdate({
end: position
});
}
/**
* For compatibility with SourceLocation in AST plugins
*
* @deprecated use module instead
*/
get source() {
return this.module;
}
collapse(where) {
switch (where) {
case "start":
return this.getStart().collapsed();
case "end":
return this.getEnd().collapsed();
}
}
extend(other) {
return span(this.data.getStart(), other.data.getEnd());
}
serialize() {
return this.data.serialize();
}
slice({
skipStart = 0,
skipEnd = 0
}) {
return span(this.getStart().move(skipStart).data, this.getEnd().move(-skipEnd).data);
}
sliceStartChars({
skipStart = 0,
chars: chars
}) {
return span(this.getStart().move(skipStart).data, this.getStart().move(skipStart + chars).data);
}
sliceEndChars({
skipEnd = 0,
chars: chars
}) {
return span(this.getEnd().move(skipEnd - chars).data, this.getStart().move(-skipEnd).data);
}
}
class CharPositionSpan {
kind = OffsetKind.CharPosition;
_locPosSpan = null;
constructor(source, charPositions) {
this.source = source, this.charPositions = charPositions;
}
wrap() {
return new SourceSpan(this);
}
asString() {
return this.source.slice(this.charPositions.start.charPos, this.charPositions.end.charPos);
}
getModule() {
return this.source.module;
}
getStart() {
return this.charPositions.start;
}
getEnd() {
return this.charPositions.end;
}
locDidUpdate() {}
toHbsSpan() {
let locPosSpan = this._locPosSpan;
if (null === locPosSpan) {
const start = this.charPositions.start.toHbsPos(),
end = this.charPositions.end.toHbsPos();
locPosSpan = this._locPosSpan = null === start || null === end ? BROKEN : new HbsSpan(this.source, {
start: start,
end: end
});
}
return locPosSpan === BROKEN ? null : locPosSpan;
}
serialize() {
const {
start: {
charPos: start
},
end: {
charPos: end
}
} = this.charPositions;
return start === end ? start : [start, end];
}
toCharPosSpan() {
return this;
}
}
class HbsSpan {
kind = OffsetKind.HbsPosition;
_charPosSpan = null;
// the source location from Handlebars + AST Plugins -- could be wrong
_providedHbsLoc;
constructor(source, hbsPositions, providedHbsLoc = null) {
this.source = source, this.hbsPositions = hbsPositions, this._providedHbsLoc = providedHbsLoc;
}
serialize() {
const charPos = this.toCharPosSpan();
return null === charPos ? OffsetKind.Broken : charPos.wrap().serialize();
}
wrap() {
return new SourceSpan(this);
}
updateProvided(pos, edge) {
this._providedHbsLoc && (this._providedHbsLoc[edge] = pos),
// invalidate computed character offsets
this._charPosSpan = null, this._providedHbsLoc = {
start: pos,
end: pos
};
}
locDidUpdate({
start: start,
end: end
}) {
void 0 !== start && (this.updateProvided(start, "start"), this.hbsPositions.start = new HbsPosition(this.source, start, null)), void 0 !== end && (this.updateProvided(end, "end"), this.hbsPositions.end = new HbsPosition(this.source, end, null));
}
asString() {
const span = this.toCharPosSpan();
return null === span ? "" : span.asString();
}
getModule() {
return this.source.module;
}
getStart() {
return this.hbsPositions.start;
}
getEnd() {
return this.hbsPositions.end;
}
toHbsLoc() {
return {
start: this.hbsPositions.start.hbsPos,
end: this.hbsPositions.end.hbsPos
};
}
toHbsSpan() {
return this;
}
toCharPosSpan() {
let charPosSpan = this._charPosSpan;
if (null === charPosSpan) {
const start = this.hbsPositions.start.toCharPos(),
end = this.hbsPositions.end.toCharPos();
if (!start || !end) return charPosSpan = this._charPosSpan = BROKEN, null;
charPosSpan = this._charPosSpan = new CharPositionSpan(this.source, {
start: start,
end: end
});
}
return charPosSpan === BROKEN ? null : charPosSpan;
}
}
class InvisibleSpan {
constructor(kind,
// whatever was provided, possibly broken
loc,
// if the span represents a synthetic string
string = null) {
this.kind = kind, this.loc = loc, this.string = string;
}
serialize() {
switch (this.kind) {
case OffsetKind.Broken:
case OffsetKind.NonExistent:
return this.kind;
case OffsetKind.InternalsSynthetic:
return this.string || "";
}
}
wrap() {
return new SourceSpan(this);
}
asString() {
return this.string || "";
}
locDidUpdate({
start: start,
end: end
}) {
void 0 !== start && (this.loc.start = start), void 0 !== end && (this.loc.end = end);
}
getModule() {
// TODO: Make this reflect the actual module this span originated from
return "an unknown module";
}
getStart() {
return new InvisiblePosition(this.kind, this.loc.start);
}
getEnd() {
return new InvisiblePosition(this.kind, this.loc.end);
}
toCharPosSpan() {
return this;
}
toHbsSpan() {
return null;
}
toHbsLoc() {
return BROKEN_LOCATION;
}
}
const span = match(m => m.when(OffsetKind.HbsPosition, OffsetKind.HbsPosition, (left, right) => new HbsSpan(left.source, {
start: left,
end: right
}).wrap()).when(OffsetKind.CharPosition, OffsetKind.CharPosition, (left, right) => new CharPositionSpan(left.source, {
start: left,
end: right
}).wrap()).when(OffsetKind.CharPosition, OffsetKind.HbsPosition, (left, right) => {
const rightCharPos = right.toCharPos();
return null === rightCharPos ? new InvisibleSpan(OffsetKind.Broken, BROKEN_LOCATION).wrap() : span(left, rightCharPos);
}).when(OffsetKind.HbsPosition, OffsetKind.CharPosition, (left, right) => {
const leftCharPos = left.toCharPos();
return null === leftCharPos ? new InvisibleSpan(OffsetKind.Broken, BROKEN_LOCATION).wrap() : span(leftCharPos, right);
}).when("IS_INVISIBLE", "MATCH_ANY", left => new InvisibleSpan(left.kind, BROKEN_LOCATION).wrap()).when("MATCH_ANY", "IS_INVISIBLE", (_, right) => new InvisibleSpan(right.kind, BROKEN_LOCATION).wrap())),
BROKEN = "BROKEN";
/**
* All positions have these details in common. Most notably, all three kinds of positions can
* must be able to attempt to convert themselves into {@see CharPosition}.
*/
/**
* Used to indicate that an attempt to convert a `SourcePosition` to a character offset failed. It
* is separate from `null` so that `null` can be used to indicate that the computation wasn't yet
* attempted (and therefore to cache the failure)
*/
/**
* A `SourceOffset` represents a single position in the source.
*
* There are three kinds of backing data for `SourceOffset` objects:
*
* - `CharPosition`, which contains a character offset into the raw source string
* - `HbsPosition`, which contains a `SourcePosition` from the Handlebars AST, which can be
* converted to a `CharPosition` on demand.
* - `InvisiblePosition`, which represents a position not in source (@see {InvisiblePosition})
*/
class SourceOffset {
/**
* Create a `SourceOffset` from a Handlebars `SourcePosition`. It's stored as-is, and converted
* into a character offset on demand, which avoids unnecessarily computing the offset of every
* `SourceLocation`, but also means that broken `SourcePosition`s are not always detected.
*/
static forHbsPos(source, pos) {
return new HbsPosition(source, pos, null).wrap();
}
/**
* Create a `SourceOffset` that corresponds to a broken `SourcePosition`. This means that the
* calling code determined (or knows) that the `SourceLocation` doesn't correspond correctly to
* any part of the source.
*/
static broken(pos = UNKNOWN_POSITION) {
return new InvisiblePosition(OffsetKind.Broken, pos).wrap();
}
constructor(data) {
this.data = data;
}
/**
* Get the character offset for this `SourceOffset`, if possible.
*/
get offset() {
const charPos = this.data.toCharPos();
return null === charPos ? null : charPos.offset;
}
/**
* Compare this offset with another one.
*
* If both offsets are `HbsPosition`s, they're equivalent as long as their lines and columns are
* the same. This avoids computing offsets unnecessarily.
*
* Otherwise, two `SourceOffset`s are equivalent if their successfully computed character offsets
* are the same.
*/
eql(right) {
return eql(this.data, right.data);
}
/**
* Create a span that starts from this source offset and ends with another source offset. Avoid
* computing character offsets if both `SourceOffset`s are still lazy.
*/
until(other) {
return span(this.data, other.data);
}
/**
* Create a `SourceOffset` by moving the character position represented by this source offset
* forward or backward (if `by` is negative), if possible.
*
* If this `SourceOffset` can't compute a valid character offset, `move` returns a broken offset.
*
* If the resulting character offset is less than 0 or greater than the size of the source, `move`
* returns a broken offset.
*/
move(by) {
const charPos = this.data.toCharPos();
if (null === charPos) return SourceOffset.broken();
{
const result = charPos.offset + by;
return charPos.source.check(result) ? new CharPosition(charPos.source, result).wrap() : SourceOffset.broken();
}
}
/**
* Create a new `SourceSpan` that represents a collapsed range at this source offset. Avoid
* computing the character offset if it has not already been computed.
*/
collapsed() {
return span(this.data, this.data);
}
/**
* Convert this `SourceOffset` into a Handlebars {@see SourcePosition} for compatibility with
* existing plugins.
*/
toJSON() {
return this.data.toJSON();
}
}
class CharPosition {
kind = OffsetKind.CharPosition;
/** Computed from char offset */
_locPos = null;
constructor(source, charPos) {
this.source = source, this.charPos = charPos;
}
/**
* This is already a `CharPosition`.
*
* {@see HbsPosition} for the alternative.
*/
toCharPos() {
return this;
}
/**
* Produce a Handlebars {@see SourcePosition} for this `CharPosition`. If this `CharPosition` was
* computed using {@see SourceOffset#move}, this will compute the `SourcePosition` for the offset.
*/
toJSON() {
const hbs = this.toHbsPos();
return null === hbs ? UNKNOWN_POSITION : hbs.toJSON();
}
wrap() {
return new SourceOffset(this);
}
/**
* A `CharPosition` always has an offset it can produce without any additional computation.
*/
get offset() {
return this.charPos;
}
/**
* Convert the current character offset to an `HbsPosition`, if it was not already computed. Once
* a `CharPosition` has computed its `HbsPosition`, it will not need to do compute it again, and
* the same `CharPosition` is retained when used as one of the ends of a `SourceSpan`, so
* computing the `HbsPosition` should be a one-time operation.
*/
toHbsPos() {
let locPos = this._locPos;
if (null === locPos) {
const hbsPos = this.source.hbsPosFor(this.charPos);
this._locPos = locPos = null === hbsPos ? BROKEN : new HbsPosition(this.source, hbsPos, this.charPos);
}
return locPos === BROKEN ? null : locPos;
}
}
class HbsPosition {
kind = OffsetKind.HbsPosition;
_charPos;
constructor(source, hbsPos, charPos = null) {
this.source = source, this.hbsPos = hbsPos, this._charPos = null === charPos ? null : new CharPosition(source, charPos);
}
/**
* Lazily compute the character offset from the {@see SourcePosition}. Once an `HbsPosition` has
* computed its `CharPosition`, it will not need to do compute it again, and the same
* `HbsPosition` is retained when used as one of the ends of a `SourceSpan`, so computing the
* `CharPosition` should be a one-time operation.
*/
toCharPos() {
let charPos = this._charPos;
if (null === charPos) {
const charPosNumber = this.source.charPosFor(this.hbsPos);
this._charPos = charPos = null === charPosNumber ? BROKEN : new CharPosition(this.source, charPosNumber);
}
return charPos === BROKEN ? null : charPos;
}
/**
* Return the {@see SourcePosition} that this `HbsPosition` was instantiated with. This operation
* does not need to compute anything.
*/
toJSON() {
return this.hbsPos;
}
wrap() {
return new SourceOffset(this);
}
/**
* This is already an `HbsPosition`.
*
* {@see CharPosition} for the alternative.
*/
toHbsPos() {
return this;
}
}
class InvisiblePosition {
constructor(kind,
// whatever was provided, possibly broken
pos) {
this.kind = kind, this.pos = pos;
}
/**
* A broken position cannot be turned into a {@see CharacterPosition}.
*/
toCharPos() {
return null;
}
/**
* The serialization of an `InvisiblePosition is whatever Handlebars {@see SourcePosition} was
* originally identified as broken, non-existent or synthetic.
*
* If an `InvisiblePosition` never had an source offset at all, this method returns
* {@see UNKNOWN_POSITION} for compatibility.
*/
toJSON() {
return this.pos;
}
wrap() {
return new SourceOffset(this);
}
get offset() {
return null;
}
}
/**
* Compare two {@see AnyPosition} and determine whether they are equal.
*
* @see {SourceOffset#eql}
*/
const eql = match(m => m.when(OffsetKind.HbsPosition, OffsetKind.HbsPosition, ({
hbsPos: left
}, {
hbsPos: right
}) => left.column === right.column && left.line === right.line).when(OffsetKind.CharPosition, OffsetKind.CharPosition, ({
charPos: left
}, {
charPos: right
}) => left === right).when(OffsetKind.CharPosition, OffsetKind.HbsPosition, ({
offset: left
}, right) => left === right.toCharPos()?.offset).when(OffsetKind.HbsPosition, OffsetKind.CharPosition, (left, {
offset: right
}) => left.toCharPos()?.offset === right).when("MATCH_ANY", "MATCH_ANY", () => !1));
class Source {
static from(source, options = {}) {
return new Source(source, options.meta?.moduleName);
}
constructor(source, module = "an unknown module") {
this.source = source, this.module = module;
}
/**
* Validate that the character offset represents a position in the source string.
*/
check(offset) {
return offset >= 0 && offset <= this.source.length;
}
slice(start, end) {
return this.source.slice(start, end);
}
offsetFor(line, column) {
return SourceOffset.forHbsPos(this, {
line: line,
column: column
});
}
spanFor({
start: start,
end: end
}) {
return SourceSpan.forHbsLoc(this, {
start: {
line: start.line,
column: start.column
},
end: {
line: end.line,
column: end.column
}
});
}
hbsPosFor(offset) {
let seenLines = 0,
seenChars = 0;
if (offset > this.source.length) return null;
// eslint-disable-next-line no-constant-condition
for (;;) {
let nextLine = this.source.indexOf("\n", seenChars);
if (offset <= nextLine || -1 === nextLine) return {
line: seenLines + 1,
column: offset - seenChars
};
seenLines += 1, seenChars = nextLine + 1;
}
}
charPosFor(position) {
let {
line: line,
column: column
} = position,
sourceLength = this.source.length,
seenLines = 0,
seenChars = 0;
for (; seenChars < sourceLength;) {
let nextLine = this.source.indexOf("\n", seenChars);
if (-1 === nextLine && (nextLine = this.source.length), seenLines === line - 1) {
if (seenChars + column > nextLine) return nextLine;
if (isDevelopingApp()) {
let roundTrip = this.hbsPosFor(seenChars + column);
debugAssert(null !== roundTrip, "the returned offset failed to round-trip"), debugAssert(roundTrip.line === line, "the round-tripped line didn't match the original line"), debugAssert(roundTrip.column === column, "the round-tripped column didn't match the original column");
}
return seenChars + column;
}
if (-1 === nextLine) return 0;
seenLines += 1, seenChars = nextLine + 1;
}
return sourceLength;
}
}
class SpanList {
static range(span, fallback = SourceSpan.NON_EXISTENT) {
return new SpanList(span.map(loc)).getRangeOffset(fallback);
}
_span;
constructor(span = []) {
this._span = span;
}
add(offset) {
this._span.push(offset);
}
getRangeOffset(fallback) {
if (isPresentArray(this._span)) {
let first = getFirst(this._span),
last = getLast(this._span);
return first.extend(last);
}
return fallback;
}
}
function loc(span) {
if (Array.isArray(span)) {
let first = getFirst(span),
last = getLast(span);
return loc(first).extend(loc(last));
}
return span instanceof SourceSpan ? span : span.loc;
}
function hasSpan(span) {
return !Array.isArray(span) || 0 !== span.length;
}
function maybeLoc(location, fallback) {
return hasSpan(location) ? loc(location) : fallback;
}
var api$2 = Object.freeze({
__proto__: null,
NON_EXISTENT_LOCATION: NON_EXISTENT_LOCATION,
SYNTHETIC_LOCATION: SYNTHETIC_LOCATION,
Source: Source,
SourceOffset: SourceOffset,
SourceSlice: SourceSlice,
SourceSpan: SourceSpan,
SpanList: SpanList,
UNKNOWN_POSITION: UNKNOWN_POSITION,
hasSpan: hasSpan,
loc: loc,
maybeLoc: maybeLoc
});
function generateSyntaxError(message, location) {
let {
module: module,
loc: loc
} = location,
{
line: line,
column: column
} = loc.start,
code = location.asString(),
quotedCode = code ? `\n\n|\n| ${code.split("\n").join("\n| ")}\n|\n\n` : "",
error = new Error(`${message}: ${quotedCode}(error occurred in '${module}' @ line ${line} : column ${column})`);
return error.name = "SyntaxError", error.location = location, error.code = code, error;
}
// ensure stays in sync with typing
// ParentNode and ChildKey types are derived from VisitorKeysMap
const visitorKeys = {
Template: ["body"],
Block: ["body"],
MustacheStatement: ["path", "params", "hash"],
BlockStatement: ["path", "params", "hash", "program", "inverse"],
ElementModifierStatement: ["path", "params", "hash"],
CommentStatement: [],
MustacheCommentStatement: [],
ElementNode: ["attributes", "modifiers", "children", "comments"],
AttrNode: ["value"],
TextNode: [],
ConcatStatement: ["parts"],
SubExpression: ["path", "params", "hash"],
PathExpression: [],
StringLiteral: [],
BooleanLiteral: [],
NumberLiteral: [],
NullLiteral: [],
UndefinedLiteral: [],
Hash: ["pairs"],
HashPair: ["value"]
},
TraversalError = function () {
function TraversalError(message, node, parent, key) {
let error = Error.call(this, message);
this.key = key, this.message = message, this.node = node, this.parent = parent, error.stack && (this.stack = error.stack);
}
return TraversalError.prototype = Object.create(Error.prototype), TraversalError.prototype.constructor = TraversalError, TraversalError;
}();
function cannotRemoveNode(node, parent, key) {
return new TraversalError("Cannot remove a node unless it is part of an array", node, parent, key);
}
function cannotReplaceNode(node, parent, key) {
return new TraversalError("Cannot replace a node with multiple nodes unless it is part of an array", node, parent, key);
}
function cannotReplaceOrRemoveInKeyHandlerYet(node, key) {
return new TraversalError("Replacing and removing in key handlers is not yet supported.", node, null, key);
}
class WalkerPath {
node;
parent;
parentKey;
constructor(node, parent = null, parentKey = null) {
this.node = node, this.parent = parent, this.parentKey = parentKey;
}
get parentNode() {
return this.parent ? this.parent.node : null;
}
parents() {
return {
[Symbol.iterator]: () => new PathParentsIterator(this)
};
}
}
class PathParentsIterator {
path;
constructor(path) {
this.path = path;
}
next() {
return this.path.parent ? (this.path = this.path.parent, {
done: !1,
value: this.path
}) : {
done: !0,
value: null
};
}
}
function getEnterFunction(handler) {
return "function" == typeof handler ? handler : handler.enter;
}
function getExitFunction(handler) {
return "function" == typeof handler ? void 0 : handler.exit;
}
function visitNode(visitor, path) {
let enter,
exit,
result,
{
node: node,
parent: parent,
parentKey: parentKey
} = path,
handler = function (visitor, nodeType) {
if (visitor.Program && ("Template" === nodeType && !visitor.Template || "Block" === nodeType && !visitor.Block)) return deprecate(`The 'Program' visitor node is deprecated. Use 'Template' or 'Block' instead (node was '${nodeType}') `), visitor.Program;
let handler = visitor[nodeType];
return void 0 !== handler ? handler : visitor.All;
}(visitor, node.type);
if (void 0 !== handler && (enter = getEnterFunction(handler), exit = getExitFunction(handler)), void 0 !== enter && (result = enter(node, path)), null != result) {
if (JSON.stringify(node) !== JSON.stringify(result)) return Array.isArray(result) ? (visitArray(visitor, result, parent, parentKey), result) : visitNode(visitor, new WalkerPath(result, parent, parentKey)) || result;
result = void 0;
}
if (void 0 === result) {
let keys = visitorKeys[node.type];
for (let i = 0; i < keys.length; i++)
// we know if it has child keys we can widen to a ParentNode
visitKey(visitor, handler, path, keys[i]);
void 0 !== exit && (result = exit(node, path));
}
return result;
}
function set(node, key, value) {
node[key] = value;
}
function visitKey(visitor, handler, path, key) {
let keyEnter,
keyExit,
{
node: node
} = path,
value = function (node, key) {
return node[key];
}(node, key);
if (value) {
if (void 0 !== handler) {
let keyHandler = function (handler, key) {
let keyVisitor = "function" != typeof handler ? handler.keys : void 0;
if (void 0 === keyVisitor) return;
let keyHandler = keyVisitor[key];
return void 0 !== keyHandler ? keyHandler : keyVisitor.All;
}(handler, key);
void 0 !== keyHandler && (keyEnter = getEnterFunction(keyHandler), keyExit = getExitFunction(keyHandler));
}
if (void 0 !== keyEnter && void 0 !== keyEnter(node, key)) throw cannotReplaceOrRemoveInKeyHandlerYet(node, key);
if (Array.isArray(value)) visitArray(visitor, value, path, key);else {
let result = visitNode(visitor, new WalkerPath(value, path, key));
void 0 !== result &&
// TODO: dynamically check the results by having a table of
// expected node types in value space, not just type space
function (node, key, value, result) {
if (null === result) throw cannotRemoveNode(value, node, key);
if (Array.isArray(result)) {
if (1 !== result.length) throw 0 === result.length ? cannotRemoveNode(value, node, key) : cannotReplaceNode(value, node, key);
set(node, key, result[0]);
} else set(node, key, result);
}(node, key, value, result);
}
if (void 0 !== keyExit && void 0 !== keyExit(node, key)) throw cannotReplaceOrRemoveInKeyHandlerYet(node, key);
}
}
function visitArray(visitor, array, parent, parentKey) {
for (let i = 0; i < array.length; i++) {
let node = unwrap(array[i]),
result = visitNode(visitor, new WalkerPath(node, parent, parentKey));
void 0 !== result && (i += spliceArray(array, i, result) - 1);
}
}
function spliceArray(array, index, result) {
return null === result ? (array.splice(index, 1), 0) : Array.isArray(result) ? (array.splice(index, 1, ...result), result.length) : (array.splice(index, 1, result), 1);
}
function traverse(node, visitor) {
visitNode(visitor, new WalkerPath(node));
}
class Walker {
stack = [];
constructor(order) {
this.order = order;
}
visit(node, visitor) {
node && (this.stack.push(node), "post" === this.order ? (this.children(node, visitor), visitor(node, this)) : (visitor(node, this), this.children(node, visitor)), this.stack.pop());
}
children(node, callback) {
switch (node.type) {
case "Block":
case "Template":
return void walkBody(this, node.body, callback);
case "ElementNode":
return void walkBody(this, node.children, callback);
case "BlockStatement":
return this.visit(node.program, callback), void this.visit(node.inverse || null, callback);
default:
return;
}
}
}
function walkBody(walker, body, callback) {
for (const child of body) walker.visit(child, callback);
}
function appendChild(parent, node) {
(function (node) {
switch (node.type) {
case "Block":
case "Template":
return node.body;
case "ElementNode":
return node.children;
}
})(parent).push(node);
}
function isHBSLiteral(path) {
return "StringLiteral" === path.type || "BooleanLiteral" === path.type || "NumberLiteral" === path.type || "NullLiteral" === path.type || "UndefinedLiteral" === path.type;
}
let _SOURCE;
function SOURCE() {
return _SOURCE || (_SOURCE = new Source("", "(synthetic)")), _SOURCE;
}
// const SOURCE = new Source('', '(tests)');
// Statements
function buildVar(name, loc) {
return b.var({
name: name,
loc: buildLoc(loc || null)
});
}
function buildPath(path, loc) {
let span = buildLoc(loc || null);
if ("string" != typeof path) {
if ("type" in path) return path;
{
debugAssert(-1 === path.head.indexOf("."), "builder.path({ head, tail }) should not be called with a head with dots in it");
let {
head: head,
tail: tail
} = path;
return b.path({
head: b.head({
original: head,
loc: span.sliceStartChars({
chars: head.length
})
}),
tail: tail,
loc: buildLoc(loc || null)
});
}
}
let {
head: head,
tail: tail
} = function (original, loc) {
let [head, ...tail] = asPresentArray(original.split(".")),
headNode = b.head({
original: head,
loc: buildLoc(loc || null)
});
return b.path({
head: headNode,
tail: tail,
loc: buildLoc(loc || null)
});
}(path, span);
return b.path({
head: head,
tail: tail,
loc: span
});
}
function buildLiteral(type, value, loc) {
return b.literal({
type: type,
value: value,
loc: buildLoc(loc || null)
});
}
// Miscellaneous
function buildHash(pairs = [], loc) {
return b.hash({
pairs: pairs,
loc: buildLoc(loc || null)
});
}
function buildBlockParams(params) {
return params.map(p => "string" == typeof p ? b.var({
name: p,
loc: SourceSpan.synthetic(p)
}) : p);
}
function buildBlockItself(body = [], params = [], chained = !1, loc) {
return b.blockItself({
body: body,
params: buildBlockParams(params),
chained: chained,
loc: buildLoc(loc || null)
});
}
function buildTemplate(body = [], blockParams = [], loc) {
return b.template({
body: body,
blockParams: blockParams,
loc: buildLoc(loc || null)
});
}
function buildLoc(...args) {
if (1 === args.length) {
let loc = args[0];
return loc && "object" == typeof loc ? SourceSpan.forHbsLoc(SOURCE(), loc) : SourceSpan.forHbsLoc(SOURCE(), SYNTHETIC_LOCATION);
}
{
let [startLine, startColumn, endLine, endColumn, _source] = args,
source = _source ? new Source("", _source) : SOURCE();
return SourceSpan.forHbsLoc(source, {
start: {
line: startLine,
column: startColumn
},
end: {
line: endLine || startLine,
column: endColumn || startColumn
}
});
}
}
var publicBuilder = {
mustache: function (path, params = [], hash = buildHash([]), trusting = !1, loc, strip) {
return b.mustache({
path: buildPath(path),
params: params,
hash: hash,
trusting: trusting,
strip: strip,
loc: buildLoc(loc || null)
});
},
block: function (path, params, hash, _defaultBlock, _elseBlock = null, loc, openStrip, inverseStrip, closeStrip) {
let defaultBlock,
elseBlock = null;
return "Template" === _defaultBlock.type ? (deprecate("b.program is deprecated. Use b.blockItself instead."), defaultBlock = b.blockItself({
params: buildBlockParams(_defaultBlock.blockParams),
body: _defaultBlock.body,
loc: _defaultBlock.loc
})) : defaultBlock = _defaultBlock, "Template" === _elseBlock?.type ? (deprecate("b.program is deprecated. Use b.blockItself instead."), debugAssert(0 === _elseBlock.blockParams.length, "{{else}} block cannot have block params"), elseBlock = b.blockItself({
params: [],
body: _elseBlock.body,
loc: _elseBlock.loc
})) : elseBlock = _elseBlock, b.block({
path: buildPath(path),
params: params || [],
hash: hash || buildHash([]),
defaultBlock: defaultBlock,
elseBlock: elseBlock,
loc: buildLoc(loc || null),
openStrip: openStrip,
inverseStrip: inverseStrip,
closeStrip: closeStrip
});
},
comment: function (value, loc) {
return b.comment({
value: value,
loc: buildLoc(loc || null)
});
},
mustacheComment: function (value, loc) {
return b.mustacheComment({
value: value,
loc: buildLoc(loc || null)
});
},
element:
// Nodes
function (tag, options = {}) {
let path,
selfClosing,
{
attrs: attrs,
blockParams: blockParams,
modifiers: modifiers,
comments: comments,
children: children,
openTag: openTag,
closeTag: _closeTag,
loc: loc
} = options;
// this is used for backwards compat, prior to `selfClosing` being part of the ElementNode AST
"string" == typeof tag ? tag.endsWith("/") ? (path = buildPath(tag.slice(0, -1)), selfClosing = !0) : path = buildPath(tag) : "type" in tag ? (debugAssert("PathExpression" === tag.type, `Invalid tag type ${tag.type}`), path = tag) : "path" in tag ? (debugAssert("PathExpression" === tag.path.type, `Invalid tag type ${tag.path.type}`), path = tag.path, selfClosing = tag.selfClosing) : (path = buildPath(tag.name), selfClosing = tag.selfClosing), selfClosing && debugAssert(null == _closeTag, "Cannot build a self-closing tag with a closeTag source location");
let params = blockParams?.map(param => "string" == typeof param ? buildVar(param) : param),
closeTag = null;
return _closeTag ? closeTag = buildLoc(_closeTag || null) : void 0 === _closeTag && (closeTag = selfClosing || isVoidTag(path.original) ? null : buildLoc(null)), b.element({
path: path,
selfClosing: selfClosing || !1,
attributes: attrs || [],
params: params || [],
modifiers: modifiers || [],
comments: comments || [],
children: children || [],
openTag: buildLoc(openTag || null),
closeTag: closeTag,
loc: buildLoc(loc || null)
});
},
elementModifier: function (path, params, hash, loc) {
return b.elementModifier({
path: buildPath(path),
params: params || [],
hash: hash || buildHash([]),
loc: buildLoc(loc || null)
});
},
attr: function (name, value, loc) {
return b.attr({
name: name,
value: value,
loc: buildLoc(loc || null)
});
},
text: function (chars = "", loc) {
return b.text({
chars: chars,
loc: buildLoc(loc || null)
});
}
// Expressions
,
sexpr: function (path, params = [], hash = buildHash([]), loc) {
return b.sexpr({
path: buildPath(path),
params: params,
hash: hash,
loc: buildLoc(loc || null)
});
},
concat: function (parts, loc) {
if (!isPresentArray(parts)) throw new Error("b.concat requires at least one part");
return b.concat({
parts: parts,
loc: buildLoc(loc || null)
});
},
hash: buildHash,
pair: function (key, value, loc) {
return b.pair({
key: key,
value: value,
loc: buildLoc(loc || null)
});
},
literal: buildLiteral,
program: function (body, blockParams, loc) {
return deprecate("b.program is deprecated. Use b.template or b.blockItself instead."), blockParams && blockParams.length ? buildBlockItself(body, blockParams, !1, loc) : buildTemplate(body, [], loc);
},
blockItself: buildBlockItself,
template: buildTemplate,
loc: buildLoc,
pos: function (line, column) {
return b.pos({
line: line,
column: column
});
},
path: buildPath,
fullPath: function (head, tail = [], loc) {
return b.path({
head: head,
tail: tail,
loc: buildLoc(loc || null)
});
},
head: function (original, loc) {
return b.head({
original: original,
loc: buildLoc(loc || null)
});
},
at: function (name, loc) {
return b.atName({
name: name,
loc: buildLoc(loc || null)
});
},
var: buildVar,
this: function (loc) {
return b.this({
loc: buildLoc(loc || null)
});
},
string: literal("StringLiteral"),
boolean: literal("BooleanLiteral"),
number: literal("NumberLiteral"),
undefined: () => buildLiteral("UndefinedLiteral", void 0),
null: () => buildLiteral("NullLiteral", null)
};
function literal(type) {
return function (value, loc) {
return buildLiteral(type, value, loc);
};
}
const DEFAULT_STRIP = {
close: !1,
open: !1
},
b = new
/**
* The Parser Builder differentiates from the public builder API by:
*
* 1. Offering fewer different ways to instantiate nodes
* 2. Mandating source locations
*/
class {
pos({
line: line,
column: column
}) {
return {
line: line,
column: column
};
}
blockItself({
body: body,
params: params,
chained = !1,
loc: loc
}) {
return {
type: "Block",
body: body,
params: params,
get blockParams() {
return this.params.map(p => p.name);
},
set blockParams(params) {
this.params = params.map(name => b.var({
name: name,
loc: SourceSpan.synthetic(name)
}));
},
chained: chained,
loc: loc
};
}
template({
body: body,
blockParams: blockParams,
loc: loc
}) {
return {
type: "Template",
body: body,
blockParams: blockParams,
loc: loc
};
}
mustache({
path: path,
params: params,
hash: hash,
trusting: trusting,
loc: loc,
strip = DEFAULT_STRIP
}) {
return function ({
path: path,
params: params,
hash: hash,
trusting: trusting,
strip: strip,
loc: loc
}) {
const node = {
type: "MustacheStatement",
path: path,
params: params,
hash: hash,
trusting: trusting,
strip: strip,
loc: loc
};
return Object.defineProperty(node, "escaped", {
enumerable: !1,
get() {
return deprecate("The escaped property on mustache nodes is deprecated, use trusting instead"), !this.trusting;
},
set(value) {
deprecate("The escaped property on mustache nodes is deprecated, use trusting instead"), this.trusting = !value;
}
}), node;
}({
path: path,
params: params,
hash: hash,
trusting: trusting,
strip: strip,
loc: loc
});
}
block({
path: path,
params: params,
hash: hash,
defaultBlock: defaultBlock,
elseBlock = null,
loc: loc,
openStrip = DEFAULT_STRIP,
inverseStrip = DEFAULT_STRIP,
closeStrip = DEFAULT_STRIP
}) {
return {
type: "BlockStatement",
path: path,
params: params,
hash: hash,
program: defaultBlock,
inverse: elseBlock,
loc: loc,
openStrip: openStrip,
inverseStrip: inverseStrip,
closeStrip: closeStrip
};
}
comment({
value: value,
loc: loc
}) {
return {
type: "CommentStatement",
value: value,
loc: loc
};
}
mustacheComment({
value: value,
loc: loc
}) {
return {
type: "MustacheCommentStatement",
value: value,
loc: loc
};
}
concat({
parts: parts,
loc: loc
}) {
return {
type: "ConcatStatement",
parts: parts,
loc: loc
};
}
element({
path: path,
selfClosing: selfClosing,
attributes: attributes,
modifiers: modifiers,
params: params,
comments: comments,
children: children,
openTag: openTag,
closeTag: closeTag,
loc: loc
}) {
let _selfClosing = selfClosing;
return {
type: "ElementNode",
path: path,
attributes: attributes,
modifiers: modifiers,
params: params,
comments: comments,
children: children,
openTag: openTag,
closeTag: closeTag,
loc: loc,
get tag() {
return this.path.original;
},
set tag(name) {
this.path.original = name;
},
get blockParams() {
return this.params.map(p => p.name);
},
set blockParams(params) {
this.params = params.map(name => b.var({
name: name,
loc: SourceSpan.synthetic(name)
}));
},
get selfClosing() {
return _selfClosing;
},
set selfClosing(selfClosing) {
_selfClosing = selfClosing, this.closeTag = selfClosing ? null : SourceSpan.synthetic(`</${this.tag}>`);
}
};
}
elementModifier({
path: path,
params: params,
hash: hash,
loc: loc
}) {
return {
type: "ElementModifierStatement",
path: path,
params: params,
hash: hash,
loc: loc
};
}
attr({
name: name,
value: value,
loc: loc
}) {
return {
type: "AttrNode",
name: name,
value: value,
loc: loc
};
}
text({
chars: chars,
loc: loc
}) {
return {
type: "TextNode",
chars: chars,
loc: loc
};
}
sexpr({
path: path,
params: params,
hash: hash,
loc: loc
}) {
return {
type: "SubExpression",
path: path,
params: params,
hash: hash,
loc: loc
};
}
path({
head: head,
tail: tail,
loc: loc
}) {
return function ({
head: head,
tail: tail,
loc: loc
}) {
const node = {
type: "PathExpression",
head: head,
tail: tail,
get original() {
return [this.head.original, ...this.tail].join(".");
},
set original(value) {
let [head, ...tail] = asPresentArray(value.split("."));
this.head = publicBuilder.head(head, this.head.loc), this.tail = tail;
},
loc: loc
};
return Object.defineProperty(node, "parts", {
enumerable: !1,
get() {
deprecate("The parts property on path nodes is deprecated, use head and tail instead");
let parts = asPresentArray(this.original.split("."));
return "this" === parts[0] ?
// parts does not include `this`
parts.shift() : parts[0].startsWith("@") && (
// parts does not include leading `@`
parts[0] = parts[0].slice(1)), Object.freeze(parts);
},
set(values) {
deprecate("The parts property on mustache nodes is deprecated, use head and tail instead");
let parts = [...values];
// you are not supposed to already have `this` or `@` in the parts, but since this is
// deprecated anyway, we will infer what you meant and allow it
"this" === parts[0] || parts[0]?.startsWith("@") || ("ThisHead" === this.head.type ? parts.unshift("this") : "AtHead" === this.head.type && (parts[0] = `@${parts[0]}`)), this.original = parts.join(".");
}
}), Object.defineProperty(node, "this", {
enumerable: !1,
get() {
return deprecate("The this property on path nodes is deprecated, use head.type instead"), "ThisHead" === this.head.type;
}
}), Object.defineProperty(node, "data", {
enumerable: !1,
get() {
return deprecate("The data property on path nodes is deprecated, use head.type instead"), "AtHead" === this.head.type;
}
}), node;
}({
head: head,
tail: tail,
loc: loc
});
}
head({
original: original,
loc: loc
}) {
return "this" === original ? this.this({
loc: loc
}) : "@" === original[0] ? this.atName({
name: original,
loc: loc
}) : this.var({
name: original,
loc: loc
});
}
this({
loc: loc
}) {
return {
type: "ThisHead",
get original() {
return "this";
},
loc: loc
};
}
atName({
name: name,
loc: loc
}) {
let _name = "";
const node = {
type: "AtHead",
get name() {
return _name;
},
set name(value) {
debugAssert("@" === value[0], "call builders.at() with a string that starts with '@'"), debugAssert(-1 === value.indexOf("."), "builder.at() should not be called with a name with dots in it"), _name = value;
},
get original() {
return this.name;
},
set original(value) {
this.name = value;
},
loc: loc
};
// trigger the assertions
return node.name = name, node;
}
var({
name: name,
loc: loc
}) {
let _name = "";
const node = {
type: "VarHead",
get name() {
return _name;
},
set name(value) {
debugAssert("this" !== value, "You called builders.var() with 'this'. Call builders.this instead"), debugAssert("@" !== value[0], `You called builders.var() with '${name}'. Call builders.at('${name}') instead`), debugAssert(-1 === value.indexOf("."), "builder.var() should not be called with a name with dots in it"), _name = value;
},
get original() {
return this.name;
},
set original(value) {
this.name = value;
},
loc: loc
};
// trigger the assertions
return node.name = name, node;
}
hash({
pairs: pairs,
loc: loc
}) {
return {
type: "Hash",
pairs: pairs,
loc: loc
};
}
pair({
key: key,
value: value,
loc: loc
}) {
return {
type: "HashPair",
key: key,
value: value,
loc: loc
};
}
literal({
type: type,
value: value,
loc: loc
}) {
return function ({
type: type,
value: value,
loc: loc
}) {
const node = {
type: type,
value: value,
loc: loc
};
return Object.defineProperty(node, "original", {
enumerable: !1,
get() {
return deprecate("The original property on literal nodes is deprecated, use value instead"), this.value;
},
set(value) {
deprecate("The original property on literal nodes is deprecated, use value instead"), this.value = value;
}
}), node;
}({
type: type,
value: value,
loc: loc
});
}
}();
class Parser {
elementStack = [];
lines;
source;
currentAttribute = null;
currentNode = null;
tokenizer;
constructor(source, entityParser = new EntityParser(namedCharRefs), mode = "precompile") {
this.source = source, this.lines = source.source.split(/\r\n?|\n/u), this.tokenizer = new EventedTokenizer(this, entityParser, mode);
}
offset() {
let {
line: line,
column: column
} = this.tokenizer;
return this.source.offsetFor(line, column);
}
pos({
line: line,
column: column
}) {
return this.source.offsetFor(line, column);
}
finish(node) {
return assign({}, node, {
loc: node.start.until(this.offset())
});
// node.loc = node.loc.withEnd(end);
}
get currentAttr() {
return expect(this.currentAttribute, "expected attribute");
}
get currentTag() {
let node = this.currentNode;
return debugAssert(node && ("StartTag" === node.type || "EndTag" === node.type), "expected tag"), node;
}
get currentStartTag() {
let node = this.currentNode;
return debugAssert(node && "StartTag" === node.type, "expected start tag"), node;
}
get currentEndTag() {
let node = this.currentNode;
return debugAssert(node && "EndTag" === node.type, "expected end tag"), node;
}
get currentComment() {
let node = this.currentNode;
return debugAssert(node && "CommentStatement" === node.type, "expected a comment"), node;
}
get currentData() {
let node = this.currentNode;
return debugAssert(node && "TextNode" === node.type, "expected a text node"), node;
}
acceptNode(node) {
return this[node.type](node);
}
currentElement() {
return getLast(asPresentArray(this.elementStack));
}
sourceForNode(node, endNode) {
let line,
lastLine,
lastColumn,
firstLine = node.loc.start.line - 1,
currentLine = firstLine - 1,
firstColumn = node.loc.start.column,
string = [];
for (endNode ? (lastLine = endNode.loc.end.line - 1, lastColumn = endNode.loc.end.column) : (lastLine = node.loc.end.line - 1, lastColumn = node.loc.end.column); currentLine < lastLine;) currentLine++, line = unwrap(this.lines[currentLine]), currentLine === firstLine ? firstLine === lastLine ? string.push(line.slice(firstColumn, lastColumn)) : string.push(line.slice(firstColumn)) : currentLine === lastLine ? string.push(line.slice(0, lastColumn)) : string.push(line);
return string.join("\n");
}
}
class HandlebarsNodeVisitors extends Parser {
// Because we interleave the HTML and HBS parsing, sometimes the HTML
// tokenizer can run out of tokens when we switch into {{...}} or reached
// EOF. There are positions where neither of these are expected, and it would
// like to generate an error, but there is no span to attach the error to.
// This allows the HTML tokenization to stash an error message and the next
// mustache visitor will attach the message to the appropriate span and throw
// the error.
pendingError = null;
parse(program, blockParams) {
let node = b.template({
body: [],
blockParams: blockParams,
loc: this.source.spanFor(program.loc)
}),
template = this.parseProgram(node, program);
// TODO: we really need to verify that the tokenizer is in an acceptable
// state when we are "done" parsing. For example, right now, `<foo` parses
// into `Template { body: [] }` which is obviously incorrect
return this.pendingError?.eof(template.loc.getEnd()), template;
}
Program(program, blockParams) {
// The abstract signature doesn't have the blockParams argument, but in
// practice we can only come from this.BlockStatement() which adds the
// extra argument for us
debugAssert(Array.isArray(blockParams), "[BUG] Program in parser unexpectedly called without block params");
let node = b.blockItself({
body: [],
params: blockParams,
chained: program.chained,
loc: this.source.spanFor(program.loc)
});
return this.parseProgram(node, program);
}
parseProgram(node, program) {
if (0 === program.body.length) return node;
let poppedNode;
try {
this.elementStack.push(node);
for (let child of program.body) this.acceptNode(child);
} finally {
poppedNode = this.elementStack.pop();
}
// Ensure that that the element stack is balanced properly.
if (node !== poppedNode) {
if ("ElementNode" === poppedNode?.type) throw generateSyntaxError(`Unclosed element \`${poppedNode.tag}\``, poppedNode.loc);
// If the stack is not balanced, then it is likely our own bug, because
// any unclosed Handlebars blocks should already been caught by now
debugAssert(void 0 !== poppedNode, "[BUG] empty parser elementStack"), debugAssert(!1, `[BUG] mismatched parser elementStack node: ${node.type}`);
}
return node;
}
BlockStatement(block) {
if ("comment" === this.tokenizer.state) return void this.appendToCommentData(this.sourceForNode(block));
if ("data" !== this.tokenizer.state && "beforeData" !== this.tokenizer.state) throw generateSyntaxError("A block may only be used inside an HTML element or another block.", this.source.spanFor(block.loc));
const {
path: path,
params: params,
hash: hash
} = acceptCallNodes(this, block),
loc = this.source.spanFor(block.loc);
// Backfill block params loc for the default block
let blockParams = [];
if (block.program.blockParams?.length) {
// Start from right after the hash
let span = hash.loc.collapse("end");
// Extend till the beginning of the block
span = block.program.loc ? span.withEnd(this.source.spanFor(block.program.loc).getStart()) : block.program.body[0] ? span.withEnd(this.source.spanFor(block.program.body[0].loc).getStart()) : span.withEnd(loc.getEnd());
// Now we have a span for something like this:
// {{#foo bar baz=bat as |wow wat|}}
// ~~~~~~~~~~~~~~~
// Or, if we are unlucky:
// {{#foo bar baz=bat as |wow wat|}}{{/foo}}
// ~~~~~~~~~~~~~~~~~~~~~~~
// Either way, within this span, there should be exactly two pipes
// fencing our block params, neatly whitespace separated and with
// legal identifiers only
const content = span.asString();
let skipStart = content.indexOf("|") + 1;
const limit = content.indexOf("|", skipStart);
for (const name of block.program.blockParams) {
let nameStart, loc;
nameStart = skipStart >= limit ? -1 : content.indexOf(name, skipStart), -1 === nameStart || nameStart + name.length > limit ? (skipStart = limit, loc = this.source.spanFor(NON_EXISTENT_LOCATION)) : (skipStart = nameStart, loc = span.sliceStartChars({
skipStart: skipStart,
chars: name.length
}), skipStart += name.length), blockParams.push(b.var({
name: name,
loc: loc
}));
}
}
// These are bugs in Handlebars upstream
block.program.loc || (block.program.loc = NON_EXISTENT_LOCATION), block.inverse && !block.inverse.loc && (block.inverse.loc = NON_EXISTENT_LOCATION);
const program = this.Program(block.program, blockParams),
inverse = block.inverse ? this.Program(block.inverse, []) : null,
node = b.block({
path: path,
params: params,
hash: hash,
defaultBlock: program,
elseBlock: inverse,
loc: this.source.spanFor(block.loc),
openStrip: block.openStrip,
inverseStrip: block.inverseStrip,
closeStrip: block.closeStrip
});
appendChild(this.currentElement(), node);
}
MustacheStatement(rawMustache) {
this.pendingError?.mustache(this.source.spanFor(rawMustache.loc));
const {
tokenizer: tokenizer
} = this;
if ("comment" === tokenizer.state) return void this.appendToCommentData(this.sourceForNode(rawMustache));
let mustache;
const {
escaped: escaped,
loc: loc,
strip: strip
} = rawMustache;
if ("original" in rawMustache.path && "...attributes" === rawMustache.path.original) throw generateSyntaxError("Illegal use of ...attributes", this.source.spanFor(rawMustache.loc));
if (isHBSLiteral(rawMustache.path)) mustache = b.mustache({
path: this.acceptNode(rawMustache.path),
params: [],
hash: b.hash({
pairs: [],
loc: this.source.spanFor(rawMustache.path.loc).collapse("end")
}),
trusting: !escaped,
loc: this.source.spanFor(loc),
strip: strip
});else {
const {
path: path,
params: params,
hash: hash
} = acceptCallNodes(this, rawMustache);
mustache = b.mustache({
path: path,
params: params,
hash: hash,
trusting: !escaped,
loc: this.source.spanFor(loc),
strip: strip
});
}
switch (tokenizer.state) {
// Tag helpers
case "tagOpen":
case "tagName":
throw generateSyntaxError("Cannot use mustaches in an elements tagname", mustache.loc);
case "beforeAttributeName":
addElementModifier(this.currentStartTag, mustache);
break;
case "attributeName":
case "afterAttributeName":
this.beginAttributeValue(!1), this.finishAttributeValue(), addElementModifier(this.currentStartTag, mustache), tokenizer.transitionTo("beforeAttributeName");
break;
case "afterAttributeValueQuoted":
addElementModifier(this.currentStartTag, mustache), tokenizer.transitionTo("beforeAttributeName");
break;
// Attribute values
case "beforeAttributeValue":
this.beginAttributeValue(!1), this.appendDynamicAttributeValuePart(mustache), tokenizer.transitionTo("attributeValueUnquoted");
break;
case "attributeValueDoubleQuoted":
case "attributeValueSingleQuoted":
case "attributeValueUnquoted":
this.appendDynamicAttributeValuePart(mustache);
break;
// TODO: Only append child when the tokenizer state makes
// sense to do so, otherwise throw an error.
default:
appendChild(this.currentElement(), mustache);
}
return mustache;
}
appendDynamicAttributeValuePart(part) {
this.finalizeTextPart();
const attr = this.currentAttr;
attr.isDynamic = !0, attr.parts.push(part);
}
finalizeTextPart() {
const text = this.currentAttr.currentPart;
null !== text && (this.currentAttr.parts.push(text), this.startTextPart());
}
startTextPart() {
this.currentAttr.currentPart = null;
}
ContentStatement(content) {
!function (tokenizer, content) {
let line = content.loc.start.line,
column = content.loc.start.column;
const offsets = function (original, value) {
if ("" === value)
// if it is empty, just return the count of newlines
// in original
return {
lines: original.split("\n").length - 1,
columns: 0
};
// otherwise, return the number of newlines prior to
// `value`
const [difference] = original.split(value),
lines = difference.split(/\n/u),
lineCount = lines.length - 1;
return {
lines: lineCount,
columns: unwrap(lines[lineCount]).length
};
}(content.original, content.value);
line += offsets.lines, offsets.lines ? column = offsets.columns : column += offsets.columns, tokenizer.line = line, tokenizer.column = column;
}(this.tokenizer, content), this.tokenizer.tokenizePart(content.value), this.tokenizer.flushData();
}
CommentStatement(rawComment) {
const {
tokenizer: tokenizer
} = this;
if ("comment" === tokenizer.state) return this.appendToCommentData(this.sourceForNode(rawComment)), null;
const {
value: value,
loc: loc
} = rawComment,
comment = b.mustacheComment({
value: value,
loc: this.source.spanFor(loc)
});
switch (tokenizer.state) {
case "beforeAttributeName":
case "afterAttributeName":
this.currentStartTag.comments.push(comment);
break;
case "beforeData":
case "data":
appendChild(this.currentElement(), comment);
break;
default:
throw generateSyntaxError(`Using a Handlebars comment when in the \`${tokenizer.state}\` state is not supported`, this.source.spanFor(rawComment.loc));
}
return comment;
}
PartialStatement(partial) {
throw generateSyntaxError("Handlebars partials are not supported", this.source.spanFor(partial.loc));
}
PartialBlockStatement(partialBlock) {
throw generateSyntaxError("Handlebars partial blocks are not supported", this.source.spanFor(partialBlock.loc));
}
Decorator(decorator) {
throw generateSyntaxError("Handlebars decorators are not supported", this.source.spanFor(decorator.loc));
}
DecoratorBlock(decoratorBlock) {
throw generateSyntaxError("Handlebars decorator blocks are not supported", this.source.spanFor(decoratorBlock.loc));
}
SubExpression(sexpr) {
const {
path: path,
params: params,
hash: hash
} = acceptCallNodes(this, sexpr);
return b.sexpr({
path: path,
params: params,
hash: hash,
loc: this.source.spanFor(sexpr.loc)
});
}
PathExpression(path) {
const {
original: original
} = path;
let parts;
if (-1 !== original.indexOf("/")) {
if ("./" === original.slice(0, 2)) throw generateSyntaxError('Using "./" is not supported in Glimmer and unnecessary', this.source.spanFor(path.loc));
if ("../" === original.slice(0, 3)) throw generateSyntaxError('Changing context using "../" is not supported in Glimmer', this.source.spanFor(path.loc));
if (-1 !== original.indexOf(".")) throw generateSyntaxError("Mixing '.' and '/' in paths is not supported in Glimmer; use only '.' to separate property paths", this.source.spanFor(path.loc));
parts = [path.parts.join("/")];
} else {
if ("." === original) throw generateSyntaxError("'.' is not a supported path in Glimmer; check for a path with a trailing '.'", this.source.spanFor(path.loc));
parts = path.parts;
}
let pathHead,
thisHead = !1;
// This is to fix a bug in the Handlebars AST where the path expressions in
// `{{this.foo}}` (and similarly `{{foo-bar this.foo named=this.foo}}` etc)
// are simply turned into `{{foo}}`. The fix is to push it back onto the
// parts array and let the runtime see the difference. However, we cannot
// simply use the string `this` as it means literally the property called
// "this" in the current context (it can be expressed in the syntax as
// `{{[this]}}`, where the square bracket are generally for this kind of
// escaping – such as `{{foo.["bar.baz"]}}` would mean lookup a property
// named literally "bar.baz" on `this.foo`). By convention, we use `null`
// for this purpose.
if (/^this(?:\..+)?$/u.test(original) && (thisHead = !0), thisHead) pathHead = b.this({
loc: this.source.spanFor({
start: path.loc.start,
end: {
line: path.loc.start.line,
column: path.loc.start.column + 4
}
})
});else if (path.data) {
const head = parts.shift();
if (void 0 === head) throw generateSyntaxError("Attempted to parse a path expression, but it was not valid. Paths beginning with @ must start with a-z.", this.source.spanFor(path.loc));
pathHead = b.atName({
name: `@${head}`,
loc: this.source.spanFor({
start: path.loc.start,
end: {
line: path.loc.start.line,
column: path.loc.start.column + head.length + 1
}
})
});
} else {
const head = parts.shift();
if (void 0 === head) throw generateSyntaxError("Attempted to parse a path expression, but it was not valid. Paths must start with a-z or A-Z.", this.source.spanFor(path.loc));
pathHead = b.var({
name: head,
loc: this.source.spanFor({
start: path.loc.start,
end: {
line: path.loc.start.line,
column: path.loc.start.column + head.length
}
})
});
}
return b.path({
head: pathHead,
tail: parts,
loc: this.source.spanFor(path.loc)
});
}
Hash(hash) {
const pairs = hash.pairs.map(pair => b.pair({
key: pair.key,
value: this.acceptNode(pair.value),
loc: this.source.spanFor(pair.loc)
}));
return b.hash({
pairs: pairs,
loc: this.source.spanFor(hash.loc)
});
}
StringLiteral(string) {
return b.literal({
type: "StringLiteral",
value: string.value,
loc: this.source.spanFor(string.loc)
});
}
BooleanLiteral(boolean) {
return b.literal({
type: "BooleanLiteral",
value: boolean.value,
loc: this.source.spanFor(boolean.loc)
});
}
NumberLiteral(number) {
return b.literal({
type: "NumberLiteral",
value: number.value,
loc: this.source.spanFor(number.loc)
});
}
UndefinedLiteral(undef) {
return b.literal({
type: "UndefinedLiteral",
value: void 0,
loc: this.source.spanFor(undef.loc)
});
}
NullLiteral(nul) {
return b.literal({
type: "NullLiteral",
value: null,
loc: this.source.spanFor(nul.loc)
});
}
}
function acceptCallNodes(compiler, node) {
let path;
switch (node.path.type) {
case "PathExpression":
path = compiler.PathExpression(node.path);
break;
case "SubExpression":
path = compiler.SubExpression(node.path);
break;
case "StringLiteral":
case "UndefinedLiteral":
case "NullLiteral":
case "NumberLiteral":
case "BooleanLiteral":
{
let value;
throw value = "BooleanLiteral" === node.path.type ? node.path.original.toString() : "StringLiteral" === node.path.type ? `"${node.path.original}"` : "NullLiteral" === node.path.type ? "null" : "NumberLiteral" === node.path.type ? node.path.value.toString() : "undefined", generateSyntaxError(`${node.path.type} "${"StringLiteral" === node.path.type ? node.path.original : value}" cannot be called as a sub-expression, replace (${value}) with ${value}`, compiler.source.spanFor(node.path.loc));
}
}
const params = node.params ? node.params.map(e => compiler.acceptNode(e)) : [],
end = isPresentArray(params) ? getLast(params).loc : path.loc;
// if there is no hash, position it as a collapsed node immediately after the last param (or the
// path, if there are also no params)
return {
path: path,
params: params,
hash: node.hash ? compiler.Hash(node.hash) : b.hash({
pairs: [],
loc: compiler.source.spanFor(end).collapse("end")
})
};
}
function addElementModifier(element, mustache) {
const {
path: path,
params: params,
hash: hash,
loc: loc
} = mustache;
if (isHBSLiteral(path)) {
const modifier = `{{${function (literal) {
return "UndefinedLiteral" === literal.type ? "undefined" : JSON.stringify(literal.value);
}(path)}}}`;
throw generateSyntaxError(`In <${element.name} ... ${modifier} ..., ${modifier} is not a valid modifier`, mustache.loc);
}
const modifier = b.elementModifier({
path: path,
params: params,
hash: hash,
loc: loc
});
element.modifiers.push(modifier);
}
// vendored from simple-html-tokenizer because it's unexported
function isSpace(char) {
return /[\t\n\f ]/u.test(char);
}
class TokenizerEventHandlers extends HandlebarsNodeVisitors {
tagOpenLine = 0;
tagOpenColumn = 0;
reset() {
this.currentNode = null;
}
// Comment
beginComment() {
this.currentNode = {
type: "CommentStatement",
value: "",
start: this.source.offsetFor(this.tagOpenLine, this.tagOpenColumn)
};
}
appendToCommentData(char) {
this.currentComment.value += char;
}
finishComment() {
appendChild(this.currentElement(), b.comment(this.finish(this.currentComment)));
}
// Data
beginData() {
this.currentNode = {
type: "TextNode",
chars: "",
start: this.offset()
};
}
appendToData(char) {
this.currentData.chars += char;
}
finishData() {
appendChild(this.currentElement(), b.text(this.finish(this.currentData)));
}
// Tags - basic
tagOpen() {
this.tagOpenLine = this.tokenizer.line, this.tagOpenColumn = this.tokenizer.column;
}
beginStartTag() {
this.currentNode = {
type: "StartTag",
name: "",
nameStart: null,
nameEnd: null,
attributes: [],
modifiers: [],
comments: [],
params: [],
selfClosing: !1,
start: this.source.offsetFor(this.tagOpenLine, this.tagOpenColumn)
};
}
beginEndTag() {
this.currentNode = {
type: "EndTag",
name: "",
start: this.source.offsetFor(this.tagOpenLine, this.tagOpenColumn)
};
}
finishTag() {
let tag = this.finish(this.currentTag);
if ("StartTag" === tag.type) {
if (this.finishStartTag(), ":" === tag.name) throw generateSyntaxError("Invalid named block named detected, you may have created a named block without a name, or you may have began your name with a number. Named blocks must have names that are at least one character long, and begin with a lower case letter", this.source.spanFor({
start: this.currentTag.start.toJSON(),
end: this.offset().toJSON()
}));
(voidMap.has(tag.name) || tag.selfClosing) && this.finishEndTag(!0);
} else "EndTag" === tag.type && this.finishEndTag(!1);
}
finishStartTag() {
let {
name: name,
nameStart: nameStart,
nameEnd: nameEnd
} = this.currentStartTag;
// <> should probably be a syntax error, but s-h-t is currently broken for that case
debugAssert("" !== name, "tag name cannot be empty"), debugAssert(null !== nameStart, "nameStart unexpectedly null"), debugAssert(null !== nameEnd, "nameEnd unexpectedly null");
let nameLoc = nameStart.until(nameEnd),
[head, ...tail] = asPresentArray(name.split(".")),
path = b.path({
head: b.head({
original: head,
loc: nameLoc.sliceStartChars({
chars: head.length
})
}),
tail: tail,
loc: nameLoc
}),
{
attributes: attributes,
modifiers: modifiers,
comments: comments,
params: params,
selfClosing: selfClosing,
loc: loc
} = this.finish(this.currentStartTag),
element = b.element({
path: path,
selfClosing: selfClosing,
attributes: attributes,
modifiers: modifiers,
comments: comments,
params: params,
children: [],
openTag: loc,
closeTag: selfClosing ? null : SourceSpan.broken(),
loc: loc
});
this.elementStack.push(element);
}
finishEndTag(isVoid) {
let {
start: closeTagStart
} = this.currentTag,
tag = this.finish(this.currentTag),
element = this.elementStack.pop();
this.validateEndTag(tag, element, isVoid);
let parent = this.currentElement();
isVoid ? element.closeTag = null : element.selfClosing ? debugAssert(null === element.closeTag, "element.closeTag unexpectedly present") : element.closeTag = closeTagStart.until(this.offset()), element.loc = element.loc.withEnd(this.offset()), appendChild(parent, b.element(element));
}
markTagAsSelfClosing() {
let tag = this.currentTag;
if ("StartTag" !== tag.type) throw generateSyntaxError("Invalid end tag: closing tag must not be self-closing", this.source.spanFor({
start: tag.start.toJSON(),
end: this.offset().toJSON()
}));
tag.selfClosing = !0;
}
// Tags - name
appendToTagName(char) {
let tag = this.currentTag;
if (tag.name += char, "StartTag" === tag.type) {
let offset = this.offset();
null === tag.nameStart && (debugAssert(null === tag.nameEnd, "nameStart and nameEnd must both be null"),
// Note that the tokenizer already consumed the token here
tag.nameStart = offset.move(-1)), tag.nameEnd = offset;
}
}
// Tags - attributes
beginAttribute() {
let offset = this.offset();
this.currentAttribute = {
name: "",
parts: [],
currentPart: null,
isQuoted: !1,
isDynamic: !1,
start: offset,
valueSpan: offset.collapsed()
};
}
appendToAttributeName(char) {
this.currentAttr.name += char,
// The block params parsing code can actually handle peek=non-space just
// fine, but this check was added as an optimization, as there is a little
// bit of setup overhead for the parsing logic just to immediately bail
"as" === this.currentAttr.name && this.parsePossibleBlockParams();
}
beginAttributeValue(isQuoted) {
this.currentAttr.isQuoted = isQuoted, this.startTextPart(), this.currentAttr.valueSpan = this.offset().collapsed();
}
appendToAttributeValue(char) {
let parts = this.currentAttr.parts,
lastPart = parts[parts.length - 1],
current = this.currentAttr.currentPart;
if (current) current.chars += char,
// update end location for each added char
current.loc = current.loc.withEnd(this.offset());else {
// initially assume the text node is a single char
let loc = this.offset();
// the tokenizer line/column have already been advanced, correct location info
loc = "\n" === char ? lastPart ? lastPart.loc.getEnd() : this.currentAttr.valueSpan.getStart() : loc.move(-1), this.currentAttr.currentPart = b.text({
chars: char,
loc: loc.collapsed()
});
}
}
finishAttributeValue() {
this.finalizeTextPart();
let tag = this.currentTag,
tokenizerPos = this.offset();
if ("EndTag" === tag.type) throw generateSyntaxError("Invalid end tag: closing tag must not have attributes", this.source.spanFor({
start: tag.start.toJSON(),
end: tokenizerPos.toJSON()
}));
let {
name: name,
parts: parts,
start: start,
isQuoted: isQuoted,
isDynamic: isDynamic,
valueSpan: valueSpan
} = this.currentAttr;
// Just trying to be helpful with `<Hello |foo|>` rather than letting it through as an attribute
if (name.startsWith("|") && 0 === parts.length && !isQuoted && !isDynamic) throw generateSyntaxError("Invalid block parameters syntax: block parameters must be preceded by the `as` keyword", start.until(start.move(name.length)));
let value = this.assembleAttributeValue(parts, isQuoted, isDynamic, start.until(tokenizerPos));
value.loc = valueSpan.withEnd(tokenizerPos);
let attribute = b.attr({
name: name,
value: value,
loc: start.until(tokenizerPos)
});
this.currentStartTag.attributes.push(attribute);
}
parsePossibleBlockParams() {
// const enums that we can't use directly
const ID_INVERSE_PATTERN = /[!"#%&'()*+./;<=>@[\\\]^`{|}~]/u;
debugAssert("attributeName" === this.tokenizer.state, "must be in TokenizerState.attributeName");
const element = this.currentStartTag,
as = this.currentAttr;
let state = {
state: "PossibleAs"
};
const handlers = {
PossibleAs: next => {
if (debugAssert("PossibleAs" === state.state, "bug in block params parser"), isSpace(next))
// " as ..."
state = {
state: "BeforeStartPipe"
}, this.tokenizer.transitionTo("afterAttributeName"), this.tokenizer.consume();else {
if ("|" === next)
// " as|..."
// Following Handlebars and require a space between "as" and the pipe
throw generateSyntaxError('Invalid block parameters syntax: expecting at least one space character between "as" and "|"', as.start.until(this.offset().move(1)));
// " as{{...", " async...", " as=...", " as>...", " as/>..."
// Don't consume, let the normal tokenizer code handle the next steps
state = {
state: "Done"
};
}
},
BeforeStartPipe: next => {
debugAssert("BeforeStartPipe" === state.state, "bug in block params parser"), isSpace(next) ? this.tokenizer.consume() : "|" === next ? (state = {
state: "BeforeBlockParamName"
}, this.tokenizer.transitionTo("beforeAttributeName"), this.tokenizer.consume()) :
// " as {{...", " as bs...", " as =...", " as ...", " as/>..."
// Don't consume, let the normal tokenizer code handle the next steps
state = {
state: "Done"
};
},
BeforeBlockParamName: next => {
if (debugAssert("BeforeBlockParamName" === state.state, "bug in block params parser"), isSpace(next)) this.tokenizer.consume();else if ("" === next)
// The HTML tokenizer ran out of characters, so we are either
// encountering mustache or <EOF>
state = {
state: "Done"
}, this.pendingError = {
mustache(loc) {
throw generateSyntaxError("Invalid block parameters syntax: mustaches cannot be used inside parameters list", loc);
},
eof(loc) {
throw generateSyntaxError('Invalid block parameters syntax: expecting the tag to be closed with ">" or "/>" after parameters list', as.start.until(loc));
}
};else if ("|" === next) {
if (0 === element.params.length)
// Following Handlebars and treat empty block params a syntax error
throw generateSyntaxError("Invalid block parameters syntax: empty parameters list, expecting at least one identifier", as.start.until(this.offset().move(1)));
state = {
state: "AfterEndPipe"
}, this.tokenizer.consume();
} else {
if (">" === next || "/" === next) throw generateSyntaxError('Invalid block parameters syntax: incomplete parameters list, expecting "|" but the tag was closed prematurely', as.start.until(this.offset().move(1)));
// slurp up anything else into the name, validate later
state = {
state: "BlockParamName",
name: next,
start: this.offset()
}, this.tokenizer.consume();
}
},
BlockParamName: next => {
if (debugAssert("BlockParamName" === state.state, "bug in block params parser"), "" === next)
// The HTML tokenizer ran out of characters, so we are either
// encountering mustache or <EOF>, HBS side will attach the error
// to the next span
state = {
state: "Done"
}, this.pendingError = {
mustache(loc) {
throw generateSyntaxError("Invalid block parameters syntax: mustaches cannot be used inside parameters list", loc);
},
eof(loc) {
throw generateSyntaxError('Invalid block parameters syntax: expecting the tag to be closed with ">" or "/>" after parameters list', as.start.until(loc));
}
};else if ("|" === next || isSpace(next)) {
let loc = state.start.until(this.offset());
if ("this" === state.name || ID_INVERSE_PATTERN.test(state.name)) throw generateSyntaxError(`Invalid block parameters syntax: invalid identifier name \`${state.name}\``, loc);
element.params.push(b.var({
name: state.name,
loc: loc
})), state = "|" === next ? {
state: "AfterEndPipe"
} : {
state: "BeforeBlockParamName"
}, this.tokenizer.consume();
} else {
if (">" === next || "/" === next) throw generateSyntaxError('Invalid block parameters syntax: expecting "|" but the tag was closed prematurely', as.start.until(this.offset().move(1)));
// slurp up anything else into the name, validate later
state.name += next, this.tokenizer.consume();
}
},
AfterEndPipe: next => {
debugAssert("AfterEndPipe" === state.state, "bug in block params parser"), isSpace(next) ? this.tokenizer.consume() : "" === next ? (
// The HTML tokenizer ran out of characters, so we are either
// encountering mustache or <EOF>, HBS side will attach the error
// to the next span
state = {
state: "Done"
}, this.pendingError = {
mustache(loc) {
throw generateSyntaxError("Invalid block parameters syntax: modifiers cannot follow parameters list", loc);
},
eof(loc) {
throw generateSyntaxError('Invalid block parameters syntax: expecting the tag to be closed with ">" or "/>" after parameters list', as.start.until(loc));
}
}) : ">" === next || "/" === next ?
// Don't consume, let the normal tokenizer code handle the next steps
state = {
state: "Done"
} : (
// Slurp up the next "token" for the error span
state = {
state: "Error",
message: 'Invalid block parameters syntax: expecting the tag to be closed with ">" or "/>" after parameters list',
start: this.offset()
}, this.tokenizer.consume());
},
Error: next => {
if (debugAssert("Error" === state.state, "bug in block params parser"), "" === next || "/" === next || ">" === next || isSpace(next)) throw generateSyntaxError(state.message, state.start.until(this.offset()));
// Slurp up the next "token" for the error span
this.tokenizer.consume();
},
Done: () => {
debugAssert(!1, "This should never be called");
}
};
let next;
do {
next = this.tokenizer.peek(), handlers[state.state](next);
} while ("Done" !== state.state && "" !== next);
debugAssert("Done" === state.state, "bug in block params parser");
}
reportSyntaxError(message) {
throw generateSyntaxError(message, this.offset().collapsed());
}
assembleConcatenatedValue(parts) {
for (const part of parts) if ("MustacheStatement" !== part.type && "TextNode" !== part.type) throw generateSyntaxError(`Unsupported node in quoted attribute value: ${part.type}`, part.loc);
assertPresentArray(parts, "the concatenation parts of an element should not be empty");
let first = getFirst(parts),
last = getLast(parts);
return b.concat({
parts: parts,
loc: this.source.spanFor(first.loc).extend(this.source.spanFor(last.loc))
});
}
validateEndTag(tag, element, selfClosing) {
if (voidMap.has(tag.name) && !selfClosing)
// EngTag is also called by StartTag for void and self-closing tags (i.e.
// <input> or <br />, so we need to check for that here. Otherwise, we would
// throw an error for those cases.
throw generateSyntaxError(`<${tag.name}> elements do not need end tags. You should remove it`, tag.loc);
if (void 0 === element.tag) throw generateSyntaxError(`Closing tag </${tag.name}> without an open tag`, tag.loc);
if (element.tag !== tag.name) throw generateSyntaxError(`Closing tag </${tag.name}> did not match last open tag <${element.tag}> (on line ${element.loc.startPosition.line})`, tag.loc);
}
assembleAttributeValue(parts, isQuoted, isDynamic, span) {
if (isDynamic) {
if (isQuoted) return this.assembleConcatenatedValue(parts);
{
assertPresentArray(parts);
const [head, a] = parts;
if (void 0 === a || "TextNode" === a.type && "/" === a.chars) return head;
throw generateSyntaxError("An unquoted attribute value must be a string or a mustache, preceded by whitespace or a '=' character, and followed by whitespace, a '>' character, or '/>'", span);
}
}
return isPresentArray(parts) ? parts[0] : b.text({
chars: "",
loc: span
});
}
}
/**
ASTPlugins can make changes to the Glimmer template AST before
compilation begins.
*/
const syntax = {
parse: preprocess,
builders: publicBuilder,
print: build,
traverse: traverse,
Walker: Walker
};
class CodemodEntityParser extends EntityParser {
// match upstream types, but never match an entity
constructor() {
super({});
}
parse() {}
}
function preprocess(input, options = {}) {
let source,
ast,
entityParser,
mode = options.mode || "precompile";
"string" == typeof input ? (source = new Source(input, options.meta?.moduleName), ast = "codemod" === mode ? parseWithoutProcessing(input, options.parseOptions) : parse(input, options.parseOptions)) : input instanceof Source ? (source = input, ast = "codemod" === mode ? parseWithoutProcessing(input.source, options.parseOptions) : parse(input.source, options.parseOptions)) : (source = new Source("", options.meta?.moduleName), ast = input), "codemod" === mode && (entityParser = new CodemodEntityParser());
let offsets = SourceSpan.forCharPositions(source, 0, source.source.length);
ast.loc = {
source: "(program)",
start: offsets.startPosition,
end: offsets.endPosition
};
let template = new TokenizerEventHandlers(source, entityParser, mode).parse(ast, options.locals ?? []);
if (options?.plugins?.ast) for (const transform of options.plugins.ast) traverse(template, transform(assign({}, options, {
syntax: syntax
}, {
plugins: void 0
})).visitor);
return template;
}
/**
* This is a convenience function for creating ASTv2 nodes, with an optional name and the node's
* options.
*
* ```ts
* export class HtmlText extends node('HtmlText').fields<{ chars: string }>() {}
* ```
*
* This creates a new ASTv2 node with the name `'HtmlText'` and one field `chars: string` (in
* addition to a `loc: SourceOffsets` field, which all nodes have).
*
* ```ts
* export class Args extends node().fields<{
* positional: PositionalArguments;
* named: NamedArguments
* }>() {}
* ```
*
* This creates a new un-named ASTv2 node with two fields (`positional: Positional` and `named:
* Named`, in addition to the generic `loc: SourceOffsets` field).
*
* Once you create a node using `node`, it is instantiated with all of its fields (including `loc`):
*
* ```ts
* new HtmlText({ loc: offsets, chars: someString });
* ```
*/
function node(name) {
if (void 0 !== name) {
const type = name;
return {
fields: () => class {
// SAFETY: initialized via `assign` in the constructor.
type;
constructor(fields) {
this.type = type, assign(this, fields);
}
}
};
}
return {
fields: () => class {
// SAFETY: initialized via `assign` in the constructor.
constructor(fields) {
assign(this, fields);
}
}
};
}
/**
* Corresponds to syntaxes with positional and named arguments:
*
* - SubExpression
* - Invoking Append
* - Invoking attributes
* - InvokeBlock
*
* If `Args` is empty, the `SourceOffsets` for this node should be the collapsed position
* immediately after the parent call node's `callee`.
*/
class Args extends node().fields() {
static empty(loc) {
return new Args({
loc: loc,
positional: PositionalArguments.empty(loc),
named: NamedArguments.empty(loc)
});
}
static named(named) {
return new Args({
loc: named.loc,
positional: PositionalArguments.empty(named.loc.collapse("end")),
named: named
});
}
nth(offset) {
return this.positional.nth(offset);
}
get(name) {
return this.named.get(name);
}
isEmpty() {
return this.positional.isEmpty() && this.named.isEmpty();
}
}
/**
* Corresponds to positional arguments.
*
* If `PositionalArguments` is empty, the `SourceOffsets` for this node should be the collapsed
* position immediately after the parent call node's `callee`.
*/
class PositionalArguments extends node().fields() {
static empty(loc) {
return new PositionalArguments({
loc: loc,
exprs: []
});
}
get size() {
return this.exprs.length;
}
nth(offset) {
return this.exprs[offset] || null;
}
isEmpty() {
return 0 === this.exprs.length;
}
}
/**
* Corresponds to named arguments.
*
* If `PositionalArguments` and `NamedArguments` are empty, the `SourceOffsets` for this node should
* be the same as the `Args` node that contains this node.
*
* If `PositionalArguments` is not empty but `NamedArguments` is empty, the `SourceOffsets` for this
* node should be the collapsed position immediately after the last positional argument.
*/
class NamedArguments extends node().fields() {
static empty(loc) {
return new NamedArguments({
loc: loc,
entries: []
});
}
get size() {
return this.entries.length;
}
get(name) {
let entry = this.entries.filter(e => e.name.chars === name)[0];
return entry ? entry.value : null;
}
isEmpty() {
return 0 === this.entries.length;
}
}
/**
* Corresponds to a single named argument.
*
* ```hbs
* x=<expr>
* ```
*/
class NamedArgument {
loc;
name;
value;
constructor(options) {
this.loc = options.name.loc.extend(options.value.loc), this.name = options.name, this.value = options.value;
}
}
/**
* Attr nodes look like HTML attributes, but are classified as:
*
* 1. `HtmlAttr`, which means a regular HTML attribute in Glimmer
* 2. `SplatAttr`, which means `...attributes`
* 3. `ComponentArg`, which means an attribute whose name begins with `@`, and it is therefore a
* component argument.
*/
/**
* `HtmlAttr` and `SplatAttr` are grouped together because the order of the `SplatAttr` node,
* relative to other attributes, matters.
*/
/**
* "Attr Block" nodes are allowed inside an open element tag in templates. They interact with the
* element (or component).
*/
/**
* `HtmlAttr` nodes are valid HTML attributes, with or without a value.
*
* Exceptions:
*
* - `...attributes` is `SplatAttr`
* - `@x=<value>` is `ComponentArg`
*/
class HtmlAttr extends node("HtmlAttr").fields() {}
class SplatAttr extends node("SplatAttr").fields() {}
/**
* Corresponds to an argument passed by a component (`@x=<value>`)
*/
class ComponentArg extends node().fields() {
/**
* Convert the component argument into a named argument node
*/
toNamedArgument() {
return new NamedArgument({
name: this.name,
value: this.value
});
}
}
/**
* An `ElementModifier` is just a normal call node in modifier position.
*/
class ElementModifier extends node("ElementModifier").fields() {}
/**
* Content Nodes are allowed in content positions in templates. They correspond to behavior in the
* [Data][data] tokenization state in HTML.
*
* [data]: https://html.spec.whatwg.org/multipage/parsing.html#data-state
*/
class GlimmerComment extends node("GlimmerComment").fields() {}
class HtmlText extends node("HtmlText").fields() {}
class HtmlComment extends node("HtmlComment").fields() {}
class AppendContent extends node("AppendContent").fields() {
get callee() {
return "Call" === this.value.type ? this.value.callee : this.value;
}
get args() {
return "Call" === this.value.type ? this.value.args : Args.empty(this.value.loc.collapse("end"));
}
}
class InvokeBlock extends node("InvokeBlock").fields() {}
/**
* Corresponds to a component invocation. When the content of a component invocation contains no
* named blocks, `blocks` contains a single named block named `"default"`. When a component
* invocation is self-closing, `blocks` is empty.
*/
class InvokeComponent extends node("InvokeComponent").fields() {
get args() {
let entries = this.componentArgs.map(a => a.toNamedArgument());
return Args.named(new NamedArguments({
loc: SpanList.range(entries, this.callee.loc.collapse("end")),
entries: entries
}));
}
}
/**
* Corresponds to a simple HTML element. The AST allows component arguments and modifiers to support
* future extensions.
*/
class SimpleElement extends node("SimpleElement").fields() {
get args() {
let entries = this.componentArgs.map(a => a.toNamedArgument());
return Args.named(new NamedArguments({
loc: SpanList.range(entries, this.tag.loc.collapse("end")),
entries: entries
}));
}
}
/**
* A Handlebars literal.
*
* {@link https://handlebarsjs.com/guide/expressions.html#literal-segments}
*/
/**
* Corresponds to a Handlebars literal.
*
* @see {LiteralValue}
*/
class LiteralExpression extends node("Literal").fields() {
toSlice() {
return new SourceSlice({
loc: this.loc,
chars: this.value
});
}
}
/**
* Returns true if an input {@see ExpressionNode} is a literal.
*/
/**
* Corresponds to a path in expression position.
*
* ```hbs
* this
* this.x
* @x
* @x.y
* x
* x.y
* ```
*/
class PathExpression extends node("Path").fields() {}
/**
* Corresponds to a known strict-mode keyword. It behaves similarly to a
* PathExpression with a FreeVarReference, but implies StrictResolution and
* is guaranteed to not have a tail, since `{{outlet.foo}}` would have been
* illegal.
*/
class KeywordExpression extends node("Keyword").fields() {}
/**
* Corresponds to a parenthesized call expression.
*
* ```hbs
* (x)
* (x.y)
* (x y)
* (x.y z)
* ```
*/
class CallExpression extends node("Call").fields() {}
/**
* Corresponds to an interpolation in attribute value position.
*
* ```hbs
* <a href="{{url}}.html"
* ```
*/
class InterpolateExpression extends node("Interpolate").fields() {}
/**
* Corresponds to an entire template.
*/
class Template extends node().fields() {}
/**
* Represents a block. In principle this could be merged with `NamedBlock`, because all cases
* involving blocks have at least a notional name.
*/
class Block extends node().fields() {}
/**
* Corresponds to a collection of named blocks.
*/
class NamedBlocks extends node().fields() {
/**
* Get the `NamedBlock` for a given name.
*/
get(name) {
return this.blocks.filter(block => block.name.chars === name)[0] || null;
}
}
/**
* Corresponds to a single named block. This is used for anonymous named blocks (`default` and
* `else`).
*/
class NamedBlock extends node().fields() {
get args() {
let entries = this.componentArgs.map(a => a.toNamedArgument());
return Args.named(new NamedArguments({
loc: SpanList.range(entries, this.name.loc.collapse("end")),
entries: entries
}));
}
}
/**
* Corresponds to `this` at the head of an expression.
*/
class ThisReference extends node("This").fields() {}
/**
* Corresponds to `@<ident>` at the beginning of an expression.
*/
class ArgReference extends node("Arg").fields() {}
/**
* Corresponds to `<ident>` at the beginning of an expression, when `<ident>` is in the current
* block's scope.
*/
class LocalVarReference extends node("Local").fields() {}
/**
* Corresponds to `<ident>` at the beginning of an expression, when `<ident>` is *not* in the
* current block's scope.
*
* The `resolution: FreeVarResolution` field describes how to resolve the free variable.
*
* Note: In strict mode, it must always be a variable that is in a concrete JavaScript scope that
* the template will be installed into.
*/
class FreeVarReference extends node("Free").fields() {}
/**
* A free variable is resolved according to a resolution rule:
*
* 1. Strict resolution
* 2. Namespaced resolution
*/
/**
* Strict resolution is used:
*
* 1. in a strict mode template
* 2. in an local variable invocation with dot paths
*/
const STRICT_RESOLUTION = {
resolution: () => opcodes.GetStrictKeyword,
serialize: () => "Strict",
isAngleBracket: !1
},
HTML_RESOLUTION = {
...STRICT_RESOLUTION,
isAngleBracket: !0
};
/**
* A `LooseModeResolution` includes one or more namespaces to resolve the variable in
*
* In practice, there are a limited number of possible combinations of these degrees of freedom,
* and they are captured by the `Namespaces` union below.
*/
class LooseModeResolution {
/**
* Namespaced resolution is used in an unambiguous syntax position:
*
* 1. `(sexp)` (namespace: `Helper`)
* 2. `{{#block}}` (namespace: `Component`)
* 3. `<a {{modifier}}>` (namespace: `Modifier`)
* 4. `<Component />` (namespace: `Component`)
*/
static namespaced(namespace, isAngleBracket = !1) {
return new LooseModeResolution([namespace], isAngleBracket);
}
/**
* Append resolution is used when the variable should be resolved in both the `component` and
* `helper` namespaces.
*
* ```hbs
* {{x}}
* ```
*
* ```hbs
* {{x y}}
* ```
*
* ^ In either case, `x` should be resolved in the `component` and `helper` namespaces.
*/
static append() {
return new LooseModeResolution([FreeVarNamespace.Component, FreeVarNamespace.Helper]);
}
/**
* Trusting append resolution is used when the variable should be resolved only in the
* `helper` namespaces.
*
* ```hbs
* {{{x}}}
* ```
*
* ```hbs
* {{{x y}}}
* ```
*
* ^ In either case, `x` should be resolved in the `helper` namespace.
*/
static trustingAppend() {
return this.namespaced(FreeVarNamespace.Helper);
}
constructor(namespaces, isAngleBracket = !1) {
this.namespaces = namespaces, this.isAngleBracket = isAngleBracket;
}
resolution() {
if (1 !== this.namespaces.length) return opcodes.GetFreeAsComponentOrHelperHead;
switch (this.namespaces[0]) {
case FreeVarNamespace.Helper:
return opcodes.GetFreeAsHelperHead;
case FreeVarNamespace.Modifier:
return opcodes.GetFreeAsModifierHead;
case FreeVarNamespace.Component:
return opcodes.GetFreeAsComponentHead;
}
}
serialize() {
return 1 === this.namespaces.length ? this.namespaces[0] : "ComponentOrHelper";
}
}
let FreeVarNamespace = function (FreeVarNamespace) {
return FreeVarNamespace.Helper = "Helper", FreeVarNamespace.Modifier = "Modifier", FreeVarNamespace.Component = "Component", FreeVarNamespace;
}({});
const HELPER_NAMESPACE = FreeVarNamespace.Helper,
MODIFIER_NAMESPACE = FreeVarNamespace.Modifier,
COMPONENT_NAMESPACE = FreeVarNamespace.Component;
var api$1 = Object.freeze({
__proto__: null,
AppendContent: AppendContent,
ArgReference: ArgReference,
Args: Args,
Block: Block,
COMPONENT_NAMESPACE: COMPONENT_NAMESPACE,
CallExpression: CallExpression,
ComponentArg: ComponentArg,
ElementModifier: ElementModifier,
FreeVarNamespace: FreeVarNamespace,
FreeVarReference: FreeVarReference,
GlimmerComment: GlimmerComment,
HELPER_NAMESPACE: HELPER_NAMESPACE,
HTML_RESOLUTION: HTML_RESOLUTION,
HtmlAttr: HtmlAttr,
HtmlComment: HtmlComment,
HtmlText: HtmlText,
InterpolateExpression: InterpolateExpression,
InvokeBlock: InvokeBlock,
InvokeComponent: InvokeComponent,
KeywordExpression: KeywordExpression,
LiteralExpression: LiteralExpression,
LocalVarReference: LocalVarReference,
LooseModeResolution: LooseModeResolution,
MODIFIER_NAMESPACE: MODIFIER_NAMESPACE,
NamedArgument: NamedArgument,
NamedArguments: NamedArguments,
NamedBlock: NamedBlock,
NamedBlocks: NamedBlocks,
PathExpression: PathExpression,
PositionalArguments: PositionalArguments,
STRICT_RESOLUTION: STRICT_RESOLUTION,
SimpleElement: SimpleElement,
SplatAttr: SplatAttr,
Template: Template,
ThisReference: ThisReference,
isLiteral: function (node, kind) {
return "Literal" === node.type && (void 0 === kind || ("null" === kind ? null === node.value : typeof node.value === kind));
},
isStrictResolution: function (value) {
return value === STRICT_RESOLUTION;
},
loadResolution:
/**
* A `Namespaced` must be resolved in one or more namespaces.
*
* ```hbs
* <X />
* ```
*
* ^ `X` is resolved in the `component` namespace
*
* ```hbs
* (x)
* ```
*
* ^ `x` is resolved in the `helper` namespace
*
* ```hbs
* <a {{x}} />
* ```
*
* ^ `x` is resolved in the `modifier` namespace
*/
// Serialization
function (resolution) {
return "Strict" === resolution ? STRICT_RESOLUTION : "ComponentOrHelper" === resolution ? LooseModeResolution.append() : LooseModeResolution.namespaced(resolution);
},
node: node
});
class SymbolTable {
static top(locals, keywords, options) {
return new ProgramSymbolTable(locals, keywords, options);
}
child(locals) {
let symbols = locals.map(name => this.allocate(name));
return new BlockSymbolTable(this, locals, symbols);
}
}
class ProgramSymbolTable extends SymbolTable {
constructor(templateLocals, keywords, options) {
super(), this.templateLocals = templateLocals, this.keywords = keywords, this.options = options;
}
symbols = [];
upvars = [];
size = 1;
named = dict();
blocks = dict();
usedTemplateLocals = [];
#hasDebugger = !1;
hasLexical(name) {
return this.options.lexicalScope(name);
}
hasKeyword(name) {
return this.keywords.includes(name);
}
getKeyword(name) {
return this.allocateFree(name, STRICT_RESOLUTION);
}
getUsedTemplateLocals() {
return this.usedTemplateLocals;
}
setHasDebugger() {
this.#hasDebugger = !0;
}
get hasEval() {
return this.#hasDebugger;
}
has(name) {
return this.templateLocals.includes(name);
}
get(name) {
let index = this.usedTemplateLocals.indexOf(name);
return -1 !== index || (index = this.usedTemplateLocals.length, this.usedTemplateLocals.push(name)), [index, !0];
}
getLocalsMap() {
return dict();
}
getDebugInfo() {
return Object.values(this.getLocalsMap());
}
allocateFree(name, resolution) {
// If the name in question is an uppercase (i.e. angle-bracket) component invocation, run
// the optional `customizeComponentName` function provided to the precompiler.
resolution.resolution() === opcodes.GetFreeAsComponentHead && resolution.isAngleBracket && (name = this.options.customizeComponentName(name));
let index = this.upvars.indexOf(name);
return -1 !== index || (index = this.upvars.length, this.upvars.push(name)), index;
}
allocateNamed(name) {
let named = this.named[name];
return named || (named = this.named[name] = this.allocate(name)), named;
}
allocateBlock(name) {
"inverse" === name && (name = "else");
let block = this.blocks[name];
return block || (block = this.blocks[name] = this.allocate(`&${name}`)), block;
}
allocate(identifier) {
return this.symbols.push(identifier), this.size++;
}
}
class BlockSymbolTable extends SymbolTable {
constructor(parent, symbols, slots) {
super(), this.parent = parent, this.symbols = symbols, this.slots = slots;
}
get locals() {
return this.symbols;
}
hasLexical(name) {
return this.parent.hasLexical(name);
}
getKeyword(name) {
return this.parent.getKeyword(name);
}
hasKeyword(name) {
return this.parent.hasKeyword(name);
}
has(name) {
return -1 !== this.symbols.indexOf(name) || this.parent.has(name);
}
get(name) {
let local = this.#get(name);
return local ? [local, !1] : this.parent.get(name);
}
#get(name) {
let slot = this.symbols.indexOf(name);
return -1 === slot ? null : unwrap(this.slots[slot]);
}
getLocalsMap() {
let dict = this.parent.getLocalsMap();
return this.symbols.forEach(symbol => dict[symbol] = this.get(symbol)[0]), dict;
}
getDebugInfo() {
return Object.values(this.getLocalsMap());
}
setHasDebugger() {
this.parent.setHasDebugger();
}
allocateFree(name, resolution) {
return this.parent.allocateFree(name, resolution);
}
allocateNamed(name) {
return this.parent.allocateNamed(name);
}
allocateBlock(name) {
return this.parent.allocateBlock(name);
}
allocate(identifier) {
return this.parent.allocate(identifier);
}
}
class Builder {
// TEMPLATE //
template(symbols, body, loc) {
return new Template({
table: symbols,
body: body,
loc: loc
});
}
// INTERNAL (these nodes cannot be reached when doing general-purpose visiting) //
block(symbols, body, loc) {
return new Block({
scope: symbols,
body: body,
loc: loc
});
}
namedBlock(name, block, loc) {
return new NamedBlock({
name: name,
block: block,
attrs: [],
componentArgs: [],
modifiers: [],
loc: loc
});
}
simpleNamedBlock(name, block, loc) {
return new BuildElement({
selfClosing: !1,
attrs: [],
componentArgs: [],
modifiers: [],
comments: []
}).named(name, block, loc);
}
slice(chars, loc) {
return new SourceSlice({
loc: loc,
chars: chars
});
}
args(positional, named, loc) {
return new Args({
loc: loc,
positional: positional,
named: named
});
}
positional(exprs, loc) {
return new PositionalArguments({
loc: loc,
exprs: exprs
});
}
namedArgument(key, value) {
return new NamedArgument({
name: key,
value: value
});
}
named(entries, loc) {
return new NamedArguments({
loc: loc,
entries: entries
});
}
attr({
name: name,
value: value,
trusting: trusting
}, loc) {
return new HtmlAttr({
loc: loc,
name: name,
value: value,
trusting: trusting
});
}
splatAttr(symbol, loc) {
return new SplatAttr({
symbol: symbol,
loc: loc
});
}
arg({
name: name,
value: value,
trusting: trusting
}, loc) {
return new ComponentArg({
name: name,
value: value,
trusting: trusting,
loc: loc
});
}
// EXPRESSIONS //
path(head, tail, loc) {
return new PathExpression({
loc: loc,
ref: head,
tail: tail
});
}
keyword(name, symbol, loc) {
return new KeywordExpression({
loc: loc,
name: name,
symbol: symbol
});
}
self(loc) {
return new ThisReference({
loc: loc
});
}
at(name, symbol, loc) {
// the `@` should be included so we have a complete source range
return debugAssert("@" === name[0], "call builders.at() with a string that starts with '@'"), new ArgReference({
loc: loc,
name: new SourceSlice({
loc: loc,
chars: name
}),
symbol: symbol
});
}
freeVar({
name: name,
context: context,
symbol: symbol,
loc: loc
}) {
return debugAssert("this" !== name, "You called builders.freeVar() with 'this'. Call builders.this instead"), debugAssert("@" !== name[0], `You called builders.freeVar() with '${name}'. Call builders.at('${name}') instead`), new FreeVarReference({
name: name,
resolution: context,
symbol: symbol,
loc: loc
});
}
localVar(name, symbol, isTemplateLocal, loc) {
return debugAssert("this" !== name, "You called builders.var() with 'this'. Call builders.this instead"), debugAssert("@" !== name[0], `You called builders.var() with '${name}'. Call builders.at('${name}') instead`), new LocalVarReference({
loc: loc,
name: name,
isTemplateLocal: isTemplateLocal,
symbol: symbol
});
}
sexp(parts, loc) {
return new CallExpression({
loc: loc,
callee: parts.callee,
args: parts.args
});
}
interpolate(parts, loc) {
return assertPresentArray(parts), new InterpolateExpression({
loc: loc,
parts: parts
});
}
literal(value, loc) {
return new LiteralExpression({
loc: loc,
value: value
});
}
// STATEMENTS //
append({
table: table,
trusting: trusting,
value: value
}, loc) {
return new AppendContent({
table: table,
trusting: trusting,
value: value,
loc: loc
});
}
modifier({
callee: callee,
args: args
}, loc) {
return new ElementModifier({
loc: loc,
callee: callee,
args: args
});
}
namedBlocks(blocks, loc) {
return new NamedBlocks({
loc: loc,
blocks: blocks
});
}
blockStatement({
program: program,
inverse = null,
...call
}, loc) {
let blocksLoc = program.loc,
blocks = [this.namedBlock(SourceSlice.synthetic("default"), program, program.loc)];
return inverse && (blocksLoc = blocksLoc.extend(inverse.loc), blocks.push(this.namedBlock(SourceSlice.synthetic("else"), inverse, inverse.loc))), new InvokeBlock({
loc: loc,
blocks: this.namedBlocks(blocks, blocksLoc),
callee: call.callee,
args: call.args
});
}
element(options) {
return new BuildElement(options);
}
}
class BuildElement {
builder;
constructor(base) {
this.base = base, this.builder = new Builder();
}
simple(tag, body, loc) {
return new SimpleElement(assign({
tag: tag,
body: body,
componentArgs: [],
loc: loc
}, this.base));
}
named(name, block, loc) {
return new NamedBlock(assign({
name: name,
block: block,
componentArgs: [],
loc: loc
}, this.base));
}
selfClosingComponent(callee, loc) {
return new InvokeComponent(assign({
loc: loc,
callee: callee,
// point the empty named blocks at the `/` self-closing tag
blocks: new NamedBlocks({
blocks: [],
loc: loc.sliceEndChars({
skipEnd: 1,
chars: 1
})
})
}, this.base));
}
componentWithDefaultBlock(callee, children, symbols, loc) {
let block = this.builder.block(symbols, children, loc),
namedBlock = this.builder.namedBlock(SourceSlice.synthetic("default"), block, loc);
// BUILDER.simpleNamedBlock('default', children, symbols, loc);
return new InvokeComponent(assign({
loc: loc,
callee: callee,
blocks: this.builder.namedBlocks([namedBlock], namedBlock.loc)
}, this.base));
}
componentWithNamedBlocks(callee, blocks, loc) {
return new InvokeComponent(assign({
loc: loc,
callee: callee,
blocks: this.builder.namedBlocks(blocks, SpanList.range(blocks))
}, this.base));
}
}
function SexpSyntaxContext(node) {
return isSimpleCallee(node) ? LooseModeResolution.namespaced(HELPER_NAMESPACE) : null;
}
function ModifierSyntaxContext(node) {
return isSimpleCallee(node) ? LooseModeResolution.namespaced(MODIFIER_NAMESPACE) : null;
}
function BlockSyntaxContext(node) {
return isSimpleCallee(node) ? LooseModeResolution.namespaced(COMPONENT_NAMESPACE) : null;
}
function ComponentSyntaxContext(node) {
return isSimplePath(node) ? LooseModeResolution.namespaced(COMPONENT_NAMESPACE, !0) : null;
}
/**
* This corresponds to attribute curlies (<Foo bar={{...}}>).
* In strict mode, this also corresponds to arg curlies.
*/
function AttrValueSyntaxContext(node) {
return isSimpleCallee(node) ? LooseModeResolution.namespaced(HELPER_NAMESPACE) : null;
}
/**
* This corresponds to append positions text curlies.
*/
function AppendSyntaxContext(node) {
let isSimple = isSimpleCallee(node),
trusting = node.trusting;
return isSimple ? trusting ? LooseModeResolution.trustingAppend() : LooseModeResolution.append() : null;
}
// UTILITIES
/**
* A call node has a simple callee if its head is:
*
* - a `PathExpression`
* - the `PathExpression`'s head is a `VarHead`
* - it has no tail
*
* Simple heads:
*
* ```
* {{x}}
* {{x y}}
* ```
*
* Not simple heads:
*
* ```
* {{x.y}}
* {{x.y z}}
* {{@x}}
* {{@x a}}
* {{this}}
* {{this a}}
* ```
*/
function isSimpleCallee(node) {
return isSimplePath(node.path);
}
function isSimplePath(node) {
return "PathExpression" === node.type && "VarHead" === node.head.type && 0 === node.tail.length;
}
function normalize(source, options = {
lexicalScope: () => !1
}) {
let ast = preprocess(source, options),
normalizeOptions = {
strictMode: !1,
...options,
locals: ast.blockParams,
keywords: options.keywords ?? []
},
top = SymbolTable.top(normalizeOptions.locals, normalizeOptions.keywords, {
customizeComponentName: options.customizeComponentName ?? (name => name),
lexicalScope: options.lexicalScope
}),
block = new BlockContext(source, normalizeOptions, top),
normalizer = new StatementNormalizer(block),
astV2 = new TemplateChildren(block.loc(ast.loc), ast.body.map(b => normalizer.normalize(b)), block).assertTemplate(top);
return [astV2, top.getUsedTemplateLocals()];
}
/**
* A `BlockContext` represents the block that a particular AST node is contained inside of.
*
* `BlockContext` is aware of template-wide options (such as strict mode), as well as the bindings
* that are in-scope within that block.
*
* Concretely, it has the `PrecompileOptions` and current `SymbolTable`, and provides
* facilities for working with those options.
*
* `BlockContext` is stateless.
*/
class BlockContext {
builder;
constructor(source, options, table) {
this.source = source, this.options = options, this.table = table, this.builder = new Builder();
}
get strict() {
return this.options.strictMode || !1;
}
loc(loc) {
return this.source.spanFor(loc);
}
resolutionFor(node, resolution) {
if (this.strict) return {
result: STRICT_RESOLUTION
};
if (this.isFreeVar(node)) {
let r = resolution(node);
return null === r ? {
result: "error",
path: printPath(node),
head: printHead(node)
} : {
result: r
};
}
return {
result: STRICT_RESOLUTION
};
}
isLexicalVar(variable) {
return this.table.hasLexical(variable);
}
isKeyword(name) {
return this.strict && !this.table.hasLexical(name) && this.table.hasKeyword(name);
}
isFreeVar(callee) {
return "PathExpression" === callee.type ? "VarHead" === callee.head.type && !this.table.has(callee.head.name) : "PathExpression" === callee.path.type && this.isFreeVar(callee.path);
}
hasBinding(name) {
return this.table.has(name) || this.table.hasLexical(name);
}
child(blockParams) {
return new BlockContext(this.source, this.options, this.table.child(blockParams));
}
customizeComponentName(input) {
return this.options.customizeComponentName ? this.options.customizeComponentName(input) : input;
}
}
/**
* An `ExpressionNormalizer` normalizes expressions within a block.
*
* `ExpressionNormalizer` is stateless.
*/
class ExpressionNormalizer {
constructor(block) {
this.block = block;
}
/**
* The `normalize` method takes an arbitrary expression and its original syntax context and
* normalizes it to an ASTv2 expression.
*
* @see {SyntaxContext}
*/
normalize(expr, resolution) {
switch (expr.type) {
case "NullLiteral":
case "BooleanLiteral":
case "NumberLiteral":
case "StringLiteral":
case "UndefinedLiteral":
return this.block.builder.literal(expr.value, this.block.loc(expr.loc));
case "PathExpression":
return debugAssert(resolution, "[BUG] resolution is required"), this.path(expr, resolution);
case "SubExpression":
{
// expr.path used to incorrectly have the type ASTv1.Expression
isLiteral(expr.path) && assertIllegalLiteral(expr.path, expr.loc);
let resolution = this.block.resolutionFor(expr, SexpSyntaxContext);
if ("error" === resolution.result) throw generateSyntaxError(`You attempted to invoke a path (\`${resolution.path}\`) but ${resolution.head} was not in scope`, expr.loc);
return this.block.builder.sexp(this.callParts(expr, resolution.result), this.block.loc(expr.loc));
}
}
}
path(expr, resolution) {
let loc = this.block.loc(expr.loc);
if ("VarHead" === expr.head.type && 0 === expr.tail.length && this.block.isKeyword(expr.head.name)) return this.block.builder.keyword(expr.head.name, this.block.table.getKeyword(expr.head.name), loc);
let tail = [],
offset = this.block.loc(expr.head.loc);
for (let part of expr.tail) offset = offset.sliceStartChars({
chars: part.length,
skipStart: 1
}), tail.push(new SourceSlice({
loc: offset,
chars: part
}));
return this.block.builder.path(this.ref(expr.head, resolution), tail, loc);
}
/**
* The `callParts` method takes ASTv1.CallParts as well as a syntax context and normalizes
* it to an ASTv2 CallParts.
*/
callParts(parts, context) {
let {
path: path,
params: params,
hash: hash,
loc: loc
} = parts,
callee = this.normalize(path, context),
paramList = params.map(p => this.normalize(p, STRICT_RESOLUTION)),
paramLoc = SpanList.range(paramList, callee.loc.collapse("end")),
namedLoc = this.block.loc(hash.loc),
argsLoc = SpanList.range([paramLoc, namedLoc]),
positional = this.block.builder.positional(params.map(p => this.normalize(p, STRICT_RESOLUTION)), paramLoc),
named = this.block.builder.named(hash.pairs.map(p => this.namedArgument(p)), this.block.loc(hash.loc));
switch (callee.type) {
case "Literal":
throw generateSyntaxError(`Invalid invocation of a literal value (\`${callee.value}\`)`, loc);
// This really shouldn't be possible, something has gone pretty wrong
case "Interpolate":
throw generateSyntaxError("Invalid invocation of a interpolated string", loc);
}
return {
callee: callee,
args: this.block.builder.args(positional, named, argsLoc)
};
}
namedArgument(pair) {
let keyOffsets = this.block.loc(pair.loc).sliceStartChars({
chars: pair.key.length
});
return this.block.builder.namedArgument(new SourceSlice({
chars: pair.key,
loc: keyOffsets
}), this.normalize(pair.value, STRICT_RESOLUTION));
}
/**
* The `ref` method normalizes an `ASTv1.PathHead` into an `ASTv2.VariableReference`.
* This method is extremely important, because it is responsible for normalizing free
* variables into an an ASTv2.PathHead *with appropriate context*.
*
* The syntax context is originally determined by the syntactic position that this `PathHead`
* came from, and is ultimately attached to the `ASTv2.VariableReference` here. In ASTv2,
* the `VariableReference` node bears full responsibility for loose mode rules that control
* the behavior of free variables.
*/
ref(head, resolution) {
let {
block: block
} = this,
{
builder: builder,
table: table
} = block,
offsets = block.loc(head.loc);
switch (head.type) {
case "ThisHead":
return builder.self(offsets);
case "AtHead":
{
let symbol = table.allocateNamed(head.name);
return builder.at(head.name, symbol, offsets);
}
case "VarHead":
if (block.hasBinding(head.name)) {
let [symbol, isRoot] = table.get(head.name);
return block.builder.localVar(head.name, symbol, isRoot, offsets);
}
{
let context = block.strict ? STRICT_RESOLUTION : resolution,
symbol = block.table.allocateFree(head.name, context);
return block.builder.freeVar({
name: head.name,
context: context,
symbol: symbol,
loc: offsets
});
}
}
}
}
/**
* `TemplateNormalizer` normalizes top-level ASTv1 statements to ASTv2.
*/
class StatementNormalizer {
constructor(block) {
this.block = block;
}
normalize(node) {
switch (node.type) {
case "BlockStatement":
return this.BlockStatement(node);
case "ElementNode":
return new ElementNormalizer(this.block).ElementNode(node);
case "MustacheStatement":
return this.MustacheStatement(node);
// These are the same in ASTv2
case "MustacheCommentStatement":
return this.MustacheCommentStatement(node);
case "CommentStatement":
{
let loc = this.block.loc(node.loc);
return new HtmlComment({
loc: loc,
text: loc.slice({
skipStart: 4,
skipEnd: 3
}).toSlice(node.value)
});
}
case "TextNode":
return new HtmlText({
loc: this.block.loc(node.loc),
chars: node.chars
});
}
}
MustacheCommentStatement(node) {
let textLoc,
loc = this.block.loc(node.loc);
return textLoc = "{{!--" === loc.asString().slice(0, 5) ? loc.slice({
skipStart: 5,
skipEnd: 4
}) : loc.slice({
skipStart: 3,
skipEnd: 2
}), new GlimmerComment({
loc: loc,
text: textLoc.toSlice(node.value)
});
}
/**
* Normalizes an ASTv1.MustacheStatement to an ASTv2.AppendStatement
*/
MustacheStatement(mustache) {
let value,
{
path: path,
params: params,
hash: hash,
trusting: trusting
} = mustache,
loc = this.block.loc(mustache.loc);
if (isLiteral(path)) 0 === params.length && 0 === hash.pairs.length ? value = this.expr.normalize(path) : assertIllegalLiteral(path, loc);else {
let resolution = this.block.resolutionFor(mustache, AppendSyntaxContext);
if ("error" === resolution.result) throw generateSyntaxError(`You attempted to render a path (\`{{${resolution.path}}}\`), but ${resolution.head} was not in scope`, loc);
// Normalize the call parts in AppendSyntaxContext
let callParts = this.expr.callParts({
path: path,
params: params,
hash: hash,
loc: loc
}, resolution.result);
value = callParts.args.isEmpty() ? callParts.callee : this.block.builder.sexp(callParts, loc);
}
return this.block.builder.append({
table: this.block.table,
trusting: trusting,
value: value
}, loc);
}
/**
* Normalizes a ASTv1.BlockStatement to an ASTv2.BlockStatement
*/
BlockStatement(block) {
let {
program: program,
inverse: inverse
} = block,
loc = this.block.loc(block.loc);
// block.path used to incorrectly have the type ASTv1.Expression
isLiteral(block.path) && assertIllegalLiteral(block.path, loc);
let resolution = this.block.resolutionFor(block, BlockSyntaxContext);
if ("error" === resolution.result) throw generateSyntaxError(`You attempted to invoke a path (\`{{#${resolution.path}}}\`) but ${resolution.head} was not in scope`, loc);
let callParts = this.expr.callParts(block, resolution.result);
return this.block.builder.blockStatement(assign({
symbols: this.block.table,
program: this.Block(program),
inverse: inverse ? this.Block(inverse) : null
}, callParts), loc);
}
Block({
body: body,
loc: loc,
blockParams: blockParams
}) {
let child = this.block.child(blockParams),
normalizer = new StatementNormalizer(child);
return new BlockChildren(this.block.loc(loc), body.map(b => normalizer.normalize(b)), this.block).assertBlock(child.table);
}
get expr() {
return new ExpressionNormalizer(this.block);
}
}
class ElementNormalizer {
constructor(ctx) {
this.ctx = ctx;
}
/**
* Normalizes an ASTv1.ElementNode to:
*
* - ASTv2.NamedBlock if the tag name begins with `:`
* - ASTv2.Component if the tag name matches the component heuristics
* - ASTv2.SimpleElement if the tag name doesn't match the component heuristics
*
* A tag name represents a component if:
*
* - it begins with `@`
* - it is exactly `this` or begins with `this.`
* - the part before the first `.` is a reference to an in-scope variable binding
* - it begins with an uppercase character
*/
ElementNode(element) {
let {
tag: tag,
selfClosing: selfClosing,
comments: comments
} = element,
loc = this.ctx.loc(element.loc),
[tagHead, ...rest] = asPresentArray(tag.split(".")),
path = this.classifyTag(tagHead, rest, element.loc),
attrs = element.attributes.filter(a => "@" !== a.name[0]).map(a => this.attr(a)),
args = element.attributes.filter(a => "@" === a.name[0]).map(a => this.arg(a)),
modifiers = element.modifiers.map(m => this.modifier(m)),
child = this.ctx.child(element.blockParams),
normalizer = new StatementNormalizer(child),
childNodes = element.children.map(s => normalizer.normalize(s)),
el = this.ctx.builder.element({
selfClosing: selfClosing,
attrs: attrs,
componentArgs: args,
modifiers: modifiers,
comments: comments.map(c => new StatementNormalizer(this.ctx).MustacheCommentStatement(c))
}),
children = new ElementChildren(el, loc, childNodes, this.ctx),
tagOffsets = this.ctx.loc(element.loc).sliceStartChars({
chars: tag.length,
skipStart: 1
});
if ("ElementHead" === path) return ":" === tag[0] ? children.assertNamedBlock(tagOffsets.slice({
skipStart: 1
}).toSlice(tag.slice(1)), child.table) : children.assertElement(tagOffsets.toSlice(tag), element.blockParams.length > 0);
if (element.selfClosing) return el.selfClosingComponent(path, loc);
{
let blocks = children.assertComponent(tag, child.table, element.blockParams.length > 0);
return el.componentWithNamedBlocks(path, blocks, loc);
}
}
modifier(m) {
// modifier.path used to incorrectly have the type ASTv1.Expression
isLiteral(m.path) && assertIllegalLiteral(m.path, m.loc);
let resolution = this.ctx.resolutionFor(m, ModifierSyntaxContext);
if ("error" === resolution.result) throw generateSyntaxError(`You attempted to invoke a path (\`{{${resolution.path}}}\`) as a modifier, but ${resolution.head} was not in scope`, m.loc);
let callParts = this.expr.callParts(m, resolution.result);
return this.ctx.builder.modifier(callParts, this.ctx.loc(m.loc));
}
/**
* This method handles attribute values that are curlies, as well as curlies nested inside of
* interpolations:
*
* ```hbs
* <a href={{url}} />
* <a href="{{url}}.html" />
* ```
*/
mustacheAttr(mustache) {
let {
path: path,
params: params,
hash: hash,
loc: loc
} = mustache;
if (isLiteral(path)) {
if (0 === params.length && 0 === hash.pairs.length) return this.expr.normalize(path);
assertIllegalLiteral(path, loc);
}
// Normalize the call parts in AttrValueSyntaxContext
let resolution = this.ctx.resolutionFor(mustache, AttrValueSyntaxContext);
if ("error" === resolution.result) throw generateSyntaxError(`You attempted to render a path (\`{{${resolution.path}}}\`), but ${resolution.head} was not in scope`, mustache.loc);
let sexp = this.ctx.builder.sexp(this.expr.callParts(mustache, resolution.result), this.ctx.loc(mustache.loc));
// If there are no params or hash, just return the function part as its own expression
return sexp.args.isEmpty() ? sexp.callee : sexp;
}
/**
* attrPart is the narrowed down list of valid attribute values that are also
* allowed as a concat part (you can't nest concats).
*/
attrPart(part) {
switch (part.type) {
case "MustacheStatement":
return {
expr: this.mustacheAttr(part),
trusting: part.trusting
};
case "TextNode":
return {
expr: this.ctx.builder.literal(part.chars, this.ctx.loc(part.loc)),
trusting: !0
};
}
}
attrValue(part) {
if ("ConcatStatement" === part.type) {
let parts = part.parts.map(p => this.attrPart(p).expr);
return {
expr: this.ctx.builder.interpolate(parts, this.ctx.loc(part.loc)),
trusting: !1
};
}
return this.attrPart(part);
}
attr(m) {
if (debugAssert("@" !== m.name[0], "An attr name must not start with `@`"), "...attributes" === m.name) return this.ctx.builder.splatAttr(this.ctx.table.allocateBlock("attrs"), this.ctx.loc(m.loc));
let offsets = this.ctx.loc(m.loc),
nameSlice = offsets.sliceStartChars({
chars: m.name.length
}).toSlice(m.name),
value = this.attrValue(m.value);
return this.ctx.builder.attr({
name: nameSlice,
value: value.expr,
trusting: value.trusting
}, offsets);
}
// An arg curly <Foo @bar={{...}} /> is the same as an attribute curly for
// our purposes, except that in loose mode <Foo @bar={{baz}} /> is an error:
checkArgCall(arg) {
let {
value: value
} = arg;
if ("MustacheStatement" !== value.type) return;
if (0 !== value.params.length || 0 !== value.hash.pairs.length) return;
let {
path: path
} = value;
if ("PathExpression" !== path.type) return;
if (path.tail.length > 0) return;
let resolution = this.ctx.resolutionFor(path, () => null);
if ("error" === resolution.result && "has-block" !== resolution.path) throw generateSyntaxError(`You attempted to pass a path as argument (\`${arg.name}={{${resolution.path}}}\`) but ${resolution.head} was not in scope. Try:\n* \`${arg.name}={{this.${resolution.path}}}\` if this is meant to be a property lookup, or\n* \`${arg.name}={{(${resolution.path})}}\` if this is meant to invoke the resolved helper, or\n* \`${arg.name}={{helper "${resolution.path}"}}\` if this is meant to pass the resolved helper by value`, arg.loc);
}
arg(arg) {
debugAssert("@" === arg.name[0], "An arg name must start with `@`"), this.checkArgCall(arg);
let offsets = this.ctx.loc(arg.loc),
nameSlice = offsets.sliceStartChars({
chars: arg.name.length
}).toSlice(arg.name),
value = this.attrValue(arg.value);
return this.ctx.builder.arg({
name: nameSlice,
value: value.expr,
trusting: value.trusting
}, offsets);
}
/**
* This function classifies the head of an ASTv1.Element into an ASTv2.PathHead (if the
* element is a component) or `'ElementHead'` (if the element is a simple element).
*
* Rules:
*
* 1. If the variable is an `@arg`, return an `AtHead`
* 2. If the variable is `this`, return a `ThisHead`
* 3. If the variable is in the current scope:
* a. If the scope is the root scope, then return a Free `LocalVarHead`
* b. Else, return a standard `LocalVarHead`
* 4. If the tag name is a path and the variable is not in the current scope, Syntax Error
* 5. If the variable is uppercase return a FreeVar(ResolveAsComponentHead)
* 6. Otherwise, return `'ElementHead'`
*/
classifyTag(variable, tail, loc) {
let uppercase = (tag = variable)[0] === tag[0]?.toUpperCase() && tag[0] !== tag[0]?.toLowerCase();
var tag;
let inScope = "@" === variable[0] || "this" === variable || this.ctx.hasBinding(variable);
if (this.ctx.strict && !inScope) {
if (uppercase) throw generateSyntaxError(`Attempted to invoke a component that was not in scope in a strict mode template, \`<${variable}>\`. If you wanted to create an element with that name, convert it to lowercase - \`<${variable.toLowerCase()}>\``, loc);
// In strict mode, values are always elements unless they are in scope
return "ElementHead";
}
// Since the parser handed us the HTML element name as a string, we need
// to convert it into an ASTv1 path so it can be processed using the
// expression normalizer.
let isComponent = inScope || uppercase,
variableLoc = loc.sliceStartChars({
skipStart: 1,
chars: variable.length
}),
tailLength = tail.reduce((accum, part) => accum + 1 + part.length, 0),
pathEnd = variableLoc.getEnd().move(tailLength),
pathLoc = variableLoc.withEnd(pathEnd);
if (isComponent) {
let path = b.path({
head: b.head({
original: variable,
loc: variableLoc
}),
tail: tail,
loc: pathLoc
}),
resolution = this.ctx.isLexicalVar(variable) ? {
result: STRICT_RESOLUTION
} : this.ctx.resolutionFor(path, ComponentSyntaxContext);
if ("error" === resolution.result) throw generateSyntaxError(`You attempted to invoke a path (\`<${resolution.path}>\`) but ${resolution.head} was not in scope`, loc);
return new ExpressionNormalizer(this.ctx).normalize(path, resolution.result);
}
// If the tag name wasn't a valid component but contained a `.`, it's
// a syntax error.
if (this.ctx.table.allocateFree(variable, STRICT_RESOLUTION), tail.length > 0) throw generateSyntaxError(`You used ${variable}.${tail.join(".")} as a tag name, but ${variable} is not in scope`, loc);
return "ElementHead";
}
get expr() {
return new ExpressionNormalizer(this.ctx);
}
}
class Children {
namedBlocks;
hasSemanticContent;
nonBlockChildren;
constructor(loc, children, block) {
this.loc = loc, this.children = children, this.block = block, this.namedBlocks = children.filter(c => c instanceof NamedBlock), this.hasSemanticContent = Boolean(children.filter(c => {
if (c instanceof NamedBlock) return !1;
switch (c.type) {
case "GlimmerComment":
case "HtmlComment":
return !1;
case "HtmlText":
return !/^\s*$/u.test(c.chars);
default:
return !0;
}
}).length), this.nonBlockChildren = children.filter(c => !(c instanceof NamedBlock));
}
}
class TemplateChildren extends Children {
assertTemplate(table) {
if (isPresentArray(this.namedBlocks)) throw generateSyntaxError("Unexpected named block at the top-level of a template", this.loc);
return this.block.builder.template(table, this.nonBlockChildren, this.block.loc(this.loc));
}
}
class BlockChildren extends Children {
assertBlock(table) {
if (isPresentArray(this.namedBlocks)) throw generateSyntaxError("Unexpected named block nested in a normal block", this.loc);
return this.block.builder.block(table, this.nonBlockChildren, this.loc);
}
}
class ElementChildren extends Children {
constructor(el, loc, children, block) {
super(loc, children, block), this.el = el;
}
assertNamedBlock(name, table) {
if (this.el.base.selfClosing) throw generateSyntaxError(`<:${name.chars}/> is not a valid named block: named blocks cannot be self-closing`, this.loc);
if (isPresentArray(this.namedBlocks)) throw generateSyntaxError(`Unexpected named block inside <:${name.chars}> named block: named blocks cannot contain nested named blocks`, this.loc);
if ((tag = name.chars)[0] !== tag[0]?.toLowerCase() || tag[0] === tag[0]?.toUpperCase()) throw generateSyntaxError(`<:${name.chars}> is not a valid named block, and named blocks must begin with a lowercase letter`, this.loc);
var tag;
if (this.el.base.attrs.length > 0 || this.el.base.componentArgs.length > 0 || this.el.base.modifiers.length > 0) throw generateSyntaxError(`named block <:${name.chars}> cannot have attributes, arguments, or modifiers`, this.loc);
let offsets = SpanList.range(this.nonBlockChildren, this.loc);
return this.block.builder.namedBlock(name, this.block.builder.block(table, this.nonBlockChildren, offsets), this.loc);
}
assertElement(name, hasBlockParams) {
if (hasBlockParams) throw generateSyntaxError(`Unexpected block params in <${name.chars}>: simple elements cannot have block params`, this.loc);
if (isPresentArray(this.namedBlocks)) {
let names = this.namedBlocks.map(b => b.name);
if (1 === names.length) throw generateSyntaxError(`Unexpected named block <:foo> inside <${name.chars}> HTML element`, this.loc);
{
let printedNames = names.map(n => `<:${n.chars}>`).join(", ");
throw generateSyntaxError(`Unexpected named blocks inside <${name.chars}> HTML element (${printedNames})`, this.loc);
}
}
return this.el.simple(name, this.nonBlockChildren, this.loc);
}
assertComponent(name, table, hasBlockParams) {
if (isPresentArray(this.namedBlocks) && this.hasSemanticContent) throw generateSyntaxError(`Unexpected content inside <${name}> component invocation: when using named blocks, the tag cannot contain other content`, this.loc);
if (isPresentArray(this.namedBlocks)) {
if (hasBlockParams) throw generateSyntaxError(`Unexpected block params list on <${name}> component invocation: when passing named blocks, the invocation tag cannot take block params`, this.loc);
let seenNames = new Set();
for (let block of this.namedBlocks) {
let name = block.name.chars;
if (seenNames.has(name)) throw generateSyntaxError(`Component had two named blocks with the same name, \`<:${name}>\`. Only one block with a given name may be passed`, this.loc);
if ("inverse" === name && seenNames.has("else") || "else" === name && seenNames.has("inverse")) throw generateSyntaxError("Component has both <:else> and <:inverse> block. <:inverse> is an alias for <:else>", this.loc);
seenNames.add(name);
}
return this.namedBlocks;
}
return [this.block.builder.namedBlock(SourceSlice.synthetic("default"), this.block.builder.block(table, this.nonBlockChildren, this.loc), this.loc)];
}
}
function isLiteral(node) {
switch (node.type) {
case "StringLiteral":
case "BooleanLiteral":
case "NumberLiteral":
case "UndefinedLiteral":
case "NullLiteral":
return !0;
default:
return !1;
}
}
function assertIllegalLiteral(node, loc) {
throw generateSyntaxError(`Unexpected literal \`${"StringLiteral" === node.type ? JSON.stringify(node.value) : String(node.value)}\``, loc);
}
function printPath(node) {
return "PathExpression" !== node.type && "PathExpression" === node.path.type ? printPath(node.path) : new Printer({
entityEncoding: "raw"
}).print(node);
}
function printHead(node) {
return "PathExpression" === node.type ? node.head.original : "PathExpression" === node.path.type ? printHead(node.path) : new Printer({
entityEncoding: "raw"
}).print(node);
}
const TARGETS = Object.freeze(['helper', 'modifier']);
function transformResolutions(env) {
let {
builders: b
} = env.syntax;
let moduleName = env.meta?.moduleName;
let {
hasLocal,
node: tracker
} = trackLocals(env);
let seen;
return {
name: 'transform-resolutions',
visitor: {
Template: {
enter() {
seen = new Set();
},
exit() {
seen = undefined;
}
},
Block: tracker,
ElementNode: {
keys: {
children: tracker
}
},
MustacheStatement(node) {
(isDevelopingApp() && !(seen) && assert('[BUG] seen set should be available', seen));
if (seen.has(node)) {
return;
}
if (isPath(node.path) && !isLocalVariable(node.path, hasLocal) && TARGETS.indexOf(node.path.original) !== -1) {
let result = b.mustache(node.path, transformParams(b, node.params, node.path.original, moduleName, node.loc), node.hash, node.trusting, node.loc, node.strip);
// Avoid double/infinite-processing
seen.add(result);
return result;
}
},
SubExpression(node) {
(isDevelopingApp() && !(seen) && assert('[BUG] seen set should be available', seen));
if (seen.has(node)) {
return;
}
if (isPath(node.path) && !isLocalVariable(node.path, hasLocal) && TARGETS.indexOf(node.path.original) !== -1) {
let result = b.sexpr(node.path, transformParams(b, node.params, node.path.original, moduleName, node.loc), node.hash, node.loc);
// Avoid double/infinite-processing
seen.add(result);
return result;
}
}
}
};
}
function isLocalVariable(node, hasLocal) {
return !(node.head.type === 'ThisHead') && node.tail.length === 1 && hasLocal(node.head.original);
}
function transformParams(b, params, type, moduleName, loc) {
let [first, ...rest] = params;
(isDevelopingApp() && !(first) && assert(`The ${type} keyword requires at least one positional arguments ${calculateLocationDisplay(moduleName, loc)}`, first));
if (isStringLiteral(first)) {
return [b.sexpr(b.path('-resolve', first.loc), [b.string(`${type}:${first.value}`)], undefined, first.loc), ...rest];
} else if (isDevelopingApp()) {
return [b.sexpr(b.path('-disallow-dynamic-resolution', first.loc), [first], b.hash([b.pair('type', b.string(type), first.loc), b.pair('loc', b.string(calculateLocationDisplay(moduleName, loc)), first.loc), b.pair('original', b.string(build(first)))]), first.loc), ...rest];
} else {
return params;
}
}
export { KEYWORDS_TYPES as K, SourceSlice as S, api$2 as a, api$1 as b, normalize as c, generateSyntaxError as g, isKeyword as i, loc as l, maybeLoc as m, node as n, transformResolutions as t };