@maniascript/parser
Version:
Maniascript parser
251 lines (250 loc) • 9.88 kB
JavaScript
import { Node, VariableDeclaration, Identifier, Main, FunctionDeclaration, FunctionParameterDeclaration, BlockStatement, ForStatement, Kind, ForeachStatement, WhileStatement, MeanwhileStatement, SwitchCase, SwitchtypeCase, ConditionalBranch, AssignmentStatement, LabelStatement, LabelDeclaration } from './ast.js';
class ScopeManager {
scopes;
nodeToScope;
#currentScope;
constructor() {
this.scopes = [];
this.nodeToScope = new Map();
this.#currentScope = null;
}
openScope(type, node) {
const scope = new Scope(type, node, this.#currentScope);
this.#currentScope = scope;
this.scopes.push(scope);
this.nodeToScope.set(node, scope);
}
closeScope() {
this.#currentScope = this.#currentScope !== null ? this.#currentScope.parent : null;
}
getScope(node) {
let scope = null;
let currentNode = node;
while (scope === null && currentNode !== undefined) {
scope = this.nodeToScope.get(currentNode) ?? null;
currentNode = currentNode.parent;
}
return scope;
}
static canCreateBlockScope(blockStatement) {
return (blockStatement.parent === undefined || (blockStatement.parent.kind !== Kind.FunctionDeclaration &&
blockStatement.parent.kind !== Kind.Main &&
blockStatement.parent.kind !== Kind.ForeachStatement &&
blockStatement.parent.kind !== Kind.ForStatement &&
blockStatement.parent.kind !== Kind.WhileStatement &&
blockStatement.parent.kind !== Kind.MeanwhileStatement &&
blockStatement.parent.kind !== Kind.SwitchCase &&
blockStatement.parent.kind !== Kind.SwitchtypeCase &&
blockStatement.parent.kind !== Kind.ConditionalBranch));
}
analyze(ast) {
this.scopes = [];
this.nodeToScope.clear();
if (ast.program !== undefined) {
this.openScope(ScopeType.Global, ast.program);
ast.program.visit((node) => {
if (node instanceof VariableDeclaration) {
if (node.alias !== undefined) {
this.#currentScope?.defineVariable(node.alias, { isGlobal: node.isGlobal, isTrait: node.forTarget !== undefined });
}
else {
this.#currentScope?.defineVariable(node.name, { isGlobal: node.isGlobal, isTrait: node.forTarget !== undefined });
}
}
else if (node instanceof Main || node instanceof FunctionDeclaration) {
this.openScope(ScopeType.Function, node);
}
else if (node instanceof FunctionParameterDeclaration) {
this.#currentScope?.defineVariable(node.name, { isFunctionParameter: true });
}
else if (node instanceof ForStatement) {
this.openScope(ScopeType.For, node);
this.#currentScope?.defineVariable(node.value, { isForValue: true });
}
else if (node instanceof ForeachStatement) {
this.openScope(ScopeType.Foreach, node);
if (node.key !== undefined) {
this.#currentScope?.defineVariable(node.key, { isForeachKey: true });
}
this.#currentScope?.defineVariable(node.value, { isForeachValue: true });
}
else if (node instanceof WhileStatement) {
this.openScope(ScopeType.While, node);
}
else if (node instanceof MeanwhileStatement) {
this.openScope(ScopeType.Meanwhile, node);
}
else if (node instanceof SwitchCase || node instanceof SwitchtypeCase) {
this.openScope(ScopeType.SwitchCase, node);
}
else if (node instanceof ConditionalBranch) {
this.openScope(ScopeType.ConditionalBranch, node);
}
else if (node instanceof LabelDeclaration) {
this.openScope(ScopeType.LabelDeclaration, node);
}
else if (node instanceof BlockStatement && ScopeManager.canCreateBlockScope(node)) {
this.openScope(ScopeType.Block, node);
}
else if (node instanceof Identifier && node.isExpression) {
this.#currentScope?.referenceVariable(node);
}
}, (node) => {
if (node instanceof Main || node instanceof FunctionDeclaration) {
this.closeScope();
}
else if (node instanceof ForStatement) {
this.closeScope();
}
else if (node instanceof ForeachStatement) {
this.closeScope();
}
else if (node instanceof WhileStatement) {
this.closeScope();
}
else if (node instanceof MeanwhileStatement) {
this.closeScope();
}
else if (node instanceof SwitchCase || node instanceof SwitchtypeCase) {
this.closeScope();
}
else if (node instanceof ConditionalBranch) {
this.closeScope();
}
else if (node instanceof LabelDeclaration) {
this.closeScope();
}
else if (node instanceof BlockStatement && ScopeManager.canCreateBlockScope(node)) {
this.closeScope();
}
else if (node instanceof LabelStatement) {
this.#currentScope?.referenceLabel();
}
});
this.closeScope();
}
}
}
var ScopeType;
(function (ScopeType) {
ScopeType["Global"] = "Global";
ScopeType["Function"] = "Function";
ScopeType["For"] = "For";
ScopeType["Foreach"] = "Foreach";
ScopeType["While"] = "While";
ScopeType["Meanwhile"] = "Meanwhile";
ScopeType["SwitchCase"] = "SwitchCase";
ScopeType["ConditionalBranch"] = "ConditionalBranch";
ScopeType["LabelDeclaration"] = "LabelDeclaration";
ScopeType["Block"] = "Block";
})(ScopeType || (ScopeType = {}));
class Variable {
name;
references;
node;
isGlobal;
isFunctionParameter;
isForValue;
isForeachKey;
isForeachValue;
isTrait;
static getNameFromIdentifier(identifier) {
return identifier.namespace !== undefined ? `${identifier.namespace}::${identifier.name}` : identifier.name;
}
constructor(node, parameters) {
this.name = Variable.getNameFromIdentifier(node);
this.references = [];
this.node = node;
this.isGlobal = parameters?.isGlobal ?? false;
this.isFunctionParameter = parameters?.isFunctionParameter ?? false;
this.isForValue = parameters?.isForValue ?? false;
this.isForeachKey = parameters?.isForeachKey ?? false;
this.isForeachValue = parameters?.isForeachValue ?? false;
this.isTrait = parameters?.isTrait ?? false;
}
}
var ReferenceFlag;
(function (ReferenceFlag) {
ReferenceFlag[ReferenceFlag["Read"] = 0] = "Read";
ReferenceFlag[ReferenceFlag["Write"] = 1] = "Write";
})(ReferenceFlag || (ReferenceFlag = {}));
class Reference {
variable;
node;
flag;
constructor(variable, node) {
this.variable = variable;
this.node = node;
if (node.parent !== undefined && node.parent instanceof AssignmentStatement && node.parent.left === node) {
this.flag = ReferenceFlag.Write;
}
else {
this.flag = ReferenceFlag.Read;
}
}
isRead() {
return this.flag === ReferenceFlag.Read;
}
isWrite() {
return this.flag === ReferenceFlag.Write;
}
}
class Scope {
type;
variables;
references;
couldReferenceVariableInLabel;
parent;
children;
node;
constructor(type, node, parent) {
this.type = type;
this.variables = new Map();
this.references = [];
this.couldReferenceVariableInLabel = false;
this.parent = parent ?? null;
this.children = [];
this.node = node;
if (this.parent !== null) {
this.parent.children.push(this);
}
}
defineVariable(node, parameters) {
const variable = new Variable(node, parameters);
this.variables.set(variable.name, variable);
return variable;
}
referenceVariable(node) {
const variable = this.getVariable(node);
const reference = new Reference(variable, node);
this.references.push(reference);
if (variable !== null) {
variable.references.push(reference);
}
return reference;
}
getVariable(name) {
let scope = this; // eslint-disable-line @typescript-eslint/no-this-alias
let variable = null;
let fullName;
if (typeof name === 'string') {
fullName = name;
}
else {
fullName = Variable.getNameFromIdentifier(name);
}
while (scope !== null && variable === null) {
variable = scope.variables.get(fullName) ?? null;
scope = scope.parent;
}
return variable;
}
referenceLabel() {
let scope = this; // eslint-disable-line @typescript-eslint/no-this-alias
while (scope !== null && !scope.couldReferenceVariableInLabel) {
scope.couldReferenceVariableInLabel = true;
scope = scope.parent;
}
}
}
export { ScopeManager, ScopeType, Variable, Scope };