Manual
Manual
ECE 3 5 1 L A B M A N U A L
U N I V E R S I T Y O F W AT E R L O O
2 derek rayside & ece351 staff
acknowledgements:
• Prof Paul Ward suggested that we look into something with vhdl to have synergy with ece327.
• Prof Mark Aagaard, as the ece327 instructor, consulted throughout the development of this material.
• Prof Patrick Lam generously shared his material from the last offering of ece251.
• Zhengfang (Alex) Duanmu & Lingyun (Luke) Li [1b Elec] wrote solutions to most labs in txl.
• Jiantong (David) Gao & Rui (Ray) Kong [3b Comp] wrote solutions to the vhdl labs in antlr.
• Aman Muthrej and Atulan Zaman [3a Comp] wrote solutions to the vhdl labs in Parboiled.
• Michael Thiessen [3a Comp] improved the vhdl grammar in Parboiled.
• TA’s Jon Eyolfson, Vajih Montaghami, Alireza Mortezaei, Wenzhu Man, and Mohammed Hassan.
• TA Wallace Wu developed the vhdl labs.
• High school students Brian Engio and Tianyu Guo drew a number of diagrams for this manual, wrote
Javadoc comments for the code, and provided helpful comments on the manual.
Licensed under Creative Commons Attribution-ShareAlike (CC BY-SA) version 2.5 or greater.
http://creativecommons.org/licenses/by-sa/2.5/ca/
http://creativecommons.org/licenses/by-sa/3.0/
Contents
0 Overview 15
Compiler Concepts: call stack, heap
Programming Concepts: version control,
0.1 How the Labs Fit Together 15 push, pull, merge, SSH keys, IDE,
0.2 Learning Progressions 17 debugger, objects, pointers
0.3 How this project compares to CS241, the text book, etc. 19
0.4 Student work load 20
0.5 How this course compares to MIT 6.035 21
0.6 Where do I learn more? 21
0.7 Full-Adder Circuit Example 22
0.7.1 Picturing Git† 24
0.8 What To Do This Week 25
0.9 Metadata 27
0.10 Checklist for Every Lab 27
0.11 How to do these labs 28
0.12 I think I found a bug in the skeleton code 29
0.13 I want to change the skeleton code for my own usage 29
0.14 Testing† 30
0.15 Phases of Compilation† 31
0.16 Engineers and Other Educated Persons 32
4.3.2 Associativity 66
4.4 Transforming BinaryExprs to NaryExprs 67
4.5 Identity Element & Absorbing Element† 68
4.6 Simplify Once 68
4.7 Object sharing in the simplifier 70
4.8 Class Representation Invariants† 71
4.9 Logical equivalence for boolean formulas† 72
4.10 BinaryExpr.examine() is a Higher-Order Function† 73
4.11 Choice of Data Structures for NaryExpr.children† 73
4.12 Evaluation 74
A.1 vhdl program used to illustrate signal declarations and the use of
undefined signals in signal assignment statements. 146
Compiler Concepts: call stack, heap
Programming Concepts: version control,
push, pull, merge, SSH keys, IDE,
debugger, objects, pointers
Overview
vizW
SVG
16 ece351 lab manual [september 2, 2023]
1. W recursive descent
4. F simplifier 6. F parboiled
0.3 How this project compares to CS241, the text book, etc.
lab1
lab2
lab3
lab4
lab5
lab6
lab7
lab8
lab9
lab10
●
30
● ● ●
●
Hour
20
● ● ● ● ● ●
●
● ● ●
10
● ● ●
●
●
●
●
●
●
0
lab0 lab1 lab2 lab3 lab4 lab5 lab6 lab7 lab8 lab9
Lab#
overview 21
Cout
Cin
overview 23
Cin
Cout
S
24 ece351 lab manual [september 2, 2023]
Prof Computer
Working Copy
commit
Student Computer
push
GitLab Server
commit
pull skeleton master
TA Computer
push
Working Copy Local Student Repo Student Repo
pull
0.9 Metadata
Your workspace has a directory named meta that contains the follow-
ing two files in which you can describe a few things about how your
work on the lab went.
collaboration.txt To record your collaborators. Each line is a triple of You must edit this file for every lab,
lab number, collaboration role, and userid. Legal values for collab- even if all of your collaborations were
just conversation.
oration roles are: converser, partner, mentor, protege. The role field
describes the role of the other person, so lab2 mentor jsmith says that
J Smith was your mentor for lab2. Similarly, lab3 protege jsmith says
that J Smith was your protégé for lab3 (i.e., you were his mentor).
Both parties are required to report collaborations. If you collab-
orated with more than one person on a lab then you should put
multiple lines into this file for that lab: one line for each collabora-
tor on each lab.
hours.txt Estimate of the hours you worked on each lab. This file will These data will be used solely for
have a line for each lab like so: lab1 5 (indicating five hours spent the staff to assess the difficulty of the
labs. This assessment will be made in
on lab 1). For pre-lab / computing environment time, use lab0. aggregate, and not on an individual
basis. These data will not be used to
assess your grade. However, we will
0.10 Checklist for Every Lab not mark your lab until you report an
estimate of your hours.
cd ~/git/ece351-labs
git checkout main
git pull
git pull skeleton main
git submodule update
manually resolve any conflicts
Update meta/collaboration.txt
Update meta/hours.txt
git add meta
git commit -m ’updated metadata for labX’
git push
28 ece351 lab manual [september 2, 2023]
The lab manual will tell you what files you need to edit, what li-
braries you should use, and what tests to run. The lab manual will
also explain what you need to do at each point in the code, and the
background concepts you need to understand the lab. Most of the
dagger († ) sections in the lab manual are to explain these background
concepts. Every place in the skeleton code that you need to edit is
marked by both a Todo351Exception and a TODO marker. We will dis-
cuss the next week’s lab in class every Friday.
Despite this clear and explicit instruction, some students have dif-
ficulty getting started on these labs. Why? What is missing from the
instructions described above? The lab manual doesn’t tell you the order In ece250 you did object-oriented
programming in the small. That is, you
in which you should edit the files. There is an important reason for this: defined structures that spanned one
execution order is the wrong way to think about developing object- or two classes and operations on those
structures that spanned one or two
oriented software (most of the time). The right way to think about
methods. The programs you worked
object-oriented software is to focus on developing cohesive and ex- on were perhaps several hundred lines
tendible modules (classes). In this way object-oriented programming long.
In ece351 you will work on a code
(in the large) is mentally quite different from procedural program- base that is over 8,000 lines of code, of
ming (in the small). In procedural programming (in the small) one which you will write about 1000 lines,
at an average rate of about 100 lines per
thinks primarily about the order in which the steps are performed.
week. The structures we will work with
Now don’t misunderstand this to mean that execution order are defined across dozens of classes,
doesn’t matter: it does. It’s just that execution order is generally a and the operations on those structures
are similarly defined across dozens of
separate design concern from modularity and extensibility. The best methods. This is the first time in the
way to understand the execution order of a large object-oriented pro- ece curriculum that you are exposed to
programming in the large. At this scale,
gram is to run it and observe it in the debugger or via some other
modularity and code structure really
tracing mechanism (e.g., printf). matter.
If you want to figure out what code you should edit first, run the By the standards of modern industrial
software development, 8,000+ lines is
test harness and see where an exception is thrown. Fix that. Then run just approaching medium sized. The
again, get another exception, etc.. The ‘fix’ step might be quick and code structuring ideas you will learn
in these labs can take you up to maybe
easy, or it might require reading several pages of the lab manual to
100,000 lines: beyond that you will need
understand what needs to be done at that particular point. Remem- new ideas and modularity mechanisms.
ber, the lab manual describes everything that needs to be done and
where it needs to be done, it just doesn’t describe the order in which
you should do it. Aligning your editing order with the program exe-
cution order is one way to guide your work that will help you build
an understanding of the code.
Thinking on your own to develop an understanding of the skele-
ton code is an important part of these labs. I promise you that it takes
less time and effort to study 8,000+ lines of code than to write it from
scratch.
overview 29
You are welcome to do what you like with the skeleton code. Our
recommendation if you want to make a change is the following:
a. Report the change you want to make (in the forum, or to a staff
member). Preferably by generating a patch.
b. We tell you that the change is misguided and you really want to
do something else.
c. We say thanks and we patch the skeleton code with the change so
everyone can use it. Potentially you earn bonus marks for partici-
pation.
If you make the change yourself without reporting it then you lose
out on class participation points, and if someone else reports the
change and we go to apply a patch the patch will fail on your code.
This might or might not end up being a problem for you.
You may change the skeleton code for your own usage. Perhaps
you have a better idea of how to write something. If you make such
changes, be careful to not create more problems than you solve. Some
things to look out for include:
• Breaking some other code that depends on the code you are
changing.
0.14 Testing†
Program testing can be used to show the
Just because your code passes all of the tests does not mean it is presence of bugs, but never to show their
correct: there could be some as of yet unidentified test that it does absence! — Edsger W. Dijstra
not pass. Worse, that not-yet-identified test might occur during some
lab later in the term.
This might be the first course in which you have to write a ‘real’
program: that is, a non-trivial program that you will have to depend
on in the future. First, the labs are non-trivial: The total size of the
code for this course is about 8500 lines, of which you will have to
write about 1500, and roughly 7000 will be provided for you. Second,
the labs are cumulative: Each week you will run some labs that you
wrote in the past. At the end of the term you will run all of them
together. So testing will be important.
The integers, for example, are totally ordered. We expect the com-
pareTo() method in Java to represent a total order.
In a previous offering of this course the
staff provided tests that only looked
A good test suite will contain some of everything mentioned
at general properties and none that
above: test inputs that are generated manually and automatically, examined specific input output pairs.
both systematically and randomly; evaluations that look at specific It turned out that students could write
code that had the general property
input/output pairs and at general properties. without actually computing anything
useful. They became unhappy when
they discovered that their code that
0.15 Phases of Compilation† passed the staff-provided tests did not
actually work when they wanted to run
it on a future lab.
Type Checker
Optimizer
2, 4, 7, 10, 11
Code Generator
7, 8, 11
32 ece351 lab manual [september 2, 2023]
The ability to quickly find relevant information. Infor- Simplicity and elegance are unpopular
mation is more accessible now than at any previous point in human because they require hard work and
discipline to achieve and education to
history. There are a wide variety of sources available to you: the be appreciated.
course notes, the lab manual, old exams, the skeleton code, the rec- – Edsgar W. Dijkstra, 1997
ommended text books, other text books, lecture notes and slides and
videos from other professors, etc. You should be facile in using all of
these sources to find what you are looking for.
Books, in particular, have helpful features for navigating them,
such as the table of contents, the index, and the preface or introduc-
tion. You should know how to use these features.
The ability to quickly assess what information is rele- Fools ignore complexity; pragmatists
vant. As old-time engineers used to say, there is always more heat suffer it; experts avoid it; geniuses
remove it.
than light. Learn to see the light without being overwhelmed by the — Alan Perlis
heat.
When doctors, lawyers, accountants, and other professionals assess
a case study problem, the first order of business is to discern what
the relevant facts and issues are. Those professions explicitly train
their people to separate the wheat from the chaff. Engineering educa-
tion, especially in the first two years, is often guilty of spoon-feeding
its students with only and all of the relevant information, and hence
developing a sense of intellectual complacency and entitlement in its
students.
For example, you should be able to take a list of topics to be cov-
ered from the course outline and be able to use that to determine
which sections of the text book are relevant and which questions
from old exams are applicable.
[lab 0] overview 33
The ability to accurately assess the credibility of a source. There are two ways of constructing a
software design. One way is to make
Information comes from a variety of sources. Some of them are more
it so simple that there are obviously no
credible than others. An educated person knows, for example, that deficiencies. And the other way is to make
a report from the Transportation Safety Board of Canada is more it so complicated that there are no obvious
deficiencies.
likely to be accurate than a newspaper report — more likely, but not — C.A.R. Hoare, 1982
infallibly so. Turing Award Speech
The ability to think deep thoughts. In elementary school you Back where I come from we have universi-
learned to multiply positive integers. In middle school you learned ties — seats of great learning — where men
go to become great thinkers. And when they
to multiply all integers (including negative ones). In high school you come out, they think deep thoughts, and
learned to multiply matrices, perhaps in two different ways: cross- with no more brains than you have.
— The Wizard of Oz, 1939
product and dot-product. In first year you learned to write programs Of course, in the modern world, women
that multiply matrices. In this course you will learn a bit about how now go to (and graduate from) univer-
to design and implement programming languages in which someone sities in greater numbers than men.
We consider that the input and outputs of a circuit are a set of wave-
forms. We use the W language for expressing waveforms in text files.
Figure 1.1 shows an example W file and Figure 1.2 gives the gram-
mar for W .
Crafting: §4.3
Figure 1.2 lists the grammar for the language W in Extended Backus-
Naur Form (ebnf). A grammar is formal specification of the syntax bnf was developed by John Backus
of a language. A grammar tells us which sentences are included in while working on what became the
Algol-60 language. Backus lead the
the language and which are excluded. For certain classes of gram- Fortran compiler team at ibm in the
mars, we can systematically derive a program to recognize and parse 1950s, which was the first commercially
successful compiler.
sentences written in the language.
The grammar in Figure 1.2 also includes the lexical specification of
the language W : that is, it also tells us what sequences of characters
make legal tokens (words). For example, it tells us that an identifier People do not usually have numerals
(i.e., a name) must start with an alphabetic character, but may con- in their names. A notable exception
is former New York Times reporter
tain numerals or underscores in subsequent positions. Some times Jennifer 8. Lee. She says that many
grammars do not explicitly include a lexical specification, and instead computer programs prohibit her middle
name, forcing her to write out ‘Eight’.
assume that characters not separated by whitespace form legal tokens. She chose the middle initial 8. because
as a teenager she realized that there
were about 10,000 other ‘Jennifer Lee’s
in the United States.
36 ece351 lab manual [september 2, 2023]
There are a number of important concepts and notations for un- Examples are given with respect to
derstanding grammars and ebnf: the grammar for the W language in
Figure 1.2.
Production Rules: Each line with an arrow (→) represents a produc- The name ‘production rule’ is some-
tion rule. A rule has a right-hand side (rhs) and a left-hand side times shortened to just ‘production’ or
‘rule’.
(lhs). We say the the lhs derives the rhs, by which we mean that
the rhs can be substituted for the lhs. The lhs will always be a
single non-terminal, whereas the rhs can be some combination of
terminals and non-terminals.
Terminals: A terminal is a symbol that cannot be derived any further. e.g., colon (:), semi-colon (;), zero (0),
one (1), and underscore (_)
Non-terminals: A non-terminal is a symbol that can be derived. Ev- e.g., Program, Waveform, Id, Char, Digit,
ery non-terminal must appear on the lhs of at least one produc- and Bits
tion.
Alternation: The bar (|) character indicates alternatives. e.g., Bit → ‘0’ | ‘1’
says that a Bit can be a zero or a one.
Repetition: The star (∗) and plus (+) characters are used to indicate e.g., Program → Waveform+
repetition. Star means zero or more, whereas plus means one or says that a Program derives one or
more. The plus, star and bar are part of Extended Backus Naur Form more Waveforms.
(ebnf), but are not part of regular Backas Naur Form (bnf). We
will learn how to convert from ebnf to bnf in [N 2.71].
Derivation: Consider the input W program A: 1 0. Let’s derive this
string from the W grammar in Figure 1.2. In this example we are doing a leftmost
Program → (Waveform)+ top-level rule derivation: that is, we are expanding
+
the leftmost non-terminal at each step.
→ (Id ‘:’ Bits ’;’) derive Waveform If we expanded Bits before Id then it
→ (‘A’ ‘:’ Bits ’;’)+ derive Id would be a rightmost derivation.
→ (‘A’ ‘:’ ‘1’ ‘0‘ ’;’) + derive Bits Pragmatics: §2.1.3, p.48
Crafting: §4.1.1, §4.1.2
→ ‘A’ ‘:’ ‘1’ ‘0‘ ’;’ reached end of input
The recursive descent recognizer and parser that you will write
in this lab will work in this way: it will descend from the top of
the grammar, recursively if necessary (the grammar of W is not
recursive, but the grammar of F in lab3 is).
1.1.2 Derivations†
Pragmatics: §2.1.3, p.48 + j1
A derivation is how we show, on paper, that an input string can be Tiger: j1
recognized by a grammar. We start with the top production in the Crafting: §4.1.1, §4.1.2
grammar and do substitutions until we derive the input string. In
most textbooks this is done algebraically, but it can also be done
graphically by drawing the partial ast at each step of the derivation.
Here we will show both ways.
Consider the input string ‘1+2+3’ and the following grammar, with
productions numbered for easy reference:
1. S → E
2. E → E+E
3. E → I NT
Here is a derivation of that input string with that grammar:
S E
S → E start at the top
S
E E
→ ( E + E) substitute by rule 2
E + E
E + E
→ (( E + E) + E) substitute by rule 2
E + E
E + E 3
1 2
→ ((1 + 2) + 3) substitute by rule 3 (3 times)
38 ece351 lab manual [september 2, 2023]
S E
S → E start at the top
S
E E
→ ( E + E) substitute by rule 2
E + E
E + E
→ ( E + ( E + E)) substitute by rule 2
E + E
1 E + E
2 3
→ (1 + (2 + 3)) substitute by rule 3 (3 times)
Since there are multiple derivations (i.e., parse trees) for the same
input string, we say that this grammar is ambiguous. [N 2.4]
Sources:
Recursive descent is a style of writing parsers (or recognizers) by hand ece351.w.rdescent.WRecursiveDescentRecognizer
(i.e., without using a parser generator tool). In this style we make a Libraries:
ece351.util.Lexer
method for each non-terminal in the grammar. For recognizers these
Tests:
methods return void. ece351.w.rdescent.TestWRDRecognizerAccept
The bodies of these non-terminal methods consume the input ece351.w.rdescent.TestWRDRecognizerReject
one token at a time. Kleene stars (*) or plusses (+) become loops.
40 ece351 lab manual [september 2, 2023]
these toString() methods just return a string: they do not actually print
to the console nor to a file. Pretty-printing is the name for the inverse Once we have a function and its inverse
we can test that f 0 ( f ( x )) = x for any
of parsing, and does not necessarily involve actually printing the input x.
result to an output stream. Whereas parsing constructs a tree from a
string, pretty-printing produces a string from a tree.
parse the pretty-printed output, and then compare the ast’s from the
two parses to see that they are the same.
[lab 1] recursive descent parsing of W 41
For the W File below the corresponding AST constructed will be:
W File AST
WProgram
+ waveforms: ImmutableList<Waveform>
[Waveform,Waveform]
Waveform Waveform
A [1,0,1] B [0,1,0]
1 0 1 0 1 0
Waveform
1.3
1.1 Lexer Pretty
Recursive Printer
Simple
Descent
Wprogram
Recognizer
Recursive
Descent
Recongnizer
Accept Reject
Double Rectangle
Accept Reject
Recursive
Descent Parser
1.2 Recursive
Descent Recognizer
1.4 Recursive
Descent Parser
Class
TXT Files
that needed to be
imported or exported Method
1.8 Evaluation
The last pushed commit before the deadline is evaluated both on the
shared test inputs and on a set of secret test inputs according to the
weights in the table below. Note that you don’t earn any points
for the rejection tests until some of the
corresponding acceptance tests pass.
Shared Secret Also, you don’t earn any points for the
TestWRegexAccept 10 5 parser until the TestWRDParserBasic
tests pass.
TestWRegexReject 5 5
TestWRDRecognizerAccept 15 5
TestWRDRecognizerReject 5 5
TestWRDParserBasic 5 0
TestWRDParserAccept 20 10
TestWRDParserReject 5 5
1.9 Reading
46 ece351 lab manual [september 2, 2023]
The file TestObjectContract contains some code stubs for testing the Exercise 2.3.2 What would happen
object contract. In order to fill in those stubs correctly you might if every hashCode method returned a
random value each time it was called?
need to read and understand the code in TestObjectContractBase.
What is the default behaviour of the equals and hashCode meth- The == (‘double equals’ or ‘equals
ods? In other words, what happens if you do not implement those equals’) comparison operator compares
the memory addresses of the objects
methods for your objects? Consider the code listings in Figure 2.4. referred to by the two variables.
What value is printed out by each statement?
2.3.3 Reading
http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object)
http://www.artima.com/lejava/articles/equality.html
http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals.html
Implementation of java.util.AbstractList.equals()
Effective Java 3 3
J. Bloch. Effective Java. Addison-Wesley,
§0.14 of this lab manual on Testing 2001
50 ece351 lab manual [september 2, 2023]
2.4 Evaluation
The last pushed commit before the deadline is evaluated both on the
shared test inputs and on a set of secret test inputs according to the
weights in the table below.
We provide you with about 30 W files for these equations.
W2SVG W2SVG2W
2.1 Write a translator from W to svg 2.2 Write a translator from svg to W
TestObjectContract
TransformW2SVG
ObjectContract
Base
LAB 1
W File
1.1
Regular Expression 1.4
Recognizer
Recursive Descent
Parser
Waveform WProgram
1.3 1.3
Pretty Printing Pretty Printing
LAB 2
2.1 2.2
TransformW2SVG TransformSVG2W
SVG File
Recall that ‘ast’ stands for abstract syntax tree, and is the important
Exercise 3.1.2 For the examples
information that we want to remember from the parse tree above. above, also draw the corresponding
This important information is the structure of the tree and the inter- ast. Both the call tree and the resulting
ast depend on the input.
esting nodes. Things like term, factor, and parentheses are ways that
the input string communicates its structure to the parser: the ast
doesn’t need to retain those things, just the structure itself.
FProgram1
FProgram
AssignmentStatement
AssignmentStatement1
outputVar = X
expr = OrExpr outputVar expr
left = A
X OrExpr1
right = B
left right
VarExpr1 VarExpr2
A B
F File AST
FProgram
+ formulas: ImmutableList<AssignmenStatement>
[AssignmentStatement]
AssignmentStatement
+ outputVar: VarExpr
+ expr: Expr
x <= a or ( b and c );
VarExpr OrExpr
+ identifier: String + left: Expr
+ right: Expr
x
VarExpr AndExpr
+ identifier: String + left: Expr
+ right: Expr
a
VarExpr VarExpr
+ identifier: String + identifier: String
b c
20 BinaryExpr b = new AndExpr(); // why isn’t the type of e AndExpr? Exercise 3.1.8 Is there any aliasing
21 Expr[] a = new Expr[3]; // an empty array of size 3 occurring in the main method? If so,
what is it?
22 // what is the type of the object stored in each element of the array?
23 a[0] = b;
24 a[1] = new OrExpr();
25 a[2] = b.newBinaryExpr(null,null); // monomorphic call site
26 for (int i = 0; i < a.length; i++) {
27 Expr e = a[i];
28 System.out.println(e.operator()); // polymorphic call site
29 System.out.println(e.toString()); // mono or polymorphic?
30 }
31 }
32 }
[lab 3] recursive descent parsing of F 57
A monomorphic call site is one where the dynamic dispatch will always
resolve to the same target. A polymorphic call site is one where the
dynamic dispatch might resolve to a different target each time the
call is executed.
The Java compiler/runtime system inserts code like the following
at each potentially polymorphic call site:
if (a[i] instanceof AndExpr) {
return AndExpr::operator(); }
else if (a[i] instanceof OrExpr) {
return OrExpr::operator(); }
equals: Two objects are equals if any computation that uses either one
will produce identical results.1 This can only be true if the objects 1
B. Liskov and J. Guttag. Program
are immutable (i.e., the values stored in their fields do not change). Development in Java: Abstraction, Spec-
ification, and Object-Oriented Design.
isomorphic: We will say that two objects are isomorphic if they have Addison-Wesley, 2001
the same elements and similar structures. For example, we will
consider the expressions X or Y and Y or X to be isomorphic: they
[lab 3] recursive descent parsing of F 59
are permutations of the same essential structure. Any two objects equals ⇒ isomorphic
that are equals are also isomorphic, but isomorphic objects are not
necessarily equals. For example, the expressions X or Y and Y or X
are isomorphic but not equals.
equivalent: We will say that two objects are equivalent if they have You will not be implementing equiva-
the same meaning, but possibly different structures and possibly lent this term, but you will eventually
(not for this lab) need to understand
different elements. For example, the expression 1 is equivalent what it means and how we have imple-
to the expression X or !X: they have the same meaning but totally mented it for FProgram.
different syntax. Any two objects that are isomorphic are also isomorphic ⇒ equivalent
equivalent, but not necessarily vice versa (as in this example).
Lexer FRecursive
VarExpr
DescentParser
FProgram
ConstantExpr FRecursive
Descent
Object FRDParser f.ast.*
Recongnizer
UnaryExpr ContractF
Assignment
Statement
FRDParser common.
Accept Reject
Commutative Basic ast.*
BinaryExpr
3.4 Write a pretty-
3.2 Write a recursive printer for the ast
3.5 Write Equals and Isomorphic 3.6 Write a recursive-
-descent recognizer for F
for the F ast classes descent parser for F
3.10 Evaluation
The last pushed commit before the deadline is evaluated both on the
shared test inputs and on a set of secret test inputs according to the
weights in the table below.
The testing equation for this lab is:
∀ AST | AST.equals(parse(prettyprint(AST)))
At present we have about 80 ast’s to plug into this equation. We
might explore some mechanical techniques to generate more ast’s. One of the great benefits of having a
testing equation is that we can separate
out the correctness condition (the
Current New equation) from the inputs. Then we can
TestFRDRecognizerAccept 5 5 look for other techniques to generate
inputs. By contrast, if we test with
TestFRDRecognizerReject 5 5 just specific input/output pairs the
TestFRDParserBasic 5 5 correctness condition is that we get the
output for that input.
TestFRDParser 30 10
TestObjectContractF 20 10
1 Introduction to Objects
6 Reusing Classes
7 Polymorphism
If you are not already familiar with
Some topics you should be comfortable with include: these topics then this lab is going to
take you longer than five hours. Once
you get through these background top-
• inheritance / subtyping / subclassing ics then the rest of the course material
• polymorphism / dynamic dispatch should be fairly straightforward.
• objects vs. variables, dynamic vs. static type
• type tests, casting
4.2 Confluence†
1 and 0
These are things you’ve probably learned and forgotten many times.
Turns out they are actually important in this course.
The term binary operators refers to the grammatical fact that these
operators take two operands (arguments) —- not to the semantic is-
sue that some operators apply to boolean (binary) values. For exam-
ple, conjunction (logical and) is binary because it takes two operands,
not because it operates on true/false values.
4.3.1 Commutativity
Changing the order of the arguments doesn’t matter, e.g.:
x+y = y+x
Addition, multiplication, conjunction (and), disjunction (or) are all
commutative. Subtraction and division are not commutative: the
order of the operands (arguments) matters.
4.3.2 Associativity
Wikipedia says it nicely:1 1
http://en.wikipedia.org/wiki/
Associative_property
Within an expression containing two or more occurrences in a row of
the same associative operator, the order in which the operations are
performed does not matter as long as the sequence of the operands is
not changed. That is, rearranging the parentheses in such an expres-
sion will not change its value. Consider, for instance, the following
equations:
1 + (2 + 3) = (1 + 2) + 3
Addition, multiplication, conjunction (logical and), disjunction (logi-
cal or) are all associative.
Subtraction, division, exponentiation, and vector cross-product are
not associative. The terminology can be a little bit confusing here.
On the one hand we say that subtraction is not associative, and on the
other hand we say it is left associative because:
x − y − z = ( x − y) − z
Exponentiation is right associative:
z z)
x y = x (y
Saying that an operator is not associative means that it is either left
associative or right associative.
[lab 4] circuit optimization: F simplifier 67
+ + +
x + + z y +
+
y z x y x z x y z
B+ Y N+ Y
N+
X Z X Z X Y Z
7→ 7→
1 Fold Identity Elements NaryExpr.foldIdentityElements()
Implement NaryExpr.singletonify()
x·1 7→ x
before implementing folds. Why?
x+0 7→ x
!0 7→ 1
!1 7→ 0
!!x 7→ x
x ·!x 7→ 0
x +!x 7→ 1
5 Deduplication NaryExpr.removeDuplicates()
x + x 7→ x
x · x 7→ x
x + ( x · y) 7→ x Is x a VarExpr or an NaryExpr?
The latter case is harder.
x · ( x + y) 7→ x
( a + b) + (( a + b) · y) 7→ a + b
Figure 4.5: Simplifications for F pro-
grams
70 ece351 lab manual [september 2, 2023]
Figure 4.6 was drawn by some students to help you visualize which
objects are shared between the original ast and the simplified ast.
The general pattern is that the leaves of the tree will be re-used
(shared), whereas the interior nodes will be replaced.
Before
<=
x or
a 1
Objects in Memory
x <= <= a 1
After
<=
Legend
AST Path
Reference
x 1
(x + y) +z = f x+ (y + z) = f !(!x · !y · !z) = f
0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1 0 1 0 1
0 1 1 1 0 1 1 1 0 1 1 1
1 0 0 1 1 0 0 1 1 0 0 1
1 0 1 1 1 0 1 1 1 0 1 1
1 1 0 1 1 1 0 1 1 1 0 1
1 1 1 1 1 1 1 1 1 1 1 1
Figure 4.8: Equivalent truth tables
We can see from examining the three truth tables that these three
formulas are equivalent. Great. But the number of rows in each truth
table is an exponential function of the number of variables in the
formula. For example, these formulas have three variables and eight
rows in their truth tables: 23 = 8.
What to do? This is a hard problem. As discussed in the Course
Notes §0 we have three main options: implement an NP-complete al-
gorithm ourselves; use a polynomial time approximation; or convert
the problem to sat and ask a sat-solver for the answer. If you look Our translation to sat occurs via an
at the skeleton code in FProgram.equivalent() you will see that it uses intermediate language named Alloy
(http://alloy.mit.edu), which is in
this last approach, and this is the approach that we generally recom- turn translated to sat. We found it
mend you follow in your future career (unless there are well known convenient to work this way, but it
would also be easy to translate this
and accepted polynomial time approximations for the problem you problem to sat directly.
are trying to solve). The sat solver that Alloy is config-
For this particular problem of computing the equivalence of ured to use in this case is sat4j, which
is open-source, written in Java, and
boolean formulas there is a fourth option: translate the formulas used by Eclipse to compute plugin
into a standardized form, and then compare that standardized form. dependencies.
This is the approach that we took above when we converted the bi-
nary expressions to sorted n-ary expressions. In the case of boolean
formulas, the most common standardized form is reduced, ordered
binary decision diagrams (or bdd for short).2 A bdd is essentially a 2
R. E. Bryant. Graph-based algorithms
compressed form of a truth table. for boolean function manipulation.
IEEE Transactions on Computers, C-
Two equivalent boolean formulas will have the exact same bdd 35(8):677–691, Aug. 1986
representation. Constructing a bdd for a boolean formula is, in the There will be no exam questions on
worst case, an exponential problem, but in practice usually runs bdds. This material is just here for
your interest. Boolean formulas are of
fairly quickly. bdds are commonly used for hardware verification fundamental and intrinsic interest both
and other tasks that require working with boolean formulas. in theory and in practice.
[lab 4] circuit optimization: F simplifier 73
// no differences
return true;
}
4.12 Evaluation
In the unlikely, but not impossible,
The last pushed commit before the deadline is evaluated both on event that you implement a better
the shared test inputs and on a set of secret test inputs according to simplifier than the staff has then your
simplifier might produce spurious
the weights in the table below. The testing equations are as follows. failures while evaluating the correctness
First, the simplifier should always produce an equivalent F program, equation. Talk to us to rectify the
situation and get some bonus marks.
for all inputs:
originalAST.simplify().equivalent(originalAST)
Second, the simplifier should be idempotent:
originalAST.simplify().equals(originalAST.simplify().simplify())
Finally, for a select set of F programs (opt*.f), the simplifier should
perform some specific transformations:
originalAST.simplify().isomorphic(staffSimplifiedAST) Note that you do not earn any marks
for this lab until TestObjectContractF
from lab3 passes. As you can see, the
Shared Secret testing equations for this lab depend
TestSimplifierEquivalence 30 10 on having the object contract correctly
TestSimplifier2 40 20 implemented for the F ast classes. If
your object contract implementation is
buggy then we cannot trust the results
of the testing equations.
Compiler Concepts: parser generators,
Parsing Expression Grammars (peg),
push-down automata
Programming Concepts: domain
specific languages (dsl): internal vs.
external, debugging generated code,
Lab 5 stacks
Files:
In this lab you will write a new parser for W . In lab1 you wrote ece351.w.parboiled.WParboiledRecognizer
a parser for W by hand. In this lab you will write a parser for W ece351.w.parboiled.WParboiledParser
The purpose of this lab is for you to learn how to use Parboiled. Learning progression: repetition with
This lab is specifically structured to facilitate this learning. You are increasing complexity. In a traditional
compilers project, without learning
already familiar with the input language W and the host language progression, you would have to learn
Java. W is an even simpler language than is used to teach Parboiled everything simultaneously in one
big lab: the source language (W ), the
on its website. The only new thing in this lab is Parboiled. host language (Java), and the parser
Once you learn to use one parser generator tool it is not too hard generator’s external dsl. Moreover, the
to learn another one. Learning your first one is the most challenging. source language would be more like
our toy subset of vhdl, which we won’t
The other reason why we are using Parboiled is that it is like a see until after midterm week, rather
pushdown automata because its main storage mechanism is a stack, than a simple regular language like W .
Even the pedagogical example on the
and so it reinforces that theoretical concept. Parboiled website is as complicated as
F (a simple context-free language).
Rule Constructors. These methods are used to describe both the Recall that in previous labs there was
grammar and the lexical specification of the language that we wish a separate Lexer class that encoded
the lexical specification for W (and
to recognize or parse, and we can subdivide this group this way: F ). Some parser generator tools have
EBNF Parboiled separate dsl’s for the lexical and
syntactic specifications of the input
* ZeroOrMore() language. In Parboiled, by contrast, we
+ OneOrMore() specify the tokenization as part of the
? Optional() grammar.
ebnf = Extended Backus Naur Form.
| FirstOf() similar but importantly different This is the name of the notation used to
Sequence() no explicit character in EBNF specify the grammars for W (Figure 1.2)
and F (Figure 3.1).
Regex Parboiled
[ab] AnyOf("ab") Regular expressions are often used for
[^ab] NoneOf("ab") lexical specifications.
a Ch(’a’) or just ’a’
For this lab we will specify whitespace
[a-z] CharRange(’a’, ’z’) explicitly. In the next lab we will
IgnoreCase() no regex equivalent learn how to specify the whitespace
implicitly, which makes the recognizer
EOI special char for end of input rules look a bit less cluttered.
W0() optional whitespace (zero or more)
W1() mandatory whitespace (at least one)
Access to input. A recognizer doesn’t need to store any of the input
that it has already examined. A parser, however, often saves sub- The match() method is in Parboiled’s
strings of the input into an ast. The match() method returns the BaseActions class, which is the super-
class of BaseParser.
substring that matched the most recently triggered rule. Exercise 5.1.1 Draw a uml class
diagram for WParboiledRecognizer and
WParboiledParser.
[lab 5] parsing W with parboiled 77
Let’s add actions to our recognizer from Figure 5.1 to make a parser.
Figure 5.2 lists code for the actions. The general idea is that we wrap
every recognizer rule in a Sequence constructor, and then add new
clauses to manipulate the stack: i.e., a clause with one of the push,
pop, peek, swap, dup, etc., commands.
Figure 5.5 augments the listing of Figure 5.2 with some debugging debugmsg() and checkType() are
clauses using the debugmsg() and checkType() methods. If you want defined in BaseParser351, which is a
superclass of our recognizer/parser
to inspect memory while your recognizer or parser is executing then classes and a subclass of Parboiled’s
use one of the debugmsg() or checkType() methods and set a break- BaseParser.
Rule constructors are executed once in
point inside that method. Setting a breakpoint inside a rule constructor a grammar analysis phase in order to
will not do what you want. generate code that will actually process
input strings.
78 ece351 lab manual [september 2, 2023]
Parboiled works with Parsing Expression Grammars (pegs). pegs are Exercise 5.4.1 What language
slightly different than Context Free Grammars (cfgs). The important feature can cfgs represent that pegs
cannot?
difference is that pegs cannot represent ambiguity, whereas cfgs
can. We never want ambiguity in programming languages, so this
‘feature’ of cfgs is not much of a feature in our context. http://en.wikipedia.org/wiki/
Parsing_expression_grammar
cfgs were originally developed by linguists to model natural lan-
http://en.wikipedia.org/wiki/
guages, which often contain grammatical ambiguities. Only after Context-free_grammar
linguists had successfully applied the idea of cfgs in their natural
language context did computer scientists try using cfgs for program-
ming languages.
80 ece351 lab manual [september 2, 2023]
You might get a weird error message that Parboiled has difficulty cre-
ating the parser class. If so, see if your rule constructors are throwing
exceptions. For example, the skeleton code ships with stubs like this: You need to get rid of all of these stubs
and replace them with code that does
public Rule Program() { something sensible — or just return null.
// TODO: 1 lines snipped If you leave the exception throwing in,
even in a rule constructor you don’t
throw new ece351.util.Todo351Exception(); call, Parboiled will complain.
}
5.7 Evaluation
The last pushed commit before the deadline is evaluated both on the
shared test inputs and on a set of secret test inputs according to the
weights in the table below.
5.8 Reading
JavaCC & SableCC: (two other parser generators) You will not be tested on specifics of
Tiger Book §3.4 JavaCC and SableCC, but you should
know of their existence and that they
Note that both JavaCC and SableCC require the grammar to be use an external dsl for specifying the
defined in an external dsl (i.e., not in Java). grammar.
Compiler Concepts: parser generators,
Parsing Expression Grammars (peg),
push-down automata
Programming Concepts: domain
specific languages (dsl): internal vs.
external, debugging generated code,
Lab 6 stacks
Tests:
This lab is similar to some previous labs: we will write a recognizer ece351.f.parboiled.TestFParboiledRecognizerAccept
and parser for F , this time using Parboiled. ece351.f.parboiled.TestFParboiledRecognizerReject
ece351.f.parboiled.TestFParboiledParserBasic
ece351.f.parboiled.TestFParboiledParser
ece351.f.parboiled.TestFParserComparison
6.1 Write a recognizer for F using Parboiled
Program → Formula+ $$ Figure 6.1: ll(1) Grammar for F
(reproduced from Figure 3.1)
Fomula → Var ‘<=’ Expr ‘;’
Expr → Term (‘or’ Term)*
Term → Factor (‘and’ Factor)*
Factor → ‘not’ Factor | ‘(’ Expr ‘)’ | Var | Constant
Constant → ‘‘0’’ | ‘‘1’’
Var → id
Inspect token without consuming it. Parboiled has two rule The staff solution code does not use the
Test() method. The FirstOf() method pro-
constructors that allow you to inspect the next token without con-
vides adequate look-ahead functionality
suming it: Test() and TestNot. For example, in a rule for Var one might for the F grammar.
include TestNot(Keyword()) to ensure that a keyword (e.g., ‘and’, ‘or’) is
not considered as a variable name.
Implicit whitespace handling. As we saw in a previous lab, If you are interested in how this trick
Parboiled incorporates both syntactic and lexical specifications. works there is a page devoted to it
on the Parboiled website. This is just
One of the practical consequences of this is that we cannot delegate a trick of the tool and not part of the
whitespace handling to a separate lexer (as we did when writing re- intellectual content of this course, so
you are not expected to understand
cursive descent parsers by hand). The explicit mention of whitespace how it works.
after every literal can make the recognizer/parser code look clut-
tered. There is a standard trick in Parboiled that if we add a single
trailing space to each literal then the match for that literal will in-
clude all trailing whitespace. For example, in the rule constructor for
Constant we would write: FirstOf("0 ", "1 ") (notice the trailing space
after the zero and the the one). If you look at the method Constan-
tExpr.make(s) you will see that it looks only at the first character of
its argument s, and so thereby ignores any trailing whitespace. When
you call match() the result will contain the trailing whitespace.
84 ece351 lab manual [september 2, 2023]
your recursive descent F parser did. We can apply the same set of We will reuse the F ast classes from
transformations, developed in a previous lab, to produce NaryExprs. the previous lab.
Exercise 6.2.2 How large can the stack grow while parsing F ?
Leaf Composite
+ operation() + operation() 1
+ add() parent
+ remove()
+ getChild()
Why didn’t we use the Composite design pattern for the W ast
classes? Because W is a regular language, and so does not have
nested expressions, it will always result in ast’s of a known fixed
height. F , on the other hand, is a context-free language with nested
expressions, so the ast depth will vary from input to input. The
Composite design pattern lets us work with these ast’s of varying
depth in a uniform way.
[lab 6] parsing F with parboiled 85
doSomething()
ConcreteClass
PrimitiveOperation1()
PrimitiveOperation2()
doSomething()
86 ece351 lab manual [september 2, 2023]
Expr:
Primitive Op Implemented By Template Method
operator() every subclass of Expr toString()
simplifyOnce() NaryExpr, AndExpr, OrExpr, NotExpr simplify()
NaryExpr:
Primitive Op Implemented By Template Method
getAbsorbingElement() NaryAndExpr, NaryOrExpr simplifyOnce() helpers
getIdentityElement() NaryAndExpr, NaryOrExpr simplifyOnce() helpers
getThatClass() NaryAndExpr, NaryOrExpr simplifyOnce() helpers
Why?
While Figure 6.4 shows the creation of two objects that have global
names so they can be used by anyone, it does not prevent anyone
from creating more objects of class ConstantExpr. To do that we make
the constructor private, and then provide an alternative method to
return a reference to one of the existing objects, as listed in Figure 6.5.
6.6 Evaluation
We evaluate the code that you have committed and pushed before the
deadline. The test harnesses are run with the F programs we have
released to you and a secret set of staff F programs. The weights for
the test harnesses are as follows:
Current New You do not get any marks for any other
TestFParboiledRecognizerAccept 10 5 parser tests until TestFParboiledParser-
Basic and TestObjectContractF pass
TestFParboiledRecognizerReject 5 0 everything.
TestFParboiledParserBasic 5 0
TestFParboiledParser 40 10
TestFParboiledParserComparison 20 5
TestFParboiledRecognizer just runs the recognizer to see if it
crashes. TestFParboiledParser checks the following equation:
∀ AST | AST.equals(parse(prettyprint(AST)))
TestFParboiledParserComparison checks that your recursive descent
and Parboiled parsers give isomorphic ast’s when given the same
input file.
Compiler Concepts: common subexpres-
sion elimination
Programming Concepts: hash struc-
tures, iteration order, object identity,
non-determinism, Visitor design pat-
tern, tree traversals: preorder, postorder,
Lab 7 inorder
Libraries:
In this lab we will produce gate diagrams of our F programs. We ece351.f.techmapper.GraphvizToF
will do this by translating F programs into the input language of ece351.f.analysis.ExtractAllExprs
ece351.common.visitor.PostOrderExprVisitor
AT&T GraphViz. Graphviz is an automatic graph layout tool: it reads
Tests:
(one dimensional) textual descriptions of graphs and produces (two ece351.f.techmapper.TestTechnologyMapper
dimensional) pictures of those graphs. Graphviz, which is primarily http://graphviz.org
a visualization tool, attempts to place nodes and edges to minimize Graphviz is a widely used graph
layout tool. Circuit layout is often
edge crossings. Tools that are intended to do digital circuit layout done by specialized tools, but the
optimize the placement of components based on other criteria, but basic idea is the same as Graphviz:
algorithmic placement of interconnected
the fundamental idea is the same: transform a (one dimensional)
components. What differs is the criteria
description of the logic into a (two dimensional) plan that can be used for placement and the ways in
visualized or fabricated. which things are connected. Graphical
layout tools such as Graphviz try to
Figure 7.1 lists a sample Graphviz input file and its rendered out- minimize edge crossings, for example,
put for the simple F program X <= A or B;. The input pins A and B are which is important for people looking
at pictures but may be less relevant for
on the left side of the diagram and the output pin X is on the right
circuit boards. Graphviz also draws
hand side of the diagram. nice Bezier curve lines that are pleasing
to look at, whereas circuit boards are
typically laid out with orthogonal lines.
digraph g { Technology mapping and circuit layout
are studied in ece647.
// header
rankdir=LR;
margin=0.01;
node [shape="plaintext"];
edge [arrowhead="diamond"];
// circuit
or12 [label="or12", image="../../gates/or_noleads.png"];
var0[label="x"];
var1[label="a"];
var2[label="b"];
var1 −> or12 ;
Figure 7.1: Example Graphviz input file
var2 −> or12 ; and rendered output for F program
or12 −> var0 ; X <= A or B;
}
90 ece351 lab manual [september 2, 2023]
The input file in Figure 7.1 contains a header section that will be
common to all of the Graphviz files that we generate. This header
says that the graph should be rendered left to right (instead of the
default top-down), that the whitespace margin around the diagram
should be 0.01 inches, and that the default look for nodes and edges
should be plain.
The main thing to notice in the lines of the input file in Figure 7.1
that describe the formula is that visual nodes are created for both the
pins (A, B, X) and the gates (there is a single or gate in this example).
Expr
NotExpr
AndExpr OrExpr
simplify()
3
Notice that in our F Expr class hierar-
Well, it’s not quite as simple as Figure 7.5 might lead you to be- chy the only classes that can be directly
lieve: there is some infrastructure that we need to add to make instantiated (i.e., are concrete) are the
leaves of the hierarchy. This is some-
this work properly. Figure 7.6 illustrates that we need to add an times known as the abstract superclass
accept(Visitor) method to each concrete class.3 Similarly, we add a rule (i.e., all super-classes should be ab-
stract), and is a good design guideline
visit() for each concrete class to the Simplifier class. These accept(Visitor)
for you to follow.
methods can be reused by any Visitor, whether it is the simplifier or
the technology mapper that we will develop in this lab.
With the use of the Visitor design pattern we can add new opera-
tions (e.g., simplification, technology mapping, etc.) to our complex
data structure (ast) in a modular manner, without having to directly
modify the ast classes.
The Visitor design pattern involves two main methods: visit and
accept. The visit methods are written on the Visitor class and the
accept methods are written on the ast classes. The abstract superclass
for the Visitors that we will write is listed in Figure 7.8.
Notice that our visit methods return an object of type Expr. This fea-
ture allows our Visitors to rewrite the ast as they visit it, which is
necessary for transformations such as the simplifier we wrote previ-
ously. The Visitor we write in this lab does not rewrite the ast, and
so all of its visit methods will simply return the ast node that they
are visiting (i.e., return e;).
[lab 7] technology mapping: F → graphviz 93
ConstantExpr VarExpr
BinaryExpr UnaryExpr
accept(Visitor) accept(Visitor)
To the FExpr class we add the signature for the accept method,
which is then implemented in each concrete ast class as a call to a
visit method, as shown in Figure 7.9.
/**
* Visit/rewrite all of the exprs in this FProgram.
* @param p input FProgram
* @return a new FProgram with changes applied
*/
public FProgram traverseFProgram(final FProgram p) {
FProgram result = new FProgram();
for (final AssignmentStatement astmt : p.formulas) {
result = result.append(traverseAssignmentStatement(astmt));
}
return result;
}
/**
* Visit/rewrite the expr in this AssignmentStatement
* @param astmt the AssignmentStatement to be visited/rewritten
* @return a new AssignmentStatement with changes applied
*/
public AssignmentStatement traverseAssignmentStatement(final AssignmentStatement astmt) {
final Expr e = traverseExpr(astmt.expr);
if (e == astmt.expr) {
// no change
return astmt;
} else {
// rewrite occured
return astmt.varyExpr(e);
}
}
}
[lab 7] technology mapping: F → graphviz 97
@Override
public final Expr traverseUnaryExpr(UnaryExpr u) {
// child first
final Expr child = traverseExpr(u.expr);
// only rewrite if something has changed
if (child != u.expr) {
u = u.newUnaryExpr(child);
}
// now parent
return u.accept(this);
}
@Override
public final Expr traverseBinaryExpr(BinaryExpr b) {
// children first
final Expr left = traverseExpr(b.left);
final Expr right = traverseExpr(b.right);
// only rewrite if something has changed
if (left != b.left || right != b.right) {
b = b.newBinaryExpr(left, right);
}
// now parent
return b.accept(this);
}
@Override
public final Expr traverseNaryExpr(NaryExpr e) {
// children first
ImmutableList<Expr> children = ImmutableList.of();
boolean change = false;
for (final Expr c1 : e.children) {
final Expr c2 = traverseExpr(c1);
children = children.append(c2);
if (c2 != c1) { change = true; }
}
// only rewrite if something changed
if (change) {
e = e.newNaryExpr(children);
}
// now parent
return e.accept(this);
}
}
98 ece351 lab manual [september 2, 2023]
When you iterate over the elements in a List you get them in the
order that they were added to the List. When you iterate over the
elements in a TreeSet you get them sorted lexicographically. When TreeSet, List, HashSet, and HashMap
you iterate over the elements in a HashSet or HashMap, what order are all part of the standard JDK Collec-
tions classes.
do you get them in? Unspecified, unknown, and non-deterministic:
the order could change the next time you iterate, and will likely
change the next time the program executes.
List list = new ArrayList(); SortedSet tset = new TreeSet(); Set hset = new HashSet();
list.add(3); tset.add(3); hset.add(3);
list.add(1); tset.add(1); hset.add(1);
list.add(2); tset.add(2); hset.add(2);
System.out.println(list); System.out.println(tset); System.out.println(hset);
Set hset = new HashSet(); Set lhset = new LinkedHashSet(); Set iset = new IdentityHashSet();
hset.add(new VarExpr("Y")); lhset.add(new VarExpr("Y")); iset.add(new VarExpr("Y"));
hset.add(new VarExpr("X")); lhset.add(new VarExpr("X")); iset.add(new VarExpr("X"));
hset.add(new VarExpr("X")); lhset.add(new VarExpr("X")); iset.add(new VarExpr("X"));
System.out.println(hset); System.out.println(lhset); System.out.println(iset);
We previously saw non-determinism when studying finite automata. In theory they also consider demonic
In that case we focused on angelic non-determinism: the automata non-determinism, where the machine
always chooses a path that leads to the
would ‘magically’ (non-deterministically) choose the right path that worst possible outcome.
will lead to success (acceptance).
In this lab we’re seeing arbitrary non-determinism: the machine
choose some path arbitrarily, and that path may lead to a good result
or a bad result.
As engineers, we want to avoid non-determinism. It makes testing
unnecessarily difficult. We transform non-deterministic machines
into deterministic ones. We are careful to avoid unnecessary sources
of non-determinism in our programs, such as iterating over hash
structures or unregulated concurrency.
The key question that the eliminator needs to answer is, for each sub-
expression, whether that sub-expression should be used, or whether
it should be eliminated in favour of another sub-expression.
What measure of ‘sameness’ should be used for this decision?
What concept of identity should we use? There are four options:
physical memory address (==), equals, isomorphic, and equivalent.
Memory address is (hopefully obviously) not a useful choice: if
our input asts are already sharing physical objects for common
subexpressions then the elimination has already been done. Equiva-
lence is, in some sense, the ideal measure. But computing equivalence
for F formulas is an np-complete problem, and we want to stick with
polynomial complexity. That leaves equals and isomorphic. The equals
method is perhaps easier to work with, since it is already used by the
standard Java collections classes. Isomorphic will give us better results,
at the cost of slightly increased design complexity — but no signifi-
cant change in computational complexity. Possible designs include: These alternatives came out in discus-
sion with the class of the summer of
a. Just use equals via the standard Java collections classes. Easy to 2017.
There is another design decision about whether the asts get rebuilt
with the substitutions, or whether the substitution table is just used
in the printing process.
104 ece351 lab manual [september 2, 2023]
7.9 Evaluation
The last pushed commit before the deadline is evaluated both on the
shared test inputs and on a set of secret test inputs according to the
weights in the table below.
The first testing equation for this lab is as follows. Let f be the ast
produced by your F parser.
f.equivalent(GraphVizToF(TechnologyMapper(simplify(f))))
In other words, take the output of your F parser, simplify it, convert
it to dot (this is the part that you are doing, the technology mapper),
then convert that output back to an F program (we provide this code
for you), and compare this result to the original ast produced by the
parser.
The second testing equation for this lab compares your output
with the staff output (dot file). Let s name the staff dot file, and f
names the ast produced by your F parser.
simplify(GraphVizToF(s)).equivalent(GraphVizToF(TechnologyMapper(simplify(f))))
The two equations above just check that you produced an equivalent
circuit, but they do not measure the number of gates used in that cir-
cuit. A third component of the evaluation is comparing the number
of gates in your circuit versus the staff circuit.
The baseline gate count marks are 16/20 and 8/10. If you produce
fewer gates than the staff solution you bump up to 20/20 and 10/10.
If you produce more gates than the staff solution than for each ex-
tra gate we subtract one point from the baseline. Some strategies
that might produce fewer gates but probably take too much time to
implement include:
Lab 8
Simulation: F → Java
Files:
Consider the following F program: X <= A or B;. A simulator for this ece351.f.simgen.SimulatorGenerator
F program would read an input W file describing the values of A Libraries:
and B at each time step, and would write an output W file describ- ece351.f.analysis.DetermineInputVars
Tests:
ing the values of X at each time step. Such a simulator is listed in ece351.f.simgen.TestSimulatorGenerator
Figure 8.1. Your task is to write a program that, given an input F
program, will produce a Java program like the one in Figure 8.1. In
other words, you are going to write a simulator generator. The gener-
ated simulators will perform the following steps:
An alternative to generating a simulator
would be to write an F interpreter.
a. Read the input W program. What we are doing here is a writing
b. Allocate storage for the output W program. an F compiler. We call it a ‘simulator
generator’ because the term ‘compile’ is
c. Loop over the time steps and compute the outputs. not clear in the context of F : in the last
d. Write the output W program. lab we ‘compiled’ F to a circuit; here
we ‘compile’ it to a simulator.
The generated simulator will have a method to compute the value An interpreter evaluates a program
with an input, but does not transform
of each output pin. In our example F program listed above the the program to another language. A
output pin is X, and so the generated simulator in Figure 8.1 has a compiler, by contrast, is almost the
opposite: it translates a program to
method named X. The body of an output pin method is generated another language, but does not evaluate
by performing a pre-order traversal of the corresponding F expres- that program on any input.
sion AST. F expressions are written with operators in-order: that is, It is usually the case that it takes less
effort to write an interpreter, but that
the operators appear between the variables. For example, in the F the target program’s runtime is faster if
program we have the in-order expression A + B, while in the Java it is compiled first.
translation we have the pre-order expression or(A, B).
Figure 8.2 lists a visitor that determines all of the input variables
used by an F expression ast. This code is listed here for your ref-
erence. It is also in your repository. You will need to call this code
when writing your simulator generator.
In lab8 here you are writing a code generator: that is, the code that
you write produces code as its output, as shown in Figure 8.3.
F YourLab8
GenJavaCode W_out
W_in
8.3 Evaluation
Current New
TestSimulatorGenerator 70 30
Lab 9
This lab developed by past ece351
In §8, your generated Java code called your W parser to read in the
W input. So your generated Java code could work with different
input W programs.
Calling between Java and native assembler code is possible, but is
annoying and time consuming to set up, so we do not want to do it
in this lab.
Instead, we will hardcode the W program into the x64 assembly
code. This means that the generated assembly will not read any input
when it executes — and also that the generated assembly code will
not have to interact with Java code.
The programmer may use as many variables as they want, but the
machine has only a finite number of registers. Levels of register allo-
cation:
b. Variables that are not live at the same time can share a register.
If there are too many variables live at the same time, then some of
them need to be spilled to main memory.
112 ece351 lab manual [september 2, 2023]
a. Every input variable gets its own register. Ok if the total number
of input variables is less than 8.
b. Each AssignmentStatement (formula) in the input F program This is the strategy used in the skeleton
might use only a subset of the input variables. So we could allo- code.
You might have other creative ideas of how to improve this lab or
other labs. Bonus marks are assessed by the instructor.
114 ece351 lab manual [september 2, 2023]
.globl out_x
.type out_x, @function
out_x:
pushq %rbp // set up a stack frame; rbp is frame pointer
movq %rsp, %rbp // set up a stack frame; rbp is frame pointer; rsp is stack pointer
subq $24, %rsp // making space on stack
pushq %r8 // value of input variable "a" is in r8
popq %rax // return value goes in rax
leave
ret
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
movq $120, %rdi // put char ’x’ in rdi
call putchar // print contents of rdi to console
movq $58, %rdi // put char ’:’ in rdi
call putchar // print contents of rdi to console
movq $32, %rdi // put char ’ ’ in rdi
call putchar // print contents of rdi to console
movq $0, %r8 // load 0 into r8 (value of "a" at first time step)
call out_x // evaluate output pin "x" at first time step
movq %rax, %rdi // move x’s computed value to rdi
add $48, %rdi // convert int to ascii (48 is ASCII value ’0’)
call putchar // print contents of rdi to console
movq $59, %rdi // put char ’;’ in rdi
call putchar // print contents of rdi to console
movq $10, %rdi // put newline char in rdi
call putchar // print contents of rdi to console
popq %rax
leave
ret
Compiler Concepts:
Programming Concepts:
Lab 10
vhdl Recognizer & Parser
Files:
In this lab we will build a recognizer and a parser using Parboiled for ece351.v.VRecognizer.java
a very small and simple subset of vhdl. The grammar for this subset ece351.v.VParser.java
AST
VDHL File
architecture XNOR_test_arch of
XNOR_test is [DesignUnit]
begin
process(x, y)
begin
F <= x xnor y;
end process;
arch
end XNOR_test_arch; DesignUnit
identifier entity
Entity
identifier
input output
entityName
Architecture statements
[processStatement]
architectureName
processStatement
XNOR_test_arch
signals components
sensitivityList sequentialStatements
[x,y]
[AssignmentStatement]
[] []
AssignmentStatement
outputVar
Expr
VarExpr XNOrExpr
left right
identifier
F VarExpr VarExpr
identifier identifier
x y
To make the code for the recognizer and parser a little bit more leg-
ible (and easier to implement), classes VRecognizer and VParser
inherit a common base class called VBase,1 which contains a set of 1
VBase itself extends BaseParser351,
rules that will match keywords found in the language in a case- which provides extra utility and de-
bugging methods over Parboiled’s
independent manner (since vhdl is case-insensitive2 – e.g., both BaseParser class.
‘signal’ and ‘SIGNAL’ represent the same keyword in the language). 2
In some vhdl compilers, there may be
some exceptions, where the case sen-
Figure 10.3 is an example of one of the rules found in VBase. Here, sitivity matters a handful of grammar
the rule matches the keyword ‘signal’ and as there is no whitespace productions
handling, your code should ensure that at least one whitespace exists
between this keyword and the next token/string that is matched.
You should be able to copy and paste the code from your F par-
boiled recognizer with minimal modifications, since F is a subset of
our vhdl grammar.
• Draw out the evolution of the stack for a simple input [Figure 5.3].
• Print out our vhdl grammar.
• Annotate the printed copy of the grammar with where stack ma-
nipulations occur according to your diagram.
[lab 10] vhdl recognizer & parser 119
• Calculate the maximum and expected size of the stack for different
points in the grammar.
• Copy your recognizer code into the parser class.
• Add some (but not all) actions (push, pop, etc.).
• Add some checkType() and debugmsg() calls [Figure 5.5].
• Run the TestVParser harness to see that it passes.
• Add more actions, checkType() calls, etc..
• Repeat until complete.
When you implement the vhdl parser, there may be a few gram-
mar productions shown in Figure 10.1 where you will need to rewrite
each of these rules in a different way so that you can instantiate all of
the required objects used to construct the AST of the program being
parsed.
10.5 Evaluation
The last pushed commit before the deadline is evaluated both on the
shared test inputs and on a set of secret test inputs according to the
weights in the table below.
Current New You do not get any marks for any other
TestVParboiledRecognizerAccept 20 5 parser tests until TestObjectContractF
passes everything. You do not get any
TestVParboiledRecognizerReject 5 0 marks for rejection tests until some
TestVParboiledParser 45 25 acceptance tests pass.
Lab 11
vhdl → vhdl: Desugaring & Elaboration
In this lab, you will be writing two vhdl to vhdl transformers us-
ing the Visitor design pattern. The first, and simpler one, will simply
rewrite expressions to replace operators like xor with their equiva-
lent formulation in terms of and, or, and not. We call this desug-
aring, where operators like xor are considered to be syntactic sugar:
things that might be convenient for the user, but do not enlarge the
set of computations that can be described by the language.
The second, and more sophisticated, transformer will expand
and inline any component instance declared within an architecture
of a design unit if the component is defined within the same vhdl
source file. The procedure that this transformer performs is known
as elaboration. Elaboration is essential for us to process the four bit
ripple-carry adder, for example, because it comprises four one-bit full
adders.
x ⊕ y ≡ x ·!y+!x · y (11.1)
Table 11.1 is the truth table for the ‘=’ operator: By observation, we
can see that this truth table is equivalent to that of xnor.
11.2 Elaboration
Sources:
The following sections describe the expected behaviour of the elabo- ece351.v.Elaborator
rator. Tests:
ece351.v.test.TestElaborator
In addition to substituting the input and output pins for a port map,
you will also encounter situations where there are signals declared in
the architecture body that you are trying to inline to the design unit
the elaborator is currently processing.
For example, consider vhdl source in Figure 11.3, which is the
extension of the program in Figure 11.1.
The two components in eight_port_structure are instances of
four_port_OR_gate_2; the architecture of four_port_OR_gate_2 de-
fines signals e and f. Now, if we elaborate, say, OR1, we determine the
mapping as before for the input and output pins, but we also need
to consider the signals defined within the architecture. If we simply
add e and f to the list of signals of eight_port_structure, we will
run into problems of multiply defined signals when we elaborate OR2;
we will obtain a signal list with two e’s and two f’s. Furthermore, we
will change the logical behaviour defined in eight_port_structure.
To address this issue, all internal signals that are added as a re-
sult of elaboration will be prefixed with ‘comp<num>_’, where <num>
is a unique identifier1 used to ensure that the elaboration does not 1
This number starts at 1 and increments
change the logical behaviour of the program. The result of elaborat- for each component that is instantiated
in the vhdl program. <num> is never
ing eight_port_OR_gate_2 is shown Figure 11.4. reset.
[lab 11] vhdl → vhdl: desugaring & elaboration 125
11.2.4 Notes
• The elaborator will only expand components when its correspond-
ing design unit is also defined in the same file.
• The elaborator processes the design units in sequential order. We
assume that the vhdl programs we are transforming are written
so that you do not encounter cases where the architecture that you
are inlining contains components that have not yet been elabo-
rated.
• We will assume that the vhdl programs being elaborated will
not result in architecture bodies with a mixture of parallel signal
assignment statements and process statements (so that the parser
from Lab 10 can parse the transformed programs).
11.3 Evaluation
The last pushed commit before the deadline is evaluated both on the
shared test inputs and on a set of secret test inputs according to the
weights in the table below. The testing equation for desugaring is:
parse(v).desugar().isomorphic(parse(vStaff))
Similarly, the testing equation for elaboration is:
parse(v).elaborate().isomorphic(parse(vStaff))
Shared Secret
TestDeSugarer 20 10
TestElaborator 50 20
Choice: either do this lab or do the lab
marked as lab12. Do not do both.
Lab 12
vhdl Process Splitting & Combinational Synthesis
Files:
For this lab, you will be writing code to perform two other transfor- ece351.v.Splitter.java
mations on vhdl programs. The first transformation is a vhdl to ece351.v.Synthesizer.java
12.3 Synthesizer
After parsing the input vhdl source file and performing all of the
transformations (i.e., desugaring, elaborating, and process splitting),
the synthesizer will traverse the (transformed) ast (of the vhdl
program), extract all expressions in the program, and generate an F
program containing these expressions.
In §8 we generated a Java program by constructing a string. We
noted that there was no guarantee that the string we generated
would be a legal Java program. One way to ensure that we gener-
ate a legal program is to build up an ast instead of building up a
string. Of course, to do that we need to have ast classes, which we
do not have for Java — but do have for F .
Because the F grammar and ast classes are a subset of those for
vhdl, we can simply reuse many of the expressions from the input
vhdl program with only minor modification to rename the variables
to avoid name collision.
Let’s refer to the variable named conditionX as an intermediate variable The synthesizer here is not the only
because it is not intended as part of the circuit’s final output, but is place where these intermediate vari-
ables are created. They also occur in
used in computing the final output pins. Our F simulator generator elaboration. They are helpful for debug-
and technology mapper do not support these intermediate variables. ging, so it’s worth keeping them here
and removing them later.
Removing them is not hard: we just inline their definition wherever
they are used. We will provide you with code to do that later.
[lab 12] vhdl process splitting & combinational synthesis 131
12.4 Evaluation
The last pushed commit before the deadline is evaluated both on the
shared test inputs and on a set of secret test inputs according to the
weights in the table below.
Current New
TestSplitter 40 10
TestSynthesizer 40 10
Lab 13 This lab by Nicholas Champion.
Options include:
• MIPS Assembler and Runtime Simulator (MARS). A Java implemen- There might be some bonus marks
tation of the risc machine described in the Hennessey & Patterson available if you develop a skeleton
and some instructional support for
computer architecture textbook. http://courses.missouristate. an assembler such as mars that has
edu/kenvollmar/mars/ Because this is a simulator written in Java, no system dependencies outside of a
regular jvm.
it might avoid a lot of the systems issues of going to x86 assembly.
Also, it might be easier to generate MIPS assembly than x86 (RISC
instruction sets are usually simpler than CISC ones).
.code
LibMain proc hInstance:DWORD, reason:DWORD, reserved1:DWORD
mov eax, 1
ret
LibMain endp
mov EAX, 0
mov EBX, 0
mov AL,in_a
mov EBX,1
or EAX, EBX
ret
Java_Simulator_1opt1_1or_1true1_Evalx endp
End LibMain
136 ece351 lab manual [september 2, 2023]
Figure 13.3 the linker for MASM32 uses a definitions file to deter-
mine which symbols should be exported. Note that the library name
does not require the replacing underscores. Additional exports are
added on separate lines.
Edit SimulatorGeneratorMasm32.java
This lab requires generating the instructions for the F-statement. and run TestSimulatorGenerator-
Masm32.java
Most of the other code is generated by the skeleton. To generate the
instructions, the ast should be walked in post order as a simple
topological sort and stored in the operations list. It is then necessary
to assign signals and intermediate signals to registers and write in-
structions for each ast object. For example VarExpr or ConstantExpr
can be converted into 8-bit immediate-mode mov instructions. The
other operators can be implemented using various Boolean operator
instruction. It is possible to implement all expressions including a
NotExpr using a single instruction and no conditionals. The result
of the last computation for the statement should be stored in EAX
as the return value for the function. x86 has four 32-bit general pur-
pose registers EAX, EBX, ECX, and EDX and supports 16-bit and
8-bit access modes. A helper class is provided which contains the
names of that correspond to the other addressing modes (i.e., AL ref-
erences the low byte of EAX). Since there are only four registers (or
fewer if you remove some from the Register.General list for debug-
ging purposes), it is possible that large assignment statements will
require the use of memory. A simple FIFO (registerQueue) strategy
is used to determine which register to save to memory and reassign
before performing an operation. Memory can be allocated using
the LOCAL keyword: LOCAL m0 :BYTE. These allocation statements
should be immediately following the function signature before any
instructions. In order to track which memory is in use the hash map
memoryMap maps the name of the memory allocation to a Boolean
value of whether it is used. For convenience the IdentityHashMap
storageMap maps an Expr to the name of either the memory or regis-
ter currently storing its output value.
[lab 13] simulation: F → assembly 137
13.4 Evaluation
Lab 14
Simulation: F → JVM
The Java operand stack treats booleans as integers because each slot on
the operand stack is 32-bits. Zero is false and one is true (as in C).
Boolean logic operators are translated into if comparisons against
zero. There are no jvm opcodes for boolean logic.
Also note the short-circuiting semantics of boolean logic opera-
tors in Java (as in C): operands are only evaluated if necessary. For
example, if the first operand of a conjunction (and) is false, then the
second operand is not evaluated. Similarly, if the first operand of a
disjunction (or) is true, the second operand is not evaluated.
public class javac_code {
boolean or (boolean x, boolean y) { return x || y; }
boolean and(boolean x, boolean y) { return x && y; }
boolean not(boolean x) { return ! x; }
}
140 ece351 lab manual [september 2, 2023]
boolean not(boolean); // ! x
Code:
0: iload_1 // push x
1: ifne 8 // if ( x != 0) goto instruction 8
4: iconst_1 // push true ( one)
5: goto 9 // goto return true
8: iconst_0 // push false ( zero)
9: ireturn // return top of stack
}
[lab 14] simulation: F → jvm 141
x <= a or b;
. class public Simulator_ex05_asm
. super java/lang/Object
. method public <init>()V
aload_0
invokespecial java/ lang/Object.<init >() V
return
. end method
. method public static x(ZZ)Z
. limit locals 2 ; one for each argument
. limit stack 2 ; depth of the AST
iload_0 ; a
iload_1 ; b
iadd ; ( a or b )
ifeq False ; if top of stack is zero, then jump to False
True: ; the true case
iconst_1 ; push true
ireturn ; return top of stack ( true)
False: ; label we can jump to
iconst_0 ; push false
ireturn ; return top of stack ( false)
. end method
x <= c and ( a or b );
. class public Simulator_ex06_asm
. super java/lang/Object
. method public <init>()V
aload_0
invokespecial java/ lang/Object.<init >() V
return
. end method
. method public static x(ZZZ)Z
. limit locals 3 ; one for each argument
. limit stack 3 ; depth of the AST
iload_0 ; c
iload_1 ; a
iload_2 ; b
iadd ; ( a or b )
imul ; ( c and ( a or b ) )
ifeq False ; if top of stack is zero, then jump to False
True: ; the true case
iconst_1 ; push true
ireturn ; return top of stack ( true)
False: ; label we can jump to
iconst_0 ; push false
ireturn ; return top of stack ( false)
. end method
144 ece351 lab manual [september 2, 2023]
14.3 Negation
2x
1+x
14.4 Evaluation
X <= A or B;
Y <= X or C;
...
Y <= (A or B) or C;
Appendix B
Advanced Programming Concepts
B.1 Immutability
ast classes
Immutability is not a ‘design pattern’ in the sense that it is not in the
design patterns book.1 It is, however, perhaps the most important 1
E. Gamma, R. Helm, R. Johnson, and
general design guideline in this section. Immutability has a number J. Vlissides. Design Patterns: Elements
of Reusable Object-Oriented Software.
of important advantages: Addison-Wesley, 1995
• LinkedList.head != null
• LinkedList.size is the number of nodes in the list
• that the values in a certain field are sorted
• immutability
• recursion
• lazy computation
• higher-order functions
• computation as a transformation from inputs to outputs
In w2013 we’ve talked about immutability, and the code has embod-
ied both immutability and computation as a transformation from
inputs to outputs.
See the Design Patterns book or
Wikipedia or SourceMaking.com
C.1 Iterator
• Behavioural
• Provide a way to access the elements of an aggregate object se-
quentially without exposing its underlying representation.
• Enables decoupling of collection classes and algorithms.
• Polymorphic traversal
C.2 Singleton
ConstantExpr
• Creational
• Ensure a class has only predetermined small number of named
instances, and provide a global point of access to them.
• Encapsulated ‘lazy’ (‘just-in-time’) initialization.
C.3 Composite
Expr class hierarchy
• Structural
• Compose objects into tree structures to represent whole-part hier-
archies. Composite lets clients treat individual objects and compo-
sitions of objects uniformly.
• Recursive composition
• e.g., ‘Directories contain entries, each of which could be a file or a
directory.’
150 ece351 lab manual [september 2, 2023]
C.5 Interpreter
Expr.simplify()
• Behavioural
• Given a language, define a representation for its grammar along
with an interpreter that uses the representation to interpret sen-
tences in the language.
• Map a domain to a language, the language to a grammar, and the Tiger: §4.3 + lab manual
grammar to a hierarchical object-oriented design.
• In other words, add a method to the AST for the desired opera-
tion. Then implement that method for every leaf AST class.
• An alternative to Visitor. Interpreter makes it easier to modify the
grammar of the language than Visitor does, but with Visitor it is
easier to add new operations.
C.6 Visitor
Expr class hierarchy
• Behavioural
• Represent an operation to be performed on the elements of an
object structure. Visitor lets you define a new operation without
changing the classes of the elements on which it operates.
• Do the right thing based on the type of two objects.
• Double dispatch
• An alternative to Interpreter.
Appendix D Term names follow the UW convention:
cyym, where c is century (0 for 20th,
Lab Instructor Notes (GitLab) 1 for 21st); yy is the last two digits of
the year; m is the number of the month
at the start of the term (either 1, 5, or
9). So 1151 is the term that starts in
January of 2015.
D.2 Setting Up
Fork student repos (see §E.3 below). Then add: Students will have to pull from skeleton
during the prelab exercise to get these
test files.
• TestPrelab*.java
[appendix d] lab instructor notes (gitlab) 153
• TestImports.java
154 ece351 lab manual [september 2, 2023]
#!/bin/bash
echo "copy this script to the appropriate location and customize it"
exit 1
t=1181
the students are supposed to write, and (2) inserts the intellectual
property header.
If export.sh runs successfully you should see many lines of output export.sh makes a clone of your local
that start with ‘dos2unix’. If you get a message that the temporary copy of the instructor code repo and
works from that. So if you have uncom-
directory exists and the script is exiting, you need to delete that old mitted changes in your local copy of the
temporary directory first and then run export.sh. repo they will not be included in the
export.
After running export.sh use a visual diff tool such as meld to com-
pare the snipped source code with the skeleton. Merge manually.
Sometimes there will be changes that are made to the skeleton manu-
ally that should not be overwritten by a future export.
lib submodule
.gitignore
.project
.classpath
build.xml
meta/hours.txt + meta/collaboration.txt
src/ece351/util/*.java
1. tests/wave/*.wave
src/ece351/w/ast/*
src/ece351/w/rdescent/* Manually remove Todo351Exception
src/ece351/w/regex/* from TestWRegexSimpleData.
2. tests/wave/staff.out/*
src/ece351/objectcontract/*
src/ece351/w/svg/* Excluding tests/f/secret!
3. tests/f/* including ungrammatical/, gates/ and staff.out/
src/ece351/common/ast/*
src/ece351/common/visitor/* for compilation dependencies
src/ece351/f/FParser.java
src/ece351/f/analysis/* for compilation dependencies
src/ece351/f/ast/*
src/ece351/f/rdescent/*
src/ece351/f/test/*
4. src/ece351/f/simplifier/*
5. src/ece351/w/parboiled/*
6. src/ece351/f/parboiled/*
7. src/ece351/f/techmapper/*
tests/f/staff.out/graph/*
8. tests/f/staff.out/simulator/*
src/ece351/f/simgen/TestSimulatorGenerator.java
src/ece351/f/simgen/SimulatorGenerator.java
9. tests/v/*
src/ece351/v/VBase.java
src/ece351/v/ast/*
src/ece351/v/test/*
src/ece351/v/test/VRecognizer.java (or class)
src/ece351/v/test/VParser.java (or class)
src/ece351/v/test/TestArchitectureEquivalence
10. src/ece351/v/PostOrderVVisitor.java
src/ece351/v/DeSugarer.java
src/ece351/v/Elaborator.java
[appendix d] lab instructor notes (gitlab) 157
src/ece351/v/test/TestDeSugarer.java
src/ece351/v/test/TestElaborator.java
Appendix E Term names follow the UW convention:
cyym, where c is century (0 for 20th,
Lab Instructor Notes (gitolite) 1 for 21st); yy is the last two digits of
the year; m is the number of the month
at the start of the term (either 1, 5, or
9). So 1151 is the term that starts in
January of 2015.
E.2 Setting Up
Fork student repos (see §E.3 below). Then add: Students will have to pull from skeleton
during the prelab exercise to get these
test files.
• TestPrelab*.java
[appendix e] lab instructor notes (gitolite) 161
• TestImports.java
162 ece351 lab manual [september 2, 2023]
#!/bin/bash
echo "copy this script to the appropriate location and customize it"
exit 1
t=1181
the students are supposed to write, and (2) inserts the intellectual
property header.
If export.sh runs successfully you should see many lines of output export.sh makes a clone of your local
that start with ‘dos2unix’. If you get a message that the temporary copy of the instructor code repo and
works from that. So if you have uncom-
directory exists and the script is exiting, you need to delete that old mitted changes in your local copy of the
temporary directory first and then run export.sh. repo they will not be included in the
export.
After running export.sh use a visual diff tool such as meld to com-
pare the snipped source code with the skeleton. Merge manually.
Sometimes there will be changes that are made to the skeleton manu-
ally that should not be overwritten by a future export.
lib submodule
.gitignore
.project
.classpath
build.xml
meta/hours.txt + meta/collaboration.txt
src/ece351/util/*.java
1. tests/wave/*.wave
src/ece351/w/ast/*
src/ece351/w/rdescent/* Manually remove Todo351Exception
src/ece351/w/regex/* from TestWRegexSimpleData.
2. tests/wave/staff.out/*
src/ece351/objectcontract/*
src/ece351/w/svg/* Excluding tests/f/secret!
3. tests/f/* including ungrammatical/, gates/ and staff.out/
src/ece351/common/ast/*
src/ece351/common/visitor/* for compilation dependencies
src/ece351/f/FParser.java
src/ece351/f/analysis/* for compilation dependencies
src/ece351/f/ast/*
src/ece351/f/rdescent/*
src/ece351/f/test/*
4. src/ece351/f/simplifier/*
5. src/ece351/w/parboiled/*
6. src/ece351/f/parboiled/*
7. src/ece351/f/techmapper/*
tests/f/staff.out/graph/*
8. tests/f/staff.out/simulator/*
src/ece351/f/simgen/TestSimulatorGenerator.java
src/ece351/f/simgen/SimulatorGenerator.java
9. tests/v/*
src/ece351/v/VBase.java
src/ece351/v/ast/*
src/ece351/v/test/*
src/ece351/v/test/VRecognizer.java (or class)
src/ece351/v/test/VParser.java (or class)
src/ece351/v/test/TestArchitectureEquivalence
10. src/ece351/v/PostOrderVVisitor.java
src/ece351/v/DeSugarer.java
src/ece351/v/Elaborator.java
[appendix e] lab instructor notes (gitolite) 165
src/ece351/v/test/TestDeSugarer.java
src/ece351/v/test/TestElaborator.java
Appendix F
Bibliography