编译原理——C++版桌面计算器
系统描述
设计并实现一个桌面计算器,支持基本四则运算,关系运算,可编程运算
基本功能
(1)以命令行方式解析用户输入的表达式,计算并显示结果;
(2)支持基本的整型和浮点类型数值计算;
(3)支持用变量和存储计算结果、变量引用,变量类型需声明,可根据保存的数据变化;
扩展功能
(1)支持输出变量类型
(2)支持输出推导产生式
(3)支持输出符号表
(4)支持输出语法分析动作表
(5)支持输出所有产生式
(6)支持输出文法first集
(7)支持输出文法follow集
实现语言和平台
实现语言:C++
实现平台:Visual Stdio2019
系统分析与设计
桌面计算器概述
通常来说编译器应具有词法分析,语法分析,语义分析,中间代码生成,代码优化,目标代码生成,符号表,错误处理器,这7个部分组成,由于本次实践目标是一个简单可编程桌面计算器,所以省去了中间代码生成,代码优化,目标代码生成,三个部分,并将语法分析与语义分析作为一个整体进行编程。
使用技术:上下文无关文法,自底向上的语法分析,与LR语法分析器结合的SDT
词法分析
词法分析是编译器的第一部分,词法分析器读入字符流并将它们组织成为有意义的lexeme序列。对于每个词素,词法分析器产生词法单元作为输出,词法单元格式为:
<token-name,attribute-value>
本编辑器的词法分析器输出存储在out.xml中
本编辑器词法分析器可识别词素如下表:
可识别符号表与运算符优先级表
Type Attribute-value Token-name
常量 Id 1
常量 True 2
常量 False 3
常量 Number 4
数据类型 Integer 5
数据类型 Decimal 6
数据类型 Bool 7
非关键字 8
运算符 ( 10
运算符 ) 11
运算符 ! 21
运算符 ++ 22
运算符 – 23
运算符 * 31
运算符 / 32
运算符 % 33
运算符 + 41
运算符 - 42
运算符 < 51
运算符 <= 52
运算符 > 53
运算符 >= 54
运算符 == 61
运算符 != 62
运算符 && 71
运算符 || 81
运算符 = 91
注释 /**/ 999
结束 ## 1000
1运算符和常量 id,true,false,number,decimal
2括号 ()
3一元运算 +, - !
4算术运算 *,/,%
5算术运算 +,-
6关系运算 <, <=, >, >=
7关系运算 ==, !=
8逻辑与 &&
9逻辑或 ||
10赋值/存储 =
语法分析
语法分析为编译器的第二步,语法分析器使用由词法分析器生成的各个Token结合给定文法进行语法分析,目的是成功由文法推导出可能的句子,常用方法为语法分析树,
本次实践中使用方法为与LR语法分析器结合的SDT,基本思路为,程序先读入给定文法,求出LR分析表,根据分析表和词法分析器给的Token,构造语法分析动作表,并将移入规约动作存入Reduce.xml。(程序中$list命令可以输出语法分析动作表)
CFG
S->L
L->L=H
L->H
H->H||G
H->G
G->G&&F
G->F
F->F!=E
F->F==E
F->E
E->EE<=D
E->E>D
E->E>=D
E->D
D->D-C
D->D+C
D->C
C->C*B
C->C/B
C->C%B
C->B
B->B++
B->B–
B->!B
B->A
A->(H)
A->id
A->true
A->false
A->var
语义分析
语义分析为编译器的第三步,语义分析器使用语法分析树和符号表中的信息来检查源程序是否和语言定义的语义一致。
根据移入规约动作与Token序列进行求结果,若有变量则去符号表中寻找值
符号表
程序中提供了符号表查看命令$symbols,符号表由语法分析过程中建立,当程序读入decimal或integer时进行符号表写入,初始值设定为0,可以在后续输入中进行变量赋值
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cctype>
#include <vector>
#include <string>
#include <queue>
#include <map>
#include <set>
#include <sstream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <stack>
#define _KEY_WORDEND "waiting for your expanding"
#define MAX 507
#define DEBUG
using namespace std;
typedef struct //词的结构,二元组形式(单词种别,单词自身的值)
{
int typenum; //单词种别
char* word;
}WORD;
char input[255];
char token[255] = "";
int p_input; //指针
int p_token;
char ch;
char* rwtab[] = {
"id","true","false","number","integer","decimal","bool",_KEY_WORDEND };//关键字
int var_list_symbol[50] = {
};//变量类型表
string var_list_name[50] = {
};//变量名字表
double var_list_value[50] = {
};//变量值
int var_list_size = 0;
bool cout_type = false;
bool cout_induction = false;
bool cout_action = false;
bool cout_list = false;
bool cout_first = false;
bool cout_follow = false;
bool cout_itemset = false;
bool cout_ERROR = false;
WORD* scanner();//扫描
int words(string input_str)
{
ofstream outfile("out.xml", ios::trunc);
int over = 1;
memset(input, 0, sizeof(input) / sizeof(char));
WORD* oneword = new WORD;
for (int i = 0; i < input_str.length(); i++) {
input[i] = input_str[i];
}
p_input = 0;
memset(token, 0, sizeof(token) / sizeof(char));
p_token = 0;
ch = 0;
//printf("your words: %s\n", input);
while (over < 1000 && over != -1)
{
oneword = scanner();
if (oneword->typenum < 1000)
{
if (oneword->typenum != 999)
outfile << oneword->typenum << " " << oneword->word << endl;
}
over = oneword->typenum;
}
outfile.close();
return 0;
}
//从输入缓冲区读取一个字符到ch中
char m_getch()
{
ch = input[p_input];
p_input++;
return ch;
}
//去掉空白符号
void getbc()
{
while (ch == ' ' || ch == 10)
{
ch = input[p_input];
p_input++;
}
}
//拼接单词
void concat()
{
token[p_token] = ch;
p_token++;
token[p_token] = '\0';
}
//判断是否字母
int letter()
{
if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z')
return 1;
else
return 0;
}
//判断是否数字
int digit()
{
if (ch >= '0' && ch <= '9')
return 1;
else if (ch == '.')
return 2;
else
return 0;
}
//检索关键字表格
int reserve()
{
int i = 0;
while (strcmp(rwtab[i], _KEY_WORDEND))
{
if (!strcmp(rwtab[i], token))
return i + 1;
i++;
}
return 8;//如果不是关键字,则返回种别码8
}
//回退一个字符
void retract()
{
p_input--;
}
//词法扫描程序
WORD* scanner()
{
WORD* myword = new WORD;
myword->typenum = 10; //初始值
myword->word = "";
p_token = 0; //单词缓冲区指针
m_getch();
getbc();//去掉空白
if (letter())//判断读取到的首字母是字母
{
//如int
while (letter() || digit())
{
concat(); //连接
m_getch();
}
retract(); //回退一个字符
myword->typenum = reserve();//判断是否为关键字,返回种别码
myword->word = token;
return myword;
}
else if (digit()) //判断读取到的单词首字符是数字
{
while (digit()) //所有数字连接起来
{
concat();
m_getch();
}
retract();
//数字单词种别码统一为1,单词自身的值为数字本身
myword->typenum = 1;
myword->word = token;
return(myword);
}
else switch (ch)
{
case '=':
m_getch();//首字符为=,再读取下一个字符判断
if (ch == '=')
{
myword->typenum = 61;
myword->word = "==";
return(myword);
}
retract();//读取到的下个字符不是=,则要回退,直接输出=
myword->typenum = 91;
myword->word = "=";
return(myword);
break;
case '+':
m_getch();//首字符为+,再读取下一个字符判断
if (ch == '+')
{
myword->typenum = 22;
myword->word = "++";
return(myword);
}
retract();//读取到的下个字符不是+,则要回退,直接输出+
myword->typenum = 41;
myword->word = "+";
return(myword);
break;
case '-':
m_getch();//首字符为+,再读取下一个字符判断
if (ch == '-')
{
myword->typenum = 23;
myword->word = "--";
return(myword);
}
retract();//读取到的下个字符不是+,则要回退,直接输出+
myword->typenum = 42;
myword->word = "-";
return(myword);
break;
case '/'://读取到该符号之后,要判断下一个字符是什么符号,判断是否为注释
m_getch();//首字符为/,再读取下一个字符判断
if (ch == '*') // 说明读取到的是注释
{
m_getch();
while (ch != '*')
{
m_getch();//注释没结束之前一直读取注释,但不输出
if (ch == '*')
{
m_getch();
if (ch == '/')//注释结束
{
myword->typenum = 999;
myword->word = "注释";
return (myword);
break;
}
}
}
}
else
{
retract();//读取到的下个字符不是*,即不是注释,则要回退,直接输出/
myword->typenum = 32;
myword->word = "/";
return (myword);
break;
}
case '*':
myword->typenum = 31;
myword->word = "*";
return(myword);
break;
case '(':
myword->typenum = 10;
myword->word = "(";
return(myword);
break;
case ')':
myword->typenum = 11;
myword->word = ")";
return(myword);
break;
case '%':
myword->typenum = 33;
myword->word = "%";
return(myword);
break;
case '>':
m_getch();
if (ch == '=')
{
myword->typenum = 54;
myword->word = ">=";
return(myword);
break;
}
retract();
myword->typenum = 53;
myword->word = ">";
return(myword);
break;
case '<':
m_getch();
if (ch == '=')
{
myword->typenum = 52;
myword->word = "<=";
return(myword);
break;
}
else
{
retract();
myword->typenum = 51;
myword->word = "<";
return (myword);
}
case '\0':
myword->typenum = 1000;
myword->word = "OVER";
return(myword);
break;
case '&':
m_getch();
if (ch == '&')
{
myword->typenum = 71;
myword->word = "&&";
return(myword);
break;
}
retract();
myword->typenum = -1;
myword->word = "ERROR";
return(myword);
break;
case '|':
m_getch();
if (ch == '|')
{
myword->typenum = 81;
myword->word = "||";
return(myword);
break;
}
retract();
myword->typenum = -1;
myword->word = "ERROR";
return(myword);
break;
case '!':
m_getch();
if (ch == '=')
{
myword->typenum = 62;
myword->word = "!=";
return(myword);
break;
}
else
{
retract();
myword->typenum = 21;
myword->word = "!";
return (myword);
}
case '$':
myword->typenum = 1000;
myword->word = "OVER";
return(myword);
break;
default:
myword->typenum = -1;
myword->word = "ERROR";
return(myword);
break;
}
}
class WF
{
public:
string left, right;
int back;
int id;
WF(char s1[], char s2[], int x, int y)
{
left = s1;
right = s2;
back = x;
id = y;
}
WF(const string& s1, const string& s2, int x, int y)
{
left = s1;
right = s2;
back = x;
id = y;
}
bool operator < (const WF& a) const
{
if (left == a.left)
return right < a.right;
return left < a.left;
}
bool operator == (const WF& a) const
{
return (left == a.left) && (right == a.right);
}
void print()
{
if (cout_itemset)printf("%s->%s\n", left.c_str(), right.c_str());
}
};
class Closure
{
public:
vector<WF> element;
void print(string str)
{
//项集族
if (cout_itemset)
printf("%-15s%-15s\n", "", str.c_str());
for (int i = 0; i < element.size(); i++)
element[i].print();
}
bool operator ==

最低0.47元/天 解锁文章
1369

被折叠的 条评论
为什么被折叠?



