[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"site-settings":3,"$fl81rye45rqkq":14,"$f25gizf43n39r2":31,"$f26dxg3rikki99":56,"$f2hf8zv88r59zg":60,"render-antlr4-04-if-else":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":4,"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},"2101605987801022465","antlr4-04-if-else","Antlr4系列④：支持分支语句","比较运算符、逻辑运算符、if\u002Felse if\u002Felse、代码块block",null,"1946000000000000001",1,6668,17,"2026-09-20T17:34:58","在上一篇文章中我们完善了语法，使其支持变量的定义和打印函数，本篇文章我们将继续完善语法，实现`if \u002F else if \u002F else`分支语句，并为此新增比较运算符和逻辑运算符，让我们的语言真正具备\"判断\"的能力。\n\n## 一、语法设计\n\n1. 比较运算符\n\n    参考主流语言的写法，设计六种比较运算符：`> \u003C >= \u003C= == !=`，运算符两侧支持数值、变量甚至一个完整的四则运算表达式，例如`a + 1 > b * 2`。\n\n2. 逻辑运算符\n\n    设计三种逻辑运算符：`&&`（与）、`||`（或）、`!`（非），用来组合或取反条件表达式，同时支持用小括号显式提升优先级。\n\n3. if语句\n\n    参考java语言if语句的写法设计如下：\n    - 使用`if (条件) { 语句 }`作为基本结构。\n    - 支持任意个`else if (条件) { 语句 }`。\n    - 支持一个可选的`else { 语句 }`，必须放在最后。\n    - `{ }`包裹的语句块内可以是之前已经支持的变量定义、打印函数，也可以是if语句本身，即支持嵌套。\n\n## 二、语法定义\n\n根据以上设计原则对之前的语法定义进行完善，完善后的结果如下：\n```\ngrammar RuleSet; \u002F\u002F程序名称和.g4名称一致即可\n\nprog: main+\n   ;\n\nmain: setArg SEMI?\n    | print SEMI?\n    | ifStmt                                                \u002F\u002F 新增\n    ;\n\n\u002F\u002F 新增：代码块，用花括号包裹0个或多个main，支持if语句内部嵌套if\nblock: '{' main* '}'                                        \u002F\u002F 新增\n     ;\n\n\u002F\u002F 新增：if \u002F else if \u002F else，else if可以出现0次或多次，else可选\nifStmt: IF '(' cond ')' block\n        (ELSE IF '(' cond ')' block)*\n        (ELSE block)?\n      ;                                                     \u002F\u002F 新增\n\n\u002F\u002F 新增：条件表达式，支持比较运算、逻辑运算以及布尔字面量\u002F布尔变量\ncond: NOT cond                                              # notCond\n    | cond AND cond                                          # andCond\n    | cond OR cond                                           # orCond\n    | calcu opt=(GT|LT|GE|LE|EQ|NE) calcu                    # compare\n    | '(' cond ')'                                           # condParens\n    | BOOL                                                    # boolLiteral\n    | ID                                                      # condArgValue\n    ;                                                        \u002F\u002F 新增\n\nsetArg :'number'  id = ID '='  calcu                      #numarg\n       |'string'  id = ID '='  STRING                     #chararg\n       |'boolean' id = ID '='  BOOL                       #boolarg\n       ;\ncalcu: calcu opt=(MUL|DIV) calcu                          # mulAndDiv\n     | calcu opt=(ADD|SUB) calcu                          # addAndSub\n     | '(' calcu ')'                                      # parens\n     | NUMBER                                             # number\n     | ID                                                 # argValue\n     ;\nprint: PRINT '(' value = ID ')'                           # printArg\n     | PRINT '(' value = BOOL ')'                         # printBoolean\n     | PRINT '(' value = STRING ')'                       # printString\n     ;\n\nWS : [ \\t\\n\\r]+ -> skip ; \u002F\u002F ->skip表示antlr4在分析语言的文本时，符合这个规则的词法将被无视\nADD : '+' ;\nSUB : '-' ;\nMUL : '*' ;\nDIV : '\u002F' ;\nNUMBER : '-'? [0-9]+('.'([0-9]+)?)? ;        \u002F\u002F 数字正则\nBOOL : 'true' | 'false' ;\nSTRING : '\"' .*? '\"';\nPRINT : 'print';\nSEMI:  ';';\n\u002F\u002F以下为本次新增词法\nIF : 'if' ;\nELSE : 'else' ;\nAND : '&&' ;\nOR : '||' ;\nNOT : '!' ;\nGT : '>' ;\nLT : '\u003C' ;\nGE : '>=' ;\nLE : '\u003C=' ;\nEQ : '==' ;\nNE : '!=' ;\nfragment ID_LETTER : [a-z] | [A-Z] | '_' ;\nfragment DIGIT : [0-9] ;\nID : ID_LETTER (ID_LETTER | DIGIT)* ;\n```\n为方便和上一篇文章中的语法进行对比，对于本次新增的部分使用注释进行了标注，接下来对本次新增的部分进行解释。\n\n1. 词法部分\n\n    `IF`、`ELSE`等关键字词法都是固定字符串，必须放在`ID`词法**之前**定义，原因和之前`BOOL`、`PRINT`一样：antlr4词法优先匹配写在前面的规则，如果把它们放在`ID`后面，`if`、`else`这些字符串就会被当成普通的标识符（变量名）而不是关键字。\n\n2. 语法部分\n\n    - `block`语法表示一个代码块，内容就是复用已有的`main`语法，用`*`表示可以包含0条或多条语句。这里没有重新设计一套语句集合，而是直接复用`main`，是因为代码块内部理应能写和最外层一样的语句，包括后续文章会讲到的循环语句，复用可以让新语句自动获得\"可以写在代码块里\"的能力，不需要每次新增语句都去修改`block`。\n\n    - `ifStmt`语法中，`(ELSE IF '(' cond ')' block)*`表示`else if`可以出现0次或多次，`(ELSE block)?`表示最后可以有一个可选的`else`。这里有一个关键特点：生成的`IfStmtContext`中，`cond()`方法返回的是一个`List\u003CCondContext>`，个数等于1（if本身）加上`else if`的个数；`block()`方法返回的也是一个`List\u003CBlockContext>`，如果最后跟了单独的`else`，那么`block`的个数会比`cond`多一个。这个\"数量差\"就是后面实现时判断是否命中最终`else`的依据。\n\n    - `cond`语法使用了和`calcu`一样的左递归写法。`NOT`写在最前面表示优先级最高，其次是`AND`，最后是`OR`，这和`calcu`语法中乘除优先于加减是同样的道理——**写在上面的规则优先级更高**。`compare`两侧使用`calcu`，所以比较运算符两侧可以是常量、变量，也可以是一个完整的四则运算表达式。`boolLiteral`和`condArgValue`分别处理直接写`true`\u002F`false`字面量，或者写一个布尔型变量作为条件的情况。\n\n## 三、语法实现\n\n接下来使用idea插件将定义好的.g4文件重新生成java文件覆盖之前的文件。在开始实现`ifStmt`之前，需要先修正上一篇文章遗留的一个小问题：`visitBoolarg`当时保存的是`BOOL`词法的原始文本（字符串\"true\"\u002F\"false\"），仅仅够打印用。而本篇的条件表达式需要拿到真正的`Boolean`类型参与逻辑运算，所以这里顺便修正为保存真正的`Boolean`：\n\n```java\n\u002F**\n * 定义布尔型变量，获取到布尔值后将变量值保存到map中\n * 这里存的是真正的Boolean而不是文本，是为了本篇新增的条件表达式中可以直接取出参与逻辑运算\n *\u002F\n@Override\npublic VisitorResult visitBoolarg(RuleSetParser.BoolargContext ctx) {\n    paramDefine.put(ctx.id.getText(), Boolean.parseBoolean(ctx.BOOL().getText())); \u002F\u002F修改\n    return VisitorResult.nil();\n}\n```\n\n接着在`MyRuleSetVisitor`中新增本篇涉及到的各个方法：\n\n```java\n\u002F\u002F ---------------------------以下函数为本次新增----------------------------\n\n\u002F**\n * 比较运算，比较符两侧都是calcu语法，先算出数值再根据操作符类型比较\n *\u002F\n@Override\npublic VisitorResult visitCompare(RuleSetParser.CompareContext ctx) {\n    double left = visit(ctx.calcu(0)).getNumber();\n    double right = visit(ctx.calcu(1)).getNumber();\n    switch (ctx.opt.getType()) {\n        case RuleSetLexer.GT:\n            return VisitorResult.value(left > right);\n        case RuleSetLexer.LT:\n            return VisitorResult.value(left \u003C right);\n        case RuleSetLexer.GE:\n            return VisitorResult.value(left >= right);\n        case RuleSetLexer.LE:\n            return VisitorResult.value(left \u003C= right);\n        case RuleSetLexer.EQ:\n            return VisitorResult.value(left == right);\n        default:\n            return VisitorResult.value(left != right);\n    }\n}\n\n\u002F**\n * 逻辑非，取反cond的布尔结果\n *\u002F\n@Override\npublic VisitorResult visitNotCond(RuleSetParser.NotCondContext ctx) {\n    boolean value = visit(ctx.cond()).getBool();\n    return VisitorResult.value(!value);\n}\n\n\u002F**\n * 逻辑与，两个cond都为真才为真\n *\u002F\n@Override\npublic VisitorResult visitAndCond(RuleSetParser.AndCondContext ctx) {\n    boolean left = visit(ctx.cond(0)).getBool();\n    boolean right = visit(ctx.cond(1)).getBool();\n    return VisitorResult.value(left && right);\n}\n\n\u002F**\n * 逻辑或，两个cond有一个为真即为真\n *\u002F\n@Override\npublic VisitorResult visitOrCond(RuleSetParser.OrCondContext ctx) {\n    boolean left = visit(ctx.cond(0)).getBool();\n    boolean right = visit(ctx.cond(1)).getBool();\n    return VisitorResult.value(left || right);\n}\n\n\u002F**\n * 带括号的条件表达式，直接访问括号内的cond\n *\u002F\n@Override\npublic VisitorResult visitCondParens(RuleSetParser.CondParensContext ctx) {\n    return visit(ctx.cond());\n}\n\n\u002F**\n * 布尔字面量true\u002Ffalse直接转为Boolean\n *\u002F\n@Override\npublic VisitorResult visitBoolLiteral(RuleSetParser.BoolLiteralContext ctx) {\n    return VisitorResult.value(Boolean.parseBoolean(ctx.getText()));\n}\n\n\u002F**\n * 条件表达式中使用布尔变量，根据变量名称从保存变量值的map中查找\n *\u002F\n@Override\npublic VisitorResult visitCondArgValue(RuleSetParser.CondArgValueContext ctx) {\n    Object value = paramDefine.get(ctx.getText());\n    if (value instanceof Boolean) {\n        return VisitorResult.value(value);\n    }\n    throw new RuntimeException(\"未定义的参数：\" + ctx.getText() + \" 或参数\" + ctx.getText() + \"定义为非布尔型\");\n}\n\n\u002F**\n * if\u002Felse if\u002Felse语句\n * ctx.cond()按顺序依次是if的条件、每个else if的条件\n * ctx.block()按顺序依次是if的代码块、每个else if的代码块，如果最后跟了单独的else，则block数量会比cond多一个\n * 依次判断每个条件，命中哪个条件就执行对应的代码块，如果都不命中且存在多出来的最后一个block，则执行它\n *\u002F\n@Override\npublic VisitorResult visitIfStmt(RuleSetParser.IfStmtContext ctx) {\n    List\u003CRuleSetParser.CondContext> conds = ctx.cond();\n    List\u003CRuleSetParser.BlockContext> blocks = ctx.block();\n    for (int i = 0; i \u003C conds.size(); i++) {\n        if (visit(conds.get(i)).getBool()) {\n            return visit(blocks.get(i));\n        }\n    }\n    if (blocks.size() > conds.size()) {\n        return visit(blocks.get(blocks.size() - 1));\n    }\n    return VisitorResult.nil();\n}\n\n\u002F**\n * 代码块，依次执行块内的每一条main语句\n *\u002F\n@Override\npublic VisitorResult visitBlock(RuleSetParser.BlockContext ctx) {\n    for (RuleSetParser.MainContext mainCtx : ctx.main()) {\n        visit(mainCtx);\n    }\n    return VisitorResult.nil();\n}\n```\n> 关于`main`和`prog`：细心的读者可能会注意到我们并没有重写`visitMain`和`visitProg`方法。这是因为`main`、`prog`两个语法都没有使用`#`定义别名，antlr4会生成默认的`visitChildren`实现，按顺序访问每个子节点，这一点在上一篇文章中就已经是这样，本篇的`ifStmt`作为`main`新增的一种子节点，同样会被默认实现自动访问到，不需要额外处理。\n\n## 四、测试代码与执行结果\n\n接下来编写测试代码，验证`if \u002F else if \u002F else`以及比较运算符、逻辑运算符是否都符合预期：\n\n```java\npublic class Test {\n    public static void main(String[] args) {\n        String expression =\n                \"number a = 10 \\n\" +\n                        \"number b = 3 \\n\" +\n                        \"boolean flag = true \\n\" +\n                        \"if (a > b && flag) { \\n\" +\n                        \"    print(\\\"a大于b且flag为真\\\") \\n\" +\n                        \"} else if (a == b) { \\n\" +\n                        \"    print(\\\"a等于b\\\") \\n\" +\n                        \"} else { \\n\" +\n                        \"    print(\\\"a不大于b\\\") \\n\" +\n                        \"} \\n\" +\n                        \"if (a \u003C b) { \\n\" +\n                        \"    print(\\\"不会执行\\\") \\n\" +\n                        \"} else if (!flag) { \\n\" +\n                        \"    print(\\\"不会执行\\\") \\n\" +\n                        \"} else { \\n\" +\n                        \"    print(\\\"最后的else命中\\\") \\n\" +\n                        \"}\";\n        calcute(expression);\n    }\n    \u002F\u002F calcute方法与上一篇文章保持一致，这里不再重复贴出\n}\n```\n\n执行结果如下：\n\n```\n执行:\nnumber a = 10\nnumber b = 3\nboolean flag = true\nif (a > b && flag) {\n    print(\"a大于b且flag为真\")\n} else if (a == b) {\n    print(\"a等于b\")\n} else {\n    print(\"a不大于b\")\n}\nif (a \u003C b) {\n    print(\"不会执行\")\n} else if (!flag) {\n    print(\"不会执行\")\n} else {\n    print(\"最后的else命中\")\n}\n\n\"a大于b且flag为真\"\n\"最后的else命中\"\n```\n\n第一段`if`中`a > b`为真且`flag`为真，`&&`的结果为真，命中第一个分支，打印\"a大于b且flag为真\"；第二段`if`中`a \u003C b`为假，`!flag`也为假（flag为true），两个条件都不命中，最终执行了`else`，打印\"最后的else命中\"，符合预期。\n\n## 五、遗留的问题\n\n细心的读者可能已经发现了一个隐患：目前所有变量都保存在`MyRuleSetVisitor`里唯一的一个`paramDefine`这个Map中，这意味着在`if`代码块内部定义的变量，出了这个代码块在外面依然能访问到——这在大多数编程语言里是不允许的，正确的行为应该是代码块内定义的变量只在该代码块内可见。这个问题在只有`if`语句时还不明显，当下一篇文章引入循环语句后会更容易暴露出来，我们将在实现完循环语句后专门用一篇文章来解决作用域的问题。下一篇文章我们先继续完善语法，实现`while`循环语句。\n","\u003Cp>在上一篇文章中我们完善了语法，使其支持变量的定义和打印函数，本篇文章我们将继续完善语法，实现\u003Ccode>if \u002F else if \u002F else\u003C\u002Fcode>分支语句，并为此新增比较运算符和逻辑运算符，让我们的语言真正具备\"判断\"的能力。\u003C\u002Fp>\n\u003Ch2>一、语法设计\u003C\u002Fh2>\n\u003Col>\n \u003Cli>\n  \u003Cp>比较运算符\u003C\u002Fp>\n  \u003Cp>参考主流语言的写法，设计六种比较运算符：\u003Ccode>&gt; &lt; &gt;= &lt;= == !=\u003C\u002Fcode>，运算符两侧支持数值、变量甚至一个完整的四则运算表达式，例如\u003Ccode>a + 1 &gt; b * 2\u003C\u002Fcode>。\u003C\u002Fp>\u003C\u002Fli>\n \u003Cli>\n  \u003Cp>逻辑运算符\u003C\u002Fp>\n  \u003Cp>设计三种逻辑运算符：\u003Ccode>&amp;&amp;\u003C\u002Fcode>（与）、\u003Ccode>||\u003C\u002Fcode>（或）、\u003Ccode>!\u003C\u002Fcode>（非），用来组合或取反条件表达式，同时支持用小括号显式提升优先级。\u003C\u002Fp>\u003C\u002Fli>\n \u003Cli>\n  \u003Cp>if语句\u003C\u002Fp>\n  \u003Cp>参考java语言if语句的写法设计如下：\u003C\u002Fp>\n  \u003Cul>\n   \u003Cli>使用\u003Ccode>if (条件) { 语句 }\u003C\u002Fcode>作为基本结构。\u003C\u002Fli>\n   \u003Cli>支持任意个\u003Ccode>else if (条件) { 语句 }\u003C\u002Fcode>。\u003C\u002Fli>\n   \u003Cli>支持一个可选的\u003Ccode>else { 语句 }\u003C\u002Fcode>，必须放在最后。\u003C\u002Fli>\n   \u003Cli>\u003Ccode>{ }\u003C\u002Fcode>包裹的语句块内可以是之前已经支持的变量定义、打印函数，也可以是if语句本身，即支持嵌套。\u003C\u002Fli>\n  \u003C\u002Ful>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2>二、语法定义\u003C\u002Fh2>\n\u003Cp>根据以上设计原则对之前的语法定义进行完善，完善后的结果如下：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>grammar RuleSet; \u002F\u002F程序名称和.g4名称一致即可\n\nprog: main+\n   ;\n\nmain: setArg SEMI?\n    | print SEMI?\n    | ifStmt                                                \u002F\u002F 新增\n    ;\n\n\u002F\u002F 新增：代码块，用花括号包裹0个或多个main，支持if语句内部嵌套if\nblock: '{' main* '}'                                        \u002F\u002F 新增\n     ;\n\n\u002F\u002F 新增：if \u002F else if \u002F else，else if可以出现0次或多次，else可选\nifStmt: IF '(' cond ')' block\n        (ELSE IF '(' cond ')' block)*\n        (ELSE block)?\n      ;                                                     \u002F\u002F 新增\n\n\u002F\u002F 新增：条件表达式，支持比较运算、逻辑运算以及布尔字面量\u002F布尔变量\ncond: NOT cond                                              # notCond\n    | cond AND cond                                          # andCond\n    | cond OR cond                                           # orCond\n    | calcu opt=(GT|LT|GE|LE|EQ|NE) calcu                    # compare\n    | '(' cond ')'                                           # condParens\n    | BOOL                                                    # boolLiteral\n    | ID                                                      # condArgValue\n    ;                                                        \u002F\u002F 新增\n\nsetArg :'number'  id = ID '='  calcu                      #numarg\n       |'string'  id = ID '='  STRING                     #chararg\n       |'boolean' id = ID '='  BOOL                       #boolarg\n       ;\ncalcu: calcu opt=(MUL|DIV) calcu                          # mulAndDiv\n     | calcu opt=(ADD|SUB) calcu                          # addAndSub\n     | '(' calcu ')'                                      # parens\n     | NUMBER                                             # number\n     | ID                                                 # argValue\n     ;\nprint: PRINT '(' value = ID ')'                           # printArg\n     | PRINT '(' value = BOOL ')'                         # printBoolean\n     | PRINT '(' value = STRING ')'                       # printString\n     ;\n\nWS : [ \\t\\n\\r]+ -&gt; skip ; \u002F\u002F -&gt;skip表示antlr4在分析语言的文本时，符合这个规则的词法将被无视\nADD : '+' ;\nSUB : '-' ;\nMUL : '*' ;\nDIV : '\u002F' ;\nNUMBER : '-'? [0-9]+('.'([0-9]+)?)? ;        \u002F\u002F 数字正则\nBOOL : 'true' | 'false' ;\nSTRING : '\"' .*? '\"';\nPRINT : 'print';\nSEMI:  ';';\n\u002F\u002F以下为本次新增词法\nIF : 'if' ;\nELSE : 'else' ;\nAND : '&amp;&amp;' ;\nOR : '||' ;\nNOT : '!' ;\nGT : '&gt;' ;\nLT : '&lt;' ;\nGE : '&gt;=' ;\nLE : '&lt;=' ;\nEQ : '==' ;\nNE : '!=' ;\nfragment ID_LETTER : [a-z] | [A-Z] | '_' ;\nfragment DIGIT : [0-9] ;\nID : ID_LETTER (ID_LETTER | DIGIT)* ;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>为方便和上一篇文章中的语法进行对比，对于本次新增的部分使用注释进行了标注，接下来对本次新增的部分进行解释。\u003C\u002Fp>\n\u003Col>\n \u003Cli>\n  \u003Cp>词法部分\u003C\u002Fp>\n  \u003Cp>\u003Ccode>IF\u003C\u002Fcode>、\u003Ccode>ELSE\u003C\u002Fcode>等关键字词法都是固定字符串，必须放在\u003Ccode>ID\u003C\u002Fcode>词法\u003Cstrong>之前\u003C\u002Fstrong>定义，原因和之前\u003Ccode>BOOL\u003C\u002Fcode>、\u003Ccode>PRINT\u003C\u002Fcode>一样：antlr4词法优先匹配写在前面的规则，如果把它们放在\u003Ccode>ID\u003C\u002Fcode>后面，\u003Ccode>if\u003C\u002Fcode>、\u003Ccode>else\u003C\u002Fcode>这些字符串就会被当成普通的标识符（变量名）而不是关键字。\u003C\u002Fp>\u003C\u002Fli>\n \u003Cli>\n  \u003Cp>语法部分\u003C\u002Fp>\n  \u003Cul>\n   \u003Cli>\n    \u003Cp>\u003Ccode>block\u003C\u002Fcode>语法表示一个代码块，内容就是复用已有的\u003Ccode>main\u003C\u002Fcode>语法，用\u003Ccode>*\u003C\u002Fcode>表示可以包含0条或多条语句。这里没有重新设计一套语句集合，而是直接复用\u003Ccode>main\u003C\u002Fcode>，是因为代码块内部理应能写和最外层一样的语句，包括后续文章会讲到的循环语句，复用可以让新语句自动获得\"可以写在代码块里\"的能力，不需要每次新增语句都去修改\u003Ccode>block\u003C\u002Fcode>。\u003C\u002Fp>\u003C\u002Fli>\n   \u003Cli>\n    \u003Cp>\u003Ccode>ifStmt\u003C\u002Fcode>语法中，\u003Ccode>(ELSE IF '(' cond ')' block)*\u003C\u002Fcode>表示\u003Ccode>else if\u003C\u002Fcode>可以出现0次或多次，\u003Ccode>(ELSE block)?\u003C\u002Fcode>表示最后可以有一个可选的\u003Ccode>else\u003C\u002Fcode>。这里有一个关键特点：生成的\u003Ccode>IfStmtContext\u003C\u002Fcode>中，\u003Ccode>cond()\u003C\u002Fcode>方法返回的是一个\u003Ccode>List&lt;CondContext&gt;\u003C\u002Fcode>，个数等于1（if本身）加上\u003Ccode>else if\u003C\u002Fcode>的个数；\u003Ccode>block()\u003C\u002Fcode>方法返回的也是一个\u003Ccode>List&lt;BlockContext&gt;\u003C\u002Fcode>，如果最后跟了单独的\u003Ccode>else\u003C\u002Fcode>，那么\u003Ccode>block\u003C\u002Fcode>的个数会比\u003Ccode>cond\u003C\u002Fcode>多一个。这个\"数量差\"就是后面实现时判断是否命中最终\u003Ccode>else\u003C\u002Fcode>的依据。\u003C\u002Fp>\u003C\u002Fli>\n   \u003Cli>\n    \u003Cp>\u003Ccode>cond\u003C\u002Fcode>语法使用了和\u003Ccode>calcu\u003C\u002Fcode>一样的左递归写法。\u003Ccode>NOT\u003C\u002Fcode>写在最前面表示优先级最高，其次是\u003Ccode>AND\u003C\u002Fcode>，最后是\u003Ccode>OR\u003C\u002Fcode>，这和\u003Ccode>calcu\u003C\u002Fcode>语法中乘除优先于加减是同样的道理——\u003Cstrong>写在上面的规则优先级更高\u003C\u002Fstrong>。\u003Ccode>compare\u003C\u002Fcode>两侧使用\u003Ccode>calcu\u003C\u002Fcode>，所以比较运算符两侧可以是常量、变量，也可以是一个完整的四则运算表达式。\u003Ccode>boolLiteral\u003C\u002Fcode>和\u003Ccode>condArgValue\u003C\u002Fcode>分别处理直接写\u003Ccode>true\u003C\u002Fcode>\u002F\u003Ccode>false\u003C\u002Fcode>字面量，或者写一个布尔型变量作为条件的情况。\u003C\u002Fp>\u003C\u002Fli>\n  \u003C\u002Ful>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2>三、语法实现\u003C\u002Fh2>\n\u003Cp>接下来使用idea插件将定义好的.g4文件重新生成java文件覆盖之前的文件。在开始实现\u003Ccode>ifStmt\u003C\u002Fcode>之前，需要先修正上一篇文章遗留的一个小问题：\u003Ccode>visitBoolarg\u003C\u002Fcode>当时保存的是\u003Ccode>BOOL\u003C\u002Fcode>词法的原始文本（字符串\"true\"\u002F\"false\"），仅仅够打印用。而本篇的条件表达式需要拿到真正的\u003Ccode>Boolean\u003C\u002Fcode>类型参与逻辑运算，所以这里顺便修正为保存真正的\u003Ccode>Boolean\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">\u002F**\n * 定义布尔型变量，获取到布尔值后将变量值保存到map中\n * 这里存的是真正的Boolean而不是文本，是为了本篇新增的条件表达式中可以直接取出参与逻辑运算\n *\u002F\n@Override\npublic VisitorResult visitBoolarg(RuleSetParser.BoolargContext ctx) {\n    paramDefine.put(ctx.id.getText(), Boolean.parseBoolean(ctx.BOOL().getText())); \u002F\u002F修改\n    return VisitorResult.nil();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>接着在\u003Ccode>MyRuleSetVisitor\u003C\u002Fcode>中新增本篇涉及到的各个方法：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">\u002F\u002F ---------------------------以下函数为本次新增----------------------------\n\n\u002F**\n * 比较运算，比较符两侧都是calcu语法，先算出数值再根据操作符类型比较\n *\u002F\n@Override\npublic VisitorResult visitCompare(RuleSetParser.CompareContext ctx) {\n    double left = visit(ctx.calcu(0)).getNumber();\n    double right = visit(ctx.calcu(1)).getNumber();\n    switch (ctx.opt.getType()) {\n        case RuleSetLexer.GT:\n            return VisitorResult.value(left &gt; right);\n        case RuleSetLexer.LT:\n            return VisitorResult.value(left &lt; right);\n        case RuleSetLexer.GE:\n            return VisitorResult.value(left &gt;= right);\n        case RuleSetLexer.LE:\n            return VisitorResult.value(left &lt;= right);\n        case RuleSetLexer.EQ:\n            return VisitorResult.value(left == right);\n        default:\n            return VisitorResult.value(left != right);\n    }\n}\n\n\u002F**\n * 逻辑非，取反cond的布尔结果\n *\u002F\n@Override\npublic VisitorResult visitNotCond(RuleSetParser.NotCondContext ctx) {\n    boolean value = visit(ctx.cond()).getBool();\n    return VisitorResult.value(!value);\n}\n\n\u002F**\n * 逻辑与，两个cond都为真才为真\n *\u002F\n@Override\npublic VisitorResult visitAndCond(RuleSetParser.AndCondContext ctx) {\n    boolean left = visit(ctx.cond(0)).getBool();\n    boolean right = visit(ctx.cond(1)).getBool();\n    return VisitorResult.value(left &amp;&amp; right);\n}\n\n\u002F**\n * 逻辑或，两个cond有一个为真即为真\n *\u002F\n@Override\npublic VisitorResult visitOrCond(RuleSetParser.OrCondContext ctx) {\n    boolean left = visit(ctx.cond(0)).getBool();\n    boolean right = visit(ctx.cond(1)).getBool();\n    return VisitorResult.value(left || right);\n}\n\n\u002F**\n * 带括号的条件表达式，直接访问括号内的cond\n *\u002F\n@Override\npublic VisitorResult visitCondParens(RuleSetParser.CondParensContext ctx) {\n    return visit(ctx.cond());\n}\n\n\u002F**\n * 布尔字面量true\u002Ffalse直接转为Boolean\n *\u002F\n@Override\npublic VisitorResult visitBoolLiteral(RuleSetParser.BoolLiteralContext ctx) {\n    return VisitorResult.value(Boolean.parseBoolean(ctx.getText()));\n}\n\n\u002F**\n * 条件表达式中使用布尔变量，根据变量名称从保存变量值的map中查找\n *\u002F\n@Override\npublic VisitorResult visitCondArgValue(RuleSetParser.CondArgValueContext ctx) {\n    Object value = paramDefine.get(ctx.getText());\n    if (value instanceof Boolean) {\n        return VisitorResult.value(value);\n    }\n    throw new RuntimeException(\"未定义的参数：\" + ctx.getText() + \" 或参数\" + ctx.getText() + \"定义为非布尔型\");\n}\n\n\u002F**\n * if\u002Felse if\u002Felse语句\n * ctx.cond()按顺序依次是if的条件、每个else if的条件\n * ctx.block()按顺序依次是if的代码块、每个else if的代码块，如果最后跟了单独的else，则block数量会比cond多一个\n * 依次判断每个条件，命中哪个条件就执行对应的代码块，如果都不命中且存在多出来的最后一个block，则执行它\n *\u002F\n@Override\npublic VisitorResult visitIfStmt(RuleSetParser.IfStmtContext ctx) {\n    List&lt;RuleSetParser.CondContext&gt; conds = ctx.cond();\n    List&lt;RuleSetParser.BlockContext&gt; blocks = ctx.block();\n    for (int i = 0; i &lt; conds.size(); i++) {\n        if (visit(conds.get(i)).getBool()) {\n            return visit(blocks.get(i));\n        }\n    }\n    if (blocks.size() &gt; conds.size()) {\n        return visit(blocks.get(blocks.size() - 1));\n    }\n    return VisitorResult.nil();\n}\n\n\u002F**\n * 代码块，依次执行块内的每一条main语句\n *\u002F\n@Override\npublic VisitorResult visitBlock(RuleSetParser.BlockContext ctx) {\n    for (RuleSetParser.MainContext mainCtx : ctx.main()) {\n        visit(mainCtx);\n    }\n    return VisitorResult.nil();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cblockquote>\n \u003Cp>关于\u003Ccode>main\u003C\u002Fcode>和\u003Ccode>prog\u003C\u002Fcode>：细心的读者可能会注意到我们并没有重写\u003Ccode>visitMain\u003C\u002Fcode>和\u003Ccode>visitProg\u003C\u002Fcode>方法。这是因为\u003Ccode>main\u003C\u002Fcode>、\u003Ccode>prog\u003C\u002Fcode>两个语法都没有使用\u003Ccode>#\u003C\u002Fcode>定义别名，antlr4会生成默认的\u003Ccode>visitChildren\u003C\u002Fcode>实现，按顺序访问每个子节点，这一点在上一篇文章中就已经是这样，本篇的\u003Ccode>ifStmt\u003C\u002Fcode>作为\u003Ccode>main\u003C\u002Fcode>新增的一种子节点，同样会被默认实现自动访问到，不需要额外处理。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2>四、测试代码与执行结果\u003C\u002Fh2>\n\u003Cp>接下来编写测试代码，验证\u003Ccode>if \u002F else if \u002F else\u003C\u002Fcode>以及比较运算符、逻辑运算符是否都符合预期：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public class Test {\n    public static void main(String[] args) {\n        String expression =\n                \"number a = 10 \\n\" +\n                        \"number b = 3 \\n\" +\n                        \"boolean flag = true \\n\" +\n                        \"if (a &gt; b &amp;&amp; flag) { \\n\" +\n                        \"    print(\\\"a大于b且flag为真\\\") \\n\" +\n                        \"} else if (a == b) { \\n\" +\n                        \"    print(\\\"a等于b\\\") \\n\" +\n                        \"} else { \\n\" +\n                        \"    print(\\\"a不大于b\\\") \\n\" +\n                        \"} \\n\" +\n                        \"if (a &lt; b) { \\n\" +\n                        \"    print(\\\"不会执行\\\") \\n\" +\n                        \"} else if (!flag) { \\n\" +\n                        \"    print(\\\"不会执行\\\") \\n\" +\n                        \"} else { \\n\" +\n                        \"    print(\\\"最后的else命中\\\") \\n\" +\n                        \"}\";\n        calcute(expression);\n    }\n    \u002F\u002F calcute方法与上一篇文章保持一致，这里不再重复贴出\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>执行结果如下：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>执行:\nnumber a = 10\nnumber b = 3\nboolean flag = true\nif (a &gt; b &amp;&amp; flag) {\n    print(\"a大于b且flag为真\")\n} else if (a == b) {\n    print(\"a等于b\")\n} else {\n    print(\"a不大于b\")\n}\nif (a &lt; b) {\n    print(\"不会执行\")\n} else if (!flag) {\n    print(\"不会执行\")\n} else {\n    print(\"最后的else命中\")\n}\n\n\"a大于b且flag为真\"\n\"最后的else命中\"\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>第一段\u003Ccode>if\u003C\u002Fcode>中\u003Ccode>a &gt; b\u003C\u002Fcode>为真且\u003Ccode>flag\u003C\u002Fcode>为真，\u003Ccode>&amp;&amp;\u003C\u002Fcode>的结果为真，命中第一个分支，打印\"a大于b且flag为真\"；第二段\u003Ccode>if\u003C\u002Fcode>中\u003Ccode>a &lt; b\u003C\u002Fcode>为假，\u003Ccode>!flag\u003C\u002Fcode>也为假（flag为true），两个条件都不命中，最终执行了\u003Ccode>else\u003C\u002Fcode>，打印\"最后的else命中\"，符合预期。\u003C\u002Fp>\n\u003Ch2>五、遗留的问题\u003C\u002Fh2>\n\u003Cp>细心的读者可能已经发现了一个隐患：目前所有变量都保存在\u003Ccode>MyRuleSetVisitor\u003C\u002Fcode>里唯一的一个\u003Ccode>paramDefine\u003C\u002Fcode>这个Map中，这意味着在\u003Ccode>if\u003C\u002Fcode>代码块内部定义的变量，出了这个代码块在外面依然能访问到——这在大多数编程语言里是不允许的，正确的行为应该是代码块内定义的变量只在该代码块内可见。这个问题在只有\u003Ccode>if\u003C\u002Fcode>语句时还不明显，当下一篇文章引入循环语句后会更容易暴露出来，我们将在实现完循环语句后专门用一篇文章来解决作用域的问题。下一篇文章我们先继续完善语法，实现\u003Ccode>while\u003C\u002Fcode>循环语句。\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},"2101608263357743105","antlr4-05-loops","Antlr4系列⑤：支持循环语句","while循环、break\u002Fcontinue（用异常做控制流跳转）",4801,13,"2026-09-20T17:44:00",{"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":24,"publishedAt":55},"2101619933819879425","antlr4-07-functions-and-call-stack","Antlr4系列⑦：自定义函数与调用栈","function定义\u002F调用、递归、词法作用域、调用栈防死递归",6508,"2026-09-20T18:33:25",{"code":4,"message":5,"data":57},{"prev":58,"next":59},{"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},{"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","编程与技术笔记","11",{"id":68,"name":69,"slug":70,"description":71,"sortOrder":22,"articleCount":72},"1946000000000000002","随笔","essay","生活与思考","0",{"html":74,"toc":75},"\u003Cp>在上一篇文章中我们完善了语法，使其支持变量的定义和打印函数，本篇文章我们将继续完善语法，实现\u003Ccode>if \u002F else if \u002F else\u003C\u002Fcode>分支语句，并为此新增比较运算符和逻辑运算符，让我们的语言真正具备\"判断\"的能力。\u003C\u002Fp>\n\u003Ch2 id=\"toc-1\">一、语法设计\u003C\u002Fh2>\n\u003Col>\n \u003Cli>\n  \u003Cp>比较运算符\u003C\u002Fp>\n  \u003Cp>参考主流语言的写法，设计六种比较运算符：\u003Ccode>&gt; &lt; &gt;= &lt;= == !=\u003C\u002Fcode>，运算符两侧支持数值、变量甚至一个完整的四则运算表达式，例如\u003Ccode>a + 1 &gt; b * 2\u003C\u002Fcode>。\u003C\u002Fp>\u003C\u002Fli>\n \u003Cli>\n  \u003Cp>逻辑运算符\u003C\u002Fp>\n  \u003Cp>设计三种逻辑运算符：\u003Ccode>&amp;&amp;\u003C\u002Fcode>（与）、\u003Ccode>||\u003C\u002Fcode>（或）、\u003Ccode>!\u003C\u002Fcode>（非），用来组合或取反条件表达式，同时支持用小括号显式提升优先级。\u003C\u002Fp>\u003C\u002Fli>\n \u003Cli>\n  \u003Cp>if语句\u003C\u002Fp>\n  \u003Cp>参考java语言if语句的写法设计如下：\u003C\u002Fp>\n  \u003Cul>\n   \u003Cli>使用\u003Ccode>if (条件) { 语句 }\u003C\u002Fcode>作为基本结构。\u003C\u002Fli>\n   \u003Cli>支持任意个\u003Ccode>else if (条件) { 语句 }\u003C\u002Fcode>。\u003C\u002Fli>\n   \u003Cli>支持一个可选的\u003Ccode>else { 语句 }\u003C\u002Fcode>，必须放在最后。\u003C\u002Fli>\n   \u003Cli>\u003Ccode>{ }\u003C\u002Fcode>包裹的语句块内可以是之前已经支持的变量定义、打印函数，也可以是if语句本身，即支持嵌套。\u003C\u002Fli>\n  \u003C\u002Ful>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"toc-2\">二、语法定义\u003C\u002Fh2>\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>grammar RuleSet; \u002F\u002F程序名称和.g4名称一致即可\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>prog: main+\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>main: setArg SEMI?\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    | print SEMI?\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    | ifStmt                                                \u002F\u002F 新增\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>\u002F\u002F 新增：代码块，用花括号包裹0个或多个main，支持if语句内部嵌套if\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>block: '{' main* '}'                                        \u002F\u002F 新增\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>\u002F\u002F 新增：if \u002F else if \u002F else，else if可以出现0次或多次，else可选\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>ifStmt: IF '(' cond ')' block\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>        (ELSE IF '(' cond ')' block)*\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>        (ELSE block)?\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>      ;                                                     \u002F\u002F 新增\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u002F\u002F 新增：条件表达式，支持比较运算、逻辑运算以及布尔字面量\u002F布尔变量\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>cond: NOT cond                                              # notCond\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    | cond AND cond                                          # andCond\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    | cond OR cond                                           # orCond\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    | calcu opt=(GT|LT|GE|LE|EQ|NE) calcu                    # compare\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    | '(' cond ')'                                           # condParens\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    | BOOL                                                    # boolLiteral\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    | ID                                                      # condArgValue\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    ;                                                        \u002F\u002F 新增\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>setArg :'number'  id = ID '='  calcu                      #numarg\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>       |'string'  id = ID '='  STRING                     #chararg\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>       |'boolean' id = ID '='  BOOL                       #boolarg\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>       ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>calcu: calcu opt=(MUL|DIV) calcu                          # mulAndDiv\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>     | calcu opt=(ADD|SUB) calcu                          # addAndSub\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>     | '(' calcu ')'                                      # parens\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>     | NUMBER                                             # number\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>     | ID                                                 # argValue\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>     ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>print: PRINT '(' value = ID ')'                           # printArg\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>     | PRINT '(' value = BOOL ')'                         # printBoolean\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>     | PRINT '(' value = STRING ')'                       # printString\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>BOOL : 'true' | 'false' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>STRING : '\"' .*? '\"';\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>PRINT : 'print';\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>SEMI:  ';';\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u002F\u002F以下为本次新增词法\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>IF : 'if' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>ELSE : 'else' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>AND : '&#x26;&#x26;' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>OR : '||' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>NOT : '!' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>GT : '>' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>LT : '&#x3C;' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>GE : '>=' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>LE : '&#x3C;=' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>EQ : '==' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>NE : '!=' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>fragment ID_LETTER : [a-z] | [A-Z] | '_' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>fragment DIGIT : [0-9] ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>ID : ID_LETTER (ID_LETTER | DIGIT)* ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>为方便和上一篇文章中的语法进行对比，对于本次新增的部分使用注释进行了标注，接下来对本次新增的部分进行解释。\u003C\u002Fp>\n\u003Col>\n \u003Cli>\n  \u003Cp>词法部分\u003C\u002Fp>\n  \u003Cp>\u003Ccode>IF\u003C\u002Fcode>、\u003Ccode>ELSE\u003C\u002Fcode>等关键字词法都是固定字符串，必须放在\u003Ccode>ID\u003C\u002Fcode>词法\u003Cstrong>之前\u003C\u002Fstrong>定义，原因和之前\u003Ccode>BOOL\u003C\u002Fcode>、\u003Ccode>PRINT\u003C\u002Fcode>一样：antlr4词法优先匹配写在前面的规则，如果把它们放在\u003Ccode>ID\u003C\u002Fcode>后面，\u003Ccode>if\u003C\u002Fcode>、\u003Ccode>else\u003C\u002Fcode>这些字符串就会被当成普通的标识符（变量名）而不是关键字。\u003C\u002Fp>\u003C\u002Fli>\n \u003Cli>\n  \u003Cp>语法部分\u003C\u002Fp>\n  \u003Cul>\n   \u003Cli>\n    \u003Cp>\u003Ccode>block\u003C\u002Fcode>语法表示一个代码块，内容就是复用已有的\u003Ccode>main\u003C\u002Fcode>语法，用\u003Ccode>*\u003C\u002Fcode>表示可以包含0条或多条语句。这里没有重新设计一套语句集合，而是直接复用\u003Ccode>main\u003C\u002Fcode>，是因为代码块内部理应能写和最外层一样的语句，包括后续文章会讲到的循环语句，复用可以让新语句自动获得\"可以写在代码块里\"的能力，不需要每次新增语句都去修改\u003Ccode>block\u003C\u002Fcode>。\u003C\u002Fp>\u003C\u002Fli>\n   \u003Cli>\n    \u003Cp>\u003Ccode>ifStmt\u003C\u002Fcode>语法中，\u003Ccode>(ELSE IF '(' cond ')' block)*\u003C\u002Fcode>表示\u003Ccode>else if\u003C\u002Fcode>可以出现0次或多次，\u003Ccode>(ELSE block)?\u003C\u002Fcode>表示最后可以有一个可选的\u003Ccode>else\u003C\u002Fcode>。这里有一个关键特点：生成的\u003Ccode>IfStmtContext\u003C\u002Fcode>中，\u003Ccode>cond()\u003C\u002Fcode>方法返回的是一个\u003Ccode>List&lt;CondContext&gt;\u003C\u002Fcode>，个数等于1（if本身）加上\u003Ccode>else if\u003C\u002Fcode>的个数；\u003Ccode>block()\u003C\u002Fcode>方法返回的也是一个\u003Ccode>List&lt;BlockContext&gt;\u003C\u002Fcode>，如果最后跟了单独的\u003Ccode>else\u003C\u002Fcode>，那么\u003Ccode>block\u003C\u002Fcode>的个数会比\u003Ccode>cond\u003C\u002Fcode>多一个。这个\"数量差\"就是后面实现时判断是否命中最终\u003Ccode>else\u003C\u002Fcode>的依据。\u003C\u002Fp>\u003C\u002Fli>\n   \u003Cli>\n    \u003Cp>\u003Ccode>cond\u003C\u002Fcode>语法使用了和\u003Ccode>calcu\u003C\u002Fcode>一样的左递归写法。\u003Ccode>NOT\u003C\u002Fcode>写在最前面表示优先级最高，其次是\u003Ccode>AND\u003C\u002Fcode>，最后是\u003Ccode>OR\u003C\u002Fcode>，这和\u003Ccode>calcu\u003C\u002Fcode>语法中乘除优先于加减是同样的道理——\u003Cstrong>写在上面的规则优先级更高\u003C\u002Fstrong>。\u003Ccode>compare\u003C\u002Fcode>两侧使用\u003Ccode>calcu\u003C\u002Fcode>，所以比较运算符两侧可以是常量、变量，也可以是一个完整的四则运算表达式。\u003Ccode>boolLiteral\u003C\u002Fcode>和\u003Ccode>condArgValue\u003C\u002Fcode>分别处理直接写\u003Ccode>true\u003C\u002Fcode>\u002F\u003Ccode>false\u003C\u002Fcode>字面量，或者写一个布尔型变量作为条件的情况。\u003C\u002Fp>\u003C\u002Fli>\n  \u003C\u002Ful>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"toc-3\">三、语法实现\u003C\u002Fh2>\n\u003Cp>接下来使用idea插件将定义好的.g4文件重新生成java文件覆盖之前的文件。在开始实现\u003Ccode>ifStmt\u003C\u002Fcode>之前，需要先修正上一篇文章遗留的一个小问题：\u003Ccode>visitBoolarg\u003C\u002Fcode>当时保存的是\u003Ccode>BOOL\u003C\u002Fcode>词法的原始文本（字符串\"true\"\u002F\"false\"），仅仅够打印用。而本篇的条件表达式需要拿到真正的\u003Ccode>Boolean\u003C\u002Fcode>类型参与逻辑运算，所以这里顺便修正为保存真正的\u003Ccode>Boolean\u003C\u002Fcode>：\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:#6A737D;--shiki-dark:#6A737D\">\u002F**\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 定义布尔型变量，获取到布尔值后将变量值保存到map中\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 这里存的是真正的Boolean而不是文本，是为了本篇新增的条件表达式中可以直接取出参与逻辑运算\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> *\u002F\u003C\u002Fspan>\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\"> VisitorResult \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitBoolarg\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.BoolargContext ctx) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    paramDefine.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">put\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.id.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(), Boolean.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">parseBoolean\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">BOOL\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>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">\u002F\u002F修改\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\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">nil\u003C\u002Fspan>\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>接着在\u003Ccode>MyRuleSetVisitor\u003C\u002Fcode>中新增本篇涉及到的各个方法：\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:#6A737D;--shiki-dark:#6A737D\">\u002F\u002F ---------------------------以下函数为本次新增----------------------------\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">\u002F**\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 比较运算，比较符两侧都是calcu语法，先算出数值再根据操作符类型比较\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> *\u002F\u003C\u002Fspan>\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\"> VisitorResult \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitCompare\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.CompareContext ctx) {\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:#6F42C1;--shiki-dark:#B392F0\"> visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">calcu\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\">getNumber\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:#6F42C1;--shiki-dark:#B392F0\"> visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">calcu\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\">getNumber\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    switch\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (ctx.opt.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getType\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">()) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        case\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> RuleSetLexer.GT\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">:\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\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">value\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:#D73A49;--shiki-dark:#F97583\">        case\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> RuleSetLexer.LT\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">:\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\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">value\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> right);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        case\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> RuleSetLexer.GE\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">:\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\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">value\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:#D73A49;--shiki-dark:#F97583\">        case\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> RuleSetLexer.LE\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">:\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\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">value\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">&#x3C;=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> right);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        case\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> RuleSetLexer.EQ\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">:\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\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">value\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:#D73A49;--shiki-dark:#F97583\">        default:\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\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">value\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\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">\u002F**\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 逻辑非，取反cond的布尔结果\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> *\u002F\u003C\u002Fspan>\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\"> VisitorResult \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitNotCond\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.NotCondContext ctx) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    boolean\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> value \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">cond\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">()).\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getBool\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\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">value\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">!\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">value);\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:#6A737D;--shiki-dark:#6A737D\">\u002F**\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 逻辑与，两个cond都为真才为真\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> *\u002F\u003C\u002Fspan>\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\"> VisitorResult \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitAndCond\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.AndCondContext ctx) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    boolean\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">cond\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\">getBool\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    boolean\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> right \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">cond\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\">getBool\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\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">value\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">&#x26;&#x26;\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:#6A737D;--shiki-dark:#6A737D\">\u002F**\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 逻辑或，两个cond有一个为真即为真\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> *\u002F\u003C\u002Fspan>\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\"> VisitorResult \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitOrCond\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.OrCondContext ctx) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    boolean\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> left \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">cond\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\">getBool\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    boolean\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> right \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">cond\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\">getBool\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\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">value\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:#6A737D;--shiki-dark:#6A737D\">\u002F**\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 带括号的条件表达式，直接访问括号内的cond\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> *\u002F\u003C\u002Fspan>\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\"> VisitorResult \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitCondParens\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.CondParensContext ctx) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">cond\u003C\u002Fspan>\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>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">\u002F**\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 布尔字面量true\u002Ffalse直接转为Boolean\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> *\u002F\u003C\u002Fspan>\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\"> VisitorResult \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitBoolLiteral\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.BoolLiteralContext ctx) {\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\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">value\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(Boolean.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">parseBoolean\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\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:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">\u002F**\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 条件表达式中使用布尔变量，根据变量名称从保存变量值的map中查找\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> *\u002F\u003C\u002Fspan>\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\"> VisitorResult \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitCondArgValue\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.CondArgValueContext ctx) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    Object value \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> paramDefine.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">get\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\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\">    if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (value \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">instanceof\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Boolean) {\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\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">value\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(value);\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:#D73A49;--shiki-dark:#F97583\">    throw\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> RuntimeException\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\"> ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">+\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\"> ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">+\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"定义为非布尔型\"\u003C\u002Fspan>\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>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">\u002F**\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * if\u002Felse if\u002Felse语句\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * ctx.cond()按顺序依次是if的条件、每个else if的条件\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * ctx.block()按顺序依次是if的代码块、每个else if的代码块，如果最后跟了单独的else，则block数量会比cond多一个\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 依次判断每个条件，命中哪个条件就执行对应的代码块，如果都不命中且存在多出来的最后一个block，则执行它\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> *\u002F\u003C\u002Fspan>\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\"> VisitorResult \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitIfStmt\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.IfStmtContext ctx) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    List&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">RuleSetParser\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">.\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">CondContext\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">> conds \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">cond\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    List&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">RuleSetParser\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">.\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">BlockContext\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">> blocks \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">block\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    for\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">int\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> i \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">; i \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> conds.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">size\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(); i\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">++\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(conds.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">get\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(i)).\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getBool\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:#6F42C1;--shiki-dark:#B392F0\"> visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(blocks.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">get\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(i));\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\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (blocks.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">size\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">>\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> conds.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">size\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:#6F42C1;--shiki-dark:#B392F0\"> visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(blocks.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">get\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(blocks.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">size\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">-\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> 1\u003C\u002Fspan>\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\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">nil\u003C\u002Fspan>\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>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">\u002F**\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 代码块，依次执行块内的每一条main语句\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> *\u002F\u003C\u002Fspan>\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\"> VisitorResult \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visitBlock\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.BlockContext ctx) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    for\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (RuleSetParser.MainContext mainCtx \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">:\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">main\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">()) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">        visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(mainCtx);\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:#D73A49;--shiki-dark:#F97583\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> VisitorResult.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">nil\u003C\u002Fspan>\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\u003Cblockquote>\n \u003Cp>关于\u003Ccode>main\u003C\u002Fcode>和\u003Ccode>prog\u003C\u002Fcode>：细心的读者可能会注意到我们并没有重写\u003Ccode>visitMain\u003C\u002Fcode>和\u003Ccode>visitProg\u003C\u002Fcode>方法。这是因为\u003Ccode>main\u003C\u002Fcode>、\u003Ccode>prog\u003C\u002Fcode>两个语法都没有使用\u003Ccode>#\u003C\u002Fcode>定义别名，antlr4会生成默认的\u003Ccode>visitChildren\u003C\u002Fcode>实现，按顺序访问每个子节点，这一点在上一篇文章中就已经是这样，本篇的\u003Ccode>ifStmt\u003C\u002Fcode>作为\u003Ccode>main\u003C\u002Fcode>新增的一种子节点，同样会被默认实现自动访问到，不需要额外处理。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"toc-4\">四、测试代码与执行结果\u003C\u002Fh2>\n\u003Cp>接下来编写测试代码，验证\u003Ccode>if \u002F else if \u002F else\u003C\u002Fcode>以及比较运算符、逻辑运算符是否都符合预期：\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>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                \"number a = 10 \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"number b = 3 \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"boolean flag = true \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"if (a > b &#x26;&#x26; flag) { \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"    print(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">a大于b且flag为真\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"} else if (a == b) { \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"    print(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">a等于b\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"} else { \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"    print(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">a不大于b\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"} \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"if (a &#x3C; b) { \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"    print(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">不会执行\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"} else if (!flag) { \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"    print(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">不会执行\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"} else { \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"    print(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">最后的else命中\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">                        \"}\"\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\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">    \u002F\u002F calcute方法与上一篇文章保持一致，这里不再重复贴出\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>执行:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>number a = 10\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>number b = 3\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>boolean flag = true\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>if (a > b &#x26;&#x26; flag) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    print(\"a大于b且flag为真\")\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>} else if (a == b) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    print(\"a等于b\")\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>} else {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    print(\"a不大于b\")\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>if (a &#x3C; b) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    print(\"不会执行\")\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>} else if (!flag) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    print(\"不会执行\")\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>} else {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    print(\"最后的else命中\")\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>\"a大于b且flag为真\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\"最后的else命中\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>第一段\u003Ccode>if\u003C\u002Fcode>中\u003Ccode>a &gt; b\u003C\u002Fcode>为真且\u003Ccode>flag\u003C\u002Fcode>为真，\u003Ccode>&amp;&amp;\u003C\u002Fcode>的结果为真，命中第一个分支，打印\"a大于b且flag为真\"；第二段\u003Ccode>if\u003C\u002Fcode>中\u003Ccode>a &lt; b\u003C\u002Fcode>为假，\u003Ccode>!flag\u003C\u002Fcode>也为假（flag为true），两个条件都不命中，最终执行了\u003Ccode>else\u003C\u002Fcode>，打印\"最后的else命中\"，符合预期。\u003C\u002Fp>\n\u003Ch2 id=\"toc-5\">五、遗留的问题\u003C\u002Fh2>\n\u003Cp>细心的读者可能已经发现了一个隐患：目前所有变量都保存在\u003Ccode>MyRuleSetVisitor\u003C\u002Fcode>里唯一的一个\u003Ccode>paramDefine\u003C\u002Fcode>这个Map中，这意味着在\u003Ccode>if\u003C\u002Fcode>代码块内部定义的变量，出了这个代码块在外面依然能访问到——这在大多数编程语言里是不允许的，正确的行为应该是代码块内定义的变量只在该代码块内可见。这个问题在只有\u003Ccode>if\u003C\u002Fcode>语句时还不明显，当下一篇文章引入循环语句后会更容易暴露出来，我们将在实现完循环语句后专门用一篇文章来解决作用域的问题。下一篇文章我们先继续完善语法，实现\u003Ccode>while\u003C\u002Fcode>循环语句。\u003C\u002Fp>",[76,80,83,86,89],{"id":77,"text":78,"level":79},"toc-1","一、语法设计",2,{"id":81,"text":82,"level":79},"toc-2","二、语法定义",{"id":84,"text":85,"level":79},"toc-3","三、语法实现",{"id":87,"text":88,"level":79},"toc-4","四、测试代码与执行结果",{"id":90,"text":91,"level":79},"toc-5","五、遗留的问题"]