صفحات التوثيق
التقييم في وقت الترجمة
تحدد هذه الصفحة تعابير وقت الترجمة وكتله (وقت_الترجمة)، ومعاملات وقت الترجمة، ودوال الانعكاس
والتحقق، وتعليقات التصريحات (annotations) والمزخرفات (decorators)، والتأكيدات المفعلة دائما في
std.debug.assert. وهي موجهة إلى المبرمجين الذين ينقلون العمل من وقت التشغيل إلى وقت الترجمة، وإلى المساهمين
الذين يعملون على المقيِّم.
كيف يعمل التقييم
الواجهة الأمامية (frontend) مسؤولة عن الصياغة والأنواع. ويحوّل خفض MSIR كل التزام من التزامات وقت الترجمة إلى
MSIR عادي متحقق منه، ويستوفيه منسق المترجم قبل أن ترى أي خلفية أو مرحلة التشغيل في المفسر البرنامج
(compiler/driver/compile_time.sn). مفسر MSIR المتحقق منه هو المنفذ الوحيد، ولا تُكرر نسخة منه
لتنفيذ وقت الترجمة. ويعمل بميزانية للتعليمات، وميزانية لعمق الاستدعاءات، وبسياسة التأثيرات الخاصة بتنفيذ وقت
الترجمة، التي لا تسمح بأي إدخال أو إخراج ولا بأي حالة من المضيف. والإخفاق رسالة تشخيص في وقت الترجمة، وليس أبدا
رجوعا إلى وقت التشغيل؛ فلا بديل في وقت التشغيل لتعبير صُرّح بأنه يُقيَّم في وقت الترجمة. والقيم الخاصة بالهدف
تتبع النموذج المسجل للهدف المختار.
ضمن هذه السياسة يجوز للعملية الأساسية (primitive) أن تغير قيمة يملكها التقييم (الإلحاق، والتبديل، وتخزين طول
المتجه في أزل_الأخير واقتطع)، وأن تحرر التخزين الخاص بالمفسر (drop glue)، فتستطيع شيفرة وقت الترجمة أن تبني المتجهات
والنصوص وتقلصها. أما العمليات الأساسية التي تطّلع على ذاكرة الجهاز أو على المضيف فتبقى محظورة
(SULTANC-COMPTIME-PROHIBITED-EFFECT).
سجل الرموز المعتمد للمحلل المعجمي هو tools/generate/registry/token_registry.def، ويجب أن تتطابق واجهتا الرموز
المولدتان بلغة C وبلغة السلطان. ولا يلزم البذرة (seed) المكتوبة بلغة C في Stage0 أن تستخدم صياغة جديدة وهي تترجم الدعم
الذي تضيفه الواجهة الأمامية لها؛ لكن بمجرد أن يستخدم مصدر المترجم نفسه تركيبا ما، يجب إما تحديث البذرة أو أن
يبقى المصدر متوافقا مع Stage0.
التعابير والكتل
- يُقيَّم
وقت_الترجمة توصيغة التعبير بين معقوفينوقت_الترجمة { ت1; ت2; }(تعابير فقط، وآخرها يعطي القيمة) عبر شريحة الاعتماديات الخاصة بالقيمة. ويُنفذ المفسر استدعاءات الدوال العادية الموجودة في الشريحة. - تُخفَّض الجملة
وقت_الترجمة { الجمل }إلى دالة MSIR مستقلة من النوعفراغ، تُنفذ مرة واحدة ثم تُزال. - كتلة القيمة تسند نتيجة جمل إلى موضع. وتُكتب مهيِّئا لـ
دعأو بعد:=. التعبير الأخير في الكتلة، أو جملة استدعاء أخيرة، هو القيمة. تشكل الكتلة وقيمتها دالة MSIR مستقلة ترجع نوع الموضع، وتُقيَّم مرة واحدة، ويستقبل الموضع الثابت الناتج. ومثل كتلة الجملة، لا يمكنها استخدام المتغيرات المحلية للدالة المحيطة ولا الرجوع منها، ويجب أن تكون قيمتها قيمة قابلة للتمثيل في وقت الترجمة: أعداد صحيحة، وقيم منطقية، وحروف، ونصوص، ومتجهات منها.
عرف الرئيسية(): عدد {
دع المربعات: عدد = وقت_الترجمة {
دع المجموع: متغير عدد = 0;
دع ف: متغير عدد = 1;
طالما (ف <= 4) { المجموع := المجموع + ف * ف; ف := ف + 1; }
المجموع
};
أرجع المربعات;
}
التعابير الفرعية الصريحة المتداخلة لوقت الترجمة، مثل وقت_الترجمة (وقت_الترجمة 20) + (وقت_الترجمة { 22; })، تُقيَّم
عبر مقيِّم الثوابت نفسه، دون سياق تنفيذ منفصل. وتوجد أمثلة بالإنجليزية والعربية في
tests/{en,ar}/language/comptime/nested_constant_expression.sn.
تصريحات دع على مستوى الوحدة ثوابت تُقيَّم في وقت الترجمة بدلالة وقت التشغيل نفسها. والعملية
التي كانت ستسبب توقفا قسريا (trap) في وقت التشغيل تجعل الثابت خطأ SULTANC-COMPTIME-EVALUATION-FAILED
(انظر نظام الأنواع).
معاملات وقت الترجمة
الدالة التي لها معامل وقت_الترجمة قالب.
- إذا كانت كل المعاملات
وقت_الترجمة، قُيّم كل استدعاء في وقت الترجمة. - وإلا فإن كل استدعاء يُخصَّص. يجب أن تكون وسائط وقت الترجمة فيه معروفة في وقت الترجمة (ثوابت، أو
نتائج وقت الترجمة، أو اسم دالة مستخدم قيمةً)، ويُوجَّه الاستدعاء إلى نسخة من الدالة
تكون فيها تلك المعاملات بتلك القيمة ولا يبقى فيها إلا معاملات وقت التشغيل. تُشارك النسخ بحسب الزوج (الدالة،
وسائط وقت الترجمة)، فتُنشأ نسخة واحدة لكل مجموعة مختلفة من وسائط وقت الترجمة. وداخل النسخة
يجوز لتعابير
وقت_الترجمةأن تستخدم معاملات وقت الترجمة. ولا يُنشأ أكثر من 4096 نسخة، ولذلك فإن العودية على معامل وقت ترجمة دون حالة أساس في وقت الترجمة تنتج رسالة تشخيص (comptime.specialization-limit-exceeded).
عرف قياس(وقت_الترجمة معامل: عدد, س: عدد): عدد { أرجع س * وقت_الترجمة (معامل + 0); }
عرف الرئيسية(): عدد {
دع ع: عدد = 7;
أرجع قياس(6, ع); // مخصصة للقيمة 6
}
معامل وقت الترجمة ثابت في النسخة في كل موضع يمكن لتعبير وقت_الترجمة أن يستخدمه فيه: وحده
(وقت_الترجمة معامل)، وفي العمليات الحسابية، ووسيطا في استدعاء (وقت_الترجمة ضعف(معامل))، وفي كتلة بعد تدفق تحكم
في وقت التشغيل. تعرّف النسخة المعامل في كتلة الدخول الخاصة بها، فيبذر تقييم تعبير وقت_الترجمة
ثوابت الدالة كلها، لا ثوابت كتلته وحدها: كل تعليمة ثابتة، وكل معامل كتلة
تمرر إليه كل الحواف الواردة من الكتل الممكن بلوغها الثابت نفسه
(ابذر_ثوابت_خارج_شريحة_وقت_الترجمة، compiler/driver/compile_time.sn).
الفروع التي تحسمها وسائط وقت الترجمة
في النسخة المخصصة، يُطوى إذا في وقت التشغيل (أو && أو || أو شرط طالما) الذي تحسم شرطه وسائط وقت
الترجمة قبل عمل وقت الترجمة في النسخة، فلا يُحلَّل إلا الفرع المأخوذ
(اطو_فروع_تخصيص_وقت_الترجمة). يُقيَّم شرط الفرع، وكل قيمة تمررها كتلة ممكن بلوغها عبر حوافها،
حيث لا يؤدي إليه إلا ثوابت وعمليات حسابية على الأعداد الصحيحة ومقارنات وتحويلات، والمتغير المحلي الذي يبقيه
الخفض في موضع (معامل يُقرأ بعد إذا) يُمرَّر من عملية التخزين الوحيدة فيه. ولا يُطوى أي شيء يحتاج إلى استدعاء أو
إلى بيانات وقت التشغيل. ونتيجة لذلك:
- لا تُقيَّم أبدا تعابير
وقت_الترجمةواستدعاءات قوالب وقت الترجمة الواقعة على مسار مطوي؛ - يؤدي
فشل_وقت_الترجمةالمتبقي على مسار ممكن البلوغ في النسخة إلى فشل الترجمة برسالته (SULTANC-COMPTIME-FAILED)، سواء أخذت وسائط وقت الترجمة ذلك المسار أم حسمته بيانات وقت التشغيل. ويُرفض دائمافشل_وقت_الترجمةالذي جرى تحليله، ولا يكون أبدا توقفا قسريا في وقت التشغيل في الشيفرة المخصصة.
استورد المكتبة.انعكاس.reflect;
عرف فحص(وقت_الترجمة ن: عدد, س: عدد): عدد {
إذا (ن > 10) { فشل_وقت_الترجمة(«ن يجب ألا تزيد على 10»); };
أرجع ن + س;
}
عرف الرئيسية(): عدد {
دع س: عدد = 7;
أرجع فحص(4, س); // تترجم: الفرع مطوي
// فحص(42, س) ستفشل: SULTANC-COMPTIME-FAILED: ن يجب ألا تزيد على 10
}
الاختبارات: tests/{ar,en}/language/comptime/parameter_in_comptime_expression.sn وfolded_compile_error.sn
وreject_folded_compile_error.sn وreject_runtime_branch_compile_error.sn.
الانعكاس والتحقق
توفر std.meta.reflect (المكتبة.انعكاس.reflect) حقائق عن النوع الذي يشير إليه المرجع. ويطويها منسق المترجم
إلى ثوابت للهدف المختار قبل أي تقييم أو توليد للشيفرة.
| الدالة | النتيجة |
|---|---|
حجم_النوع(&ق) |
حجم النوع بالبايت في الهدف (مالك التخطيط backend/layout) |
محاذاة_النوع(&ق) |
المحاذاة في الهدف |
عدد_حقول_النوع(&ق) |
حقول السجل أو عناصر الصف (tuple)، وإلا 0 |
عدد_منشئات_النوع(&ق) |
منشئات التعداد، وإلا 0 |
فشل_وقت_الترجمة(الرسالة) |
يُفشل التقييم في وقت الترجمة |
استورد المكتبة.انعكاس.reflect;
نوع زوج = { اليسار: ص32, اليمين: ص32 };
نوع شكل = | دائرة | مربع | مثلث;
عرف الرئيسية(): عدد {
دع ز: زوج = { اليسار: 1, اليمين: 2 };
دع ش: شكل = شكل.مربع();
دع الحجم: ط64 = حجم_النوع(&ز);
دع الحقول: ط64 = عدد_حقول_النوع(&ز);
دع المنشئات: ط64 = عدد_منشئات_النوع(&ش);
أرجع (الحجم + الحقول + المنشئات) مثل عدد;
}
النوع الذي لم يُثبَّت تخطيطه الفعلي عمدا (النصوص، والمتجهات، والشرائح، وقيم الدوال) ليس له حجم ولا
محاذاة، وطلب أحدهما رسالة تشخيص في وقت الترجمة. والوصول إلى فشل_وقت_الترجمة أثناء التقييم في وقت
الترجمة، أو بقاؤه على مسار ممكن البلوغ في نسخة مخصصة، يبلّغ SULTANC-COMPTIME-FAILED برسالته.
وفي الدالة العادية، يسبب مسار وقت التشغيل الذي يصل إليه توقفا قسريا. والعمليات الأساسية للمترجم التي تقف خلف هذه الدوال
(بدائي_حجم_النوع وغيرها) مسجلة مثل كل عملية أساسية أخرى للمترجم.
الفحوص التي يضيفها المترجم
فحص التنسيق في std.fmt التزام من التزامات وقت الترجمة يضيفه الخفض قبل استدعاء دالة تنسيق من المكتبة القياسية
تكون سلسلة التنسيق فيه قيمة حرفية. وهو استدعاء لدالة الفحص الخاصة بـ std.fmt مع القيمة الحرفية وأنواع الوسائط،
يُقيّمه المفسر مثل أي استدعاء في وقت الترجمة ثم يُزال (التنسيق، قسم الفحص في وقت
الترجمة). وتشترك كل الفحوص من هذا النوع في الترجمة الواحدة في سياق مفسر واحد على البرنامج المتحقق منه
(أنشئ_سياق_وقت_الترجمة_مسير).
تعليقات التصريحات والمزخرفات
@عام هو معدِّل التصريح العام. وأي تعليق آخر على دالة مزخرِف: دالة
عادية تُطبَّق على الدالة المزخرَفة، وتُحل مثل أي هدف استدعاء (@م، @م(وسائط)،
@وحدة.م(وسائط)).
@م(و1, و2) عرف د(س: ن): ر { الجسم }
// تُربط د بـ: عرف د(ص: ن): ر { أرجع م(و1, و2, <د الأصلية>, ص); }
يستقبل المزخرِف وسائطه، والدالة الأصلية قيمةً من النوع دالة(ن...): ر، ووسائط الاستدعاء،
ويرجع ر. ويُفحص كل ذلك بوصفه استدعاء عاديا، بما في ذلك ملكية الوسائط
المُمرَّرة. تتركب المزخرفات من التصريح إلى الخارج، فـ @أ @ب عرف د هي أ مطبقة على (ب مطبقة على
د). والتصريح بمعامل الدالة في المزخرِف على أنه وقت_الترجمة يخصص كل تطبيق في وقت الترجمة بقيمة
دالة مباشرة، ويستطيع المزخرِف حينها التحقق من وسائط وقت الترجمة الخاصة به. وتُقيَّم وسائط المزخرِف
عند كل استدعاء.
عرف زد_واحدا(وقت_الترجمة د: دالة(عدد): عدد, س: عدد): عدد { أرجع د(س) + 1; }
@زد_واحدا
عرف ثلاثة(س: عدد): عدد { أرجع س * 3; }
عرف الرئيسية(): عدد {
دع ع: عدد = 7;
أرجع ثلاثة(ع);
}
تجري الواجهة الأمامية التوسيع على نص مصدري يُلحق بعد الوحدة
(compiler/frontend/annotations/decorators.sn)، ولذلك تشير رسائل التشخيص في تطبيق المزخرِف إلى ذلك النص
المولد. وتُرفض التعليقات على الأنواع وقيم الوحدة، وعلى الدوال الخارجية أو العامة (generic) أو التي لها معاملات
وقت ترجمة، بالخطأ SULTANC-ANNOTATION-UNSUPPORTED. والسلوك مثل تسجيل مسارات HTTP والتخزين المؤقت (cache)
أو اكتشاف الاختبارات من شأن المكتبات، لا المترجم.
مزخرفات وقت التعريف
@@د(وسائط) عرف ف يترك ف كما كُتبت وتستدعي نقطة الدخول د(وسائط، ف) مرة قبل جسمها، بترتيب الوحدات والتصريحات،
كمزخرفات التسجيل في Python وinit في Go: يضيف @@get("/users") المعالج إلى جدول الموجه. لا يرجع المزخرف قيمة.
JSON المشتق
@@json (@@جسون) على نوع سجل أو تعداد يولد من التصريح (انعكاس وقت الترجمة كاشتقاق serde): جسون__T
(to_json__T)، ومن_جسون__T (from_json__T)، والمرمز الملحق ألحق_جسون__T، والمفكك من قيمة محللة، ومخطط_جسون__T
(json_schema__T) لـ OpenAPI. السجل كائن بترتيب الحقول، والاختياري null حين يغيب ويجوز غيابه في المدخل، والمتجه
مصفوفة، والمنشئ بلا حمولات نص وغيره {"tag": ..., "values": [...]}. وتسمي أخطاء التفكيك مسار الحقل.
std.debug.assert
التصريح المعتمد هو تأكد(الشرط: منطقي)، وله اسم بديل بالإنجليزية. وتأخذ تأكد_برسالة
قيمة منطقية وسلسلة رسالة مستعارة. والدالتان مفعلتان دائما، بما في ذلك في
الناتج المحسَّن ونسخ الإصدار. تقيّمان الشرط مرة واحدة بالضبط، وعند الإخفاق تكتبان رسالة التشخيص على
الخرج القياسي للأخطاء وتنهيان العملية بحالة الخروج 1. وتستخدمان إمكانات std.io وstd.process
الموجودة، لا عملية أساسية من عمليات المترجم. انظر الأخطاء لمعرفة إطار الإخفاق.
والدالتان موسومتان بـ@@يتتبع_المستدعي: تطبعان تحت الإطار موضع النداء الذي فشل (انظر
الاختبار).
استورد المكتبة.تصحيح.assert;
عرف الرئيسية(): عدد {
تأكد(2 + 2 == 4);
دع الرسالة: نص = «اختل الثابت»;
تأكد_برسالة(صواب, &الرسالة);
أرجع 0;
}
تعبير الرسالة الاختياري يقيّمه المستدعي بوصفه وسيط دالة عاديا، ولذلك فهو، بخلاف الماكرو
assert! في Rust، ليس كسولا. ويحتاج التأكيد الفاشل إلى تأثيرات std.io وstd.process في المفسر كي
يكتمل، وهو ليس رسالة تشخيص في وقت الترجمة.
الميزانية والكلفة والتشخيص
يعمل كل تقييم وقت ترجمة بميزانية عشرة ملايين تعليمة MSIR وعمق نداء 128. تأتي الميزانية من
--comptime-budget=N (تقبل الشرطات السفلية)، وإلا من متغير البيئة SULTANC_COMPTIME_BUDGET، وإلا فالقيمة
الافتراضية. والكتلة التي تقوم بعمل أكبر عن قصد ترفع ميزانيتها بـميزانية_وقت_الترجمة(ن) (comptime_budget
في std.meta) تُستدعى في دالة تشغلها الكتلة: فمن تلك النقطة يجوز للتقييم تشغيل ن تعليمة أخرى. ولا يفعل
النداء شيئا خارج التقييم وقت الترجمة.
من std.meta استورد ميزانية_وقت_الترجمة;
عرف جدول(): عدد {
ميزانية_وقت_الترجمة(60000000);
// ... حساب طويل
أرجع 0;
}
عرف الرئيسية(): عدد { دع ج: عدد = وقت_الترجمة { جدول() }; أرجع ج; }
تُحسب كل تعليمة MSIR واحدة أيا كان عملها: ألحق_من واحجز_سعة واقتطع وألحق والطول خطوة واحدة كل منها،
واستنسخ لمتجه من قيم بسيطة (لا تخزين مملوكا في عناصره) نسخ كتلة واحد. والكتابة عبر مسار حقل
((*س).الجدول[ف] := ق) كلفتها واحدة أيا كان حجم الجدول؛ فالتجميع يحدَّث في مكانه. ويشغّل المقيِّم المسارات
السريعة للمفسر محتسبا كل تعليمة على الميزانية.
ويجوز لشيفرة وقت الترجمة استعمال قيم الدوال (جدول مسارات من دوال معالجة، دالة تمرر حجة) وقراءة ثوابت
الوحدات غير القابلة للتغيير (جداول البحث، كالتي ينسق بها std.text الأعداد العشرية). وتبقى حالة الوحدات
القابلة للتغيير، والتخزين المستورد، والمؤشرات الخام، ونداءات المضيف مرفوضة.
ويُبلَّغ عن الإخفاق داخل التقييم في سطر المصدر المخفق، مع الدالة التي كان فيها، ونطاق «استُدعي من هنا» لكل نداء بين كتلة وقت الترجمة وذلك السطر (يظهر نداء التكرار مرة واحدة)، و:
- في
SULTANC-COMPTIME-LIMIT-EXCEEDEDالحد الذي بلغه (الميزانية، أو العمق 128) وكيف ترفع الميزانية؛ - في
SULTANC-COMPTIME-PROHIBITED-EFFECTالعملية المرفوضة بلغة الرسالة (نداء دالة خارجية، نداء بدائي من بدائيات المترجم، مؤشر خام مصنوع من مرجع، …) واسم الدالة أو البدائي في حالة النداء.
القيود
- يجب أن تكون نتائج وقت الترجمة قيما قابلة للتمثيل في وقت الترجمة: أعداد صحيحة، وقيم منطقية، وحروف، ونصوص، ومتجهات منها.
- لا يمكن لكتلة القيمة ولا لكتلة الجملة استخدام المتغيرات المحلية للدالة المحيطة ولا الرجوع منها.
- لا يُنشأ أكثر من 4096 تخصيصا للبرنامج الواحد.
- التعليقات على الأنواع، وقيم الوحدة، والدوال الخارجية، والدوال العامة (generic)، والدوال التي لها معاملات وقت ترجمة غير مدعومة.
- لتنفيذ وقت الترجمة ميزانية للتعليمات (قابلة للضبط، انظر أعلاه) وعمق استدعاءات أقصاه 128، ولا إدخال ولا إخراج فيه ولا حالة من المضيف.