Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

المفسّر الشجري (Interpreter)

ماذا ستتعلّم: كيف يزور Interpreter شجرة AST ويقيّمها فورًا (tree‑walking) — نمط الزائر بحامل النتيجة، تنسيق المدراء (نطاقات · متغيّرات · دوال · كائنات · ملكيّة)، الطور الساكن الذي يسبق أوّل جملة، دورة التقييم من البرنامج إلى القيمة، ونموذج التزامن (goroutines) وحدودُ عزله.

📎 المصدر: interpreter/include/core/interpreter_core.h · visitors/expression_evaluator.h · shared/ast/include/ast_visitor.h

الدور: المرجع الدلاليّ

المفسّر يزور AST ويقيّمه مباشرةً دون توليد كودٍ وسيط — الأسرع للتطوير والـREPL، وهو المرجع الدلاليّ الذي يجب أن يطابقه المترجم: إن طابق المفسّر وخالف المترجم، فالعيب في SIR/LLVM لا في الدلالة (BF‑08).

flowchart LR
  SRC[".ص"] --> LEX["المعجمي"] --> PAR["النحوي"] --> AST["AST"]
  AST --> INT["Interpreter<br/>(tree-walking)"]
  INT --> VAL["Value (نتيجة حيّة)"]
  AST -. "مسار الترجمة" .-> SIR["SIR → LLVM"]
  INT -. "يجب أن يطابق" .-> SIR

① التنسيق: Interpreter والمدراء

الصنف Interpreter منسِّقٌ نحيف: واجهته execute(program) / executeStatement / evaluateExpression، ويفوّض العمل إلى مدراء متخصّصين وزائرَين:

flowchart TB
  INT["Interpreter<br/>execute · executeStatement · evaluateExpression"]
  INT --> SE["StatementExecutor<br/>(تنفيذ الجمل)"]
  INT --> EE["ExpressionEvaluator<br/>(تقييم التعابير)"]
  subgraph MGR["مدراء المفسّر (interpreter/include/managers/)"]
    SM["ScopeManager — النطاقات"]
    VM["VariableManager — المتغيّرات"]
    FM["FunctionManager — الدوال"]
    OM["ObjectManager — الكائنات"]
    OW["OwnershipManager — الملكيّة"]
  end
  SE --> MGR
  EE --> MGR
  SE -.->|"getInstance()"| CM["ClassManager<br/>(مُفرَدٌ في shared/types — خارج المفسّر)"]
المديريديرأين
ScopeManagerسلسلة النطاقات (دخول/خروج، البحث الهرميّ)managers/scope_manager.h
VariableManagerربط الأسماء بالقيم داخل النطاقmanagers/variable_manager.h
FunctionManagerتعريفات الدوال (مشترَكٌ للقراءة فقط بين الخيوط)managers/function_manager.h
ObjectManagerمثيلات الكائنات (OOP)managers/object_manager.h
OwnershipManagerتتبّع الملكيّة/الاستعارة — يربط نظام الذاكرةmanagers/ownership_manager.h
GoroutineManagerدورة حياة الخيوط المتزامنةinterpreter/include/channel.h
UIStateManagerحالة عناصر الواجهة أثناء التفسيرinterpreter/include/ui/ui_state_manager.h

⚠️ ClassManager ليس من مدراء المفسّر. هو مُفرَدٌ (singleton) يعيش في shared/types/include/class_manager.h ويُنادى بـClassManager::getInstance() من المحلّل والمفسّر والمترجم جميعًا — فسجلُّ الأصناف عابرٌ للمحرّكين وعمليّةٌ واحدة، لا حالةٌ مملوكةٌ لمثيل Interpreter. راجع أثرَ ذلك على التزامن في §⑤.

② نمط الزائر بحامل النتيجة

كلّ عقدة AST تَقبل زائرًا (node.accept(visitor))، والزائر ExpressionEvaluator يرث BaseASTVisitor ويُنفّذ visitXxxExpr لكلّ نوع. لأن visit* تُرجع void، تُخزَّن النتيجة في lastResult_ وتُسحَب بـgetResult():

sequenceDiagram
  participant C as evaluateExpression(expr)
  participant N as عقدة AST
  participant V as ExpressionEvaluator
  C->>N: expr.accept(V)
  N->>V: visitBinaryExpr(node)  (إرسالٌ مزدوج)
  V->>V: قيّم الطرفين + طبّق العامل
  V->>V: lastResult_ = القيمة
  C->>V: getResult()
  V-->>C: Value

