c语言解释器1-词法分析器

2024-01-24 18:20

本文主要是介绍c语言解释器1-词法分析器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

c语言解释器1-词法分析器

  • 词法分析概述
    • 待分析的C语言子集的词法
    • 词法分析算法
    • c语言实现
    • 运行示例

词法分析概述

依据语言构词规则,从输入的源程序(字符串)中识别出一个
个单词(符号)。
例如,给定如下输入:

position = initial + rate * 60 

词法分析器将识别出7个单词符号

position, =, initial, +, rate, *, 60

待分析的C语言子集的词法

  1. 关键字
    在这里插入图片描述
  2. 专用符号
    在这里插入图片描述
  3. 其他标记ID和NUM
    在这里插入图片描述
  4. 空格由空白、制表符和换行符组成
    空格一般用来分隔ID、NUM、专用符号和关键字,词法分析阶段通常被忽略。
  5. 注释
    行注释 //…
    块注释 //

词法分析算法

在这里插入图片描述

c语言实现

clib.h
定义了一些用基础的数据结构。

#ifndef CLIB_H_INCLUDE
#define CLIB_H_INCLUDE
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef struct c_reader c_reader;		//
typedef struct c_buffer c_buffer;		//缓存
typedef struct c_token c_token;			//单词符号串结构体
typedef enum c_type c_type;				//单词符号类型(即种别码)
typedef struct srouce_location location_t;	//单词符号起始字符所在位置//c语言运算符
#define OP_TABLE                  \OP(EQ,	    	"=")						\OP(EQ_EQ,		"==")					\OP(NOT,	    	"!")						\OP(NOT_EQ,		"!=")						\OP(GREATER,		">")					\OP(GREATER_EQ,	">=")						\OP(LESS,		"<")						\OP(LESS_EQ,		"<=")						\OP(PLUS,		"+")					\OP(PLUS_EQ,		"+=")					\OP(PLUS_PLUS,	"++")					\OP(MINUS,		"-")						\OP(MINUS_EQ,	"-=")						\OP(MINUS_MINUS,	"--")						\OP(MULT,		"*")						\OP(MULT_EQ,		"*=")						\OP(DIV,		    "/")						\OP(DIV_EQ,		"/=")						\OP(MOD,		    "%")						\OP(MOD_EQ,		"%=")						\OP(AND,		    "&")					\OP(AND_EQ,		"&=")					\OP(OR,		    "|")						\OP(OR_EQ,		"|=")						\OP(XOR,		    "^")						\OP(XOR_EQ,		"^=")						\OP(RSHIFT,		">>")						\OP(RSHIFT_EQ,   ">>=")                      \OP(LSHIFT,		"<<")						\OP(LSHIFT_EQ,	"<<=")						\OP(COMPL,		"~")						\OP(AND_AND,		"&&")					\OP(OR_OR,		"||")						\OP(QUERY,		"?")						\OP(COLON,		":")						\OP(COMMA,		",")					\OP(OPEN_PAREN,	"(")						\OP(CLOSE_PAREN,	")")						\OP(OPEN_SQUARE,	"[")						\OP(CLOSE_SQUARE,"]")						\OP(OPEN_BRACE,	"{")						\OP(CLOSE_BRACE,	"}")						\OP(SEMICOLON,	";")					\OP(DEREF,		"->")					\OP(DOT,		    ".")				    \OP(DOT_DOT_DOT, "...")                  	\OP(SHARP,       "#")                        \OP(SHARP_SHARP, "##")                           //
#define TK_TABLE    \TK(NAME,		IDENT)	 				\TK(NUMBER,		LITERAL) 			\TK(CHARACTER,		LITERAL) 				\TK(STRING,      LITERAL)              \TK(OTHER,		LITERAL) 		\TK(HEADER_NAME,	LITERAL) 		\TK(COMMENT,		LITERAL) 	\TK(MACRO_ARG,		NONE)	 			    //c语言关键字枚举
#define KW_TABLE       \KW(STATIC,	    "static")		    \KW(UNSIGNED,	"unsigned")		\KW(LONG,		"long")			\KW(CONST,		"const")		\KW(EXTERN,	    "extern")		    \KW(REGISTER,	"register")		\KW(TYPEDEF,	    "typedef")		    \KW(SHORT,		"short")		\KW(INLINE,	    "inline")		    \KW(VOLATILE,	"volatile")		\KW(SIGNED,	    "signed")		    \KW(AUTO,		"auto")			\KW(INT,		    "int")			    \KW(CHAR,		"char")			\KW(FLOAT,		"float")		\KW(DOUBLE,	    "double")		    \KW(VOID,		"void")			\KW(ENUM,		"enum")			\KW(STRUCT,	    "struct")		    \KW(UNION,		"union")		\KW(IF,		    "if")			    \KW(ELSE,		"else")			\KW(WHILE,		"while")		\KW(DO,		    "do")			    \KW(FOR,		    "for")			    \KW(SWITCH,	    "switch")		    \KW(CASE,		"case")			\KW(DEFAULT,	    "default")		    \KW(BREAK,		"break")		\KW(CONTINUE,	"continue")		\KW(RETURN,	    "return")		    \KW(GOTO,		"goto")			\KW(SIZEOF,  	"sizeof")	    \KW(RESTRICT,    "restrict")    #define OP(e,s) C_ ## e,
#define TK(e,s) C_ ## e,
#define KW(e,s) C_ ## e,
//枚举单词符号串的种别码
enum c_type
{OP_TABLEKW_TABLETK_TABLEN_TYPES            //No type
};
#undef KW
#undef OP
#undef TK#define KW(e,s) if(!strcmp(str,s)) return C_ ## e;
c_type is_keyword(const char *base,int size)
{char str[256];memcpy(str,base,size);str[size] = '\0';KW_TABLEreturn C_NAME;
}
#undef KW//单词符号的位置
struct srouce_location
{const char *file;int line;int column;
};//单词符号结构体
struct c_token
{location_t src_loc;              //单词符号串的位置c_type type;                     //token type//A string or number or iden or charchar *val;
};//
struct c_buffer
{char *cur;       //当前位置char *line_base;  /*行起始位置  */char *buf;       //字符串缓冲unsigned int line_number;   //行号
};struct c_reader
{char *file;             //文件名c_buffer *buffer;       //the file bufferunsigned int vaild;     //is buffer vaild,vaild=1,else vaild=0c_reader *prev;         //预留,用以支持include预处理以及宏定义的预处理
};//读入一个c文件
#define xmalloc(T,size) (T *)malloc(sizeof(T)*(size))
#define xcalloc(type,size) (type *)calloc(sizeof(type),size)
c_reader *read_file(const char *file)
{FILE *fp = fopen(file,"rb");if(fp==NULL){return NULL;printf("NULL");}c_reader *cr = xmalloc(c_reader,1);cr->prev = NULL;cr->file = xmalloc(char,strlen(file)+1);strcpy(cr->file,file);int size = 8*1024;c_buffer *cb = xmalloc(c_buffer,1);char *buff = xcalloc(char,size+1);int fd = fread(buff,sizeof(char),size,fp);while(!feof(fp)){fseek(fp,0,SEEK_SET);size *= 2;buff = realloc(buff,size+1);fd = fread(buff,sizeof(char),size,fp);}buff[fd] = '\0';cb->buf = buff;cb->cur = buff;cb->line_base = buff;cb->line_number = 1;cr->buffer = cb;cr->vaild = 1;fclose(fp);return cr;
}#endif //CLIB_H_INCLUDE

