+
9
-

回答

创立一门新的编程语言是一个宏大但极其有价值的挑战,它能让你深入理解计算机工作的每一个层面。

我将为你设计一门全新的、简单的编程语言,并一步步教你如何为它编写一个编译器

这个编译器将把我们的新语言代码转换成C语言代码(这个过程称为转译),然后利用成熟的C编译器(如GCC)生成最终的可执行程序。这是一种非常实用且高效的创建新语言的方法,因为我们不必自己处理复杂的机器码生成和优化。

我们将创建的语言叫做 "Aura"

第1部分:Aura 语言设计

首先,我们需要设计语言。为了保持简单,Aura将具备以下特性:

变量声明: 使用 let 关键字。

数据类型: 只有整数 (int)。

运算: 支持 +, -, *, / 和括号 ()。

函数: 内置一个 print() 函数,用于打印整数。

语法: 每条语句以分号 ; 结尾。

一个 Aura 程序的例子 (example.aura):

// This is a comment
let x = 10;
let y = 20;
let z = x + (y * 2); // z should be 50

print(z);
print(z - 10);

第2部分:编译器架构

我们的编译器 (aurac) 将遵循经典的三步流程:

词法分析 (Lexing):将源代码文本(let x = 10;)分解成一系列有意义的“令牌”(Tokens)。例如:TOKEN(LET), TOKEN(IDENTIFIER, 'x'), TOKEN(EQUALS), TOKEN(INTEGER, 10), TOKEN(SEMICOLON).

语法分析 (Parsing):将令牌流转换成一个树形结构,称为抽象语法树(Abstract Syntax Tree, AST)。这个树准确地表示了代码的结构和运算优先级。

代码生成 (Code Generation):遍历AST,并根据树的结构生成等效的C语言代码。

第3部分:用 Python 实现编译器

我们将用Python编写这个编译器。创建一个名为 compiler.py 的文件。

步骤 1: 环境准备

确保你安装了 Python 3 和一个 C 编译器(在Linux/macOS上通常是 gcc 或 clang)。

# 在 Debian/Ubuntu 上安装 GCC
sudo apt update
sudo apt install build-essential
步骤 2: 编写 compiler.py

将下面的代码完整地复制到 compiler.py 文件中。代码中包含了详细的注释来解释每一步。

# compiler.py
import sys
import subprocess
from enum import Enum

# --- 1. 词法分析 (Lexer) ---

class TokenType(Enum):
    INTEGER   = 'INTEGER'
    PLUS      = 'PLUS'
    MINUS     = 'MINUS'
    MUL       = 'MUL'
    DIV       = 'DIV'
    LPAREN    = 'LPAREN'
    RPAREN    = 'RPAREN'
    LET       = 'LET'
    IDENTIFIER= 'IDENTIFIER'
    EQUALS    = 'EQUALS'
    SEMICOLON = 'SEMICOLON'
    PRINT     = 'PRINT'
    EOF       = 'EOF' # End of File

class Token:
    def __init__(self, type, value=None):
        self.type = type
        self.value = value

    def __str__(self):
        return f'Token({self.type.name}, {repr(self.value)})'

class Lexer:
    def __init__(self, text):
        self.text = text
        self.pos = 0
        self.current_char = self.text[self.pos] if self.pos < len(self.text) else None
        self.keywords = {'let': Token(TokenType.LET), 'print': Token(TokenType.PRINT)}

    def advance(self):
        self.pos += 1
        self.current_char = self.text[self.pos] if self.pos < len(self.text) else None

    def skip_whitespace(self):
        while self.current_char is not None and self.current_char.isspace():
            self.advance()

    def skip_comment(self):
        if self.current_char == '/' and self.peek() == '/':
            while self.current_char is not None and self.current_char != '\n':
                self.advance()
            self.skip_whitespace()

    def peek(self):
        peek_pos = self.pos + 1
        return self.text[peek_pos] if peek_pos < len(self.text) else None

    def integer(self):
        result = ''
        while self.current_char is not None and self.current_char.isdigit():
            result += self.current_char
            self.advance()
        return int(result)

    def identifier(self):
        result = ''
        while self.current_char is not None and self.current_char.isalnum():
            result += self.current_char
            self.advance()
        return self.keywords.get(result, Token(TokenType.IDENTIFIER, result))

    def get_next_token(self):
        while self.current_char is not None:
            if self.current_char.isspace():
                self.skip_whitespace()
                continue

            if self.current_char == '/' and self.peek() == '/':
                self.skip_comment()
                continue

            if self.current_char.isdigit():
                return Token(TokenType.INTEGER, self.integer())

            if self.current_char.isalpha():
                return self.identifier()

            if self.current_char == '=':
                self.advance()
                return Token(TokenType.EQUALS, '=')

            if self.current_char == ';':
                self.advance()
                return Token(TokenType.SEMICOLON, ';')

            if self.current_char == '+':
                self.advance()
                return Token(TokenType.PLUS, '+')

            if self.current_char == '-':
                self.advance()
                return Token(TokenType.MINUS, '-')

            if self.current_char == '*':
                self.advance()
                return Token(TokenType.MUL, '*')

            if self.current_char == '/':
                self.advance()
                return Token(TokenType.DIV, '/')

            if self.current_char == '(':
                self.advance()
                return Token(TokenType.LPAREN, '(')

            if self.current_char == ')':
                self.advance()
                return Token(TokenType.RPAREN, ')')

            raise Exception(f'Invalid character: {self.current_char}')

        return Token(TokenType.EOF)

