@sudoo/marked
Version:
JavaScript & TypeScript code runner in JavaScript, safe with marked territory, asynchronous
224 lines (223 loc) • 6.95 kB
JavaScript
"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;