词法、语法、语义分析全过程学习

(1)      设计思想

语义分析对象重点考虑经过语法分析后已是正确的语法范畴。在实验二或实验三“语法分析器”的里面添加 PL/0 语言“表达式”。部分的语义处理,输出表达式的中间代码,计算表达式的语义值。中间代码用四元式序列表示。

(2)      算法流程

源语言程序首先翻译成一种特殊形式的中间语言代码形式,并对其进行优化,然后再将它翻译成最终的目标代码。

中间代码

源程序的一种内部表示,不依赖目标机的结构,复杂性介于源语言和机器语言之间。

中间语言/中间代码的优点

1、逻辑结构清楚;

2、利于不同目标机上实现同一种语言;

3、利于进行与机器无关的优化;


源代码:

#include<stdio.h>

#include<string.h>

#include<conio.h>

#include<malloc.h>

#include<stdlib.h>

struct quad
{
    char result[12];
    char ag1[12];
    char op[12];
    char ag2[12];
};

struct quad quad[30];
int count=0;
char *expression(void);
char prog[200],token[9];//function长度为8,加上'\0',所以数组最小大小应该为9
char ch;
int syn,p,m,n,sum=0;
int kk=0,k=0;
char *rwtab[6]= {"function","if","then","while","do","endfunc"};

void scaner()
{
    m=0;
    ch=prog[p++];//ch为读入第一个数据(一个新的字符串中第一个数据,并不代表整个文本第一个数据)
    for(n=0; n<8; n++) //初始化token数组
        token[n]='\0';
    while(ch==' ')
        ch=prog[p++]; //跳过空格
    if((ch>='a' && ch<='z')||(ch>='A' && ch<='Z'))//如果读入的第一个数据为字符 (字符串先存放在token数组中)
    {
        while((ch>='a' && ch<='z')||(ch>='A' && ch<='Z')||(ch>='0'&&ch<='9'))
        {
            token[m++]=ch;//暂时存放在token数组中
            ch=prog[p++];
        }//字符串
        token[m++]='\0';
        p--;//使得prog[p]被ch读入的最后一个字符
        syn=10;//用来区分输入数据类型
        for(n=0; n<6; n++)
        {
            if(strcmp(token,rwtab[n])==0)//判断读入字符串是否为程序中定义的特殊字符串
            {
                syn=n+1;//function:1,if:2,then:3,while:4,do:5,endfunc:6
                break;
            }
        }
    }
    else if(ch>='0' && ch<='9')//读入第一个数据为数字(读入为数字,直接经过sum运算,结果存放在sum中)
    {
        sum=0;
        while(ch>='0' && ch<='9')
        {
            sum=sum*10+ch-'0';//将输入数字转换成10进制数
            ch=prog[p++];
        }
        p--;//使得prog[p]被ch读入的最后一个数字
        syn=11;//若为数字,则,syn为11
    }
    else switch(ch)//读入第一个数据为运算符(运算符先存放在token数组中)
        {
        case '<':
            m=0;
            token[m++]=ch;//token[0]='<',m=1
            ch=prog[++p];//ch取下一个数据
            if(ch=='=')//<=
            {
                syn=22;
                //token[m+1]=ch;这一句的意思是token[2]=ch,错误,修改如下
                token[m++]=ch;//token[1]='='
            }
            else//<
            {
                syn=20;
                ch=prog[--p];//ch='<'
            }
            break;
        case '>':
            m=0;
            token[m++]=ch;
            ch=prog[++p];
            if(ch=='=')
            {
                syn=24;
                token[m++]=ch;
            }
            else
            {
                syn=23;
                ch=prog[--p];
            }
            break;
        case '=':
            m=0;
            token[m++]=ch;
            ch=prog[++p];
            if(ch=='=')
            {
                syn=25;
                token[m++]=ch;
            }
            else
            {
                syn=18;
                ch=prog[--p];
            }
            break;
        case '!':
            m=0;
            token[m++]=ch;
            ch=prog[++p];
            if(ch=='=')
            {
                syn=22;
                token[m++]=ch;
            }
            else syn=-1;
            break;
        case '+':
            syn=13;
            token[0]=ch;
            break;
        case '-':
            syn=14;
            token[0]=ch;
            break;
        case '*':
            syn=15;
            token[0]=ch;
            break;
        case '/':
            syn=16;
            token[0]=ch;
            break;
        case ';':
            syn=26;
            token[0]=ch;
            break;
        case '(':
            syn=27;
            token[0]=ch;
            break;
        case ')':
            syn=28;
            token[0]=ch;
            break;
        case '#':
            syn=0;
            token[0]=ch;
            break;
        default:
            syn=-1;
        }
}

