CS 5363 Programming Languages and Compilers
Translating Functions and Parameters
Special Note: Several students
submitted programs for the last assignment so similar to one
another that copying must be suspected. It is completely
all right to discuss the assignment with others and even to help out
someone else with ideas. You are not permitted to trade code.
It is completely unacceptable to
submit a program that you did not write and do not understand
as your own work, even if it has extensive trivial changes:
changing variable names, recommenting, reformatting, and making
other trivial alterations.
In such a case I will take official disciplinary measures.
Initial Work on Assignment: This is the final assignment
translating the language described by
the recursive descent parser into MIPS assembly code.
This assignment deals only with function definitions
and function calls. We will leave the exponentiation operator ^ unimplemented.
This assignment can still use single-character identifiers and
single-character integers.
Portions of the Grammar to Implement (new features with
this assignment are highlighted in
red bold below):
M ---> { ( S | D ) } '#'
S ---> I | W | A | P | C | G
D ---> '(' id '(' [ id { ',' id } ] ')' { S } ')'
I ---> '[' E '?' { S } ':' { S } ']' | '[' E '?' { S } ']'
W ---> '{' E '?' { S } '}'
A ---> id '=' E ';'
P ---> '<' E ';'
C ---> '<' ( 'B' | 'T' | 'N' ) ';'
G ---> '>' id ';'
E ---> Q [ ('&' | '|') Q ]
Q ---> R [ ('<' | '>' | '<=' | '>=' | '==' | '!=' ) R ]
R ---> T { ('+' | '-') T }
T ---> U { ('*' | '/' | '%') U }
U ---> F '^' U | F
F ---> ['+' | '-' | '!'] ( '(' E ')' | id | num |
id '(' [ E { ',' E } ] ')' )
id ---> letter { letter | digit }
num ---> digit { digit }
Sample Source:
So far I've only supplied one source program involving functions:
( g (x, y) [y ? g = g(y, x%y); : g = x;] )
> a; > b;
c = g(a, b);
< a; < B; < b; < B; < c; < N;
#
This is a recursive greatest common divisor algorithm.
Here is a similar non-recursive version that does the
extended gcd algorithm (although only returns the gcd):
( g (x, y)
u = 1; v = 0; w = x; a = 0; b = 1; c = y;
{c != 0 ?
q = w/c;
r = u - a*q; s = v - b*q; t = w - c*q;
u = a; v = b; w = c;
a = r; b = s; c = t;
}
< u; < B; < v; < B; < w; < N; g = w;
)
> d; > e;
f = g(d, e);
< d; < B; < e; < B; < f; < N;
#
Issues arising during compilation:
- Syntatic issues:
- Notice the syntax of the return inside a function definition.
It uses the old Pascal notation of determining a value to return
from a function g, say, with the
pseudo-assignment statement: g = ....
However, this does not cause an immediate return, and could
be followed by other statements.
- There are no explicit declarations, though a formal parameter
is similar to the declaration of a local variable.
Otherwise, I intended that the appearance of any variable that
is not a parameter will mean that the variable is declared as a
global variable, even if the variable first appears in a function
declaration. As an interesting alternative, you could follow
the convention that all variables inside a function are
either formal parameters or local variable, and in either
case are separate from any global variables with the same name.
Special Note: I now think this second alternative is
the best way to handle the semantics of no declarations.
- The rest of the syntax is straightforward, though unconventional,
since the entire function definition is enclosed in parentheses.
- Compile-time issues:
- At compile time one needs to keep track of the names
of functions defined. There is nothing to keep from using
the same (one-character) name for a function and for an
integer variable, since the syntax of their use is different.
You can optionally store the number of variables of each function
in the symbol table.
- The formal parameters of functions behave like local
variables. As such they must not have scope outside the
definition of the function, and a parameter name that duplicates
the name of a global variable will mask
the name of the global inside the function.
(This is the same as C, C++, and Java. However, in C++ one
can use the scope resolution operator :: to
access global variables with the same name as a parameter.)
- It is necessary to maintain a symbol table so that inside
the definition of a function, a variable is first looked up
as a formal parameter, and then taken to be a global variable
(without any required declaration) in case it is not a
parameter.
- As we mentioned in class, the compile-time symbol table
must be maintained like a stack (to access parameters first when
compiling function definitions), or as a stack of two symbol
tables, with the symbol table for the function pushed in top,
- Run-time issues:
- At run-time you should use a separate activation record on the MIPS
stack for each call to a function (recursive or not).
The MIPS code in the factorial example below may help you see
how to manage these activation records.
- When a function is called, you must decide how to put the
actual parameters on the stack.
- Inside the body of a function, you must create the
activation record on the stack, you must save the return address,
and you must successfully
access the values of the formal parameters. Just before the
return you must restore the return address from the stack.
- You must arrange inside the body of a function to provide
the value returned to the calling function, and the calling
function must access this value.
- Sample MIPS code that may help:
Factorial program:
MIPS (.s),
PDF (.pdf),
Postscript (.ps).
The only version listed under .s, and the version on
the left in .pdf or .ps format is the one you should look at.
It uses the stack completely for parameters, for the return value,
and for the return address; the version on the right makes
more use of registers. (Of course some use of registers is
required.)
What to turn in: You should turn in the program source
with extra introductory documentation. You should
show runs of the two sample programs above, along with the
MIPS output for one of them. In case you can't get your program
to work for the source above, but can for simpler source,
just turn this in instead. In case the MIPS code looks OK
but doesn't run or doesn't run correctly, you should still
turn in the program and the MIPS output for substantial
partial credit. In case you have worked on the assignment,
but have nothing that executes correctly, you should still turn
in what you have (with an explanation in the introductory
documentation) for partial credit.
The introductory documentation is required and
must be in "bulleted" form,
rather than long paragraphs of discussion, and must not be
hand-written. It should answer the various questions that I
would naturally be interested in about your program:
- Your name and the course number.
- The results of your work. (Is everything working as
nearly as you know? If not, what is working and what is not?)
- Explain how you are handling variable names at compile time.
- Describe your run-time mechanisms: how you are handling
actual parameters and formal parameters, your particular
arrangement of items on the run-time stack, your particular
method for returning the value of a function, and any other
details.
- Difficulties you had during implementation and debugging.
Revision date: 2002-12-01.
(Use ISO 8601,
an International Standard.)