Skip to content

المطابقةُ النحويّةُ بالبناءِ لا بالنيّة

مجموعةُ مطابقةٍ عاديّةٌ تُجيبُ سؤالَ «أتنجحُ هذه الاختبارات؟». والسؤالُ الأهمُّ الذي لا تُجيبُه: أيُّ قاعدةٍ لا اختبارَ لها أصلًا؟ فالتغطيةُ الغائبةُ لا تُخفِقُ؛ إنّها ببساطةٍ غيرُ موجودة.

الجوابُ ههنا بنيويّ: اسمُ مجلَّدِ البذورِ هو معرّفُ القاعدةِ حرفًا.

tests/behavior/rules_matrix/
└── 10_statements/
    └── gr.stmt.break/        ← الاسمُ = المعرّفُ في language-truth/grammar/
        ├── basic/    001_in_while.ص …
        ├── edge/     001_inner_only.ص …
        └── negative/ 001_top_level.ص …

فالقاعدةُ بلا مجلَّدٍ يُقاسُ غيابُها بفرقِ مجموعتَين، لا بحدسِ مراجع.

١ · الأرقامُ، وكيف تُعادُ

bash
python - <<'EOF'
import glob, os, yaml
ids = set()
for f in glob.glob("language-truth/grammar/*.yaml"):
    if os.path.basename(f) in ("lowers_to.yaml", "_notation.yaml"):
        continue
    def walk(o):
        if isinstance(o, dict):
            if isinstance(o.get("id"), str) and o["id"].startswith("gr."):
                ids.add(o["id"])
            for v in o.values(): walk(v)
        elif isinstance(o, list):
            for v in o: walk(v)
    walk(yaml.safe_load(open(f, encoding="utf-8")))
root = "tests/behavior/rules_matrix"
dirs = {d for c in os.listdir(root) if os.path.isdir(f"{root}/{c}")
          for d in os.listdir(f"{root}/{c}") if os.path.isdir(f"{root}/{c}/{d}")}
print("قواعدُ مُعلَنة:", len(ids))
print("مجلَّدات:", len(dirs), "· منها بمعرّفِ قاعدة:", len(ids & dirs))
print("قواعدُ بلا مجلَّد:", sorted(ids - dirs))
EOF
المقيسالقيمة
قواعدُ نحوٍ مُعلَنةٌ (معرّفاتٌ فريدة)١٠٧
مجلَّداتٌ في rules_matrix١٢٦
منها اسمُها معرّفُ قاعدة١٠٣
ملفّاتُ بذورٍ تحتَ rules_matrix٣٬٠٥٥

المجلَّداتُ الثلاثةُ والعشرونَ الزائدةُ ليست قواعدَ: _combos و_generated ومجلَّداتُ تداخلٍ بأسماءٍ موضوعيّةٍ (إذا_تداخلات، شرط_متغير_نطاق، …). والأربعُ بلا مجلَّد: gr.adv.ffi_ctype · gr.adv.ffi_extern_block · gr.adv.ffi_linkage · gr.adv.property_test.

٢ · المرساةُ وسمٌ لا موضعٌ فحسب

المجلَّدُ ليس المقياسَ الوحيد. كلُّ بذرةٍ تحملُ في سطرِها الأوّلِ وسمًا:

sad
# @rule: gr.stmt.break
# @description: توقف في بينما
# @expected: 0
1

فبذرةُ تداخلٍ في _interactions/ تشهدُ لعدّةِ قواعدَ دفعةً واحدة. وبعدّ الوسومِ لا المجلَّدات، الصورةُ تتغيّر: قاعدتانِ فقط بلا شاهدٍ في الشجرةِ كلِّها — gr.adv.ffi_ctype وgr.adv.property_test. والحارسُ check_anchor_integrity.py يطبعُها بندًا مستقلًّا:

② مُعلَنٌ بلا مرساة — دَينُ شهادةٍ نازلٌ لا يُرفَع:
    النحو                    105 / 107   · بلا شهادة:    2 (السقف 2)
① مرساةٌ يتيمة (ترسو على غيرِ مُعلَن): 0 (المسموح 0 — لا سقفَ له)

الاتّجاهانِ محروسانِ معًا: مُعلَنٌ بلا شاهدٍ سقفُه ٢ ولا يُرفَع، وشاهدٌ يرسو على قاعدةٍ مخترَعةٍ صفرٌ بلا سقف. فوسمُ @rule ليس تعليقًا؛ هو مفتاحُ ربطٍ يُحَلُّ في مصدرِ الحقيقة.