/*该函数的功能是生成一个三地址语句送到四元式表中。*/
void emit(char *result,char *ag1,char *op,char *ag2)
{
    strcpy(quad[count].result,result);
    strcpy(quad[count].ag1,ag1);
    strcpy(quad[count].op,op);
    strcpy(quad[count].ag2,ag2);
    count++;
    return;
}


/*该函数回送一个新的临时变量名,临时变量名产生的顺序为t1,t2,…*/
char *newtemp()
{
//k初值为0
    char *p;
    char m[8];
    p=(char *)malloc(8);
    k++;//假设第一次调用该函数,此时k=1;
    itoa(k,m,10);//m=1;
    strcpy(p+1,m);//把m的值复制到p指针指向的地址后面一个字节开始的内存中,p[1]=1
    p[0]='t';
    return(p);
}

/*factor函数:
检查是否标识符
    如果是,调用scaner函数,
    如果不是,检查是否是数值,
        如果是,调用scaner函数,
        如果不是,检查是否是'(',
            如果不是,进行出错处理,
            如果是,调用scaner函数,再调用expression()函数,返回后检查是否是')',
                如果不是,进行出错处理
                如果是,调用scaner函数,返回*/
char *factor(void)
{
    char *fplace;
    fplace=(char *)malloc(12);
    strcpy(fplace," ");//初始化fplace
    if(syn==10)//该数据单位为字符串
    {
        strcpy(fplace,token);//将该字符串赋值给fplace
        scaner();//获取下一个数据单位
    }
    else if(syn==11)//该数据单位为数字
    {
        itoa(sum,fplace,10);//将sum的值以10进制的形式存放在fplace中
        scaner();//获取下一个数据单位
    }
    else if(syn==27)//该数据单位为(
    {
        scaner();//获取下一个数据单位
        fplace=expression();//调用expression(其实就是获取完下一个数据单位后,在factor函数中对其分析,当下一个数据单位不为(,获取下一个数据单位,且执行下一句)
        if(syn==28)//如果下一个数据单位为')'
            scaner();//直接获取下一个数据单位
        else
        {
            printf("\n')'错误");
            kk=1; //标志
        }
    }
    else
    {
        printf("\n')'错误");
        kk=1;
    }
    return (fplace);//返回数据单位的内容,如果是(,则返回(后面一个数据单位的内容
}

/*term()函数的作用是判断输入的是否是由'*''/'连接成的因式,并将对应的四元式保存并输出*/
char *term(void)
{
    char *tp,*ep2,*eplace,*tt;
    tp=(char *)malloc(12);
    ep2=(char *)malloc(12);
    eplace=(char *)malloc(12);
    tt=(char *)malloc(12);
    strcpy(eplace,factor());//调用factor,eplace为上一个数据单位的内容
    while(syn==15||syn==16)//该数据单位为'*'或者'/'
    {
        if(syn==15)//如果为'*'
        {
            tt[0]='*';
            tt[1]='\0';//tt存放该数据单位的内容
        }
        else if(syn==16)//如果为'/'
        {
            tt[0]='/';
            tt[1]='\0';//tt存放该数据单位的内容
        }
        scaner();//获取下一个数据单位
        strcpy(ep2,factor());//调用factor()函数,并将函数返回值赋值给ep2(字符串,数字,'(',出错)
        strcpy(tp,newtemp());//调用newtemp()函数,将该函数返回值赋值给tp,tp为临时变量名
        emit(tp,eplace,tt,ep2);//调用emit函数,生成一个三地址语句存放在四元式表中
        strcpy(eplace,tp);//eplace为四元式
    }
    return(eplace);
}

