我正在为一种语言编写一个解析器,该语言对于 Genlex
+ camlp4
流解析器来说足够简单来处理它。然而,我仍然对有一个或多或少精确的位置(即至少一个行号)感兴趣,以防解析错误。
我的想法是在原始 char Stream
和 Genlex
的 token Stream
之间使用中间流,负责行计数,就像下面的代码一样,但我想知道是否有更简单的解决方案?
let parse_file s =
let num_lines = ref 1 in
let bol = ref 0 in
let print_pos fmt i =
(* Emacs-friendly location *)
Printf.fprintf fmt "File %S, line %d, characters %d-%d:"
s !num_lines (i - !bol) (i - !bol)
in
(* Normal stream *)
let chan =
try open_in s
with
Sys_error e -> Printf.eprintf "Cannot open %s: %s\n%!" s e; exit 1
in
let chrs = Stream.of_channel chan in
(* Capture newlines and move num_lines and bol accordingly *)
let next i =
try
match Stream.next chrs with
| '\n' -> bol := i; incr num_lines; Some '\n'
| c -> Some c
with Stream.Failure -> None
in
let chrs = Stream.from next in
(* Pass that to the Genlex's lexer *)
let toks = lexer chrs in
let error s =
Printf.eprintf "%a\n%s %a\n%!"
print_pos (Stream.count chrs) s print_top toks;
exit 1
in
try
parse toks
with
| Stream.Failure -> error "Failure"
| Stream.Error e -> error ("Error " ^ e)
| Parsing.Parse_error -> error "Unexpected symbol"
最佳答案
一个更简单的解决方案是使用 Camlp4 grammars .
以这种方式构建的解析器允许人们“免费”获得体面的错误消息,这与流解析器(这是一种低级工具)的情况不同。
可能不需要定义自己的词法分析器,因为 OCaml 的词法分析器已经满足您的需求。但如果您确实需要自己的词法分析器,那么您可以轻松插入自定义词法分析器:
module Camlp4Loc = Camlp4.Struct.Loc
module Lexer = MyLexer.Make(Camlp4Loc)
module Gram = Camlp4.Struct.Grammar.Static.Make(Lexer)
open Lexer
let entry = Gram.Entry.mk "entry"
EXTEND Gram
entry: [ [ ... ] ];
END
let parse str =
Gram.parse rule (Loc.mk file) (Stream.of_string str)
如果您是 OCaml 新手,那么所有这些模块系统技巧乍一看可能看起来像黑色巫毒魔法:-) Camlp4 是一个严重缺乏文档记录的野兽,这一事实也可能有助于体验超现实感。
因此,请毫不犹豫地在 mailing list 上提出问题(即使是愚蠢的问题)。 .
关于stream - 在 Genlex 中追踪位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13126914/