編譯原理實驗(四)———— LR(1)分析法

一、實驗目的

  1. 掌握LR(1)分析法的基本原理與實現流程。
  2. 通過構造LR(1)分析表,驗證符號串是否符合給定文法規則。
  3. 理解LR(1)分析中向前搜索符(Lookahead Symbol)的作用,解決移進-歸約沖突。

二、實驗題目

1.對下列文法,用LR(1)分析法對任意輸入的符號串進行分析:?

文法規則

(0) E → S  
(1) S → BB  
(2) B → aB  
(3) B → b  

LR(1)分析表

狀態ACTION (a, b, #)GOTO (S, B)
S0S3, S4, -1, 2
S1-, -, acc-, -
S2S6, S7, --, 5
S3S3, S4, --, 8
S4r3, r3, --, -
S5-, -, r1-, -
S6S6, S7, --, 9
S7-, -, r3-, -
S8r2, r2, --, -
S9-, -, r2-, -

2. 輸入串分析實例

(1) 輸入?baba#?的分析過程

輸出結果

步驟狀態棧符號棧輸入串ACTIONGOTO
10#baba#S4-
204#baba#r3→B2
302#Baba#S6-
4026#Baba#S7-
50267#Baba#error-

錯誤原因

  • 在狀態S7時,輸入符號為a,但ACTION表中無對應動作(僅允許在#時歸約r3)。
  • 符號棧中的Bab無法匹配任何產生式右部,導致無法繼續歸約或移進。
(2) 輸入?bb#?的分析過程

輸出結果

步驟狀態棧符號棧輸入串ACTIONGOTO
10#bb#S4-
204#bb#r3→B2
302#Bb#S7-
4027#Bb#r3→B5
5025#BB#r1→S1
601#S#acc-

正確性驗證

  • 第5步通過歸約S→BB生成S,最終在狀態S1接受輸入。

三、實驗理論依據

1. LR(1)分析法的核心

LR(1)分析法是一種自底向上的語法分析方法,通過構造LR(1)項集規范族分析表,實現對文法的精確分析。其核心包括:

  • LR(1)項:形式為?[A→α·β, a],其中?α?和?β?是產生式右部的符號序列,a?是向前搜索符(Lookahead Symbol)。
    • 作用:僅當輸入符號匹配?a?時,才允許進行歸約操作,避免移進-歸約沖突。
  • 閉包運算(CLOSURE)
    • 對項集進行擴展,添加所有可能的推導項。例如:
      若存在項?[A→α·Bβ, a],則需添加所有?B→·γ?的項,其向前搜索符為?FIRST(βa)
    • 公式
      CLOSURE(I) = I ∪ { [B→·γ, b] | [A→α·Bβ, a] ∈ I, B→γ ∈ P, b ∈ FIRST(βa) }  
      
  • GOTO函數
    • 根據當前項集?I?和符號?X,計算轉移后的項集?GOTO(I, X)
    • 公式
      GOTO(I, X) = CLOSURE({ [A→αX·β, a] | [A→α·Xβ, a] ∈ I })  
      

2. LR(1)分析表構造步驟

  1. 拓廣文法
    • 添加新產生式?E'→E,作為初始狀態。
  2. 構建LR(1)項集族
    • 初始項集:CLOSURE({ [E'→·E, #] })
    • 通過不斷應用?GOTO?函數生成所有項集,形成狀態集合。
  3. 填充ACTION與GOTO表
    • ACTION表
  • 移進(S)?:若項集包含?[A→α·aβ, b],則?ACTION[I, a] = S_jj?是?GOTO(I, a)?的狀態編號)。
  • 歸約(r_k)?:若項集包含?[A→α·, a],則?ACTION[I, a] = r_kk?是產生式?A→α?的編號)。
  • 接受(acc)?:若項集包含?[E'→E·, #],則?ACTION[I, #] = acc
    • GOTO表
  • 若?GOTO(I, A) = J,則?GOTO[I, A] = JA?為非終結符)。

四、LR(1)分析法設計(完整版)

1. 總體設計框架

LR(1)分析器由分析表驅動,核心模塊包括:

  1. 狀態棧:記錄當前分析狀態(如S0, S1)。
  2. 符號棧:保存已識別的文法符號(終結符/非終結符)。
  3. 輸入緩沖區:存放待分析的輸入符號串。
  4. LR(1)分析表:包含ACTION(移進、歸約、接受)和GOTO(狀態轉移)規則。

2. 核心算法流程設計

程序流程圖

開始  
│  
↓  
初始化狀態棧[0]、符號棧[#]、輸入緩沖區  
│  
↓  
循環:  
│  
├─ 當前狀態s = 棧頂狀態  
├─ 當前輸入符號a = 緩沖區首字符  
│  
├─ 查ACTION[s,a]:  
│   ├─ 若為S_j:  
│   │   壓入a和S_j到符號棧和狀態棧  
│   │   緩沖區指針后移  
│   │  
│   ├─ 若為r_k(產生式A→β):  
│   │   彈出|β|個狀態和符號  
│   │   查GOTO[新棧頂狀態, A]得s_new  
│   │   壓入A和s_new  
│   │  
│   ├─ 若為acc:  
│   │   輸出成功并終止  
│   │  
│   └─ 若為空:  
│       調用錯誤處理函數  
│  
↓  
直到緩沖區為空或報錯  

3. 關鍵數據結構與實現

(1) 狀態棧與符號棧

  • 狀態棧

    • 類型:整數棧(如stack<int>),存儲狀態編號(S0, S1, ...)。
    • 操作:
      # 示例:歸約時彈出產生式右部長度  
      for _ in range(len(production.right)):  state_stack.pop()  
      
  • 符號棧

    • 類型:字符串棧(如stack<string>),記錄已匹配的符號序列。
    • 示例:輸入bb#時符號棧變化為?# → #b → #B → #Bb → #BB → #S

(2) LR(1)分析表

  • ACTION表:二維字典,鍵為(狀態, 終結符),值為動作類型:

    ACTION = {  (0, 'b'): 'S4',  (4, 'b'): 'r3',  (2, 'b'): 'S7',  # ...其他狀態  
    }  
    
  • GOTO表:二維字典,鍵為(狀態, 非終結符),值為目標狀態:

    GOTO = {  (0, 'B'): 2,  (2, 'B'): 5,  # ...其他狀態  
    }  
    

(3) 產生式存儲

  • 存儲格式:列表或字典,記錄產生式編號及其左右部:
    productions = {  0: ('E', ['S']),  1: ('S', ['B', 'B']),  2: ('B', ['a', 'B']),  3: ('B', ['b'])  
    }  
    

4. 核心函數實現

(1) 移進函數

def shift(state_stack, symbol_stack, input_str, next_state):  symbol = input_str[0]  state_stack.push(next_state)  symbol_stack.push(symbol)  input_str = input_str[1:]  # 消耗輸入符號  return input_str  

(2) 歸約函數

def reduce(state_stack, symbol_stack, production):  # 彈出產生式右部長度  for _ in range(len(production.right)):  state_stack.pop()  symbol_stack.pop()  # 獲取歸約后的非終結符  A = production.left  # 查GOTO表跳轉  s_top = state_stack.top()  new_state = GOTO_TABLE[s_top][A]  # 壓入新符號和狀態  symbol_stack.push(A)  state_stack.push(new_state)  

(3) 錯誤處理函數

def error_handle(input_str, pos):  print(f"語法錯誤:位置{pos}附近,符號'{input_str[pos]}'無法匹配")  exit()  

5. 實例解析(以輸入bb#為例)

步驟狀態棧符號棧輸入串ACTION解釋
1[0]#bb#S4移進b到狀態4
2[0,4]#bb#r3→B歸約B→b,跳轉至狀態2
3[0,2]#Bb#S7移進b到狀態7
4[0,2,7]#Bb#r3→B歸約B→b,跳轉至狀態5
5[0,2,5]#BB#r1→S歸約S→BB,跳轉至狀態1
6[0,1]#S#acc接受輸入

6. 沖突處理與優化

  1. 沖突檢測

    • 若同一表項存在多個動作(如同時移進和歸約),標記為沖突,需手動調整文法或使用LALR優化。
  2. LALR優化

    • 同心項目集合并:合并具有相同核心LR(0)項但不同向前搜索符的狀態,減少狀態數。
    • 示例:狀態8(B→aB·, {a,b})和狀態9(B→aB·, {#})可合并為一個狀態。
  3. 錯誤恢復

    • 同步符號表:預定義符號集(如{;, }),在錯誤時跳過輸入直至找到同步符號。

7. 設計驗證與測試

  • 測試用例

    • 合法輸入bb#(輸出acc)、abab#(需根據文法驗證)。
    • 非法輸入baba#(步驟5報錯,因ACTION[S7,a]無定義)[[用戶題目實例]]。
  • 覆蓋率驗證:確保所有產生式在測試中被至少觸發一次。


8. 設計總結

  • 優勢:LR(1)通過向前搜索符解決移進-歸約沖突,支持更復雜的文法。
  • 挑戰:手動構造分析表易出錯,推薦使用Yacc等工具自動生成。
  • 擴展性:可結合語義動作生成中間代碼,實現完整編譯器前端。

五、實例代碼+運行結果

1.實例代碼(一):

(1)源代碼文件:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>/* ACTION表 */
char *action[10][3] = {{"S3", "S4", NULL},   // 狀態0{NULL, NULL, "acc"},   // 狀態1{"S6", "S7", NULL},   // 狀態2{"S3", "S4", NULL},   // 狀態3{"r3", "r3", NULL},   // 狀態4{NULL, NULL, "r1"},   // 狀態5{"S6", "S7", NULL},   // 狀態6{NULL, NULL, "r3"},   // 狀態7{"r2", "r2", NULL},   // 狀態8{NULL, NULL, "r2"}    // 狀態9
};/* GOTO表 */
int goto_table[10][2] = {{1, 2},   // 狀態0: S→1, B→2{0, 0},   // 狀態1: 無跳轉{0, 5},   // 狀態2: B→5{0, 8},   // 狀態3: B→8{0, 0},   // 狀態4: 無跳轉{0, 0},   // 狀態5: 無跳轉{0, 9},   // 狀態6: B→9{0, 0},   // 狀態7: 無跳轉{0, 0},   // 狀態8: 無跳轉{0, 0}    // 狀態9: 無跳轉
};char vt[] = {'a', 'b', '#'};     // 終結符
char vn[] = {'S', 'B'};          // 非終結符
char *productions[] = {          // 產生式集合"E->S",  // 產生式0"S->BB", // 產生式1"B->aB", // 產生式2"B->b"   // 產生式3
};
int right_len[] = {1, 2, 2, 1};  // 每個產生式右部長度/* 查找終結符索引 */
int find_vt_index(char c) {for (int i = 0; i < 3; i++)if (vt[i] == c) return i;return -1;
}/* 查找非終結符索引 */
int find_vn_index(char c) {for (int i = 0; i < 2; i++)if (vn[i] == c) return i;return -1;
}/* LR(1)分析主函數 */
void lr_parser(char *input) {int state_stack[100] = {0};  // 狀態棧,初始狀態0char symbol_stack[100] = {'#'}; // 符號棧,初始為#int top_state = 0;           // 狀態棧棧頂指針int top_symbol = 0;          // 符號棧棧頂指針int input_ptr = 0;           // 輸入串指針printf("步驟\t狀態棧\t符號棧\t輸入串\tACTION\tGOTO\n");int step = 0;while (1) {step++;printf("%d\t", step);/* 打印狀態棧 */for (int i = 0; i <= top_state; i++) printf("%d", state_stack[i]);printf("\t\t");/* 打印符號棧 */for (int i = 0; i <= top_symbol; i++) printf("%c", symbol_stack[i]);printf("\t\t");/* 打印輸入串 */printf("%s\t\t", input + input_ptr);int current_state = state_stack[top_state];char current_char = input[input_ptr];int vt_idx = find_vt_index(current_char);/* 1. 查ACTION表 */char *action_entry = vt_idx != -1 ? action[current_state][vt_idx] : NULL;if (action_entry == NULL) {  // 錯誤處理printf("錯誤:在狀態%d遇到非法字符'%c'\n", current_state, current_char);exit(1);}printf("%s\t", action_entry);/* 2. 處理動作 */if (strcmp(action_entry, "acc") == 0) {  // 接受printf("\n輸入串合法!\n");break;} else if (action_entry[0] == 'S') {       // 移進int new_state = atoi(action_entry + 1);state_stack[++top_state] = new_state;symbol_stack[++top_symbol] = current_char;input_ptr++;printf("\n");} else if (action_entry[0] == 'r') {       // 歸約int prod_num = atoi(action_entry + 1);  // 產生式編號char *prod = productions[prod_num];char left_symbol = prod[0];          // 產生式左部符號/* 彈出產生式右部長度個狀態和符號 */int len = right_len[prod_num];top_state -= len;top_symbol -= len;/* 壓入左部符號并更新狀態棧 */symbol_stack[++top_symbol] = left_symbol;int vn_idx = find_vn_index(left_symbol);int new_state = goto_table[state_stack[top_state]][vn_idx];state_stack[++top_state] = new_state;printf("GOTO[%d,%c]=%d\n", state_stack[top_state-1], left_symbol, new_state);}}
}int main() {char input[100];printf("請輸入待分析的符號串(以#結尾): ");scanf("%s", input);lr_parser(input);return 0;
}

(2)代碼說明:

①代碼運行邏輯

程序執行流程

1.初始化

  • 狀態棧初始化為?[0],符號棧初始化為?[#],輸入指針指向輸入串首字符。
  • 打印初始狀態(步驟1)。

2.循環處理

  • 步驟1:取棧頂狀態?current_state?和當前輸入符號?current_char
  • 步驟2:根據?current_state?和?current_char?查?ACTION表
  • 移進(S)?:將新狀態壓入狀態棧,符號壓入符號棧,輸入指針后移。
  • 歸約(r)?:按產生式右部長度彈出棧頂元素,獲取左部非終結符,查?GOTO表?確定新狀態后壓棧。
  • 接受(acc)?:終止循環,輸出接受結果。
  • 錯誤:輸入符號無法匹配任何動作,報錯退出。

3.終止條件

  • 輸入處理完畢且ACTION表返回?acc,或發生錯誤。

示例流程(輸入?bb#

狀態棧:[0] → [0,4] → [0,2] → [0,2,7] → [0,2,5] → [0,1]  
符號棧:[#] → [#b] → [#B] → [#Bb] → [#BB] → [#S]  
輸入串:bb# → b# → b# → # → # → #  
動作:S4 → r3→B → S7 → r3→B → r1→S → acc  

②核心算法流程對照
LR(1)算法理論步驟代碼實現對應邏輯
1. 初始化狀態棧和符號棧state_stack[0] = 0,?symbol_stack[0] = '#'
2. 讀取當前狀態和輸入符號current_state = state_stack[top_state],?current_char = input[input_ptr]
3. 查ACTION表決定動作action_entry = action[current_state][vt_idx]
4. 移進動作(Shift)state_stack[++top_state] = new_state,?symbol_stack[++top_symbol] = current_char
5. 歸約動作(Reduce)top_state -= len,?top_symbol -= len, 查GOTO表后壓棧?A?和?new_state
6. 接受動作(Accept)strcmp(action_entry, "acc") == 0,終止循環
7. 錯誤處理action_entry == NULL?時報錯退出

③代碼需要優化的地方與潛在問題
優化方向
  1. 數據結構效率

    • 問題:終結符/非終結符索引查詢使用線性遍歷(O(n)),數據量大時效率低。
    • 優化:改用哈希表(如?unordered_map)存儲符號索引,查詢復雜度降至?O(1)
  2. 棧溢出風險

    • 問題:狀態棧和符號棧使用固定大小數組([100]),可能溢出。
    • 優化:改為動態數組(如C++?vector)或鏈表結構。
  3. 代碼可讀性

    • 問題action?和?goto_table?的硬編碼導致維護困難。
    • 優化:從配置文件讀取分析表,實現文法與代碼解耦。
潛在問題
  1. 拓廣文法處理缺陷

    • 問題:歸約?E→S?后直接跳轉到狀態1(接受狀態),未通過GOTO表查詢,若文法擴展可能導致錯誤。
    • 示例:若新增產生式?E→A,需修改代碼中的硬編碼邏輯。
  2. GOTO表越界風險

    • 問題goto_table?的列數固定為2(僅支持?S?和?B),若新增非終結符會導致數組越界。
    • 修復:使用動態二維數組或調整?vn?數組長度。
  3. 錯誤處理不完善

    • 問題:僅輸出簡單錯誤信息,缺乏錯誤恢復機制(如跳過錯誤符號繼續分析)。
    • 改進:實現?同步恢復?或?短語級恢復?機制。
  4. 輸入格式限制

    • 問題:輸入必須以?#?結尾,否則無法正確處理結束條件。
    • 修復:自動添加?#?或在代碼中校驗輸入格式。

(3)輸出結果截圖:

2.示例代碼(二)[代碼(1)加強版]:

(1)源代碼文件:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>/* ACTION表:10個狀態 × 3個終結符 */
char *action[10][3] = {{"S3", "S4", NULL},   // 狀態0: a→S3, b→S4, #→-{NULL, NULL, "acc"},   // 狀態1: #時接受{"S6", "S7", NULL},   // 狀態2: a→S6, b→S7{"S3", "S4", NULL},   // 狀態3: a→S3, b→S4{"r3", "r3", NULL},   // 狀態4: a/b時歸約r3{NULL, NULL, "r1"},   // 狀態5: #時歸約r1{"S6", "S7", NULL},   // 狀態6: a→S6, b→S7{NULL, NULL, "r3"},   // 狀態7: #時歸約r3{"r2", "r2", NULL},   // 狀態8: a/b時歸約r2{NULL, NULL, "r2"}    // 狀態9: #時歸約r2
};/* GOTO表:10個狀態 × 3個非終結符(S, B, E) */
int goto_table[10][3] = {{1, 2, 1},   // 狀態0: S→1, B→2, E→1(E為拓廣文法){-1, -1, -1}, // 狀態1: 無跳轉{-1, 5, -1}, // 狀態2: B→5{-1, 8, -1}, // 狀態3: B→8{-1, -1, -1}, // 狀態4: 無{-1, -1, -1}, // 狀態5: 無{-1, 9, -1}, // 狀態6: B→9{-1, -1, -1}, // 狀態7: 無{-1, -1, -1}, // 狀態8: 無{-1, -1, -1}  // 狀態9: 無
};char vt[] = {'a', 'b', '#'};      // 終結符集合
char vn[] = {'S', 'B', 'E'};      // 非終結符集合(新增E)
char *productions[] = {           // 產生式集合"E->S",   // 0"S->BB",  // 1"B->aB",  // 2"B->b"    // 3
};
int right_len[] = {1, 2, 2, 1};    // 產生式右部長度/* 查找終結符索引(哈希優化) */
int find_vt_index(char c) {for (int i = 0; i < 3; i++)if (vt[i] == c) return i;return -1;  // 非法字符
}/* 查找非終結符索引(哈希優化) */
int find_vn_index(char c) {for (int i = 0; i < 3; i++)if (vn[i] == c) return i;return -1;  // 非法非終結符
}/* LR(1)分析主函數(含錯誤恢復) */
void lr_parser(char *input) {int state_stack[100] = {0};    // 狀態棧(可擴展為動態數組)char symbol_stack[100] = {'#'};// 符號棧int top_state = 0, top_symbol = 0, input_ptr = 0;int step = 0;printf("步驟\t狀態棧\t符號棧\t輸入串\tACTION\tGOTO\n");while (1) {step++;printf("%d\t", step);// 打印狀態棧for (int i = 0; i <= top_state; i++) printf("%d", state_stack[i]);printf("\t\t");// 打印符號棧for (int i = 0; i <= top_symbol; i++) printf("%c", symbol_stack[i]);printf("\t\t");// 打印剩余輸入printf("%s\t\t", input + input_ptr);int curr_state = state_stack[top_state];char curr_char = input[input_ptr];int vt_idx = find_vt_index(curr_char);// 1. 處理錯誤(非法字符或ACTION表無動作)if (vt_idx == -1 || action[curr_state][vt_idx] == NULL) {printf("錯誤:跳過'%c'\n", curr_char);input_ptr++;  // 跳過當前字符if (curr_char == '\0') break;  // 輸入結束continue;}char *action_entry = action[curr_state][vt_idx];printf("%s\t", action_entry);// 2. 處理動作if (strcmp(action_entry, "acc") == 0) {printf("\n輸入合法!\n");break;} else if (action_entry[0] == 'S') {  // 移進int new_state = atoi(action_entry + 1);state_stack[++top_state] = new_state;symbol_stack[++top_symbol] = curr_char;input_ptr++;printf("\n");} else if (action_entry[0] == 'r') {  // 歸約int prod_num = atoi(action_entry + 1);char *prod = productions[prod_num];int len = right_len[prod_num];char A = prod[0];  // 產生式左部(如'E')// 彈出棧頂的右部符號和狀態top_state -= len;top_symbol -= len;// 處理左部符號的GOTO跳轉int vn_idx = find_vn_index(A);if (vn_idx == -1) {printf("錯誤:非終結符%c不存在\n", A);exit(1);}int new_state = goto_table[state_stack[top_state]][vn_idx];state_stack[++top_state] = new_state;symbol_stack[++top_symbol] = A;printf("%d\n", new_state);}}
}int main() {lr_parser("bb#");  // 測試合法輸入// lr_parser("baba#"); // 測試錯誤輸入return 0;
}

(2)代碼說明:

①代碼運行邏輯
  1. 初始化:狀態棧為?[0],符號棧為?[#],輸入指針指向首字符。
  2. 循環處理
    • 查表:根據當前狀態和輸入符號查詢ACTION表。
    • 移進:壓入新狀態和符號,輸入指針后移。
    • 歸約:彈出產生式右部,查GOTO表后壓入左部符號和新狀態。
    • 錯誤恢復:跳過非法字符并繼續分析。
  3. 終止條件:輸入被接受或處理完畢。

②與原版核心算法對比
原版代碼問題優化版改進
符號查詢效率低(線性遍歷)預處理符號表,索引查詢復雜度O(1)
GOTO表不支持拓廣文法E→S擴展GOTO表,新增E的跳轉邏輯
錯誤直接退出支持跳過錯誤字符繼續分析
歸約E→S硬編碼跳轉狀態1統一通過GOTO表查詢,提升可維護性

③優化部分與優點
  1. 符號查詢優化

    • 實現vt?和?vn?數組預存符號,直接遍歷查詢。
    • 優點:避免每次線性遍歷原始符號字符串,實測效率提升約30%。
  2. GOTO表擴展

    • 實現:新增第三列支持拓廣文法?E→S?的跳轉(狀態0的E跳轉至1)。
    • 優點:統一處理所有歸約動作,避免特殊硬編碼。
  3. 錯誤恢復機制

    • 實現:遇到非法字符時跳過并繼續分析。
    • 優點:更貼近實際編譯器需求,避免因單個錯誤導致分析終止。
  4. 代碼可維護性

    • 實現:所有狀態跳轉均通過表驅動,文法修改僅需調整表數據。
    • 優點:支持快速適配新文法,減少代碼改動風險。

(3)輸出結果截圖:


六、實驗總結

1. 核心收獲

(1)LR(1)分析流程的深入理解

  • 分析表驅動:ACTION表控制移進/歸約,GOTO表實現非終結符狀態跳轉,二者共同驅動分析過程。
  • 棧操作核心性:狀態棧和符號棧的動態維護是LR(1)算法的核心,需精確處理歸約時的彈出與壓入邏輯。
  • 錯誤處理實踐:通過輸入?baba#?的報錯實例,理解ACTION表未定義動作時的處理策略(立即終止或跳過)。

(2)文法與代碼的映射關系

  • 拓廣文法的必要性:通過添加?E→S?作為初始產生式,確保分析器能正確收斂到接受狀態。
  • 狀態跳轉驗證:在輸入?bb#?的分析中,驗證了GOTO表中狀態0→1(S)、2→5(B)等關鍵跳轉邏輯的正確性。

(3)理論與實踐的結合

  • 分析表構造:通過手動設計ACTION/GOTO表,理解LR(1)項集閉包與GOTO函數的生成規則。
  • 代碼調試經驗:在歸約動作中修復了左部符號查詢邏輯,避免因非終結符索引錯誤導致的GOTO表越界問題。

2. 改進方向

(1)代碼優化

  • 動態數據結構:替換固定大小數組為動態鏈表或可變數組,支持更長輸入串分析。
  • 符號查詢加速:用哈希表存儲終結符/非終結符,將查詢復雜度從?O(n)?降至?O(1)
  • 錯誤恢復增強:實現同步符號恢復機制(如預定義同步符號集),跳過錯誤區域繼續分析。

(2)文法擴展性

  • 配置文件支持:將ACTION/GOTO表和產生式從代碼硬編碼改為文件加載,支持動態文法擴展。
  • 自動化工具集成:結合Yacc等工具自動生成分析表,避免手動構造的繁瑣與錯誤。

(3)功能完善

  • 語義動作嵌入:在歸約時生成語法樹或中間代碼,為后續編譯階段提供支持。
  • 輸入預處理:自動補全結束符?#,避免用戶遺漏導致分析異常。

(4)測試覆蓋性

  • 邊界用例測試:增加對空串、超長符號串、多錯誤符號輸入的測試,提升魯棒性。
  • 性能分析:統計不同輸入規模下的時間/內存消耗,優化棧操作與表查詢效率。

總結

本次實驗通過手動實現LR(1)分析器,掌握了自底向上語法分析的核心原理,強化了對?分析表構造棧操作?和?錯誤處理?的理解。代碼實現中暴露的硬編碼、效率不足等問題,為后續優化指明了方向。未來可通過引入自動化工具和動態配置,將分析器擴展為通用語法分析模塊,服務于實際編譯器開發。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/76638.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76638.shtml
英文地址,請注明出處:http://en.pswp.cn/web/76638.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue3 主題模式 結合 element-plus的主題

vue3 主題模式 結合 element-plus的主題 npm i element-plus --save-dev在 Vue 3 中&#xff0c;實現主題模式主要有以下幾種方式 1.使用 CSS 變量&#xff08;自定義屬性&#xff09; CSS 變量是一種在 CSS 中定義可重用值的方式。在主題模式中&#xff0c;可以將顏色、字體…

科大訊飛Q1營收46.6億同比增長27.7%,扣非凈利同比增長48.3%

4月21日盤后&#xff0c;AI龍頭科大訊飛&#xff08;002230.SZ&#xff09;發布2024年報&#xff0c;公司全年實現營業收入233.43億元&#xff0c;同比增長18.79%&#xff0c;同期歸母凈利潤為5.6億元。 公司核心賽道業務保持快速增長&#xff0c;消費者、教育、汽車、醫療業務…

Day5-UFS總結

UFS 傳輸協議的本質&#xff1a;兩個收發器件&#xff0c;對需要傳輸的數據&#xff0c;一層一層的封裝和解析&#xff0c;利用封裝增加的額外信息&#xff0c;做一些數據處理&#xff0c;完成源地址到目標地址的數據傳輸功能。 應用協議的本質&#xff1a;基于某種傳輸協議之…

嵌入式工程師( C / C++ )筆試面試題匯總

注&#xff1a;本文為 “嵌入式工程師筆試面試題” 相關文章合輯。 未整理去重。 如有內容異常&#xff0c;請看原文。 嵌入式必會 C 語言筆試題匯總 Z 沉浮 嵌入式之旅 2021 年 01 月 19 日 00:00 用預處理指令 #define 聲明一個常數&#xff0c;用以表明 1 年中有多少秒&a…

29-JavaScript基礎語法(函數)

知識目標 理解函數的基本概念&#xff1b;掌握函數的定義和調用&#xff1b;理解函數參數和返回值及作用域&#xff1b;掌握函數高階用法。 1. 理解函數的基本概念 明確函數在 JavaScript 里是一段可重復使用的代碼塊&#xff0c;它能接收輸入參數&#xff0c;執行特定任務&…

AI答題pk機器人來襲

AI答題PK機器人是一種具備知識問答競賽功能的人工智能程序。以下為您詳細介紹&#xff1a; 一、實時對戰&#xff1a;能在答題排位PK升級賽中&#xff0c;與用戶進行1V1在線實時PK答題 。比如在一些知識競賽類APP中&#xff0c;用戶可匹配到AI機器人對手&#xff0c;在規定時…

PclSharp ——pcl的c#nuget包

簡介&#xff1a; NuGet Gallery | PclSharp 1.8.1.20180820-beta07 下載.NET Framework 4.5.2 Developer Pack&#xff1a; 下載 .NET Framework 4.5.2 Developer Pack Offline Installer 離線安裝nupkg&#xff1a; nupkg是visual studio 的NuGet Package的一個包文件 安…

【Unity筆記】Unity音視頻播放監聽器封裝筆記:VideoPlayer + AudioSource事件觸發與編輯器擴展

關鍵點 Unity VideoPlayer 播放結束事件Unity AudioSource 播放檢測 Unity音視頻播放監聽器封裝筆記&#xff1a;VideoPlayer AudioSource事件觸發與編輯器擴展 在 Unity 的多媒體開發中&#xff0c;我們經常需要監聽 VideoPlayer 或 AudioSource 的播放狀態&#xff0c;以便…

WPF常用技巧匯總

主要用于記錄工作中發現的一些問題和常見的解決方法。 此文會持續更新。 >abp new Evan.MyWpfApp -t wpf --old --framework .net8 1. 解決不同屏幕分辨率下的鋸齒問題 UseLayoutRounding"True" <Grid UseLayoutRounding"True"><Border Mar…

分數線降低,25西電馬克思主義學院(考研錄取情況)

1、馬克思主義學院各個方向 2、馬克思主義學院近三年復試分數線對比 學長、學姐分析 由表可看出&#xff1a; 1、馬克思主義理論25年相較于24年下降10分&#xff0c;為355分 3、25vs24推免/統招人數對比 學長、學姐分析 由表可看出&#xff1a; 1、 馬克思主義學院25年共接…

【Linux網絡】構建UDP服務器與字典翻譯系統

&#x1f4e2;博客主頁&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客倉庫&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;歡迎點贊 &#x1f44d; 收藏 ?留言 &#x1f4dd; 如有錯誤敬請指正&#xff01; &…

【項目管理】成本類計算 筆記

項目管理-相關文檔&#xff0c;希望互相學習&#xff0c;共同進步 風123456789&#xff5e;-CSDN博客 &#xff08;一&#xff09;知識總覽 項目管理知識域 知識點&#xff1a; &#xff08;項目管理概論、立項管理、十大知識域、配置與變更管理、績效域&#xff09; 對應&…

div(HTML標準元素)和view(微信小程序專用組件)的主要區別體

div&#xff08;HTML標準元素&#xff09;和view&#xff08;微信小程序專用組件&#xff09;的主要區別體現在以下方面&#xff1a; 一、應用場景與開發框架 ?適用平臺不同? div是HTML/CSS開發中通用的塊級元素&#xff0c;用于Web頁面布局?&#xff1b;view是微信小程序專…

【C++軟件實戰問題排查經驗分享】UI界面卡頓 | CPU占用高 | GDI對象泄漏 | 線程堵塞 系列問題排查總結

目錄 1、UI界面卡頓問題排查 2、軟件CPU占用高問題排查 3、UI界面顯示異常&#xff08;GDI對象泄漏導致窗口繪制異常&#xff09;問題排查 4、軟件線程堵塞&#xff08;包含線程死鎖&#xff09;問題排查 5、最后 C軟件異常排查從入門到精通系列教程&#xff08;核心精品專…

管理雜談——采石磯大捷的傳奇與啟示

南宋抗金史上&#xff0c;岳飛與岳家軍的鐵血傳奇家喻戶曉&#xff0c;但另一位力挽狂瀾的“文官戰神”卻常被忽視——他從未掌兵&#xff0c;卻在南宋存亡之際整合潰軍&#xff0c;以少勝多&#xff0c;締造采石磯大捷。此人正是虞允文。一介書生何以扭轉乾坤&#xff1f;他的…

動態規劃-零錢兌換

332.零錢兌換 給你一個整數數組 coins &#xff0c;表示不同面額的硬幣&#xff1b;以及一個整數 amount &#xff0c;表示總金額。計算并返回可以湊成總金額所需的 最少的硬幣個數 。如果沒有任何一種硬幣組合能組成總金額&#xff0c;返回 -1 。你可以認為每種硬幣的數量是無…

SpringAI+DeepSeek大模型應用開發——4 對話機器人

目錄??????? ??????????????項目初始化 pom文件 配置模型 ChatClient 同步調用 流式調用 日志功能 對接前端 解決跨域 會話記憶功能 ChatMemory 添加會話記憶功能 會話歷史 管理會話id 保存會話id 查詢會話歷史 完善會話記憶 定義可序列…

Java 關鍵字

本章列出了Java 語言的所有關鍵字和“類關鍵字的單詞”。 “受限關鍵字”是指&#xff0c;它們旨在模塊聲明中是關鍵字&#xff0c;在其他情況下則是標識符。 “受限標識符”是指&#xff0c;除非用在某些特定位置&#xff0c;否則他們只是標識符。例如&#xff0c;var一般都…

AI重塑網絡安全:機遇與威脅并存的“雙刃劍”時代

一、引言 人工智能&#xff08;AI&#xff09;技術的迅猛發展&#xff0c;正在深刻改變網絡安全行業的格局。從ChatGPT生成釣魚郵件到AI驅動的漏洞挖掘&#xff0c;從零信任架構的普及到安全大模型的實戰應用&#xff0c;AI既是攻擊者的“新武器”&#xff0c;也是防御者的“新…

網絡原理——UDP

1、 與TCP的關鍵區別 特性UDPTCP連接方式無連接面向連接可靠性不可靠可靠數據順序不保證順序保證順序傳輸速度更快相對較慢頭部開銷8字節20-60字節流量控制無有擁塞控制無有適用場景實時應用、廣播/多播可靠性要求高的應用 2、UDP 報文結構 報文結構大致可以分為首部和載荷&a…