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
JavaScript
/**
* 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;