编译原理-词法分析(实验 C语言)

编译原理-词法分析


1. 实验目的

设计、编写并调试一个词法分析程序,加深对词法分析原理的理解

2. 实验要求

2.1 待分析的简单语言的词法

  1. 关键字:begin,if,then,while,do,end
    所有关键字都是小写
  2. 运算符和界符::,=,+,-,*,/,<,<=,<>,>,>=,=,;,(,),#
  3. 其他单词是标识符(id)和整型常数(NUM),通过以下正规式定义:
    ID = letter(letter|digit)*
    NUM = digit digit*
  4. 空格由空白、制表符和换行符组成。空格一般用来分隔ID、NUM、运算符、界符和关键字,词法分析阶段通常被忽视

2.2 各种单词符号对应的种别码

单词符号种别码单词符号种别码
begin1:17
if2:=18
then3<20
while4<>21
do5<=22
end6>23
letter(letter digit)*10>=24
digit digit*11=25
+13;26
-14(27
*15)28
/16#0
[29]30
{31}32
,33!=40
==39

2.3 词法分析程序的功能

输入:所给文法的源程序字符串
输出:二元组(syn,token或sum)构成的序列

其中:syn为单词种别码;token为存放的单词自身字符串;sum为整型常数
例如: 对源程序
begin x:=9; if x > 0 then x:=2 * x + 1 / 3; end #
的源文件,经词法分析后输出如下序列:
(1,begin)(10,‘x’)(18,:=)(11,9)(26,😉(2,if)…

3. 词法分析程序的算法思想

算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到单词符号的第一个字种类,拼出相应地单词符号

3.1 主程序示意图

主程序示意图如图所示,其中初值包括如下两个方面

  1. 关键字表的初值
    关键字作为特殊标识符处理,把它们预先安排在一张表各中(称为关键字表),当扫描程序识别出标识符时,查关键字表。如能查到匹配的单词,则该单词为关键字,否则为一般标识符,关键字表为一个字符串数组,描述如下:

    char* rwtab[] = {“begin”,“if”,“then”,“while”,“do”,“end”,_KEY_WORD_END}; //关键字数组

  2. 程序中需要用到的主要变量为syn,token和sum
    在这里插入图片描述

3.2 扫描子程序的算法思想

首先设置3个变量:

  1. token用来存放构成单词符号的字符串
  2. sum用来存放整型单词
  3. syn用来存放单词符号的 种别码

扫描子程序主要部分流程如图:
在这里插入图片描述

4. 词法分析程序的C语言程序框架

最终代码:

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
// #include <iostream>

#define _KEY_WORD_END "waiting for your expanding"  // 定义关键字结束标志

// 单词二元组的结构
typedef struct 
{
    int typenum;
    char* word;
}WORD;

//函数声明
char m_getch();
void getbc();
void concat();
int letter();
int digit();
int reserve();
void retract();
char *dtb();
WORD *scaner();

char input[255];  //输入换缓冲区
char token[255] = "";  //单词缓冲区
int p_input;  //输入换缓冲区指针
int p_token;  //单词缓冲区指针

char ch;  //当前读入字符
char* rwtab[] = {"begin","if","then","while","do","end",_KEY_WORD_END};  //关键字数组

WORD* scaner();  //词法扫描函数,获得一个单词

int main(void)
{
    // printf("111");
    int over = 1;
    WORD* oneword = new WORD;  //new为c++中的关键字 在使用new进行内存分配时,需要包含相关类型的头文件。#include <iostream>
    while (1)
    {
        printf("Enter Your words(end with #):");
        scanf("%[^#]s",input);  //读入源程序字符串到缓冲区,以#结束,允许多行输入
        p_input = 0;
        printf("Your words: \n %s \n",input);
        while (over < 1000 && over != -1) 
        {
            oneword = scaner();  //获得新单词 
            if(oneword -> typenum < 1000)
            {
                printf("(%d,%s)",oneword -> typenum,oneword -> word);  //打印种别码和单词本身的值
            }
            over = oneword -> typenum;
            free(oneword);
        }
        over = 1;
        // printf("\n press # to exit: \n");  //#号退出程序
        // scanf("%[^#]s",input);

        while(getchar() != '\n')
        {

		}
		printf("\npress # to exit:");
		if(getchar() == 35)
        {
			return 0;
		}
		while(getchar() != '\n')
        {

        }
    }      
}


// 从输入缓冲区读取一个字符到ch中
char m_getch()
{
    ch = input[p_input];
    p_input = p_input + 1;
    return(ch);
}

// 去掉空白符号
void getbc()
{
    while(ch == ' ' || ch == 10)
    {
        ch = input[p_input];
        p_input = p_input + 1;
    }
}

//拼写单词
void concat()
{
    token[p_token] = ch;
    p_token = p_token + 1;
    token[p_token] = '\0';
}

//判断是否字母
int letter()
{
    if(ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z')
        return 1;
    else 
        return 0;
}

//判断是否数字
int digit()
{
    if(ch >= '0' && ch <= '9')
        return 1;
    else 
        return 0;
}

// 检索关键字表格
int reserve(){
    int i = 0;
    while(strcmp(rwtab[i],_KEY_WORD_END))
    {
        if(!strcmp(rwtab[i],token))
        {
            return i + 1;
        }
        i = i + 1;
    }
    return 10;
}

//回退一个字符
void retract()
{
    p_input = p_input - 1;
}

//数字转换成二进制
char* dtb(char *buffer)
{
    int j = 0;
	int flag = 0;
	int k = (sizeof(char)<<3) - 1;
	
	char temp = ch - '0';
	for(int i = 0; i < (sizeof(char)<<3); i++,k--){
		if((temp >> k & 0x01) == 0){
			if(flag == 1){
				buffer[j++] = 0 + '0';
			}	
		}
		else{
			flag = 1;
			buffer[j++] = (temp >> k & 0x01) + '0';
		}
	}
	buffer[j] = 0;
	return buffer;
}


WORD* scaner()
{
    WORD* myword = new WORD;
    myword -> typenum = 10;
    myword -> word = "";
    p_token = 0;
    m_getch();
    getbc();
    if(letter())
    {
        while (letter() || digit())
        {
            concat();
            m_getch();
        }
        retract();
        myword -> typenum = reserve();
        myword -> word = token;
        return (myword);
    }
    else if(digit())
    {
        while (digit())
        {
            concat();
            m_getch();
        }
        retract();
        myword ->typenum = 11;
        myword -> word = token;
        return (myword);
    }
    else switch (ch)
    {
        case '=' : 
            m_getch();
            if(ch == '=')
            {
                myword -> typenum = 39; // == 的种别码为39
                myword -> word = "==";
                return (myword);
            }
            retract();
            myword -> typenum = 25; // = 的种别码为25
            myword -> word = "=";
            return (myword);
            break;
        case '+' : 
            myword -> typenum = 13; // + 的种别码为13
            myword -> word = "+";
            return (myword);
            break;
        case '-' : 
            myword -> typenum = 14; // - 的种别码为14
            myword -> word = "-";
            return (myword);
            break;
        case '*' : 
            myword -> typenum = 15; // * 的种别码为15
            myword -> word = "*";
            return (myword);
            break;
        case '/' : 
            myword -> typenum = 16; // / 的种别码为16
            myword -> word = "/";
            return (myword);
            break;
        case '(' : 
            myword -> typenum = 27;
            myword -> word = "(";
            return (myword);
            break;
        case ')' : 
            myword -> typenum = 28;
            myword -> word = ")";
            return (myword);
            break;
        case '[' : 
            myword -> typenum = 29;
            myword -> word = "[";
            return (myword);
            break;
        case ']' : 
            myword -> typenum = 30;
            myword -> word = "]";
            return (myword);
            break;
        case '{' : 
            myword -> typenum = 31;
            myword -> word = "{";
            return (myword);
            break;
        case '}' : 
            myword -> typenum = 32;
            myword -> word = "}";
            return (myword);
            break;
        case ',' : 
            myword -> typenum = 33;
            myword -> word = ",";
            return (myword);
            break;
        case ':' : 
            m_getch();
            if(ch == '=')
            {
                myword -> typenum = 18;
                myword -> word = ":=";
                return (myword);
            }
            retract();
            myword -> typenum = 17;
            myword -> word = ":";
            return (myword);
            break;
        case ';' : 
            myword -> typenum = 26;
            myword -> word = ";";
            return (myword);
            break;
        case '>' : 
            m_getch();
            if(ch == '=')
            {
                myword -> typenum = 24;
                myword -> word = ">=";
                return (myword);
            }
            retract();
            myword -> typenum = 23;
            myword -> word = ">";
            return (myword);
            break;
        case '<' : 
            m_getch();
            if(ch == '=')
            {
                myword -> typenum = 22;
                myword -> word = "<=";
                return (myword);
            }
            retract();
            myword -> typenum = 20;
            myword -> word = "<";
            return (myword);
            break;
        case '!' : 
            m_getch();
            if(ch == '=')
            {
                myword -> typenum = 40;
                myword -> word = "!=";
                return (myword);
            }
            retract();
            myword -> typenum = -1;
            myword -> word = "ERROR";
            return (myword);
            break;
        case '\0' : 
            myword -> typenum = 1000;
            myword -> word = "OVER";
            return (myword);
            break;
        default:
            myword -> typenum = 0;
            myword -> word = "#";
            return (myword);
    }
}

5. 实验结果

输入源程序后得出结果:
在这里插入图片描述

press # to exit:
在这里插入图片描述

输入其他字符,继续输入源程序:
在这里插入图片描述

6. 实验小结

  1. 关键词new出错
    改进:将c源程序改为c++
    原因:new为c++中的关键字 在使用new进行内存分配时,需要包含相关类型的头文件。#include <iostream>

  2. void main()出错 error: '::main' must return 'int'
    解决方式:将void main改为int main(void)

  3. 数字转换二进制
    十进制转二进制代码

    //数字转换成二进制
    char* dtb(char *buffer)
    {
        int j = 0;
    	int flag = 0;
    	int k = (sizeof(char)<<3) - 1;
    	
    	char temp = ch - '0';
    	for(int i = 0; i < (sizeof(char)<<3); i++,k--){
    		if((temp >> k & 0x01) == 0){
    			if(flag == 1){
    				buffer[j++] = 0 + '0';
    			}	
    		}
    		else{
    			flag = 1;
    			buffer[j++] = (temp >> k & 0x01) + '0';
    		}
    	}
    	buffer[j] = 0;
    	return buffer;
    }
    
  4. 增加:=的判断

    case ':' : 
                m_getch();
                if(ch == '=')
                {
                    myword -> typenum = 18;
                    myword -> word = ":=";
                    return (myword);
                }
                retract();
                myword -> typenum = 17;
                myword -> word = ":";
                return (myword);
                break;
    
  5. 程序运行后会自动退出,无法满足按#退出
    解决方式:

    修改main函数
       
    int main(void)
    {
        // printf("111");
        int over = 1;
        WORD* oneword = new WORD;  //new为c++中的关键字 在使用new进行内存分配时,需要包含相关类型的头文件。#include <iostream>
        while (1)
        {
            printf("Enter Your words(end with #):");
            scanf("%[^#]s",input);  //读入源程序字符串到缓冲区,以#结束,允许多行输入
            p_input = 0;
            printf("Your words: \n %s \n",input);
            while (over < 1000 && over != -1) 
            {
                oneword = scaner();  //获得新单词 
                if(oneword -> typenum < 1000)
                {
                    printf("(%d,%s)",oneword -> typenum,oneword -> word);  //打印种别码和单词本身的值
                }
                over = oneword -> typenum;
                free(oneword);
            }
            over = 1;
            // printf("\n press # to exit: \n");  //#号退出程序
            // scanf("%[^#]s",input);
    
            while(getchar() != '\n')
            {
    
    		}
    		printf("\npress # to exit:");
    		if(getchar() == 35)
            {
    			return 0;
    		}
    		while(getchar() != '\n')
            {
    
            }
        }      
    }
    
    
    
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/691540.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nginx(openresty) 查看连接数和并发送

1 通过浏览器查看 #修改nginx配置文件 location /status {stub_status on;access_log off;allow 192.168.50.0/24;deny all;} #重新加载 sudo /usr/local/openresty/nginx/sbin/nginx -s reloadActive connections //当前 Nginx 当前处理的活动连接数。 server accepts handl…

HPC: perf入门

如果你想查看你的程序在cpu上运行时&#xff0c;耗时时如何分布的&#xff0c;那么perf是一个合理的选择。 准备工作 为了支持使用perf&#xff0c;首先你要安装相关的库 sudo apt install linux-tools-5.15.0-67-generic此外&#xff0c;因为使用perf进行benchmark&#xf…

四种跨域解决方案

文章目录 1.引出跨域1.基本介绍2.具体演示1.启动之前学习过的springboot-furn项目2.浏览器直接访问 [localhost:8081/furns](http://localhost:8081/furns) 可以显示信息3.启动前端项目&#xff0c;取消请求拦截器&#xff0c;这样设置&#xff0c;就会出现跨域4.跨域原因 2.跨…

linux指令--sed

sed 主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。 语法解析 sed [选项] 编辑命令 文件 选项&#xff1a; -n&#xff1a;只显示匹配处理的行-e&#xff1a;执行多个编辑命令时-i&#xff1a;在原文件中进行修改&#xff0c;不输出到屏幕-…

零基础入门学用Arduino 第二部分(一)

重要的内容写在前面&#xff1a; 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。个人把这个教程学完之后&#xff0c;整体感觉是很好的&#xff0c;如果有条件的可以先学习一些相关课程&#xff0c;学起来会更加轻松&#xff0c;相关课程有数字电路…

系统思考—决策

为‮么什‬模型及‮式公‬通常比人‮决类‬策更有效&#xff1f;究‮是竟‬什么让‮些某‬模型和公‮表式‬现出色&#xff1f;事实上&#xff0c;我‮应们‬该探究的‮人是‬类在决策‮程过‬中的不足。关‮在键‬于人类‮策决‬中存在的“噪声”。尽‮模管‬型或公‮不式‬完…

【JavaScript】内置对象 - 字符串对象 ⑤ ( 判断对象中是否有某个属性 | 统计字符串中每个字符出现的次数 )

文章目录 一、判断对象中是否有某个属性1、获取对象属性2、判定对象是否有某个属性 二、统计字符串中每个字符出现的次数1、算法分析2、代码示例 String 字符串对象参考文档 : https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String 一、判…

【NI国产替代】电池模拟器,快速模拟 3C 产品电池的充放电功能

电池模拟器 快速模拟 3C 产品电池的充放电功能输出灵活可调节的电压/电流内置双向 DC-DC 降压变换器为 3C 产品提供漏电检测 电池模拟器系列包含单节双通道&#xff08;1S&#xff09;、双节双通道&#xff08;2S&#xff09;、三节单通道&#xff08;3S&#xff09;三种规格&…

贪心(不相交的开区间、区间选点、带前导的拼接最小数问题)

目录 1.简单贪心 2.区间贪心 不相交的开区间 1.如何删除&#xff1f; 2.如何比较大小 区间选点问题 3.拼接最小数 1.简单贪心 比如&#xff1a;给你一堆数&#xff0c;你来构成最大的几位数 2.区间贪心 不相交的开区间 思路&#xff1a; 首先&#xff0c;如果有两个…

LeetCode刷题之HOT100之颜色分类

下午好呀&#xff0c;大家&#xff01;昨天估计是喝了假酒&#xff0c;现在没有胃口&#xff0c;喝酒真的没有任何好处。以后尽量避免此活动。今天几乎没睡觉&#xff0c;准备做完这题回宿舍&#xff0c;把电脑也带回去。 1、题目描述 2、逻辑分析 对颜色排序&#xff0c;要求…

数字孪生技术体系和核心能力整理

最近对数字孪生技术进行了跟踪调研学习,整理形成了调研成果,供大家参考。通过学习,发现数字孪生技术的构建过程其实就是数字孪生体的构建与应用过程,数字孪生体的构建是一个体系化的系统工程,数字化转型的最终形态应该就是数实融合互动互联的终极状态。数实融合是每个行业…

自定义类型:结构体+结构体内存对齐+结构体实现位段

结构体内存对齐实现位段 一.结构体1.结构体的声明2.结构体变量成员访问操作符3.结构体传参4.匿名结构体5.结构的自引用 二.结构体内存对齐1.对齐规则2.为什么存在内存对齐&#xff1f;3.修改默认对齐数 三.结构体实现位段1.什么是位段2.位段的内存分配3.位段的跨平台问题4.位段…

flink读取hive写入http接口

目录 0、创建hive数据 1、pom.xml 2、flink代码 3、sink 4、提交任务jar 5、flink-conf.yaml 6、数据接收 flink-1.17.2jdk1.8hive-3.1.3hadoop3.3.6passwordhttp0、创建hive数据 /cluster/hive/bin/beeline !connect jdbc:hive2://ip:10000 create database demo; d…

2024 年最新 Python 基于百度智能云实现短语音识别详细教程

百度智能云语音识别 采用国际领先的流式端到端语音语言一体化建模算法&#xff0c;将语音快速准确识别为文字&#xff0c;支持手机应用语音交互、语音内容分析、机器人对话等场景。百度短语音识别可以将 60 秒以下的音频识别为文字。适用于语音对话、语音控制、语音输入等场景…

HTTP-web服务器

web服务器 web服务器实现了http和相关的tcp连接处理&#xff0c;负责管理web服务器提供的资源&#xff0c;以及对服务器的配置&#xff0c;控制以及拓展等方面的管理 web服务器逻辑实现了http协议&#xff0c;并负责提供web服务器的管理功能&#xff0c;web服务器逻辑和操作系…

skywalking基础使用

skywalking基础使用 找链路追踪Id将链路追踪Id拿到skywalking-ui中筛选对应链路补充说明例如, sql的打印能让我们了解到代码中对应的sql是否符合预期 找链路追踪Id 在接口响应header中复制x-trace-id 这个接口响应正常了, 异常没有暴露到前端, 且调用链路很长, 但我们借助s…

校园外卖系统的技术架构与实现方案

随着校园生活的日益现代化&#xff0c;外卖需求在高校学生群体中迅速增长。为了满足这一需求&#xff0c;校园外卖系统应运而生。本文将详细探讨校园外卖系统的技术架构及其实现方案&#xff0c;帮助读者了解这一系统的核心技术与实现路径。 一、系统概述 校园外卖系统主要包…

【旅行】关于毕业旅行与长期旅行计划(城市、攻略、预算、交通、面基等)

【旅行】关于毕业旅行与长期旅行计划&#xff08;城市、攻略、预算、交通、面基等&#xff09; 文章目录 一、目的地与去哪儿玩1、可能2、人民币3、国家地理4、省份与城市5、环球旅行 二、攻略之怎么玩&#xff08;旅行预算、攻略&#xff09;1、旅行预算之交通、住宿、门票等2…

Linux 35.5 + JetPack v5.1.3@RACER编译安装

Linux 35.5 JetPack v5.1.3RACER编译安装 1. 源由2. 编译&安装Step 1&#xff1a;依赖库安装Step 2&#xff1a;LKH-3安装Step 3&#xff1a;建立工程Step 4&#xff1a;编译工程Step 5&#xff1a;安装工程 3. 问题汇总3.1 组件ros-noetic-multi-map-server问题3.2 swarm…

自动化测试-Selenium-元素定位

一.元素定位 因为使用selenium进行自动化测试&#xff0c;元素定位是必不可少的&#xff0c;所以这篇文章用于自动化测试中的selenium中的元素定位法。 1.根据id属性进行定位&#xff08;id是唯一的&#xff09; id定位要求比较高&#xff0c;要求这个元素的id必须是固定且唯…