import importlib
import sys
import builtins as py_builtins
import asyncio
import inspect
import re
import os
import time
from pathlib import Path
from renzmc.core.token import TokenType

from renzmc.core.type_system import TypeChecker, TypeValidator, BaseType
from renzmc.core.type_integration import TypeIntegrationMixin

try:
    import numba
    from renzmc.jit import JITCompiler
    JIT_AVAILABLE = True
except ImportError:
    JIT_AVAILABLE = False
    JITCompiler = None
from renzmc.core.ast import (
    AST,
    Program,
    Block,
    BinOp,
    UnaryOp,
    Num,
    String,
    Boolean,
    List,
    Dict,
    Set,
    Tuple,
    DictComp,
    Var,
    VarDecl,
    Assign,
    MultiVarDecl,
    MultiAssign,
    NoOp,
    Print,
    Input,
    If,
    While,
    For,
    ForEach,
    Break,
    Continue,
    FuncDecl,
    FuncCall,
    Return,
    ClassDecl,
    MethodDecl,
    Constructor,
    AttributeRef,
    MethodCall,
    Import,
    PythonImport,
    PythonCall,
    TryCatch,
    Raise,
    IndexAccess,
    SliceAccess,
    SelfVar,
    Lambda,
    ListComp,
    SetComp,
    Generator,
    Yield,
    YieldFrom,
    Decorator,
    AsyncFuncDecl,
    AsyncMethodDecl,
    Await,
    TypeHint,
    FormatString,
    Ternary,
    Unpacking,
    WalrusOperator,
    CompoundAssign,
    Switch,
    Case,
    With,
)
from renzmc.core.error import (
    RenzmcError,
    LexerError,
    ParserError,
    InterpreterError,
    RenzmcNameError,
    RenzmcTypeError,
    RenzmcValueError,
    RenzmcImportError,
    RenzmcAttributeError,
    RenzmcIndexError,
    RenzmcKeyError,
    RenzmcRuntimeError,
    DivisionByZeroError,
    FileError,
    PythonIntegrationError,
    RenzmcSyntaxError,
    TypeHintError,
    AsyncError,
)

NameError = RenzmcNameError
TypeError = RenzmcTypeError
ValueError = RenzmcValueError
ImportError = RenzmcImportError
AttributeError = RenzmcAttributeError
IndexError = RenzmcIndexError
KeyError = RenzmcKeyError
RuntimeError = RenzmcRuntimeError
SyntaxError = RenzmcSyntaxError
from renzmc.core.base_visitor import NodeVisitor
from renzmc.runtime.builtin_manager import BuiltinManager
from renzmc.runtime.scope_manager import ScopeManager
from renzmc.runtime.python_integration import PythonIntegration
from renzmc.runtime.file_operations import FileOperations
from renzmc.runtime.crypto_operations import CryptoOperations
from renzmc.runtime.renzmc_module_system import RenzmcModuleManager
from renzmc.runtime.advanced_features import (
    AdvancedFeatureManager,
    timing_decorator,
    retry_decorator,
    cache_decorator,
    simple_retry_decorator,
    universal_retry_decorator,
)
import renzmc.builtins as renzmc_builtins

try:
    from cryptography.fernet import Fernet
    from cryptography.hazmat.primitives import hashes
    from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

    CRYPTOGRAPHY_AVAILABLE = True
except ImportError:
    Fernet = None
    hashes = None
    PBKDF2HMAC = None
    CRYPTOGRAPHY_AVAILABLE = False


