GNU M4 ifelse分支未匹配仍解析参数引发eval报错问题求解
核心原因:M4 不存在短路求值,所有参数会先全量展开再执行函数逻辑
这是M4和大多数编程语言最核心的差异之一:
- 常见的C、Python等语言的if/else逻辑是短路的,只要匹配到第一个满足的分支,后续分支代码完全不会被解析执行
- 但M4的所有宏/内置函数,都会先把所有传入的参数全部展开求值完成后,才会执行函数自身的逻辑,
ifelse也不例外
针对第一个报错版本的拆解
你最初写的ifelse结构简化后如下:
ifelse( $#,2,`for($1,$2,1)', eval($1+$3>$2),1,$1, `$1, for(eval($1+$3),$2,$3)' )
当你调用for(3,9)(仅传2个参数)时,执行流程是:
- M4先展开
ifelse的所有参数:- 前两个参数展开为
2,2无异常 - 第四个参数(第二个匹配条件)是
eval($1+$3>$2),此时$3为空,直接展开为eval(3+>9),eval执行直接抛出语法错误
- 前两个参数展开为
- 参数全部展开完成后,
ifelse才开始做分支匹配,发现$#=2匹配第一个分支,返回正确的递归调用语句,所以最终输出结果符合预期,但参数展开阶段的报错已经被抛出了
解决方法:用引号延迟不需要提前展开的表达式
把需要延迟到分支匹配后再执行的表达式用反引号包裹,让它在参数展开阶段只被当做普通字符串,等到进入对应分支、引号被脱掉后再执行:
define(`for',`ifelse($#,2,`for($1,$2,1)',`eval($1+$3>$2)',1,$1,`$1, for(eval($1+$3),$2,$3)')')
上述修改仅给eval($1+$3>$2)加了一层反引号,就不会出现提前执行eval的报错。
你后续优化的版本也是利用了这个规则,对所有需要条件触发的求值逻辑都做了正确的引号嵌套处理,所以不会出现提前展开的错误。
内容的提问来源于stack exchange,提问作者NoComprende
相关产品推荐
相关产品推荐