char *expression(void)
{
    char *tp,*ep2,*eplace,*tt;
    tp=(char *)malloc(12);
    ep2=(char *)malloc(12);
    eplace=(char *)malloc(12);
    tt=(char *)malloc(12);
    strcpy(eplace,term());//调用term函数,将四元式的内容赋值给eplace
    while(syn ==13 || syn==14) //当它不为*或者/时,接着判断是否为+或者-
    {
        if(syn==13)
        {
            tt[0]='+';
            tt[1]='\0';
        }
        else if(syn==14)
        {
            tt[0]='-';
            tt[1]='\0';
        }
        scaner();
        strcpy(ep2,term());
        strcpy(tp,newtemp());
        emit(tp,eplace,tt,ep2);
        strcpy(eplace,tp);
    }
    return(eplace);//返回对应四元式的值
}

int statement()
{
    char tt[8],eplace[8];
    int schain=0;
    switch(syn)
    {
    case 10:
        strcpy(tt,token);//若第二个数据单位的syn为10,即字符串(as1),将第二个数据单位的内容复制给tt
        scaner();//再取下一个数据单位,并获得该数据单位的syn
        if(syn==18)//第三个数据单位为'='
        {
            scaner();//再取下一个数据单位,并获得该数据单位的syn(第四个数据单位)
            strcpy(eplace,expression());//调用expression,如果为+或者-,返回对应四元式的值,如果为*或者/,则返回其四元式的值
            emit(tt,eplace," "," ");
            schain=0;
        }
        else
        {
            printf("\n缺少赋值句\n");
            kk=1;
        }
        break;
    }
    return(schain);
}

int yucu()
{
    int schain=0;
    schain=statement();//调用statement()函数,
    while(syn==26)//该数据单位为;
    {
        scaner();
        schain=statement();
    }
    return(schain);
}

int lrparser()
{
    int schain=0;
    kk=0;
    if(syn==1)//输入数据为function
    {
        scaner();//因为p停留在上一次扫描结束的地方,所以再次调用该函数ch可以获取下一个数据单位,得到该数据单位的syn
        schain=yucu();//调用yucu()函数
        if(syn==0)//判断 #
        {
            scaner();
            if(syn==-1 && kk==0)
                printf("\n ......语法,语义分析成功! \n");
        }
        else
        {
            if(kk!=0)//endfunc
            {
                printf("\n缺少endfunc\n");
                kk=1;
            }
        }
    }
    else
    {
        printf("\n缺少function\n");
        kk=1;
    }
    return(schain);
}

void main()
{
    int i;
    p=0;
    FILE *fp;

    if((fp=fopen("input.txt","r"))==NULL)
    {
        printf("\n 文件打开失败:\n");
        exit(0);
    }
    do
    {
        ch=fgetc(fp);
        prog[p++]=ch;
    }
    while(ch!='#');  //将输入字符串赋值到prog数组
    p=0; //使得ch从第一个字符开始获取
    printf("种别码 单词符号\n");
    do
    {
        scaner(); //扫描输入的数据,并作出类型判断(以字符串为单位)
        switch(syn)
        {
        case 11:
            printf("%-3d %d\n",syn,sum);
            break; //输入为数字,打印出它的syn与大小
        case -1:
            printf("词法分析失败,程序终止!\n");
            return; //输入数据不正确
        default:
            printf("%-3d %s\n",syn,token); //字符串,运算符
        }
    }
    while(syn!=0); //读入所有输入的数据(只是判断输入数据是否正确,并没有进行语法,语义分析操作)
    printf("词法分析成功,按任意键进行语法、语义分析");
    getch();//按任意键,程序往下执行
    p=0;//词法分析后,重新对ch赋值,使得ch从第一个字符开始获取
    scaner();//扫描第一个单位,判断其类型
    lrparser();//将将 第一个输入字符串 翻译为四元式
    if(kk!=0)
    {
        printf("语法分析失败,程序终止!");
        return;
    }
    printf("\n三地址指令如下:\n");
    for(i=0; i<count; i++)
    {
        printf("%s=",quad[i].result);
        printf("%s",quad[i].ag1);
        printf("%s",quad[i].op);
        printf("%s\n",quad[i].ag2);
    }
    getch();
    return;
}

源码转载于前人博客,借鉴学习并修改部分代码。链接:点击打开链接


使用方法:

在工程目录下或与该.c文件平行目录下创建一个input.txt文件。

