iT邦幫忙

2023 iThome 鐵人賽

DAY 23
0
自我挑戰組

Lex & Yacc 學習筆記系列 第 23

[Day23] Lex 進階 - State 其他用法

  • 分享至 

  • xImage
  •  

本篇內容

  • 介紹 State 的共用語法
  • 範例 - DNA字母統計2

State 的共用語法

我們在 [Day4] Lex - State 中,有介紹 State 的基本用法。我們可以經由不同 State 之間的切換,做不同字串的分類與標記方式。然而,如果某個標記方式在多個 State 都通用的話呢?暴力解當然就是把這個規則在每個 State 都寫一遍,但其實有更方便的做法。

我們可以將適用於多個State的規則寫成像下面這樣:

<STATE1, STATE2>reg_expr  { operations(); }

如此一來,reg_expr 這個規則就能同時在 STATE1 跟 STATE2 使用了。

如果要讓某個規則在所有 State 都適用的話,可以寫成這樣:

<*>reg_expr  { operations(); }

範例 - DNA字母統計3

說明

給定一份文件,內容有一段DNA核酸序列(由A, T, C, G組成),以及一段RNA序列(由A, U, C, G組成)。
試著計算各個字母(含氮鹼基)的數量。

檔案的格式如下:

* DNA
AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT
* RNA
AAAAAAAAAAUUUUUCCCCCGGGGGAAAAA
  • 注意:若是序列中出現不存在的字母,需要顯示出錯誤訊息。
    • 例如:在DNA序列中不會出現U,在RNA中不會出現T

程式實作

Definitions

  • 我們在此定義五個全域變數,記錄各種字母的數量。
  • 我們定義兩個 State: DNA_S 與 RNA_S,分別以DNA跟RNA的規則統計字母數量。
%{
    int numA, numT, numC, numG, numU; 
%}

%x DNA_S
%x RNA_S

blank       [ \t]

Rules

  • 定義 State 切換
    • 當我們讀到 DNA 字樣時,要進入 DNA_S
    • 當我們讀到 RNA 字樣時,要進入 RNA_S
    • 當我們讀到 * 字樣時,要回到初始 State → 這部分常常會被忘記!
  • 定義規則
    • 任何時候,遇到空白跟換行符,可直接忽略
    • 在DNA跟RNA中皆會出現ACG,故把規則寫在一起
    • 在DNA階段紀錄T,在RNA階段紀錄U
    • 在DNA階段讀到ACGT以外的字時,顯示錯誤訊息
    • 在DNA階段讀到ACGU以外的字時,顯示錯誤訊息
%%

\*              { BEGIN 0; }
<*>{blank}      { ; }
<*>\n           { ; }
DNA             { BEGIN DNA_S; }
RNA             { BEGIN RNA_S; }
<DNA_S,RNA_S>A  { numA++; }
<DNA_S,RNA_S>C  { numC++; }
<DNA_S,RNA_S>G  { numG++; }
<DNA_S>T        { numT++; }
<RNA_S>U        { numU++; }
<DNA_S,RNA_S>\* { BEGIN 0; }
<DNA_S>[^ACGT]  { printf("wrong alphabet in DNA: "); ECHO; printf(".\n"); }
<RNA_S>[^ACGU]  { printf("wrong alphabet in RNA: "); ECHO; printf(".\n"); }

.               { ; } 

完整程式碼

%{
    int numA, numT, numC, numG, numU; 
%}

%x DNA_S
%x RNA_S

blank       [ \t]

%%

\*              { BEGIN 0; }
<*>{blank}      { ; }
<*>\n           { ; }
DNA             { BEGIN DNA_S; }
RNA             { BEGIN RNA_S; }
<DNA_S,RNA_S>A  { numA++; }
<DNA_S,RNA_S>C  { numC++; }
<DNA_S,RNA_S>G  { numG++; }
<DNA_S>T        { numT++; }
<RNA_S>U        { numU++; }
<DNA_S,RNA_S>\* { BEGIN 0; }
<DNA_S>[^ACGT]  { printf("wrong alphabet in DNA: "); ECHO; printf(".\n"); }
<RNA_S>[^ACGU]  { printf("wrong alphabet in RNA: "); ECHO; printf(".\n"); }

.               { ; } 

%%

int yywrap(void) {
    return 1;
}

int main(void) {
    const char* sFile = "file.txt";
    FILE* fp = fopen(sFile, "r");
    if (fp == NULL)
    {
        printf("cannot open %s\n", sFile);
        return -1;
    }
    yyin = fp;
    yylex();
    printf("Counter :\nA: %d\nT: %d\nC: %d\nG: %d\nU: %d\n", numA, numT, numC, numG, numU);
    return 0;
}

執行結果

執行結果

輸入內容

* DNA
AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT
* RNA
AAAAAAAAAAUUUUUCCCCCGGGGGAAAAA

輸出結果

Counter :
A: 30
T: 3
C: 16
G: 8
U: 5

結語

繼第一次介紹 State 後,今天的 state 語法可以簡化 lex 規則,容易維護。

參考資料

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

上一篇
[Day22] Yacc - Error Handling
下一篇
[Day24] Lex 進階 - yywrap 應用
系列文
Lex & Yacc 學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言