فــُـصّـِــلـــَــــت

الفصائل "الفاسقة"

كثيرا ما يتساءل البعض حول الجدوى من تطبيق الكَبْسَلة في نمط البرمجة الكائنية، فيقول لماذا عليَّ أن أنشئ دوالَ أخذٍ وردٍ لكل خاصية عوض أن أجعل الخاصية ظاهرةً بحيث يمكن الوصول إليها مباشرة؟ وجوابا على هذا التساؤل البريء أقدّم هذه التدوينة المجرمة.

مصطلحات التدوينة


  • الكبسلة: هي عملية إخفاء الخصائص لمنع الوصول المباشر إليها.
  • دوال الأخذ والرد: هي الدوال التي يمكن عبرها الوصول إلى الخصائص المخفية.
  • فصيلة: وحدة تحوي خصائص ودوال، يمكن إنشاء كائنات متعددة من نفس الفصيلة.

الفصائل الفاسقة


الفصائل الفاسقة هي التي لا تستخدم الكبسلة، وهي فاسقة لأنها:

  • تسمح لك بالوصول إلى خصوصيتها دون صعوبة.
  • لا تهتم لما أنت تنوي فعله.
  • قد تبدو كاستجابة سريعة لرغباتك.
  • يمكن استعمالها بطرق عديدة، بمعطيات مختلفة، لكن لا واحدة منها مضمونة.
  • لا يمكن الوثوق بها.
  • يجب اختبارها جيدا.
  • تُحدِثُ فوضى عارمة على المدى البعيد.

وعليه فاحرص أيها المبرمج على ستر عورة الفصائل التي تصنع لكي لا نقول عنها "فاسقة".

توضيح


لمن لا يعرف كيف يُطَبِّق الكبسلة في نجم، فإنها كما يلي:

فصيلة إنسان

    ظاهر:
        نص اسم   'هذه خاصية غير مكبسلة'

    خفي:
        نص نسب   'هذه خاصية مكبسلة، ويمكن الوصول إليها من خلال دوال الأخذ والرد أدناه'

    ظاهر:
        خذ_نسب(نص ن)
            نسب = ن
        تم

        رد_نسب() يرد نص
            رد نسب
        تم
تم
أكمل القراءة

إستدعاء مكتبات خارجية من داخل نجم

المستوى: مبتدئ

يصعب على أية لغة برمجةٍ أن تعيش إذا لم تدعم النداءات الخارجية، وإمكانية استعمالها لأكواد مكتوبة بغيرها، ذلك لأنه إن لم تفعل، يجب حينها أن تعاد كتابة كلّ الخدمات والإمكانيات المتاحة حتّى الآن، وتوفير مكتبات لكلّ شيء بهذه اللغة، والأكثر من هذا، وجب الحرص على إعادة كتابة أية تقنية جديدة فور ظهورها، ذلك حتى تدعمها هذه اللغة، وإلا فإنّها تعجز وتشيخ وتصير مُتجاوَزة. وعليه فقد تمّ دعم النداءات الخارجية في لغة نجم وذلك حتى تحافظ لنا على شبابها وحيويتها ومواكبتها للتطورات، فنستمتع بها لوقت أطول.

المكتبات الخارجية


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

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

المكتبات الساكنة ضد المكتبات الحركية


المكتبة الساكنة هي ملفات يتمّ دمجها وقت الترجمة مع البرنامج الذي يستدعيها لتكوّن بذلك ملفا تنفيذيا واحدًا يضم كلّ شيء، فتصير بذلك كما لو أنّه تمّت كتابتها مع البرنامج، أما المكتبات الحركية فهي ملفات يتم تحميلها وقت التنفيذ إلى حيّز مشترك من الذاكرة، فيمكن بذلك الوصول إليها من خلال أيّ برنامج يحتاجها. تُعرَف المكتبات الساكنة في نظام الويندوز بالامتداد (.lib)، وتُعرَف في نظام اللينكس بالامتداد (.a)، فيما تُعرف المكتبات الحركية في نظام الويندوز بالامتداد (.dll)، وتُعرف في نظام اللينكس بالامتداد (.so).

تتميّز المكتبات الساكنة بأنّها أكثر سرعة وثباتًا، كما أنّها لا تطرح مشاكل تعدد النسخ والتوافق، فيما تتميّز المكتبات الحركية بأنّها لا تزيد في حجم التنفيذ لأنّها لا تندمج معه، إضافة إلى أنّه يمكن مشاركتها بين جميع البرامج التي تحتاجها فقط بوضعها في مكان واحد، في حين أنّ المكتبة الساكنة تحتاج إلى أن يتمّ نسخها داخل كلّ برنامج يحتاجها.

