You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Racket中识别并标记gosub语句后的行号令牌

解决Racket parser-tools/lex中gosub后数字识别为LINE-NUM的问题

问题核心

你需要让词法分析器区分普通数字和gosub语句后的行号数字,当前存在的问题:

  • 词法分析器无状态,无法识别"gosub"上下文
  • line-num正则定义错误(仅匹配单个1-9数字)
  • 关键字规则错误使用(:+),会匹配重复关键字(如"readread")
  • 普通数字规则优先级高于行号规则,导致gosub后的数字被识别为NUMBER

修改方案

利用parser-tools/lex的状态机功能,在识别到gosub后切换到专属状态,将后续数字识别为LINE-NUM,完成后切回初始状态,同时修正正则和规则优先级。

1. 修正正则缩写定义

调整define-lex-abbrevs,修复行号和关键字的正则匹配:

(define-lex-abbrevs
  [read       "read"]
  [write      "write"]
  [goto       "goto"]
  [gosub      "gosub"]
  [line-num   (:+ digit)]  ; 匹配一个或多个数字作为行号
  [letter     (:or (:/ "a" "z") (:/ #\A #\Z) "?" "!")]
  [digit      (:/ #\0 #\9)]
  [mult-op    (or "*" "/")]
  [add-op     (or "+" "-")]
  [end-of-file "$$"]
  [paren-start "("]
  [paren-end  ")"]
)

2. 带状态的词法分析器实现

通过状态切换实现上下文感知,完整修改后的lexer代码:

(define lex
  (lexer-src-pos
    ;; 所有状态通用规则
    [(eof) 'EOF]

    [(:+ end-of-file)
     (token-END-OF-PROGRAM (string->symbol lexeme))]

    [(:or #\tab #\space #\newline)
     (return-without-pos (lex input-port))]

    ["\r"]
    (token-newline)

    [(:or ":" ":=" "^" "<" ">" "=")]
    (string->symbol lexeme)

    [(:or "+" "-")]
    (token-ADD-OP (string->symbol lexeme))

    [(:or "*" "/")]
    (token-MULT-OP (string->symbol lexeme))

    [read]
    (token-READ lexeme)

    [write]
    (token-WRITE lexeme)

    [goto]
    (token-GOTO lexeme)

    ;; 初始状态规则
    [INITIAL
     [gosub
      (begin
        (token-GOSUB lexeme)
        (lexer-set-state! 'AFTER-GOSUB)  ; 切换到gosub后状态
        (return-without-pos (lex input-port)))]

     [(:+ digit)
      (token-NUMBER (string->number lexeme))]

     [(:+ letter)
      (token-IDENTIFIER lexeme)]

     [(:+ paren-start)
      (token-PAREN-START lexeme)]

     [(:+ paren-end)
      (token-PAREN-END lexeme)]]

    ;; gosub后的专属状态规则
    [AFTER-GOSUB
     [line-num
      (begin
        (token-LINE-NUM (string->number lexeme))  ; 行号转数值类型
        (lexer-set-state! 'INITIAL)  ; 切回初始状态
        (return-without-pos (lex input-port)))]

     ;; 若遇到非数字,切回初始状态重新扫描
     [any-char
      (begin
        (lexer-set-state! 'INITIAL)
        (return-without-pos (lex input-port)))]]
    ))

3. 效果验证

对于输入片段30 gosub 400,会输出:

  • token-GOSUB: "gosub"
  • token-line-num: 400

而普通数字(如40 if C = 400 then write C中的400)仍会被识别为NUMBER令牌,满足需求。

关键说明

  • 状态机作用:通过切换状态让分析器感知上下文,区分不同场景的数字
  • 规则优先级:parser-tools/lex按规则顺序匹配最长前缀,需将上下文相关规则放在普通规则之前
  • 数值转换:将LINE-NUM的字符串转为数值类型,方便后续语法分析使用

内容的提问来源于stack exchange,提问作者Flurrshify

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 04:55:12