I am creating a simple imperative language from scratch, I already have a working syntactic tree, without much complication, it just uses the bottom-up style of parsing to create it using a simple tree data structure. The idea now is to implement a complete LeBlanc-Cook style symbol table. Its structure is not complicated, the problem is that I don't know how to make happy fill it while creating the tree at the same time.
The idea behind doing it all in a single pass is that in this way the AST can be filled with only what is the minimum necessary, ignoring stuff like variable or type declarations, which only effects are in the symbol table. Traversing the AST to fill the table is my last option.
I have the basic idea of it being some kind of global state, modified just in certain select times, like when a new block is open or when a variable is declared, but I have no idea how to use the environment that happy gives me together with whatever monadic structure I would create.
I know this question could be reduced to something like "how does happy work?", but anyways. Any comment is appreciated.
Here's an example to illustrate a little better my question.
% monad {MyState}
...
START: INSTRUCTIONS { (AST_Root $1, Final_Symtable_State) } -- Ideally
INSTRUCTIONS: INSTRUCTIONS INSTRUCTION { $2:$1 } -- A list of all instructions
| INSTRUCTION { [$1] }
INSTRUCTION : VARDEF {%???}
| TYPEDEF
| VARMOD
| ...
...
VARDEF: let identifier : Int {%???} -- This should modify the symtable state and not the tree
| let identifier : Int = number {%???} -- This should modify the symtable and provide a new branch for the tree
It seems like a single state would maybe not work out, and there is a combination of monadic and not monadic actions, what would be the best structure to work this out.