ماژول‌ها

    کوروش دارای یک سیستم ماژول قدرتمند است که برای کامپایل افزایشی (incremental compilation) و ذخیره‌سازی کارا طراحی شده است. ماژول‌ها مرزهای واضحی برای حوزه‌بندی، سازماندهی کد و قابلیت استفاده مجدد فراهم می‌کنند.

    فایل‌های ماژول

    در کوروش، هر فایل یک ماژول است.

    • نام‌گذاری: فایل‌ها باید از snake_case استفاده کنند.
    • پسوند: فایل‌ها باید به .cyrus ختم شوند.
    • قابلیت مشاهده: به طور پیش‌فرض، همه چیز خصوصی است. فقط نمادهایی که با کلمه کلیدی pub علامت‌گذاری شده‌اند، صادر شده و برای سایر ماژول‌ها قابل دسترسی هستند.

    مثال: auth_utils.cyrus یک ماژول به نام auth_utils تعریف می‌کند.

    // auth_utils.cyrus
    pub fn login(user: char*) {
        // ...
    }
    
    fn internal_helper() {
        // خارج از این فایل قابل مشاهده نیست
    }
    

    وارد کردن ماژول‌ها

    از کلمه کلیدی import برای آوردن سایر ماژول‌ها یا نمادهای خاص به حوزه جاری استفاده می‌کنید.

    Import پایه

    کل ماژول را وارد می‌کند. نمادها از طریق جداکننده فضای نام :: قابل دسترسی هستند.

    import auth_utils;
    
    pub fn main() {
        auth_utils::login("Cyrus");
    }
    

    نمادهای تکی و نام‌دار

    می‌توانید به‌صورت انتخابی نمادها را وارد کنید تا از تکرار نام ماژول جلوگیری شود.

    import auth_utils{login};
    
    pub fn main() {
        login("Cyrus");
    }
    

    Importهای گروهی

    برای خوانایی بهتر، کوروش از گروه‌بندی چندین import پشتیبانی می‌کند. جدا کردن importهای کتابخانه استاندارد/libc از ماژول‌های پروژه محلی، ایدئوماتیک است.

    import (
        std::libc{printf},
        std::math{fabs}
    );
    
    import (
        auth_utils{login},
        network_ops as net
    );
    

    تغییر نام (نام‌گذاری مستعار)

    از کلمه کلیدی as برای تغییر نام ماژول‌ها یا نمادها به منظور حل تداخل‌های نام‌گذاری استفاده کنید.

    import math_ops as m;           // تغییر نام یک ماژول
    import user_utils{greet as hi}; // تغییر نام یک نماد
    

    وضوح‌بخشی ماژول

    کوروش importها را بر اساس محیط و پیکربندی پروژه وضوح می‌بخشد:

    ۱. کامپایل تک‌فایل

    اگر مستقیماً یک فایل واحد را کامپایل می‌کنید، ماژول‌ها نسبت به دایرکتوری آن فایل وضوح می‌یابند.

    ۲. کامپایل پروژه

    هنگام کامپایل یک پروژه که حاوی Project.toml است، کامپایلر importها را با استفاده از مسیرهای تعریف‌شده در کلید sources وضوح می‌بخشد.

    [compiler]
    sources = ["./src", "./vendor"]
    

    اگر my_mod را import کنید، کامپایلر ابتدا در ./src و سپس در ./vendor به دنبال my_mod.cyrus می‌گردد.

    ۳. فضای نام std

    ماژول‌هایی که با std:: شروع می‌شوند (مانند std::libc یا std::math) برای کتابخانه استاندارد کوروش محفوظ هستند و از مسیرهای کتابخانه داخلی کامپایلر وضوح می‌یابند.

    فایل ورودی (فایلی که به عنوان نقطه ورودی اصلی به کامپایلر ارسال می‌شود) به عنوان ماژول ریشه (Root Module) در نظر گرفته می‌شود. در حالی که ریشه می‌تواند ماژول‌های دیگر را import کند، نمی‌تواند توسط آنها import شود تا از وابستگی‌های حلقوی در سطح بالا جلوگیری گردد.

    مسیرهای تودرتو و واجد شرایط کامل

    کوروش از import کردن ماژول‌های تودرتو با استفاده از جداکننده :: پشتیبانی می‌کند. همچنین می‌توانید از مسیر کاملاً واجد شرایط (fully qualified path) یک ماژول مستقیماً در کد خود استفاده کنید، اگر والد یا خود ماژول را import کرده‌اید.

    import std::libc;
    
    pub fn main() {
        // دسترسی از طریق مسیر کامل
        std::libc::printf("Hello World\n");
    }
    
    // import کردن یک نماد خاص از یک مسیر عمیق
    import graphics::utils::canvas{draw_rect};
    

    فضاهای نام داخلی

    در حالی که هر فایل یک ماژول است، می‌توانید کد را درون یک فایل واحد با استفاده از کلمه کلیدی mod بیشتر سازماندهی کنید. این کار یک فضای نام تودرتو ایجاد می‌کند.

    • pub mod: از خارج فایل قابل دسترسی است.
    • mod: خصوصی برای فایلی است که در آن تعریف شده است.

    مثال: foo.cyrus

    import std::libc;
    
    pub mod graphics {
        // فقط در داخل فضای نام 'graphics' در دسترس است
        const INTERNAL_SCALE = 1.0;
    
        // از خارج قابل دسترسی است
        pub const DEFAULT_COLOR = 0xFFFFFF;
    
        pub fn render() {
            libc::printf("Rendering...\n");
            prepare(); // فراخوانی داخلی
        }
    
        fn prepare() {
            libc::printf("Preparing buffer...\n");
        }
    }
    
    mod internal_logic {
        // کل این فضای نام برای foo.cyrus خصوصی است
    }
    

    دسترسی به فضاهای نام

    هنگام import یک فایل، می‌توانید فضاهای نام داخلی آن را دقیقاً مانند هر نماد دیگری به حوزه جاری بیاورید.

    // main.cyrus
    import foo::graphics;
    import foo{graphics as gfx};
    
    pub fn main() {
        graphics::render();
        const c = graphics::DEFAULT_COLOR;
    
        // graphics::prepare();      // خطا: prepare عمومی نیست
        // graphics::INTERNAL_SCALE; // خطا: INTERNAL_SCALE عمومی نیست
        // foo::internal_logic;      // خطا: internal_logic برای foo.cyrus خصوصی است
    }
    

    یکتایی ماژول و تداخل‌ها

    کوروش یک قانون نام‌گذاری سختگیرانه را برای جلوگیری از ابهام در درخت ماژول اعمال می‌کند. نام یک ماژول باید در دایرکتوری والد خود یکتا باشد، صرف‌نظر از اینکه فایل باشد یا پوشه.

    یک ماژول نمی‌تواند به طور همزمان به صورت فایل و دایرکتوری وجود داشته باشد.

    project/
      ├── auth.cyrus
      └── auth/
           └── index.cyrus
    
    
    خطا: تداخل تشخیص داده شد!
    

    اگر کامپایلر هم auth.cyrus و هم یک دایرکتوری به نام auth/ پیدا کند، یک خطای کامپایل ایجاد می‌کند، زیرا مسیر import یعنی import auth; مبهم خواهد بود.

    ماژول‌های دایرکتوری

    کوروش به شما اجازه می‌دهد با استفاده از یک فایل index.cyrus، یک دایرکتوری را به عنوان یک ماژول واحد در نظر بگیرید. این برای سازماندهی یک ماژول بزرگ به چندین فایل زیرمجموعه و در عین حال ارائه یک API تمیز در سطح بالا مفید است.

    اگر یک دایرکتوری حاوی index.cyrus باشد، نام دایرکتوری به نام ماژول تبدیل می‌شود.

    ساختار مثال:

    my_lib/
      ├── index.cyrus
      ├── internal_math.cyrus
      └── internal_io.cyrus
    

    در داخل my_lib/index.cyrus، می‌توانید نمادها را صادر یا تعریف کنید:

    // my_lib/index.cyrus
    import my_lib::internal_math{add};
    pub fn run() { ... }
    

    هنگامی که my_lib را import می‌کنید، در واقع با محتویات index.cyrus تعامل دارید:

    import my_lib;
    
    pub fn main() {
        my_lib::run(); // دسترسی به کد از index.cyrus
    }
    

    این الگو برای ماژول‌های «نما (facade)» ایده‌آل است، جایی که index.cyrus منطق داخلی را از سایر فایل‌های همان دایرکتوری import کرده و API عمومی را مجدداً صادر می‌کند.