Skip to content
Original file line number Diff line number Diff line change
@@ -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) مع دفعة التنفيذ نفسها أم يلحقها.
Loading