Description
Construct a parser for a subset of tiger that includes the following
exp:
lvalue
nil
int
string
exp binop exp
lvalue := exp
id ( explist )
( expseq )
let declist in expseq end
if exp then exp
if exp then exp else exp
lvalue:
# These are simple variables
id
binop:
+ - * / = <> < > <= >= & |
declist:
dec
dec declist
dec:
type id = id
var id : id := exp
function id ( typefields ) : id = exp
expseq:
exp
expseq ; exp
explist:
exp
explist , exp
typefields:
typefield
typefield, typefields
typefield:
id : id
NOTE: the only valid type ids in our language are aliases of the built in "int" and "string" types. We do not have arrays or structures or other compound types.
Part 1
Your assignment is to use the sample code in
chapter 3 to
implement (in tiger.grm) a parser for this subset of tiger. The full
description of tiger is in the Appendix A of the book, and you can get
a
short description of Tiger. Remember,
you don't have to implement the whole language, just what I describe
above. Also, each grammar production should return unit ()
. In the next part you will build an abstract systax tree, but for now just get the grammar working. Save a copy of your working grammar, in case you can't get Part 2 working.
You will definitely need to read Chapter 3 to see examples of building a parser, and the ML-Yacc manual is also useful.
Try to keep the number of conflicts in your parser to a minimum. You must not have any reduce-reduce conflicts, and explain any shift-reduce conflicts and how they are resolved.
Example code
You can parse a sample program using:
- CM.make "sources.cm";
- Parse.parse "test.tig";
() : unit
Here's a simple program that should parse.
let
function fac(n : int) : int =
if n <= 1 then
1
else
n * fac(n - 1)
in
fac(5)
end
Part 2
Your assignment is to use the sample code in
chapter 4 to
implement (in tiger.grm
) a parser for this subset of tiger that
builds the appropriate abstract syntax tree for each
production. Remember, in the last part all the productions
returned unit ()
? We said later you would have to change it? Now
each production must return an Absyn
. Look at the absyn.sml
to see
the data structures you must build.
For example, in absyn.sml
we see that one possible expression is a
StringExp
that has a string and an int (representing the postition
of the string in the file). Then in your tiger.grm
you should have a line like
exp : STRING (Absyn.StringExp (STRING, STRINGleft))
(if you get tired of typing Absyn.XXXX
all the time, alias Absyn
to A
, like the book says.)
The full description of tiger is in the Appendix A of the book, and you can get a short description of Tiger. Remember, you don't have to implement the whole language, just what I describe above.
You will definitely need to read Chapter 4 to see examples of building a parser and abstract syntax tree, and the ML-Yacc manual is also useful.
Example code
I built a parser for Straight Line Programs (SLP), that reads the text
and outputs a AST using a structure like slp.sml
from Chapter 1. I
also connected that parser with my own SLP interpreter. You can read
my code to see how
to build AST for SLP in slp/slp.grm
. Your assignment is not to
implement an SLP parser, but a parser for tiger. This is just so you
can get an idea for building the AST in the productions.
$ cat prog.slp
a := 5 + 3;
b := (print (a, a-1), 10*a);
print (b)
$ rlwrap sml
Standard ML of New Jersey v110.76 [built: Mon Jul 7 23:25:08 2014]
- CM.make "sources.cm";
[autoloading]
[library $smlnj/cm/cm.cm is stable]
...
[loading (sources.cm):parse.sml]
[New bindings added.]
val it = true : bool
- val prog = Parse.parse "prog.slp";
val prog =
CompoundStm (AssignStm ("a",OpExp #),CompoundStm (PrintStm #,PrintStm #))
: Absyn.stm
- Interp.interp prog;
8
7
80
val it = () : unit