使用Variable
// 新增[1]
class Variable {
public:
string name;
double value;
Variable(string n, double v) :name{ n }, value{ v } { }
};
// 新增[2]
vector var_table;
// 新增[3]
double get_value(string s)
// 返回 名字为 s 的变量的 值
{
for (int i = 0; i
顶层设计
变量初始化(声明) 与 变量赋值 的严格区分
// 变量声明
double x = 3.14;
double y {3.14};
// 变量赋值
x = 5.67;
y = 5.67;
- 我们选择更短的关键词
let
来代替 double
的功能
// 声明变量 x
let x = 3.14;
修改顶层设计的文法
Calculation:
Statement
Print
Quit
Calculation Statement
Statement:
Declaration
Expression
Declaration:
"let" Name "=" Expression
- 可见,从
Expression
开始就可以无缝衔接之前的文法;
新增文法对应函数 statement()
// 新增[5]
double statement()
{
Token t = ts.get();
switch (t.kind) {
case let:
return declaration();
default:
ts.putback(t);
return expression();
}
}
将函数 calculate() 中的 expression() 修改成 statement()
// 修改
void calculate()
{
while (cin)
cout << result << statement() << '\n'; // 修改成statement()
}
catch (exception& e) {
}
}
判断变量是否已经声明 与 新增变量
// 新增[6]
bool is_declared(string var)
// 变量 var 在 var_table 了吗?
{
for (int i = 0; i
declaration()
/*
Declaration:
"let" Name "=" Expression
*/
double declaration()
// 假设已经看到了 "let"
// 处理: name = expression
// 声明一个变量 "name",使用初始值 "expression"
{
Token t = ts.get();
if (t.kind != name) error("name expected in declaration");
string var_name = t.name;
Token t2 = ts.get();
if (t2.kind != '=') error("= missing in declaration of ", var_name);
double d = expression();
define_name(var_name, d);
return d;
}
引入name
使用常量
const char name = 'a'; // name token
const char let = 'L'; // declaration token
const string declkey = "let"; // declaration keyword
修改 Token 定义以存储字符串
// 修改
class Token { /* . to-do[1] . */
public:
char kind; // 记录token的类型
double value; // 对于浮点数而言:这是浮点数的
string name; // 变量 names: name 自身
Token(char ch) : kind{ ch }, value{ 0 } {}
Token(char ch, double val) : kind{ ch }, value{ val } {}
Token(char ch, string n) : kind{ ch }, name{ n } {}
};
定义 name
// 合法的name
a
aa
abc
assssssssssssssssssssssssssssd
ab1024
// 不合法的name
123abc
¥a
@sdd
x+y/2; // 一个表达式而非一个变量名
修改Token_stream::get() 来识别 name
Token Token_stream::get() { /* . to-do[4] . */
// 不变
switch (ch) {
// 不变
default:
if (isalpha(ch)) { // 字母开头
string s;
s += ch;
// 遇到字母或者数字就不断添加到字符串 s
while (cin.get(ch) && (isalpha(ch) || isdigit(ch))) s += ch;
cin.putback(ch);
if (s == declkey) return Token(let); // 关键词 "let"
return Token{ name, s};
}
error("Bad token");
}
}
修改primary 使变量加入计算
修改文法
Primary:
Number
Name
( Expression )
- Primary
+ Primary
- 这里新增的
Name
与Declaration
里面的 相呼应
修改代码
double primary()
{
Token t = ts.get();
switch (t.kind) {
case '(': // handle '(' expression ')'
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected");
return d;
}
case number:
return t.value; // return the number's value
case name:
return get_value(t.name); // 返回 变量的值
case '-':
return - primary();
case '+':
return primary();
default:
error("primary expected");
}
}
case name:
return get_value(t.name); // 返回 变量的值
全部功能已经实现 用例展示
程序使用 与 辅助检验
1. 编译运行源代码
2. 命令行窗口跳出来了
3. 在输入提示符 > 后面输入表达式,并且以分号作为一次输入的结束
4. 可以在一行之内进行多次计算比如
1+1;2-2;3*3;4/4;
5. 输入表达式不需要给使用的数字、变量以及运算符之间显式地用空格隔开,变量声明例外
6. 声明变量的语法是
let 变量名 = 变量值;
最后的分号不能省略
7. 使用变量就像使用普通的浮点数值一样,浮点数值可以放在哪里,变量就可以放在哪里
- 如何使用 octave-online
octave-online https://octave-online.net/
使用在线的计算工具检验用例的计算结果
1. 在 octave-online 里面要计算一个表达式,只需要输入这个表达式就可以了
2. 在下面的闪动的光标 》后面敲入 1+1 就会给出 ans = 2
3. ans 就是默认记录运算结果的变量
4. 旁边的橙色区域会显示处现在有的变量列表 Vars
加 减 乘 除 取余 括号 负数 功能
> 1;
= 1
> 1+1;
= 2
> 1+2+3;
= 6
> 1-2-3;
= -4
> 1+2*3;
= 7
> 1+2*3-4/5+6%2;
= 6.2
> (1);
= 1
> (1+2)*3;
= 9
> ((1+1)*1-(1+1)*1*1*1+(((1+1))));
= 2
> (1+2)*-3+-4*5-(7*8);
= -85
加减乘除取余检验用例结果
octave-online 加 减 乘 除 取余 负数 括号
变量功能
> let x = 1.11;
= 1.11
> x;
= 1.11
> let y = 2.22;
= 2.22
> y;
= 2.22
> x+x+x+x+x+x+x;
= 7.77
> x+y;
= 3.33
> let z = x+y;
= 3.33
> z;
= 3.33
> x+y+z;
= 6.66
> 1+x*(y-z)/2;
= 0.38395
>
变量功能检验用例结果
至此,全部完整源码
/*
Simple calculator
This program implements a basic expression calculator.
Input from cin; output to cout.
The grammar for input is:
Calculation:
Statement
Print
Quit
Calculation Statement
Statement:
Declaration
Expression
Declaration:
"let" Name "=" Expression
Print:
;
Quit:
q
Expression:
Term
Expression + Term
Expression - Term
Term:
Primary
Term * Primary
Term / Primary
Term % Primary
Primary:
Number
Name
( Expression )
- Primary
+ Primary
Number:
floating-point-literal
Input comes from cin through the Token_stream called ts.
*/
#include "std_lib_facilities.h"
const char number = '8';
const char quit = 'q';
const char print = ';';
const string prompt = "> ";
const string result = "= ";
const char name = 'a'; // name token
const char let = 'L'; // declaration token
const string declkey = "let"; // declaration keyword
class Token { /* . to-do[1] . */
public:
char kind; // 记录token的类型
double value; // 对于浮点数而言:这是浮点数的值
string name; // 变量 names: name 自身
Token(char ch) : kind{ ch }, value{ 0 } {}
Token(char ch, double val) : kind{ ch }, value{ val } {}
Token(char ch, string n) : kind{ ch }, name{ n } {}
};
class Token_stream { /* . to-do[2] . */
public:
Token_stream(); // 创建一个Token流,从cin读取字符
Token get(); // 获取一个Token
void putback(Token t); // 放回一个Token
void ignore(char c); // 忽略直到字符c为止的全部字符(最后字符c也被忽略掉)
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 quit:
case print:
case '(':
case ')':
case '+':
case '-':
case '*':
case '/':
case '%':
case '=':
return Token(ch); // 字符表示自己
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{ number, val }; // 用'8'表示读取了一个浮点数
}
default:
if (isalpha(ch)) {
string s;
s += ch;
while (cin.get(ch) && (isalpha(ch) || isdigit(ch))) s += ch;
cin.putback(ch);
if (s == declkey) return Token(let); // keyword "let"
return Token{ name, s };
}
error("Bad token");
}
}
void Token_stream::ignore(char c)
// c 表示Token的类型
{
// 先观察缓冲区
if (full && c == buffer.kind) {
full = false;
return;
}
full = false;
// 现在查找输入流
char ch = 0;
while (cin >> ch)
if (ch == c) return;
}
// 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]
class Variable {
public:
string name;
double value;
Variable(string n, double v) :name{ n }, value{ v } { }
};
vector var_table;
double get_value(string s)
// 返回 名字为 s 的变量的 值
{
for (int i = 0; i
基于文法的简单算术表达式计算器 系列索引
- [C++][第0篇] 系列索引 基于文法的算术表达式解释器
关键词 系列索引