Оцените дерево с выражением внутри другого выражения - PullRequest
0 голосов
/ 14 ноября 2018

Для начала я хотел бы извиниться, если я не совсем точен в этом вопросе.

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. сдвигать "текст"на 2 слева (не имеет значения, что он делает на самом деле).2. Вернуть смещенное значение «вверх».3. Предполагается, что верхнее выражение собирает все, что произведено функцией left (), и выполняет свою работу, в этом случае переносит «text» в верхний регистр.</p>

Это целое «вложенное выражение» вызывает проблему.Я реализовал свой собственный класс посетителя, и у меня есть куча методов для переопределения, например, выражение, подстрока, shiftright и т. Д. - все они взяты из грамматики, но я понятия не имею, как их использовать в случае, с которыми я сталкиваюсь, какие методыиспользовать, чтобы я мог на самом деле использовать грамматику.

Ответы [ 2 ]

0 голосов
/ 14 ноября 2018

Благодаря @ 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);

    }
0 голосов
/ 14 ноября 2018

Прежде всего, тот факт, что у вас есть много почти идентичных имен, таких как shift_left против shiftLeft, просто требует внесения ошибок, поэтому я настоятельно рекомендую вам изменить свою грамматику на:

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

Методы посещения для других типов выражений будут следовать той же логике.

...