"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ValueScope = exports.ValueScopeName = exports.Scope = exports.varKinds = exports.UsedValueState = void 0; const code_1 = require("./code"); class ValueError extends Error { constructor(name) { super(`CodeGen: "code" for ${name} not defined`); this.value = name.value; } } var UsedValueState; (function (UsedValueState) { UsedValueState[UsedValueState["Started"] = 0] = "Started"; UsedValueState[UsedValueState["Completed"] = 1] = "Completed"; })(UsedValueState = exports.UsedValueState || (exports.UsedValueState = {})); exports.varKinds = { const: new code_1.Name("const"), let: new code_1.Name("let"), var: new code_1.Name("var"), }; class Scope { constructor({ prefixes, parent } = {}) { this._names = {}; this._prefixes = prefixes; this._parent = parent; } toName(nameOrPrefix) { return nameOrPrefix instanceof code_1.Name ? nameOrPrefix : this.name(nameOrPrefix); } name(prefix) { return new code_1.Name(this._newName(prefix)); } _newName(prefix) { const ng = this._names[prefix] || this._nameGroup(prefix); return `${prefix}${ng.index++}`; } _nameGroup(prefix) { var _a, _b; if (((_b = (_a = this._parent) === null || _a === void 0 ? void 0 : _a._prefixes) === null || _b === void 0 ? void 0 : _b.has(prefix)) || (this._prefixes && !this._prefixes.has(prefix))) { throw new Error(`CodeGen: prefix "${prefix}" is not allowed in this scope`); } return (this._names[prefix] = { prefix, index: 0 }); } } exports.Scope = Scope; class ValueScopeName extends code_1.Name { constructor(prefix, nameStr) { super(nameStr); this.prefix = prefix; } setValue(value, { property, itemIndex }) { this.value = value; this.scopePath = code_1._ `.${new code_1.Name(property)}[${itemIndex}]`; } } exports.ValueScopeName = ValueScopeName; const line = code_1._ `\n`; class ValueScope extends Scope { constructor(opts) { super(opts); this._values = {}; this._scope = opts.scope; this.opts = { ...opts, _n: opts.lines ? line : code_1.nil }; } get() { return this._scope; } name(prefix) { return new ValueScopeName(prefix, this._newName(prefix)); } value(nameOrPrefix, value) { var _a; if (value.ref === undefined) throw new Error("CodeGen: ref must be passed in value"); const name = this.toName(nameOrPrefix); const { prefix } = name; const valueKey = (_a = value.key) !== null && _a !== void 0 ? _a : value.ref; let vs = this._values[prefix]; if (vs) { const _name = vs.get(valueKey); if (_name) return _name; } else { vs = this._values[prefix] = new Map(); } vs.set(valueKey, name); const s = this._scope[prefix] || (this._scope[prefix] = []); const itemIndex = s.length; s[itemIndex] = value.ref; name.setValue(value, { property: prefix, itemIndex }); return name; } getValue(prefix, keyOrRef) { const vs = this._values[prefix]; if (!vs) return; return vs.get(keyOrRef); } scopeRefs(scopeName, values = this._values) { return this._reduceValues(values, (name) => { if (name.scopePath === undefined) throw new Error(`CodeGen: name "${name}" has no value`); return code_1._ `${scopeName}${name.scopePath}`; }); } scopeCode(values = this._values, usedValues, getCode) { return this._reduceValues(values, (name) => { if (name.value === undefined) throw new Error(`CodeGen: name "${name}" has no value`); return name.value.code; }, usedValues, getCode); } _reduceValues(values, valueCode, usedValues = {}, getCode) { let code = code_1.nil; for (const prefix in values) { const vs = values[prefix]; if (!vs) continue; const nameSet = (usedValues[prefix] = usedValues[prefix] || new Map()); vs.forEach((name) => { if (nameSet.has(name)) return; nameSet.set(name, UsedValueState.Started); let c = valueCode(name); if (c) { const def = this.opts.es5 ? exports.varKinds.var : exports.varKinds.const; code = code_1._ `${code}${def} ${name} = ${c};${this.opts._n}`; } else if ((c = getCode === null || getCode === void 0 ? void 0 : getCode(name))) { code = code_1._ `${code}${c}${this.opts._n}`; } else { throw new ValueError(name); } nameSet.set(name, UsedValueState.Completed); }); } return code; } } exports.ValueScope = ValueScope; //# sourceMappingURL=scope.js.map