المحلل المعجمي (Lexer)
ماذا ستتعلّم: كيف يحوّل
LexerCoreنصّ.ص(UTF-8) إلى تيارToken، بتفصيل دقيق من الكود الفعليّ — بما فيه المعالجة الذكيّة للمحارف العربية متعدّدة البايتات.
📎 المصدر:
shared/lexer/src/lexer_core.cpp·token.h·lexer_keywords.cpp
البناء والنظر المسبق
LexerCore(source) يحتفظ بالمصدر ومؤشّر current_ وموقع Position. الأدوات الأساسيّة:
peek()— المحرف الحاليّ دون استهلاك.peekNext(offset)— نظر مسبق.advance()— يستهلك ويتقدّم (يحدّث السطر/العمود).skipWhitespace()— يتخطّى الفراغات.
Position يبدأ من 1 (سطر/عمود) وoffset من 0.
موزِّع nextToken() — ترتيب الإرسال
الدالة المحوريّة nextToken() تتبع ترتيبًا حسّاسًا (الترتيب يمنع لبس المحارف العربية):
flowchart TD
N["nextToken()"] --> WS["حلقة: skipWhitespace + التعليقات (# #* ## #**)"]
WS --> EOF{"نهاية الملف؟"}
EOF -- نعم --> E["END_OF_FILE"]
EOF -- لا --> D{"تصنيف المحرف"}
D -->|رقم عربيّ/إنجليزيّ| NUM["scanNumber()"]
D -->|'r\"' أو 'ح\"'| RAW["scanRawString()"]
D -->|'f\"' أو 'م\"' أو 'ص\"'| FS["scanFString()"]
D -->|'\"'| STR["scanString()"]
D -->|'×' (0xC3 0x97)| MUL["OP_MULTIPLY"]
D -->|'،' '؛' '؟' (0xD8 ..)| ARB["ARABIC_COMMA / SEMICOLON / QUESTION"]
D -->|حرف/_/عربيّ| ID["scanIdentifier()"]
D -->|عامل| OP["scanOperator()"]
⭐ المعالجة الدقيقة لـUTF-8 (نقطة متقدّمة)
المحارف العربية متعدّدة البايتات تُفحَص قبل scanIdentifier وإلّا ابتلعها كجزء من مُعرّف:
| المحرف | UTF-8 | الرمز الناتج | الموقع |
|---|---|---|---|
| رقم عربيّ ٠–٩ | 0xD9 + 0xA0..0xA9 | عدد → scanNumber | L1646 |
ح" (نصّ خام) | 0xD8 0xAD + " | STRING_RAW | L1675 |
م" / ص" (نصّ منسَّق) | 0xD9 0x85 / 0xD8 0xB5 + " | STRING_FSTRING | L1694 |
× ضرب | 0xC3 0x97 | OP_MULTIPLY | L1726 |
، فاصلة | 0xD8 0x8C | ARABIC_COMMA | L1743 |
؛ منقوطة | 0xD8 0x9B | ARABIC_SEMICOLON | L1753 |
؟ استفهام | 0xD8 0x9F | QUESTION (و؟.→QUESTION_DOT) | L1760 |
💡 لهذا تعمل
س، صوأ ؟ ب : جو٣ × ٤كما لو كُتبت باللاتينيّة — المعجمي يطبّع المحارف العربية إلى رموز قياسيّة.
الماسحات (Scanners)
| الدالة | الموقع | يمسح |
|---|---|---|
scanNumber | L323 | صحيح/عشريّ + 0x/0b/0o؛ يترك .. لماسح العوامل (المدى) |
scanString | L754 | "..." مع هروب \n \t \\ \" \r \b \f \v \0 \u \U \x |
scanRawString | L998 | r"..."/ح"..." بلا معالجة هروب |
scanFString | L1054 | f"...{تعبير}..." |
scanDocComment | L1167 | ## / #** **# (يُرفَق بأوّل تصريح) |
scanIdentifier | L1250 | مُعرّف UTF-8 (يتوقّف قبل ،/؛) |
scanOperator | L1347 | العوامل (switch على `+ - * / % = < > ! . ^ |
التعليقات
حلقة التخطّي في بداية nextToken تعالج: # سطر · #* … *# كتلة · ## توثيق سطر ·
#** … **# توثيق كتلة (التعليق غير المغلق يرمي خطأً). تعليقات ## تُلتقَط وتُرفَق لاحقًا
بأوّل تصريح (انظر تهيئة المحلل النحوي).
الكلمات والرموز
- محجوزة (40): مسجّلة في
lexer_keywords.cpp(KeywordTable::initialize())، مصدرهاlanguage-truth/keywords.yaml. - سياقيّة: لها
KEYWORD_*فيtoken.hلكنها لا تُسجَّل كمحجوزة؛ يميّزها المحلل النحوي بالسياق. - أنواع مدمجة كمُعرّفات:
رقم/نص/… ليست محجوزة.
إضافة كلمة مفتاحيّة (مختصر)
- أضف
KEYWORD_FOOإلىtoken.h. 2. محجوزة: سجّلها فيkeywords.yamlثم أعد توليدkeywords_generated. سياقيّة: لا تسجّلها، واستخدم التحقّق المزدوج في المحلل النحوي. - أضف القاعدة + عقدة AST + الزوّار + codegen + اختبار. → التشابك.
اقرأ بعده: المحلل النحوي.