UNPKG

uppaal-to-tchecker

Version:

JavaScript implementation of Uppaal to TChecker translator - convert Uppaal timed automata models to TChecker format

223 lines (187 loc) 7.04 kB
/** * Main entry point for uppaal-to-tchecker JavaScript implementation */ const fs = require('fs').promises; const path = require('path'); const UppaalParser = require('./uppaal-parser'); const Translator = require('./translator'); const TCheckerOutputter = require('./tchecker-outputter'); const { UTOTException, TranslationException, ParserException } = require('./exceptions'); class UppaalToTChecker { constructor() { this.parser = new UppaalParser(); this.translator = new Translator(); this.verboseLevel = 0; this.debug = false; } /** * Main translation function * @param {string} inputContent - Input file content * @param {Object} options - Translation options * @returns {string} - Translated TChecker format */ async translate(inputContent, options = {}) { const { format = 'auto', language = 'xta', systemName = 'System', verbose = 0, debug = false } = options; this.verboseLevel = verbose; this.debug = debug; try { // Detect format if auto const detectedFormat = format === 'auto' ? this.detectFormat(inputContent) : format; if (this.verboseLevel > 0) { console.log(`Parsing ${language}/${detectedFormat} input.`); } // Parse input const timedAutomataSystem = await this.parser.parse(inputContent, detectedFormat, language); // Check for parsing errors if (timedAutomataSystem.errors && timedAutomataSystem.errors.length > 0) { for (const error of timedAutomataSystem.errors) { console.error(error); } throw new ParserException('Parsing failed with errors'); } // Show warnings if verbose if (timedAutomataSystem.warnings && timedAutomataSystem.warnings.length > 0 && this.verboseLevel > 0) { for (const warning of timedAutomataSystem.warnings) { console.warn(warning); } } if (this.verboseLevel > 0) { console.log('Translating model into TChecker file format.'); } // Create output stream let output = ''; const outputStream = { write: (data) => { output += data; } }; const outputter = new TCheckerOutputter(outputStream); // Add header comment outputter.commentln(); outputter.commentln('This file has been generated automatically with uppaal-to-tchecker (JavaScript version)'); if (this.verboseLevel > 0 && options.inputFileName) { outputter.commentln(`from file: ${options.inputFileName}`); } outputter.commentln(); // Translate model this.translator.translateModel(systemName, timedAutomataSystem, outputter); return output; } catch (error) { if (error instanceof UTOTException) { throw error; } throw new TranslationException(`Translation failed: ${error.message}`); } } /** * Translate file * @param {string} inputFile - Input file path * @param {string} outputFile - Output file path (optional) * @param {Object} options - Translation options * @returns {string} - Translated content */ async translateFile(inputFile, outputFile = null, options = {}) { try { // Read input file const inputContent = await fs.readFile(inputFile, 'utf8'); // Set input filename for comments options.inputFileName = inputFile; // Auto-detect format from file extension if not specified if (!options.format || options.format === 'auto') { options.format = this.detectFormatFromExtension(inputFile); } // Auto-detect language from format if (!options.language) { options.language = this.detectLanguageFromFormat(options.format); } // Generate system name from filename if not provided if (!options.systemName) { options.systemName = this.generateSystemName(inputFile); } // Translate const result = await this.translate(inputContent, options); // Write output file if specified if (outputFile) { await fs.writeFile(outputFile, result); if (this.verboseLevel > 0) { console.log(`Output written to: ${outputFile}`); } } return result; } catch (error) { if (error.code === 'ENOENT') { throw new UTOTException(`Input file not found: ${inputFile}`); } throw error; } } /** * Detect format from content * @param {string} content - File content * @returns {string} - Detected format */ detectFormat(content) { const trimmedContent = content.trim(); if (trimmedContent.startsWith('<?xml') || trimmedContent.startsWith('<nta')) { return 'xml'; } return 'xta'; } /** * Detect format from file extension * @param {string} filename - File name * @returns {string} - Detected format */ detectFormatFromExtension(filename) { const ext = path.extname(filename).toLowerCase(); switch (ext) { case '.xml': return 'xml'; case '.xta': case '.ta': return 'xta'; default: return 'auto'; } } /** * Detect language from format * @param {string} format - File format * @returns {string} - Detected language */ detectLanguageFromFormat(format) { if (format === 'xml') { return 'xta'; // XML format typically uses XTA language } return 'xta'; // Default to XTA } /** * Generate system name from filename * @param {string} filename - File name * @returns {string} - Generated system name */ generateSystemName(filename) { const basename = path.basename(filename, path.extname(filename)); // Replace non-alphanumeric characters with underscores (except underscores themselves) return basename.replace(/[^a-zA-Z0-9_]/g, '_'); } /** * Set verbose level * @param {number} level - Verbose level */ setVerbose(level) { this.verboseLevel = level; } /** * Set debug mode * @param {boolean} enabled - Debug enabled */ setDebug(enabled) { this.debug = enabled; } } module.exports = UppaalToTChecker;