UNPKG

@prism-lang/core

Version:

A programming language for uncertainty

289 lines 12.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.moduleSystem = exports.ModuleSystem = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const tokenizer_1 = require("./tokenizer"); const parser_1 = require("./parser"); const runtime_1 = require("./runtime"); const runtime_2 = require("./runtime"); class ModuleSystem { modules = new Map(); currentModulePath = null; fileReader; constructor(fileReader) { // Allow custom file reader for testing/different environments this.fileReader = fileReader || ((p) => fs.readFileSync(p, 'utf-8')); } /** * Resolve a module path relative to the current module */ resolvePath(importPath, fromPath) { // Handle relative paths if (importPath.startsWith('./') || importPath.startsWith('../')) { const dir = path.dirname(fromPath); let resolved = path.resolve(dir, importPath); // Add .prism extension if not present if (!resolved.endsWith('.prism')) { resolved += '.prism'; } return resolved; } // Handle absolute paths or node_modules (simplified for now) // In a real implementation, we'd search node_modules directories if (!importPath.endsWith('.prism')) { importPath += '.prism'; } return importPath; } /** * Load and execute a module */ async loadModule(modulePath, runtime) { // Check cache const existingModule = this.modules.get(modulePath); if (existingModule) { if (existingModule.isExecuting) { // Module is currently executing - this is a circular dependency // Return the module with its current (partial) exports return existingModule; } if (existingModule.isExecuted) { // Module is fully loaded return existingModule; } } // Create new module const module = { path: modulePath, exports: { named: new Map() }, environment: null, isExecuted: false, isExecuting: false }; // Add to cache before execution to handle circular deps this.modules.set(modulePath, module); // Execute module await this.executeModule(module, runtime); return module; } /** * Execute a module's code */ async executeModule(module, _parentRuntime) { module.isExecuting = true; try { // Read file const source = this.fileReader(module.path); // Parse const tokens = (0, tokenizer_1.tokenize)(source); const parser = new parser_1.Parser(tokens, source); const ast = parser.parse(); // Create module-specific runtime with its own environment const moduleRuntime = (0, runtime_2.createRuntime)(); // Store current module path for import resolution const previousModulePath = this.currentModulePath; this.currentModulePath = module.path; // Inject module system into runtime (need to access interpreter) const interpreter = moduleRuntime.interpreter; interpreter.__moduleSystem = this; interpreter.__currentModule = module; // Execute module await moduleRuntime.execute(ast); // Store module environment module.environment = interpreter.environment; // Restore previous module path this.currentModulePath = previousModulePath; module.isExecuted = true; module.isExecuting = false; } catch (error) { module.isExecuting = false; throw new Error(`Failed to load module ${module.path}: ${error instanceof Error ? error.message : String(error)}`); } } /** * Handle import statement execution */ async executeImport(importStmt, runtime) { const currentModule = runtime.__currentModule; if (!currentModule) { throw new Error('Import statements can only be used within modules'); } // Resolve module path const resolvedPath = this.resolvePath(importStmt.source, currentModule.path); // Load module - create a new runtime for it const importRuntime = (0, runtime_2.createRuntime)(); const importedModule = await this.loadModule(resolvedPath, importRuntime); // Special handling for circular dependencies const isCircular = importedModule.isExecuting && !importedModule.isExecuted; // Process import specifiers if (importStmt.defaultImport) { const defaultExport = importedModule.exports.default; if (!defaultExport && !isCircular) { throw new Error(`Module ${resolvedPath} has no default export`); } runtime.environment.define(importStmt.defaultImport, defaultExport || new runtime_1.UndefinedValue()); } // Handle namespace import (import * as name from 'module') if (importStmt.namespaceImport) { // Create an object with all exports const namespaceObj = {}; // Add default export if exists if (importedModule.exports.default) { namespaceObj.default = importedModule.exports.default; } // Add named exports for (const [name, value] of importedModule.exports.named) { namespaceObj[name] = value; } // Create object value from namespace const namespaceMap = new Map(); for (const [key, value] of Object.entries(namespaceObj)) { namespaceMap.set(key, value); } runtime.environment.define(importStmt.namespaceImport, new runtime_1.ObjectValue(namespaceMap)); } // Handle named imports for (const specifier of importStmt.specifiers) { const exportName = specifier.imported; const localName = specifier.local || specifier.imported; // Use local name if provided, otherwise use imported name const exportedValue = importedModule.exports.named.get(exportName); if (!exportedValue && !isCircular) { throw new Error(`Module ${resolvedPath} has no export named '${exportName}'`); } runtime.environment.define(localName, exportedValue || new runtime_1.UndefinedValue()); } } /** * Handle export statement execution */ async executeExport(exportStmt, runtime) { const currentModule = runtime.__currentModule; if (!currentModule) { throw new Error('Export statements can only be used within modules'); } // Handle default export if (exportStmt.isDefault && exportStmt.declaration) { const value = await runtime.interpret(exportStmt.declaration); currentModule.exports.default = value; return; } // Handle named exports from declaration if (exportStmt.declaration) { // Evaluate the declaration const value = await runtime.interpret(exportStmt.declaration); // Handle different declaration types if (exportStmt.declaration.type === 'AssignmentStatement') { // export name = value const assignment = exportStmt.declaration; if (assignment.identifier) { currentModule.exports.named.set(assignment.identifier, value); } } else if (exportStmt.declaration.type === 'VariableDeclaration') { // export const/let name = value // Variable declarations return 0, so we need to get the actual value const varDecl = exportStmt.declaration; if (varDecl.identifier) { // The variable has already been defined by interpreting the declaration const actualValue = runtime.environment.get(varDecl.identifier); currentModule.exports.named.set(varDecl.identifier, actualValue); } } else if (exportStmt.declaration.type === 'FunctionDeclaration') { // export function name() { ... } const funcDecl = exportStmt.declaration; if (funcDecl.name) { currentModule.exports.named.set(funcDecl.name, value); } } } // Handle export list (export { a, b, c }) if (exportStmt.specifiers) { for (const specifier of exportStmt.specifiers) { const localName = specifier.local; const exportName = specifier.exported || localName; let value; try { value = runtime.environment.get(localName); } catch (e) { throw new Error(`Cannot export '${localName}': variable not defined`); } currentModule.exports.named.set(exportName, value); } } // Handle re-exports (export { x } from './other') if (exportStmt.source) { const resolvedPath = this.resolvePath(exportStmt.source, currentModule.path); // Create a new runtime for the imported module const importRuntime = (0, runtime_2.createRuntime)(); const sourceModule = await this.loadModule(resolvedPath, importRuntime); if (!exportStmt.specifiers || exportStmt.specifiers.length === 0) { // export * from './other' // Re-export all named exports for (const [name, value] of sourceModule.exports.named) { currentModule.exports.named.set(name, value); } } else { // export { specific } from './other' for (const specifier of exportStmt.specifiers) { const importName = specifier.local; const exportName = specifier.exported || importName; const value = sourceModule.exports.named.get(importName); if (!value) { throw new Error(`Module ${resolvedPath} has no export named '${importName}'`); } currentModule.exports.named.set(exportName, value); } } } } /** * Clear module cache (useful for testing) */ clearCache() { this.modules.clear(); } } exports.ModuleSystem = ModuleSystem; // Create a singleton instance exports.moduleSystem = new ModuleSystem(); //# sourceMappingURL=module-system.js.map