ESC
输入关键词搜索文章
目录

micro-parser-combinators

C++ 元编程之 Parser Combinator | Netcan on Programming

orangeduck/mpc: A Parser Combinator library for C

简而言之,MPC就是一个语法解释器。其最主要的作用是用来设计新的编程语言,或者是用来解释一个己经存在的编程语言。

初印象

以下代码用来给读者带来一个对 mpc 的初印象:

//新的语法元素
mpc_parser_t *Expr  = mpc_new("expression");
mpc_parser_t *Prod  = mpc_new("product");
mpc_parser_t *Value = mpc_new("value");
mpc_parser_t *Maths = mpc_new("maths");

//新的语法结构
mpca_lang(MPCA_LANG_DEFAULT,
  " expression : <product> (('+' | '-') <product>)*; "
  " product    : <value>   (('*' | '/')   <value>)*; "
  " value      : /[0-9]+/ | '(' <expression> ')';    "
  " maths      : /^/ <expression> /$/;               ",
  Expr, Prod, Value, Maths, NULL);

mpc_result_t r;

if (mpc_parse("input", input, Maths, &r)) {
  mpc_ast_print(r.output);
  mpc_ast_delete(r.output);
} else {
  mpc_err_print(r.error);
  mpc_err_delete(r.error);
}

mpc_cleanup(4, Expr, Prod, Value, Maths);

Ast

Abstract syntax tree, 被存放在 mpca_* 命名域下。所有的 fold 与 destructor 都是内置的。

syntax requirement
"ab" "ab"
&#39;a&39; a
&#39;a&39; &#39;b&39; 先要 a, 然后要 b
'a' | 'b' a 或 b
&#39;a&39;+
&#39;a&39;? a 或什么都没有
&#39;a&39;{x} x 个 a
<expr>
typedef struct mpc_ast_t {
  char* tag;
  char* contents;
  mpc_state_t state;
  int children_num;
  struct mpc_ast_t** children;
} mpc_ast_t;

tag: 如:expr|number|regex contents: 结点的真实内容 &#39;*&39; &#39;(&39; &#39;5&39; state: line number, column number chidren, children_num: 邦助我们可以遍历整棵树

Parsing

有了这些基本解析器之后,就可以对输入内容进行解析了。解析完以后会得到一个结果 =mpc_result_t=,结果的定义如下:

typedef union {
  mpc_err_t *error;
  mpc_val_t *output;
} mpc_result_t;

有以下几个函数可以被用来运行 parser:

int mpc_parse(const char *filename, const char *string, mpc_parser_t *p, mpc_result_t *r);
// 解析一个 string, 如:mpc_parse("<stdin>", input, Lispy, &r) ss
int mpc_parse_file(const char *filename, FILE *file, mpc_parser_t *p, mpc_result_t *r);
// 解析一个文件??与解析文件的内容的区别在哪
int mpc_parse_pipe(const char *filename, FILE *pipe, mpc_parser_t *p, mpc_result_t *r);
//解析一个管道,如 stdin
int mpc_parse_contents(const char *filename, mpc_parser_t *p, mpc_result_t *r);
//解析一个文件的内容

Combinators

Combinator 同时取多个 parsers, 并且返回一个新的 parser.

C 语言使用手动内存管理,而如果解析过程中解析器失败,那么就需要清理所有过程中存 储的变量。C 语方必须显式传入 destructor 为输入,从而清理收的数据。

mpc_parser_t *mpc_expect(mpc_parser_t *a, const char *e);
mpc_parser_t *mpc_expectf(mpc_parser_t *a, const char *fmt, ...);
// 成功就返回一个执行 a 的解析器,失败则告知本应得到什么(e)
mpc_parser_t *mpc_apply(mpc_parser_t *a, mpc_apply_t f);
mpc_parser_t *mpc_apply_to(mpc_parser_t *a, mpc_apply_to_t f, void *x);
// 对 a 的结果进行 f 处理
mpc_parser_t *mpc_check(mpc_parser_t *a, mpc_dtor_t da, mpc_check_t f, const char *e);
mpc_parser_t *mpc_check_with(mpc_parser_t *a, mpc_dtor_t da, mpc_check_with_t f, void *x, const char *e);
mpc_parser_t *mpc_checkf(mpc_parser_t *a, mpc_dtor_t da, mpc_check_t f, const char *fmt, ...);
mpc_parser_t *mpc_check_withf(mpc_parser_t *a, mpc_dtor_t da, mpc_check_with_t f, void *x, const char *fmt, ...);
// 将 f 施加到 a 的结果上,如果其值为 0,那就失败,并返回错误信息 e ,并使用 df 销毁。如果返回非零值,那就返回 a
mpc_parser_t *mpc_not(mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_not_lift(mpc_parser_t *a, mpc_dtor_t da, mpc_ctor_t lf);
//Returns a parser with the following behaviour. If parser a succeeds, then it fails and consumes no input. If parser a fails, then it succeeds, consumes no input and returns NULL (or the result of lift function lf). Destructor da is used to destroy the result of a on success.
mpc_parser_t *mpc_maybe(mpc_parser_t *a);
mpc_parser_t *mpc_maybe_lift(mpc_parser_t *a, mpc_ctor_t lf);
//Returns a parser that runs a. If a is successful then it returns the result of a. If a is unsuccessful then it succeeds, but returns NULL (or the result of lf).
mpc_parser_t *mpc_many(mpc_fold_t f, mpc_parser_t *a);
mpc_parser_t *mpc_or(int n, ...);
mpc_parser_t *mpc_and(int n, mpc_fold_t f, ...);

函数类型

有用的函数

这些函数被写在 mpcf* 命名空间中,如 mpcf_strfold

mpc_val_t *strfold(int n, mpc_val_t **xs) {
  char *x = calloc(1, 1);
  int i;
  for (i = 0; i < n; i++) {
    x = realloc(x, strlen(x) + strlen(xs[i]) + 1);
    strcat(x, xs[i]);
    free(xs[i]);
  }
  return x;
}

正则

mpc_parser_t *ident = mpc_re("[a-zA-Z_][a-zA-Z_0-9]*");

/* Do Some Parsing... */

mpc_delete(ident);