编译原理实验3

@

实验三: Tiny扩充语言的语法分析

一、实验内容:

扩充的语法规则有:实现 while、do while、for、if语句、+= 加法赋值运算符号(类似于C语言的+=)、
求余%、乘方^、<=(小于等于)、<>(不等于)运算符号,具体文法规则自行构造。

可参考:云盘中参考书P97及P136的文法规则。

(1) While-stmt --> while(exp) stmt-sequence endwhile
(2) Dowhile-stmt-->do stmt-sequence while(exp);
(3) for-stmt-->for identifier:=simple-exp to simple-exp do stmt-sequence enddo 步长递增1
(4) for-stmt-->for identifier:=simple-exp downto simple-exp do stmt-sequence enddo 步长递减1
(5) += 加法赋值运算符号、求余%、乘方^、<=(小于等于)、<>(不等于)运算符号的文法规则请自行组织。
(6) 把TINY语言原有的if语句书写格式
if_stmt-->if exp then stmt-sequence end | | if exp then stmt-sequence else stmt-sequence end
改写为:
if_stmt-->if(exp) stmt-sequence else stmt-sequence | if(exp) stmt-sequence
(7)为了实现以上的扩充或改写功能,还需要对原tiny语言的文法规则做如何的处理?

二、要求:
(1)要提供一个源程序编辑界面,以让用户输入源程序(可保存、打开源程序)
(2)可由用户选择是否生成语法树,并可查看所生成的语法树。
(3)应该书写完善的软件文档

三、完成时间:四周(第9周-第13周)

四、上交方法:
由各班班长或学习委员将每个同学的实验源程序、可执行程序、测试用例、文档刻录成光盘。

五、完成方式:每个学生自行独立完成。

六、测试数据

测试文件1:

