ANTLR 嵌套函数
ANTLR Nested Functions
ANTLR 适合这个项目吗?
我正在寻找处理和转换用户输入的字符串,其中可能包含自定义函数。例如,用户可能会在字符串中写入类似 $CAPITALIZE('word') 的内容,而我想执行
使用 StringUtils 在后台进行实际转换。
我想用户有时会编写如下嵌套函数:
$RIGHT_PAD($RIGHT($CAPITALIZE('a123456789'),6),3,'0')
其中预期输出将是字符串值 'A12345000'。
我尝试使用正则表达式将函数分开,但一旦嵌套,就没那么容易了。我想我可能会尝试编写自己的解析器,在进行研究时我发现了一篇建议改用 ANTLR 的文章。
这是 ANTLR 适合的东西吗?如果是这样,是否有任何类似的例子可供我查看?或者有人会给我一个例子,说明我如何在 ANTLR 中写出这个,这样我就可以拥有可以单独处理和以嵌套方式处理的自定义函数。
函数:
- $CAPITALIZE(字符串 str)
- $INDEX_OF(字符串序列,字符串搜索序列)
- $LEFT(字符串 str, int len)
- $LEFT_PAD(String str, int size,char padChar)
- $LOWERCASE(字符串 str)
- $RIGHT(字符串 str, int len)
- $RIGHT_PAD(String str, int size, char padChar)
- $STRIP(字符串 str)
- $STRIP_ACCENTS(字符串输入)
- $SUBSTRING(String str, int start)
- $SUBSTRING(String str, int start, int end)
- $TRIM(字符串 str)
- $TRUNCATE(字符串 str, int maxWidth)
- $UPPERCASE(字符串 str)
基本示例:
- $CAPITALIZE('word') → 'Word'
- $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'
- $STRIP_ACCENTS('wórd') → 'word'
- $SUBSTRING('word', 1) → 'ord'
- $SUBSTRING('word', 0, 2) → 'wor'
- $TRIM('word ') → 'word'
- $TRUNCATE('more words', 3) → 'more'
- $大写('word') → 'WORD'
嵌套示例
- $LEFT_PAD($LEFT('123456789',6),3,'0') → '000123456'
- $RIGHT_PAD($RIGHT($CAPITALIZE('a123456789'),6),3,'0') → 'A12345000'
实例:
我所说的实际示例的意思是,这就是我期望的字符串值的样子。你会注意到有些变量写成 ${var}。在将字符串传递给 ANTLR 之前,这些变量将使用 Apache Commons StringSubstitutor 替换为实际字符串值(如果结果证明我应该使用它)
用户写入的初始字符串
\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\Students\20\000123456
我应该将 ANTLR 用于此项目,还是其他更适合?
是的,ANTLR 将是一个不错的选择。请记住,ANTLR 只为您进行解析,并为您提供一种遍历生成的解析树的机制。您将必须编写代码来评估表达式。
在你的例子中,你的词法分析器需要在它通过将词法状态推送为 "in-a-function-mode" 偶然发现 '$'
时被触发。当它看到 ')'
时,一个这样的 "in-a-function-mode" 应该从词法堆栈中弹出。
在 ANTLR wiki 上阅读所有关于词法 modes/stack 的内容: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
;
使用 IntelliJ 的 ANTLR4 插件,您可以轻松地从解析器测试 parse
方法并为其提供以下输入:foo $RIGHT_PAD($RIGHT($CAPITALIZE('a123456789'), 6), 3, '0') bar
,这将生成以下解析树图像:
ANTLR 适合这个项目吗?
我正在寻找处理和转换用户输入的字符串,其中可能包含自定义函数。例如,用户可能会在字符串中写入类似 $CAPITALIZE('word') 的内容,而我想执行 使用 StringUtils 在后台进行实际转换。
我想用户有时会编写如下嵌套函数:
$RIGHT_PAD($RIGHT($CAPITALIZE('a123456789'),6),3,'0')
其中预期输出将是字符串值 'A12345000'。
我尝试使用正则表达式将函数分开,但一旦嵌套,就没那么容易了。我想我可能会尝试编写自己的解析器,在进行研究时我发现了一篇建议改用 ANTLR 的文章。
这是 ANTLR 适合的东西吗?如果是这样,是否有任何类似的例子可供我查看?或者有人会给我一个例子,说明我如何在 ANTLR 中写出这个,这样我就可以拥有可以单独处理和以嵌套方式处理的自定义函数。
函数:
- $CAPITALIZE(字符串 str)
- $INDEX_OF(字符串序列,字符串搜索序列)
- $LEFT(字符串 str, int len)
- $LEFT_PAD(String str, int size,char padChar)
- $LOWERCASE(字符串 str)
- $RIGHT(字符串 str, int len)
- $RIGHT_PAD(String str, int size, char padChar)
- $STRIP(字符串 str)
- $STRIP_ACCENTS(字符串输入)
- $SUBSTRING(String str, int start)
- $SUBSTRING(String str, int start, int end)
- $TRIM(字符串 str)
- $TRUNCATE(字符串 str, int maxWidth)
- $UPPERCASE(字符串 str)
基本示例:
- $CAPITALIZE('word') → 'Word'
- $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'
- $STRIP_ACCENTS('wórd') → 'word'
- $SUBSTRING('word', 1) → 'ord'
- $SUBSTRING('word', 0, 2) → 'wor'
- $TRIM('word ') → 'word'
- $TRUNCATE('more words', 3) → 'more'
- $大写('word') → 'WORD'
嵌套示例
- $LEFT_PAD($LEFT('123456789',6),3,'0') → '000123456'
- $RIGHT_PAD($RIGHT($CAPITALIZE('a123456789'),6),3,'0') → 'A12345000'
实例: 我所说的实际示例的意思是,这就是我期望的字符串值的样子。你会注意到有些变量写成 ${var}。在将字符串传递给 ANTLR 之前,这些变量将使用 Apache Commons StringSubstitutor 替换为实际字符串值(如果结果证明我应该使用它)
用户写入的初始字符串 \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\Students\20\000123456
我应该将 ANTLR 用于此项目,还是其他更适合?
是的,ANTLR 将是一个不错的选择。请记住,ANTLR 只为您进行解析,并为您提供一种遍历生成的解析树的机制。您将必须编写代码来评估表达式。
在你的例子中,你的词法分析器需要在它通过将词法状态推送为 "in-a-function-mode" 偶然发现 '$'
时被触发。当它看到 ')'
时,一个这样的 "in-a-function-mode" 应该从词法堆栈中弹出。
在 ANTLR wiki 上阅读所有关于词法 modes/stack 的内容: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
;
使用 IntelliJ 的 ANTLR4 插件,您可以轻松地从解析器测试 parse
方法并为其提供以下输入:foo $RIGHT_PAD($RIGHT($CAPITALIZE('a123456789'), 6), 3, '0') bar
,这将生成以下解析树图像: