I've a grammar rule like this:
fctDecl
: id AS FUNCTION O_PAR (argDecl (COMMA argDecl)*)? C_PAR COLON (scalar|VOID)
(DECLARE LOCAL (varDecl SEMICOLON)+)?
DO (instruction)+ (RETURN id)? DONE;
When i'm in the visitFctDecl
, I have to visit all children of FctDecl
. As children can be different type of rule, how can I know which is the type of the current child?
@Override
public FunctionNode visitFctDecl(B314Parser.FctDeclContext ctx) {
for (int i = 0; i < ctx.children.size() ;i++)
{
//what kind of rule is ?
ctx.children.get(i);
}
return null;
}
I'm not sure at all how to use visitor as it should be.
Start by chopping up that big parser rule. Something like this perhaps:
fctDecl
: id AS FUNCTION fctParams COLON ( scalar | VOID ) localDecl doStat
;
fctParams
: O_PAR ( argDecl ( COMMA argDecl )* )? C_PAR
;
localDecl
: ( DECLARE LOCAL ( varDecl SEMICOLON )+ )?
;
doStat
: DO instruction+ ( RETURN id )? DONE
;
and then in your visitor, you simply return custom classes like this (I'm returning Maps and Lists, but that could be your own domain objects):
public class DemoVisitor extends B314BaseVisitor<Object> {
// fctDecl
// : id AS FUNCTION fctParams COLON ( scalar | VOID ) localDecl doStat
// ;
@Override
public Object visitFctDecl(B314Parser.FctDeclContext ctx) {
Map<String, Object> result = new LinkedHashMap<String, Object>();
result.put("id", ctx.id().getText());
result.put("fctParams", visit(ctx.fctParams()));
result.put("type", ctx.scalar() != null ? ctx.scalar().getText() : ctx.VOID().getText());
result.put("localDecl", visit(ctx.localDecl()));
result.put("doStat", visit(ctx.doStat()));
return result;
}
// fctParams
// : O_PAR ( argDecl ( COMMA argDecl )* )? C_PAR
// ;
//
// argDecl
// : varDecl
// ;
//
// varDecl
// : ID AS type
// ;
@Override
public Object visitFctParams(B314Parser.FctParamsContext ctx) {
List<Map<String, String>> declarations = new ArrayList<Map<String, String>>();
if (ctx.argDecl() != null) {
for (B314Parser.ArgDeclContext argDecl : ctx.argDecl()) {
Map<String, String> declaration = new LinkedHashMap<String, String>();
declaration.put(argDecl.varDecl().ID().getText(), argDecl.varDecl().type().getText());
declarations.add(declaration);
}
}
return declarations;
}
// localDecl
// : ( DECLARE LOCAL ( varDecl SEMICOLON )+ )?
// ;
@Override
public Object visitLocalDecl(B314Parser.LocalDeclContext ctx) {
// See `visitFctParams(...)` how to traverse `( varDecl SEMICOLON )+`
return "TODO";
}
// doStat
// : DO instruction+ ( RETURN id )? DONE
// ;
@Override
public Object visitDoStat(B314Parser.DoStatContext ctx) {
// See `visitFctParams(...)` how to traverse `instruction+`
return "TODO";
}
}
If you now run the following class that uses the visitor above:
public class Main {
public static void main(String[] args) {
String source =
"mu as function(i as integer, b as boolean): integer\n" +
"declare local x as square; y as square;\n" +
"do\n" +
" skip\n" +
"done";
B314Lexer lexer = new B314Lexer(CharStreams.fromString(source));
B314Parser parser = new B314Parser(new CommonTokenStream(lexer));
ParseTree fctDeclTree = parser.fctDecl();
Object result = new DemoVisitor().visit(fctDeclTree);
System.out.printf("source:\n\n%s\n\nresult:\n\n%s\n", source, result);
}
}
the following will be printed on your console:
source:
mu as function(i as integer, b as boolean): integer
declare local x as square; y as square;
do
skip
done
result:
{id=mu, fctParams=[{i=integer}, {b=boolean}], type=integer, localDecl=TODO, doStat=TODO}