コンパイラの原理実験(3)語彙文法分析の包括的な設計

著作権表示:この記事はオリジナルの記事であり、著作権はGeekerstarに帰属します

この記事へのリンク:http : //www.geekerstar.com/technology/105.html

特記記事以外は転載を歓迎しますが、出典を必ず明記してください。形式は上記のとおりですので、どうぞよろしくお願いいたします。

1。概要

特定の高水準言語(C / C ++、Javaなど)を使用して、字句解析および構文解析の機能を実現します。

2実験の目的

  1. 字句解析と文法解析の原理と方法を理解して習得します。

  2. 特定の言語で字句解析および文法解析プログラムを実装できる。

  3. 基本的な概念、原則、コンパイルの方法を完全かつ明確に理解し、それらを正確かつ上手に使用できるようにします。

3実験の説明

3.1実験要件

文法解析プログラムのサブルーチンのメソッドとして字句解析プログラムを使用して、字句解析と文法解析を実現します。

具体的な要件は次のとおりです。

  • 字句解析プログラムは、構文解析プログラムのサブルーチンです。

  • 入力データ:プログラムセグメント。

  • 出力結果:文法解析結果(エラーリストを含む)。

3.2この実験の概要

これに基づいてC ++構文アナライザーで書かれた実験は、再帰的降下
構文チェックと構造分析手順、部分
分析によって提供される字句分析プログラムの単語シーケンスを使用します。PL / 0言語の機能はシンプルで、明確に構造化され、読み取り可能ですが、一般的な高水準
はプログラミング言語の一部である必要があるため、
高水準言語コンパイラの実施形態に完全に対応するコンパイラPL / 0言語を使用して、基本的なメソッドとテクノロジー。したがって、語彙分析にはPL / 0言語が使用されます。

4テクニカル分析

4.1字句解析

この実験では、主に基本的なデータ構造分析、C ++プログラミング技術、コンパイルの原理などを使用します。コンパイル
の基本的な概念は、字句解析と構文解析の2つの部分を理解することで理解できます。

  • 字句解析

各トークンのソースを識別する字句アナライザの字句ルール
各トークンはクラスワードを表しますソース共通マークは、キーワード、識別子、リテラル、
特殊記号など、いくつかのカテゴリに分類できます字句解析プログラムの入力はソースプログラムであり、出力は認識されたトークンストリームです。字句解析
タスクは、トークンのストリームへのソースファイルストリームの特性です。これは、コンパイルプロセスの最初の段階です。その
主なタスクは、ソースの各文字列記述の左から右に記述
され、その中の1つの単語によって識別され、それを解析のために内部の単語エンコード形式のシンボル文字列出力に変換します。

一言で言えば、文法は一般的にその作業中に次のタスクを完了する必要があります。

(1)ソースプログラム内の各単語記号を識別し、それを内部コード形式に変換します。

(2)不要な空白文字、復帰文字、およびその他の重要でない文字を削除します。

(3)コメントを削除する。

(4)字句チェックを実行し、見つかったエラーを報告します。

さらに、組織のコンパイルされたワークフローに応じて、一部のコンパイラーは字句解析中にだけでなく
、識別された識別子完成させて、シンボルテーブルの作業にログオンします。

  • 解析:

構文解析はコンパイルプロセスの中核です。タスク分析は、文法
文法構造のルールに従ってソースコードを分析し、ソースコード構文チェックの分析では、構文エラーがない場合
、正しい文法構造、セマンティックを提供します分析とコード生成の準備をします。

現在、文法解析にはさまざまな種類があり、大別するとトップダウンとボトムアップの2つのカテゴリに分類されます。
上から下に、LL(1)分析法と再帰降下分析法に分けられます。ボトムアップは、単純な
単一優先度の文法、演算子の優先度の文法、LR(K)分析に分けられます。以下では、主にボトムアップ
LR(K)分析法を紹介します。

ボトムアップ分析法は、シフト削減分析法とも呼ばれます。そのアイデアは
、左から右へのスキャンから入力シンボル文字列を実現し
、ハンドルがスタックシンボル文字列文で構成されると、文字を1つずつLIFOスタックに入力し、サイドエッジを分析に入力します(これはハンドルは、プロダクションの右部分に対応します)、
対応する右部分の代わりに文法シンボル非終端記号のプロダクション文字列の左部分に対応します。
このプロセスは、文法の開始記号、つまり実際に
認識された入力文字列が文法内の文である場合にのみ、分析がスタックへの削減に成功するまで繰り返されました。それ以外の場合、障害分析は、シンボル入力文字列が
の文法ではないことを表します。これは構文エラーである必要があります。

上記のコンパイルの関連知識と、C ++プログラミング言語の知識および語彙パーサーを作成するためのいくつかのデータ構造に基づく。

5設計と実装

5.1設計アイデア

