ما هو المترجم (Compiler)؟
المترجم (compiler) برنامج يحوّل الكود المصدري المكتوب بلغة برمجة إلى لغة الآلة، أو إلى صيغة أدنى مستوى مثل البايت كود، قبل تشغيل البرنامج. وبعد ذلك يعمل البرنامج المترجَم دون الحاجة إلى الكود المصدري الأصلي.
آخر تحديث 24 سبتمبر 2026
- 1print("Hello")
- 2total = 2 + 3
- 3print(total)
- 4print("Done")
ترجم مرة واحدة، ثم شغّل البرنامج كما تشاء من المرات: يزداد عدّاد التشغيل، ولا يزداد عدّاد الترجمة. فعّل الخطأ الإملائي وسيرفض المترجم البرنامج كله، فلا يُطبع شيء.
احفظ برنامج C هذا باسم hello.c:
#include <stdio.h>
int main(void) {
int price = 4;
printf("Total: %d\n", price * 3);
return 0;
}
ثم اكتب gcc hello.c -o hello. لا يحدث شيء ظاهر للحظة، ثم يظهر ملف جديد اسمه hello بجوار ملفك المصدري. هذا الملف هو ناتج المترجم: لغة آلة يستطيع معالجك تنفيذها. احذف hello.c وسيظل ./hello يعمل، لأن كل ما يحتاجه البرنامج قد تُرجم بالفعل.
gcc hello.c -o hello
./hello
Total: 12
كيف يعمل المترجم خطوة بخطوة
يقرأ المترجم الكود المصدري كنص عادي ويعالجه على مراحل. لنأخذ السطر الواحد total = price * 3:
- التحليل المعجمي (lexical analysis). يُقسَّم النص إلى رموز (tokens)، وهي "كلمات" اللغة: الاسم
total، والعامل=، والاسمprice، والعامل*، والعدد3. وتُحذف المسافات والتعليقات في هذه المرحلة. - التحليل النحوي (parsing). تُرتَّب الرموز في شجرة صياغة تسجّل البنية: عملية إسناد قيمتها حاصل ضرب
priceفي3. والكود الذي يخالف قواعد اللغة يتوقف هنا مع خطأ في الصياغة. - التحليل الدلالي (semantic analysis). يفحص المترجم المعنى: هل
priceمعرَّف؟ هل الأنواع متوافقة؟ هل الدالة المستدعاة موجودة؟ - التحسين (optimization). يُعاد كتابة البرنامج ليعمل أسرع أو يستهلك ذاكرة أقل دون تغيير ما يفعله. قد يصبح
x * 2تعليمة إزاحة واحدة، ويُحذف الكود الذي لا يمكن أن يُنفَّذ أبدًا. - توليد الكود (code generation). يكتب المترجم الناتج لمنصة مستهدفة: لغة آلة لمعالجات x86-64 أو ARM، أو بايت كود لآلة افتراضية.
وفي C وC++ توجد خطوة أخيرة هي الربط (linking)، تجمع ملفاتك المترجَمة مع كود المكتبات (مثل printf من مكتبة C القياسية) في ملف تنفيذي واحد. يشغّل gcc الرابط نيابةً عنك، ولهذا تبدو العملية كأنها أمر واحد.
توفّر Python المرحلتين الأوليين كوحدات قياسية، فيمكنك مشاهدتهما تعملان على مدخلات حقيقية:
NAME 'total'
OP '='
NAME 'price'
OP '*'
NUMBER '3'
Assign(
targets=[
Name(id='total', ctx=Store())],
value=BinOp(
left=Name(id='price', ctx=Load()),
op=Mult(),
right=Constant(value=3)))
هذه الشجرة هي ما تعمل عليه بقية مراحل المترجم. لم تعد تهتم بالمسافات أو الأسطر الجديدة، بل بالبنية فقط: أسند إلى total نتيجة ضرب price في 3.
وقت الترجمة: أخطاء تُكتشف قبل تشغيل البرنامج
لأن المترجم يقرأ البرنامج كله قبل تشغيل أي جزء منه، يستطيع رفض الأخطاء مبكرًا. في هذا المثال لا يُنفَّذ أي سطر من أسطر print الثلاثة داخل source، ولا حتى الأول، لأن مترجم Python يرفض النص قبل بدء التنفيذ:
Rejected before running anything:
'(' was never closed (line 3)
مترجمات اللغات ذات الأنواع الثابتة (statically typed) تكتشف أكثر من أخطاء الصياغة. هذا السطر من C، في ملف اسمه bad.c، صحيح نحويًا، لكنه يخزّن نصًا في متغير صحيح:
int count = "five";
يتوقف GCC 14 وما بعده مع خطأ:
bad.c:4:17: error: initialization of 'int' from 'char *' makes integer from pointer without a cast [-Wint-conversion]
الخطأ نفسه في Python لن يظهر إلا عند تنفيذ ذلك السطر. واكتشافه في وقت الترجمة من أسباب اختيار الفرق الكبيرة للغات المترجَمة ذات الأنواع الثابتة. لكن المترجم لا يستطيع اكتشاف كل شيء: القسمة على عدد يتضح أنه صفر، أو قراءة ملف غير موجود، ما زالا يفشلان في وقت التشغيل.
أمثلة على المترجمات
| المترجم | يقرأ | يُنتج |
|---|---|---|
| GCC وClang | C وC++ | لغة آلة لمعالجات x86-64 وARM وغيرها |
| MSVC (Microsoft Visual C++) | C وC++ | ملفات تنفيذية لنظام Windows (.exe و.dll) |
javac | Java | بايت كود JVM (ملفات .class) |
rustc | Rust | لغة آلة |
go build | Go | ملف تنفيذي واحد |
tsc | TypeScript | JavaScript |
يُظهر صفّان من الجدول أن "لغة الآلة" ليست الناتج الوحيد الممكن. يُنتج javac بايت كود تشغّله آلة Java الافتراضية، وهي جزء من بيئة تشغيل Java. ويحوّل tsc لغة TypeScript إلى JavaScript، وهي لغة عالية المستوى أخرى؛ والمترجم الذي يفعل ذلك يُسمّى غالبًا محوّلًا (transpiler).
لغة الآلة المترجَمة مرتبطة بعائلة معالجات واحدة ونظام تشغيل واحد. البرنامج المترجَم لنظام Windows على x86-64 لا يعمل على جهاز Mac بشريحة Apple من سلسلة M؛ عليك ترجمة الكود المصدري نفسه من جديد لكل منصة.
مترجم أم مفسر
المفسر يشغّل الكود المصدري مباشرة بدلًا من إنتاج ملف برنامج منفصل، وهكذا تُشغَّل Python وRuby عادةً. ومعظم أدوات اللغات الحديثة تستخدم الفكرتين معًا: CPython يترجم ملفك إلى بايت كود ثم يفسّره، وJVM ومحرك V8 الخاص بـ JavaScript يترجمان الكود كثير الاستخدام إلى لغة الآلة أثناء تشغيل البرنامج. الجدول الكامل للمقارنة موجود في صفحة المترجم مقابل المفسر.
مفاهيم خاطئة شائعة
- "إذا تُرجم البرنامج فهو يعمل." المترجم يتحقق من أن البرنامج صالح، لا من أنه صحيح. الأخطاء المنطقية وأعطال وقت التشغيل مثل القسمة على صفر تمرّ دون أن يكتشفها.
- "Python لا تُترجَم." يترجم CPython كل ملف إلى بايت كود قبل تشغيله. ملفات
.pycفي مجلدات__pycache__هي ذلك البايت كود، محفوظًا للوحدات التي يستوردها برنامجك حتى يتخطى التشغيل التالي هذه الخطوة. - "المترجم الإلكتروني (online compiler) مترجم." المواقع التي تشغّل كودك في المتصفح ترسله إلى خادم يشغّل سلسلة الأدوات كاملة (المترجم والرابط والبرنامج نفسه) ثم يعيد الناتج. المترجم قطعة واحدة من ذلك.
- "الكود المترجَم يعمل في أي مكان." إنه يعمل على المعالج ونظام التشغيل اللذين بُني لهما. وقابلية النقل تأتي من إعادة الترجمة، أو من آلة افتراضية مثل JVM.
إلى أين بعد ذلك
توثيق C يُظهر المترجم في الاستخدام اليومي: صفحة كيف تترجم برنامج C وتشغّله تشرح المعالجة المسبقة والترجمة والتجميع والربط بأوامر gcc حقيقية. ولترى الطريقة الأخرى لتشغيل الكود، اقرأ المترجم مقابل المفسر، ثم بيئة التشغيل لتعرف ما يحيط بالبرنامج بعد أن يبدأ. ودورة C مكان جيد للتدرّب على لغة مترجَمة.