Bison代码无法正确打印数组值问题求助
解决Bison数组语法仅打印第一个元素的问题
你的问题主要出在两个核心点:语法规则没处理元素间的逗号分隔,以及没有正确聚合array_vals的语义值,导致只能拿到第一个元素。下面一步步帮你修正:
1. 先补全语法:处理逗号分隔
你的输入是[10, 20, 30, 40],但原语法完全没匹配元素间的逗号——这会直接导致解析错误(除非你的Lexer把逗号当成空白符忽略了,但这不符合常规数组语法)。首先给逗号声明token,再修改array_vals规则:
%token ',' // 把逗号声明为合法token
然后调整array_vals,支持空数组、单个元素、逗号分隔的多个元素:
array_vals: /* 空数组,匹配[]的情况 */ | vals // 单个元素,比如[10] | array_vals ',' vals // 多个元素,逗号分隔,比如[10,20]
2. 声明语义类型,实现元素累积
原代码没给非终结符指定语义类型,Bison默认无法把多个元素的值攒起来。我们用std::vector存储数组元素,用std::variant兼容int、float、string三种值类型,先在Bison定义段添加必要的代码:
%{ #include <iostream> #include <vector> #include <variant> #include <string> // 定义能容纳三种类型值的变体 using Value = std::variant<int, float, std::string>; // 用vector存储整个数组的元素列表 using ValueList = std::vector<Value>; %} // 定义语义值联合体,绑定终结符和非终结符的类型 %union { int ival; float fval; std::string* sval; ValueList* list; Value* val; } // 给终结符绑定对应的语义类型 %token <ival> INT %token <fval> FLOAT %token <sval> STRING %token ',' // 给非终结符绑定语义类型 %type <list> array_vals %type <val> vals
3. 给规则加语义动作,累积所有元素
先修改vals规则,把每个token的值包装成Value对象:
vals: INT { $$ = new Value($1); } | FLOAT { $$ = new Value($1); } | STRING { $$ = new Value(*$1); delete $1; } // 注意Lexer中STRING是new出来的,要释放内存
再修改array_vals规则,用vector把所有元素攒起来(推荐左递归,Bison处理左递归效率更高):
array_vals: /* 空数组 */ { $$ = new ValueList(); } | vals { $$ = new ValueList(); $$->push_back(*$1); delete $1; } | array_vals ',' vals { $1->push_back(*$3); $$ = $1; delete $3; }
4. 修改array规则,打印全部元素
现在$2是存储了所有元素的ValueList*,我们可以遍历它打印每个值:
array: '[' array_vals ']' { std::cout << "数组元素:"; // 用std::visit访问variant里的不同类型值 for (const auto& val : *$2) { std::visit([](auto&& arg) { std::cout << arg << " "; }, val); } std::cout << std::endl; delete $2; // 释放内存 }
关键问题解释
- 原代码里
array_vals没有语义值聚合逻辑,Bison只会保留第一个vals的语义值,后续递归的元素直接被丢弃了。 - 必须显式声明语义类型,让Bison知道如何传递和累积多个元素的值。
- 逗号是数组语法的必要部分,必须在规则中匹配,否则根本无法正确解析带逗号的输入。
如果不想手动管理内存,可以用std::unique_ptr代替裸指针,比如把ValueList*改成std::unique_ptr<ValueList>,这样就不用手动delete了。
内容的提问来源于stack exchange,提问作者user9362751
相关产品推荐
相关产品推荐

