= Developer Guide BNF Tools = BNF Tools is a BNF editor and IDE based on xText. == Overview == This documentation includes hints related to the deployment of the tools as well as to the implementation of the following features: * Grammar definition and code generation * Validation * Quick-fixing * Generation of content from the BNF * Formatting * Outlining * File import * Deployment as Plugin * Deployment as RCP (Rich Client Platform) == Grammar Definition and Code Generation == The xText grammer is defined in `de.ugoe.cs.swe.bnftools.ebnf/de.ugoe.cs.swe.bnftools.ebnf/EBNF.xtext`. The first rule is the Start e.g.: {{{ EtsiBnf: 'grammar' name=ID ( type='/bnf'? ';' (importSection=ImportSection)? (bnfEntry+=BnfEntry)+ ) | ( type='/delta' ';' (importSection=ImportSection)? (deltaEntry+=DeltaEntry)* ) | ( type='/merge' ';' (importSection=ImportSection)? (mergeEntry+=MergeEntry)* ) ; }}} To turn this into a runable application the .mwe2 file in the same folder must be executed as MWE2 Workflow. After this, the whole project can be executed as an Eclipse Application for testing. == Validation == Validation allows to check for constraints and conditions within the BNF-Document. Validation rules can be defined in Xtend according to the general framework provided by xText in the file `de.ugoe.cs.swe.bnftools.ebnf/de.ugoe.cs.swe.ebnf.validation/EbnfValidator.xtend`, e.g.: {{{ @Check def void checkUnusedRule(Rule rule) { var List references = EbnfAnalysisUtils.findReferences(rule); var List references1 = EbnfAnalysisUtils.findReferences(rule,resourceDescriptions); if ((references.size+references1.size == 0) && (rule.getRulenumber() != 1)) warning(unusedRuleDescription, EbnfPackage$Literals::RULE__NAME, unusedRuleDescription, rule.name); } }}} The parameter can be any entity type from the previously defined grammar and the validation rule will be applied on every entity of this type. If the validation conditions are not met, a warning message will be produced and displayed next to the violating entity Instance in the editor. The other files in the package contain supporting methods for the validation, such as `EbnfAnalysisUtils.findReferences(rule)` or `EbnfAnalysisUtils.findReferences(rule,resourceDescriptions)`, which provide functionality for finding rule references within a BNF file or within a set of BNF files. == Quick-fixing == Quick-fixing can be applied to warnings raised by violations of validation constraints. Quick-fixes can be defined in Xtend according to the general framework provided by xText in the file `de.ugoe.cs.swe.bnftools.ebnf.ui/de.ugoe.cs.swe.bnftools.ui.quickfix/EbnfQuickfixProvider.xtend`, e.g.: {{{ @Fix(EbnfValidator.unusedRuleDescription) def void fixUnusedRule(Issue issue, IssueResolutionAcceptor acceptor) { acceptor.accept(issue, "Remove unused rule", "Delete the unused rule", "upcase.png", [ element, context | var Rule rule = element as Rule; var IXtextDocument xtextDocument = context.getXtextDocument(); var ICompositeNode node = NodeModelUtils.findActualNodeFor(rule); var int offset = node.textRegion.offset; var String nodeText = node.text; var int textLength = nodeText.length - 2; xtextDocument.replace(offset, textLength, ""); ]) } }}} The `@Fix(EbnfValidator.unusedRuleDescription)` annotation identifies the following method as a quick-fix for the corresponding validation warning, i.e. `EbnfValidator.unusedRuleDescription` in this case, which is raised by the validation rule defined above: {{{ warning(unusedRuleDescription, EbnfPackage$Literals::RULE__NAME, unusedRuleDescription, rule.name); }}} The acceptor inside applies the changes in two possible ways: 1. Change the Document itself (as shown in the example). 1. Change the underlying Ecore model. == Generation == Generation allows the user to generate other files from a BNF document. In our case we create an intermediate `.fo` document, that can be transformed into a PDF or an RTF document by using Apache FOP. The generation of the intermediate `.fo` document can be customised in Xtend according to the general framework provided by Xtext in the file `de.ugoe.cs.swe.bnftools.ebnf/de.ugoe.cs.swe.ebnf.generator/EbnfGenerator.xtend`. The `doGenerate` method defines how the file described by a `Resource` and an `IFileSystemAccess` shall be processed in order generate a new file in the target format. The `compile` methods then handle the transformation, for each relevant entity instance and all related entity instances, e.g.: {{{ def void doGenerate(Resource resource, IFileSystemAccess fsa, boolean mode) { var String workspacePath = WorkspaceResolver.getWorkspace(); for (e : resource.allContents.toIterable.filter(EtsiBnf)) { if (e.bnfEntry.size != 0) { fsa.generateFile(e.name + ".fo", e.compile) } } } }}} Based on the generated .fo file a PDF document can be generated with the help of the `de.ugoe.cs.swe.bnftools.ebnf/de.ugoe.cs.swe.ebnf.generator/foToPDF` class, either by providing the .fo file and the output URI without ending or simply by providing the path of the file. To enable this, the `doGenerate` method needs an upgrade to access the filesystem via URIs: {{{ def void doGenerate(Resource resource, IFileSystemAccess fsa, boolean mode) { var String workspacePath = WorkspaceResolver.getWorkspace(); for (e : resource.allContents.toIterable.filter(EtsiBnf)) { if (e.bnfEntry.size != 0) { fsa.generateFile(e.name + ".fo", e.compile) //generate pdf var uri = (fsa as IFileSystemAccessExtension2).getURI(e.name + ".fo"); var String fullUri = workspacePath + uri.path.substring(10, uri.path.length); var File file = new File(fullUri); if (file.exists) { //true -> pdf, false -> rtf if (mode) { FoToPdfOrRtf.createRtfFromFo(fullUri.substring(0, fullUri.length - 3)); } else { FoToPdfOrRtf.createPdfFromFo(fullUri.substring(0, fullUri.length - 3)); } // fsa.deleteFile(e.name + ".fo"); } } } } }}} The Apache FOP libraries need to be added to the build path and the plugin dependencies. == Formatting == Formatting (or pretty-printing) can be used to automatically format the BNF document by inserting white space where appropriate in order to improve the readability of the document. Formatting can be defined in Xtend according to the general framework provided by Xtext by adapting the file `de.ugoe.cs.swe.bnftools.ebnf/de.ugoe.cs.swe.ebnf.formatting/EbnfFormatter.xtend`. The method `configureFormatting(FormattingConfig c)` describes the formatting rules before, after, or between entity instances or keywords, e.g.: {{{ @Inject extension EbnfGrammarAccess override protected void configureFormatting(FormattingConfig c) { c.setLinewrap(0,1,2).before(SL_COMMENTRule); c.setLinewrap(0,1,2).before(ML_COMMENTRule); c.setLinewrap(0,1,1).after(ML_COMMENTRule); var EbnfGrammarAccess f = getGrammarAccess as EbnfGrammarAccess;c.setLinewrap.before(f.ruleRule); c.setLinewrap.before(f.importRule);c.setNoSpace.after(f.ruleAccess.rulenumberINTTerminalRuleCall_0_0_0); } }}} == Outlining == Outlining and labelling are features that show the document structure of the BNF document. Outlining can be customised in Xtend according to the general framework provided by xText in the file `de.ugoe.cs.swe.bnftools.ebnf.ui/de.ugoe.cs.swe.bnftools.ui.outline/EbnfOutlineTreeProvider.xtend`. There a `createChildren()` method with `rootNode` and the BNF entity of the grammar as parameters can be defined to change the outline sequence: {{{ def void_createChildren(DocumentRootNode parentNode, EtsiBnf bnf) { createNode(parentNode,bnf); } }}} Labelling can be customised in Xtend according to the general framework provided by xText in the file `de.ugoe.cs.swe.bnftools.ebnf.ui/de.ugoe.cs.swe.bnftools.ui.labeling/EbnfLabelProvider.xtend` in order to customise what the outline text for an entity should look like, e.g.: {{{ def text(ImportSection sec){'Imports'} }}} == File Imports == File imports allow referencing rules from one BNF document into another. There are two ways for imports, via URIs and via name-space. The BNF grammar uses the URI-based approach. To activate this the lines {{{ fragment= scoping.ImportNamespacesScopingFragmentauto-inject{} fragment= exporting.QualifiedNamesFragmentauto-inject{} fragment= builder.BuilderIntegrationFragmentauto-inject{} fragment= types.[wiki:TypesGeneratorFragmentauto]-inject{} }}} in the `.mwe2` file need to be commented out and the lines: {{{ fragment= scoping.ImportURIScopingFragmentauto-inject{} fragment= exporting.SimpleNamesFragmentauto-inject{} }}} need to be included, after which imports can be defined in the grammar, e.g.: {{{ 'import' importURI = STRING }}} == Generation UI == See the guide at http://flipsomel.wordpress.com/ (skip the @Override annotations). TODO: Transfer relevant instructions. == Deployment as Plugin == The simplest way to deploy the BNF Tools is to deploy them as a plugin: * Right click the xTextProject --> Export... --> Plug-in Development --> Deployable plug-ins and fragments * Choose all parts of the project, *.ebnf *.ebnf.tests *.ebnf.ui and an output location. * Click finish. * Transfer the generated JARs in the output location to the pluigin-folder of the target Eclipse distribution and it should be installed. == Deployment as RCP == A more portable way to deploy the BNF Tools is as a standalone Rich Client Platform (RCP) application. This results in a standalone minimal workbench setup with only the BNF plugin and required dependencies. * Create a new Plug-in Project e.g. `de.ugoe.cs.swe.bnftools.ebnf.product`. Click next, and uncheck ''Generate an Activator'', a Java class that controls the plug-in's life cycle and ''This plug-in will make contributions to the UI''. Also choose ''no'' at ''Rich Client Platform''. Press finish. * Open the `MANIFEST.MF` of the newly created project, go to the ''Overview'' page and choose ''This plug-in in a singleton'', then go to the ''Dependencies'' page and add `org.eclipse.core.runtime`. * Create a product configuration in your product project * On its ''Overview'' page, click ''New'', choose a fitting ''name'' and ''ID'', select the product project as ''defining plug-in'' and `org.eclipse.ui.ide.workbench` as ''application''. * Go back to the `MANIFEST.MF` and open the ''Extensions'' page. There you should now see one ''Extension'' `org.eclipse.core.runtime.products` with a newly created product inside. This should have `org.eclipse.ui.ide.workbench` as ''application'' and the given name of the product configuration as ''name''. * Right-click on the product and create a new ''property'' and optionally give it a customised ''name'' and ''value''. * Go back to the product configuration and its ''Dependencies'' page. * Add all the xText projects and the newly created product, then click ''Add required plug-ins''. * Manually add the plug-ins `org.eclipse.ui.ide.application` and `org.eclipse.core.net`. * To make the generator run properly, manually add also `org.eclipse.xtext.xbase` to the dependencies. * Test your product by running it as a ''Runtime Eclipse Application''. * If there is a missing plug-in you can find it using the ''Validate plug-ins'' option in the run configurations plug-ins page. * Deploy it using ''Export as an Eclipse Product'' in the product configuration.