编译原理——词法分析程序

要求

一、目的和内容

    1.实验目的:通过完成词法分析程序,了解词法分析的过程。

    2.实验内容:用C/C++实现对Pascal的子集程序设计语言的词法识别程序。

    3.实验要求:将该语言的源程序,也就是相应字符流转换成内码,并根据需要是否对于标识符填写相应的符号表供编译程序的以后各阶段使用。

二、程序设计语言的描述

程序设计语言的描述采用扩充的BNF表示:

<程序>→<程序首部><分程序>.

<程序首部>→PROGRAM标识符;

<分程序>→[<常量说明部分>][<变量说明部分>][<过程说明部分>]<复合语句>

<常量说明部分>→CONST<常量定义>{,<常量定义>};    

<常量定义>→标识符=无符号整数

<变量说明部分>→VAR<变量定义>(;<变量定义>);

<变量定义>→标识符{,标识符}:<类型>

<类型>→INTEGER|LONG

<过程说明部分>→<过程首部><分程序>;{<过程首部><分程序>;}

<过程首部>→PROCEDURE标识符;| PROCEDURE标识符(标识符:<类型>);

<语句>→<赋值语句>|<条件语句>|<当型循环语句>|<过程调用语句>

|<读语句>|<写语句>|<复合语句>|ε

<赋值语句>→标识符:=<表达式>

<条件语句>→IF<条件>THEN<语句>

<当型循环语句>→WHILE<条件>DO<语句>

<过程调用语句>→标识符 | 标识符(<表达式>)

<读语句>→READ(标识符,{标识符})

<写语句>→WRITE(<表达式>{,<表达式>})

<复合语句>→BEGIN<语句>{;<语句>}END

<条件>→<表达式><关系运算符><表达式> | ODD<表达式>

<表达式>→[+|-]<项>{<加型运算符><项>}

<项>→<因子>{<乘型运算符><因子>}

<因子>→标识符 | 无符号整数 | (<表达式>)

<加型运算符>→+|-

<乘型运算符>→* | /

<关系运算符>→=|<>|<|<=|>|>=

其中:

 <   >:用左右尖括号括起的字符串表示非终结符号

::= : 定义为

{  }:表示该语法成分可以0—n次重复。

[  ]:表示方括号内为可选项,即0或1次。

三、程序设计语言单词的内部编码

如表1-1为词法分析中的内码单词对照表。

表1-1  内部码对照表

内码

单词

内码

单词

内码

单词

内码

单词

1

PROGRAM

2

CONST

3

VAR

4

INTEGER

5

LONG

6

PROCEDURE

7

IF

8

THEN

9

WHILE

10

DO

11

READ

12

WRITE

13

BEGIN

14

END

15

ODD

16

+

17

-

18

*

19

/

20

=

21

<>

22

<

23

<=

24

>

25

>=

26

.

27

.

28

;

29

:

30

:=

31

(

32

)

33

无符号整数

34

标识符

35

#

四、词法分析程序的设计思想

为了实现的编译程序实用,这里规定源程序可采用自由书写格式,即一行内可以书写多个语句,一个语句也可以占领多行书写;标识符的前20个字符有效;整数用2个字节表示;长整数用4个字节表示。这样词法分析程序的主要工作为:

(1)从源程序文件中读入字符。

(2)统计行数和列数用于错误单词的定位。

(3)删除空格类字符,包括回车、制表符空格。

(4)按拼写单词,并用(内码,属性)二元式表示。

(5)根据需要是否填写标识符表供以后各阶段使用。

 

代码实现

1.判断函数

// 判断是否为关键字  
int isKeyword(char* s) {
    const char* letter[15] = { "program", "const", "var", "integer", "long", "procedure", "if", "then", "while", "do", "read", "write", "begin", "end", "odd" };  
    for (int i = 0; i < 15; i++){
        if (strcmp(s, letter[i]) == 0) 
            return 1;
    } 
    return 0;
}

// 判断是否为算符或界符  
int isOpdelimiter(char* s) {  
    const char* opdelimiter[17] = { "+", "-", "*", "/", "=", "<>", "<", "<=", ">", ">=", ",", ".", ";", ":", ":=", "(", ")" };
    for (int i = 0; i < 17; i++){
        if (strcmp(s, opdelimiter[i]) == 0) 
            return 1;
    }
    return 0;
}

// 判断是否为数字字符串  
int isDigitString(char* s) {
    int i, f = 1; // 初始化 f 为 1,假设字符串都是数字  
    for (i = 0; i < strlen(s); i++) {
        if (s[i] < '0' || s[i] > '9') {
            f = 0; // 如果发现非数字字符,设置 f 为 0  
            break;
        }
    }
    return f;
}

// 判断是否为合法标识符  
int isValidIdentifier(char* s) {
    // 检查字符串的第一个字符是否为字母或下划线  
    if (!isalpha(s[0]) && s[0] != '_')
        return 0;
    for (int i = 1; s[i] != '\0'; i++) {
        if (!isalnum(s[i]) && s[i] != '_')
            return 0;
    }
    return 1;
}

//判断“#”
int isJin(char* s){
    if (s[0] == '#')
        return 1;
    else
        return 0;
}

//判断类型
int findType(char* s){
    if (isKeyword(s))   // 关键字  
        return 1;
    else if (isOpdelimiter(s)) // 算符和界符  
        return 2;
    else if (isDigitString(s))  // 数字字符串
        return 3;
    else if (isValidIdentifier(s)) // 标识符  
        return 4;
    else if (isJin(s)) // '#'字符  
        return 5;
    else
        return 0;
}

