[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"site-settings":3,"$f1baxfjkteg3j2":14,"$f127x0f0kusbgi":31,"$f179lxun9782vl":57,"$f2hf8zv88r59zg":60,"render-antlr4-01-getting-started":73},{"code":4,"message":5,"data":6},0,"ok",{"author_role":7,"icp":8,"skill_one_title":7,"about_hero_image":7,"skill_two_title":7,"author_status":7,"skill_one_desc":7,"site_description":9,"site_tagline":7,"author_years":10,"author_city":11,"home_hero_image":7,"tagline_line_two":7,"site_name":12,"closing_quote":7,"author_bio_two":7,"tagline_line_one":7,"weekly_thought":7,"author_tagline":7,"site_url":13,"site_footer":7,"author_bio":7,"skill_two_desc":7,"toolkit":7,"closing_quote_date":7,"og_default_image":7},"","陕ICP备2021009682号-1","花开空白 - 个人博客","10","西安","花开空白","https:\u002F\u002Firule.cn",{"code":4,"message":5,"data":15},{"id":16,"slug":17,"title":18,"summary":19,"coverUrl":20,"categoryId":21,"status":22,"topFlag":4,"viewCount":22,"likeCount":4,"wordCount":23,"readingMinutes":24,"publishedAt":25,"contentMd":26,"contentHtml":27,"tags":28,"metaTitle":20,"metaDescription":20,"keywords":20,"canonicalUrl":20,"robots":30,"ogImage":20,"faq":20,"aiSummary":20},"2101581915440463873","antlr4-01-getting-started","Antlr4系列①：Antlr4初体验","环境搭建、最简单的四则运算语法、Visitor模式入门",null,"1946000000000000001",1,4129,11,"2026-09-20T15:59:09","Antlr4作为一个强大的语法分析器，被广泛应用于各领域定制领域特定语言（DSL），支持包括Java、Python、C++、JavaScript在内的多种语言。本系列文章没有太多概念性的描述语言，旨在通简单代码，由浅入深，讲解关键部分代码，让读者快速掌握antlr4，理解Antlr4的原理。\n\n## 一 、环境搭建\n\n### 1. idea插件安装\n\n在idea的插件商店中搜索antlr4进行安装，或通过\n[插件下载地址](https:\u002F\u002Fplugins.jetbrains.com\u002Fplugin\u002F7358-antlr-v4\u002Fversions)选择对应的版本进行离线安装，安装完成后重启idea，出现以下窗口代表安装成功：\n\n![插件窗口](http:\u002F\u002Fqiniu.irule.cn\u002Fantlr4\u002Fantlr4_plugin_menu.png)\n\n### 2. 引入依赖包\n   \n   使用antlr4需要引入运行时的依赖包，新建工程后添加以下gradle引用：\n   ```\n   implementation 'org.antlr:antlr4-runtime:4.9'\n   ```\n## 二、简单的应用\n\n### 1. 建立语法文件\n   \n   首先新建一个语法规则文件，antlr4的语法规则文件是以.g4结尾的，新建一个文件指定以.g4结尾即可。在文件中填写以下内容。\n   ```\n    grammar RuleSet; \u002F\u002F程序名称和.g4名称一致即可\n\n    calcu: NUMBER MUL NUMBER                          # mul\n        | NUMBER DIV NUMBER                          # div\n        | NUMBER ADD NUMBER                          # add\n        | NUMBER SUB NUMBER                          # sub\n    ;\n\n    WS : [ \\t\\n\\r]+ -> skip ; \u002F\u002F ->skip表示antlr4在分析语言的文本时，符合这个规则的词法将被无视\n    ADD : '+' ;\n    SUB : '-' ;\n    MUL : '*' ;\n    DIV : '\u002F' ;\n    NUMBER : '-'? [0-9]+('.'([0-9]+)?)? ;        \u002F\u002F 数字正则\n   ```\n   在antlr4中的规则文件一般由两部分组成，小写字母定义的叫做语法，大写字母定义的叫词法，词法一般以固定的字符或正则进行描述，语法中可以使用词法，通常会用一次词法的组合来描述一个语法。\n   \n   使用【小写字母名称+英文冒号+规则】的方式来定义一个语法。用`|`来分割多个规则，每个规则后面可以用【#+空格+名称】的方式来为改规则取一个别名，该别名后续会生成一个方法名。别名是可选项，但是需要注意的是如果一个语法下有多个规则时，一旦给其中一个规则定义了别名，剩下的都必须定义，否则生成代码文件时会报错。\n\n### 2. 测试语法的正确性\n   在idea将光标放到要测试的语法上，右键选择 `Test Rule xxx` 即可打开语法树界面，在左侧输入文本，右侧会自动将语法解析为语法树，如下：\n\n   ![JMM](http:\u002F\u002Fqiniu.irule.cn\u002Fantlr4\u002Ftest_rule_success.png)\n\n   语法测试成功时右侧会将左侧的文本解析成一颗语法树，上图中两个1分别识别出来是NUMBER词法，+识别为ADD词法，整个语法识别为 calcu的add（我们取的别名）规则\n\n   ![JMM](http:\u002F\u002Fqiniu.irule.cn\u002Fantlr4\u002Ftest_rule_error.png)\n\n   语法测试失败时会有错误提示，提示我们+号的右边缺少了NUMBER，因为我们输入的是1+a，a不满足NUMBER词法的正则。\n\n### 3. 生成代码文件\n   在.g4文件中选择右键菜单中的`Generate ANTLR Recognizer`来生成代码文件，当然在生成之前也可以通过右键菜单中的`Configure ANTLR...`来对生成代码的输出目录等可配置项进行配置，自行研究即可。\n   生成好以后将生成的代码复制到工程中，默认的生成路径是.g4文件所在目录的gen目录下，根据所需调整好项目结构，至此.g4文件的使命就已经完成了。最终生成的文件如下：\n\n   ![JMM](http:\u002F\u002Fqiniu.irule.cn\u002Fantlr4\u002Fgenerate_files.png)\n\n   上图中红框中的文件就是自动生成的代码文件，重点需要关注的就是【.g4文件名+BaseListener】和【.g4文件名+BaseVisitor】结尾的两个java文件，这两个文件我们后续需要选择一个继承以后来实现业务逻辑。\n\n### 4. 实现业务逻辑\n   antlr4提供了两种方式来实现我们的业务逻辑，访问器和监听器，也就是上面所说的需要关注的两个类，两种实现方式选其一即可。我们这边选择实现访问器来编写我们的业务逻辑代码。\n   新建一个java类继承上面生成的XXXBaseVisitor接口，重写加减乘除方法，并实现真正的加减乘除功能，如下：\n\n   ```java\n    public class MyRuleSetVisitor extends RuleSetBaseVisitor\u003CDouble> {\n\n        @Override\n        public Double visitMul(RuleSetParser.MulContext ctx) {\n            double left = Double.parseDouble(ctx.NUMBER(0).getText());\n            double right = Double.parseDouble(ctx.NUMBER(1).getText());\n            return left * right;\n        }\n\n        @Override\n        public Double visitDiv(RuleSetParser.DivContext ctx) {\n            double left = Double.parseDouble(ctx.NUMBER(0).getText());\n            double right = Double.parseDouble(ctx.NUMBER(1).getText());\n            return left \u002F right;\n        }\n\n        @Override\n        public Double visitAdd(RuleSetParser.AddContext ctx) {\n            double left = Double.parseDouble(ctx.NUMBER(0).getText());\n            double right = Double.parseDouble(ctx.NUMBER(1).getText());\n            return left + right;\n        }\n\n        @Override\n        public Double visitSub(RuleSetParser.SubContext ctx) {\n            double left = Double.parseDouble(ctx.NUMBER(0).getText());\n            double right = Double.parseDouble(ctx.NUMBER(1).getText());\n            return left - right;\n        }\n\n   ```\n   > 关于泛型：\n   上述代码中`RuleSetBaseVisitor`的泛型就代表每次访问一个语法树节点的返回类型，示例是做四则运算，因此指定了返回类型为Double。\n\n   >关于取值：因为在制定规则的时候每个运算，因此0就代表左边的，1就代表右边的，从左到右按顺序排列，因为NUMBER是正则匹配的数值类型，所以在此处就不需要再进行运行时校验就可以直接转为double型而不用担心运行时报错。\n\n   接着编写一个测试类，如下：\n   ```java\n    public class Test {\n        public static void main(String[] args) {\n            String expression = \"3 - 1\";\n            calcute(expression);\n            String expression2 = \"3.5 + 1\";\n            calcute(expression2);\n            String expression3 = \"3 * 5\";\n            calcute(expression3);\n            String expression4 = \"10 \u002F 5\";\n            calcute(expression4);\n            String expression5 = \"10 \u002F a\";\n            calcute(expression5);\n        }\n\n        private static void calcute(String expression) {\n            System.out.println(\"执行:\" + expression);\n            MyRuleSetVisitor visitor = new MyRuleSetVisitor();\n            RuleSetLexer lexer = new RuleSetLexer(CharStreams.fromString(expression));\n            RuleSetParser parser = new RuleSetParser(new CommonTokenStream(lexer));\n            RuleSetParser.CalcuContext calcu = parser.calcu();\n            Double result = visitor.visit(calcu);\n            System.out.println(\"结果:\" + result);\n        }\n    }\n   ```\n   运行结果如下：\n\n   ```\n   > Task :Test.main() FAILED\n执行:3 - 1\n结果:2.0\n执行:3.5 + 1\n结果:4.5\n执行:3 * 5\n结果:15.0\n执行:10 \u002F 5\n结果:2.0\n执行:10 \u002F a\n2 actionable tasks: 2 executed\nline 1:5 token recognition error at: 'a'\nline 1:6 missing NUMBER at '\u003CEOF>'\nException in thread \"main\" java.lang.NumberFormatException: For input string: \"\u003Cmissing NUMBER>\"\n\tat sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)\n\tat sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)\n\tat java.lang.Double.parseDouble(Double.java:538)\n\tat cn.irule.MyRuleSetVisitor.visitDiv(MyRuleSetVisitor.java:21)\n\tat cn.irule.MyRuleSetVisitor.visitDiv(MyRuleSetVisitor.java:9)\n\tat cn.irule.RuleSetParser$DivContext.accept(RuleSetParser.java:124)\n\tat org.antlr.v4.runtime.tree.AbstractParseTreeVisitor.visit(AbstractParseTreeVisitor.java:18)\n\tat cn.irule.Test.calcute(Test.java:31)\n\tat cn.irule.Test.main(Test.java:22)\n   ```\n\n   至此，简单的四则运算就已经完成。但是可以看到在执行`10 \u002F a`时报错了，这是因为在执行前未调用antlr4对输入进行语法校验，后续将继续讲解如何校验以及更为复杂的功能实现。\n\n   \n\n\n","\u003Cp>Antlr4作为一个强大的语法分析器，被广泛应用于各领域定制领域特定语言（DSL），支持包括Java、Python、C++、JavaScript在内的多种语言。本系列文章没有太多概念性的描述语言，旨在通简单代码，由浅入深，讲解关键部分代码，让读者快速掌握antlr4，理解Antlr4的原理。\u003C\u002Fp>\n\u003Ch2>一 、环境搭建\u003C\u002Fh2>\n\u003Ch3>1. idea插件安装\u003C\u002Fh3>\n\u003Cp>在idea的插件商店中搜索antlr4进行安装，或通过 \u003Ca href=\"https:\u002F\u002Fplugins.jetbrains.com\u002Fplugin\u002F7358-antlr-v4\u002Fversions\" rel=\"nofollow\">插件下载地址\u003C\u002Fa>选择对应的版本进行离线安装，安装完成后重启idea，出现以下窗口代表安装成功：\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"http:\u002F\u002Fqiniu.irule.cn\u002Fantlr4\u002Fantlr4_plugin_menu.png\" alt=\"插件窗口\">\u003C\u002Fp>\n\u003Ch3>2. 引入依赖包\u003C\u002Fh3>\n\u003Cp>使用antlr4需要引入运行时的依赖包，新建工程后添加以下gradle引用：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>implementation 'org.antlr:antlr4-runtime:4.9'\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>二、简单的应用\u003C\u002Fh2>\n\u003Ch3>1. 建立语法文件\u003C\u002Fh3>\n\u003Cp>首先新建一个语法规则文件，antlr4的语法规则文件是以.g4结尾的，新建一个文件指定以.g4结尾即可。在文件中填写以下内容。\u003C\u002Fp>\n\u003Cpre>\u003Ccode> grammar RuleSet; \u002F\u002F程序名称和.g4名称一致即可\n\n calcu: NUMBER MUL NUMBER                          # mul\n     | NUMBER DIV NUMBER                          # div\n     | NUMBER ADD NUMBER                          # add\n     | NUMBER SUB NUMBER                          # sub\n ;\n\n WS : [ \\t\\n\\r]+ -&gt; skip ; \u002F\u002F -&gt;skip表示antlr4在分析语言的文本时，符合这个规则的词法将被无视\n ADD : '+' ;\n SUB : '-' ;\n MUL : '*' ;\n DIV : '\u002F' ;\n NUMBER : '-'? [0-9]+('.'([0-9]+)?)? ;        \u002F\u002F 数字正则\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>在antlr4中的规则文件一般由两部分组成，小写字母定义的叫做语法，大写字母定义的叫词法，词法一般以固定的字符或正则进行描述，语法中可以使用词法，通常会用一次词法的组合来描述一个语法。\u003C\u002Fp>\n\u003Cp>使用【小写字母名称+英文冒号+规则】的方式来定义一个语法。用\u003Ccode>|\u003C\u002Fcode>来分割多个规则，每个规则后面可以用【#+空格+名称】的方式来为改规则取一个别名，该别名后续会生成一个方法名。别名是可选项，但是需要注意的是如果一个语法下有多个规则时，一旦给其中一个规则定义了别名，剩下的都必须定义，否则生成代码文件时会报错。\u003C\u002Fp>\n\u003Ch3>2. 测试语法的正确性\u003C\u002Fh3>\n\u003Cp>在idea将光标放到要测试的语法上，右键选择 \u003Ccode>Test Rule xxx\u003C\u002Fcode> 即可打开语法树界面，在左侧输入文本，右侧会自动将语法解析为语法树，如下：\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"http:\u002F\u002Fqiniu.irule.cn\u002Fantlr4\u002Ftest_rule_success.png\" alt=\"JMM\">\u003C\u002Fp>\n\u003Cp>语法测试成功时右侧会将左侧的文本解析成一颗语法树，上图中两个1分别识别出来是NUMBER词法，+识别为ADD词法，整个语法识别为 calcu的add（我们取的别名）规则\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"http:\u002F\u002Fqiniu.irule.cn\u002Fantlr4\u002Ftest_rule_error.png\" alt=\"JMM\">\u003C\u002Fp>\n\u003Cp>语法测试失败时会有错误提示，提示我们+号的右边缺少了NUMBER，因为我们输入的是1+a，a不满足NUMBER词法的正则。\u003C\u002Fp>\n\u003Ch3>3. 生成代码文件\u003C\u002Fh3>\n\u003Cp>在.g4文件中选择右键菜单中的\u003Ccode>Generate ANTLR Recognizer\u003C\u002Fcode>来生成代码文件，当然在生成之前也可以通过右键菜单中的\u003Ccode>Configure ANTLR...\u003C\u002Fcode>来对生成代码的输出目录等可配置项进行配置，自行研究即可。 生成好以后将生成的代码复制到工程中，默认的生成路径是.g4文件所在目录的gen目录下，根据所需调整好项目结构，至此.g4文件的使命就已经完成了。最终生成的文件如下：\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"http:\u002F\u002Fqiniu.irule.cn\u002Fantlr4\u002Fgenerate_files.png\" alt=\"JMM\">\u003C\u002Fp>\n\u003Cp>上图中红框中的文件就是自动生成的代码文件，重点需要关注的就是【.g4文件名+BaseListener】和【.g4文件名+BaseVisitor】结尾的两个java文件，这两个文件我们后续需要选择一个继承以后来实现业务逻辑。\u003C\u002Fp>\n\u003Ch3>4. 实现业务逻辑\u003C\u002Fh3>\n\u003Cp>antlr4提供了两种方式来实现我们的业务逻辑，访问器和监听器，也就是上面所说的需要关注的两个类，两种实现方式选其一即可。我们这边选择实现访问器来编写我们的业务逻辑代码。 新建一个java类继承上面生成的XXXBaseVisitor接口，重写加减乘除方法，并实现真正的加减乘除功能，如下：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\"> public class MyRuleSetVisitor extends RuleSetBaseVisitor&lt;Double&gt; {\n\n     @Override\n     public Double visitMul(RuleSetParser.MulContext ctx) {\n         double left = Double.parseDouble(ctx.NUMBER(0).getText());\n         double right = Double.parseDouble(ctx.NUMBER(1).getText());\n         return left * right;\n     }\n\n     @Override\n     public Double visitDiv(RuleSetParser.DivContext ctx) {\n         double left = Double.parseDouble(ctx.NUMBER(0).getText());\n         double right = Double.parseDouble(ctx.NUMBER(1).getText());\n         return left \u002F right;\n     }\n\n     @Override\n     public Double visitAdd(RuleSetParser.AddContext ctx) {\n         double left = Double.parseDouble(ctx.NUMBER(0).getText());\n         double right = Double.parseDouble(ctx.NUMBER(1).getText());\n         return left + right;\n     }\n\n     @Override\n     public Double visitSub(RuleSetParser.SubContext ctx) {\n         double left = Double.parseDouble(ctx.NUMBER(0).getText());\n         double right = Double.parseDouble(ctx.NUMBER(1).getText());\n         return left - right;\n     }\n\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cblockquote>\n \u003Cp>关于泛型： 上述代码中\u003Ccode>RuleSetBaseVisitor\u003C\u002Fcode>的泛型就代表每次访问一个语法树节点的返回类型，示例是做四则运算，因此指定了返回类型为Double。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cblockquote>\n \u003Cp>关于取值：因为在制定规则的时候每个运算，因此0就代表左边的，1就代表右边的，从左到右按顺序排列，因为NUMBER是正则匹配的数值类型，所以在此处就不需要再进行运行时校验就可以直接转为double型而不用担心运行时报错。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>接着编写一个测试类，如下：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\"> public class Test {\n     public static void main(String[] args) {\n         String expression = \"3 - 1\";\n         calcute(expression);\n         String expression2 = \"3.5 + 1\";\n         calcute(expression2);\n         String expression3 = \"3 * 5\";\n         calcute(expression3);\n         String expression4 = \"10 \u002F 5\";\n         calcute(expression4);\n         String expression5 = \"10 \u002F a\";\n         calcute(expression5);\n     }\n\n     private static void calcute(String expression) {\n         System.out.println(\"执行:\" + expression);\n         MyRuleSetVisitor visitor = new MyRuleSetVisitor();\n         RuleSetLexer lexer = new RuleSetLexer(CharStreams.fromString(expression));\n         RuleSetParser parser = new RuleSetParser(new CommonTokenStream(lexer));\n         RuleSetParser.CalcuContext calcu = parser.calcu();\n         Double result = visitor.visit(calcu);\n         System.out.println(\"结果:\" + result);\n     }\n }\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>运行结果如下：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>&gt; Task :Test.main() FAILED\n执行:3 - 1\n结果:2.0\n执行:3.5 + 1\n结果:4.5\n执行:3 * 5\n结果:15.0\n执行:10 \u002F 5\n结果:2.0\n执行:10 \u002F a\n2 actionable tasks: 2 executed\nline 1:5 token recognition error at: 'a'\nline 1:6 missing NUMBER at '&lt;EOF&gt;'\nException in thread \"main\" java.lang.NumberFormatException: For input string: \"&lt;missing NUMBER&gt;\"\n\tat sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)\n\tat sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)\n\tat java.lang.Double.parseDouble(Double.java:538)\n\tat cn.irule.MyRuleSetVisitor.visitDiv(MyRuleSetVisitor.java:21)\n\tat cn.irule.MyRuleSetVisitor.visitDiv(MyRuleSetVisitor.java:9)\n\tat cn.irule.RuleSetParser$DivContext.accept(RuleSetParser.java:124)\n\tat org.antlr.v4.runtime.tree.AbstractParseTreeVisitor.visit(AbstractParseTreeVisitor.java:18)\n\tat cn.irule.Test.calcute(Test.java:31)\n\tat cn.irule.Test.main(Test.java:22)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>至此，简单的四则运算就已经完成。但是可以看到在执行\u003Ccode>10 \u002F a\u003C\u002Fcode>时报错了，这是因为在执行前未调用antlr4对输入进行语法校验，后续将继续讲解如何校验以及更为复杂的功能实现。\u003C\u002Fp>",[29],"Antlr4","index,follow",{"code":4,"message":5,"data":32},[33,41,49],{"id":34,"slug":35,"title":36,"summary":37,"coverUrl":20,"categoryId":21,"status":22,"topFlag":4,"viewCount":4,"likeCount":4,"wordCount":38,"readingMinutes":39,"publishedAt":40},"2101584400070656001","antlr4-02-syntax-check-and-priority","Antlr4系列②：语法检查和多规则的优先级","BaseErrorListener做词法\u002F语法校验、calcu递归支持连续混合运算、用opt=别名解决优先级问题",5762,15,"2026-09-20T16:09:01",{"id":42,"slug":43,"title":44,"summary":45,"coverUrl":20,"categoryId":21,"status":22,"topFlag":4,"viewCount":4,"likeCount":4,"wordCount":46,"readingMinutes":47,"publishedAt":48},"2101585710597718018","antlr4-03-variables-and-print","Antlr4系列③：支持变量定义和打印函数","number\u002Fstring\u002Fboolean变量定义、print函数、VisitorResult统一返回类型",8172,21,"2026-09-20T16:14:14",{"id":50,"slug":51,"title":52,"summary":53,"coverUrl":20,"categoryId":21,"status":22,"topFlag":4,"viewCount":4,"likeCount":4,"wordCount":54,"readingMinutes":55,"publishedAt":56},"2101605987801022465","antlr4-04-if-else","Antlr4系列④：支持分支语句","比较运算符、逻辑运算符、if\u002Felse if\u002Felse、代码块block",6668,17,"2026-09-20T17:34:58",{"code":4,"message":5,"data":58},{"prev":20,"next":59},{"id":34,"slug":35,"title":36,"summary":37,"coverUrl":20,"categoryId":21,"status":22,"topFlag":4,"viewCount":4,"likeCount":4,"wordCount":38,"readingMinutes":39,"publishedAt":40},{"code":4,"message":5,"data":61},[62,67],{"id":21,"name":63,"slug":64,"description":65,"sortOrder":4,"articleCount":66},"技术","tech","编程与技术笔记","5",{"id":68,"name":69,"slug":70,"description":71,"sortOrder":22,"articleCount":72},"1946000000000000002","随笔","essay","生活与思考","0",{"html":74,"toc":75},"\u003Cp>Antlr4作为一个强大的语法分析器，被广泛应用于各领域定制领域特定语言（DSL），支持包括Java、Python、C++、JavaScript在内的多种语言。本系列文章没有太多概念性的描述语言，旨在通简单代码，由浅入深，讲解关键部分代码，让读者快速掌握antlr4，理解Antlr4的原理。\u003C\u002Fp>\n\u003Ch2 id=\"toc-1\">一 、环境搭建\u003C\u002Fh2>\n\u003Ch3 id=\"toc-2\">1. idea插件安装\u003C\u002Fh3>\n\u003Cp>在idea的插件商店中搜索antlr4进行安装，或通过 \u003Ca href=\"https:\u002F\u002Fplugins.jetbrains.com\u002Fplugin\u002F7358-antlr-v4\u002Fversions\" rel=\"nofollow\">插件下载地址\u003C\u002Fa>选择对应的版本进行离线安装，安装完成后重启idea，出现以下窗口代表安装成功：\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"http:\u002F\u002Fqiniu.irule.cn\u002Fantlr4\u002Fantlr4_plugin_menu.png\" alt=\"插件窗口\" loading=\"lazy\" decoding=\"async\">\u003C\u002Fp>\n\u003Ch3 id=\"toc-3\">2. 引入依赖包\u003C\u002Fh3>\n\u003Cp>使用antlr4需要引入运行时的依赖包，新建工程后添加以下gradle引用：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan>implementation 'org.antlr:antlr4-runtime:4.9'\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"toc-4\">二、简单的应用\u003C\u002Fh2>\n\u003Ch3 id=\"toc-5\">1. 建立语法文件\u003C\u002Fh3>\n\u003Cp>首先新建一个语法规则文件，antlr4的语法规则文件是以.g4结尾的，新建一个文件指定以.g4结尾即可。在文件中填写以下内容。\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan> grammar RuleSet; \u002F\u002F程序名称和.g4名称一致即可\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan> calcu: NUMBER MUL NUMBER                          # mul\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>     | NUMBER DIV NUMBER                          # div\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>     | NUMBER ADD NUMBER                          # add\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>     | NUMBER SUB NUMBER                          # sub\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan> ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan> WS : [ \\t\\n\\r]+ -> skip ; \u002F\u002F ->skip表示antlr4在分析语言的文本时，符合这个规则的词法将被无视\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan> ADD : '+' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan> SUB : '-' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan> MUL : '*' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan> DIV : '\u002F' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan> NUMBER : '-'? [0-9]+('.'([0-9]+)?)? ;        \u002F\u002F 数字正则\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>在antlr4中的规则文件一般由两部分组成，小写字母定义的叫做语法，大写字母定义的叫词法，词法一般以固定的字符或正则进行描述，语法中可以使用词法，通常会用一次词法的组合来描述一个语法。\u003C\u002Fp>\n\u003Cp>使用【小写字母名称+英文冒号+规则】的方式来定义一个语法。用\u003Ccode>|\u003C\u002Fcode>来分割多个规则，每个规则后面可以用【#+空格+名称】的方式来为改规则取一个别名，该别名后续会生成一个方法名。别名是可选项，但是需要注意的是如果一个语法下有多个规则时，一旦给其中一个规则定义了别名，剩下的都必须定义，否则生成代码文件时会报错。\u003C\u002Fp>\n\u003Ch3 id=\"toc-6\">2. 测试语法的正确性\u003C\u002Fh3>\n\u003Cp>在idea将光标放到要测试的语法上，右键选择 \u003Ccode>Test Rule xxx\u003C\u002Fcode> 即可打开语法树界面，在左侧输入文本，右侧会自动将语法解析为语法树，如下：\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"http:\u002F\u002Fqiniu.irule.cn\u002Fantlr4\u002Ftest_rule_success.png\" alt=\"JMM\" loading=\"lazy\" decoding=\"async\">\u003C\u002Fp>\n\u003Cp>语法测试成功时右侧会将左侧的文本解析成一颗语法树，上图中两个1分别识别出来是NUMBER词法，+识别为ADD词法，整个语法识别为 calcu的add（我们取的别名）规则\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"http:\u002F\u002Fqiniu.irule.cn\u002Fantlr4\u002Ftest_rule_error.png\" alt=\"JMM\" loading=\"lazy\" decoding=\"async\">\u003C\u002Fp>\n\u003Cp>语法测试失败时会有错误提示，提示我们+号的右边缺少了NUMBER，因为我们输入的是1+a，a不满足NUMBER词法的正则。\u003C\u002Fp>\n\u003Ch3 id=\"toc-7\">3. 生成代码文件\u003C\u002Fh3>\n\u003Cp>在.g4文件中选择右键菜单中的\u003Ccode>Generate ANTLR Recognizer\u003C\u002Fcode>来生成代码文件，当然在生成之前也可以通过右键菜单中的\u003Ccode>Configure ANTLR...\u003C\u002Fcode>来对生成代码的输出目录等可配置项进行配置，自行研究即可。 生成好以后将生成的代码复制到工程中，默认的生成路径是.g4文件所在目录的gen目录下，根据所需调整好项目结构，至此.g4文件的使命就已经完成了。最终生成的文件如下：\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"http:\u002F\u002Fqiniu.irule.cn\u002Fantlr4\u002Fgenerate_files.png\" alt=\"JMM\" loading=\"lazy\" decoding=\"async\">\u003C\u002Fp>\n\u003Cp>上图中红框中的文件就是自动生成的代码文件，重点需要关注的就是【.g4文件名+BaseListener】和【.g4文件名+BaseVisitor】结尾的两个java文件，这两个文件我们后续需要选择一个继承以后来实现业务逻辑。\u003C\u002Fp>\n\u003Ch3 id=\"toc-8\">4. 实现业务逻辑\u003C\u002Fh3>\n\u003Cp>antlr4提供了两种方式来实现我们的业务逻辑，访问器和监听器，也就是上面所说的需要关注的两个类，两种实现方式选其一即可。我们这边选择实现访问器来编写我们的业务逻辑代码。 新建一个java类继承上面生成的XXXBaseVisitor接口，重写加减乘除方法，并实现真正的加减乘除功能，如下：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> class\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> MyRuleSetVisitor\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> extends\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> RuleSetBaseVisitor\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">     @\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Override\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">     public\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Double \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitMul\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.MulContext \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">ctx\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">         double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Double.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">parseDouble\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">NUMBER\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">).\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">         double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> right \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Double.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">parseDouble\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">NUMBER\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">1\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">).\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">         return\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">*\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> right;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">     }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">     @\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Override\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">     public\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Double \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitDiv\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.DivContext \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">ctx\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">         double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Double.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">parseDouble\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">NUMBER\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">).\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">         double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> right \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Double.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">parseDouble\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">NUMBER\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">1\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">).\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">         return\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> right;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">     }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">     @\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Override\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">     public\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Double \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitAdd\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.AddContext \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">ctx\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">         double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Double.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">parseDouble\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">NUMBER\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">).\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">         double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> right \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Double.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">parseDouble\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">NUMBER\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">1\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">).\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">         return\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">+\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> right;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">     }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">     @\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Override\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">     public\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Double \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitSub\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.SubContext \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">ctx\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">         double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Double.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">parseDouble\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">NUMBER\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">).\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">         double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> right \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Double.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">parseDouble\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">NUMBER\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">1\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">).\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">         return\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">-\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> right;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">     }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cblockquote>\n \u003Cp>关于泛型： 上述代码中\u003Ccode>RuleSetBaseVisitor\u003C\u002Fcode>的泛型就代表每次访问一个语法树节点的返回类型，示例是做四则运算，因此指定了返回类型为Double。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cblockquote>\n \u003Cp>关于取值：因为在制定规则的时候每个运算，因此0就代表左边的，1就代表右边的，从左到右按顺序排列，因为NUMBER是正则匹配的数值类型，所以在此处就不需要再进行运行时校验就可以直接转为double型而不用担心运行时报错。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>接着编写一个测试类，如下：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> class\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Test\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">     public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> static\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> main\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">String\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">[] \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">args\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">         String expression \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"3 - 1\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">         calcute\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(expression);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">         String expression2 \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"3.5 + 1\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">         calcute\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(expression2);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">         String expression3 \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"3 * 5\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">         calcute\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(expression3);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">         String expression4 \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"10 \u002F 5\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">         calcute\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(expression4);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">         String expression5 \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"10 \u002F a\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">         calcute\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(expression5);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">     }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">     private\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> static\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> calcute\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(String \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">expression\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">         System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"执行:\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> expression);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">         MyRuleSetVisitor visitor \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> MyRuleSetVisitor\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">         RuleSetLexer lexer \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> RuleSetLexer\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(CharStreams.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">fromString\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(expression));\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">         RuleSetParser parser \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> RuleSetParser\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> CommonTokenStream\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(lexer));\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">         RuleSetParser.CalcuContext calcu \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> parser.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">calcu\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">         Double result \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> visitor.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(calcu);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">         System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"结果:\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> result);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">     }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>运行结果如下：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan>> Task :Test.main() FAILED\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>执行:3 - 1\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>结果:2.0\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>执行:3.5 + 1\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>结果:4.5\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>执行:3 * 5\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>结果:15.0\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>执行:10 \u002F 5\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>结果:2.0\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>执行:10 \u002F a\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>2 actionable tasks: 2 executed\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>line 1:5 token recognition error at: 'a'\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>line 1:6 missing NUMBER at '&#x3C;EOF>'\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Exception in thread \"main\" java.lang.NumberFormatException: For input string: \"&#x3C;missing NUMBER>\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\tat sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\tat sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\tat java.lang.Double.parseDouble(Double.java:538)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\tat cn.irule.MyRuleSetVisitor.visitDiv(MyRuleSetVisitor.java:21)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\tat cn.irule.MyRuleSetVisitor.visitDiv(MyRuleSetVisitor.java:9)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\tat cn.irule.RuleSetParser$DivContext.accept(RuleSetParser.java:124)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\tat org.antlr.v4.runtime.tree.AbstractParseTreeVisitor.visit(AbstractParseTreeVisitor.java:18)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\tat cn.irule.Test.calcute(Test.java:31)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\tat cn.irule.Test.main(Test.java:22)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>至此，简单的四则运算就已经完成。但是可以看到在执行\u003Ccode>10 \u002F a\u003C\u002Fcode>时报错了，这是因为在执行前未调用antlr4对输入进行语法校验，后续将继续讲解如何校验以及更为复杂的功能实现。\u003C\u002Fp>",[76,80,84,87,90,93,96,99],{"id":77,"text":78,"level":79},"toc-1","一 、环境搭建",2,{"id":81,"text":82,"level":83},"toc-2","1. idea插件安装",3,{"id":85,"text":86,"level":83},"toc-3","2. 引入依赖包",{"id":88,"text":89,"level":79},"toc-4","二、简单的应用",{"id":91,"text":92,"level":83},"toc-5","1. 建立语法文件",{"id":94,"text":95,"level":83},"toc-6","2. 测试语法的正确性",{"id":97,"text":98,"level":83},"toc-7","3. 生成代码文件",{"id":100,"text":101,"level":83},"toc-8","4. 实现业务逻辑"]