💡 الإرسال المزدوج (double dispatch): العقدة تعرف نوعها، فتستدعي visitBinaryExpr الصحيحة دون switch على نوعٍ مُعدَّد — إضافة عقدةٍ جديدة = دالة visit جديدة في الزائر.

الزائران مقسَّمان على ٣٠ ملفًّا في interpreter/src/visitors/ (تخفيفًا لزمن الترجمة): expression_evaluator_core · ..._binary_ops/..._binary_logic · ..._calls/..._calls_dispatch/..._calls_invoke/..._calls_user_func/..._calls_macro · ..._members/..._members_advanced/..._members_assign · ..._oop/..._oop_new/..._oop_array_methods/..._oop_string_map_methods/..._oop_concurrency · ..._overloads · ..._ui. ومثلها للجمل: statement_executor · ..._control · ..._control_exceptions · ..._functions/..._functions_templates · ..._modules · ..._oop/..._oop_types/..._oop_struct_test. ومعها sem045_report (تقرير SEM045 — عقد الغياب) وجسرا الواجهة ui_eval_bridge_core وui_widget_expr_dispatch.

③ الطور الساكن: execute لا يبدأ بالتنفيذ

🔑 المفسّر ليس «مشيًا على الشجرة» فقط. قبل تنفيذ أوّل جملة يمسح execute() البرنامجَ كلَّه — مرّتين افتراضيًّا، وثلاثًا إن فُعِّل فحصُ الأنواع (فهو مطفأٌ افتراضيًّا، انظر أدناه) — ويشمل المسحُ الدوالَّ غيرَ المُنادَاة والفروعَ الميّتة. مَن ظنّه محضَ مُقيِّمٍ كسولٍ فوجئ بتشخيصٍ يخرج من كودٍ لا يُنفَّذ أبدًا.

flowchart TD
  S0["execute(program)"] --> S1["مسحٌ عن الدالّة «رئيسية»"]
  S1 --> S2{"قاعدة الدالّة الرئيسيّة<br/>checkMainFunctionRule"}
  S2 -->|"كودٌ تنفيذيٌّ خارج الدوال مع وجود رئيسية"| ERR1["SEM_MAIN_FUNCTION_RULE ⇒ توقّف"]
  S2 --> S3{"options_.enableTypeCheck؟"}
  S3 -->|"false (الافتراضيّ)"| S4
  S3 -->|"true"| TC["TypeChecker على كلّ جملة ⇒ فشلٌ عند أيّ خطأ"]
  TC --> S4["NullSafetyAnalyzer — دائمًا"]
  S4 --> CLR["ErrorManager::clear()"]
  CLR --> P1["الطور الأوّل: تنفيذ كلّ الجمل العلويّة<br/>(تسجيل الدوال والأصناف والعوامّ)"]
  P1 --> P2["الطور الثاني: تنفيذ «رئيسية» إن وُجدت"]
الطورمتى يعملإن فشل
قاعدة الدالّة الرئيسيّةحين توجد رئيسيةSEM_MAIN_FUNCTION_RULE وتوقّفٌ قبل التنفيذ
فحص الأنواع (TypeChecker)مطفأٌ افتراضيًّا: enableTypeCheck = falseتوقّفٌ بعدّ الأخطاء
تحليل أمان العدمدائمًا، وصرامتُه مشتقّةٌ من سياسة الذاكرة عبر strictnessFromOwnershipModeتحذيراتٌ دائمًا؛ وتوقّفٌ عند الصرامة القاتلة

⚠️ العَلَمُ الافتراضيّ يكذبُ على القارئ. enableTypeCheck = false — فالمفسّر يعمل بفاحصِ الأنواع مُطفأً ما لم يُطلَب صراحةً. فإن جرى برنامجٌ على sad-run وسقط على sad-build، فليس بالضرورة تباعُدَ محرّكين: قد يكون طورًا ساكنًا شغّله المترجمُ ولم يشغّله المفسّر.

⚠️ الأطوارُ الساكنة كلُّها محجوبةٌ بـ#if !defined(__EMSCRIPTEN__) && !defined(SAD_PLATFORM_ANDROID) — فبناءُ wasm أو أندرويد لا يفحص شيئًا قبل التنفيذ. لا تَقِس دلالةَ اللغة على أيٍّ منهما.