2.对应内码函数

//对应内码
int codeOfkey(char* str){
    const char* key[15] = { "PROGRAM","CONST","VAR","INTEGER","LONG","PROCEDURE","IF","THEN","WHILE","DO","READ","WRITE","BEGIN","END","ODD" };
    const int arr[15] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,24,15 };
    for (int i = 0; i < 15; i++){
        if (strcmp(str, key[i]) == 0)
            return arr[i];
    }
}
int codeOfopdelimiter(char* str)
{
    const char* opebounder[17] = { "+","-","*","/","=", "<>","<", "<=",">",">=",",",".",";",":",":=","(",")" };
    const int arr[17] = { 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32 };
    for (int i = 0; i < 17; i++){
        if (strcmp(str, opebounder[i]) == 0)
            return arr[i];
    }
}

3.返回二元式

void printBinaryform(char* s){
    int type = findType(s);
    switch (type) {
    case 1:printf("(%d,%s)", codeOfkey(s), s); break;  //关键字
    case 2:printf("(%d,%s)", codeOfopdelimiter(s), s); break; //算符与界符
    case 3:printf("(33,%s)", s); break;  //数字字符串
    case 4:printf("(34,%s)", s); break;  //标识符
    case 5:printf("(35,%s)", s); break;  //“#”字符 
    default:printf("(NULL,%s)", s);
    }
}

4.GETSYM

int GETSYM(const char* filename) {
    FILE* file = fopen(filename, "r");
    if (file == NULL) {
        printf("无法打开文件\n");
        return 0;
    }
    int row = 1; // 初始化行计数器  
    int line = 1; // 初始化列计数器  
    int ch;
    //Getch
    while ((ch = fgetc(file)) != EOF) {
        if (isspace(ch)) {
            continue;
        }
        if (ch == '\n') {
            row++;
            line = 1;
            printf("\n");
        }
        else 
            line++;

        char word[100];
        int index = 0;
        // 读取当前单词  
        while (!isspace(ch) && ch != EOF) {
            word[index++] = ch;
            ch = fgetc(file); // 继续读取下一个字符  
        }
        word[index] = '\0';            
        // 发生错误
        if (findType(word) == 0) {
            printBinaryform(word);
            printf("*****Error information: row=%d, line=%d\n", row, line);
            /*
            fclose(file);  
            return 0;
            */
        }
        else {
            printBinaryform(word);
        }
    }
    fclose(file);  
    return 1; 
}

 结果展示

Demo:

PROGRAM SimplePascal ;  

VAR  
    num1 , num2 , sum : INTEGER ;  
  
BEGIN  
    WRITE ( ' Enter the first integer : ' ) ;  
    READ ( num1 ) ;  

    WRITE ( ' Enter the second integer : ' ) ;  
    READ ( num2 ) ;  
  
    sum : = num1 + num2 ;  
  
    WRITE ( ' The sum of the two integers is : ' , sum ) ;  
    WRITE ;  
    READ ; { Wait for the user to press a key to prevent the program from exiting immediately }  
END .

结果:

不足:
因为程序是按照“ ”区分字符串,在测试的Pascal代码中,算符、界符必须与其他字符之间有“ ”。

  • 49
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
附录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); }
实验一:词法分析程序 一、实验目的     通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。 编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的类型码及单词符号的自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示) 二、实验要求 用C或C++写一个简单的词法分析程序程序可以满足下列要求: 1、能分析如下几种简单的语言词法 (1) 标识符: ID=letter(letter|digit)* (2) 关键字(全部小写) main int float double char if then else switch case break continue while do for (3)整型常量:NUM=digit digit* (4)运算符 = + - * / < <= == != > >= ; ( )? : (5)空格由空白、制表符和换行符组成,用以分隔ID、NUM、运算符等,字符分析时被忽略。 2、单词符号和相应的类别码 假定单词符号和相应的类别码如下: 单词符号 种别码 int 1 = 17 float 2 < 20 if 3 <= 21 switch 4 == 22 while 5 != 23 Do 6 > 24 标识符 10 >= 25 整型常量 11 ; 26 + 13 ( 27 - 14 ) 28 * 15 ? 29 / 16 : 30 3、词法分析程序实现的功能 输入:单词序列(以文件形式提供),输出识别的单词的二元组序列到文件和屏幕 输出:二元组构成: (syn,token或sum) 其中: syn 为单词的种别码 token 为存放的单词自身符号串 sum 为整型常数 例: 源程序: int ab; float ef=20; ab=10+ef; 输出: (保留字--1,int) (标识符--10,ab) (分号--26,;) (保留字--2,float) (标识符--10,ef) (等号--17,=) (整数--11,20) (分号--26,;) (标识符--10,ab) (等号--17,=) (整数--11,10) (加号--13,+) (标识符--10,ef) (分号--26,;) 4、自己准备测试数据存放于TestData.txt文件中,测试数据中应覆盖有以上5种数据,测试结果要求以原数据与结果对照的形式输出并保存在Result.txt中,同时要把结果输出到屏幕。 5、提前准备 ① 实验前,先编制好程序,上机时输入并调试程序。 准备好多组测试数据(存放于文件TestData.txt中)。 6、写出实验报告 报告格式:要求有实验名称、实验目的、实验要求、实验内容、实验小结。 其中实验内容包括算法分析、程序流程图及程序代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值