package org.sugarj.driver; import java.io.IOException; import java.text.ParseException; import org.spoofax.interpreter.terms.IStrategoTerm; import org.spoofax.jsglr.client.InvalidParseTableException; import org.spoofax.jsglr.shared.BadTokenException; import org.spoofax.jsglr.shared.SGLRException; import org.spoofax.jsglr.shared.TokenExpectedException; import org.strategoxt.lang.StrategoException; import org.sugarj.AbstractBaseProcessor; import org.sugarj.common.ATermCommands; import org.sugarj.common.Environment; import org.sugarj.common.FileCommands; import org.sugarj.common.Log; import org.sugarj.common.path.Path; import org.sugarj.common.path.RelativePath; import org.sugarj.util.Pair; /** * @author seba */ public class ImportCommands { private AbstractBaseProcessor baseProcessor; private Environment environment; private Driver driver; private Result driverResult; private STRCommands str; public ImportCommands(AbstractBaseProcessor baseProcessor, Environment environment, Driver driver, Result driverResult, STRCommands str) { this.baseProcessor = baseProcessor; this.environment = environment; this.driver = driver; this.driverResult = driverResult; this.str = str; } /** * Resolve module * * @param term * @param toplevelDecl * @param asModel If true, looks for models. If false, looks for transformations. * @return */ private RelativePath resolveModule(IStrategoTerm term, IStrategoTerm toplevelDecl, boolean asModel) throws TokenExpectedException, IOException, ParseException, InvalidParseTableException, SGLRException, InterruptedException { if (ATermCommands.isApplication(term, "TransApp")) { IStrategoTerm model = ATermCommands.getApplicationSubterm(term, "TransApp", 1); IStrategoTerm transformation = ATermCommands.getApplicationSubterm(term, "TransApp", 0); Pair<String, Boolean> transformedModel = transformModel(model, transformation, toplevelDecl); if (transformedModel != null) { if (asModel) return ModuleSystemCommands.importModel(transformedModel.a, environment, driverResult); else return ModuleSystemCommands.importStratego(transformedModel.a, environment, driverResult); } return null; } String path = baseProcessor.getModulePath(term); if (path.contains("/")) { boolean isCircularImport = driver.prepareImport(toplevelDecl, path); if (isCircularImport) return null; if (asModel) return ModuleSystemCommands.importModel(path, environment, driverResult); else return ModuleSystemCommands.importStratego(path, environment, driverResult); } throw new RuntimeException("TODO support non-qualifed transformations and model paths"); // TODO support non-qualifed transformations and model paths // return null; } /** * Transforms the given model with the given transformation. * * @param model AST part of the import that denotes the model. * @param transformation AST part of the import that denotes the transformation. * @param toplevelDecl * @param environment * @param driver * @return a pair consisting of the path to the transformed model and a flag indicating a circular import (if true). */ public Pair<String, Boolean> transformModel(IStrategoTerm model, IStrategoTerm transformation, IStrategoTerm toplevelDecl) throws TokenExpectedException, IOException, ParseException, InvalidParseTableException, SGLRException, InterruptedException { RelativePath modelPath = resolveModule(model, toplevelDecl, true); RelativePath transformationPath = resolveModule(transformation, toplevelDecl, false); if (modelPath == null) { // something's wrong String name; try { name = baseProcessor.getModulePath(model); } catch (Exception e) { name = model.toString(); } driver.setErrorMessage(toplevelDecl, "model not found " + name); return null; } if (transformationPath == null) { // something's wrong String name; try { name = baseProcessor.getModulePath(transformation); } catch (Exception e) { name = transformation.toString(); } driver.setErrorMessage(toplevelDecl, "transformation not found " + name); return null; } Log.log.beginTask("Transform model " + FileCommands.fileName(modelPath) + " with transformation " + FileCommands.fileName(transformationPath), Log.TRANSFORM); try { RelativePath transformedModelSourceFile = getTransformedModelSourceFilePath(modelPath, transformationPath, environment); String transformedModelPath = FileCommands.dropExtension(transformedModelSourceFile.getRelativePath()); Result transformedModelResult = ModuleSystemCommands.locateResult(transformedModelPath, environment); if (transformedModelResult != null && transformedModelResult.isUpToDate(environment)) { // result of transformation is already up-to-date, nothing to do here. driverResult.addDependency(transformedModelResult); return Pair.create(transformedModelPath, false); } else { // transform the model, prepare the import of the resulting code. IStrategoTerm transformedModel = executeTransformation(modelPath, transformationPath, toplevelDecl, environment, str, driver); String transformedModelText = ATermCommands.atermToString(transformedModel); driverResult.generateFile(transformedModelSourceFile, transformedModelText); boolean isCircularImport = driver.prepareImport(toplevelDecl, transformedModelPath); return Pair.create(transformedModelPath, isCircularImport); } } finally { Log.log.endTask(); } } /** * Apply the transformation to the model and return the result. * * Assumes that the model and transformation are already registered as dependencies with the current driver result. * * @param model Path to the *.model file that contains the Aterm model. * @param transformationPath Path to the *.str transformation. */ private static IStrategoTerm executeTransformation(RelativePath model, RelativePath transformationPath, IStrategoTerm toplevelDecl, Environment environment, STRCommands str, Driver driver) throws IOException, TokenExpectedException, BadTokenException, InvalidParseTableException, SGLRException { IStrategoTerm modelTerm = ATermCommands.atermFromFile(model.getAbsolutePath()); String strat = "main-" + FileCommands.dropExtension(transformationPath.getRelativePath()).replace('/', '_'); Result transformationResult = ModuleSystemCommands.locateResult(FileCommands.dropExtension(transformationPath.getRelativePath()), environment); Path trans = str.compile(transformationPath, strat, transformationResult.getTransitiveFileDependencies()); IStrategoTerm transformationInput = ATermCommands.makeTuple( modelTerm, ATermCommands.makeString(FileCommands.dropExtension(model.getRelativePath()), null), ATermCommands.makeString(FileCommands.dropExtension(transformationPath.getRelativePath()), null)); try { IStrategoTerm transformedTerm = str.assimilate(strat, trans, transformationInput); return transformedTerm; } catch (StrategoException e) { String msg = "Failed to apply transformation " + transformationPath.getRelativePath() + " to model " + model.getRelativePath() + ": " + e.getMessage(); driver.setErrorMessage(toplevelDecl, msg); throw new StrategoException(msg); } } /** * Computes the path of the model file generated from the given model path by the given transformation path. * * @param modelPath * @param transformationPath * @param environment * @return */ public static RelativePath getTransformedModelSourceFilePath(RelativePath modelPath, RelativePath transformationPath, Environment environment) { if (modelPath == null) return null; if (transformationPath == null) return environment.createOutPath(modelPath + ".model"); String transformationPathString = FileCommands.dropExtension(transformationPath.getRelativePath()); String transformedModelPath = FileCommands.dropExtension(modelPath.getRelativePath()) + "__" + transformationPathString.replace('/', '_'); return environment.createOutPath(transformedModelPath + ".model"); } /** * Retrieves the right-most model in the given transformation application and returns the model's name. * * @param appl * @param base processor * @return */ public static String getTransformationApplicationModelPath(IStrategoTerm appl, AbstractBaseProcessor baseProcessor) { if (ATermCommands.isApplication(appl, "TransApp")) return getTransformationApplicationModelPath(appl.getSubterm(1), baseProcessor); return baseProcessor.getModulePath(appl); } }