再帰的降下パーサーの準備は、
構文チェックと構造分析のシーケンスによって提供される語彙分析プログラムのための言葉を達成します。C ++を使用して、再帰降下パーサーとPL / 0言語
構文分析を作成します。中核となるアイデアは、文法の展開に従って、状態の最初から開始し、
分析が完了するまで状態を段階的に分析します。この期間中に不一致の状態(構文エラー)がある場合は、分析を停止します。もちろん、
実際のパーサーエラー回復メカニズムは、他の文法エラーを見つける必要があります。つまり、新聞
はより多くの構文エラーを報告しました。また、構文解析を行うには、構文解析の字句解析
結果を使用して、まず字句解析を行う必要があります。

拡張BNFは次のように表されます。

再帰的降下パーサーの準備は、
構文チェックと構造分析のシーケンスによって提供される語彙分析プログラムのための言葉を達成します。

C ++を使用して再帰降下分析プログラムを作成し、PL / 0言語で文法分析を実行します。

\ <program> :: = begin \ <ステートメント文字列> end

\ <ステートメント文字列>:= \ <ステートメント> {; \ <ステートメント>}

\ <ステートメント>:= \ <割り当てステートメント>

\ <割り当てステートメント> :: = ID:= \ <式>

\ <式> :: = \ <アイテム> {+ \ <アイテム> |-\ <アイテム>}