فيما يخصّ نجم


يدعم نجم الآن المكتبات الخارجية الساكنة، والمكتوبة بأية لغة برمجة مُترجَمة، مثل سي وسي++ وغيرها، ذلك بخلاف لغات البرمجة المُفسّرة التي تستهدف آلات وهمية، مثل الجافا، لأنها تُنتِج مكتبات تتطلب توفّر آلتها الوهمية لتنفيذها، مما يُصعّب عملية دعمها.

إذا كانت في حوزتك مكتبة ساكنة (ملفات ".a" أو ملفات ".lib") تريد استخدامها من داخل نجم، فعليك بما يلي:
  1. نسخ الملفات داخل مشروعك.
  2. إضمام هذه الملفات داخل ملفك المصدر.
  3. استدعاء الدوال وكأنها جزء من مصدرك.

مثال حي


ولتوضيح الأمور، لنفترض أنّه لديك مكتبة بلغة سي والتي هي مجموعة من الملفات ".a"، وأنت تريد استخدام الملفّين "libio.a" و "libc.a". فإنّك تقوم أوّلا بنسخهما داخل مشروعك مثلا في "مشروعي\مكتبة\سي\"، ثم تقوم بإضمامهما كما يلي:

ربط "مشروعي\مكتبة\سي\libio.a"
ربط "مشروعي\مكتبة\سي\libc.a"
'... أكواد نجم'
لاحظ أنّه لإضمام مصادر مكتوبة بنجم فنحن نستخدم المفرد ضم، لكن لإضمام مكتبات خارجية فنحن نستخدم المفرد ربط.

إذا أردت مثلا إستدعاء الدالة "sum(int x, int y)" فإنّك تكتب ما يلي:

عدد ع1 = 4
عدد ع2 = 5
استدع "sum"، ع1، ع2   'استدعاء الدالة الخارجية: جمع العددين ع1 و ع2'
عدد ناتج = {عدد}.       'يُمثل المفرد . قيمة آخر عملية تمّت في نجم، وفي هذا السياق هي ردّ الدالة الخارجية'
هذا بشرط أن تكون الدالة sum موجودة فعلا في أحد الملفّين اللذين ثم ربطهما. وفي حال أعطيتَ الدالة الخارجية معطيات أقل مما تتطلّب، فإنّ نجم يعطيها قيمة إفتراضية 0، أما إذا أعطيتها أكثر مما تتطلب، فإن نجم يتجاهل المعطيات الزائدة ببساطة.

تَسهُل ملاحظة الفرق بين دوال نجم، والدوال الخارجية، حيث أنّه لمناداة دالة نجم فنحن نستخدم الصيغة الإعتيادية: اسم_دالة(معطى1، معطى2)، أما لاستدعاء دالة خارجية فنحن نستخدم المفرد استدع.

يمكن إذاً للمبرمج كتابة برنامجه باستخدام مكتباته المفضّلة، ومع ذلك تجب الإشارة إلى أنّ نجم يمتلك مكتبة أساسية في تطوّر جد سريع، ونحن نحاول من خلالها توفير قدر الإمكان من الخدمات التي قد يحتاجها المبرمج، ولذلك فإنّ استخدام المكتبات الخارجية هو فقط خيار وليس اضطرار.
أكمل القراءة

نجم العنكبوتية: رؤيةٌ نحو إدخال نجم للويب

بما أنّ العالم يتجّه الآن لكل ما هو شبكي، بحيث صار النّاس يرفضون تثبيت البرامج على حواسيبهم، وصاروا يفضّلون الاشتغال على الشابِكة، فقد راودتني فكرة الترجمة العنكبوتية، وما أدراك ما الترجمة العنكبوتية؟ هي ببساطة جعل المترجم يعمل عن بعد، بحيث أنّه يمكن كتابة البرامج وترجمتها عبر الشبكة، فإذا فعلنا ذلك، فإننا نستطيع حينها دفع الأمور إلى مستوى أكبر وبناء خادم تطبيقات عبر الشابِكة، وبعبارات أوضح: إمكانية بناء مواقع بلغة نجم. فكيف ذلك؟

مصطلحات التدوينة


  • الشابِكة: كلمة عربية مرادفة لما هو معروف ب "الأنترنت".

الترجمة العنكبوتية


