C++实现简易PL/0词法分析器

该文介绍了一个用C++编写的词法分析器,它能读取文件内容,将字符序列分解为标识符、数字、关键字和符号等单词类型,为编译器的后续步骤做准备。程序处理注释、空格,并将结果写入输出文件,适合作为编译器开发的基础组件。
摘要由CSDN通过智能技术生成

 实验环境:

操作系统:window 10

编写环境:Visual Studio 2022

编写语言:c++

分析语言:PL/0

解析:

当我们编写程序时,通常需要从文件或用户输入中获取数据,这些数据需要经过解析才能被程序所理解。而词法分析就是将输入流中的字符序列分解成一个一个的单词,每个单词都有其对应的类型和值。

本文介绍了一个简单的词法分析器程序,它可以读取输入的文件并将其分解为单个单词。程序采用 C++ 语言编写,并使用了文件流、字符串和枚举等多种数据类型和语法。

在程序中,定义了四种单词类型:标识符(ident)、数字(number)、关键字(keyword)和符号(symbol)。其中,标识符和关键字都是由字母和数字组成的字符串,而数字则是由数字组成的字符串。符号则是单个字符,例如加号、减号、乘号、除号等。

程序通过逐个读取输入流中的字符,并根据其类型进行判断,从而将其归类为上述四种类型中的一种。对于标识符和关键字,程序会读取连续的字母和数字,直到遇到一个非字母或数字的字符。程序还定义了一些常见的符号,并根据读取到的字符进行匹配,从而确定其类型和值。

最后,程序将每个单词的类型和值写入输出文件中,以供后续程序使用。这个简单的词法分析器程序可以帮助我们更好地理解程序的输入解析过程,也可以作为其他编译器程序的基础组件进行扩展和优化。

在本程序中,还定义了一个关键字列表,用于判断是否为关键字。程序还实现了一个函数,用于判断一个字符是否是有意义的符号,并通过 switch 语句将其转换为对应的符号类型。同时,程序还处理了注释和空格等无意义字符。

此外,程序还提供了一个函数,用于在控制台输出输入文件的内容,以便我们更好地了解程序的输入数据。最后,程序将每个单词的类型和值写入输出文件中,以供后续程序使用。

在使用这个程序时,我们只需要将需要解析的文件作为输入,运行程序即可得到输出文件。输出文件中的每一行都表示一个单词,其中括号内的第一个值表示单词的类型,第二个值表示单词的值。

总之,词法分析是编译器中的一个重要组成部分,它负责将输入流中的字符序列分解成一个一个的单词,为后续的语法分析和代码生成等工作提供了基础。本文介绍了一个简单的词法分析器程序,它可以帮助我们更好地理解程序的输入解析过程,也可以作为其他编译器程序的基础组件进行扩展和优化。

注意:

input.txt和output.txt 文件要事先创建好,并放在和cpp文件同一目录下,否则运行会报错;

源程序:

#include <iostream>
#include <fstream>
#include <vector>
#include <string>

using namespace std;

// 定义单词类型枚举
enum TokenType {
    ident, // 标识符
    number, // 数字
    keyword ,// 关键字
    symbol  //符号
  
};
const char* type_names[] = { //定义数组
    "ident", // 标识符
    "number", // 数字
    "keyword" ,// 关键字
    "symbol"  //符号

};

// 定义符号类型枚举
enum Symbol {  

    Plus, // "+"
    Minus, // "-"
    times, // "*"
    slash, // "/"
    eql, // "="
    lss, // "<"
    gtr, // ">"
    lparen, // "("
    rparen, // ")"
    comma, // ","
    semicolon, // ";"
    colon, // ":"
    period, // "."
    becomes, // ":="
    leq, // "<="
    geq, // ">="
    neq ,// "#"
    error//无效字符
    
};
const char* symbol_names[] = {

    "Plus", // "+"
    "Minus", // "-"
    "times", // "*"
    "slash", // "/"
    "eql", // "="
    "lss", // "<"
    "gtr", // ">"
    "lparen", // "("
    "rparen", // ")"
    "comma", // ","
    "semicolon", // ";"
    "colon", // ":"
    "period", // "."
    "becomes", // ":="
    "leq", // "<="
    "geq", // ">="
    "neq" ,// "#"
    "error"//无效字符

};

// 定义单词结构体
struct Token {
    TokenType type; // 单词类型
    Symbol symSignal;//符号类型
    string value; // 单词值
};

// 定义关键字
vector<string> keywords = { "const", "var", "procedure", "begin", "end", "if", "then", "while", "do", "call", "odd", "write", "read" };

// 判断一个字符串是否是关键字
bool isKeyword(string str) {
    for (string keyword : keywords) {
        if (str == keyword) {
            return true;
        }
    }
    return false;
}

