import { parse } from './parser/DSLParser.js'; import { RuleGenerator } from './generator/RuleGenerator.js'; import { validateDslText } from './validation/DSLValidation.js'; /** * DSL Compiler - Main integration layer * Compiles DSL text into rule configurations for the zanzibar-graph system */ export class DSLCompiler { constructor(arbiter, options = {}) { this.arbiter = arbiter; this.parser = parse; this.generator = new RuleGenerator(arbiter, options); this.compiledPrograms = new Map(); } /** * Compile DSL text into rule configurations * @param {string} dslText - DSL text to compile * @param {string} programName - Optional name for the program * @returns {Object} Compilation result */ compile(dslText, programName = 'default') { try { const validation = validateDslText(dslText); if (!validation.success) { return { success: false, errors: validation.errors, warnings: validation.warnings, program: null, generatedRules: new Map() }; } const program = validation.program; // Convert plain AST to ProgramNode structure const programNode = { definitions: program.body.filter(s => s.type === 'Definition'), facts: program.body.filter(s => s.type === 'Fact'), evidence: program.body.filter(s => s.type === 'Evidence'), measures: program.body.filter(s => s.type === 'Measure'), validate: () => ({ isValid: true, errors: [], warnings: [] }) }; // Generate rules from AST const generationResult = this.generator.generateRules(programNode); if (!generationResult.success) { return { success: false, errors: generationResult.errors, warnings: [], program: programNode, generatedRules: new Map() }; } // Store compiled program this.compiledPrograms.set(programName, { program: programNode, generatedRules: this.generator.getGeneratedRules(), dependencyIndex: this.generator.getDependencyIndex(), compiledAt: new Date() }); return { success: true, errors: validation.errors, warnings: validation.warnings, program: programNode, generatedRules: this.generator.getGeneratedRules(), dependencyIndex: this.generator.getDependencyIndex(), generatedCount: generationResult.generatedCount }; } catch (error) { return { success: false, errors: [`Compilation error: ${error.message}`], warnings: [], program: null, generatedRules: new Map() }; } } /** * Compile multiple DSL programs * @param {Object} programs - Map of program names to DSL text * @returns {Object} Compilation result for all programs */ compileMultiple(programs) { const results = {}; let overallSuccess = true; const allErrors = []; const allWarnings = []; for (const [name, dslText] of Object.entries(programs)) { const result = this.compile(dslText, name); results[name] = result; if (!result.success) { overallSuccess = false; } allErrors.push(...result.errors.map(err => `${name}: ${err}`)); allWarnings.push(...result.warnings.map(warn => `${name}: ${warn}`)); } return { success: overallSuccess, errors: allErrors, warnings: allWarnings, results: results }; } /** * Get compiled program by name * @param {string} programName - Name of the program * @returns {Object|null} Compiled program or null */ getCompiledProgram(programName) { return this.compiledPrograms.get(programName) || null; } /** * Get all compiled programs * @returns {Map} Map of all compiled programs */ getAllCompiledPrograms() { return this.compiledPrograms; } /** * Remove compiled program * @param {string} programName - Name of the program to remove * @returns {boolean} True if removed successfully */ removeCompiledProgram(programName) { return this.compiledPrograms.delete(programName); } /** * Clear all compiled programs */ clearCompiledPrograms() { this.compiledPrograms.clear(); } /** * Get compilation statistics * @returns {Object} Compilation statistics */ getCompilationStats() { const stats = { totalPrograms: this.compiledPrograms.size, totalRules: 0, programs: {} }; this.compiledPrograms.forEach((program, name) => { const programStats = { name: name, compiledAt: program.compiledAt, ruleCount: program.generatedRules.size, definitions: program.program.definitions.length, facts: program.program.facts.length, evidence: program.program.evidence.length, measures: program.program.measures.length }; stats.programs[name] = programStats; stats.totalRules += program.generatedRules.size; }); return stats; } /** * Validate DSL text without compiling * @param {string} dslText - DSL text to validate * @returns {Object} Validation result */ validate(dslText) { const validation = validateDslText(dslText); return { success: validation.success, errors: validation.errors, warnings: validation.warnings, program: validation.program }; } /** * Get parser errors from last parse * @returns {string[]} Array of parser errors */ getParserErrors() { return this.parser.getErrors(); } /** * Get generator errors from last generation * @returns {string[]} Array of generator errors */ getGeneratorErrors() { return this.generator.getErrors(); } /** * Check if a relation is configured * @param {string} relation - Relation name to check * @returns {boolean} True if relation is configured */ isRelationConfigured(relation) { return this.arbiter.relationConfigs.has(relation); } /** * Get relation configuration * @param {string} relation - Relation name * @returns {Object|null} Relation configuration or null */ getRelationConfig(relation) { return this.arbiter.relationConfigs.get(relation) || null; } /** * Get all configured relations * @returns {Map} Map of all relation configurations */ getAllRelationConfigs() { return this.arbiter.relationConfigs; } /** * Export compiled program to JSON * @param {string} programName - Name of the program to export * @returns {string|null} JSON string or null if program not found */ exportProgram(programName) { const program = this.getCompiledProgram(programName); if (!program) { return null; } return JSON.stringify({ name: programName, compiledAt: program.compiledAt, program: this.serializeProgram(program.program), generatedRules: Array.from(program.generatedRules.entries()) }, null, 2); } /** * Import compiled program from JSON * @param {string} jsonString - JSON string to import * @returns {boolean} True if imported successfully */ importProgram(jsonString) { try { const data = JSON.parse(jsonString); const program = this.deserializeProgram(data.program); this.compiledPrograms.set(data.name, { program: program, generatedRules: new Map(data.generatedRules), compiledAt: new Date(data.compiledAt) }); // Apply rules to arbiter data.generatedRules.forEach(([relation, config]) => { this.arbiter.setRelationConfig(relation, config); }); return true; } catch (error) { return false; } } /** * Serialize program to plain object * @param {ProgramNode} program - Program to serialize * @returns {Object} Serialized program */ serializeProgram(program) { // This is a simplified serialization - in a real implementation, // you'd want to properly serialize all node types return { type: 'Program', definitions: program.definitions.map(def => ({ type: 'Definition', name: def.name, definitionType: def.definitionType, fields: def.fields.map(field => ({ type: 'Field', name: field.name, type: field.type, isArray: field.isArray, isOptional: field.isOptional })) })), facts: program.facts.map(fact => ({ type: 'Fact', name: fact.name, parameters: fact.parameters.map(param => ({ type: 'Parameter', name: param.name, type: param.type, isArray: param.isArray })) })), evidence: program.evidence.map(ev => ({ type: 'Evidence', name: ev.name, parameters: ev.parameters.map(param => ({ type: 'Parameter', name: param.name, type: param.type, isArray: param.isArray })), returnType: ev.returnType })), measures: program.measures.map(measure => ({ type: 'Measure', name: measure.name, parameters: measure.parameters.map(param => ({ type: 'Parameter', name: param.name, type: param.type, isArray: param.isArray })), returnType: measure.returnType })) }; } /** * Deserialize program from plain object * @param {Object} data - Serialized program data * @returns {ProgramNode} Deserialized program */ deserializeProgram(data) { // This is a simplified deserialization - in a real implementation, // you'd want to properly deserialize all node types const program = new ProgramNode(); // Note: This is a basic implementation. In practice, you'd need // to properly reconstruct all the AST nodes from the serialized data return program; } }