قد راودتني هذه الفكرة وأحببت أن أكتب عنها في مدونتي هذه، ذلك لأن الكتابة تساعدني على تنظيم أفكاري وتوضيحها لنفسي أوّلا ثم لكم أيّها السادة، وهذه الفكرة هي جعل مترجم نجم يعمل عبر الشابكة، إذ أنّك تُرسل إليه الأكواد المصدرية، فيقوم هو من جهته بترجمتها ثم يرسل إليك النتيجة، وهذا سيحتاج إلى برنامج خادم يتنصّت على منفذ معيّن، فيستقبل الرسائل ثم يحلّلها ليستخرج منها الأكواد، فيقوم بعد ذلك بتشغيل مترجم نجم كإجراء فرعي وإعطائه الأكواد المنشود ترجمتها، بعد أن ينتهي إجراء الترجمة من العمل، يقوم البرنامج الخادم بتشغيل الملف التنفيذي الناتج كإجراء فرعي وإرسال مخرجاته إلى الزبون. والزبون هو المتصفّح، بمعنى أنّ هذه العملية قد تنجح بالنسبة للبرامج النصّية، أمّا البرامج الرسومية فلا سبيل لإرسالها عبر الشابكة ذلك لأن الزبون (المتصفّح) لا يعرف كيف يعرضها، وهذا الإشكال يحيلنا إلى التفكير في ما يلي: كيف لنا يا تُرى أن نجعل المتصفّح الزبون يعرض البرامج الرسومية؟ الجواب في الفقرة الموالية.

بناء مواقع بنجم


إذا نجحنا في تحقيق الفكرة أعلاه، نكون حينها قادرين على تنفيذ برامج نصّية عبر الشابِكة، أمّا في ما يتعلّق بالبرامج الرسومية، فنحن كلّنا نعرف أن المتصفّحات المتداولة لا تعرف التعامل مع النوافذ والأزرار والمحرّرات التي يتعامل معها نظام التشغيل عادة، إذ أنّ كلّ ما يفهمه محرّكها الداخلي هو ما نكتبه بلغة الهتمل والجافاسكربت، لدى فإذا استطعنا كتابة برنامجٍ خادمٍ يستطيع إرسال أكواد هتمل ردّا على أكواد نجم التي نرسلها، فإنّنا حينها نكون قادرين على إنشاء مواقع تفاعلية حركية على الشابِكة. نسمي البرنامج الذي يتنصّت على المنفذ ويستقبل الأكواد بخادم الشبكة، ونسمّي البرنامج الذي يُخَرِّج أكواد الهتمل بخادم التطبيقات.

هل يفعلها نجم؟


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

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


أكمل القراءة

مُحاكات مُحِثّ الويندوز -ج2-

المستوى: متقدم

هذا الجزء الثاني من مشروع محاكات مُحِث الويندوز، إذا لم تقرأ الجزء الأوّل فعليك بذلك قبل المباشرة في هذا الجزء، ذلك لأن الجزء الأوّل يتضمّن تقديمات وتوضيحات حول المشروع والمصطلحات المستخدمة وطريقة العمل التي سنسير عليها.

محاكات محث الويندوز -ج1-

تذكير


قمنا بتقسيم المشروع إلى أربع دورات، أنجزنا اثنتان في الجزء الأوّل من التدوين، وفي هذا الجزء سنحاول إتمام المشروع بإنجاز الدورتان الباقيتان. نبدأ إذًا من حيث انتهينا.. الدورة الثالثة.

الدورة الثالثة


هدفنا في هذه الدورة جعل ملف الخرج مؤقتا، وذلك لاستخدامه كصَوْنٍ للخرج، إذ تضع فيه البرامج مخرجاتها ليقرأها المحث، ثم نتخلّص منه بعد ذلك، ونعرض محتواه على شاشة المحث.

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

في الوصول المتزامن، عندما نستدعي دالة الكتابة في ملف، فإنّ دالة الكتابة لا تعود إلا بعد الإنتهاء من الكتابة، بمعنى أنّ سيل التنفيذ يتوقّف في انتظار أن تتم عملية الكتابة أو أن تحبط أو تفشل، والأمر سيان بالنسبة لدالة القراءة، إذ يتوقف التنفيذ انتظارًا لتمام عملية القراءة أو أن تحبط أو تفشل. لذلك فإنّه لا يمكن القراءة والكتابة في ملفّ معيّن في نفس الوقت وهذا ما نحتاجه في مشروعنا، قراءة ما تمت كتابته في الحين، فكيف السبيل إلى ذلك؟

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

