Вложенные функции ANTLR

2

Действительно ли ANTLR подходит для этого проекта?

Я ищу для обработки и преобразования строки, введенной пользователем, которая может включать пользовательские функции. Например, пользователь может написать что-то вроде $ CAPITALIZE ("слово") в строке, и я хочу выполнить фактическое преобразование в фоновом режиме с помощью StringUtils.

Я бы предположил, что пользователи иногда будут писать вложенные функции, такие как:

$ RIGHT_PAD ($ ПРАВЫЙ ($ прописными буквами ( 'a123456789'), 6), 3, '0')

Где ожидаемый результат будет строковым значением "A12345000".

Я попытался использовать регулярное выражение для разделения функций отдельно, но после вложенности это было не так просто. Я решил, что могу попробовать написать свой собственный парсер, и во время исследования я наткнулся на статью, которая предложила использовать ANTLR.

Является ли это чем-то ANTLR правильным? Если да, есть ли подобные примеры, которые уже доступны для просмотра? Или кто-то будет достаточно любезен, чтобы дать мне пример того, как я могу записать это в ANTLR, чтобы у меня были обе пользовательские функции, которые можно обрабатывать индивидуально и вложенным образом.

Функции:

  • $ CAPITALIZE (String str)
  • $ INDEX_OF (String seq, String searchSeq)
  • $ LEFT (String str, int len)
  • $ LEFT_PAD (String str, int size, char padChar)
  • $ LOWERCASE (String str)
  • $ RIGHT (String str, int len)
  • $ RIGHT_PAD (String str, int size, char padChar)
  • $ STRIP (String str)
  • $ STRIP_ACCENTS (ввод строки)
  • $ SUBSTRING (String str, int start)
  • $ SUBSTRING (String str, int start, int end)
  • $ TRIM (String str)
  • $ TRUNCATE (String str, int maxWidth)
  • $ UPPERCASE (String str)

Основные примеры:

  • $ CAPITALIZE ("слово") → "Слово"
  • $ INDEX_OF ('word', 'r') → 2
  • $ LEFT ('0123456789', 6) → '012345'
  • $ LEFT_PAD ('0123456789', 3, '0') → '0000123456789'
  • $ LOWERCASE ('WoRd') → 'word'
  • $ RIGHT ('0123456789', 6) → '456789'
  • $ RIGHT_PAD ('0123456789', 3, '0') → '0123456789000'
  • $ STRIP ('word') → 'word'
  • $ STRIP_ACCENTS ('wórd') → 'word'
  • $ SUBSTRING ('word', 1) → 'ord'
  • $ SUBSTRING ('word', 0, 2) → 'wor'
  • $ TRIM ('word') → 'word'
  • $ TRUNCATE ('more words', 3) → 'more'
  • $ UPPERCASE ('word') → 'WORD'

Вложенные примеры

  • $ LEFT_PAD ($ LEFT ('123456789', 6), 3, '0') → '000123456'
  • $ RIGHT_PAD ($ RIGHT ($ CAPITALIZE ('a123456789'), 6), 3, '0') → 'A12345000'

Фактический пример: я имею в виду, например, что это то, что я ожидаю, что значение строки может выглядеть. Вы заметите, что есть переменные, написанные как $ {var}. Эти переменные будут заменены фактическими строковыми значениями, используя Apache Commons StringSubstitutor перед передачей String в ANTLR (если окажется, что я должен его использовать)

Начальная строка, написанная пользователем\HomeDir\Students\$ RIGHT ($ {graduation.year}, 2)\$ LEFT_PAD ($ LEFT ($ {state.id}, 6), 3, '0')

Строка после обработки StringSubstitutor\HomeDir\Students\$ RIGHT ('2020', 2)\$ LEFT_PAD ($ LEFT ('123456789', 6), 3, '0')

Строка после обработки ANTLR (И мой окончательный результат)

\HomeDir\Студенты\20\000123456

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

Теги:
antlr4
antlr3

1 ответ

2
Лучший ответ

Да, ANTLR будет хорошим выбором. Имейте в виду, что ANTLR выполняет только синтаксический анализ для вас и предоставляет вам механизм для перемещения сгенерированного дерева синтаксического анализа. Вам нужно будет написать код для оценки выражений.

В вашем случае ваш лексер должен быть запущен, когда он наткнется на '$', нажав лексическое состояние как "in-a-function-mode". И когда он видит ')', один такой "in-a-function-mode" должен быть удален из лексического стека.

Читайте все о лексических режимах/стеках в вики-базе ANTLR: https://github.com/antlr/antlr4/blob/master/doc/lexer-rules.md

Вот быстрая демонстрация того, как это может работать для ANTLR4 (ANTLR3 не поддерживает лексические режимы):

файл: TLexer.g4

lexer grammar TLexer;

TEXT
 : ~[$]
 ;

FUNCTION_START
 : '$' -> pushMode(IN_FUNCTION), skip
 ;

mode IN_FUNCTION;
  FUNTION_NESTED : '$' -> pushMode(IN_FUNCTION), skip;
  ID             : [a-zA-Z_]+;
  PAR_OPEN       : '(';
  PAR_CLOSE      : ')' -> popMode;
  NUMBER         : [0-9]+;
  STRING         : '\'' ( ~'\'' | '\'\'' )* '\'';
  COMMA          : ',';
  SPACE          : [ \t\r\n]-> skip;

файла: TParser.g4

parser grammar TParser;

options {
  tokenVocab=TLexer;
}

parse
 : atom* EOF
 ;

atom
 : text
 | function
 ;

text
 : TEXT+
 ;

function
 : ID params
 ;

params
 : PAR_OPEN ( param ( COMMA param )* )? PAR_CLOSE
 ;

param
 : NUMBER
 | STRING
 | function
 ;

С плагином ANTLR4 от IntelliJ вы можете легко протестировать метод parse из анализатора и foo $RIGHT_PAD($RIGHT($CAPITALIZE('a123456789'), 6), 3, '0') bar ему следующий вход: foo $RIGHT_PAD($RIGHT($CAPITALIZE('a123456789'), 6), 3, '0') bar, который будет генерировать следующий образ дерева разбора:

Изображение 174551

  • 0
    Это было очень полезно. Спасибо, Барт!
  • 0
    Добро пожаловать @DanWhitehouse

Ещё вопросы

Сообщество Overcoder
Наверх
Меню