Regex 如何在OCaml中将字符串解析为正则表达式类型
我们定义的正则表达式类型如下:Regex 如何在OCaml中将字符串解析为正则表达式类型,regex,ocaml,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\u到\u regex:string->regex\u t 空字符串的唯一字符是“E” Char的唯一字符是'a'..'z' “|”表示Union
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\u到\u regex:string->regex\u t
空字符串的唯一字符是“E”
的唯一字符是'a'..'z'Char
- “|”表示
Union
- “*”表示
Star
假设用于连续解析Concat
- “(“/”)的优先级最高,然后是星,然后是concat,然后是union
(a | E)*(a | b)
将
Concat(Star(Union(Char 'a',Empty_String)),Union(Char 'a',Char 'b'))
如何实现
string\u to_regex
ocamlex和menhir是编写lexer和解析器的极好工具
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 }
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)
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
并进行编译
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))
ocamlex和menhir是编写lexer和解析器的极好工具 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 }
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)
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
并进行编译
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))
@Thomas的注释非常完整,但实际上运算符的优先级不正确:解析
a | aa
将导致Concat(Choice(Char a,Char a,Char a)
,即等于(a | a)a
。要求a | aa=a |(aa)
,这将导致选择(Char a,Concat(Char a,Char a))
。问题是,CONCAT
标记是一种黑客行为,即使在parser.mly
文件中的%left-CHOICE
之前指定了%left-CHOICE
,这并不意味着优先权将得到尊重。一个可能的解决方案是执行,有效地使语法明确无误。如果要使用此方法,可以使用以下内容修改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 }
%{open Ast%}
%令牌字符
%令牌为空
%象征之星
%代币选择
%令牌LPAR
%令牌RPAR
%令牌EOF
%起动总管
%类型主
%%
main:r=regex EOF{r}
正则表达式:
|r=析取{r}
析取:
|a=析取选择b=连接{选择(a,b)}
|r=concat{r}
康卡特:
|a=concat b=repeation{concat(a,b)}
|r=重复{r}
重复:
|r=重复星{starr}
|r=原子{r}
原子:
|LPAR r=regex RPAR{r}
|c=CHAR{CHAR c}
|空的{EMPTY}
这不会导致歧义(=不需要指定运算符的关联性和优先级),并将产生正确的结果。Thomas的注释非常完整,但实际上运算符的优先级不正确:解析
a | aa
将导致Concat(Choice(Char a,Char a),Char a)
,即等于(a | a)a
。要求a | aa=a |(aa)
,这将导致选择(Char a,Concat(Char a,Char a))
。问题是,CONCAT
标记是一种黑客行为,即使在parser.mly
文件中的%left-CHOICE
之前指定了%left-CHOICE
,这并不意味着优先权将得到尊重。一个可能的解决方案是执行,有效地使语法明确无误。如果要使用此方法,可以使用以下内容修改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 }
%{open Ast%}
%令牌字符
%令牌为空
%象征之星
%代币选择
%令牌LPAR
%令牌RPAR
%令牌EOF
%起动总管
%类型主
%%
main:r=regex EOF{r}
正则表达式:
|r=析取{r}
析取:
|a=析取选择b=连接{选择(a,b)}
|r=concat{r}
康卡特:
|a=concat b=repeation{concat(a,b)}
|r=重复{r}
重复:
|r=重复星{starr}
|r=原子{r}
原子:
|LPAR r=regex RPAR{r}
|c=CHAR{CHAR c}
|空的{EMPTY}
这不会导致歧义(=不需要指定运算符的关联性和优先级),并将产生正确的结果。这只是一个解析问题,对吗?我想说gasche最近给你写了一些优秀的代码,你可以修改。@JeffreyScofield是的,我试着从他的代码中学习,但我不认为我能理解或修改。这个正则表达式解析比sexp解析更复杂。我不认为我真的明白,盖什的解决方案是。这很容易掌握。从写一点语法开始。@JeffreyScofield但是这个正则表达式解析涉及操作符,它适合递归下降解析吗?你可以使用递归下降为一种严肃的语言构建一个编译器。(第一个Pascal编译器就是这样编写的。)是的,它将适用于您的RE语言。这只是一个解析问题,对吗?我想说gasche最近给你写了一些优秀的代码,你可以修改。@JeffreyScofield是的,我试着从他的代码中学习,但我不认为我能理解或修改。这个正则表达式解析比sexp解析更复杂。我不认为我真的明白,盖什的解决方案是。这很容易掌握。从写一点语法开始。@JeffreyScofield但是这个正则表达式解析涉及操作符,它适合递归下降解析吗?你可以使用递归下降为一种严肃的语言构建一个编译器。(第一个Pascal编译器是这样编写的。)是的,它将适用于您的RE语言。@JacksonTale(OP)我不确定您真正想要的是什么,但很难打败这个解决方案!请注意,即使您的令牌都是1个字符长的,仍然需要进行非平凡的词法分析。也就是说,你需要对它们进行分类。@JeffreyScofield我实际上是在寻找一种将字符串解析为正则表达式类型的原始方法,而不是使用Ocamlex和menhirI。我不得不说,我不明白这一点,但你可以使用递归下降法解决几乎任何解析问题。@JeffreyScofield我在做这个练习:你可以使用相同的想法。也就是说,一个lexer可以处理输入流的“标记”,然后将其处理成正则表达式语言,就像
parser.mly
一样。所以你有,input\u channel->token list
,和token list->regexp
。你甚至可以很容易地使通道处理变得懒惰。@JacksonTale(OP)我不是