为什么写?
由于工作需要,正在学Antlr,自然作者写的那本经典《The Definitive ANTLR 4 Reference》是必读的。边读边写代码是程序员的常规看书姿势,我也不例外。不过习惯IDE了(Antlr每次都生成一堆堆代码,IDE下看还是不错的),来回命令行和IDE切换很是费事,所以打起了命令行下交互式菜单的主意。可惜google了一圈,没现成的。好吧,那就撸起袖子做一个吧。
项目在github上,有兴趣的可以看下,或者直接一起撸😊
准备实现的功能
- 基本信息的显示
- 参数名称(别名)
- 参数描述
- 回调(关键,不然谈啥交互)
- 输入校验
- 退出前整体有效性校验
- 流程管理 (看下面的流程图)
- 按需刷新菜单
- 注解(@annotation)的方式配置(简化使用是王道)(TODO)
- 命名规范(Naming Convention):这个比注解更是无侵入(只要是Bean规范的即可)(TODO)
- 2018/08/10 Update: field+setter的简单实现(暂不支持继承、非String参数)
- 格式化(太丑的估计没人用吧)(TODO)
- Debug模式(对开发必须友好)(TODO)
流程
Sample
Bean based builder
2018-08-10更新
运行BeanBuilderSample.java,按照下面的输入,可以得到相应的输出:
Wrong arguments:
-null: [Invalid_value]
Only 'Csv' is acceptable!
1) -,--grammar []
2) -,--startRule [token]
--
R): refresh menu; X): exit
Your input [# value]: 1 Csv
Your input [# value]: R
1) -,--grammar [Csv]
2) -,--startRule [token]
--
R): refresh menu; X): exit
Your input [# value]: x
Back to main, let's continue.
BeanBuilderSample.java的源代码:
public class BeanBuilderSample {
private String grammar;
private String startRule;
public static void main(final String... args) {
final BeanBuilderSample sample = new BeanBuilderSample();
// mock arguments
final String[] mockedArgs = {"--grammar", "Invalid_value", "--startRule", "token"};
new BeanMenuBuilder().bean(sample).build(mockedArgs).render();
System.out.println("\nBack to main, let's continue.");
}
public void setGrammar(final String value) {
if (!"Csv".equals(value))
throw new IllegalArgumentException("Only 'Csv' is acceptable!");
grammar = value;
}
public void setStartRule(final String value) {
startRule = value;
}
}
全手工配置
运行BasicBuilderExample.java,按照下面的输入,可以得到输出:
Wrong arguments:
-g: [Invalid_value]
Only 'Csv' is acceptable!
1) -g,--grammar []: Grammar name
2) -,--startRule [token]: Start rule of this parser
--
其他同上
BasicBuilderExample.java的源代码:
class BasicBuilderSample {
private String grammar;
private String startRule;
public static void main(final String... args) {
final BasicBuilderSample sample = new BasicBuilderSample();
// mock arguments
final String[] mockedArgs = {"-g", "Invalid_value", "--startRule", "token"};
final BasicMenuBuilder builder = new BasicMenuBuilder();
builder
.item(new MenuItemBuilder()
.argName("g")
.longArgName("grammar")
.value(() -> sample.grammar)
.description("It should be same with file name of your .g4")
.inputChecker(sample::setGrammar)
.build()
)
.item(new MenuItemBuilder()
.argName("r").longArgName("startRule")
.value(() -> sample.startRule)
.inputChecker(sample::setStartRule)
.build()
)
.build(mockedArgs)
.render();
System.out.println("\nBack to main, let's continue.");
}
void setGrammar(final String value) {
if (!"Csv".equals(value))
throw new IllegalArgumentException("Only 'Csv' is acceptable!");
grammar = value;
}
void setStartRule(final String value) {
startRule = value;
}
}
链式调用
2018-08-11 更新
运行ChainedBuilderExample.java,可得到同BasicBuilderExample.java相同的输出。具体调用代码如下:
public class ChainedBuilderSample {
private String grammar;
private String startRule;
public void setGrammar(final String value) {
if (!"Csv".equals(value))
throw new IllegalArgumentException("Only 'Csv' is acceptable!");
grammar = value;
}
public void setStartRule(final String value) {
startRule = value;
}
public static void main(final String... args) {
final ChainedBuilderSample sample = new ChainedBuilderSample();
// mock arguments
final String[] mockedArgs = {"--grammar", "Invalid_value", "--startRule", "token"};
new BeanMenuBuilder()
.bean(sample) // 从Bean中提取配置
.with( // 链式调用补上Bean中缺失的信息,譬如描述信息
new BasicMenuBuilder()
.item("g", "grammar", "Grammar name")
.item(null, "startRule", "Start rule of this parser")
)
.build(mockedArgs)
.render();
System.out.println("\nBack to main, let's continue.");
}
}
项目在github上,有兴趣的可以看下,或者直接一起撸😊