编译原理实验一:C++词法分析器(python实现)

写在所有的前面:

本文采用PYTHON实现代码
文章更新时间:2025.5.16,17:44

实验说明

要求

设计并实现C++词法分析器,将 C++ 源代码分解为一个个有意义的词法单元。

设计

词法单元类型

  1. 预处理指令PREPROCESSOR:
    #include#define
  2. 注释:
    单行//、多行/* */
  3. 关键字:
    数据类型关键字:intlongshortdoublefloatcharvoid
    语句关键字:ifelseforwhileswitchcasebreakcontinuereturnprintf
    特殊关键字:(hrw老师自定义扩展类型)
    mainint36
    非直接识别关键字:char*
  4. int36类型字面量: 0z(XX) (包含字母a-z,不区分大小写)
  5. 标识符:任意单词
  6. 整数:支持十六进制、八进制、二进制和十进制
  7. 浮点数:支持小数、指数形式,如 3.14e10
  8. 字符字面量 转义字符字面量 '\a' '\b' '\f' '\n' '\r' '\t' '\v' '\\' '\'' '\"' '3位\数字0-7'(8进制数) '任意字符'
  9. 运算符: (不包括点,因为点在标识符中已经处理)
    算术运算符:+-*/%
    赋值运算符:=+=-=*=/=
    比较运算符:==!=><>=<=
    逻辑运算符:&&||!
    位运算符:&|^~<<>>
    自增 / 自减:++--
  10. 分隔符:
    括号:(){}[]
    标点符号:,;"'\.
  11. 格式说明符:%d%s%f等(用于 printf/scanf
  12. 转义字符:\a \b \f \n \r \t \v \\ \' \"
  13. 字符串处理:
    拆解处理,非转义,非格式说明符,则为普通字符串内容:如 "Hello, world!" 中的Helloworld!

代码设计

1. 模块导入与数据结构

  1. re 模块(正则表达式操作)
    功能:提供正则表达式匹配功能,用于识别和提取特定模式的文本。
  2. sys 模块(系统相关功能)
    功能:提供对 Python 解释器和系统环境的访问。
import re
import sys

2. 正则表达式(词法单元类型定义)

  1. 使用正则表达式精确匹配各种词法单元
  2. 特殊格式支持:36 进制整数、点分标识符、格式说明符等
  3. 优先级控制:长模式优先(如++先于+),关键字优先于标识符
# 定义 token 类型与对应的正则表达式
token_specification = [
    # 预处理指令: #include <任意内容> 或 #define 单词1 单词2
    ('PREPROCESSOR', r'#(include\s+<[^>]+>|define\s+\w+\s+\w+)'),
    # 多行注释: /* 尽可能少的字符 */
    ('COMMENT_MULTI', r'/\*.*?\*/'),
    # 单行注释: // 任意字符至本行末尾
    ('COMMENT_SINGLE', r'//.*'),
    # 关键字:数据类型: int|long|short|int36|double|float|char|void|struct
    #        语句:    if|else|for|while|switch|case|break|continue|return|printf
    # 老师要求:增加更多的 C++ 关键字,包括 int36(36进制整数)
    ('KEYWORD', r'\b(int|long|short|int36|double|float|char|void|struct|if|else|for|while|switch|case|break|continue|return'
                r'|printf)\b'),
    # int36类型字面量: 0z(XX) (包含字母a-z,不区分大小写)
    ('INT36_LITERAL', r'0[zZ]\([0-9a-zA-Z]+\)'),
    # 标识符(单词)
    ('IDENTIFIER', r'[a-zA-Z_]\w*'),
    # 浮点数:支持小数、指数形式,如 3.14e10
    ('FLOAT_LITERAL', r'\d+\.\d+(e[+-]?\d+)?'),
    # 整数:支持十六进制、八进制、二进制和十进制
    ('INT_LITERAL', r'0[xX][0-9a-fA-F]+|0[0-7]+|\d+'),
    # 字符字面量 转义字符字面量 '\a' '\b' '\f' '\n' '\r' '\t' '\v' '\\' '\'' '\"' '3位\数字0-7'(8进制数) 或 '任意字符'
    ('CHAR_LITERAL', r"'(\\[abfnrtv\\\'\"?0-7]|[^\\'])'"),
    # 运算符 (不包括点,因为点在标识符中已经处理)
    ('OPERATOR', r'\+\+|--|==|>=|<=|!=|&&|\|\||\+\-|\*/|<<|>>|&|\||\^|~|=|\+|\-|\*|\/|%|<|>'),
    # 分隔符 ( ) { } , ; \ [ ] ' " .
    ('DELIMITER', r'[(){},;\[\]\'"\.]'),
    # 转义字符 \a \b \f \n \r \t \v \\ \' \"
    ('ESCAPE_CHAR', r'\\[abfnrtv\\\'\"]'),
    # 格式说明符 %数字.数字字符 如:%3.3d
    ('FORMAT_SPECIFIER', r'%[0-9]*\.?[0-9]*[diouxXfFeEgGaAcspn]'),
    # 字符串内容 除了%、双引号、换行符之外任意字符
    ('STRING_CONTENT', r'[^%\\"\n]+'),
    # 空白符
    ('WHITESPACE', r'\s+'),
]

3. 按行处理

# 按行处理,方便处理预处理指令或注释
    lines = code.split('\n')
    line_num = 1  # 当前行数
    in_multi_comment = False  # 标记是否已在多行注释里

    # 逐行处理
    for line in lines:
        stripped_line = line.lstrip()

3.1 注释处理

# 处理多行注释的情况
        if in_multi_comment:
            end_pos = stripped_line.find('*/')
            if end_pos != -1:
                # 多行注释在此行结束,提取注释内容
                comment_content = stripped_line[:end_pos + 2]
                tokens.append(('COMMENT_MULTI', comment_content, line_num))
                in_multi_comment = False

                # 处理注释后的剩余部分
                remaining = stripped_line[end_pos + 2:].lstrip()
                if remaining:
                    # 递归处理剩余部分(简化版,实际可能需要更复杂逻辑)
                    tokens.extend(tokenize(remaining))
            else:
                # 多行注释继续到下一行
                tokens.append(('COMMENT_MULTI', stripped_line, line_num))
            line_num += 1
            continue

        # 检查是否开始新的多行注释
        if stripped_line.startswith('/*'):
            end_pos = stripped_line.find('*/')
            if end_pos != -1:
                # 单行内的多行注释 /* ... */
                tokens.append(('COMMENT_MULTI', stripped_line[:end_pos + 2], line_num))

                # 处理注释后的剩余部分
                remaining = stripped_line[end_pos + 2:].lstrip()
                if remaining:
                    tokens.extend(tokenize(remaining))
            else:
                # 多行注释开始但未结束
                tokens.append(('COMMENT_MULTI', stripped_line, line_num))
                in_multi_comment = True
            line_num += 1
            continue

        # 检查是否为单行注释
        comment_pos = line.find('//')

3.2 预处理指令处理

# 处理预处理指令
        if line.startswith('#'):
            tokens.extend(tokenize_preprocessor(line, line_num))
            line_num += 1
            continue

调用函数

def tokenize_preprocessor(line, line_num):
    """处理预处理指令"""
    tokens = []
    if line.startswith('#include'):
        tokens.append(('PREPROCESSOR', line, line_num))
    elif line.startswith('#define'):
        tokens.append(('PREPROCESSOR', line, line_num))
    return tokens

4. 按字处理

		# 对非预处理指令的行应用常规的词法分析
        pos = 0  # 本行已处理指针
        # 逐字符判断
        while pos < len(line):
            # 跳过空白字符
            if line[pos].isspace():
                pos += 1
                continue

4.1 单行注释

# 单行注释
            if pos == comment_pos:
                comment_text = line[comment_pos:]
                tokens.append(('COMMENT_SINGLE', comment_text, line_num))
                break

4.2 字符串处理

4.1 字符串内容处理

  1. 解析字符串中的格式说明符(如%d)
  2. 识别转义字符(如\n)
  3. 提取普通文本内容
# 检查是否是字符串字面量
            if line[pos] == '"':
                # 添加左引号
                tokens.append(('DELIMITER', '"', line_num))
                pos += 1
                # 找到右引号(非转义)
                end_pos = pos
                while end_pos < len(line):
                    if line[end_pos] == '"':
                        # 检查是否为转义引号
                        backslash_count = 0
                        prev_pos = end_pos - 1
                        while prev_pos >= pos and line[prev_pos] == '\\':
                            backslash_count += 1
                            prev_pos -= 1
                        # 如果反斜杠数量为奇数,则是转义引号,继续寻找
                        if backslash_count % 2 != 0:
                            end_pos += 1
                            continue
                        # 否则找到真正的右引号
                        break
                    end_pos += 1

                if end_pos < len(line):
                    # 提取字符串内容
                    string_content = line[pos:end_pos]

                    # 处理字符串内部的各种元素
                    string_tokens = tokenize_string(string_content)
                    tokens.extend([(token_type, token, line_num) for token_type, token in string_tokens])

                    # 添加右引号
                    tokens.append(('DELIMITER', '"', line_num))
                    pos = end_pos + 1
                else:
                    # 未闭合的字符串,错误处理
                    tokens.append(('错误', '未闭合的字符串', line_num))
                    pos = end_pos
                continue

调用函数

def tokenize_string(content):
    """
    将字符串内容拆分为 STRING_CONTENT、FORMAT_SPECIFIER 和 ESCAPE_CHAR
    """
    tokens = []
    pos = 0

    while pos < len(content):
        # 处理转义字符
        if content[pos] == '\\' and pos + 1 < len(content):
            tokens.append(('ESCAPE_CHAR', content[pos:pos + 2]))
            pos += 2
            continue

        # 处理格式说明符
        if content[pos] == '%' and pos + 1 < len(content):
            m = re.match(r'%[0-9]*\.?[0-9]*[diouxXfFeEgGaAcspn]', content[pos:])
            if m:
                tokens.append(('FORMAT_SPECIFIER', m.group(0)))
                pos += len(m.group(0))
                continue

        # 处理普通字符串内容
        next_p = content.find('%', pos)
        next_b = content.find('\\', pos)

        end = min(
            next_p if next_p != -1 else len(content),
            next_b if next_b != -1 else len(content)
        )

        if end > pos:
            tokens.append(('STRING_CONTENT', content[pos:end]))
            pos = end
        else:
            pos += 1

    return tokens

4.3 界符

            # 单字符的界符直接处理
            elif line[pos] in "(){},;[]":
                tokens.append(('DELIMITER', line[pos], line_num))
                pos += 1

4.4 普通词法匹配

            else:
                # 使用正则模式进行普通词法分析
                match = None
                for token_type, pattern in token_specification:
                    # 不处理空白符和注释
                    if token_type in ('WHITESPACE', 'COMMENT_MULTI', 'COMMENT_SINGLE'):
                        continue

                    # 尝试匹配当前位置的模式
                    regex = re.compile(pattern)
                    m = regex.match(line[pos:])
                    if m and m.start() == 0:  # 确保匹配从当前位置开始
                        lexeme = m.group(0)
                        if lexeme == 'char':
                            next_pos = pos + m.end()  # 计算匹配结束后的位置
                            if next_pos < len(line):  # 确保没有超出字符串范围
                                next_char = line[next_pos]  # 获取下一个字符
                                if next_char == '*':
                                    lexeme = 'char*'

                        tokens.append((token_type, lexeme, line_num))
                        pos += len(lexeme)
                        match = True
                        break

4.5 容错跳过(可删除)以及返回

容错跳过

                if not match:
                    # 如果没有匹配任何模式,跳过当前字符
                    pos += 1

返回

        line_num += 1

    return tokens                    

5. 主程序入口

  1. 输入: C++ 源文件名称
  2. 调用词法分析器处理代码
  3. 输出:词法单元 (类型) 行号
  4. 词典将输出结果转中文
def main():
    file_path = input("请输入测试文件名:")  # example1.cpp

    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            code = f.read()
    except FileNotFoundError:
        print(f"错误: 文件 '{file_path}' 未找到.")
        sys.exit(1)

    tokens = tokenize(code)

    # 输出结果,类型用中文表示
    type_map = {
        'PREPROCESSOR': '预处理器',
        'COMMENT_MULTI': '多行注释',
        'COMMENT_SINGLE': '单行注释',
        'KEYWORD': '关键字',
        'IDENTIFIER': '标识符',
        'FLOAT_LITERAL': '浮点数',
        'INT_LITERAL': '整数',
        'INT36_LITERAL': '36进制整数',
        'OPERATOR': '运算符',
        'DELIMITER': '分隔符',
        'CHAR_LITERAL': '字符字面量',
        'FORMAT_SPECIFIER': '格式说明符',
        'STRING_CONTENT': '字符串内容',
        'ESCAPE_CHAR': '转义字符',
        'WHITESPACE': '空白符',
    }

    # 类型 内容 行号
    for ttype, lexeme, lineno in tokens:
        print(f'{lexeme} ({type_map[ttype]}) {lineno}')


if __name__ == '__main__':
    main()

6. 特别说明

36 进制整数 int36

r'0[zZ]\([0-9a-zA-Z]+\)'

格式:0z(XX)(如0z(1A)表示十进制 46)

字符串边界判断

  1. 识别双引号边界
  2. 避免转义字符的影响
  3. 能正确处理连续多个反斜杠的情况(如 \\")
            # 检查是否是字符串字面量
            if line[pos] == '"':
                # 添加左引号
                tokens.append(('DELIMITER', '"', line_num))
                pos += 1
                # 找到右引号(非转义)
                end_pos = pos
                while end_pos < len(line):
                    if line[end_pos] == '"':
                        # 检查是否为转义引号
                        backslash_count = 0
                        prev_pos = end_pos - 1
                        while prev_pos >= pos and line[prev_pos] == '\\':
                            backslash_count += 1
                            prev_pos -= 1
                        # 如果反斜杠数量为奇数,则是转义引号,继续寻找
                        if backslash_count % 2 != 0:
                            end_pos += 1
                            continue
                        # 否则找到真正的右引号
                        break
                    end_pos += 1

char* 数据类型关键词

                    # 尝试匹配当前位置的模式
                    regex = re.compile(pattern)
                    m = regex.match(line[pos:])
                    if m and m.start() == 0:  # 确保匹配从当前位置开始
                        lexeme = m.group(0)
                        if lexeme == 'char':
                            next_pos = pos + m.end()  # 计算匹配结束后的位置
                            if next_pos < len(line):  # 确保没有超出字符串范围
                                next_char = line[next_pos]  # 获取下一个字符
                                if next_char == '*':
                                    lexeme = 'char*'

词法分析器工作流程

  1. 按行扫描:逐行处理源代码
  2. 注释优先:先标注处理单行注释和多行注释
  3. 预处理优先:先处理#include和#define
  4. 字符串特殊处理:识别完整字符串并解析内部结构
  5. 正则匹配:按顺序使用正则表达式匹配各种词法单元
  6. 容错处理:无法匹配的字符直接跳过(简化处理)

完整代码 lexer1.py

import re
import sys

# 定义 token 类型与对应的正则表达式
token_specification = [
    # 预处理指令: #include <任意内容> 或 #define 单词1 单词2
    ('PREPROCESSOR', r'#(include\s+<[^>]+>|define\s+\w+\s+\w+)'),
    # 多行注释: /* 尽可能少的字符 */
    ('COMMENT_MULTI', r'/\*.*?\*/'),
    # 单行注释: // 任意字符至本行末尾
    ('COMMENT_SINGLE', r'//.*'),
    # 关键字:数据类型: int|long|short|int36|double|float|char|void|struct
    #        语句:    if|else|for|while|switch|case|break|continue|return|printf
    # 老师要求:增加更多的 C++ 关键字,包括 int36(36进制整数)
    ('KEYWORD', r'\b(int|long|short|int36|double|float|char|void|struct|if|else|for|while|switch|case|break|continue|return'
                r'|printf)\b'),
    # int36类型字面量: 0z(XX) (包含字母a-z,不区分大小写)
    ('INT36_LITERAL', r'0[zZ]\([0-9a-zA-Z]+\)'),
    # 标识符(单词)
    ('IDENTIFIER', r'[a-zA-Z_]\w*'),
    # 浮点数:支持小数、指数形式,如 3.14e10
    ('FLOAT_LITERAL', r'\d+\.\d+(e[+-]?\d+)?'),
    # 整数:支持十六进制、八进制、二进制和十进制
    ('INT_LITERAL', r'0[xX][0-9a-fA-F]+|0[0-7]+|\d+'),
    # 字符字面量 转义字符字面量 '\a' '\b' '\f' '\n' '\r' '\t' '\v' '\\' '\'' '\"' '3位\数字0-7'(8进制数) 或 '任意字符'
    ('CHAR_LITERAL', r"'(\\[abfnrtv\\\'\"?0-7]|[^\\'])'"),
    # 运算符 (不包括点,因为点在标识符中已经处理)
    ('OPERATOR', r'\+\+|--|==|>=|<=|!=|&&|\|\||\+\-|\*/|<<|>>|&|\||\^|~|=|\+|\-|\*|\/|%|<|>'),
    # 分隔符 ( ) { } , ; \ [ ] ' " .
    ('DELIMITER', r'[(){},;\[\]\'"\.]'),
    # 转义字符 \a \b \f \n \r \t \v \\ \' \"
    ('ESCAPE_CHAR', r'\\[abfnrtv\\\'\"]'),
    # 格式说明符 %数字.数字字符 如:%3.3d
    ('FORMAT_SPECIFIER', r'%[0-9]*\.?[0-9]*[diouxXfFeEgGaAcspn]'),
    # 字符串内容 除了%、双引号、换行符之外任意字符
    ('STRING_CONTENT', r'[^%\\"\n]+'),
    # 空白符
    ('WHITESPACE', r'\s+'),
]


def tokenize(code):
    """
    传入 C++ 代码字符串,返回一个 (token_type, lexeme) 的列表
    """
    tokens = []

    # 按行处理,方便处理预处理指令或注释
    lines = code.split('\n')
    line_num = 1  # 当前行数
    in_multi_comment = False  # 标记是否已在多行注释里

    # 逐行处理
    for line in lines:
        stripped_line = line.lstrip()

        # 处理多行注释的情况
        if in_multi_comment:
            end_pos = stripped_line.find('*/')
            if end_pos != -1:
                # 多行注释在此行结束,提取注释内容
                comment_content = stripped_line[:end_pos + 2]
                tokens.append(('COMMENT_MULTI', comment_content, line_num))
                in_multi_comment = False

                # 处理注释后的剩余部分
                remaining = stripped_line[end_pos + 2:].lstrip()
                if remaining:
                    # 递归处理剩余部分(简化版,实际可能需要更复杂逻辑)
                    tokens.extend(tokenize(remaining))
            else:
                # 多行注释继续到下一行
                tokens.append(('COMMENT_MULTI', stripped_line, line_num))
            line_num += 1
            continue

        # 检查是否开始新的多行注释
        if stripped_line.startswith('/*'):
            end_pos = stripped_line.find('*/')
            if end_pos != -1:
                # 单行内的多行注释 /* ... */
                tokens.append(('COMMENT_MULTI', stripped_line[:end_pos + 2], line_num))

                # 处理注释后的剩余部分
                remaining = stripped_line[end_pos + 2:].lstrip()
                if remaining:
                    tokens.extend(tokenize(remaining))
            else:
                # 多行注释开始但未结束
                tokens.append(('COMMENT_MULTI', stripped_line, line_num))
                in_multi_comment = True
            line_num += 1
            continue

        # 检查是否为单行注释
        comment_pos = line.find('//')

        # 处理预处理指令
        if line.startswith('#'):
            tokens.extend(tokenize_preprocessor(line, line_num))
            line_num += 1
            continue

        # 对非预处理指令的行应用常规的词法分析
        pos = 0  # 本行已处理指针

        # 逐字符判断
        while pos < len(line):
            # 跳过空白字符
            if line[pos].isspace():
                pos += 1
                continue

            # 单行注释
            if pos == comment_pos:
                comment_text = line[comment_pos:]
                tokens.append(('COMMENT_SINGLE', comment_text, line_num))
                break

            # 检查是否是字符串字面量
            if line[pos] == '"':
                # 添加左引号
                tokens.append(('DELIMITER', '"', line_num))
                pos += 1
                # 找到右引号(非转义)
                end_pos = pos
                while end_pos < len(line):
                    if line[end_pos] == '"':
                        # 检查是否为转义引号
                        backslash_count = 0
                        prev_pos = end_pos - 1
                        while prev_pos >= pos and line[prev_pos] == '\\':
                            backslash_count += 1
                            prev_pos -= 1
                        # 如果反斜杠数量为奇数,则是转义引号,继续寻找
                        if backslash_count % 2 != 0:
                            end_pos += 1
                            continue
                        # 否则找到真正的右引号
                        break
                    end_pos += 1

                if end_pos < len(line):
                    # 提取字符串内容
                    string_content = line[pos:end_pos]

                    # 处理字符串内部的各种元素
                    string_tokens = tokenize_string(string_content)
                    tokens.extend([(token_type, token, line_num) for token_type, token in string_tokens])

                    # 添加右引号
                    tokens.append(('DELIMITER', '"', line_num))
                    pos = end_pos + 1
                else:
                    # 未闭合的字符串,错误处理
                    tokens.append(('错误', '未闭合的字符串', line_num))
                    pos = end_pos
                continue

            # 单字符的界符直接处理
            elif line[pos] in "(){},;[]":
                tokens.append(('DELIMITER', line[pos], line_num))
                pos += 1

            else:
                # 使用正则模式进行普通词法分析
                match = None
                for token_type, pattern in token_specification:
                    # 不处理空白符和注释
                    if token_type in ('WHITESPACE', 'COMMENT_MULTI', 'COMMENT_SINGLE'):
                        continue

                    # 尝试匹配当前位置的模式
                    regex = re.compile(pattern)
                    m = regex.match(line[pos:])
                    if m and m.start() == 0:  # 确保匹配从当前位置开始
                        lexeme = m.group(0)
                        if lexeme == 'char':
                            next_pos = pos + m.end()  # 计算匹配结束后的位置
                            if next_pos < len(line):  # 确保没有超出字符串范围
                                next_char = line[next_pos]  # 获取下一个字符
                                if next_char == '*':
                                    lexeme = 'char*'

                        tokens.append((token_type, lexeme, line_num))
                        pos += len(lexeme)
                        match = True
                        break

                if not match:
                    # 如果没有匹配任何模式,跳过当前字符
                    pos += 1

        line_num += 1

    return tokens


def tokenize_preprocessor(line, line_num):
    """处理预处理指令"""
    tokens = []
    if line.startswith('#include'):
        tokens.append(('PREPROCESSOR', line, line_num))
    elif line.startswith('#define'):
        tokens.append(('PREPROCESSOR', line, line_num))
    return tokens


def tokenize_string(content):
    """
    将字符串内容拆分为 STRING_CONTENT、FORMAT_SPECIFIER 和 ESCAPE_CHAR
    """
    tokens = []
    pos = 0

    while pos < len(content):
        # 处理转义字符
        if content[pos] == '\\' and pos + 1 < len(content):
            tokens.append(('ESCAPE_CHAR', content[pos:pos + 2]))
            pos += 2
            continue

        # 处理格式说明符
        if content[pos] == '%' and pos + 1 < len(content):
            m = re.match(r'%[0-9]*\.?[0-9]*[diouxXfFeEgGaAcspn]', content[pos:])
            if m:
                tokens.append(('FORMAT_SPECIFIER', m.group(0)))
                pos += len(m.group(0))
                continue

        # 处理普通字符串内容
        next_p = content.find('%', pos)
        next_b = content.find('\\', pos)

        end = min(
            next_p if next_p != -1 else len(content),
            next_b if next_b != -1 else len(content)
        )

        if end > pos:
            tokens.append(('STRING_CONTENT', content[pos:end]))
            pos = end
        else:
            pos += 1

    return tokens


def main():
    file_path = input("请输入测试文件名:")  # example1.cpp

    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            code = f.read()
    except FileNotFoundError:
        print(f"错误: 文件 '{file_path}' 未找到.")
        sys.exit(1)

    tokens = tokenize(code)

    # 输出结果,类型用中文表示
    type_map = {
        'PREPROCESSOR': '预处理器',
        'COMMENT_MULTI': '多行注释',
        'COMMENT_SINGLE': '单行注释',
        'KEYWORD': '关键字',
        'IDENTIFIER': '标识符',
        'FLOAT_LITERAL': '浮点数',
        'INT_LITERAL': '整数',
        'INT36_LITERAL': '36进制整数',
        'OPERATOR': '运算符',
        'DELIMITER': '分隔符',
        'CHAR_LITERAL': '字符字面量',
        'FORMAT_SPECIFIER': '格式说明符',
        'STRING_CONTENT': '字符串内容',
        'ESCAPE_CHAR': '转义字符',
        'WHITESPACE': '空白符',
    }

    # 类型 内容 行号
    for ttype, lexeme, lineno in tokens:
        print(f'{lexeme} ({type_map[ttype]}) {lineno}')


if __name__ == '__main__':
    main()

实验样例

  1. 新建 example1.cpp 样例文件
  2. 输入样例代码(这里采用实验2老师给的样例)
struct student{
    char* name; //姓名
    int num;   //学号
    int age;   //年龄
    float score;  //成绩
};
void main(){
    int i, num_140 = 0;
    float sum = 0;
    student sts[2]={{"Li ping", 5, 18, 145.0},
                    {"Wang ming", 6, 18, 150.0}};
    if(sts[1].score<140) flag=-1;
    else flag=1;
    printf("%d ", flag);
}

  1. 启动程序 lexer1.py 并输入 example1.cpp
  2. 输出结果

struct (关键字) 1
student (标识符) 1
{ (分隔符) 1
char* (关键字) 2
name (标识符) 2
; (分隔符) 2
//姓名 (单行注释) 2
int (关键字) 3
num (标识符) 3
; (分隔符) 3
//学号 (单行注释) 3
int (关键字) 4
age (标识符) 4
; (分隔符) 4
//年龄 (单行注释) 4
float (关键字) 5
score (标识符) 5
; (分隔符) 5
//成绩 (单行注释) 5
} (分隔符) 6
; (分隔符) 6
void (关键字) 7
main (标识符) 7
( (分隔符) 7
) (分隔符) 7
{ (分隔符) 7
int (关键字) 8
i (标识符) 8
, (分隔符) 8
num_140 (标识符) 8
= (运算符) 8
0 (整数) 8
; (分隔符) 8
float (关键字) 9
sum (标识符) 9
= (运算符) 9
0 (整数) 9
; (分隔符) 9
student (标识符) 10
sts (标识符) 10
[ (分隔符) 10
2 (整数) 10
] (分隔符) 10
= (运算符) 10
{ (分隔符) 10
{ (分隔符) 10
" (分隔符) 10
Li ping (字符串内容) 10
" (分隔符) 10
, (分隔符) 10
5 (整数) 10
, (分隔符) 10
18 (整数) 10
, (分隔符) 10
145.0 (浮点数) 10
} (分隔符) 10
, (分隔符) 10
{ (分隔符) 11
" (分隔符) 11
Wang ming (字符串内容) 11
" (分隔符) 11
, (分隔符) 11
6 (整数) 11
, (分隔符) 11
18 (整数) 11
, (分隔符) 11
150.0 (浮点数) 11
} (分隔符) 11
} (分隔符) 11
; (分隔符) 11
if (关键字) 12
( (分隔符) 12
sts (标识符) 12
[ (分隔符) 12
1 (整数) 12
] (分隔符) 12
. (分隔符) 12
score (标识符) 12
< (运算符) 12
140 (整数) 12
) (分隔符) 12
flag (标识符) 12
= (运算符) 12

  • (运算符) 12
    1 (整数) 12
    ; (分隔符) 12
    else (关键字) 13
    flag (标识符) 13
    = (运算符) 13
    1 (整数) 13
    ; (分隔符) 13
    printf (关键字) 14
    ( (分隔符) 14
    " (分隔符) 14
    %d (格式说明符) 14
    (字符串内容) 14
    " (分隔符) 14
    , (分隔符) 14
    flag (标识符) 14
    ) (分隔符) 14
    ; (分隔符) 14
    } (分隔符) 15

其他解释

本小组自用广西大学编译原理课程设计,指导教师 hrw
个别功能符号的识别未实现(面向结果的编程)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值