// 判断一个字符是否是有意义的符号
bool isSymbol(char c) {
    string symbols = "+-*/=<>(),.;:";
    return symbols.find(c) != string::npos;
}

// 获取下一个单词
Token getNextToken(ifstream& ifs) {
    Token token;
    char c;

    // 跳过空格和注释
    while (ifs.get(c)) {
        if (isspace(c)) {
            continue;
        }
        else if (c == '/') {
            if (ifs.get(c) && c == '*') {
                // 跳过注释
                while (ifs.get(c)) {
                    if (c == '*' && ifs.get(c) && c == '/') {
                        break;
                    }
                }
            }
            else {
                ifs.putback(c);
                break;
            }
        }
        else {
            break;
        }
    }

    // 判断标识符、关键字等类型
    if (isalpha(c)) {
        token.type = ident;
        token.value += c;
        while (ifs.get(c) && isalnum(c)) {
            token.value += c;
        }
        ifs.putback(c);
        if (isKeyword(token.value)) {
            token.type = keyword;
        }
    }

    // 判断数字类型
    else if (isdigit(c)) {
        token.type = number;
        token.value += c;
        while (ifs.get(c) && isdigit(c)) {
            token.value += c;
        }
        ifs.putback(c);
    }

    // 判断符号类型
    else if (isSymbol(c)) {
        token.type = symbol;
        token.value += c;
        switch (c) {
        case '+':  token.symSignal = Plus; token.value = '+'; break;
        case '-': token.symSignal = Minus; token.value = '-'; break;
        case '*': token.symSignal = times; token.value = '*'; break;
        case '/': token.symSignal = slash; token.value = '/'; break;
        case '=': token.symSignal = eql; token.value = '='; break;
        case '#': token.symSignal = neq; token.value = '#'; break;
        case '<': {
            if (ifs.get(c) && c == '=') {
                token.symSignal = leq;
                token.value = "<=";
            }
            
            else {
                ifs.putback(c);
                token.symSignal = lss;
                token.value = "<";
            }
            break;
        }
        case '>': {
            if (ifs.get(c) && c == '=') {
                token.symSignal = geq;
                token.value = ">=";
            }
            else {
                ifs.putback(c);
                token.symSignal = gtr;
                token.value = ">";
            }
            break;
        }
        case '(': token.symSignal = lparen; token.value = '('; break;
        case ')': token.symSignal = rparen; token.value = ')'; break;
        case ',': token.symSignal = comma; token.value = ','; break;
        case ';': token.symSignal = semicolon; token.value = ';'; break;
        case ':': {
            if (ifs.get(c) && c == '=') {
                token.symSignal = becomes;
                token.value = ":=";
            }
            else {
                ifs.putback(c);
                token.symSignal = colon;
                token.value = ":";
            }
            break;
        }
        case '.': token.symSignal = period; token.value = "."; break;
        }
    }

    // 无效字符处理
    else {
        token.symSignal = error;
        token.value += c;
    }

    return token;
}
void printFileContents(const std::string& filename) {  //在控制端打印输入文件数据
    std::ifstream file(filename);
    std::string line;
    while (std::getline(file, line)) {
        std::cout << "source program: " << line << std::endl;
    }
}

int main() {
    // 打开输入,并输出在控制端

//测试文本1
    ifstream ifs("input.txt");
    std::string filename = "input.txt";

//测试文本2

    //ifstream ifs("input2.txt");
    //std::string filename = "input2.txt";
    printFileContents(filename);
    //打开输出
    ofstream ofs("output.txt");
    // 逐个单词写入到输出文件中 
    Token token;
    while ((token = getNextToken(ifs)).type != symbol || token.value != ".")
    {
        if (token.type == keyword)
        {
            ofs << "(" << token.value << ")" << endl;
        }
        else if (token.type == symbol) {
            ofs << "(" << symbol_names[token.symSignal] << ", " << token.value << ")" << endl;
        }
        else {
            ofs << "(" << type_names[token.type] << ", " << token.value << ")" << endl;
        }
    }
    ofs << "(" << symbol_names[token.symSignal] << ", " << token.value << ")" << endl;
    // 关闭输入和输出文件
    ifs.close();
    ofs.close();
    return 0;
}

输入测试:

测试1:

input.txt

const a=10;
var b,c;
procedure p;
  begin
    c:=b+a
  end;
begin
  read(b);
  while b#0 do
    begin
      call p; write(2*c); read(b)
    end
end.

测试2:

input2.txt

const
  MaxValue = 100;
var
  x, y, result: integer;
  
procedure addNumbers;
begin
  result := x + y;
end;
end.

控制端输出:

测试1

测试2

 文件输出:

output.txt

测试1

