词法分析器

【问题描述】

请根据给定的文法设计并实现词法分析程序,从源程序中识别出单词,记录其单词类别和单词值,输入输出及处理要求如下:

(1)数据结构和与语法分析程序的接口请自行定义;类别码需按下表格式统一定义;

(2)为了方便进行自动评测,输入的被编译源文件统一命名为 testfile.txt(注意不要写错文件名);输出的结果文件统一命名为 output.txt(注意不要写错文件名),结果文件中每行按如下方式组织:

            单词类别码 单词的字符/字符串形式(中间仅用一个空格间隔)

            单词的类别码请统一按如下形式定义:

词法分析类别码定义如下:

单词名称 类别码 单词名称 类别码 单词名称 类别码 单词名称 类别码
Ident IDENFR ! NOT * MULT = ASSIGN
IntConst INTCON && AND / DIV ; SEMICN
FormatString STRCON || OR % MOD , COMMA
main MAINTK while WHILETK < LSS ( LPARENT
const CONSTTK getint GETINTTK <= LEQ ) RPARENT
int INTTK printf PRINTFTK > GRE [ LBRACK
break BREAKTK return RETURNTK >= GEQ ] RBRACK
continue CONTINUETK + PLUS == EQL { LBRACE
if IFTK - MINU != NEQ } RBRACE
else ELSETK void VOIDTK

前三种Token和注释的正则表达式如下:

1、Ident标识符identifier:

identifier -> identifier-nondigit // 存在标识符即可

         | identifier identifier-nondigit 

         | identifier digit

其中 identifier-nondigit 为下划线或26个英文小写字母以及26个英文大写字母之⼀。digit为0-9十个数字之一。

2、IntConst数值常量integer-const:

integer-const -> decimal-const | 0

decimal-const -> nonzero-digit | decimal-const digit

其中 nonzero-digit 为1-9九个数字之⼀。

3、FormatString格式字符串:

-> %d

-> ⼗进制编码为32,33,40-126的ASCII字符,‘’(编码92)出现当且仅当为’\n’

    <char> -> <FormatChar> | <NormalChar>

-> ‘"’{}‘"’

4、注释:

SysY 语⾔中注释的规范与 C 语⾔⼀致,如下:

单⾏注释:以序列 ‘//’ 开始,直到换⾏符结束,不包括换⾏符。

多⾏注释:以序列 ‘/’ 开始,直到第⼀次出现 ‘/’ 时结束,包括结束处‘*/’。

【输入形式】testfile.txt中的符合文法要求的测试程序。
【输出形式】要求将词法分析结果输出至output.txt中。

【特别提醒】 (1)读取的字符串要原样保留着便于输出,特别是数字,这里输出的并不是真正的单词值,其实是读入的字符串,单词值需另行记录。

                (2)本次作业只考核对正确程序的处理,但需要为今后可能出现的错误情况预留接口。

                (3)在今后的错误处理作业中,需要输出错误的行号,在词法分析的时候注意记录该信息。

                (4)单词的类别和单词值以及其他关注的信息,在词法分析阶段获取后,后续的分析阶段会使用,请注意记录;当前要求的输出只是为了便于评测,完成编译器中无需出现这些信息,请设计为方便打开/关闭这些输出的方案。
#include <iostream>
#include <fstream>
#include <cctype>
#include <string>

using namespace std;

// 定义类别码
enum TokenType {
    IDENFR, // 标识符
    INTCON, // 数值常量 
    STRCON, // 格式字符串
    COMMENT_LINE, // 单行注释
    COMMENT_BLOCK, // 多行注释
    PARENTHESIS, 
    MAINTK,
    WHILETK,
    LSS,
    LPARENT,
    CONSTTK,
    GETINTTK,
    LEQ,
    RPARENT,
    INTTK,
    PRINTFTK,
    GRE,
    LBRACK,
    BREAKTK,
    RETURNTK,
    GEQ,
    RBRACK,
    CONTINUETK,
    IFTK,
    PLUS,
    MINU,
    NOT,
    AND,
    MULT,
    DIV,
    MOD,
    OR,
    SEMICN,
    COMMA,
    LBRACE,
    ELSETK,
    VOIDTK,
    RBRACE,
    EQL,
    NEQ,
    ASSIGN,
    OPERATOR, 
    ERROR//不是规定的类型则输出错误 
};

// Token 结构体,用于存储单词的类型和内容
struct Token {
    TokenType type;
    string value;
};


bool Is_Keyword(const string& word) {
    return word == "const" || word == "int" || word == "main" || word == "while" || word == "break" ||
           word == "continue" || word == "if" || word == "else" || word == "void" || word == "getint" ||
           word == "printf" || word == "return";
}// 判断读取的单词是否是关键字