أمر_آخر(نص اسم، نص معطيات)
    نص اسم_كامل = رد_اسم_كامل(اسم)
    لو ليس نظام_ملفات.هو_موجود(اسم_كامل)
        ملف_خرج.زد_نص("ما أدخلته ليس أمرًا صالحًا للتنفيذ")
    أما لو نظام_ملفات.هو_مجلد(اسم_كامل)
        ملف_خرج.زد_نص("قد أدخلت عنوان مجلد، لا يمكن تنفيذه")
    وإلا
        إج_حالي = إجراء جديد(اسم_كامل، {عدد}عدم، ملف_خرج.رد_مقبض()، ملف_خرج.رد_مقبض())
        إج_حالي.نفذ(معطيات)
        طالما إج_حالي.هو_حي()
            لو ملف_خرج.رد_حجم() > 0
                عرض_ملف()
            تم
        تم
    تم
تم
قبل تنفيذ الأمر فنحن نقوم ببعض الفحوصات للتأكد من أنّ العنوان المطلوب تنفيذه هو عنوان موجود، وبأنّه فعلا ملف لا مجلد، نقوم بعد ذلك بإنشاء الإجراء وإعطائه مقابض الدخل والخرج الأساسية، ثم تنفيذه، وطالما هو قيد التنفيذ، فنحن نقيس حجمه، فإذا صار أكبر من صفر، فحينها نعلم أنه قد تمت كتابة شيء في الملف، نقوم عندئذ بالقراءة لكن على الشكل التالي:

عرض_ملف()
    ملف_خرج.زحف(ملف.من_أوله، 0)
    نص ن = ملف_خرج.إقرأ_الكل()
    ملف_خرج.بتر(0)
    جدول<نص> جص = ن.شق(حرف.رس)
    لأجل عدد ع=0، ع<جص.طول، ع++
        لو ع = 0
            سطور.خذ(سطور.طول-1، سطر_حالي() + جص.صف(ع))
        وإلا
            سطر_جديد(جص.صف(ع))
        تم
        ن_محث.حدّث()
    تم
تم
بدأنا بالزحف إلى بداية الملف، ذلك لأنه بعد الكتابة فإن مؤشر الملف يكون عند نهايته، فإذا قرأنا من النهاية فنحن لن نجد ما نقرأه وستعود دالة القراءة دون نتيجة، نقرأ بعد ذلك كلّ محتوى الملف، ثم نقوم بتصفير حجمه، أي أننا نقوم بمحو محتواه وإرجاع حجمه إلى صفر، وهذا أمر مهم لأنه إن لم نفعل فنحن دائما سنجد حجم الملف أكبر من صفر وسنكرر قراءة ما تمّت قراءته سابقا. نقسّم بعد ذلك النصّ الذي قرأناه حسب السطور ليسهل تخزينه في صون الخرج وعرضه على الشاشة.

بهذا نكون قد انتهينا من الدورة الثالثة، أعلم أنّ الشرح كان سريعا ومقتضبا لكن هذا ضروري لكي لا أجعل الموضوع طويلا ومملا، إذا كانت لديكم أسئلة فالأجوبة تجدونها إمّا في المصدر الكامل للمشروع وهو في المرفقات أسفله، أو عبر طرحها في التعليقات.

نُكمِلُ قريبًا مع الدورة الرابعة.

أكمل القراءة

مُحاكات مُحِثّ الويندوز -ج1-

المستوى: متقدم

نظرا لعدم توفّر مُحثٍ (سطر أوامر) يدعم الحروف العربية بفعالية، فقد أدركتني الحاجة إلى تصميم محث عربي من عدم، وعليه قمت بتصميم وإنجاز برنامج يحاكي المحث القياسي الموجود في الويندوز، يدعم افتراضيا المحارف العربية، ويدعم نسخًا عربية لعدد من الأوامر المتداولة.

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

ما هو المُحِث؟


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

الحاجة إلى مُحِث


لقد حاولتُ مرارًا إعداد محث الويندوز لكي يدعم النصوص العربية بكفاءة، وقد نجحت في ذلك لكن إلى حد غير مُرضي، ذلك لأن مُحِثّ الويندوز لم يُصمّم ليدعم الحروف الغير الإنجليزية، ولقد بحتث مطوّلا على الأنترنت ووجدت محاكيات، بعضها أفضل من بعض في التعامل مع العربية، إلّا أنّها تبقى كلّها قاصرة خصوصا عند تمرير المعطيات إلى البرامج، والتعرف على النصوص المُدخلة.

لهذا فقد قرّرت إنشاء محاكي محثٍ يدعم العربية، ذلك لأنّني أحتاج التعامل مع النصوص العربية بكثرة من خلال البرامج التي أشغّلها، تمّ تطوير المحاكي بلغة البرمجة نجم، وهو يشتغل على نظام الويندوز ويعمل تماما مثل محث الويندوز، إذ تُدخل سطر الأمر (اسم برنامج ومعطياته) فينفّذه ويعرض لك ردّه.

