ANTLR嵌套函数

时间:2018-08-21 19:27:49

标签: java antlr4 antlr3

ANTLR是否适合该项目?

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

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

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

期望的输出将是字符串值“ A12345000”。

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

这是ANTLR合适的吗?如果是这样,是否有类似的例子可供我参考?还是有人愿意给我一个例子,说明如何在ANTLR中写出这样的例子,这样我就可以拥有既可以单独处理又可以嵌套处理的自定义函数。

功能

  • $ CAPITALIZE(String str)
  • $ INDEX_OF(字符串序列,字符串searchSeq)
  • $ LEFT(String str,int len)
  • $ LEFT_PAD(字符串str,int size,char padChar)
  • $ LOWERCASE(字符串str)
  • $ RIGHT(String str,int len)
  • $ RIGHT_PAD(字符串str,int size,char padChar)
  • $ STRIP(String str)
  • $ STRIP_ACCENTS(字符串输入)
  • $ SUBSTRING(String str,int start)
  • $ SUBSTRING(字符串str,int start,int end)
  • $ TRIM(String str)
  • $ TRUNCATE(String str,int maxWidth)
  • $ UPPERCASE(String 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')→'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}这样写。在将字符串传递给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看起来像是我应该在该项目中使用的东西,还是其他更适合的东西?

1 个答案:

答案 0 :(得分:2)

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

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

在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;

文件: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,这将生成以下解析树图像:

enter image description here