YACC 例項分析
本文例子來自於 <<lex & yacc >> 第二版
LEX 負責詞法分析,每次解析出一個 token。
一、 token 的型別和值
token 具有型別,在計算器例子中,包括如下型別:
1)、 NUMBER 一串數字
2)、 NAME 一個名稱
3)、 '+', '-', '*', '/' 等符號
同時 token 具有值,不同型別的 token, 值的含義不一樣,例如,
'1000': 型別是 NUMBER,值是1000
'abc': 型別是 NAME, 值是 'abc'
LEX 解析出一個 token 後,將此 token 的值,儲存在 yylval 變數中, 並將型別返回給 YACC。
為了能儲存不同型別的值, yylval 被定義成 union
%union {
double dval;
struct symtab *symp;
}
其中, dval 儲存 NUMBER 型別的值,symp 儲存 NAME 型別的值。
為了儲存 NAME 型別的值,定義了一個結構
struct symtab {
char *name;
double (*funcptr)();
double value;
}
其中 name 記錄了“符號”的名稱,而 value 則用於儲存計算結果,後文再介紹。
NAME 型別的 token,又被稱為 “符號”, 跟我們寫程式的時候定義的變數作用相同。
因此,當 LEX遇到數字串的時候,就把數字串的值儲存到 yylval 的 dval 中,並返回 NUMBER 型別。遇到字串的時候,根據字串名稱生成 symtab 結構,儲存其名稱,並將結構的地址儲存到 yylval 的 symp 中,並返回 NAME 型別。遇到 '+', '-' 等符號的時候,則返回該符號的 ascii 碼值。
二、 YACC 中的計算
在“產生式” 或者“規則”部分,通過 $1, $2, $3 的方式,可獲取對應 token 的值,對這些變數的訪問,實際就是對 yylval 的訪問。此時,YACC 已經知道相應 token 的型別了,因此對 NUMBER 型別,token 的值就是 yylval.dval, 對 NAME 型別, token 的值就是 yylval.symp
例如:
statement: NAME '=' expression { $1->value = $3; printf("(%s) = (%g)\n", $1->name, $1->value); }<br>
expression:
| NUMBER { $$ = $1; }
| NAME { $$ = $1->value; }
對於 NUMBER, $1 對應的就是數值
對於 NAME, $1->name 就是符號名稱
非終結符號的型別和值
%type <dval> expression
三、 附錄: 原始碼
symbol.h
#define NSYMS 1024 /* maximum number of symbols */
struct symtab {
char *name;
double (*funcptr)();
double value;
} symtab[NSYMS];
struct symtab *symlook(char* s);
symbol.c
#include "symbol.h"
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
struct symtab *symlook(char* s)
{
char *p;
struct symtab *sp;
for(sp = symtab; sp < &symtab[NSYMS]; sp++) {
/* is it already here? */
if(sp->name && !strcmp(sp->name, s)) {
printf("found symbol: (%s)\n", sp->name);
return sp;
}
/* is it free */
if(!sp->name) {
sp->name = strdup(s);
printf("add symbol: (%s)\n", s);
return sp;
}
/* otherwise continue to next */
}
yyerror("Too many symbols");
exit(1); /* cannot continue */
} /* symlook */
calc.l
%{
#include "y.tab.h"
#include "symbol.h"
#include <math.h>
%}
%%
([0-9]+|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?) {
yylval.dval = atof(yytext);
return NUMBER;
}
[ \t] ; /* ignore white space */
[A-Za-z][A-Za-z0-9]* { /* return symbol pointer */
struct symtab *sp = symlook(yytext);
yylval.symp = sp;
return NAME;
}
"$" { return 0; }
\n |
. return yytext[0];
%%
calc.y%{
#include <string.h>
#include <math.h>
#include "symbol.h"
%}
%union {
double dval;
struct symtab *symp;
}
%token <symp> NAME
%token <dval> NUMBER
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS
%type <dval> expression
%%
statement_list: statement '\n'
| statement_list statement '\n'
;
statement: NAME '=' expression { $1->value = $3; printf("(%s) = (%g)\n", $1->name, $1->value); }
| expression { printf("= %g\n", $1); }
;
expression: expression '+' expression { $$ = $1 + $3; }
| expression '-' expression { $$ = $1 - $3; }
| expression '*' expression { $$ = $1 * $3; }
| expression '/' expression
{ if($3 == 0.0)
yyerror("divide by zero");
else
$$ = $1 / $3;
}
| '-' expression %prec UMINUS { $$ = -$2; }
| '(' expression ')' { $$ = $2; }
| NUMBER { $$ = $1; }
| NAME { $$ = $1->value; }
| NAME '(' expression ')' {
if($1->funcptr)
$$ = ($1->funcptr)($3);
else {
printf("%s not a function\n", $1->name);
$$ = 0.0;
}
}
;
%%
相關文章
- Redis 例項分析工具Redis
- for迴圈的例項分析
- GBase8s 的yacc語法分析語法分析
- fastcgi協議分析與例項AST協議
- jQuery鏈式呼叫例項分析jQuery
- SQL優化例項-思路分析SQL優化
- React元件/元素與例項分析React元件
- Java HashMap例項原始碼分析JavaHashMap原始碼
- SHELL-分析日誌例項
- 遞迴中Return例項分析遞迴
- lex yacc 學習
- 例項分析理解Java位元組碼Java
- CRLF Injection漏洞的利用與例項分析
- JVM指令分析例項四(陣列、switch)JVM陣列
- 需求過程化分析方法-例項分享
- 類的例項化順序和分析
- eMarketer:各行業VR例項應用分析行業VR
- js解析json資料例項分析JSON
- (轉)例項分析:MySQL優化經驗MySql優化
- mysql例項cpu超過100%分析MySql
- Java延時例項分析:Lock vs SynchronizedJavasynchronized
- 安川變頻器維修例項分析
- JVM 位元組碼執行例項分析JVM
- lex yacc 入門教程
- 例項分析JAVA CLASS的檔案結構Java
- Java Class檔案結構例項分析(下)Java
- Java Class檔案結構例項分析(上)Java
- EntityFramework Core上下文例項池原理分析Framework
- Lua 和 C/C++ 互相呼叫例項分析C++
- 單例項刪除ASM例項單例ASM
- Spring 原始碼分析之 bean 例項化原理Spring原始碼Bean
- 嵌入式產品例項分析-智慧檯燈
- 嵌入式產品例項分析-智慧手環
- 非同步(一):Promise深入理解與例項分析非同步Promise
- Hive 分析函式lead、lag例項應用Hive函式
- 對比分析Java反射獲取例項的速度Java反射
- javascript 物件合併程式碼例項簡單分析JavaScript物件
- Oracle資料庫例項啟動步驟分析Oracle資料庫