I've been working through "Modern Compiler Implementation in ML", converting SML to OCaml as I go. The book defines a language called Tiger that has a let ... in ... end
syntax for declaring types, variables, and functions in scope for a given expression. Additionally, adjacent declarations of the same kind should be grouped together to allow for mutual recursion.
I've tried to represent this is Menhir with the following grammar snippet:
%right FUNCTION TYPE
.
.
.
decs: l = list(dec) { l }
dec:
| l = nonempty_list(tydec) { A.TypeDec l }
| v = vardec { v }
| l = nonempty_list(fundec) { A.FunctionDec l }
tydec:
| TYPE; name = ID; EQUAL; ty = ty {
A.{
type_name = Symbol.symbol name;
type_ty = ty;
type_pos = Position.make $startpos $endpos
}
}
With this, I get a shift/reduce conflict but Menhir resolves it the way I'd like. I want the nonempty_list(typec)
to be greedy so adjacent TYPE
declarations are grouped together. I.e., with Menhir resolving the conflict my generated AST looks something like:
(LetExp
(decs
((TypeDec
(((type_name (my_type)) (type_ty (NameTy (int))))
((type_name (my_type2)) (type_ty (NameTy (string))))
))))
(body (SeqExp ())))
I'd like to get rid of the warning, but I can't figure out to resolve the conflict the same way as Menhir. I've tried using %inline tydec
, which does make the warning go away, but the shift of TYPE
isn't applied as I would expect. Instead, preference is given to the list in decs
, yielding an AST that looks like this:
(LetExp
(decs
((TypeDec
(((type_name (my_type)) (type_ty (NameTy (int))))))
(TypeDec
(((type_name (my_type2)) (type_ty (NameTy (string)))
)))))
(body (SeqExp ())))
I've also tried explicitly setting the precedence, but Menhir warns me that it's a useless declaration.
I'm sure I'm missing something fundamental here. Give productions that yield lists of lists, how can I make the inner list greedy?