Pineのコンパイラ

※動作環境として、Linux または WSL (Windows Subsystem for Linux) を想定しています。また、アセンブラとして ナズム を使用するため、事前に sudo apt install nasm などでインストールしておいてください。

import sys
import re
import subprocess
import os

# ==========================================
# 1. 字句解析器 (Lexer)
# ソースコードをトークンのリストに分割します
# ==========================================
def tokenize(code):
    rules =[
        ('LET',   r'\blet\b'),
        ('PRINT', r'\bprint\b'),
        ('ID',    r'*'),
        ('NUM',   r'\d+'),
        ('ASSIGN',r'='),
        ('PLUS',  r'\+'),
        ('MINUS', r'-'),
        ('MUL',   r'\*'),
        ('DIV',   r'/'),
        ('LPAREN',r'\('),
        ('RPAREN',r'\)'),
        ('SEMI',  r';'),
        ('WS',    r'\s+'),
    ]
    tok_regex = '|'.join(f'(?P<{name}>{pattern})' for name, pattern in rules)
    tokens =[]
    
    for mo in re.finditer(tok_regex, code):
        kind = mo.lastgroup
        value = mo.group(kind)
        if kind == 'WS':
            continue
        tokens.append((kind, value))
    return tokens

# ==========================================
# 2. 構文解析器 (Parser)
# 再帰下降構文解析を行い、抽象構文木(AST)を構築します
# ==========================================
class Parser:
    def __init__(self, tokens):
        self.tokens = tokens
        self.pos = 0

    def current(self):
        return self.tokens if self.pos < len(self.tokens) else ('EOF', '')

    def consume(self, expected_kind):
        kind, val = self.current()
        if kind == expected_kind:
            self.pos += 1
            return val
        raise RuntimeError(f"構文エラー: '{expected_kind}' が必要ですが、'{val}' が見つかりました。")

    def parse_program(self):
        statements = []
        while self.current() != 'EOF':
            statements.append(self.parse_statement())
        return ('Program', statements)

    def parse_statement(self):
        kind, _ = self.current()
        if kind == 'LET':
            self.consume('LET')
            name = self.consume('ID')
            self.consume('ASSIGN')
            expr = self.parse_expr()
            self.consume('SEMI')
            return ('Let', name, expr)
        elif kind == 'PRINT':
            self.consume('PRINT')
            expr = self.parse_expr()
            self.consume('SEMI')
            return ('Print', expr)
        else:
            raise RuntimeError(f"構文エラー: 予期しないステートメントの開始 '{self.current()}'")

    def parse_expr(self):
        node = self.parse_term()
        while self.current() in ('PLUS', 'MINUS'):
            op = self.consume(self.current())
            right = self.parse_term()
            node = ('BinOp', op, node, right)
        return node

    def parse_term(self):
        node = self.parse_factor()
        while self.current() in ('MUL', 'DIV'):
            op = self.consume(self.current())
            right = self.parse_factor()
            node = ('BinOp', op, node, right)
        return node

    def parse_factor(self):
        kind, val = self.current()
        if kind == 'NUM':
            self.consume('NUM')
            return ('Num', int(val))
        elif kind == 'ID':
            self.consume('ID')
            return ('Var', val)
        elif kind == 'LPAREN':
            self.consume('LPAREN')
            node = self.parse_expr()
            self.consume('RPAREN')
            return node
        else:
            raise RuntimeError(f"構文エラー: 式の中に予期しないトークン '{val}'")

