读SIM源码笔记 -- 2

背景

承接上一篇博客,这篇博客主要是开始分析词法分析部分了。

入口

来回顾一下代码逻辑模块是如何进入到词法分析部分的,在主函数中调用了输入的函数Read_Input_Files,它又通过循环调用read_file挨个读入文件,read_file经read_text把文件转成token后进行存储,read_text经过text模块中转,调用到stream模块进行词法分析器的初始化和打开。

token模块

为了搞清楚token模块具体是实现了什么功能,我把Dick爷爷提供的一篇“非正式”paper又读了一遍,其中提到关于词法的工作是这样的:
paper中关于词法部分的截图
把文本压缩成一个8-bits的“基本单词”,更加具体一点的方式如图中我的注释所说。但是实际上我在token中发现的映射方式是用4个16进制数,也就是总共16-bits来表示一个Token,所以token中是用16-bits来表示的,README中,对于如何扩展其他语言检测方法的叙述也证实了这一点:

To add another language L, write a file Llang.l along the lines of clang.l or the other *lang.l files, extend the Makefile and recompile.
All knowledge about a given language L is located in Llang.l; the rest of the program expects each token to be a 16-bit character.

共有16-bits,所以可以表示token的数值范围是0x0000 ~ 0xFFFF。我试图具体分析映射的方法:

#include <bits>
/* Macros for the composition of tokens */		/* range (gaps unused)*/
#define	No_Token	int2Token(0)			/* 0x0000 */
/* 8-bit bytes */					/* 0x0001-0x00FF */ 
#define	CTRL(ch)	int2Token(0x100|((ch)&0x01F))	/* 0x0101-0x011E */
#define	NORM(ch)	int2Token(0x100|((ch)&0x07F))	/* 0x0121-0x017E */
#define	IDF		int2Token(0x17F)		/* 0x017F */
#define	STR		int2Token(0x180)		/* 0x0180 */
#define	MTCT(ch)	int2Token(0x180|((ch)&0x01F))	/* 0x0181-0x019E */
#define	META(ch)	int2Token(0x180|((ch)&0x07F))	/* 0x01A1-0x01FE */
/* tokens from idf_hashed() */				/* 0x0200-0xFFFE */
#define	End_Of_Line	int2Token(0xFFFF)		/* 0xFFFF */

注释里有这样的解释:

2. macros for defining summary tokens (with ranges of their parameters):
		CTRL(ch)	ch in 'A'-'~'
		NORM(ch)	ch in '!'-'~'
		MTCT(ch)	ch in 'A'-'~'
		META(ch)	ch in '!'-'~'

稍微分析一下它的来头,只看算式中(ch)&***的部分,会发现CTRL(ch)和MTCT(ch)相同,运算是&0x01F,也就是只选取ch二进制位的后5位。从注释中可以推断,ch的后五位范围在0x01到0x1E。这是什么意思呢?翻出ASCII码表,关注英文字母,可以看到:

在这里插入图片描述
关注高位为0100(二进制)到0111这四列,可以看到A和a的最后5个二进制位是相同的,不同字母之间的最后五位又可以区分出来,但是对大小写不敏感。

类似地可以推断出,NORM(ch) META(ch)是在对所有ASCII打印字符进行映射。这个当然是大小写敏感了。

那么除了这四个定义的方法,还有两个区间,分别是8-bit bytes和idf_hashed()。暂时没有找到8-bits bytes是如何产生的,但是对于idf_hashed,看过了它的函数体之后,它的功能就是传入一个字符串,传出一个范围在0X200到0XFFE之间的16bit数,过程中的哈希函数看起来没有规律:

#define	HASH(h,ch)	(((h) * 8209) + (ch)*613)

像是随便哈希了一下。

C++lang.l

看到这个.l文件中,开头大几十行定义了两个idf数组,这里即使不看idf的数据结构,只看内容,也知道这是关于字符串和映射之间关系的数组。比如{“define”, META(‘d’)},就是把define映射成了一个数字。

在它的决策部分可以看到,引号中间的字符串和字符全部视作一样的:

\"{StrChar}*\"  {           /* strings */
        return_ch(STR);
    }

\'{ChrChar}+\'  {           /* characters */
        return_ch('\'');
    }

由#开头的行,如果是include则忽略,否则匹配出的token丢进ppcmd里面查找是哪个预编译指令,找不到就按#映射了。

对于在左括号之前的idf,也就是函数名,有一个设置是F,如果设定了这个F,就把这个函数名进行hash,否则就只当做token统一的IDF进行映射。关于这一点,可以看到manual中对于F的解释:

-F The names of routines in calls are required to match exactly (not in sim text)

BTW,原来routine可以用来表示“函数”的意思,看源码之前先看manual的时候一脸懵逼。

普通的idf就不进行hash了,全部当做一样的IDF来看待。

分号,如果设定了f就增加一个分号的ch,否则不去管。f的作用是保证run有balancing parentheses,来分离潜在的函数体,要探究怎么实现
的话需要看后面匹配的部分。

换行符需要记录。

ASCII95为[\040-\176],匹配的是95个ASCII能显示的字符,如果上面的这些匹配都没有匹配到的话,就由ASCII95来收场,结果直接保留在token中。

经历以上过程还没有匹配到的,属于非ASCII字符,单独计数,不计token。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值