Knuth's Binary Numbers
In his seminal article on attribute grammars from 1968, Semantics of Context-Free Languages, Knuth provided an example grammar computing the values of binary numbers. The example is a nice illustration of attribute grammars because it is small and simple, and makes use of both inherited and synthesized attributes. This directory contains the corresponding JastAdd implementation.
Knuth's example
Context-free grammar
Knuth uses the following context-free grammar to defines the structure of binary numbers
N ::= L N ::= L "." L L ::= B L ::= L B B ::= "0" B ::= "1"
Examples of binary numbers following this grammar are both integral numbers like 0, 1, 10, 11, 100, and so on, and rational numbers with an integral and a fractional part, like 1.1, 1.01, 10.01, and so on.
In the JastAdd abstract grammar for this example, the nonterminals N, L, and B, are renamed to BinaryNumber, BitList, and Bit. Furthermore, the productions are given explicit names like IntegralNumber, RationalNumber, SingularBitList, PluralBitList, Zero, and One. See BinaryNumber.ast for details.
Attribution
The idea is to compute the values of the binary numbers using an attribute grammar. Here are a number of test cases:
Binary number |
Value (in decimal notation) |
0 |
0 |
1 |
1 |
101 |
5 |
0.1 |
0.5 |
0.01 |
0.25 |
10.01 |
2.25 |
1101.01 |
13.25 |
Knuth provides a couple of different attribute grammars to define the computation. The first one uses synthesized attributes only. The second one, which he argues is closer to the way people usually think about the binary notation, uses both inherited and synthesized attributes. The latter attribution is the one we will illustrate using JastAdd.
In this attribution, each bit has a synthesized attribute value which is a rational number that takes the position of the bit into account. For example, the first bit in the binary number 10.01 will have the value 2 and the last bit will have the value 0.25. In order to define these attributes, each bit also has an inherited attribute scale used to compute the value of a One bit as value = 2scale. So for the bits in 10.01, scale will be 1, 0, -1, -2, respectively.
If we have the values of the individual bits, these values can simply be summed up to the total value. This is done by adding synthesized attributes value both for bit lists and for binary numbers. In order to compute the scale attribute for the individual bits, an inherited attribute scale is added also for bit lists (representing the scale of the rightmost bit in that list), and a synthesized attribute length for bit lists, holding the length of the list.
Knuth uses the abbreviations v, s, and l for the value, scale, and length attributes. The table below shows Knuth's resulting attribute grammar.
Syntactic rules |
Semantic rules |
B ::= "0" |
v(B) = 0 |
B ::= "1" |
v(B) = 2s(B) |
L ::= B |
v(L) = v(B) |
L1 ::= L2 B |
v(L1) = v(L2) + v(B) |
N ::= L |
v(N) = v(L) |
N ::= L1 "." L2 |
v(N) = v(L1) + v(L2) |
The corresponding attribute grammar specified in JastAdd is shown in BinaryNumberValue.jrag. The attributes and equations are exactly the same as in Knuth's example, but using fully spelled out names and (aspect) Java syntax for accessing attributes and the different parts of a production.
Contents of this directory
- BinaryNumbers.zip for downloading the example to your computer
- tools: directory containing all the tools you need to
run this JastAdd project
- junit.jar contains the JUnit framework
- jastadd2.jar contains the JastAdd generation program
- BinaryNumber.ast abstract grammar for the binary numbers language (input to jastadd).
- BinaryNumberValue.jrag: An attribute grammar defining how to compute the value of a binary number.
- build.xml: Ant build file to be
used from a terminal window and from Eclipse
- Use the targets build (default), test, and
clean, when working from a terminal window. For example,
type
ant
generate all java files and compile them
ant test
run all test cases
ant clean
remove all generated files and all the class files for both generated and handwritten java files
- Use the targets gen and cleanGen when working
from eclipse (see Running
JastAdd Under Eclipse):
ant gen
generate all java files
ant cleanGen
remove all generated files and their class files.
- Use the targets build (default), test, and
clean, when working from a terminal window. For example,
type
- AST: package containing AST files generated by JastAdd. This directory is not present when the project is "clean".
- tests: package containing JUnit test cases and related
input and output files
- BinaryNumbersTests.java: test cases for computing the values of some binary numbers. The test cases create ASTs explicitly by new calls.
- testframework: package containing classes useful for testing
- TestAll.java: Used for finding all tesetcases when running tests from a terminal window.
- release.txt Release and version information about the example