lex.h

#ifndef _LEX_H
#define _LEX_H#include "clib.h"
#include <ctype.h>//用以简化代码
#define IF_ELSE(condition,one,other)  \if(*cur == condition) {tk->type = one;cur++;}  \else tk->type = other; \break;
#define IF_ELIF_ELSE(c1,one,c2,two,other) \if(*cur == c1) {tk->type = one;cur++;} \else IF_ELSE(c2,two,other)static c_token *lex_token(c_reader *pfile)
{c_token *tk = xmalloc(c_token,1); char *base, *cur = pfile->buffer->cur;		//初始化base、cur指针,base为单词符号的起始位置,cur为当前字符所在位置//略过空白字符
start:while(isspace(*cur)&&(*cur!='\n'))++cur;base = cur;switch (*cur++)		//移动cur指针{case '\0':pfile->vaild = 0;tk->type=N_TYPES;break;//行号处理case '\n':pfile->buffer->line_number++;pfile->buffer->line_base = cur;goto start; break;//处理id和关键字case '_':case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm':case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': while(isalnum(*cur)||*cur=='_')cur++;tk->type=is_keyword(base,cur-base);break;//处理数字,目前只支持整数处理case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': while(isdigit(*cur))cur++;tk->type = C_NUMBER;break;//处理字符串及单字符case '\'': case '\"': while((*cur!=*base)&&(*cur!='\n')&&(*cur!='\0')) cur++;tk->type = (*(base) == '\'') ? C_CHARACTER:C_STRING;cur += (*base == *cur) ? 1:0;break;//跳过注释case '/':   if(*cur == '/'){ while(*cur!='\n'&&*cur!='\0')++cur; goto start; }      // 跳过单行注释else if(*cur == '*'){ cur++;                                            // 跳过块注释while (!(*cur == '/' && cur[-1] == '*' || cur[0] == '\0')){if(*cur++ == '\n') {pfile->buffer->line_number++;pfile->buffer->line_base = cur;}}cur++; goto start;} else IF_ELSE('=',C_DIV_EQ,C_DIV)//专有符号处理case '-': if(*cur == '-'){tk->type = C_MINUS_MINUS;cur++;}else IF_ELIF_ELSE('>',C_DEREF,'=',C_MINUS_EQ,C_MINUS)case '=': IF_ELSE('=',C_EQ_EQ,C_EQ)case '!': IF_ELSE('=',C_NOT_EQ,C_NOT)case '^': IF_ELSE('=',C_XOR_EQ,C_XOR)case '*': IF_ELSE('=',C_MULT_EQ,C_MULT)case '%': IF_ELSE('=',C_MOD_EQ,C_MOD)case '#': IF_ELSE('#',C_SHARP_SHARP,C_SHARP)case '>': if(*cur == '>') {cur++;IF_ELSE('=',C_RSHIFT_EQ,C_RSHIFT)} else IF_ELSE('=',C_GREATER_EQ,C_GREATER)case '<': if(*cur == '<') {cur++;IF_ELSE('=',C_LSHIFT_EQ,C_LSHIFT)} else IF_ELSE('=',C_LESS_EQ,C_LESS)case '+': IF_ELIF_ELSE('+',C_PLUS_PLUS,'=',C_PLUS_EQ,C_PLUS)case '&': IF_ELIF_ELSE('&',C_AND_AND,'=',C_AND_EQ,C_AND)case '|': IF_ELIF_ELSE('|',C_OR_OR,'=',C_OR_EQ,C_OR)case '~': tk->type = C_COMPL;break;case '?': tk->type = C_QUERY;break;case ':': tk->type = C_COLON;break;case ',': tk->type = C_COMMA;break;case '(': tk->type = C_OPEN_PAREN;break;case ')': tk->type = C_CLOSE_PAREN;break;case '[': tk->type = C_OPEN_SQUARE;break;case ']': tk->type = C_CLOSE_SQUARE;break;case '{': tk->type = C_OPEN_BRACE;break;case '}': tk->type = C_CLOSE_BRACE;break;case ';': tk->type = C_SEMICOLON;break;case '.': tk->type = C_DOT;if(*cur=='.'&&cur[1]=='.'){tk->type=C_DOT_DOT_DOT;cur+=2;} break;default:tk->type = C_OTHER;break;}int size = cur - base;		//此时cur指向单词符号串的尾部,base指向单词符号串的起始位置pfile->buffer->cur = cur;	//记录处理到的位置tk->src_loc.line = pfile->buffer->line_number;		//将行号信息记录到该单词tk->src_loc.column = base+1 - pfile->buffer->line_base;		//将列号信息记录到该单词tk->src_loc.file = pfile->file;		//将文件信息记录到该单词tk->val = xcalloc(char,size+1);	memcpy(tk->val,base,size);			//记录单词符号的值,即一个字符串// printf("%s\n",tk->val);return tk;
}
#endif //_LEX_H