قاموس مصطلحات هذا التدوين


  • صَوْن: حيّز ذاكرة مؤقت يستخدم غالبا لنقل البيانات من مكان إلى مكان، أو لتخزين البيانات بشكل مؤقت.
  • طابور: مجموعة من العناصر مرتبة واحدا تلو الآخر.
  • مِحرَف: حرف عربي أو عجمي، أو رقم، أو علامة ترقيم، أو مسافة أو فراغ أو عودة إلى السطر...
  • إجراء: برنامج قيد التنفيذ.
  • خيط: سلسلة تعليمات يتم تنفيذها، يتكون الإجراء من خيط واحد أو أكثر، ويمكن تنفيذ عدّة خيوط بالتزامن.
  • مقبض: هو رقم يستخدمه نظام التشغيل لتعريف كائن معيّن كملف أو إجراء أو خيط..
  • حجب دالة: إعادة كتابة دالة موجودة في الفصيلة الموروثة.

خلفيات نظرية


ما سنقوم به أساسا هو إنشاء برنامج رسومي يعمل كالمحث، يتكوّن من نافذة فقط، ومن خلال الأكواد سنجعل النافذة تعمل تماما كالمحث، التحدي يكمن في تشغيل الأوامر (البرامج) وعرض ردودها في وقت حقيقي. سنبدأ أوّلا بالتعرّف على كيفية عمل المحث والفكرة المحورية التي سنعتمدها لبلوغ هدفنا.

يتكون المحث أساسا من صونٍ للدخْل وصونٍ واحد أو أكثر للخرْج. يحوي صون الدخل طابورًا من تسجيلات الدخل، كلٌ منها يضم معلومات حول حدث دخْل معيّن، منها أحداث لوحة المفاتيح كالضغط على مفتاح وتركه، وأحداث الفأرة كتحريكها والضغط على زرٍ وتركه. أمّا صونُ الخرج ويسمى أيضا صون الشاشة، هو عبارة عن جدول ذو بُعدين من المحارفِ وألوانِها ليتم عرضها على شاشة المحث. يمكن لأي عدد من الإجراءات مشاركة محث واحد.

سنقوم بإنشاء نافذة، سنحاول جعل هذه النافذة هي الدّخل والخرج الأساسي للبرنامج، وذلك عبر إنشاء ملفّين مؤقتين على الذاكرة وجعل مقبضَيهما مقبضَي الدخل والخرج الأساسيَين، وعند تنفيذ أمر معيّن، يقوم البرنامج بإنشاء إجراء جديد وتوريثه مقابض الدّخل والخرج الأساسية، وهكذا سيقوم هذا الإجراء الفرعي بكتابة مُخرَجاته على ملف الخرج المؤقت.. يقوم برنامجنا بقراءة محتوى الملف المؤقت وعرضه على النافذة.

خطة العمل


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

الدورة الأولى: إنشاء نافذة وجعلها تستجيب لأحداث لوحة المفاتيح، بحيث أنها تعرض الحروف التي نكتبها، وتعطينا سطرا جديدا عند الضغط على مفتاح الإدخال.

الدورة الثانية: بالضغط على إدخال، يتم تنفيذ الأوامر التي تمت كتابتها على النافذة وتوجيه مخرجاتها إلى ملف على القرص الصلب والتأكد من سلامة ترميزها.

الدورة الثالثة: جعل الملف مؤقتا وقراءة محتواه وعرضه على الشاشة في كل مرة يتم تنفيذ أمر معين، مما يعطي إنطباع الوقت الحقيقي.

الدورة الرابعة: دعم الدّخل بحيث تستطيع البرامج التفاعل مع ما يكتبه المستخدم، وذلك عن طريق الملف المؤقت الثاني الخاص بالدّخل، وسوف يعمل بنفس طريقة ملفّ الخرج الذي سبق أن صنعناه في الدورات السابقة.

الدورة الأولى


هدفنا في هذه الدورة إنشاء نافذة وتلوينها بالأسود، وجعلها تستجيب لأحداث لوحة المفاتيح حيث تعرض الحروف التي نكتبها باللون الأبيض، كما تستجيب لمفتاح الإدخال بأن تعطي سطرًا جديدا. يبدأ كلّ سطر جديد بكلمة: "محث>"، ويقوم المستخدم بكتابة الأوامر أمامها، ويجب الحرص على عدم مسحها إذا ضغط المستخدم على مفتاح المسح. سوف نعتمد الخط الكوفي كخط افتراضي للمحث، يمكن البحث عنه على الشابكة أو تنزيله من الرابط الملحق أسفل التدوين.

نبدأ بإنشاء الفصيلة الأساسية التي تضم عناصر المشروع، نسمّيها نافذة_محث:

