The balicomposer Tool

The balicomposer tool is a command-line application that composes Bali grammar files into a single Bali grammar file. Generally, the composer will automatically invoke balicomposer when Bali grammar files (with a .b extension) are specified as operands, but this document describes how balicomposer can be invoked directly. It briefly describes how Bali grammars are composed. The reader should be familiar with the Bali language documentation and the HowTo guide.

Invoking balicomposer

The balicomposer tool accepts one or more Bali grammar files as inputs and it produces a single Bali grammar file as output. The form of the generated output is described in the next section. For now, we describe only the command invocation.

Suppose there are two Bali input grammars to be composed named, respectively, expression.b and statement.b. The result of the composition is to be placed into grammar.b. This can be accomplished with the following command:

balicomposer expression.b statement.b -output grammar.b

That's all there is to it! This produces a new grammar file in grammar.b. In general, there can be as many input grammar files as desired, but there are no other options possible. As a preferred alternative, the composer could also be invoked to perform the same composition. When using the composer, the command invocation is:

composer expression.b statement.b --target=grammar.b

The two invocations are semantically equivalent.

Generated Output

A Bali grammar defines a set of symbols, both terminal symbols and non-terminal symbols. Composing Bali grammars modifies the definitions by merging non-terminal definitions and by overriding terminal definitions.

Terminal symbols. For example, suppose we have a base grammar base.b that defines terminal symbol REQUIRE to be "require" and that a refinement grammar refine.b defines it to be "import". Then the composition of base.b and refine.b, in that order, outputs a definition of REQUIRE with value "import". Reversing the order of composition outputs the other definition. Notice that the order of composition is important!

Non-terminal symbols. Non-terminal symbols are rule names and composing them is a little more complex. First, we'll eliminate the easy cases: It's illegal to compose complex lists and simple lists! That leaves only named productions and sub-productions that can be composed.

A named production is a production of a non-terminal that has an assigned name. When composing productions of such a non-terminal, a later version of a named production overrides any earlier versions. For example, suppose we have the following named productions:

In base.b:     If : "if" Test ThenClause :: IfNode ;
In refine.b:     If : "if" Test ThenClause [ElseClause]:: IfNode ;

Then, the composition of base.b and refine.b will result in the second named production being placed into the definition of non-terminal If in the output grammar:

If : "if" Test ThenClause [ElseClause]:: IfNode ;

Finally, sub-productions of the same non-terminal symbol are simply combined with a set union. For example, suppose we have the following sub-productions:

In base.b:     Statement : If | Require ;
In refine.b:     Statement : While ;

Then the composition of base.b and refine.b will define Statement with three sub-productions as shown below:

Statement : While | If | Require ;

The order of the merged sub-productions is arbitrary.

Example (with Generated Output!)

This section expands on the examples from the previous section. Again, we have two input grammar files, base.b and refine.b. These two files are shown below:

base.b refine.b
    | <#LETTER: ["a"-"z", "A"-"Z"]>
    | <#DIGIT: ["0"-"9"]>

"require" REQUIRE

Statement : If | Require ;
If : "if" Test ThenClause :: IfNode ;
Test : "(" IDENTIFIER ")" :: TestNode ;
ThenClause : "{" Statement "}" :: ThenNode ;
Require : REQUIRE IDENTIFIER :: RequireNode ;
require Statement, Test, ThenClause ;

"import" REQUIRE

Statement : While ;
If : "if" Test ThenClause [ElseClause] :: IfNode ;
ElseClause : "else" ThenClause :: ElseNode ;
While : "while" Test ThenClause :: WhileNode ;

Notice that those non-terminals that are referenced but not defined in refine.b are specified with a require statement! This is not strictly necessary, but can be helpful when debugging. Now, we compose these two files with the following command:

balicomposer base.b refine.b -output grammar.b

The result is shown below. Notice that several empty sections are automatically inserted by balicomposer. This is harmless, though it does unnecessarily increase the size of the output file.

// Automatically generated Bali code.  Edit at your own risk!
// Generated by "balicomposer" v2003.02.17.

// Option block:

// No options blocks in Bali grammar.

// Parser code block:

code {} code

// Token manager declarations:

// No TOKEN_MGR_DECLS defined in Bali grammar.

// Bali tokens:

"import"        	REQUIRE

// Regular expression tokens:

    |  <#LETTER: ["a"-"z", "A"-"Z"]>
    |  <#DIGIT: ["0"-"9"]>

// Java code blocks:

// No JAVACODE blocks in Bali grammar.

// Bali productions:

	: While
	| If
	| Require

	: "else" ThenClause :: ElseNode

	: "if" Test ThenClause [ElseClause] :: IfNode


	: "(" IDENTIFIER ")" :: TestNode

	: "{" Statement "}" :: ThenNode

	: "while" Test ThenClause :: WhileNode

ATS Home Page

Copyright Software Systems Generator Research Group. All rights reserved.
Last modified: Thu May 1 13:01:00 CDT 2003

Jacob Sarvela