OCaml保留值后的语法

问题描述 投票:0回答:2

我不太理解此处使用的语法:

let rec lex = parser
  (* Skip any whitespace. *)
  | [< ' (' ' | '\n' | '\r' | '\t'); stream >] -> lex stream

首先,我不明白使用后跟parser的保护线(垂直线)的含义。其次,我似乎找不到与[<>]

包围的条件相关的语法

here获得代码。预先感谢!

syntax ocaml camlp4
2个回答
0
投票

正如@glennsl所说,此页面使用了campl4预处理器,OCaml社区中的许多人都认为过时了。

这里是2019年8月的论坛消息,描述了如何从camlp4转移到最新的ppx:

The end of campl4

[不幸的是,这并不能真正帮助您了解LLVM页面试图教给您的内容,似乎与OCaml无关。

这是我发现使用语法扩展名是有问题的原因之一。它们没有基本语言的持久力。

(另一方面,OCaml确实是用于编写编译器和其他语言工具的出色语言。)


1
投票
| 

意味着:“或”(流是否匹配此char或此char或...?)

| [< ' (' ' | '\n' | '\r' | '\t'); stream >] -> lex stream

表示:

  • 如果流(在此子句中为一个字符,但可以是几个字符)匹配“空格”或“换行”或“回车”,或“列表”。
  • [消耗(“ white”)匹配字符并使用其余部分。
  • ELSE使用next子句(在您的示例中:“将A过滤为Z,将a过滤为z个字符”作为标识符)。因为已消耗匹配的字符通过本条,

(顺便说一句,添加'\ n \ r',即“换行符+回车”会更好地解决这个历史案例;您可以作为练习来做)。

为了能够使用这种语法在OCaml中解析流,您需要OCaml stdlib中的模块(至少是Stream和Buffer),并且需要知道关键字parser,[ C0],等等。在顶层,您可以执行以下操作:

[<'

现在您可以开始处理流和LL(1)流解析器。您提到的#use "topfind";; (* useless if already in your ~/.ocamlinit file *) #camlp4o;; (* Topfind directive to load camlp4o in the Toplevel *) # let st = Stream.of_string "OCaml" val st : char Stream.t = <abstr> # Stream.next st - : char = 'O' # Stream.next flux_car - : char = 'C' (* btw, Exception: Stdlib.Stream.Failure must be handled(empty stream) *) # let rec lex = parser | [< ' (' ' | '\n' | '\r' | '\t'); stream >] -> lex stream | [< >] -> [< >] (* just the beginning of the parser definition *) # val lex : char Stream.t -> 'a = <fun> 效果很好。如果您在顶级游戏中玩游戏,则可以使用#use指令评估token.ml和lexer.ml文件,以遵守模块名称(#use“ token.ml”)。或者,如果您将类型令牌嵌套在模块令牌中,则可以直接评估lexer.ml的表达式。

exammple

您将获得预期的令牌类型流。

现在关于camlp4和camlp5的一些技术词汇

实际上,建议不要使用不建议使用的所谓“ camlp4”,而应使用实际上是“真正的camlp4”的“ camlp5”(请参见下文)。假设您要使用LL(1)解析器。为此,可以使用以下camlp5 Toplevel指令代替camlp4一个:

# let rec lex = parser (* complete definition *)
val lex : char Stream.t -> Token.token Stream.t = <fun>
val lex_number : Buffer.t -> char Stream.t -> Token.token Stream.t = <fun>
val lex_ident : Buffer.t -> char Stream.t -> Token.token Stream.t = <fun>
val lex_comment : char Stream.t -> Token.token Stream.t = <fun>

# let pgm =
  "def fib(x) \
     if x < 3 then \
    1 \
  else \
    fib(x-1)+fib(x-2)";;
val pgm : string = "def fib(x) if x < 3 then 1 else fib(x-1)+fib(x-2)"
# let cs' = lex (Stream.of_string pgm);;
val cs' : Token.token Stream.t = <abstr>
# Stream.next cs';;
- : Token.token = Token.Def
# Stream.next cs';;
- : Token.token = Token.Ident "fib"
# Stream.next cs';;
- : Token.token = Token.Kwd '('
# Stream.next cs';;
- : Token.token = Token.Ident "x"
# Stream.next cs';;
- : Token.token = Token.Kwd ')'

有关camlp4和camlp5的更多历史记录。

免责声明:虽然我尽力保持中立和真实,但这种简短的解释也可能反映了我的个人观点。当然,欢迎进行讨论。作为Ocaml的初学者,我发现camlp4非常吸引人并且功能强大,但是要区分出确切的camlp4并查找其最新文档并不容易。简单来说:这是一个古老而混乱的故事,主要是因为“ camlp4”的命名。 campl4是OCaml的/历史语法扩展系统。有人决定在2006年左右对camlp4进行改进/改装,但似乎有些设计决策将它以某种方式被某些人认为是“野兽”(通常,少即是多)。因此,它起作用了,但是“引擎盖下有很多东西”(它的签名变得非常大)。他的历史作家Daniel de Rauglaudre决定继续以自己的方式开发camlp4,并将其重命名为“ campl5”,以区别于“新camlp4”(命名为camlp4)。即使没有大量使用camlp5,例如,它仍由最近集成了campl5一部分的coq维护,操作和使用,而不是依赖于整个camlp5库(这并不意味着“ coq不会如您所见,请使用camlp5”。ppx已成为OCaml世界中的主流语法扩展技术(似乎专用于进行“有限且可靠的” OCaml语法扩展,主要用于小型且非常有用的代码生成(辅助函数等);这是附带讨论) 。这并不意味着camlp5已被“弃用”。 camlp5当然是被误解了。一开始我很辛苦,主要是因为它的文档。希望我可以在那时阅读这篇文章!无论如何,在OCaml中进行编程时,我相信探索各种技术是一件好事。您可以发表自己的意见。

因此,今天所谓的“ camlp4”实际上是“旧的campl4”(或“过去的新camlp4”;我知道,这很复杂)。LALR(1)解析器(例如ocamlyacc或menhir)已成为或已成为主流。他们有一个自下而上的方法(定义.mll和.mly,然后编译为OCaml代码)。LL(1)解析器(例如camlp4 / camlp5)具有自顶向下的方法,非常接近功能样式。最好的事情是您自己比较一下。为此,实现语言的词法分析器/解析器是完美的:使用ocamllex / menhir和ocamllex / camlp5,甚至只用camlp5,因为它也是一个词法分析器(具有优点/缺点)。

希望您会喜欢LLVM教程。

非常欢迎所有技术和历史补充评论。

© www.soinside.com 2019 - 2024. All rights reserved.