de.monticore.MontiCoreScript.java Source code

Java tutorial

Introduction

Here is the source code for de.monticore.MontiCoreScript.java

Source

/*
 * ******************************************************************************
 * MontiCore Language Workbench
 * Copyright (c) 2015, MontiCore, All rights reserved.
 *
 * This project is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this project. If not, see <http://www.gnu.org/licenses/>.
 * ******************************************************************************
 */

package de.monticore;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.codehaus.groovy.control.customizers.ImportCustomizer;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.io.Resources;

import de.monticore.codegen.GeneratorHelper;
import de.monticore.codegen.cd2java.ast.AstGenerator;
import de.monticore.codegen.cd2java.ast.CdDecorator;
import de.monticore.codegen.cd2java.ast_emf.CdEmfDecorator;
import de.monticore.codegen.cd2java.cocos.CoCoGenerator;
import de.monticore.codegen.cd2java.od.ODGenerator;
import de.monticore.codegen.cd2java.visitor.VisitorGenerator;
import de.monticore.codegen.mc2cd.MC2CDTransformation;
import de.monticore.codegen.mccoder.McCoderGenerator;
import de.monticore.codegen.mccoder.McCoderPPGenerator;
import de.monticore.codegen.mchammer.MCHammerGenerator;
import de.monticore.codegen.mchammerparser.McHammerParserGenerator;
import de.monticore.codegen.mchammer.checker.McHammerCheckerGenerator;
import de.monticore.codegen.mchammer.parsetree.McHammerParseTreeGenerator;
import de.monticore.codegen.mchcexamples.McHCExampleGenerator;
import de.monticore.codegen.parser.ParserGenerator;
import de.monticore.codegen.symboltable.SymbolTableGenerator;
import de.monticore.codegen.symboltable.SymbolTableGeneratorBuilder;
import de.monticore.codegen.symboltable.SymbolTableGeneratorHelper;
import de.monticore.generating.templateengine.GlobalExtensionManagement;
import de.monticore.generating.templateengine.reporting.Reporting;
import de.monticore.generating.templateengine.reporting.commons.ReportingConstants;
import de.monticore.generating.templateengine.reporting.reporter.InputOutputFilesReporter;
import de.monticore.grammar.cocos.GrammarCoCos;
import de.monticore.grammar.grammar._ast.ASTMCGrammar;
import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker;
import de.monticore.incremental.IncrementalChecker;
import de.monticore.io.paths.IterablePath;
import de.monticore.io.paths.ModelPath;
import de.monticore.languages.grammar.MCGrammarSymbol;
import de.monticore.languages.grammar.MontiCoreGrammarLanguage;
import de.monticore.languages.grammar.visitors.MCGrammarSymbolTableCreator;
import de.monticore.symboltable.GlobalScope;
import de.monticore.symboltable.ResolverConfiguration;
import de.monticore.symboltable.Scope;
import de.monticore.umlcd4a.CD4AnalysisLanguage;
import de.monticore.umlcd4a.cd4analysis._ast.ASTCDCompilationUnit;
import de.monticore.umlcd4a.symboltable.CD4AnalysisSymbolTableCreator;
import de.monticore.umlcd4a.symboltable.CDSymbol;
import de.se_rwth.commons.Joiners;
import de.se_rwth.commons.Names;
import de.se_rwth.commons.configuration.Configuration;
import de.se_rwth.commons.groovy.GroovyInterpreter;
import de.se_rwth.commons.groovy.GroovyRunner;
import de.se_rwth.commons.groovy.GroovyRunnerBase;
import de.se_rwth.commons.logging.Log;
import groovy.lang.Script;
import parser.MCGrammarParser;

/**
 * The actual top level functional implementation of MontiCore. This is the
 * top-most interface of MontiCore. The static members of this class constitute
 * the functional API of MontiCore to be used from within Groovy scripts (by
 * default). They represent the main functional blocks which make up the
 * MontiCore functionality, i.e. parsing of grammar files, generation of ASTs,
 * generation of parsers, etc. They also provide logging methods for from within
 * a Groovy script.<br>
 * <br>
 * This class extends {@link Script} for the purpose of being used as a base
 * class for Groovy scripts. This allows to use Groovy scripts for controlling
 * the actual workflow(s) in contrast to statically compiled Java byte code.
 * Language developers can hence very easily implement their own language
 * processing workflows without having to recompile things.
 *
 * @author Galina Volkova, Andreas Horst
 */
