develop.miniC のソースコード

#!/usr/bin/env python3
import sys
import re, sys, time


infile = "miniC.mnc"

if __name__ == "__main__":
    argv = sys.argv[1:]  # コマンドライン引数を取得
    nargv = len(argv)
    if nargv != 1:
        print("Usage: python miniC2.py <source_file>")
        sys.exit(1)
    if nargv == 1:
        infile = argv[0]  # 引数から入力ファイル名を取得


# NEW: ASTを綺麗に表示するための関数



# (トークン定義、Tokenクラス、tokenize関数は変更なし)
# ... [Previous code for TOKENS, Token class, tokenize function] ...
# ── トークン定義 ─────────────────────────────────────────
TOKENS = [
    ('COMMENT',  r'#.*'),
    ('NUMBER',   r'\d+(\.\d*)?'),
    ('STRING',   r'"(?:\\.|[^"])*"'),
    ('RETURN',   r'return'),
    ('BOOL',     r'true|false'),
    ('NULL',     r'null'),
    ('BREAK',    r'break'),
    ('CONTINUE', r'continue'),
    ('TYPE',     r'void|int|double|str|list|dict'),
    ('KEYWORD',  r'print|exit|for|if|elif|else|delete'),
    ('NAME',     r'[A-Za-z_]\w*'),
    ('OP',       r'&&|\|\||\+\+|\+=|==|!=|<=|>=|[+\-*/<>=&!]'),
    ('LPAREN',   r'\('), ('RPAREN', r'\)'),
    ('LBRACE',   r'\{'), ('RBRACE', r'\}'),
    ('LBRACK',   r'\['), ('RBRACK', r'\]'),
    ('COLON',    r':'), ('DOT', r'\.'),
    ('SEMI',     r';'), ('COMMA', r','),
    ('SKIP',     r'[ \t\r]+'),
    ('NEWLINE',  r'\n'),
]
TOK_RE = re.compile('|'.join(f'(?P<{n}>{p})' for n,p in TOKENS))