bool Is_Parenthesis(char ch) {
    return ch == '(' || ch == ')';
}// 判断单词是不是括号


bool Is_StringStart(char ch) {
    return ch == '"';
}// 判断是是不是字符串类型 


bool Is_SingleLineCommentStart(char ch1, char ch2) {
    return ch1 == '/' && ch2 == '/';
}// 判断是否是单行注释


bool Is_MultiLineCommentStart(char ch1, char ch2) {
    return ch1 == '/' && ch2 == '*';
}// 判断是否是多行注释


bool Is_MultiLineCommentEnd(char ch1, char ch2) {
    return ch1 == '*' && ch2 == '/';
}// 判断是否是多行注释的结束 


/*bool isDoubleOperatorFirst(char ch1) {
    return ch1 == '<' || ch1 == '>' || ch1 == '=';
}// 判断是否是双字符运算符的第一个字符*/

// 词法分析函数,将输入的字符串逐个分解为 Token
Token getNextToken(ifstream &input) {
    char ch;
    while (input.get(ch)) {
        // 跳过空白字符
        if (isspace(ch)) {
            continue;
        }

        // 识别字符串常量
        if (Is_StringStart(ch)) {
            string str;
            input.get(ch);
            while (ch!= '"') {
                str += ch;
                input.get(ch);
            }
            return {STRCON, "\"" + str + "\""};
        }

        // 识别单行注释但不存储注释内容
        if (Is_SingleLineCommentStart(ch, input.peek())) {
            string comment;
            input.get(ch);
            input.get(ch);
            while (input.get(ch) && ch!= '\n') {
            }
            return getNextToken(input);   
        }

        // 识别多行注释但不存储注释内容
        if (Is_MultiLineCommentStart(ch, input.peek())) {
            input.get(ch);
            input.get(ch);
            string comment;
            while (!Is_MultiLineCommentEnd(ch, input.peek())) {
                if (!input.get(ch)) {
                    return {ERROR, ""};
                }
            }
            input.get(ch);
            input.get(ch);
            return getNextToken(input);
        }

        // 识别关键字或标识符
        if (isalpha(ch)) {
            string identifier(1, ch);
            while (input.get(ch) && (isalnum(ch) || ch == '_')) {
                identifier += ch;
            }
            input.putback(ch); // 回退多读的字符
            if (Is_Keyword(identifier)) {
                if (identifier == "const") return {CONSTTK, identifier};
                if (identifier == "int") return {INTTK, identifier};
                if (identifier == "main") return {MAINTK, identifier};
                if (identifier == "while") return {WHILETK, identifier};
                if (identifier == "break") return {BREAKTK, identifier};
                if (identifier == "continue") return {CONTINUETK, identifier};
                if (identifier == "if") return {IFTK, identifier};
                if (identifier == "else") return {ELSETK, identifier};
                if (identifier == "void") return {VOIDTK, identifier};
                if (identifier == "getint") return {GETINTTK, identifier};
                if (identifier == "printf") return {PRINTFTK, identifier};
                if (identifier == "return") return {RETURNTK, identifier};
            } else {
                return {IDENFR, identifier};//不是关键字则为标识符 
            }
        }

        // 识别数值常量 
        if (isdigit(ch)) {
            string number(1, ch);
            while (input.get(ch) && isdigit(ch)) {
                number += ch;
            }
            input.putback(ch);
            return {INTCON, number};
        }

        // 识别特定字符和多字符运算符
        if (ch == '+') return {PLUS, string(1, ch)};
        if (ch == '-') return {MINU, string(1, ch)};
        if (ch == '*') return {MULT, string(1, ch)};
        if (ch == '/') return {DIV, string(1, ch)};
        if (ch == '%') return {MOD, string(1, ch)};
        //判断是单字符运算符还是双字符运算符 
        if (ch == '<') {
            if (input.peek() == '=') {
                input.get(ch);
                return {LEQ, "<="};
            } else {
                return {LSS, string(1, ch)};
            }
        }
        if (ch == '>') {
            if (input.peek() == '=') {
                input.get(ch);
                return {GEQ, ">="};
            } else {
                return {GRE, string(1, ch)};
            }
        }
        if (ch == '=') {
            if (input.peek() == '=') {
                input.get(ch);
                return {EQL, "=="};
            } else {
                return {ASSIGN, string(1, ch)};
            }
        }
        if ((ch == '&') && (input.peek() == '&')) {
            input.get(ch);
            return {AND, "&&"};
        }
        if ((ch == '|') && (input.peek() == '|')) {
            input.get(ch);
            return {OR, "||"};
        }
        if (ch == '!') {
            if (input.peek() == '=') {
                input.get(ch);
                return {NEQ, "!="};
            } else {
                return {NOT, string(1, ch)};
            }
        }
        
        if (Is_Parenthesis(ch)) {
            if (ch == '(') return {LPARENT, string(1, ch)};
            if (ch == ')') return {RPARENT, string(1, ch)};
        }
        if (ch == '{') return {LBRACE, string(1, ch)};
        if (ch == '}') return {RBRACE, string(1, ch)};
        if (ch == '[') return {LBRACK, string(1, ch)};
        if (ch == ']') return {RBRACK, string(1, ch)};
        if (ch == ',') return {COMMA, string(1, ch)};
        if (ch == ';') return {SEMICN, string(1, ch)};

        return {ERROR, string(1, ch)};
    }
    return {ERROR, ""}; // 文件结束
}