public class MontiCoreScript extends Script implements GroovyRunner {

    /* The logger name for logging from within a Groovy script. */
    static final String LOG_ID = "MAIN";

    private final CD4AnalysisLanguage cd4AnalysisLanguage = new CD4AnalysisLanguage();

    /**
     * Executes the default MontiCore Groovy script (parses grammars, generates
     * ASTs, parsers, etc.).
     *
     * @see Configuration
     * @param configuration of MontiCore for this execution
     * @see Configuration
     */
    public void run(Configuration configuration) {
        try {
            ClassLoader l = MontiCoreScript.class.getClassLoader();
            String script = Resources
                    .asCharSource(l.getResource("de/monticore/monticore_emf.groovy"), Charset.forName("UTF-8"))
                    .read();
            run(script, configuration);
        } catch (IOException e) {
            Log.error("0xA1015 Failed to default MontiCore script.", e);
        }
    }

    /**
     * Executes the given Groovy script with the given
     * {@link MontiCoreConfiguration}.
     *
     * @see Configuration
     * @param configuration of MontiCore for this execution
     * @param script to execute (NOT file or path, the actual Groovy source code)
     */
    @Override
    public void run(String script, Configuration configuration) {
        /* Note to coders: this method is implemented here to allow usage of this
         * class as Groovy runner; even though in fact the class
         * MontiCoreScript.Runner does all the work. Letting MontiCore script also
         * be an implementation of the GroovyRunner interface allows for better
         * integration with the se-groovy-maven-plugin. This method should do
         * nothing more than simple delegation to the MontiCoreScript.Runner. */
        new Runner().run(script, configuration);
    }

    /**
     * Parses the given grammar file.
     *
     * @param grammar - path to the grammar file
     * @return grammar AST
     */
    public Optional<ASTMCGrammar> parseGrammar(Path grammar) {
        if (!grammar.toFile().isFile()) {
            error("0xA1016 Cannot read " + grammar.toString() + " as it is not a file.");
        }
        return MCGrammarParser.parse(grammar);
    }

    /**
     * Parses all grammars in the given {@link IterablePath}.
     *
     * @param grammarPath set of file and directory entries which are/contain
     * grammar files to be parsed
     * @return list of all successfully created grammar ASTs
     */
    public List<ASTMCGrammar> parseGrammars(IterablePath grammarPath) {
        List<ASTMCGrammar> result = Lists.newArrayList();

        Iterator<Path> grammarPathIt = grammarPath.getResolvedPaths();
        while (grammarPathIt.hasNext()) {
            Path it = grammarPathIt.next();
            Optional<ASTMCGrammar> ast = parseGrammar(it);
            if (!ast.isPresent()) {
                error("0xA1017 Failed to parse " + it.toString());
            } else {
                result.add(ast.get());
            }
        }

        return result;
    }

    protected MontiCoreConfiguration __configuration;

    protected Iterator<Path> grammarIterator;

    protected GlobalExtensionManagement glex;

    protected GlobalScope symbolTable;

    protected Map<ASTMCGrammar, ASTCDCompilationUnit> firstPassGrammars;

    public void initGlobals(MontiCoreConfiguration configuration) {
        this.__configuration = configuration;
        IncrementalChecker.initialize(configuration.getOut());
        enableReporting();
        this.grammarIterator = configuration.getGrammars().getResolvedPaths();
        this.glex = new GlobalExtensionManagement();
        this.symbolTable = initSymbolTable(configuration.getModelPath());
        this.firstPassGrammars = new LinkedHashMap<>();
    }

    protected void storeCDForGrammar(ASTMCGrammar grammar, ASTCDCompilationUnit cdAst) {
        this.firstPassGrammars.put(grammar, cdAst);
    }

    protected ASTCDCompilationUnit getCDOfParsedGrammar(ASTMCGrammar grammar) {
        return this.firstPassGrammars.get(grammar);
    }

    protected Iterable<ASTMCGrammar> getParsedGrammars() {
        return this.firstPassGrammars.keySet();
    }

    public boolean isUpToDate(Path grammar) {
        return IncrementalChecker.isUpToDate(grammar, __configuration.getOut(), __configuration.getModelPath(),
                __configuration.getHandcodedPath());
    }

    public void cleanUp(Path grammar) {
        IncrementalChecker.cleanUp(grammar);
    }

