创立一门新的编程语言是一个宏大但极其有价值的挑战,它能让你深入理解计算机工作的每一个层面。
我将为你设计一门全新的、简单的编程语言,并一步步教你如何为它编写一个编译器。
这个编译器将把我们的新语言代码转换成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 库可以帮助你完成这个任务。
这个项目为你打开了通往系统编程、编译器设计和计算机体系结构的大门。
网友回复