diff --git "a/text/0034-\331\206\331\202\331\204-\330\256\330\247\330\261\330\254\331\212-\330\245\331\204\331\211-\331\205\331\210\330\266\330\271-\330\247\331\204\330\265\331\201\330\251-\330\247\331\204\331\204\330\247\330\255\331\202\330\251.md" "b/text/0034-\331\206\331\202\331\204-\330\256\330\247\330\261\330\254\331\212-\330\245\331\204\331\211-\331\205\331\210\330\266\330\271-\330\247\331\204\330\265\331\201\330\251-\330\247\331\204\331\204\330\247\330\255\331\202\330\251.md" new file mode 100644 index 0000000..4e16ecd --- /dev/null +++ "b/text/0034-\331\206\331\202\331\204-\330\256\330\247\330\261\330\254\331\212-\330\245\331\204\331\211-\331\205\331\210\330\266\330\271-\330\247\331\204\330\265\331\201\330\251-\330\247\331\204\331\204\330\247\330\255\331\202\330\251.md" @@ -0,0 +1,245 @@ +- **عنوان المقترح:** نقل صفة الربط الخارجيّ إلى موضع الصفة اللاحقة بمطابقة الجنس — «دالة خارجية» بدل «خارجي دالة» +- **النطاق:** لغة `text/` (المحلّل النحويّ + مصدر الحقيقة + الأدوات المستهلِكة) +- **تاريخ البدء:** 2026-07-11 +- **رقم الـ RFC:** 0034 +- **الحالة:** مسودّة +- **Issue التتبُّع:** (يُترك فارغاً حتى القبول) + +# ملخّص + +تصريح الربط الخارجيّ (FFI) هو البنية الوحيدة المتبقّية في لغة ص التي تضع +المعدِّل **قبل** «دالة»: `خارجي دالة اسم(...)`. هذا يخالف المبدأ المعلَن في +مصدر الحقيقة «**الصفة بعد الموصوف**» (`20_declarations.yaml:29,53` وعموم +`30_oop.yaml`)، ويخالف السابقة المباشرة التي أزالت `غير_متزامن دالة` لصالح +`دالة غير_متزامن` بحاجز هجرة صريح نصُّه «في العربية الصفة تأتي بعد الاسم» +(`shared/parser/src/core/parser_main.cpp` بعد السطر ~568). المقترح — **بقرار +مالك اللغة**: نقل صفة الربط الخارجيّ إلى موضع الصفة اللاحقة **مع مطابقة +الموصوف في الجنس** («دالة» مؤنّثة ⇒ «خارجية»): + +```sad +دالة خارجية printf(نص) +دالة خارجية("cos") عشري جيب_التمام(عشري) +``` + +وتُرسى بهذا **قاعدة عامّة**: لفظ الصفة يتبع جنس الموصوف الذي قبلها — +«خارجية» بعد موصوف مؤنّث (دالة)، و«خارجي» بعد موصوف مذكّر (إن استُحدث +موصوفٌ مذكّر يقبل هذه الصفة مستقبلًا). مع إبقاء **صيغة الكتلة** +`خارجي "C" ... نهاية` كما هي (فهي بنية حاوية لا صفة لدالة مفردة — انظر +§القسم 3)، وحاجز هجرة توجيهيّ للنمط القديم على نمط حاجز `غير_متزامن دالة` +القائم. + +# الدافع (Motivation) + +1. **اتّساق نحويّ عربيّ**: كلّ معدِّلات لغة ص لاحقة اليوم. `خارجي دالة` هو + الاستثناء الوحيد الباقي من ترتيب الصفة الإنجليزيّ (`extern function`). +2. **الفصاحة لا الموضع وحده**: نقل الموضع مع إبقاء التذكير («دالة خارجي») + يستبدل خطأً نحويًّا بآخر — تنافر تأنيث ظاهر لكلّ قارئ عربيّ. لغةٌ شعارها + السيادة اللغويّة لا تكتفي بنصف القاعدة: **الصفة بعد الموصوف، وتطابقه + جنسًا**. +3. **سابقة ملزمة منطقيًّا**: اللغة أزالت فعليًّا `غير_متزامن دالة` بحاجز خطأ + توجيهيّ. إبقاء `خارجي دالة` يجعل رسالة ذلك الحاجز متناقضة مع بنية حيّة + أخرى في اللغة نفسها. +4. **نافذة زمنيّة**: FFI ما يزال محدود الانتشار في برامج `.ص` (صيغة الكتلة + `experimental` في SoT)، فتكلفة الهجرة اليوم أدنى ممّا ستكون لاحقًا. + +# الشرح التوجيهي (Guide-level explanation) + +بعد المقترح، تُكتب تصاريح الربط الخارجيّ المفردة هكذا: + +```sad +# الشكل الأساسيّ — الصفة بعد الموصوف بمطابقة الجنس +دالة خارجية printf(نص) + +# نوع الإرجاع الاختياريّ — يبقى قبل اسم الدالة (كما في دالة عاديّة) +دالة خارجية رقم clear_screen() + +# اسم الربط الاختياريّ — يبقى ملتصقًا بالصفة بين قوسين +دالة خارجية("cos") عشري جيب_التمام(عشري) +``` + +وتبقى كتلة الربط كما هي (بنية حاوية تسبق دوالّ متعدّدة): + +```sad +خارجي "C" + دالة رقم getchar() + دالة putchar(رقم) +نهاية +``` + +ومن يكتب النمط القديم يتلقّى رسالة توجيهيّة (لا انهيارًا غامضًا): + +``` +خطأ نحوي: 'خارجي دالة' أُزيلت. استخدم 'دالة خارجية' بدلاً منها. +💡 في العربية الصفة تأتي بعد الاسم وتطابقه في الجنس: دالة خارجية printf(نص) +💡 مثال: + دالة خارجية("cos") عشري جيب_التمام(عشري) +``` + +## 1) الصيغة المقترحة للمفرد ومواضع اسم الربط والنوع + +الصيغة الكاملة المقترحة (EBNF) — بنصّ قرار المالك: + +``` +ExternDeclaration = 'دالة' 'خارجية' [ '(' StringLiteral ')' ] [ Type ] + Identifier '(' [ Parameters ] ')' ; +``` + +- **لفظ الصفة**: «خارجية» هنا لأنّ الموصوف «دالة» مؤنّث. القاعدة المعياريّة: + **الصفة تطابق جنس الكلمة التي قبلها** — فلو استُحدث مستقبلًا موصوف مذكّر + يقبل صفة الربط (مثلًا متغيّر خارجيّ) لكان لفظها معه «خارجي». +- **موضع اسم الربط**: يبقى بين قوسين **ملتصقًا بالصفة مباشرةً** — + `دالة خارجية("رمز")`. المسوّغ: اسم الربط **معمولُ الصفة** لا معمول الدالة؛ + هو يجيب عن سؤال «خارجيّةٌ بأيّ رمز؟»، فبقاؤه لصيق الصفة يحفظ الدلالة + ويُبقي التحليل النحويّ محدَّدًا (بعد «خارجية»: قوس ⇒ اسم ربط؛ غير ذلك ⇒ + نوع/اسم). البديل المرفوض `دالة("رمز") خارجية` يفصل المعمول عن عامله ويلتبس + باستدعاء دالة. +- **موضع نوع الإرجاع**: يبقى **قبل اسم الدالة** — `دالة خارجية رقم اسم()` — + اتّساقًا حرفيًّا مع الدالة العاديّة `دالة رقم اسم()` (gr.decl.function). + ترتيب «خارجية ثمّ النوع» محسوم بأنّ الصفة للدالة كلّها بينما النوع جزء + من توقيعها؛ ولأنّ المحلّل الحيّ أصلًا يفصل الموضعين (اسم الربط يُلتقط في + `parseDeclaration` والنوع في `parseExternFunctionDecl`) فالتغيير التنفيذيّ + محصور في نقطة دخول واحدة. +- **الترتيب مع معدِّلات أخرى**: «خارجية» تنافي دلاليًّا `غير_متزامن` + والمولّدات (التصريح بلا جسم)، فلا مسألة ترتيب مركّب اليوم. إن استُحدث + مستقبلًا معدِّل متوافق، يسري عرف اللغة: المعدِّلات تتوالى بعد «دالة». + +## 2) قاعدة مطابقة الجنس — قرار المالك وأثره الرجعيّ + +**القرار (مالك اللغة، 2026-07-11): الصفة تطابق الموصوف جنسًا** — «دالة +خارجية» لا «دالة خارجي». هذا يعكس التوصية الأوّليّة للمسودّة (التي رجّحت +المذكّر الثابت اتّساقًا مع «دالة ساكن» القائمة) ويحسم الاتّجاه المعياريّ: +الفصاحة العربيّة الكاملة مقدَّمة على الاتّساق مع عرفٍ قائم معيب. + +الأثر المباشر على المعجم والأدوات: + +- «خارجية» تدخل المعجم (كتالوج المفردات في SoT) لفظًا معياريًّا لصفة الربط + بعد موصوف مؤنّث. «خارجي» تبقى في المعجم لموضعين: افتتاح الكتلة + (`خارجي "C"`)، والصفة بعد موصوف مذكّر إن استُحدث. +- **تبعة معياريّة مفتوحة**: العرف الحاليّ «دالة ساكن»، «دالة عامة/عام»، + «دالة مجرد» يصير بعد هذا القرار **شذوذًا معلَنًا** عن قاعدة المطابقة. + مواءمة بقيّة المعدِّلات المؤنَّثة («ساكنة»، «مجردة»، ...) **RFC مستقلّ + لاحق** يطالها دفعة واحدة بمسار هجرة خاصّ — هذا الـRFC يرسي القاعدة ويطبّقها + على صفة الربط حصرًا، ولا يعدّل المعدِّلات القائمة. + +## 3) مصير صيغة الكتلة `خارجي "C" ... نهاية` + +الكتلة **لا يمكن قلبها** لصفة لاحقة: «خارجي» فيها لا تصف دالةً مفردة بل تفتح +**نطاق ربطٍ** يضمّ دوالّ متعدّدة تسبقها. القلب الحرفيّ (`"C" خارجي`؟) عبث، +وتعليق الصفة على كلّ دالة داخليّة يلغي جدوى الكتلة أصلًا. + +**التوصية: إبقاء الكتلة كما هي، مع إعادة توصيفها لا كصفة بل كبنية حاوية.** +المسوّغ الاتّساقيّ: لغة ص تعرف أصلًا كلماتٍ تفتتح بنى حاوية باسمها — +«فضاء اسم ... نهاية»، «صنف اسم ... نهاية». «خارجي "C"» من هذا الجنس: **خبرٌ +عن النطاق** لا نعتٌ لدالة، والدوالّ داخلها تُكتب `دالة اسم(...)` بلا تكرار +الصفة (هذا هو السلوك الحيّ فعلًا — `parser_main.cpp:517-557`). وبذلك يصير +التقسيم الدلاليّ نظيفًا: + +- **صفة لاحقة** للتصريح المفرد: `دالة خارجية [("رمز")] [نوع] اسم(...)`. +- **بنية حاوية** للنطاق: `خارجي "نصّ_الربط" { دالة ... } نهاية`. + +بديل دُرس ورُفض: استحداث رأسٍ صريح مثل `كتلة خارجي "C"` — يضيف كلمة/تركيبًا +جديدًا بلا مكسب دلاليّ، ويكسر كلّ كود الكتل القائم مجّانًا. + +تفصيلة حسم إضافيّة: بعد المقترح يظلّ التمييز النحويّ عند «خارجي» الافتتاحيّة +قاطعًا — نصّ حرفيّ عارٍ بعدها ⇒ كتلة؛ أيّ شيء آخر بعد «خارجي» في صدر تصريح ⇒ +حاجز الهجرة (§4). ويُحسم هنا سؤال فرعيّ: **هل يجوز اسم ربط لدالة داخل كتلة؟** +التوصية: نعم لاحقًا كامتداد طبيعيّ (`دالة("رمز") ...` داخل الكتلة تلتبس +بالاستدعاء، أمّا `دالة خارجية("رمز")` داخل الكتلة فتكرار للصفة) — يُرحَّل +كسؤال غير محسوم ولا يعطّل هذا الـRFC. + +## 4) مسار الهجرة — قطيعة فوريّة بقرار المالك + +**قرار المالك (2026-07-11): لا توافق خلفيّ ولا نافذة دعم مزدوج** — تُعتمد +الصيغة الجديدة وحدها فورًا. (المسودّة الأولى اقترحت ثلاث مراحل متدرّجة على +نمط إهلاك `غير_متزامن`؛ أُسقطت لصالح القطيعة لأنّ FFI محدود الانتشار +والمستودعات المستهلِكة معدودة وتحت سيطرة المشروع.) + +- **إصدار واحد (هذا الـRFC)**: المحلّل يقبل `دالة خارجية ...` حصرًا، والصيغ + القديمة (`خارجي دالة ...` و`خارجي("رمز") دالة ...`) تصير **خطأً نحويًّا + بحاجز توجيهيّ** يستهلك الرموز ويكمل التعافي (كما يفعل حاجز `غير_متزامن` + اليوم — `parser_main.cpp:568-591`): رسالة ثنائيّة اللغة عبر `errorBilingual` + جوهرها: «'خارجي دالة' أُزيلت. استخدم 'دالة خارجية' — في العربية الصفة تأتي + بعد الاسم وتطابقه في الجنس» + مثال تصحيحيّ. +- كتلة `خارجي "نصّ"` لا يمسّها التغيير إطلاقًا (نصّ عارٍ بعد «خارجي» ⇒ كتلة؛ + أيّ شيء آخر ⇒ الحاجز). +- إزالة الحاجز نفسه لاحقًا (تنظيف) تُجدوَل كحواجز مماثلة، مع إبقاء رسالة + افتراضيّة واضحة («توقّع نصّ ربط بعد 'خارجي'») كي لا يرتدّ النمط القديم + إلى خطأ غامض. + +أداة مساعدة (اختياريّة): قاعدة تصحيح تلقائيّ في LSP/نِبراس (QuickFix يقلب +`خارجي دالة` إلى `دالة خارجية`) — التحويل نصّيّ حتميّ خالٍ من الالتباس، +فهو مرشّح مثاليّ لإصلاح آليّ شامل للمستودعات المستهلِكة. + +## 5) الأثر على مصدر الحقيقة والمستهلكين + +- **`gr.decl.extern` (`language-truth/grammar/20_declarations.yaml:243`)**: + استبدال EBNF وalternatives بصيغة قرار المالك أعلاه + (`'دالة' 'خارجية' [ '(' StringLiteral ')' ] [ Type ] Identifier ...`) + وتحديث description_ar والأمثلة. (ملاحظة: سدّ فجوتَي اسم الربط ونوع الإرجاع + في هذه القاعدة **يجري مستقلًّا الآن** كتوثيقٍ للواقع الحيّ؛ هذا الـRFC + يبني فوقه فلا تعارض.) +- **كتالوج المفردات (SoT)**: تسجيل «خارجية» لفظًا معياريًّا جديدًا (مقترنة + بـKEYWORD قائمة أو جديدة حسب معمار المعجم) — مع ملاحظة قاعدة المطابقة. +- **`gr.adv.ffi_extern_block` و`gr.adv.ffi_linkage` (`60_advanced.yaml:439,463`)**: + لا تغيير نحويًّا (القرار §3 يبقي الكتلة)؛ يلزم فقط تحديث description_ar + ليعكس التوصيف الجديد (بنية حاوية مقابل صفة لاحقة) والإحالة المتبادلة. +- **المحلّل**: نقطتا تعديل — التقاط الصفة في `parseFunctionDecl` (الصيغة + الجديدة)، وتحويل فرع `KEYWORD_EXTERN` في `parseDeclaration` إلى: كتلة + (نصّ عارٍ) أو حاجز هجرة (أيّ شيء آخر). `parseExternFunctionDecl` نفسها + بلا تغيير تقريبًا (التوقيع الداخليّ ثابت). +- **AST/المفسّر/المترجم**: **صفر تغيير** — العقدة `FunctionDecl` بحقلَي + `isExtern`/`linkName` كما هي؛ التغيير نحويّ سطحيّ بحت. +- **الاختبارات**: كلّ اختبارات FFI الموسومة بميزانيّة gr.decl.extern + (basic 4/edge 3/negative 2/interaction 1) تُحدَّث للصيغة الجديدة + اختبارات + سلبيّة جديدة للحاجز (النمط القديم ⇒ الرسالة التوجيهيّة) قياسًا على اختبارات + حاجز `غير_متزامن`. اختبارات الكتلة (gr.adv.ffi_extern_block) بلا مساس. +- **الأدوات المستهلكة**: tmGrammar (تلوين `دالة خارجية`)، المقتطفات + (gen_snippets — مقتطف extern)، LSP (الإكمال + الرموز الدلاليّة)، REPL + (كشف الكتل متعدّدة الأسطر لا يتأثّر — «نهاية» للكتلة فقط)، الوثائق المولَّدة + (`docs/parser_rule/_generated` تتجدّد آليًّا من SoT)، sadlang-docs، + والأمثلة والمشاريع المستهلِكة المكتوبة بـ`خارجي دالة` (هجرة نصّيّة حتميّة). +- **التوافق الخلفيّ**: كاسر عمدًا وفوريًّا للصيغة المفردة القديمة — قطيعة + بلا نافذة دعم مزدوج بقرار المالك (§4)، مع حاجز توجيهيّ وتحويل آليّ ممكن. + صيغة الكتلة متوافقة تمامًا. + +# السلبيات (Drawbacks) + +- كسر توافق خلفيّ حقيقيّ (وإن كان FFI قليل الانتشار بعد). +- ازدواج لفظيّ مؤقّت في المعجم: «خارجية» صفة المفرد و«خارجي» افتتاحيّة + الكتلة — يُخفَّف بالتوصيف الدلاليّ الواضح (§3) وبقاعدة المطابقة المعلنة. +- القرار يجعل المعدِّلات القائمة غير المطابِقة («دالة ساكن») شذوذًا معلَنًا + حتى يلحقها RFC المواءمة (§2) — فجوة اتّساق مقصودة ومؤقّتة. +- تكلفة تحديث سلسلة الأدوات كاملة (وإن كان معظمها مولَّدًا من SoT آليًّا). + +# المبرّرات والبدائل (Rationale and alternatives) + +- **عدم الفعل**: يُبقي تناقضًا معلنًا مع مبدأ SoT ومع رسالة حاجز حيّة في + المحلّل — كلفة مصداقيّة تتفاقم مع انتشار FFI. +- **قبول الصيغتين للأبد**: يضاعف سطح الاختبار والتوثيق ويميّع القاعدة + النحويّة؛ رُفض كما رُفض لـ`غير_متزامن`. +- **`دالة خارجي` (مذكّر ثابت)**: كان توصية المسودّة الأولى اتّساقًا مع + «دالة ساكن»؛ **رفضه مالك اللغة** لأنّه يستبدل مخالفةً نحويّة (الموضع) + بأخرى (المطابقة) — القاعدة العربيّة تؤخذ كاملة. +- **قلب الكتلة أو استبدالها**: نوقش ورُفض في §3. + +# أعمال سابقة (Prior art) + +- **لغة ص نفسها**: إزالة `غير_متزامن دالة` — السابقة الحاكمة شكلًا ومسارًا + (حاجز توجيهيّ ثنائيّ اللغة ثمّ إزالة). +- **Rust**: `extern "C" fn` و`extern "C" { ... }` — تعايش صيغة مفردة وكتلة + بنفس الكلمة، وهو ما نستبقيه مع نقل موضع الصفة عربيًّا. +- **Zig**: المعدِّلات (`export`/`extern`) لصيقة التصريح بموضع ثابت — قيمة + القاعدة الواحدة بلا استثناء. +- الخصوصيّة العربيّة تقلب اتّجاه الاقتباس مرّتين: ما هو «بادئة» لاتينيًّا + حقّه عربيًّا التأخير عن الموصوف، وما هو لفظ ثابت لاتينيًّا حقّه عربيًّا + المطابقة في الجنس. + +# أسئلة غير محسومة (Unresolved questions) + +- هل يُسمح باسم ربط لدالة **داخل** كتلة `خارجي "C"` (اقتراح مبدئيّ: + `دالة خارجية("رمز") ...` داخل الكتلة)، أم يبقى اسم الربط حكرًا على المفرد؟ +- نطاق وجدولة **RFC مواءمة المعدِّلات القائمة** مع قاعدة المطابقة + («ساكن»⇒«ساكنة» بعد دالة، ...) — يُفتح مستقلًّا (§2). +- موعد إزالة الحاجز التوجيهيّ نفسه (التنظيف النهائيّ) — حُسم عدم وجود نافذة + دعم مزدوج (§4)، وبقي فقط جدول تقاعد رسالة الحاجز. +- هل يُشحن التصحيح الآليّ (QuickFix) مع دفعة التنفيذ نفسها أم يلحقها.