diff options
Diffstat (limited to 'js/baba-yaga/src/legacy/engine.js')
-rw-r--r-- | js/baba-yaga/src/legacy/engine.js | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/js/baba-yaga/src/legacy/engine.js b/js/baba-yaga/src/legacy/engine.js new file mode 100644 index 0000000..6afece3 --- /dev/null +++ b/js/baba-yaga/src/legacy/engine.js @@ -0,0 +1,289 @@ +// engine.js - Main Baba Yaga engine with improved error handling and configuration + +import { createLexer } from './lexer.js'; +import { createParser } from './parser.js'; +import { createInterpreter } from './interpreter.js'; +import { BabaYagaConfig } from '../core/config.js'; +import { InputValidator, SecurityValidator } from '../core/validation.js'; +import { BabaError } from '../core/error.js'; + +/** + * Main Baba Yaga engine class + */ +export class BabaYagaEngine { + constructor(config = new BabaYagaConfig()) { + this.config = config; + this.validator = config.sandboxMode + ? new SecurityValidator(config) + : new InputValidator(config); + + // Performance tracking + this.stats = { + totalExecutions: 0, + totalTime: 0, + averageTime: 0, + errors: 0 + }; + } + + /** + * Execute Baba Yaga source code + */ + async execute(source, options = {}) { + const startTime = performance.now(); + + try { + // Validate input + this.validator.validateSourceCode(source, options.filename || '<input>'); + + // Lexical analysis + const lexer = createLexer(source); + const tokens = lexer.allTokens(); + + if (this.config.enableDebugMode) { + console.log('[DEBUG] Tokens:', tokens.length); + } + + // Parsing + const parser = createParser(tokens, this.config.enableDebugMode, source); + const ast = parser.parse(); + + // Validate AST + this.validator.validateAST(ast, source); + + if (this.config.enableDebugMode) { + console.log('[DEBUG] AST depth:', this.getASTDepth(ast)); + } + + // Interpretation + const host = this.createHostInterface(source, options); + const interpreter = createInterpreter(ast, host); + + // Set up execution timeout + 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, + source, + ['Reduce recursion depth', 'Optimize algorithm complexity', 'Increase maxExecutionTime'] + )); + }, this.config.maxExecutionTime); + }); + + const result = await Promise.race([executionPromise, timeoutPromise]); + clearTimeout(timeoutId); + + // Update statistics + const executionTime = performance.now() - startTime; + this.updateStats(executionTime, false); + + if (this.config.showTimings) { + console.log(`[TIMING] Execution completed in ${executionTime.toFixed(2)}ms`); + } + + return { + result, + executionTime, + success: true + }; + + } 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 + 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'] + }; + } + } + } + + /** + * Create host interface for interpreter + */ + createHostInterface(source, options) { + return { + source, + scope: options.scope || new Map(), + io: { + out: (...args) => { + 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 () => {}; // No-op unsubscribe + }, + debug: this.config.enableDebugMode ? console.log : () => {}, + ...this.config.ioHandlers + } + }; + } + + /** + * Get AST depth for validation + */ + getASTDepth(node, depth = 0) { + if (!node || typeof node !== 'object') { + return depth; + } + + let maxDepth = depth; + + // Check common AST node children + 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; + } + + /** + * Update execution statistics + */ + updateStats(executionTime, isError) { + this.stats.totalExecutions++; + this.stats.totalTime += executionTime; + this.stats.averageTime = this.stats.totalTime / this.stats.totalExecutions; + + if (isError) { + this.stats.errors++; + } + } + + /** + * Get engine statistics + */ + getStats() { + return { + ...this.stats, + errorRate: this.stats.totalExecutions > 0 ? this.stats.errors / this.stats.totalExecutions : 0 + }; + } + + /** + * Reset statistics + */ + resetStats() { + this.stats = { + totalExecutions: 0, + totalTime: 0, + averageTime: 0, + errors: 0 + }; + } + + /** + * Validate configuration + */ + validateConfig() { + return this.config.validate(); + } + + /** + * Update configuration + */ + updateConfig(newConfig) { + if (newConfig instanceof BabaYagaConfig) { + this.config = newConfig; + } else { + this.config = this.config.merge(newConfig); + } + + // Update validator if security mode changed + this.validator = this.config.sandboxMode + ? new SecurityValidator(this.config) + : new InputValidator(this.config); + } +} + +/** + * Convenience function for quick execution + */ +export async function execute(source, config = new BabaYagaConfig()) { + 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(); + break; + case 'production': + config = BabaYagaConfig.production(); + break; + case 'testing': + config = BabaYagaConfig.testing(); + break; + case 'sandbox': + config = BabaYagaConfig.sandbox(); + break; + default: + config = new BabaYagaConfig(); + } + + return new BabaYagaEngine(config); +} |