实现一个计算器,包括加减,乘除,括号,错误提示,id等,相当于一个小型的解释器。
其中重要的思想是
- 把字符转换成token
- 递归解析
//编译原理实现计算器
#include<bits/stdc++.h>
using namespace std;
//int factor(const char **) throw();
enum { //枚举:第一个元素=0,第二个就为1,依次类推
TOKEN_UNKNOWN = 0,
TOKEN_DIGITS,
TOKEN_ADD,
TOKEN_SUB,
TOKEN_MUL,
TOKEN_DIV,
TOKEN_MOD,
TOKEN_OPEN,
TOKEN_CLOSE,
TOKEN_SEMI,
TOKEN_EQUA,
TOKEN_ID,
};
struct Token {
int type;
int value;
string id;
};
struct Exp {
char *exps;
Token t;
map<string, int> m; //存参数
} e;
class ed:public exception {
public:
const char* what() const throw() {
return "ERROR_TYPE_EXPECT_DIGIT";
}
};
class ecp:public exception {
public:
const char* what() const throw() {
return "ERROR_TYPE_EXPECT_CLOSE_PARENTHESIS";
}
};
class dbz:public exception {
public:
const char* what() const throw() {
return "ERROR_TYPE_DIVISION_BY_ZERO";
}
};
class rbz:public exception {
public:
const char* what() const throw() {
return "ERROR_TYPE_REMAINDER_BY_ZERO";
}
};
class un:public exception {
public:
const char* what() const throw() {
return "ERROR_TYPE_UNKNOWN";
}
};
/*
本代码的next函数虽然名为next,但是主要功能还是只有判断token,没有地址++
*/
void next(Exp *e) { //一边扫一边解析就可以
while (*(e->exps) == ' ') { //空格则忽略
(e->exps)++;
}
char c = *(e->exps);
if (isdigit(c)) {
e->t.type = TOKEN_DIGITS;
e->t.value = c - '0';
while (isdigit(*(++(e->exps)))) {
e->t.value = ( e->t.value ) * 10 + (*(e->exps) - '0');
}
(e->exps)--;
}
else if (isalpha(c) || c == '_') {
string s;
s.push_back(c);
cout << s << endl;
char *ch = ++(e->exps);
while (isalpha(*ch) || isdigit(*ch) || *ch == '_') { //不能直接++(e->exps),会加三次
s.push_back(*(e->exps));
cout << "string=" << s << endl;
ch = ++(e->exps);
}
(e->exps)--;
e->t.id = s; //当前id
e->t.type = TOKEN_ID;
//cout << "first:id = " << e->t.id << " other:" << e->t.value <<" " << e->t.type << endl;
if (e->m.find(s) == e->m.end()) {
e->m[s] = 0; //默认值为0;
//e->t.value = INT_MAX; //value = 0 代表以前未出现过,但是不能这样
cout << "firstid = " << e->t.id << " value=:" << e->t.value <<" type=" << e->t.type << endl;
}
else {
e->t.value = e->m[s];
cout << "secondid="<<e->t.id << "value= " << e->t.value <<" type=" << e->t.type << endl;
}
}
else if (c == '+') {
e->t.type = TOKEN_ADD;
}
else if (c == '-') {
e->t.type = TOKEN_SUB;
}
else if (c == '*') {
e->t.type = TOKEN_MUL;
}
else if (c == '/') {
e->t.type = TOKEN_DIV;
}
else if (c == '%') {
e->t.type = TOKEN_MOD;
}
else if (c == '(') {
e->t.type = TOKEN_OPEN;
}
else if (c == ')') {
e->t.type = TOKEN_CLOSE;
}
else if (c == ';') {
e->t.type = TOKEN_SEMI;
}
else if (c == '=') {
e->t.type = TOKEN_EQUA;
}
/*不能有这一步,因为会使enter键变成unknown
else {
e->t.type = TOKEN_UNKNOWN;
throw un();
}*/
}
int expstr(Exp *e);
int expst(Exp *e);
/*
简单加法减法
expstr => num expstr1
expstr1 => '+' expstr | '-' expstr | null => '+' num expstr1 | '-' num expstr1 | null
即while循环
简单乘除法
明白为什么要使用指针的指针了, 因为expstr,term,num都要c++
重要:将++放到number里 ,更加方便
小括号即最高优先级
错误处理
*/
int number(Exp *e) {
if(e->t.type != TOKEN_DIGITS) throw ed();
else {
int ans = e->t.value;
(e->exps)++;
return ans;
}
}
int factor(Exp *e) {
next(e);
int ans;
if (e->t.type == TOKEN_OPEN) {
(e->exps)++;
ans = expst(e);
if (e->t.type == TOKEN_CLOSE) {
(e->exps)++;
}
else if (e->t.type != TOKEN_CLOSE) throw ecp();
}
else if (e->t.type == TOKEN_ID) {
string s = e->t.id;
ans = e->m[s];
cout << "canshu" << s << ans << endl;
(e->exps)++;
}
else ans = number(e);
return ans;
}
int term(Exp *e) { //乘除法
int ans = factor(e); //字符转换
next(e);
while(e->t.type == TOKEN_MUL || e->t.type == TOKEN_DIV || e->t.type == TOKEN_MOD) { //循环而非递归
if (e->t.type == TOKEN_MUL) {
(e->exps)++;
ans *= factor(e);
}
else if (e->t.type == TOKEN_DIV) {
(e->exps)++;
int f = factor(e);
if (f == 0) throw dbz();
else ans /= f;
}
else if (e->t.type == TOKEN_MOD) {
(e->exps)++;
ans %= factor(e);
int f = factor(e);
if (f == 0) throw rbz();
else ans %= f;
}
}
return ans;
}
int expstr(Exp *e) { //优先级最低的加减法 ,将所有的number()变成term()
int ans = term(e); //字符转换
next(e);
while(e->t.type == TOKEN_ADD || e->t.type == TOKEN_SUB) { //循环而非递归
if (e->t.type == TOKEN_ADD) {
(e->exps)++;
ans += term(e);
}
else if (e->t.type == TOKEN_SUB) {
(e->exps)++;
ans -= term(e);
}
}
return ans;
}
int expst (Exp *e) {
int ans;
next(e);
if (e->t.type == TOKEN_ID) {
string str = e->t.id;
cout << "here str" << str << endl;
(e->exps)++;
next(e);
//cout << "ok1" << endl;
if (e->t.type == TOKEN_EQUA) {
//cout << "ok2" << endl;
(e->exps)++;
e->m[str]= expstr(e);
cout << "e->m[s]:" << e->m[str] << endl;
ans = e->m[str];
}
}
else {
ans = expstr(e);
}
return ans;
}
int exp(Exp *e) {
int ans = expst(e);
next(e);
while(e->t.type == TOKEN_SEMI) {
cout << "OK" << endl;
(e->exps)++;
ans = expst(e);
next(e);
}
return ans;
}
int main() {
char s[100];
gets(s);
e.exps = s;
try {
printf("%d\n", exp(&e));
}catch(ecp &m) {
printf("%s\n", m.what());
}catch(ed &m) {
printf("%s\n", m.what());
}catch(dbz &m) {
printf("%s\n", m.what());
}catch(rbz &m) {
printf("%s\n", m.what());
}/*catch(un &m) {
printf("%s\n", m.what());
}*/catch(...){
printf("Unkown exception caught!\n");
}
return 0;
}
参考教程:
https://blog.harrisonxi.com/2019/07/编译原理入门课:(前言)实现一个表达式解析计算器.html
https://lotabout.me/2015/write-a-C-interpreter-0/