> For the complete documentation index, see [llms.txt](https://overleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://overleaf-pro.ayaka.space/latex/ar/mqalat-mtamqh/09-an-introduction-to-luatex-part-2-understanding-directlua.md).

# مقدمة إلى LuaTeX (الجزء 2): فهم \directlua

## هدف هذه المقالة

في الجزء الأول من هذه المقالة، [مقدمة إلى LuaTeX (الجزء 1): ما هو — وما الذي يجعله مختلفًا إلى هذا الحد؟](/latex/ar/mqalat-mtamqh/07-an-introduction-to-luatex-part-1-what-is-it-and-what-makes-it-so-different.md)، استعرضنا بإيجاز LuaTeX بوصفه محرك TeX شديد التنوع: نظامًا متطورًا وقابلًا للبرمجة ومرتبطًا بتنسيق الصفحات، يوفّر مجموعة واسعة من الأدوات لبناء حلول هندسة المستندات والإنتاج.

في هذه الحلقة الختامية، نلقي نظرة فاحصة على أهم مكوّن في صندوق أدوات LuaTeX: الأمر `\directlua` الذي يوفّر «البوابة» إلى التحكم البرمجي في تنسيق LuaTeX من خلال لغة برمجة Lua.

ومع ذلك، فإن الاستفادة الكاملة من LuaTeX عبر `\directlua` تتطلب بعض المعرفة الخلفية بعدة موضوعات في TeX: الرموز المميزة في TeX، وقوائم الرموز وآلية التوسيع. هدف هذه المقالة هو استكشاف هذه المفاهيم الأساسية في TeX وشرحها: تجميع العمليات المرتبطة بـTeX وراء `\directlua` من أجل تطوير فهم لكيفية عمله وتوفير الأسس التي يمكنك بناء حلولك الخاصة لتنسيق الصفحات باستخدام LuaTeX عليها.

تتضمن هذه المقالة العديد من الأمثلة القصيرة لإظهار وشرح الجوانب الرئيسية من `\directlua`لسلوكه، مع تجنب الشيفرة المعقدة عمدًا لصالح مقاطع شيفرة قصيرة. وعند الحاجة، تستخدم الأمثلة TeX الأساسية (الخام/البسيط)—مع أن معظم الناس يستخدمون LaTeX (الماكروز) ويفضلونه، فإن أوامر TeX الأساسية تتميز بالبساطة.

## مقدمة إلى Lua في LuaTeX

[Lua](https://www.lua.org/about.html) هي لغة برمجة نصية، ويكون [الشيفرة المصدرية](https://www.lua.org/download.html) شديدة القابلية للنقل وسهلة التضمين في تطبيقات البرمجيات، مما يتيح للمطورين دمج إمكانات البرمجة النصية في برامجهم. وقد تم تضمين Lua في [العديد من التطبيقات](https://en.wikipedia.org/wiki/List_of_applications_using_Lua) وهي خيار شائع في صناعة ألعاب البرمجيات—وربما أشهر مثال هو [World of Warcraft](https://wowwiki.fandom.com/wiki/Lua_functions).

LuaTeX، كما يوحي اسمه، هو محرك TeX يضم لغة Lua النصية، مما يوفّر للمستخدمين القدرة على التحكم في سلوك التنسيق في LuaTeX من خلال تضمين برامج Lua (النصوص البرمجية) في مستنداتهم. وإلى جانب التحكم المباشر في LuaTeX، يمكن للمستخدمين الاستفادة من Lua بوصفها لغة برمجة شديدة القدرة لتنفيذ مهام قد يكون من الصعب جدًا تحقيقها باستخدام لغة TeX—وهي، بأي معيار منصف، لغة صعبة التعلم والإتقان. ومن خلال إضافة Lua ودمجها، يصبح LuaTeX محرك TeX متعدد الاستخدامات وقويًا جدًا يدعم لغتين برمجيتين مباشرة.

### استخدام Lua وTeX في مستندك: أدخل \directlua

Lua وTeX هما لغتان *مختلفتان جدًا* في البرمجة: Lua أقرب بكثير إلى ما يعتبره معظم الناس لغة برمجة، لكن TeX، بما فيه من رموز الفئات والرموز المميزة والماكروز وآلية التوسيع، بعيد جدًا عن تجارب/توقعات معظم الناس للغة تُكتب بها البرامج. ومع ذلك، كما أظهر التاريخ، فإن لغة TeX صمدت لأنها جيدة في ما صُممت له: التحكم في التنسيق، حتى لو كانت طريقة عملها غامضة بعض الشيء.

ولمواجهة تحدي مزج لغتي Lua وTeX في مستند TeX واحد، قدّم مطورو LuaTeX أمرًا جديدًا يُسمى `\directlua` وهو الطريق لاستخدام Lua—بوصفها لغة برمجة مستقلة بذاتها، وكذلك للتحكم في سلوك التنسيق في LuaTeX.

ال `\directlua` يتيح الأمر للمستخدمين تضمين شيفرة Lua في مستندات TeX الخاصة بهم؛ ثم تُمرَّر تلك الشيفرة لاحقًا إلى مفسّر لغة Lua المدمج في LuaTeX. ومع ذلك، `\directlua` يتيح لك أيضًا أن *تدمج* شيفرة Lua و(La)TeX معًا، داخل الأمر `\directlua` —وإن كان ذلك يضيف تعقيدات إضافية بسبب الاختلافات الأساسية بين Lua ولغات البرمجة القائمة على TeX. والتحدي الرئيسي عند استخدام مزيج من شيفرة (La)TeX وLua هو ضمان تعايش اللغتين بسلام وعدم «إعاقة» إحداهما للأخرى.

`\directlua` يُعد هذا الأنسب للاستخدام مع مقاطع Lua الأقصر داخل المستند، لكن يمكنك استخدامه مع برامج Lua أطول إذا أردت. وبوجه عام، تُحفظ برامج Lua الأكبر حجمًا ومكتبات شيفرات Lua في ملفات خارجية يمكن تحميلها باستخدام دالة Lua `dofile()` داخل أمر `\directlua` ومن منظور معالجة TeX، فإن إحدى المزايا المهمة لاستخدام ملفات شيفرة Lua خارجية هي تجنب التعقيدات التي تنشأ من آلية رموز الفئات في TeX—وهو موضوع ستتناوله هذه المقالة بالتفصيل.

### وصف أكثر رسمية لـ \directlua

ال [دليل مرجع LuaTeX](http://www.pragma-ade.com/general/manuals/luatex.pdf) يصف `\directlua` على النحو التالي (مع بعض التعديل):

> ولدمج شيفرة Lua مع مدخلات TeX، هناك حاجة إلى بضع بدائيات جديدة. تُستخدم البدائية `\directlua` لتنفيذ شيفرة Lua فورًا. والبنية الأساسية هي `\directlua{⟨code⟩}`. الحزمة `⟨code⟩` يُوسَّع بالكامل، ثم يُمرَّر إلى مفسّر Lua. وبعد إجراء القراءة والتوسيع على `⟨code⟩`، تُحوَّل قائمة الرموز الناتجة إلى سلسلة كما لو كانت معروضة باستخدام `\the\toks`.

وبالطبع هذا دقيق من الناحية التقنية، لكنه ربما ليس سهل الفهم دون معرفة ببعض عمليات TeX منخفضة المستوى—مثل الرموز المميزة والتوسيع.

## فهم \directlua: ما الموضوعات التي سنغطيها؟

في هذه المقالة سنلقي نظرة أقرب على بعض الموضوعات الأساسية الخلفية ونقدّم عددًا من الأمثلة المصممة لإظهار كيف `\directlua` يعمل، وأين (أو لماذا) تحتاج إلى توخي الحذر عند دمج TeX وLua في `⟨code⟩`.

سنستكشف الموضوعات التالية بتفصيل كافٍ لتوفير أساس لفهم `\directlua` و«المعالجة المسبقة» للشيفرة التي تستخدمها بداخله:

* رموز الفئات ورموز TeX المميزة: تحويل النص إلى رموز مميزة والرموز المميزة إلى نص؛
* عملية التوسيع في TeX (ومنْع التوسيع);
* آليات/تسلسلات الهروب في Lua للحروف والسلاسل;
* استخدام التعليقات بأسلوب Lua;
* مقدمة قصيرة إلى واجهة Lua البرمجية الخاصة بـ LuaTeX.

إذا فهمت كيف تنشئ محركات TeX الرموز المميزة وتستخدمها، وطوّرت وعيًا بآلية التوسيع في TeX، فستكون لديك الأسس اللازمة لإطلاق العنان للتنوع المذهل لـ `\directlua` الأمر.

## الأسس: من النص إلى الرموز المميزة ومن الرموز المميزة إلى النص

نشرت Overleaf عدة مقالات تنظر بعمق إلى رموز TeX المميزة والمفاهيم ذات الصلة، لذلك لن نكرر كل تلك المادة هنا؛ بل سنلخص تلك المجالات/الموضوعات ذات الصلة بتطوير فهم أفضل لـ `\directlua`.

فيما يلي قائمة بالمقالات المنشورة سابقًا والتي قد تهمك:

* [ما هو رمز TeX المميز؟](/latex/ar/mqalat-mtamqh/53-what-is-a-tex-token.md)
* [ما هي قائمة رموز TeX؟](/latex/ar/mqalat-mtamqh/54-what-is-a-tex-token-list.md)
* [كيف تعمل \expandafter: مقدمة إلى رموز TeX المميزة](/latex/ar/mqalat-mtamqh/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md)
* [سلسلة من ستة أجزاء: كيف تعمل ماكروز TeX فعليًا؟](/latex/ar/mzyd-mn-almwdhwaat/01-a-six-part-series-how-do-tex-macros-actually-work.md)

### فهم رموز الحروف

أي حرف يمكن لمحرك TeX قراءته من ملف نصي يتم تمثيله بقيمتين عدديتين:

* قيمته *الرمزية للحرف* (قيمة ASCII أو، اليوم، نقطة شفرته في Unicode);
* قيمة ثانية، خاصة بـTeX، تُسمى *رمز الفئة*.

قد يرغب القراء الذين يودون معرفة المزيد عن رموز الفئات في قراءة هذه المقدمة المنشورة بواسطة Overleaf: [فمن أين نبدأ؟ مع رموز الفئات](/latex/ar/mzyd-mn-almwdhwaat/19-how-tex-macros-actually-work-part-1.md#so2c-where-do-we-start3f-with-category-codes).

على سبيل المثال، إذا قرأ محرك TeX الحرف `مشروع` فسيكون لديه وصول إلى قطعتين من المعلومات: `مشروع`القيمة الرمزية للحرف (65)، ورمز فئته (11، عادةً). وبمجرد أن يُدخِل TeX ذلك الحرف `مشروع`، فلن تتغير فئة رموزه، لكن الماكروز الخاصة بالمستخدم يمكنها إجراء تغييرات على رموز الفئات قد تؤثر في أي *لاحق* الحرف `مشروع` الذي *لم يُقرأ بعد* بواسطة TeX. وبالتالي، يحتاج TeX إلى تسجيل أن *هذا* الحرف `مشروع`, *الذي قُرئ للتو*، له رمز فئة 11. وللقيام بذلك، يستخدم TeX الزوج الصحيح (65،11) لحساب قيمة عددية أخرى يسميها *رمزًا مميزًا للحرف*. وبحساب قيمة الرمز المميز تلك، التي تُمرَّر إلى المعالجة الداخلية في TeX، فإن ذلك `مشروع` والرمز الفئوي الخاص به *مرتبطان معًا*; وفي الواقع، فإن ذلك الرمز المميز للحرف *يحتوي* البيانات التي يحتاج TeX إلى معرفتها عن ذلك الحرف لاستخدامها في أي أنشطة تنسيق لاحقة أعمق داخل محرك TeX.

#### كيف تُحسب الرموز المميزة للحروف؟

أولًا، علينا أن نتذكر أن محركات TeX تستخدم رمز الفئة 13 لغرض إنشاء ما يُسمى *حروفًا نشطة*: أي حرف يحمل رمز فئة 13 يتصرف مثل ماكرو صغير؛ وبالتالي، وكما سنرى أدناه، تُحسب رموز الحروف النشطة بشكل مختلف عن الحروف العادية ذات رموز الفئات الأخرى مثل 10 أو 11 أو 12.

بالنسبة إلى *غير النشطة* الحروف:

* المحركات الأقدم ذات 8-بت (TeX لِنُوث، e-TeX، pdfTeX) تحسب الرموز المميزة للحروف *غير النشطة* الحروف باستخدام

$$\text{(non-active) character token} = (256 \times \text{category code}) + (\text{ASCII character code})$$

* أما بالنسبة إلى LuaTeX، الذي عليه التعامل مع قيم أحرف Unicode، فإن الحساب لـ *غير النشطة* الحروف مشابه لكنه ينتج قيمًا عددية أكبر بكثير:

$$\text{(non-active) character token} = (2^{21} \times \text{category code}) + (\text{Unicode value})$$

بالعودة إلى مثالنا السابق للحرف A ذي رمز الفئة 11، سيحسب LuaTeX قيمة رمز مميز للحرف مقدارها $$2^{21} \times 11 + 65 = 23068737$$. وبمجرد حسابها، فإن قيمة الرمز المميز تلك *تربط* ذلك الحرف A تحديدًا بقيمة رمز فئة قدرها 11. قد تغيّر ماكروز المستخدم رمز الفئة لأي حرف A لاحق، لكن رمز فئة هذا الحرف قد ثُبّت بتحويله إلى رمز مميز لاستخدامه أثناء مروره عبر الآليات الداخلية لـLuaTeX. لقد حافظ LuaTeX على المعنى المقصود لذلك الحرف، أو أودعه في رمز، كما كان محددًا في وقت قراءته.

تستخدم محركات TeX ما مجموعه [16 رمز فئة مختلفًا](/latex/ar/mzyd-mn-almwdhwaat/43-table-of-tex-category-codes.md) و *ويمكن إسناد أي* من تلك الرموز الفئوية، عبر الأمر `\catcode` إلى *ويمكن إسناد أي* أي حرف يستطيع محرك TeX قراءته. تُستخدم تغييرات رموز الفئات لتغيير الطريقة التي تعالج بها محركات TeX حروفًا معينة في المدخل، مما يتيح لمستخدمي TeX كتابة ماكروز تنتج نتائج أو سلوكيات خاصة في التنسيق.

**الحروف النشطة**

كما ذُكر، تستخدم محركات TeX رمز الفئة 13 لإضفاء «معنى خاص» على حرف، مما يجعله ما يُسمى *حرفًا نشطًا* يتصرف مثل ماكرو صغير: لا حاجة إلى أي `\` بادئة، فالحرف المنفرد، بفضل رمز فئته، يكفي لتفعيل سلوكه الشبيه بالماكرو.

لأن الحرف النشط يعمل مثل ماكرو صغير، فإنه لا يُحوَّل إلى *رمزًا مميزًا للحرف* بل إلى نوع ثانٍ من الرموز (العددية) يُسمى *رمز أمر*. وتُحسب هذه على النحو التالي:

* في المحركات الأقدم ذات 8-بت (TeX لِكْنُوث، e-TeX، pdfTeX) تُحسب الرموز للحروف النشطة عبر:

1. احسب قيمة وسيطة تُسمّى $$\text{curcs}$$ (**cur**rent **c**ontrol **s**equence) حيث $$\text{curcs} = \text{character code} + 1$$3. احسب قيمة الرمز المميز حيث $$\text{active character token} = \text{curcs} + \text{4095}$$

* في LuaTeX يكون الحساب أكثر تعقيدًا قليلًا لأنه عليه التعامل مع النطاق الكامل من أحرف Unicode، وقد يمكن جعل أي واحد منها حرفًا نشطًا:

1. احسب القيمة العددية الوسيطة $$\text{curcs}$$ بتطبيق ما يُسمى *دالة تجزئة* على قيمة نقطة شيفرة Unicode للحرف النشط كما هي معبَّر عنها بصيغة UTF-8: $$\text{curcs}=\texttt{hashfunction}\text{(UTF-8 text for Unicode value of active character)}$$3. احسب قيمة الرمز المميز العددية: $$\text{active character token} = \text{curcs} + 2^{29} - 1$$

**أمثلة**

* محركات 8-بت: حساب الرمز المميز للحرف النشط `~` (رمز الحرف 126) ينتج $$\text{curcs} = 126 + 1 = 127$$، مما يعطي قيمة رمز مقدارها $$4095 + 127 = 4222$$.
* LuaTeX: حساب الرمز المميز للحرف النشط `~` ينتج $$\text{curcs}=3186$$ مما يعطي قيمة رمز مقدارها $$3186 + 2^{29} - 1 = 536874097$$. تستخدم رموز LuaTeX قيمًا عددية أكبر بكثير!

### فهم رموز الأوامر

إضافةً إلى معالجة *الحروف* المنفردة، يمكن لمحركات TeX بالطبع معالجة *سلاسل* من الحروف تُسمى *الأوامر* (أو، بشكل أدق، *سلاسل تحكم*). وبحسب التقليد، يُستخدم `\` الحرف للإشارة إلى بداية أمر، لكن ذلك مجرد عرف—وفي الواقع، يمكن استخدام أي حرف يحمل رمز الفئة 0 (حرف الهروب) بدلًا منه.

تتعرف محركات TeX على نوعين من الأوامر يُعرفان باسم *كلمات التحكم* و *الرموز التحكمية*:

* **كلمات التحكم**: أوامر تُبنى من حرف واحد أو أكثر تحمل رمز الفئة 11;
* **الرموز التحكمية**: أوامر من حرف واحد حيث يكون رمز فئة ذلك الحرف *ليس* 11: مثل `\$`, `\#` أو `\\`.

**ملاحظة**: البدائيات في TeX `\chardef`, `\mathchardef`, `\countdef`, `\dimendef`, `\skipdef`, `\muskipdef` و `\toksdef` تُستخدم أيضًا لتعريف سلاسل التحكم، لكن بخلاف تعريفات الماكرو العادية، فإن سلاسل التحكم الناتجة (كلمات التحكم أو الرموز التحكمية) *ليست قابلة للتوسيع*—سنستكشف هذه بتفصيل أكبر أدناه.

#### كيف تُحسب رموز الأوامر؟

تمامًا مثل الحروف النشطة، تستخدم محركات TeX النوع الثاني من القيم العددية الرمزية لتمثيل الأوامر: *رموز الأوامر*—تذكّر أن الحروف النشطة تولد أيضًا رموز أوامر لأنها تتصرف كحروف ماكرو صغيرة.

يمكن العثور على الحسابات التي تستخدمها محركات 8-بت لإنشاء الأعداد الصحيحة لرموز الأوامر في هذه [مقالة Overleaf](/latex/ar/mqalat-mtamqh/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md#how-tex-calculates-token-values). وهنا سنلخص الخطوات الرئيسية في حساب رموز الأوامر لـLuaTeX—وهي مختلفة قليلًا لأن LuaTeX عليه معالجة قيم أكواد أحرف Unicode التي قد تكون أكبر بكثير من قيم 8-بت؛ ومع ذلك، تتبع حسابات LuaTeX المبادئ العامة نفسها التي تستخدمها المحركات الأقدم ذات 8-بت.

بعد اكتشاف أمر وارد، تتجاهل محركات TeX، بما فيها LuaTeX، `\` الحرف: فهو لا يُستخدم في حسابات قيم رموز الأوامر، بل يعمل فقط كمفتاح «تشغيل» لإبلاغ محرك TeX بأنه يحتاج إلى معالجة أمر. تُحسب قيمة رمز الأمر باستخدام تسلسل الحروف (واحد أو أكثر) الموجود في اسم الأمر—ويحسب LuaTeX رموز الأوامر لكلا الرمزين/الكلمتين التحكمية باستخدام الخوارزمية نفسها:

1. احسب القيمة العددية الوسيطة $$\text{curcs}$$ بتطبيق ما يُسمى [دالة تجزئة](https://en.wikipedia.org/wiki/Hash_function) إلى سلسلة Unicode UTF-8 من الحروف الموجودة في اسم الأمر: $$\text{curcs}=\texttt{hashfunction}\text{(Unicode UTF-8 string of characters in command name)}$$3. احسب قيمة رمز الأمر حيث $$\text{command token} = \text{curcs} + 2^{29} - 1$$

**أمثلة**

* لِـ `\\` أمر (رمز تحكمي)، يحسب LuaTeX $$\text{curcs}=94$$، مما ينتج قيمة رمز لـ `\\` لـ $$94 + 2^{29} - 1 = 536871005$$.
* لِـ `\vskip` الأمر البدائي (كلمة تحكم) يحسب LuaTeX $$\text{curcs}=3560$$، مما ينتج قيمة رمز لـ `\vskip` لـ $$3560 + 2^{29} -1 = 536874471$$.
* للماكرو المعرّف من قبل المستخدم `\mynewmacro` (كلمة تحكم) يحسب LuaTeX $$\text{curcs} = 2971$$، مما ينتج قيمة رمز لـ `\mynewmacro` لـ $$2971 + 2^{29} -1 = 536873882$$.

بمجرد إنشائها، قد تُخزَّن الرموز لاستخدامها لاحقًا عبر ما يُسمى *قوائم الرموز المميزة* أو يمكن تمريرها فورًا لمزيد من المعالجة داخل محرك TeX. إن استخدام القيم العددية لتمثيل الرموز لا يعمل فقط عبر جميع أنواع منصات الحوسبة/أنظمة التشغيل، بل هو أيضًا طريقة فعّالة جدًا لـTeX لتخزين/معالجة البيانات.

### كيف يحدد محرك TeX نوع الرمز (أمر أم حرف)

إذا أُعطيَت قيمة رمز صحيحة معينة، $$T$$، يمكن لمحرك TeX أن يحدد بسهولة ما إذا كانت $$T$$ تمثل أمرًا أم حرفًا عبر اختبار ما إذا كانت $$T$$ تتجاوز حدًا $$\text{threshold value}$$—وذلك $$\text{threshold value}$$ يعتمد على محرك TeX. إذا كانت $$T \geq \text{threshold value}$$ فإن $$T$$ رمز أمر، وإلا $$T$$ فهي رمز حرف. أما $$\text{threshold value}$$ هو $$4095$$ للمحركات ذات 8-بت، و $$2^{29}-1$$ (536,870,911) لـLuaTeX. وقد صمم كنوث الطرق المستخدمة في صيغ حساب الرموز بحيث يمكن لمحرك TeX الخاص به، وجميع المحركات اللاحقة المبنية على شيفرته/هندسته، اختبار قيم الرموز بسرعة وسهولة.

## يمكن تفكيك الرموز (وإعادتها إلى نص)

الرموز (الأعداد الصحيحة) هي الآلية التي من خلالها «يحتوي» محرك TeX كل ما يحتاج إلى تسجيله عن عنصر إدخال (حرف أو أمر). ومع ذلك، هناك أوقات يحتاج فيها محرك TeX إلى عكس عملية الترميز—للتعرف على ما قُرئ أصلاً لإنتاج تلك القيمة الرمزية—حرف منفرد أو تسلسل من حرف واحد أو أكثر يشكّل اسم أمر:

* **للرموز الحرفية**: يمكن تقسيم أي رمز حرف إلى جزأيه المكوّنين: رمز الحرف ورمز الفئة المقابل المعيّن لذلك الحرف *عند النقطة التي قُرئ فيها أصلًا*. وكما هو الحال مع جميع محركات TeX، لن يغيّر LuaTeX ذلك التخصيص الأصلي لرمز الفئة، لكنه سيستفيد منه أثناء أنشطة المعالجة الداخلية اللاحقة.
* **لرموز الأوامر:** هذه أكثر تفصيلًا قليلًا، لكن إذا نظرت إلى حساب LuaTeX لرموز الأوامر، بما في ذلك رموز الحروف النشطة، فسترى أنها تتبع نمطًا: $$\text{command token} = \text{curcs} + 2^{29} -1$$

حيث $$\text{curcs}$$ يُحسب وفقًا لنوع رمز الأمر الجاري إنشاؤه: حرف نشط، رمز تحكمي، أو كلمة تحكم. إن $$\text{curcs}$$ المتغير هو *شديدة* مكوّن مهم من العمليات الداخلية لمحرك TeX: فبأي قيمة رمز أمر (عدد صحيح) يستطيع LuaTeX بسهولة كبيرة استخراج قيمة $$\text{curcs}$$ من ذلك رمز الأمر باستخدام $$\text{curcs} = \text{token value} - (2^{29} -1)$$.

### لماذا يُعد $$\text{curcs}$$ مهمًا جدًا؟

المتغير الداخلي في TeX $$\text{curcs}$$ (**cur**rent **c**ontrol **s**التسلسل) هو مكوّن بالغ الأهمية من العمليات الداخلية «تحت الغطاء» لمحرك TeX. وعلى الرغم من أنك لن تستخدمه، ولا يمكنك، الوصول إليه مباشرة في شيفرتك، $$\text{curcs}$$ يلعب دورًا حاسمًا لأن محركات TeX تستخدم القيمة الحالية لـ $$\text{curcs}$$ بوصفها فهرسًا في جداول داخلية تخزّن بيانات عن كل أمر معروف حاليًا للمحرك. وتخزّن تلك الجداول معلومات عن المعنى الحالي لأمر ما: ماذا يفعل، أو ماذا يمثّل، كما تسجّل أيضًا تسلسل الحروف المستخدم أصلاً لحساب تلك $$\text{curcs}$$ القيمة. ومن خلال استخراج قيمة $$\text{curcs}$$ من رمز أمر، يستطيع محرك TeX تحديد الاسم، أي النص المقروء بشريًا، المقابل لأي رمز (أمر)، مما يمكّنه من إجراء تحويلات الرمز إلى نص التي تُعد جانبًا رئيسيًا من `\directlua`عملية LuaTeX.

### تحويل الأعداد الصحيحة الرمزية إلى أحرف أو سلاسل أحرف (أسماء الأوامر)

لقد رأينا أن محركات TeX تحوّل حروف الإدخال، أو سلاسل الحروف، إلى قيم رموز صحيحة، لكن هناك مناسبات يحتاج فيها محرك TeX إلى *عكس* هذه العملية—لإخراج النص المقروء بشريًا الذي استُخدم أصلًا لإنشاء قيم الرموز الصحيحة تلك؛ على سبيل المثال:

* كتابة رسائل الخطأ أو التحذير إلى الشاشة أو `.log` الملف؛
* إخراج شيفرة TeX/LaTeX إلى ملف نصي عبر الأمر `\write` الأمر;
* عند تحويل تسلسل من الرموز إلى نص داخل `\directlua` (كما سنرى قريبًا!)

#### تحويل الرموز الحرفية إلى نص

كما أُشير، تُحسب الرموز للحروف غير النشطة باستخدام رمز فئة الحرف المدخل ورمز الحرف (قيمة Unicode). ويستخدم LuaTeX الصيغة:

$$\text{character token} = (2^{21} \times \text{category code}) + (\text{Unicode value})$$

إنه من السهل برمجيًا فصل العدد الصحيح $$\text{character token}$$ للحصول على رمز الحرف المكوّن منه ($$\text{Unicode value}$$) و $$\text{category code}$$.

#### تحويل رموز الأوامر إلى نص

تخزّن جميع محركات TeX الاسم (تسلسل الحروف) لكل أمر «تعرفه»: سواء كان ذلك الأمر ماكرو معرّفًا من قبل المستخدم أو بدائيًا مدمجًا—ويتم تخزين أسماء الأوامر البدائية عندما يبدأ محرك TeX التشغيل، قبل وقت طويل من بدء معالجة شيفرتك. أما بالنسبة إلى الأوامر المعرّفة من قبل المستخدم (الماكروز)، فيُخزّن اسم ذلك الماكرو (من دون `\`) كجزء من عمليات تعريف الماكرو داخل محركات TeX.

عندما يحتاج محرك TeX إلى الوصول إلى النص المقروء بشريًا أو إخراجه الذي حُسِب منه رمز أمر صحيح عددياً في الأصل، فإنه سيحدد أولًا $$\text{curcs}$$ قيمة ذلك الرمز؛ وفي LuaTeX، $$\text{curcs} = \text{token} - (2^{29} -1$$). وباستخدام قيمة $$\text{curcs}$$ المستخرجة من رمز أمر، يستطيع محرك TeX الوصول إلى بنية بيانات داخلية تُسمى *مجمع السلاسل* لتحديد تسلسل الحروف المقروء بشريًا الذي استُخدم أصلًا لحساب تلك القيمة الخاصة بـ $$\text{curcs}$$ ومن ثم الرمز الأمر المقابل.

وكما سنرى، فإن أنشطة معالجة الرموز هذه—تحويل سلاسل الحروف إلى قيم رموز صحيحة وتحويل قيم الرموز الصحيحة إلى سلاسل حروف («فك الترميز»)—هي *الآليات الأساسية* المستخدمة داخل `\directlua`.

## قوائم الرموز

بينما يقرأ محرك TeX المدخل ويولّد رموز الحروف والأوامر (ويعالجها)، قد يصادف أوامر معينة تُعلّم المحرك بأن يتوقف (مؤقتًا) عن تمرير الرموز إلى الأمام لمزيد من المعالجة، بل بدلًا من ذلك يحفظها لاستخدام لاحق. وأكثر الأمثلة شيوعًا هو تعريف ماكرو باستخدام أحد أوامر تعريف الماكرو `\def`, `\edef`, `\gdef` أو `\xdef`—مثل أوامر LaTeX مثل `\newcommand` هي ماكروز توفّر وظائف إضافية مبنية حول بدائيات منخفضة المستوى، والتي تقوم في النهاية بعملية تعريف الماكرو الفعلية. ويمكن اعتبار الماكرو الاسمَ المعطى لقائمة معينة من الرموز المخزنة: قائمة رموز.

تستخدم محركات TeX *بشكل واسع* قوائم الرموز، خاصة [القوائم الداخلية المؤقتة](/latex/ar/mqalat-mtamqh/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md) المستخدمة لأغراض المعالجة الداخلية. كما يوفّر كل محرك TeX أيضًا أوامر على مستوى المستخدم لإنشاء قوائم رموز تُخزَّن لاستخدامها عندما يحتاجها المستخدم، أو يحتاجها محرك TeX نفسه. ويختلف عدد أوامر إنشاء قوائم الرموز (البدائيات المدمجة) باختلاف محرك TeX، لكنها تشترك جميعها في مجموعة أساسية دنيا تدعمها كل المحركات، مثل البدائية `\toks` البدائية.

عمليًا، قائمة الرموز ليست سوى تسلسل مخزن من القيم الصحيحة:

* تُقرأ المدخلات لتوليد (حساب) رموز منفردة، تمثل حرفًا أو أمرًا؛
* ثم يُخزن كل رمز على حدة، مع الحفاظ على التسلسل الذي وُلّدت به الرموز من المدخلات.

تخزّن محركات TeX قوائم الرموز باستخدام بنية بيانات تُسمى [قائمة مرتبطة](https://en.wikipedia.org/wiki/Linked_list) (النوع أحادي الارتباط). ويدعى القراء الراغبون في معرفة المزيد عن قوائم الرموز إلى قراءة مقالة Overleaf [ما هي قائمة رموز TeX؟](/latex/ar/mqalat-mtamqh/54-what-is-a-tex-token-list.md) التي تستخدم تشبيهًا لبناء المفاهيم/الأفكار الكامنة وراء قائمة الرموز. ويمكن العثور على استكشاف متعمق لقوائم الرموز في TeX، وكيف تُستخدم في معالجة الماكرو، في سلسلة مقالات Overleaf [كيف تعمل ماكروات TeX فعليًا؟](/latex/ar/mzyd-mn-almwdhwaat/01-a-six-part-series-how-do-tex-macros-actually-work.md)

#### قائمة رموز في شكل رسومي

يوضح الرسم التالي قائمة رموز من LuaTeX مع قيم الرموز المقابلة الناتجة من المدخل التالي

`Hi, \TeX! \hskip 5bp`

على سبيل المثال، إذا عرّفنا `\mymacro` as `\def\mymacro{Hi, \TeX! \hskip 5bp}` سيتم تخزين تعريف `\mymacro` في الذاكرة باستخدام قائمة رموز مثل هذه:

![](/files/0f03dc734205c2c10efd03ed53e9c4903094ab71)

قائمة الرموز هي تسلسل من العناصر المرتبطة المسماة *عُقد*، وهو الاسم المُطلق على حزمة صغيرة من ذاكرة LuaTeX المخصصة للاحتفاظ بكل عنصر في القائمة (مثل الروابط الفردية في سلسلة). تحتوي كل عقدة على قيمة رمز صحيحة وعنوان الذاكرة لـ *العقدة التالية* في السلسلة، مكوّنة بنية بيانات تُسمى [قائمة مرتبطة](https://en.wikipedia.org/wiki/Linked_list). وتشير العقدة الأخيرة إلى نهاية القائمة باستخدام قيمة «null» خاصة للعقدة التالية—لأنه لا توجد عقدة تالية.

**ملاحظات:**

* للتسهيل، أضفنا عنوان كل عقدة منفردة، لكن عمليًا لا تُخزَّن هذه البيانات داخل عقد قوائم الرموز؛ بل يُحتاج فقط إلى عنوان *العقدة التالية* لبناء قوائم رموز محرك TeX.
* يعرض العمود الثاني في الرسم بعنوان «ماذا يعني كل رمز» سلسلة من الصناديق الرمادية التي تحتوي على معلومات عن الرمز الموجود في كل عقدة: وهذه مخصصة للمعلومات فقط و *لا* لا تشكّل جزءًا من البيانات الفعلية المخزنة في قائمة الرموز.

فيما يلي جدول بقيم الرموز الموجودة في قائمة الرموز الموضحة أعلاه:

|                  |                 |                                                                           |                |
| ---------------- | --------------- | ------------------------------------------------------------------------- | -------------- |
| **عنصر الإدخال** | **نوع الإدخال** | <p><strong>رمز الفئة</strong><br><br><strong>(إذا كان حرفًا)</strong></p> | **قيمة الرمز** |
| H                | الحرف           | 11                                                                        | 23068744       |
| i                | الحرف           | 11                                                                        | 23068777       |
| ,                | الحرف           | 12                                                                        | 25165868       |
|                  | الحرف           | 10                                                                        | 20971552       |
| \TeX             | أمر (ماكرو)     |                                                                           | 536871539      |
| !                | الحرف           | 12                                                                        | 25165857       |
|                  | الحرف           | 10                                                                        | 20971552       |
| \hskip           | أمر (بدائي)     |                                                                           | 536874247      |
| 5                | الحرف           | 12                                                                        | 25165877       |
| b                | الحرف           | 11                                                                        | 23068770       |
| ص                | الحرف           | 11                                                                        | 23068784       |

**ملاحظة:** النص الأصلي المُدخل لدينا يحتوي على مسافة بعد `\hskip` الأمر، لكن لا يوجد رمز يمثل ذلك الحرف في قائمة الرموز. لقد امتصت عملية مسح/قراءة المدخل في LuaTeX ذلك الحرف لأنه استُخدم لإنهاء بحث LuaTeX عن الحروف المكوّنة لـ `\hskip` الأمر.

## كيف يعمل \directlua حقًا

الآن بعد أن استكشفنا الرموز، وقوائم الرموز، وتحويل الرموز إلى نص، فإن التحدي التالي هو فهم مفهوم الرمز في محرك TeX *الماكرو*.

كما أُشير، `\directlua{⟨code⟩}` يمكن أن يُطلب منها معالجة `⟨code⟩` الذي يحتوي على كلٍّ من Lua وTeX/LaTeX، لكن مفسر لغة Lua المدمج في LuaTeX لا يفهم TeX أو LaTeX: فكيف يمكن أن يعمل هذا؟ كيف يمكن لـ `⟨code⟩` أن يحتوي على تعليمات TeX/LaTeX من دون أن يربك مفسر Lua تمامًا بأوامر لا يفهمها؟ على سبيل المثال، الأمر التالي `\directlua` لا يستخدم سوى وحدات TeX الماكرو، لكنه يعمل:

```
\def\aa{tex}
\def\bb{.}
\def\cc{print}
\def\dd{("Hello")}
\directlua{
   \aa\bb\cc\dd
}
```

هذا `\directlua` ينتج الأمر تنضيد LuaTeX `مرحبا` لكن لماذا وكيف يعمل هذا بينما لغة Lua لا تفهم وحدات TeX الماكرو؟

الإجابة موجودة ضمن الوصف السابق الذي استعرناه من [دليل مرجع LuaTeX](http://www.pragma-ade.com/general/manuals/luatex.pdf) لكن يمكننا أن نعتبر أن `\directlua{⟨code⟩}` تعمل عن طريق قيام LuaTeX أولًا بـ"المعالجة المسبقة" لـ `⟨code⟩` قبل أن يُمرَّر أي شيء إلى مفسر Lua. إن طبيعة هذه "المعالجة المسبقة" — أي ما الذي تعنيه حقًا وما هي عواقبها على `⟨code⟩`— هي الموضوع التالي الذي سنعالجُه لمساعدة القراء الراغبين في الاستفادة من قوة `\directlua`.

### كيف يعالج LuaTeX \directlua: نظرة أولى

لبناء فهمنا لـ `\directlua`لأنشطة "المعالجة المسبقة" فيمكننا البدء بالمخطط المبسط التالي الذي يعطي نظرة عامة عمّا يحدث. إن `⟨code⟩` المقدَّم إلى `\directlua{⟨code⟩}` يُحوَّل أولًا إلى رموز باستخدام العمليات والحسابات التي نوقشت أعلاه؛ ويُخزَّن ذلك التسلسل من الرموز في قائمة رموز. وبعد بناء تلك القائمة، يُحوَّل كل رمز فيها مرة أخرى إلى تمثيله النصي: ويُجمَع النص الناتج من كل رمز — سواء كان رمز حرف أو رمز أمر — (يُدمج/يُسلسل) لإنشاء سلسلة واحدة من الشيفرة تُمرَّر إلى مفسر Lua للتنفيذ.

![](/files/837e8667c3598b2e313ad0a6a497b43e84544c5c)

لكن انتظر، ما الفائدة من الانتقال من النص إلى الرموز ثم تحويل تلك الرموز مباشرةً مرة أخرى إلى نص؟ قد لا تتفاجأ إذا علمتَ أن هناك، نعم، عملية إضافية وحاسمة لم نضمّنها في هذا الرسم: *توسيع الرموز*. كل رمز يُنتَج من النص في `⟨code⟩` يخضع لنوع من "الفحص" حيث يطبّق LuaTeX اختبارًا ليرى ما إذا كان ذلك الرمز يمثل أمرًا ينتمي إلى المجموعة الفرعية المعروفة باسم *الأوامر القابلة للتوسيع*. إذا كان كذلك، فإن LuaTeX يعزل ذلك الأمر عبر *إزالة* إزالته من `⟨code⟩` و *واستبداله* بالنتيجة/النتائج الناتجة عن عملية تسميها محركات TeX *توسيع الرموز*.

### كيف يعالج LuaTeX \directlua: نظرة ثانية (على التوسيع)

إن آلية التوسيع في TeX هي مكوّن أساسي في جميع محركات التنضيد المبنية على TeX لأن كلًّا منها، في النهاية، مشتق من (أو قائم على) الشيفرة المصدرية والتصميم الأصليين لـTeX اللذين وضعهما Knuth. ومع ذلك، فإن مفهوم التوسيع يصعب شرحه بلغة موجزة وفي الوقت نفسه سهلة الفهم، لأن التوسيع في الممارسة هو مصطلح "مظلّة" يُستخدم لوصف عملية واحدة — لكنها تنتج نطاقًا من المخرجات. وتلك النتائج المتنوعة هي نتيجة لمجموعة الأوامر المتباينة إلى حد ما التي يمكن تطبيق التوسيع عليها، لذا يمكنك أن تعتبر أن لكل أمر قابل للتوسيع سلوكَ "توسيع" معينًا.

كتـ *تقريب أولي* لفهم التوسيع يمكننا القول إن توسيع رمز (أمر) يعني *إزالة* إزالة ذلك الأمر (الرمز) من المدخل الحالي لـTeX و *استبداله* استبداله بتسلسل من الرموز الناتج عن تنفيذ ذلك الأمر القابل للتوسيع بعينه — أي استبدال الرمز الأصلي بنتائج/عواقب توسيعه *السلوك*ومع ذلك، فإن هذا "التعريف" الأولي للتوسيع — من حيث توليد رموز جديدة ليقرأها TeX — ليس دقيقًا تمامًا بالنسبة إلى جميع الأوامر القابلة للتوسيع، لكنه كافٍ جدًا كنقطة بداية.

لتقديم مثال مباشر: البدائية في TeX `\jobname` هي أمر قابل للتوسيع، و *الماكرو* هو تسلسل من رموز المحارف التي تمثل اسم ملف إدخال TeX الرئيسي. إذا قرر TeX توسيع `\jobname` أمر (رمز) فإنه *يُزال* من مصدر الإدخال الحالي لـTeX و *يُستبدل* بتسلسل رموز المحارف الذي يولّده — ثم يواصل TeX قراءته/معالجته.

ضمن `\directlua`، بعد معالجة (إزالة) رمز قابل للتوسيع واستبداله برموز جديدة، يواصل LuaTeX قراءة تلك الرموز الجديدة التي وضعها للتو — لكن بعض تلك الرموز الجديدة قد تكون أيضًا قابلة للتوسيع. ولأن `\directlua` ينفّذ ما يُسمّى *التوسيع الكامل*، فسوف يقرأ LuaTeX تلك الرموز الجديدة، ثم يمر مرة أخرى بعملية التوسيع لتوسيع (إزالة) أي رموز جديدة (قابلة للتوسيع) — وتستمر عملية التوسيع هذه حتى لا تبقى أي رموز قابلة للتوسيع. ومع ذلك، هناك استثناءان مهمان لهذه القاعدة "استمر في التوسيع"، وسنناقش كليهما أدناه:

* باستخدام البنية `\the\toks`;
* المنع المتعمد (الكبح) للتوسيع عن رمز واحد أو أكثر مختارين.

كما أُشير، فإن تعريفنا العملي (التقريب الأولي) لفهم التوسيع لا يغطي النطاق الكامل لسلوكيات التوسيع التي تعرضها المجموعة الفرعية من الأوامر القابلة للتوسيع. فعلى سبيل المثال، بعض الأوامر القابلة للتوسيع لا تولّد رموزًا بالطريقة التي `\jobname` يفعلها، لكنها قد:

* "تفلتر" الرموز من الإدخال: أوامر الشرط في محرك TeX (`\if`, `\ifcat`, `\ifnum`, `\ifdim`, `\ifodd`, `\ifvmode`، …) قابلة للتوسيع. وسلوك توسعها هو نوع من "تصفية الرموز" — ويمكن استخدام الشروط في `\directlua`.
* "التلاعب" بالرموز في الإدخال: [`\expandafter` الأمر](/latex/ar/mqalat-mtamqh/03-a-six-part-article-series-on-expandafter-tex-tokens-and-expansion.md) هو أمر قابل للتوسيع ويغيّر التسلسل الذي يُوسَّع به رمزان.
* منع التوسيع: الأوامر القابلة للتوسيع `\noexpand` و `\unexpanded` تمنع توسع رموز الأوامر في الإدخال.
* تحويل تسلسلات المحارف في الإدخال إلى رموز أوامر: `\csname … \endcsname.`
* تحويل الكميات الداخلية إلى تسلسل من رموز المحارف: `\number` و `\the` هي أوامر قابلة للتوسيع تولّد تسلسلًا من رموز المحارف يمثّل قيمة كمية داخلية.
* تحويل رموز الأوامر إلى رموز محارف: `\string` و `\detokenize` هما أمران قابلان للتوسيع يحوّلان وسائطهما إلى تسلسل من رموز المحارف ذات رمز الفئة 12. لاحظ أن `\detokenize` يختلف عن `\string`: `\detokenize` يمكنه معالجة عدة رموز ويضيف حرف مسافة، ذو رمز الفئة 10، بعد معالجة رموز الأوامر المنشأة من *كلمات التحكم*. عمليًا، `\detokenize` يضيف حرف مسافة في نهاية اسم الأمر — سنرى بعض الأمثلة لاحقًا في المقال.

#### تنقيح "تعريفنا" للتوسيع

يمكننا الآن تعميم تعريفنا لنقول إن توسيع أمر (رمز) يتضمن *الإزالة* لذلك الأمر (الرمز) من مصدر الإدخال الحالي لـTeX و *واستبداله* بنتيجة *عملية/عمليات الرموز* التي ينفذها ذلك الأمر. في الجوهر، تتسبب عملية التوسيع في أن ينفذ أمر قابل للتوسيع نوعًا ما من "العملية" على الرموز في المدخل الحالي لـTeX، وهو ما يؤثر في العدد، أو السلوك، للرموز التي سيقرأها TeX لاحقًا — وتعتمد الطبيعة الدقيقة لتلك "العملية" على الأمر الذي يجري توسيعه. جميع الماكروات، والمحارف النشطة، قابلة للتوسيع، لكن عددًا صغيرًا فقط من الأوامر المدمجة (البدائيات) في محرك TeX يصنَّف على أنه قابل للتوسيع — وتختلف قائمة الأوامر القابلة للتوسيع بحسب محرك TeX الذي تستخدمه.

يرث كل محرك TeX جديد الأوامر البدائية المدمجة في أسلافه — أي محركات TeX من الأجيال السابقة التي اشتُق منها — وستكون بعض تلك البدائيات الموروثة قابلة للتوسيع. بالطبع، قد يختار محرك TeX جديد ألا يطبّق بعض الأوامر البدائية الموجودة في المحركات الأقدم أو أن يغيّر سلوكها بما يلائم احتياجات المحرك الجديد. بالإضافة إلى ذلك، تطبّق محركات TeX الجديدة عادةً بدائيات إضافية لتوفير دعم لوظائفها المحسّنة الخاصة — وبعض تلك البدائيات قد تكون أيضًا قابلة للتوسيع. وبالتالي، فإن عدد الأوامر القابلة للتوسيع المتاحة لك سيختلف بحسب محرك TeX الذي تستخدمه — وLuaTeX لديه مجموعة كبيرة منها.

ومن الصعوبات الأخرى في شرح/فهم التوسيع، وربما التحدي الحقيقي، هو معرفة بالضبط *متى* يكون محرك TeX، أو لا يكون، بصدد تنفيذ عملية التوسيع فعلًا. وهذا موضوع واسع ومعقّد لأن التوسيع مدمج بعمق في آليات عمل محركات TeX الداخلية: لا توجد لدينا مساحة لمناقشة هذا بالتفصيل إلا بخصوص استخدام التوسيع في `\directlua`.

### كيف يعالج LuaTeX \directlua: نظرة أخيرة

تلخّص المخططات التالية `\directlua` أنشطة المعالجة المسبقة التي تتم داخل محرك LuaTeX نفسه. وفي هذا المخطط نعرض أيضًا وظيفتين داخليتين منخفضتي المستوى في LuaTeX تقومان فعليًا بالعمل: `scan_toks()` و `tokenlist_to_cstring()`. هاتان الوظيفتان مكتوبتان بلغة C وتوجدان في أعماق برنامج LuaTeX التنفيذي: وهما جزء من الآلية الداخلية لـLuaTeX وليستا *تحرير* متاحتين لك في كود TeX/LaTeX الخاص بك.

![](/files/75d7f8a80dbb3c463fc391a4301ce65f12a6f60d)

الوصف التالي لـ `\directlua ⟨code⟩`لنشاطه في المعالجة المسبقة يلخّص المخطط أعلاه.

1. يُعالَج تسلسل المحارف في ⟨code⟩ الخاص بك بواسطة `scan_toks()`. والغرض منه قراءة ⟨code⟩ حرفًا حرفًا لتوليد رموز محارف ورموز أوامر. ولأنه ينشئ رموزًا، فإن رمز الفئة المخصص لكل حرف في ⟨code⟩ عند قراءته مهم للغاية.
2. خلال توليد رموز `scan_toks()`يُوسَّع أي أمر قابل للتوسيع (رمز) *إلا إذا* ويُمنع ذلك عبر أوامر مثل `\protected` (تعريفات الماكرو)، `\noexpand`, `\unexpanded` إلخ. كما أن المحارف النشطة (رمز الفئة 13) تُوسَّع أيضًا (ما لم يُمنع ذلك).
3. يُبنى تيار الرموز الذي ينشئه `scan_toks()` في قائمة رموز واحدة طويلة — وتضم الرموز الموجودة في تلك القائمة تلك الناتجة من تطبيق التوسيع على الأوامر القابلة للتوسيع (مثل الماكروات) الموجودة في `⟨code⟩`. لاحظ أيضًا أن `scan_toks()` *لا* تُفعِّل أو تتسبب في تنفيذ أي رمز يمثل أمرًا غير قابل للتوسيع: فهذه الرموز غير القابلة للتوسيع تُمرَّر ببساطة ليُدرَجَت في قائمة الرموز التي يجري بناؤها.
4. بمجرد الانتهاء من قائمة الرموز واكتمال نشاط التوسيع كله، تُعالَج تلك القائمة بواسطة دالة أخرى تُسمّى `tokenlist_to_cstring()` التي تُحوّل كل رمز في قائمة الرموز النهائية مرة أخرى إلى تمثيله النصي. وهذا يولّد سلسلة من النص هي شيفرة Lua التي ستُمرَّر إلى مفسر Lua. ولنجاح التنفيذ، يجب أن تحتوي تلك السلسلة على شيفرة Lua صحيحة نحويًا.
5. تتم معالجة Lua لتلك الشيفرة على خطوتين:
6. يحلّل مفسر Lua المدمج في LuaTeX شيفرة Lua الناتجة في الخطوات السابقة ويقوم "بتجميعها". إذا فشل هذا التحليل/التجميع، فسيولّد مفسر Lua أخطاءً (مثل أخطاء الصياغة) — وقد تتسبب تلك الأخطاء في فشل تشغيل LuaTeX ما لم تكن قد اخترت استخدام `--interaction=nonstopmode` في سطر الأوامر.
7. إذا نجح التحليل/التجميع، ينفذ مفسر Lua الشيفرة المترجمة في الخطوة (5a).

في الجوهر، فإن `scan_toks()` هي قلب أنشطة المعالجة المسبقة في LuaTeX: فمهمتها الرئيسية هي توسيع كل الأوامر القابلة للتوسيع في TeX/LaTeX الموجودة في نص `⟨code⟩` وبناء قائمة رموز من كل ما عالجه. ومرة أخرى نؤكد أن `scan_toks()` *لا ينفذ الأوامر غير القابلة للتوسيع* (الرموز): فهو ببساطة *يخزن* تلك الرموز في قائمة الرموز التي يبنيها. وبمجرد اكتمالها، تُحوَّل تلك القائمة لاحقًا *إلى تمثيل نصي مرة أخرى* بواسطة `tokenlist_to_cstring()`— فقائمة الرموز هي مفهوم خاص بـTeX فقط، وهو غريب تمامًا على مفسر Lua، ومن ثم الحاجة إلى تحويلها إلى نص لتصبح شيفرة Lua تُمرَّر إلى مفسر Lua.

## التوسيع كواجهة "لغة برمجة"

يمكنك أن تفكر في `\directlua`بوصف عملية التوسيع الخاصة بـLuaTeX تُستخدم كآلية، أو كواجهة، لتمرير البيانات/المعلومات من "عالم TeX" إلى "عالم Lua": أي توفير طريقة للغة TeX للتواصل بالبيانات مع لغة Lua. على سبيل المثال، يمكن استخدام كود TeX مثل `\number\count75` لنقل قيمة من "عالم TeX" مخزنة في سجل العدّ 75 إلى المتغير الصحيح x في "عالم Lua":

```
\count75=1564 % بيانات موجودة في "عالم TeX"
\directlua{
   local x=\number\count75 \space % نقل بيانات TeX إلى "عالم Lua"
   tex.print("x= "..x)
   local y = (2*x-65)/5
   tex.print(" و y = "..y)
}
```

هذا يولّد شيفرة Lua

```
 local x=1564 tex.print("x= "..x) local y = (2*x-65)/5 tex.print(" و y = "..y)
```

**ملاحظة**‏: أضفنا `<space>\space` بعد `\number\count75` للتأكد من الحفاظ على حرف مسافة بين `1564` و `tex.print`— وهذا ليس ضروريًا هنا تمامًا لأن Lua سيحلل الشيفرة بشكل صحيح حتى لو حذفناه. حرف المسافة الذي يأتي مباشرة بعد `\count75` يُمتَص أثناء العملية التي تستخدمها محركات TeX للبحث عن القيم العددية — وهنا القيمة المقدَّمة إلى `\count`. حرف المسافة بعد `75` يُستخدم لإنهاء بحث LuaTeX عن تسلسل الأرقام `75` ويُمتص من الإدخال. و `\space` الماكرو يتوسع لتوفير حرف المسافة اللازم لفصل النص `1564` و `tex.print`.

وباستخدام الشيفرة أعلاه سيقوم LuaTeX بتنضيد

`x= 1564 و y = 612.6`

هنا تتحقق آلية "تمرير البيانات" بواسطة `\number`‏: أمر قابل للتوسيع يوجّه TeX، في هذه الحالة، إلى استرجاع القيمة المخزنة في `\count` مسجل TeX `75` ومن تلك القيمة (`1546`) توليد سلسلة من رموز المحارف، رمز محرف واحد لكل رقم، مما ينتج تسلسل رموز للأرقام `1`, `5`, `6` و `4`. وتُدمَج تلك الرموز الأربعة للمحارف في قائمة الرموز الرئيسية التي يبنيها `\directlua` ثم تُحوَّل لاحقًا مرة أخرى إلى تمثيلها النصي عندما تُحوَّل قائمة الرموز إلى نص. ومن المؤكد أنها طريق ملتوية جدًا للانتقال من `\count75` قيمة السجل المخزنة داخل LuaTeX إلى أرقام مخصصة لشيفرة Lua، لكن في النهاية، إنها تعمل.

**تلميح:** إذا أردتَ فحص نتائج أنشطة التوسيع في LuaTeX فيمكنك كتابة شيفرة مثل هذه:

```
\directlua{
   local foo=[[local x=\number\count75
   tex.print("x= "..x)
   local y = (2*x-65)/5
   tex.print(" و y = "..y)]]
   print(foo)
}
```

في هذا المثال نستخدم طريقة الأقواس الطويلة لإنشاء متغير سلسلة `foo` والذي هدفه الاحتفاظ بسلسلة شيفرة Lua الناتجة من توسيع كل ما يقع بين `[[` و `]]`. وتُطبع تلك السلسلة على وحدة التحكم عبر استدعاء دالة Lua `print(foo)`.

في Overleaf يمكنك عرض نتائج مشابهة عن طريق كتابة محتويات `foo` إلى `.log` الملف باستخدام دالة Lua الخاصة بـLuaTeX `texio.write()`:

```
\directlua{
   local foo=[[local x=\number\count75
   tex.print("x= "..x)
   local y = (2*x-65)/5
   tex.print(" و y = "..y)]]
   texio.write(foo)
}
```

## الرموز في قائمة رموز \directlua: الرموز غير القابلة للتوسيع والرموز غير الموسعة

لقد أشرنا إلى أن `\directlua{⟨code⟩}` ينفذ *التوسيع الكامل* من `⟨code⟩`المعالجة المسبقة لـك: فهو يزيل ويُوسّع كل الأوامر القابلة للتوسيع حتى لا تبقى سوى الرموز غير القابلة للتوسيع. وتتسلسل الرموز التي ينشئها `\directlua`في عملية المعالجة (في دالة `scan_toks()` ) لتشكّل قائمة رموز تُحوَّل رموزها الفردية مرة أخرى إلى نص لتمريرها إلى Lua.

ومع ذلك، لم نتناول بعد الجزء الأخير من هذه القصة لأننا نحتاج إلى النظر في الفئتين من رموز الأوامر اللتين يمكنهما الوصول إلى قائمة الرموز التي يجري بناؤها داخل `\directlua`: وسنشير إليهما باسم *رموز الأوامر المختصرة* و *غير الموسعة* الرموز:

* **رموز الأوامر المختصرة**: ينشأ هذا النوع من رموز الأوامر من سلاسل تحكم معرّفة باستخدام إحدى بدائيات TeX `\chardef`, `\mathchardef`, `\countdef`, `\dimendef`, `\skipdef`, `\muskipdef` و `\toksdef`. تُستخدم هذه الأوامر البدائية لتعريف سلاسل تحكم تمثل قيمة عددية — وتكون سلاسل التحكم الناتجة *تحتوي* قابلة للتوسيع.
* **رموز غير موسعة**: ينشأ هذا النوع من الرموز من أوامر كان من المفترض عادةً أن تُوسَّع لكنها `\directlua` إما:
* قد صدرت لها تعليمات صريحة *تحتوي* بتوسيعها؛ على سبيل المثال، كبح التوسيع بواسطة الأوامر `\noexpand` أو `\unexpanded`— وسنشرح قريبًا كيف يتم ذلك؛
* أُدرجت الرموز عن طريق معالجة التسلسل `\the\toks` (المزيد عن ذلك أدناه).

### مجموعتان من الرموز في قائمة رموز \directlua

استنادًا إلى مناقشاتنا، يمكننا القول إن الرموز الموجودة في قائمة الرموز الجاري بناؤها خلال المرحلة الأولى من `\directlua`للمعالجة المسبقة لـ `scan_toks()` (في دالة

1. *غير قابلة للتوسيع بطبيعتها* رموز

* أي رمز يمثل *الحرف*;
* أي رمز يمثل *بدائية* *الأمر*;
* أي رمز يمثل *أمرًا مختصرًا* (هذه ليست قابلة للتوسيع، انظر أدناه).

3. *غير الموسعة* الرموز:

* أي رمز يمثل أمرًا قابلًا للتوسيع وكان توسعُه *مُكبوتًا* (أو متجنبًا) أثناء `\directlua`المعالجة المسبقة لـ

#### رموز الأوامر المختصرة: إنشاء أوامر غير قابلة للتوسيع

كما أُشير، توفر محركات TeX مجموعة من البدائيات (الأوامر المدمجة) التي يمكن استخدامها لبناء *غير قابلة للتوسيع* سلاسل تحكم (يُشار إليها هنا بـ `⟨command⟩`). تأتي هذه البدائيات على الشكل:

* `\chardef ⟨command⟩ = ⟨numeric value⟩`
* `\mathchardef ⟨command⟩ = ⟨numeric value⟩`
* `\countdef ⟨command⟩ = ⟨numeric value⟩`
* `\dimendef ⟨command⟩ = ⟨numeric value⟩`
* `\skipdef ⟨command⟩ = ⟨numeric value⟩`
* `\muskipdef ⟨command⟩ = ⟨numeric value⟩`
* `\toksdef ⟨command⟩ = ⟨numeric value⟩`

حيث `⟨numeric value⟩` هي قيمة صحيحة ما ملائمة لكل أمر.

هنا سنراجع بإيجاز استخدام `\chardef` لإظهار الميزة الأساسية لهذه البدائيات — وهي إنتاج `⟨command⟩` غير قابل للتوسيع. يمكنك استخدام ``\chardef\mydollar=`\$`` لإنشاء سلسلة التحكم `\mydollar` واستخدامها لتنضيد `$`:

`I paid \mydollar30.`

سيُنضَّد هذا على النحو `I paid $30.` سلسلة التحكم `\mydollar` التي أنشأها `\chardef` ليست قابلة للتوسيع، كما يمكننا أن نرى من المثال التالي:

```
\chardef\mydollar=`\$
\directlua{
   local x =[[I paid \mydollar30.]]
   texio.write(x)
}
```

والذي ينتج النص التالي في `.log` قابل للتحرير

`I paid \mydollar 30.`

وهذا يبيّن `\mydollar` قد *تحتوي* توسّع أثناء `\directlua`المعالجة المسبقة لـ `\mydollar` تُضاف المسافة الظاهرة بعد

عندما تستخدم `\chardef` لإنشاء سلسلة تحكم، فإن التصنيف الداخلي في TeX لتلك السلسلة (الأمر) يجعلها *غير قابلة للتوسيع* وهو سلوك مختلف جدًا مقارنة بسلاسل التحكم المعرّفة بواسطة أحد أوامر تعريف الماكرو: \def أو \edef أو \gdef أو \xdef. كما أُشير أعلاه، أثناء عملية بناء قائمة الرموز الخاصة به `\directlua` يفحص كل رمز أمر وارد للتحقق من قابلية التوسيع. إذا لم يكن رمز الأمر قابلًا للتوسيع، فإنه يمر مباشرةً إلى قائمة الرموز وستظهر لاحقًا تمثيلاته النصية مرة أخرى في سلسلة شيفرة Lua الناتجة عن تحويل الرموز في قائمة الرموز إلى صيغتها النصية.

**ملاحظات موجزة حول plain TeX مقابل LaTeX**

تاريخيًا، عرّف plain TeX الأصلي الذي وضعه Knuth رمزي التحكم الشائعين `\%`, `\&`, `\#` و `\$` باستخدام `\chardef`— وليس باستخدام أحد أوامر تعريف الماكرو القياسية `\def`, `\edef`, `\gdef` أو `\xdef`. على سبيل المثال:

```
   \chardef\#=`\#
   \chardef\$=`\$
   \chardef\%=`\%
   \chardef\&=`\&
```

الـ `` `\ `` التركيب هو طريقة في TeX للحصول على القيمة الرقمية لرمز المحرف. في نظام plain TeX القديم، هذه الرموز التحكمية ليست قابلة للتوسيع (بسبب `\chardef`) لكن LaTeX (أو الحزم) قد يعيد تعريفها على أنها *ماكروهات* لتوفير وظائف محسّنة — وهذا سيجعلها قابلة للتوسيع، لذا قد تحتاج إلى الانتباه إلى ذلك.

**كيف يؤثر هذا في \directlua؟**

لنقارن نتيجة الشيفرة التالية عند تشغيلها تحت plain TeX وLaTeX. وللتبسيط سنكتب النتائج إلى `.log` الملف باستخدام دالة Lua API الخاصة بـLuaTeX `texio.write()`.

```
\directlua{
   local x=[[\$150 for the "\#1" product---20\%! more than its competitor, Widget \& Co.]]
   texio.write(x)
}
```

عند تشغيل هذه الشيفرة باستخدام **plain TeX** تنتج المخرجات التالية في `.log` الملف، موضحةً نتيجة أي توسعات:

```
\$150 for the "\#1" product---20\%! more than its competitor, Widget \& Co.
```

من الواضح أنه في plain TeX لم تُوسَّع أيٌّ من رموز التحكم`\$`, `\#`, `\%` أو `\&` — لأنها جميعًا أُنشئت باستخدام `\chardef`.

تشغيل تلك الشيفرة باستخدام **LaTeX** المستند:

```
\documentclass{article}
\begin{document}
   \directlua{local x=[[\$150 for the "\#1" product---20\%! more than its competitor, Widget \& Co.]] texio.write(x)}
\end{document}
```

تنتج المخرجات التالية في `.log` قابل للتحرير

```
\protect \TU\textdollar 150 for the "\#1" product---20\%! more than its competitor, Widget \& Co.
```

من الواضح أن تشغيل LaTeX يولّد نتيجة مختلفة عن plain TeX لأن الأمر `\$` قد توسّع، مما يدل على أنه ماكرو.

**ملاحظة:** في كلٍّ من plain TeX وLaTeX `\directlua` لم يعالج بالكامل أيًّا من رموز التحكم `\%`, `\&`, `\#` و `\$` لتوليد المحرف المقابل. أثناء عملية التوسيع التي ينفذها `\directlua` تمر الرموز الممثلة لهذه الرموز التحكمية — أو، في حالة LaTeX، توسعها — مباشرةً إلى قائمة الرموز الرئيسية التي يجري بناؤها.

**ملاحظة:** تتكوّن الرموز التحكمية من محرف واحد ليس من الفئة 11، مثل `\#`. عندما يُحوَّل رمز يمثل رمزًا تحكميًا مرة أخرى إلى تمثيله النصي، فإن محركات TeX لا تُدرج حرف مسافة بعد ذلك النص. وهذه المعالجة الخاصة للرموز التحكمية هي قاعدة مدمجة لكيفية عمل محركات TeX.

### الرموز غير الموسعة: كبح التوسيع

`\directlua`تُعدّ المعالجة المسبقة إحدى الحالات التي ينفّذ فيها محرك TeX عملية التوسيع، ولكن قد ترغب في *منع* تطبيق التوسيع على رمز مميز واحد أو أكثر كان سيتم توسيعه لولا ذلك. وبطريقة مثال آخر، ينفّذ LuaTeX (وجميع محركات TeX) عملية توسيع، مشابهةً لتلك الخاصة بـ `\directlua`، عند معالجتها `\write` الأمر:

`\write file-number {⟨material⟩}`

يأمر ‎\write‎ محركَ TeX بإخراج `⟨material⟩`—وغالبًا ما تحتوي على أوامر TeX/LaTeX—إلى ملف نصي (`file-number`); وأي أوامر قابلة للتوسيع داخل `⟨material⟩` سيتم، ما لم يُمنع ذلك، توسيعها قبل أن `⟨material⟩` تُكتب فعليًا إلى ذلك الملف.

وكما قد تتوقع، توفّر محركات TeX أوامر لكبح التوسيع أو التحكّم فيه:

* `\noexpand⟨token⟩`: يمنع توسيع الرمز المميز الوحيد `⟨token⟩`;
* `\unexpanded{⟨material⟩}`: يمنع توسيع جميع الأوامر القابلة للتوسيع (الرموز المميزة) في `⟨material⟩`. وهو، في الواقع، نسخة متعددة الرموز من `\noexpand`;
* `\protected`: بادئة تُضاف إلى تعريفات الماكرو وتمنع توسيع ذلك الماكرو في ظروف معيّنة (مثل أثناء `\directlua`, `\write` أو `\edef`).

وعلى الرغم من الأسماء التي توحي بغير ذلك، فإن كلاً من `\noexpand` و `\unexpanded` كنت *الأوامر القابلة للتوسيع* ويوفّر مثالين جيدين للنظر إلى عملية التوسيع في محرك TeX بوصفها «عمليات على الرموز المميزة»: فالمهمة هنا هي منع توسيع رمز مميز واحد أو أكثر لاحقًا (أوامر). ولأن `\noexpand` و `\unexpanded` كلاهما أوامر قابلة للتوسيع، فإنهما تُزالان وتُعالجان (تُنفّذان) أثناء `\directlua`المعالجة المسبقة الخاصة به أثناء بنائه قائمة الرموز المميزة من `⟨code⟩`.

#### \noexpand ⟨token⟩

`\noexpand ⟨token⟩` يمنع توسيع الرمز المميز الوحيد `⟨token⟩`. `\noexpand` داخل `\directlua` سيتم توسيعه (ويُزال من الإدخال) ويُستبدل بنتائج «سلوكه التوسعي». ونتيجة توسيع `\noexpand` هي إنشاء رمز مميز خاص (مخفي) `⟨marker token⟩` يوضع أمام الرمز المميز الأصلي `⟨token⟩` الذي يُراد كبح توسّعه: وهذا `⟨marker token⟩` يعمل كإشارة تقول «لا توسّع الرمز المميز التالي». ولأن `\directlua` ينفّذ توسعًا كاملًا، فإنه سيعيد معالجة أي رموز مميزة تنتج عن «سلوك التوسيع» لأي أمر قابل للتوسيع. وبالتالي، عندما يكتمل توسيع `\noexpand ⟨token⟩` ، يعود LuaTeX لقراءة النتائج ويرى تسلسل الرمزَين المميزَين `⟨marker token⟩⟨token⟩` الأمر الذي يتسبب في مرور الرمز المميز الأصلي `⟨token⟩` كما هو، دون توسيع، إلى قائمة الرموز المميزة التي يجري بناؤها بواسطة `\directlua`.

**مثال**

إذا كتبنا

```
\directlua{
   local x= "\TeX"
}
```

موقع `\TeX` يُوسَّع الماكرو إلى رموزه المميزة المكوِّنة له، والتي ستؤدي في TeX العادي إلى تمرير النص التالي إلى Lua (ملاحظة: لا يستطيع Lua معالجة هذا الرمز، فهو مجرد مثال لتوضيح العملية):

`local x = "T\kern -.1667em\lower .5ex\hbox {E}\kern -.125emX"`

إذا قمنا بـ *كبح* توسيع `\TeX` الماكرو باستخدام `\noexpand`

`\directlua{local x= "\noexpand\TeX"}`

ينتج كود Lua التالي (ومرة أخرى، لا يستطيع Lua تشغيل هذا الكود؛ إنه مجرد مثال لتوضيح `\noexpand`):

`local x= "\TeX "`

بسبب `\noexpand`, `\directlua` لن يُوسَّع `\TeX` بل سيكتفي بالسماح لقيمة الرمز المميز التي تمثل `\TeX` الأمر بالمرور سليمةً، إلى قائمة الرموز المميزة التي تُبنى أثناء المرحلة الأولى من `\directlua`المعالجة المسبقة لـ

**ملاحظة:** حرف المسافة الذي يظهر بعد `\TeX` يُضاف من خلال تحويل LuaTeX اللاحق لـ `\TeX` قيمة الرمز المميز الصحيح العددية إلى تمثيلها النصي (داخل `tokenlist_to_cstring()` الدالة).

#### \unexpanded{⟨material⟩}

`\unexpanded` هو أمر قابل للتوسيع يكبح توسيع جميع الرموز المميزة المتكوِّنة من `⟨material⟩`. وكما ذكرنا، عندما ينفذ محرك TeX التوسيع فإن أي أمر قابل للتوسيع *يُزال* يُزال من الإدخال و *يُستبدل* يُستبدل بنتائج «سلوكه التوسعي»؛ فما الذي يعنيه ذلك فعليًا بالنسبة إلى `\unexpanded`؟ عادةً، أثناء *التوسيع الكامل*، وبمجرد اكتمال عملية توسيع أمر معيّن، يواصل محرك TeX قراءة/معالجة أي رموز مميزة ناتجة عن «سلوك التوسيع» لذلك الأمر—إذ يحتاج إلى مواصلة توسيع أي رموز مميزة تم إنتاجها. ولكن، `\unexpanded` *يتجاوز* أي توسيع إضافي: وهذه هي الطريقة التي يفعل بها ذلك.

داخل محرك TeX، يقوم `\unexpanded` الأمر أولًا بتحويل الأحرف والأوامر الموجودة في `⟨material⟩` إلى قائمة رموز مميزة مؤقتة مكوّنة من *غير الموسعة* رموز مميزة. وبعد إنشاء جميع الرموز المميزة وتخزينها في تلك القائمة المؤقتة، فإن `\unexpanded` الأمر يتسبب في `\directlua` إلى *تجاوز* العودة لقراءتها ومعالجتها—على الرغم من أن \directlua ينفّذ توسعًا كاملًا. بدلًا من ذلك، فإن تلك *غير الموسعة* الرموز المميزة تمر مباشرةً وتُدمج في قائمة الرموز المميزة الرئيسية التي يتم بناؤها بواسطة `\directlua` (في `scan_toks()` الدالة). وبهذه الطريقة، يُحوَّل كل ما في `⟨material⟩` إلى رموز مميزة، وتُتجاوز عملية التوسيع بالنسبة إلى تلك المجموعة من الرموز المميزة. وتُعد عملية `\unexpanded{⟨material⟩}` مشابهة لاستخدام `\the\toks`، والذي نناقشه أدناه.

**مثال**

`\unexpanded` ينتج نتائج بطريقة مشابهة لـ `\noexpand` إلا أنه يمكنه منع توسيع عدة رموز مميزة؛ وإليك مثالًا:

```
\directlua{
   local x = "\unexpanded{\foo\bar\foobar}. But Lua can't process this code!"
}
```

والذي ينتج النص التالي بوصفه كودًا لـ Lua:

`local x = "\foo \bar \foobar . But Lua can't process this code!"`

**ملاحظة**: توجد محارف مسافة بعد اسم كل أمر. وهذا أيضًا نتيجة تحويل LuaTeX اللاحق للرموز المميزة غير الموسَّعة `\foo`, `\bar` و `\foobar` إلى نص داخل `tokenlist_to_cstring()` الدالة.

#### تعريفات الماكرو \protected

ال `\protected` هو بادئة تُضاف إلى تعريف ماكرو لمنع توسيع ذلك الماكرو عندما يقوم TeX ببناء قائمة رموز مميزة موسعة، مثل القائمة التي يبنيها `\directlua`المعالجة المسبقة لـ

**مثال**

افترض أنك تعرّف الماكروهات التالية مع استخدام البادئة التالية وبدون استخدامها: `\protected` البادئة:

```
\def\macroA{"This unprotected macro contains a string"}
\protected\def\macroB{"This protected macro also contains a string"}
```

إذا استخدمت عامل ربط السلاسل النصية في Lua (`..`) لكتابة

```
\directlua{
   local x=\macroA..\macroB
}
```

`\directlua`ستنتج المعالجة المسبقة لـ LuaTeX الكود التالي لتمريره إلى Lua:

`local x="This unprotected macro contains a string"..\macroB`

`\macroA` لم يُعرَّف باستخدام `\protected` لذلك يتم توسيعه، منتجًا الجزء الأول من السلسلة المراد ربطها، لكن `\macroB` مُعرّف باستخدام `\protected` لذلك لم يُوسَّع.

أثناء المعالجة المسبقة، تقوم `scan_toks()` دالة LuaTeX بإنشاء رمز مميز لـ `\macroA`، وتعرّف عليه بوصفه أمرًا عاديًا قابلًا للتوسيع وقامت بتوسيعه: ويُنتج ذلك التوسيع تسلسلًا من الرموز المميزة الحرفية، رمزًا مميزًا واحدًا لكل حرف في `"This unprotected macro contains a string"`. ويُمرَّر كل رمز مميز حرفي ويُضاف إلى قائمة الرموز المميزة التي يجري بناؤها.

عندما `scan_toks()` ينشئ الرمز المميز لـ `\macroB` تلاحظ أن هذا الأمر عُرِّف على أنه `\protected` ولا تُوسّعه: فالرمز المميز الذي يمثّل `\macroB` يمر كما هو، دون مساس (من دون توسيع)، إلى قائمة الرموز المميزة التي يجري بناؤها. وبعد إنشاء تلك القائمة، تكون المرحلة التالية من المعالجة المسبقة، داخل `tokenlist_to_cstring()` الدالة، هي تحويل جميع الرموز المميزة في القائمة مرةً أخرى إلى تمثيلها النصي. والرمز المميز غير الموسَّع الذي يمثّل `\macroB` يُكتشَف ويُحوَّل إلى تمثيله النصي، مما ينتج النص `\macroB` الذي يظهر في الكود الموجَّه إلى Lua. لاحظ أن Lua لا يستطيع فعليًا ربط `"This unprotected macro contains a string"..\macroB` لإنتاج السلسلة النهائية لأن `\macroB` لا يحمل أي معنى في صياغة Lua، مما يؤدي إلى الخطأ `unexpected symbol near '\'`.

**معلومة جانبية**: إن `\protected` تم تقديم الأمر بواسطة $$\varepsilon\text{-}\mathrm{\TeX}$$، وهو أول توسعة رئيسية لبرنامج TeX الأصلي الذي وضعه Knuth، وتدعمه جميع محركات TeX التي يتضمن نسب شيفرتها $$\varepsilon\text{-}\mathrm{\TeX}$$.

### الرموز المميزة غير الموسَّعة: استخدام \the\toks في \directlua

لن تكون الحياة في البرمجة كما هي من دون تلك «الحالات الخاصة» التي نتعامل معها واستخدام `\the` بالاقتران مع `\toks` في `\directlua` هو إحدى تلك الحالات الخاصة.

#### خلفية موجزة عن \toks

البدائية في TeX `\toks` يأمر محركَ TeX بحفظ بعض الرموز المميزة لاستخدامها لاحقًا: فبدلًا من تمريرها لمزيد من المعالجة، تُوضع تلك الرموز جانبًا وتُخزَّن في موقع ذاكرة يُحدَّد باستخدام *سجل رموز مميزة*. على سبيل المثال، يمكننا أن نطلب من محرك TeX إنشاء بعض الرموز المميزة وتخزينها في موقع سجل الرموز المميزة `100` باستخدام

`\toks100={Hi, \TeX! \hskip 5bp}`

هنا، يستخدم TeX سجل الرموز المميزة `100` للوصول إلى موقع معروف داخل ذاكرته: وهي منطقة تخزين مخصّصة للاحتفاظ بقوائم من الرموز المميزة.

تُنشأ الرموز المميزة التي تمثل كل ما بين `{` و `}` ، *لكن لا تُوسَّع*، وتُربط معًا في قائمة رموز مميزة—مشابهة لقائمة الرموز المميزة التي استعرضناها سابقًا في هذا المقال. ولإعادة استخدام تلك الرموز المميزة نكتب `\the\toks100` حيث إن `\the` ‎(أمر قابل للتوسيع) يأمر TeX بجلب الرموز المميزة المخزّنة وإدراجها في الموضع الذي كتبت فيه `\the\toks100`. ويمكن التفكير في هذا أيضًا على أنه `\the\toks` يتسبب في إدراج بعض الرموز المميزة في ذلك الموضع.

ال `\toks` الأمر *لا يُوسّع* أيًا من الرموز المميزة التي يُطلب منه إنشاؤها وحفظها: فهو ببساطة يحوّل الأحرف والأوامر بين `{` و `}` إلى رموز مميزة ويخزنها.

#### العودة إلى \directlua

في مناقشة التوسيع لاحظنا `\directlua{⟨code⟩}` ينفذ *التوسيع الكامل* لـ `⟨code⟩`: إزالة جميع الأوامر القابلة للتوسيع واستبدالها بنتيجة سلوكها التوسعي—مع الاستمرار في *مواصلة توسيع* أي رموز مميزة ناتجة عن التوسيع الأولي لأمر قابل للتوسيع.

`\the` هو أمر قابل للتوسيع، لذا `\directlua` سيتم توسيعه؛ ولكن عندما `\the` يُستخدم بالاقتران مع `\toks` داخل `\directlua`، كما في `\the\toks⟨token register⟩`، فإن الرموز المميزة المُدرجة *لا تُوسَّع أكثر من ذلك*. إن توسيع `\the\toks⟨token register⟩` يُدرِج تسلسل *غير الموسعة* من الرموز المميزة، المخزنة في `⟨token register⟩`، مباشرةً في قائمة الرموز المميزة التي يجري بناؤها بواسطة `\directlua`: يتجاوز هذا السلوك العملية المعتادة للتوسيع الكامل. وفي الواقع، تمر تلك الرموز المميزة *غير الموسعة*، لتُدمج في قائمة الرموز المميزة الرئيسية التي يجري بناؤها بواسطة `\directlua`—وتشبه عملية المرور هذه للرموز المميزة غير الموسَّعة في عملها `\unexpanded`، كما نوقش سابقًا.

**مثال**

افترض أننا نعرّف الماكرو `\mymacro` as `\def\mymacro{\TeX}`. وهو يحتوي على رمز مميز واحد فقط لـ `\TeX` الأمر (وهو ماكرو): لذلك لدينا أمر قابل للتوسيع `\mymacro` يحتوي على ماكرو آخر `\TeX`، وهو أيضًا قابل للتوسيع.

سيؤدي الكود التالي إلى محاولة Lua إنشاء متغير سلسلة نصية `x`:

```
\def\mymacro{\TeX}
\directlua{
   local x="\mymacro"
}
```

داخل \\`directlua`، فإن الرمز المميز لـ `\mymacro` يُوسَّع، لكن ذلك ينتج رمزًا مميزًا قابلاً للتوسيع آخر، `\TeX`، والذي يُوسَّع أكثر. وفي TeX العادي، تؤدي تلك التوسيعات إلى تمرير النص التالي إلى Lua:

`local x = "T\kern -.1667em\lower .5ex\hbox {E}\kern -.125emX"`

يحاول هذا الكود تعريف سلسلة نصية تحتوي على نص يمثّل النسخة الموسعة من `\TeX` الماكرو. إذا حاولت تشغيل هذا المثال فسيحاول Lua إنشاء تلك السلسلة النصية لكنه سيفشل، مولّدًا خطأً:

`تسلسل هروب غير صالح بالقرب من ' "T\k'.`

لاحقًا في هذا المقال سنستكشف معنى «تسلسل هروب غير صالح».

دعونا الآن نقارن استخدام `\mymacro` مع وضع `\TeX` الرمز المميز داخل قائمة رموز مميزة تم إنشاؤها بواسطة `\toks` الأمر:

```
\toks100={\TeX}
\directlua{
   local x="\the\toks100"
}
```

إن `\directlua` معالجة LuaTeX ستُنتج سلسلة النص هذه لـ Lua:

`local x = "\TeX "`

محرف المسافة بعد `\TeX` يُنتَج بواسطة عملية تحويل أوامر/رموز LuaTeX إلى سلسلة نصية.

**لكن لاحظ**: إن `\TeX` أن الماكرو قد *تحتوي* تم توسيعه إلى رموزه المميزة المكوِّنة له. `\the\toks100` تسبّب في إدراج الرموز المميزة المخزنة في السجل 100، وهذا كل شيء: فهي *تحتوي* لا تُوسَّع أكثر من ذلك وتُدمج في قائمة الرموز المميزة الرئيسية التي يجري بناؤها بواسطة `\directlua` (داخل الدالة `scan_toks()`). إن وضع الرموز المميزة في قائمة رموز مميزة أُنشئت بواسطة `\toks` هو طريقة أخرى لمنع توسيع الرموز المميزة.

إذا شغّلنا هذا المثال فسيُنتج هو أيضًا خطأً:

`تسلسل هروب غير صالح بالقرب من ' "\T'.`

سنستكشف تسلسلات الهروب في Lua لاحقًا في المقال.

## أوامر/تقنيات أخرى مستخدمة في التوسيع

في هذا القسم نلقي نظرة على بعض أوامر/طرائق TeX الإضافية التي قد تكون مفيدة في الحالات التي يُطبَّق فيها التوسيع (مثل داخل `\directlua`).

### \string ⟨token⟩

`\string` هو أمر قابل للتوسيع يحوّل الرمز المميز ⟨token⟩ إلى سلسلة من الرموز المميزة الحرفية، يحمل كل منها الرمز التصنيفي 12.

على سبيل المثال، `\string\TeX` سينتج سلسلة من 4 رموز مميزة حرفية `\`, `T`, `e` و `X` حيث يُسنَد إلى كل حرف الرمز التصنيفي 12 (بما في ذلك الحرف `\` ).

إذا كتبنا

```
\directlua{
   local x="I will use \string\newcommand"
   print(x)
}
```

موقع `\string` سيُوسَّع الأمر، مما ينتج تسلسلًا من الرموز المميزة الحرفية ذات الرمز التصنيفي 12. وبعد `\string` توسيع `\newcommand`)، ستُدمج الرموز المميزة الحرفية الناتجة (التي تمثل كل حرف في `\directlua`. وبمجرد أن `\directlua` ينتهي من بناء قائمة الرموز المميزة الرئيسية، تُحوَّل رموزه المميزة المكوِّنة له مرةً أخرى إلى تمثيلها النصي، مما ينتج الكود التالي لتمريره إلى مفسّر Lua:

`local x="I will use \newcommand" print(x)`

عند تمرير هذا الكود إلى Lua، `print(x)` سيطبع السلسلة النصية `x` على الشاشة (وحدة التحكّم). ولكننا كنّا ماكرين قليلًا واستخدمنا عمدًا أمرًا تجريبيًا يبدأ بـ `\n`. إذا كنت قادرًا على تشغيل هذا المثال على تثبيت TeX محلي فستلاحظ أن Lua يطبع النص التالي على الشاشة:

```
   سأستخدم
   ewcommand
```

لتشغيل هذا الكود على Overleaf يمكنك أن تأمر LuaTeX بالكتابة مباشرة إلى `.log` الملف باستخدام دالة Lua API الخاصة بـLuaTeX `texio.write(*string*)`:

```
\directlua{
   local x="I will use \string\newcommand"
   texio.write(x)
}
```

إذا فحصت الملف الناتج `.log` فسترى أنه يحتوي أيضًا على

```
   سأستخدم
   ewcommand
```

يعود سبب هذا الخرج غير المتوقع إلى أن Lua يفسّر `\n` في بداية `**\n**ewcommand` بوصفه تسلسل الهروب الخاص بمحرف تغذية السطر (رمز المحرف 10): فهو يفترض أنك تريد بدء سطر جديد من النص يبدأ بـ `ewcommand`. نناقش تسلسلات الهروب في Lua لاحقًا في هذا المقال.

### \detokenize{⟨material⟩}

`\detokenize` هو، في تأثيره، نسخة متعددة الرموز من `\string` وهو أيضًا أمر قابل للتوسيع يحوّل كل ما في `⟨material⟩` إلى سلسلة من الرموز المميزة الحرفية ذات الرمز التصنيفي 12—*باستثناء* حروف المسافة (قيمة ASCII/Unicode 32) التي تحصل على الرمز التصنيفي 10. `\detokenize` يضيف أيضًا حرف مسافة لاحقًا بعد أسماء الأوامر التي تكون *كلمات التحكم* (مثل، `\foo`) لكن لا يُدرَج أي حرف مسافة بعد *الرموز التحكمية* (مثل، `\#`, `\%` إلخ).

### مثال

حتى لو كانت الماكروهات `\foohoo`, `\foo`, `\bar` و `\foobar` غير معرّفة، فإذا كتبت هذا:

```
\directlua{
   local x = "\string\foohoo\detokenize{\foo\bar\foobar}"
}
```

فسينتج النص التالي بوصفه كودًا لتمريره إلى مفسّر Lua

`local x = "\foohoo\foo \bar \foobar "`

إذا لم تستخدم `\string` و `\detokenize` وكتبت:

`\directlua{local x = "\foohoo\foo\bar\foobar"}`

`\directlua` سيعالج `\foohoo`، ويتعرّف عليه بوصفه أمرًا ويحاول توسيعه؛ ولكن لأن `\foohoo` غير معرّف، فسيؤدي ذلك إلى خطأ:

```
   ! Undefined control sequence.
   l.1 \directlua{local x = "\foohoo
                      \foo\bar\foobar"}
         ?
```

لأن `\string` و `\detokenize` تحوّل وسائطها إلى سلسلة من الرموز المميزة الحرفية، `\directlua`فتتسنّى لعملية التوسيع في TeX فرصة اكتشاف الرموز المميزة للأوامر القابلة للتوسيع `\foohoo`, `\foo`, `\bar`، أو `\foobar`: إذ تُحوَّل إلى سلاسل من الرموز المميزة الحرفية قبل وقت طويل من أن تتمكن من تحفيز التوسيع.

كما أشرنا سابقًا، فإن توسيع أمرٍ ما يتضمن إزالته من الإدخال واستبداله بنتيجة «سلوكه التوسعي». ثم يقرأ محرك TeX نتائج التوسيع (عادةً رموزًا مميزة) لاحقًا. وهنا، فإن «سلوك التوسيع» الخاص بـ `\string` و `\detokenize` هو امتصاص الرموز المميزة الحرفية ورموز الأوامر من الإدخال وتحويلها إلى سلاسل من الرموز المميزة الحرفية، تُخزَّن في البداية في قائمة رموز مميزة مؤقتة، والتي `\directlua` يقرأها لاحقًا. ثم تُدمج تلك الرموز المميزة الحرفية في قائمة الرموز المميزة الرئيسية التي يجري بناؤها بواسطة `\directlua`.

يوضح الرسم التالي كيف `\string` يحوّل `\foohoo` الأمر إلى تسلسل من الرموز المميزة الحرفية، منتجًا قائمة رموز مميزة مؤقتة تُقرأ لاحقًا بواسطة `\directlua` لدمج تلك الرموز المميزة الحرفية في قائمة الرموز المميزة الرئيسية التي يجري بناؤها.

![](/files/151bbbf8aa326e1b20362d47b15e1ac32d131c48)

إذا `\string` أو `\detokenize` تواجه الأحرف في وسيطها، مثل `\string a` أو `\detokenize{abc}` تُنتج تلك الأحرف (هنا، ذات الرمز التصنيفي 11) رموزًا مميزة حرفية، ولكن بالرمز التصنيفي 12.

ملاحظات:

إذا عدنا إلى المثال أعلاه:

`\directlua{local x = "\string\foohoo\detokenize{\foo\bar\foobar}"}`

والذي ينتج النص التالي بوصفه كودًا لتمريره إلى مفسّر Lua

`local x = "\foohoo\foo \bar \foobar "`

يمكننا ملاحظة ما يلي:

* `\detokenize` أدرج حرف مسافة بعد اسم كل ماكرو، لكن `\string` لم يفعل ذلك.
* `\string` يعمل على رمز مميز واحد.
* في السلسلة النصية `"\foohoo\foo \bar \foobar "` المستخدمة لتعريف `x` سنصادف مرة أخرى آلية محرف الهروب في Lua (المناقشة أدناه):

  * `\bar` تبدأ بـ `\b` وهو تسلسل الهروب في Lua المستخدم لتمثيل [محرف رجوع للخلف](https://en.wikipedia.org/wiki/Backspace) (رمز المحرف 8)؛
  * الأوامر `\foohoo`, `\foo` و `\foobar` وكلها تبدأ بـ `\f`، وهو تسلسل الهروب في Lua المستخدم لتمثيل [محرف تغذية الصفحة](https://en.wikipedia.org/wiki/Page_break#Form_feed) (رمز المحرف 12).

  لأن تسلسلات المحارف `\b` و `\f` تُستخدم داخل سلسلة تم إنشاؤها باستخدام علامات اقتباس مزدوجة `"..."` ستنتج عنها نتائج غير مرغوب فيها ما لم تُتخذ خطوات لمنع ذلك باستخدام ما يُسمّى في Lua *الأقواس الطويلة* للسلاسل النصية: وهو موضوع يمكننا الآن مناقشته مع تسلسلات الهروب في Lua.

## ما هي «تسلسلات الهروب في Lua»؟

تحجز لغات البرمجة بعض الأحرف لاستخدام «خاص» ضمن بنية اللغة: أي إن تلك الأحرف تُعرَّف على أنها تحمل نوعًا من المعنى الخاص. ومع ذلك، هناك أوقات تحتاج فيها مؤقتًا إلى «إيقاف» هذا المعنى الخاص لحرف ما إذا كنت، مثلًا، تريد إدراج ذلك الحرف كجزء من سلسلة أطول حيث سيتسبب سلوكه القياسي في أخطاء نحوية. وباختصار، يحتاج ذلك الحرف إلى أن يُعالَج *بدون* مما يفعّل تفسيره القياسي—ليمرّ دون أن يُلاحظ. وللقيام بذلك، يستخدم المبرمجون تقنية تُسمّى *الهروب* حيث يُمثَّل «الحرف الخاص» بما يُسمّى *تسلسل الهروب*.

مثال قياسي (مدعوم أيضًا في Lua) هو استخدام علامات اقتباس مزدوجة داخل سلسلة، حيث يتم إخفاء علامات الاقتباس المزدوجة الداخلية باستخدام تسلسل الهروب `\\"`:

`"عندما سُئلوا عن LuaTeX أجابوا: \\"إنه محرك TeX رائع!\\" ووافقت."`

توفّر لغة Lua عددًا من الآليات للتعامل مع تسلسلات الهروب:

* تسلسلات قياسية تشمل `\n` (سطر جديد)، `\\r` (إرجاع العربة)، `\\` (شرطة مائلة عكسية)، `\\"` (علامة اقتباس مزدوجة)، `\\t` (جدولة أفقية)، `\\v` (جدولة عمودية) و `\\'` (علامة اقتباس مفردة);
* `\\xXX`، حيث `XX` هو تسلسل من رقمين ست عشريين بالضبط;
* `\\ddd`، حيث `ddd` هو تسلسل يتألف من ثلاثة أرقام عشرية كحد أقصى;
* في وقت كتابة هذه المقالة (أغسطس 2019)، كانت أحدث نسخة من LuaTeX، رغم أنها غير متاحة بعد على Overleaf، تستخدم الإصدار 5.3 من Lua الذي قدّم دعمًا لتسلسلات الهروب الخاصة بـ UTF-8: `\\u{XXX}`. آلية الهروب هذه مخصّصة لأحرف يونيكود المشفّرة بـ UTF-8 حيث `XXX` هو تسلسل من رقم ست عشري واحد أو أكثر يمثّل نقطة الترميز للحرف. لاحظ أن الأقواس المحيطة `{ }` إلزامية.

### التحكم في تسلسلات الهروب

تقليديًا، تُعرَّف السلاسل النصية باستخدام علامات اقتباس مزدوجة كما في `"هذه سلسلة نصية"`؛ داخل مثل هذه السلسلة يمكنك استخدام تسلسلات الهروب: `"هذه سلسلة نصية.\\nسأبدأ الآن سطرًا جديدًا."`. ومع ذلك، لدى Lua آلية ثانية و *جدًا* مريحة لتعريف السلاسل النصية: وهي ما يُسمّى *الأقواس الطويلة* آلية تقوم فيها بتعريف سلسلة نصية بإحاطة النص بـ `[[` و `]]`:

`[[أنا سلسلة نصية بالأقواس الطويلة]]`

داخل سلسلة تم إنشاؤها باستخدام طريقة الأقواس الطويلة، تكون آلية هروب الأحرف في Lua *معطّلة*: تُعامل تسلسلات الهروب كأحرف عادية. على سبيل المثال، في السلسلة

`[[أنا سلسلة نصية بالأقواس الطويلة\\n سلسلة]]`

موقع `\n` لا يُعامَل تسلسل الهروب على أنه حرف إرجاع العربة المفرد (الرمز ASCII 13)، بل كحرفين عاديين: `\` تليه `n`.

### لماذا تُعدّ سلاسل الأقواس الطويلة مفيدة جدًا؟

كما سنستكشف لاحقًا، يوفّر LuaTeX مجموعة من دوال Lua المدمجة والمتخصصة التي يمكنك استخدامها مع `\directlua` للتحكم في سلوك التنضيد في LuaTeX. ومن بين تلك الدوال الكثيرة دالة تُسمّى `tex.print(*string*)` التي تتيح لك تمرير `*string*` محتوى من كود Lua إلى LuaTeX من جديد من أجل التنضيد. مثال بسيط جدًا هو:

`\\directlua{tex.print("Hello, World!")}`

مما سيجعل LuaTeX ينضّد `مرحبًا، أيها العالم!`

ال `*string*` المستخدم في `tex.print(*string*)` يمكنه أيضًا أن يتضمن نصًا يمثّل أوامر TeX وLaTeX ليعالجها LuaTeX. ومع ذلك، تبدأ أوامر TeX/LaTeX بـ `\` حرف، وهذا يسبب مشكلة مع السلاسل المُنشأة باستخدام علامات اقتباس مزدوجة لأن Lua ستحاول تحليل السلسلة، وتكتشف الحرف الأول `\` وتفسّره على أنه بداية تسلسل هروب. وعندما تحاول Lua معالجة تسلسل الهروب، فغالبًا ما تفشل لأن الحرف الأول `\` المقترن بالحرف الأول في كثير من أسماء أوامر TeX/LaTeX لا يشكّل تسلسل هروب صالحًا معروفًا لدى Lua. على سبيل المثال، عند معالجة سلسلة مثل `"أنا أحب \\LaTeX"` سترى Lua `\\L` وتفشل مع الخطأ «تسلسل هروب غير صالح»، وهذا هو سبب الأخطاء المذكورة أعلاه.

#### تأتي سلاسل الأقواس الطويلة للإنقاذ!

تُعد طريقة الأقواس الطويلة في إنشاء (تعريف) السلاسل النصية مفيدة جدًا لأنها `\` حرف، فإن طريقة الأقواس الطويلة للسلاسل تعطل (توقف) آلية تسلسلات الهروب في Lua. إليك مثالًا قصيرًا، مع تذكّر أننا نحتاج إلى منع توسيع الماكروهات باستخدام، مثلًا، `\protected` أو `\noexpand`.

لنفترض أننا نعرّف `\\newtest` ماكرو هكذا

`**\\protected**\\def\\newtest#1{The argument: #1}`

ونستخدمه في `\directlua` مع دالة Lua API الخاصة بـ LuaTeX `tex.print()`:

```
\directlua{
   tex.print("\\newtest{Hello}")
}
```

بسبب استخدام `\protected`، لا يتم توسيع الماكرو `\\newtest` مما يؤدي إلى تمرير النص التالي إلى Lua:

`tex.print("\\newtest {Hello}")`

حرف المسافة المضاف بعد `\\newtest` وقبل القوس المفتوح (`{`) هو أثر جانبي لـ `\directlua`تحويل رموز الأوامر إلى تمثيلها النصي.

يُمرَّر هذا الكود إلى Lua التي تنفذ لاحقًا دالة LuaTeX `tex.print()` لكن توجد مشكلة تظهر بطرق تعتمد على الخطوط التي تستخدمها. في LaTeX على Overleaf سترى مخرجات مثل هذه:

![](/files/eb8c3ce78495951d7695055dd466815d87832f8f)

إلى جانب تحذير في ملف السجل:

```
   الحرف مفقود: لا يوجد
   (U+000A) في الخط [lmroman10-regular]:+tlig;!
```

في plain TeX قد ترى مخرجات تبدو شبيهة بهذا:

![](/files/cda5416f23492d0b70fc0fb47a37181d146a0746)

في كلتا الحالتين، `\\newtest` لا يتم استدعاء الماكرو، والمخرجات ليست كما قصدنا. الخطأ ناتج عن آلية حرف الهروب في Lua: في النص `\\newtest {Hello}` يبدأ اسم الماكرو بـ `\n` الذي تتعرّف عليه Lua كتسلسل الهروب الخاص بحرف تغذية سطر، فتستبدله `\n` بحرف ASCII 10، أو 0A بالصيغة الست عشرية. وفي رسالة خطأ LaTeX، `U+000A` هي طريقة لتمثيل قيمة يونيكود باستخدام 4 أرقام ست عشرية.

لأن `\n` يُحوَّل إلى حرف تغذية السطر، لا يرى LuaTeX استدعاء ماكرو، بل يعتقد بدلًا من ذلك أنه طُلب منه تنضيد نص يبدأ برمز ASCII 10:

`⟨ASCII 10⟩ewtest {Hello}`

اعتمادًا على الخط المستخدم، قد يتمكن LuaTeX أو لا يتمكن من تنضيد `⟨ASCII 10⟩` الحرف، لكن النص المتبقي يُخرج كما هو مع `{` و `}` تعامُلِه كمجموعة وعدم طباعته.

يُعطي Plain TeX نتيجة مختلفة لأن الخط الافتراضي هو Computer Modern Roman الذي يملك ترميزًا غريبًا يؤدي إلى تنضيد حرف أوميغا الكبير عند رؤية الرمز 10.

لمنع هذه المشكلات نحتاج إلى استخدام سلاسل الأقواس الطويلة لمنع تطبيق آلية الهروب في Lua. وتنتج النتيجة الصحيحة باستخدام

`\\directlua{tex.print([[\\newtest{Hello}]])}`

والذي ينتج النتيجة المعروضة في لقطة الشاشة التالية:

![](/files/7098af86a579e52afae23a57aeb6bfc3a4a408a3)

### التوسيع وعدم تنفيذ الأوامر غير القابلة للتوسيع

عند مناقشة التوسيع، أشرنا إلى أنه عملية يقوم فيها محرك TeX *يزيل* أمرًا قابلًا للتوسيع (رمزًا) من الإدخال الحالي و *يستبدله* بالنتائج التي ينتجها ذلك الأمر القابل للتوسيع. ولأن \\\directlua ينفّذ *عمليات التوسيع فقط* لا يدفع معالجة LuaTeX إلى ما هو أبعد من ذلك. وبمجرد قراءة أمر قابل للتوسيع وتوسيعه بالكامل، ستُدمَج نتائج ذلك التوسيع—التي تتضمن غالبًا أوامر غير قابلة للتوسيع (رموزًا)—في قائمة الرموز التي يجري بناؤها، لتصبح جاهزة لإعادتها إلى نص وتمريرها إلى Lua. *لا* هناك مبدأ مهم يحكم ذلك: أثناء

الأنشطة المصممة لإنتاج قائمة رموز، لا تقوم محركات TeX، بما فيها LuaTeX، *عمليات التوسيع فقط* بتنفيذ *أي أوامر TeX أولية أو مدمجة غير قابلة للتوسيع.* في حالة

، إذا كانت النسخة الموسعة بالكامل من `\directlua{⟨code⟩}`، إذا كانت النسخة الموسعة بالكامل من `⟨code⟩` تنتج، أو تحتوي، على أوامر TeX/LaTeX غير قابلة للتوسيع فإنها *سيتم تمريرها إلى Lua* (مُمثَّلة كنص).

#### مثال

إليك مثالًا يوضّح أن الأوامر الأولية غير القابلة للتوسيع لا تُنفَّذ أثناء المعالجة الخاصة بالتوسيع فقط (مثلًا ضمن `\directlua`). لنفترض أننا نعرّف ماكرو `\\setcountreg` بهذا الشكل:

`\\def\\setcountreg#1#2{\\count#1=#2\\relax}`

**ملاحظة**: نستخدم `\\relax` بعد المعامل `#2` لمنع LuaTeX من تجاوز الموضع أثناء مسح الإدخال بحثًا عن القيمة العددية (الحجة) لمطابقة المعامل `#2`.

إذا، خارج `\directlua`، شغّلنا الماكرو لاحقًا هكذا

```
   \\setcountreg{100}{50}
   القيمة في سجل العد 100 هي \\the\\count100.
```

فسيُخرج

`القيمة في سجل العد 100 هي 50.`

في هذا السياق، سيعالج أي محرك TeX الماكرو `\\setcountreg`—فيقوم بتوسيع الماكرو، وتحديد الوسائط، ويواصل القراءة *واتخاذ الإجراءات* (تنفيذ) الأوامر الموجودة في نص الاستبدال الخاص بالماكرو (تعريفه). والنتيجة هنا هي إسناد `50` كقيمة مخزّنة في السجل `\\count100`.

ومع ذلك، عندما يكون محرك TeX في طور تنفيذ *عمليات التوسيع فقط* الأنشطة، كما هو الحال مع `\directlua`، فإنه *لن ينفّذ* الأوامر غير القابلة للتوسيع الموجودة في تعريف الماكرو.

إذا كتبنا

```
\\def\\setcountreg#1#2{\\count#1=#2\\relax}
\directlua{
   local x = [[\\setcountreg{100}{50}]]
}
```

فإنه ينتج النص التالي ككود لـ Lua:

`local x = [[\\count 100=50\\relax ]]`

يُظهر كود Lua الناتج أعلاه أنه داخل `\directlua` موقع `\\setcountreg` تم توسيعه، وتم تحديد وسائطه واستبدالها في المعامل المناسب (`#1` و `#2`)، لكنه لا يتجاوز ذلك: الأمر الأولي غير القابل للتوسيع في TeX `\count` قد *لم يُنفَّذ* أثناء `\directlua`معالجة التوسيع الخاصة بـ

ومع ذلك، سينفّذ LuaTeX كود TeX إذا مررنا السلسلة الناتجة `x` *مرة أخرى إلى LuaTeX* عبر `tex.print(x)` هكذا

```
\\count100=50 % ضبط \\count100 على قيمة ابتدائية مقدارها 50
\\def\\setcountreg#1#2{\\count#1=#2\\relax}
\directlua{
   local x = [[\\setcountreg{100}{250}]]
   tex.print(x)
}
القيمة المخزنة في سجل العد 100 هي \\the\\count100.
```

بعد `\directlua` انتهائه، ستكون المخرجات

`القيمة المخزنة في سجل العد 100 هي 250.`

مما يبيّن أن سجل العد `100` يحتوي الآن على القيمة `250`.

كود Lua الناتج من المثال أعلاه هو

`local x = [[\\count 100=250\\relax ]] tex.print(x)`

هذا الكود يعرّف `x` على أنه سلسلة تم إنشاؤها بطريقة الأقواس الطويلة، وهي تُستخدم لتجنب الأخطاء الناتجة عن تسلسلات الهروب الخاطئة. وإذا استخدمنا علامات اقتباس مزدوجة `"..."` لتعريف x، فإن التركيبة الحرفية `\\c` في بداية `\count` ستتسبب في خطأ: `تسلسل هروب غير صالح قرب ' "\\c'`.

استدعاء Lua API الخاص بـ LuaTeX `tex.print(x)` يؤدي إلى تنفيذ LuaTeX لتسلسل كود TeX `\\count 100=250\\relax` و `\\count100` تُسند إليه قيمة `250` كما يظهر من المخرجات المنضّدة:

`القيمة المخزنة في سجل العد 100 هي 250.`

#### تنبيه: الماكروهات وLua API الخاص بـ LuaTeX

في المثال أعلاه رأينا أنه أثناء `\directlua`المعالجة المسبقة (التوسيع) لم يقم LuaTeX بتنفيذ الكود `\\count 100=250`، الذي يحتوي على `غير قابلة للتوسيع` الأمر الأولي `\count`: لكي نشغّل (ننّفذ) ذلك الكود كان علينا أن *نمرره مرة أخرى إلى LuaTeX* عبر `tex.print()`.

`\directlua` ليس سوى مثال واحد على قيام LuaTeX بمعالجة التوسيع فقط لبناء قائمة رموز. وهناك أوامر أخرى تقوم بعمليات توسع مشابهة وتوليد لقائمة الرموز، مثل `\write` و `\edef`: فهذه الأوامر أيضًا لا تنفذ الأوامر الأولية غير القابلة للتوسيع أثناء معالجة التوسيع الخاصة بها. وهو مبدأ عام بأن محركات TeX لا تنفّذ الأوامر الأولية غير القابلة للتوسيع عند إنشاء قائمة رموز أثناء أنشطة المعالجة الخاصة بالتوسيع فقط.

**إعادة كتابة الماكرو الخاص بنا لاستخدام Lua API في LuaTeX**

يمكننا إعادة كتابة `\\setcountreg` الماكرو باستخدام دالة في Lua API الخاصة بـ LuaTeX تُسمّى `tex.setcount()`، وبالتالي تجنب أوامر TeX لتغيير القيمة المخزنة في سجل العد `100`:

```
   \\def\\setcount#1#2{\\directlua{tex.setcount(#1,#2)}}
   \\count100=50
   سجل العد 100 يحتوي على \\the\\count100\\par
   \\setcount{100}{250}
   سجل العد 100 يحتوي الآن على \\the\\count100\\par
```

سيُنتج هذا الكود تنضيد:

```
سجل العد 100 يحتوي على 50
سجل العد 100 يحتوي الآن على 250
```

نحن هنا نستخدم `tex.setcount()`، وهي إحدى دوال Lua API العديدة في LuaTeX، من أجل *الوصول مباشرةً إلى* منطقة التخزين الداخلية للبيانات في LuaTeX لوضع القيمة `250` في موقع الذاكرة الممثّل لسجل العد `100`. وبذلك نكون، في الواقع، قد *تجاوزنا* أساليب معالجة الإدخال القياسية في محرك TeX الخاص بـ LuaTeX: قراءة الإدخال، وإنشاء الرموز، وتنفيذ أوامر TeX الأولية. ومع ذلك، هناك قصة تحذيرية: باستخدام دوال Lua API الخاصة بـ LuaTeX، يمكن لنشاط المعالجة الخاصة بالتوسيع فقط *أن يؤدي إلى آثار جانبية*: تغييرات في القيم المخزنة داخل محرك TeX كانت لن تكون ممكنة بأوامر TeX/LaTeX البحتة.

**مثال: آثار جانبية غير متوقعة**

إليك مثالًا يوضّح *غير متوقعة* آثارًا جانبية قد تنشأ مع الماكروهات التي تستخدم `\directlua`. لنفترض أننا نكتب الكود التالي:

```
\\def\\dochange{\\directlua{tex.setcount(999,12345)}}
\\edef\\careful{\\dochange}
\\the\\count999
```

عند تشغيل هذا الكود سيُنتج تنضيدًا لـ `12345`!

كيف يمكن ذلك؟ نحن لم *صراحةً* نستدعِ أي كود أو ماكرو لوضع تلك القيمة في سجل العد `999`. أم أننا فعلنا؟

لقد عرّفنا `أمرًا يستخدم` بـ `\directlua` لتخزين القيمة `tex.setcount()` في سجل العد `12345` : ففي كود TeX يعادل `999`\\\count999=12345 `. ثم استخدمنا الأمر الأولي القياسي في TeX`لتعريف الماكرو `\edef` \\\careful `—إن استخدام`هو ما يفعّل الأثر الجانبي غير المتوقع. `\edef` تحويله لرموز الأوامر إلى تمثيلها النصي.

`\edef` يوسّع وسيطه بالكامل: هنا يكتشف ماكرو قابلًا للتوسيع `أمرًا يستخدم` ويقوم بتوسيعه. الـ `أمرًا يستخدم` ماكرو يستخدم الأمر القابل للتوسيع `\directlua` الذي يحتوي على استدعاء لـ Lua API؛ لذا فإن توسيع `أمرًا يستخدم` يؤدي إلى توسيع `\directlua` وهذا يتسبب في `tex.setcount()` استدعاء، وهو ما يغيّر القيمة في سجل العد `999`.

إذا أعدنا تعريف `أمرًا يستخدم` ليستخدم أوامر TeX:

```
   قبل: سجل العد 999 يحتوي على \\the\\count999.\\par
   \\def\\dochange{\\count999=12345\\relax}
   \\edef\\careful{\\dochange}
   بعد: سجل العد 999 يحتوي على \\the\\count999.\\par
```

عند تشغيل هذا الكود سيُنتج تنضيدًا لـ

```
قبل: سجل العد 999 يحتوي على 0.
بعد: سجل العد 999 يحتوي على 0.
```

من الواضح أنه لم يكن هناك أي تأثير على `\\count999`. عندما `\edef` يعرّف `—إن استخدام` فهو يوسّع `أمرًا يستخدم` لكن ذلك التوسيع ينتج أوامر TeX أولية غير قابلة للتوسيع فقط: فهي *لم يُنفَّذ* بل ببساطة *مخزنة* في قائمة الرموز المكوِّنة لتعريف `—إن استخدام`.

ولتمام الفائدة، يفسر المبدأ نفسه سبب إنتاج هذا تنضيدًا للمخرجات:

```
\\def\\dochange{\\directlua{tex.print("Hello")}}
\\edef\\careful{\\dochange}
```

## مقدمة موجزة إلى Lua API الخاصة بـ LuaTeX

كما رأينا، `\directlua` لا يتيح لك فقط كتابة كود Lua تقليدي، أو مزيج من كود Lua وكود TeX/LaTeX، بل يوفّر أيضًا إمكانية الوصول إلى مجموعة من دوال Lua الإضافية (الخاصة بـ LuaTeX) التي يمكنك استخدامها (استدعاؤها) للتواصل مع الآليات الداخلية لبرنامج التنضيد LuaTeX أو التحكم فيها مباشرةً. لقد استخدمنا عدة دوال Lua في هذه المقالة، `tex.print()`, `texio.write()`, `tex.setcount()` وهذه، إلى جانب *الكثير من* غيرها، موثّقة في [دليل LuaTeX المرجعي](http://www.pragma-ade.com/general/manuals/luatex.pdf) حيث تُسمّى مجموعات الدوال المرتبطة وفقًا لغرضها أو أفعالها *مكتبات*.

يمكنك اعتبار دوال Lua هذه بمثابة LuaTeX’s Lua API (**مشروع**التطبيق **P**البرمجة **I**الواجهة) التي توفّر الأدوات اللازمة لبناء حلول متقدمة للتنضيد وهندسة المستندات من خلال التحكم في سلوك التنضيد في LuaTeX باستخدام Lua كسائق.

كما ذُكر، ينظّم LuaTeX واجهته البرمجية في مجموعة من الدوال التي يطلق عليها مكتبات: مجموعات من الدوال المرتبطة من حيث الغرض أو الأفعال. صُممت كل مجموعة من الدوال لتوفير الوصول إلى جانب معيّن من العمليات الداخلية في LuaTeX، وهياكل البيانات، وتخزين البيانات، وخوارزميات التنضيد. داخليًا، يتكوّن LuaTeX من عدة مكوّنات: مكتبات/أدوات برمجية (مكتوبة غالبًا بلغة C) لا تشمل محرك TeX نفسه فحسب، بل أيضًا أنظمة فرعية أخرى مثل Lua وMetaPost وKpathsea وFontForge وlibpng وzlib. وتتكامل هذه المكتبات لبناء ميزات ووظائف البرنامج التنفيذي LuaTeX، ومن خلال Lua API يُمنح المستخدمون الوصول إلى وظائف LuaTeX الناتجة عن تكامل تلك المكوّنات البرمجية المتعددة وتنسيقها.

## بعض الأمثلة والمزالق

في هذا القسم نقدم بعض الأمثلة الإضافية التي تستفيد من الموضوعات والمفاهيم والشروحات المقدمة في هذه المقالة.

### استخدام الحرف \~ (التيلدا)

تستخدم لغة Lua `~` الحرف (المسمّى تيلدا) كجزء من بنيتها، بما في ذلك بنيتها لإجراء اختبار «لا يساوي»؛ على سبيل المثال، لاختبار ما إذا كان متغير `x` لا يساوي `4` يمكننا أن نكتب:

```
   local x=3
   if x ~= 4 then
   print("x is not equal to 4")
   end
```

إذا حاولنا تشغيل هذا الكود البسيط بلغة Lua عبر `\directlua`:

```
\directlua{
   local x=3
   if x ~= 4 then
   print("x is not equal to 4")
   end
}
```

سنحصل على خطأ:

`[\directlua]:1: 'then' expected near '\'.`

هذا غريب لأن كودنا صحيح: فقد استخدمنا `'then'` ولا توجد `\` رمز في كودنا، فما الذي حدث؟ لفهم ذلك، علينا أن نتذكر أنه بالنسبة إلى TeX/LaTeX، `~` يُعرَّف عادةً على أنه «حرف خاص» برمز فئة 13: ما يُسمّى بالأحرف النشطة، وهي وحدات ماكرو صغيرة وبالتالي تخضع للتوسيع. عندما `\directlua` يكتشف `~` الحرف يتم توسيعه بواسطة *إزالته* يُزال من الإدخال و *واستبداله* مع استبداله بنتيجة توسيعه. عند استخدام Plain TeX، فإن النص الناتج (الكود) الذي ينتجه LuaTeX ويمرره إلى مفسّر Lua لا يحتوي فعليًا على `~` الحرف، وهو:

`local x=3 if x \penalty \@M \ = 4 then print("x is not equal to 4") end`

ال `~` تم *يُزال* و *توسعيه* إلى أوامره المكوِّنة — وينتج كود Lua أعلاه من تعريف Plain TeX للحرف النشط `~`. الآن يمكننا أن نرى لماذا يستجيب Lua بالخطأ `'then' expected near '\'`—فهو يبدأ بتحليل هذا الكود لكنه يصادف الكلمة `\penalty` التي لا تعني شيئًا بالنسبة إلى Lua وتولّد خطأً نحويًا.

لإصلاح ذلك، يجب أن يكون الـ `~` الحرف ذا رمز فئة آمنًا في الوقت الذي `\directlua` يعالج فيه LuaTeX كودك؛ على سبيل المثال، يمكننا تغيير رمز فئة `~` مؤقتًا إلى 11 (حرف) من خلال إحاطة الكود في مجموعة:

```
\begingroup
\catcode`\~=11
\directlua{
   local x=3
   if x ~= 4 then
   print("x is not equal to 4")
   end
}
\endgroup
```

هذا الكود يعمل كما هو متوقع و `x لا يساوي 4` يتم طباعته في وحدة التحكم. توجد خيارات أخرى: يمكننا استخدام الأوامر القابلة للتوسيع `\noexpand` أو `\string`.

#### استخدام \string⟨token⟩

يمكننا تطبيق `\string` على الحرف أحادي الرمز `⟨token⟩` `~` الذي يحمل رمز الفئة 13 (حرف نشط); `\string` يحوّل `~` الحرف لإنشاء token حرفي يحمل رمز الفئة 12. إذا فعلنا

```
\directlua{
   local x=3
   if x \string~= 4 then
   print("x is not equal to 4")
   end
}
```

فسيُنتج كود Lua الذي نريده:

`local x=3 if x ~= 4 then tex.print("x is not equal to 4") end`

#### استخدام \noexpand⟨token⟩

يمكننا استخدام `\noexpand~` لمنع توسّع الحرف النشط `~`

```
\directlua{
   local x=3
   if x \noexpand~= 4 then
   print("x is not equal to 4")
   end
}
```

الرمز غير الموسَّع `~` يمر إلى قائمة الرموز التي يتم بناؤها في `\directlua` وسيُحوَّل مرةً أخرى إلى نص ينتج كود Lua صالحًا للعمل.

### استخدام الرمز \#

داخل لغة Lua يمكن استخدام `#` الرمز للعثور على طول جدول. ولكن إذا جرّبنا الكود التالي

```
\directlua{
   local tbl = {}
   tbl[1] = "Hello"
   tbl[2] = "World"
   tex.print("Table length is "..#tbl)
}
```

قد نتوقع أن يقوم LuaTeX بطباعة

`طول الجدول هو 2`

لكنه يولد خطأً:

`\directlua]:1: attempt to get length of a number value`

ينتج هذا الخطأ لأن `#` الرمز يحمل عادةً رمز الفئة 6 (معامل ماكرو) — فالـ `#` الرمز له استخدامان في TeX/LaTeX: للإشارة إلى معاملات الماكرو (`#1`, `#2`… `#9`) والنص البديل في قوالب المحاذاة (لـ `\halign` و `\valign`).

عندما `\directlua` عندما يقوم LuaTeX بتوليد الرموز لبناء قائمة الرموز الخاصة به فإنه يرى `#` الحرف ذو رمز الفئة 6 ويُنشئ رمز حرف مناسبًا لتمثيله. وعندما يحين وقت تحويل قائمة الرموز النهائية مرة أخرى إلى صيغة نصية، فإن رمز الحرف # (ذو رمز الفئة 6) يخضع لمعاملة خاصة: إذ يُخرَج على أنه *حرفان متتاليان*: `##`، مما يؤدي إلى تمرير الكود التالي إلى Lua:

`local tbl = {} tbl[1] = "Hello" tbl[2] = "World" print(##tbl)`

عند التحويل إلى كود Lua، يكون الأصل `#` قد تمّت مضاعفته وهذا يولد خطأً:

`\directlua]:1: attempt to get length of a number value`

تنشأ هذه المشكلة بسبب صياغة TeX التي تستخدم رمز الهاش المزدوج `##` لتمثيل أو توليد رمز `#` مفرد؛ وتُستخدم هذه الصياغة في الماكروات التي تُعرّف ماكروات أخرى تأخذ معاملات، أو في الماكروات المستخدمة لإنشاء قوالب `\halign` أو `\valign` لأوامر إنشاء الجداول. وهذا مربك بعض الشيء فلننظر إلى مثال.

#### مثال

لنفترض أننا نعرّف ماكرو `\mymacro` يأخذ معاملًا واحدًا، `#1`لكنه يعرّف أيضًا ماكروًا ثانيًا `\foo` يأخذ هو الآخر معاملًا واحدًا. وللتمييز بين المعامل `#1` المستخدم مع `\mymacro` والحاجة إلى تعريف `\foo` ليستخدم معاملَه الخاص `#1` تتطلب صياغة TeX أن تستخدم `##1` داخل `\mymacro` لتمثيل المعامل الذي سيُستخدم مع `\foo`:

`\def\mymacro#1{\def\foo##1{#1 Hello##1}}`

إذا كتبت `\mymacro{Hey!}` فسوف يعرّف الماكرو `\foo` على أنه

`\def\foo#1{Hey! Hello#1}`

لاحظ أن `\mymacro`معامل `#1` (`مرحبًا!`) قد أُدرج في تعريف `\foo` والتسلسل `##1` قد تحوّل إلى `#1` في تعريف `\foo`. لذا يمكننا استخدام `\foo` بهذا الشكل:

`\foo{, World!}`

أن ينضّد `مرحبًا! مرحبًا، يا عالم!`

يمكننا حل `\directlua`طريقة معالجة الـ `#` الحرف عن طريق تغيير رمز فئته مؤقتًا قبل أن يعالج LuaTeX الكود. على سبيل المثال:

```
\begingroup
   \catcode`\#=11
   \directlua{
   local tbl = {}
   tbl[1] = "Hello"
   tbl[2] = "World"
   tex.print("Table length is "..#tbl)
}
\endgroup
```

هذا يولّد شيفرة Lua

```
local tbl = {} tbl[1] = "Hello" tbl[2] = "World" tex.print("Table length is "..#tbl)
```

الأمر الذي يطبع النتيجة التي توقعناها:

`طول الجدول هو 2`

### استخدام الرمز %

داخل TeX/LaTeX، يُستخدم `%` الرمز عادةً لإدراج تعليقات أحادية السطر في الكود: للإشارة إلى محرك TeX بأنه يجب أن يتجاهل كل شيء من تلك النقطة حتى نهاية السطر الذي كُتب فيه `%` ومع ذلك، داخل لغة Lua، يُستخدم `%` الرمز ضمن بعض دوال معالجة السلاسل المفيدة جدًا، مثل `string.format(...)`, `string.gmatch(...)`، و `string.gsub(...)` التي يلعب فيها `%` الرمز دورًا مهمًا كجزء من صياغة تلك الدوال.

عند استخدامه مع TeX/LaTeX، `%` يعمل كرمز التعليق لأنه مُخصَّص له رمز الفئة 14. ولكي يتصرف كحرف عادي، ونعطّل سلوكه المعتاد في TeX/LaTeX، نحتاج إلى تغيير رمز فئته إلى قيمة آمنة مثل 12. ويستخدم المثال `\directlua` أدناه عددًا من التقنيات التي نوقشت سابقًا في المقال، مع تقنية لم نذكرها بعد: ``\catcode`\^^M=12``، مما يتيح لنا استخدام تعليقات Lua في الكود؛ وسيُناقش ذلك أدناه.

#### مثال

الأمثلة التالية مقتبسة من [lua-users.org](http://lua-users.org/wiki/StringLibraryTutorial)، مع تعديلها بما يناسب الاستخدام داخل `\directlua`.

```
\documentclass{article}
\begin{document}
\begingroup
\ttfamily
\let\\\relax
\catcode`\^^M=12 %<---we further explore this below!
\catcode`\%=12
\directlua{
   local str -- التصريح عن متغير محلي للاحتفاظ بالنتيجة

   tex.print("Using string.format():".."\\par")

   str=string.format("%s %q", "Hello", "Lua user!") -- string and quoted string
   tex.print(str.."\\par")
   str = string.format("%c%c%c", 76, 117, 97) -- char
   tex.print(str.."\\par")
   str=string.format("%e, %E", math.pi, math.pi) -- exponent
   tex.print(str.."\\par")
   str=string.format("%f", math.pi) -- float
   tex.print(str.."\\par")
   str=string.format("%g, %g", math.pi, 10^9) -- float or exponent
   tex.print(str.."\\par")
   str = string.format("%o, %x, %X", 99, 125, 125)  -- octal, hexadecimal, hexadecimal
   tex.print(str.."\\par")

   tex.print("\\vskip3mm".."Using string.gmatch():".."\\par")

   for word in string.gmatch("Hello TeX user", "%a+") do
      tex.print(word.."\\par")
   end

   tex.print("\\vskip3mm".."Using string.gsub():".."\\par")
   str=string.gsub("banana", "(an)", "%1-") -- capture any occurrences of "an" and replace
   tex.print(str.."\\par")
}
\endgroup
\end{document}
```

تُظهر لقطة الشاشة التالية النتيجة المطبوعة للكود أعلاه:

![استخدام دوال السلاسل النصية في Lua داخل \directlua](/files/372375ab78698c02cac2b54bf63bfc0e8afceb92)

## لماذا يظهر كود Lua في سطر واحد؟

كما قد تكون قد لاحظت، فإن جميع مقاطع كود Lua (المولَّدة) المعروضة في أمثلة هذا المقال تُقدَّم كسطر واحد من النص: ففواصل الأسطر الموجودة أصلًا في `\directlua` مقاطع الكود لا تُحافَظ عليها. لماذا ذلك؟ لأن فواصل الأسطر في كود Lua قد تم *إزالتها* أثناء المعالجة المسبقة في LuaTeX داخل `\directlua`، مما يجعل كود Lua يتحول إلى سطر نصي طويل واحد. ويمكن إرجاع هذا السلوك إلى الطريقة التي تتعامل بها محركات TeX مع أحرف نهاية السطر—التي يُرمز إليها بـ `\\r` (عودة العربة) و `\n` (تغذية السطر) في أدبيات البرمجة. وسيتضح لنا سبب الحاجة إلى القلق بشأن هذه التفاصيل الدقيقة عندما نناقش استخدام آليات Lua للتعليق على أجزاء من الكود.

عندما يكتب البرنامج (يحفظ) ملفًا نصيًا، تنتهي كل سطر من النص بما يُسمّى أحرف «سطر جديد» — أما أحرف السطر الجديد الفعلية فتعتمد على التطبيق ونظام التشغيل المستخدمين لكتابة ذلك الملف. لدى ويكيبيديا [مقالة مثيرة للاهتمام](https://en.wikipedia.org/wiki/Newline) تستعرض تاريخ/تطور أحرف السطر الجديد المستخدمة اليوم.

في أي ملف نصي، قد تنتهي أسطره المختلفة بتوليفات متعددة من الأحرف، تُسمّى عودة العربة (حرف ASCII/Unicode 13) و/أو تغذية السطر (حرف ASCII/Unicode 10)، والتي يُرمز إليها بـ `\\r` و `\n` على الترتيب. ولأن محركات TeX مصممة لتكون مستقلة عن المنصة، فهي تحتاج إلى طريقة لتجاوز الطبيعة المعتمدة جوهريًا على المنصة لنهايات الأسطر المستخدمة في الملفات النصية. وبالطبع، لدى محركات TeX طريقة مدمجة (ولكن قابلة للتهيئة) للتعامل مع أحرف إنهاء السطر.

### كيف تتعامل محركات TeX مع نهايات الأسطر

عندما يعالج LuaTeX `\directlua{⟨code⟩}` فهو يقرأ النص الموجود في `⟨code⟩` ويطبّق أساليب محرك TeX القياسية لمعالجة أي نهايات أسطر موجودة في `⟨code⟩`. وبشكل افتراضي، تؤدي أساليب TeX القياسية هذه إلى إزالة جميع أحرف إنهاء السطر (عوائد العربة وتغذية الأسطر) واستبدالها بأحرف مسافة. ونقول «افتراضيًا» لأن معالجة محرك TeX لأحرف إنهاء السطر يمكن تعديلها عبر معامل قابل للتهيئة من قبل المستخدم يُسمّى `\endlinechar`. هنا سنقدّم نظرة عامة قصيرة من خطوتين، لكن يمكن العثور على مزيد من التفاصيل في مقال Overleaf [مقدمة إلى \endlinechar: كيف يقرأ TeX الأسطر من الملفات النصية](/latex/ar/mqalat-mtamqh/05-an-introduction-to-endlinechar-how-tex-reads-lines-from-text-files.md).

#### الخطوة 1: يُدرج TeX حرف نهاية السطر الخاص به

بعد قراءة سطر نصي من ملف الإدخال، تقوم محركات TeX فورًا بإزالة أي `\\r` أو `\n` أحرف من نهاية ذلك السطر. بعد ذلك، تقوم محركات TeX *بإدراج* (إضافة) حرف إنهاء السطر الخاص بها إلى نهاية ذلك السطر. ويُحدَّد ذلك الحرف بقيمة معامل TeX قابل للتهيئة من قبل المستخدم يُسمّى `\endlinechar` ومن خلال هذه الآلية تستطيع محركات TeX معالجة أحرف نهاية السطر بطريقة مستقلة عن المنصة: فهي تختار حرف نهاية السطر وتضبطه بغض النظر عما كان موجودًا أصلًا في ملف النص المدخل.

عادةً ما تستخدم محركات TeX الإعداد

`\endlinechar=13`

وهو حرف عودة العربة (`\\r`). ومع ذلك، يمكن للمستخدمين دائمًا تعيين رمز حرف آخر كقيمة لـ `\endlinechar`—كما سنرى لاحقًا في هذا المقال.

وبالتالي، فإن أي أحرف لإنهاء السطر موجودة في `⟨code⟩` ليتمت معالجتها بواسطة `\directlua{⟨code⟩}` تُزال وتُستبدل بحرف واحد يحدده محرك TeX نفسه. لاحظ أن محركات TeX تنفذ معالجة نهاية السطر هذه مباشرة بعد قراءة سطر جديد من النص من ملف و *قبل أن* معالجة أي أحرف في ذلك السطر (لإنتاج الرموز). ومع ذلك، فهذه ليست نهاية القصة: ما يفعله محرك TeX *لا* بتلك أحرف نهاية السطر (التي أدرجها) يفسر سبب تحول كود Lua إلى سطر واحد.

#### الخطوة 2: يحوّل TeX حرف نهاية السطر إلى مسافة

إلى جانب إدراج حرف إنهاء السطر الخاص به، المحدد بقيمة `\endlinechar`، تستخدم محركات TeX أيضًا رمز الفئة 5 للأحرف التي ينبغي أن *تُعامَل على أنها* حرف نهاية السطر. وينتج عن ذلك أن محركات TeX تعمل عادةً مع:

1. حرف نهاية سطر محدد بواسطة `\endlinechar`;
2. ذلك الحرف نفسه *عادةً* ويُخصَّص له رمز الفئة 5.

إن ما يفعله TeX بذلك الحرف هو ما يفسر حيرتنا بشأن أسطر Lua الأحادية. عندما يعالج محرك TeX سطرًا من المدخلات فإنه سيكتشف، في النهاية، الحرف الأخير في ذلك السطر: الحرف المحدد بواسطة `\endlinechar`. وعادةً ما يكون لهذا الحرف رمز الفئة 5، مما يدفع TeX إلى *استبداله* بحرف مسافة: أي أن TeX، في نهاية الأسطر، يزيل فعليًا حرف إنهاء السطر ويستبدله بمسافة. وعلى الهامش، تستخدم محركات TeX أيضًا الأحرف ذات رمز الفئة 5 لاكتشاف الأسطر الفارغة وبدء فقرة جديدة، لكننا لن نتناول ذلك هنا.

وبالطبع، وبما أنه TeX، يمكنك تنفيذ شتى الحيل الخاصة ببرمجة الماكرو عبر إعادة ضبط `\endlinechar` إلى حرف آخر، و/أو إعطاء `\endlinechar` الحرف المعيّن لـ

قيمة رمز فئة تختارها أنت. `\endlinechar` إذا أردت منع تحول كود Lua إلى سطر نصي واحد، فيمكنك إما (مؤقتًا) تغيير القيمة المعيّنة لـ `\\r`.

### ترميز TeX الغريب ^^

في الأقسام التالية سنواجه ترميز TeX غير المعتاد `^^` ، المعروف باسم «آلية الأحرف الموسّعة». وقد صُمم هذا الترميز بواسطة Knuth لتسهيل كتابة «أحرف التحكم» مثل منتهيات السطر وعلامات الجدولة وما إلى ذلك. على سبيل المثال:

* `^^J` يمثل رمز الحرف 10 (`\n`، تغذية السطر);
* `^^M` يمثل رمز الحرف 13 (`\\r`، عودة العربة).

تسلسلات الأحرف مثل `^^M` تُحوَّل إلى رموزها الحرفية المقابلة في وقت مبكر من عملية فحص المدخلات في TeX، عندما يقرأ TeX أحرف الإدخال لتوليد الرموز الحرفية المقابلة.

### تغيير الحرف المعيّن لـ \endlinechar

مع تذكّر أننا ما زلنا بحاجة إلى منع توسع `~` الحرف، يمكننا أن نكتب

```
\begingroup
\endlinechar=10 % Change the end-of-line character to \n
\directlua{
   local x=3
   if x \noexpand~= 4 then
   print("x is not equal to 4")
   end
}% don’t want the \n appearing here
\endgroup% or a \n here
```

الإعداد أعلاه لـ `\endlinechar` يجعل LuaTeX يضيف رمز الحرف 10 (`\n`، تغذية السطر) إلى نهاية كل سطر يقرؤه. ونحن نفعل ذلك لأن `\n` (تغذية السطر) يكون عادةً له رمز الفئة 12، ويمكنك اختبار ذلك بكتابة ``\the\catcode`\^^J``. ولأن `\n` لا يحمل رمز الفئة 5، فلن يحوله LuaTeX إلى حرف مسافة، بل سيبقى في نهاية كل سطر يقرؤه LuaTeX. وينتج عن ذلك بقاء حرف ذي الرمز 10 في نهاية كل سطر، وبالتالي يمر إلى قائمة الرموز التي يبنيها `\directlua` ثم يظهر لاحقًا في كود Lua بمجرد تحويل قائمة الرموز إلى نص. ومع هذا التغيير، يُرسل كود Lua إلى مفسر Lua على هيئة التسلسل التالي من الأحرف:

**\n**local x=3\*\*\n**if x \~= 4 then**\n**print("x is not equal to 4")**\n**end**\n\*\*

حيث إن **\n** الترميز يُقصد به تمثيل رمز الحرف 10 *تحتوي* ماكرو غير معروف `\n`. الآن سيرى مفسر Lua فواصل الأسطر في الكود، تمامًا كما كُتب في الأصل في `\directlua` الأمر:

```
   local x=3
   if x ~= 4 then
   print("x is not equal to 4")
   end
```

ومن الجدير بالذكر أن أول حرف في سلسلة كود Lua هو `\n` (قبل `المحلية` الكلمة المفتاحية). `\n` ينشأ من السطر

`\directlua{`

لأن هناك فاصل سطر مباشرة بعد علامة الفتح `{` وهذا أيضًا يُحافَظ عليه. ولمنع ذلك يمكنك كتابة

`\directlua{%`

### تغيير رمز الفئة لـ \r

للحفاظ على فواصل الأسطر في كود Lua يمكننا أيضًا تغيير رمز الفئة لـ `\\r` إلى شيء غير 5، بحيث `\\r` لم يعد يُعرَف (ويُعامَل) كحرف نهاية سطر. ومع هذه التقنية يظل LuaTeX يستخدم `\endlinechar=13` وسيواصل إضافة `\\r` إلى نهاية كل سطر؛ ومع ذلك، وبما أن `\\r` لم يعد يحمل رمز الفئة 5، فلن يتعرف LuaTeX على `\\r` الحرف على أنه نهاية سطر: فلن يحوله إلى مسافة ويمرره كما هو ليظهر في كود Lua.

مع تذكّر أننا ما زلنا بحاجة إلى منع توسع `~` الحرف، يمكننا أن نكتب

```
\begingroup
\catcode`\^^M=12 % change category code of \r to 12
\directlua{
   local x=3
   if x \noexpand~= 4 then
   print("x is not equal to 4")
   end
}
\endgroup
```

في هذه الحالة يُرسل كود Lua إلى مفسر Lua على النحو التالي:

**\\\r**local x=3\*\*\r**if x \~= 4 then**\\\r**print("x is not equal to 4")**\\\r**end**\r\*\*

حيث إن `\\r` الترميز يُقصد به تمثيل رمز الحرف 13 وليس ماكروًا غير معروف `\\r`. وكما هو الحال مع `\endlinechar` المثال، سيرى مفسر Lua الآن فواصل الأسطر في الكود، تمامًا كما كُتب في الأصل في `\directlua` الأمر:

```
   local x=3
   if x ~= 4 then
   print("x is not equal to 4")
   end
```

ومن الجدير بالذكر مرة أخرى أن أول حرف في سلسلة كود Lua هو `\\r` (قبل الكلمة المفتاحية local): وهذا أيضًا ينشأ من السطر

`\directlua{`

#### لماذا استُخدم لـ \r رمز الفئة 12 وليس رمز الفئة 11؟

الجواب يعود إلى خطر إدخال أخطاء عن غير قصد تُستَفَز بواسطة `\\r` (ذو رمز الفئة 11) تُضاف إلى نهاية أوامر TeX/LaTeX المقروءة من ملف الإدخال لدينا. خذ هذا المثال:

```
\begingroup
\catcode`\^^M=11 % change category code of \r to 11
\directlua{
   local x=3
   if x \noexpand~= 4 then
   print("x is not equal to 4")
   end
}
\endgroup
```

مما يولد خطأً:

```
   ! Undefined control sequence.
   l.9 \endgroup
```

كيف يمكن أن يكون ذلك صحيحًا بينما `\endgroup` أمرًا أوليًا قياسيًا في TeX؟ سبب الخطأ دقيق جدًا: عندما قرأ LuaTeX السطر الأخير من النص — وهو الذي يحتوي على `\endgroup`— فقد أضاف أيضًا `\endlinechar` الحرف `\\r` إلى نهاية ذلك السطر. والآن، داخل ذاكرته، يرى LuaTeX تسلسل الأحرف

`\endgroup\r`

حيث نستخدم `\\r` للدلالة على الحرف ذي الرمز 13 — وليس اسم ماكرو TeX غير معروف `\\r`.

في الوقت الذي قرأ فيه LuaTeX هذا السطر من ملف النص لدينا، كان الـ `\begingroup` الأصلي لا يزال ساريًا: فنحن داخل مجموعة لم تُغلق بعد بتنفيذ الأمر المطابق `\endgroup` —والذي كان سيؤدي إلى `\\r` الرجوع إلى قيمة رمز الفئة السابقة وهي 5.

عندما يبدأ LuaTeX في معالجة (إنشاء الرموز) من سطر النص `\endgroup\r` يتعرف على الحرف الأول `\` على أنه حرف الهروب الذي يدفع LuaTeX إلى البدء في البحث عن اسم أمر. ولتحديد اسم الأمر يبحث LuaTeX عن تسلسل من الأحرف ذات رمز الفئة 11، ولكن لأن `\\r` يحمل أيضًا رمز الفئة 11، يعتقد LuaTeX أن `\\r` الحرف (الذي ما يزال يحمل رمز الفئة 11) يشكّل *جزءًا من أمر* يُسمّى `\endgroup\r` والذي، بالطبع، غير موجود، لذا يبلّغ LuaTeX عن `تسلسل تحكّم غير معرّف` خطأ. ولهذا استخدمنا رمز الفئة 12 وليس 11.

لأن رسالة خطأ LuaTeX كُتبت إلى وحدة التحكم، لم نتمكن بسهولة من رؤية/ملاحظة `\\r` الحرف، لذلك لم يكن واضحًا ما الذي تسبب في الخطأ.

### لماذا نهتم بنهايات الأسطر؟

السبب هو تمكين استخدام طريقة التعليق في Lua داخل كودك! يمكنك استخدام آلية LuaTeX القياسية بإضافة `%` أحرف للتعليق على أسطر مفردة داخل كودك؛ لكن لغة Lua لديها آلياتها الخاصة المفيدة جدًا *متعددة الأسطر* للترجمة/التعليق التي قد ترغب في الاستفادة منها.

لنبدأ برؤية ما يحدث إذا حاولنا استخدام تعليقات Lua أحادية السطر دون معالجة مشاكل فواصل الأسطر. بينما يستخدم TeX الرمز `%` للتعليق على أسطر منفردة من الكود، تستخدم Lua شرطتين متتاليتين: `--`.

ماذا يحدث إذا حاولنا تشغيل هذا:

```
\directlua{
   local x=3
   if x \noexpand~= 4 then
   -- I'm going to output the result of this complex test
   print("x is not equal to 4")
   end
}
```

سنحصل على خطأ:

`[\directlua]:1: 'end' expected near <eof>`

ينتج هذا الخطأ عن غياب فواصل الأسطر في كود Lua الممرَّر إلى المفسر، الذي لا يرى سوى سلسلة واحدة متصلة يبدأ فيها التعليق في منتصف تلك السلسلة:

```

local x=3 if x ~= 4 then -- I'm going to output the result of this complex test print("x is not equal to 4") end
```

كل ما بعد `**local x=3 if x ~= 4 then**` يُعامَل على أنه مُعلَّق عليه، مما يجعل المفسر يرى جزءًا غير مكتمل من كود Lua، فينتج عن ذلك الخطأ

`'end' expected near <eof>`.

حيث `<eof>` يعني نهاية الملف.

كما قد تكون قد خمنت، علينا معالجة ذلك بضمان تمرير فواصل الأسطر إلى كود Lua الناتج، وهو ما يمكننا تحقيقه، على سبيل المثال، بتغيير رمز الفئة لـ `\\r` إلى 12:

```
\begingroup
\catcode`\^^M=12 % change category code of \r to 12
\directlua{
   local x=3
   if x \noexpand~= 4 then
   -- سأقوم بإخراج نتيجة هذا الاختبار المعقد
   print("x is not equal to 4")
   end
}
\endgroup
```

الآن، يرى مفسّر Lua سلسلة نصية لكنها تحتوي على `\\r` فواصل أسطر كما كُتبت في `\directlua` المقتطف:

**\\\r**local x=3\*\*\r**if x \~= 4 then**\r\*\*-- سأقوم بإخراج نتيجة هذا الاختبار المعقد\*\*\r**tex.print("x is not equal to 4")**\\\r**end**\r\*\*

هذا، في الواقع، يعادل كتابة

```
   local x=3
   if x \noexpand~= 4 then
   -- سأقوم بإخراج نتيجة هذا الاختبار المعقد
   print("x is not equal to 4")
   end
```

مما يعني أن Lua قادرة على معالجة هذا الرمز البرمجي بشكل صحيح وتجاهل السطر الذي علّقناه.

**التعليقات الكتلية**

تدعم لغة Lua أيضًا صيغة تُسمّيها [«تعليقًا كتليًا»](https://www.lua.org/pil/1.3.html) (أو *تعليق طويل*): تبدأ بـ `--[[` وتظل سارية حتى `]]`. يمكننا استخدام هذه الصيغة الملائمة لكتابة تعليقات متعددة الأسطر، أو لتعليق أجزاء من الشفرة التي نريد إزالتها مؤقتًا:

```
\begingroup
\catcode`\^^M=12 % change category code of \r to 12
\directlua{
   local x=3
   if x \noexpand~= 4 then
   --[[ سأقوم بإخراج نتيجة هذا الاختبار المعقد
   فقط لأنه حقًا
   استنتاج مذهل إلى هذا الحد]]
   print("x is not equal to 4")
   end
}
\endgroup
```

## في الختام

أولًا، تهانينا إذا تمكنت من قراءة هذا المقال المطوّل حتى نهايته! لقد حاولنا إعداد دليل شامل إلى حد معقول حول المفاهيم والموضوعات المتعلقة بـ TeX، والتي توفر الخلفية اللازمة للاستفادة القصوى من LuaTeX عبر `\directlua` الأمر. ونأمل أن نكون قد قدّمنا مقالًا تعليميًا يضيف شيئًا ذا فائدة وقيمة إلى مجتمع مستخدمي Overleaf، وما وراءه. وكما هو دائمًا، يسعدنا تلقي الملاحظات، لذا لا تترددوا في [الاتصال بنا](https://www.overleaf.com/contact) مشاركتنا تعليقاتكم على هذا المقال أو اقتراحاتكم لمواضيع أخرى تودون أن نكتب عنها.

بـ $$\text{Lua}\mathrm{\TeX}\text{-ing!}$$ من Graham Douglas وفريق Overleaf.

### وأخيرًا... ما عليك سوى استخدام حزمة luacode

على الرغم من أن TeX وLua يعملان بطرق مختلفة جذريًا، فإن هاتين اللغتين تتشاركان عددًا من الأحرف التي لها «معانٍ خاصة» ضمن سياق كل لغة—مثل \\\\, %, \~, #, ^, &—وبالطبع، تمنح Lua وTeX تلك المعاني الخاصة لأغراض *جدًا* مختلفة. إن استكشافنا للأحرف الإشكالية يبيّن لماذا قد تنشأ الصعوبات وكيف يمكنك حلها؛ ومع ذلك، قد يكون من المملّ إلى حد ما إصلاح العديد من مقتطفات Lua الصغيرة يدويًا، لذا يفضّل معظم المستخدمين استخدام حزم LaTeX التي تزيل تلك التحديات. ومن هذه الحزم [`luacode`](https://ctan.org/pkg/luacode?lang=en) التي توفّر مجموعة من الميزات المصممة لتسهيل العمل مع `\directlua`، ولكن على الأقل قد يكون لديك الآن فهم أفضل للمشكلات `luacode` التي يحلّها لك.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://overleaf-pro.ayaka.space/latex/ar/mqalat-mtamqh/09-an-introduction-to-luatex-part-2-understanding-directlua.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
