// src/core/engine.js - Main Baba Yaga engine (optimized by default) import { BabaYagaConfig } from './config.js'; import { InputValidator, SecurityValidator } from './validation.js'; import { BabaError } from './error.js'; import { createLexer, createOptimizedLexer, createLexerWithFallback } from './lexer.js'; import { createParser } from './parser.js'; import { createInterpreter } from './interpreter.js'; import { ScopeStack, CompatibleScopeStack } from './scope-stack.js'; import { OptimizedBuiltins } from './builtins.js'; import { globalASTPool } from './ast-pool.js'; /** * Main Baba Yaga engine with optimizations enabled by default * This is the primary engine that should be used for all new code */ export class BabaYagaEngine { constructor(config = new BabaYagaConfig()) { this.config = config; this.validator = config.sandboxMode ? new SecurityValidator(config) : new InputValidator(config); // Initialize optimization components (enabled by default) this.optimizedBuiltins = new OptimizedBuiltins(); this.astPool = globalASTPool; // Performance tracking this.stats = { totalExecutions: 0, totalTime: 0, averageTime: 0, errors: 0, lexingTime: 0, parsingTime: 0, interpretingTime: 0, optimizationStats: { lexerOptimizations: 0, scopeOptimizations: 0, builtinOptimizations: 0, astPoolHits: 0 } }; // Warm up optimization components if enabled if (config.enableOptimizations) { this.warmUp(); } } /** * Warm up optimization components for better initial performance */ warmUp() { // Warm up AST pools this.astPool.warmUp('BinaryExpression', 50); this.astPool.warmUp('FunctionCall', 30); this.astPool.warmUp('Identifier', 100); this.astPool.warmUp('NumberLiteral', 50); // Warm up with a simple program const warmupCode = 'x : 1 + 2; y : x * 3;'; try { this.executeSync(warmupCode, { silent: true }); } catch (error) { // Ignore warmup errors } } /** * Execute Baba Yaga source code */ async execute(source, options = {}) { const startTime = performance.now(); try { // Validate input this.validator.validateSourceCode(source, options.filename || ''); // Lexical analysis (use legacy lexer by default due to critical bug in optimized version) const lexStart = performance.now(); const lexer = this.config.enableOptimizations ? await createLexerWithFallback(source, true) // Try optimized with fallback : await createLexerWithFallback(source, false); // Use legacy directly const tokens = lexer.allTokens(); const lexTime = performance.now() - lexStart; if (this.config.enableDebugMode) { console.log(`[DEBUG] Lexing: ${lexTime.toFixed(2)}ms, Tokens: ${tokens.length}`); } // Parsing with AST pooling const parseStart = performance.now(); const parser = this.createOptimizedParser(tokens, source); const ast = parser.parse(); const parseTime = performance.now() - parseStart; // Validate AST this.validator.validateAST(ast, source); if (this.config.enableDebugMode) { console.log(`[DEBUG] Parsing: ${parseTime.toFixed(2)}ms, AST depth: ${this.getASTDepth(ast)}`); } // Optimized interpretation const interpretStart = performance.now(); const host = this.createOptimizedHostInterface(source, options); const interpreter = this.createOptimizedInterpreter(ast, host); // Set up execution timeout const result = await this.executeWithTimeout(interpreter, host); const interpretTime = performance.now() - interpretStart; // Update statistics const executionTime = performance.now() - startTime; this.updateStats(executionTime, false, lexTime, parseTime, interpretTime); if (this.config.showTimings) { console.log(`[TIMING] Total: ${executionTime.toFixed(2)}ms (Lex: ${lexTime.toFixed(2)}ms, Parse: ${parseTime.toFixed(2)}ms, Interpret: ${interpretTime.toFixed(2)}ms)`); } // Clean up AST if pooling is enabled if (this.config.enableOptimizations) { this.astPool.releaseTree(ast); } return { result, executionTime, success: true, breakdown: { lexingTime: lexTime, parsingTime: parseTime, interpretingTime: interpretTime } }; } catch (error) { const executionTime = performance.now() - startTime; this.updateStats(executionTime, true); // Format error for display if (error instanceof BabaError) { const formattedError = this.config.verboseErrors ? error.formatError() : error.message; return { error: formattedError, errorType: error.name, executionTime, success: false, suggestions: error.suggestions }; } else { // Unexpected error if (this.config.enableDebugMode) { console.error('[INTERNAL ERROR]', error); } return { error: 'Internal error occurred', errorType: 'InternalError', executionTime, success: false, suggestions: ['Report this as a bug', 'Check for malformed input'] }; } } } /** * Synchronous execution for simple cases */ executeSync(source, options = {}) { // Simple implementation for sync cases let result; let error; this.execute(source, options).then( res => { result = res; }, err => { error = err; } ); // Simple busy wait (not recommended for production) const start = Date.now(); while (result === undefined && error === undefined && Date.now() - start < 1000) { // Wait } if (error) throw error; return result; } // [Include all the optimization methods from engine-optimized.js] createOptimizedParser(tokens, source) { const parser = createParser(tokens, this.config.enableDebugMode, source); if (this.config.enableOptimizations) { const originalParse = parser.parse.bind(parser); parser.parse = () => { const ast = originalParse(); this.stats.optimizationStats.astPoolHits += this.astPool.getStats().poolHits; return ast; }; } return parser; } createOptimizedInterpreter(ast, host) { const interpreter = createInterpreter(ast, host); if (this.config.enableOptimizations) { // Replace scope with optimized scope stack const originalScope = interpreter.scope; const optimizedScope = new CompatibleScopeStack(); // Copy existing scope data for (const [key, value] of originalScope.entries()) { optimizedScope.set(key, value); } interpreter.scope = optimizedScope; // Inject optimized built-ins this.injectOptimizedBuiltins(interpreter); } return interpreter; } injectOptimizedBuiltins(interpreter) { const originalVisitFunctionCall = interpreter.visitFunctionCall; interpreter.visitFunctionCall = (node) => { // Try optimized path first if (node.callee && node.callee.type === 'Identifier') { const functionName = node.callee.name; const args = node.arguments.map(arg => interpreter.visit(arg)); if (this.optimizedBuiltins.canOptimize(functionName, args)) { const result = this.optimizedBuiltins.execute(functionName, args, interpreter); if (result !== null) { this.stats.optimizationStats.builtinOptimizations++; return result; } } } // Fall back to standard implementation return originalVisitFunctionCall.call(interpreter, node); }; } createOptimizedHostInterface(source, options) { return { source, scope: options.scope || new Map(), io: { out: (...args) => { if (options.silent) return; if (options.onOutput) { options.onOutput(...args); } else { console.log(...args); } }, in: () => { if (options.onInput) { return options.onInput(); } else { throw new BabaError('Input not available in this context'); } }, emit: (event) => { if (options.onEvent) { options.onEvent(event); } }, addListener: (topic, handler) => { if (options.onAddListener) { return options.onAddListener(topic, handler); } return () => {}; }, debug: this.config.enableDebugMode ? console.log : () => {}, ...this.config.ioHandlers }, optimizations: this.config.enableOptimizations ? { builtins: this.optimizedBuiltins, astPool: this.astPool } : undefined }; } async executeWithTimeout(interpreter, host) { let timeoutId; const executionPromise = new Promise((resolve, reject) => { try { const result = interpreter.interpret(); resolve(result); } catch (error) { reject(error); } }); const timeoutPromise = new Promise((_, reject) => { timeoutId = setTimeout(() => { reject(new BabaError( `Execution timeout after ${this.config.maxExecutionTime}ms`, null, host.source, ['Reduce recursion depth', 'Optimize algorithm complexity', 'Increase maxExecutionTime'] )); }, this.config.maxExecutionTime); }); try { const result = await Promise.race([executionPromise, timeoutPromise]); clearTimeout(timeoutId); return result; } catch (error) { clearTimeout(timeoutId); throw error; } } getASTDepth(node, depth = 0) { if (!node || typeof node !== 'object') { return depth; } let maxDepth = depth; const childFields = ['body', 'left', 'right', 'operand', 'callee', 'arguments', 'elements', 'discriminants', 'cases']; for (const field of childFields) { const child = node[field]; if (child) { if (Array.isArray(child)) { for (const item of child) { maxDepth = Math.max(maxDepth, this.getASTDepth(item, depth + 1)); } } else { maxDepth = Math.max(maxDepth, this.getASTDepth(child, depth + 1)); } } } return maxDepth; } updateStats(executionTime, isError, lexTime = 0, parseTime = 0, interpretTime = 0) { this.stats.totalExecutions++; this.stats.totalTime += executionTime; this.stats.averageTime = this.stats.totalTime / this.stats.totalExecutions; this.stats.lexingTime += lexTime; this.stats.parsingTime += parseTime; this.stats.interpretingTime += interpretTime; if (isError) { this.stats.errors++; } } getStats() { const builtinStats = this.optimizedBuiltins.getStats(); const astPoolStats = this.astPool.getStats(); return { ...this.stats, errorRate: this.stats.totalExecutions > 0 ? this.stats.errors / this.stats.totalExecutions : 0, averageLexTime: this.stats.totalExecutions > 0 ? this.stats.lexingTime / this.stats.totalExecutions : 0, averageParseTime: this.stats.totalExecutions > 0 ? this.stats.parsingTime / this.stats.totalExecutions : 0, averageInterpretTime: this.stats.totalExecutions > 0 ? this.stats.interpretingTime / this.stats.totalExecutions : 0, optimizations: { builtinOptimizationRate: builtinStats.optimizationRate, astPoolHitRate: astPoolStats.hitRate, astPoolReuseRate: astPoolStats.reuseRate, totalOptimizations: this.stats.optimizationStats.builtinOptimizations + this.stats.optimizationStats.astPoolHits } }; } resetStats() { this.stats = { totalExecutions: 0, totalTime: 0, averageTime: 0, errors: 0, lexingTime: 0, parsingTime: 0, interpretingTime: 0, optimizationStats: { lexerOptimizations: 0, scopeOptimizations: 0, builtinOptimizations: 0, astPoolHits: 0 } }; this.optimizedBuiltins.resetStats(); this.astPool.resetStats(); } } /** * Convenience function for quick execution */ export async function execute(source, config = new BabaYagaConfig({ enableOptimizations: true })) { const engine = new BabaYagaEngine(config); return engine.execute(source); } /** * Create engine with preset configurations */ export function createEngine(preset = 'default') { let config; switch (preset) { case 'development': config = BabaYagaConfig.development(); config.enableOptimizations = true; break; case 'production': config = BabaYagaConfig.production(); config.enableOptimizations = true; break; case 'testing': config = BabaYagaConfig.testing(); config.enableOptimizations = true; break; case 'sandbox': config = BabaYagaConfig.sandbox(); config.enableOptimizations = true; break; default: config = new BabaYagaConfig({ enableOptimizations: true }); } return new BabaYagaEngine(config); }