④ دورة التقييم الكاملة

flowchart TD
  P["الطور الأوّل/الثاني: تنفيذ الجمل"] --> LOOP{"لكلّ جملة"}
  LOOP --> ES["executeStatement → StatementExecutor"]
  ES --> KIND{"نوع الجملة"}
  KIND -->|تعبيريّة| EVAL["evaluateExpression → accept → getResult"]
  KIND -->|تحكّم (إذا/طالما/لكل)| CTRL["statement_executor_control"]
  KIND -->|دالة/صنف| DEF["تسجيل في FunctionManager/ClassManager"]
  KIND -->|استثناء (حاول/أمسك)| EXC["statement_executor_control_exceptions"]
  EVAL --> R["ExecutionResult{success, Value, error}"]
  CTRL --> R
  DEF --> R
  EXC --> R

العمليّات الثنائيّة تُفرَّق داخل الزائر حسب الصنف: evaluateArithmeticOp · evaluateComparisonOp · evaluateLogicalOp (قِصَر دائرة) · evaluateBitwiseOp — كلّها تأخذ (left, TokenType op, right, Position) وتتشاور مع نظام الأنواع للتحميل الزائد والإكراه.

⑤ التزامن (Goroutines)

نموذج التزامن يقوم على العزل: كلّ goroutine يعمل بـStatementExecutor مستقلّ مع ScopeManager / VariableManager / OwnershipManager خاصّةٍ به، ويُشارَك FunctionManager للقراءة فقط:

flowchart LR
  MAIN["الخيط الرئيسيّ"] -->|اذهب func() | SNAP["captureVisibleVariables()<br/>(لقطة من المتغيّرات المرئيّة)"]
  SNAP --> G1["goroutine #1<br/>Executor + مدراء خاصّون"]
  SNAP --> G2["goroutine #2<br/>Executor + مدراء خاصّون"]
  FM["FunctionManager<br/>(مشترَك — قراءة فقط)"] --- G1
  FM --- G2
  G1 <-->|قناة| CH["SadChannel<br/>(mutex داخليّ)"]
  G2 <-->|قناة| CH

⚠️ المتغيّرات تُلتقَط لقطةً عبر captureVisibleVariables() لا بالمرجع — فلا سباق على نطاق المنشئ. FunctionManager مشترَكٌ لكنّه للقراءة فقط، والقنوات (SadChannel) آمنةٌ بـmutex داخليّ.

🔑 حدُّ العزل: ClassManager خارجه. العزلُ أعلاه يغطّي مدراءَ المفسّر، لا المُفرَدَ العابرَ للمحرّكين. وقياسُ class_manager.cpp يقول: getInstance وresetInstance وحدهما يقفلان instanceMutex_ — أمّا registerClass وgetClass وhasClass وregisterTrait وسائرُ الخريطة فبلا قفل. فالتعليقُ الذي يصف الصنفَ بأنّه «آمنٌ للخيوط المتعدّدة» يصفُ إنشاءَ المُفرَد لا محتواه. عمليًّا: أطلق على شيفرةٍ تُصرِّح صنفًا (أو تحمِّل وحدةً تُصرِّحه) يكتبُ في خريطةٍ عامّةٍ غيرِ متزامنة. هذا عقدٌ معلَنٌ لا يقيسه أحد، لا حكمٌ بأنّه عطبٌ مرصود — قِسه قبل أن تبنيَ عليه.

ملاحظات للمطوّر

  • القيم كلّها Value (std::variant على ValueType)؛ OBJECT يحمل shared_ptr<ObjectInstance> ⇒ تمرير الكائنات بالمرجع → نظام الأنواع.
  • شغّل .ص مباشرةً بـsad-run لاختبارٍ سريع — لا حاجة لخطوة ترجمة.
  • إن طابق المفسّر وخالف المترجم ⇒ المشكلة في SIR/LLVM لا في الدلالة (BF‑08).
  • لإضافة عقدة AST جديدة: أضف visit<Node> الصرفة إلى ASTVisitor وتطبيقًا فارغًا في BaseASTVisitor، ثمّ نفّذها في الزائرَين → شجرة AST.
  • الأصنافُ تُسجَّل في ClassManager::getInstance() (مُفرَدُ shared/types)، لا في مديرٍ يملكه المفسّر.

اقرأ بعده: التمثيل الوسيط SIR.