54bf2d5c7fa9622fd6477b974d12ec1a
Python的词法分析三

Python的词法分析三

这篇是最后一篇讲词法分析了,如果再看不懂,就略过吧。

《Python的词法分析》的文末,我们提及了Python所有词法单元的定义都在token.h里面,限于篇幅,我们来感受下Python中是怎么词法分析获得NUMBER这个符号的及其对应的值。

parsetok

从本质上来说,Python的词法解析入口始于parsetok,返回一个node对象,代表了语法分析结束后建立的抽象语法树。之所以这么设计的原因是单独的词法流其实对使用者意义不大,缺少了场景联系,独立的个体很难得到有意义的分析结果。但是本文聚焦于词法分析,因此我们需要分析的点实质上是tok_get函数。

tok_get

/* Period or number starting with period? */
    if (c == '.') {
        c = tok_nextc(tok);
        if (isdigit(c)) {
            goto fraction;
        }
        else {
            tok_backup(tok, c);
            *p_start = tok->start;
            *p_end = tok->cur;
            return DOT;
        }
    }

    /* Number */
    if (isdigit(c)) {
        if (c == '0') {
            /* Hex, octal or binary -- maybe. */
            c = tok_nextc(tok);
            if (c == '.')
                goto fraction;
#ifndef WITHOUT_COMPLEX
            if (c == 'j' || c == 'J')
                goto imaginary;
#endif
            if (c == 'x' || c == 'X') {

                /* Hex */
                c = tok_nextc(tok);
                if (!isxdigit(c)) {
                    tok->done = E_TOKEN;
                    tok_backup(tok, c);
                    return ERRORTOKEN;
                }
                do {
                    c = tok_nextc(tok);
                } while (isxdigit(c));
            }
            else if (c == 'o' || c == 'O') {
                /* Octal */
                c = tok_nextc(tok);
                if (c < '0' || c >= '8') {
                    tok->done = E_TOKEN;
                    tok_backup(tok, c);
                    return ERRORTOKEN;
                }
                do {
                    c = tok_nextc(tok);
                } while ('0' <= c && c < '8');
            }
            else if (c == 'b' || c == 'B') {
                /* Binary */
                c = tok_nextc(tok);
                if (c != '0' && c != '1') {
                    tok->done = E_TOKEN;
                    tok_backup(tok, c);
                    return ERRORTOKEN;
                }
                do {
                    c = tok_nextc(tok);
                } while (c == '0' || c == '1');
            }
            else {
                int found_decimal = 0;
                /* Octal; c is first char of it */
                /* There's no 'isoctdigit' macro, sigh */
                while ('0' <= c && c < '8') {
                    c = tok_nextc(tok);
                }
                if (isdigit(c)) {
                    found_decimal = 1;
                    do {
                        c = tok_nextc(tok);
                    } while (isdigit(c));
                }
                if (c == '.')
                    goto fraction;
                else if (c == 'e' || c == 'E')
                    goto exponent;
#ifndef WITHOUT_COMPLEX
                else if (c == 'j' || c == 'J')
                    goto imaginary;
#endif
                else if (found_decimal) {
                    tok->done = E_TOKEN;
                    tok_backup(tok, c);
                    return ERRORTOKEN;
                }
            }
            if (c == 'l' || c == 'L')
                c = tok_nextc(tok);
        }
        else {
            /* Decimal */
            do {
                c = tok_nextc(tok);
            } while (isdigit(c));
            if (c == 'l' || c == 'L')
                c = tok_nextc(tok);
            else {
                /* Accept floating point numbers. */
                if (c == '.') {
        fraction:
                    /* Fraction */
                    do {
                        c = tok_nextc(tok);
                    } while (isdigit(c));
                }
                if (c == 'e' || c == 'E') {
                    int e;
                  exponent:
                    e = c;
                    /* Exponent part */
                    c = tok_nextc(tok);
                    if (c == '+' || c == '-') {
                        c = tok_nextc(tok);
                        if (!isdigit(c)) {
                            tok->done = E_TOKEN;
                            tok_backup(tok, c);
                            return ERRORTOKEN;
                        }
                    } else if (!isdigit(c)) {
                        tok_backup(tok, c);
                        tok_backup(tok, e);
                        *p_start = tok->start;
                        *p_end = tok->cur;
                        return NUMBER;
                    }
                    do {
                        c = tok_nextc(tok);
                    } while (isdigit(c));
                }
#ifndef WITHOUT_COMPLEX
                if (c == 'j' || c == 'J')
                    /* Imaginary part */
        imaginary:
                    c = tok_nextc(tok);
#endif
            }
        }
        tok_backup(tok, c);
        *p_start = tok->start;
        *p_end = tok->cur;
        return NUMBER;
    }

Python中与数字相关的词法分析过程就基本包含在上述代码中了,乍一看非常复杂,原因在于Python需要支持的数字种类还是比较多的。

  • 10进制数
  • 2进制数
  • 8进制数
  • 16进制数
  • 小数
  • 指数(类似科学记数法1e5
  • 复数(a + bi的形式)

状态迁移图如果有不熟悉的,可以回顾上一篇文章。

十进制整数

还是从最简单的10进制数开始,必须为1-9的数字开头(把纯0当成8进制数字会解析更容易)

如果是个非0的数字,并且一直是数字,则不断的读取下一个字符:

do {
      c = tok_nextc(tok);
} while (isdigit(c));

如果while终止,说明不再是数字,这个时候判断下当前终止的字符是不是Ll,可以扩展成long类型。

if (c == 'l' || c == 'L')
    c = tok_nextc(tok);

如果是,继续读;不是,代表是普通的非long扩展类型

这个时候,这个10进制的整数说明解析完成,可以返回了,返回前我们需要特别注意,对之前预先多解析进行保存,留在下次使用。

top Created with Sketch.