Skip to content

اتبعْ قاعدةً من مصدرِ الحقيقةِ إلى الاختبارِ الذي يحرسُها

في هذا الدرسِ تتبعُ قاعدةً واحدةً بعينِهاgr.stmt.break، أي الكلمةَ توقف — من سطرِها في مصدرِ الحقيقةِ حتّى الحارسِ الذي يحمرُّ لو انفصلت عن شاهدِها. تنتهي وقد ترجمتَ وشغَّلتَ ورأيتَ التنزيل.

ما تحتاجُه

مستودعُ اللغةِ مبنيًّا، والثنائيُّ في build/bin/Release/sad-build.exe. والاستعمالُ خطوتان دائمًا: sad-build.exe file.ص -o file.exe ثمّ تشغيلُ الثنائيّ. لا مفسّرَ ولا REPL ولا آلةَ بايتكود — محرّكٌ واحد.

الخطوة ١ · اقرأِ المدخلَ في مصدرِ الحقيقة

القاعدةُ في language-truth/grammar/10_statements.yaml:

yaml
- id: gr.stmt.break
  lhs: { nonterminal: BreakStatement, name_ar: "جملة توقف", name_en: break_statement }
  ebnf: "BreakStatement = 'توقف' ;"
  alternatives:
    - symbols: [{ kind: terminal, ref: KEYWORD_BREAK, value: "توقف" }]
  references: { keywords: [KW-RES-017] }
  maps_to:
    - file: shared/parser/src/statements/parser_statements.cpp
      function: ParserCore::parseBreakStmt
  ast_node: BreakStmt
  since: "1.0.0"
  status: stable
  conformance:
    compiler_optional: false
    test_budget: { basic: 3, edge: 3, negative: 1, interaction: 2 }

خمسةُ أشياءَ ههنا معرّفاتٌ تُحَلُّ، لا نثر: الرمزُ الطرفيُّ KEYWORD_BREAK، ومعرّفُ الكلمةِ KW-RES-017، وموضعُ التحليلِ maps_to، وعقدةُ الشجرة، وميزانيّةُ الاختبارات.

الخطوة ٢ · حُلَّ الإحالةَ إلى كتالوجِ الكلمات

bash
grep -n "KW-RES-017" language-truth/keywords.yaml
- { id: "KW-RES-017", subcategory: "control_flow", since: "1.0.0",
    word: "توقف", tokenType: KEYWORD_BREAK, english: break }

الكلمةُ العربيّةُ ورمزُها الطرفيُّ في مكانٍ واحدٍ في المستودعِ كلِّه، ومنه يُولَّدُ جدولُ الكلماتِ في المُعجَم. وcheck_grammar_terminal_refs.py يمنعُ قاعدةً تُحيلُ إلى رمزٍ لا وجودَ له في هذا الكتالوج.

الخطوة ٣ · اذهبْ إلى مجلَّدِ المرساة

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

