Reputation: 11347
I'm trying to write a simple ANTLR parser to handle date adjustment, so for example I could write: +1w+1d
to mean "traverse a week and a day", or MIN(+30d, +1m)
to mean "30 days from the input date, or 1 month, whichever is sooner". These rules should be composable, so MIN(+30d, +1m)+1d
means "the day after (30 days from the input date, or 1 month from the input date, whichever is sooner)" and +1dMIN(+30d, +1m)
means "30 days from (the day after the input date) or 1 month after (the day after the input date) whichever is sooner".
[I appreciate the examples here are relatively trite - the real grammar needs to understand about weekends, holidays, month boundaries etc, so it might be things like "one month after(the end of the input date's month or the Friday after the input date - whichever comes first)" etc etc]
The code I want to write is:
DateAdjutmeParser parser = buildFromString("MAX(+30d,+1m)");
ParseTree tree = parser.rootNode();
return new MyVisitor().visit(tree, LocalDate.of(2020,4,23)); //Not allowed extra parameters here.
The problem is how exactly can I pass the "context Date"? I can't store it in the MyVisitor
class as a member since the visit()
call is recursive and that would overwrite the context. I could build up a parallel set of objects that did have the right methods, but that seems a lot of boilerplate.
Is there an ANTLR solution?
More details:
This is the Visitor I'd like to write:
public class MyVisitor extends DateAdjustBaseVisitor<LocalDate> {
@Override
public LocalDate visitOffsetRule(DateAdjustParser.OffsetRuleContext ctx) {
LocalDate contextDate = ???; //
return contextDate.plus(Integer.valueOf(ctx.num.toString()), ChronoUnit.valueOf(ctx.unit.toString()));
}
@Override
public LocalDate visitMinMaxRule(DateAdjustParser.MinMaxRuleContext ctx) {
LocalDate contextDate = ???; //
LocalDate left = this.visitChildren(ctx.left, contextDate);
LocalDate right = this.visitChildren(ctx.right, contextDate);
if(ctx.type.getText().equals("MIN")) {
return left.compareTo(right) > 0 ? left : right;
} else {
return left.compareTo(right) < 0 ? left : right;
}
}
}
here's my grammar:
grammar DateAdjust;
rootNode: offset+;
offset
: num=NUMBER unit=UNIT #OffsetRule
| type=( MIN | MAX ) '(' left=offset ',' right=offset ')' #MinMaxRule
;
UNIT: [dwmy]; //Days Weeks Months Years
NUMBER: [+-]?[0..9]+;
MAX: 'MAX';
MIN: 'MIN';
Upvotes: 2
Views: 1024
Reputation: 5991
Not an Antlr-specific solution, but a typical DSL solution is to use a scoped state table (aka symbol table) to accumulate the results of an AST/CST walk.
See this answer for an implementation. Another exists in the Antlr repository.
Upvotes: 1