我有一个奇怪的字符串语法,其中含义为a 分隔符取决于上下文。在以下示例中 输入:
( (foo) (bar) )
结果是两个字符串["foo"; "bar"]
的列表。
外部括号对进入列表模式。
然后,下一对括号分隔字符串。
在字符串内部,平衡的括号对将是
被视为字符串的一部分。
现在,词法分析员决定返回什么
在全局变量inside
上。
{
open Sample_parser
exception Error of string
let inside = ref false (* <= to be eliminated *)
}
分隔符是括号。如果词法分子击中了 开括号,然后
inside
false ,则会发出一个
Enter
令牌和inside
设置为 true 。inside
true ,则会切换为字符串词法分析器
它会处理任何正确嵌套的括号
作为字符串的一部分。如果嵌套级别返回
零,字符串缓冲区传递给解析器。如果在字符串外遇到右括号,
发出Leave
令牌,并且inside
未设置。
我的问题是:如何在没有的情况下重写词法分析器
全局变量inside
?
Fwiw我使用的是menhir但是afaict也是如此 ocamlyacc。 (对不起,如果这听起来很混乱,我真的是新手 yacc / lex方法。 我可以表达以上所有内容而不考虑PEG,但我 还没习惯精神上保持lexer和解析器 分离。 随意指出代码的其他问题!)
简单示例:* sample_lexer.mll *
{
open Sample_parser
exception Error of string
let inside = ref false (* <= to be eliminated *)
}
let lpar = "("
let rpar = ")"
let ws = [' ' '\t' '\n' '\r']
rule tokenize = parse
| ws { tokenize lexbuf }
| lpar { if not !inside then begin
inside := true;
Enter
end else begin
let buf = Buffer.create 20 in
String (string_scanner
(Lexing.lexeme_start lexbuf)
0
buf
lexbuf)
end }
| rpar { inside := false; Leave }
and string_scanner init depth buf = parse
| rpar { if depth = 0 then begin
Buffer.contents buf;
end else begin
Buffer.add_char buf ')';
string_scanner init (depth - 1) buf lexbuf end }
| lpar { Buffer.add_char buf '(';
string_scanner init (depth + 1) buf lexbuf }
| eof { raise (Error (Printf.sprintf
"Unexpected end of file inside string, pos %d--%d]!\n"
init
(Lexing.lexeme_start lexbuf))) }
| _ as chr { Buffer.add_char buf chr;
string_scanner init depth buf lexbuf }
* sample_scanner.mly *:
%token <string> String
%token Enter
%token Leave
%start <string list> process
%%
process:
| Enter lst = string_list Leave { lst }
string_list:
| elm = element lst = string_list { elm :: lst }
| elm = element { [elm] }
element:
| str = String { str }
main.ml :
open Batteries
let sample_input = "( (foo (bar) baz) (xyzzy) )"
(* EibssssssssssssseibssssseiL
* where E := enter inner
* L := leave inner
* i := ignore (whitespace)
* b := begin string
* e := end string
* s := part of string
*
* desired result: [ "foo (bar) baz"; "xyzzy" ] (type string list)
*)
let main () =
let buf = Lexing.from_string sample_input in
try
List.print
String.print stdout
(Sample_parser.process Sample_lexer.tokenize buf);
print_string "\n";
with
| Sample_lexer.Error msg -> Printf.eprintf "%s%!" msg
| Sample_parser.Error -> Printf.eprintf
"Invalid syntax at pos %d.\n%!"
(Lexing.lexeme_start buf)
let _ = main ()
答案 0 :(得分:3)
您可以将状态作为参数传递给tokenize
。它仍然必须是可变的,但不是全球性的。
rule tokenize inside = parse | ws { tokenize inside lexbuf } | lpar { if not !inside then begin inside := true; Enter end else begin let buf = Buffer.create 20 in String (string_scanner (Lexing.lexeme_start lexbuf) 0 buf lexbuf) end } | rpar { inside := false; Leave }
您可以按如下方式调用解析器:
Sample_parser.process (Sample_lexer.tokenize (ref false)) buf