Report On Little Quilt Language
Report On Little Quilt Language
Bombay
PROJECT REPORT
Little Quilt
1
2 Scanner & Parser for turn and sew operations
We learnt the flex and bison tools which generate program segments that can
read the source program and discover its structure.
The translation rules in our lex script recognizes all the symbols in our input
and returns the corresponding token(s). The lex script also defines regular
expression for the whitespaces and takes no action when the whitespaces are
found in the input.
Start → Expression
Expression → turn(Expression) | sew(Expression, Expression) | a | b
where, ’Start’ and ’Expression’ are the non-terminals, ’a’, ’b’, ’turn’, ’sew’,
’,’, ’(’, and ’)’ are terminals.
2
3 Constructing the Abstract Syntax Tree
To construct the AST for a given expression, we defined an abstract base
class for Expression (Exp Node). We also defined it’s subclasses correspond-
ing to the ASTs of ’a’ (A AST), ’b’ (B AST), ’turn’ (TURN AST) and ’sew’
(SEW AST).
The abstract class contains a virtual function print(), which implies that all
of its subclasses must implement the print() function to print their respective
ASTs to the file or standard output.
The SEW AST subclass includes attributes ”left” and ”right” which are
pointers to the Exp Node object, where ”left” and ”right” corresponds to
the first and second argument passed to the sew() function.
The print() function for the subclasses A AST and B AST print their respec-
tive tokens.
The print() function for the subclass TURN AST first prints the TURN to-
ken and then calls the print function for it’s attribute ”child” (which is an
expression passed to the turn function as an argument). The print() func-
tion of ”child” recursively prints it’s AST and then returns back to the caller
function, which is the print() function for the TURN AST.
The print() function for the subclass SEW AST first prints the SEW token
and then calls the print function for it’s ”left” attribute, which recursively
prints the AST of the expression passed as first argument to the sew function.
Then, the print function for it’s ”right” attribute is called which recursively
prints the AST of the expression passed as second argument to the sew func-
tion.
3
The problem we faced in implementing the print() function for ASTs is in
ensuring the appropriate spacing such that one can easily understand the
AST to determine the function and it’s arguments. We solved this issue by
declaring and initializing a global variable through which the recursive calls
to the print() function of an argument subexpression can track how many
spaces it has to print for the proper alignment.
4
4 Evaluating the AST
To generate the resulting quilt in a matrix form, we require to evaluate the
AST of the expression.
For the turn(arg) operation, where ’arg’ is the expression passed to turn func-
tion as an argument, We have to first compute the matrix for ’arg’, and then
rotate it 90 degrees clockwise to obtain the resulting matrix. Note that each
element of the matrix for ’arg’ will be rotated by 90 degrees in addition to
the 90 degrees clockwise orientation of the whole matrix.
For the sew(arg1, arg2) operation, where ’arg1’ and ’arg2’ are the expressions
passed to sew function as arguments. We have to compute the matrices for
’arg1’ and ’arg2’ and then combine these matrices to obtain the resulting ma-
trix. Note that the number of rows in the matrix for ’arg1’ and the matrix
for ’arg2’ must be same.
Next, we created a class in order to represent the quilt in matrix form, namely
”Quilt”, which has a two-dimensional vector as a data member which repre-
sents the quilt in matrix form.
5
• void nextElement(): replaces the element at position (i, j) of the ma-
trix with it’s next element.
• void printQuilt(): prints the 2D-vector ’result’ in matrix form.
Then, we declared a data member called ’q’ of type ’Quilt’ in the base class,
which means that all of its subclasses (of terminals ’a’, ’b’, ’turn’ and ’sew’)
also has this data member, which will be used for storing their resulting quilt.
Since doing the calculation for computing the matrix for quilt is easier if the
entries in the matrix are in the form of integers. We mapped the integers 0,
1, 2, 3, 4, 5, 6, 7, 8 to the strings ”a0”, ”a1”, ”a2”, ”a3”, ”b0”, ”b1”, ”b2”,
”b3”, ”” respectively, where the integer ’8’ will be used as a dummy entry in
order to make it a square matrix (if it isn’t).
The evaluate() function of class for terminals ’a’, ’b’ inserts only single entries
0 and 4 respectively into their matrices.
For the turn(q) function, where ’q’ is the matrix form of the quilt on which
the ’turn’ operation has to be applied. We first compute the matrix form of
’q’ recursively by calling q → evaluate(). Let us denote the matrix for quilt
’q’ by M(q). Then, we check whether M(q) is a square matrix or not. We per-
formed this check because we were using an algorithm which rotates a matrix
90 degrees clockwise, but the matrix should be a square matrix. So, if M(q)
is not a square matrix, then we extend it’s rows and/or columns and insert
’8’ to the newly created positions. This is just to make the M(q) a square
matrix and while printing this matrix, entry ’8’ will be ignored. Hence, the
integer ’8’ was mapped to ””.
The drawback of this implementation is that for a matrix in which the dif-
ference between number of rows and number of columns is large, there will
be so many redundant entries (8) inserted in the matrix in order to make it
a square matrix, which un-necessarily increases the space complexity as well
as time complexity while applying the turn operation.
6
To eliminate this redundancy, we dumped the idea of mapping 8 to ””. We
also modified the algorithm for turning a matrix which can turn the matrix of
any dimension and not only the square matrix. The algorithm is as follows:
1. The number of rows in the resulting matrix will be equal to the number of
columns in the M(q) and the number of columns in the resulting matrix
will be equal to the number of rows in M(q).
2. An entry at the position (i, j) of M(q) is inserted at the position (j, mRows−
i − 1) of the resulting matrix where ’mRows’ is the number of rows in
M(q).
3. Turn the entry which has been inserted at the position (j, mRows−i−1)
using the nextElement() function.
For the sew(q1, q2) function, where ’q1’ and ’q2’ are the matrix forms of the
first quilt and second quilt which are to be combined. The algorithm is as
follows:
1. Recursively compute the matrices of the expressions corresponding to
’q1’ and ’q2’ by first calling q1 → evaluate() and then q2 → evaluate().
Let M(q1) denote the matrix for ’q1’ and M(q2) denote the matrix for
’q2’.
2. The number of rows in the resulting matrix will be same as the number
of rows in ’q1’ and ’q2’. The number of columns in the resulting matrix
will be sum of number of columns in M(q1) and M(q2).
3. An entry (i, j) in the resulting matrix is the entry (i, j) of M(q1) if j <
q1Cols and the entry (i, j % q2Cols) of M(q2) otherwise, where ’q1Cols’
is the number of columns in M(q1) and ’q2Cols’ is the number of columns
in M(q2).
It is considered to be a bad programming practice to deal with integers with-
out specifying what those integers really mean. It makes it harder for other
programmers to understand the code and determine what’s going on.
So, we declared an ’enum’ with the constants a0, a1, a2, a3, b0, b1, b2, b3
which corresponds to the integers 0, 1, 2, 3, 4, 5, 6, 7 respectively. Now, if
7
we want to insert an entry, say a0, to a matrix, then we can directly insert
the value as a0 instead of 0, since internally this a0 will be treated as 0.
8
5 Compiling the AST & generating the C++ code
For generating the equivalent C++ code for a given AST, we first tried to
create a global variable which was a vector of strings to act as a stack of
function calls and to store the expressions which are assigned to temporary
variables (of type ’Quilt’). We declared a global variable to keep track of the
total number of temporary variables. And then we created a print function to
print the C++ code using the stack (a vector of strings) and the temporary
variable counter.
But, it is not desirable to use strings in our program and the use of global
variables should be as minimum as possible.
So, we created a class for the data structure ’Quadruple’, which contains four
fields: operator, operand1, operand2 and result.
We also extended our enum to store the constants a, b, turn and sew.
The ’operator’ and ’result’ are of integer type, where ’operator’ can be one
of ’a’, ’b’, ’turn’ or ’sew’, and ’result’ is used to hold the temporary variable
number in which the result of this expression is going to be stored.
We declared a static data member within this class to count the total number
of quadruple objects. As a quadruple object gets created, the result field is
assigned the current value of the static counter for temporary variable. The
’operand1’ and ’operand2’ are pointers to the Quadruple object. This cre-
ates the link between quadruple objects which can be used to generate C++
statements in a bottom-up manner.
The ’Quadruple’ class also includes a print() function which is used to gen-
erate the C++ statements.
To generate the C++ code for turn(arg1) function, where ’arg1’ is the ex-
pression passed to turn function as an argument. The print() function of
’Quadruple’ class first invokes the print() function of ’arg1’ which generates
9
the C++ code for ’arg1’ recursively in a bottom-up manner. Then, this func-
tion generates the C++ code corresponding to the quadruple object of turn.
To generate the C++ code for sew(arg1,arg2) function, where ’arg1’ and
’arg2’ are the expressions passed to sew function as arguments. The print()
function of ’Quadruple’ class first invokes the print() function of ’arg1’ which
generates the C++ code for ’arg1’ recursively in a bottom-up manner. Then,
it invokes the print() function of ’arg2’ which generates the C++ code for
’arg2’ in a similar manner. Finally, the function generates the C++ code
corresponding to the quadruple object of sew.
10
6 Let bindings for defining VAL and FUN
This allows us to define our own variables and functions and use them in our
expressions.
The main challenge was to find the scope of the variable or the function in
the expression. This is done by creating a stack of symbol tables and pushing
a symbol table for every LET expression and popping off that symbol table
from the stack after the end of let expression.
Then, we created a class for symbol tables called SymTab, which consists
of a list of symbol entries active in the scope to which that symbol table
belongs. We implemented symbol entry using the ’Vector STL’ and thus the
list of symbol entries in a SymTab is simply a vector of integers. We created
functions for lookup, for inserting a symbol entry into symbol table and for
retrieving the symbol entry corresponding to the name of a function or a
variable.
The tokens and the grammar for our language is also extended to support
the LET bindings, the new productions included in our grammar are:
11
F ormals → N AM E , F ormals
F ormals → N AM E
Actuals → Expression , Actuals
Actuals → Expression
Now, for constructing the abstract syntax trees and for its evaluation, we
created the following AST classes which inherit the base class Exp Node:
• LET AST: This AST class consists of two data members ’decls’ (for
declarations) and ’exprs’ (for expressions).
• CALL AST: This AST class consists of three data members: ’symEn-
tryIndex’, which contains the symbol entry index of the variable or the
function or the parameter called, ’actuals’ which is a pointer to the AST
class of actual parameters of a function and is NULL in case of variables
or parameters, and ’funcExpr’ which holds the function expression.
• DECLS AST: This AST class consists of two data members, both are
pointers to DECLS AST.
• FUN AST: This AST class consists of three data members: ’symEn-
tryIndex’, which contains the symbol entry index of the function, ’for-
mals’ which is a pointer to the AST of formal parameters and ’expr’
which holds the expression of the function
• VAL AST: This AST class consists of two data members: ’symEntryIn-
dex’, which contains the symbol entry index of the variable and ’expr’,
which contains the expression that the variable holds.
• FORMAL AST: This AST class consists of two data members, one
contains the symbol entry index of the formal parameter and the other
12
is a pointer to the FORMAL AST.
• ACTUALS AST: This AST class consists of two data members: ’expr’
which holds the expression for the current actual parameter and ’actuals’
which is a pointer to the ACTUALS AST and acts as a link to the other
actual parameters.
All of the above AST classes implement print() and evaluate() functions of
printing the AST and evaluating the AST respectively.
The print() function for LET AST first calls the print() function of DE-
CLS AST, which prints all the declarations and expression under them re-
cursively, and then it invokes the print() function of the expression between
IN and END, which recursively prints the AST of that expression.
The print() function for CALL AST determines whether a variable, a func-
tion or a parameter is called. It prints the AST accordingly.
The print() function for DECLS AST first prints the AST corresponding to
the declaration. Then, it calls the print() function of it’s other data member
which acts as a link to all other declarations, and all other declarations are
printed recursively.
The print() function for FUN AST first calls the print() function for printing
the formal parameters and then calls the print() function for printing the
AST for it’s expression. Similarly, the print() function for VAL AST prints
the expression recursively after printing the variable name.
The print() function for FORMAL AST first calls the print() function to
print the current formal parameter and then calls the print() function of it’s
other data member which as a link to all other formal parameters, and all
other formal parameters are printed recursively. Similarly, the print() func-
tion for the ACTUALS AST first prints the current actual parameter and
13
then recursively prints all other actual parameters.
Figuring out how to implement the evaluation mechanism was the toughest
part.
During the declaration of a variable, it’s expression is evaluated and the re-
sulting quilt is mapped to the variable such that whenever the evaluation of
that variable is called when it is a part of an expression, it simply returns
the quilt mapped to it during it’s declaration.
In our first attempt to implement this evaluation, we were not using the AC-
TUALS AST in our evaluation, so we had to create some global variables for
stack of actual parameters and then we were trying to evaluate them and map
them to formal parameters, which made the whole mechanism very compli-
cated and it was not even working for all kinds of input. For example, it was
not working when a user-defined function calls an already defined function.
Then, we tried to implement the evaluation again from scratch, this time,
we made use of the ACTUALS AST evaluate() method, which calculates
the quilts corresponding to all the actual parameters of a function and then
pushes this vector of quilts onto a stack of vector of quilts, the topmost vec-
tor of this stack corresponds to the quilts of actual parameters of the most
recent function call. Hence, it becomes easy to map the formal parameters
with the actual parameters just by interpreting the quilt vector. We used
14
this vector of quilts in our function expression to evaluate the resulting quilt
of the function call.
15