فصيلة نافذة_محث يرث مراقب_أحداث

    خفي:
        نافذة ن_محث            'النافذة الرئيسية'
        خط خط_كوفي           'سيتم تحميل الخط من ملف على القرص'
        أحخز خلفية              'لون خلفية النافذة بمعيار أحمر أخضر أزرق'
        أحخز لون_نص          'لون الخط على النافذة'
        رقعة رقع                 'رقعة الرسم'
        جدول<نص> سطور    'صون الشاشة'
        عدد أقصى    'عدد السطور الأقصى التي يمكن عرضها على الشاشة'

    ظاهر:
        جديد()
            ن_محث = نافذة جديد("المحث"، 800، 600، 0، 0)  'إنشاء نافذة عنوانها المحث'<
            ن_محث.خذ_مراقب_أحداث({مراقب_أحداث}هذا)    'جعل هذا الكائن مراقب أحداث النافذة'
            خط_كوفي = خط جديد({متفاعل}ن_محث، "مسار ملف الخط"، "كوفي عربي"، 14)
            عدد ارتفاع_شاشة = 600 - (خط_كوفي.ارتفاع_حرف×2) 'الارتفاع الأقصى لعرض الحروف'
            أقصى = ارتفاع_شاشة ÷ خط_كوفي.ارتفاع_حرف   'عدد السطور الأقصى'
            خلفية = أحخز جديد(0، 0، 0)    'خلفية سوداء'
            لون_نص = أحخز جديد(255، 255، 255)    'نص أبيض'
        تم

        عرض()
            ن_محث.عرض()   'عرض النافذة. تذكير: هذه الدالة لا تعود إلا بعد إغلاق النافذة'
        تم
تم
تقوم الأكواد أعلاه بإنشاء وتمهيد الكائنات التي سنتعامل معها، لاحظ أن الفصيلة نافذة_محث ترث الفصيلة مراقب_أحداث، وهي فصيلة مجرّدة تُعرّف الدوال الأساسية لدعم مختلف الأحداث. قمنا أيضا بتحميل الخط الكوفي من ملف خطوط، يجب تعويض "مسار ملف خط" بعنوان الملف على الحاسوب، وكذلك تعويض النص "كوفي عربي" بإسم الخط في ملف الخطوط، وغالبا ما يكون الإسم بحروف لاتينية.

بعد أن صارت كائناتنا جاهزة، ننتقل الآن إلى دعم الأحداث عن طريق حجب الدوال المناسبة في مراقب_الأحداث، دعنا نبدأ بدعم أحداث لوحة المفاتيح.

'حجب عند_ضغط_مفتاح من مراقب_أحداث'
عند_ضغط_مفتاح(متفاعل من، حرف ح)
    لو ح = حرف.بس         'ضغط على إدخال. بس = بداية سطر'
        لو سطور.طول = أقصى  'وصلنا إلى أسفل الصفحة'
            سطور.حذف(0)    'حذف أوّل سطر، لن يتم عرضه على الشاشة'
        تم
        سطور.زد("محث>")    'إضافة صفّ جديد إلى جدول السطور'
        {نافذة}من.حدّث()        'إعادة رسم النافذة لعرض التغييرات'
    أما لو ح = حرف.مسح    'ضغط على مسح'
        لو سطر_حالي().طول > 4   'لا تمسح الكلمة محث>'
            سطر_حالي().طول--
            {نافذة}من.حدّث()
        تم
    وإلا   'ضغط على حرف'
        سطور,خذ(سطور.طول-1، سطر_حالي()+ح)   'زد الحرف في السطر الأخير'
        {نافذة}من.حدّث()
    تم
تم
'رد آخر سطر في جدول السطور المعروضة على المحث'
سطر_حالي() يرد نص
    رد سطور.صف(سطور.طول-1)
تم
إلى الآن، نحن نترقب أحداث لوحة المفاتيح ونخزّن الحروف المضغوطة في صون الشاشة الذي هو عبارة عن جدول نصوص سمّيناه سطور، كلّ سطر في الجدول يمثّل سطرأ على الشاشة، وكلّ سطر يحوي نصّا، عند الضغط على حرف معيّن، فنحن نضيفه إلى النصّ الموجود في السطر الأخير.

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

