词法分析程序设计原理与实现

一、程序功能描述

[实验项目]

以下为正则文法所描述的 C 语言子集单词符号的示例,请补充单词符号:

++,–, >>, <<, += , -= ,*=, /= ,&&(逻辑与),||(逻辑或),!(逻辑非)等等,给出补充后描述 C 语言子集单词符号的正则文法,设计并实现其词法分析程序。

以下文法为左线性正则文法:

<标识符>→字母︱ <标识符>字母︱ <标识符>d

<无符号整数>→数字︱ <无符号整数>数字

<单字符分界符>→+ ︱- ︱* ︱;︱, ︱(︱) ︱{︱}

<双字符分界符>→<大于>=︱<小于>=︱<小于>>︱<感叹号>=︱<等于>=︱<斜竖>*

<小于>→< <等于>→= <大于>→> <斜竖> →/

<感叹号>→!

该语言的保留字 :void、int、float、double、if、else、for、do、while 等等(也可补充)。

[设计说明]

(1)可将该语言设计成大小写不敏感,也可设计成大小写敏感,用户定义的标识符最长不超过 32 个字符;

(2)字母为 a-z A-Z,数字为 0-9;

(3)可以对上述文法进行扩充和改造;

(4)“/*……*/”和“//”(一行内)为程序的注释部分。

[设计要求]

(1)给出各单词符号的类别编码;

(2)词法分析程序应能发现输入串中的错误;

(3)词法分析作为单独一遍编写,词法分析结果为二元式序列组成的中间文件;

(4)设计至少 4 个测试用例(尽可能完备),并给出测试结果。

二、主要数据结构

变量及类型 用途
枚举类型 TokenType

 

定义了词法分析器中可能出现的各种记号类型。

每个枚举值代表一种特定的记号类型,例如 IDENTIFIER 表示标识符,INTEGER 表示整数,PLUS 表示加号等。

结构体 Token

 

表示一个记号(token),包含两个成员变量:

type:记号的类型,使用 TokenType 枚举类型表示。

value:记号的具体值,使用 std::string 表示。

提供了一个构造函数,用于初始化 Token 对象。

关键字映射表 keywords

 

使用 std::unordered_map 存储关键字及其对应的记号类型。

在词法分析器中,当遇到一个标识符时,可以通过查找这个映射表来确定它是否是一个关键字。

类 Lexer

 

实现了一个词法分析器类,用于将输入字符串转换为记号序列。

包含以下成员变量:

input:输入的源代码字符串。

position:当前解析的位置。

current_char:当前解析的字符。

包含以下成员方法:

advance:向前移动一个字符。

skip_whitespace:跳过空白字符。

identifier_or_keyword:处理标识符或关键字。

number:处理数字。

single_char_token:处理单字符记号。

double_char_token:处理双字符记号。

peek:查看下一个字符而不改变当前位置。

get_next_token:获取下一个记号。

三、程序结构描述

设计方法

总体设计思路:

词法分析器(Lexer):将输入的源代码字符串分解成一系列记号(tokens)。

记号(Token):表示源代码中的每个基本单元,包括类型和值。

关键字映射表:用于识别关键字并将其转换为相应的记号类型。

主函数:提供用户界面,允许用户选择测试用例或手动输入代码,并调用词法分析器进行处理。

函数定义及函数间的调用关系

函数名称 函数功能描述
main() 程序入口点,负责初始化程序环境,显示菜单选项,接收用户输入,并根据用户选择调用相应的处理函数。
display_menu() 显示程序的菜单选项,供用户选择操作。
get_user_choice() 获取用户的输入选择,用于决定接下来要执行的操作。
Lexer(input_string) Lexer 类的构造函数,接收输入字符串并初始化词法分析器的状态。
Get_next_token() 从输入字符串中获取下一个记号(token),并返回该记号。这是词法分析的核心函数。
skip_whitespace() 跳过输入字符串中的所有空白字符,包括空格、制表符、换行符等。
identifier_or_keyword() 识别输入字符串中的标识符或关键字,并返回相应的记号。
number() 识别输入字符串中的数字,并返回相应的记号。
single_char_token() 处理输入字符串中的单个字符记号,如括号、运算符等。
double_char_token() 处理输入字符串中的双字符记号,例如“==”、“!=”等。
peek() 查看输入字符串中的下一个字符,但不改变当前的位置指针。
advance() 在输入字符串中向前移动一个字符的位置指针。
is_alpha(char c) 检查给定字符是否为字母。
is_digit(char c) 检查给定字符是否为数字。
is_alphanumeric(char c) 检查给定字符是否为字母或数字。

