基于给定文法实现S表达式校验:如何处理{<S-exp>}的重复匹配规则
S表达式校验代码修正方案
现有代码核心问题
- 解析位置传递错误:原代码中
Sexp函数的索引参数i采用值传递,递归匹配完成后无法同步实际解析进度,即使内部匹配成功,外层函数的i位置不会更新,导致后续token判断完全错位 - 重复匹配逻辑缺失:未实现文法中
{<S-exp>}要求的零次或多次连续匹配逻辑,匹配完左括号后的首个S表达式后直接跳转判断右括号/点号,无法校验中间多个连续S表达式的合法性 - 全局状态冗余:定义全局
bool check存储匹配状态,会被多层递归调用反复覆盖,引发不可预期的逻辑冲突 - 原子匹配未同步位置:原代码判断为合法原子后直接返回true,未将
i前移到原子之后的位置,导致后续判断逻辑完全错误 - 右括号判断不统一:左括号判断使用
token.type匹配,右括号判断却直接比对token.data,和预先定义的TokenList类型体系不匹配 - 缺少边界保护:访问
token[i]前未判断索引是否越界,输入不合法时会直接崩溃
修正实现逻辑
采用递归下降解析的标准设计:将位置索引i改为引用传递,每次成功匹配一个语法单元就同步更新i的位置,通过循环实现{<S-exp>}对应的零次或多次匹配。
核心循环逻辑说明:在匹配完左括号后的首个必填S表达式后,只要未遇到右括号、点号或token列表末尾,就持续尝试匹配后续S表达式——若首次进入循环即遇到终止条件,循环零次执行,完全符合文法的“零次匹配”要求;若连续匹配多个S表达式,循环多次执行,符合“多次匹配”要求
修正后完整代码
// 原子合法性判断,增加越界保护 bool Atom(const vector<TokenList>& token, int i) { if (i >= token.size()) return false; return (token[i].type == "SYMBOL" || token[i].type == "INT" || token[i].type == "FLOAT" || token[i].type == "STRING" || token[i].type == "NIL" || token[i].type == "T" || token[i].type == "LEFT_PAREN" || token[i].type == "RIGHT_PAREN"); } // 移除全局check变量,位置i改为引用传递 bool Sexp(const vector<TokenList>& token, int& i) { // 基础越界判断 if (i >= token.size()) return false; if (token[i].type == "LEFT_PAREN") { i++; // 消费左括号token // 匹配文法要求的左括号后第一个必填S表达式 if (!Sexp(token, i)) { return false; } // 实现{<S-exp>}的零次或多次连续匹配 // 循环终止条件:到token末尾、遇到右括号、遇到点号 while (i < token.size() && token[i].type != "RIGHT_PAREN" && token[i].type != "DOT") { if (!Sexp(token, i)) { return false; } } // 处理可选的点号分支 if (i < token.size() && token[i].type == "DOT") { i++; // 消费点号token if (!Sexp(token, i)) { return false; } } // 匹配右括号 if (i < token.size() && token[i].type == "RIGHT_PAREN") { i++; // 消费右括号token return true; } else { return false; } } else if (token[i].type == "QUOTE") { i++; // 消费QUOTE token return Sexp(token, i); } else if (Atom(token, i)) { i++; // 消费原子token return true; } else return false; } // 入口校验:确保所有token被完全消费 bool ValidateWholeSexp(const vector<TokenList>& token) { int i = 0; bool matchSuccess = Sexp(token, i); return matchSuccess && (i == token.size()); }
校验注意事项
- 调用校验时需要通过
ValidateWholeSexp入口函数确认所有token都被消费完成,避免出现“部分匹配成功但存在多余非法token”的漏判 - 逻辑完全严格匹配给定的文法定义,未对原有文法规则做额外调整(例如保留了LEFT_PAREN、RIGHT_PAREN作为合法原子的规则)
内容的提问来源于stack exchange,提问作者Kias
相关产品推荐
相关产品推荐

