C词法分析器。使用开关分析和计数小数/非小数
我的词法分析器可以识别数字(5555543667)、小数(44.65,4.1)和句点(.) 我可以很好地数数数字、小数和句点,但当我碰到一个数字和句点相邻时,它会将其计为小数 考虑包含以下内容的文本文件:555 2.3 55.23 44 5 我的输出将是 1类型1:555C词法分析器。使用开关分析和计数小数/非小数,c,switch-statement,case,analyzer,lexical,C,Switch Statement,Case,Analyzer,Lexical,我的词法分析器可以识别数字(5555543667)、小数(44.65,4.1)和句点(.) 我可以很好地数数数字、小数和句点,但当我碰到一个数字和句点相邻时,它会将其计为小数 考虑包含以下内容的文本文件:555 2.3 55.23 44 5 我的输出将是 1类型1:555 2类型3:2.3 3类型3:55.23 4类型1:44 5类型3:5 其中类型3是我的十进制标识符 我希望第5和第6个代币被计算为一个数字,然后是一个句号 下面是我如何处理switch语句的 switch(*b) {
2类型3:2.3
3类型3:55.23
4类型1:44
5类型3:5 其中类型3是我的十进制标识符 我希望第5和第6个代币被计算为一个数字,然后是一个句号 下面是我如何处理switch语句的
switch(*b) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
digits:
t.length++;
switch(*(b + t.length)) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
goto digits;
case '.':
goto decimal;
break;
default:
break;
}
t.type = TOKEN_DIGITS;
t.string = (char *)calloc(t.length + 1, sizeof(char));
strncpy(t.string, b, t.length);
break;
decimal:
t.length++;
switch(*(b + t.length)) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
goto decimal;
break;
}
t.type = TOKEN_DECIMAL;
t.string = (char *)calloc(t.length+1,sizeof(char));
strncpy(t.string,b,t.length);
break;
尝试了很多事情,但我被正式卡住了 您真的应该使用这种练习,而不是长开关语句。您的代码将简单得多,根本不必使用
goto
例如,可以使用以下正则表达式来描述数字(添加空格以分解各个块):
这已经显示了可能的状态转换:
- 数字可以(可选)以
或+
开头(如果您支持签名数字)-
- 它可能有0..n个数字
- 如果以下字符不是小数点符号,则应为分隔符,否则为无效符号。如果是分隔符,则您的号码终止
- 小数点后应为1..n位
- 当到达输入的末尾或遇到分隔符时,数字将终止
现在,这种特殊的方法不使用科学记数法等来处理浮点数。但是,一旦你完成了基本操作,添加这些附加值就非常简单。使用像
digit\u follow\u peroid
这样的变量来保持状态。每次遇到peroid时,将变量设置为false,然后在decimal开关块中遇到数字时,将其设置为true。在strncpy
之前,检查变量值以确定t.length。也许你还需要其他变量来配合它。最好的方法是定义一个状态转移矩阵,它比gotos好得多。我认为这是对xxbbcc答案的补充
*非常粗略,大概是这样
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
yylex() {
int c;
char *p, buf[1000];
for(c = get(); isspace(c); c = get());
if(isdigit(c)) {
p = buf;
while(isdigit(c)) {
*p++ = c;
c = get();
}
*p = 0;
if(c != '.') {
unget(c);
int i = atoi(buf);
return INT;
}
assert(c == '.');
*p++ = c;
c = get();
while(isdigit(c)) {
*p++ = c;
c = get();
}
*p = 0;
float f = atof(buf);
unget(c);
return DECIMAL;
}
}
#包括
#包括
#包括
yylex(){
INTC;
char*p,buf[1000];
for(c=get();isspace(c);c=get());
if(isdigit(c)){
p=buf;
while(isdigit(c)){
*p++=c;
c=get();
}
*p=0;
如果(c!='。){
unget(c);
int i=原子(buf);
返回INT;
}
断言(c='.');
*p++=c;
c=get();
while(isdigit(c)){
*p++=c;
c=get();
}
*p=0;
浮点数f=atof(buf);
unget(c);
返回小数;
}
}
还有很多细节没有说。关注EOF。缓冲区溢出。将yylval设置为int或float。解析除简单数字以外的标记 词法分析不是一个需要大量goto的地方。考虑一些for循环和isdigit()调用。goto是邪恶的,尽量不要习惯它们。至于这个问题,你的问题是,在处理完整个项目之前,你不知道这个案例。您需要首先将字符串分解为“单词”,然后再逐个处理它们,将每个单词作为一个整体(而不是一个字符一个字符地处理)。使用flex不是更容易吗?如果这是一个学习练习,你学到了什么?快速键入-我花了更长的时间键入纯文本答案。:)我通常把这类代码放在函数中,当调用者遇到在当前上下文中以数字开头的东西时,就会调用它。@xxbbc,你的答案更好,也许结合起来对操作理论和实践会有帮助。是的,小功能很好。@Ryker,我粗略地说。get()和unget()是返回下一个字符或将其推回的通用函数。可能来自文件,可能来自缓冲区。莱克斯提供了他们,我模仿了那个界面。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
yylex() {
int c;
char *p, buf[1000];
for(c = get(); isspace(c); c = get());
if(isdigit(c)) {
p = buf;
while(isdigit(c)) {
*p++ = c;
c = get();
}
*p = 0;
if(c != '.') {
unget(c);
int i = atoi(buf);
return INT;
}
assert(c == '.');
*p++ = c;
c = get();
while(isdigit(c)) {
*p++ = c;
c = get();
}
*p = 0;
float f = atof(buf);
unget(c);
return DECIMAL;
}
}