CSE 423 HW#3: a Parser
Due: Tuesday February 25 11:59pm.
- Group assignment: one student turnin on canvas a .zip file named hw3.zip
along with names of group members. All students: turnin in a README
file listing the full names of your group members and assigning them
group grades: 2=full participation, 1=partial participation,
0=poor or non-participation
- The .zip should unpack in the current directory, include
a makefile and all files necessary to compile to executable.
- The compile should be free of warnings, other than shift/reduce
conflicts.
- Your makefile must use -Wall option for all GCC compiles
- Do not leave yydebug/YYDEBUG turned on. Do not leave any spurious
or debugging output in your submitted version.
- Execution should be valgrind-clean, meaning no illegal memory reads
or writes, and no use of uninitialized variables.
Write or adapt a Kotlin(-subset) grammar.
The recommended starting point is
You will have to produce it into a .y file that works with Bison, and use it
to produce a syntax tree for input K0 source files.
To the extent that you modify the grammar, you are advised to ignore/allow
any number of shift/reduce conflicts unless you have an example where they
result in an error. Do not tolerate any reduce/reduce
conflicts -- they generally require grammar changes such as refactoring
common sequences within two or more production rules. Whichever
grammar/parser you use, you are responsible
for making it work for you and for any bugs.
Your parser should include at least the following:
- To use your flex homework, rename terminal symbols from
your flex file or from the grammar (.y file), or vice-versa,
so that the Flex and Bison specifications use the same names/#defines.
- Add terminal symbols required by the .y file, possibly adapting missing
symbols' regular expressions from your .l file.
- For every production rule in the grammar:
- if the right hand side has > 1 children, add a semantic action to
construct a construct a tree node and assign $$ to point at it
- if the right hand side
has 0 or 1 children, add a semantic action to set $$ to NULL
(0 children) or to the child if there is 1 child ($$ = $1).
- Place a pointer to your token in a yylval field on each call to yylex(),
and ensure that those tokens get inserted as leaves into your syntax
tree.
- If the parse succeeds, assign the root of the tree to a global variable
from the semantic action associated with your start symbol.
- Revise the main() procedure from the Flex homework to call yyparse()
and use the yyparse()
return value and/or global variables to ascertain whether the parse succeeded.
- If the parse succeeded, traverse/print your syntax tree as described below.
If there is a syntax error, you should report the filename and line number.
Your program's exit status should be 0
if there are no errors, and a nonzero number to indicate errors. For
lexical errors, use the exit status 1. For syntax errors, the exit status
should be 2.
If there are no errors, you should print a text representation of a syntax
tree.
Suppose we have a tree structure that looks something like:
struct tree {
int prodrule;
char *symbolname;
int nkids;
struct tree *kids[9]; /* if nkids >0 */
struct token *leaf; /* if nkids == 0; might be null */
};
int treeprint(struct tree *t, int depth)
{
int i;
printf("%*s %s: %d\n", depth*2, " ", humanreadable(t), t->nkids);
for(i=0; i<t->nkids; i++)
treeprint(t->kids[i], depth+1);
}
If you write a function humanreadable(t) that returns a human readable string,
either from the production rule # or the optional symbolname,
you can get output that looks something like
program: 1
function: 2
function header: 3
typedecl: 1
functioname: 1
parmlist: 3
....
For leaves, print out the integer code and the lexeme (yytext[]) for the
token, if any.