6

我正在使用 ocamllex 为脚本语言编写词法分析器,但我正面临与我的评论规则的冲突。

我想让我的命令参数不被引用,只要它们只包含字母数字字符和斜杠“/”。例如:

echo "quoted argument !@#%" /this/second/argument/is/unquoted

此外,我的先决条件之一是带有“//”的 C++ 风格注释

//this is a comment
echo hello world

这带来的问题是

echo foo//comment

我希望我的词法分析器生成一个“foo”标记,同时保持“//”不变,以便在下次我向词法分析器请求标记时使用。那可能吗?这样做的原因是输入缓冲区可能尚未到达评论的末尾,我宁愿立即返回“foo”令牌,也不愿不必要地阻止试图急切地使用评论。

4

1 回答 1

8

以下是一个小型词法分析器,它只匹配echo、带引号和不带引号的字符串、注释,并打印出结果标记:

{
  type token = NEWLINE | ECHO | QUOTED of string | UNQUOTED of string | COMMENT of string
  exception Eof

  type state = CODE | LINE_COMMENT
  let state = ref CODE
}

let newline      = '\n'
let alphanum     = [ 'A'-'Z' 'a'-'z' '0'-'9' '_' ] 
let comment_line = "//"([^ '\n' ]+)
let space        = [ ' ' '\t' ]
let quoted       = '"'([^ '"' ]+)'"'
let unquoted     = ('/'?(alphanum+'/'?)+)

rule code = parse
  space+                      { code lexbuf }
| newline                     { code lexbuf }
| "echo"                      { ECHO }
| quoted                      { QUOTED (Lexing.lexeme lexbuf) }
| "//"                        { line_comment "" lexbuf }
| ('/'|alphanum+)             { unquoted (Lexing.lexeme lexbuf) lexbuf }
| eof                         { raise Eof }

and unquoted buff = parse
  newline                     { UNQUOTED buff }
| "//"                        { state := LINE_COMMENT; if buff = "" then line_comment "" lexbuf else UNQUOTED buff }
| ('/'|alphanum+)             { unquoted (buff ^ Lexing.lexeme lexbuf) lexbuf }
| space+                      { UNQUOTED buff }
| eof                         { raise Eof }

and line_comment buff = parse
  newline                     { state := CODE; COMMENT buff }
| _                           { line_comment (buff ^ Lexing.lexeme lexbuf) lexbuf }

{

  let lexer lb =
    match !state with
      CODE -> code lb
    | LINE_COMMENT -> line_comment "" lb

  let _ =
    try
      let lexbuf = Lexing.from_channel stdin in
      while true do
        let () =
          match lexer lexbuf with
            ECHO -> Printf.printf "ECHO\n"
          | QUOTED s -> Printf.printf "QUOTED(%s)\n" s
          | UNQUOTED s -> Printf.printf "UNQUOTED(%s)\n" s
          | COMMENT s -> Printf.printf "COMMENT(%s)\n" s
          | NEWLINE -> Printf.printf "\n"
        in flush stdout
      done
    with Eof -> exit 0

}

这是我在我的一个项目中使用的一个技巧,以克服 ocamllex 中的相同限制(与原始 C lex程序相比,它允许在“前瞻模式”中匹配模式)。基本上,它将模棱两可的规则拆分为不同的部首,并相应地将词法分析器切换到不同的解析器。它还跟踪当前使用的解析器和下一个入口点。

在您的情况下,它需要跟踪的唯一状态是默认状态 ( CODE) 和注释模式 ( LINE_COMMENT)。如果需要,这可以扩展到支持其他州。

于 2014-11-28T11:51:47.980 回答