2

我正在为一种语言编写一个解析器,该语言对于Genlex+camlp4流解析器来说足够简单来处理它。但是,在解析错误的情况下,我仍然对拥有或多或少精确的位置(即至少一个行号)感兴趣。

char Stream我的想法是在 original和token Streamof之间使用中间流Genlex,它负责处理行数,就像下面的代码一样,但我想知道是否有更简单的解决方案?

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"
4

1 回答 1

2

一个更简单的解决方案是使用Camlp4 语法

以这种方式构建的解析器允许人们“免费”获得体面的错误消息,这与流解析器(这是一种低级工具)的情况不同。

可能不需要定义您自己的词法分析器,因为 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 是一个严重缺乏记录的野兽这一事实也可能有助于体验的超现实性。

因此,请不要犹豫,在邮件列表中提出问题(甚至是愚蠢的问题) 。

于 2012-10-30T19:25:34.463 回答