imba
Version:
2,384 lines (2,010 loc) • 492 kB
JavaScript
import { defineStyleNodes } from "./ast/style.mjs";
import * as helpers from "./helpers.mjs";
import * as constants from "./constants.mjs";
import * as fspath from "path";
import { conv } from "../../vendor/colors.mjs";
import { colord } from "./colord.mjs";
import { ImbaParseError, ImbaTraverseError } from "./errors.mjs";
import { Token as Token } from "./token.mjs";
import { SourceMap as SourceMap } from "./sourcemap.mjs";
import { StyleRule, StyleTheme, Color, StyleSheet, parseColorString } from "./styler.mjs";
import { Compilation } from "./compilation.mjs";
import { SourceMapper } from "./sourcemapper.mjs";
import { ClassFlags as ClassFlags } from "../imba/runtime.mjs";
import { extractGenericNames } from "./utils.mjs";
function len$(a) {
return (a && (a.len instanceof Function ? a.len() : a.length)) || 0;
}
function idx$(a, b) {
return b && b.indexOf ? b.indexOf(a) : [].indexOf.call(a, b);
}
function iter$(a) {
return a ? (a.toArray ? a.toArray() : a) : [];
}
var self = {};
// imba$inlineHelpers=1
// imba$v2=0
// TODO Create Expression - make all expressions inherit from these?
var ReservedIdentifierRegex = helpers.ReservedIdentifierRegex,
InternalPrefixes = helpers.InternalPrefixes,
toJSIdentifier = helpers.toJSIdentifier,
toCustomTagIdentifier = helpers.toCustomTagIdentifier;
class MappedString {
constructor(value, source) {
this._value = value;
this._source = source;
}
startLoc() {
return this._source.startLoc();
}
endLoc() {
return this._source.endLoc();
}
toString() {
return this._value;
}
c() {
return M(this._value, this);
}
}
class Templated {
constructor(template, options, source) {
this._template = template;
this._options = options;
this._source = source;
}
c() {
return TPL(this._options, this._template);
}
toString() {
return this.c();
}
}
class InternalName {
constructor(value, source) {
this._source = source || value;
if (value.toClassName) {
value = value.toClassName();
}
if (value.c instanceof Function) {
value = value.c({ mark: false });
}
value = "Ω" + SourceMapper.strip(value).split(".").join("__");
let nr = STACK.incr(value);
if (nr > 1) {
value += "Ω" + nr;
}
this._value = value;
}
startLoc() {
return this._source.startLoc();
}
endLoc() {
return this._source.endLoc();
}
toString() {
return this._value;
}
c() {
return this._value;
}
}
var TAG_NAMES = constants.TAG_NAMES;
var TAG_GLOBAL_ATTRIBUTES = constants.TAG_GLOBAL_ATTRIBUTES;
var TAG_TYPES = {};
var TAG_ATTRS = {};
var TSC = false;
var USE_SAFE_RENDER_SELF = true;
var IMBA_FIELD_REGISTRY_ENABLED = true;
var IMBA_FIELD_REGISTRY_KEY_PREFIX = "_$INTERNAL$_imbaFieldRegistry";
var IMBA_FIELD_TARGET_KEY_PREFIX = "_$INTERNAL$_imbaFieldTarget";
var CONTEXT = {};
var GLOBAL_INTERFACES = {
Array: { interface: "ArrayConstructor" },
Number: { interface: "NumberConstructor", thistype: "number" },
String: { interface: "StringConstructor", thistype: "string" },
Object: { interface: "ObjectConstructor" },
Math: { namespace: "Math" },
window: { global: true },
};
var EXT_LOADER_MAP = {
svg: "image",
png: "image",
apng: "image",
jpg: "image",
jpeg: "image",
gif: "image",
tiff: "image",
bmp: "image",
};
TAG_TYPES.HTML =
"a abbr address area article aside audio b base bdi bdo big blockquote body br button canvas caption cite code col colgroup data datalist dd del details dfn div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hr html i iframe img input ins kbd keygen label legend li link main map mark menu menuitem meta meter nav noscript object ol optgroup option output p param pre progress q rp rt ruby s samp script section select small source span strong strike style sub summary sup table tbody td textarea tfoot th thead time title tr track u ul var video wbr".split(
" ",
);
TAG_TYPES.SVG =
"circle defs ellipse g line linearGradient mask path pattern polygon polyline radialGradient rect stop svg text tspan".split(
" ",
);
TAG_ATTRS.HTML =
"accept accessKey action allowFullScreen allowTransparency alt async autoComplete autoFocus autoPlay cellPadding cellSpacing charSet checked className cols colSpan content contentEditable contextMenu controls coords crossOrigin data dateTime defer dir disabled download draggable encType form formNoValidate frameBorder height hidden href hrefLang htmlFor httpEquiv icon id label lang list loop max maxLength mediaGroup method min multiple muted name noValidate pattern placeholder poster preload radioGroup readOnly rel required role rows rowSpan sandbox scope scrollLeft scrolling scrollTop seamless selected shape size span spellCheck src srcDoc srcSet start step style tabIndex target title type useMap value width wmode";
TAG_ATTRS.SVG =
"cx cy d dx dy fill fillOpacity fontFamily fontSize fx fy gradientTransform gradientUnits markerEnd markerMid markerStart offset opacity patternContentUnits patternUnits points preserveAspectRatio r rx ry spreadMethod stopColor stopOpacity stroke strokeDasharray strokeLinecap strokeOpacity strokeWidth textAnchor transform version viewBox x1 x2 x y1 y2 y";
var CUSTOM_EVENTS = {
intersect: "events_intersect",
selection: "events_selection",
resize: "events_resize",
mutate: "events_mutate",
hotkey: "events_hotkey",
touch: "events_touch",
pointer: "events_pointer",
pointerdown: "events_pointer",
pointermove: "events_pointer",
pointerover: "events_pointer",
pointerout: "events_pointer",
pointerup: "events_pointer",
pointercancel: "events_pointer",
lostpointercapture: "events_pointer",
click: "events_mouse",
mousedown: "events_mouse",
mouseup: "events_mouse",
mouseenter: "events_mouse",
mouseleave: "events_mouse",
mousemove: "events_mouse",
mouseout: "events_mouse",
mouseover: "events_mouse",
mousewheel: "events_mouse",
keydown: "events_keyboard",
keyup: "events_keyboard",
keypress: "events_keyboard",
};
var AST = {};
var TREE_TYPE = {
DYNAMIC: 1,
STATIC: 2,
SINGLE: 3,
OPTLOOP: 4,
LOOP: 5,
};
var F = {
TAG_INITED: 2 ** 0,
TAG_BUILT: 2 ** 1, // available
TAG_CUSTOM: 2 ** 2, // available
TAG_AWAKENED: 2 ** 3,
TAG_MOUNTED: 2 ** 4,
TAG_SCHEDULE: 2 ** 5, // available
TAG_SCHEDULED: 2 ** 6,
TAG_FIRST_CHILD: 2 ** 7,
TAG_LAST_CHILD: 2 ** 8,
TAG_HAS_DYNAMIC_FLAGS: 2 ** 9,
TAG_HAS_BRANCHES: 2 ** 10,
TAG_HAS_LOOPS: 2 ** 11,
TAG_HAS_DYNAMIC_CHILDREN: 2 ** 12,
TAG_IN_BRANCH: 2 ** 13,
TAG_BIND_MODEL: 2 ** 14,
TAG_INDEXED: 2 ** 15, // not used
TAG_KEYED: 2 ** 16, // not used
EL_INITED: 2 ** 0,
EL_HYDRATED: 2 ** 1,
EL_HYDRATING: 2 ** 2,
EL_AWAKENED: 2 ** 3,
EL_MOUNTING: 2 ** 4,
EL_MOUNTED: 2 ** 5,
EL_SCHEDULE: 2 ** 6, // available
EL_SCHEDULED: 2 ** 7,
EL_RENDERING: 2 ** 8,
EL_RENDERED: 2 ** 9,
EL_SSR: 2 ** 10,
EL_TRACKED: 2 ** 11, // emit mount/unmount events
EL_SUSPENDED: 2 ** 12, // block commit from rendering
EL_UNRENDERED: 2 ** 13,
EL_MOVING: 2 ** 14,
// render marks
DIFF_BUILT: 2 ** 0,
DIFF_FLAGS: 2 ** 1,
DIFF_ATTRS: 2 ** 2,
DIFF_CHILDREN: 2 ** 3,
DIFF_MODIFIERS: 2 ** 4,
DIFF_INLINE: 2 ** 5,
};
var NESTED_TPL_REGEX =
/@\{(@(\!?\w+)\?)?([^{}]*(:?\{([^{}]*(:?\{[^{}]*\}[^{}]*)*)\}[^{}]*)*)\}/g;
var TPL = function (vars, string, o, d) {
if (o === undefined) o = {};
if (d === undefined) d = 0;
return STACK.call({ template: string }, function () {
string = string.replace(/\%/g, "@");
// lazy to use regex but it works for these simple templates
string = string.replace(NESTED_TPL_REGEX, function (m, g, cond, subtpl) {
if (cond) {
if (cond[0] == "!") {
if (vars[cond.slice(1)]) {
return "εε";
}
} else {
if (!vars[cond]) {
return "εε";
}
}
}
let o = {};
let sub = TPL(vars, subtpl, o, d + 1);
return o.replaced || cond ? sub : "εε";
});
string = string.replace(/\@([\w\-]+)\|?/g, function (m, k) {
if (k == "ts-ignore") {
return "@ts-ignore";
}
let v = vars[k];
if (v === true) {
o.replaced = true;
return k;
}
if (v) {
v = M(v);
}
if (v) {
o.replaced = true;
}
return v || "εε";
});
string = string.replace(/(\n\s*)(εε)\s*(?=\n)/g, ""); // .replace(/εε/g,'')
string = string.replace(/(^|\s)(εε[ ]?)*/gm, "$1").replace(/εε/g, "");
string = string.replace(/(^[ ]+)/gm, "");
if (d == 0) {
string = string.trim();
}
return string;
});
};
var DECLARE = function (node, keyword) {
if (!Compilation.current.tsc) {
return LIT("");
}
return node.set({ declareOnly: keyword });
};
var GLOBAL = function (node, keyword) {
return node.set({ global: keyword });
};
var SETTYPE = function (node, type, ctx) {
// dont even include any types when not in TSC mode
if (!!!Compilation.current.tsc) {
return node;
}
// console.log "set datatype {node} {type:constructor:name}",!!Compilation:current:tsc
if (type instanceof Generics) {
node.set({ generics: type });
return node;
// return node.set(generics: type)
}
return node.set({ datatype: type });
};
// Helpers for operators
var OP = function (op, l, r) {
var o = String(op);
switch (o) {
case ".":
case "?.": {
if (l instanceof Super && !l._member) {
((l._member = r), l);
return l;
}
if (typeof r == "string" || r instanceof String) {
r = new Identifier(r);
}
// r = r.value if r isa VarOrAccess
// if r.@value and r.@value.@value == 'new'
// # TODO remove support for this
// return New.new(l).set(keyword: r)
return new Access(op, l, r);
break;
}
case "=": {
return new Assign(op, l, r);
break;
}
case "~=": {
return OP("&=", l, OP("~", r));
break;
}
case "||=":
case "&&=":
case "??=": {
return new ConditionalAssign(op, l, r);
break;
}
case "+=":
case "-=":
case "*=":
case "/=":
case "^=":
case "%=":
case "**=": {
return new CompoundAssign(op, l, r);
break;
}
case "instanceof":
case "isa": {
return new InstanceOf(op, l, r);
break;
}
case "in": {
return new In(op, l, r);
break;
}
case "typeof": {
return new TypeOf(op, l, r);
break;
}
case "delete": {
return new Delete(op, l, r);
break;
}
case "--":
case "++":
case "!":
case "√":
case "not":
case "!!": {
return new UnaryOp(op, l, r);
break;
}
case ">":
case "<":
case ">=":
case "<=":
case "==":
case "===":
case "!=":
case "!==": {
return new ComparisonOp(op, l, r);
break;
}
case "..":
case "...": {
return new Range(op, l, r);
break;
}
default:
return new Op(op, l, r);
}
};
var PATHIFY = function (val) {
// console.log "PATHIFY {val}"
if (val instanceof TagAttrValue) {
val = val.value();
}
if (val instanceof ArgList) {
val = val.values()[0];
}
while (val instanceof Parens) {
val = val.value();
}
if (val instanceof VarOrAccess) {
val = val._variable || val.value();
}
if (val instanceof Access) {
let left = val._left;
let right = val._right instanceof Index ? val._right.value() : val._right;
if (left instanceof VarOrAccess) {
left = left._variable || left.value();
}
if (right instanceof VarOrAccess) {
right = right._variable || right.value();
}
if (val instanceof IvarAccess) {
left || (left = val.scope__().context());
}
if (right instanceof SymbolIdentifier) {
true;
} else if (right instanceof Identifier) {
right = helpers.singlequote(String(right.js()));
right = new Str(right);
}
return [left, right];
}
return val;
};
var OPTS = {};
var ROOT = null;
var NODES = [];
var C = function (node, opts) {
return typeof node == "string" || typeof node == "number"
? node
: node.c(opts);
};
var MLOC = function (a, b) {
if (b == undefined) {
b = a;
}
return {
startLoc: function () {
return a;
},
endLoc: function () {
return b;
},
};
};
var MPREV = [-1, -1, -1];
var MARK_SOURCE = true;
var M = function (val, mark, o) {
if (mark == undefined) {
mark = val;
o || (o = { mark: false });
}
if (mark && mark.startLoc) {
val = C(val, o);
if (!MARK_SOURCE) {
return val;
}
// what if the value itself creates a location?
let ref = STACK.incr("sourcePair");
let start = mark.startLoc();
let end = mark.endLoc();
let m0 = "";
let m1 = "";
if (
start === MPREV[0] &&
end === MPREV[1] &&
ref == MPREV[2] + 1 &&
val.startsWith("/*%")
) {
if (true) {
// val.indexOf("/*%{start}|{MPREV[2]}") >= 0 and false
STACK.decr("sourcePair");
// only if the value contains this, no?
return val;
}
}
if (start == 0 || start > 0) {
m0 =
end >= start
? "/*%" + start + "|" + ref + "$*/"
: "/*%" + start + "$*/";
}
if (end == 0 || end > 0) {
m1 = start >= 0 ? "/*%" + end + "|" + ref + "$*/" : "/*%" + end + "$*/";
}
MPREV = [start, end, ref];
return m0 + val + m1;
}
return C(val, o);
};
var MSTART = function () {
var $0 = arguments,
i = $0.length;
var params = new Array(i > 0 ? i : 0);
while (i > 0) params[i - 1] = $0[--i];
for (
let i = 0, items = iter$(params), len = items.length, item;
i < len;
i++
) {
item = items[i];
if (typeof item == "number" || item instanceof Number) {
return item;
}
if (item && item.startLoc instanceof Function) {
return item.startLoc();
}
}
return null;
};
var MEND = function () {
var $0 = arguments,
i = $0.length;
var params = new Array(i > 0 ? i : 0);
while (i > 0) params[i - 1] = $0[--i];
for (
let i = 0, items = iter$(params), len = items.length, item;
i < len;
i++
) {
item = items[i];
if (typeof item == "number" || item instanceof Number) {
return item;
}
if (item && item.endLoc instanceof Function) {
return item.endLoc();
}
}
return null;
};
var TYP = function (item, format) {
let typ = item._options && item._options.datatype;
typ || (typ = item.datatype instanceof Function && item.datatype());
if (typ) {
let str = C(typ);
if (format == "jsdoc") {
return "/** @type \{" + M(str, typ) + "\} */";
} else {
return ":" + M(str, typ);
}
} else {
return "";
}
};
var TYPED = function (item, typ) {
if (typ) {
return "" + C(item) + ":" + C(typ);
} else {
return C(item);
}
};
var LIT = function (val, src) {
if (src) {
return new MappedString(val, src);
}
return val instanceof RawScript ? val : new RawScript(val);
};
var SYM = function (val) {
return new Symbol(val);
};
var KEY = function (val) {
if (val instanceof Token) {
val = val.value();
}
if (typeof val == "string" || val instanceof String) {
if (val.match(/^[a-zA-Z\$\_]+[\d\w\$\_]*$/)) {
val = new Identifier(val);
} else {
val = new Str(helpers.singlequote(String(val)));
}
}
return val;
};
var STR = function (val) {
if (val instanceof Str) {
return val;
}
return new Str(helpers.singlequote(String(val)));
};
var IF = function (cond, body, alt, o) {
if (o === undefined) o = {};
var node = new If(cond, body, o);
if (alt) {
node.addElse(alt);
}
return node;
};
var NODIFY = function (val) {
if (val == null) {
return new Nil();
} else if (val == false) {
return new False();
} else if (val == true) {
return new True();
} else if (typeof val == "string" || val instanceof String) {
return STR(val);
} else if (typeof val == "number" || val instanceof Number) {
return new Num(val);
} else {
return val;
}
};
var FN = function (pars, body, scope) {
let fn = new Func(pars, body);
if (scope) {
fn._scope._systemscope = scope;
}
return fn;
};
var METH = function (pars, body) {
return new ClosedFunc(pars, body);
};
var CALL = function (callee, pars) {
// possibly return instead(!)
if (pars === undefined) pars = [];
return new Call(callee, pars);
};
var STDCALL = function (name, pars) {
if (pars === undefined) pars = [];
return CALL(STACK.corelib()[name], pars);
};
var IIFE = function (body) {
// possibly return instead(!)
return new IifeFunc([], body);
};
var GET = function (left, right) {
return OP(".", left, right);
};
var CALLSELF = function (name, pars) {
if (pars === undefined) pars = [];
var ref = new Identifier(name);
return new Call(OP(".", SELF, ref), pars);
};
var BLOCK = function () {
return Block.wrap([].slice.call(arguments));
};
var WHILE = function (test, code) {
return new While(test).addBody(code);
};
var SPLAT = function (value) {
return new Splat(value);
// if value isa Assign
// value.left = Splat.new(value.left)
// return value
// else
// Splat.new(value)
};
var hasTrailingSemicolon = function (str) {
let i = str.length - 1;
if (str.charCodeAt(i) == 59) return true;
while (i >= 0) {
let code = str.charCodeAt(i);
if (!(code == 9 || code == 10 || code == 13 || code == 32)) {
break;
}
i--;
}
if (i < 0) return false;
if (str.charCodeAt(i) == 59) return true;
let lineStart = str.lastIndexOf("\n", i) + 1;
let comment = str.indexOf("//", lineStart);
if (comment < 0 || comment > i) return false;
let j = comment - 1;
while (j >= 0) {
let code = str.charCodeAt(j);
if (!(code == 9 || code == 10 || code == 13 || code == 32)) {
break;
}
j--;
}
return j >= 0 && str.charCodeAt(j) == 59;
};
var RESERVED_TEST = /^(default|char|for)$/;
// captures error from parser
var parseError = (self.parseError = function (str, o) {
var err = Compilation.error({
category: "parser",
severity: "error",
offset: o.offset,
length: o.length,
message: str,
});
return err.raise();
});
AST.c = function (obj) {
return typeof obj == "string" ? obj : obj.c();
};
AST.compileRaw = function (item) {
let o = "";
if (item instanceof Array) {
o = "[";
for (let i = 0, items = iter$(item), len = items.length; i < len; i++) {
o += AST.compileRaw(items[i]) + ",";
}
o = o.slice(0, -1) + "]";
} else if (item instanceof Object) {
o = "{ ";
for (
let v, i = 0, keys = Object.keys(item), l = keys.length, k;
i < l;
i++
) {
// maybe quote?
k = keys[i];
v = item[k];
o += "" + k + ": " + AST.compileRaw(v) + ",";
}
o = o.slice(0, -1) + " }";
} else {
o = JSON.stringify(item);
}
return o;
};
AST.blk = function (obj) {
return obj instanceof Array ? Block.wrap(obj) : obj;
};
AST.sym = function (obj) {
// console.log "sym {obj}"
return helpers.symbolize(String(obj), STACK);
};
AST.cary = function (ary, params) {
if (params === undefined) params = null;
return ary.map(function (v) {
if (typeof v == "string") {
return v;
} else if (v && v.c) {
return params ? v.c(params) : v.c();
} else {
// console.warn 'could not compile',v
return String(v);
}
});
};
AST.dump = function (obj, key) {
if (obj instanceof Array) {
return obj.map(function (v) {
return v && v.dump ? v.dump(key) : v;
});
} else if (obj && obj.dump) {
return obj.dump();
}
};
AST.compact = function (ary) {
if (ary instanceof ListNode) {
return ary.compact();
}
return ary.filter(function (v) {
return v != undefined && v != null;
});
};
AST.reduce = function (res, ary) {
for (let i = 0, items = iter$(ary), len = items.length, v; i < len; i++) {
v = items[i];
v instanceof Array ? AST.reduce(res, v) : res.push(v);
}
return;
};
AST.flatten = function (ary, compact) {
if (compact === undefined) compact = false;
var out = [];
for (let i = 0, items = iter$(ary), len = items.length, v; i < len; i++) {
v = items[i];
v instanceof Array ? AST.reduce(out, v) : out.push(v);
}
return out;
};
AST.loc = function (item) {
if (!item) {
return [0, 0];
} else if (item instanceof Token) {
return item.region();
} else if (item instanceof Node) {
return item.loc();
}
};
AST.parse = function (str, opts) {
if (opts === undefined) opts = {};
var indent = str.match(/\t+/)[0];
// really? Require the compiler, not this
return Imbac.parse(str, opts);
};
AST.inline = function (str, opts) {
if (opts === undefined) opts = {};
return this.parse(str, opts).body();
};
AST.node = function (typ, pars) {
if (typ == "call") {
if (pars[0].c() == "return") {
pars[0] = "tata";
}
return new Call(pars[0], pars[1], pars[2]);
}
};
AST.escapeComments = function (str) {
if (!str) {
return "";
}
return str;
};
var shortRefCache = [];
AST.counterToShortRef = function (nr) {
var base = "A".charCodeAt(0);
nr += 30;
while (shortRefCache.length <= nr) {
var num = shortRefCache.length + 1;
var str = "";
while (true) {
num -= 1;
str = String.fromCharCode(base + (num % 26)) + str;
num = Math.floor(num / 26);
if (num <= 0) {
break;
}
}
shortRefCache.push(str.toLowerCase());
}
return shortRefCache[nr];
};
AST.truthy = function (node) {
if (node instanceof True) {
return true;
}
if (node instanceof False) {
return false;
}
if (node.isTruthy) {
return node.isTruthy();
}
return undefined;
};
var indentGeneratedString = function (str) {
let idx = str.indexOf("\n");
if (idx < 0) {
return "\t" + str;
}
if (str[str.length - 1] == "\n") {
return "\t" + str.slice(0, -1).split("\n").join("\n\t") + "\n";
}
return "\t" + str.split("\n").join("\n\t");
};
class Indentation {
constructor(a, b) {
this._open = a;
this._close = b;
this;
}
isGenerated() {
return this._open && this._open.generated;
}
aloc() {
return (this._open && this._open._loc) || 0;
}
bloc() {
return (this._close && this._close._loc) || 0;
}
startLoc() {
return this.aloc();
}
endLoc() {
return this.bloc();
}
wrap(str) {
var om = this._open && this._open._meta;
var pre = (om && om.pre) || "";
var post = (om && om.post) || "";
var esc = AST.escapeComments;
var out = this._close;
// the first newline should not be indented?
if (post[0] == "\n") {
post = post.slice(1);
}
str = indentGeneratedString(post + str);
str = pre + "\n" + str;
if (out instanceof Terminator) {
str += out.c();
}
if (str[str.length - 1] != "\n") {
str = str + "\n";
}
return str;
}
}
var INDENT = new Indentation({}, {});
class Stash {
constructor() {
this._entities = [];
}
add(item) {
this._entities.unshift(item);
return this;
}
pluck(item) {
var match = null;
for (
let i = 0, items = iter$(this._entities), len = items.length, entity;
i < len;
i++
) {
entity = items[i];
if (entity == item || entity instanceof item) {
match = entity;
this._entities.splice(i, 1);
return match;
}
}
return null;
}
}
class Stack {
constructor() {
this.reset();
}
nodes(v) {
return this._nodes;
}
root(v) {
return this._root;
}
meta(v) {
return this._meta;
}
reset() {
this._nodes = [];
this._scoping = [];
this._scopes = [];
this._stash = new Stash(this);
this._loglevel = 3;
this._counter = 0;
this._counters = {};
this._options = {};
this._state = {};
this._tag = null;
this._sourceId = null;
this._symbols = {};
this._fieldRegistryEntries = [];
this._css = new StyleSheet(this);
this._theme = null;
this._meta = {};
// @css = ''
this._runtime;
MPREV = [-1, -1, -1];
return this;
}
runtime() {
return this._root.runtime();
}
corelib() {
return this._root.importProxy("core", "imba/runtime", "").proxy();
}
cssns() {
return this._root.cssns();
}
use(item) {
return this._root.use(item);
}
addFieldRegistryEntry(entry) {
this._fieldRegistryEntries || (this._fieldRegistryEntries = []);
this._fieldRegistryEntries.push(entry);
return this;
}
fieldRegistryDeclaration() {
if (!IMBA_FIELD_REGISTRY_ENABLED) {
return "";
}
if (!(this._fieldRegistryEntries && this._fieldRegistryEntries.length)) {
return "";
}
let lines = ["declare global {", "\tinterface ImbaFieldRegistry {"];
for (
let i = 0,
items = iter$(this._fieldRegistryEntries),
len = items.length,
entry;
i < len;
i++
) {
entry = items[i];
lines.push("\t\t" + JSON.stringify(entry.key) + ": {");
lines.push("\t\t\towner: " + entry.owner);
lines.push("\t\t\townerType: typeof " + entry.ownerType);
lines.push("\t\t\tfield: " + JSON.stringify(entry.field));
lines.push("\t\t\tdecorator: " + JSON.stringify(entry.decorator));
if (entry.firstArg) {
lines.push("\t\t\tfirstArg: " + entry.firstArg);
lines.push("\t\t\tfirstArgType: " + entry.firstArgType);
}
lines.push("\t\t\targs: [" + entry.args.join(", ") + "]");
lines.push("\t\t}");
}
lines.push("\t}");
lines.push("}");
for (
let i = 0,
items = iter$(this._fieldRegistryEntries),
len = items.length,
entry;
i < len;
i++
) {
entry = items[i];
if (entry.firstArgTarget) {
lines.push(
this.fieldRegistryTargetDeclaration(entry, entry.firstArgTarget),
);
}
}
return lines.join("\n");
}
fieldRegistryTargetDeclaration(entry, target) {
let prop = JSON.stringify(target.key);
let value = "ImbaFieldRegistry[" + JSON.stringify(entry.key) + "]";
let member = "readonly " + prop + "?: " + value;
if (target.module) {
return [
"declare module " + target.module + " {",
"\tinterface " + target.name + " {",
"\t\t" + member,
"\t}",
"}",
].join("\n");
} else if (target.global) {
return [
"declare global {",
"\tinterface " + target.name + " {",
"\t\t" + member,
"\t}",
"}",
].join("\n");
} else {
let prefix = target.export ? "export " : "";
return [
prefix + "interface " + target.name + " {",
"\t" + member,
"}",
].join("\n");
}
}
incr(name) {
this._counters[name] || (this._counters[name] = 0);
return (this._counters[name] += 1);
}
decr(name) {
this._counters[name] || (this._counters[name] = 0);
return (this._counters[name] -= 1);
}
strip(val) {
return SourceMapper.strip(val);
}
generateId(ns) {
if (ns === undefined) ns = "oid";
return AST.counterToShortRef(STACK.tsc() ? 1 : STACK.incr(ns));
return AST.counterToShortRef(STACK.incr(ns));
}
getSymbol(ref, alias, name) {
if (alias === undefined) alias = null;
if (name === undefined) name = "";
let key = ref || (STACK.tsc() ? 1 : this.incr("symbols"));
// ref ||= "" + incr('symbols')
// Belongs in root
return (
this._symbols[key] ||
(this._symbols[key] = this._root
.declare(
alias || ref,
LIT("Symbol(" + (name ? helpers.singlequote(name) : "") + ")"),
{ system: true, alias: alias || ref },
)
.resolve()
.c())
);
}
symbolFor(name) {
return this._root.symbolRef(name);
}
imbaSymbol(name) {
return STACK.isStdLib()
? this.symbolFor("#" + name)
: this.corelib()[name + "$"];
}
toInternalName(name) {
let base = name;
if (name.c instanceof Function) {
name = name.c();
}
let str = "Ω" + this.strip(name).split(".").join("__");
let nr = this.incr(str);
if (nr > 1) {
str += "Ω" + nr;
}
// Include something for sourcemapping?
return str;
}
toInternalClassName(name) {
if (name.toClassName) {
name = name.toClassName();
} else if (name.c instanceof Function) {
name = name.c();
}
let stripped = this.strip(name);
let str = "Ω" + this.strip(name).split(".").join("__");
let nr = this.incr(str);
if (nr > 1) {
str += "Ω" + nr;
}
return str;
}
domCall(name) {
if (true) {
name =
{
start: "beforeVisit",
end: "afterVisit",
open: "beforeReconcile",
close: "afterReconcile",
insert: "placeChild",
}[name] || name;
return "[" + this.symbolFor("#" + name) + "]";
}
}
sourceId() {
if (this._sourceId || (this._sourceId = this._options.sourceId)) {
return this._sourceId;
}
let src = this.sourcePath();
let cwd = this.cwd();
// relativize the cwd thing
// TODO rename+document this option. sourceBase or sourceRoot?
if (this._options.path && cwd) {
src = this._options.path.relative(cwd, src);
}
if (!src) {
throw new Error(
"Include sourceId or sourcePath in options compile(code,options)",
);
}
this._sourceId = helpers.identifierForPath(src);
return this._sourceId;
}
theme() {
return this._theme || (this._theme = StyleTheme.wrap(this._options.config));
}
set(obj) {
this._options || (this._options = {});
for (
let v, i = 0, keys = Object.keys(obj), l = keys.length, k;
i < l;
i++
) {
k = keys[i];
v = obj[k];
this._options[k] = v;
}
return this;
}
option(key, val) {
if (val != undefined) {
this._options || (this._options = {});
this._options[key] = val;
return this;
}
return this._options && this._options[key];
}
platform() {
return this._options.platform || "browser";
}
mode() {
return this._options.mode || "production";
}
format() {
return this._options.format;
}
sourcePath() {
return this._options.sourcePath;
}
imbaPath() {
return this._options.imbaPath;
}
resolveColors() {
return this._options.styles !== "extern" || this._options.resolveColors;
}
config() {
return this._options.config || {};
}
cwd() {
return this.config() && this.config().cwd;
}
tsc() {
return this.platform() == "tsc" || this._options.tsc;
}
hmr() {
return !!this._options.hmr;
}
isStdLib() {
return !!this._options.stdlib;
}
isWeb() {
return this.platform() == "browser" || this.platform() == "web";
}
isWorker() {
return this.platform() == "worker";
}
isNode() {
return this.platform() == "node";
}
isDev() {
return this.mode() == "development";
}
isProd() {
return this.mode() == "production";
}
env(key) {
var e;
var val = this._options["ENV_" + key];
if (val != undefined) {
return val;
}
if (F[key] !== undefined) {
return F[key];
}
var lowercased = key.toLowerCase();
if (this._options[lowercased] != undefined) {
return this._options[lowercased];
}
if (key == "WEB" || key == "BROWSER") {
this._meta.universal = false;
return this.isWeb();
} else if (key == "NODE") {
this._meta.universal = false;
return this.isNode();
} else if (key == "DEV") {
return this.isDev();
} else if (key == "PROD") {
return this.isProd();
} else if (key == "NODEISH") {
this._meta.universal = false;
return this.isNode() || !!this.tsc();
} else if (key == "TSC") {
return this.tsc();
} else if (key == "WORKER") {
this._meta.universal = false;
return this.platform() && this.platform().indexOf("worker") >= 0;
} else if (key == "WEBWORKER") {
this._meta.universal = false;
return this.platform() == "webworker";
} else if (key == "HMR") {
return !!this._options.hmr;
}
if ((e = this._options.env)) {
if (e.hasOwnProperty(key)) {
return e[key];
} else if (e.hasOwnProperty(key.toLowerCase())) {
return e[key.toLowerCase()];
}
}
if (true && typeof process != "undefined" && process.env) {
val = process.env[key.toUpperCase()];
if (val != undefined) {
return val;
}
return null;
}
return undefined;
}
addScope(scope) {
this._scopes.push(scope);
return this;
}
traverse(node) {
return this;
}
push(node) {
this._nodes.push(node);
// not sure if we have already defined a scope?
return this;
}
pop(node) {
this._nodes.pop(); // (node)
return this;
}
call(ctx, cb) {
let prev = CONTEXT;
CONTEXT = ctx;
let res = cb();
CONTEXT = prev;
return res;
}
get is_top_level() {
return this._nodes.length < 4;
}
parent() {
return this._nodes[this._nodes.length - 2];
}
current() {
return this._nodes[this._nodes.length - 1];
}
indexOf(test) {
if (test && test.prototype instanceof Node) {
var i = this._nodes.length - 2;
while (i >= 0) {
if (this._nodes[i] instanceof test) {
return i;
}
i -= 1;
}
return -1;
}
let res = this.up(test);
return res ? this._nodes.indexOf(res) : -1;
}
up(test) {
if (typeof test == "number") {
return this._nodes[this._nodes.length - (1 + test)];
}
var i = this._nodes.length - 2;
var node;
if (!test) {
while (i >= 0) {
node = this._nodes[i--];
if (!(node instanceof VarOrAccess)) {
return node;
}
}
return null;
}
if (test.prototype instanceof Node) {
while (i >= 0) {
node = this._nodes[i--];
if (node instanceof test) {
return node;
}
}
return null;
}
while (i >= 0) {
node = this._nodes[i];
if (test(node)) {
return node;
}
i -= 1;
}
return null;
}
parents(test) {
test ||
(test = function (v) {
return !(v instanceof VarOrAccess);
});
if (test.prototype instanceof Node) {
let cls = test;
test = function (v) {
return v instanceof cls;
};
}
return this._nodes.filter(test);
}
relative(node, offset) {
if (offset === undefined) offset = 0;
var idx = this._nodes.indexOf(node);
return idx >= 0 ? this._nodes[idx + offset] : null;
}
scope(lvl) {
if (lvl === undefined) lvl = 0;
if (this._withScope) {
return this._withScope;
}
var i = this._nodes.length - 1 - lvl;
while (i >= 0) {
var node = this._nodes[i];
if (node._scope) {
return node._scope;
}
i -= 1;
}
return null;
}
withScope(scop, cb) {
let prev = this._withScope;
this._withScope = scop;
cb();
this._withScope = prev;
return;
}
scopes() {
// include deeper scopes as well?
var scopes = [];
var i = this._nodes.length - 1;
while (i >= 0) {
var node = this._nodes[i];
if (node._scope) {
scopes.push(node._scope);
}
i -= 1;
}
return scopes;
}
closure() {
return this.scope().closure();
}
closures() {
return this.scopes().filter(function (scope) {
return scope.closure() == scope;
});
}
method() {
return this.up(MethodDeclaration);
}
block() {
return this.up(Block);
}
blockpart() {
let i = this._nodes.length - 1;
while (i) {
if (this._nodes[i - 1] instanceof Block) {
return this._nodes[i];
}
i--;
}
return;
}
prependInBlock(node) {
return this.block().add([node, BR], { before: this.blockpart() });
}
lastImport() {
let scopes = this._scopes;
for (
let i = 0, items = iter$(scopes), len = items.length, scope;
i < len;
i++
) {
scope = items[i];
if (scope._lastImport) {
return scope._lastImport;
}
}
return null;
}
isExpression() {
var i = this._nodes.length - 1;
while (i >= 0) {
var node = this._nodes[i];
// why are we not using isExpression here as well?
if (
node instanceof Code ||
node instanceof Loop ||
node.isStatementLike()
) {
return false;
}
if (node.isExpression()) {
return true;
}
// probably not the right test - need to be more explicit
i -= 1;
}
return false;
}
toString() {
return "Stack(" + this._nodes.join(" -> ") + ")";
}
scoping() {
return this._nodes
.filter(function (n) {
return n._scope;
})
.map(function (n) {
return n._scope;
});
}
currentRegion() {
let l = this._nodes.length;
let node = this._nodes[--l];
return node && [node.startLoc(), node.endLoc()];
}
}
// get and set
// Lots of globals -- really need to deal with one stack per file / context
var STACK = new Stack();
// use a bitmask for these
class Node {
constructor() {
if (new.target == Node) {
this.setup();
}
this;
}
script() {
// TODO don't use global state for this
return Compilation.current;
}
safechain() {
return false;
}
addEnv(env) {
this._envs || (this._envs = []);
this._envs.push(new EnvFlag(env));
return this;
}
isExcluded() {
return false;
}
sourcecode() {
let src = STACK.SOURCECODE;
let start = this.startLoc();
let end = this.endLoc();
return src.slice(start, end);
}
oid() {
return this._oid || (this._oid = STACK.generateId(""));
}
tid() {
return this._tid || (this._tid = STACK.generateId("tag"));
}
osym(ns, name) {
if (ns === undefined) ns = "";
if (name === undefined) name = "";
return STACK.getSymbol(this.oid() + ns, null, name);
}
symbolRef(name) {
return STACK.root().symbolRef(name);
}
domCall(name) {
return STACK.domCall(name);
}
gsym(name) {
return STACK.root().symbolRef(name);
}
sourceId() {
return STACK.sourceId();
}
slf() {
return this.scope__().context();
}
p() {
// allow controlling this from CLI
if (STACK._loglevel > 0) {
console.log.apply(console, arguments);
}
return this;
}
runtime() {
return STACK.runtime();
}
typeName() {
return this.constructor.name;
}
namepath() {
return this.typeName();
}
setup() {
this._expression = false;
this._traversed = false;
this._parens = false;
this._cache = null;
this._value = null;
return this;
}
setRegion(loc) {
if (loc instanceof Node) {
loc = loc.region();
}
if (loc instanceof Array) {
this._startLoc = loc[0];
this._endLoc = loc[1];
}
return this;
}
setEnds(start, end) {
if (end && end.endLoc) {
this._endLoc = end.endLoc();
}
if (start && start.startLoc) {
this._startLoc = start.startLoc();
}
return this;
}
startLoc() {
return this._startLoc;
}
endLoc() {
return this._endLoc;
}
set(obj) {
this._options || (this._options = {});
for (
let v, i = 0, keys = Object.keys(obj), l = keys.length, k;
i < l;
i++
) {
k = keys[i];
v = obj[k];
this._options[k] = v;
}
return this;
}
option(key, val) {
if (val != undefined) {
this._options || (this._options = {});
this._options[key] = val;
return this;
}
return this._options && this._options[key];
}
o() {
return this._options || (this._options = {});
}
keyword() {
return this._keyword || (this._options && this._options.keyword);
}
datatype() {
return this._options ? this._options.datatype : null;
}
setDatatype(val) {
this.option("datatype", val);
return this;
}
configure(obj) {
return this.set(obj);
}
region() {
return [0, 0];
}
loc() {
return [this.startLoc() || 0, this.endLoc() || 0];
}
token() {
return null;
}
compile() {
return this;
}
visit() {
return this;
}
stack() {
return STACK;
}
isString() {
return false;
}
isPrimitive(deep) {
return false;
}
isReserved() {
return false;
}
isGlobal(name) {
return false;
}
isConstant() {
return false;
}
traverse(o) {
if (this._traversed) {
return this;
}
// NODES.push(self)
this._traversed = true;
let prev;
if (o) {
prev = STACK._state;
((STACK._state = o), STACK);
}
STACK.push(this);
this.visit(STACK, STACK._state);
STACK.pop(this);
if (o) {
((STACK._state = prev), STACK);
}
return this;
}
inspect() {
return { type: this.constructor.toString() };
}
js(o) {
return "NODE";
}
toString() {
return "" + this.constructor.name;
}
consume(node) {
if (node instanceof TagLike) {
return node.register(this);
}
if (node instanceof PushAssign) {
node.register(this);
return new PushAssign(node.op(), node._left, this);
}
if (node instanceof Assign) {
// node.right = self
return OP(node.op(), node._left, this);
} else if (node instanceof VarDeclaration) {
return OP("=", node._left, this);
} else if (node instanceof Op) {
return OP(node.op(), node._left, this);
} else if (node instanceof Return) {
return new Return(this);
} else if (node == NumberLike) {
return new NumberLike(this);
} else if (node instanceof Util.Is) {
return node.clone(this);
} else if (node instanceof AmperWalker) {
node.test(this);
}
return this;
}
toExpression() {
this._expression = true;
return this;
}
forceExpression() {
this._expression = true;
return this;
}
isExpressable() {
return true;
}
isExpression() {
return this._expression || false;
}
isStatementLike() {
return false;
}
isRuntimeReference() {
return false;
}
hasSideEffects() {
return true;
}
isUsed() {
return true;
}
shouldParenthesize() {
return false;
}
shouldParenthesizeInTernary() {
return true;
}
block() {
return Block.wrap([this]);
}
node() {
return this;
}
unwrappedNode() {
return this;
}
scope__() {
return STACK.scope();
}
up() {
return STACK.parent();
}
util() {
return Util;
}
receiver() {
return this;
}
indented(a, b) {
if (a instanceof Indentation) {
this._indentation = a;
return this;
}
// this is a _BIG_ hack
if (b instanceof Array) {
this.add(b[0]);
b = b[1];
}
// if indent and indent.match(/\:/)
this._indentation ||
(this._indentation = a && b ? new Indentation(a, b) : INDENT);
return this;
}
prebreak(term) {
if (term === undefined) term = "\n";
return this;
}
invert() {
return OP("!", this);
}
cache(o) {
if (o === undefined) o = {};
this._cache = o;
o.var = (o.scope || this.scope__()).temporary(this, o);
o.lookups = 0;
return this;
}
cachevar() {
return this._cache && this._cache.var;
}
decache() {
if (this._cache) {
this.cachevar().free();
this._cache = null;
}
return this;
}
alias() {
return null;
}
mo(val, optional) {
if (optional === undefined) optional = false;
let src = this._options && this._options[val];
return optional && !src ? "" : M(val, src);
}
warn(message, opts) {
if (opts === undefined) opts = {};
let loc = opts.loc || this.loc() || [0, 0];
if (loc instanceof Node) {
loc = [loc.startLoc(), loc.endLoc()];
}
if (loc instanceof Token) {
loc = loc.loc();
}
// if loc[0] == 0 and loc[1] == 0
// console.log 'loc warn',loc,script.rangeAt(loc[0],loc[1])
return this.script().addDiagnostic(opts.severity || "warning", {
message: message,
range: this.script().rangeAt(loc[0], loc[1]),
});
// Compilation.warn(
// severity: opts:severity or 'warning'
// message: message
// offset: (loc ? loc[0] : 0)
// length: (loc ? (loc[1] - loc[0]) : 0)
// )
}
error(message, opts) {
if (opts === undefined) opts = {};
opts.severity = "error";
return this.warn(message, opts);
}
c(o) {
var indent;
var s = STACK;
var ch = this._cache;
if (ch && ch.cached) {
return this.c_cached(ch);
}
if (
!o &&
!ch &&
!this._indentation &&
this.shouldParenthesize == Node.prototype.shouldParenthesize &&
!(this._options && this._options.braces) &&
!OPTS.sourcemap
) {
s.push(this);
let out = this.js(s);
s.pop(this);
return out;
}
s.push(this);
if (o && o.expression) this.forceExpression();
if (o && o.indent) {
this._indentation || (this._indentation = INDENT);
}
var out = this.js(s, o);
var shouldParenthesize = this.shouldParenthesize;
var paren =
shouldParenthesize == Node.prototype.shouldParenthesize
? false
: shouldParenthesize.call(this);
s.pop(this);
if (out == undefined) {
return out;
}
if ((indent = this._indentation)) {
out = indent.wrap(out, o);
this;
}
// should move this somewhere else really
if (paren) {
out = "(" + out + ")";
}
if ((o && o.braces) || (this._options && this._options.braces)) {
if (indent) {
out = "{" + out + "}";
} else {
out = "{ " + out + " }";
}
}
if ((ch = this._cache)) {
if (!ch.manual) {
out = "" + ch.var.c() + " = " + out;
}
var par = s.current();
if (par instanceof ValueNode) {
par = par.node();
}
if (par instanceof Access || par instanceof Op) {
out = "(" + out + ")";
} // others? #
ch.cached = true;
}
if (OPTS.sourcemap && (!o || o.mark !== false)) {
out = M(out, this);
}
return out;
}
c_cached(cache) {
cache.lookups++;
if (cache.uses == cache.lookups) {
cache.var.free();
}
return cache.var.c(); // recompile every time??
}
}
// reference to the script object this node
// is part of
// get global symbol with name
// shorthand for the self context for a node
// get and set
// should rather do traversals
// o = {}, up, key, index
// swallow might be better name
// the "name-suggestion" for nodes if they need to be cached
// Shorthand for outputting sourcemapped keywords where it looks
// for an option with the same name in options
class ValueNode extends Node {
constructor(value) {
super(...arguments);
this.setup();
this._value = this.load(value);
}
value(v) {
return this._value;
}
startLoc() {
let loc = this._startLoc;
return typeof loc == "number"
? loc
: this._value && this._value.startLoc
? this._value.startLoc()
: -1;
}
load(value) {
return value;
}
js(o) {
return typeof this._value == "string" ? this._value : this._value.c();
}
visit() {
if (this._value instanceof Node) {
this._value.traverse();
} // && @value:traverse
return this;
}
region() {
return [this._value._loc, this._value._loc + this._value._len];
}
}
class ValueReferenceNode extends Node {
constructor(value, orig) {
super(...arguments);
this.setup();
this._value = value;
this._orig = orig || value;
}
value(v) {
return this._value;
}
startLoc() {
return this._orig && this._orig.startLoc && this._orig.startLoc();
}
endLoc() {
return this._orig && this._orig.endLoc && this._orig.endLoc();
}
load(value) {
return value;
}
js(o) {
let res = M(this._value.c({ mark: false }), this);
return res;
}
visit() {
if (this._value instanceof Node) {
this._value.traverse();
} // && @value:traverse
return this;
}
region() {
return [this._orig._loc, this._orig._loc + this._orig._len];
}
}
class ExpressionNode extends ValueNode {}
class AssertionNode extends ValueNode {
sourceFor(node) {
var ary;
let src = STACK.SOURCECODE;
var ary = iter$(node.loc());
let start = ary[0],
end = ary[1];
return JSON.stringify(src.slice(start, end));
}
isInspectableBinary(op) {
return (
op instanceof Op &&
!(op instanceof Access) &&
!(op instanceof UnaryOp) &&
!op.isLogical() &&
!op.isAssignment() &&
op._left &&
op._right
);
}
js(o) {
let op = this._value;
let out = [];
if (op instanceof Assign) {
let osrc = this.sourceFor(op);
out.push(
"globalThis.IMBA_ASSERT=\{type:'assignment',source:" + osrc + "\}",
);
out.push(op.c(o));
} else if (this.isInspectableBinary(op)) {
let l = op._left;
let r = op._right;
let osrc = this.sourceFor(op);
let lsrc = this.sourceFor(l);
let rsrc = this.sourceFor(r);
let oval = JSON.stringify(op._op);
let lval = l.cache().c(o);
let rval = r.cache().c(o);
out = [
"source:" + osrc,
"operator:" + oval,
"left:\{source:" + lsrc + ",value:" + lval + "\}",