تبقى الآن عملية الرسم على الشاشة، وذلك لعرض الحروف التي نخزّنها، وكذلك تلوين الشاشة بالأسود وهو لون المحث الاعتيادي، من أجل ذلك نقوم بحجب الدالة عند_رسم، وهو الحدث الذي يمثّل إعادة رسم الشاشة، وهو يطرأ في كلّ مرّة يتم فيها تحريك الشاشة أو تحجيمها... وكذلك في كلّ مرّة نستدعي الدالة حدّث، وهذا ما كنّا نفعله بعد كلّ عملية إضافة حرف جديد أو مسحه (سطور 5، 9، 13 أعلاه). وللتوضيح، لاحظ المعطى "من"، وهو كائن من فصيلة متفاعل، ذلك لأن كلّ كائن رسومي هو متفاعل (يرث من الفصيلة متفاعل)، يعبّر المعطى "من" عن الكائن الذي وقع عليه الحدث (ضغط_مفتاح في هذه الحالة)، وبما أنّنا نملك كائنًا وحيدًا هو نافذتنا الأساسية، فنحن نعرف أن "من" هو هذه النافذة، لدى فنحن نحوّله إلى نافذة دون أيّ تخوّف ونستدعي دالة التحديث التي تسبب حدث عند_رسم وبالتالي إعادة رسم النافذة لعرض التغييرات.

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

'حجب عند_رسم من مراقب_أحداث'
عند_رسم(نافذة من)
    لو سطور = عدم  'أوّل مرّة يتم فيها الرسم'
        سطور = ["محث>"]   'جعل كلمة محث> في بداية السطر'
    تم
    رقع = ن_محث.رد_رقعة()   'رد رقعة النافذة للرسم عليها'
    رقع.بدء_رسم()   'تمهيد الرقعة للرسم'
    رقع.ملء(خلفية)   'تلوين خلفية الرقعة (النافذة) بالأسود'
    رقع.خذ_خلف_نص(خلفية)   'تلوين خلفية النصوص بالأسود'
    رقع.خذ_لون_نص(لون_نص)    'تلوين النصوص بالأبيض'
    رقع.خذ_خط(خط_كوفي)    'إعتماد الخط الكوفي الذي تمّ تحميله في دالة التكوين'
    'عرض السطور'
    لأجل عدد ع=0، ع<سطور.طول، ع++
        رقع.عرض_نص(سطور.صف(ع)، 0، ع×خط_كوفي.ارتفاع_حرف)
    تم
    رقع.ختم_رسم()    'نهاية الرسم'
تم
يتم استدعاء عند_رسم تلقائيا عند كلّ حدث يغيّر معالم النافذة، ونحن نعيد أخذ رقعة النافذة في كلّ مرّة لكي تتسع رقعة الرسم إذا تمّ تكبير النافذة وتتقلّص بتصغيرها، وهكذا تبقى رقعة الرسم دائما منسوبة إلى إطار النافذة، نقوم بطلاء الرقعة بالأسود لتصير النافذة كلّها سوداء، ثم نكتب محتوى السطور وهو ما نسمّيه صون الشاشة، أو صون الخرج، وهو ما تعرضه النافذة. نبدأ الرسم (الكتابة) دائما من الأفصول 0، ومن الأرتوب الذي يناسب رقم السطر الحالي مضروبا في ارتفاع كلّ حرف في الخط الكوفي، ذلك لكي تتمّ العودة إلى السطر دون تداخل مع السطر الذي فوقه.

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

يمكن تحميل الكود الكامل للدورة الأولى من خلال الرابط الملحق أسفل التدوين، وهذه صورة لما قد صنعت أيدينا حتى الآن:

محث الويندوز العربي في دورته الأولى

الدورة الثانية


سنقوم في هذه الدورة بتنفيذ الأوامر التي تتم كتابتها عوض الرجوع إلى السطر دون تجاوب، سنقوم بإعادة توجيه مخرجات (ردود) هذه البرامج إلى ملف على القرص الصلب، ذلك لكي نستطيع فتح الملف والتأكد من صحّة ترميز النصوص. على أن نقوم في دورة مقبلة بجعل هذا الملف مؤقتًا حتى يظهر على القرص الصلب ويتم حذفه بعد الانتهاء منه.

أشرنا سابقا إلى أنّه في كلّ دورة ننشئ برنامجا مستقلا قابلا للتنفيذ والاختبار، لكن هذا لا يعني أنّنا لا نتسطيع التغيير في الأكواد التي تمّ إنشاؤها في الدورة السابقة، فمن أجل تحقيق متطلبات الدورة الحالية، قد نحتاج إلى تعديل بعض مقاطع الأكواد لتطويعها حسب ما نراه يناسب الدورة.

