<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Bison on farmer3-c Blog</title><link>https://farmer3-c.github.io/tags/bison/</link><description>Recent content in Bison on farmer3-c Blog</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sat, 23 May 2026 16:26:30 +0000</lastBuildDate><atom:link href="https://farmer3-c.github.io/tags/bison/index.xml" rel="self" type="application/rss+xml"/><item><title>Flex 和 Bison 教程</title><link>https://farmer3-c.github.io/posts/flex-%E5%92%8C-bison-%E6%95%99%E7%A8%8B/</link><pubDate>Sat, 23 May 2026 16:26:30 +0000</pubDate><guid>https://farmer3-c.github.io/posts/flex-%E5%92%8C-bison-%E6%95%99%E7%A8%8B/</guid><description>&lt;blockquote&gt;
&lt;p&gt;说明：本篇内容译自 Santa Clara University COEN 259 编译原理课程讲义，用于学习 Flex 和 Bison 编译器工具。
本篇内容为个人学习，不构成任何商业用途。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="flex"&gt;Flex&lt;/h2&gt;
&lt;p&gt;Flex 是一个用于词法分析的扫描器生成工具，它基于有限状态机 (FSM)。输入是一组正则表达式，输出是根据输入规则实现扫描器的代码。&lt;/p&gt;
&lt;p&gt;为了实现计算器的一个扫描器，我们可以将文件 “cal1.l” 编写如下：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/* this is only for scanner, not link with parser yet */
%{
int lineNum = 0;
%}
%%
&amp;#34;(&amp;#34; { printf(&amp;#34;(\n&amp;#34;); }
&amp;#34;)&amp;#34; { printf(&amp;#34;)\n&amp;#34;); }
&amp;#34;+&amp;#34; { printf(&amp;#34;+\n&amp;#34;); }
&amp;#34;*&amp;#34; { printf(&amp;#34;*\n&amp;#34;); }
\n { lineNum++; }
[ \t]+ { }
[0-9]+ { printf(&amp;#34;%s\n&amp;#34;, yytext); }
%%
int yywrap() {
return 1;
}
int main () {
yylex();
return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这是用于构建扫描器的 Makefile：&lt;/p&gt;</description><content:encoded><![CDATA[<blockquote>
<p>说明：本篇内容译自 Santa Clara University COEN 259 编译原理课程讲义，用于学习 Flex 和 Bison 编译器工具。
本篇内容为个人学习，不构成任何商业用途。</p>
</blockquote>
<h2 id="flex">Flex</h2>
<p>Flex 是一个用于词法分析的扫描器生成工具，它基于有限状态机 (FSM)。输入是一组正则表达式，输出是根据输入规则实现扫描器的代码。</p>
<p>为了实现计算器的一个扫描器，我们可以将文件 “cal1.l” 编写如下：</p>
<pre tabindex="0"><code>/* this is only for scanner, not link with parser yet */ 
%{ 
int lineNum = 0; 
%} 
 
%% 
 
&#34;(&#34; { printf(&#34;(\n&#34;); } 
&#34;)&#34; { printf(&#34;)\n&#34;); } 
&#34;+&#34; { printf(&#34;+\n&#34;); } 
&#34;*&#34; { printf(&#34;*\n&#34;); } 
\n { lineNum++; } 
[ \t]+ { } 
[0-9]+ { printf(&#34;%s\n&#34;, yytext); } 
 
%% 
 
int yywrap() { 
 return 1; 
} 
 
int main () { 
 yylex(); 
 return 0; 
} 
</code></pre><p>这是用于构建扫描器的 Makefile：</p>
<pre tabindex="0"><code>p1: lex.yy.o 
 gcc -g -o p1 lex.yy.o 
 
lex.yy.o: cal1.l 
 flex cal1.l; gcc -g -c lex.yy.c 
 
clean: 
 rm -f p1 *.o lex.yy.c
</code></pre><p><strong>注意</strong>：对于更复杂的 flex 输入文件，你可能会收到类似的错误信息</p>
<pre tabindex="0"><code>parse tree too big, try %a num (or %e num)&#34; 
</code></pre><p>然后你需要定义 <code>%e &lt;num&gt;</code>。你应该把它放在宏和 <code>%start</code> 符号之间。其他选项有 <code>%a、%o、%n、%p</code> 等。</p>
<h2 id="bison">Bison</h2>
<p>Bison 是一个用于语法分析的 LALR(1) 解析器生成工具，它基于下推自动机 (PDA)。输入是一组上下文无关文法 (CFG) 规则，输出是根据输入规则实现解析器的代码。</p>
<p>要实现计算器的一个解析器，我们可以按如下方式编写文件“cal.y”:</p>
<pre tabindex="0"><code>%{ 
#include &lt;stdio.h&gt; 
#include &lt;ctype.h&gt; 
int lineNum = 1; 
void yyerror(char *ps, ...) {  /* need this to avoid 
link problem */ 
 printf(&#34;%s\n&#34;, ps); 
} 
%} 
 
%union { 
 int d; 
} 
// need to choose token type from union above 
%token &lt;d&gt; NUMBER 
%token &#39;(&#39; &#39;)&#39; 
%left &#39;+&#39; 
%left &#39;*&#39; 
%type &lt;d&gt; exp factor term 
 
%start cal 
 
%% 
 
cal 
  : exp 
 { printf(&#34;The result is %d\n&#34;, $1); } 
  ; 
 
exp 
  : exp &#39;+&#39; factor 
 { $$ = $1 + $3; } 
  | factor 
 { $$ = $1; } 
  ; 
 
factor
 : factor &#39;*&#39; term 
 { $$ = $1 * $3; } 
  | term 
 { $$ = $1; } 
  ; 
 
term 
  : NUMBER 
 { $$ = $1; } 
  | &#39;(&#39; exp &#39;)&#39; 
 { $$ = $2; } 
  ; 
 
%% 
 
int main() { 
 yyparse(); 
 return 0; 
}
</code></pre><p>为了整合扫描器和解析器，我们需要修改扫描器输入文件“cal1.l”，并将其保存为“cal.l”，如下所示：</p>
<pre tabindex="0"><code>%{ 
#include &lt;stdlib.h&gt; /* for atoi call */ 
#define DEBUG  /* for debuging: print tokens and 
their line numbers */ 
#define NUMBER 258 /* copy this from cal.tab.c */ 
typedef union {  /* copy this from cal.tab.c */ 
 int d; 
} YYSTYPE; 
YYSTYPE yylval; /* for passing value to parser */ 
extern int lineNum; /* line number from cal.tab.c */ 
%} 
 
%% 
 
[ \t]+ {} 
[\n] { lineNum++; } 
&#34;(&#34; { 
#ifdef DEBUG 
  printf(&#34;token &#39;(&#39; at line %d\n&#34;, lineNum); 
#endif 
  return &#39;(&#39;; 
 } 
&#34;)&#34; { 
#ifdef DEBUG 
  printf(&#34;token &#39;)&#39; at line %d\n&#34;, lineNum); 
#endif 
  return &#39;)&#39;; 
 } 
&#34;+&#34; {
#ifdef DEBUG 
  printf(&#34;token &#39;+&#39; at line %d\n&#34;, lineNum); 
#endif 
  return &#39;+&#39;; 
 } 
&#34;*&#34; { 
#ifdef DEBUG 
  printf(&#34;token &#39;*&#39; at line %d\n&#34;, lineNum); 
#endif 
  return &#39;*&#39;; 
 } 
[0-9]+ { 
#ifdef DEBUG 
 printf(&#34;token %s at line %d\n&#34;, yytext, lineNum); 
#endif 
 yylval.d = atoi(yytext); 
 return NUMBER; 
} 
 
%% 
 
int yywrap() {  /* need this to avoid link problem */ 
 return 1; 
}
</code></pre><p>这是用于构建扫描器和解析器的 Makefile：</p>
<pre tabindex="0"><code>p2: lex.yy.o cal.tab.o 
 gcc -o p2 lex.yy.o cal.tab.o 
 
lex.yy.o: cal.l 
 flex cal.l; gcc -c lex.yy.c 
 
cal.tab.o: cal.y 
 bison -d cal.y; gcc -c cal.tab.c 
 
clean: 
 rm -f p2 cal.output *.o cal.tab.c lex.yy.c 
</code></pre><p>有一些调试 Bison 的技巧。</p>
<ol>
<li>使用 -v 选项运行 Bison，然后会生成一个名为 cal.output 的文件。它包含所有的冲突和/或永远不会被归约的规则，以及 Bison 生成的所有状态。</li>
<li>获取 Bison 的调试信息：首先，在编译 cal.tab.c 时添加 -DYYDEBUG；其次，设置环境变量 YYDEBUG=1。然后它会打印大量的调试信息，例如如何进行移入或归约。</li>
</ol>
<hr>
<p><a href="https://www.cse.scu.edu/~m1wang/compiler/TutorialFlexBison.pdf">原文</a></p>
]]></content:encoded></item></channel></rss>