(举例)内容加入:function result = a * (b + c)#  endfunc 

输入可加空格,但不可回车换行。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
附录c 编译程序实验 实验目的:用c语言对一个简单语言的子集编制一个一遍扫描的编译程序,以加深对编译原理的理解,掌握编译程序的实现方法和技术。 语法分析 C2.1 实验目的 编制一个递归下降分析程序,实现对词法分析程序所提供的单词序列的语法检查和结构分析. C2.2 实验要求 利用C语言编制递归下降分析程序,并对简单语言进行语法分析. C2.2.1待分析的简单语言的语法 实验目的 通过上机实习,加深对语法制导翻译原理的理解,掌握将语法分析所识别的语法成分变换为中间代码语义翻译方法. 实验要求 采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。 实验的输入和输出 输入是语法分析提供的正确的单词串,输出为三地址指令形式的四元式序列。 例如:对于语句串 begin a:=2+3*4;x:=(a+b)/c end# 输出的三地址指令如下: (1) t1=3*4 (2) t2=2+t1 (3) a=t2 (4) t3=a+b (5) t4=t3/c (6) x=t4 算法思想 1设置语义过程 (1) emit(char *result,char *arg1,char *op,char *ag2) 该函数功能是生成一个三地址语句送到四元式表中。 四元式表的结构如下: struct {char result[8]; char ag1[8]; char op[8]; char ag2[8]; }quad[20]; (2)char *newtemp() 该函数回送一个新的临时变量名,临时变量名产生的顺序为T1,T2,…. Char *newtemp(void) { char *p; char m[8]; p=(char *)malloc(8); k++; itoa(k,m,10); strcpy(p+1,m); p[0]=’t’; return(p); } (2)主程序示意图如图c.10所示。 (2) 函数lrparser在原来语法分析的基础上插入相应的语义动作:将输入串翻译成四元式序列。在实验中我们只对表达式、赋值语句进行翻译。 语义分析程序的C语言程序框架 int lrparser() { int schain=0; kk=0; if(syn=1) { 读下一个单词符号; schain=yucu; /调用语句串分析函数进行分析/ if(syn=6) { 读下一个单词符号; if(syn=0 && (kk==0)) 输出(“success”); } else { if(kk!=1 ) 输出 ‘缺end’ 错误;kk=1;} else{输出’begin’错误;kk=1;} } return(schain); int yucu() { int schain=0; schain=statement();/调用语句分析函数进行分析/ while(syn=26) {读下一个单词符号; schain=statement(); /调用语句分析函数进行分析/ } return(schain); } int statement() { char tt[8],eplace[8]; int schain=0; {switch(syn) {case 10: strcpy(tt,token); scanner(); if(syn=18) {读下一个单词符号; strcpy(eplace,expression()); emit(tt,eplace,””,””); schain=0; } else {输出’缺少赋值号’的错误;kk=1; } return(schain); break; } } char *expression(void) {char *tp,*ep2,*eplace,*tt; tp=(char *)malloc(12);/分配空间/ ep2=(char *)malloc(12); eplace=(char *)malloc(12); tt =(char )malloc(12); strcpy(eplace,term ());/调用term分析产生表达式计算的第一项eplace/ while(syn=13 or 14) { 操作符 tt= ‘+’或者‘—’; 读下一个单词符号; strcpy(ep2,term());/调用term分析产生表达式计算的第二项ep2/ strcpy(tp,newtemp());/调用newtemp产生临时变量tp存储计算结果/ emit(tp,eplace,tt,ep2);/生成四元式送入四元式表/ strcpy(eplace,tp); } return(eplace); } char *term(void)/仿照函数expression编写/ char *factor(void) {char *fplace; fplace=(char *)malloc(12); strcpy(fplace, “ ”); if(syn=10) {strcpy(fplace,,token);/将标识符token的值赋给fplace/ 读下一个单词符号; } else if(syn=11) {itoa(sum,fplace,10); 读下一个单词符号; } else if (syn=27) {读下一个单词符号; fplace=expression();/调用expression分析返回表达式的值/ if(syn=28) 读下一个单词符号; else{输出‘}’错误;kk=1; } } else{输出‘(’错误;kk=1; } return(fplace); }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值