ANTLR 嵌套函数

Dan*_*use 3 java antlr3 antlr4

ANTLR 适合这个项目吗?

\n\n

我正在寻找处理和转换用户输入的字符串,其中可能包含自定义函数。例如,用户可能会在字符串中写入类似 $CAPITALIZE('word') 的内容,而我想使用 StringUtils 在后台执行\n实际转换。

\n\n

我想用户有时会编写嵌套函数,例如:

\n\n

$RIGHT_PAD($RIGHT($CAPITALIZE('a123456789'),6),3,'0')

\n\n

其中预期输出为字符串值“A12345000”。

\n\n

我尝试使用正则表达式将函数分开,但一旦嵌套,就不那么容易了。我想我可以尝试编写自己的解析器,在进行研究时我发现了一篇建议使用 ANTLR 的文章。

\n\n

ANTLR 适合这样做吗?如果是这样,是否有任何类似的示例可供我查看?或者有人可以给我一个例子,说明如何在 ANTLR 中编写此代码,以便我可以拥有可以单独处理和以嵌套方式处理的自定义函数。

\n\n

功能:

\n\n
    \n
  • $CAPITALIZE(字符串 str)
  • \n
  • $INDEX_OF(字符串序列,字符串搜索序列)
  • \n
  • $LEFT(字符串 str, int len)
  • \n
  • $LEFT_PAD(字符串 str, int 大小,字符 padChar)
  • \n
  • $LOWERCASE(字符串 str)
  • \n
  • $RIGHT(字符串 str, int len)
  • \n
  • $RIGHT_PAD(字符串 str, int 大小, 字符 padChar)
  • \n
  • $STRIP(字符串 str)
  • \n
  • $STRIP_ACCENTS(字符串输入)
  • \n
  • $SUBSTRING(字符串 str, int 开始)
  • \n
  • $SUBSTRING(字符串 str, int 开始, int 结束)
  • \n
  • $TRIM(字符串 str)
  • \n
  • $TRUNCATE(字符串 str, int maxWidth)
  • \n
  • $UPPERCASE(字符串 str)
  • \n
\n\n

基本示例:

\n\n
    \n
  • $CAPITALIZE('单词') \xe2\x86\x92 '单词'
  • \n
  • $INDEX_OF('单词', 'r') \xe2\x86\x92 2
  • \n
  • $LEFT('0123456789',6) \xe2\x86\x92 '012345'
  • \n
  • $LEFT_PAD('0123456789',3, '0') \xe2\x86\x92 '0000123456789'
  • \n
  • $LOWERCASE('Word') \xe2\x86\x92 '单词'
  • \n
  • $RIGHT('0123456789',6) \xe2\x86\x92 '456789'
  • \n
  • $RIGHT_PAD('0123456789',3, '0') \xe2\x86\x92 '0123456789000'
  • \n
  • $STRIP('单词') \xe2\x86\x92 '单词'
  • \n
  • $STRIP_ACCENTS('w\xc3\xb3rd') \xe2\x86\x92 '单词'
  • \n
  • $SUBSTRING('word', 1) \xe2\x86\x92 'ord'
  • \n
  • $SUBSTRING('word', 0, 2) \xe2\x86\x92 'wor'
  • \n
  • $TRIM('单词') \xe2\x86\x92 '单词'
  • \n
  • $TRUNCATE('更多单词', 3) \xe2\x86\x92 '更多'
  • \n
  • $UPPERCASE('单词') \xe2\x86\x92 '单词'
  • \n
\n\n

嵌套示例

\n\n
    \n
  • $LEFT_PAD($LEFT('123456789',6),3,'0') \xe2\x86\x92 '000123456'
  • \n
  • $RIGHT_PAD($RIGHT($CAPITALIZE('a123456789'),6),3,'0') \xe2\x86\x92 'A12345000'
  • \n
\n\n

实际示例: \n我所说的实际示例的意思是,这就是我期望的字符串值的样子。你会注意到有一些变量的写法类似于${var}。在将字符串传递到 ANTLR 之前,这些变量将使用 Apache Commons StringSubstitutor 替换为实际字符串值(如果结果证明我应该使用它)

\n\n

用户写入的初始字符串\n\\HomeDir\\Students\\$RIGHT(${graduation.year},2)\\$LEFT_PAD($LEFT(${state.id},6),3,'0' )

\n\n

StringSubstitutor 处理后的字符串\n\\HomeDir\\Students\\$RIGHT('2020',2)\\$LEFT_PAD($LEFT('123456789',6),3,'0')

\n\n

由 ANTLR 处理后的字符串 (以及我的最终输出)

\n\n

\\HomeDir\\Students\\20\\000123456

\n\n

ANTLR 看起来像我应该用于这个项目的东西,还是其他更适合的东西?

\n

Bar*_*ers 5

是的,ANTLR将是一个不错的选择。请记住,ANTLR 仅为您进行解析,并为您提供遍历生成的解析树的机制。您必须编写代码来计算表达式。

在您的情况下,当您的词法分析器偶然发现 a 时,需要'$'通过将词法状态推为“处于功能模式”来触发。当它看到 a 时')',应该从词法堆栈中弹出一个这样的“函数模式”。

在 ANTLR wiki 上阅读有关词法模式/堆栈的所有内容: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;
Run Code Online (Sandbox Code Playgroud)

文件: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
 ;
Run Code Online (Sandbox Code Playgroud)

使用 IntelliJ 的 ANTLR4 插件,您可以轻松测试parse解析器中的方法并向其提供以下输入:foo $RIGHT_PAD($RIGHT($CAPITALIZE('a123456789'), 6), 3, '0') bar,这将生成解析树的以下图像:

在此输入图像描述