UNPKG

@sudoo/marked

Version:

JavaScript & TypeScript code runner in JavaScript, safe with marked territory, asynchronous

224 lines (223 loc) 6.95 kB
"use strict"; /** * @author WMXPY * @namespace Variable * @description Scope */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Scope = exports.EXECUTE_SCOPE_SYMBOL = exports.BRIDGE_SCOPE_SYMBOL = void 0; const error_code_1 = require("../declare/error-code"); const variable_1 = require("../declare/variable"); const sand_to_native_1 = require("../parse/sand-to-native"); const error_1 = require("../util/error/error"); const variable_2 = require("../variable/variable"); const sand_map_1 = require("./sand-map"); exports.BRIDGE_SCOPE_SYMBOL = Symbol("bridge-scope"); exports.EXECUTE_SCOPE_SYMBOL = Symbol("execute-scope"); class Scope { static bridgeScope() { const scope = new Scope(undefined, exports.BRIDGE_SCOPE_SYMBOL); scope.initThis(); return scope; } static executeScope(parent) { if (!parent.isBridgeScope()) { throw (0, error_1.error)(error_code_1.ERROR_CODE.INTERNAL_ERROR, "only bridge scope can create execute scope"); } const scope = new Scope(parent, exports.EXECUTE_SCOPE_SYMBOL); scope.initThis(); return scope; } constructor(scope, symbol) { this._symbol = symbol; this._parent = scope || null; this._defaultExposed = undefined; this._exposed = new Map(); this._labelListeners = new Map(); this._constantMap = new Map(); this._scopeMap = new Map(); this._configs = new Map(); this._throwValue = null; this._this = null; } get constantMap() { return this._constantMap; } get scopeMap() { return this._scopeMap; } get exposed() { if (this.hasParent()) { const parent = this.ensureParent(); if (!parent.isBridgeScope()) { return parent.exposed; } } const namedObject = {}; for (const key of this._exposed.keys()) { namedObject[key] = this._exposed.get(key); } const result = { default: this._defaultExposed, named: namedObject, }; return result; } isBridgeScope() { return this._symbol === exports.BRIDGE_SCOPE_SYMBOL; } isExecuteScope() { return this._symbol === exports.EXECUTE_SCOPE_SYMBOL; } config(name, value) { this._configs.set(name, value === undefined ? true : value); return this; } child() { return new Scope(this); } findThis() { if (this._this) { return this._this; } else { if (this._parent) { return this._parent.findThis(); } throw (0, error_1.error)(error_code_1.ERROR_CODE.UNKNOWN_ERROR, "this"); } } initThis() { this._this = sand_map_1.SandMap.fromScratch(); return this; } replaceThis(thisValue) { this._this = thisValue; return this; } exist(name) { if (this._scopeMap.has(name)) { return true; } if (this._constantMap.has(name)) { return true; } return false; } hasParent() { return this._parent !== null; } ensureParent() { if (this._parent) { return this._parent; } throw (0, error_1.error)(error_code_1.ERROR_CODE.INTERNAL_ERROR); } register(type) { if (type === variable_1.VARIABLE_TYPE.VARIABLE) { throw (0, error_1.error)(error_code_1.ERROR_CODE.VAR_DECLARATION_NOT_SUPPORT); } return type === variable_1.VARIABLE_TYPE.CONSTANT ? this._declareConst.bind(this) : this._declareLet.bind(this); } rummage(name) { if (this._scopeMap.has(name)) { return this._scopeMap.get(name); } if (this._constantMap.has(name)) { return this._constantMap.get(name); } return this._parent ? this._parent.rummage(name) : null; } registerLabelListener(label, listener) { this._labelListeners.set(label, listener); return this; } executeLabelListener(label, type) { if (this._labelListeners.has(label)) { const listener = this._labelListeners.get(label); listener(type); return true; } if (this._parent) { return this._parent.executeLabelListener(label, type); } return false; } validateEditable(name) { if (this._constantMap.has(name)) { throw (0, error_1.error)(error_code_1.ERROR_CODE.CONSTANT_VARIABLE_CANNOT_BE_EDITED, name); } if (this._scopeMap.has(name)) { return this; } if (!this._parent) { throw (0, error_1.error)(error_code_1.ERROR_CODE.VARIABLE_IS_NOT_DEFINED, name); } this._parent.validateEditable(name); return this; } expose(key, value, trace) { if (this.hasParent()) { const parent = this.ensureParent(); if (!parent.isBridgeScope()) { parent.expose(key, value, trace); return this; } } if (!trace.scriptLocation.isRoot()) { this._exposed.set(key, value); } else { const extractedValue = (0, sand_to_native_1.extractSandToNative)(value); this._exposed.set(key, extractedValue); } return this; } exposeDefault(value, trace) { if (this.hasParent()) { const parent = this.ensureParent(); if (!parent.isBridgeScope()) { parent.exposeDefault(value, trace); return this; } } if (!trace.scriptLocation.isRoot()) { this._defaultExposed = value; } else { const extractedValue = (0, sand_to_native_1.extractSandToNative)(value); this._defaultExposed = extractedValue; } return this; } setThrow(value) { const variable = variable_2.Variable.immutable(value); this._throwValue = variable; return this; } hasThrow() { return this._throwValue !== null; } getThrow() { return this._throwValue; } _declareConst(name, value) { if (this.exist(name)) { throw (0, error_1.error)(error_code_1.ERROR_CODE.DUPLICATED_VARIABLE); } const variable = variable_2.Variable.immutable(value); this._constantMap.set(name, variable); return this; } _declareLet(name, value) { if (this.exist(name)) { throw (0, error_1.error)(error_code_1.ERROR_CODE.DUPLICATED_VARIABLE); } const variable = variable_2.Variable.mutable(value); this._scopeMap.set(name, variable); return this; } } exports.Scope = Scope;