@prism-lang/core
Version:
A programming language for uncertainty
289 lines • 12.4 kB
JavaScript
;
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