scan.c
用以展示词法分析的结果

#include "lex.h"
#include <stdio.h>/*
* 原型:void scan(c_reader *pfile,FILE *out)
* 功能:从字符串表示的源程序中识别出具有独立意义的单词符号
* 参数:输入参数:pfile ---缓冲区
*       输出参数:out --- 输出文件
* 返回值:无
*/
void scan(c_reader *pfile,FILE *out)
{char *dest = xmalloc(char,64);fprintf(out,"(line  ,column,offset):\t(c_type,token)\n");while(pfile->vaild){c_token *tk = lex_token(pfile);switch (tk->type){case N_TYPES:break;        default:fprintf(out,"(%-6d,%-6d,%-6d):\t(%-2d,%s)\n",tk->src_loc.line,tk->src_loc.column,strlen(tk->val),tk->type,tk->val);break;}}
}int main(int argn,char **argv)
{FILE *fo;if(argn<2)return -1;if(argn<3){fo = stdout;}else{fo = fopen(argv[2],"w");}c_reader *cr = read_file(argv[1]);scan(cr,fo);fclose(fo);return 0;
}

运行示例

编译

gcc scan.c -o scan.exe

运行方式

//运行方式一
.\scan.exe test.c //结果将输出到屏幕
//运行方式二
.\scan.exe test.c abc.txt //结果将输出到abc.txt文件

