用其他表达式内的表达式求值树

时间:2018-11-13 21:48:14

标签: c# antlr antlr4

对于初学者,如果我对此事不够准确,我要表示歉意。

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

我要实现的是评估树(或者换句话说,能够实际执行简单的操作)。语法的整体思想是对字符串执行简单的操作。问题本身是我不知道如何实际遍历树并评估其表达式。我在这里举个例子会容易得多:

“ upper(left(” text“),2)” <-此操作是一个嵌套操作,应执行以下操作:1.将“ text”向左移动2(实际上的作用并不重要)。 2.返回移位后的值“向上”。 3.应该使用大写表达式来收集left()产生的所有内容并执行其操作,在这种情况下,将“文本”转换为大写。

整个“嵌套表达式”都引起了问题。我已经实现了自己的访问者类,并且我有很多方法可以覆盖,例如表达式,子字符串,右移等,这些都是从语法中获取的,但是我不知道在面对的情况下如何使用它们,什么方法使用,以便我实际上可以使用语法。

2 个答案:

答案 0 :(得分:1)

首先,您拥有许多几乎完全相同的名称,例如shift_leftshiftLeft,这只是在请求引入错误,因此,我强烈建议您重构语法要么:

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;

或者:

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
           ;

我会选择后者,因为它会生成更简单的树。

要访问嵌套表达式,只需在子表达式上递归调用Visit,然后根据需要合并结果。因此,涵盖您示例的访问者可能看起来像这样:

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

其他表达式类型的访问方法将遵循相同的逻辑。

答案 1 :(得分:1)

感谢@ sepp2k-我将整个解决方案放在访问者的C#中:

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

    }