更加自然的语法规则

mpc 允许我们使用一种更加自然的方式来编写语法规则。我们可以将整个语言的语法规则写在一个长字符串中,而不是使用啰嗦难懂的 C 语句。我们也不再需要关心如何 使用 mpcf_strfold 或是 free 参数组织或删除各个语句。所有的这些工作都是都是自动完成的。

下面,我们使用这个方法重新编写了上面实现过的 Doge 语言:

  1. mpc_parser_t* Adjective = mpc_new("adjective");
  2. mpc_parser_t* Noun = mpc_new("noun");
  3. mpc_parser_t* Phrase = mpc_new("phrase");
  4. mpc_parser_t* Doge = mpc_new("doge");
  5. mpca_lang(MPCA_LANG_DEFAULT,
  6. " \
  7. adjective : \"wow\" | \"many\" \
  8. | \"so\" | \"such\"; \
  9. noun : \"lisp\" | \"language\" \
  10. | \"book\" | \"build\" | \"c\"; \
  11. phrase : <adjective> <noun>; \
  12. doge : <phrase>*; \
  13. ",
  14. Adjective, Noun, Phrase, Doge);
  15. /* Do some parsing here... */
  16. mpc_cleanup(4, Adjective, Noun, Phrase, Doge);

即使你现在暂时不理解上面的长字符串的语法规则,也能明显地感觉到这个方法要比之前的清晰的多。下面就来具体的学习一下其中的某些特殊符号的意义及用法。

注意到,现在定义一个语法规则分为两个步骤:

  1. 使用 mpc_new 函数定义语法规则的名字。
  2. 使用 mpca_lang 函数具体定义这些语法规则。

mpca_lang 函数的第一个参数是操作标记,在这里我们使用默认选项。第二个参数是 C 语言的一个长字符串。这个字符串中定义了具体的语法。它包含一系列的递归规则。每个规则分为两部分,用冒号 : 隔开,冒号左边是规则的名字,右边是规则的定义,使用 ; 表示规则结束。

定义语法规则的一些特殊符号的作用如下:

语法表示 作用
"ab" 要求字符串 ab
'a' 要求字符 a
'a' 'b' 要求先有一个字符 a,后面紧跟一个字符 b
'a'|'b' 要求有字符 a 或字符 b
'a'* 要求有 0 个或多个字符 a
'a'+ 要求有 1 个或多个字符 a
<abba> 要求满足名为 abba 定义的语法规则

似曾相识的感觉?

上面的一些语法规则有没有似曾相识的感觉?你没有猜错,mpca_lang 函数就是用 mpc_manympc_andmpc_or 这些函数来实现的,干净利落,不拖泥带水。

根据表中给出的规则尝试着理解一下上面的代码,看看是不是等价于之前我们前面用代码定义过的语法解析器?

在后面的章节中,我们会使用这个方法来定义我们的语法规则。刚开始可能并不是那么容易理解,但随着时间的推移,我们练习的越来越多,你也将会更加熟悉,并将知道如何创建和编辑自己的语法规则。

本章更加注重的是理论知识,所以在做彩蛋部分时,不要太在意正确性,思考实现的方式才是最重要的。