    /**
     * Generates the parser for the given grammar.
     *
     * @param grammar to generate the parser for
     * @param symbolTable
     * @param outputDirectory output directory for generated Java code
     */
    public void generateParser(ASTMCGrammar grammar, GlobalScope symbolTable, IterablePath handcodedPath,
            File outputDirectory) {
        Log.errorIfNull(grammar,
                "0xA4038 Parser generation can't be processed: the reference to the grammar ast is null");
        ParserGenerator.generateParser(grammar, symbolTable, handcodedPath, outputDirectory);
    }

    /**
     * Generates the model language infrastructure for the given grammar (e.g.,
     * modeling language, model loader, symbols, symbol kinds, etc.)
     *
     * @param astGrammar to generate the parser for
     * @param symbolTable
     * @param astCd
     * @param outputDirectory output directory for generated Java code
     */
    public void generateSymbolTable(ASTMCGrammar astGrammar, GlobalScope symbolTable, ASTCDCompilationUnit astCd,
            File outputDirectory, IterablePath handcodedPath) {
        Log.errorIfNull(astGrammar);
        SymbolTableGeneratorHelper genHelper = new SymbolTableGeneratorHelper(astGrammar, symbolTable, astCd);
        SymbolTableGenerator symbolTableGenerator = new SymbolTableGeneratorBuilder().build();
        symbolTableGenerator.generate(astGrammar, genHelper, outputDirectory, handcodedPath);
    }

    /**
     * TODO: Write me! TODO: doc that the grammar AST reference might change
     *
     * @param ast
     * @return
     */
    public ASTMCGrammar createSymbolsFromAST(GlobalScope globalScope, ASTMCGrammar ast) {
        // Build grammar symbol table (if not already built)
        String qualifiedGrammarName = Names.getQualifiedName(ast.getPackage(), ast.getName());
        Optional<MCGrammarSymbol> grammarSymbol = globalScope.<MCGrammarSymbol>resolveDown(qualifiedGrammarName,
                MCGrammarSymbol.KIND);

        ASTMCGrammar result = ast;

        if (grammarSymbol.isPresent()) {
            result = grammarSymbol.get().getASTGrammar();
        } else {
            MontiCoreGrammarLanguage language = new MontiCoreGrammarLanguage();

            ResolverConfiguration resolverConfiguration = new ResolverConfiguration();
            resolverConfiguration.addTopScopeResolvers(language.getResolvers());

            MCGrammarSymbolTableCreator stCreator = language
                    .getSymbolTableCreator(resolverConfiguration, globalScope).get();
            stCreator.createFromAST(result);
            globalScope.cache(language.getModelLoader(), qualifiedGrammarName);
        }

        MCGrammarSymbol symbol = (MCGrammarSymbol) result.getSymbol().get();
        for (MCGrammarSymbol it : symbol.getAllSuperGrammars()) {
            if (!it.getFullName().equals(symbol.getFullName())) {
                Reporting.reportOpenInputFile(null,
                        Paths.get(it.getFullName().replaceAll("\\.", "/").concat(".mc4")));
                Reporting.reportOpenInputFile(null,
                        Paths.get(it.getFullName().replaceAll("\\.", "/").concat(".cd")));

            }
        }

        return result;
    }

    /**
     * TODO: Write me! TODO: doc that the grammar AST reference might change
     *
     * @param ast
     * @return
     */
    public ASTCDCompilationUnit createSymbolsFromAST(GlobalScope globalScope, ASTCDCompilationUnit ast) {
        // Build grammar symbol table (if not already built)

        final String qualifiedCDName = Names.getQualifiedName(ast.getPackage(), ast.getCDDefinition().getName());
        Optional<CDSymbol> cdSymbol = globalScope.<CDSymbol>resolveDown(qualifiedCDName, CDSymbol.KIND);

        ASTCDCompilationUnit result = ast;

        if (cdSymbol.isPresent() && cdSymbol.get().getEnclosingScope().getAstNode().isPresent()) {
            result = (ASTCDCompilationUnit) cdSymbol.get().getEnclosingScope().getAstNode().get();
            info("Used present symbol table for " + cdSymbol.get().getFullName());
        } else {
            ResolverConfiguration resolverConfiguration = new ResolverConfiguration();
            resolverConfiguration.addTopScopeResolvers(cd4AnalysisLanguage.getResolvers());

            CD4AnalysisSymbolTableCreator stCreator = cd4AnalysisLanguage
                    .getSymbolTableCreator(resolverConfiguration, globalScope).get();
            stCreator.createFromAST(result);
            globalScope.cache(cd4AnalysisLanguage.getModelLoader(), qualifiedCDName);
        }

        return result;
    }

