1. 程式人生 > >編譯原理(七) 算符優先分析法(構造算符優先關係表演算法及C++實現)

編譯原理(七) 算符優先分析法(構造算符優先關係表演算法及C++實現)

概念簡述

移動歸約分析法:自底向上的語法分析方法,也稱為移動歸約分析法

  • 最易於實現的一種移動歸約分析方法,叫做算符優先分析法
  • 而更一般的移動歸約分析方法叫做LR分析法,LR分析法可以用作許多自動的語法分析器的生成器。

短語:文法G[S],αβδ是文法G的一個句型,S=>*αAδ且A=>+β則稱β是句型αβδ相對於非終結符A的短語。
直接短語:若有A +β則稱β是句型αβδ相對於該規則A→β的直接短語。
控制代碼:一個句型的最左直接短語稱為該句型的控制代碼。
其實作為一個常年和樹打交道的acmer來說,我覺得下面這種定義方法更容易理解….
短語:一棵子樹的所有葉子自左至右排列起來形成一個相對於子樹根的短語。
直接短語:

僅有父子兩代的一棵子樹,它的所有葉子自左至右排列起來所形成的符號串。
控制代碼:一個句型的分析樹中最左那棵只有父子兩代的子樹的所有葉子的自左至右排列。
算符文法的定義:

  • 所有產生式的右部都不是ε或兩個相鄰的非終結符
  • 設有一個文法G,如果G中沒有形如A→…BC…的產生式,其中B和C為非終結符,則稱G為算符文法(Operator Grammar)也稱OG文法.

算符優先文法的特點:

  • 一旦我們構造了算符優先語法分析器,就可以忽略原來的文法,棧中的非終結符僅僅作為與這些非終結符相關的屬性的佔位符
  • 難以處理像減號這樣有不同優先順序的符號
  • 由於分析的語言的文法和算符優先語法分析器本身的關係不是很緊密,所以不能肯定語法分析器接受的就是所期望的語言

演算法優先關係構造演算法

一、首先對於優先關係進行如下定義:

  • a的優先順序低於b
    a < b: 文法中有形如A→…aB…的產生式而B+b…或B+Cb…
  • a的優先順序等於b
    a = b: 文法中有形如A→…ab…或者A→…aBb…的產生式
  • a的優先順序高於b
    a > b: 文法中有形如A…Bb…的產生式,而B+…a或B+…aC
  • 算符的優先關係是有序的
    • 如果a > b,不能推出b < a
    • 如果a > b,有可能b > a
    • 如果a > b, b > c,不一定a > c
      根據這個大小關係的定義,我們知道為了確定兩個終止符的優先關係,我們需要知道它的在所有的產生式中和前後非終止符的關係,那麼我們做一個如(二)預處理:

二、定義並構造FIRSTVT和LASTVT兩個集合

FIRSTVT(P)={a|P+aP+Qa}
LASTVT(P)={a|P+aP+aQ}
FIRSTVT(P)直接根據定義遞迴的構造即可:
  • a) 若有產生式 P→a••• 或 p→Qa••• 
    則 a∈FIRSTVT(P)

  • b) 若有產生式 P→Q••• ,
    若 a∈FIRSTVT(Q)
    則 a∈FIRSTVT(P)

LASTVT(P)直接根據定義遞迴的構造即可:

  • a) 若有產生式 P→•••a 或 p→•••aQ 
    則 a∈LASTVT(P)

  • b) 若有產生式 P→•••Q ,
    若 a∈LASTVT(Q)
    則 a∈LASTVT(P)

程式碼實現見程式碼部分的make_first和make_last函式,是兩個簡單的遞迴實現。

三、利用FIRSTVT和LASTVT集合構造演算法優先關係表

FOR 每個產生式 P->X1 X2 ……Xn   
DO  FOR  i:=1  TO   n-1    DO      
      IF  X[i]和X[i+1]均為終結符
        THEN   置 X[i]=X[i+1]        
      IF  X[i]和X[i+2]均為終結符,X[i+1]為非終結符,i≤n-2,       
        THEN   置 X[i]=X[i+2]                
      IF  X[i]為終結符, 但X[i+1]為非終結符                                    
      THEN  FOR  FIRSTVT(X[i+1])中的每個a                                    
          DO  置 X[i]<a                
      IF  Xi為非終結符, 但X i+1 為終結符                                  
          THEN  FOR  LASTVT(X i )中的每個a
              DO   置 a>X[i+1]      

C++程式碼實現

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <map>
#include <set>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <cctype>
#define MAX 507

using namespace std;

class WF
{
    public:
    string left;
    vector<string> right;
    WF ( const string& str )
    {
        left = str;
    }
    void insert ( char str[] )
    {
        right.push_back(str);
    }
    void print ( )
    {
        printf ( "%s->%s" , left.c_str() , right[0].c_str() );
        for ( int i = 1 ; i < right.size() ; i++ )
            printf ( "|%s" , right[i].c_str() );
        puts("");
    }
};

