低级输入

  使用至今所定义的这个计算器,可以发现一些不方便的地方:需要记住在每个表达式的最后加一个分号,以使它的值能够被打印出来;每个名字都需要用空白结束等。这些都非常令人讨厌。例如,x=7将被作为一个标识符,而不是标识符x后跟运算符=和数7。用读入单个字符的代码来代替get_token()里那种基于类型的默认输入操作,就可以解决这两个问题。

  首先,我们可将换行符号看做与分号等价,也当做表达式结束:

    Token_value get_token()
    {
        char ch;
        do {        // 跳过空白,除了'\n'
            if(!cin.get(ch)) return curr_tok = END;
        }while(ch!='\n' && isspace(ch));

        switch(ch) {
        case ';':
        case '\n':
            return curr_tok = PRINT;
    }

这里用了一个do语句,它等价于while语句,除了被控制的语句至少总要执行一次之外。调用cin.get(ch)从标准输入流读一个字符到ch里。按照默认方式,get不会像 >> 运算符那样跳过空白。检测if(!cin.get(ch))在无法从cin读到字符时为真;在这种情况下,就返回END以结束计算器的本次执行。这里用了运算符!(否定),因为get()在成功的情况下将返回true。

  标准库函数isspace()提供了对空白的标准检测(20.4.2节)。当c是空白字符时,isspace()比直接检测各个空白字符要快很多。类似的函数包括检测一个字符是否是数字---isdigit(),是否是字母---isalpha(),是否是数字或字母---isalnum()。

  在跳过空白之后,下一个字符将用于确定读来的是哪些词法单词。

  采用 >> 读入字符串直到遇到空白会引起问题,这一问题可以通过一次读一个字符,直到遇到非字母非数字字符的方式解决:

    default:                // NAME, NAME=, 或者错误
        if(isalpha(ch)) {
            string_value = ch;
            while(cin.get(ch) && isalnum(ch)) string_value.push_back(ch);
            cin.putback(ch);
            return curr_tok = NAME;
        }
        error("bad token");
        return curr_tok = PRINT;

幸运是是,这两个改进的实现都只要修改一小段局部代码。构造出这样的程序,使程序的改进能通过局部性修改实现,这也是一个非常重要的设计目标。

🔚