编写语法规则
下面我们来编写一个柴犬语( Doge )的语法解析器以便熟悉 mpc
的用法。
先来看一下 Doge
语言的语法描述:
- 形容词 (
Adjective
) 包括wow
、many
、so
、such
符号。 - 名词 (
Noun
) 包括lisp
、language
、c
、book
、build
符号。 - 短语 (
Phrase
) 由形容词 (Adjective
) 后接名词 (Noun
) 组成。 Doge
语言由 0 到多个 短语(Phrase
) 组成。
现在我们尝试定义一下形容词和名词,为此我们创建两个解析器,类型是 mpc_parser_t*
,然后将解析器存储在 Adjective
和 Noun
两个变量中。mpc_or
函数产生一个解析器,它可接受的语句必须是指定语句中的一个。而 mpc_sym
将字符串转化为一个语句。
下面的代码也正如我们上面的描述一样:
/* Build a parser 'Adjective' to recognize descriptions */
mpc_parser_t* Adjective = mpc_or(4,
mpc_sym("wow"), mpc_sym("many"),
mpc_sym("so"), mpc_sym("such")
);
/* Build a parser 'Noun' to recognize things */
mpc_parser_t* Noun = mpc_or(5,
mpc_sym("lisp"), mpc_sym("language"),
mpc_sym("book"),mpc_sym("build"),
mpc_sym("c")
);
我怎样才能使用上面的这些
mpc
库提供的函数?现在先不用担心编译和运行程序的事情,先确保理解背后的理论知识。在下一章中我们将使用使用
mpc
实现一个更加接近我们的 Lisp 的语言。
接下来,我们使用已经定义好的解析器 Adjective
、 Noun
来定义短语(Phrase
)解析器。mpc_and
函数返回的解析器可接受的语句必须是各个语句按照顺序出现。所以我们将先前定义的 Adjective
和 Noun
传递给它,表示形容词后面紧跟名词组成短语。mpcf_strfold
和 free
指定了各个语句的组织及删除方式,我们可以暂时忽略它们。
mpc_parser_t* Phrase = mpc_and(2, mpcf_strfold, Adjective, Noun, free);
Doge 语言是由 0 到多个短语(Phrase) 组成的。mpc_many
函数表达的正是这种逻辑关系。同样的,我们可以暂时忽略 mpcf_strfold
参数。
mpc_parser_t* Doge = mpc_many(mpcf_strfold, Phrase);
上述语句表明 Doge 可以接受任意多条语句。这也意味着 Doge 语言是无穷的。下面列出了一些符合 Doge 语法的例子:
"wow book such language many lisp"
"so c such build such language"
"many build wow c"
""
"wow lisp wow c many language"
"so c"
我们可以继续使用 mpc
提供的其他函数,一步一步地编写能解析更加复杂的语法的解析器。相应地,随着复杂度的增加,代码的可读性也会越来越差。所以,这种写法其实并不简单。mpc
还提供了一系列的帮助函数来帮助用户更加简单地完成常见的任务,具体的文档说明可以参见项目主页。使用这些函数能够更好更快地构建复杂语言的解析器,并能够提供更加精细地控制。