[ドキュメント] class Token: def __init__(self, typ, val, line, col): self.type, self.value, self.line, self.col = typ, val, line, col def __repr__(self): return f"Token({self.type!r},{self.value!r}, L{self.line}:{self.col})"
[ドキュメント] def tokenize(code): line_num, line_start = 1, 0 for mo in TOK_RE.finditer(code): typ = mo.lastgroup; col_num = mo.start() - line_start if typ == 'NEWLINE': line_start = mo.end(); line_num += 1; continue if typ in ('SKIP', 'COMMENT'): continue yield Token(typ, mo.group(typ), line_num, col_num) yield Token('EOF','', line_num, 0)
# (ASTノード、パーサは変更なし) # ... [Previous code for AST nodes and Parser class] ... # ── AST ノード ───────────────────────────────────
[ドキュメント] class AST: pass
[ドキュメント] class Number(AST): def __init__(self,v): self.v = float(v) def __repr__(self): return f"Number({self.v})"
[ドキュメント] class String(AST): def __init__(self,s): self.s = s[1:-1] def __repr__(self): return f"String({self.s!r})"
[ドキュメント] class Boolean(AST): def __init__(self, v): self.v = (v == 'true') def __repr__(self): return f"Boolean({self.v})"
[ドキュメント] class Null(AST): def __repr__(self): return "Null()"
[ドキュメント] class Var(AST): def __init__(self,n): self.n = n def __repr__(self): return f"Var({self.n!r})"
[ドキュメント] class BinOp(AST): def __init__(self,op,l,r): self.op, self.l, self.r = op,l,r def __repr__(self): return f"BinOp({self.op!r}, {self.l!r}, {self.r!r})"
[ドキュメント] class UnaryOp(AST): def __init__(self,op,e): self.op, self.e = op,e def __repr__(self): return f"UnaryOp({self.op!r}, {self.e!r})"
[ドキュメント] class ListLiteral(AST): def __init__(self, elements): self.elements = elements def __repr__(self): return f"ListLiteral({self.elements!r})"
[ドキュメント] class DictLiteral(AST): def __init__(self, pairs): self.pairs = pairs def __repr__(self): return f"DictLiteral({self.pairs!r})"
[ドキュメント] class IndexAccess(AST): def __init__(self, collection, index): self.collection, self.index = collection, index def __repr__(self): return f"IndexAccess({self.collection!r}, {self.index!r})"
[ドキュメント] class Assign(AST): def __init__(self,name,expr): self.name,self.expr = name,expr def __repr__(self): return f"Assign({self.name!r}, {self.expr!r})"
[ドキュメント] class AddAssign(AST): def __init__(self,name,expr): self.name,self.expr = name,expr def __repr__(self): return f"AddAssign({self.name!r}, {self.expr!r})"
[ドキュメント] class Inc(AST): def __init__(self,name,inc): self.name,self.inc= name,inc def __repr__(self): return f"Inc({self.name!r}, {self.inc!r})"
[ドキュメント] class Print(AST): def __init__(self, args): self.args = args def __repr__(self): return f"Print({self.args!r})"
[ドキュメント] class Exit(AST): def __repr__(self): return "Exit()"
[ドキュメント] class Return(AST): def __init__(self, expr): self.expr = expr def __repr__(self): return f"Return({self.expr!r})"
[ドキュメント] class Break(AST): def __repr__(self): return "Break()"
[ドキュメント] class Continue(AST): def __repr__(self): return "Continue()"
[ドキュメント] class Delete(AST): def __init__(self, target): self.target = target def __repr__(self): return f"Delete({self.target!r})"
[ドキュメント] class Block(AST): def __init__(self,stmts): self.stmts = stmts def __repr__(self): return f"Block({self.stmts!r})"
[ドキュメント] class If(AST): def __init__(self, cond, then_blk, elifs, else_blk): self.cond, self.then_blk, self.elifs, self.else_blk = cond,then_blk,elifs,else_blk def __repr__(self): return f"If({self.cond!r}, {self.then_blk!r}, {self.elifs!r}, {self.else_blk!r})"
[ドキュメント] class For(AST): def __init__(self, init, cond, inc, body): self.init, self.cond, self.inc, self.body = init,cond,inc,body def __repr__(self): return f"For({self.init!r}, {self.cond!r}, {self.inc!r}, {self.body!r})"
[ドキュメント] class FuncDef(AST): def __init__(self,return_type, name,params,body): self.return_type, self.name,self.params,self.body = return_type, name,params,body def __repr__(self): return f"FuncDef({self.return_type!r}, {self.name!r}, {self.params!r}, {self.body!r})"
[ドキュメント] class FuncCall(AST): def __init__(self,name,args): self.name,self.args = name,args def __repr__(self): return f"FuncCall({self.name!r}, {self.args!r})"
# ── 再帰下降パーサ ─────────────────────────────────────
[ドキュメント] class Parser: def __init__(self, tokens): self.tokens = iter(tokens); self._advance() def _advance(self): self.tok = next(self.tokens) def _eat(self, typ): if self.tok.type == typ: val = self.tok.value; self._advance(); return val raise SyntaxError(f"L{self.tok.line}:{self.tok.col}: Expected {typ}, got {self.tok.type}")
[ドキュメント] def parse(self): stmts = []; while self.tok.type != 'EOF': stmts.append(self.statement()) return Block(stmts)
[ドキュメント] def parse_simple_statement_nosemi(self): name = self._eat('NAME') if self.tok.type == 'OP' and self.tok.value == '=': self._advance(); expr = self.expr(); return Assign(Var(name), expr) if self.tok.type == 'OP' and self.tok.value in ('++', '--'): op_val = self.tok.value; self._advance(); return Inc(name, +1 if op_val == '++' else -1) raise SyntaxError(f"L{self.tok.line}:{self.col}: Expected assignment or increment in for-loop clause")
[ドキュメント] def statement(self): if self.tok.type == 'KEYWORD': kw = self.tok.value if kw == 'print': self._advance(); self._eat('LPAREN'); args = self.arguments(); self._eat('RPAREN'); self._eat('SEMI'); return Print(args) if kw == 'exit': self._advance(); self._eat('SEMI'); return Exit() if kw == 'for': self._advance(); self._eat('LPAREN'); init = self.parse_simple_statement_nosemi() if self.tok.type != 'SEMI' else None; self._eat('SEMI'); cond = self.expr() if self.tok.type != 'SEMI' else None; self._eat('SEMI'); inc = self.parse_simple_statement_nosemi() if self.tok.type != 'RPAREN' else None; self._eat('RPAREN'); body = self.block(); return For(init, cond, inc, body) if kw == 'if': self._advance(); self._eat('LPAREN'); cond = self.expr(); self._eat('RPAREN'); then_blk = self.block(); elifs, else_blk = [], None while self.tok.type=='KEYWORD' and self.tok.value=='elif': self._advance(); self._eat('LPAREN'); elif_cond = self.expr(); self._eat('RPAREN'); elif_blk = self.block(); elifs.append((elif_cond, elif_blk)) if self.tok.type=='KEYWORD' and self.tok.value=='else': self._advance(); else_blk = self.block() return If(cond, then_blk, elifs, else_blk) if kw == 'delete': self._advance(); target = self.postfix(); self._eat('SEMI'); return Delete(target) if self.tok.type == 'BREAK': self._advance(); self._eat('SEMI'); return Break() if self.tok.type == 'CONTINUE': self._advance(); self._eat('SEMI'); return Continue() if self.tok.type == 'RETURN': self._advance(); expr = self.expr() if self.tok.type != 'SEMI' else None; self._eat('SEMI'); return Return(expr) if self.tok.type == 'TYPE': return_type = self._eat('TYPE') name = self._eat('NAME') self._eat('LPAREN'); params = []; if self.tok.type in ('TYPE', 'NAME'): if self.tok.type == 'TYPE': self._advance() params.append(self._eat('NAME')) while self.tok.type=='COMMA': self._advance(); params.append(self._eat('NAME')) self._eat('RPAREN') body = self.block() return FuncDef(return_type, name, params, body) node = self.postfix() if self.tok.type == 'OP' and self.tok.value == '=': self._advance(); expr = self.expr(); self._eat('SEMI'); return Assign(node, expr) if isinstance(node, FuncCall): self._eat('SEMI'); return node if isinstance(node, Var): name = node.n if self.tok.type == 'OP' and self.tok.value == '+=': self._advance(); expr=self.expr(); self._eat('SEMI'); return AddAssign(name, expr) if self.tok.type == 'OP' and self.tok.value in ('++', '--'): op_val = self.tok.value; self._advance(); self._eat('SEMI'); return Inc(name, +1 if op_val == '++' else -1) raise SyntaxError(f"L{self.tok.line}:{self.tok.col}: Invalid statement {self.tok}")
[ドキュメント] def block(self): self._eat('LBRACE'); stmts = [] while self.tok.type != 'RBRACE': stmts.append(self.statement()) self._eat('RBRACE'); return Block(stmts)
[ドキュメント] def arguments(self): args = [] if self.tok.type != 'RPAREN': args.append(self.expr()) while self.tok.type == 'COMMA': self._advance(); args.append(self.expr()) return args
[ドキュメント] def expr(self): return self._binop(self.logic_and, ['||'])
[ドキュメント] def logic_and(self): return self._binop(self.equality, ['&&'])
[ドキュメント] def equality(self): return self._binop(self.comparison, ['==', '!='])
[ドキュメント] def comparison(self):return self._binop(self.concat, ['<', '>', '<=', '>='])
[ドキュメント] def concat(self): return self._binop(self.term, ['&'])
[ドキュメント] def term(self): return self._binop(self.factor, ['+','-'])
[ドキュメント] def factor(self): return self._binop(self.unary, ['*','/'])
def _binop(self, sub, ops): left = sub(); while self.tok.type=='OP' and self.tok.value in ops: op = self.tok.value; self._advance(); right = sub(); left = BinOp(op,left,right) return left
[ドキュメント] def unary(self): if self.tok.type=='OP' and self.tok.value in ('-', '!'): op = self.tok.value; self._advance(); return UnaryOp(op, self.unary()) return self.postfix()
[ドキュメント] def postfix(self): node = self.atom() while self.tok.type in ('LPAREN', 'LBRACK', 'DOT'): if self.tok.type == 'LPAREN': self._advance(); args = self.arguments(); self._eat('RPAREN'); node = FuncCall(node, args) elif self.tok.type == 'LBRACK': self._advance(); index_expr = self.expr(); self._eat('RBRACK'); node = IndexAccess(node, index_expr) elif self.tok.type == 'DOT': self._advance(); method_name = self._eat('NAME'); self._eat('LPAREN'); args = self.arguments(); self._eat('RPAREN'); node = FuncCall(Var(method_name), [node] + args) return node
[ドキュメント] def atom(self): tok_type = self.tok.type tok_val = self.tok.value if tok_type == 'BOOL': self._advance(); return Boolean(tok_val) if tok_type == 'NULL': self._advance(); return Null() if tok_type in ('NAME', 'KEYWORD', 'TYPE'): self._advance(); return Var(tok_val) if tok_type=='NUMBER': self._advance(); return Number(tok_val) if tok_type=='STRING': self._advance(); return String(tok_val) if tok_type=='LPAREN': self._advance(); e = self.expr(); self._eat('RPAREN'); return e if tok_type == 'LBRACK': self._advance(); elements = self.arguments(); self._eat('RBRACK'); return ListLiteral(elements) if tok_type == 'LBRACE': self._advance(); pairs = [] if self.tok.type != 'RBRACE': while True: key = self.expr(); self._eat('COLON'); val = self.expr(); pairs.append((key, val)) if self.tok.type != 'COMMA': break self._advance() self._eat('RBRACE'); return DictLiteral(pairs) raise SyntaxError(f"L{self.tok.line}:{self.tok.col}: Bad atom: {self.tok}")
[ドキュメント] class ReturnValue(Exception): def __init__(self, value): self.value = value
[ドキュメント] class BreakLoop(Exception): pass
[ドキュメント] class ContinueLoop(Exception): pass
# ── インタプリタ ─────────────────────────────────────
[ドキュメント] class Interpreter: def __init__(self, ast: Block): self.ast = ast # NEW: ファイルハンドル管理用の辞書とIDを追加 self.file_handles = {} self.next_handle_id = 0 self.builtins = { 'time': self._builtin_time, 'len': self._builtin_len, 'append': self._builtin_append, 'insert': self._builtin_insert, # NEW: ファイル操作用の組み込み関数を追加 'open': self._builtin_open, 'read': self._builtin_read, 'write': self._builtin_write, 'close': self._builtin_close, # 追加テスト 'add': self._builtin_add, } self.scopes = [self.builtins.copy()] # NEW: ファイル操作用メソッド def _get_file_handle(self, handle_id): handle = self.file_handles.get(int(handle_id)) if handle is None: raise ValueError(f"Invalid file handle: {handle_id}") return handle def _builtin_open(self, args): if len(args) != 2: raise TypeError("open() takes 2 arguments (filepath, mode)") path, mode = str(args[0]), str(args[1]) try: file_obj = open(path, mode) handle_id = self.next_handle_id self.file_handles[handle_id] = file_obj self.next_handle_id += 1 return float(handle_id) # スクリプト側には数値としてハンドルを返す except Exception as e: raise IOError(f"Could not open file '{path}': {e}") from e def _builtin_read(self, args): if len(args) != 1: raise TypeError("read() takes 1 argument (handle)") file_obj = self._get_file_handle(args[0]) return file_obj.read() def _builtin_write(self, args): if len(args) != 2: raise TypeError("write() takes 2 arguments (handle, data)") file_obj = self._get_file_handle(args[0]) return file_obj.write(str(args[1])) def _builtin_close(self, args): if len(args) != 1: raise TypeError("close() takes 1 argument (handle)") handle_id = int(args[0]) file_obj = self._get_file_handle(handle_id) file_obj.close() del self.file_handles[handle_id] # ハンドル管理辞書から削除 return None def _builtin_add(self, args): if len(args) != 2: raise TypeError("add() takes 2 arguments (val1, val2)") return float(args[0]) + float(args[1]) # (以降の組み込み関数、環境管理メソッドは変更なし) # ... [Previous code for other builtins and env methods] ... def _builtin_time(self, args): if args: raise TypeError("time() takes no arguments"); return time.time() def _builtin_len(self, args): if len(args) != 1: raise TypeError("len() takes exactly one argument"); arg = args[0] if isinstance(arg, (str, list, dict)): return float(len(arg)) raise TypeError(f"Object of type {type(arg).__name__} has no len()") def _builtin_append(self, args): if len(args) != 2: raise TypeError("append() takes 2 arguments (list, value)"); if not isinstance(args[0], list): raise TypeError("First argument to append must be a list") args[0].append(args[1]); return None def _builtin_insert(self, args): if len(args) != 3: raise TypeError("insert() takes 3 arguments (list, index, value)"); if not isinstance(args[0], list): raise TypeError("First argument to insert must be a list") args[0].insert(int(args[1]), args[2]); return None def _env_assign(self, name, value): self.scopes[-1][name] = value def _env_lookup(self, name): for scope in reversed(self.scopes): if name in scope: return scope[name] raise NameError(f"Name '{name}' is not defined.") def _env_update(self, name, value): for scope in reversed(self.scopes): if name in scope: scope[name] = value; return raise NameError(f"Name '{name}' is not defined for update.")
[ドキュメント] def run(self): try: self._exec_block(self.ast) except (ReturnValue, BreakLoop, ContinueLoop) as e: print(f"Error: '{type(e).__name__}' can only be used inside a function or loop.", file=sys.stderr) finally: # NEW: プログラム終了時に開いているファイルをすべて閉じる for handle_id in list(self.file_handles.keys()): self._builtin_close([handle_id])
# (_exec_block, _exec, _eval, _to_bool, to_strは変更なし) # ... [Previous code for _exec_block, _exec, _eval, etc.] ... def _exec_block(self, block: Block): for stmt in block.stmts: self._exec(stmt) def _exec(self, node): t = type(node) if t is Return: raise ReturnValue(self._eval(node.expr) if node.expr else None) if t is Break: raise BreakLoop() if t is Continue: raise ContinueLoop() if t is Assign: value = self._eval(node.expr) assign_to = node.name if isinstance(assign_to, Var): self._env_assign(assign_to.n, value) elif isinstance(assign_to, IndexAccess): collection = self._eval(assign_to.collection) index = self._eval(assign_to.index) if isinstance(collection, list): collection[int(index)] = value else: collection[index] = value # dict else: raise SyntaxError("LHS of assignment must be a variable or index access.") elif t is AddAssign: new_val = self._env_lookup(node.name) + self._eval(node.expr); self._env_update(node.name, new_val) elif t is Inc: self._env_update(node.name, self._env_lookup(node.name) + node.inc) elif t is Print: print(*(self.to_str(self._eval(a)) for a in node.args)) elif t is Exit: sys.exit(0) elif t is Delete: target = node.target if isinstance(target, IndexAccess): collection = self._eval(target.collection); index = self._eval(target.index) del collection[int(index) if isinstance(collection, list) else index] else: raise TypeError("delete target must be an index or key access") elif t is Block: self._exec_block(node) elif t is If: if self._to_bool(self._eval(node.cond)): self._exec_block(node.then_blk) else: executed_elif = False for elif_cond, elif_blk in node.elifs: if self._to_bool(self._eval(elif_cond)): self._exec_block(elif_blk); executed_elif = True; break if not executed_elif and node.else_blk: self._exec_block(node.else_blk) elif t is For: self.scopes.append({}) try: if node.init: self._exec(node.init) while not node.cond or self._to_bool(self._eval(node.cond)): try: self._exec_block(node.body) except ContinueLoop: if node.inc: self._exec(node.inc) continue except BreakLoop: break if node.inc: self._exec(node.inc) finally: self.scopes.pop() elif t is FuncDef: func_info = {'type': 'user_function', 'params': node.params, 'body': node.body, 'return_type': node.return_type} self._env_assign(node.name, func_info) elif t is FuncCall: self._eval(node) else: raise RuntimeError(f"Unknown exec node {type(node)}") def _eval(self, node): t = type(node) if t is Number: return node.v if t is String: return node.s if t is Boolean: return node.v if t is Null: return None if t is Var: return self._env_lookup(node.n) if t is ListLiteral: return [self._eval(e) for e in node.elements] if t is DictLiteral: return {self._eval(k): self._eval(v) for k, v in node.pairs} if t is IndexAccess: collection = self._eval(node.collection); index = self._eval(node.index) if isinstance(collection, (list, str)): return collection[int(index)] return collection[index] if t is FuncCall: func_obj_node = node.name if isinstance(func_obj_node, Var) and func_obj_node.n in self.builtins: func_obj = self.builtins[func_obj_node.n] evaluated_args = [self._eval(arg) for arg in node.args] return func_obj(evaluated_args) func_obj = self._eval(func_obj_node) evaluated_args = [self._eval(arg) for arg in node.args] if isinstance(func_obj, dict) and func_obj.get('type') == 'user_function': params, body = func_obj['params'], func_obj['body'] if len(params) != len(evaluated_args): raise TypeError(f"Function expects {len(params)} args, got {len(evaluated_args)}") new_scope = dict(zip(params, evaluated_args)); self.scopes.append(new_scope) return_value = None try: self._exec_block(body) except ReturnValue as e: return_value = e.value finally: self.scopes.pop() return return_value else: raise TypeError(f"Object '{func_obj_node.n if isinstance(func_obj_node, Var) else func_obj}' is not a function") if t is UnaryOp: op = node.op; val = self._eval(node.e) if op == '-': return -val if op == '!': return not self._to_bool(val) if t is BinOp: op = node.op if op == '&&': return self._to_bool(self._eval(node.l)) and self._to_bool(self._eval(node.r)) if op == '||': return self._to_bool(self._eval(node.l)) or self._to_bool(self._eval(node.r)) l, r = self._eval(node.l), self._eval(node.r) if op == '&': return str(l) + str(r) if op == '+': return l + r if op == '-': return l - r if op == '*': return l * r if op == '/': if r == 0: raise ZeroDivisionError("division by zero in script"); return l / r if op == '>': return l > r if op == '<': return l < r if op == '==': return l == r if op == '!=': return l != r if op == '<=': return l <= r if op == '>=': return l >= r raise RuntimeError(f"Unknown expr {node}") def _to_bool(self, value): if isinstance(value, bool): return value if value is None: return False if isinstance(value, (int, float)): return value != 0 return bool(value)
[ドキュメント] def to_str(self, value): if value is None: return 'null' if isinstance(value, bool): return 'true' if value else 'false' return str(value)
# ── 動作確認用コード ───────────────────────────────── if __name__ == "__main__": print() print(f"infile: {infile}") with open(infile, 'r', encoding='utf-8') as f: source = f.read() tokens = tokenize(source) ast = Parser(tokens).parse() print("ast:", ast) print_ast(ast) print() print("run:") Interpreter(ast).run()