bash
ls tests/behavior/rules_matrix/10_statements/gr.stmt.break/*/
basic/    001_in_while.ص  002_in_for.ص  003_first_iter.ص
edge/     001_inner_only.ص … 050_break_nested_loop.ص
negative/ 001_top_level.ص … 004_break_in_switch_outside_loop.ص

ستَّ عشرةَ بذرة. والفئاتُ الثلاثُ تقابلُ test_budget أعلاه.

الخطوة ٤ · ترجِمْ بذرةً وشغِّلْها

basic/001_in_while.ص كما هي في الشجرة:

sad
# @rule: gr.stmt.break
# @description: توقف في بينما
# @priority: P1
# @requires: اطبع_سطر
# @expected: 0\n1

متغير ع = 0
بينما (ع < 10)
    إذا (ع == 2) توقف نهاية
    اطبع_سطر(ع)
    ع = ع + 1
نهاية
bash
sad-build.exe 001_in_while.ص -o brk.exe
./brk.exe
0
1

المخرَجُ يطابقُ @expected حرفًا. لاحظْ أنّ الوسمَ عقدٌ يُحاكَمُ، لا تعليق: check_seed_contract.py يعدُّ البذورَ التي تُشغَّلُ بلا عقدٍ ويضربُ عليها سقفًا نازلًا.

الخطوة ٥ · انظرْ إلى التنزيل

bash
sad-build.exe 001_in_while.ص --أظهر-sir -o brk2.exe
define void @__sad_main() {
entry:
    %%ع = alloc
    store 0, %%ع
    br label %while_cond_0
while_cond_0:
    %%2 = lt %%ع, 10
    br.cond %%2, label %while_body_1, label %while_exit_2
while_body_1:
    %%4 = eq %%ع, 2
    br.cond %%4, label %while_exit_2, label %merge_4
while_exit_2:
    ret.void
merge_4:
    builtin.print %%ع
    ...
}

🔑 لا أوپكودَ اسمُه BREAK. توقف صارت br.cond تقفزُ إلى while_exit_2 مباشرةً. وهذا بالضبطِ ما يجعلُ جدولَ التنزيلِ مشتقًّا بالتنفيذِ لا مكتوبًا باليد: كاتبٌ بشريٌّ كان سيخترعُ خانةً باسمِ القاعدة.

الخطوة ٦ · اقرأِ الصفَّ المشتقَّ وشواهدَه

في language-truth/grammar/lowers_to.yaml:

yaml
- id: gr.stmt.break
  status: derived
  lowers_to: [ADD_I64, ALLOC, ARRAY_GET, ARRAY_LEN, ARRAY_NEW, ARRAY_SET,
              BR, BR_COND, BUILTIN_PRINT, EQ, LOAD, LT, MOVE, RET_VOID, STORE]
  native_ok: [arm64, x86_64]
  evidence: [001_in_while.ص, 002_in_for.ص, 003_first_iter.ص]

evidence يُسمّي البذورَ الثلاثَ التي اشتُقَّ منها الصفّ — أُولاها هي التي ترجمتَها للتوّ. والدعوى قابلةٌ للتكذيبِ بخطوةٍ واحدة.

الخطوة ٧ · شغِّلْ مجموعةَ القاعدةِ كلَّها

bash
python tests/runner.py --compiler build/bin/Release/sad-build.exe \
  --dir rules_matrix/10_statements/gr.stmt.break
  إجمالي: 16
  نجح:    15
  فشل:    0
  تخطي:   1

والمتخطّاةُ ليست مسكوتًا عنها؛ سببُها مكتوبٌ في رأسِها:

sad
# @rule: gr.stmt.break
# @description: توقف من داخل «حاول» تُنفّذ «أخيراً» قبل الخروج
#               (المفسّر صحيح؛ المصرّف يتخطّاها — ISSUE-104)
# @skip_compiler

هذا دَينٌ مُعلَنٌ باسمِه ورقمِه، لا خضرةٌ زائفة. وعدُّ هذه البذورِ محروسٌ بسقفٍ نازلٍ في check_seed_contract.py (المقيسُ اليوم: ٧٧ متخطّاةً في الشجرةِ كلِّها).

الخطوة ٨ · شاهِدِ الحارسَ الذي يحرسُ هذا الرباط

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

الاتّجاهانِ محروسانِ معًا:

  • لو غيَّرتَ وسمَ بذرةٍ إلى # @rule: gr.stmt.zz_invented — قاعدةٍ لا وجودَ لها — لحمِرَ البندُ ①، لأنّ سقفَه صفرٌ بلا هامش.
  • ولو نزعتَ آخرَ مرساةٍ لقاعدةٍ ما لصارَ «بلا شهادة: 3 > 2» فحمِرَ البندُ ②.

وهاتانِ الحالتانِ بالذاتِ ليستا افتراضًا: هما المجسّانِ ① و② في scripts/codegen/calibration/check_anchor_integrity.yaml، حُقِنَا فعلًا وأُثبِتَ احمرارُ الحارسِ بهما وسُجِّلَ ذلك ببصمةِ SHA. انظرْ لماذا يُعايَرُ الحارسُ أصلًا.

ماذا صارَ عندَك الآن

تتبَّعتَ سلسلةً كاملةً لا حلقةَ فيها مكتوبةٌ باليد مرّتين:

الحلقةالموضعما يحرسُها
القاعدةgrammar/10_statements.yamlcheck_grammar_terminal_refs.py
الكلمةkeywords.yamlمولِّدُ المُعجَمِ + x.py gen --check
المرساةrules_matrix/…/gr.stmt.break/check_anchor_integrity.py
العقد@expected في البذرةcheck_seed_contract.py
التنزيلlowers_to.yamlcheck_lowers_to.py

وللتوسُّع: مصدرُ الحقيقة · المطابقة · التنزيل · كيف تُضافُ قاعدةٌ جديدة.

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