0
我有一個奇怪的字符串語法,其中 分隔符的含義取決於上下文。在下面的示例 輸入:取決於上下文的令牌含義
((foo) (bar))
結果是兩個串["foo"; "bar"]
的列表。 外部括號對進入列表模式。 然後,下一對括號分隔字符串。 在內部字符串中,平衡的括號對將被作爲字符串的一部分處理爲 。
現在詞法分析器根據全局變量inside
決定返回什麼取決於 。
{
open Sample_parser
exception Error of string
let inside = ref false (* <= to be eliminated *)
}
分隔符是括號。如果詞法分析器擊中 左括號,然後
- 如果
inside
是假,就會發出Enter
令牌和inside
設置爲真。 - 如果
inside
是真,它切換到一個字符串的詞法 它把任何適當嵌套對括號 作爲字符串的一部分。如果嵌套級別返回到 零,則字符串緩衝區將傳遞給解析器。
如果一個右括號是一個字符串外遇到 一個Leave
令牌被髮射和inside
未設置。
我的問題是:如何重寫詞法分析器沒有 全局變量inside
?
Fwiw我使用menhir,但afaict同樣適用於 ocamlyacc。 (很抱歉,如果這聽起來很困惑,我真的 的YACC /法方法一新手。 我可以表達所有上述不假思索的PEG,但我 還沒有習慣在精神上保持詞法和語法分析器 分離。 隨意的代碼指出的其他問題)
簡單的例子:* 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()
這確實和設計改進,並回答了這個問題。但是有沒有辦法徹底擺脫指針? –