Evaluate tree with expression inside other expression
For starters I would like to apologise if I am not being precise on the matter.
grammar Test;
@parser::header #pragma warning disable 3021
@lexer::header #pragma warning disable 3021
prog : expression? EOF;
expression : TEXT #text
| shift_left #shiftLeft
| shift_right #shiftRight
| upper_case #upperCase
| lower_case #lowerCase
| substring #ssubstring
| expression CONCANTENATE expression #concatenate
;
substring : SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET;
shift_left : SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET;
shift_right : SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET;
upper_case : UPPER OBRACKET expression CBRACKET;
lower_case : LOWER OBRACKET expression CBRACKET;
compileUnit
: EOF
;
/*
* Lexer Rules
*/
fragment L : ('L'|'l') ;
fragment E : ('E'|'e') ;
fragment F : ('F'|'f') ;
fragment T : ('T'|'t') ;
fragment U : ('U'|'u') ;
fragment P : ('P'|'p') ;
fragment R : ('R'|'r') ;
fragment O : ('O'|'o') ;
fragment W : ('W'|'w') ;
fragment I : ('I'|'i') ;
fragment G : ('G'|'g') ;
fragment H : ('H'|'h') ;
fragment S : ('S'|'s') ;
fragment B : ('B'|'b') ;
fragment N : ('N'|'n') ;
COMMA : ',';
OBRACKET : '(';
CBRACKET : ')';
CONCANTENATE : '+';
NUMBER : [1-9] (DIGIT)*;
DIGIT : [0-9];
SHIFT_RIGHT : R I G H T;
UPPER : U P P E R;
LOWER : L O W E R;
SUBSTRING : S U B S T R I N G;
SHIFT_LEFT : L E F T;
TEXT : '"' .*? '"';
WHITESPACE : (' '|'t'|'r'|'n')+ -> skip ;
WS
: ' ' -> channel(HIDDEN)
;
What I want to achive is to evaluate the tree (or in other words - to be able to actually do simple operations). The whole idea of the grammar is to perform simple operations on strings. The problem itself is that I do not know how to actually traverse the tree and evaluate its expressions. It will be much easier for me to put an example here:
"upper(left("text"),2)" <- this operation is a nested operation which is supposed to: 1. shift "text" by 2 to the left (it does not really matter what it does actually). 2. Return the shifted value "up". 3. Upper expression is supposed to gather whatever the left() produced and do its thing, in that case take shifted "text" to upper case.
This whole "nested expressions" are causing the problem. I have implemented my own visitor class and I have bunch of methods to override, for example expression, substring, shiftright and so on - all taken from the grammar but I have no idea how to use them in the case I am facing, what methods to use so I could actually use the grammar.
c# antlr antlr4
add a comment |
For starters I would like to apologise if I am not being precise on the matter.
grammar Test;
@parser::header #pragma warning disable 3021
@lexer::header #pragma warning disable 3021
prog : expression? EOF;
expression : TEXT #text
| shift_left #shiftLeft
| shift_right #shiftRight
| upper_case #upperCase
| lower_case #lowerCase
| substring #ssubstring
| expression CONCANTENATE expression #concatenate
;
substring : SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET;
shift_left : SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET;
shift_right : SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET;
upper_case : UPPER OBRACKET expression CBRACKET;
lower_case : LOWER OBRACKET expression CBRACKET;
compileUnit
: EOF
;
/*
* Lexer Rules
*/
fragment L : ('L'|'l') ;
fragment E : ('E'|'e') ;
fragment F : ('F'|'f') ;
fragment T : ('T'|'t') ;
fragment U : ('U'|'u') ;
fragment P : ('P'|'p') ;
fragment R : ('R'|'r') ;
fragment O : ('O'|'o') ;
fragment W : ('W'|'w') ;
fragment I : ('I'|'i') ;
fragment G : ('G'|'g') ;
fragment H : ('H'|'h') ;
fragment S : ('S'|'s') ;
fragment B : ('B'|'b') ;
fragment N : ('N'|'n') ;
COMMA : ',';
OBRACKET : '(';
CBRACKET : ')';
CONCANTENATE : '+';
NUMBER : [1-9] (DIGIT)*;
DIGIT : [0-9];
SHIFT_RIGHT : R I G H T;
UPPER : U P P E R;
LOWER : L O W E R;
SUBSTRING : S U B S T R I N G;
SHIFT_LEFT : L E F T;
TEXT : '"' .*? '"';
WHITESPACE : (' '|'t'|'r'|'n')+ -> skip ;
WS
: ' ' -> channel(HIDDEN)
;
What I want to achive is to evaluate the tree (or in other words - to be able to actually do simple operations). The whole idea of the grammar is to perform simple operations on strings. The problem itself is that I do not know how to actually traverse the tree and evaluate its expressions. It will be much easier for me to put an example here:
"upper(left("text"),2)" <- this operation is a nested operation which is supposed to: 1. shift "text" by 2 to the left (it does not really matter what it does actually). 2. Return the shifted value "up". 3. Upper expression is supposed to gather whatever the left() produced and do its thing, in that case take shifted "text" to upper case.
This whole "nested expressions" are causing the problem. I have implemented my own visitor class and I have bunch of methods to override, for example expression, substring, shiftright and so on - all taken from the grammar but I have no idea how to use them in the case I am facing, what methods to use so I could actually use the grammar.
c# antlr antlr4
add a comment |
For starters I would like to apologise if I am not being precise on the matter.
grammar Test;
@parser::header #pragma warning disable 3021
@lexer::header #pragma warning disable 3021
prog : expression? EOF;
expression : TEXT #text
| shift_left #shiftLeft
| shift_right #shiftRight
| upper_case #upperCase
| lower_case #lowerCase
| substring #ssubstring
| expression CONCANTENATE expression #concatenate
;
substring : SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET;
shift_left : SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET;
shift_right : SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET;
upper_case : UPPER OBRACKET expression CBRACKET;
lower_case : LOWER OBRACKET expression CBRACKET;
compileUnit
: EOF
;
/*
* Lexer Rules
*/
fragment L : ('L'|'l') ;
fragment E : ('E'|'e') ;
fragment F : ('F'|'f') ;
fragment T : ('T'|'t') ;
fragment U : ('U'|'u') ;
fragment P : ('P'|'p') ;
fragment R : ('R'|'r') ;
fragment O : ('O'|'o') ;
fragment W : ('W'|'w') ;
fragment I : ('I'|'i') ;
fragment G : ('G'|'g') ;
fragment H : ('H'|'h') ;
fragment S : ('S'|'s') ;
fragment B : ('B'|'b') ;
fragment N : ('N'|'n') ;
COMMA : ',';
OBRACKET : '(';
CBRACKET : ')';
CONCANTENATE : '+';
NUMBER : [1-9] (DIGIT)*;
DIGIT : [0-9];
SHIFT_RIGHT : R I G H T;
UPPER : U P P E R;
LOWER : L O W E R;
SUBSTRING : S U B S T R I N G;
SHIFT_LEFT : L E F T;
TEXT : '"' .*? '"';
WHITESPACE : (' '|'t'|'r'|'n')+ -> skip ;
WS
: ' ' -> channel(HIDDEN)
;
What I want to achive is to evaluate the tree (or in other words - to be able to actually do simple operations). The whole idea of the grammar is to perform simple operations on strings. The problem itself is that I do not know how to actually traverse the tree and evaluate its expressions. It will be much easier for me to put an example here:
"upper(left("text"),2)" <- this operation is a nested operation which is supposed to: 1. shift "text" by 2 to the left (it does not really matter what it does actually). 2. Return the shifted value "up". 3. Upper expression is supposed to gather whatever the left() produced and do its thing, in that case take shifted "text" to upper case.
This whole "nested expressions" are causing the problem. I have implemented my own visitor class and I have bunch of methods to override, for example expression, substring, shiftright and so on - all taken from the grammar but I have no idea how to use them in the case I am facing, what methods to use so I could actually use the grammar.
c# antlr antlr4
For starters I would like to apologise if I am not being precise on the matter.
grammar Test;
@parser::header #pragma warning disable 3021
@lexer::header #pragma warning disable 3021
prog : expression? EOF;
expression : TEXT #text
| shift_left #shiftLeft
| shift_right #shiftRight
| upper_case #upperCase
| lower_case #lowerCase
| substring #ssubstring
| expression CONCANTENATE expression #concatenate
;
substring : SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET;
shift_left : SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET;
shift_right : SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET;
upper_case : UPPER OBRACKET expression CBRACKET;
lower_case : LOWER OBRACKET expression CBRACKET;
compileUnit
: EOF
;
/*
* Lexer Rules
*/
fragment L : ('L'|'l') ;
fragment E : ('E'|'e') ;
fragment F : ('F'|'f') ;
fragment T : ('T'|'t') ;
fragment U : ('U'|'u') ;
fragment P : ('P'|'p') ;
fragment R : ('R'|'r') ;
fragment O : ('O'|'o') ;
fragment W : ('W'|'w') ;
fragment I : ('I'|'i') ;
fragment G : ('G'|'g') ;
fragment H : ('H'|'h') ;
fragment S : ('S'|'s') ;
fragment B : ('B'|'b') ;
fragment N : ('N'|'n') ;
COMMA : ',';
OBRACKET : '(';
CBRACKET : ')';
CONCANTENATE : '+';
NUMBER : [1-9] (DIGIT)*;
DIGIT : [0-9];
SHIFT_RIGHT : R I G H T;
UPPER : U P P E R;
LOWER : L O W E R;
SUBSTRING : S U B S T R I N G;
SHIFT_LEFT : L E F T;
TEXT : '"' .*? '"';
WHITESPACE : (' '|'t'|'r'|'n')+ -> skip ;
WS
: ' ' -> channel(HIDDEN)
;
What I want to achive is to evaluate the tree (or in other words - to be able to actually do simple operations). The whole idea of the grammar is to perform simple operations on strings. The problem itself is that I do not know how to actually traverse the tree and evaluate its expressions. It will be much easier for me to put an example here:
"upper(left("text"),2)" <- this operation is a nested operation which is supposed to: 1. shift "text" by 2 to the left (it does not really matter what it does actually). 2. Return the shifted value "up". 3. Upper expression is supposed to gather whatever the left() produced and do its thing, in that case take shifted "text" to upper case.
This whole "nested expressions" are causing the problem. I have implemented my own visitor class and I have bunch of methods to override, for example expression, substring, shiftright and so on - all taken from the grammar but I have no idea how to use them in the case I am facing, what methods to use so I could actually use the grammar.
c# antlr antlr4
c# antlr antlr4
asked Nov 13 '18 at 21:48
ThatKidMikeThatKidMike
185
185
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
First of all, the fact that you have a lot of almost-identical names such as shift_left
vs. shiftLeft
is just begging for bugs to be introduced, so I strongly recommend you refactor your grammar to either:
expression : text
| shiftLeft
| shiftRight
| upperCase
| lowerCase
| substring
| concatenate
;
text : TEXT;
substring : SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET;
shiftLeft : SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET;
shiftRight : SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET;
upperCase : UPPER OBRACKET expression CBRACKET;
lowerCase : LOWER OBRACKET expression CBRACKET;
concatenate : expression CONCANTENATE expression;
Or:
expression : TEXT #text
| SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET #shiftLeft
| SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET #shiftRight
| UPPER OBRACKET expression CBRACKET #upperCase
| LOWER OBRACKET expression CBRACKET #lowerCase
| SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET #substring
| expression CONCANTENATE expression #concatenate
;
I'd go with the latter because it produces a simpler tree.
To visit nested expressions, you simply call Visit
recursively on the subexpressions and then combine the results as appropriate. So a visitor that covers your example could simply look like this:
override String VisitText(TextContext ctx)
return ctx.TEXT().Text();
override String VisitUpper(UpperContext ctx)
return Visit(ctx.expression()).ToUpper();
override String VisitShiftLeft(ShiftLeftContext ctx)
int n = int.Parse(ctx.NUMBER().Text());
// I'm assuming here that "shift left by N" means "remove N first chars"
return Visit(ctx.expression()).Substring(n);
The visit methods for the other expression types would follow the same logic.
Thank you very much. You've just saved my life. :) Now everything's work like a charm. I used your latter option with grammar and after some tweaks the whole visitor is pretty much done. I'm putting the code down here for anyone who might need it in future.
– ThatKidMike
Nov 14 '18 at 16:23
add a comment |
Thanks to @sepp2k - I'm putting the whole solution in C# of the visitor:
public sealed class TreeEvaluationVisitor : TestBaseVisitor<Object> {
public override object VisitText([NotNull] TestParser.TextContext context)
int string_length = context.TEXT().ToString().Length;
return context.TEXT().ToString().Substring(1, string_length - 2);
//Substring() up here is for omitting the quote marks in the final output
public override object VisitUpperCase([NotNull] TestParser.UpperCaseContext context)
int string_length = Visit(context.expression()).ToString().Length;
return Visit(context.expression()).ToString().ToUpper();
public override object VisitLowerCase([NotNull] TestParser.LowerCaseContext context)
int string_length = Visit(context.expression()).ToString().Length;
return Visit(context.expression()).ToString().ToLower();
public override object VisitShiftLeft([NotNull] TestParser.ShiftLeftContext context)
int n = int.Parse(context.NUMBER().ToString());
return sh_left(Visit(context.expression()).ToString(), n);
public override object VisitShiftRight([NotNull] TestParser.ShiftRightContext context)
int n = int.Parse(context.NUMBER().ToString());
return sh_right(Visit(context.expression()).ToString(), n);
public override object VisitConcatenate([NotNull] TestParser.ConcatenateContext context)
string left = Visit(context.expression(0)).ToString();
string right = Visit(context.expression(1)).ToString();
return left + right;
public override object VisitSubstring([NotNull] TestParser.SubstringContext context)
int n1 = int.Parse(context.NUMBER(0).ToString());
int n2 = int.Parse(context.NUMBER(1).ToString());
return Visit(context.expression()).ToString().Substring(n1, n2);
//shift methods for shifting strings, i. e. left("abc",2) -> result = cab
private static string sh_left(string chain, int amount)
return (chain.Substring(amount) + chain.Substring(0, amount));
private static string sh_right(string chain, int amount)
return chain.Substring(chain.Length - amount)
+ chain.Substring(0, chain.Length - amount);
You can putusing static TestParser;
at the beginning of the file if you want to get rid of all theTestParser.
s in the class names.
– sepp2k
Nov 14 '18 at 16:31
Oh, thanks for that. Surely, I'll do that. Nevertheless, for academic purpose, you've basically covered the whole problem for me.
– ThatKidMike
Nov 14 '18 at 16:34
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53290014%2fevaluate-tree-with-expression-inside-other-expression%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
First of all, the fact that you have a lot of almost-identical names such as shift_left
vs. shiftLeft
is just begging for bugs to be introduced, so I strongly recommend you refactor your grammar to either:
expression : text
| shiftLeft
| shiftRight
| upperCase
| lowerCase
| substring
| concatenate
;
text : TEXT;
substring : SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET;
shiftLeft : SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET;
shiftRight : SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET;
upperCase : UPPER OBRACKET expression CBRACKET;
lowerCase : LOWER OBRACKET expression CBRACKET;
concatenate : expression CONCANTENATE expression;
Or:
expression : TEXT #text
| SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET #shiftLeft
| SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET #shiftRight
| UPPER OBRACKET expression CBRACKET #upperCase
| LOWER OBRACKET expression CBRACKET #lowerCase
| SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET #substring
| expression CONCANTENATE expression #concatenate
;
I'd go with the latter because it produces a simpler tree.
To visit nested expressions, you simply call Visit
recursively on the subexpressions and then combine the results as appropriate. So a visitor that covers your example could simply look like this:
override String VisitText(TextContext ctx)
return ctx.TEXT().Text();
override String VisitUpper(UpperContext ctx)
return Visit(ctx.expression()).ToUpper();
override String VisitShiftLeft(ShiftLeftContext ctx)
int n = int.Parse(ctx.NUMBER().Text());
// I'm assuming here that "shift left by N" means "remove N first chars"
return Visit(ctx.expression()).Substring(n);
The visit methods for the other expression types would follow the same logic.
Thank you very much. You've just saved my life. :) Now everything's work like a charm. I used your latter option with grammar and after some tweaks the whole visitor is pretty much done. I'm putting the code down here for anyone who might need it in future.
– ThatKidMike
Nov 14 '18 at 16:23
add a comment |
First of all, the fact that you have a lot of almost-identical names such as shift_left
vs. shiftLeft
is just begging for bugs to be introduced, so I strongly recommend you refactor your grammar to either:
expression : text
| shiftLeft
| shiftRight
| upperCase
| lowerCase
| substring
| concatenate
;
text : TEXT;
substring : SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET;
shiftLeft : SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET;
shiftRight : SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET;
upperCase : UPPER OBRACKET expression CBRACKET;
lowerCase : LOWER OBRACKET expression CBRACKET;
concatenate : expression CONCANTENATE expression;
Or:
expression : TEXT #text
| SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET #shiftLeft
| SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET #shiftRight
| UPPER OBRACKET expression CBRACKET #upperCase
| LOWER OBRACKET expression CBRACKET #lowerCase
| SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET #substring
| expression CONCANTENATE expression #concatenate
;
I'd go with the latter because it produces a simpler tree.
To visit nested expressions, you simply call Visit
recursively on the subexpressions and then combine the results as appropriate. So a visitor that covers your example could simply look like this:
override String VisitText(TextContext ctx)
return ctx.TEXT().Text();
override String VisitUpper(UpperContext ctx)
return Visit(ctx.expression()).ToUpper();
override String VisitShiftLeft(ShiftLeftContext ctx)
int n = int.Parse(ctx.NUMBER().Text());
// I'm assuming here that "shift left by N" means "remove N first chars"
return Visit(ctx.expression()).Substring(n);
The visit methods for the other expression types would follow the same logic.
Thank you very much. You've just saved my life. :) Now everything's work like a charm. I used your latter option with grammar and after some tweaks the whole visitor is pretty much done. I'm putting the code down here for anyone who might need it in future.
– ThatKidMike
Nov 14 '18 at 16:23
add a comment |
First of all, the fact that you have a lot of almost-identical names such as shift_left
vs. shiftLeft
is just begging for bugs to be introduced, so I strongly recommend you refactor your grammar to either:
expression : text
| shiftLeft
| shiftRight
| upperCase
| lowerCase
| substring
| concatenate
;
text : TEXT;
substring : SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET;
shiftLeft : SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET;
shiftRight : SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET;
upperCase : UPPER OBRACKET expression CBRACKET;
lowerCase : LOWER OBRACKET expression CBRACKET;
concatenate : expression CONCANTENATE expression;
Or:
expression : TEXT #text
| SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET #shiftLeft
| SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET #shiftRight
| UPPER OBRACKET expression CBRACKET #upperCase
| LOWER OBRACKET expression CBRACKET #lowerCase
| SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET #substring
| expression CONCANTENATE expression #concatenate
;
I'd go with the latter because it produces a simpler tree.
To visit nested expressions, you simply call Visit
recursively on the subexpressions and then combine the results as appropriate. So a visitor that covers your example could simply look like this:
override String VisitText(TextContext ctx)
return ctx.TEXT().Text();
override String VisitUpper(UpperContext ctx)
return Visit(ctx.expression()).ToUpper();
override String VisitShiftLeft(ShiftLeftContext ctx)
int n = int.Parse(ctx.NUMBER().Text());
// I'm assuming here that "shift left by N" means "remove N first chars"
return Visit(ctx.expression()).Substring(n);
The visit methods for the other expression types would follow the same logic.
First of all, the fact that you have a lot of almost-identical names such as shift_left
vs. shiftLeft
is just begging for bugs to be introduced, so I strongly recommend you refactor your grammar to either:
expression : text
| shiftLeft
| shiftRight
| upperCase
| lowerCase
| substring
| concatenate
;
text : TEXT;
substring : SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET;
shiftLeft : SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET;
shiftRight : SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET;
upperCase : UPPER OBRACKET expression CBRACKET;
lowerCase : LOWER OBRACKET expression CBRACKET;
concatenate : expression CONCANTENATE expression;
Or:
expression : TEXT #text
| SHIFT_LEFT OBRACKET expression COMMA NUMBER CBRACKET #shiftLeft
| SHIFT_RIGHT OBRACKET expression COMMA NUMBER CBRACKET #shiftRight
| UPPER OBRACKET expression CBRACKET #upperCase
| LOWER OBRACKET expression CBRACKET #lowerCase
| SUBSTRING OBRACKET expression COMMA NUMBER COMMA NUMBER CBRACKET #substring
| expression CONCANTENATE expression #concatenate
;
I'd go with the latter because it produces a simpler tree.
To visit nested expressions, you simply call Visit
recursively on the subexpressions and then combine the results as appropriate. So a visitor that covers your example could simply look like this:
override String VisitText(TextContext ctx)
return ctx.TEXT().Text();
override String VisitUpper(UpperContext ctx)
return Visit(ctx.expression()).ToUpper();
override String VisitShiftLeft(ShiftLeftContext ctx)
int n = int.Parse(ctx.NUMBER().Text());
// I'm assuming here that "shift left by N" means "remove N first chars"
return Visit(ctx.expression()).Substring(n);
The visit methods for the other expression types would follow the same logic.
answered Nov 13 '18 at 22:19
sepp2ksepp2k
294k38595610
294k38595610
Thank you very much. You've just saved my life. :) Now everything's work like a charm. I used your latter option with grammar and after some tweaks the whole visitor is pretty much done. I'm putting the code down here for anyone who might need it in future.
– ThatKidMike
Nov 14 '18 at 16:23
add a comment |
Thank you very much. You've just saved my life. :) Now everything's work like a charm. I used your latter option with grammar and after some tweaks the whole visitor is pretty much done. I'm putting the code down here for anyone who might need it in future.
– ThatKidMike
Nov 14 '18 at 16:23
Thank you very much. You've just saved my life. :) Now everything's work like a charm. I used your latter option with grammar and after some tweaks the whole visitor is pretty much done. I'm putting the code down here for anyone who might need it in future.
– ThatKidMike
Nov 14 '18 at 16:23
Thank you very much. You've just saved my life. :) Now everything's work like a charm. I used your latter option with grammar and after some tweaks the whole visitor is pretty much done. I'm putting the code down here for anyone who might need it in future.
– ThatKidMike
Nov 14 '18 at 16:23
add a comment |
Thanks to @sepp2k - I'm putting the whole solution in C# of the visitor:
public sealed class TreeEvaluationVisitor : TestBaseVisitor<Object> {
public override object VisitText([NotNull] TestParser.TextContext context)
int string_length = context.TEXT().ToString().Length;
return context.TEXT().ToString().Substring(1, string_length - 2);
//Substring() up here is for omitting the quote marks in the final output
public override object VisitUpperCase([NotNull] TestParser.UpperCaseContext context)
int string_length = Visit(context.expression()).ToString().Length;
return Visit(context.expression()).ToString().ToUpper();
public override object VisitLowerCase([NotNull] TestParser.LowerCaseContext context)
int string_length = Visit(context.expression()).ToString().Length;
return Visit(context.expression()).ToString().ToLower();
public override object VisitShiftLeft([NotNull] TestParser.ShiftLeftContext context)
int n = int.Parse(context.NUMBER().ToString());
return sh_left(Visit(context.expression()).ToString(), n);
public override object VisitShiftRight([NotNull] TestParser.ShiftRightContext context)
int n = int.Parse(context.NUMBER().ToString());
return sh_right(Visit(context.expression()).ToString(), n);
public override object VisitConcatenate([NotNull] TestParser.ConcatenateContext context)
string left = Visit(context.expression(0)).ToString();
string right = Visit(context.expression(1)).ToString();
return left + right;
public override object VisitSubstring([NotNull] TestParser.SubstringContext context)
int n1 = int.Parse(context.NUMBER(0).ToString());
int n2 = int.Parse(context.NUMBER(1).ToString());
return Visit(context.expression()).ToString().Substring(n1, n2);
//shift methods for shifting strings, i. e. left("abc",2) -> result = cab
private static string sh_left(string chain, int amount)
return (chain.Substring(amount) + chain.Substring(0, amount));
private static string sh_right(string chain, int amount)
return chain.Substring(chain.Length - amount)
+ chain.Substring(0, chain.Length - amount);
You can putusing static TestParser;
at the beginning of the file if you want to get rid of all theTestParser.
s in the class names.
– sepp2k
Nov 14 '18 at 16:31
Oh, thanks for that. Surely, I'll do that. Nevertheless, for academic purpose, you've basically covered the whole problem for me.
– ThatKidMike
Nov 14 '18 at 16:34
add a comment |
Thanks to @sepp2k - I'm putting the whole solution in C# of the visitor:
public sealed class TreeEvaluationVisitor : TestBaseVisitor<Object> {
public override object VisitText([NotNull] TestParser.TextContext context)
int string_length = context.TEXT().ToString().Length;
return context.TEXT().ToString().Substring(1, string_length - 2);
//Substring() up here is for omitting the quote marks in the final output
public override object VisitUpperCase([NotNull] TestParser.UpperCaseContext context)
int string_length = Visit(context.expression()).ToString().Length;
return Visit(context.expression()).ToString().ToUpper();
public override object VisitLowerCase([NotNull] TestParser.LowerCaseContext context)
int string_length = Visit(context.expression()).ToString().Length;
return Visit(context.expression()).ToString().ToLower();
public override object VisitShiftLeft([NotNull] TestParser.ShiftLeftContext context)
int n = int.Parse(context.NUMBER().ToString());
return sh_left(Visit(context.expression()).ToString(), n);
public override object VisitShiftRight([NotNull] TestParser.ShiftRightContext context)
int n = int.Parse(context.NUMBER().ToString());
return sh_right(Visit(context.expression()).ToString(), n);
public override object VisitConcatenate([NotNull] TestParser.ConcatenateContext context)
string left = Visit(context.expression(0)).ToString();
string right = Visit(context.expression(1)).ToString();
return left + right;
public override object VisitSubstring([NotNull] TestParser.SubstringContext context)
int n1 = int.Parse(context.NUMBER(0).ToString());
int n2 = int.Parse(context.NUMBER(1).ToString());
return Visit(context.expression()).ToString().Substring(n1, n2);
//shift methods for shifting strings, i. e. left("abc",2) -> result = cab
private static string sh_left(string chain, int amount)
return (chain.Substring(amount) + chain.Substring(0, amount));
private static string sh_right(string chain, int amount)
return chain.Substring(chain.Length - amount)
+ chain.Substring(0, chain.Length - amount);
You can putusing static TestParser;
at the beginning of the file if you want to get rid of all theTestParser.
s in the class names.
– sepp2k
Nov 14 '18 at 16:31
Oh, thanks for that. Surely, I'll do that. Nevertheless, for academic purpose, you've basically covered the whole problem for me.
– ThatKidMike
Nov 14 '18 at 16:34
add a comment |
Thanks to @sepp2k - I'm putting the whole solution in C# of the visitor:
public sealed class TreeEvaluationVisitor : TestBaseVisitor<Object> {
public override object VisitText([NotNull] TestParser.TextContext context)
int string_length = context.TEXT().ToString().Length;
return context.TEXT().ToString().Substring(1, string_length - 2);
//Substring() up here is for omitting the quote marks in the final output
public override object VisitUpperCase([NotNull] TestParser.UpperCaseContext context)
int string_length = Visit(context.expression()).ToString().Length;
return Visit(context.expression()).ToString().ToUpper();
public override object VisitLowerCase([NotNull] TestParser.LowerCaseContext context)
int string_length = Visit(context.expression()).ToString().Length;
return Visit(context.expression()).ToString().ToLower();
public override object VisitShiftLeft([NotNull] TestParser.ShiftLeftContext context)
int n = int.Parse(context.NUMBER().ToString());
return sh_left(Visit(context.expression()).ToString(), n);
public override object VisitShiftRight([NotNull] TestParser.ShiftRightContext context)
int n = int.Parse(context.NUMBER().ToString());
return sh_right(Visit(context.expression()).ToString(), n);
public override object VisitConcatenate([NotNull] TestParser.ConcatenateContext context)
string left = Visit(context.expression(0)).ToString();
string right = Visit(context.expression(1)).ToString();
return left + right;
public override object VisitSubstring([NotNull] TestParser.SubstringContext context)
int n1 = int.Parse(context.NUMBER(0).ToString());
int n2 = int.Parse(context.NUMBER(1).ToString());
return Visit(context.expression()).ToString().Substring(n1, n2);
//shift methods for shifting strings, i. e. left("abc",2) -> result = cab
private static string sh_left(string chain, int amount)
return (chain.Substring(amount) + chain.Substring(0, amount));
private static string sh_right(string chain, int amount)
return chain.Substring(chain.Length - amount)
+ chain.Substring(0, chain.Length - amount);
Thanks to @sepp2k - I'm putting the whole solution in C# of the visitor:
public sealed class TreeEvaluationVisitor : TestBaseVisitor<Object> {
public override object VisitText([NotNull] TestParser.TextContext context)
int string_length = context.TEXT().ToString().Length;
return context.TEXT().ToString().Substring(1, string_length - 2);
//Substring() up here is for omitting the quote marks in the final output
public override object VisitUpperCase([NotNull] TestParser.UpperCaseContext context)
int string_length = Visit(context.expression()).ToString().Length;
return Visit(context.expression()).ToString().ToUpper();
public override object VisitLowerCase([NotNull] TestParser.LowerCaseContext context)
int string_length = Visit(context.expression()).ToString().Length;
return Visit(context.expression()).ToString().ToLower();
public override object VisitShiftLeft([NotNull] TestParser.ShiftLeftContext context)
int n = int.Parse(context.NUMBER().ToString());
return sh_left(Visit(context.expression()).ToString(), n);
public override object VisitShiftRight([NotNull] TestParser.ShiftRightContext context)
int n = int.Parse(context.NUMBER().ToString());
return sh_right(Visit(context.expression()).ToString(), n);
public override object VisitConcatenate([NotNull] TestParser.ConcatenateContext context)
string left = Visit(context.expression(0)).ToString();
string right = Visit(context.expression(1)).ToString();
return left + right;
public override object VisitSubstring([NotNull] TestParser.SubstringContext context)
int n1 = int.Parse(context.NUMBER(0).ToString());
int n2 = int.Parse(context.NUMBER(1).ToString());
return Visit(context.expression()).ToString().Substring(n1, n2);
//shift methods for shifting strings, i. e. left("abc",2) -> result = cab
private static string sh_left(string chain, int amount)
return (chain.Substring(amount) + chain.Substring(0, amount));
private static string sh_right(string chain, int amount)
return chain.Substring(chain.Length - amount)
+ chain.Substring(0, chain.Length - amount);
answered Nov 14 '18 at 16:29
ThatKidMikeThatKidMike
185
185
You can putusing static TestParser;
at the beginning of the file if you want to get rid of all theTestParser.
s in the class names.
– sepp2k
Nov 14 '18 at 16:31
Oh, thanks for that. Surely, I'll do that. Nevertheless, for academic purpose, you've basically covered the whole problem for me.
– ThatKidMike
Nov 14 '18 at 16:34
add a comment |
You can putusing static TestParser;
at the beginning of the file if you want to get rid of all theTestParser.
s in the class names.
– sepp2k
Nov 14 '18 at 16:31
Oh, thanks for that. Surely, I'll do that. Nevertheless, for academic purpose, you've basically covered the whole problem for me.
– ThatKidMike
Nov 14 '18 at 16:34
You can put
using static TestParser;
at the beginning of the file if you want to get rid of all the TestParser.
s in the class names.– sepp2k
Nov 14 '18 at 16:31
You can put
using static TestParser;
at the beginning of the file if you want to get rid of all the TestParser.
s in the class names.– sepp2k
Nov 14 '18 at 16:31
Oh, thanks for that. Surely, I'll do that. Nevertheless, for academic purpose, you've basically covered the whole problem for me.
– ThatKidMike
Nov 14 '18 at 16:34
Oh, thanks for that. Surely, I'll do that. Nevertheless, for academic purpose, you've basically covered the whole problem for me.
– ThatKidMike
Nov 14 '18 at 16:34
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53290014%2fevaluate-tree-with-expression-inside-other-expression%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown