使用全局变量
Token_stream ts; // provides get() and putback() // to-do[5]
全部文法回顾
// a simple expression grammar:
Expression:
Term
Expression "+" Term
Expression "–" Term
Term:
Primary
Term "*" Primary
Term "/" Primary
Term "%" Primary
Primary:
Number
"(" Expression ")"
Number:
floating-point-literal
to-do[?] int main() { }
int main() /* .to-do[ ]. */
try
{
double val = 0;
while (cin) {
Token t = ts.get();
if (t.kind == 'q') break; // 'q' 停止程序
if (t.kind == ';') // ';' 立即输出运算结果
cout << "=" << val << '\n';
else
ts.putback(t);
val = expression();
}
}
catch (exception& e) {
cerr << "error: " << e.what() << '\n';
return 1;
}
catch (...) {
cerr << "Oops: unknown exception!\n";
return 2;
}
测试用例及其输出效果
用例截图
用例使用
- 运行代码后在命令行窗口输入用例
2+3*7+5;
; - 2+3*7+5; 不用刻意留下空格,这是代码的功能,能正确识别成
Token
; - 注意以分号;作为单个算术表达式的结束 ;
- 命令行窗口立刻给出计算答案
=28
。
计算结果
2+3*7+5;
=28
3-(9/2);
=-1.5
33.33+22.22*7*2;
=344.41
7-8/2;
=3
q
to-do[6] double expression() {}
expression 文法回顾
Expression:
Term
Expression "+" Term
Expression "–" Term
expression() C++ 实现代码
double expression() { /* . to-do[8] . */
double left = term(); // 读入并计算一个 Term
Token t = ts.get(); // 获取下一个 Token
while(true) {
switch(t.kind) {
case '+':
left += term(); // 计算一个 Term 并且 相加
t = ts.get();
break;
case '-':
left -= term(); // 计算一个 Term 并且 相减
t = ts.get();
break;
default:
ts.putback(t); // 将 t 放回到 token 流
return left; // 再没有+ - 时返回计算结果
}
}
} // 处理 + 法 以及 - 法
to-do[7] double term() {}
term 文法回顾
Term:
Primary
Term "*" Primary
Term "/" Primary
Term "%" Primary
term C++ 实现代码
double term() { /* . to-do[7] . */
double left = primary();
Token t = ts.get(); // 从 token流 中获取下一个 token
while(true) {
switch (t.kind) {
case '*':
left *= primary();
t = ts.get();
break;
case '/':
{
double d = primary();
if (d == 0) error("divide by zero");
left /= d;
t = ts.get();
break;
}
default:
ts.putback(t); // put t back into the token stream
return left;
}
}
} // deal with * and /
to-do[8] double primary() { }
primary 文法回顾
Primary:
Number
"(" Expression ")"
( Expression ) 是一个 primary
- 体现到具体的C++代码实现之中,在定义
primary()
之前一定要写上一句关于expression()
的声明,回顾代码结构如下:
double expression(); // 仅仅是声明 让primary() 可以调用 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 –
primary() C++ 实现代码
double primary() { /* . to-do[8] . */
Token t = ts.get();
switch (t.kind) {
case '(': // 处理 '(' expression ')'
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected");
return d;
}
case '8': // 使用 '8' 来标记一个浮点数
return t.value; // 返回浮点数的值
default:
error("primary expected");
}
} // deal with numbers and parentheses
t = ts.get(); ts.putback(t); 作用是什么?
答:以Expression来说,文法显示任何一个Expression都是以一个Term开始,Term后面可以接一个+或者-。因此,必须先寻找Term,看它后面是否有一个+或者一个-,并且重复此步骤直到Term后面没有加号或者减号为止,为此写出一个while(true){}
的循环;对于像1+2+3;这样的表达式应该给出答案6,对于1-2-3这样的表达式应该给出答案 -4。用t = ts.get();
就是在帮助循环取下一个运算符,同时,ts.putback(t);
把读取的单词放回到全局变量的输入流ts里面,结合main()里面的条件语句,如果遇到了分号;就可以直接输出答案了,比如输入用例2;
应该是马上输出结果=2
;
至今为止的全部代码
#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] . */
Token t = ts.get();
switch (t.kind) {
case '(': // 处理 '(' expression ')'
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected");
return d;
}
case '8': // 使用 '8' 来标记一个浮点数
return t.value; // 返回浮点数的值
default:
error("primary expected");
}
} // deal with numbers and parentheses
double term() { /* . to-do[7] . */
double left = primary();
Token t = ts.get(); // 从 token流 中获取下一个 token
while(true) {
switch (t.kind) {
case '*':
left *= primary();
t = ts.get();
break;
case '/':
{
double d = primary();
if (d == 0) error("divide by zero");
left /= d;
t = ts.get();
break;
}
default:
ts.putback(t); // put t back into the token stream
return left;
}
}
} // deal with * and /
double expression() { /* . to-do[6] . */
double left = term(); // 读入并计算一个 Term
Token t = ts.get(); // 获取下一个 Token
while(true) {
switch(t.kind) {
case '+':
left += term(); // 计算一个 Term 并且 相加
t = ts.get();
break;
case '-':
left -= term(); // 计算一个 Term 并且 相减
t = ts.get();
break;
default:
ts.putback(t); // 将 t 放回到 token 流
return left; // 再没有+ - 时返回计算结果
}
}
} // 处理 + 法 以及 - 法
int main() /* .to-do[ ]. */
try
{
double val = 0;
while (cin) {
Token t = ts.get();
if (t.kind == 'q') break; // 'q' for quit
if (t.kind == ';') // ';' for "print now"
cout << "=" << val << '\n';
else
ts.putback(t);
val = expression();
}
}
catch (exception& e) {
cerr << "error: " << e.what() << '\n';
return 1;
}
catch (...) {
cerr << "Oops: unknown exception!\n";
return 2;
}
- 第六章的代码到此为止,已经可以出截图里面的效果了;
- 第七章的开始增加 变量功能 并且 对代码进行清理 等,之后代码长度会增加,变量个数会增加,但是这个算术表达式解释器的基本功能已经都实现了。
基于文法的简单算术表达式计算器 系列索引
- [C++][第0篇] 系列索引 基于文法的算术表达式解释器
关键词 系列索引