# --- 2. 语法分析 (Parser) & AST 节点 ---

class AST:
    pass

class BinOp(AST):
    def __init__(self, left, op, right):
        self.left = left
        self.op = op
        self.right = right

class Num(AST):
    def __init__(self, token):
        self.token = token
        self.value = token.value

class Var(AST):
    def __init__(self, token):
        self.token = token
        self.name = token.value

class Assign(AST):
    def __init__(self, left, op, right):
        self.left = left # Variable
        self.op = op   # '=' token
        self.right = right # Expression

class Print(AST):
    def __init__(self, expr):
        self.expr = expr

class Program(AST):
    def __init__(self):
        self.statements = []

class Parser:
    def __init__(self, lexer):
        self.lexer = lexer
        self.current_token = self.lexer.get_next_token()

    def eat(self, token_type):
        if self.current_token.type == token_type:
            self.current_token = self.lexer.get_next_token()
        else:
            raise Exception(f'Parsing error: expected {token_type}, got {self.current_token.type}')

    def factor(self):
        """ factor : INTEGER | IDENTIFIER | LPAREN expr RPAREN """
        token = self.current_token
        if token.type == TokenType.INTEGER:
            self.eat(TokenType.INTEGER)
            return Num(token)
        elif token.type == TokenType.IDENTIFIER:
            self.eat(TokenType.IDENTIFIER)
            return Var(token)
        elif token.type == TokenType.LPAREN:
            self.eat(TokenType.LPAREN)
            node = self.expr()
            self.eat(TokenType.RPAREN)
            return node

    def term(self):
        """ term : factor ((MUL | DIV) factor)* """
        node = self.factor()
        while self.current_token.type in (TokenType.MUL, TokenType.DIV):
            op = self.current_token
            self.eat(op.type)
            node = BinOp(left=node, op=op, right=self.factor())
        return node

    def expr(self):
        """ expr : term ((PLUS | MINUS) term)* """
        node = self.term()
        while self.current_token.type in (TokenType.PLUS, TokenType.MINUS):
            op = self.current_token
            self.eat(op.type)
            node = BinOp(left=node, op=op, right=self.term())
        return node

    def assignment_statement(self):
        """ assignment_statement : LET IDENTIFIER EQUALS expr SEMICOLON """
        self.eat(TokenType.LET)
        left = Var(self.current_token)
        self.eat(TokenType.IDENTIFIER)
        op = self.current_token
        self.eat(TokenType.EQUALS)
        right = self.expr()
        node = Assign(left, op, right)
        self.eat(TokenType.SEMICOLON)
        return node

    def print_statement(self):
        """ print_statement : PRINT LPAREN expr RPAREN SEMICOLON """
        self.eat(TokenType.PRINT)
        self.eat(TokenType.LPAREN)
        expr_node = self.expr()
        self.eat(TokenType.RPAREN)
        self.eat(TokenType.SEMICOLON)
        return Print(expr_node)

    def statement(self):
        if self.current_token.type == TokenType.LET:
            return self.assignment_statement()
        elif self.current_token.type == TokenType.PRINT:
            return self.print_statement()
        else:
            # For now, we can add more statement types here
            raise Exception(f"Invalid statement start: {self.current_token}")

    def parse(self):
        program_node = Program()
        while self.current_token.type != TokenType.EOF:
            program_node.statements.append(self.statement())
        return program_node


# --- 3. 代码生成 (Code Generator) ---