أوّل ما سنبدأ به هو إضافة كائنٍ "ملف" إلى قائمة كائنات المشروع.

    خفي:
       نافذة ن_محث            'النافذة الرئيسية'
       خط خط_كوفي           'سيتم تحميل الخط من ملف على القرص'
       أحخز خلفية              'لون خلفية النافذة بمعيار أحمر أخضر أزرق'
       أحخز لون_نص          'لون الخط على النافذة'
       رقعة رقع                 'رقعة الرسم'
       جدول<نص> سطور    'صون الشاشة'
       عدد أقصى         'عدد السطور الأقصى التي يمكن عرضها على الشاشة'
       ملف ملف_خرج      'الملف الذي سيضم مخرجات الأوامر'
ثم نباشر بتمهيد الكائن ملف_خرج في دالة التكوين.

    ظاهر:
        جديد()
            ...
            ملف_خرج = ملف جديد(تنفيذ.رد_مسار_تطبيق() + "خرج.نص"، ملف.للكل، ملف.أنشئ_دائما)
        تم
سيتم إنشاء ملفّنا في نفس مجلّد التنفيذ الحالي، وسيكون اسمه "خرج.نص"، يشير المعطى ملف.للكل إلى أنّ الملف سيكون مفتوحًا للقراءة وللكتابة، ويشير المعطى ملف.أنشئ_دائما إلى أنّه سيتم إنشاء ملف جديد حتى ولو كان موجودًا بالفعل، إذ يحذفه وينشئ آخر مكانه.

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

'حجب عند_ضغط_مفتاح من مراقب_أحداث'
عند_ضغط_مفتاح(متفاعل من، حرف ح)
    لو ح = حرف.بس         'ضغط على إدخال. بس = بداية سطر'
        سطر_جديد()   'هنا سيتم تنفيذ الأمر'
        {نافذة}من.حدّث()
    أما لو ...
    ...
تم
تَمّ استدعاء الدالة سطر_جديد في السطر 4 أعلاه، وهي مسؤولة عن تنفيذ الأمر ثم العودة إلى السطر:

سطر_جديد()
    لو سطور.طول = أقصى  'آخر الصفحة'
        سطور.حذف(0)  'حذف أوّل سطر'
    تم
    نفذ_أمر()
    سطور.زد("محث>")
تم
'تحليل سطر الأمر وتنفيذه كإجراء فرعي'  
نفذ_أمر()
    نص سطر_أمر = سطر_حالي().رد_قطعة(4، سطر_حالي().طول-1) 'استثناء محث>'
    جدول<نص> كلمات = سطر_أمر.شق(" ")  'تقسيم الأمر إلى كلمات حسب الفراغات'
    نص اسم_برنامج = كلمات.صف(0)  'أوّل كلمة هي اسم برنامج التنفيذ'
    نص معطيات = ""   'تركيب المعطيات من باقي الكلمات'
    لأجل عدد ع=1، ع<كلمات.طول، ع++
        معطيات = معطيات + كلمات.صف(ع)
    تم
    ملف_خرج.فتح()   'فتح الملف لكتابة المخرجات'
    'إنشاء إجراء جديد، يرث مقبض ملف_خرج كمقبض خرج أساسي'
    إجراء إج_برنامج = إجراء جديد(اسم_برنامج، {عدد}عدم، ملف_خرج.رد_مقبض()، ملف_خرج.رد_مقبض())
    'تنفيذ الإجراء بالمعطيات'
    إج_برنامج.نفذ(معطيات، إجراء.انتظر_نهاية)
    ملف_خرج.غلق()
تم
لقد قمتُ أيضا بدعم بعض الأوامر المضمنة التي هي ليست برامج يقوم المحث بتنفيذها وإنّما هي أوامر يدعمها المحث مباشرة وهي: الأمر "إلى" والأمر "أين"، حيث أن الأمر "أين" يعطيك العنوان الحالي حيث أنت، والأمر "إلى" ينتقل بك إلى عنوان جديد. لن أستعرض هذه الأكواد في هذا التدوين خشية التطويل، تجدونها في مصدر البرنامج الملحق أسفل التدوين.

عند تنفيذ البرنامج الناتج من هذه الدورة، يمكننا تنفيذ أيّ ملفّ قابل للتنفيذ على الحاسوب، وذلك إمّا بكتابة عنوان البرنامج كاملا، أو بالإنتقال إلى مجلّد البرنامج باستخدام الأمر "إلى عنوان_مجلد"، ثم كتابة اسمه والضغط على إدخال. يتمّ بذلك تنفيذ البرنامج وتوجيه مخرجاته إلى الملف "خرج.نص"، إذا فتحنا هذا الملفّ فإنّنا نرى ما ردّه البرنامج بالترميز الصحيح. وبهذا فإنّنا قد أنجزنا متطلّبات الدورة الثانية، فماذا بعد؟ إي نعم، الدورة الثالثة.
أكمل القراءة