class Interpreter(NodeVisitor, TypeIntegrationMixin):

    def __init__(self):
        self.safe_mode = True
        self.scope_manager = ScopeManager()
        self.python_integration = PythonIntegration()
        self.file_ops = FileOperations()
        self.crypto_ops = CryptoOperations()
        self.module_manager = RenzmcModuleManager(self)
        self.advanced_features = AdvancedFeatureManager()
        self.advanced_features.create_decorator("waktu", timing_decorator)
        self.advanced_features.create_decorator("cache", cache_decorator)
        self.advanced_features.create_decorator("coba_ulang", universal_retry_decorator)
        from renzmc.runtime.advanced_features import (
            create_custom_decorator,
            web_route_decorator,
            clear_cache,
            get_cache_stats,
            jit_compile_decorator,
            jit_force_decorator,
            parallel_decorator,
            gpu_decorator,
            profile_decorator,
        )
        self.advanced_features.create_decorator("jit_compile", jit_compile_decorator)
        self.advanced_features.create_decorator("jit_force", jit_force_decorator)
        self.advanced_features.create_decorator("parallel", parallel_decorator)
        self.advanced_features.create_decorator("gpu", gpu_decorator)
        self.advanced_features.create_decorator("profile", profile_decorator)

        self._init_type_system(strict_mode=False)

        self.jit_call_counts = {}
        self.jit_execution_times = {}
        self.jit_compiled_functions = {}
        self.jit_threshold = 10

        if JIT_AVAILABLE and JITCompiler:
            self.jit_compiler = JITCompiler()
        else:
            self.jit_compiler = None

        self.builtin_functions = BuiltinManager.setup_builtin_functions()
        self.builtin_functions.update(
            {
                "buat_decorator_kustom": create_custom_decorator,
                "route": web_route_decorator,
                "bersihkan_cache": clear_cache,
                "info_cache": get_cache_stats,
                "jit_compile": jit_compile_decorator,
                "jit_force": jit_force_decorator,
                "parallel": parallel_decorator,
                "gpu": gpu_decorator,
                "profile": profile_decorator,
            }
        )
        self.return_value = None
        self.break_flag = False
        self.continue_flag = False
        self.scope_manager.builtin_functions = self.builtin_functions
        self.builtin_functions.update(
            {
                "impor_python": self._import_python_module,
                "panggil_python": self._call_python_function,
                "impor_dari_python": self._from_python_import,
                "buat_objek_python": self._create_python_object,
                "daftar_atribut_python": self._list_python_attributes,
                "bantuan_python": self._python_help,
                "instal_paket_python": self._install_python_package,
                "impor_otomatis": self._auto_import_python,
                "konversi_ke_python": self._convert_to_python,
                "konversi_dari_python": self._convert_from_python,
                "bungkus_pintar": self._create_smart_wrapper,
                "cek_modul_tersedia": self._check_module_available,
                "getattr": py_builtins.getattr,
                "setattr": py_builtins.setattr,
                "hasattr": py_builtins.hasattr,
                "dir": py_builtins.dir,
                "isinstance": py_builtins.isinstance,
                "callable": py_builtins.callable,
                "len": py_builtins.len,
                "ambil_atribut": self._smart_getattr,
                "atur_atribut": self._smart_setattr,
                "cek_atribut": self._smart_hasattr,
                "impor_renzmc": self._import_renzmc_module,
                "impor_dari_renzmc": self._import_from_renzmc_module,
                "impor_semua_dari_renzmc": self._import_all_from_renzmc_module,
                "muat_ulang_modul": self._reload_renzmc_module,
                "daftar_modul_renzmc": self._list_renzmc_modules,
                "info_modul_renzmc": self._get_renzmc_module_info,
                "tambah_jalur_modul": self._add_module_search_path,
                "buat_decorator": self._create_decorator,
                "terapkan_decorator": self._apply_decorator,
                "buat_context_manager": self._create_context_manager,
                "gunakan_context": self._use_context_manager,
                "buat_generator_lanjutan": self._create_advanced_generator,
                "buat_async_function": self._create_async_function,
                "daftar_fitur_lanjutan": self._list_advanced_features,
            }
        )
        self.builtin_functions.update(
            {
                "atur_mode_aman": self._set_safe_mode,
                "cek_mode_aman": self._check_safe_mode,
            }
        )
        self.scope_manager.builtin_functions = self.builtin_functions
        self._setup_python_builtins()
        self._setup_compatibility_adapters()
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)

    def _call_magic_method(self, obj, method_name, *args):
        if hasattr(obj, method_name):
            method = getattr(obj, method_name)
            if callable(method):
                try:
                    result = method(*args)
                    if result is not NotImplemented:
                        return result
                except (TypeError, ValueError, AttributeError) as e:
                    from renzmc.utils.logging import logger

                    logger.debug(f"Magic method {method_name} failed: {e}")
                except Exception as e:
                    from renzmc.utils.logging import logger

                    logger.error(
                        f"Unexpected error in magic method {method_name}: {e}",
                        exc_info=True,
                    )
                    raise
        return NotImplemented

    def _set_safe_mode(self, enabled=True):
        self.safe_mode = enabled
        mode_text = "diaktifkan" if enabled else "dinonaktifkan"
        print(f"🔒 Mode aman {mode_text}")
        return enabled

    def _check_safe_mode(self):
        return self.safe_mode

    def _setup_compatibility_adapters(self):
        pass

    @property
    def global_scope(self):
        return self.scope_manager.global_scope

    @property
    def local_scope(self):
        return self.scope_manager.local_scope

    @local_scope.setter
    def local_scope(self, value):
        self.scope_manager.local_scope = value

    @property
    def functions(self):
        return self.scope_manager.functions

    @functions.setter
    def functions(self, value):
        self.scope_manager.functions = value

    @property
    def classes(self):
        return self.scope_manager.classes

    @classes.setter
    def classes(self, value):
        self.scope_manager.classes = value

    @property
    def modules(self):
        return self.scope_manager.modules

    @modules.setter
    def modules(self, value):
        self.scope_manager.modules = value

    @property
    def current_instance(self):
        return self.scope_manager.current_instance

    @current_instance.setter
    def current_instance(self, value):
        self.scope_manager.current_instance = value

    @property
    def instance_scopes(self):
        return self.scope_manager.instance_scopes

    @property
    def generators(self):
        return self.scope_manager.generators

    @property
    def async_functions(self):
        return self.scope_manager.async_functions

    @property
    def decorators(self):
        return self.scope_manager.decorators

    @property
    def type_registry(self):
        return self.scope_manager.type_registry

    def _setup_builtin_functions(self):
        self.builtin_functions = {
            "panjang": renzmc_builtins.panjang,
            "jenis": renzmc_builtins.jenis,
            "ke_teks": renzmc_builtins.ke_teks,
            "ke_angka": renzmc_builtins.ke_angka,
            "huruf_besar": renzmc_builtins.huruf_besar,
            "huruf_kecil": renzmc_builtins.huruf_kecil,
            "potong": renzmc_builtins.potong,
            "gabung": renzmc_builtins.gabung,
            "pisah": renzmc_builtins.pisah,
            "ganti": renzmc_builtins.ganti,
            "mulai_dengan": renzmc_builtins.mulai_dengan,
            "akhir_dengan": renzmc_builtins.akhir_dengan,
            "berisi": renzmc_builtins.berisi,
            "hapus_spasi": renzmc_builtins.hapus_spasi,
            "bulat": renzmc_builtins.bulat,
            "desimal": renzmc_builtins.desimal,
            "akar": renzmc_builtins.akar,
            "pangkat": renzmc_builtins.pangkat,
            "absolut": renzmc_builtins.absolut,
            "pembulatan": renzmc_builtins.pembulatan,
            "pembulatan_atas": renzmc_builtins.pembulatan_atas,
            "pembulatan_bawah": renzmc_builtins.pembulatan_bawah,
            "sinus": renzmc_builtins.sinus,
            "cosinus": renzmc_builtins.cosinus,
            "tangen": renzmc_builtins.tangen,
            "tambah": renzmc_builtins.tambah,
            "hapus": renzmc_builtins.hapus,
            "hapus_pada": renzmc_builtins.hapus_pada,
            "masukkan": renzmc_builtins.masukkan,
            "urutkan": renzmc_builtins.urutkan,
            "balikkan": renzmc_builtins.balikkan,
            "hitung": renzmc_builtins.hitung,
            "indeks": renzmc_builtins.indeks,
            "extend": renzmc_builtins.extend,
            "gabung_daftar": renzmc_builtins.extend,
            "zip": renzmc_builtins.zip,
            "enumerate": renzmc_builtins.enumerate,
            "filter": renzmc_builtins.filter,
            "saring": renzmc_builtins.saring,
            "map": renzmc_builtins.map,
            "peta": renzmc_builtins.peta,
            "reduce": renzmc_builtins.reduce,
            "kurangi": renzmc_builtins.kurangi,
            "all": renzmc_builtins.all,
            "semua": renzmc_builtins.semua,
            "any": renzmc_builtins.any,
            "ada": renzmc_builtins.ada,
            "sorted": renzmc_builtins.sorted,
            "terurut": renzmc_builtins.terurut,
            "kunci": renzmc_builtins.kunci,
            "nilai": renzmc_builtins.nilai,
            "item": renzmc_builtins.item,
            "hapus_kunci": renzmc_builtins.hapus_kunci,
            "acak": renzmc_builtins.acak,
            "waktu": renzmc_builtins.waktu,
            "tidur": renzmc_builtins.tidur,
            "tanggal": renzmc_builtins.tanggal,
            "baca_file": renzmc_builtins.baca_file,
            "tulis_file": renzmc_builtins.tulis_file,
            "tambah_file": renzmc_builtins.tambah_file,
            "hapus_file": renzmc_builtins.hapus_file,
            "jalankan_perintah": renzmc_builtins.jalankan_perintah,
            "atur_sandbox": renzmc_builtins.atur_sandbox,
            "tambah_perintah_aman": renzmc_builtins.tambah_perintah_aman,
            "hapus_perintah_aman": renzmc_builtins.hapus_perintah_aman,
            "impor_python": self._import_python_module,
            "panggil_python": self._call_python_function,
            "impor_dari_python": self._from_python_import,
            "buat_objek_python": self._create_python_object,
            "daftar_atribut_python": self._list_python_attributes,
            "bantuan_python": self._python_help,
            "instal_paket_python": self._install_python_package,
            "buat_generator": self._create_generator,
            "buat_async": self._create_async_function,
            "jalankan_async": self._run_async_function,
            "tunggu_semua": self._wait_all_async,
            "daftar_ke_generator": self._list_to_generator,
            "cek_tipe": self._check_type,
            "format_teks": self._format_string,
            "buka_file": self._open_file,
            "tutup_file": self._close_file,
            "baca_baris": self._read_line,
            "baca_semua_baris": self._read_all_lines,
            "tulis_baris": self._write_line,
            "flush_file": self._flush_file,
            "cek_file_ada": self._file_exists,
            "buat_direktori": self._make_directory,
            "hapus_direktori": self._remove_directory,
            "daftar_direktori": self._list_directory,
            "gabung_path": self._join_path,
            "path_file": self._file_path,
            "path_direktori": self._directory_path,
            "ukuran_file": self._file_size,
            "waktu_modifikasi": self._file_modification_time,
            "json_ke_teks": self._json_to_text,
            "teks_ke_json": self._text_to_json,
            "enkripsi": self._encrypt,
            "dekripsi": self._decrypt,
            "hash_teks": self._hash_text,
            "buat_uuid": self._create_uuid,
            "url_encode": self._url_encode,
            "url_decode": self._url_decode,
            "http_request": self._http_request,
            "http_get": self._http_get,
            "http_post": self._http_post,
            "http_put": self._http_put,
            "http_delete": self._http_delete,
            "atur_static": self._set_static,
            "template_html": self._render_template,
            "buat_form": self._create_form,
            "validate_form": self._validate_form,
        }

    def _setup_python_builtins(self):
        for name in dir(py_builtins):
            if not name.startswith("_"):
                self.global_scope[f"py_{name}"] = getattr(py_builtins, name)

    def _import_python_module(self, module_name, alias=None):
        try:
            wrapped_module = self.python_integration.import_python_module(
                module_name, alias
            )
            if alias:
                if isinstance(wrapped_module, dict):
                    self.global_scope[alias] = wrapped_module[alias]
                    return wrapped_module[alias]
                else:
                    self.global_scope[alias] = wrapped_module
                    return wrapped_module
            else:
                module_var_name = module_name.replace(".", "_")
                self.global_scope[module_var_name] = wrapped_module
                return wrapped_module
        except RenzmcImportError as e:
            print(f"❌ Gagal mengimpor modul Python '{module_name}': {str(e)}")
            print(
                f"""💡 Saran: Pastikan modul terinstal dengan 'instal_paket_python("{module_name}")'"""
            )
            raise e
        except Exception as e:
            import_error = RenzmcImportError(
                f"Error tidak terduga saat mengimpor '{module_name}': {str(e)}"
            )
            print(f"❌ {import_error}")
            raise import_error

    def _call_python_function(self, func, *args, **kwargs):
        return self.python_integration.call_python_function(func, *args, **kwargs)

    def _from_python_import(self, module_name, *items):
        try:
            imported_items = self.python_integration.import_python_module(
                module_name, from_items=list(items)
            )
            for item_name, item_value in imported_items.items():
                enhanced_value = self.python_integration.convert_python_to_renzmc(
                    item_value
                )
                self.global_scope[item_name] = enhanced_value
            print(
                f"✓ Berhasil mengimpor {len(imported_items)} item dari modul '{module_name}'"
            )
            return imported_items
        except RenzmcImportError as e:
            print(f"❌ Gagal mengimpor dari modul Python '{module_name}': {str(e)}")
            print("💡 Saran: Periksa nama modul dan item yang akan diimpor")
            raise e
        except Exception as e:
            import_error = RenzmcImportError(
                f"Error tidak terduga saat mengimpor dari '{module_name}': {str(e)}"
            )
            print(f"❌ {import_error}")
            raise import_error

    def _create_python_object(self, class_obj, *args, **kwargs):
        return self.python_integration.create_python_object(class_obj, *args, **kwargs)

    def _list_python_attributes(self, module_name):
        return self.python_integration.list_module_attributes(module_name)

    def _python_help(self, obj):
        return self.python_integration.get_python_help(obj)

    def _install_python_package(self, package_name):
        if self.safe_mode:
            raise RuntimeError(
                "🔒 Instalasi paket Python diblokir dalam mode aman. Gunakan `atur_mode_aman(salah)` untuk mengaktifkan (tidak disarankan untuk server)."
            )
        return self.python_integration.install_package(package_name)

    def _auto_import_python(self, module_name):
        return self.python_integration.auto_import_on_demand(module_name)

    def _convert_to_python(self, obj):
        return self.python_integration.convert_renzmc_to_python(obj)

    def _convert_from_python(self, obj):
        return self.python_integration.convert_python_to_renzmc(obj)

    def _create_smart_wrapper(self, obj):
        return self.python_integration.create_smart_wrapper(obj)

    def _import_all_from_python(self, module_name):
        return self.python_integration.enable_star_imports(
            module_name, self.global_scope
        )

    def _list_python_modules(self):
        return self.python_integration.get_all_python_modules()

    def _check_module_available(self, module_name):
        try:
            importlib.import_module(module_name)
            return True
        except ImportError:
            return False

    def _execute_python_code(self, code_string, local_vars=None):
        raise RuntimeError(
            "🔒 Eksekusi kode Python dinamis dinonaktifkan untuk keamanan.\nGunakan fungsi built-in atau impor modul Python secara eksplisit.\nContoh: gunakan 'impor_python &quot;math&quot;' lalu 'panggil_python math.sqrt(16)'"
        )

    def _evaluate_python_expression(self, expression, context=None):
        raise RuntimeError(
            "🔒 Evaluasi ekspresi Python dinamis dinonaktifkan untuk keamanan.\nGunakan fungsi built-in atau impor modul Python secara eksplisit.\nContoh: gunakan 'impor_python &quot;math&quot;' lalu 'panggil_python math.sqrt(16)'"
        )

    def _import_renzmc_module(self, module_name, alias=None):
        try:
            module = self.module_manager.load_module(module_name, alias)
            module_var_name = alias or module_name.replace(".", "_")
            self.global_scope[module_var_name] = module
            return module
        except Exception as e:
            raise RuntimeError(
                f"Error mengimpor modul RenzmcLang '{module_name}': {str(e)}"
            )

    def _import_from_renzmc_module(self, module_name, *items):
        try:
            imported_items = self.module_manager.import_from_module(
                module_name, list(items)
            )
            for item_name, item_value in imported_items.items():
                self.global_scope[item_name] = item_value
            return imported_items
        except Exception as e:
            raise RuntimeError(
                f"Error mengimpor dari modul RenzmcLang '{module_name}': {str(e)}"
            )

    def _import_all_from_renzmc_module(self, module_name):
        try:
            imported_items = self.module_manager.import_all_from_module(module_name)
            for item_name, item_value in imported_items.items():
                if not item_name.startswith("_"):
                    self.global_scope[item_name] = item_value
            return imported_items
        except Exception as e:
            raise RuntimeError(
                f"Error mengimpor semua dari modul RenzmcLang '{module_name}': {str(e)}"
            )

    def _reload_renzmc_module(self, module_name):
        try:
            return self.module_manager.reload_module(module_name)
        except Exception as e:
            raise RuntimeError(
                f"Error memuat ulang modul RenzmcLang '{module_name}': {str(e)}"
            )

    def _list_renzmc_modules(self):
        return self.module_manager.list_available_modules()

    def _get_renzmc_module_info(self, module_name):
        return self.module_manager.get_module_info(module_name)

    def _add_module_search_path(self, path):
        try:
            self.module_manager.add_search_path(path)
            return True
        except Exception:
            return False

    def _create_decorator(self, name, decorator_func):
        return self.advanced_features.create_decorator(name, decorator_func)

    def _apply_decorator(self, decorator_name, func):
        return self.advanced_features.apply_decorator(decorator_name, func)

    def _create_context_manager(self, name, enter_func=None, exit_func=None):
        return self.advanced_features.create_context_manager(
            name, enter_func, exit_func
        )

    def _use_context_manager(self, context_manager, action_func):
        try:
            with context_manager:
                return action_func()
        except Exception as e:
            raise RuntimeError(f"Error menggunakan context manager: {str(e)}")

    def _create_advanced_generator(self, name, generator_func, *args, **kwargs):
        return self.advanced_features.create_generator(
            name, generator_func, *args, **kwargs
        )

    def _create_async_function(self, name, func):
        return self.advanced_features.create_async_function(name, func)

    def _list_advanced_features(self):
        return self.advanced_features.list_features()

    def _create_generator(self, func, *args, **kwargs):
        if callable(func):
            return func(*args, **kwargs)
        else:
            raise TypeError(f"Objek '{func}' tidak dapat dipanggil sebagai generator")

    def _run_async_function(self, coro):
        if asyncio.iscoroutine(coro):
            return self.loop.run_until_complete(coro)
        else:
            raise TypeError(f"Objek '{coro}' bukan coroutine")

    def _wait_all_async(self, coros):
        if all((asyncio.iscoroutine(coro) for coro in coros)):
            return self.loop.run_until_complete(asyncio.gather(*coros))
        else:
            raise TypeError("Semua objek harus berupa coroutine")

    def _list_to_generator(self, lst):
        if hasattr(lst, "__iter__"):

            def gen():
                for item in lst:
                    yield item

            return gen()
        else:
            raise TypeError(f"Objek '{lst}' tidak dapat diiterasi")

    def _check_type(self, obj, type_name):
        if type_name == "None" or type_name == "NoneType":
            return obj is None
        if "|" in type_name:
            union_types = [t.strip() for t in type_name.split("|")]
            return any((self._check_type(obj, t) for t in union_types))
        list_match = re.match("(?:list|array)\\[(.*)\\]", type_name)
        if list_match:
            if not isinstance(obj, list):
                return False
            if not obj:
                return True
            element_type = list_match.group(1)
            return all((self._check_type(item, element_type) for item in obj))
        dict_match = re.match("dict\\[(.*),(.*)\\]", type_name)
        if dict_match:
            if not isinstance(obj, dict):
                return False
            if not obj:
                return True
            key_type = dict_match.group(1).strip()
            value_type = dict_match.group(2).strip()
            return all(
                (
                    self._check_type(k, key_type) and self._check_type(v, value_type)
                    for k, v in obj.items()
                )
            )
        tuple_match = re.match("tuple\\[(.*)\\]", type_name)
        if tuple_match:
            if not isinstance(obj, tuple):
                return False
            element_types = [t.strip() for t in tuple_match.group(1).split(",")]
            if len(obj) != len(element_types):
                return False
            return all(
                (self._check_type(obj[i], element_types[i]) for i in range(len(obj)))
            )
        optional_match = re.match("Optional\\[(.*)\\]", type_name)
        if optional_match:
            if obj is None:
                return True
            return self._check_type(obj, optional_match.group(1))
        if type_name.endswith("?"):
            if obj is None:
                return True
            return self._check_type(obj, type_name[:-1])
        if type_name == "callable" or type_name == "Callable":
            return callable(obj)
        if type_name in self.type_registry:
            try:
                expected_type = self.type_registry[type_name]
                if isinstance(expected_type, type):
                    return isinstance(obj, expected_type)
            except TypeError:
                pass
            return False
        elif hasattr(py_builtins, type_name):
            try:
                expected_type = getattr(py_builtins, type_name)
                if isinstance(expected_type, type):
                    return isinstance(obj, expected_type)
            except TypeError:
                pass
            return False
        elif type_name.lower() == "string" or type_name.lower() == "str":
            return isinstance(obj, str)
        elif type_name.lower() == "integer" or type_name.lower() == "int":
            return isinstance(obj, int)
        elif type_name.lower() == "float" or type_name.lower() == "double":
            return isinstance(obj, float)
        elif type_name.lower() == "boolean" or type_name.lower() == "bool":
            return isinstance(obj, bool)
        elif type_name.lower() == "list" or type_name.lower() == "array":
            return isinstance(obj, list)
        elif type_name.lower() == "dict" or type_name.lower() == "dictionary":
            return isinstance(obj, dict)
        elif type_name.lower() == "tuple":
            return isinstance(obj, tuple)
        elif type_name.lower() == "set":
            return isinstance(obj, set)
        elif type_name.lower() == "any":
            return True
        return False

    def _format_string(self, template, **kwargs):
        return template.format(**kwargs)

    def _open_file(self, filename, mode="r"):
        try:
            return open(filename, mode)
        except Exception as e:
            raise FileError(f"Gagal membuka file '{filename}': {str(e)}")

    def _close_file(self, file):
        try:
            file.close()
        except Exception as e:
            raise FileError(f"Gagal menutup file: {str(e)}")

    def _read_line(self, file):
        try:
            return file.readline()
        except Exception as e:
            raise FileError(f"Gagal membaca baris dari file: {str(e)}")

    def _read_all_lines(self, file):
        try:
            return file.readlines()
        except Exception as e:
            raise FileError(f"Gagal membaca semua baris dari file: {str(e)}")

    def _write_line(self, file, line):
        try:
            file.write(line)
        except Exception as e:
            raise FileError(f"Gagal menulis ke file: {str(e)}")

    def _flush_file(self, file):
        try:
            file.flush()
        except Exception as e:
            raise FileError(f"Gagal flush file: {str(e)}")

    def _file_exists(self, path):
        try:
            import os.path

            return os.path.exists(path)
        except Exception as e:
            raise FileError(f"Gagal memeriksa keberadaan file '{path}': {str(e)}")

    def _make_directory(self, path):
        try:
            import os

            os.makedirs(path, exist_ok=True)
        except Exception as e:
            raise FileError(f"Gagal membuat direktori '{path}': {str(e)}")

    def _remove_directory(self, path):
        try:
            import shutil

            shutil.rmtree(path)
        except Exception as e:
            raise FileError(f"Gagal menghapus direktori '{path}': {str(e)}")

    def _list_directory(self, path="."):
        try:
            import os

            return os.listdir(path)
        except Exception as e:
            raise FileError(f"Gagal membaca direktori '{path}': {str(e)}")

    def _join_path(self, *paths):
        try:
            import os.path

            return os.path.join(*paths)
        except Exception as e:
            raise FileError(f"Gagal menggabungkan path: {str(e)}")

    def _file_path(self, path):
        try:
            import os.path

            return os.path.basename(path)
        except Exception as e:
            raise FileError(f"Gagal mendapatkan nama file dari '{path}': {str(e)}")

    def _directory_path(self, path):
        try:
            import os.path

            return os.path.dirname(path)
        except Exception as e:
            raise FileError(f"Gagal mendapatkan direktori dari '{path}': {str(e)}")

    def _file_size(self, path):
        try:
            import os.path

            return os.path.getsize(path)
        except Exception as e:
            raise FileError(f"Gagal mendapatkan ukuran file '{path}': {str(e)}")

    def _file_modification_time(self, path):
        try:
            import os.path

            return os.path.getmtime(path)
        except Exception as e:
            raise FileError(
                f"Gagal mendapatkan waktu modifikasi file '{path}': {str(e)}"
            )

    def _json_to_text(self, obj):
        try:
            import json

            return json.dumps(obj)
        except Exception as e:
            raise ValueError(f"Gagal mengkonversi objek ke JSON: {str(e)}")

    def _text_to_json(self, text):
        try:
            import json

            return json.loads(text)
        except Exception as e:
            raise ValueError(f"Gagal mengkonversi JSON ke objek: {str(e)}")

    def _encrypt(self, text, key):
        if not CRYPTOGRAPHY_AVAILABLE:
            raise ImportError(
                "Modul 'cryptography' tidak terinstal. Silakan instal dengan 'pip install cryptography'"
            )
        try:
            import base64
            from cryptography.fernet import Fernet
            from cryptography.hazmat.primitives import hashes
            from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

            salt = b"renzmc_salt"
            kdf = PBKDF2HMAC(
                algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000
            )
            key_bytes = kdf.derive(key.encode())
            key_base64 = base64.urlsafe_b64encode(key_bytes)
            f = Fernet(key_base64)
            encrypted = f.encrypt(text.encode())
            return base64.urlsafe_b64encode(encrypted).decode()
        except Exception as e:
            raise ValueError(f"Gagal mengenkripsi teks: {str(e)}")

    def _decrypt(self, encrypted_text, key):
        if not CRYPTOGRAPHY_AVAILABLE:
            raise ImportError(
                "Modul 'cryptography' tidak terinstal. Silakan instal dengan 'pip install cryptography'"
            )
        try:
            import base64
            from cryptography.fernet import Fernet
            from cryptography.hazmat.primitives import hashes
            from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

            salt = b"renzmc_salt"
            kdf = PBKDF2HMAC(
                algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000
            )
            key_bytes = kdf.derive(key.encode())
            key_base64 = base64.urlsafe_b64encode(key_bytes)
            f = Fernet(key_base64)
            encrypted = base64.urlsafe_b64decode(encrypted_text.encode())
            decrypted = f.decrypt(encrypted)
            return decrypted.decode()
        except Exception as e:
            raise ValueError(f"Gagal mendekripsi teks: {str(e)}")

    def _hash_text(self, text, algorithm="sha256"):
        try:
            import hashlib

            if algorithm == "md5":
                return hashlib.md5(text.encode()).hexdigest()
            elif algorithm == "sha1":
                return hashlib.sha1(text.encode()).hexdigest()
            elif algorithm == "sha256":
                return hashlib.sha256(text.encode()).hexdigest()
            elif algorithm == "sha512":
                return hashlib.sha512(text.encode()).hexdigest()
            else:
                raise ValueError(f"Algoritma hash '{algorithm}' tidak didukung")
        except Exception as e:
            raise ValueError(f"Gagal melakukan hash teks: {str(e)}")

    def _create_uuid(self):
        try:
            import uuid

            return str(uuid.uuid4())
        except Exception as e:
            raise ValueError(f"Gagal membuat UUID: {str(e)}")

    def _url_encode(self, text):
        try:
            import urllib.parse

            return urllib.parse.quote(text)
        except Exception as e:
            raise ValueError(f"Gagal melakukan URL encode: {str(e)}")

    def _url_decode(self, text):
        try:
            import urllib.parse

            return urllib.parse.unquote(text)
        except Exception as e:
            raise ValueError(f"Gagal melakukan URL decode: {str(e)}")

    def _http_request(
        self, url, method="GET", headers=None, data=None, json=None, timeout=30
    ):
        try:
            import requests

            response = requests.request(
                method=method,
                url=url,
                headers=headers,
                data=data,
                json=json,
                timeout=timeout,
            )
            return {
                "status_code": response.status_code,
                "headers": dict(response.headers),
                "text": response.text,
                "json": (
                    response.json()
                    if response.headers.get("content-type", "").startswith(
                        "application/json"
                    )
                    else None
                ),
                "content": response.content,
            }
        except ImportError:
            raise ImportError(
                "Modul 'requests' tidak terinstal. Silakan instal dengan 'pip install requests'"
            )
        except Exception as e:
            raise ValueError(f"Gagal melakukan HTTP request: {str(e)}")

    def _http_get(self, url, headers=None, params=None, timeout=30):
        try:
            import requests

            response = requests.get(
                url, headers=headers, params=params, timeout=timeout
            )
            return {
                "status_code": response.status_code,
                "headers": dict(response.headers),
                "text": response.text,
                "json": (
                    response.json()
                    if response.headers.get("content-type", "").startswith(
                        "application/json"
                    )
                    else None
                ),
                "content": response.content,
            }
        except ImportError:
            raise ImportError(
                "Modul 'requests' tidak terinstal. Silakan instal dengan 'pip install requests'"
            )
        except Exception as e:
            raise ValueError(f"Gagal melakukan HTTP GET request: {str(e)}")

    def _http_post(self, url, headers=None, data=None, json=None, timeout=30):
        try:
            import requests

            response = requests.post(
                url, headers=headers, data=data, json=json, timeout=timeout
            )
            return {
                "status_code": response.status_code,
                "headers": dict(response.headers),
                "text": response.text,
                "json": (
                    response.json()
                    if response.headers.get("content-type", "").startswith(
                        "application/json"
                    )
                    else None
                ),
                "content": response.content,
            }
        except ImportError:
            raise ImportError(
                "Modul 'requests' tidak terinstal. Silakan instal dengan 'pip install requests'"
            )
        except Exception as e:
            raise ValueError(f"Gagal melakukan HTTP POST request: {str(e)}")

    def _http_put(self, url, headers=None, data=None, json=None, timeout=30):
        try:
            import requests

            response = requests.put(
                url, headers=headers, data=data, json=json, timeout=timeout
            )
            return {
                "status_code": response.status_code,
                "headers": dict(response.headers),
                "text": response.text,
                "json": (
                    response.json()
                    if response.headers.get("content-type", "").startswith(
                        "application/json"
                    )
                    else None
                ),
                "content": response.content,
            }
        except ImportError:
            raise ImportError(
                "Modul 'requests' tidak terinstal. Silakan instal dengan 'pip install requests'"
            )
        except Exception as e:
            raise ValueError(f"Gagal melakukan HTTP PUT request: {str(e)}")

    def _http_delete(self, url, headers=None, timeout=30):
        try:
            import requests

            response = requests.delete(url, headers=headers, timeout=timeout)
            return {
                "status_code": response.status_code,
                "headers": dict(response.headers),
                "text": response.text,
                "json": (
                    response.json()
                    if response.headers.get("content-type", "").startswith(
                        "application/json"
                    )
                    else None
                ),
                "content": response.content,
            }
        except ImportError:
            raise ImportError(
                "Modul 'requests' tidak terinstal. Silakan instal dengan 'pip install requests'"
            )
        except Exception as e:
            raise ValueError(f"Gagal melakukan HTTP DELETE request: {str(e)}")

    def get_variable(self, name):
        if (
            self.current_instance is not None
            and self.current_instance in self.instance_scopes
        ):
            instance_scope = self.instance_scopes[self.current_instance]
            if name in instance_scope:
                return instance_scope[name]
        if name in self.local_scope:
            return self.local_scope[name]
        if name in self.global_scope:
            return self.global_scope[name]
        if name in self.builtin_functions:
            return self.builtin_functions[name]
        raise NameError(f"Variabel '{name}' tidak ditemukan")

    def set_variable(self, name, value, is_local=False):
        if self.current_instance is not None:
            if self.current_instance not in self.instance_scopes:
                self.instance_scopes[self.current_instance] = {}
            self.instance_scopes[self.current_instance][name] = value
            return value
        if is_local or name in self.local_scope:
            self.local_scope[name] = value
        else:
            self.global_scope[name] = value
        return value

    def visit_Program(self, node):
        result = None
        for statement in node.statements:
            result = self.visit(statement)
            if self.return_value is not None or self.break_flag or self.continue_flag:
                break
        return result

    def visit_Block(self, node):
        result = None
        for statement in node.statements:
            result = self.visit(statement)
            if self.return_value is not None or self.break_flag or self.continue_flag:
                break
        return result

    def visit_BinOp(self, node):
        left = self.visit(node.left)
        right = self.visit(node.right)
        if node.op.type == TokenType.TAMBAH:
            if isinstance(left, str) or isinstance(right, str):
                return str(left) + str(right)
            return left + right
        elif node.op.type == TokenType.KURANG:
            return left - right
        elif node.op.type == TokenType.KALI_OP:
            if isinstance(left, str) and isinstance(right, int):
                return left * right
            elif isinstance(left, int) and isinstance(right, str):
                return right * left
            return left * right
        elif node.op.type == TokenType.BAGI:
            if right == 0:
                raise DivisionByZeroError("Pembagian dengan nol tidak diperbolehkan")
            return left / right
        elif node.op.type == TokenType.SISA_BAGI:
            if right == 0:
                raise DivisionByZeroError("Pembagian dengan nol tidak diperbolehkan")
            return left % right
        elif node.op.type == TokenType.PANGKAT:
            return left**right
        elif node.op.type == TokenType.PEMBAGIAN_BULAT:
            if right == 0:
                raise DivisionByZeroError("Pembagian dengan nol tidak diperbolehkan")
            return left // right
        elif node.op.type == TokenType.SAMA_DENGAN:
            return left == right
        elif node.op.type == TokenType.TIDAK_SAMA:
            return left != right
        elif node.op.type == TokenType.LEBIH_DARI:
            return left > right
        elif node.op.type == TokenType.KURANG_DARI:
            return left < right
        elif node.op.type == TokenType.LEBIH_SAMA:
            return left >= right
        elif node.op.type == TokenType.KURANG_SAMA:
            return left <= right
        elif node.op.type == TokenType.DAN:
            return left and right
        elif node.op.type == TokenType.ATAU:
            return left or right
        elif node.op.type in (TokenType.BIT_DAN, TokenType.BITWISE_AND):
            return int(left) & int(right)
        elif node.op.type in (TokenType.BIT_ATAU, TokenType.BITWISE_OR):
            return int(left) | int(right)
        elif node.op.type in (TokenType.BIT_XOR, TokenType.BITWISE_XOR):
            return int(left) ^ int(right)
        elif node.op.type == TokenType.GESER_KIRI:
            return int(left) << int(right)
        elif node.op.type == TokenType.GESER_KANAN:
            return int(left) >> int(right)
        elif node.op.type in (TokenType.DALAM, TokenType.DALAM_OP):
            if not hasattr(right, '__iter__') and not hasattr(right, '__contains__'):
                raise TypeError(f"argument of type '{type(right).__name__}' is not iterable")
            return left in right
        elif node.op.type == TokenType.TIDAK_DALAM:
            if not hasattr(right, '__iter__') and not hasattr(right, '__contains__'):
                raise TypeError(f"argument of type '{type(right).__name__}' is not iterable")
            return left not in right
        elif node.op.type in (TokenType.ADALAH, TokenType.ADALAH_OP):
            return left is right
        elif node.op.type == TokenType.BUKAN:
            return left is not right
        raise RuntimeError(f"Operator tidak didukung: {node.op.type}")

    def visit_UnaryOp(self, node):
        expr = self.visit(node.expr)
        if node.op.type == TokenType.TAMBAH:
            return +expr
        elif node.op.type == TokenType.KURANG:
            return -expr
        elif node.op.type in (TokenType.TIDAK, TokenType.NOT):
            return not expr
        elif node.op.type in (TokenType.BIT_NOT, TokenType.BITWISE_NOT):
            return ~int(expr)
        raise RuntimeError(f"Operator unary tidak didukung: {node.op.type}")

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

    def visit_String(self, node):
        return node.value

    def visit_Boolean(self, node):
        return node.value

    def visit_NoneValue(self, node):
        return None

    def visit_List(self, node):
        return [self.visit(element) for element in node.elements]

    def visit_Dict(self, node):
        return {self.visit(key): self.visit(value) for key, value in node.pairs}

    def visit_Set(self, node):
        return {self.visit(element) for element in node.elements}

    def visit_Tuple(self, node):
        return tuple((self.visit(element) for element in node.elements))

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

    def visit_VarDecl(self, node):
        value = self.visit(node.value)

        if node.type_hint:
            try:
                self._check_variable_type(node.var_name, value, node.type_hint)
            except Exception as e:
                type_name = node.type_hint.type_name
                if type_name in self.type_registry:
                    expected_type = self.type_registry[type_name]
                    try:
                        if isinstance(expected_type, type) and not isinstance(value, expected_type):
                            raise TypeHintError(f"Nilai '{value}' bukan tipe '{type_name}'")
                    except TypeError:
                        pass
                elif hasattr(py_builtins, type_name):
                    expected_type = getattr(py_builtins, type_name)
                    try:
                        if isinstance(expected_type, type) and not isinstance(value, expected_type):
                            raise TypeHintError(f"Nilai '{value}' bukan tipe '{type_name}'")
                    except TypeError:
                        pass

        return self.set_variable(node.var_name, value)

    def visit_Assign(self, node):
        value = self.visit(node.value)
        if isinstance(node.var, Var):
            return self.set_variable(node.var.name, value)
        elif isinstance(node.var, AttributeRef):
            obj = self.visit(node.var.obj)
            attr = node.var.attr
            if id(obj) in self.instance_scopes:
                self.instance_scopes[id(obj)][attr] = value
                return value
            elif hasattr(obj, attr):
                setattr(obj, attr, value)
                return value
            elif isinstance(obj, dict):
                obj[attr] = value
                return value
            else:
                raise AttributeError(
                    f"Objek '{type(obj).__name__}' tidak memiliki atribut '{attr}'"
                )
        elif isinstance(node.var, IndexAccess):
            obj = self.visit(node.var.obj)
            index = self.visit(node.var.index)
            if isinstance(obj, (list, dict)):
                obj[index] = value
                return value
            else:
                raise TypeError(
                    f"Objek tipe '{type(obj).__name__}' tidak mendukung pengindeksan"
                )
        raise RuntimeError(f"Tipe assignment tidak didukung: {type(node.var).__name__}")

    def visit_CompoundAssign(self, node):
        from renzmc.core.token import TokenType

        if isinstance(node.var, Var):
            current_value = self.get_variable(node.var.name)
        elif isinstance(node.var, IndexAccess):
            obj = self.visit(node.var.obj)
            index = self.visit(node.var.index)
            current_value = obj[index]
        elif isinstance(node.var, AttributeRef):
            obj = self.visit(node.var.obj)
            attr = node.var.attr
            if id(obj) in self.instance_scopes:
                current_value = self.instance_scopes[id(obj)].get(attr)
            elif hasattr(obj, attr):
                current_value = getattr(obj, attr)
            elif isinstance(obj, dict):
                current_value = obj[attr]
            else:
                raise AttributeError(
                    f"Objek '{type(obj).__name__}' tidak memiliki atribut '{attr}'"
                )
        else:
            raise RuntimeError(
                f"Tipe compound assignment tidak didukung: {type(node.var).__name__}"
            )
        operand = self.visit(node.value)
        if node.op.type == TokenType.TAMBAH_SAMA_DENGAN:
            new_value = current_value + operand
        elif node.op.type == TokenType.KURANG_SAMA_DENGAN:
            new_value = current_value - operand
        elif node.op.type == TokenType.KALI_SAMA_DENGAN:
            new_value = current_value * operand
        elif node.op.type == TokenType.BAGI_SAMA_DENGAN:
            new_value = current_value / operand
        elif node.op.type == TokenType.SISA_SAMA_DENGAN:
            new_value = current_value % operand
        elif node.op.type == TokenType.PANGKAT_SAMA_DENGAN:
            new_value = current_value**operand
        elif node.op.type == TokenType.PEMBAGIAN_BULAT_SAMA_DENGAN:
            new_value = current_value // operand
        elif node.op.type in (TokenType.BIT_DAN_SAMA_DENGAN, TokenType.BITWISE_AND_SAMA_DENGAN):
            new_value = current_value & operand
        elif node.op.type in (TokenType.BIT_ATAU_SAMA_DENGAN, TokenType.BITWISE_OR_SAMA_DENGAN):
            new_value = current_value | operand
        elif node.op.type in (TokenType.BIT_XOR_SAMA_DENGAN, TokenType.BITWISE_XOR_SAMA_DENGAN):
            new_value = current_value ^ operand
        elif node.op.type == TokenType.GESER_KIRI_SAMA_DENGAN:
            new_value = current_value << operand
        elif node.op.type == TokenType.GESER_KANAN_SAMA_DENGAN:
            new_value = current_value >> operand
        else:
            raise RuntimeError(
                f"Operator compound assignment tidak dikenal: {node.op.type}"
            )
        if isinstance(node.var, Var):
            return self.set_variable(node.var.name, new_value)
        elif isinstance(node.var, IndexAccess):
            obj = self.visit(node.var.obj)
            index = self.visit(node.var.index)
            obj[index] = new_value
            return new_value
        elif isinstance(node.var, AttributeRef):
            obj = self.visit(node.var.obj)
            attr = node.var.attr
            if id(obj) in self.instance_scopes:
                self.instance_scopes[id(obj)][attr] = new_value
            elif hasattr(obj, attr):
                setattr(obj, attr, new_value)
            elif isinstance(obj, dict):
                obj[attr] = new_value
            else:
                raise AttributeError(
                    f"Objek '{type(obj).__name__}' tidak memiliki atribut '{attr}'"
                )
            return new_value

    def visit_MultiVarDecl(self, node):
        values = self.visit(node.values)
        if isinstance(values, (list, tuple)):
            if len(node.var_names) != len(values):
                raise ValueError(
                    f"Tidak dapat membongkar {len(values)} nilai menjadi {len(node.var_names)} variabel"
                )
            results = []
            for var_name, value in zip(node.var_names, values):
                result = self.set_variable(var_name, value)
                results.append(result)
            return tuple(results)
        elif len(node.var_names) == 1:
            return self.set_variable(node.var_names[0], values)
        else:
            raise ValueError(
                f"Tidak dapat membongkar 1 nilai menjadi {len(node.var_names)} variabel"
            )

    def visit_MultiAssign(self, node):
        values = self.visit(node.values)
        if isinstance(values, (list, tuple)):
            if len(node.vars) != len(values):
                raise ValueError(
                    f"Tidak dapat membongkar {len(values)} nilai menjadi {len(node.vars)} variabel"
                )
            results = []
            for var_node, value in zip(node.vars, values):
                if isinstance(var_node, Var):
                    result = self.set_variable(var_node.name, value)
                elif isinstance(var_node, AttributeRef):
                    obj = self.visit(var_node.obj)
                    attr = var_node.attr
                    if hasattr(obj, attr):
                        setattr(obj, attr, value)
                    elif isinstance(obj, dict):
                        obj[attr] = value
                    else:
                        raise AttributeError(
                            f"Objek '{type(obj).__name__}' tidak memiliki atribut '{attr}'"
                        )
                    result = value
                elif isinstance(var_node, IndexAccess):
                    obj = self.visit(var_node.obj)
                    index = self.visit(var_node.index)
                    if isinstance(obj, (list, dict)):
                        obj[index] = value
                    else:
                        raise TypeError(
                            f"Objek tipe '{type(obj).__name__}' tidak mendukung pengindeksan"
                        )
                    result = value
                else:
                    raise RuntimeError(
                        f"Tipe assignment tidak didukung: {type(var_node).__name__}"
                    )
                results.append(result)
            return tuple(results)
        elif len(node.vars) == 1:
            var_node = node.vars[0]
            if isinstance(var_node, Var):
                return self.set_variable(var_node.name, values)
            else:
                from renzmc.core.ast import Assign as AssignNode

                temp_assign = AssignNode(var_node, node.values)
                return self.visit_Assign(temp_assign)
        else:
            raise ValueError(
                f"Tidak dapat membongkar 1 nilai menjadi {len(node.vars)} variabel"
            )

    def visit_NoOp(self, node):
        pass

    def visit_Print(self, node):
        value = self.visit(node.expr)
        print(value)
        return None

    def visit_Input(self, node):
        prompt = self.visit(node.prompt)
        value = input(prompt)
        if node.var_name:
            try:
                int_value = int(value)
                self.set_variable(node.var_name, int_value)
                return int_value
            except ValueError:
                try:
                    float_value = float(value)
                    self.set_variable(node.var_name, float_value)
                    return float_value
                except ValueError:
                    self.set_variable(node.var_name, value)
                    return value
        return value

    def visit_If(self, node):
        condition = self.visit(node.condition)
        if condition:
            if_block = Block(node.if_body)
            return self.visit(if_block)
        elif node.else_body:
            else_block = Block(node.else_body)
            return self.visit(else_block)
        return None

    def visit_While(self, node):
        result = None
        while self.visit(node.condition):
            body_block = Block(node.body)
            result = self.visit(body_block)
            if self.break_flag:
                self.break_flag = False
                break
            if self.continue_flag:
                self.continue_flag = False
                continue
            if self.return_value is not None:
                break
        return result

    def visit_For(self, node):
        var_name = node.var_name
        start = self.visit(node.start)
        end = self.visit(node.end)
        result = None
        for i in range(start, end + 1):
            self.set_variable(var_name, i)
            body_block = Block(node.body)
            result = self.visit(body_block)
            if self.break_flag:
                self.break_flag = False
                break
            if self.continue_flag:
                self.continue_flag = False
                continue
            if self.return_value is not None:
                break
        return result

    def visit_ForEach(self, node):
        var_name = node.var_name
        iterable = self.visit(node.iterable)
        result = None
        if not hasattr(iterable, "__iter__"):
            raise TypeError(
                f"Objek tipe '{type(iterable).__name__}' tidak dapat diiterasi"
            )
        for item in iterable:
            if isinstance(var_name, tuple):
                if hasattr(item, '__iter__') and not isinstance(item, str):
                    unpacked = list(item)
                    if len(unpacked) != len(var_name):
                        raise ValueError(
                            f"Tidak dapat unpack {len(unpacked)} nilai ke {len(var_name)} variabel"
                        )
                    for var, val in zip(var_name, unpacked):
                        self.set_variable(var, val)
                else:
                    raise TypeError(f"Tidak dapat unpack nilai tipe '{type(item).__name__}'")
            else:
                self.set_variable(var_name, item)

            body_block = Block(node.body)
            result = self.visit(body_block)
            if self.break_flag:
                self.break_flag = False
                break
            if self.continue_flag:
                self.continue_flag = False
                continue
            if self.return_value is not None:
                break
        return result

    def visit_Break(self, node):
        self.break_flag = True

    def visit_Continue(self, node):
        self.continue_flag = True

    def visit_FuncDecl(self, node):
        name = node.name
        params = node.params
        body = node.body
        return_type = node.return_type
        param_types = node.param_types
        self.functions[name] = (params, body, return_type, param_types)

        if JIT_AVAILABLE:
            self.jit_call_counts[name] = 0
            self.jit_execution_times[name] = 0.0

        def renzmc_function(*args, **kwargs):
            return self._execute_user_function(
                name, params, body, return_type, param_types, list(args), kwargs
            )

        renzmc_function.__name__ = name
        renzmc_function.__renzmc_function__ = True
        self.global_scope[name] = renzmc_function

    def visit_FuncCall(self, node):
        if hasattr(node, "func_expr") and node.func_expr is not None:
            try:
                func = self.visit(node.func_expr)
                args = [self.visit(arg) for arg in node.args]
                kwargs = {k: self.visit(v) for k, v in node.kwargs.items()}
                if callable(func):
                    try:
                        return func(*args, **kwargs)
                    except Exception as e:
                        func_name = getattr(func, "__name__", str(type(func).__name__))
                        raise RuntimeError(
                            f"Error dalam pemanggilan fungsi '{func_name}': {str(e)}"
                        )
                else:
                    raise RuntimeError(
                        f"Objek '{type(func).__name__}' tidak dapat dipanggil"
                    )
            except NameError:
                if isinstance(node.func_expr, Var):
                    func_name = node.func_expr.name
                    args = [self.visit(arg) for arg in node.args]
                    kwargs = {k: self.visit(v) for k, v in node.kwargs.items()}
                    if func_name in self.functions:
                        params, body, return_type, param_types = self.functions[
                            func_name
                        ]
                        return self._execute_user_function(
                            func_name,
                            params,
                            body,
                            return_type,
                            param_types,
                            args,
                            kwargs,
                        )
                    else:
                        raise NameError(f"Fungsi '{func_name}' tidak ditemukan")
                else:
                    raise
        elif hasattr(node, "name"):
            return_type = None
            name = node.name
            args = [self.visit(arg) for arg in node.args]
            kwargs = {k: self.visit(v) for k, v in node.kwargs.items()}
            if name in self.builtin_functions:
                try:
                    return self.builtin_functions[name](*args, **kwargs)
                except Exception as e:
                    raise RuntimeError(f"Error dalam fungsi '{name}': {str(e)}")
            if name in self.classes:
                return self.create_class_instance(name, args)
            if name not in self.functions:
                try:
                    lambda_func = self.get_variable(name)
                    if callable(lambda_func):
                        try:
                            return lambda_func(*args, **kwargs)
                        except Exception as e:
                            raise RuntimeError(f"Error dalam lambda '{name}': {str(e)}")
                except NameError:
                    pass
            if (
                hasattr(self, "_decorated_functions")
                and name in self._decorated_functions
            ):
                raw_decorator_func, original_func = self._decorated_functions[name]
                try:
                    return raw_decorator_func(original_func, *args, **kwargs)
                except Exception as e:
                    raise RuntimeError(
                        f"Error dalam fungsi terdekorasi '{name}': {str(e)}"
                    )
            if name not in self.functions:
                raise NameError(f"Fungsi '{name}' tidak ditemukan")
            function_data = self.functions[name]
            if len(function_data) == 5 and function_data[4] == "ASYNC":
                params, body, return_type, param_types, _ = function_data

                async def async_coroutine():
                    return self._execute_user_function(
                        name, params, body, return_type, param_types, args, kwargs
                    )

                return async_coroutine()
            else:
                params, body, return_type, param_types = function_data
                return self._execute_user_function(
                    name, params, body, return_type, param_types, args, kwargs
                )

    def _execute_user_function(
        self, name, params, body, return_type, param_types, args, kwargs
    ):
        if JIT_AVAILABLE and name in self.jit_compiled_functions:
            compiled_func = self.jit_compiled_functions[name]
            if compiled_func is not None:
                try:
                    return compiled_func(*args, **kwargs)
                except Exception:
                    pass

        start_time = time.time()
        param_values = {}
        for i, arg in enumerate(args):
            if i >= len(params):
                raise RuntimeError(
                    f"Fungsi '{name}' membutuhkan {len(params)} parameter, tetapi {len(args)} posisional diberikan"
                )
            param_values[params[i]] = arg
        for param_name, value in kwargs.items():
            if param_name not in params:
                raise RuntimeError(
                    f"Parameter '{param_name}' tidak ada dalam fungsi '{name}'"
                )
            if param_name in param_values:
                raise RuntimeError(
                    f"Parameter '{param_name}' mendapat nilai ganda (posisional dan kata kunci)"
                )
            param_values[param_name] = value
        missing_params = [p for p in params if p not in param_values]
        if missing_params:
            raise RuntimeError(
                f"Parameter hilang dalam fungsi '{name}': {', '.join(missing_params)}"
            )
        if param_types:
            for param_name, value in param_values.items():
                if param_name in param_types:
                    type_hint = param_types[param_name]
                    type_name = type_hint.type_name
                    if type_name in self.type_registry:
                        expected_type = self.type_registry[type_name]
                        try:
                            if isinstance(expected_type, type) and not isinstance(value, expected_type):
                                raise TypeHintError(
                                    f"Parameter '{param_name}' harus bertipe '{type_name}'"
                                )
                        except TypeError:
                            pass
                    elif hasattr(py_builtins, type_name):
                        expected_type = getattr(py_builtins, type_name)
                        try:
                            if isinstance(expected_type, type) and not isinstance(value, expected_type):
                                raise TypeHintError(
                                    f"Parameter '{param_name}' harus bertipe '{type_name}'"
                                )
                        except TypeError:
                            pass
        old_local_scope = self.local_scope.copy()
        self.local_scope = {}
        for param_name, value in param_values.items():
            self.set_variable(param_name, value, is_local=True)
        self.return_value = None
        for stmt in body:
            self.visit(stmt)
            if hasattr(self, "return_flag") and self.return_flag:
                break
            if (
                hasattr(self, "break_flag")
                and self.break_flag
                or (hasattr(self, "continue_flag") and self.continue_flag)
            ):
                if hasattr(self, "break_flag"):
                    self.break_flag = False
                if hasattr(self, "continue_flag"):
                    self.continue_flag = False
                break
        return_value = self.return_value
        if return_type and return_value is not None:
            if hasattr(return_type, 'type_name'):
                type_name = return_type.type_name
                if type_name in self.type_registry:
                    expected_type = self.type_registry[type_name]
                    try:
                        if isinstance(expected_type, type) and not isinstance(return_value, expected_type):
                            raise TypeHintError(
                                f"Nilai kembali fungsi '{name}' harus bertipe '{type_name}'"
                            )
                    except TypeError:
                        pass
                elif hasattr(py_builtins, type_name):
                    expected_type = getattr(py_builtins, type_name)
                    try:
                        if isinstance(expected_type, type) and not isinstance(return_value, expected_type):
                            raise TypeHintError(
                                f"Nilai kembali fungsi '{name}' harus bertipe '{type_name}'"
                            )
                    except TypeError:
                        pass
            else:
                from renzmc.core.advanced_types import TypeParser, AdvancedTypeValidator
                if isinstance(return_type, str):
                    type_spec = TypeParser.parse_type_string(return_type)
                else:
                    type_spec = return_type
                if type_spec:
                    is_valid, error_msg = AdvancedTypeValidator.validate(return_value, type_spec, "return")
                    if not is_valid:
                        raise TypeHintError(f"Fungsi '{name}': {error_msg}")
        self.local_scope = old_local_scope
        self.return_value = None

        if JIT_AVAILABLE and name in self.jit_call_counts:
            execution_time = time.time() - start_time
            self.jit_call_counts[name] += 1
            self.jit_execution_times[name] += execution_time

            if (self.jit_call_counts[name] >= self.jit_threshold and
                name not in self.jit_compiled_functions):
                self._compile_function_with_jit(name, params, body)

        return return_value

    def _compile_function_with_jit(self, name, params, body):
        if not self.jit_compiler:
            self.jit_compiled_functions[name] = None
            return

        try:
            interpreter_func = self.global_scope.get(name)

            if not interpreter_func:
                self.jit_compiled_functions[name] = None
                return

            compiled_func = self.jit_compiler.compile_function(
                name, params, body, interpreter_func
            )

            if compiled_func:
                self.jit_compiled_functions[name] = compiled_func

            else:
                self.jit_compiled_functions[name] = None

        except Exception as e:
            self.jit_compiled_functions[name] = None

    def _create_user_function_wrapper(self, name):

        def user_decorator_wrapper(func, *args, **kwargs):
            if name in self.functions:
                function_data = self.functions[name]
                if len(function_data) == 5:
                    params, body, return_type, param_types, _ = function_data
                else:
                    params, body, return_type, param_types = function_data
                all_args = [func] + list(args)
                return self._execute_user_function(
                    name, params, body, return_type, param_types, all_args, kwargs
                )
            else:
                raise RuntimeError(f"User function '{name}' not found for decorator")

        return user_decorator_wrapper

    def _create_user_decorator_factory(self, name, decorator_args):

        def decorator_factory(func):
            if name in self.functions:
                function_data = self.functions[name]
                if len(function_data) == 5:
                    params, body, return_type, param_types, _ = function_data
                else:
                    params, body, return_type, param_types = function_data
                all_args = list(decorator_args) + [func]
                decorator_result = self._execute_user_function(
                    name, params, body, return_type, param_types, all_args, {}
                )
                if callable(decorator_result):
                    return decorator_result
                else:
                    return func
            else:
                raise RuntimeError(
                    f"User function '{name}' not found for decorator factory"
                )

        return decorator_factory

    def create_class_instance(self, class_name, args):
        class_info = self.classes[class_name]

        class Instance:

            def __init__(self, class_name):
                self.__class__.__name__ = class_name

        instance = Instance(class_name)
        instance_id = id(instance)
        self.instance_scopes[instance_id] = {}
        if class_info["constructor"]:
            constructor_params, constructor_body, param_types = class_info[
                "constructor"
            ]
            if len(args) != len(constructor_params):
                raise RuntimeError(
                    f"Konstruktor kelas '{class_name}' membutuhkan {len(constructor_params)} parameter, tetapi {len(args)} diberikan"
                )
            old_instance = self.current_instance
            old_local_scope = self.local_scope.copy()
            self.current_instance = instance_id
            self.local_scope = {}
            self.local_scope["diri"] = instance
            for i, param in enumerate(constructor_params):
                self.set_variable(param, args[i], is_local=True)
            self.visit_Block(Block(constructor_body))
            self.current_instance = old_instance
            self.local_scope = old_local_scope
        return instance

    def visit_Return(self, node):
        if node.expr:
            self.return_value = self.visit(node.expr)
        else:
            self.return_value = None
        return self.return_value

    def visit_ClassDecl(self, node):
        name = node.name
        methods = {}
        constructor = None
        parent = node.parent
        class_vars = {}
        for var_decl in node.class_vars:
            if isinstance(var_decl, VarDecl):
                var_name = var_decl.var_name
                value = self.visit(var_decl.value)
                class_vars[var_name] = value
        for method in node.methods:
            if isinstance(method, MethodDecl):
                methods[method.name] = (
                    method.params,
                    method.body,
                    method.return_type,
                    method.param_types,
                )
            elif isinstance(method, Constructor):
                constructor = (method.params, method.body, method.param_types)
        self.classes[name] = {
            "methods": methods,
            "constructor": constructor,
            "parent": parent,
            "class_vars": class_vars,
        }

    def visit_MethodDecl(self, node):
        pass

    def visit_Constructor(self, node):
        pass

    def visit_AttributeRef(self, node):
        obj = self.visit(node.obj)
        attr = node.attr
        if id(obj) in self.instance_scopes:
            instance_scope = self.instance_scopes[id(obj)]
            if attr in instance_scope:
                return instance_scope[attr]
            else:
                raise AttributeError(
                    f"Objek '{type(obj).__name__}' tidak memiliki atribut '{attr}'"
                )
        elif hasattr(obj, attr):
            return getattr(obj, attr)
        elif isinstance(obj, dict) and attr in obj:
            return obj[attr]
        else:
            if (
                hasattr(obj, "__name__")
                and hasattr(obj, "__package__")
                and (not isinstance(obj, dict))
            ):
                try:
                    submodule_name = f"{obj.__name__}.{attr}"
                    submodule = importlib.import_module(submodule_name)
                    setattr(obj, attr, submodule)
                    return submodule
                except ImportError:
                    pass
            raise AttributeError(
                f"Objek '{type(obj).__name__}' tidak memiliki atribut '{attr}'"
            )

    def visit_MethodCall(self, node):
        obj = self.visit(node.obj)
        method = node.method
        args = [self.visit(arg) for arg in node.args]
        if hasattr(obj, method) and callable(getattr(obj, method)):
            try:
                return getattr(obj, method)(*args)
            except KeyboardInterrupt:
                print(f"\n✓ Operasi '{method}' dihentikan oleh pengguna")
                return None
            except Exception as e:
                obj_type = type(obj).__name__
                raise RuntimeError(
                    f"Error saat memanggil metode '{method}' pada objek '{obj_type}': {str(e)}"
                ) from e
        if id(obj) in self.instance_scopes:
            class_name = obj.__class__.__name__
            if (
                class_name in self.classes
                and method in self.classes[class_name]["methods"]
            ):
                old_instance = self.current_instance
                old_local_scope = self.local_scope.copy()
                self.current_instance = id(obj)
                self.local_scope = {}
                params, body, return_type, param_types = self.classes[class_name][
                    "methods"
                ][method]
                self.local_scope["diri"] = obj
                if params and len(params) > 0:
                    start_param_idx = 1 if params[0] == "diri" else 0
                    expected_user_params = len(params) - start_param_idx
                    if len(args) != expected_user_params:
                        raise RuntimeError(
                            f"Metode '{method}' membutuhkan {expected_user_params} parameter, tetapi {len(args)} diberikan"
                        )
                    if param_types and len(param_types) > start_param_idx:
                        for i, (arg, type_hint) in enumerate(
                            zip(args, param_types[start_param_idx:])
                        ):
                            type_name = type_hint.type_name
                            if type_name in self.type_registry:
                                expected_type = self.type_registry[type_name]
                                try:
                                    if isinstance(expected_type, type) and not isinstance(arg, expected_type):
                                        raise TypeHintError(
                                            f"Parameter ke-{i + 1} '{params[i + start_param_idx]}' harus bertipe '{type_name}'"
                                        )
                                except TypeError:
                                    pass
                            elif hasattr(py_builtins, type_name):
                                expected_type = getattr(py_builtins, type_name)
                                try:
                                    if isinstance(expected_type, type) and not isinstance(arg, expected_type):
                                        raise TypeHintError(
                                            f"Parameter ke-{i + 1} '{params[i + start_param_idx]}' harus bertipe '{type_name}'"
                                        )
                                except TypeError:
                                    pass
                    for i, param_name in enumerate(params[start_param_idx:]):
                        self.local_scope[param_name] = args[i]
                elif len(args) != 0:
                    raise RuntimeError(
                        f"Metode '{method}' tidak membutuhkan parameter, tetapi {len(args)} diberikan"
                    )
                self.return_value = None
                self.visit_Block(Block(body))
                return_value = self.return_value
                if return_type and return_value is not None:
                    type_name = return_type.type_name
                    if type_name in self.type_registry:
                        expected_type = self.type_registry[type_name]
                        try:
                            if isinstance(expected_type, type) and not isinstance(return_value, expected_type):
                                raise TypeHintError(
                                    f"Nilai kembali metode '{method}' harus bertipe '{type_name}'"
                                )
                        except TypeError:
                            pass
                    elif hasattr(py_builtins, type_name):
                        expected_type = getattr(py_builtins, type_name)
                        try:
                            if isinstance(expected_type, type) and not isinstance(return_value, expected_type):
                                raise TypeHintError(
                                    f"Nilai kembali metode '{method}' harus bertipe '{type_name}'"
                                )
                        except TypeError:
                            pass
                self.current_instance = old_instance
                self.local_scope = old_local_scope
                self.return_value = None
                return return_value
        raise AttributeError(
            f"Objek '{type(obj).__name__}' tidak memiliki metode '{method}'"
        )

    def visit_Import(self, node):
        module = node.module
        alias = node.alias or module
        try:
            rmc_module = self._load_rmc_module(module)
            if rmc_module:
                self.modules[alias] = rmc_module
                self.global_scope[alias] = rmc_module
                if hasattr(rmc_module, "get_exports"):
                    exports = rmc_module.get_exports()
                    for name, value in exports.items():
                        self.global_scope[name] = value
                        if (
                            hasattr(self, "local_scope")
                            and self.local_scope is not None
                        ):
                            self.local_scope[name] = value
                return
            try:
                imported_module = __import__(
                    f"renzmc.builtins.{module}", fromlist=["*"]
                )
                self.modules[alias] = imported_module
                self.global_scope[alias] = imported_module
            except ImportError:
                imported_module = importlib.import_module(module)
                self.modules[alias] = imported_module
                self.global_scope[alias] = imported_module
        except ImportError:
            raise ImportError(f"Modul '{module}' tidak ditemukan")

    def _load_rmc_module(self, module_name):
        search_paths = [
            f"{module_name}.rmc",
            f"modules/{module_name}.rmc",
            f"examples/modules/{module_name}.rmc",
            f"lib/{module_name}.rmc",
            f"rmc_modules/{module_name}.rmc",
        ]
        if "__file__" in globals():
            script_dir = Path(__file__).parent
            search_paths.extend(
                [
                    str(script_dir / f"{module_name}.rmc"),
                    str(script_dir / "modules" / f"{module_name}.rmc"),
                    str(script_dir / "lib" / f"{module_name}.rmc"),
                ]
            )
        for file_path in search_paths:
            if os.path.exists(file_path):
                try:
                    with open(file_path, "r", encoding="utf-8") as f:
                        source_code = f.read()
                    from renzmc.core.lexer import Lexer
                    from renzmc.core.parser import Parser

                    module_interpreter = Interpreter()
                    lexer = Lexer(source_code)
                    parser = Parser(lexer)
                    ast = parser.parse()
                    module_interpreter.visit(ast)

                    class RenzmcModule:

                        def __init__(self, scope):
                            self._exports = {}
                            builtin_names = set(self._get_builtin_names())
                            for name, value in scope.items():
                                if (
                                    not name.startswith("_")
                                    and name not in builtin_names
                                    and (not name.startswith("py_"))
                                ):
                                    setattr(self, name, value)
                                    self._exports[name] = value

                        def _get_builtin_names(self):
                            return {
                                "tampilkan",
                                "panjang",
                                "jenis",
                                "ke_teks",
                                "ke_angka",
                                "huruf_besar",
                                "huruf_kecil",
                                "potong",
                                "gabung",
                                "pisah",
                                "ganti",
                                "mulai_dengan",
                                "akhir_dengan",
                                "berisi",
                                "hapus_spasi",
                                "bulat",
                                "desimal",
                                "akar",
                                "pangkat",
                                "absolut",
                                "pembulatan",
                                "pembulatan_atas",
                                "pembulatan_bawah",
                                "sinus",
                                "cosinus",
                                "tangen",
                                "tambah",
                                "hapus",
                                "hapus_pada",
                                "masukkan",
                                "urutkan",
                                "balikkan",
                                "hitung",
                                "indeks",
                                "extend",
                                "kunci",
                                "nilai",
                                "item",
                                "hapus_kunci",
                                "acak",
                                "waktu",
                                "tanggal",
                                "tidur",
                                "tulis_file",
                                "baca_file",
                                "tambah_file",
                                "file_exists",
                                "ukuran_file",
                                "hapus_file",
                                "json_ke_teks",
                                "teks_ke_json",
                                "url_encode",
                                "url_decode",
                                "hash_teks",
                                "base64_encode",
                                "base64_decode",
                                "buat_uuid",
                                "regex_match",
                                "regex_replace",
                                "regex_split",
                                "http_get",
                                "http_post",
                                "http_put",
                                "http_delete",
                                "panggil",
                                "daftar_direktori",
                                "buat_direktori",
                                "direktori_exists",
                            }

                        def get_exports(self):
                            return self._exports.copy()

                        def __getitem__(self, key):
                            return getattr(self, key)

                        def __contains__(self, key):
                            return hasattr(self, key)

                    return RenzmcModule(module_interpreter.global_scope)
                except Exception as e:
                    raise ImportError(
                        f"Gagal memuat modul RenzMC '{module_name}': {str(e)}"
                    )
        return None

    def visit_PythonImport(self, node):
        module = node.module
        alias = node.alias
        try:
            if not hasattr(self, "python_integration"):
                from renzmc.runtime.python_integration import PythonIntegration

                self.python_integration = PythonIntegration()
            wrapped_module = self.python_integration.import_python_module(module, alias)
            if alias:
                var_name = alias
                self.modules[var_name] = wrapped_module
                self.global_scope[var_name] = wrapped_module
            elif "." in module:
                parts = module.split(".")
                current_scope = self.global_scope
                current_modules = self.modules
                for i, part in enumerate(parts[:-1]):
                    if part not in current_scope:
                        parent_module_name = ".".join(parts[: i + 1])
                        try:
                            parent_module = importlib.import_module(parent_module_name)
                            wrapped_parent = (
                                self.python_integration.convert_python_to_renzmc(
                                    parent_module
                                )
                            )
                            current_scope[part] = wrapped_parent
                            current_modules[part] = wrapped_parent
                        except ImportError:
                            current_scope[part] = type("SimpleNamespace", (), {})()
                            current_modules[part] = current_scope[part]
                    current_scope = current_scope[part]
                    if hasattr(current_scope, "__dict__"):
                        current_scope = current_scope.__dict__
                    else:
                        break
                final_name = parts[-1]
                if hasattr(current_scope, "__setitem__"):
                    current_scope[final_name] = wrapped_module
                else:
                    setattr(current_scope, final_name, wrapped_module)
                self.modules[module] = wrapped_module
                self.global_scope[module.replace(".", "_")] = wrapped_module
            else:
                self.modules[module] = wrapped_module
                self.global_scope[module] = wrapped_module
        except Exception as e:
            raise RenzmcImportError(
                f"Modul Python '{module}' tidak ditemukan: {str(e)}"
            )

    def visit_PythonCall(self, node):
        func = self.visit(node.func_expr)
        args = [self.visit(arg) for arg in node.args]
        kwargs = {key: self.visit(value) for key, value in node.kwargs.items()}
        return self._call_python_function(func, *args, **kwargs)

    def visit_TryCatch(self, node):
        try:
            return self.visit_Block(Block(node.try_block))
        except Exception as e:
            for exception_type, var_name, except_block in node.except_blocks:
                should_catch = False
                if exception_type is None:
                    should_catch = True
                else:
                    try:
                        exc_type = eval(exception_type)
                        if isinstance(exc_type, type):
                            should_catch = isinstance(e, exc_type)
                    except Exception:
                        should_catch = True

                if should_catch:
                    if var_name:
                        self.set_variable(var_name, e)
                    return self.visit_Block(Block(except_block))
            raise e
        finally:
            if node.finally_block:
                self.visit_Block(Block(node.finally_block))

    def visit_Raise(self, node):
        exception = self.visit(node.exception)
        raise exception

    def visit_Switch(self, node):
        match_value = self.visit(node.expr)
        for case in node.cases:
            for case_value_node in case.values:
                case_value = self.visit(case_value_node)
                if match_value == case_value:
                    return self.visit_Block(Block(case.body))
        if node.default_case:
            return self.visit_Block(Block(node.default_case))
        return None

    def visit_Case(self, node):
        pass

    def visit_With(self, node):
        context_manager = self.visit(node.context_expr)
        if not (
            hasattr(context_manager, "__enter__")
            and hasattr(context_manager, "__exit__")
        ):
            raise TypeError(
                f"Objek tipe '{type(context_manager).__name__}' tidak mendukung context manager protocol"
            )
        context_value = context_manager.__enter__()
        if node.var_name:
            self.set_variable(node.var_name, context_value)
        try:
            result = self.visit_Block(Block(node.body))
            return result
        except Exception as e:
            exc_type = type(e)
            exc_value = e
            exc_traceback = e.__traceback__
            if not context_manager.__exit__(exc_type, exc_value, exc_traceback):
                raise
        finally:
            if not hasattr(self, "_exception_occurred"):
                context_manager.__exit__(None, None, None)

    def visit_IndexAccess(self, node):
        obj = self.visit(node.obj)
        index = self.visit(node.index)
        try:
            return obj[index]
        except (IndexError, KeyError):
            raise IndexError(
                f"Indeks '{index}' di luar jangkauan untuk objek tipe '{type(obj).__name__}'"
            )
        except TypeError:
            raise TypeError(
                f"Objek tipe '{type(obj).__name__}' tidak mendukung pengindeksan"
            )

    def visit_SliceAccess(self, node):
        obj = self.visit(node.obj)
        start = self.visit(node.start) if node.start else None
        end = self.visit(node.end) if node.end else None
        step = self.visit(node.step) if node.step else None
        try:
            return obj[start:end:step]
        except TypeError:
            raise TypeError(
                f"Objek tipe '{type(obj).__name__}' tidak mendukung slicing"
            )

    def visit_Lambda(self, node):
        params = node.params
        body = node.body
        param_types = node.param_types
        return_type = node.return_type

        def lambda_func(*args):
            if len(args) != len(params):
                raise RuntimeError(
                    f"Lambda membutuhkan {len(params)} parameter, tetapi {len(args)} diberikan"
                )
            if param_types:
                for i, (arg, type_hint) in enumerate(zip(args, param_types)):
                    type_name = type_hint.type_name
                    if type_name in self.type_registry:
                        expected_type = self.type_registry[type_name]
                        try:
                            if isinstance(expected_type, type) and not isinstance(arg, expected_type):
                                raise TypeHintError(
                                    f"Parameter ke-{i + 1} harus bertipe '{type_name}'"
                                )
                        except TypeError:
                            pass
                    elif hasattr(py_builtins, type_name):
                        expected_type = getattr(py_builtins, type_name)
                        try:
                            if isinstance(expected_type, type) and not isinstance(arg, expected_type):
                                raise TypeHintError(
                                    f"Parameter ke-{i + 1} harus bertipe '{type_name}'"
                                )
                        except TypeError:
                            pass
            old_local_scope = self.local_scope.copy()
            self.local_scope = {}
            for i in range(len(params)):
                self.set_variable(params[i], args[i], is_local=True)
            result = self.visit(body)
            if return_type:
                type_name = return_type.type_name
                if type_name in self.type_registry:
                    expected_type = self.type_registry[type_name]
                    try:
                        if isinstance(expected_type, type) and not isinstance(result, expected_type):
                            raise TypeHintError(
                                f"Nilai kembali lambda harus bertipe '{type_name}'"
                            )
                    except TypeError:
                        pass
                elif hasattr(py_builtins, type_name):
                    expected_type = getattr(py_builtins, type_name)
                    try:
                        if isinstance(expected_type, type) and not isinstance(result, expected_type):
                            raise TypeHintError(
                                f"Nilai kembali lambda harus bertipe '{type_name}'"
                            )
                    except TypeError:
                        pass
            self.local_scope = old_local_scope
            return result

        return lambda_func

    def visit_ListComp(self, node):
        var_name = node.var_name
        iterable = self.visit(node.iterable)
        if not hasattr(iterable, "__iter__"):
            raise TypeError(
                f"Objek tipe '{type(iterable).__name__}' tidak dapat diiterasi"
            )
        result = []
        old_local_scope = self.local_scope.copy()
        for item in iterable:
            self.set_variable(var_name, item, is_local=True)
            if node.condition:
                condition_result = self.visit(node.condition)
                if not condition_result:
                    continue
            expr_result = self.visit(node.expr)
            result.append(expr_result)
        self.local_scope = old_local_scope
        return result

    def visit_DictComp(self, node):
        var_name = node.var_name
        iterable = self.visit(node.iterable)
        if not hasattr(iterable, "__iter__"):
            raise TypeError(
                f"Objek tipe '{type(iterable).__name__}' tidak dapat diiterasi"
            )
        result = {}
        old_local_scope = self.local_scope.copy()
        for item in iterable:
            self.set_variable(var_name, item, is_local=True)
            if node.condition:
                condition_result = self.visit(node.condition)
                if not condition_result:
                    continue
            key_result = self.visit(node.key_expr)
            value_result = self.visit(node.value_expr)
            result[key_result] = value_result
        self.local_scope = old_local_scope
        return result

    def visit_SetComp(self, node):
        var_name = node.var_name
        iterable = self.visit(node.iterable)
        if not hasattr(iterable, "__iter__"):
            raise TypeError(
                f"Objek tipe '{type(iterable).__name__}' tidak dapat diiterasi"
            )
        result = set()
        old_local_scope = self.local_scope.copy()
        for item in iterable:
            self.set_variable(var_name, item, is_local=True)
            if node.condition:
                condition_result = self.visit(node.condition)
                if not condition_result:
                    continue
            expr_result = self.visit(node.expr)
            result.add(expr_result)
        self.local_scope = old_local_scope
        return result

    def visit_Generator(self, node):
        var_name = node.var_name
        iterable = self.visit(node.iterable)
        if not hasattr(iterable, "__iter__"):
            raise TypeError(
                f"Objek tipe '{type(iterable).__name__}' tidak dapat diiterasi"
            )
        old_local_scope = self.local_scope.copy()

        def gen():
            self.local_scope = old_local_scope.copy()
            for item in iterable:
                self.set_variable(var_name, item, is_local=True)
                if node.condition:
                    condition_result = self.visit(node.condition)
                    if not condition_result:
                        continue
                expr_result = self.visit(node.expr)
                yield expr_result

        return gen()

    def visit_Yield(self, node):
        if node.expr:
            value = self.visit(node.expr)
        else:
            value = None
        return value

    def visit_YieldFrom(self, node):
        iterable = self.visit(node.expr)
        if not hasattr(iterable, "__iter__"):
            raise TypeError(
                f"Objek tipe '{type(iterable).__name__}' tidak dapat diiterasi"
            )
        return list(iterable)

    def visit_Decorator(self, node):
        name = node.name
        args = [self.visit(arg) for arg in node.args]
        decorated = self.visit(node.decorated)
        if name in self.advanced_features.decorators:
            raw_decorator_func = self.advanced_features.decorators[name]
            try:
                from renzmc.runtime.advanced_features import RenzmcDecorator

                decorator_instance = RenzmcDecorator(raw_decorator_func, args)
                decorated_function = decorator_instance(decorated)
                if hasattr(node.decorated, "name"):
                    func_name = node.decorated.name
                    self._decorated_functions = getattr(
                        self, "_decorated_functions", {}
                    )

                    def original_func_callable(*call_args, **call_kwargs):
                        if func_name in self.functions:
                            params, body, return_type, param_types = self.functions[
                                func_name
                            ]
                            return self._execute_user_function(
                                func_name,
                                params,
                                body,
                                return_type,
                                param_types,
                                call_args,
                                call_kwargs,
                            )
                        else:
                            raise NameError(
                                f"Fungsi asli '{func_name}' tidak ditemukan"
                            )

                    original_func_callable.__name__ = func_name
                    if args:
                        actual_decorator = decorator_instance.actual_decorator
                        self._decorated_functions[func_name] = (
                            actual_decorator,
                            original_func_callable,
                        )
                    else:
                        self._decorated_functions[func_name] = (
                            raw_decorator_func,
                            original_func_callable,
                        )
                return decorated_function
            except Exception as e:
                raise RuntimeError(f"Error dalam dekorator '{name}': {str(e)}")
        if name in self.functions:
            try:
                if args:
                    decorator_factory = self._create_user_decorator_factory(name, args)
                    return decorator_factory(decorated)
                else:
                    user_decorator_func = self._create_user_function_wrapper(name)
                    from renzmc.runtime.advanced_features import RenzmcDecorator

                    decorator_instance = RenzmcDecorator(user_decorator_func)
                    return decorator_instance(decorated)
            except Exception as e:
                raise RuntimeError(f"Error dalam dekorator '{name}': {str(e)}")
        if hasattr(self, "decorators") and name in self.decorators:
            decorator_func = self.decorators[name]
            try:
                from renzmc.runtime.advanced_features import RenzmcDecorator

                decorator_instance = RenzmcDecorator(decorator_func, args)
                return decorator_instance(decorated)
            except Exception as e:
                raise RuntimeError(f"Error dalam dekorator '{name}': {str(e)}")
        raise NameError(f"Dekorator '{name}' tidak ditemukan")

    def visit_AsyncFuncDecl(self, node):
        name = node.name
        params = node.params
        body = node.body
        return_type = node.return_type
        param_types = node.param_types
        self.async_functions[name] = (params, body, return_type, param_types)
        self.functions[name] = (params, body, return_type, param_types, "ASYNC")

    def visit_AsyncMethodDecl(self, node):
        pass

    def visit_Await(self, node):
        coro = self.visit(node.expr)
        if asyncio.iscoroutine(coro):
            return self.loop.run_until_complete(coro)
        else:
            raise AsyncError(f"Objek '{coro}' bukan coroutine")

    def visit_TypeHint(self, node):
        return node.type_name

    def visit_TypeAlias(self, node):
        self.type_registry[node.name] = node.type_expr
        return None

    def visit_LiteralType(self, node):
        return node

    def visit_TypedDictType(self, node):
        return node

    def visit_FormatString(self, node):
        result = ""
        for part in node.parts:
            if isinstance(part, String):
                result += part.value
            else:
                try:
                    value = self.visit(part)
                    if value is not None:
                        result += str(value)
                    else:
                        result += "None"
                except Exception as e:
                    result += f"<Error: {str(e)}>"
        return result

    def visit_Ternary(self, node):
        condition = self.visit(node.condition)
        if condition:
            return self.visit(node.if_expr)
        else:
            return self.visit(node.else_expr)

    def visit_Unpacking(self, node):
        value = self.visit(node.expr)
        if not hasattr(value, "__iter__"):
            raise TypeError(
                f"Objek tipe '{type(value).__name__}' tidak dapat diiterasi"
            )
        return value

    def visit_WalrusOperator(self, node):
        value = self.visit(node.value)
        self.set_variable(node.var_name, value)
        return value

    def visit_SelfVar(self, node):
        if self.current_instance is None:
            raise NameError("Variabel 'diri' tidak dapat diakses di luar konteks kelas")
        if "diri" in self.local_scope:
            return self.local_scope["diri"]
        else:
            raise NameError("Variabel 'diri' tidak ditemukan dalam konteks saat ini")

    def visit_NoneType(self, node):
        return None

    def _smart_getattr(self, obj, name, default=None):
        try:
            if hasattr(obj, "_obj"):
                actual_obj = obj._obj
            else:
                actual_obj = obj
            result = getattr(actual_obj, name, default)
            if hasattr(obj, "_integration"):
                return obj._integration.convert_python_to_renzmc(result)
            elif hasattr(self, "python_integration"):
                return self.python_integration.convert_python_to_renzmc(result)
            else:
                return result
        except Exception as e:
            if default is not None:
                return default
            raise AttributeError(f"Error mengakses atribut '{name}': {str(e)}")

    def _smart_setattr(self, obj, name, value):
        try:
            if hasattr(obj, "_obj"):
                actual_obj = obj._obj
                converted_value = obj._integration.convert_renzmc_to_python(value)
            else:
                actual_obj = obj
                converted_value = self.python_integration.convert_renzmc_to_python(
                    value
                )
            setattr(actual_obj, name, converted_value)
            return True
        except Exception as e:
            raise AttributeError(f"Error mengatur atribut '{name}': {str(e)}")

    def _smart_hasattr(self, obj, name):
        try:
            if hasattr(obj, "_obj"):
                actual_obj = obj._obj
            else:
                actual_obj = obj
            return hasattr(actual_obj, name)
        except Exception:
            return False

    def interpret(self, tree):
        return self.visit(tree)

    def visit_SliceAssign(self, node):
        target = self.visit(node.target)
        start = self.visit(node.start) if node.start else None
        end = self.visit(node.end) if node.end else None
        step = self.visit(node.step) if node.step else None
        value = self.visit(node.value)
        try:
            slice_obj = slice(start, end, step)
            target[slice_obj] = value
        except Exception as e:
            self.error(f"Kesalahan dalam slice assignment: {str(e)}", node.token)

    def visit_ExtendedUnpacking(self, node):
        value = self.visit(node.value)
        if not isinstance(value, (list, tuple)):
            try:
                value = list(value)
            except (TypeError, ValueError) as e:
                self.error(
                    f"Nilai tidak dapat di-unpack: {type(value).__name__} - {e}",
                    node.token,
                )
        starred_index = None
        for i, (name, is_starred) in enumerate(node.targets):
            if is_starred:
                if starred_index is not None:
                    self.error(
                        "Hanya satu target yang dapat menggunakan * dalam unpacking",
                        node.token,
                    )
                starred_index = i
        num_targets = len(node.targets)
        num_values = len(value)
        if starred_index is None:
            if num_targets != num_values:
                self.error(
                    f"Jumlah nilai ({num_values}) tidak sesuai dengan jumlah target ({num_targets})",
                    node.token,
                )
            for (name, _), val in zip(node.targets, value):
                self.current_scope.set(name, val)
        else:
            num_required = num_targets - 1
            if num_values < num_required:
                self.error(
                    f"Tidak cukup nilai untuk unpack (dibutuhkan minimal {num_required}, ada {num_values})",
                    node.token,
                )
            for i in range(starred_index):
                name, _ = node.targets[i]
                self.current_scope.set(name, value[i])
            num_after_starred = num_targets - starred_index - 1
            starred_count = num_values - num_required
            starred_name, _ = node.targets[starred_index]
            starred_values = value[starred_index : starred_index + starred_count]
            self.current_scope.set(starred_name, list(starred_values))
            for i in range(num_after_starred):
                target_index = starred_index + 1 + i
                value_index = starred_index + starred_count + i
                name, _ = node.targets[target_index]
                self.current_scope.set(name, value[value_index])

    def visit_StarredExpr(self, node):
        value = self.visit(node.expr)
        if isinstance(value, (list, tuple)):
            return value
        try:
            return list(value)
        except (TypeError, ValueError) as e:
            self.error(
                f"Nilai tidak dapat di-unpack: {type(value).__name__} - {e}", node.token
            )

    def visit_PropertyDecl(self, node):
        prop = property(fget=node.getter, fset=node.setter, fdel=node.deleter)
        self.current_scope.set(node.name, prop)
        return prop

    def visit_StaticMethodDecl(self, node):
        static_func = staticmethod(node.func)
        self.current_scope.set(node.name, static_func)
        return static_func

    def visit_ClassMethodDecl(self, node):
        class_func = classmethod(node.func)
        self.current_scope.set(node.name, class_func)
        return class_func