# ==========================================
# 3. コード生成器 (Code Generator)
# ASTをたどり、C言語などに頼らない純粋なx86-64アセンブリを出力します
# ==========================================
class CodeGen:
    def __init__(self):
        self.asm =[]
        self.vars = {}  # 変数名 -> スタックのオフセット(RBPからの相対位置)
        self.var_offset = 0

    def emit(self, code):
        self.asm.append(code)

    def generate(self, ast):
        # アセンブリのヘッダ (Linux用のシステムコールなど)
        self.emit("section .bss")
        self.emit("  digit_space resb 100")
        self.emit("  digit_space_pos resb 8")
        
        self.emit("section .text")
        self.emit("  global _start")
        
        # エントリポイント
        self.emit("_start:")
        self.emit("  push rbp")
        self.emit("  mov rbp, rsp")
        self.emit("  sub rsp, 256") # ローカル変数用のスタック領域を確保

        # プログラム本体の生成
        for stmt in ast:
            self.gen_statement(stmt)

        # 正常終了のシステムコール (sys_exit)
        self.emit("  mov rax, 60") # sys_exit
        self.emit("  mov rdi, 0")  # 終了コード 0
        self.emit("  syscall")

        # --- 標準出力 (print_int) のサブルーチン ---
        # OSのシステムコール(sys_write)を直接使って数値を文字列に変換し出力します
        self.emit("print_int:")
        self.emit("  mov rcx, digit_space")
        self.emit("  mov rbx, 10")
        self.emit("  mov, byte 10") # 改行コード(\n)
        self.emit("  inc rcx")
        self.emit("  mov, rcx")
        self.emit(".loop:")
        self.emit("  xor rdx, rdx")
        self.emit("  div rbx")
        self.emit("  add dl, 48") # 数値をASCII文字に変換
        self.emit("  mov rcx,")
        self.emit("  mov, dl")
        self.emit("  inc rcx")
        self.emit("  mov, rcx")
        self.emit("  test rax, rax")
        self.emit("  jnz .loop")
        self.emit(".print:")
        self.emit("  mov rcx,")
        self.emit("  mov rax, 1") # sys_write
        self.emit("  mov rdi, 1") # stdout
        self.emit("  mov rsi, rcx")
        self.emit("  mov rdx, 1") # 1バイトずつ出力
        self.emit("  syscall")
        self.emit("  mov rcx,")
        self.emit("  dec rcx")
        self.emit("  mov, rcx")
        self.emit("  cmp rcx, digit_space")
        self.emit("  jge .print")
        self.emit("  ret")

        return '\n'.join(self.asm)

    def gen_statement(self, node):
        if node == 'Let':
            name, expr = node, node
            self.gen_expr(expr) # 式を評価して RAX に結果を入れる
            if name not in self.vars:
                self.var_offset += 8
                self.vars = self.var_offset
            offset = self.vars
            self.emit(f"  mov, rax") # 変数をスタックに保存
            
        elif node == 'Print':
            self.gen_expr(node)
            self.emit("  call print_int") # RAX の値を出力する関数を呼ぶ

    def gen_expr(self, node):
        if node == 'Num':
            self.emit(f"  mov rax, {node}")
        elif node == 'Var':
            name = node
            if name not in self.vars:
                raise RuntimeError(f"コンパイルエラー: 未定義の変数 '{name}'")
            offset = self.vars
            self.emit(f"  mov rax,")
        elif node == 'BinOp':
            op = node
            self.gen_expr(node) # 左辺を評価
            self.emit("  push rax")
            self.gen_expr(node) # 右辺を評価
            self.emit("  mov rcx, rax")
            self.emit("  pop rax")
            if op == '+':
                self.emit("  add rax, rcx")
            elif op == '-':
                self.emit("  sub rax, rcx")
            elif op == '*':
                self.emit("  imul rax, rcx")
            elif op == '/':
                self.emit("  cqo")
                self.emit("  idiv rcx")

# ==========================================
# 4. コンパイラ・ドライバ
# ==========================================
def main():
    if len(sys.argv) != 2:
        print("使用法: python pine_compiler.py <file.pine>")
        sys.exit(1)

    with open(sys.argv, 'r') as f:
        code = f.read()

    print(" 字句解析・構文解析を行っています...")
    try:
        tokens = tokenize(code)
        parser = Parser(tokens)
        ast = parser.parse_program()
    except Exception as e:
        print(e)
        sys.exit(1)

    print(" x86-64 アセンブリを生成しています...")
    codegen = CodeGen()
    try:
        asm_code = codegen.generate(ast)
    except Exception as e:
        print(e)
        sys.exit(1)

    asm_file = "out.asm"
    obj_file = "out.o"
    exe_file = "out"

    with open(asm_file, 'w') as f:
        f.write(asm_code)

    print(" アセンブルしています (nasm)...")
    try:
        subprocess.run(, check=True)
    except FileNotFoundError:
        print("エラー: 'nasm' コマンドが見つかりません。 'sudo apt install nasm' でインストールしてください。")
        sys.exit(1)

    print(" リンクして実行ファイルを作成しています (ld)...")
    subprocess.run(, check=True)

    print(f"\n🎉 コンパイル成功! 実行ファイル '{exe_file}' が作成されました。")
    print(f"実行するには: ./{exe_file} と入力してください。")

if __name__ == '__main__':
    main()
  1. 完全なオリジナル言語: 構文規則(トークンの分解やAST木構造)を独自に定義しています。

  2. C言語ランタイム (libc) を使っていない: gcc に依存せず、OSの標準機能(Linuxの標準出力のシステムコールである sys_write や sys_exit )をアセンブリ言語で直接叩いています。これは、Go言語などが自前の実行ファイルを作るのと同じ(非常に低レイヤな)アプローチです。

  3. スタンドアロンの実行ファイル: 生成された 外 ファイルは、PythonもC言語も不要で、OS上でそのまま動く純粋なバイナリファイルです。

いいなと思ったら応援しよう!

コメント

コメントするには、 ログイン または 会員登録 をお願いします。
小ぶりなプログラムを試しに作っているんですが、 ここではその説明書きをしていこうと思います。 こういう機能をつけてみてほしいだとかいった要望があれば コメント欄に書いてみて下さい。 ひまをみて対応します。
Pineのコンパイラ|古井和雄
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1