基于令牌的内容在解析器级别进行分支



我正在为一个小项目开发一个简单的解析器/lexer示例,但遇到了一个问题。

我正在沿着以下几条线分析内容:

Name SEP Gender SEP Birthday
Name SEP Gender SEP Birthday

…其中SEP|,或空白中的任意一个(但不是多个!(。

现在,我不想把字段顺序锁定在lexer顺序,所以我试图用一组非常简单的令牌来lex这个:

%token <string> SEP
%token <string> VAL
%token NL
%token EOF

现在,如果gender字段不包含一小组每个确定的值,比如{male,female,neither,unspecified},那么我会产生一个解析错误。我可以包装解析器并处理它,但我真的想把这个需求编码到自动机中,以便将来扩展。

我的第一次尝试,看起来像这样,失败得很可怕:

doc:
| EOF              { [] }
| it = rev_records { it }
;
rev_records:
| (* base-case: empty *) { [] }
| rest = rev_records; record; NL  { record :: rest }
| rest = rev_records; record; EOF { record :: rest }
;
record:
last_name = name_field; SEP; first_name = name_field; SEP;
gender = gender_field; SEP; favourite_colour = colour_field; SEP;
birthday = date_field
{ {last_name; first_name; gender; favourite_colour; birthday} }
name_field: str = VAL { str }
gender_field:
| VAL "male" { Person.Male }
| VAL "female" { Person.Female }
| VAL "neither" { Person.Neither }
| VAL "unspecified" { Person.Unspecified }
;

是的,没有骰子。显然,我对非结构化词汇的尝试已经很糟糕了。

解析这样的东西的惯用方法是什么?

解析器,如Menhir和OCamlYacc,对令牌进行操作,而不是对字符串或字符进行操作。从字符到令牌的转换是在lexer级别上进行的。这就是为什么不能在生成规则中指定字符串的原因。

当然,您可以在语义操作中执行任何检查并引发异常,例如

record:
last_name = name_field; SEP; first_name = name_field; SEP;
gender_val = VAL; SEP; favourite_colour = colour_field; SEP;
birthday = date_field
{ 
let gender = match gender_val with
| "male" -> Person.Male
| "female" -> Person.Female
| "neither" -> Person.Neither
| "unspecified" -> Person.Unspecified
| _ -> failwith "Parser error: invalid value in the gender field" in
{last_name; first_name; gender; favourite_colour; birthday}   
}

您还可以标记可能的性别,或者您可以在lexer级别使用正则表达式来防止无效字段,例如

rule token = parser
| "male" | "female" | "neither" | "unspecified" as -> {GENDER s}
...

然而,这是不推荐的,因为事实上,它会将malefemale等变成关键字,所以它们在其他地方的出现会破坏你的语法。

最新更新