Racket LL1解析器代码报错:application: not a procedure,求排查
问题排查与修复
1. 直接触发错误的原因:测试用例列表格式错误
你在run-tests的调用中,没有将所有测试用例包裹在一个单个引号列表里。当前写法中,'("1" #t)是一个列表,但后面的("1+1" #t)会被Racket解析为尝试调用字符串"1+1"作为过程,这就导致了application: not a procedure错误。
修正后的测试用例写法
(run-tests '(("1" #t) ("1+1" #t) ("1 / 3" #t) (" √10 " #t) ("579438593 + 666 - 555" #t) ("1000" #t) ("2^64" #t) ("√2 + √64 * 233 + 6" #t) ("" #f) ("aaa" #f) ("/44" #f) ("579438593 +" #f) ("1000 * + 111" #f) ("2 + 6 4" #f) ("3.14 + 7" #f) ("√√2" #f)))
注意所有测试二元组都被包含在'(...)内部,形成一个嵌套列表。
2. 解析器逻辑的其他问题
除了测试用例的错误,你的LL(1)解析器还有几个逻辑缺陷,会导致部分合法表达式被错误判定为无效:
问题1:√的字符匹配错误
你在parse-S和parse-A中使用(equal? (car exp) "√"),但exp是通过string->list生成的字符列表,car exp是单个字符,而"√"是字符串,两者永远不相等。应该改为字符匹配:(char=? (car exp) #\√)。
问题2:未处理多位数数字
当前parse-S仅检查第一个字符是数字,就直接调用parse-A,但后续的数字字符会被parse-A判定为非法(因为parse-A只识别运算符)。需要在parse-S中跳过所有连续的数字字符,再进入parse-A。
问题3:parse-tokens的循环逻辑不完善
原loop函数仅检查开头是否能被parse-S匹配,但未确保parse-S处理完整个表达式后没有剩余非法字符。需要调整为:当parse-S匹配成功后,检查剩余字符是否都是空白或结束。
修正后的完整代码
#lang racket (define (is-number? str) (let ((maybe-number (string->number str))) (and maybe-number #t))) (define (parse-expression expr) (define (parse-tokens expr) (define (skip-digits exp) (cond [(empty? exp) exp] [(char-numeric? (car exp)) (skip-digits (cdr exp))] [else exp])) (define (parse-S exp) (cond [(empty? exp) #f] [(char-numeric? (car exp)) (parse-A (skip-digits exp))] [(char=? (car exp) #\√) (if (and (not (empty? (cdr exp))) (char-numeric? (cadr exp))) (parse-A (skip-digits (cdr exp))) #f)] [else #f])) (define (parse-A exp) (cond [(empty? exp) #t] [(char-whitespace? (car exp)) (parse-A (cdr exp))] [(member (car exp) '(#\+ #\- #\* #\/ #\^)) (let ([rest (cdr exp)]) (if (empty? rest) #f (parse-S rest)))] [(char=? (car exp) #\√) (if (and (not (empty? (cdr exp))) (char-numeric? (cadr exp))) (parse-A (skip-digits (cdr exp))) #f)] [else #f])) (let loop ([expr expr]) (cond [(empty? expr) #t] [(char-whitespace? (car expr)) (loop (cdr expr))] [else (parse-S expr)])) (parse-tokens (string->list expr))) (define (analyze-expression expr) (parse-expression expr)) (define (test-expressions) (define (run-tests tests) (for ([test tests]) (define result (analyze-expression (first test))) (define expected (second test)) (printf "Expression: ~a, Expected: ~a, Result: ~a -> ~a\n" (first test) expected result (equal? result expected)))) (run-tests '(("1" #t) ("1+1" #t) ("1 / 3" #t) (" √10 " #t) ("579438593 + 666 - 555" #t) ("1000" #t) ("2^64" #t) ("√2 + √64 * 233 + 6" #t) ("" #f) ("aaa" #f) ("/44" #f) ("579438593 +" #f) ("1000 * + 111" #f) ("2 + 6 4" #f) ("3.14 + 7" #f) ("√√2" #f)))) (test-expressions)
关键修改说明
- 添加
skip-digits函数:用于跳过连续的数字字符,支持多位数解析。 - 修正
√的匹配逻辑:使用char=?匹配字符,同时检查√后是否有合法数字。 - 完善
parse-A的空白处理:允许运算符前后有空白字符。 - 优化测试输出:增加预期结果对比,方便验证正确性。
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

