编写语法规则

下面我们来编写一个柴犬语( Doge )的语法解析器以便熟悉 mpc 的用法。

先来看一下 Doge 语言的语法描述:

  • 形容词 (Adjective) 包括 wowmanysosuch 符号。
  • 名词 (Noun) 包括 lisplanguagecbookbuild 符号。
  • 短语 (Phrase) 由形容词 (Adjective) 后接名词 (Noun) 组成。
  • Doge 语言由 0 到多个 短语(Phrase) 组成。

现在我们尝试定义一下形容词和名词,为此我们创建两个解析器,类型是 mpc_parser_t*,然后将解析器存储在 AdjectiveNoun 两个变量中。mpc_or 函数产生一个解析器,它可接受的语句必须是指定语句中的一个。而 mpc_sym 将字符串转化为一个语句。

下面的代码也正如我们上面的描述一样:

  1. /* Build a parser 'Adjective' to recognize descriptions */
  2. mpc_parser_t* Adjective = mpc_or(4,
  3. mpc_sym("wow"), mpc_sym("many"),
  4. mpc_sym("so"), mpc_sym("such")
  5. );
  6. /* Build a parser 'Noun' to recognize things */
  7. mpc_parser_t* Noun = mpc_or(5,
  8. mpc_sym("lisp"), mpc_sym("language"),
  9. mpc_sym("book"),mpc_sym("build"),
  10. mpc_sym("c")
  11. );

我怎样才能使用上面的这些 mpc 库提供的函数?

现在先不用担心编译和运行程序的事情,先确保理解背后的理论知识。在下一章中我们将使用使用mpc 实现一个更加接近我们的 Lisp 的语言。

接下来,我们使用已经定义好的解析器 AdjectiveNoun 来定义短语(Phrase)解析器。mpc_and 函数返回的解析器可接受的语句必须是各个语句按照顺序出现。所以我们将先前定义的 AdjectiveNoun 传递给它,表示形容词后面紧跟名词组成短语。mpcf_strfoldfree 指定了各个语句的组织及删除方式,我们可以暂时忽略它们。

  1. mpc_parser_t* Phrase = mpc_and(2, mpcf_strfold, Adjective, Noun, free);

Doge 语言是由 0 到多个短语(Phrase) 组成的。mpc_many 函数表达的正是这种逻辑关系。同样的,我们可以暂时忽略 mpcf_strfold 参数。

  1. mpc_parser_t* Doge = mpc_many(mpcf_strfold, Phrase);

上述语句表明 Doge 可以接受任意多条语句。这也意味着 Doge 语言是无穷的。下面列出了一些符合 Doge 语法的例子:

  1. "wow book such language many lisp"
  2. "so c such build such language"
  3. "many build wow c"
  4. ""
  5. "wow lisp wow c many language"
  6. "so c"

我们可以继续使用 mpc 提供的其他函数,一步一步地编写能解析更加复杂的语法的解析器。相应地,随着复杂度的增加,代码的可读性也会越来越差。所以,这种写法其实并不简单。mpc 还提供了一系列的帮助函数来帮助用户更加简单地完成常见的任务,具体的文档说明可以参见项目主页。使用这些函数能够更好更快地构建复杂语言的解析器,并能够提供更加精细地控制。