    public GlobalScope initSymbolTable(ModelPath modelPath) {
        final MontiCoreGrammarLanguage mcLanguage = new MontiCoreGrammarLanguage();

        final ResolverConfiguration resolverConfiguration = new ResolverConfiguration();
        resolverConfiguration.addTopScopeResolvers(mcLanguage.getResolvers());
        resolverConfiguration.addTopScopeResolvers(cd4AnalysisLanguage.getResolvers());

        return new GlobalScope(modelPath, Arrays.asList(mcLanguage, cd4AnalysisLanguage), resolverConfiguration);
    }

    /**
     * TODO: Write me!
     *
     * @param ast
     * @param scope
     */
    public void runGrammarCoCos(ASTMCGrammar ast, GlobalScope scope) {
        // Run context conditions
        Grammar_WithConceptsCoCoChecker checker = new GrammarCoCos().getCoCoChecker();
        checker.handle(ast);
        return;
    }

    /**
     * TODO: write me!
     *
     * @param grammar TODO
     * @param globalScope TODO
     * @param outputDirectory TODO
     */
    public void generateParserWrappers(ASTMCGrammar grammar, GlobalScope globalScope, IterablePath targetPath,
            File outputDirectory) {
        Log.errorIfNull(grammar,
                "0xA4037 Generation of parser wrappers can't be processed: the reference to the grammar ast is null");
        ParserGenerator.generateParserWrappers(grammar, globalScope, targetPath, outputDirectory);
    }

    /**
     * Transforms grammar AST to class diagram AST.
     *
     * @param astGrammar - grammar AST
     * @param glex TODO
     * @param targetPath TODO
     */
    public ASTCDCompilationUnit transformAstGrammarToAstCd(GlobalExtensionManagement glex, ASTMCGrammar astGrammar,
            GlobalScope symbolTable, IterablePath targetPath) {

        // transformation
        ASTCDCompilationUnit compUnit = new MC2CDTransformation(glex).apply(astGrammar);

        return compUnit;
    }

    /**
     * Prints Cd4Analysis AST to the CD-file (*.cd) in the subdirectory
     * {@link MontiCoreScript#DIR_REPORTS}
     *
     * @param astCd - the top node of the Cd4Analysis AST
     * @param outputDirectory - output directory
     */
    public void storeInCdFile(ASTCDCompilationUnit astCd, File outputDirectory) {
        // we also store the class diagram fully qualified such that we can later on
        // resolve it properly for the generation of sub languages
        String subDir = Joiner.on(File.separator).join(astCd.getPackage());
        String reportSubDir = Joiners.DOT.join(astCd.getPackage());
        reportSubDir = reportSubDir.isEmpty() ? astCd.getCDDefinition().getName()
                : reportSubDir.concat(".").concat(astCd.getCDDefinition().getName());
        GeneratorHelper.prettyPrintAstCd(astCd, outputDirectory,
                ReportingConstants.REPORTING_DIR + File.separator + reportSubDir);
        GeneratorHelper.prettyPrintAstCd(astCd, outputDirectory, subDir);

        String fqn = Names.getQualifiedName(astCd.getPackage(), astCd.getCDDefinition().getName());
        Reporting.reportOpenInputFile(outputDirectory.toPath().toAbsolutePath(),
                Paths.get(fqn.replaceAll("\\.", "/").concat(".cd")));
    }

    /**
     * Decorates class diagram AST by adding of new classes and methods using in
     * ast files TODO: rephrase!
     *
     * @param glex - object for managing hook points, features and global
     * variables
     * @param astClassDiagram - class diagram AST
     * @param targetPath the directory to produce output in
     */
    public void decorateCd(GlobalExtensionManagement glex, ASTCDCompilationUnit astClassDiagram,
            GlobalScope symbolTable, IterablePath targetPath) {
        boolean emfCompatible = false;
        createCdDecorator(glex, symbolTable, targetPath, emfCompatible).decorate(astClassDiagram);
    }