class CodeGenerator:
    def __init__(self, parser):
        self.ast = parser.parse()
        self.declared_variables = set()

    def visit(self, node):
        method_name = f'visit_{type(node).__name__}'
        visitor = getattr(self, method_name, self.generic_visit)
        return visitor(node)

    def generic_visit(self, node):
        raise Exception(f'No visit_{type(node).__name__} method')

    def visit_Program(self, node):
        c_code = "#include <stdio.h>\n\nint main() {\n"
        for statement in node.statements:
            c_code += "    " + self.visit(statement) + "\n"
        c_code += "    return 0;\n}\n"
        return c_code

    def visit_Assign(self, node):
        var_name = node.left.name
        # Declare variable with `int` only the first time it's assigned
        if var_name not in self.declared_variables:
            self.declared_variables.add(var_name)
            return f"int {var_name} = {self.visit(node.right)};"
        return f"{var_name} = {self.visit(node.right)};"

    def visit_Var(self, node):
        return node.name

    def visit_Num(self, node):
        return str(node.value)

    def visit_BinOp(self, node):
        return f"({self.visit(node.left)} {node.op.value} {self.visit(node.right)})"

    def visit_Print(self, node):
        expr_code = self.visit(node.expr)
        return f'printf("%d\\n", {expr_code});'

    def generate(self):
        return self.visit(self.ast)


# --- 主程序 ---
def main():
    if len(sys.argv) != 2:
        print("Usage: python compiler.py <source_file.aura>")
        sys.exit(1)

    source_filename = sys.argv[1]
    if not source_filename.endswith('.aura'):
        print("Error: Source file must have a .aura extension")
        sys.exit(1)

    base_filename = source_filename[:-5]
    c_filename = base_filename + '.c'
    executable_filename = base_filename

    print(f"[*] Compiling {source_filename}...")

    with open(source_filename, 'r') as f:
        source_code = f.read()

    # 1. Lexing
    lexer = Lexer(source_code)

    # 2. Parsing
    parser = Parser(lexer)

    # 3. Code Generation
    generator = CodeGenerator(parser)
    c_code = generator.generate()

    # 4. Write C code to file
    with open(c_filename, 'w') as f:
        f.write(c_code)
    print(f"[*] Generated C code: {c_filename}")

    # 5. Compile C code using GCC
    print(f"[*] Compiling C code with gcc...")
    try:
        compile_process = subprocess.run(
            ['gcc', c_filename, '-o', executable_filename],
            check=True,
            capture_output=True,
            text=True
        )
        print(f"[*] Successfully created executable: {executable_filename}")
    except subprocess.CalledProcessError as e:
        print(f"Error during C compilation:\n{e.stderr}")
        sys.exit(1)
    except FileNotFoundError:
        print("Error: 'gcc' command not found. Is GCC installed and in your PATH?")
        sys.exit(1)


if __name__ == '__main__':
    main()

第4部分:如何使用你的新语言

创建Aura源文件:创建一个名为 test.aura 的文件,并把下面的Aura代码粘贴进去:

// My first Aura program!
let a = 100;
let b = 50;

let result = (a - b) * 3; // (100-50)*3 = 150

print(result);
print(123); // Print a literal value

用你的编译器编译Aura代码:在终端中运行 compiler.py 脚本,并将 test.aura作为参数:

python3 compiler.py test.aura

观察编译过程:你会看到类似下面的输出:

[*] Compiling test.aura...
[*] Generated C code: test.c
[*] Compiling C code with gcc...
[*] Successfully created executable: test

你的编译器读取了 test.aura。

它生成了一个名为 test.c 的C语言文件。如果你打开它,会看到:

#include <stdio.h>

int main() {
    int a = 100;
    int b = 50;
    int result = ((a - b) * 3);
    printf("%d\n", result);
    printf("%d\n", 123);
    return 0;
}

脚本自动调用 gcc 编译了 test.c,并生成了一个名为 test 的可执行文件。

运行你的程序:现在,你可以直接运行这个新编译出的程序!

./test

输出:

150
123

恭喜!你已经成功创建了一门新的编程语言,并编写了它的编译器,最终生成了可以在你的操作系统上直接运行的程序!

第5部分:未来的方向(The Professional Path)

这个编译器是一个很棒的起点,但专业的语言还需要更多功能。以下是你的下一步:

更丰富的数据类型: 添加 float, string, bool。

控制流: 实现 if/else 语句和 while/for 循环。

用户自定义函数: 这是构建大型程序的关键。

错误处理: 提供更友好的错误信息,包括行号和列号。

语义分析: 在代码生成前检查错误,例如使用未声明的变量、类型不匹配等。

使用LLVM: 对于非常严肃的语言项目,可以跳过生成C代码的步骤,直接生成LLVM中间表示(IR)。LLVM是一个强大的编译器基础设施,能为你提供高级优化和对多种硬件架构(x86, ARM等)的支持。Python有 llvmlite 库可以帮助你完成这个任务。

这个项目为你打开了通往系统编程、编译器设计和计算机体系结构的大门。

网友回复

我知道答案,我要回答