在 lex/flex 中标记整数与浮点数

tokenizing ints vs floats in lex/flex

为了好玩,我正在自学一点 flex/bison。我正在为 1975 版的 MS Extended BASIC(扩展为 "has strings")编写解释器。不过有一个问题让我有点难过。

可以通过查找 .E(等)来识别浮点数,否则故障转移到 int。所以我这样做了...

[0-9]*[0-9.][0-9]*([Ee][-+]?[0-9]+)? {
                      yylval.d = atof(yytext);
                      return FLOAT;
                    }
[0-9]+ {
                      yylval.i = atoi(yytext);
                      return INT;
                    }

yylval 联合中的子字段是 .d 表示双精度,.i 表示整数,.s 表示字符串。

但也有可能您需要使用浮点数,因为数字太大而无法存储在 int 中 - 在本例中是 16 位有符号数。

有没有办法在正则表达式中做到这一点?或者我是否必须在关联的 c 端代码中使用 if 执行此操作?

对于许多编程语言,整数和浮点数的字面量是相同的。例如,Java 语言规范(以及其他几个规范)包含互联网和浮点文字的语法规则。在这些规则中,0 验证为浮点文字。这是我看到的您当前方法的主要问题。

解析时,您不应使用 atoi 或 atof,因为它们不检查错误。请改用 strtoul 和 strtod。

整数的操作应该是:

if strtoul succeeds:
    if the number is less than 0x8000:
        llval.i = number
        return INT
strtod must succeed
llval.d = number
return FLOAT

如果你想让整数优先于浮点数(这样一个看起来像整数的文字就是整数),那么你需要把整数模式放在第一位。 (匹配最长的模式总是获胜,但如果两个模式都匹配相同的最长前缀,则第一个获胜。)所以你的基本大纲是:

integer-pattern     { /* integer rule */ }
float-pattern       { /* float rule */ }

您的浮动规则看起来很合理,但请注意,它将匹配单个 .,可能后跟一个指数。很少有语言将单独的 . 视为浮点常量(该文字通常写为 0 :-) )因此您可能希望将其更改为

[0-9]*([0-9]\.?|\.[0-9])[0-9]*([Ee][-+]?[0-9]+)

要使用正则表达式匹配适合 16 位有符号 int 的非负整数,您可以使用以下难看的模式:

0*([12]?[0-9]{1,4}|3(2(7(6[0-7]|[0-5][0-9])|[0-6][0-9]{2})|[0-1][0-9]{3}))

(F)lex 将生成高效代码来实现此正则表达式,但这并不一定是个好主意。

备注:

  1. 该模式识别带有冗余前导零的整数,例如 09。某些语言(如 C)认为这是无效的八进制文字,但我认为 Basic 没有该限制。

  2. 该模式无法识别 32768,因为它太大而不是正整数。但是,作为负整数也不算太大; -32768 就可以了。这始终是解析整数文字的极端情况。如果您只是对整数文字进行词法分析,则可以通过为以 - 开头的文字设置单独的模式来轻松处理正负限制之间的差异,但是在整数文字中包含符号不适用于表达式解析器,因为它对 a-1 产生了不正确的词法分析。 (-32768 是一个有效的整型字面值,而 - 32768 被分析为一个浮点表达式,计算结果为 -32768.0,这也有点奇怪。)这里真的没有好的解决方案,除非您的语言包含无符号整数文字(如 C),在这种情况下,您可以将 0 到 32767 之间的文字分析为有符号整数;从 32768 到 65535 作为无符号整数;从 65536 及以上作为浮点数。