int main() {
    ifstream inputFile("testfile.txt");
    ofstream outputFile("output.txt");
    Token token;
    while (inputFile) {
        token = getNextToken(inputFile);
        if (token.value.empty()) {
		break; 

		}
		
        switch (token.type) {
            case IDENFR:
                outputFile << "IDENFR " << token.value << endl;// 将对应的结果输出到 output.txt 文件
                break;
            case INTCON:
                outputFile << "INTCON " << token.value << endl;
                break;
            case OPERATOR:
                outputFile << "OPERATOR " << token.value << endl;
                break;
            case PARENTHESIS:
                outputFile << "PARENTHESIS " << token.value << endl;
                break;
            case STRCON:
                outputFile << "STRCON " << token.value << endl;
                break;
            case COMMENT_LINE:
                break;//注释内容不输出 
            case COMMENT_BLOCK:
                break;//注释内容不输出 
            case MAINTK:
                outputFile << "MAINTK " << token.value << endl;
                break;
            case WHILETK:
                outputFile << "WHILETK " << token.value << endl;
                break;
            case LSS:
                outputFile << "LSS " << token.value << endl;
                break;
            case LPARENT:
                outputFile << "LPARENT " << token.value << endl;
                break;
            case CONSTTK:
                outputFile << "CONSTTK " << token.value << endl;
                break;
            case GETINTTK:
                outputFile << "GETINTTK " << token.value << endl;
                break;
            case LEQ:
                outputFile << "LEQ " << token.value << endl;
                break;
            case RPARENT:
                outputFile << "RPARENT " << token.value << endl;
                break;
            case INTTK:
                outputFile << "INTTK " << token.value << endl;
                break;
            case PRINTFTK:
                outputFile << "PRINTFTK " << token.value << endl;
                break;
            case GRE:
                outputFile << "GRE " << token.value << endl;
                break;
            case LBRACK:
                outputFile << "LBRACK " << token.value << endl;
                break;
            case BREAKTK:
                outputFile << "BREAKTK " << token.value << endl;
                break;
            case RETURNTK:
                outputFile << "RETURNTK " << token.value << endl;
                break;
            case GEQ:
                outputFile << "GEQ " << token.value << endl;
                break;
            case RBRACK:
                outputFile << "RBRACK " << token.value << endl;
                break;
            case CONTINUETK:
                outputFile << "CONTINUETK " << token.value << endl;
                break;
            case IFTK:
                outputFile << "IFTK " << token.value << endl;
                break;
            case NOT:
                outputFile << "NOT " << token.value << endl;
                break;
            case AND:
                outputFile << "AND " << token.value << endl;
                break;
            case PLUS:
                outputFile << "PLUS " << token.value << endl;
                break; 
            case MINU:
                outputFile << "MINU " << token.value << endl;
                break; 
            case MULT:
                outputFile << "MULT " << token.value << endl;
                break;
            case DIV:
                outputFile << "DIV " << token.value << endl;
                break;
            case MOD:
                outputFile << "MOD " << token.value << endl;
                break;
            case OR:
                outputFile << "OR " << token.value << endl;
                break;
            case SEMICN:
                outputFile << "SEMICN " << token.value << endl;
                break;
            case COMMA:
                outputFile << "COMMA " << token.value << endl;
                break;
            case LBRACE:
                outputFile << "LBRACE " << token.value << endl;
                break;
            case ELSETK:
                outputFile << "ELSETK " << token.value << endl;
                break;
            case VOIDTK:
                outputFile << "VOIDTK " << token.value << endl;
                break;
            case RBRACE:
                outputFile << "RBRACE " << token.value << endl;
                break;
            case EQL:
                outputFile << "EQL " << token.value << endl;
                break;
            case NEQ:
                outputFile << "NEQ " << token.value << endl;
                break;
            case ASSIGN:
                outputFile << "ASSIGN " << token.value << endl;
                break;
            default:
                outputFile << "ERROR " << token.value << endl;
                break;
        }
    }

    inputFile.close();
    outputFile.close();
    cout << "对testfile文件的词法分析已完成,结果已输出到output文件。" << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值