    /**
     * Generates ast files for the given class diagram AST TODO: rephrase!
     *
     * @param glex - object for managing hook points, features and global
     * variables
     * @param astClassDiagram - class diagram AST
     * @param outputDirectory TODO
     */
    public void generate(GlobalExtensionManagement glex, GlobalScope globalScope,
            ASTCDCompilationUnit astClassDiagram, File outputDirectory, IterablePath templatePath) {
        boolean emfCompatible = false;
        AstGenerator.generate(glex, globalScope, astClassDiagram, outputDirectory, templatePath, emfCompatible);
        VisitorGenerator.generate(glex, globalScope, astClassDiagram, outputDirectory);
        CoCoGenerator.generate(glex, globalScope, astClassDiagram, outputDirectory);
        ODGenerator.generate(glex, globalScope, astClassDiagram, outputDirectory);
    }

    /**
     * Decorates class diagram AST by adding of new classes and methods using in
     * ast files TODO: rephrase!
     *
     * @param glex - object for managing hook points, features and global
     * variables
     * @param astClassDiagram - class diagram AST
     * @param targetPath the directory to produce output in
     */
    public void decorateEmfCd(GlobalExtensionManagement glex, ASTCDCompilationUnit astClassDiagram,
            GlobalScope symbolTable, IterablePath targetPath) {
        boolean emfCompatible = true;
        createCdDecorator(glex, symbolTable, targetPath, emfCompatible).decorate(astClassDiagram);
    }

    /**
     * Generates ast files for the given class diagram AST TODO: rephrase!
     *
     * @param glex - object for managing hook points, features and global
     * variables
     * @param astClassDiagram - class diagram AST
     * @param outputDirectory TODO
     */
    public void generateEmfCompatible(GlobalExtensionManagement glex, GlobalScope globalScope,
            ASTCDCompilationUnit astClassDiagram, File outputDirectory, IterablePath templatePath) {
        boolean emfCompatible = true;
        AstGenerator.generate(glex, globalScope, astClassDiagram, outputDirectory, templatePath, emfCompatible);
        VisitorGenerator.generate(glex, globalScope, astClassDiagram, outputDirectory);
        CoCoGenerator.generate(glex, globalScope, astClassDiagram, outputDirectory);
        ODGenerator.generate(glex, globalScope, astClassDiagram, outputDirectory);
    }

    /**
     * Creates instance of the {@link CdDecorator} 
     * 
     * @param glex
     * @param symbolTable
     * @param targetPath
     * @param emfCompatible - create the CdDecorator for the emf compatible code
     * @return
     */
    private CdDecorator createCdDecorator(GlobalExtensionManagement glex, GlobalScope symbolTable,
            IterablePath targetPath, boolean emfCompatible) {
        if (emfCompatible) {
            return new CdEmfDecorator(glex, symbolTable, targetPath);
        }
        return new CdDecorator(glex, symbolTable, targetPath);
    }

    // #######################
    // log functions
    // #######################

    public boolean isDebugEnabled() {
        return Log.isDebugEnabled(LOG_ID);
    }

    /**
     * @see Log#debug(String, String)
     * @param msg
     */
    public void debug(String msg) {
        Log.debug(msg, LOG_ID);
    }

    /**
     * @see Log#debug(String, Throwable, String)
     * @param msg
     * @param t
     */
    public void debug(String msg, Throwable t) {
        Log.debug(msg, t, LOG_ID);
    }

    /**
     * @see Log#isInfoEnabled(String) TODO: Write me!
     * @return
     */
    public boolean isInfoEnabled() {
        return Log.isInfoEnabled(LOG_ID);
    }

    /**
     * @see Log#info(String, String)
     * @param msg
     */
    public void info(String msg) {
        Log.info(msg, LOG_ID);
    }

    /**
     * @see Log#info(String, Throwable, String)
     * @param msg
     * @param t
     */
    public void info(String msg, Throwable t) {
        Log.info(msg, t, LOG_ID);
    }

    /**
     * @see Log#warn(String)
     * @param msg
     */
    public void warn(String msg) {
        Log.warn(msg);
    }

    /**
     * @see Log#warn(String, Throwable)
     * @param msg
     * @param t
     */
    public void warn(String msg, Throwable t) {
        Log.warn(msg, t);
    }

    /**
     * @see Log#error(String)
     * @param msg
     */
    public void error(String msg) {
        Log.error(msg);
    }

    /**
     * @see Log#error(String, Throwable)
     * @param msg
     * @param t
     */
    public void error(String msg, Throwable t) {
        Log.error(msg, t);
    }

