如何在OCaml中将字符串解析为正则表达式类型

时间:2014-05-27 13:41:53

标签: regex ocaml

我们定义一个这样的正则表达式:

type regex_t =
    | Empty_String
    | Char of char
    | Union of regex_t * regex_t 
    | Concat of regex_t * regex_t 
    | Star of regex_t 

我们想写一个函数string_to_regex: string -> regex_t

  • Empty_string的唯一字符是' E'
  • Char的唯一字符是' a' ...' z'
  • ' |'适用于Union
  • ' *'适用于Star
  • 假设
  • Concat用于连续解析。
  • '(' /')'有最高的优先权,然后是明星,然后是concat,然后是工会

例如,

(a|E)*(a|b)

Concat(Star(Union(Char 'a',Empty_String)),Union(Char 'a',Char 'b'))

如何实施string_to_regex

2 个答案:

答案 0 :(得分:5)

Ocamllex和menhir是编写词法分析器和解析器的绝佳工具

<强> ast.mli

type regex_t =
| Empty
| Char of char
| Concat of regex_t * regex_t
| Choice of regex_t * regex_t
| Star of regex_t

<强> lexer.mll

{ open Parser }

rule token = parse
| ['a'-'z'] as c { CHAR c }
| 'E' { EMPTY }
| '*' { STAR }
| '|' { CHOICE }
| '(' { LPAR }
| ')' { RPAR }
| eof { EOF }

<强> parser.mly

%{ open Ast %}

%token <char> CHAR
%token EMPTY STAR CHOICE LPAR RPAR CONCAT
%token EOF

%nonassoc LPAR EMPTY CHAR

%left CHOICE
%left STAR
%left CONCAT

%start main
%type <Ast.regex_t> main

%%

main: r = regex EOF { r }

regex:
| EMPTY { Empty }
| c = CHAR { Char c }
| LPAR r = regex RPAR { r }
| a = regex CHOICE b = regex { Choice(a, b) }
| r = regex STAR { Star r }
| a = regex b = regex { Concat(a, b) } %prec CONCAT

<强> main.ml

open Ast

let rec format_regex = function
| Empty -> "Empty"
| Char c -> "Char " ^ String.make 1 c
| Concat(a, b) -> "Concat("^format_regex a^", "^format_regex b^")"
| Choice(a, b) -> "Choice("^format_regex a^", "^format_regex b^")"
| Star(a) -> "Star("^format_regex a^")"

let () =
  let s = read_line () in
  let r = Parser.main Lexer.token (Lexing.from_string s) in
  print_endline (format_regex r)

并编译

ocamllex lexer.mll
menhir parser.mly
ocamlc -c ast.mli
ocamlc -c parser.mli
ocamlc -c parser.ml
ocamlc -c lexer.ml
ocamlc -c main.ml
ocamlc -o regex parser.cmo lexer.cmo main.cmo

然后

$ ./regex
(a|E)*(a|b)
Concat(Star(Choice(Char a, Empty)), Choice(Char a, Char b))

答案 1 :(得分:0)

@Thomas 的评论很完整,但实际上运算符的优先级不正确:解析 a|aa 会导致 Concat(Choice(Char a, Char a), Char a),即等于 (a|a)aregular expression operators precedence 需要 a|aa = a|(aa),然后应该导致 Choice(Char a, Concat(Char a, Char a))。问题在于 CONCAT 令牌是一个 hack,即使您在 %left CHOICE 文件中的 %left CONCAT 之前指定 parser.mly,这并不意味着优先级将是尊重。一种可能的解决方案是执行 recursive descending parsing,有效地使语法无歧义。如果您想使用这种方法,您可以使用以下命令修改 parser.mly

%{ open Ast %}

%token <char> CHAR
%token EMPTY
%token STAR
%token CHOICE
%token LPAR
%token RPAR
%token EOF

%start main
%type <Ast.regex_t> main

%%

main: r = regex EOF { r }

regex:
| r = disjunction { r }

disjunction:
| a = disjunction CHOICE b = concat { Choice(a, b) }
| r = concat {r}

concat:
| a = concat b = repetition { Concat(a, b) } 
| r = repetition {r}

repetition:
| r = repetition STAR { Star r }
| r = atom { r }

atom:
| LPAR r = regex RPAR { r }
| c = CHAR { Char c }
| EMPTY { Empty }

这不会导致歧义(= 无需指定运算符关联性和优先级)并且会产生正确的结果。