我有一个非常巨大的野牛规则文件,想知道是否有一种简单的方法可以从后面的程序中覆盖这些规则。
我用 bison 生成了一个解析器。针对不同的文件运行解析器,并希望查看 .yy 文件中哪些行已被触及。
据我所知,没有官方方法可以生成这样的报告。但如果你愿意稍微深入了解野牛的内部结构,这是有可能做到的。
请注意,您只能为解析器语义操作生成覆盖率报告。 “解析器规则”没有其他直接翻译;规则被编译成状态机,状态与规则的对应关系是多对多的。但这会告诉您每条规则完成了多少次,这可能就是您想知道的。
还要注意,解析器不区分规则最终语义操作和中间规则操作,因为中间规则操作实际上被编译为生成的非终结符的归约操作。
本文的其余部分是非官方的,不应依赖于与 Bison 的所有未来版本一起使用。它也不能与其他野牛骨骼一起使用(尽管它可能会被改编);我是根据当前版本 3.8.2 编写的。
挂钩解析器的最简单方法是劫持宏
YY_REDUCE_PRINT
,跟踪工具使用该宏来跟踪归约操作。所以每次减少时都会执行它。即使跟踪没有编译到解析器中也会发生这种情况;在这种情况下,宏被定义为无操作。不过,YY_REDUCE_PRINT
不是界面的官方部分,因此其名称和功能可能会更改,恕不另行通知。此外,它并不正式可用于自定义,并且生成的源代码不会尝试检查它是否之前已定义。所以你必须等到解析器模板中定义它,然后重新定义它。当然,重新定义它会使其无法用于跟踪日志,因此这与调试跟踪不兼容。原来,½initial-action
代码块是在YY_REDUCE_PRINT
定义之后注入的,所以这就是我重新定义的地方。这也不能保证。
我使用
lalr1.cc
骨架,使用几个最近的 bison 版本(3.7.1 和 3.8.2)非常轻松地测试了以下代码。这似乎有效,但您的里程可能会有所不同。
代码非常简单。首先,重新定义
YY_REDUCE_PRINT
,将其放入您的 .yy
文件中。您可能希望使其以某些配置宏为条件,以便保留生成调试跟踪的可能性。重新定义的 YY_REDUCE_PRINT
宏所做的就是向覆盖率直方图添加 1。 (这里,drv
是解析器驱动程序的一个实例,按照 Calc++ 示例):
%initial-action {
#undef YY_REDUCE_PRINT
#define YY_REDUCE_PRINT(Rule) drv.register_rule(Rule)
}
直方图本身需要实现;它可以进入
driver.hh
和 driver.cc
:
标题:
// Register execution of a semantic action.
void register_rule(int rule);
// Count of executions of each rule.
std::vector<unsigned> rule_count;
实施:
void
driver::register_rule(int ruleno) {
if (ruleno > 0) {
if (ruleno > rule_count.size()) rule_count.resize(ruleno);
++rule_count[ruleno - 1];
}
}
规则编号与生成的报告文件中的编号相对应。规则0(接受规则)不计算在内。
这是使用 bison 3.6 的解决方案: 在 texput.tex 中为 LaTex 构建并打印语法树
syntaxtree-2.y:
%{
/*
Einfacher Baum aus Strings, typedef genügt
*/
#include <iostream>
#include <string>
using namespace std;
#include "tree.hpp"
typedef tree<string> syntaxTree;
syntaxTree * syntaxTree_epsilon_node () {
return new syntaxTree("$\\epsilon$");
}
syntaxTree * root;
int yylex();
int yyerror(string s);
#define YYDEBUG 1
%}
%union {syntaxTree * tree;}
%token t_plus t_minus t_mal t_div t_kla_auf t_kla_zu t_fehler t_zahl
%initial-action {
#undef YY_REDUCE_PRINT
#define YY_REDUCE_PRINT(Rule) { if (yytname[yyr1[Rule]][0] != '$') { \
int n = 0; \
root = yyval.tree = new syntaxTree(yytname[yyr1[Rule]]); \
for (int i = 1 - yyr2[Rule]; i <= 0; i++) \
if (yytname[YY_ACCESSING_SYMBOL (yyssp[i])][0] != '$') \
n++, yyval.tree->append(yyvsp[i].tree); \
if (!n) yyval.tree->append(syntaxTree_epsilon_node()); \
} \
}
} // %initial-action
%%
expr: term | expr t_plus {} term | expr t_minus term {};
term: factor | term t_mal {} factor| term t_div factor;
factor: t_zahl | t_kla_auf expr t_kla_zu | t_minus factor;
%%
#include "lex.yy.c"
int yyerror(string s) {
cout << s << endl;
return 0;
}
int main() {
int rc = yyparse();
if (!rc)
root->tikz("texput.tex");
else
cerr << "Parse-Ergebnis " << rc << endl;
return rc;
}
扫描仪-2.l:
%{
#define PROCESS(id) {\
yylval.tree = new syntaxTree(yytext); \
return id;\
}
%}
%%
"+" PROCESS(t_plus);
"-" PROCESS(t_minus);
"*" PROCESS(t_mal);
"/" PROCESS(t_div);
"(" PROCESS(t_kla_auf);
")" PROCESS(t_kla_zu);
[0-9]+ PROCESS(t_zahl);
[ \t\n] /* do nothing */;
. PROCESS(t_fehler);
%%
int yywrap() {
return 1;
}
树.hpp:
/*
Template-Klasse tree
*/
#pragma once
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
template <class T> class tree {
public:
tree();
~tree();
tree(T);
tree(T, tree<T> *);
tree(T, tree<T> *, tree<T> *);
tree(T, tree<T> *, tree<T> *, tree<T> *);
tree(T, vector<tree<T> *>);
void set(const T & p);
T get();
void ascii(ostream & o = cout, int level = 0);
void tikz(ostream & o = cout, int level = 0, int path = 0);
void tikz(string filename, int path = 0);
void postorder(ostream & o = cout, int level = 0);
void append(tree *);
vector<tree<T> *> & childs();
tree<T> * childs(int);
int size();
tree * parent;
tree<T> * operator [] (int);
protected:
vector<tree *> child;
T v;
void tikz_path(ostream & o = cout, int level = 0);
};
template <class T> tree<T> * tree<T>::operator [] (int index) {
return (index < 0 || index >= child.size()) ? nullptr : child[index];
}
template <class T> int tree<T>::size() {
return child.size();
}
template <class T> tree<T>::tree(T _v) :v(_v) {}
template <class T> tree<T>::tree(T _v ,tree<T> * p) : v(_v) {
append(p);
}
template <class T> tree<T>::tree(T _v ,tree<T> * p1, tree<T> * p2) : v(_v) {
append(p1), append(p2);
}
template <class T> tree<T>::tree(T _v ,tree<T> * p1, tree<T> * p2, tree<T> * p3) : v(_v) {
append(p1), append(p2), append(p3);
}
template <class T> tree<T>::tree(T _v, vector<tree<T> *> childs) : v(_v) {
for (int i = 0; i < childs.size(); i++)
append(childs[i]);
}
template <class T> tree<T>::tree() {
}
template <class T> tree<T>::~tree() {
//for (auto i: child) delete i; // C++ 11 !!!!
for (int i = 0; i < child.size(); i++)
delete child[i];
}
template <class T> void tree<T>::set(const T & _v) {
v = _v;
}
template <class T> T tree<T>::get() {
return v;
}
template <class T> void tree<T>::append(tree * t) {
if (t != nullptr) {
child.push_back(t);
t->parent = this;
}
}
template <class T> vector<tree<T> *> & tree<T>::childs() {
return child;
}
template <class T> tree<T> * tree<T>::childs(int n) {
return (n < 0 || n >= child.size()) ? nullptr : child[n];
}
template <class T> void tree<T>::ascii(ostream & o, int level) {
for (unsigned i=0; i < level; i++)
o << "\t";
o << v << " " << child.size() << endl;
for (unsigned i=0; i < child.size(); i++) {
child[i]->ascii(o, level + 1);
}
}
template <class T> void tree<T>::postorder(ostream & o, int level) {
for (unsigned i=0; i < child.size(); i++) {
child[i]->postorder(o, level + 1);
}
o << v << " ";
if (!level)
o << endl;
}
template <class T> void tree<T>::tikz(string filename, int path) {
fstream f;
f.open (filename, fstream::out);
tikz(f, 0, path);
f.close();
}
template <class T> void tree<T>::tikz(ostream & o, int n, int path) {
// Baum-Traversierung First-Order
int i;
if (!n) {
o << "\\documentclass{standalone}\n";
o << "\\usepackage{tikz,tikz-qtree}\n";
o << "\\begin{document}\n";
// "Normale" Baumansicht
o << "\\begin{tikzpicture}\n[level distance=1.25cm,sibling distance=.25cm,every tree node/.style={anchor=north,align=center,draw,top color=white, bottom color=blue!20,minimum width=1.5em,minimum height=1.5em},every leaf node/.style={anchor=north,align=center,draw,rounded corners,top color=white, bottom color=red!20,minimum width=1.5em,minimum height=1.5em},blank/.style={draw=none,color=white!0, top color=white!0, bottom color=white!0},"
// Horizontal wachsend, ggf. optisch geeigneter für Hohe Bäume und Hochformatansicht
//o << [grow'=right,level distance=4cm,sibling distance=.25cm,every tree node/.style={anchor= west,align=center,draw,top color=white, bottom color=blue!20,minimum width=1.5em,,minimum height=1.5em},every leaf node/.style={anchor=north,align=center,draw,rounded corners,top color=white, bottom color=red!20,minimum width=1.5em,minimum height=1.5em},blank/.style={draw=none,color=white!0, top color=white!0, bottom color=white!0},]
"]";
o << "\n\\Tree\n";
}
for (i = 0; i < n; i++)
o << "\t";
if (child.size()) {
o << "[." << "\\node(" << this << "){" << v << "};" << endl;
for (unsigned i=0; i < child.size(); i++)
child[i]->tikz(o, n + 1);
for (i = 0; i < n; i++)
o << "\t";
o << "]\n";
}
else
o << "" << "\\node(" << this << "){" << v << "};" << endl;
if (!n) {
if (path)
tikz_path(o, 0);
o << "\\end{tikzpicture}\n";
o << "\\end{document}\n";
}
}
template <class T> void tree<T>::tikz_path(ostream & o, int level) {
if (!level)
o << "\\draw [red, >->] plot [smooth, tension=.75] coordinates {\n";
if (child.size() == 0) {
o << "([xshift=-4mm]" << this << ")\n";
o << "([yshift=-4mm]" << this << ")\n";
o << "([xshift=+4mm]" << this << ")\n";
}
/* else if (child.size() == 1) {
o << "([xshift=-4mm]" << this << ")\n";
child[0]->tikz_path(o, level + 1);
o << "([xshift=+4mm]" << this << ")\n";
}*/
else if (child.size() == 2) {
o << "([xshift=-4mm]" << this << ")\n";
child[0]->tikz_path(o, level + 1);
o << "([yshift=-4mm]" << this << ")\n";
child[1]->tikz_path(o, level + 1);
o << "([xshift=+4mm]" << this << ")\n";
}
else {
o << "([xshift=-4mm]" << this << ")\n";
for (int i = 0; i < child.size(); i++)
child[i]->tikz_path(o, level + 1);
o << "([xshift=+4mm]" << this << ")\n";
}
if (!level)
o << "};\n";
}
品牌:
syntaxtree-2: syntaxtree-2.y scanner-2.l
lex scanner-2.l
bison syntaxtree-2.y -o y.tab.cpp
${CPP} y.tab.cpp -I${INCL} -o syntaxtree-2