测试2:

  • 9
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
pl0的语法分析器,编译原理实验; 用C写的。 /语法分析过程中 /利用词法分析的结果进行分析 /严格按照PL0程序定义来编写 / /<程序> ::= <程序首部> <分程序>. /<程序首部> ::= PROGRAM <标识符>; /<分程序> ::= [<常量说明部分>][<变量说明部分>][<过程说明部分>]<语句部分> /<常量说明部分> ::= CONST <常量定义>{,<常量定义>}; /<常量定义> ::= <标识符>=<无符号整数> /<变量说明部分> ::= VAR <标识符>{,<标识符>}; /<过程说明部分> ::= <过程首部>;<分程序>;【原课件中没有最后的分号,经分析应该有分号】 /<过程首部> ::= PROCEDURE <标识符> /<语句部分> ::= <语句>|<复合语句> /<复合语句> ::= BEGIN <语句>{;<语句>} END【符合语句应该注意的是,END前距离END最近的那条语句一定没有分号,其他语句必须有分号】 /<语句>::= <赋值语句>|<条件语句>|<当型 循环语句>|<过程调用语句>|<读语句>|<写语句>|<复合语句> /<赋值语句> ::= <标识符>:=<表达式> /<读语句> ::= READ(<标识符>{,<标识符>}) /<写语句> ::= WRITE(<表达式>{,<表达式>}) /<过程调用语句> ::= CALL <标识符>【原课件中有分号,实际不应该有】 /<条件语句> ::= IF <条件> THEN <语句> /<当型循环语句> ::= WHILE <条件> DO <语句> /<因子> ::= <标识符>|<常量>|(<表达式>) /<项> ::= <因子>{<乘法运算符><因子>} /<乘法运算符> ::= *|/ /<表达式> ::= [+|-]<项>{<加法运算符><项>} /<加法运算符> ::= +|- /<条件> ::= <表达式><关系运算符><表达式>|ODD <表达式> /<关系运算符> ::= #|=|>|>=|<|<=
好的,PL/0的词法分析包括以下几个步骤: 1. 定义词法单元:PL/0的词法单元包括关键字、标识符、常量和运算符。 2. 定义正则表达式:通过正则表达式定义每个词法单元的形式。 3. 编写词法分析器:识别输入的代码,将其按照正则表达式分割成词法单元。 下面是一个简单的PL/0词法分析器的示例代码: ```c++ #include <iostream> #include <string> #include <vector> #include <cctype> using namespace std; // 定义词法单元类型 enum class TokenType { KEYWORD, IDENTIFIER, NUMBER, OPERATOR, DELIMITER }; // 定义词法单元结构体 struct Token { TokenType type; string value; }; // 定义关键字列表 vector<string> keywords = {"const", "var", "procedure", "begin", "end", "if", "then", "else", "while", "do", "call", "read", "write"}; // 判断是否为关键字 bool isKeyword(string str) { for (auto keyword : keywords) { if (str == keyword) { return true; } } return false; } // 词法分析函数 vector<Token> lexicalAnalysis(string input) { vector<Token> tokens; int pos = 0; while (pos < input.size()) { char ch = input[pos]; if (isspace(ch)) { pos++; continue; } if (isalpha(ch)) { // 标识符或关键字 string str; while (isalnum(ch)) { str += ch; pos++; ch = input[pos]; } if (isKeyword(str)) { tokens.push_back({TokenType::KEYWORD, str}); } else { tokens.push_back({TokenType::IDENTIFIER, str}); } } else if (isdigit(ch)) { // 数字 string str; while (isdigit(ch)) { str += ch; pos++; ch = input[pos]; } tokens.push_back({TokenType::NUMBER, str}); } else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '=' || ch == '#' || ch == '<' || ch == '>' || ch == '(' || ch == ')' || ch == ',' || ch == ';' || ch == '.') { // 运算符或分隔符 tokens.push_back({TokenType::OPERATOR, string(1, ch)}); pos++; } else { cout << "Error: Unknown token at position " << pos << endl; return {}; } } return tokens; } int main() { string input = "const a = 1; var b; begin a := a + b; end."; vector<Token> tokens = lexicalAnalysis(input); for (auto token : tokens) { cout << "Type: "; switch (token.type) { case TokenType::KEYWORD: cout << "Keyword "; break; case TokenType::IDENTIFIER: cout << "Identifier "; break; case TokenType::NUMBER: cout << "Number "; break; case TokenType::OPERATOR: cout << "Operator "; break; case TokenType::DELIMITER: cout << "Delimiter "; break; } cout << "Value: " << token.value << endl; } return 0; } ``` 该代码通过判断字符类型,将输入的PL/0代码分割成一个个词法单元,并将其存储在一个vector中。在存储词法单元时,还需要将其类型和值存储在Token结构体中。在识别到未知的词法单元时,将输出错误信息并返回空vector。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值