C语言实现简单 词法分析程序(编译原理)

要求

在这里插入图片描述

代码书写(详细注释,亲自修改,测试无误)

#include <stdio.h>
#include  <string.h>
char prog[80],token[8];
char ch, en='a';
int syn,p,m,n,sum;
char *rwtab[6]={"begin","if","then","while","do","end"};
void main()
{
	void scaner();
	p=0;
	// 不以# 结尾,程序将一直等待输入:查看getchar()的用法:https://zhidao.baidu.com/question/60668767.html
	printf("\n please input string: 并以#号结尾!\n");
	//printf("\na\n");
	//printf("\n%c\n", en);
	do 
	{
		ch=getchar();
		prog[p++]=ch;
		//printf("\n%c\n", ch);
		//printf("\n%d\n", p);
	  }while(ch !='#');
	p=0;
	//printf("\n%d\n", p);
	do
	  {
		scaner();
		// 执行完函数后,全局变量 syn, token的值就会自动改变。
		switch(syn)
		{  
			// 11数字 二元组
			case 11:printf("\n(%d, %d)",syn,sum); break;
			// 不符合语法的字符,报错
			case -1:printf("\nerror"); break;
			// 输出关键字,或者是 标识符
			default:printf("\n(%d, %s)", syn, token);
		}
	  }while(syn != 0);  // 遇# 为0,结束循环。
}

void scaner()
{ 
	// 前三行是初始化数据,初始化字符数组token ,元素全都是 null
	for(n=0;n<8;n++) token[n]=NULL; 
	// 先读取值,再p++,也就是从下标0开始读取
    m=0;
	ch=prog[p++];
	//printf("\n%c\n", ch);
	//printf("\n%d\n", p);
	while(ch==' ') ch=prog[p++];
	// 运用 ASCLL码,对字母范围确定,下面判断数字同理
	// 判断关键字和标识符
    if((ch >= 'a' && ch <= 'z')||(ch >= 'A' && ch <= 'Z')) 
	{ 
		// 当读取到字母开头是, 就开始进入循环了,取得以该字符开头的 关键字(或者是标识符)
		while ((ch >= 'a' && ch <= 'z')||(ch >= 'A'&& ch <= 'Z')||(ch >= '0' && ch <= '9'))
           { 
			token[m++]=ch;
            ch=prog[p++];
			//printf("\nm\n");
           }	
        token[m++]='\0';  // 字符数组结尾符号
        /*回退一个字符; 此时的p代表 非字母或数字的字符的 下一个字符*/
		p--;  // 回退到 非字母或数字的那个字符
        
		// 先预先设置为 标识符的 种别码
		syn=10;
        for(n=0; n<6; n++)
        if(strcmp(token,rwtab[n])==0) // 判等函数,相等返回0, 大于返回正整数,小于返回负整数
        {
			// 进入if里说明相等是关键字,给syn 赋值标识符的种别码
			syn = n + 1;
			break;
			
		}
   }
	else if(ch >= '0' && ch <= '9') // 用来判断数字
		{	
			sum = 0; // 老师的代码忘记了初始化sum这一步, 并且绝对不能放到while循环内部初始化
			while(ch >= '0' && ch<= '9')
			{ 
				sum = sum * 10 + ch -'0';  // 也是利用ASCLL码,将字符转化为数字,然后实现累加,计算数值(巧妙)
				ch=prog[p++];
			}
		p--; // 同样p--,因为这个p--代表的字符,还没被处理
		syn=11;
   }
	else
		switch(ch)
   {
     case'<': token[m++]=ch;
			// 下面这些是用来判断是否有 <=  或者 <>的
            ch = prog[p++];
            if(ch=='>') {syn=21; token[m++]=ch;}
            else if(ch=='=') {syn=22; token[m++]=ch;}
            else {syn=20; p--;}
            break;
     case'>': token[m++]=ch;
             ch=prog[p++];
			// 同上,不过老师的 syn 值给错了,改过来就行
            if(ch=='=') { syn=24; token[m++]=ch;}
            else { syn=23; p--;}
            break;
     case':': token[m++]=ch;
            ch=prog[p++];
            if(ch=='=') {syn=18; token[m++]=ch;}
            else {syn=17; p--;}
            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=25; token[0]=ch;break;
     case'#': syn= 0; 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;
     default:syn=-1;  
	}
}

逻辑分析

在这里插入图片描述

结果展示

在这里插入图片描述