char relation[MAX][MAX];
vector<char> VT;
vector<WF> VN_set;
map<string,int> VN_dic;
set<char> first[MAX];
set<char> last[MAX];
int used[MAX];
int vis[MAX];


void dfs (  int x )
{
    if ( vis[x] ) return;
    vis[x] = 1;
    string& left = VN_set[x].left;
    for ( int i = 0 ; i < VN_set[x].right.size() ; i++ )
    {
        string& str = VN_set[x].right[i];
        if ( isupper(str[0]) )
        {
            int y = VN_dic[str.substr(0,1)]-1;
            if ( str.length() > 1 && !isupper(str[1] ) )
                first[x].insert ( str[1] );
            dfs ( y );
            set<char>::iterator it = first[y].begin();
            for ( ; it!= first[y].end() ; it++ )
                first[x].insert ( *it );
        }
        else 
            first[x].insert ( str[0] );
    }
}

void make_first ( )
{
    memset ( vis , 0 , sizeof ( vis ) );
    for ( int i = 0 ; i < VN_set.size() ; i++ )
        if ( vis[i] ) continue;
        else dfs ( i );
#define DEBUG
#ifdef DEBUG
    puts("------------FIRSTVT集-------------------");
    for ( int i = 0 ; i < VN_set.size() ; i++ )
    {
        printf ( "%s : " , VN_set[i].left.c_str() );
        set<char>::iterator it = first[i].begin();
        for ( ; it!= first[i].end() ; it++ )
            printf ( "%c " , *it );
        puts ("" );
    }
#endif 
}

void dfs1 ( int x )
{
    if ( vis[x] ) return;
    vis[x] = 1;
    string& left = VN_set[x].left;
    for ( int i = 0 ; i < VN_set[x].right.size() ; i++ )
    {
        string& str = VN_set[x].right[i];
        int n = str.length() -1;
        if ( isupper(str[n] ) )
        {
            int y = VN_dic[str.substr(n,1)]-1;
            if ( str.length() > 1 && !isupper(str[n-1]) )
                last[x].insert ( str[1] );
            dfs1 ( y );
            set<char>::iterator it = last[y].begin();
            for ( ; it != last[y].end() ; it++ )
                last[x].insert ( *it );
        }
        else 
            last[x].insert ( str[n] );
    }
}


void make_last ( )
{
    memset ( vis , 0 , sizeof ( vis ) );
    for ( int i = 0 ; i < VN_set.size() ; i++ )
        if ( vis[i] ) continue;
        else dfs1 ( i );
#define DEBUG
#ifdef DEBUG
    puts("--------------LASTVT集---------------------");
    for ( int i = 0 ; i < VN_set.size() ; i++ )
    {
        printf ( "%s : " , VN_set[i].left.c_str() );
        set<char>::iterator it = last[i].begin();
        for ( ; it!= last[i].end() ; it++ )
            printf ( "%c " , *it );
        puts ("" );
    }
#endif
}

void make_table ( )
{
    for ( int i = 0 ; i < MAX ; i++ )
        for ( int j = 0 ; j < MAX ; j++ )
            relation[i][j] = ' ';
    for ( int i = 0 ; i < VN_set.size() ; i++ )
        for ( int j = 0 ; j < VN_set[i].right.size() ; j++ )
        {
            string& str = VN_set[i].right[j];
            for ( int k = 0 ; k < str.length()-1 ; k++ )
            {
                if ( !isupper(str[k]) && !isupper(str[k+1]) )
                    relation[str[k]][str[k+1]] = '=';
                if ( !isupper(str[k]) && isupper(str[k+1]) )
                {
                    int x = VN_dic[str.substr(k+1,1)]-1;
                    set<char>::iterator it = first[x].begin();
                    for ( ; it != first[x].end() ; it++ )
                        relation[str[k]][*it] = '<';
                }
                if ( isupper(str[k]) && !isupper(str[k+1]) )
                {
                    int x = VN_dic[str.substr(k,1)]-1;
                    set<char>::iterator it = last[x].begin();
                    for ( ; it != last[x].end() ; it++ )
                        relation[*it][str[k+1]] = '>';
                }
                if ( k > str.length()-2 ) continue;
                if ( !isupper(str[k]) && !isupper(str[k+2]) && isupper(str[k+1]) )
                    relation[str[k]][str[k+2]] = '=';
            }
        }
#define DEBUG
#ifdef DEBUG
    for ( int i = 0 ; i < VT.size()*5 ; i++ )
        printf ("-");
    printf ( "算符優先關係表" );
    for ( int i = 0 ; i < VT.size()*5 ; i++ )
        printf ( "-" );
    puts("");
    printf ( "|%8s|" , "" );
    for ( int i = 0 ; i < VT.size() ; i++ )
        printf ( "%5c%5s" , VT[i] , "|" );
    puts ("");
    for ( int i = 0 ; i < (VT.size()+1)*10 ; i++ )
        printf ("-");
    puts("");
    for ( int i = 0 ; i < VT.size() ; i++ )
    {
        printf ( "|%4c%5s" , VT[i] , "|");
        for ( int j = 0 ; j < VT.size() ; j++ )
            printf ( "%5c%5s" , relation[VT[i]][VT[j]] , "|" );
        puts ("");
        for ( int i = 0 ; i < (VT.size()+1)*10 ; i++ )
            printf ("-");
        puts("");
    }
#endif
}


