Я пишу плагин Custom Language для IntelliJ.
Вот упрощенный пример языка. Обратите внимание, что структура является рекурсивной:
Я успешно реализовал файлы FLEX и BNF, но не знаю, как добавить восстановление после ошибок. Я читал о RecoverWhile и pin в HOWTO Grammar-Kit, но я не уверен, как применить их к моему сценарию.
Я называю коричневые элементы выше ("aaa", "ccc" и т. д.) "элементы".
Я называю желтые ("bbb", "ddd", ...) "свойствами".
Каждый элемент имеет имя элемента (например, "aaa"), одно свойство (например, "bbb") и может содержать другие элементы (например, "aaa" содержит "ccc ", "ээээ" и "гг").
На данный момент плагин плохо себя ведет, когда элемент имеет неверный формат. Например:
В этом примере я хотел бы, чтобы синтаксический анализатор «понимал», что «ccc» — это имя элемента с отсутствующим свойством (например, путем обнаружения новой строки перед закрывающей скобкой).
Я не хочу, чтобы сломанный элемент "ccc" влиял на синтаксический анализ "eeee" (но я хочу, чтобы PSI-дерево имело элементы "ccc", присутствующие в тексте, в в этом случае - его имя).
Вот FLEX и BNF, которые я использую:
ФЛЕКС:
CRLF= \n|\r|\r\n
WS=[\ \t\f]
WORD=[a-zA-Z0-9_#\-]+
%state EOF
%%
<YYINITIAL> {WORD} { yybegin(YYINITIAL); return MyLangTypes.TYPE_FLEX_WORD; }
<YYINITIAL> \[ { yybegin(YYINITIAL); return MyLangTypes.TYPE_FLEX_OPEN_SQUARE_BRACKET; }
<YYINITIAL> \] { yybegin(YYINITIAL); return MyLangTypes.TYPE_FLEX_CLOSE_SQUARE_BRACKET; }
<YYINITIAL> \{ { yybegin(YYINITIAL); return MyLangTypes.TYPE_FLEX_OPEN_CURLY_BRACKET; }
<YYINITIAL> \} { yybegin(YYINITIAL); return MyLangTypes.TYPE_FLEX_CLOSE_CURLY_BRACKET; }
({CRLF}|{WS})+ { return TokenType.WHITE_SPACE; }
{WS}+ { return TokenType.WHITE_SPACE; }
. { return TokenType.BAD_CHARACTER; }
БНФ:
myLangFile ::= (item|COMMENT|CRLF)
item ::=
itemName
(TYPE_FLEX_OPEN_SQUARE_BRACKET itemProperty? TYPE_FLEX_CLOSE_SQUARE_BRACKET?)?
itemBody?
itemName ::= TYPE_FLEX_WORD
itemProperty ::= TYPE_FLEX_WORD
itemBody ::= TYPE_FLEX_OPEN_CURLY_BRACKET item* TYPE_FLEX_CLOSE_CURLY_BRACKET