diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c5b012..75ffb2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,11 @@ jobs: - name: فحص الروابط الداخليّة (lychee على الإخراج المبنيّ) uses: lycheeverse/lychee-action@v2 with: - args: --offline --no-progress "book/**/*.html" + # (AR) --root-dir يحلّ روابط الجذر «/» المحلّيّة (يولّدها قالب 404.html في mdBook) + # إلى جذر الكتاب المبنيّ، وإلّا فشل lychee دون اتّصال («Cannot convert path '/'»). + # (EN) --root-dir resolves local root links "/" (emitted by mdBook's 404.html) to the + # built book root; otherwise offline lychee errors with "Cannot convert path '/'". + args: --offline --no-progress --root-dir "${{ github.workspace }}/book" "book/**/*.html" fail: true # حُرّاس نظام المزامنة: سلامة البيان + منع «الكتم الصامت» للقفل diff --git a/src/SUMMARY.md b/src/SUMMARY.md index bf66aa0..4f764f2 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -29,6 +29,7 @@ - [المحلل المعجمي (Lexer)](frontend/lexer.md) - [المحلل النحوي (Parser)](frontend/parser.md) - [شجرة AST](frontend/ast.md) +- [دراسة حالة: الاستيعابات (أنتج → SIR)](frontend/comprehensions.md) # الجزء الخامس · الواجهة الخلفيّة (Backend) diff --git a/src/frontend/comprehensions.md b/src/frontend/comprehensions.md new file mode 100644 index 0000000..b773214 --- /dev/null +++ b/src/frontend/comprehensions.md @@ -0,0 +1,265 @@ +# دراسة حالة: الاستيعابات (من `أنتج` إلى SIR) + +مثالٌ متكامل على تمرير ميزة لغويّة عبر خطّ الأنابيب: كيف تُحلَّل الاستيعابات (قوائم/ +مجموعات/قواميس) بترتيب `أنتج` العربيّ، وتُبنى في AST، وتُنفَّذ في المحرّكين. المرجع +اللغويّ للمستخدم في [sadlang-docs](https://github.com/sadlang/sadlang-docs)؛ هذه +الصفحة للمساهم في **التنفيذ**. + +الصيغة: `[لكل <متغيّر> في <مصدر> [إذا <شرط>] أنتج <ناتج>]` (والمعقوفة `{}` للمجموعة/ +القاموس). أُقرّت في RFC 25 (م1ب). + +> **المبدأ الأهمّ:** التغيير في **المحلّل فقط**. عقد AST لم تتغيّر (نفس الحقول)، فلم +> يُمَسّ المفسّر ولا المترجم في *بناء العقدة* — فقط **ترتيب القراءة** انقلب. هذا يقلّل +> سطح التغيير جذريًّا ويحافظ على تكافؤ المحرّكين. + +## خطّ الأنابيب: عقدة AST هي المحور + +عقدة الاستيعاب هي **نقطة الالتقاء** بين المحرّكين: المحلّل يبنيها مرّة، ثمّ يستهلكها +المفسّر (تقييم مباشر) والمترجم (توليد SIR) كلٌّ على حدة. ولأنّ التغيير لم يمسّ العقدة، +بقي الطرفان متكافئين تلقائيًّا — وهذا ما تفرضه اختبارات التكافؤ المزدوج. + +```mermaid +flowchart LR + SRC["شيفرة ص
لكل س في مصدر أنتج ناتج"] --> LEX["المحلّل المعجميّ
رموز: KEYWORD_FOR، KEYWORD_YIELD…"] + LEX --> PAR["المحلّل النحويّ
parseArrayLiteral / parseMapLiteral"] + PAR --> AST["عقدة AST المحوريّة
List / Set / DictComprehensionExpr"] + AST --> INT["المفسّر الشجريّ
تقييم مباشر"] + AST --> SIR["المترجم → SIR
أوكواد ARRAY_* + مسح إزالة تكرار"] + SIR --> LLVM["LLVM → ملفّ تنفيذيّ"] + INT -.->|"تكافؤ مزدوج مضمون"| LLVM +``` + +--- + +## 1) المحلّل: كشف مبكّر + تمييز + +الاستيعاب يُكتشَف **مبكّرًا** عبر `لكل` (KEYWORD_FOR) في أوّل المحتوى، قبل تحليل أيّ +تعبير — فلا لبس مع مصفوفة/خريطة عاديّة: + +- القائمة: + [`ParserCore::parseArrayLiteral`](https://github.com/sadlang/s-programming-language/blob/dev/shared/parser/src/statements/parser_advanced.cpp) + — إن كان أوّل رمز `لكل`: `في` → مصدر → `[إذا شرط]` → `أنتج` → ناتج → `]`. +- المجموعة/القاموس: + [`ParserCore::parseMapLiteral`](https://github.com/sadlang/s-programming-language/blob/dev/shared/parser/src/statements/parser_advanced.cpp) + — بعد `أنتج` يُحلَّل الناتج الأوّل بـ`parseTernary` (لتجنّب التهام `:`)، ثمّ: + **وجود `:` (أو `=`) ⇒ قاموس** (نُحلّل القيمة)، **غيابها ⇒ مجموعة**. + +`أنتج` = `KEYWORD_YIELD` **السياقيّة الموجودة أصلًا** (تُستعمَل أيضًا للتوليد)، فلا +تعديل على [`keywords.yaml`](https://github.com/sadlang/s-programming-language/blob/dev/language-truth/keywords.yaml). +شرط القبول: `match(KEYWORD_YIELD) || matchContextual(KEYWORD_YIELD)` (في الكود حارسٌ +منفيّ: `if (!match(...) && !matchContextual(...)) error`). + +الفائدة من الكشف المبكّر: **نظرة أمام برمز واحد** (`لكل` أوّلًا) تحسم أنّ ما بين +الأقواس استيعابٌ لا مصفوفة/خريطة — **بلا تراجُع** (backtracking) ولا تحليل تخمينيّ. +والتمييز بين قائمة ومجموعة وقاموس يتأخّر إلى نقطتين حاسمتين فقط: نوع القوس، ووجود +`:` بعد الناتج. + +```mermaid +flowchart TD + START["بعد فتح قوس مربّع أو معقوف"] --> Q1{"أوّل رمز = لكل؟"} + Q1 -.->|"لا"| PLAIN["مصفوفة / خريطة عاديّة
(المسار الافتراضيّ)"] + Q1 -->|"نعم"| HEAD["رأس الحلقة:
متغيّر → في → مصدر
→ (إذا شرط: اختياريّ)
→ أنتج → ناتج"] + HEAD --> Q2{"نوع القوس؟"} + Q2 -->|"قوس مربّع (قائمة)"| LIST["ListComprehensionExpr"] + Q2 -->|"قوس معقوف"| Q3{"بعد الناتج ':' أو '='؟"} + Q3 -->|"نعم"| DICT["DictComprehensionExpr
مفتاح : قيمة"] + Q3 -.->|"لا"| SET["SetComprehensionExpr
ناتج مفرد"] +``` + +> **فخّ:** الترتيب البايثونيّ القديم (`[تعبير لكل …]`) لم يعد يُبنى كاستيعاب — يُحلَّل +> كمصفوفة عاديّة ثمّ يتعثّر عند `لكل`. حُذفت دالّتا `parseListComprehension`/ +> `parseDictComprehension` القديمتان من **مسار المحلّل الحيّ** (`parser_helpers.cpp`). +> (تبقى نسخة بالترتيب القديم في `shared/parser/src/specs/flow/parser_comprehension.cpp` +> لكنّها نموذج ميت **غير مُترجَم** — لا إحالة إليه في CMake؛ يُنظَّف في طور لاحق.) + +--- + +## 2) عقد AST (لم تتغيّر) + +في [`shared/ast/include/expressions.h`](https://github.com/sadlang/s-programming-language/blob/dev/shared/ast/include/expressions.h) +(لا `comprehension_nodes.h` الميت): + +| العقدة | الحقول | +|---|---| +| `ListComprehensionExpr` | `element`، `variable`، `valueVariable`، `iterable`، `condition` | +| `SetComprehensionExpr` | `expression`، `variable`، `valueVariable`، `iterable`، `condition` | +| `DictComprehensionExpr` | `key`، `value`، `variable`، `valueVariable`، `iterable`، `condition` | + +الحقل `valueVariable` (افتراضيّ فارغ) أُضيف لدعم **فكّ الزوج** على الخرائط +(`لكل مفتاح، قيمة في خريطة`) — انظر قسم **«فكّ الزوج والتكرار على الخرائط»** أدناه. +يبقى فارغًا في الصيغة المفردة، فلا يتأثّر أيّ مستهلك قائم. + +--- + +## 3) الواجهة الخلفيّة: التوليد في SIR + +بانيات الاستيعاب في `compiler/src/frontend/builders/`: + +- **القائمة والقاموس** — + [`expression_comprehensions.cpp`](https://github.com/sadlang/s-programming-language/blob/dev/compiler/src/frontend/builders/expression_comprehensions.cpp): + `buildExprListComp` يستعمل أوكواد المصفوفة المُلوَّنة في الخلفيّة + (`ARRAY_NEW`/`ARRAY_LEN`/`ARRAY_GET`/`ARRAY_APPEND`)، و`buildExprDictComp` يستعمل + `__sad_map_create` + `__sad_map_set_typed` (نفس مسار الخريطة الحرفيّة). هذا هو + الإصلاح التاريخيّ لـISSUE-016/017 (بدل نداءات رموز وقت تشغيل غير معرَّفة). +- **المجموعة** — + [`expression_comp2.cpp`](https://github.com/sadlang/s-programming-language/blob/dev/compiler/src/frontend/builders/expression_comp2.cpp): + `buildExprSetComp`. المجموعة = **مصفوفة بعناصر فريدة** (كالمفسّر). يستعمل نفس أوكواد + المصفوفة **زائد حلقة مسح داخليّة لإزالة التكرار**: يبني قيمة الناتج، يمسح مصفوفة + النتيجة، ويضيف عبر `ARRAY_APPEND` **فقط إن غابت القيمة**. إزالة التكرار على **قيمة + الناتج** (لا متغيّر الحلقة) — مطابقةً للمفسّر. + +> **لماذا بانٍ منفصل للمجموعة؟** إزالة التكرار تُدخِل حلقة مسح داخليّة متداخلة +> (كتل `scan_cond`/`scan_body`/`scan_found`/`scan_next`/`scan_done`/`append`) تضاعف +> حجم البانِي، فلا تتقاسم بنية List/Dict المستقيمة؛ ويشارك الملفّ `buildExprGenerator`. + +نقاط تنفيذ دقيقة في بانِي المجموعة: + +- علَم «موجود» وعدّاد المسح الداخليّ يُخصَّصان بـ`ALLOC` **مرّة في كتلة الدخول** (قبل + الحلقة الخارجيّة) — لا داخل الجسم — تفاديًا لتسريب مكدس (alloca متكرّر لكلّ عنصر). +- هيمنة SSA محفوظة: `elemExprResult` يُعرَّف في كتلة القيمة التي تُهيمِن على عنقود + المسح والإضافة؛ `curIdxReg` يُعرَّف في كتلة الشرط التي تُهيمِن على الجسم والزيادة. + +### الرسم البيانيّ للتحكّم (CFG) لبانِي المجموعة + +حلقتان متداخلتان: **خارجيّة** تمرّ على المصدر (`sc_cond` → `sc_body` → … → `sc_inc`)، +و**داخليّة** تمسح مصفوفة النتيجة لكلّ عنصر (`sc_scan_*`) وتضيف عبر `ARRAY_APPEND` +فقط إن غابت القيمة. التعقيد O(ن²) في أسوأ حالة (مقبول لأحجام الاستيعاب المعتادة، ومطابق +لدلالة «أضِف-إن-غاب» في المفسّر). العلَم `found` والعدّاد `jdx` يُخصَّصان في `entry` +(لا في الحلقة) فلا تسريب مكدس: + +```mermaid +flowchart TD + ENTRY["entry
ARRAY_NEW النتيجة
ALLOC idx، found، jdx"] --> COND{"sc_cond
idx أصغر من طول المصدر؟"} + COND -.->|"لا"| EXIT["sc_exit
return النتيجة (Array)"] + COND -->|"نعم"| BODY["sc_body
elem = ARRAY_GET المصدر عند idx
ربط متغيّر الحلقة"] + BODY --> CIF{"شرط إذا؟"} + CIF -.->|"خطأ"| INC + CIF -->|"صحيح / لا شرط"| VAL["sc_val
elemExpr = بناء الناتج
found = 0، jdx = 0"] + VAL --> SCOND{"sc_scan_cond
jdx أصغر من طول النتيجة؟"} + SCOND -->|"نعم"| SBODY{"sc_scan_body
عنصر النتيجة عند jdx == elemExpr؟"} + SCOND -.->|"لا"| SDONE{"sc_scan_done
found == 0؟"} + SBODY -->|"تساوٍ"| FOUND["sc_scan_found
found = 1"] + SBODY -.->|"اختلاف"| SNEXT["sc_scan_next
jdx = jdx + 1"] + FOUND --> SDONE + SNEXT --> SCOND + SDONE -->|"غاب (found=0)"| APP["sc_append
ARRAY_APPEND النتيجة، elemExpr"] + SDONE -.->|"موجود"| INC["sc_inc
idx = idx + 1"] + APP --> INC + INC --> COND +``` + +> **حدّ معروف:** مقارنة إزالة التكرار (`EQ`) **عدديّة** (كبقيّة بنية الاستيعابات +> عدديّة النوع)، فالمجموعات الصحيحة الإزالة للأعداد؛ مجموعات النصوص/العشريّ تتباعد +> صامتًا حتى يُعمَّم النوع في الاستيعابات الثلاثة. + +> **فخّ متبقٍّ:** `buildExprGenerator` في نفس الملفّ ما زال بالنهج المكسور (`CALL +> __sad_len`/`__sad_array_push` غير المعرَّفة) — سينهار الربط متى استُخدم مولِّد؛ خارج +> نطاق م1ب، يُتابَع بنفس نمط الإصلاح. + +--- + +## فكّ الزوج والتكرار على الخرائط (RFC 25 التعارض 1أ) + +توسعة تجعل **مصدر الاستيعاب خريطةً** لا مصفوفةً، وتضيف صيغة فكّ الزوج +`[لكل مفتاح، قيمة في خريطة أنتج ناتج]`. تمرّ عبر الطبقات الخمس، وتبني على إصلاحٍ +تأسيسيّ لتكرار الخرائط في المترجم. + +### أ) الأساس: تكرار الخريطة في حلقة `لكل` بالمترجم + +قبل هذه التوسعة كان المترجم يعامل الخريطة في حلقة `لكل` **كأنّها مصفوفة**: يطبّق +`ARRAY_GET` على بنية الخريطة `{count, cap, keys*, values*, types*}` مباشرةً ⇒ قمامة +(مؤشّرات خام تُطبع أعدادًا)، بينما المفسّر صحيح. الإصلاح في +[`statement_for_range.cpp`](https://github.com/sadlang/s-programming-language/blob/dev/compiler/src/frontend/builders/statement_for_range.cpp): +عند `iterableResult.type == SadTypeKind::Map` نستبدل المصدر بمصفوفة مفاتيح الخريطة +عبر `__sad_map_keys` (تُرجع `SadArray {len, cap, data}` عبر `getOrCreateMapCollect` +في [`map_ops.cpp`](https://github.com/sadlang/s-programming-language/blob/dev/compiler/src/backend/llvm/builders/collections/map_ops.cpp))، +ونجلب القيم عبر `__sad_map_values` لمتغيّر القيمة إن وُجد. اسما الدالّتين ثابتان +موحَّدان في [`sir_constants.h`](https://github.com/sadlang/s-programming-language/blob/dev/compiler/include/frontend/sir_constants.h) +(`kRuntimeMapKeys`/`kRuntimeMapValues`) يتقاسمهما مسار الحلقة وبانِي الاستيعاب. + +### ب) الطبقات الخمس + +| الطبقة | التغيير | +|---|---| +| **AST** | حقل `valueVariable` (افتراضيّ فارغ) في العقد الثلاث | +| **المحلّل** | بعد المتغيّر الأوّل، `matchComma()` اختياريّ ⇒ متغيّر قيمة ثانٍ (نفس نمط حلقة `لكل` في `parseForStmt`) في `parseArrayLiteral` و`parseMapLiteral` | +| **المفسّر** | الزائرات الثلاث تقبل `isMap()`؛ تمرّ على `toMapRef()` وتربط `variable`=المفتاح و`valueVariable`=القيمة | +| **المترجم** | مساعِد مشترك `lowerMapComprehensionIterable` يستدعيه البانون الثلاثة | +| **SoT** | القواعد الثلاث في [`60_advanced.yaml`](https://github.com/sadlang/s-programming-language/blob/dev/language-truth/grammar/60_advanced.yaml) تضيف `[ '،' Identifier ]` (نفس نمط حلقة `لكل`، قاعدة `gr.stmt.for`) | + +### ج) المساعِد المشترك في المترجم + +بدل تكرار منطق الخريطة في البانين الثلاثة، يجمعه +`ExpressionBuilder::lowerMapComprehensionIterable` +([`expression_comprehensions.cpp`](https://github.com/sadlang/s-programming-language/blob/dev/compiler/src/frontend/builders/expression_comprehensions.cpp)): +إن كان المصدر خريطةً استبدله بمصفوفة مفاتيحها ويُصدِر مصفوفة قيمها، ويحسم نوعَي +المفتاح والقيمة. ثمّ يربط كلّ بانٍ متغيّر القيمة بتسجيل SSA (`registerName = valElemReg`) +مُهيمَن عليه من كتلة الجسم. + +**تصنيف النوع دقيق** (يطابق تمثيل التخزين الفعليّ في `buildExprMap`): + +- **المفتاح دائمًا `String`**: الخريطة تُخزّن المفاتيح بـ`strdup` وتحوّل المفاتيح + العدديّة إلى نصّ (ISSUE-044). +- **القيمة** تُشتقّ من `elementType` الذي يعقّبه بانِي الخريطة الحرفيّة (يُلتقَط **قبل** + دهسه): `Integer`/`Boolean` ⇒ نفسها؛ `String`/`Float` ⇒ `String` (العشريّ يُخزَّن + نصًّا داخليًّا)؛ مختلط (`Void`) ⇒ `Integer`. + +```mermaid +flowchart TD + ITER["buildExpression(المصدر)
iterResult"] --> Q{"iterResult.type == Map؟"} + Q -.->|"لا (مصفوفة)"| ARR["نوع العنصر = iterResult.elementType
لا مصفوفة قيم"] + Q -->|"نعم"| CAP["التقاط mapValueType = elementType
(قبل الدهس)"] + CAP --> KEYS["CALL __sad_map_keys ⇒ keysReg"] + KEYS --> VQ{"valueVar غير فارغ؟"} + VQ -.->|"لا"| DONE["استبدال المصدر بمصفوفة المفاتيح
elementType := String"] + VQ -->|"نعم"| VALS["CALL __sad_map_values ⇒ valuesReg
حسم valueVarType"] + VALS --> DONE + DONE --> BODY["في الجسم: ARRAY_GET المفتاح + (القيمة إن وُجدت)
تسجيل SSA لكلٍّ"] +``` + +### د) إغلاق قيد الإخراج النصّيّ + +المفاتيح نصّيّة، فطبعها كان يكشف قيدًا **كونيًّا** (يمسّ حتّى `اطبع(["أ","ب"])`): نتيجة +الاستيعاب كانت `elementType = Void` فالوصول المفهرَس يطبع مؤشّرًا؛ ومساعِد الطبع +`__sad_array_to_string` غير موسوم فيطبع كلّ عنصر بـ`%lld`. الإصلاح شقّان: + +1. **تمرير نوع العنصر**: البانون يمرّرون `elemExprResult.type` إلى `elementType` + لنتيجة القائمة/المجموعة ⇒ الوصول المفهرَس النصّيّ يعمل (كالمصفوفة الحرفيّة). +2. **طبع كامل موسوم**: أُضيف `elementType` إلى `SIROperand`، يمرّره بانِي الطبع + ([`builtins_core.cpp`](https://github.com/sadlang/s-programming-language/blob/dev/compiler/src/frontend/builders/builtins_core.cpp))؛ + وفي الخلفيّة + ([`io_builtins_ops.cpp`](https://github.com/sadlang/s-programming-language/blob/dev/compiler/src/backend/llvm/builders/builtins/io_builtins_ops.cpp)) + يوزَّع على مساعِد نصّيّ `__sad_array_to_string_str` (تمريرتان: `strlen` للحجم ثمّ + `sprintf("%s")`، يخصّص مخزنه) عند `elementType == String`. غير النصّيّة تبقى على + المسار العدديّ الأصليّ — توافق تامّ. + +> **ترتيب المدخلات غير محدَّد.** المفسّر يستعمل `std::unordered_map` والمترجم ترتيب +> الخانات؛ فترتيب المرور غير مضمون (كحلقة `لكل` على الخرائط). اختبارات التكافؤ تستعمل +> خرائط بمفتاح واحد أو تجميعًا لا-ترتيبيًّا لتفادي هشاشة المقارنة الحرفيّة. + +--- + +## 4) الاختبار (طبقتان) + +- **سلوك (تكافؤ مزدوج)** — `tests/behavior/rules_matrix/60_advanced/gr.adv.{list,set, + dict}_comprehension/`، مولَّدة بـ`_generators/gen_comprehension_tests.py` (يحاكي + الدلالة في بايثون ⇒ `@expected` حتميّ). **حسّاس:** اختبارات المجموعة تستعمل خرائط + **غير حقنيّة** (`س % 3`) + probe قيمة بالفهرسة — وإلّا فخريطة حقنيّة تجعل الطول + مستقلًّا عن التحويل، فيمرّ محرّك يتجاهل `أنتج` أو يزيل التكرار على المصدر **زائفًا**. +- **وحدة (C++)** — [`tests/unit/parser/test_comprehensions_antaj.cpp`](https://github.com/sadlang/s-programming-language/blob/dev/tests/unit/parser/test_comprehensions_antaj.cpp) + (إطار `sad_test.h`): يتحقّق من عقد AST + تمييز قاموس/مجموعة + رفض الترتيب القديم + + بنية `BinaryExpr` للناتج. مُسجَّل في CTest كـ`ComprehensionAntajTests` (وسم `Unit`) + عبر [`cmake/tests.cmake`](https://github.com/sadlang/s-programming-language/blob/dev/cmake/tests.cmake). + +> **تغطية فكّ الزوج:** يضيف ملفّ الوحدة قسم `PairUnpack` (يتحقّق أنّ `valueVariable` +> يُملأ بالفاصلة ويبقى فارغًا بدونها، والحالة السلبيّة «فاصلة بلا اسم»)؛ وتضيف مجلّدات +> السلوك حالات فكّ زوج بإخراج **صحيح** أو **فهرسة** (لتفادي عدم تحديد الترتيب) — قائمة +> ومجموعة وقاموس. القِيَم النصّيّة تُختبَر عبر الفهرسة والطبع الكامل بعد إصلاح الإخراج +> النصّيّ (القسم د أعلاه). + +--- + +## انظر أيضًا + +- [المحلل النحوي (Parser)](parser.md) · [شجرة AST](ast.md) · [التمثيل الوسيط SIR](../backend/sir.md) +- [قواعد المحلل كمصدر موحّد](../sot/grammar-sot.md) — قواعد `gr.adv.*_comprehension` في `language-truth/grammar/`. diff --git a/sync/sources.yaml b/sync/sources.yaml index f940a54..ab212de 100644 --- a/sync/sources.yaml +++ b/sync/sources.yaml @@ -31,6 +31,20 @@ chapters: sources: - shared/ast/include/ast_visitor.h + - file: src/frontend/comprehensions.md + sources: + - shared/parser/src/statements/parser_advanced.cpp # parseArrayLiteral / parseMapLiteral (+ matchComma فكّ الزوج) + - shared/ast/include/expressions.h # List/Set/DictComprehensionExpr (+ valueVariable) + - interpreter/src/visitors/expression_evaluator_members_advanced.cpp # زائرات الاستيعاب + فرع الخريطة + - compiler/src/frontend/builders/expression_comprehensions.cpp # بانِي القائمة/القاموس + lowerMapComprehensionIterable + - compiler/src/frontend/builders/expression_comp2.cpp # بانِي المجموعة (مسح إزالة التكرار) + - compiler/src/frontend/builders/statement_for_range.cpp # أساس تكرار الخريطة في المترجم + - compiler/include/frontend/sir_constants.h # kRuntimeMapKeys/kRuntimeMapValues (عقد الأسماء المشترك) + - compiler/src/backend/llvm/builders/collections/map_ops.cpp # __sad_map_keys/values + getOrCreateMapCollect + - compiler/src/frontend/builders/builtins_core.cpp # تمرير elementType لمُعامل الطبع + - compiler/src/backend/llvm/builders/builtins/io_builtins_ops.cpp # طبع المصفوفة الموسوم (__sad_array_to_string_str) + - language-truth/grammar + - file: src/backend/interpreter.md sources: - interpreter/include/core/interpreter_core.h