Syntax OCaml保护值后的语法

Syntax OCaml保护值后的语法,syntax,ocaml,camlp4,Syntax,Ocaml,Camlp4,我不太理解这里使用的语法: let rec lex = parser (* Skip any whitespace. *) | [< ' (' ' | '\n' | '\r' | '\t'); stream >] -> lex stream let rec lex=parser (*跳过任何空格。*) |[Token.Token Stream.t= val lex_编号:Buffer.t->char Stream.t->Token.Token Stream.t= va

我不太理解这里使用的语法:

let rec lex = parser
  (* Skip any whitespace. *)
  | [< ' (' ' | '\n' | '\r' | '\t'); stream >] -> lex stream
let rec lex=parser
(*跳过任何空格。*)
|[<'(''|'\n'|'\r'|'\t');流>]->lex流
首先,我不明白在
解析器之后使用保护(垂直线)意味着什么。
其次,我似乎找不到
[]


我从你那里得到了密码。提前谢谢

正如@glennsl所说,该页面使用了campl4预处理器,OCaml社区的许多人认为它已经过时了

以下是2019年8月发布的论坛信息,介绍了如何从camlp4转移到最近的ppx:

不幸的是,这并不能真正帮助您了解LLVM页面试图教给您的内容,这似乎与OCaml没有什么关系

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

(另一方面,OCaml确实是编写编译器和其他语言工具的一种极好的语言。)

表示:“或”(流是否匹配此字符或此字符或…?)

现在,您可以开始运行streams和LL(1)流解析器了。 你提到的方法很有效。如果您在顶层玩,您可以使用#use指令评估token.ml和lexer.ml文件,以尊重模块名称(#use“token.ml”)。或者,如果将类型标记嵌套在模块标记中,则可以直接计算lexer.ml的表达式

# 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 ')'
#让rec lex=parser(*完整定义*)
val lex:char Stream.t->Token.Token Stream.t=
val lex_编号:Buffer.t->char Stream.t->Token.Token Stream.t=
val lex_ident:Buffer.t->char Stream.t->Token.Token Stream.t=
val lex_注释:char Stream.t->Token.Token Stream.t=
#让pgm=
“def fib(x)\
如果x<3,则\
1 \
否则\
fib(x-1)+fib(x-2)”;;
val pgm:string=“def fib(x)如果x<3那么1其他fib(x-1)+fib(x-2)”
#设cs'=lex(字符串pgm的流);;
val cs':Token.Token Stream.t=
#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')'
您将获得token类型的预期流

现在就camlp4和camlp5讲几句技术性的话

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

#require "camlp5";;  (* add the path + loads the module (topfind directive) *)
#load "camlp5o.cma";;(* patch: manually loads camlp50 module, 
                        because #require forgets to do it (why?) 
                        "o" in "camlp5o" stands for "original syntax" *)

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

# val lex : char Stream.t -> 'a = <fun>
#需要“camlp5”;;(*添加路径+加载模块(topfind指令)*)
#加载“camlp5o.cma”;;(*补丁:手动加载camlp50模块,
因为需要忘记去做(为什么?)
“camlp5o”中的“o”代表“原始语法”*)
让rec lex=parser
|[<'(''|'\n'|'\r'|'\t');流>]->lex流
| [< >] -> [< >]
#val lex:char Stream.t->'a=
有关camlp4和camlp5的更多历史记录。

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

因此,今天所谓的“camlp4”实际上是“旧campl4”(或“过去的新camlp4”;我知道,它很复杂)。 LALR(1)解析器(如ocamlyacc或menhir)已经成为主流。它们采用自底向上的方法(定义.mll和.mly,然后编译成OCaml代码)。 LL(1)解析器,如camlp4/camlp5,采用自顶向下的方法,非常接近函数式。 最好是你自己比较。实现语言的lexer/parser非常适合于此:使用ocamlex/menhir和ocamlex/camlp5,甚至只使用camlp5,因为它也是一个lexer(有优点/缺点)

我希望你会喜欢你的LLVM教程


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

如页面所述,它使用camlp4,这是一种古老的语法预处理器。所以这个
#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>
# 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 ')'
#require "camlp5";;  (* add the path + loads the module (topfind directive) *)
#load "camlp5o.cma";;(* patch: manually loads camlp50 module, 
                        because #require forgets to do it (why?) 
                        "o" in "camlp5o" stands for "original syntax" *)

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

# val lex : char Stream.t -> 'a = <fun>