【问题标题】:How can I add state to a lexer in Racket?如何向 Racket 中的词法分析器添加状态?
【发布时间】:2019-08-02 07:56:01
【问题描述】:

我正在尝试为 Racket 中的 sed 语言编写词法分析器(例如“s/find/replace/”)。我遇到的一个问题是处理许多令牌没有确定的形式并且可以更改的事实。例如,我可以将上面的示例写为“ssfindsreplaces”,其中使用字母“s”而不是“/”。

我已经开始编写一个词法分析器,例如,

(define sed-lexer
  (lexer-srcloc
   ["\n" (token 'NEWLINE lexeme)]
   ["/" (token 'DIVIDER lexeme]
   [(:or "s" "y" "d" "p") (token 'CMD lexeme)]
   [(:* (complement "/") (token 'LITERAL lexeme)]))

但这在多个层面上都失败了:

  • 该命令只能出现在开头(在此简化示例中)。读取命令后,我想在换行之前忽略命令大小写。
  • DIVIDER 标记不能始终设置为斜线。

我可以想象这个问题的解决方案可能是向这个词法分析器添加状态。例如,词法分析器从“开始”状态开始寻找命令,然后进入“分隔符1”状态,寻找分隔符字符。 http://pygments.org/docs/lexerdevelopment/ 这里似乎存在这样的功能。考虑到 Racket 生态系统中的工具,解决此问题的最佳方法是什么?

【问题讨论】:

  • 您使用的是哪个词法分析器? github.com/racket/racket/wiki/…
  • 我试图让它与“br-parser-tools/lex”的词法分析器一起工作,但我愿意寻求其他解决方案。

标签: racket lexer


【解决方案1】:

词法分析器只是一个使用输入端口并返回令牌的函数。如果(br-)parser-tools/lex对你来说还不够,你可以自己写(应该不难)。

理论上,有限状态机和正则表达式的表达能力是一样的,所以我想你其实可以用parser-tools/lex来完成你想要的。由于您需要在所有可能的“分隔符”上拆分案例(因为纯正则表达式没有反向引用能力),它看起来真的很乏味。我认为你提到的那些 pygments 会有类似的问题。

另一种可能性是使用比正则表达式更强大的东西。由于 sed 语法非常简单,您甚至可以立即解析它而无需先进行词法分析。这是我使用解析器组合库 megaparsack 快速编写的糟糕版本

#lang racket

(require megaparsack megaparsack/text
         data/monad data/applicative)

(struct substitution (search replace flags) #:transparent)

(define substitution/p (do (char/p #\s)
                           [divider <- any-char/p]
                           [search <- (many/p (char-not/p divider))]
                           (char/p divider)
                           [replace <- (many/p (char-not/p divider))]
                           (char/p divider)
                           [flags <- (many/p (char-in/p "gIp"))]
                           (pure (substitution search replace flags))))

(define dummy-command/p (string/p "dummy-command"))

(define line/p (or/p substitution/p
                     dummy-command/p))

(define program/p (do [result <- (many/p line/p #:sep (char/p #\newline))]
                      eof/p
                      (pure result)))

(pretty-print
 (parse-result!
  (parse-string program/p
                "s/hello/world/\ndummy-command\ns|search|replace|gp")))

#|

Result:

(list
 (substitution '(#\h #\e #\l #\l #\o) '(#\w #\o #\r #\l #\d) '())
 "dummy-command"
 (substitution
  '(#\s #\e #\a #\r #\c #\h)
  '(#\r #\e #\p #\l #\a #\c #\e)
  '(#\g #\p)))

|#

【讨论】:

  • 谢谢!这真的很有帮助。很高兴看到这个我不知道的问题的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-29
  • 1970-01-01
  • 2021-11-13
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多