iT邦幫忙

2023 iThome 鐵人賽

DAY 26
0
自我挑戰組

Lex & Yacc 學習筆記系列 第 26

[Day26] Yacc 進階 - Embedded Actions

  • 分享至 

  • xImage
  •  

本篇內容

  • 介紹Embedded Actions
  • 範例:比大小

Embedded Actions: 邊讀取邊執行

在Yacc語法中,可以允許在規則定義之間插入語法動作,稱為嵌入式動作。

我們直接來看看下面的例子

// ex1: general
thing: 
		A PRINT B;
PRINT: 
		{
				printf("Function A is finished! Will do function B.");
		}

// ex2: embedded action
thing: 
		A { printf("Function A is finished! Will do function B."); } B

在例子1中,我們將A跟B語法間設定一個空的規則,並將其命名為PRINT。當A語法匹配且相對的A動作執行完成時,便會執行PRINT語法的匹配。因為PRINT是一個空白規則,所以會直接視為匹配成功,便接著執行PRINT動作。最後,編譯器會繼續進行B語法的匹配。

在例子2中,在A跟B之間的PRINT即為嵌入式動作。當讀取到的字串匹配A語法並執行完A動作後,便繼續執行B動作,最後再匹配B語法。

以上兩個做法是等價的。

最後,嵌入式動作在相對位置中也屬於一個項目,因此,相對位置中的參數如下

thing: 
		A                      // $1 
		{ printf("Function A is finished! Will do function B.");}  // $2
		B                      // $3
		

範例 - 比大小

說明

給定一個檔案,內有一個不等式(大於或小於)。請分別計算出左式與右式的值,並判斷該不等式是否正確。

程式實作

Lex

  • 需要回傳大於及小於符號
%{
#include "main.h"
#include "yacc.tab.h"
%}

integer     (-?([0-9]+))
float       (-?([0-9]*\.?[0-9]+))
blank_chars ([ \f\r\t\v]+)
expressions ([-+*/()])
comparison  ([<>])

%%

{integer}       { sscanf(yytext, "%d", &(yylval.intNum)); return INTEGER; }
{float}         { sscanf(yytext, "%f", &(yylval.floatNum)); return FLOAT; }
{expressions}   { return yytext[0]; }
{blank_chars}   { ; }
{comparison}    { return yytext[0]; }
"="             { return yytext[0]; }
\n              { ; }

%%

int yywrap(void) {
    return 1;
}

Yacc

  • 在 func 規則進行比大小
    • 當左式運算結束後,要先印出左式的值,故使用 embedded action
    • 當左式運算結束後,要先印出右式的值,再比較左式跟右式
    • 注意:此時右式的值被存在 $4 而非 $3
%{
#include "main.h"

void yyerror(const char *s);
extern int yylex();
extern int yyparse();

%}

%union {
    float   floatNum;
    int     intNum;
}

%token <intNum>   INTEGER
%token <floatNum> FLOAT

%type <floatNum> value expr

%left '+' '-'
%left '*' '/'
%right UMINUS

%%

func:
      expr
        {
            printf("Left: %f\n", $1);
        } 
      '>' expr         
        {   printf("Right: %f\n", $4); 
            if ($1 > $4) 
                printf("Result: true\n"); 
            else 
                printf("Result: false\n"); 
        }
    ;

expr:
      value                 { $$ = $1; }
    | expr '+' expr         { $$ = $1 + $3; }
    | expr '-' expr         { $$ = $1 - $3; }
    | expr '*' expr         { $$ = $1 * $3; }
    | expr '/' expr         
        { 
            if ($3 == 0.0) { 
                yyerror("Error: divisor cannot be zero!"); 
                YYABORT; 
            } else { 
                $$ = $1 / $3; 
            } 
        }
    | '-' expr %prec UMINUS { $$ = -$2; }
    | '(' expr ')'          { $$ = $2; }
    ;

value:
      FLOAT                 { $$ = $1; }
    | INTEGER               { $$ = (float)$1; }
    ;

%%

void yyerror(const char *s) {
    cerr << s << endl;
}

執行結果

輸入內容

45 + 24 / 3 - ( 7 * 2 - 4 ) > 1 + 95 / 5 + ( 22 - 3 ) * 2

輸出結果

Left: 43.000000
Right: 58.000000
Result: false

結語

有了 embedded action 後,便可以一邊匹配規則,一邊執行操作了。

參考資料

  • Levine, John R., Tony Mason and Doug Brown [1992]. Lex & Yacc. O’Reilly & Associates, Inc. Sebastopol, California.
  • Tom Niemann. Lex & Yacc

上一篇
[Day25] Yacc 進階 - 空白規則
下一篇
[Day27] Yacc 進階 - 使用外部變數
系列文
Lex & Yacc 學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言