import { ConditionDeclaration, MainAgent, Monitor, ProcedureDeclaration, ProcedureParameter, Struct, VariableDeclarator } from "@pseuco/lang"; import { PseucoError } from "../../PseucoError"; import { TypeEnvironment } from "./TypeEnvironment"; import { ClassType } from "types/ClassType"; // Environment that maps identifiers to procedures, variables and conditions. // Can be used in structs and monitors export class ProceduresEnvironment extends TypeEnvironment { private readonly procedures: Map = new Map(); private readonly variables: Map = new Map(); private readonly conditions: Map = new Map(); private mainAgent: MainAgent | undefined = undefined; constructor(parentEnvironment: TypeEnvironment, private readonly _enclosingClassDeclaration?: Monitor | Struct) { super(parentEnvironment); } override getDeclarationForVariableIdentifier(id: string): VariableDeclarator | ProcedureParameter | undefined { const myRes = this.variables.get(id); if (myRes != undefined) { return myRes; } else { return super.getDeclarationForVariableIdentifier(id); } } override getDeclarationForConditionIdentifier(id: string): ConditionDeclaration | undefined { const myRes = this.conditions.get(id); if (myRes != undefined) { return myRes; } else { return super.getDeclarationForConditionIdentifier(id); } } override getDeclarationForProcedureIdentifier(id: string): ProcedureDeclaration | undefined { if (id === "mainAgent") { throw Error("Procedures cannot be named 'mainAgent'."); } const myRes = this.procedures.get(id); if (myRes != undefined) { return myRes; } else { return super.getDeclarationForProcedureIdentifier(id); } } override getDeclarationForMainAgent(): MainAgent | undefined { return this.mainAgent; } override _addVariable(id: string, decl: VariableDeclarator | ProcedureParameter, calledOn: TypeEnvironment): TypeEnvironment { if (this.variables.has(id) || this.conditions.has(id)) { return (new PseucoError(decl, "Type", `'${id}' is already defined in scope.`)).throw(); } else { this.variables.set(id, decl); return calledOn; } } override _addCondition(id: string, decl: ConditionDeclaration, calledOn: TypeEnvironment): TypeEnvironment { if (this.conditions.has(id) || this.variables.has(id)) { return (new PseucoError(decl, "Type", `'${id}' is already defined in scope.`)).throw(); } else { this.conditions.set(id, decl); return calledOn; } } override _addProcedure(id: string, decl: ProcedureDeclaration, calledOn: TypeEnvironment): TypeEnvironment { if (id === "mainAgent") { return (new PseucoError(decl, "Type", "Procedures cannot be named 'mainAgent'.")).throw(); } const reservedNames = ["lock", "unlock", "signal", "signalAll", "join", "waitForCondition", "println", "assert", "while", "for", "do", "if", "select", "case", "break", "continue", "return"]; const match = reservedNames.indexOf(id); if (match != -1) { return (new PseucoError(decl, "Type", `${reservedNames[match]} is a pseuCo primitive and cannot be used as a procedure name.`)).throw(); } if (this.procedures.has(id)) { return (new PseucoError(decl, "Type", `Procedure '${id}' is already defined in scope.`)).throw(); } else { this.procedures.set(id, decl); return calledOn; } } protected override _setMainAgent(declaration: MainAgent, calledOn: TypeEnvironment): TypeEnvironment { if (this.mainAgent != null) { return (new PseucoError(declaration, "Type", "You can define only one mainAgent.")).throw(); } this.mainAgent = declaration; return calledOn; } override get enclosingClassDeclarator(): Monitor | Struct | undefined { return this._enclosingClassDeclaration; } }