C语言实现简单的编译器

2025-05发布6次浏览

实现一个简单的C语言编译器是一项复杂但非常有意义的任务。它可以帮助我们深入了解编译器的工作原理,包括词法分析、语法分析、语义分析以及代码生成等阶段。下面我们将逐步介绍如何用C语言实现一个简单的编译器,主要针对一个非常基础的子集语言(例如只支持基本的算术表达式)。


1. 编译器的基本结构

一个典型的编译器可以分为以下几个阶段:

  1. 词法分析:将源代码转换为一系列的标记(tokens)。
  2. 语法分析:根据语法规则检查这些标记是否合法,并构建语法树。
  3. 语义分析:检查程序是否有语义错误(如类型不匹配)。
  4. 中间代码生成:生成一种中间表示形式。
  5. 优化:对中间代码进行优化。
  6. 目标代码生成:将中间代码转换为目标机器代码或字节码。

为了简化实现,我们将专注于前三个阶段(词法分析、语法分析和语义分析),并生成伪汇编代码作为输出。


2. 实现步骤

2.1 词法分析

词法分析器的任务是将输入的源代码分解成一个个标记(tokens)。例如,对于输入 a = 3 + 4;,词法分析器会将其分解为以下标记序列:

  • IDENTIFIER: a
  • ASSIGNMENT: =
  • INTEGER: 3
  • PLUS: +
  • INTEGER: 4
  • SEMICOLON: ;

以下是实现词法分析器的代码示例:

#include <stdio.h>
#include <ctype.h>
#include <string.h>

typedef enum {
    TOKEN_EOF,
    TOKEN_IDENTIFIER,
    TOKEN_INTEGER,
    TOKEN_PLUS,
    TOKEN_MINUS,
    TOKEN_MULTIPLY,
    TOKEN_DIVIDE,
    TOKEN_ASSIGNMENT,
    TOKEN_SEMICOLON
} TokenType;

typedef struct {
    TokenType type;
    char value[100];
} Token;

Token current_token;

void error(const char *msg) {
    fprintf(stderr, "Error: %s\n", msg);
    exit(1);
}

void skip_whitespace() {
    while (isspace(current_token.value[0])) {
        current_token.value[0] = getchar();
    }
}

Token get_next_token() {
    skip_whitespace();

    if (isalpha(current_token.value[0])) {
        int i = 0;
        while (isalnum(current_token.value[i] = getchar())) {
            i++;
        }
        current_token.value[i] = '\0';
        return (Token){TOKEN_IDENTIFIER, current_token.value};
    } else if (isdigit(current_token.value[0])) {
        int i = 0;
        while (isdigit(current_token.value[i] = getchar())) {
            i++;
        }
        current_token.value[i] = '\0';
        return (Token){TOKEN_INTEGER, current_token.value};
    } else {
        switch (current_token.value[0]) {
            case '+': return (Token){TOKEN_PLUS, "+"};
            case '-': return (Token){TOKEN_MINUS, "-"};
            case '*': return (Token){TOKEN_MULTIPLY, "*"};
            case '/': return (Token){TOKEN_DIVIDE, "/"};
            case '=': return (Token){TOKEN_ASSIGNMENT, "="};
            case ';': return (Token){TOKEN_SEMICOLON, ";"};
            default: error("Unknown token");
        }
    }
}

2.2 语法分析

语法分析器的任务是解析标记流并构建语法树。我们可以使用递归下降解析器来实现这一功能。以下是一个简单的语法分析器示例,支持表达式的解析:

typedef struct {
    char name[100];
    int value;
} Variable;

Variable variables[100];
int variable_count = 0;

int find_variable_index(const char *name) {
    for (int i = 0; i < variable_count; i++) {
        if (strcmp(variables[i].name, name) == 0) {
            return i;
        }
    }
    return -1;
}

void declare_variable(const char *name) {
    int index = find_variable_index(name);
    if (index == -1) {
        strcpy(variables[variable_count].name, name);
        variables[variable_count].value = 0;
        variable_count++;
    } else {
        error("Variable already declared");
    }
}

int parse_expression() {
    int result = parse_term();
    while (1) {
        Token t = get_next_token();
        if (t.type == TOKEN_PLUS) {
            result += parse_term();
        } else if (t.type == TOKEN_MINUS) {
            result -= parse_term();
        } else {
            unget_token(t);
            break;
        }
    }
    return result;
}

int parse_term() {
    int result = parse_factor();
    while (1) {
        Token t = get_next_token();
        if (t.type == TOKEN_MULTIPLY) {
            result *= parse_factor();
        } else if (t.type == TOKEN_DIVIDE) {
            result /= parse_factor();
        } else {
            unget_token(t);
            break;
        }
    }
    return result;
}

int parse_factor() {
    Token t = get_next_token();
    if (t.type == TOKEN_INTEGER) {
        return atoi(t.value);
    } else if (t.type == TOKEN_IDENTIFIER) {
        int index = find_variable_index(t.value);
        if (index == -1) {
            error("Undefined variable");
        }
        return variables[index].value;
    } else {
        error("Expected integer or identifier");
    }
}

void parse_statement() {
    Token t = get_next_token();
    if (t.type == TOKEN_IDENTIFIER) {
        declare_variable(t.value);
        t = get_next_token();
        if (t.type != TOKEN_ASSIGNMENT) {
            error("Expected '='");
        }
        int value = parse_expression();
        int index = find_variable_index(t.value);
        variables[index].value = value;
        t = get_next_token();
        if (t.type != TOKEN_SEMICOLON) {
            error("Expected ';'");
        }
    } else {
        error("Expected identifier");
    }
}

2.3 语义分析与代码生成

在语义分析阶段,我们需要确保变量声明正确且没有重复定义。同时,我们可以生成伪汇编代码作为输出。例如,对于输入 a = 3 + 4;,我们可以生成如下伪汇编代码:

LOAD_CONST 3
LOAD_CONST 4
ADD
STORE_VAR a

以下是生成伪汇编代码的示例函数:

void generate_code(int value, const char *var_name) {
    printf("LOAD_CONST %d\n", value);
    printf("STORE_VAR %s\n", var_name);
}

3. 总结

通过上述步骤,我们实现了一个非常基础的编译器。尽管这个编译器的功能有限,但它展示了编译器的核心工作流程:从词法分析到语法分析,再到语义分析和代码生成。