    /**
     * @see Log#enableFailQuick(boolean)
     * @param enable
     */
    public void enableFailQuick(boolean enable) {
        Log.enableFailQuick(enable);
    }

    /**
     * @see Log#getErrorCount()
     * @return
     */
    public long getErrorCount() {
        return Log.getErrorCount();
    }

    // #######################
    // log functions
    // #######################

    /**
     * The global reporting initialization. This method configures reporting
     * (i.e., configuring the output directory and all active reporters).
     * 
     * @param outputDir
     */
    public void enableReporting() {
        // TODO AHo: document!
        InputOutputFilesReporter.resetModelToArtifactMap();
        // ##
        MontiCoreReports reports = new MontiCoreReports(__configuration.getOut().getAbsolutePath(),
                __configuration.getHandcodedPath(), __configuration.getTemplatePath());
        Reporting.init(__configuration.getOut().getAbsolutePath(), reports);
    }

    /**
     * Causes the reporting system to flush its reports (i.e., to write their
     * gathered reports to disk).
     * 
     * @param ast the grammar for which to flush the reporting
     */
    public void flushReporting(ASTMCGrammar ast) {
        Reporting.flush(ast);
    }

    /**
     * Disables reporting.
     * 
     * @return the name of the model for which reporting was previously active
     * @see MontiCoreScript#startReportingFor(ASTMCGrammar, Path)
     */
    public String reportingOff() {
        return Reporting.off();
    }

    /**
     * Initializes reporting for a particular model (in this case grammar). This
     * method also serves as an initial reporting hook for reporting the parsing
     * of the main model (grammar) file.
     * 
     * @param grammar for which to report
     * @param grammarInput path to the artifact containing the grammar
     * @return whether reporting is activated
     */
    public boolean startReportingFor(ASTMCGrammar grammar, Path grammarInput) {
        String fqn = Names.getQualifiedName(grammar.getPackage(), grammar.getName());
        boolean result = Reporting.on(fqn);
        Reporting.reportParseInputFile(grammarInput, fqn);
        return result;
    }

    /**
     * Reporting is done per model (in this case grammar). This method provides
     * the switch to determine for which model (grammar) reporting should occur.
     * 
     * @param grammar to report for after invokation of this method
     * @return
     */
    public boolean reportingFor(ASTMCGrammar grammar) {
        String fqn = Names.getQualifiedName(grammar.getPackage(), grammar.getName());
        return Reporting.on(fqn);
    }

    /**
     * @see groovy.lang.Script#run()
     */
    @Override
    public Object run() {
        return true;
    }

    /**
     * The actual Groovy runner used by MontiCore.
     *
     * @author (last commit) $Author$
     * @version $Revision$, $Date$
     */
    public static class Runner extends GroovyRunnerBase {

        /**
         * The default (Java) imports for within Groovy scripts.
         */
        public static final String[] DEFAULT_IMPORTS = { "mc.grammar._ast",
                "de.monticore.generating.templateengine", "de.monticore.codegen.cd2java.ast.cddecoration",
                "de.monticore.grammar.grammar._ast", "de.monticore.symboltable", "de.monticore.io.paths",
                "de.monticore.languages.grammar" };

        /**
         * @see de.se_rwth.commons.groovy.GroovyRunnerBase#doRun(java.lang.String,
         * de.se_rwth.commons.configuration.Configuration)
         */
        @Override
        protected void doRun(String script, Configuration configuration) {
            GroovyInterpreter.Builder builder = GroovyInterpreter.newInterpreter()
                    .withScriptBaseClass(MontiCoreScript.class)
                    .withImportCustomizer(new ImportCustomizer().addStarImports(DEFAULT_IMPORTS));

            Optional<Configuration> config = Optional.ofNullable(configuration);
            if (config.isPresent()) {
                MontiCoreConfiguration mcConfig = MontiCoreConfiguration.withConfiguration(config.get());
                // we add the configuration object as property with a special property
                // name
                builder.addVariable(MontiCoreConfiguration.CONFIGURATION_PROPERTY, mcConfig);

                mcConfig.getAllValues().forEach((key, value) -> builder.addVariable(key, value));

                // after adding everything we override a couple of known variable
                // bindings
                // to have them properly typed in the script
                builder.addVariable(MontiCoreConfiguration.Options.GRAMMARS.toString(), mcConfig.getGrammars());
                builder.addVariable(MontiCoreConfiguration.Options.MODELPATH.toString(), mcConfig.getModelPath());
                builder.addVariable(MontiCoreConfiguration.Options.OUT.toString(), mcConfig.getOut());
                builder.addVariable(MontiCoreConfiguration.Options.FORCE.toString(), mcConfig.getForce());
                builder.addVariable(MontiCoreConfiguration.Options.HANDCODEDPATH.toString(),
                        mcConfig.getHandcodedPath());
                builder.addVariable(MontiCoreConfiguration.Options.TEMPLATEPATH.toString(),
                        mcConfig.getTemplatePath());
            }

            GroovyInterpreter g = builder.build();
            g.evaluate(script);
        }

    }