测试源文件

#include <stdio.h>int main(int argn,char **argv)
{int a,*b;a += *b;*b = a << 2;return 0;
}

结果输出

(line  ,column,offset):	(c_type,token)
(1     ,1     ,1     ):	(46,#)
(1     ,2     ,7     ):	(82,include)
(1     ,10    ,1     ):	(6 ,<)
(1     ,11    ,5     ):	(82,stdio)
(1     ,16    ,1     ):	(44,.)
(1     ,17    ,1     ):	(82,h)
(1     ,18    ,1     ):	(4 ,>)
(3     ,1     ,3     ):	(60,int)
(3     ,5     ,4     ):	(82,main)
(3     ,9     ,1     ):	(36,()
(3     ,10    ,3     ):	(60,int)
(3     ,14    ,4     ):	(82,argn)
(3     ,18    ,1     ):	(35,,)
(3     ,19    ,4     ):	(61,char)
(3     ,24    ,1     ):	(14,*)
(3     ,25    ,1     ):	(14,*)
(3     ,26    ,4     ):	(82,argv)
(3     ,30    ,1     ):	(37,))
(4     ,1     ,1     ):	(40,{)
(5     ,5     ,3     ):	(60,int)
(5     ,9     ,1     ):	(82,a)
(5     ,10    ,1     ):	(35,,)
(5     ,11    ,1     ):	(14,*)
(5     ,12    ,1     ):	(82,b)
(5     ,13    ,1     ):	(42,;)
(6     ,5     ,1     ):	(82,a)
(6     ,7     ,2     ):	(9 ,+=)
(6     ,10    ,1     ):	(14,*)
(6     ,11    ,1     ):	(82,b)
(6     ,12    ,1     ):	(42,;)
(7     ,5     ,1     ):	(14,*)
(7     ,6     ,1     ):	(82,b)
(7     ,8     ,1     ):	(0 ,=)
(7     ,10    ,1     ):	(82,a)
(7     ,12    ,2     ):	(28,<<)
(7     ,15    ,1     ):	(83,2)
(7     ,16    ,1     ):	(42,;)
(8     ,5     ,6     ):	(78,return)
(8     ,12    ,1     ):	(83,0)
(8     ,13    ,1     ):	(42,;)
(9     ,1     ,1     ):	(41,})

这篇关于c语言解释器1-词法分析器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/640564

相关文章

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

Go语言中nil判断的注意事项(最新推荐)

《Go语言中nil判断的注意事项(最新推荐)》本文给大家介绍Go语言中nil判断的注意事项,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.接口变量的特殊行为2.nil的合法类型3.nil值的实用行为4.自定义类型与nil5.反射判断nil6.函数返回的

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

Go语言代码格式化的技巧分享

《Go语言代码格式化的技巧分享》在Go语言的开发过程中,代码格式化是一个看似细微却至关重要的环节,良好的代码格式化不仅能提升代码的可读性,还能促进团队协作,减少因代码风格差异引发的问题,Go在代码格式... 目录一、Go 语言代码格式化的重要性二、Go 语言代码格式化工具:gofmt 与 go fmt(一)

Go语言中泄漏缓冲区的问题解决

《Go语言中泄漏缓冲区的问题解决》缓冲区是一种常见的数据结构,常被用于在不同的并发单元之间传递数据,然而,若缓冲区使用不当,就可能引发泄漏缓冲区问题,本文就来介绍一下问题的解决,感兴趣的可以了解一下... 目录引言泄漏缓冲区的基本概念代码示例:泄漏缓冲区的产生项目场景:Web 服务器中的请求缓冲场景描述代码

Go语言如何判断两张图片的相似度

《Go语言如何判断两张图片的相似度》这篇文章主要为大家详细介绍了Go语言如何中实现判断两张图片的相似度的两种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 在介绍技术细节前,我们先来看看图片对比在哪些场景下可以用得到:图片去重:自动删除重复图片,为存储空间"瘦身"。想象你是一个

Go语言中Recover机制的使用

《Go语言中Recover机制的使用》Go语言的recover机制通过defer函数捕获panic,实现异常恢复与程序稳定性,具有一定的参考价值,感兴趣的可以了解一下... 目录引言Recover 的基本概念基本代码示例简单的 Recover 示例嵌套函数中的 Recover项目场景中的应用Web 服务器中

Go语言中使用JWT进行身份验证的几种方式

《Go语言中使用JWT进行身份验证的几种方式》本文主要介绍了Go语言中使用JWT进行身份验证的几种方式,包括dgrijalva/jwt-go、golang-jwt/jwt、lestrrat-go/jw... 目录简介1. github.com/dgrijalva/jwt-go安装:使用示例:解释:2. gi

Go 语言中的 Struct Tag 的用法详解

《Go语言中的StructTag的用法详解》在Go语言中,结构体字段标签(StructTag)是一种用于给字段添加元信息(metadata)的机制,常用于序列化(如JSON、XML)、ORM映... 目录一、结构体标签的基本语法二、json:"token"的具体含义三、常见的标签格式变体四、使用示例五、使用

Go语言使用slices包轻松实现排序功能

《Go语言使用slices包轻松实现排序功能》在Go语言开发中,对数据进行排序是常见的需求,Go1.18版本引入的slices包提供了简洁高效的排序解决方案,支持内置类型和用户自定义类型的排序操作,本... 目录一、内置类型排序:字符串与整数的应用1. 字符串切片排序2. 整数切片排序二、检查切片排序状态: