/** * Template Processor * * Processes template files by replacing placeholders with actual values */ import { readFileSync, writeFileSync, readdirSync, statSync, mkdirSync, copyFileSync } from 'fs'; import { join, dirname, basename, extname } from 'path'; import { LanguageArchitecture, LanguageFeatures, LanguageSyntax } from '../commands/new.js'; export interface TemplateContext { architecture: LanguageArchitecture; features: LanguageFeatures; syntax: LanguageSyntax; } /** * Process a template directory and create a project */ export function processTemplate( templateDir: string, outputDir: string, context: TemplateContext ): void { // Create output directory mkdirSync(outputDir, { recursive: true }); // Process all files in template directory processDirectory(templateDir, outputDir, context); } /** * Process a directory recursively */ function processDirectory( sourceDir: string, targetDir: string, context: TemplateContext ): void { const items = readdirSync(sourceDir); for (const item of items) { const sourcePath = join(sourceDir, item); const targetPath = join(targetDir, processFileName(item, context)); const stat = statSync(sourcePath); if (stat.isDirectory()) { mkdirSync(targetPath, { recursive: true }); processDirectory(sourcePath, targetPath, context); } else { processFile(sourcePath, targetPath, context); } } } /** * Process a single file */ function processFile( sourcePath: string, targetPath: string, context: TemplateContext ): void { const content = readFileSync(sourcePath, 'utf-8'); const processedContent = processContent(content, context); // Ensure target directory exists mkdirSync(dirname(targetPath), { recursive: true }); writeFileSync(targetPath, processedContent, 'utf-8'); } /** * Process file name placeholders */ function processFileName(fileName: string, context: TemplateContext): string { const replacements = getReplacements(context); let processed = fileName; for (const [placeholder, value] of Object.entries(replacements)) { processed = processed.replace(new RegExp(placeholder, 'g'), value); } return processed; } /** * Process file content placeholders */ function processContent(content: string, context: TemplateContext): string { const replacements = getReplacements(context); let processed = content; for (const [placeholder, value] of Object.entries(replacements)) { processed = processed.replace(new RegExp(placeholder, 'g'), value); } return processed; } /** * Get all placeholder replacements */ function getReplacements(context: TemplateContext): Record { const { architecture, features, syntax } = context; return { '__DSL_NAME__': architecture.name, '__PARADIGM__': architecture.paradigm, '__PURPOSE__': architecture.purpose, '__DATA_PHILOSOPHY__': architecture.dataPhilosophy, // Syntax elements '__VARIABLE_KEYWORD__': syntax.variables.keyword, '__ASSIGNMENT_OPERATOR__': syntax.variables.operator, '__TERMINATOR__': syntax.variables.terminator, '__VARIABLE_EXAMPLE__': syntax.variables.example, '__COMMENT_EXAMPLE__': syntax.comments.pattern, '__STRING_EXAMPLE__': syntax.strings.examples[0] || '"hello"', '__NUMBER_EXAMPLE__': syntax.numbers.examples[0] || '42', '__IDENTIFIER_EXAMPLE__': syntax.identifiers.examples[0] || 'myVar', // Paradigm-specific examples '__PARADIGM_EXAMPLE__': getParadigmExample(architecture, syntax), // File extension '__EXT__': getFileExtension(architecture.name), // Feature lists '__FEATURES_LIST__': generateFeaturesList(features), '__DATA_STRUCTURES__': features.dataStructures.join(', ') || 'Basic types', // Control flow '__CONTROL_FLOW__': features.controlFlow.join(', ') || 'Sequential execution' }; } /** * Get paradigm-specific example code */ function getParadigmExample(architecture: LanguageArchitecture, syntax: LanguageSyntax): string { if (syntax.paradigmExamples.class) { return syntax.paradigmExamples.class; } if (syntax.paradigmExamples.function) { return syntax.paradigmExamples.function; } if (syntax.paradigmExamples.rule) { return syntax.paradigmExamples.rule; } // Fallback to variable example return syntax.variables.example; } /** * Get appropriate file extension for the language */ function getFileExtension(languageName: string): string { // Simple heuristic - could be made configurable const name = languageName.toLowerCase(); if (name.includes('script')) return 'script'; if (name.includes('lang')) return 'lang'; if (name.includes('dsl')) return 'dsl'; // Default: use first 3 characters of name return name.substring(0, 3); } /** * Generate features list for README */ function generateFeaturesList(features: LanguageFeatures): string { const items: string[] = []; if (features.controlFlow.length > 0) { items.push(`- **Control Flow**: ${features.controlFlow.join(', ')}`); } if (features.dataStructures.length > 0) { items.push(`- **Data Structures**: ${features.dataStructures.join(', ')}`); } if (features.functionTypes.length > 0) { items.push(`- **Functions**: ${features.functionTypes.join(', ')}`); } return items.length > 0 ? items.join('\n') : '- Basic language constructs'; }