Evaluate tree with expression inside other expression










1















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.










share|improve this question


























    1















    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.










    share|improve this question
























      1












      1








      1








      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.










      share|improve this question














      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






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 13 '18 at 21:48









      ThatKidMikeThatKidMike

      185




      185






















          2 Answers
          2






          active

          oldest

          votes


















          1














          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.






          share|improve this answer























          • 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


















          1














          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);







          share|improve this answer























          • 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










          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
          );



          );













          draft saved

          draft discarded


















          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









          1














          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.






          share|improve this answer























          • 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















          1














          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.






          share|improve this answer























          • 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













          1












          1








          1







          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.






          share|improve this answer













          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.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          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

















          • 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













          1














          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);







          share|improve this answer























          • 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















          1














          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);







          share|improve this answer























          • 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













          1












          1








          1







          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);







          share|improve this answer













          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);








          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 14 '18 at 16:29









          ThatKidMikeThatKidMike

          185




          185












          • 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

















          • 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
















          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

















          draft saved

          draft discarded
















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Top Tejano songwriter Luis Silva dead of heart attack at 64

          ReactJS Fetched API data displays live - need Data displayed static

          政党