[C++][第2篇]从标准输入流cin中读取Token 什么是Token?

to-do[1] class Token {}

为什么要用 Token

答:因为需要把运算符、括号、浮点数以及其他区分开;

什么是运算符、什么是括号、什么是浮点数

  • 运算符 + - * /
  • 括号 ( )
  • 浮点数 举例: 3.14

Token C++实现代码

#include "std_lib_facilities.h"

class Token {   /* . to-do[1] . */ 
public:
    char kind;      // 记录token的类型
    double value;   // 对于浮点数而言:这是浮点数的值
    Token(char ch) 
        : kind{ch}, value{0} {}
    Token(char ch, double val) 
        : kind{ch}, value{val} {}
};

针对Token的说明

char kind; 

表达字符类型
运算符 + - * / 以及 括号( )都用 字面量 作为自己的 kind

拿 + 举例 : 
Token t_add;
t_add.kind = '+';


浮点数
拿 3.14 举例:
Token t_num;
t_num.kind = '8';
t_num.value = 3.14;

  • 约定char类型 '8' 做 'kind' 的值,来表示浮点数,这是书上的约定,在编写代码初期使用了简化的代码,属于后期代码清理要处理的部分,当然可以换成任意喜欢的字符,只要不和运算符和括号重叠即可;
  • 添加了构造函数之后,可以使用Token t {'+'}; 以及Token t{'8', 3.14};来初始化一个Token对象;

to-do[2] class Token_stream { }

Token_stream 要做什么

答:从cin(C++的标准输入)中读入字符,将其组装成 Token

Token_stream { } C++代码实现

class Token_stream {   /* . to-do[2] . */ 
public:
    Token_stream(); // 创建一个Token流,从cin读取字符
    Token get();    // 获取一个Token
    void putback(Token t); // 放回一个Token
private:
    bool full{false}; // 缓冲区中是否有Token
    Token buffer;    // 存放着通过putback(t)操作放回的Token,这个地方称之为缓冲区
};

to-do[3] void Token_stream::putback(Token t) { }

putback() 对 谁 做了 什么

  • putback(t) 这个函数里的参数 t 被放到上面 Token_stream{} private变量 buffer 里面;
  • buffer就是一个Token变量,
    • 注意是1个Token,不是2个、不是3个、不是4个...数量是1;
    • 注意是1个Token,不是1个char,1个double,1串字符,1个其他什么玩意儿...类型是Token;

putback() C++代码实现

class Token_stream {   /* . to-do[2] . */ 
public:
    Token_stream(); // 创建一个Token流,从cin读取字符
    Token get();    // 获取一个Token
    void putback(Token t); // 放回一个Token
private:
    bool full{false}; // 缓冲区中是否有Token
    Token buffer;    // 存放着通过putback(t)操作放回的Token,这个地方称之为缓冲区
};

void Token_stream::putback(Token t) {   /* . to-do[3] . */ 
    buffer = t;     // 拷贝t到缓冲区
    full = true;    // 表示缓冲区被占用
}

to-do[4] Token Token_stream::get()

get() 做了什么

答:从cin中读取字符,识别,返回一个Token;

get() C++代码实现

Token Token_stream::get() {   /* . to-do[4] . */ 
    if(full) {
        full = false;
        return buffer;
    }   

    char ch;
    cin >> ch;     // >> 会跳过空白(空格、换行、制表符等)

    switch(ch) {
        case ';':   // 表示立即输出结果
        case 'q':   // 表示退出
        case '(': case ')': case '+': case '–': case '*': case '/':
            return Token{ch};   // 每个字符自身表示一个Token
        case '.':
        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
        { 
            cin.putback(ch);        // 将数字(或小数点)放回到 标准输入cin 中
            double val;
            cin >> val;             // cin 自动读取一个 double
            return Token{'8',val};  // 用'8'表示读取了一个浮点数
        }
        default:
            error("Bad token");
    }

}
  • 对于浮点数的读取,关键在于利用 cin,因为cin 十分清楚怎么样读取一个浮点数;
  • C++ 中的浮点数,要么是以数字开头(比如123),要么是以小数点开头(.34);因此在遇到读取的字符是数字或者小数点时,利用 cin.putback(ch);将其放回到cin之中,然后就可以一口气读取这个浮点数;
  • error()是这本书配套的头文件std_lib_facilities.h 里定义的错误输出函数,导入就可以用了;

关于 Case ';'

  • 编写代码初期,测试用例使用如下,一次运算以分号 ; 表示输入结束,并立即输出结果
1 + 2.34*5.67;

to-do[5] Token_stream 构造函数 以及 创建全局变量 ts

使用全局变量

// to-do[5]
// 构造函数 Token_stream()
Token_stream::Token_stream()
    :full{false}, buffer{0}   // no Token in buffer
{
}

Token_stream ts;   // provides get() and putback()  

至此为止的全部代码

#include "std_lib_facilities.h"

class Token {   /* . to-do[1] . */ 
public:
    char kind;      // 记录token的类型
    double value;   // 对于浮点数而言:这是浮点数的值
    Token(char ch) 
        : kind{ch}, value{0} {}
    Token(char ch, double val) 
        : kind{ch}, value{val} {}
};

class Token_stream {   /* . to-do[2] . */ 
public:
    Token_stream(); // 创建一个Token流,从cin读取字符
    Token get();    // 获取一个Token
    void putback(Token t); // 放回一个Token
private:
    bool full{false}; // 缓冲区中是否有Token
    Token buffer;    // 存放着通过putback(t)操作放回的Token,这个地方称之为缓冲区
};

void Token_stream::putback(Token t) {   /* . to-do[3] . */ 
    buffer = t;     // 拷贝t到缓冲区
    full = true;    // 表示缓冲区被占用
}



Token Token_stream::get() {   /* . to-do[4] . */ 
    if(full) {
        full = false;
        return buffer;
    }   

    char ch;
    cin >> ch; // >> 会跳过空白(空格、换行、制表符等)

    switch(ch) {
        case ';':   // 表示立即输出结果
        case 'q':   // 表示退出
        case '(': case ')': case '+': case '–': case '*': case '/':
            return Token{ch};   // 每个字符自身表示一个Token
        case '.':
        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
        { 
            cin.putback(ch);        // 将数字(或小数点)放回到 标准输入cin 中
            double val;
            cin >> val;             // cin 自动读取一个 double
            return Token{'8',val};  // 用'8'表示读取了一个浮点数
        }
        default:
            error("Bad token");
    }

}

// to-do[5]
// 构造函数 Token_stream()
Token_stream::Token_stream()
    :full{false}, buffer{0}   // no Token in buffer
{
}
Token_stream ts;   // provides get() and putback()  // to-do[5]

double expression();   // declaration so that primary() can call expression()

double primary() {   /* . to-do[8] . */ }   // deal with numbers and parentheses

double term() {   /* . to-do[7] . */ }   // deal with * and /

double expression() {   /* . to-do[6] . */  } // deal with + and –

int main() /* .to-do[ ]. */ 
{}

基于文法的简单算术表达式计算器 系列索引

  • [C++][第0篇] 系列索引 基于文法的算术表达式解释器
    关键词 系列索引

你可能感兴趣的:([C++][第2篇]从标准输入流cin中读取Token 什么是Token?)