int main ( )
{
    int n;
    char s[MAX];
    while ( ~scanf ( "%d" , &n ) )
    {
        memset ( used , 0 , sizeof ( used ) );
        for ( int i = 0 ; i < n ; i++ )
        {
            scanf ( "%s" , s );
            int len = strlen(s),j;
            for ( j = 0 ; j < len ; j++ )
                if ( s[j] == '-' ) 
                    break;
            s[j] = 0;
            if ( !VN_dic[s] )
            {
                VN_set.push_back ( WF(s) );
                VN_dic[s] = VN_set.size();
            }
            int x = VN_dic[s]-1;
            VN_set[x].insert ( s+j+2 );
            for ( int k = 0 ; k < j; k++ )
                if ( !isupper(s[k] ) )
                {
                    if ( used[s[k]] ) continue;
                    used[s[k]] = 1;
                    VT.push_back ( s[k] );
                }
            for ( int k = j+2 ; k < len; k++ )
                if ( !isupper(s[k] ) )
                {
                    if ( used[s[k]] ) continue;
                    VT.push_back ( s[k] );
                    used[s[k]] = VT.size();
                }   
        }
#define DEBUG
#ifdef DEBUG
        puts ("************VT集*******************");
        for ( int i = 0 ; i < VT.size() ; i++ )
            printf ( "%c " , VT[i] );
        puts ("");
        puts("*************產生式*****************");
        for ( int i = 0 ; i < VN_set.size() ; i++ )
            VN_set[i].print();
        puts("************************************");
#endif
        make_first();
        make_last();
        make_table();
    }
}

Input:
這裡寫圖片描述
Output:
這裡寫圖片描述

相關推薦

編譯原理() 優先分析(構造優先關係演算法C++實現)

概念簡述 移動歸約分析法:自底向上的語法分析方法,也稱為移動歸約分析法。 最易於實現的一種移動歸約分析方法,叫做算符優先分析法, 而更一般的移動歸約分析方法叫做LR分析法,LR分析法可以用作許多自動的語法分析器的生成器。 短語:文法G[S],αβδ是文

編譯原理(九) LR(0)文法分析(演算法描述和C++程式碼實現)

後期DEBUG發現make_set函式和make_go存在問題,於2015年12月4日更新了程式碼,見諒 概念梳理 最左推導:每一步替換最左邊的非終結符 最右推導:每一步替換最右邊的非終結符,最右推導稱為規範推導 短語:令G是一個文法,S是文法的開始符號

編譯原理(五)語法分析之自底向上分析優先分析

logs cnblogs div mar 分析法 clas pos block mark 語法分析之自頂向下分析 說明:以老師PPT為標準,借鑒部分教材內容,AlvinZH學習筆記。 先看看PPT吧! 引用說明 - 邵老師課堂PDF - 《編譯原理級編譯程序構造》 編譯

編譯原理——優先分析詳解

轉載地址 https://blog.csdn.net/qq_37977106/article/details/80301761 概述        算符優先分析法(Operator Prece

自上而下的分析——優先分析

概述        算符優先分析法(Operator Precedence Parse)是仿效四則運算的計算過程而構造的一種語法分析方法。算符優先分析法的關鍵是比較兩個相繼出現的終結符的優先順序而決定應採取的動作。  &n

編譯原理】自下而上的語法分析之LR分析

LR分析器是一種由下而上(bottom-up)的上下文無關語法分析器。LR意指由左(Left)至右處理輸入字串,並以最右邊優先派生(Right derivation)的推導順序(相對於LL分析器)建構語法樹。能以此方式分析的語法稱為LR語法。而在LR(k)這樣的名稱中,

編譯原理】自上而下的語法分析之預測分析

最近學習了LL(1)文法的預測分析法,這種方法不同於遞迴下降分析法,可直觀通過棧檢視語法分析過程,現總結如下. 1. 定義 第一個L表示從左到右掃描輸入串,第二個L表示最左推導,1表示每步只需向前看一個符號。 LL(1)文法定義: 對於文法的任意兩個不同的產生式A-&g