\ <item> :: = \ <factor> {* \ <factor> | / \ <factor>

\ <factor> :: = ID | NUM |(\ <expression>)

文字列を入力し、「#」で終了します。文法的に正しい文の場合、成功メッセージが出力され、
「構文解析が成功しました!」と出力されます。それ以外の場合は、「構文解析エラー(エラー理由)」が出力されます。

例えば:

begin a:= 4; b:= 2 * 3; c:= a + b end#と入力します。

出力構文分析が成功しました!

x:= a + b * c endと入力します#

出力が欠落し始めています!

単語記号 種コード
ベギン 1
もし 2
その後
ながら 4
行う 5
終わり 6
識別子 10
デジタル 20
+ 13
- 14
* 15
/ 16
17
:= 18
\ < 20
\ <> 21
\ <= 22
> 23
> = 24
= 25
; 26日
27日
28
0

5.2実装方法

この実験では、C ++コーディングを使用しています。このコーディングでは、主に次の関数と関数が記述されています。

Void cifa() //词法分析

Void fun_yufa() //判断语法是否有错误

Void fun_op() //处理运算符(\*和/)

Void exp() //处理运算符(+和-)

Void fun_yuju() //判断是否有语句错误(:=)

Void fun_end() //判断程序是否结束

Void yufa() //采用递归下降的语法分析

このうち、cifa()は字句解析を実行し、yufa()を呼び出して字句解析の結果を使って文法解析を実行します。

833655145ca0bdfcc389a76dd9b2e8d3.png

図1字句解析コードの一部

4a32ed6124fc05f333997cecf27a248f.png

図2構文解析機能の一部

5.3テストケース

プロジェクト/ソフトウェア 字句パーサー プログラムのバージョン V1.0
汎用モジュール名 字句解析モジュール 編集者 XX
ユースケース番号 T1.0 準備時間 207.11.20
特徴 字句文法定義の分析と判断
テスト目的 語彙文法が正しいかどうかを判断する
テストデータ 1:開始a:= 3; b:= 2 * 4; c:= a + b;終了#2:a:= 3; b:= 2 * 4; c:= a + b; end#3:begin a:= 3; b:= 2 * 4; c:= a + b;
テストケース 動作説明 コード 望ましい結果 実績 試験状況
1 最初のコードを入力してください 開始a:= 3; b:= 2 * 4; c:= a + b;終了# 構文は正しい 構文は正しい 良い
2 2番目のコードを入力してください a:=3;b:=2*4;c:=a+b; end # 缺少Begin 缺少begin 良好
3 输入第三段代码 begin a:=3;b:=2*4;c:=a+b; # 缺少结束符 缺少结束符 良好

5.4 实验结果及分析

输入一段PL/0语言,比如输入:begin a:=3;b:=2*4;c:=a+b; end #

此段代码语法是正确的,所以经过词法语法分析输出的结果应该是:语
法分析正确!如下图所示:

字句解析結果グラフ

图3 实验结果1

当我们漏掉begin,语法分析器应该检测出并输出:缺少begin!如下图:

テストの最終結果

图4 实验结果2

6 总结

经过这次实验,我对编译原理有了更近一步的理解,让我知道了词法分
析的功能是输出把它组织成单个程序,让我了解到如何设计、编写并调试词
法分析程序,对语法规则有明确的定义;编写的分析程序能够进行正确的语
法分析;对于遇到的语法错误,能够做出简单的错误处理,给出简单的错误
提示,保证顺利完成语法分析过程,并且通过实验的练习,加强了对基本概
念的理解和应用,对以后的学习也打下了基础。目前程序也存在着少量不足
之处,主要是语法分析部分还有不完善的地方,错误报告也有待改进,希望
在经过进一步的学习后,这些问题能逐步解决。

7 参考文献

1.互联网:百度,CSDN博客。

2.教材:《编译技术》张莉 高等教育出版社。

3.教材:C++ primer plus(第六版)

8 代码展示

#include "cstdio"
#include "string"
#include "iostream"
#include "algorithm"
#include "cstring"
using namespace std;

char str[1000];            //从键盘输入
char bzf[8];      //判断是否是关键字
char ch;
char *keyword[6]={
   
   "begin","if","then","while","do","end"};
int num,p,m,n,sum;
int x;


void cifa()   //词法分析
{
    sum=0;
    for(m=0;m<8;m++)
        bzf[m++]=NULL;
    m=0;
    ch=str[p++];
    while(ch==' ')       //去掉空格
        ch=str[p++];
    if(((ch<='z')&&(ch>='a'))||((ch<='Z')&&(ch>='A')))  //标识符
    {
        while(((ch<='z')&&(ch>='a'))||((ch<='Z')&&(ch>='A'))||((ch>='0')&&(ch<='9')))
        {
            bzf[m++]=ch;
            ch=str[p++];
        }
        p--;
        num=10;
        bzf[m++]='\0';
        for(n=0;n<6;n++)
        if(strcmp(bzf,keyword[n])==0)
        {
            num=n+1;
            break;
        }
    }
    else if((ch>='0')&&(ch<='9'))  //数字
    {
        while((ch>='0')&&(ch<='9'))
        {
            sum=sum*10+ch-'0';
            ch=str[p++];
        }
        p--;
        num=11;
    }
    else
    switch(ch)     //符号
    {
        case '<':
            m=0;
            ch=str[p++];
            if(ch=='>')
            {
                num=21;
            }
            else if(ch=='=')
            {
                num=22;
            }
            else
            {
                num=20;
                p--;
            }
        break;

        case '>':
            m=0;
            ch=str[p++];
            if(ch=='=')
            {
                num=24;
            }
            else
            {
                num=23;
                p--;
            }
        break;

        case ':':
            m=0;
            ch=str[p++];
            if(ch=='=')
            {
                num=18;
            }
            else
            {
                num=17;
                p--;
            }
            break;

        case '+':
            num=13;
        break;

        case '-':
            num=14;
        break;

        case '*':
            num=15;
        break;

        case '/':
            num=16;
        break;

        case '(':
            num=27;
        break;

        case ')':
            num=28;
        break;

        case '=':
            num=25;
        break;

        case ';':
            num=26;
        break;

        case '#':
            num=0;
        break;

        default:
            num=-1;
        break;
    }
}
void term();
void exp();
void fun_yufa()   //判断语法是否错误
{
    if((num==10)||(num==11))//关键字,数字
    {
        cifa();
    }
    else if(num==27)
    {
        cifa();
        exp();

        if(num==28)
        {
            cifa();          /*读下一个单词符号*/
        }
        else
        {
            printf("缺少‘(’\n");
            x=1;
        }
    }
    else
    {
        printf("语法错误\n");
        x=1;
    }
    return;
}
void fun_op()  //处理运算符
{
    fun_yufa();
    while((num==15)||(num==16))//  '*'和'/'
    {
        cifa();             /*读下一个单词符号*/
        fun_yufa();
    }
    return;
}

void exp()   //处理运算符
{
    fun_op();
    while((num==13)||(num==14))   //+和-
    {
        cifa();               /*读下一个单词符号*/
        fun_op();
    }

    return;
}


void fun_yuju()  //判断是否有语句错误
{
    if(num==10)
    {
        cifa();        /*读下一个单词符号*/
        if(num==18)
        {
            cifa();      /*读下一个单词符号*/
            exp();              }
        else
        {
            printf("':='错误\n");
            x=1;
        }
    }
    else
    {
        printf("语法错误!\n");
        x=1;
    }

    return;
}

void fun_end()  //判断程序结束的标志
{
    fun_yuju();         /*调用函数statement();*/

    while(num==26)
    {
        cifa();          /*读下一个单词符号*/
        if(num!=6)
            fun_yuju();          /*调用函数statement();*/
    }

    return;
}

void yufa()  //递归下降语法分析
{
    if(num==1)
    {
        cifa();
        fun_end();
        if(num==6)
        {
            cifa();
            if((num==0)&&(x==0))
            printf("语法分析正确!\n");
        }
        else
        {
            if(x!=1) printf("缺少end!\n");
            x=1;
        }
    }
    else
    {
        printf("缺少begin!\n");
        x=1;
    }

    return;
}

int main()
{
    p=x=0;
    printf("请输入一段语句以#结束: \n");
    do
    {
        scanf("%c",&ch);
        str[p++]=ch;
    }while(ch!='#');
    p=0;
    cifa();
    yufa();
    return 0;
}

版权声明:本文为原创文章,版权归 Geekerstar 所有。

本文链接:http://www.geekerstar.com/technology/105.html

除了有特殊标注文章外欢迎转载,但请务必标明出处,格式如上,谢谢合作。

おすすめ

転載: blog.csdn.net/geekerstar/article/details/79518312