【问题标题】:C lexical analyzer. Using switch to analyze and count a decimal/not decimalC 词法分析器。使用开关分析和计算小数/非小数
【发布时间】:2013-10-12 01:26:04
【问题描述】:

我的词法分析器可以识别数字 (5,555,543667)、小数 (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) {

    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 的地方。想想一些 for 循环和 isdigit() 调用。
  • gotos 是邪恶的,尽量不要习惯它们。至于问题,您的问题是您在处理整个项目之前不知道该案例。您需要首先将字符串分解为“单词”,然后将它们一个接一个地处理,将每个单词作为一个整体(而不是逐个字符地处理)。
  • 只使用flex不是更容易吗?如果这是一个学习练习,你学到了什么?

标签: c switch-statement case analyzer lexical


【解决方案1】:

你真的应该在这种练习中使用character classification functions 而不是长的 switch 语句。您的代码会简单很多,而且您根本不必使用goto

例如,可以使用以下正则表达式来描述一个数字(添加空格以分解各个块):

[-+]? [0-9]* \.? [0-9]+

这已经显示了可能的状态转换:

  • 号码可以(可选)以+- 开头(如果您支持签名号码)
  • 可能有 0..n 个数字
  • 如果后面的字符不是小数点符号,则应为分隔符,否则为无效符号。如果是分隔符,您的号码将被终止。
  • 小数点后应该有 1..n 位
  • 当您到达输入末尾或遇到分隔符时,数字将终止

所有这一切都可以在几行代码中完成 - 只需有一个指向您当前输入字符的指针,然后一个接一个地向前推进并检查每个字符并根据字符类决定做什么.

现在,这种特殊的方法不使用科学计数法等处理浮点数,但是一旦你完成了基础知识,添加这些额外的东西真的很简单。

【讨论】:

    【解决方案2】:

    我认为这补充了 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;
            }
    }
    

    还有很多细节没有说清楚。关注 EOF。缓冲区溢出。将 yylval 设置为 int 或 float。解析除简单数字以外的标记。

    【讨论】:

    • 快速输入 - 我花了更长的时间输入纯文本答案。 :) 我通常将这种代码放在一个函数中,然后当调用者遇到在当前上下文中以数字开头的东西时调用它。
    • @xxbbc,你的答案更好,也许一起它会帮助 OP。理论与实践。是的,小功能很好。
    • @ryyker,我粗略地说。 get() 和 unget() 是返回下一个字符或将其推回的通用函数。也许来自文件,也许来自缓冲区。 Lex 提供了它们,我模仿了那个界面。
    【解决方案3】:

    使用像digit_follow_peroid 这样的变量来保持状态。每次遇到 peroid 时,将变量设置为 false,然后当遇到十进制开关块中的数字时,将其设置为 true。检查变量的值以确定strncpy 之前的t.length。也许您还需要其他变量与它一起工作。最好的办法是定义一个状态转移矩阵,比 goto 好很多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-21
      • 2022-10-22
      • 2011-03-12
      • 2013-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-02
      相关资源
      最近更新 更多