串模式匹配KMP中的next數組C++實現

完整 牛客網 names 數據 代碼 str 關於 clu .com 一、問題描述: 對於兩個字符串S、T,找到T在S中第一次出現的起始位置,若T未在S中出現,則返回-1。 二、輸入描述: 兩個字符串S、T。 三、輸出描述: 字符串T在S中第一次出現的起始位置,若未出現,則

編譯原理(4)---語義分析(未完成)

導讀 1。逆波蘭表示式 (1)字尾表示式 (2)語法制導生成字尾式 2。三元式和樹 (1)三元式 (2)樹 3。四元式 (1)四元式 (2)算術表示式和賦值句翻譯為四元式 (3)布林表示式翻譯為四元式 4。控制語句的翻譯 (1)標號和轉移語句 (2)條件語句 (3)分叉語句

編譯原理(3)---語法分析

導讀 1。文法 (1)分類 (2)上下文無關文法 (3)語法樹 2。自上而下語法分析 (1)自上而下面臨的兩大問題 (2)預測分析 (3)怎樣實現預測分析 (4)LL(1)文法 3。自下而上語法分析 (1)直觀算符優先分析法 (2)規範歸約與算符優先文法 (3)優先函式 4。L

編譯原理 第三章 詞法分析(上)

3.1.1 為什麼編譯器要把詞法分析和語法分析分開 3.1.2 詞法單元、模式和詞素(重要)   例:   3.1.3 詞法單元的屬性(重要)   詞法單元的屬性是用來記錄相對應的詞素的一些相關屬性資訊。 例: int x = 10 + 20

編譯原理 第三章 詞法分析(下)

3.6 有窮自動機(非常重要) 3.6.1 不確定的有窮自動機(重要) 例:  狀態0是開始狀態, 在狀態0上輸入符號b會進入狀態0,輸入a可能進去狀態0也有可能進入狀態1。所以對於狀態0來說一個確定的輸入符號a他有兩種離開狀態,這就是一種不確定的狀態。   &nbs

現代編譯原理——第二章:語法分析之LL(K)

  轉自: http://www.cnblogs.com/BlackWalnut/p/4472122.html   LL(K)語法分析技術是建立在預測分析的技術之上的。我們先來了解預測分析技術。考慮以下文法:         當使用該文法對(1*2-3)+4和(1

編譯原理學習筆記之自上而下分析

一、自上而下分析法 從根部開始構造語法樹 自上而下分析法不能因為左遞迴存在而陷入死迴圈,不能產生回溯,即每一步推導的產生式必須是唯一的 1.消除左遞迴 左遞迴 形如A->Aa |b ,這種在建立分析樹時一直在左子樹死迴圈,因為推導的結果中最左還是A 或者多個

編譯原理第四章-語法分析

語法分析的知識要點 1)最左推導 總是選擇每個句型的最左非終結符進行替換; 根據輸入流中的下一個終結符,選擇最左非終結符的一個候選式; 自頂向下的語法分析採用最左推導方式。 2)非終結符的後繼符號集 可能在某個句型中緊跟在A後邊的終結符a的集合,記為FOLLOW

編譯原理--遞迴下降語法分析原始碼(C Language)

    花了一晚上寫的編譯原理作業,遞迴下降語法分析,實現'i'字元進行的+ - * / 操作,錯誤跳出(未完善錯誤提示),語法分析過程.  現把源程式貼出來,時間倉促,難免有錯誤請給與指正.   執行,例如輸入:i+i#                           

哈工大編譯原理第一次實驗--詞法分析(Java版本)

1.在判斷空行的時候,java裡面用 line == "" 不好使,除錯發現進不去if,然後用line.equals("")就好使。 2.java標準化輸出,可以有:System.out.printf("%-10s\t<ERROR:識別符號重複!>\n",tok

編譯原理第四章—語法分析

第四章1、知識點圖重點記憶:語法分析器功能:語法分析的工作:       判斷一個輸入串是否符合語法規則如何判斷?       從文法的起始符出發進行句子的推導,即自上而下的分析從句子本身出發,進行歸約,看能否把句子規約為到起始符,即自下而上的規約分析的結果:構造一棵語法樹自

編譯原理簡單的LALR(1)分析構造

閒著無聊,,,,寫了一個簡單的LALR(1)分析表的構造,就當是複習了 #include <cstdio> #include <cstring> #include <map> #include <vector> #includ

編譯原理 第五章 語法分析----自下而上分析

一、知識總結               自下而上分析是從輸入串開始,逐步進行規約,直至規約到文法的開始符號,就是一種“移進-規約”法。自上而下分析的中心問題是怎樣判斷棧訂單符號串的可歸約性以及如何規約。解決方案是規範規約。所謂規範規約就是每一步都把控制代碼換成它對應的產生式