٣ · أزواجُ التداخلِ مشتقّةٌ من النحوِ لا من حاصلِ ضرب

gen_rules_matrix.py يبني رسمَ قابليّةِ التركيبِ من مصدرِ الحقيقةِ نفسِه: القاعدةُ A تحتوي B إن أشارت alternatives إلى { kind: nonterminal, ref: gr.B }. فالأزواجُ المطلوبةُ هي حوافُ الرسم لا ١٠٧×١٠٧ زوجًا أعمى.

bash
python scripts/codegen/check_grammar_conformance.py
④ تغطية الأزواج (تداخل قاعدتين قابلتين للتركيب نحويًا):
  مُغطّى 164/170 — ناقص 6

٤ · ما يحرسُه البناءُ فعلًا — وما لا يحرسُه

تصريحٌ يلزمُ ألّا يُطوى

check_grammar_conformance.py — الذي يقيسُ ميزانيّاتِ الفئاتِ لكلِّ قاعدة — يُخفِقُ اليومَ برمزِ ١ ولا يُشغَّلُ في x.py gen --check. المُشغَّلُ في البوّابةِ هو gen_rules_matrix.py --check، وهو يحرسُ السلامةَ المرجعيّةَ وحدَها:

✓ [rules_matrix --check] سلامة مرجعيّة: 107 قاعدة · 3055 ملف اختبار
  · لا وسوم @rule مجهولة.

فالمحروسُ في الدمجِ ثلاثةٌ: أن يكونَ لكلِّ قاعدةٍ مدخلٌ، وألّا يرسوَ وسمٌ على قاعدةٍ لا وجودَ لها، وألّا ينموَ دَينُ الشهادةِ فوقَ سقفِه النازل. أمّا عمقُ التغطيةِ لكلِّ فئةٍ فمقيسٌ ومنشورٌ وليس بوّابة.

٥ · النحوُ يعلنُ ما يُقبَل، ولا يعلنُ ما يجبُ رفضُه

هذا أهمُّ قيدٍ مفهوميٍّ في المسارِ كلِّه. مدخلُ القاعدةِ في language-truth/grammar/ يحملُ ebnf وalternatives — أي لغةَ المقبول. ولا يحملُ حقلًا يقولُ «وهذه الصياغةُ يجبُ أن تُرفَضَ بالرمزِ كذا».

وينتجُ عن ذلك أثرٌ عمليٌّ صريح: بذرةٌ في negative/ تدّعي رفضًا قد لا يكونُ خلفَها قاعدةٌ في مصدرِ الحقيقة. فهي رأيُ بذرةٍ لا قاعدةُ لغة. والمقيسُ اليومَ بـpython scripts/codegen/check_seed_contract.py:

④ وسمٌ سالبٌ بلا رمزِ خطأ            360 (السقف 360 — نازل)

ثلاثُمئةٍ وستّونَ وسمًا سالبًا لا يُسمّي رمزَ خطأٍ من الكتالوج. الوسمُ السالبُ الذي يُسمّي رمزًا يُقابَلُ به الكتالوجُ فعلًا — والمِحقنةُ تُثبِتُ ذلك بحقنِ SEM999 والتحقّقِ من احمرارِ الحارس.

خمسةُ رموزٍ لا تُدرَّسُ

INT008 · INT009 · INT010 · RUN059 · SEM027 مُعلَنةٌ retired لأنّ تعريفَها نفسَه كان المفسّرَ المحذوف. توجيهُ بذرةٍ سالبةٍ إليها يحملُ سلوكَ محرّكٍ محذوفٍ في ثوبِ قاعدةٍ مُعلَنة، و check_retired_error_codes.py يمنعُ عودةَ باعثٍ لها.

٦ · حدودٌ مقيسة

  • عشرٌ من ١٠٧ قاعدةً وسمُها experimental — كلُّها في 60_advanced (FFI الثلاث، الأسمبليّ المضمَّن ولهجتُه، وخمسُ قواعدِ واجهة).
  • وصفٌ إنجليزيٌّ على قاعدةٍ واحدةٍ من ١٠٧، ومثالٌ على ٥٢ منها. الوصفُ العربيُّ على ١٠٧ كلِّها.
  • أربعُ بذورٍ بلا وسمِ @rule أصلًا في 20_declarations/gr.decl.variable/negative/ — يطبعُها فاحصُ المطابقةِ بندًا ② ولا يُوقِفُ الدمج.

مُرخَّص بموجب رخصة MIT