    /**
     * Generates the McHammerParser
     * 
     * @param symbolTable AST grammar symbols
     * @param astGrammar AST of the input grammar
     * @param outputDirectory Output directory for the generated parser files
     */
    public void generateMcHammerParser(Scope symbolTable, ASTMCGrammar astGrammar, File outputDirectory) {
        Log.errorIfNull(astGrammar,
                "MCHC0001 Generation of MCHammerParser can't be processed: the reference to the grammar ast is null");
        Log.info("generating McHammerParser!", LOG_ID);

        McHammerParserGenerator.generate(symbolTable, astGrammar, outputDirectory);
    }

    /**
     * Generates the McHammerParser
     * 
     * @param symbolTable AST grammar symbols
     * @param astGrammar AST of the input grammar
     * @param outputDirectory Output directory for the generated parser files
     */
    public void generateMcHammerChecker(Scope symbolTable, ASTMCGrammar astGrammar, File outputDirectory) {
        Log.errorIfNull(astGrammar,
                "MCHC0001 Generation of MCHammerChecker can't be processed: the reference to the grammar ast is null");
        Log.info("generating McHammerChecker!", LOG_ID);

        McHammerCheckerGenerator.generate(symbolTable, astGrammar, outputDirectory);
    }

    /**
     * Generates the McHammerParseTree
     * 
     * @param symbolTable AST grammar symbols
     * @param astGrammar AST of the input grammar
     * @param outputDirectory Output directory for the generated parser files
     */
    public void generateMcHammerParseTree(Scope symbolTable, ASTMCGrammar astGrammar, File outputDirectory) {
        Log.errorIfNull(astGrammar,
                "MCHC0002 Generation of MCHammerParseTree can't be processed: the reference to the grammar ast is null");
        Log.info("generating MCHammerParseTree!", LOG_ID);

        McHammerParseTreeGenerator.generate(symbolTable, astGrammar, outputDirectory);
    }

    /**
     * Generates the McCoder
     * 
     * @param symbolTable AST grammar symbols
     * @param astGrammar AST of the input grammar
     * @param outputDirectory Output directory for the generated parser files
     */
    public void generateMcCoder(Scope symbolTable, File outputDirectory, ASTMCGrammar astGrammar) {
        Log.errorIfNull(astGrammar,
                "MCHC0003 Generation of McCoder can't be processed: the reference to the grammar ast is null");
        Log.info("generating McCoder!", LOG_ID);

        McCoderGenerator.generate(symbolTable, outputDirectory, astGrammar);
    }

    /**
     * Generates the McPrettyPrinter
     * 
     * @param symbolTable AST grammar symbols
     * @param astGrammar AST of the input grammar
     * @param outputDirectory Output directory for the generated parser files
     */
    public void generatePP(GlobalExtensionManagement glex, File outputDirectory, ASTMCGrammar astGrammar) {
        Log.errorIfNull(astGrammar,
                "MCHC0004 Generation of MCPrettyPrinter can't be processed: the reference to the grammar ast is null");
        Log.info("generating McPrettyPrinter!", LOG_ID);

        McCoderPPGenerator.generate(glex, outputDirectory, astGrammar);
    }

    /**
     * Generates some examples on how to use McHammerCoder in an application
     * 
     * @param symbolTable AST grammar symbols
     * @param astGrammar AST of the input grammar
     * @param outputDirectory Output directory for the generated parser files
     */
    public void generateExample(GlobalExtensionManagement glex, File outputDirectory, ASTMCGrammar astGrammar) {
        Log.errorIfNull(astGrammar,
                "MCHC0005 Generation of Examples can't be processed: the reference to the grammar ast is null");
        Log.info("generating Examples!", LOG_ID);

        McHCExampleGenerator.generate(glex, outputDirectory, astGrammar);
    }
}