四、程序测试

测试用例1:

#include <stdio.h>

int main() {

printf("Hello, World!");

return 0;

}

程序执行结果:

(28, #include)

(2, <stdio.h>)

(1, int)

(2, main)

(8, ()

(9, ))

(10, {

(1, printf)

(8, (

(2, "Hello, World!")

(9, ))

(11, ;

(12, return)

(2, 0)

(11, ;

(13, })

测试用例2:

int main() {

char c = 'A';

char str[] = "Hello, World!";

return 0;

}

程序执行结果:

(1, int)

(2, main)

(8, ()

(9, ))

(10, {

(1, char)

(1, c)

(6, =

(2, 'A')

(11, ;

(1, char)

(1, str)

(26, [])

(6, =

(2, "Hello, World!")

(11, ;

(12, return)

(2, 0)

(11, ;

(13, })

测试用例3:

int main() {

int a = 10;

a = a + 1;

return 0

}

程序执行结果:

(0, int)

(0, main)

(8, ()

(9, ))

(22, {)

(0, int)

(0, a)

(6, =)

(1, 10)

(7, ;)

(0, a)

(6, =)

(0, a)

(2, +)

(1, 1)

(7, ;)

(0, return)

(1, 0)

(23, })

五、源代码附录

#include 
#include 
#include 
#include 
#include 
#include 
#include 

enum class TokenType {
    IDENTIFIER, INTEGER, PLUS, MINUS, TIMES, DIVIDE,
    ASSIGN, SEMICOLON, LPAREN, RPAREN, EOF_TOKEN,
    INCREMENT, DECREMENT, LSHIFT, RSHIFT, PLUS_ASSIGN,
    MINUS_ASSIGN, TIMES_ASSIGN, DIVIDE_ASSIGN, AND, OR, NOT,
    LBRACE, RBRACE,
    // 可以继续添加其他类型
};

struct Token {
    TokenType type;
    std::string value;

    Token(TokenType t, std::string v) : type(t), value(v) {}
};

std::ostream& operator<<(std::ostream& os, const Token& token) {
    return os << "(" << static_cast(token.type) << ", " << token.value << ")";
}

const std::unordered_map<std::string, TokenType> keywords = {
    {"void", TokenType::IDENTIFIER}, // 这里假设关键字也作为标识符处理,可以根据需求调整
    {"int", TokenType::IDENTIFIER},
    {"float", TokenType::IDENTIFIER},
    {"double", TokenType::IDENTIFIER},
    {"if", TokenType::IDENTIFIER},
    {"else", TokenType::IDENTIFIER},
    {"for", TokenType::IDENTIFIER},
    {"do", TokenType::IDENTIFIER},
    {"while", TokenType::IDENTIFIER}
};

class Lexer {
public:
    Lexer(const std::string& input) : input(input), position(0), current_char(input[position]) {}

    Token get_next_token();

private:
    void advance();
    void skip_whitespace();
    Token identifier_or_keyword();
    Token number();
    Token single_char_token(char ch, TokenType type);
    Token double_char_token(std::string chars, TokenType type);
    char peek();

    std::string input;
    size_t position;
    char current_char;
};

void Lexer::advance() {
    position++;
    if (position < input.length()) { current_char = input[position]; } else { current_char = '\0'; // 表示已到达输入末尾 } } void Lexer::skip_whitespace() { while (current_char != '\0' && isspace(current_char)) { advance(); } } Token Lexer::identifier_or_keyword() { std::string value; while (isalnum(current_char) || current_char == '_') { value += current_char; advance(); } auto it = keywords.find(value); if (it != keywords.end()) { return Token(it->second, value);
    }
    else {
        return Token(TokenType::IDENTIFIER, value);
    }
}

Token Lexer::number() {
    std::string value;
    while (isdigit(current_char)) {
        value += current_char;
        advance();
    }

    if (current_char == '.') {
        value += current_char;
        advance();
        while (isdigit(current_char)) {
            value += current_char;
            advance();
        }
        return Token(TokenType::INTEGER, value); // 假设这里处理的是浮点数
    }
    else {
        return Token(TokenType::INTEGER, value);
    }
}

Token Lexer::single_char_token(char ch, TokenType type) {
    advance();
    return Token(type, std::string(1, ch));
}

Token Lexer::double_char_token(std::string chars, TokenType type) {
    advance();
    advance();
    return Token(type, chars);
}

Token Lexer::get_next_token() {
    while (current_char != '\0') {
        if (isspace(current_char)) {
            skip_whitespace();
            continue;
        }

        if (isalpha(current_char) || current_char == '_') {
            return identifier_or_keyword();
        }

        if (isdigit(current_char)) {
            return number();
        }

        if (current_char == '+') {
            if (peek() == '+') {
                return double_char_token("++", TokenType::INCREMENT);
            }
            else if (peek() == '=') {
                return double_char_token("+=", TokenType::PLUS_ASSIGN);
            }
            else {
                return single_char_token('+', TokenType::PLUS);
            }
        }

        if (current_char == '-') {
            if (peek() == '-') {
                return double_char_token("--", TokenType::DECREMENT);
            }
            else if (peek() == '=') {
                return double_char_token("-=", TokenType::MINUS_ASSIGN);
            }
            else {
                return single_char_token('-', TokenType::MINUS);
            }
        }

        if (current_char == '*') {
            if (peek() == '=') {
                return double_char_token("*=", TokenType::TIMES_ASSIGN);
            }
            else {
                return single_char_token('*', TokenType::TIMES);
            }
        }

        if (current_char == '/') {
            if (peek() == '=') {
                return double_char_token("/=", TokenType::DIVIDE_ASSIGN);
            }
            else {
                return single_char_token('/', TokenType::DIVIDE);
            }
        }

        if (current_char == '&') {
            if (peek() == '&') {
                return double_char_token("&&", TokenType::AND);
            }
        }

        if (current_char == '|') {
            if (peek() == '|') {
                return double_char_token("||", TokenType::OR);
            }
        }

        if (current_char == '!') {
            if (peek() == '=') {
                return double_char_token("!=", TokenType::NOT);
            }
            else {
                return single_char_token('!', TokenType::NOT);
            }
        }

        if (current_char == '=') {
            if (peek() == '=') {
                return double_char_token("==", TokenType::ASSIGN);
            }
            else {
                return single_char_token('=', TokenType::ASSIGN);
            }
        }

        if (current_char == ';') {
            return single_char_token(';', TokenType::SEMICOLON);
        }

        if (current_char == '(') {
            return single_char_token('(', TokenType::LPAREN);
        }

        if (current_char == ')') {
            return single_char_token(')', TokenType::RPAREN);
        }

        if (current_char == '{') {
            return single_char_token('{', TokenType::LBRACE);
        }

        if (current_char == '}') {
            return single_char_token('}', TokenType::RBRACE);
        }

        std::cout << "未识别的字符: '" << current_char << "'" << std::endl;
        throw std::runtime_error("非法字符");
    }

    return Token(TokenType::EOF_TOKEN, "");
}

char Lexer::peek() {
    if (position + 1 < input.length()) {
        return input[position + 1];
    }
    else {
        return '\0';
    }
}

int main() {
    // 从 example.txt 文件中读取内容
    std::ifstream input_file("example.txt");
    if (!input_file.is_open()) {
        std::cerr << "无法打开 example.txt 文件" << std::endl;
        return 1;
    }

    std::string input((std::istreambuf_iterator(input_file)), std::istreambuf_iterator());
    input_file.close();

    // 创建 Lexer 对象
    Lexer lexer(input);

    // 打开 result.txt 文件,准备写入结果
    std::ofstream output_file("result.txt", std::ios::out | std::ios::trunc);
    if (!output_file.is_open()) {
        std::cerr << "无法打开 result.txt 文件" << std::endl;
        return 1;
    }

    // 获取并输出记号
    Token token = lexer.get_next_token();
    while (token.type != TokenType::EOF_TOKEN) {
        output_file << token << std::endl;
        token = lexer.get_next_token();
    }

    output_file.close();

    std::cout << "词法分析完成,结果已写入 result.txt 文件" << std::endl;

    return 0;
}
作者:星尘旅人
1.本网站部分素材来源于网络,仅供大家参考学习,如有侵权,请联系博主删除处理。
2.本网站一切内容不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
3.版权&许可请详阅版权声明
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇
//音乐播放