Antlr4 C# пропуск пробелов с помощью (-> пропустить) или WS * не работает - PullRequest
0 голосов
/ 13 января 2020

Я улучшаю инструмент тестирования интеграции, который используется для вызовов RP C. В настоящее время я могу отвечать на вызовы с помощью простых ответов c, например:

<PackageType>Success/Reply</PackageType> <Server>default</Server> <Date>13.01.2020</Date>...

Чтобы не перекомпилировать код для различных тестовых случаев, протоколов и разработчиков, Я хочу интегрировать небольшой DSL. Это должно работать как в следующем примере:

<PackageType>{Random ( "Success","Error")}/Reply</PackageType><Server>{Input ("COMPUTERNAME") }</Server> <Date>{DateTime}</DateTime>
<PackageType>Error/Reply</PackageType><Server>sv_15</Server><Date>01/13/2020 16:45:23</Date>

Для этого я разработал следующую грамматику в Antlr:

grammar GRAM;
expression  : expression expression                     #ExpressionWalk
            | OPENTAG instruction CLOSETAG              #GetInstruction
            | STRING                                    #StringBlock
            ;
instruction : function ( '(' param(',' param)* ')' )?   #InstructionBuilder
            ;           
function    : FUNC;                                     #FunctionCreator
param       : '"' STRING '"'                            #StringParam
            | NEGATOR? INT                              #IntParam
            ; 
STRING      : (~( OPENTAG | CLOSETAG | '"' | ',' ) )+;
INT         : [0-9]+;
NEGATOR     : '-';
OPENTAG     : '{' ;
CLOSETAG    : '}';
FUNC        : 'Random' | 'Input' | 'DateTime' ;   
WS          : [ \r\n\t] -> skip;

Поскольку TestRig не работает с целью CSharp, я используя следующий код для проверки моей грамматики:

public static void Main(string[] args)
{
    string input = "albalbalb{ Random ( \"string1\" , \"string2\")}blablabla ";
    ICharStream stream = CharStreams.fromstring(input);
    ITokenSource lexer = new GRAMLexer(stream);
    ITokenStream tokens = new CommonTokenStream(lexer);
    GRAMParser parser = new GRAMParser(tokens);
    parser.BuildParseTree = true;
    IParseTree tree = parser.expression();
    Console.WriteLine(tree.ToStringTree(parser));
    Console.ReadKey();
}

Когда я запускаю код, я получаю следующую ошибку:

line 1:11 mismatched input ' Random ' expecting FUNC
line 1:19 no viable alternative at input ','

Пробелы вокруг 'Random' говорят мне, что как-то Antlr не может отделить их от лексера FUN C. Это также верно, когда я изменяю (-> skip) вручную помещенные инструкции "WS *" (мне это нужно, если я хочу разрешить пробелы в строках).

Кто-нибудь знает, что я делаю не так?

Заранее спасибо!

Ответы [ 2 ]

1 голос
/ 18 января 2020

Существует ряд проблем с исходной и измененной грамматикой.

(1) Ваша исходная грамматика не анализируется. Альт с меткой был помещен после точки с запятой, которая обозначает конец правила ( "function: FUNC; #FunctionCreator" должно быть "function: FUN C #FunctionCreator;" ).

(2) Если вы распечатаете поток токенов из лексера для вашей исходной грамматики, вы увидите, что входные данные не могут быть проанализированы, поскольку они ожидают токен FUN C, но получают Вместо этого жетон STRING Поток токенов - это «albalbalb» (STRING; match); «{« (OPENTAG; матч); «Случайно (« (STRING; ожидается FUN C).

(3) Хотя ваша измененная грамматика может работать, у вас есть лексер, распознающий каждый отдельный символ как отдельный токен. Большинство анализаторов не делают этого, потому что смысл лексера - группировать символы вместе в «токены», которые затем считываются синтаксическим анализатором. Лексер использует быструю машину для распознавания токенов. С другой стороны, синтаксический анализатор использует более медленную расширенную (или, альтернативно, сетевую) сеть перехода. machine.

(4) Причина, по которой вам нужно правило CHAR в вашей измененной грамматике, заключается в том, что строковые литералы в правилах синтаксического анализатора определяют большинство токенов, распознаваемых лексером. Правила staticstring и paramstring определяют только '{', '}', '”'. '(', ')'. Другими строковыми литералами в синтаксической части грамматики являются '-', '', 'Variable', et c. Распознавание завершается неудачно с «Ошибка распознавания токена строки 1: 0 при: 'a'" (ошибка лексера), потому что у вас нет строкового литерала 'a' в синтаксическом анализаторе. Правило CHAR в грамматике позволяет лексеру распознавать все символы.

(5) Я бы порекомендовал проверить некоторые грамматики в https://github.com/antlr/grammars-v4. Чтобы писать хорошие грамматики, каждый должен их изучать.

0 голосов
/ 13 января 2020

Я не мог заставить его работать, поэтому я начал снова с самого начала. Для записи кажется, что вам нужно добавить правило Lexer «CHAR», которое содержит все возможные символы, чтобы синтаксический анализатор «знал» их, прежде чем он сможет их удалить.

Вот что я делаю вместо этого:

grammar GRAM;
/* Each expression can be multiple functions or strings
 * Strings can contain every char except those specified under string
*/
start           : expression EOF                                            #StartRule
                ;
expression      : expression expression                                     #ExpressionWalk
                | '{' function '}'                                          #GetFunction
                | staticstring                                              #GetStaticString
                ;
/* Function always have ONE instruction and zero or more parameters
*/
function        : ' '* instruction ' '* parameters? ' '*                    #FunctionBuilder
                ;
/* Instructions are specified here so its easier to work with them in code
*/  
instruction     : 'Variable'                                                #GetVariableInstruction
                | 'DateTime'                                                #GetDateTimeInstruction
                | 'RandomInt'                                               #GetRandomIntInstruction
                | 'Random'                                                  #GetRandomInstruction
                | 'CountInt'                                                #GetCountIntInstruction
                ;
/* If parameters exist, it is always one or more parameters
*/
parameters      : '(' ' '* parameter ' '* ( ',' ' '* parameter  ' '* )+  ')' #WalkParameters
                ;
/* Two types of parameters are allowed: Strings, which must be incapsulated in ",
 * and Integers, which don't have "
 */
parameter       : '"' paramstring '"'                                       #StringParam
                | '-'? DIGIT+                                               #IntParam
                ;
/* List of disallowed characters for parameters and static text
*/
staticstring    : (~( '{' | '}' ))+                                         #OuterExclude
                ;
paramstring     : (~('"' | '(' | ')' ))+                                    #InnerExclude
                ;

/* Lexer rules. CHAR is needed to allow characters in parser system
*/                      
DIGIT           : [0-9]                 ;
CHAR            : ('\u0000'..'\uFFFF')  ;

Я подожду еще один день, прежде чем принять это сам, поэтому, если у кого-то есть лучшее, еще есть возможность опубликовать его

...