背景
我想要编写一个类似awk的程序。用于文本行的处理。基本程序流程框架确定了以后,我遇到了一个语法解析的问题。这看起来没有那么容易。所以想要借鉴一下现有的工具。这时就发现了bison、flex。事实上,早有耳闻但是从未实际的上手。
参考资料
http://alumni.cs.ucr.edu/~jiayu/152spring/java.y
实践
使用Bison、flex创建语法解析程序可以有如下几个步骤:
首先是安装这个工具
yum install bison flex
zypper in bison flex
brew install bison flex
上面是几个流行操作系统的安装命令。
程序代码
ast.h
#ifndef AST_H
#define AST_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct ASTNode {
char *type;
} ASTNode;
ASTNode *createNode(const char *type);
void freeNode(ASTNode *node);
#endif // AST_H
calc.y
%{
#include "ast.h"
void yyerror(const char *s) {
fprintf(stderr, "Error: %s\n", s);
}
int yylex(void);
%}
%union {
ASTNode *node;
}
%token <node> NUMBER
%type <node> expr
%left '+'
%%
expr:
NUMBER {
$$ = $1; // NUMBER 已经是 ASTNode* 类型
}
| expr '+' expr {
$$ = createNode("add");
}
;
%%
ASTNode *createNode(const char *type) {
ASTNode *node = (ASTNode *)malloc(sizeof(ASTNode));
node->type = strdup(type);
return node;
}
void freeNode(ASTNode *node) {
if (node) {
free(node->type);
free(node);
}
}
int main(int argc, char **argv) {
yyparse();
return 0;
}
calc.l
%{
#include "ast.h"
#include "calc.tab.h"
%}
%%
[0-9]+ {
yylval.node = createNode("number");
return NUMBER;
}
\+ {
return '+';
}
\- {
return '-';
}
\n { /* 忽略换行 */ }
. { /* 忽略其他字符 */ }
%%
程序的编译
程序的编译流程如下:
#!/bin/bash
bison -d calc.y # 生成 calc.tab.c 和 calc.tab.h
flex calc.l # 生成 lex.yy.c
os_type=$(uname)
case "$os_type" in
"Darwin")
# macOS 专有的操作
echo "Running on macOS"
# 在这里添加macOS专有的命令
# libfl-static include the static library that include main() and yywrap();
# https://stackoverflow.com/questions/26064096/using-flex-the-lexical-analizer-on-os-x
gcc -o calc lex.yy.c calc.tab.c -ll # 编译生成的 C 文件
;;
"Linux")
# Linux 专有的操作
echo "Running on Linux"
# 在这里添加Linux专有的命令
gcc -o calc lex.yy.c calc.tab.c -lfl # 编译生成的 C 文件
;;
*)
# 其他操作系统
echo "Unknown OS: $os_type"
;;
esac