Грамматика LaTeX, как со смешанными пробелами UTF и командами - PullRequest
1 голос
/ 27 марта 2019

Я попытался реализовать грамматику, подобную LaTeX, которая могла бы позволить мне разобрать предложение такого типа:

\title{Un pré é"'§è" \VAR state \draw( 200\if{expression kjlkjé} ) bis tèr }

Как видите, \ title {} может содержать несколько видов элементов:

  • строка в utf8 без кавычек и с пробелами, которые я хотел бы хранить в одном жетоне

  • вызов переменной как: \ variable_name

  • некоторые \ ключевые слова следуют в скобках или другие с фигурными скобками: например, \ draw (utf8 \ var \ if {} ...) или \ if {idem}.

Эти элементы могут быть вложенными.

Я получаю вдохновение от анализатора XML, представленного в книге 4 ANTLR, и пытаюсь использовать режим. Я сталкиваюсь с проблемой, касающейся распознавания закрывающих скобок закрывающих скобок. Я также сталкиваюсь с проблемой с некоторыми пробелами, например с тем, который следует за \ variable_name (я получаю: посторонний ввод '').

Вот мой грамматический код лексера:

 lexer grammar OEFLexer;
    // Default mode rules (the SEA)
    SEA_WS      :   (' '|'\t'|'\r'? '\n')+ ;
    TITLE : '\\title';
    OB    : '{';
    OP    : '(';
    BSLASH  : '\\'                  -> mode(CALLREFERENCE) ;      
    TEXT  : ~[\\({]+;                         // clump all text together 
    // ----------------- Everything Callreference ---------------------
    mode CALLREFERENCE;

    CLOSECALLVAR : ' '          -> mode(DEFAULT_MODE) ; // back to SEA mode 
    CB           : '}'          -> mode(DEFAULT_MODE) ; // back to SEA mode 
    CP           : ')'          -> mode(DEFAULT_MODE) ; // back to SEA mode 

    DRAW    :   'draw' OP;
    IF      :   'if' OB;
    ID      :   [a-zA-Z]+ ;       // match/send ID in tag to parser

Вот моя грамматика парсера

parser grammar OEFParser;
options { tokenVocab=OEFLexer; }

document: TITLE OB ( callreference | string )* CB;

string  : TEXT;
var     : ID;
commandDraw : DRAW ( callreference | string )* CP ;
commandIf   : IF ( callreference | string )* CB ;

callreference : BSLASH ID | BSLASH commandDraw CP | BSLASH commandIf CP;

Когда я пытался проанализировать код заголовка, упомянутый в начале, я получаю:

line 1:25 extraneous input ' ' expecting {'\', TEXT, '}'}
line 1:37 extraneous input ' ' expecting {'\', TEXT, ')'}
line 1:45 mismatched input 'expression' expecting {'\', TEXT, '}'}
line 1:75 extraneous input '<EOF>' expecting {'\', TEXT, ')'}

С этим сгенерированным деревом, сгенерированным Grun

enter image description here

Спасибо за вашу помощь, чтобы помочь мне решить эту проблему. Chris

1 Ответ

1 голос
/ 27 марта 2019

Проблема в пробеле после expression:

\title{Un pré é"'§è" \VAR state \draw( 200\if{expression kjlkjé} ) bis tèr }
                                                        ^
                                                        ^
                                                        ^

, который заставляет режим вернуться к DEFAULT_MODE:

CLOSECALLVAR : ' ' -> mode(DEFAULT_MODE) ;

То, что вам не нужно, потому что вы (очевидно) все еще находитесь в контексте CALLREFERENCE.

Одним из способов справиться с этим является использование директив -> pushMode(...) и -> popMode, вызывающих создание стека из режимов CALLREFERENCE. Всякий раз, когда вы наталкиваетесь на \... ( и \... {, вы помещаете новый CALLREFERENCE в этот стек, а затем выталкиваете его, когда видите ) или }.

Демонстрация быстрой грамматики лексера:

lexer grammar OEFLexer;

TITLE   : '\\title' S? OB -> pushMode(CALLREFERENCE);

fragment OB : '{';
fragment OP : '(';
fragment S : [ \t\r\n]+;

mode CALLREFERENCE;

  CB       : '}'          -> popMode;
  CP       : ')'          -> popMode;

  DRAW     : '\\draw' S? OP -> pushMode(CALLREFERENCE);
  IF       : '\\if' S? OB   -> pushMode(CALLREFERENCE);

  BSLASH   : '\\';
  ID       : [a-zA-Z]+;
  CR_OTHER : .;

и грамматика синтаксического анализатора:

parser grammar OEFParser;

options { tokenVocab=OEFLexer; }

document
 : TITLE ( callreference | string )* CB EOF
 ;

string
 : CR_OTHER+
 | ID
 ;

commandDraw
 : DRAW ( callreference | string )* CP
 ;

commandIf
 : IF ( callreference | string )* CB
 ;

callreference
 : BSLASH ID
 | commandDraw
 | commandIf
 ;

Анализ вашего примера ввода приведет к следующему дереву разбора:

enter image description here

...