[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"site-settings":3,"$f3nf5vw28hti20":14,"$f22gia5ro8h9sc":31,"$f38s5m1m0mdf8f":57,"$f2hf8zv88r59zg":68,"render-antlr4-10-arrays":81},{"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},"2101635146136862722","antlr4-10-arrays","Antlr4系列⑩：支持数组","array类型、字面量、下标读写、len",null,"1946000000000000001",1,5036,13,"2026-09-20T19:30:48","前几篇文章连续实现了调试器的能力，我们的小型DSL解释器已经能够被断点、单步调试了。这篇文章回到语言本身，继续补完语言的基础能力——数组（严格来说是\"元素类型统一为数值的动态数组\"，不支持不同类型混装，保持和函数参数一样的简化设计）。\n\n## 一、语法设计\n\n1. 数组定义\n\n    参考主流语言，用方括号包裹一组用逗号分隔的值作为数组字面量，比如`[1, 2, 3]`。定义一个数组类型的变量，复用已有的`number`\u002F`string`\u002F`boolean`风格，新增`array`关键字：`array arr = [1, 2, 3]`。\n\n2. 读取元素 \u002F 修改元素\n\n    用`arr[下标]`读取数组中某个位置的值，下标从0开始；`arr[下标] = 值`修改数组中某个位置的值。\n\n3. 数组长度\n\n    新增内置的`len(arr)`获取数组长度，配合`while`循环即可遍历整个数组，不需要额外设计`for-each`这样的新语法。\n\n## 二、语法定义\n\n根据以上设计新增语法：\n```\nsetArg :'number'  id = ID '='  calcu                      #numarg\n       |'string'  id = ID '='  STRING                     #chararg\n       |'boolean' id = ID '='  BOOL                       #boolarg\n       |'array'   id = ID '='  arrayLiteral                #arrayarg  \u002F\u002F 新增\n       ;\n\n\u002F\u002F 新增：数组字面量，方括号包裹0个或多个用逗号分隔的calcu\narrayLiteral: '[' (calcu (',' calcu)*)? ']'\n            ;\n\ncalcu: calcu opt=(MUL|DIV) calcu                          # mulAndDiv\n     | calcu opt=(ADD|SUB) calcu                          # addAndSub\n     | '(' calcu ')'                                      # parens\n     | NUMBER                                             # number\n     | ID '(' (calcu (',' calcu)*)? ')'                   # funcCall\n     | LEN '(' ID ')'                                      # arrayLen  \u002F\u002F 新增\n     | ID '[' calcu ']'                                    # arrayGet  \u002F\u002F 新增\n     | ID                                                 # argValue\n     ;\n\n\u002F\u002F assignStmt新增一个分支，用于修改数组中某个下标的值\nassignStmt: id = ID '=' calcu                              #numAssign\n          | id = ID '=' STRING                              #charAssign\n          | id = ID '=' BOOL                                #boolAssign\n          | arr = ID '[' index = calcu ']' '=' value = calcu #arrayElemAssign  \u002F\u002F 新增\n          ;\n\n\u002F\u002F以下为本篇新增词法\nLEN : 'len' ;\n```\n`arrayLiteral`里的元素直接复用`calcu`，意味着数组的每个元素既可以是字面量，也可以是变量甚至一个完整的四则运算表达式，比如`array arr = [a + 1, b * 2]`。`arr[下标]`这种写法在`calcu`里新增了一个`arrayGet`分支，在`assignStmt`里新增了一个`arrayElemAssign`分支，分别对应\"读\"和\"写\"两种场景——这和之前\"定义用`setArg`，赋值用`assignStmt`\"是同一种思路的延续。\n\n## 三、语法实现\n\n先扩展一下`VisitorResult`，之前只支持数值、字符串、布尔三种类型，现在加上数组（内部用`List\u003CDouble>`表示）：\n\n```java\nprivate VisitorResult(Object value) {\n    if (value instanceof String\n            || value instanceof Double\n            || value instanceof Boolean\n            || value instanceof List\n            || value == null) {\n        this.value = value;\n    } else {\n        throw new RuntimeException(\"不支持的参数类型\" + value);\n    }\n}\n\n@SuppressWarnings(\"unchecked\")\npublic List\u003CDouble> getArray() {\n    if (isArray()) {\n        return (List\u003CDouble>) value;\n    }\n    throw new RuntimeException(\"非Array类型\");\n}\n\npublic boolean isArray() {\n    return this.value instanceof List;\n}\n```\n\n接着在`MyRuleSetVisitor`里补充数组的定义、字面量求值、读取、长度：\n\n```java\n\u002F**\n * 定义数组类型变量，先求出数组字面量对应的List，再保存到当前作用域\n *\u002F\n@Override\npublic VisitorResult visitArrayarg(RuleSetParser.ArrayargContext ctx) {\n    VisitorResult array = visit(ctx.arrayLiteral());\n    currentScope.define(ctx.id.getText(), array.getArray());\n    return VisitorResult.nil();\n}\n\n\u002F**\n * 数组字面量，依次对每个元素求值后收集成一个List\n *\u002F\n@Override\npublic VisitorResult visitArrayLiteral(RuleSetParser.ArrayLiteralContext ctx) {\n    List\u003CDouble> values = new ArrayList\u003C>();\n    for (RuleSetParser.CalcuContext element : ctx.calcu()) {\n        values.add(visit(element).getNumber());\n    }\n    return VisitorResult.value(values);\n}\n\n\u002F**\n * 按下标读取数组元素\n *\u002F\n@Override\npublic VisitorResult visitArrayGet(RuleSetParser.ArrayGetContext ctx) {\n    String name = ctx.ID().getText();\n    List\u003CDouble> array = getArray(name);\n    int index = visit(ctx.calcu()).getNumber().intValue();\n    checkIndex(name, array, index);\n    return VisitorResult.value(array.get(index));\n}\n\n\u002F**\n * len(数组)，返回数组长度\n *\u002F\n@Override\npublic VisitorResult visitArrayLen(RuleSetParser.ArrayLenContext ctx) {\n    List\u003CDouble> array = getArray(ctx.ID().getText());\n    return VisitorResult.value((double) array.size());\n}\n\nprivate List\u003CDouble> getArray(String name) {\n    Object value = currentScope.get(name);\n    if (!(value instanceof List)) {\n        throw new RuntimeException(\"未定义的数组：\" + name);\n    }\n    return (List\u003CDouble>) value;\n}\n\nprivate void checkIndex(String name, List\u003CDouble> array, int index) {\n    if (index \u003C 0 || index >= array.size()) {\n        throw new RuntimeException(\"数组\" + name + \"下标越界：\" + index + \"，数组长度为\" + array.size());\n    }\n}\n```\n\n修改元素值的实现有一个值得说的细节：\n\n```java\n\u002F**\n * 修改数组中某一个下标的值。数组本身是可变的List，被currentScope持有的是同一个引用，\n * 这里直接对取出来的List调用set，不需要像标量变量那样再调用一次assign把新值写回作用域，\n * 因为\"修改数组里某个元素\"和\"数组变量本身指向另一个对象\"是两回事。\n *\u002F\n@Override\npublic VisitorResult visitArrayElemAssign(RuleSetParser.ArrayElemAssignContext ctx) {\n    List\u003CDouble> array = getArray(ctx.arr.getText());\n    int index = visit(ctx.index).getNumber().intValue();\n    checkIndex(ctx.arr.getText(), array, index);\n    array.set(index, visit(ctx.value).getNumber());\n    return VisitorResult.nil();\n}\n```\n> 关于引用语义：前几篇文章里，`number`\u002F`string`\u002F`boolean`变量的赋值都要调用`Scope.assign`重新把新值`put`进作用域，是因为这些值本身是不可变的（Java的`Double`、`String`、`Boolean`都是不可变对象），\"改值\"唯一的办法就是\"换一个新对象塞回去\"。而数组用的是Java的`ArrayList`，本身是可变对象，`Scope`里保存的其实是这个`List`对象的引用。所以`arr[1] = 99`不需要调用`assign`把一个新的List重新塞回作用域，直接对拿到的这个List调用`set`修改其中一项即可——这也意味着，如果把同一个数组传给两处（目前的语言还没有支持把数组当函数参数传递，但设计上要提前想到这一点），两边修改的会是同一份数据，这是可变对象天然带来的\"共享\"特性，后续如果要支持数组作为函数参数，需要格外注意这一点。\n\n另外值得一提的是，`print(arr)`不需要任何额外改造就能直接支持打印数组——`visitPrintArg`本来就是拿到变量的原始值直接调用`System.out.println`，Java的`List`本身有现成的`toString()`实现（会打印成`[10.0, 20.0, 30.0]`这样的格式），正好够用。\n\n## 四、测试代码与执行结果\n\n```java\nString expression =\n        \"array arr = [10, 20, 30] \\n\" +\n                \"print(arr) \\n\" +\n                \"number first = arr[0] \\n\" +\n                \"print(first) \\n\" +\n                \"arr[1] = 99 \\n\" +\n                \"print(arr) \\n\" +\n                \"number i = 0 \\n\" +\n                \"number sum = 0 \\n\" +\n                \"while (i \u003C len(arr)) { \\n\" +\n                \"    sum = sum + arr[i] \\n\" +\n                \"    i = i + 1 \\n\" +\n                \"} \\n\" +\n                \"print(sum) \\n\" +\n                \"number bad = arr[10]\";     \u002F\u002F 故意制造一个越界访问\ncalcute(expression);\n```\n\n执行结果如下：\n\n```\n执行:\narray arr = [10, 20, 30]\nprint(arr)\nnumber first = arr[0]\nprint(first)\narr[1] = 99\nprint(arr)\nnumber i = 0\nnumber sum = 0\nwhile (i \u003C len(arr)) {\n    sum = sum + arr[i]\n    i = i + 1\n}\nprint(sum)\nnumber bad = arr[10]\n\n[10.0, 20.0, 30.0]\n10.0\n[10.0, 99.0, 30.0]\n139.0\nException in thread \"main\" java.lang.RuntimeException: 数组arr下标越界：10，数组长度为3\n\tat cn.irule.MyRuleSetVisitor.checkIndex(MyRuleSetVisitor.java:276)\n\t......\n```\n\n依次验证：数组字面量定义并打印`[10.0, 20.0, 30.0]`；按下标读取`arr[0]`得到`10.0`；`arr[1] = 99`修改元素后再打印，变成`[10.0, 99.0, 30.0]`，说明修改是原地生效的；用`while`配合`len(arr)`遍历数组求和，`10 + 99 + 30 = 139.0`，结果正确；最后故意访问下标10（数组只有3个元素），程序抛出了预期的越界异常，符合边界检查的设计。\n\n## 五、遗留的问题\n\n目前数组的元素只能是数值，还不支持字符串数组或者混合类型数组，也还没有支持把数组当作参数传给函数（第⑦篇实现的函数目前只接受数值参数）。这些都是可以在数组这个基础上继续扩展的方向。下一篇文章我们计划把目光转向字符串相关的能力：字符串拼接、和数值之间的相互转换，以及内置函数的注册机制，让`len(arr)`这样的写法未来能够以一种更通用、可扩展的方式支持更多内置能力。\n","\u003Cp>前几篇文章连续实现了调试器的能力，我们的小型DSL解释器已经能够被断点、单步调试了。这篇文章回到语言本身，继续补完语言的基础能力——数组（严格来说是\"元素类型统一为数值的动态数组\"，不支持不同类型混装，保持和函数参数一样的简化设计）。\u003C\u002Fp>\n\u003Ch2>一、语法设计\u003C\u002Fh2>\n\u003Col>\n \u003Cli>\n  \u003Cp>数组定义\u003C\u002Fp>\n  \u003Cp>参考主流语言，用方括号包裹一组用逗号分隔的值作为数组字面量，比如\u003Ccode>[1, 2, 3]\u003C\u002Fcode>。定义一个数组类型的变量，复用已有的\u003Ccode>number\u003C\u002Fcode>\u002F\u003Ccode>string\u003C\u002Fcode>\u002F\u003Ccode>boolean\u003C\u002Fcode>风格，新增\u003Ccode>array\u003C\u002Fcode>关键字：\u003Ccode>array arr = [1, 2, 3]\u003C\u002Fcode>。\u003C\u002Fp>\u003C\u002Fli>\n \u003Cli>\n  \u003Cp>读取元素 \u002F 修改元素\u003C\u002Fp>\n  \u003Cp>用\u003Ccode>arr[下标]\u003C\u002Fcode>读取数组中某个位置的值，下标从0开始；\u003Ccode>arr[下标] = 值\u003C\u002Fcode>修改数组中某个位置的值。\u003C\u002Fp>\u003C\u002Fli>\n \u003Cli>\n  \u003Cp>数组长度\u003C\u002Fp>\n  \u003Cp>新增内置的\u003Ccode>len(arr)\u003C\u002Fcode>获取数组长度，配合\u003Ccode>while\u003C\u002Fcode>循环即可遍历整个数组，不需要额外设计\u003Ccode>for-each\u003C\u002Fcode>这样的新语法。\u003C\u002Fp>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2>二、语法定义\u003C\u002Fh2>\n\u003Cp>根据以上设计新增语法：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>setArg :'number'  id = ID '='  calcu                      #numarg\n       |'string'  id = ID '='  STRING                     #chararg\n       |'boolean' id = ID '='  BOOL                       #boolarg\n       |'array'   id = ID '='  arrayLiteral                #arrayarg  \u002F\u002F 新增\n       ;\n\n\u002F\u002F 新增：数组字面量，方括号包裹0个或多个用逗号分隔的calcu\narrayLiteral: '[' (calcu (',' calcu)*)? ']'\n            ;\n\ncalcu: calcu opt=(MUL|DIV) calcu                          # mulAndDiv\n     | calcu opt=(ADD|SUB) calcu                          # addAndSub\n     | '(' calcu ')'                                      # parens\n     | NUMBER                                             # number\n     | ID '(' (calcu (',' calcu)*)? ')'                   # funcCall\n     | LEN '(' ID ')'                                      # arrayLen  \u002F\u002F 新增\n     | ID '[' calcu ']'                                    # arrayGet  \u002F\u002F 新增\n     | ID                                                 # argValue\n     ;\n\n\u002F\u002F assignStmt新增一个分支，用于修改数组中某个下标的值\nassignStmt: id = ID '=' calcu                              #numAssign\n          | id = ID '=' STRING                              #charAssign\n          | id = ID '=' BOOL                                #boolAssign\n          | arr = ID '[' index = calcu ']' '=' value = calcu #arrayElemAssign  \u002F\u002F 新增\n          ;\n\n\u002F\u002F以下为本篇新增词法\nLEN : 'len' ;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>arrayLiteral\u003C\u002Fcode>里的元素直接复用\u003Ccode>calcu\u003C\u002Fcode>，意味着数组的每个元素既可以是字面量，也可以是变量甚至一个完整的四则运算表达式，比如\u003Ccode>array arr = [a + 1, b * 2]\u003C\u002Fcode>。\u003Ccode>arr[下标]\u003C\u002Fcode>这种写法在\u003Ccode>calcu\u003C\u002Fcode>里新增了一个\u003Ccode>arrayGet\u003C\u002Fcode>分支，在\u003Ccode>assignStmt\u003C\u002Fcode>里新增了一个\u003Ccode>arrayElemAssign\u003C\u002Fcode>分支，分别对应\"读\"和\"写\"两种场景——这和之前\"定义用\u003Ccode>setArg\u003C\u002Fcode>，赋值用\u003Ccode>assignStmt\u003C\u002Fcode>\"是同一种思路的延续。\u003C\u002Fp>\n\u003Ch2>三、语法实现\u003C\u002Fh2>\n\u003Cp>先扩展一下\u003Ccode>VisitorResult\u003C\u002Fcode>，之前只支持数值、字符串、布尔三种类型，现在加上数组（内部用\u003Ccode>List&lt;Double&gt;\u003C\u002Fcode>表示）：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">private VisitorResult(Object value) {\n    if (value instanceof String\n            || value instanceof Double\n            || value instanceof Boolean\n            || value instanceof List\n            || value == null) {\n        this.value = value;\n    } else {\n        throw new RuntimeException(\"不支持的参数类型\" + value);\n    }\n}\n\n@SuppressWarnings(\"unchecked\")\npublic List&lt;Double&gt; getArray() {\n    if (isArray()) {\n        return (List&lt;Double&gt;) value;\n    }\n    throw new RuntimeException(\"非Array类型\");\n}\n\npublic boolean isArray() {\n    return this.value instanceof List;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>接着在\u003Ccode>MyRuleSetVisitor\u003C\u002Fcode>里补充数组的定义、字面量求值、读取、长度：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">\u002F**\n * 定义数组类型变量，先求出数组字面量对应的List，再保存到当前作用域\n *\u002F\n@Override\npublic VisitorResult visitArrayarg(RuleSetParser.ArrayargContext ctx) {\n    VisitorResult array = visit(ctx.arrayLiteral());\n    currentScope.define(ctx.id.getText(), array.getArray());\n    return VisitorResult.nil();\n}\n\n\u002F**\n * 数组字面量，依次对每个元素求值后收集成一个List\n *\u002F\n@Override\npublic VisitorResult visitArrayLiteral(RuleSetParser.ArrayLiteralContext ctx) {\n    List&lt;Double&gt; values = new ArrayList&lt;&gt;();\n    for (RuleSetParser.CalcuContext element : ctx.calcu()) {\n        values.add(visit(element).getNumber());\n    }\n    return VisitorResult.value(values);\n}\n\n\u002F**\n * 按下标读取数组元素\n *\u002F\n@Override\npublic VisitorResult visitArrayGet(RuleSetParser.ArrayGetContext ctx) {\n    String name = ctx.ID().getText();\n    List&lt;Double&gt; array = getArray(name);\n    int index = visit(ctx.calcu()).getNumber().intValue();\n    checkIndex(name, array, index);\n    return VisitorResult.value(array.get(index));\n}\n\n\u002F**\n * len(数组)，返回数组长度\n *\u002F\n@Override\npublic VisitorResult visitArrayLen(RuleSetParser.ArrayLenContext ctx) {\n    List&lt;Double&gt; array = getArray(ctx.ID().getText());\n    return VisitorResult.value((double) array.size());\n}\n\nprivate List&lt;Double&gt; getArray(String name) {\n    Object value = currentScope.get(name);\n    if (!(value instanceof List)) {\n        throw new RuntimeException(\"未定义的数组：\" + name);\n    }\n    return (List&lt;Double&gt;) value;\n}\n\nprivate void checkIndex(String name, List&lt;Double&gt; array, int index) {\n    if (index &lt; 0 || index &gt;= array.size()) {\n        throw new RuntimeException(\"数组\" + name + \"下标越界：\" + index + \"，数组长度为\" + array.size());\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>修改元素值的实现有一个值得说的细节：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">\u002F**\n * 修改数组中某一个下标的值。数组本身是可变的List，被currentScope持有的是同一个引用，\n * 这里直接对取出来的List调用set，不需要像标量变量那样再调用一次assign把新值写回作用域，\n * 因为\"修改数组里某个元素\"和\"数组变量本身指向另一个对象\"是两回事。\n *\u002F\n@Override\npublic VisitorResult visitArrayElemAssign(RuleSetParser.ArrayElemAssignContext ctx) {\n    List&lt;Double&gt; array = getArray(ctx.arr.getText());\n    int index = visit(ctx.index).getNumber().intValue();\n    checkIndex(ctx.arr.getText(), array, index);\n    array.set(index, visit(ctx.value).getNumber());\n    return VisitorResult.nil();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cblockquote>\n \u003Cp>关于引用语义：前几篇文章里，\u003Ccode>number\u003C\u002Fcode>\u002F\u003Ccode>string\u003C\u002Fcode>\u002F\u003Ccode>boolean\u003C\u002Fcode>变量的赋值都要调用\u003Ccode>Scope.assign\u003C\u002Fcode>重新把新值\u003Ccode>put\u003C\u002Fcode>进作用域，是因为这些值本身是不可变的（Java的\u003Ccode>Double\u003C\u002Fcode>、\u003Ccode>String\u003C\u002Fcode>、\u003Ccode>Boolean\u003C\u002Fcode>都是不可变对象），\"改值\"唯一的办法就是\"换一个新对象塞回去\"。而数组用的是Java的\u003Ccode>ArrayList\u003C\u002Fcode>，本身是可变对象，\u003Ccode>Scope\u003C\u002Fcode>里保存的其实是这个\u003Ccode>List\u003C\u002Fcode>对象的引用。所以\u003Ccode>arr[1] = 99\u003C\u002Fcode>不需要调用\u003Ccode>assign\u003C\u002Fcode>把一个新的List重新塞回作用域，直接对拿到的这个List调用\u003Ccode>set\u003C\u002Fcode>修改其中一项即可——这也意味着，如果把同一个数组传给两处（目前的语言还没有支持把数组当函数参数传递，但设计上要提前想到这一点），两边修改的会是同一份数据，这是可变对象天然带来的\"共享\"特性，后续如果要支持数组作为函数参数，需要格外注意这一点。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>另外值得一提的是，\u003Ccode>print(arr)\u003C\u002Fcode>不需要任何额外改造就能直接支持打印数组——\u003Ccode>visitPrintArg\u003C\u002Fcode>本来就是拿到变量的原始值直接调用\u003Ccode>System.out.println\u003C\u002Fcode>，Java的\u003Ccode>List\u003C\u002Fcode>本身有现成的\u003Ccode>toString()\u003C\u002Fcode>实现（会打印成\u003Ccode>[10.0, 20.0, 30.0]\u003C\u002Fcode>这样的格式），正好够用。\u003C\u002Fp>\n\u003Ch2>四、测试代码与执行结果\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-java\">String expression =\n        \"array arr = [10, 20, 30] \\n\" +\n                \"print(arr) \\n\" +\n                \"number first = arr[0] \\n\" +\n                \"print(first) \\n\" +\n                \"arr[1] = 99 \\n\" +\n                \"print(arr) \\n\" +\n                \"number i = 0 \\n\" +\n                \"number sum = 0 \\n\" +\n                \"while (i &lt; len(arr)) { \\n\" +\n                \"    sum = sum + arr[i] \\n\" +\n                \"    i = i + 1 \\n\" +\n                \"} \\n\" +\n                \"print(sum) \\n\" +\n                \"number bad = arr[10]\";     \u002F\u002F 故意制造一个越界访问\ncalcute(expression);\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>执行结果如下：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>执行:\narray arr = [10, 20, 30]\nprint(arr)\nnumber first = arr[0]\nprint(first)\narr[1] = 99\nprint(arr)\nnumber i = 0\nnumber sum = 0\nwhile (i &lt; len(arr)) {\n    sum = sum + arr[i]\n    i = i + 1\n}\nprint(sum)\nnumber bad = arr[10]\n\n[10.0, 20.0, 30.0]\n10.0\n[10.0, 99.0, 30.0]\n139.0\nException in thread \"main\" java.lang.RuntimeException: 数组arr下标越界：10，数组长度为3\n\tat cn.irule.MyRuleSetVisitor.checkIndex(MyRuleSetVisitor.java:276)\n\t......\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>依次验证：数组字面量定义并打印\u003Ccode>[10.0, 20.0, 30.0]\u003C\u002Fcode>；按下标读取\u003Ccode>arr[0]\u003C\u002Fcode>得到\u003Ccode>10.0\u003C\u002Fcode>；\u003Ccode>arr[1] = 99\u003C\u002Fcode>修改元素后再打印，变成\u003Ccode>[10.0, 99.0, 30.0]\u003C\u002Fcode>，说明修改是原地生效的；用\u003Ccode>while\u003C\u002Fcode>配合\u003Ccode>len(arr)\u003C\u002Fcode>遍历数组求和，\u003Ccode>10 + 99 + 30 = 139.0\u003C\u002Fcode>，结果正确；最后故意访问下标10（数组只有3个元素），程序抛出了预期的越界异常，符合边界检查的设计。\u003C\u002Fp>\n\u003Ch2>五、遗留的问题\u003C\u002Fh2>\n\u003Cp>目前数组的元素只能是数值，还不支持字符串数组或者混合类型数组，也还没有支持把数组当作参数传给函数（第⑦篇实现的函数目前只接受数值参数）。这些都是可以在数组这个基础上继续扩展的方向。下一篇文章我们计划把目光转向字符串相关的能力：字符串拼接、和数值之间的相互转换，以及内置函数的注册机制，让\u003Ccode>len(arr)\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},"2101642321643094018","antlr4-11-builtin-functions","Antlr4系列⑪：内置函数注册机制","BuiltinFunction注册表、len\u002Fsum\u002Fabs\u002Fmax、放宽变量类型限制",5528,14,"2026-09-20T19:59:19",{"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},"2101619933819879425","antlr4-07-functions-and-call-stack","Antlr4系列⑦：自定义函数与调用栈","function定义\u002F调用、递归、词法作用域、调用栈防死递归",6508,17,"2026-09-20T18:33:25",{"id":50,"slug":51,"title":52,"summary":53,"coverUrl":20,"categoryId":21,"status":22,"topFlag":4,"viewCount":4,"likeCount":4,"wordCount":54,"readingMinutes":55,"publishedAt":56},"2101585710597718018","antlr4-03-variables-and-print","Antlr4系列③：支持变量定义和打印函数","number\u002Fstring\u002Fboolean变量定义、print函数、VisitorResult统一返回类型",8172,21,"2026-09-20T16:14:14",{"code":4,"message":5,"data":58},{"prev":59,"next":67},{"id":60,"slug":61,"title":62,"summary":63,"coverUrl":20,"categoryId":21,"status":22,"topFlag":4,"viewCount":4,"likeCount":4,"wordCount":64,"readingMinutes":65,"publishedAt":66},"2101631008783122433","antlr4-09-debugger-step-over","Antlr4系列⑨：调试器进阶之Step Over与条件断点","Step Into\u002FOver\u002FOut、调用栈展示、条件断点",5666,15,"2026-09-20T19:14:22",{"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":69},[70,75],{"id":21,"name":71,"slug":72,"description":73,"sortOrder":4,"articleCount":74},"技术","tech","编程与技术笔记","13",{"id":76,"name":77,"slug":78,"description":79,"sortOrder":22,"articleCount":80},"1946000000000000002","随笔","essay","生活与思考","0",{"html":82,"toc":83},"\u003Cp>前几篇文章连续实现了调试器的能力，我们的小型DSL解释器已经能够被断点、单步调试了。这篇文章回到语言本身，继续补完语言的基础能力——数组（严格来说是\"元素类型统一为数值的动态数组\"，不支持不同类型混装，保持和函数参数一样的简化设计）。\u003C\u002Fp>\n\u003Ch2 id=\"toc-1\">一、语法设计\u003C\u002Fh2>\n\u003Col>\n \u003Cli>\n  \u003Cp>数组定义\u003C\u002Fp>\n  \u003Cp>参考主流语言，用方括号包裹一组用逗号分隔的值作为数组字面量，比如\u003Ccode>[1, 2, 3]\u003C\u002Fcode>。定义一个数组类型的变量，复用已有的\u003Ccode>number\u003C\u002Fcode>\u002F\u003Ccode>string\u003C\u002Fcode>\u002F\u003Ccode>boolean\u003C\u002Fcode>风格，新增\u003Ccode>array\u003C\u002Fcode>关键字：\u003Ccode>array arr = [1, 2, 3]\u003C\u002Fcode>。\u003C\u002Fp>\u003C\u002Fli>\n \u003Cli>\n  \u003Cp>读取元素 \u002F 修改元素\u003C\u002Fp>\n  \u003Cp>用\u003Ccode>arr[下标]\u003C\u002Fcode>读取数组中某个位置的值，下标从0开始；\u003Ccode>arr[下标] = 值\u003C\u002Fcode>修改数组中某个位置的值。\u003C\u002Fp>\u003C\u002Fli>\n \u003Cli>\n  \u003Cp>数组长度\u003C\u002Fp>\n  \u003Cp>新增内置的\u003Ccode>len(arr)\u003C\u002Fcode>获取数组长度，配合\u003Ccode>while\u003C\u002Fcode>循环即可遍历整个数组，不需要额外设计\u003Ccode>for-each\u003C\u002Fcode>这样的新语法。\u003C\u002Fp>\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>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>       |'array'   id = ID '='  arrayLiteral                #arrayarg  \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个或多个用逗号分隔的calcu\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>arrayLiteral: '[' (calcu (',' calcu)*)? ']'\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>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 '(' (calcu (',' calcu)*)? ')'                   # funcCall\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>     | LEN '(' ID ')'                                      # arrayLen  \u002F\u002F 新增\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>     | ID '[' calcu ']'                                    # arrayGet  \u002F\u002F 新增\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>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u002F\u002F assignStmt新增一个分支，用于修改数组中某个下标的值\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>assignStmt: id = ID '=' calcu                              #numAssign\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>          | id = ID '=' STRING                              #charAssign\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>          | id = ID '=' BOOL                                #boolAssign\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>          | arr = ID '[' index = calcu ']' '=' value = calcu #arrayElemAssign  \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以下为本篇新增词法\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>LEN : 'len' ;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>arrayLiteral\u003C\u002Fcode>里的元素直接复用\u003Ccode>calcu\u003C\u002Fcode>，意味着数组的每个元素既可以是字面量，也可以是变量甚至一个完整的四则运算表达式，比如\u003Ccode>array arr = [a + 1, b * 2]\u003C\u002Fcode>。\u003Ccode>arr[下标]\u003C\u002Fcode>这种写法在\u003Ccode>calcu\u003C\u002Fcode>里新增了一个\u003Ccode>arrayGet\u003C\u002Fcode>分支，在\u003Ccode>assignStmt\u003C\u002Fcode>里新增了一个\u003Ccode>arrayElemAssign\u003C\u002Fcode>分支，分别对应\"读\"和\"写\"两种场景——这和之前\"定义用\u003Ccode>setArg\u003C\u002Fcode>，赋值用\u003Ccode>assignStmt\u003C\u002Fcode>\"是同一种思路的延续。\u003C\u002Fp>\n\u003Ch2 id=\"toc-3\">三、语法实现\u003C\u002Fh2>\n\u003Cp>先扩展一下\u003Ccode>VisitorResult\u003C\u002Fcode>，之前只支持数值、字符串、布尔三种类型，现在加上数组（内部用\u003Ccode>List&lt;Double&gt;\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\">private\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> VisitorResult\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(Object value) {\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\"> String\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            ||\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\"> Double\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            ||\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\">            ||\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\"> List\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            ||\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> value \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">        this\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">.value \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>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\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\"> 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:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">@\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">SuppressWarnings\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"unchecked\"\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:#24292E;--shiki-dark:#E1E4E8\"> List\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">Double\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">>\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> getArray\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\">isArray\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\"> (List\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">Double\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\">\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\">\"非Array类型\"\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:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> boolean\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> isArray\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:#005CC5;--shiki-dark:#79B8FF\"> this\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\"> List;\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**\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 定义数组类型变量，先求出数组字面量对应的List，再保存到当前作用域\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\">visitArrayarg\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.ArrayargContext ctx) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    VisitorResult array \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\">arrayLiteral\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    currentScope.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">define\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\">(), array.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getArray\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\">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\"> * 数组字面量，依次对每个元素求值后收集成一个List\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\">visitArrayLiteral\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.ArrayLiteralContext 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\">Double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">> values \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> ArrayList&#x3C;>();\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.CalcuContext element \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\">calcu\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">()) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        values.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">add\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\">(element).\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:#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\">(values);\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\"> * 按下标读取数组元素\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\">visitArrayGet\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.ArrayGetContext ctx) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    String name \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\">ID\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">().\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    List&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">> array \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> getArray\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(name);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    int\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> index \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:#6F42C1;--shiki-dark:#B392F0\">getNumber\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">().\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">intValue\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">    checkIndex\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(name, array, index);\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\">(array.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">get\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(index));\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\"> * len(数组)，返回数组长度\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\">visitArrayLen\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.ArrayLenContext 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\">Double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">> array \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> getArray\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">ID\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">().\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> 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\">double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) array.\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:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">private\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> List\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">Double\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">>\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> getArray\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(String name) {\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\"> currentScope.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">get\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(name);\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:#D73A49;--shiki-dark:#F97583\">!\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\"> List)) {\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\"> name);\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\"> (List\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">Double\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:#D73A49;--shiki-dark:#F97583\">private\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> checkIndex\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(String name, List\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">Double\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">>\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> array, \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">int\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> index) {\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\"> (index \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> ||\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> index \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">>=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> array.\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\">        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\"> name \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\"> index \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\"> array.\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:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>修改元素值的实现有一个值得说的细节：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">\u002F**\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 修改数组中某一个下标的值。数组本身是可变的List，被currentScope持有的是同一个引用，\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 这里直接对取出来的List调用set，不需要像标量变量那样再调用一次assign把新值写回作用域，\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\"> * 因为\"修改数组里某个元素\"和\"数组变量本身指向另一个对象\"是两回事。\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\">visitArrayElemAssign\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(RuleSetParser.ArrayElemAssignContext 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\">Double\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">> array \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> getArray\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.arr.\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\">    int\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> index \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.index).\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getNumber\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">().\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">intValue\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">    checkIndex\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.arr.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getText\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(), array, index);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    array.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">set\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(index, \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">visit\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(ctx.value).\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\">    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>number\u003C\u002Fcode>\u002F\u003Ccode>string\u003C\u002Fcode>\u002F\u003Ccode>boolean\u003C\u002Fcode>变量的赋值都要调用\u003Ccode>Scope.assign\u003C\u002Fcode>重新把新值\u003Ccode>put\u003C\u002Fcode>进作用域，是因为这些值本身是不可变的（Java的\u003Ccode>Double\u003C\u002Fcode>、\u003Ccode>String\u003C\u002Fcode>、\u003Ccode>Boolean\u003C\u002Fcode>都是不可变对象），\"改值\"唯一的办法就是\"换一个新对象塞回去\"。而数组用的是Java的\u003Ccode>ArrayList\u003C\u002Fcode>，本身是可变对象，\u003Ccode>Scope\u003C\u002Fcode>里保存的其实是这个\u003Ccode>List\u003C\u002Fcode>对象的引用。所以\u003Ccode>arr[1] = 99\u003C\u002Fcode>不需要调用\u003Ccode>assign\u003C\u002Fcode>把一个新的List重新塞回作用域，直接对拿到的这个List调用\u003Ccode>set\u003C\u002Fcode>修改其中一项即可——这也意味着，如果把同一个数组传给两处（目前的语言还没有支持把数组当函数参数传递，但设计上要提前想到这一点），两边修改的会是同一份数据，这是可变对象天然带来的\"共享\"特性，后续如果要支持数组作为函数参数，需要格外注意这一点。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>另外值得一提的是，\u003Ccode>print(arr)\u003C\u002Fcode>不需要任何额外改造就能直接支持打印数组——\u003Ccode>visitPrintArg\u003C\u002Fcode>本来就是拿到变量的原始值直接调用\u003Ccode>System.out.println\u003C\u002Fcode>，Java的\u003Ccode>List\u003C\u002Fcode>本身有现成的\u003Ccode>toString()\u003C\u002Fcode>实现（会打印成\u003Ccode>[10.0, 20.0, 30.0]\u003C\u002Fcode>这样的格式），正好够用。\u003C\u002Fp>\n\u003Ch2 id=\"toc-4\">四、测试代码与执行结果\u003C\u002Fh2>\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:#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\">        \"array arr = [10, 20, 30] \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(arr) \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 first = arr[0] \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(first) \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\">                \"arr[1] = 99 \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(arr) \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 i = 0 \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 sum = 0 \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\">                \"while (i &#x3C; len(arr)) { \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\">                \"    sum = sum + arr[i] \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\">                \"    i = i + 1 \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\">                \"print(sum) \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 bad = arr[10]\"\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:#6F42C1;--shiki-dark:#B392F0\">calcute\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(expression);\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>array arr = [10, 20, 30]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>print(arr)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>number first = arr[0]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>print(first)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>arr[1] = 99\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>print(arr)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>number i = 0\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>number sum = 0\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>while (i &#x3C; len(arr)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    sum = sum + arr[i]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    i = i + 1\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>print(sum)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>number bad = arr[10]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>[10.0, 20.0, 30.0]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>10.0\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>[10.0, 99.0, 30.0]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>139.0\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Exception in thread \"main\" java.lang.RuntimeException: 数组arr下标越界：10，数组长度为3\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\tat cn.irule.MyRuleSetVisitor.checkIndex(MyRuleSetVisitor.java:276)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\t......\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>依次验证：数组字面量定义并打印\u003Ccode>[10.0, 20.0, 30.0]\u003C\u002Fcode>；按下标读取\u003Ccode>arr[0]\u003C\u002Fcode>得到\u003Ccode>10.0\u003C\u002Fcode>；\u003Ccode>arr[1] = 99\u003C\u002Fcode>修改元素后再打印，变成\u003Ccode>[10.0, 99.0, 30.0]\u003C\u002Fcode>，说明修改是原地生效的；用\u003Ccode>while\u003C\u002Fcode>配合\u003Ccode>len(arr)\u003C\u002Fcode>遍历数组求和，\u003Ccode>10 + 99 + 30 = 139.0\u003C\u002Fcode>，结果正确；最后故意访问下标10（数组只有3个元素），程序抛出了预期的越界异常，符合边界检查的设计。\u003C\u002Fp>\n\u003Ch2 id=\"toc-5\">五、遗留的问题\u003C\u002Fh2>\n\u003Cp>目前数组的元素只能是数值，还不支持字符串数组或者混合类型数组，也还没有支持把数组当作参数传给函数（第⑦篇实现的函数目前只接受数值参数）。这些都是可以在数组这个基础上继续扩展的方向。下一篇文章我们计划把目光转向字符串相关的能力：字符串拼接、和数值之间的相互转换，以及内置函数的注册机制，让\u003Ccode>len(arr)\u003C\u002Fcode>这样的写法未来能够以一种更通用、可扩展的方式支持更多内置能力。\u003C\u002Fp>",[84,88,91,94,97],{"id":85,"text":86,"level":87},"toc-1","一、语法设计",2,{"id":89,"text":90,"level":87},"toc-2","二、语法定义",{"id":92,"text":93,"level":87},"toc-3","三、语法实现",{"id":95,"text":96,"level":87},"toc-4","四、测试代码与执行结果",{"id":98,"text":99,"level":87},"toc-5","五、遗留的问题"]