I have an assignment to use JavaCC to make a Top-Down Parser with Semantic Analysis for a language supplied by the lecturer. I have the production rules written out and no errors. I'm completely stuck on how to use JJTree for my code and my hours of scouring the internet for tutorials hasn't gotten me anywhere. Just wondering could anyone take some time out to explain how to implement JJTree in the code? Or if there's a hidden step-by-step tutorial out there somewhere that would be a great help!
Here are some of my production rules in case they help. Thanks in advance!
void program() : {}
{
(decl())* (function())* main_prog()
}
void decl() #void : {}
{
(
var_decl() | const_decl()
)
}
void var_decl() #void : {}
{
<VAR> ident_list() <COLON> type()
(<COMMA> ident_list() <COLON> type())* <SEMIC>
}
void const_decl() #void : {}
{
<CONSTANT> identifier() <COLON> type() <EQUAL> expression()
( <COMMA> identifier() <COLON> type() <EQUAL > expression())* <SEMIC>
}
void function() #void : {}
{
type() identifier() <LBR> param_list() <RBR>
<CBL>
(decl())*
(statement() <SEMIC> )*
returnRule() (expression() | {} )<SEMIC>
<CBR>
}
Here is an example that uses JJTree http://anandsekar.github.io/writing-an-interpretter-using-javacc/
Creating an AST using JavaCC looks a lot like creating a "normal" parser (defined in a
jj
file). If you already have a working grammar, it's (relatively) easy :)Here are the steps needed to create an AST:
jj
grammar file tojjt
jjtree
on yourjjt
grammar, which will generate ajj
file for youjavacc
on your generatedjj
grammarjava
source filesHere's a quick step-by-step tutorial, assuming you're using MacOS or *nix, have the
javacc.jar
file in the same directory as your grammar file(s) andjava
andjavac
are on your system's PATH:1
Assuming your
jj
grammar file is calledTestParser.jj
, rename it:2
Now the tricky part: decorating your grammar so that the proper AST structure is created. You decorate an AST (or node, or production rule (all the same)) by adding a
#
followed by an identifier after it (and before the:
). In your original question, you have a lot of#void
in different productions, meaning you're creating the same type of AST's for different production rules: this is not what you want.If you don't decorate your production, the name of the production is used as the type of the node (so, you can remove the
#void
):Now the rule simply returns whatever AST the rule
var_decl()
orconst_decl()
returned.Let's now have a look at the (simplified)
var_decl
rule:which I decorated with the
#VAR
type. This now means that this rule will return the following tree structure:As you can see, the terminals are discarded from the AST! This also means that the
id
andexpr
rules loose the text their<ID>
terminal matched. Of course, this is not what you want. For the rules that need to keep the inner text the terminal matched, you need to explicitly set the.value
of the tree to the.image
of the matched terminal:causing the input
"var x : int = i;"
to look like this:This is how you create a proper structure for your AST. Below follows a small grammar that is a very simple version of your own grammar including a small
main
method to test it all:3
Let the
jjtree
class (included injavacc.jar
) create ajj
file for you:4
The previous step has created the file
TestParser.jj
(if everything went okay). Letjavacc
(also present injavacc.jar
) process it:5
To compile all source files, do:
(on Windows, do:
javac -cp .;javacc.jar *.java
)6
The moment of truth has arrived: let's see if everything actually works! To let the parser process the input:
execute the following:
and you should see the following being printed on your console:
Note that you don't see the text the
ID
's matched, but believe me, they're there. The methoddump()
simply does not show it.HTH
EDIT
For a working grammar including expressions, you could have a look at the following expression evaluator of mine: https://github.com/bkiers/Curta (the grammar is in
src/grammar
). You might want to have a look at how to create root-nodes in case of binary expressions.