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篇] 系列索引 基于文法的算术表达式解释器
关键词 系列索引