{ Sample program
in TINY language -
computes factorial
}
read x; { input an integer }
if (0<x) { don't compute if x <= 0 }
fact := 1;
do
fact := fact * x;
x := x - 1
while ( 0<x );
write fact; { output factorial of x }

测试文件2:

{ Sample program
in TINY language -
computes factorial
}
read x; { input an integer }
if ( 0<x ) { don't compute if x <= 0 }
for fact := x downto 1 do
fact := fact * x;
enddo
write fact; { output factorial of x }

测试文件3:

{ Sample program
in TINY language -
computes factorial
}
read x; { input an integer }
if ( 0<x ) { don't compute if x <= 0 }
fact := 1;
while(0<x)
fact := fact * x;
x := x - 1
endwhile
write fact; { output factorial of x }

为了做好全面的测试,你还需要增加相应的测试文件。

任务

扩充文法规则

更改前:

program -> stmt-sequence
stmt-sequence -> statement { ; statement }
statement -> if-stmt | repeat-stmt | assign-stmt | read-stmt | write-stmt
if-stmt -> if exp then stmt-sequence[else stmt-sequence] end
repeat-stmt -> repeat stmt-sequence until exp
assign-stmt -> identifier := exp
read-stmt -> read identifier
write-stmt -> write exp
exp -> simple-exp[comparison-op simple-exp]
comparison-op -> < | =
simple-exp -> term {addop term}
addop -> + | -
term -> factor {mulop factor}
mulop -> * | /
factor -> (exp) | number | identifier

更改后:

program -> stmt-sequence
stmt-sequence -> statement { ; statement }
statement -> if-stmt | repeat-stmt | assign-stmt | read-stmt | write-stmt | While-stmt | Dowhile-stmt | for-stmt
if-stmt -> if(exp)stmt-sequence[else stmt-sequence]
repeat-stmt -> repeat stmt-sequence until exp
assign-stmt -> identifier assignop exp
assignop -> [:= | +=]
read-stmt -> read identifier
write-stmt -> write exp
While-stmt -> while(exp) stmt-sequence endwhile
Dowhile-stmt -> do stmt-sequence while(exp)
for-stmt -> for identifier:=simple-exp [to | downto] simple-exp do stmt-sequence enddo
exp -> simple-exp[comparison-op simple-exp]
comparison-op -> < | = | <= | <>
simple-exp -> term {addop term}
addop -> + | -
term -> factor {mulop factor}
mulop -> * | / | %
factor -> powernum {powerop powernum}
powerop -> ^
powernum -> (exp) | number | identifier

改写对应parse代码

#include <iostream>
#include <fstream>
#include <string>
#include <string.h>
using namespace std;

/*********************************************************
创建核心类Core,功能为:
(1) 类中拥有属性fileString,并且该fileString内容
可以被更改
(2) char getNextChar(); 读取fileString中的字符
(3) Token getToken(); 返回Token对象(tokenType, tokenString)
(4) TreeNode * parse(); 返回一棵语法树
(5) string treeStr(); 将语法树以字符串的形式呈现
*********************************************************/

#define MAXRESERVED 15
#define TRUE 1
#define FALSE 0

enum TokenType
/* book-keeping tokens */
{ENDFILE,ERROR,
/* reserved words */
IF,THEN,ELSE,END,REPEAT,UNTIL,READ,WRITE,WHILE,ENDWHILE,
DO, ENDDO, FOR, TO, DOWNTO,
/* multicharacter tokens */
ID,NUM,
/* special symbols */
ASSIGN,EQ,LT,LTEQ,LTRT,PLUS,MINUS,TIMES,OVER,POWER,REM,
LPAREN,RPAREN,SEMI
};

enum StateType{ START,INASSIGN,INPLUS,INLT,INCOMMENT,INNUM,INID,DONE };

enum NodeKind{StmtK,ExpK};

enum StmtKind{IfK,RepeatK,AssignK,ReadK,WriteK,WhileK,DowhileK,ForK};

enum ExpKind{OpK,ConstK,IdK};

enum ExpType{Void,Integer,Boolean};
   
class TreeNode{
public:
    TreeNode * child[3];
    TreeNode * sibling;
    NodeKind nodekind;
    union {StmtKind stmtkind; ExpKind expkind;} kind;
    union {
        TokenType op;
        int val;
        char * name;
    }attr;
    char * forType;
    ExpType type;
};

struct Token{
    TokenType type;
    string str;
    Token(TokenType t=END, string s=""){
        type = t;
        str = s;
    }
    Token(const Token &token){
        type = token.type;
        str = token.str;
    }
};

struct{ 
    string tokenString;
    TokenType tokenType;
}reservedWords[MAXRESERVED]
= {{"if",IF},{"then",THEN},{"else",ELSE},{"end",END},
    {"repeat",REPEAT},{"until",UNTIL},{"read",READ},
    {"write",WRITE},{"while", WHILE},{"endwhile", ENDWHILE},
    {"do", DO}, {"for", FOR}, {"to", TO}, {"downto", DOWNTO},
    {"enddo", ENDDO}};

class Core{
public:
    string fileString = "";  //
    int strIndex = 0;
    Token token = Token();

public:
    string run(const string &fs);

public:
    TreeNode * parse(); //生成语法树
    string treeStr(TreeNode * root, int num); //获取语法数的字符串形式
    Token getToken();   //获取token
    char getNextChar();  //获取字符 
    void delTree(TreeNode * root);//删除语法树(防止系统开辟的动态内存过多)

    TreeNode * stmt_sequence();
    TreeNode * statement();
    TreeNode * if_stmt();
    TreeNode * repeat_stmt();
    TreeNode * assign_stmt();
    TreeNode * read_stmt();
    TreeNode * write_stmt();
    TreeNode * while_stmt();
    TreeNode * dowhile_stmt();
    TreeNode * for_stmt();
    TreeNode * exp();
    TreeNode * simple_exp();
    TreeNode * term();
    TreeNode * factor();
    TreeNode * powernum();
    void match(TokenType expected);
    TreeNode * newStmtNode(StmtKind kind);
    TreeNode * newExpNode(ExpKind kind);
    TokenType reservedLookup(string TokenString);
    static string tokenTypeStr(TokenType token);
};

string Core::run(const string &fs){
    strIndex = 0;
    fileString = fs;
    TreeNode * root = parse();
    string result = treeStr(root, 0);
    delTree(root);
    return result;
}

TreeNode * Core::parse(){
    TreeNode * t;
    token = getToken();
    t = stmt_sequence();
    if (token.type != ENDFILE)
        cout << "Code ends before file" << endl;
    return t;
}

string Core::treeStr(TreeNode * root, int num){
    string all = "";
    while(root != nullptr){
        string a = "";
        for(int i=0; i<num; ++i)
            a += " ";
        if(root->nodekind==StmtK){
            switch(root->kind.stmtkind){
                case IfK:
                    a += "IF\n";
                    break;
                case RepeatK:
                    a += "Repeat\n";
                    break;
                case AssignK:
                    a += "Assign to ";
                    a += root->attr.name;
                    a += '\n';
                    break;
                case ReadK:
                    a += "Read ";
                    a += root->attr.name;
                    a += '\n';
                    break;
                case WriteK:
                    a += "Write\n";
                    break;
                case WhileK:
                    a += "While\n";
                    break;
                case DowhileK:
                    a += "Dowhile\n";
                    break;
                case ForK:
                    a += "For ";
                    a += root->attr.name;
                    a += root->forType;
                    a += '\n';
                    break;
                default:
                    break;
            }
        }
        else{
            switch(root->kind.expkind){
                case OpK:
                    a += "Op: ";
                    a += tokenTypeStr(root->attr.op);
                    a += '\n';
                    break;
                case ConstK:
                    a += "Const: ";
                    a += to_string(root->attr.val);
                    a += '\n';
                    break;
                case IdK:
                    a += "Id: ";
                    a += root->attr.name;
                    a += '\n';
                    break;
                default:
                    break;
            }
        }
        for(int i=0; i<3; ++i){
            a += treeStr(root->child[i], num+2);
        }
        root = root->sibling;
        all += a;
    }
    return all;
}

void Core::delTree(TreeNode * root){
    if(root == nullptr)
        return;
    delTree(root->sibling);
    for(int i=0; i<3; ++i)
        delTree(root->child[i]);
    delete root;
}

char Core::getNextChar(){
    return fileString[strIndex++];
}

Token Core::getToken(){
    TokenType tokenType;
    string tokenString = "";
    StateType state = START;
    int save;
    while (state != DONE){ 
        char c = getNextChar();
        save = TRUE;
        switch (state)
        { 
            case START:
            if (isdigit(c))
            state = INNUM;
            else if (isalpha(c))
            state = INID;
            else if (c == ':')
            state = INASSIGN;
            else if (c == '+')
            state = INPLUS;
            else if (c == '<')
            state = INLT;
            else if ((c == ' ') || (c == '\t') || (c == '\n'))
            save = FALSE;
            else if (c == '{')
            { save = FALSE;
            state = INCOMMENT;
            }
            else{   
                state = DONE;
                switch (c){ 
                    case '\0':
                    save = FALSE;
                    tokenType = ENDFILE;
                    break;
                    case '=':
                    tokenType = EQ;
                    break;
                    case '-':
                    tokenType = MINUS;
                    break;
                    case '*':
                    tokenType = TIMES;
                    break;
                    case '/':
                    tokenType = OVER;
                    break;
                    case '^':
                    tokenType = POWER;
                    break;
                    case '%':
                    tokenType = REM;
                    break;
                    case '(':
                    tokenType = LPAREN;
                    break;
                    case ')':
                    tokenType = RPAREN;
                    break;
                    case ';':
                    tokenType = SEMI;
                    break;
                    default:
                    tokenType = ERROR;
                    break;
                }
            }
            break;
        case INCOMMENT:
            save = FALSE;
            if (c == '\0')
            { state = DONE;
            tokenType = ENDFILE;
            }
            else if (c == '}') state = START;
            break;
        case INASSIGN:
            state = DONE;
            if (c == '=')
            tokenType = ASSIGN;
            else{
            --strIndex;
            save = FALSE;
            tokenType = ERROR;
            }
            break;
        case INPLUS:
            state = DONE;
            if(c == '=')
            tokenType = ASSIGN;
            else{
                --strIndex;
                save = FALSE;
                tokenType = PLUS;
            }
            break;
        case INLT:
            state = DONE;
            if(c == '=')
            tokenType = LTEQ;
            else if(c == '>')
            tokenType = LTRT;
            else{
                --strIndex;
                save = FALSE;
                tokenType = LT;
            }
            break;
        case INNUM:
            if (!isdigit(c))
            { /* backup in the input */
            --strIndex;
            save = FALSE;
            state = DONE;
            tokenType = NUM;
            }
            break;
        case INID:
            if (!isalpha(c))
            { /* backup in the input */
            --strIndex;
            save = FALSE;
            state = DONE;
            tokenType = ID;
            }
            break;
        case DONE:
        default: /* should never happen */
            state = DONE;
            tokenType = ERROR;
            break;
        }
        if (save)
            tokenString += c;
        if (state == DONE)
        { 
            if (tokenType == ID)
                tokenType = reservedLookup(tokenString);
        }
    }
    return Token(tokenType, tokenString);
}

TokenType Core::reservedLookup(string tokenString){
    for(int i=0; i<MAXRESERVED; ++i){
        if (tokenString == reservedWords[i].tokenString)
            return reservedWords[i].tokenType;
    }
    return ID;
}

TreeNode * Core::stmt_sequence(){
    TreeNode * t = statement();
    TreeNode * p = t;
    TreeNode * q;
    match(SEMI);
    while((token.type==IF) || (token.type==REPEAT) || 
    (token.type==ID) || (token.type==READ) || (token.type==WRITE) ||
    (token.type==WHILE) || (token.type==DO) || (token.type==FOR)){
        q = statement();
        if(q!=nullptr){
            if(t==nullptr) t = p = q;
            else{
                p->sibling = q;
                p = q;
            }
        }
        match(SEMI);
    }
    return t; 
}

TreeNode * Core::statement()
{ TreeNode * t = nullptr;
  switch (token.type) {
    case IF : t = if_stmt(); break;
    case REPEAT : t = repeat_stmt(); break;
    case ID : t = assign_stmt(); break;
    case READ : t = read_stmt(); break;
    case WRITE : t = write_stmt(); break;
    case WHILE : t = while_stmt(); break;
    case DO: t = dowhile_stmt(); break;
    case FOR: t = for_stmt(); break;
    default: 
            token = getToken();
            break;
  } 
  return t;
}

TreeNode * Core::if_stmt(void)
{ TreeNode * t = newStmtNode(IfK);
  match(IF);
  match(LPAREN);
  if (t!=nullptr) t->child[0] = exp();
  match(RPAREN);
  if (t!=nullptr) t->child[1] = stmt_sequence();
  if (token.type==ELSE) {
    match(ELSE);
    if (t!=nullptr) t->child[2] = stmt_sequence();
  }
  return t;
}

TreeNode * Core::repeat_stmt(void)
{ TreeNode * t = newStmtNode(RepeatK);
  match(REPEAT);
  if (t!=nullptr) t->child[0] = stmt_sequence();
  match(UNTIL);
  if (t!=nullptr) t->child[1] = exp();
  return t;
}

TreeNode * Core::assign_stmt(void)
{ TreeNode * t = newStmtNode(AssignK);
  if ((t!=nullptr) && (token.type==ID)){
    t->attr.name = new char[token.str.size()+1];
    strcpy(t->attr.name, token.str.c_str());
  }
  match(ID);
  match(ASSIGN);
  if (t!=nullptr) t->child[0] = exp();
  return t;
}

TreeNode * Core::read_stmt(void)
{ TreeNode * t = newStmtNode(ReadK);
  match(READ);
  if ((t!=nullptr) && (token.type==ID)){
    t->attr.name = new char[token.str.size()+1];
    strcpy(t->attr.name, token.str.c_str());
  }
  match(ID);
  return t;
}

TreeNode * Core::write_stmt(void)
{ TreeNode * t = newStmtNode(WriteK);
  match(WRITE);
  if (t!=nullptr) t->child[0] = exp();
  return t;
}

TreeNode * Core::while_stmt(void){
    TreeNode * t = newStmtNode(WhileK);
    match(WHILE);
    match(LPAREN);
    if(t!=nullptr) t->child[0] = exp();
    match(RPAREN);
    if(t!=nullptr) t->child[1] = stmt_sequence();
    match(ENDWHILE);
    return t;
}

TreeNode * Core::dowhile_stmt(void){
    TreeNode * t = newStmtNode(DowhileK);
    match(DO);
    if(t!=nullptr) t->child[0] = stmt_sequence();
    match(WHILE);
    match(LPAREN);
    if(t!=nullptr) t->child[1] = exp();
    match(RPAREN);
    return t;
}

TreeNode * Core::for_stmt(void){
    TreeNode * t = newStmtNode(ForK);
    if ((t!=nullptr) && (token.type==ID)){
        t->attr.name = new char[token.str.size()+1];
        strcpy(t->attr.name, token.str.c_str());
    }
    match(ID);
    match(ASSIGN);
    if (t!=nullptr) t->child[0] = simple_exp();
    if(token.type == TO){
        t->forType = "To";
        match(TO);
    }
    else{
        t->forType = "Downto";
        match(DOWNTO);
    }
    if (t!=nullptr) t->child[1] = simple_exp();
    match(DO);
    if (t!=nullptr) t->child[2] = stmt_sequence();
    match(ENDDO);
    return t;
}

TreeNode * Core::exp(void)
{ TreeNode * t = simple_exp();
  if ((token.type==LT)||(token.type==EQ)||(token.type==LTEQ)||(token.type==LTRT)) {
    TreeNode * p = newExpNode(OpK);
    if (p!=nullptr) {
      p->child[0] = t;
      p->attr.op = token.type;
      t = p;
    }
    match(token.type);
    if (t!=nullptr)
      t->child[1] = simple_exp();
  }
  return t;
}

TreeNode * Core::simple_exp(void)
{ TreeNode * t = term();
  while ((token.type==PLUS)||(token.type==MINUS)||(token.type==REM))
  { TreeNode * p = newExpNode(OpK);
    if (p!=nullptr) {
      p->child[0] = t;
      p->attr.op = token.type;
      t = p;
      match(token.type);
      t->child[1] = term();
    }
  }
  return t;
}

TreeNode * Core::term(void)
{ TreeNode * t = factor();
  while ((token.type==TIMES)||(token.type==OVER))
  { TreeNode * p = newExpNode(OpK);
    if (p!=nullptr) {
      p->child[0] = t;
      p->attr.op = token.type;
      t = p;
      match(token.type);
      p->child[1] = factor();
    }
  }
  return t;
}

TreeNode * Core::factor(void){
    TreeNode * t = powernum();
    while(token.type==POWER){
        TreeNode * p = newExpNode(OpK);
        if(p!=nullptr){
            p->child[0] = t;
            p->attr.op = token.type;
            t = p;
            match(token.type);
            p->child[1] = powernum();
        }
    }
    return t;
}

TreeNode * Core::powernum(void)
{ TreeNode * t = nullptr;
  switch (token.type) {
    case NUM :
      t = newExpNode(ConstK);
      if ((t!=nullptr) && (token.type==NUM))
        t->attr.val = stoi(token.str);
      match(NUM);
      break;
    case ID :
      t = newExpNode(IdK);
      if ((t!=nullptr) && (token.type==ID)){
        t->attr.name = new char[token.str.size()+1];
        strcpy(t->attr.name, token.str.c_str());
      }
      match(ID);
      break;
    case LPAREN :
      match(LPAREN);
      t = exp();
      match(RPAREN);
      break;
    default:
      token = getToken();
      break;
    }
  return t;
}

void Core::match(TokenType expected)
{ 
    if (token.type == expected) token = getToken();
}

TreeNode * Core::newStmtNode(StmtKind kind)
{ 
  TreeNode * t = new TreeNode();
  int i;
  if (t==nullptr)
    cout << "Out of memory error at line\n";
  else {
    for (i=0;i<3;i++) t->child[i] = nullptr;
    t->sibling = nullptr;
    t->nodekind = StmtK;
    t->kind.stmtkind = kind;
  }
  return t;
}

TreeNode * Core::newExpNode(ExpKind kind)
{
  TreeNode * t = new TreeNode();
  int i;
  if (t==nullptr)
    cout << "Out of memory error at line\n";
  else {
    for (i=0;i<3;i++) t->child[i] = nullptr;
    t->sibling = nullptr;
    t->nodekind = ExpK;
    t->kind.expkind = kind;
    t->type = Void;
  }
  return t;
}

string Core::tokenTypeStr(TokenType token){
    switch(token){
        case 0:
            return "ENDFILE";
        case 1:
            return "ERROR";
        case 2:
            return "IF";
        case 3:
            return "THEN";
        case 4:
            return "ELSE";
        case 5:
            return "END";
        case 6:
            return "REPEAT";
        case 7:
            return "UNTIL";
        case 8:
            return "READ";
        case 9:
            return "WRITE";
        case 10:
            return "WHILE";
        case 11:
            return "ENDWHILE";
        case 12:
            return "DO";
        case 13:
            return "ENDDO";
        case 14:
            return "FOR";
        case 15:
            return "TO";
        case 16:
            return "DOWNTO"; 
        case 17:
            return "ID";
        case 18:
            return "NUM";
        case 19:
            return "ASSIGN";
        case 20:
            return "EQ";
        case 21:
            return "LT";
        case 22:
            return "LTEQ";
        case 23:
            return "LTRT";
        case 24:
            return "PLUS";
        case 25:
            return "MINUS";
        case 26:
            return "TIMES";
        case 27:
            return "OVER";
        case 28:
            return "POWER";
        case 29:
            return "REM";
        case 30:
            return "LPAREN";
        case 31:
            return "RPAREN";
        case 32:
            return "SEMI";
    }
}

int main(){
    ifstream in("source.tny", ios::in);
    istreambuf_iterator<char> beg(in), end;
    string str(beg, end);
    in.close(); 
    Core core;
    string result = core.run(str);
    cout << result;
}

可视化界面(QT)

代码
global.h

#ifndef _GLOBAL_H_
#define _GLOBAL_H_

#include <string>
using namespace std;
#define MAXRESERVED 15
#define TRUE 1
#define FALSE 0

enum TokenType
/* book-keeping tokens */
{ENDFILE,ERROR,
/* reserved words */
IF,THEN,ELSE,END,REPEAT,UNTIL,READ,WRITE,WHILE,ENDWHILE,
DO, ENDDO, FOR, TO, DOWNTO,
/* multicharacter tokens */
ID,NUM,
/* special symbols */
ASSIGN,EQ,LT,LTEQ,LTRT,PLUS,MINUS,TIMES,OVER,POWER,REM,
LPAREN,RPAREN,SEMI
};

enum StateType{ START,INASSIGN,INPLUS,INLT,INCOMMENT,INNUM,INID,DONE };

enum NodeKind{StmtK,ExpK};

enum StmtKind{IfK,RepeatK,AssignK,ReadK,WriteK,WhileK,DowhileK,ForK};

enum ExpKind{OpK,ConstK,IdK};

enum ExpType{Void,Integer,Boolean};
   
class TreeNode{
public:
    TreeNode * child[3];
    TreeNode * sibling;
    NodeKind nodekind;
    union {StmtKind stmtkind; ExpKind expkind;} kind;
    union {
        TokenType op;
        int val;
        char * name;
    }attr;
    char * forType;
    ExpType type;
};

struct Token{
    TokenType type;
    string str;
    Token(TokenType t=END, string s=""){
        type = t;
        str = s;
    }
    Token(const Token &token){
        type = token.type;
        str = token.str;
    }
};

struct{ 
    string tokenString;
    TokenType tokenType;
}reservedWords[MAXRESERVED]
= {{"if",IF},{"then",THEN},{"else",ELSE},{"end",END},
    {"repeat",REPEAT},{"until",UNTIL},{"read",READ},
    {"write",WRITE},{"while", WHILE},{"endwhile", ENDWHILE},
    {"do", DO}, {"for", FOR}, {"to", TO}, {"downto", DOWNTO},
    {"enddo", ENDDO}};

#endif

core.h

#ifndef _CORE_H_
#define _CORE_H_

#include <string>
#include "global.h"
using namespace std;

class Core{
public:
    string fileString = "";  //
    int strIndex = 0;
    Token token = Token();

public:
    string run(const string &fs);

public:
    TreeNode * parse(); //生成语法树
    string treeStr(TreeNode * root, int num); //获取语法数的字符串形式
    Token getToken();   //获取token
    char getNextChar();  //获取字符 
    void delTree(TreeNode * root);//删除语法树(防止系统开辟的动态内存过多)

    TreeNode * stmt_sequence();
    TreeNode * statement();
    TreeNode * if_stmt();
    TreeNode * repeat_stmt();
    TreeNode * assign_stmt();
    TreeNode * read_stmt();
    TreeNode * write_stmt();
    TreeNode * while_stmt();
    TreeNode * dowhile_stmt();
    TreeNode * for_stmt();
    TreeNode * exp();
    TreeNode * simple_exp();
    TreeNode * term();
    TreeNode * factor();
    TreeNode * powernum();
    void match(TokenType expected);
    TreeNode * newStmtNode(StmtKind kind);
    TreeNode * newExpNode(ExpKind kind);
    TokenType reservedLookup(string TokenString);
    static string tokenTypeStr(TokenType token);
};

#endif

core.cpp

#include <iostream>
#include <fstream>
#include <string>
#include <string.h>
#include "core.h"
using namespace std;

/*********************************************************
创建核心类Core,功能为:
(1) 类中拥有属性fileString,并且该fileString内容
可以被更改
(2) char getNextChar(); 读取fileString中的字符
(3) Token getToken(); 返回Token对象(tokenType, tokenString)
(4) TreeNode * parse(); 返回一棵语法树
(5) string treeStr(); 将语法树以字符串的形式呈现
*********************************************************/


string Core::run(const string &fs){
    strIndex = 0;
    fileString = fs;
    TreeNode * root = parse();
    string result = treeStr(root, 0);
    delTree(root);
    return result;
}

TreeNode * Core::parse(){
    TreeNode * t;
    token = getToken();
    t = stmt_sequence();
    if (token.type != ENDFILE)
        cout << "Code ends before file" << endl;
    return t;
}

string Core::treeStr(TreeNode * root, int num){
    string all = "";
    while(root != nullptr){
        string a = "";
        for(int i=0; i<num; ++i)
            a += " ";
        if(root->nodekind==StmtK){
            switch(root->kind.stmtkind){
                case IfK:
                    a += "IF\n";
                    break;
                case RepeatK:
                    a += "Repeat\n";
                    break;
                case AssignK:
                    a += "Assign to ";
                    a += root->attr.name;
                    a += '\n';
                    break;
                case ReadK:
                    a += "Read ";
                    a += root->attr.name;
                    a += '\n';
                    break;
                case WriteK:
                    a += "Write\n";
                    break;
                case WhileK:
                    a += "While\n";
                    break;
                case DowhileK:
                    a += "Dowhile\n";
                    break;
                case ForK:
                    a += "For ";
                    a += root->attr.name;
                    a += " ";
                    a += root->forType;
                    a += '\n';
                    break;
                default:
                    break;
            }
        }
        else{
            switch(root->kind.expkind){
                case OpK:
                    a += "Op: ";
                    a += tokenTypeStr(root->attr.op);
                    a += '\n';
                    break;
                case ConstK:
                    a += "Const: ";
                    a += to_string(root->attr.val);
                    a += '\n';
                    break;
                case IdK:
                    a += "Id: ";
                    a += root->attr.name;
                    a += '\n';
                    break;
                default:
                    break;
            }
        }
        for(int i=0; i<3; ++i){
            a += treeStr(root->child[i], num+2);
        }
        root = root->sibling;
        all += a;
    }
    return all;
}

void Core::delTree(TreeNode * root){
    if(root == nullptr)
        return;
    delTree(root->sibling);
    for(int i=0; i<3; ++i)
        delTree(root->child[i]);
    delete root;
}

char Core::getNextChar(){
    return fileString[strIndex++];
}

Token Core::getToken(){
    TokenType tokenType;
    string tokenString = "";
    StateType state = START;
    int save;
    while (state != DONE){ 
        char c = getNextChar();
        save = TRUE;
        switch (state)
        { 
            case START:
            if (isdigit(c))
            state = INNUM;
            else if (isalpha(c))
            state = INID;
            else if (c == ':')
            state = INASSIGN;
            else if (c == '+')
            state = INPLUS;
            else if (c == '<')
            state = INLT;
            else if ((c == ' ') || (c == '\t') || (c == '\n'))
            save = FALSE;
            else if (c == '{')
            { save = FALSE;
            state = INCOMMENT;
            }
            else{   
                state = DONE;
                switch (c){ 
                    case '\0':
                    save = FALSE;
                    tokenType = ENDFILE;
                    break;
                    case '=':
                    tokenType = EQ;
                    break;
                    case '-':
                    tokenType = MINUS;
                    break;
                    case '*':
                    tokenType = TIMES;
                    break;
                    case '/':
                    tokenType = OVER;
                    break;
                    case '^':
                    tokenType = POWER;
                    break;
                    case '%':
                    tokenType = REM;
                    break;
                    case '(':
                    tokenType = LPAREN;
                    break;
                    case ')':
                    tokenType = RPAREN;
                    break;
                    case ';':
                    tokenType = SEMI;
                    break;
                    default:
                    tokenType = ERROR;
                    break;
                }
            }
            break;
        case INCOMMENT:
            save = FALSE;
            if (c == '\0')
            { state = DONE;
            tokenType = ENDFILE;
            }
            else if (c == '}') state = START;
            break;
        case INASSIGN:
            state = DONE;
            if (c == '=')
            tokenType = ASSIGN;
            else{
            --strIndex;
            save = FALSE;
            tokenType = ERROR;
            }
            break;
        case INPLUS:
            state = DONE;
            if(c == '=')
            tokenType = ASSIGN;
            else{
                --strIndex;
                save = FALSE;
                tokenType = PLUS;
            }
            break;
        case INLT:
            state = DONE;
            if(c == '=')
            tokenType = LTEQ;
            else if(c == '>')
            tokenType = LTRT;
            else{
                --strIndex;
                save = FALSE;
                tokenType = LT;
            }
            break;
        case INNUM:
            if (!isdigit(c))
            { /* backup in the input */
            --strIndex;
            save = FALSE;
            state = DONE;
            tokenType = NUM;
            }
            break;
        case INID:
            if (!isalpha(c))
            { /* backup in the input */
            --strIndex;
            save = FALSE;
            state = DONE;
            tokenType = ID;
            }
            break;
        case DONE:
        default: /* should never happen */
            state = DONE;
            tokenType = ERROR;
            break;
        }
        if (save)
            tokenString += c;
        if (state == DONE)
        { 
            if (tokenType == ID)
                tokenType = reservedLookup(tokenString);
        }
    }
    return Token(tokenType, tokenString);
}

TokenType Core::reservedLookup(string tokenString){
    for(int i=0; i<MAXRESERVED; ++i){
        if (tokenString == reservedWords[i].tokenString)
            return reservedWords[i].tokenType;
    }
    return ID;
}

TreeNode * Core::stmt_sequence(){
    TreeNode * t = statement();
    TreeNode * p = t;
    TreeNode * q;
    match(SEMI);
    while((token.type==IF) || (token.type==REPEAT) || 
    (token.type==ID) || (token.type==READ) || (token.type==WRITE) ||
    (token.type==WHILE) || (token.type==DO) || (token.type==FOR)){
        q = statement();
        if(q!=nullptr){
            if(t==nullptr) t = p = q;
            else{
                p->sibling = q;
                p = q;
            }
        }
        match(SEMI);
    }
    return t; 
}

TreeNode * Core::statement()
{ TreeNode * t = nullptr;
  switch (token.type) {
    case IF : t = if_stmt(); break;
    case REPEAT : t = repeat_stmt(); break;
    case ID : t = assign_stmt(); break;
    case READ : t = read_stmt(); break;
    case WRITE : t = write_stmt(); break;
    case WHILE : t = while_stmt(); break;
    case DO: t = dowhile_stmt(); break;
    case FOR: t = for_stmt(); break;
    default: 
            token = getToken();
            break;
  } 
  return t;
}

TreeNode * Core::if_stmt(void)
{ TreeNode * t = newStmtNode(IfK);
  match(IF);
  match(LPAREN);
  if (t!=nullptr) t->child[0] = exp();
  match(RPAREN);
  if (t!=nullptr) t->child[1] = stmt_sequence();
  if (token.type==ELSE) {
    match(ELSE);
    if (t!=nullptr) t->child[2] = stmt_sequence();
  }
  return t;
}

TreeNode * Core::repeat_stmt(void)
{ TreeNode * t = newStmtNode(RepeatK);
  match(REPEAT);
  if (t!=nullptr) t->child[0] = stmt_sequence();
  match(UNTIL);
  if (t!=nullptr) t->child[1] = exp();
  return t;
}

TreeNode * Core::assign_stmt(void)
{ TreeNode * t = newStmtNode(AssignK);
  if ((t!=nullptr) && (token.type==ID)){
    t->attr.name = new char[token.str.size()+1];
    strcpy(t->attr.name, token.str.c_str());
  }
  match(ID);
  match(ASSIGN);
  if (t!=nullptr) t->child[0] = exp();
  return t;
}

TreeNode * Core::read_stmt(void)
{ TreeNode * t = newStmtNode(ReadK);
  match(READ);
  if ((t!=nullptr) && (token.type==ID)){
    t->attr.name = new char[token.str.size()+1];
    strcpy(t->attr.name, token.str.c_str());
  }
  match(ID);
  return t;
}

TreeNode * Core::write_stmt(void)
{ TreeNode * t = newStmtNode(WriteK);
  match(WRITE);
  if (t!=nullptr) t->child[0] = exp();
  return t;
}

TreeNode * Core::while_stmt(void){
    TreeNode * t = newStmtNode(WhileK);
    match(WHILE);
    match(LPAREN);
    if(t!=nullptr) t->child[0] = exp();
    match(RPAREN);
    if(t!=nullptr) t->child[1] = stmt_sequence();
    match(ENDWHILE);
    return t;
}

TreeNode * Core::dowhile_stmt(void){
    TreeNode * t = newStmtNode(DowhileK);
    match(DO);
    if(t!=nullptr) t->child[0] = stmt_sequence();
    match(WHILE);
    match(LPAREN);
    if(t!=nullptr) t->child[1] = exp();
    match(RPAREN);
    return t;
}

TreeNode * Core::for_stmt(void){
    TreeNode * t = newStmtNode(ForK);
    match(FOR);
    if ((t!=nullptr) && (token.type==ID)){
        t->attr.name = new char[token.str.size()+1];
        strcpy(t->attr.name, token.str.c_str());
    }
    match(ID);
    match(ASSIGN);
    if (t!=nullptr) t->child[0] = simple_exp();
    if(token.type == TO){
        t->forType = "To";
        match(TO);
    }
    else{
        t->forType = "Downto";
        match(DOWNTO);
    }
    if (t!=nullptr) t->child[1] = simple_exp();
    match(DO);
    if (t!=nullptr) t->child[2] = stmt_sequence();
    match(ENDDO);
    return t;
}

TreeNode * Core::exp(void)
{ TreeNode * t = simple_exp();
  if ((token.type==LT)||(token.type==EQ)||(token.type==LTEQ)||(token.type==LTRT)) {
    TreeNode * p = newExpNode(OpK);
    if (p!=nullptr) {
      p->child[0] = t;
      p->attr.op = token.type;
      t = p;
    }
    match(token.type);
    if (t!=nullptr)
      t->child[1] = simple_exp();
  }
  return t;
}

TreeNode * Core::simple_exp(void)
{ TreeNode * t = term();
  while ((token.type==PLUS)||(token.type==MINUS)||(token.type==REM))
  { TreeNode * p = newExpNode(OpK);
    if (p!=nullptr) {
      p->child[0] = t;
      p->attr.op = token.type;
      t = p;
      match(token.type);
      t->child[1] = term();
    }
  }
  return t;
}

TreeNode * Core::term(void)
{ TreeNode * t = factor();
  while ((token.type==TIMES)||(token.type==OVER))
  { TreeNode * p = newExpNode(OpK);
    if (p!=nullptr) {
      p->child[0] = t;
      p->attr.op = token.type;
      t = p;
      match(token.type);
      p->child[1] = factor();
    }
  }
  return t;
}

TreeNode * Core::factor(void){
    TreeNode * t = powernum();
    while(token.type==POWER){
        TreeNode * p = newExpNode(OpK);
        if(p!=nullptr){
            p->child[0] = t;
            p->attr.op = token.type;
            t = p;
            match(token.type);
            p->child[1] = powernum();
        }
    }
    return t;
}

TreeNode * Core::powernum(void)
{ TreeNode * t = nullptr;
  switch (token.type) {
    case NUM :
      t = newExpNode(ConstK);
      if ((t!=nullptr) && (token.type==NUM))
        t->attr.val = stoi(token.str);
      match(NUM);
      break;
    case ID :
      t = newExpNode(IdK);
      if ((t!=nullptr) && (token.type==ID)){
        t->attr.name = new char[token.str.size()+1];
        strcpy(t->attr.name, token.str.c_str());
      }
      match(ID);
      break;
    case LPAREN :
      match(LPAREN);
      t = exp();
      match(RPAREN);
      break;
    default:
      token = getToken();
      break;
    }
  return t;
}

void Core::match(TokenType expected)
{ 
    if (token.type == expected) token = getToken();
}

TreeNode * Core::newStmtNode(StmtKind kind)
{ 
  TreeNode * t = new TreeNode();
  int i;
  if (t==nullptr)
    cout << "Out of memory error at line\n";
  else {
    for (i=0;i<3;i++) t->child[i] = nullptr;
    t->sibling = nullptr;
    t->nodekind = StmtK;
    t->kind.stmtkind = kind;
  }
  return t;
}

TreeNode * Core::newExpNode(ExpKind kind)
{
  TreeNode * t = new TreeNode();
  int i;
  if (t==nullptr)
    cout << "Out of memory error at line\n";
  else {
    for (i=0;i<3;i++) t->child[i] = nullptr;
    t->sibling = nullptr;
    t->nodekind = ExpK;
    t->kind.expkind = kind;
    t->type = Void;
  }
  return t;
}

string Core::tokenTypeStr(TokenType token){
    switch(token){
        case 0:
            return "ENDFILE";
        case 1:
            return "ERROR";
        case 2:
            return "IF";
        case 3:
            return "THEN";
        case 4:
            return "ELSE";
        case 5:
            return "END";
        case 6:
            return "REPEAT";
        case 7:
            return "UNTIL";
        case 8:
            return "READ";
        case 9:
            return "WRITE";
        case 10:
            return "WHILE";
        case 11:
            return "ENDWHILE";
        case 12:
            return "DO";
        case 13:
            return "ENDDO";
        case 14:
            return "FOR";
        case 15:
            return "TO";
        case 16:
            return "DOWNTO"; 
        case 17:
            return "ID";
        case 18:
            return "NUM";
        case 19:
            return "ASSIGN";
        case 20:
            return "EQ";
        case 21:
            return "LT";
        case 22:
            return "LTEQ";
        case 23:
            return "LTRT";
        case 24:
            return "PLUS";
        case 25:
            return "MINUS";
        case 26:
            return "TIMES";
        case 27:
            return "OVER";
        case 28:
            return "POWER";
        case 29:
            return "REM";
        case 30:
            return "LPAREN";
        case 31:
            return "RPAREN";
        case 32:
            return "SEMI";
    }
}

mywindow.h

#ifndef _MYWINDOW_H_
#define _MYWINDOW_H_

#include <QMainWindow>
#include <QPlainTextEdit>
#include "core.h"

class Mywindow : public QMainWindow{
    Q_OBJECT

private:
    QPlainTextEdit * edit1, * edit2;
    QAction * openAction;
    QAction * saveAction;
    QAction * runAction;
    Core * core;

public:
    explicit Mywindow(QWidget *parent = nullptr);

signals: //用来声明信号函数

public slots: //用来声明槽函数
    void open();
    void save();
    void run();
};

#endif

mywindow.cpp

#include "mywindow.h"
#include <QAction>
#include <QMenuBar>
#include <QToolBar>
#include <QHBoxLayout>
#include <iostream>
#include <QFileDialog>
#include <fstream>
using namespace std;

Mywindow::Mywindow(QWidget * parent) : QMainWindow(parent){
    setWindowTitle(tr("Main Window"));
    //文本编辑框
    edit1 = new QPlainTextEdit(this);
    edit2 = new QPlainTextEdit(this);
    QWidget * cw = new QWidget();
    QHBoxLayout * layout = new QHBoxLayout(cw);
    layout->addWidget(edit1);
    layout->addWidget(edit2);
    setCentralWidget(cw);
    //动作
    openAction = new QAction(QIcon(":/images/open"), tr("&Open..."), this);
    openAction->setStatusTip(tr("Open a source file"));
    runAction = new QAction(QIcon(":/images/run"), tr("&Run..."), this);
    runAction->setStatusTip(tr("Run the program"));
    saveAction = new QAction(QIcon(":/images/save"), tr("&Save..."), this);
    saveAction->setStatusTip(tr("Save the source file"));
    connect(openAction, SIGNAL(triggered()), this, SLOT(open()));
    connect(runAction, SIGNAL(triggered()), this, SLOT(run()));
    connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));
    //工具栏
    QToolBar * openToolBar = addToolBar(tr("&Open"));
    openToolBar->addAction(openAction);
    QToolBar * saveToolBar = addToolBar(tr("&Save"));
    saveToolBar->addAction(saveAction);
    QToolBar * runToolBar = addToolBar(tr("&Run"));
    runToolBar->addAction(runAction);
    
    //状态栏
    statusBar();
    //core
    core = new Core();
}

void Mywindow::open(){
    QString openfilename = QFileDialog::getOpenFileName(this,
    tr("Open File"), "", "", 0);
    if(!openfilename.isNull()){
        string filename = openfilename.toStdString();
        ifstream in(filename, ios::in);
        istreambuf_iterator<char> beg(in), end;
        string str(beg, end);
        in.close();
        edit1->setPlainText(QString::fromStdString(str));   
    }
}

void Mywindow::save(){
    QString savefilename = QFileDialog::getSaveFileName(this,
    tr("Open Config"), "", "Config Files (*.tny)");
    if(!savefilename.isNull()){
        string filename = savefilename.toStdString();
        fstream out(filename, ios::out);
        out << edit1->toPlainText().toStdString();
        out.close();
    }
}

void Mywindow::run(){
    QString s = edit1->toPlainText();
    QString result = QString::fromStdString(core->run(s.toStdString()));
    edit2->setPlainText(result);
}

main.cpp

#include "mywindow.h"
#include <QApplication>

int main(int argc, char *argv[]){
    //应用程序抽象类
    QApplication app(argc, argv);
    //窗口
    Mywindow w;
    w.show();
    //进入消息循环,一直接受消息,直到窗口被关闭
    return app.exec();
}

res.qrc

<RCC>
    <qresource prefix="/images">
        <file alias="open">open.jpg</file>
    </qresource>
    <qresource prefix="/images/">
        <file alias="run">run.jpg</file>
    </qresource>
    <qresource prefix="/images/">
        <file alias="save">save.jpg</file>
    </qresource>
</RCC>

posted @ 2020-11-23 09:18  爱弹琴的小黑  阅读(1562)  评论(0编辑  收藏  举报