结束~ 感谢观看

  • 49
    点赞
  • 279
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、实验目的: 通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。 编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的内部编码及单词符号自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示) 二、实验预习提示 1、词法分析器的功能和输出格式 词法分析器的功能是输入源程序,输出单词符号。词法分析器的单词符号常常表示成以下的二元式(单词种别码,单词符号的属性值)。本实验中,采用的是按类来安排种别码的方式。 2、部分单词的BNF表示(可参考教材43页的状态转换图) -> ->|| |ε -> -> |ε -> + -> - -> > -> >= 3、 做词法分析器需要把对象语言的词法全部描述出来,在这我们取C语言子集,它的词法如下: (1)关键字 main if else int return void while…….. 所有的关键字都是小写。 (2)专用符号 = + - * / <= > >= == != ; : , { } [ ] ( ) (3)空格和空白、制表符和换行符。 空格一般用来分隔ID、NUM、专用符号和关键字,在词法分析阶段通常被忽略。 各种单词符号的种别码,这是一种符号一个编码的设计。只供参考! 单词符号 种别码 单词符号 种别码 main 2 [ 28 int 1 ] 29 char 3 { 30 If 4 } 31 else 5 , 32 for 6 : 33 while 7 ; 34 ID 10 > 35 NUM 20 = 37 + 22 +”,当前字符为’>’,此时,分析器倒底是将其分析为大于关系运算符还是大于等于关系运算符呢?显然,只有知道下一个字符是什么才能下结论。于是分析器读入下一个字符’+’,这时可知应将’>’解释为大于运算符。但此时,超前读了一个字符’+’,所以要回退一个字符,词法分析器才能正常运行。在分析标识符,无符号整数等时也有类似情况。 5、模块结构 见附图 三、实验过程和指导: (一)准备: 1.阅读课本有关章节,明确语言的语法,写出基本保留字、标识符、常数、运算符、分隔符和程序例。 2.编制好程序。 3.准备好多组测试数据。 (二)上机调试: (三)程序要求: 程序输入/输出示例: 如源程序C语言。输入如下一段: main() { int a,b; a = 10; b = a + 20; } 要求输出如右图。 (2,“main”) (5,“(” ) (5,“ )” ) (5,“{ ” ) (1,“int” ) (2,“a” ) (5,“,” ) (2,“b” ) (5,“;” ) (2,“a” ) (4,“=” ) (3,“10” ) (5,“;” ) (2,“b” ) (4,“=” ) (2,“a” ) (4,“+” ) (3,“20” ) (5,“;” ) (5,“}” ) 说明: 识别保留字:if、int、for、while、do、return、break、continue; 单词种别码为1。 其他的都识别为标识符;单词种别码为2。 常数为无符号整形数;单词种别码为3。 运算符包括:+、-、*、/、=、>、=、<=、!= ;单词种别码为4。 分隔符包括:,、;、{、}、(、); 单词种别码为5。 以上为参考,具体可自行增删。 程序思路(参考): 这里以开始定义的C语言子集的源程序作为词法分析程序的输入数据。在词法分析中,自文件头开始扫描源程序字符,一旦发现符合“单词”定义的源程序字符串时,将它翻译成固定长度的单词内部表示,并查填适当的信息表。 经过词法分析后,源程序字符串(源程序的外部表示)被翻译成具有等长信息的单词串(源程序的内部表示),并产生两个表格:常数表和标识符表,它们分别包含了源程序中的所有常数和所有标识符。 0.定义部分:定义常量、变量、数据结构。 1.初始化:从文件将源程序全部输入到字符缓冲区中。 2.取单词前:去掉多余空白。 3.取单词后:去掉多余空白(可选,看着办)。 4.取单词:利用实验一的成果读出单词的每一个字符,组成单词,分析类型。(关键是如何判断取单词结束?取到的单词是什么类型的单词?) 5.显示结果。 为了设计好程序,注意以下事情: 1.模块设计:将程序分成合理的多个模块(函数),每个模块做具体的同一事情。 2.写出(画出)设计方案:模块关系简图、流程图、全局变量、函数接口等。 3.编程时注意编程风格:空行的使用、注释的使用、缩进的使用等。
以下是一个简单C语言子集词法分析程序的示例: ```c #include <stdio.h> #include <ctype.h> #define MAX_TOKEN_LEN 100 // token最大长度 // token类型枚举 typedef enum { TOK_INT, // 整数 TOK_OP, // 运算符 TOK_EOL, // 行末 TOK_ERR, // 错误 TOK_EOF // 文件结束 } TokenType; // token结构体 typedef struct { TokenType type; // token类型 char value[MAX_TOKEN_LEN + 1]; // token值 } Token; // 获取下一个字符 char next_char(void) { return getchar(); } // 预读下一个字符 char peek_char(void) { char c = getchar(); ungetc(c, stdin); return c; } // 跳过空白字符 void skip_whitespace(void) { char c; do { c = next_char(); } while (isspace(c)); ungetc(c, stdin); } // 识别整数 Token get_int_token(void) { Token token = { TOK_INT, "" }; char c = next_char(); int i = 0; while (isdigit(c) && i < MAX_TOKEN_LEN) { token.value[i++] = c; c = next_char(); } ungetc(c, stdin); return token; } // 识别运算符 Token get_op_token(void) { Token token = { TOK_OP, "" }; char c = next_char(); if (c == '+' || c == '-' || c == '*' || c == '/') { token.value[0] = c; } else { token.type = TOK_ERR; token.value[0] = c; token.value[1] = '\0'; } return token; } // 获取下一个token Token get_token(void) { skip_whitespace(); char c = next_char(); Token token = { TOK_ERR, "" }; switch (c) { case '\n': token.type = TOK_EOL; break; case '+': case '-': case '*': case '/': token = get_op_token(); break; default: if (isdigit(c)) { token = get_int_token(); } else if (c == EOF) { token.type = TOK_EOF; } else { token.value[0] = c; token.value[1] = '\0'; } break; } return token; } // 输出token void print_token(Token token) { switch (token.type) { case TOK_INT: printf("TOK_INT: %s\n", token.value); break; case TOK_OP: printf("TOK_OP: %s\n", token.value); break; case TOK_EOL: printf("TOK_EOL\n"); break; case TOK_ERR: printf("TOK_ERR: %s\n", token.value); break; case TOK_EOF: printf("TOK_EOF\n"); break; } } int main(void) { Token token; do { token = get_token(); print_token(token); } while (token.type != TOK_EOF); return 0; } ``` 以上程序可以识别整数和四则运算符,并输出对应的token。程序中使用了getchar()和ungetc()函数来获取字符和预读字符,使用isspace()函数来判断空白字符,使用isdigit()函数来判断数字字符。程序的主要逻辑在get_token()函数中,根据不同的字符类型返回不同的token。程序中使用了Token结构体来表示token,结构体中包含了token类型和token值两个成员。程序还定义了TokenType枚举来表示不同的token类型,以及一些常量来表示最大token长度和不同的token类型。程序使用了print_token()函数来输出token。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值