Skip to content

مدخل إلى مبادئ الترجمة البرمجية

مقدمة

عندما تضغط على زر "تشغيل"، كيف يتحول الكود إلى النتيجة على الشاشة؟ كل سطر كود تكتبه، الحاسوب في الواقع "لا يفهمه" -- فهو لا يتعرف إلا على 0 و 1. المترجم البرمجي هو "المترجم" الذي يحول اللغة البشرية إلى لغة الآلة. فهم مبادئ الترجمة البرمجية يساعدك على فهم من أين تأتي رسائل الخطأ، ولماذا بعض اللغات سريعة وبعضها بطيء، والمنطق الأساسي لتحسين الكود.

ماذا ستتعلم في هذه المقالة؟

بعد إكمال هذا الفصل، ستكتسب:

  • رؤية شاملة: إتقان خط أنابيب الترجمة الكامل من الكود المصدري إلى البرنامج القابل للتنفيذ
  • التحليل المعجمي: فهم كيف يقسم المترجم الكود إلى وحدات Token
  • التحليل النحوي: فهم بناء AST (شجرة التركيب المجرد)
  • تصور AST: رؤية بنية الشجرة للكود بشكل حدسي
  • التحليل الدلالي والتحسين: فهم مبادئ فحص الأنواع وتحسين الكود
  • تطبيق تقنيات التحسين: إتقان التقنيات الأساسية مثل طي الثوابت، وإزالة الكود الميت
  • نموذج التنفيذ: التمييز بين الترجمة والتأويل و JIT
الفصلالمحتوىالمفهوم الأساسي
الفصل 1ما هو المترجم البرمجيتشبيه المترجم، خط أنابيب الترجمة
الفصل 2التحليل المعجميToken، القواعد المعجمية
الفصل 3التحليل النحويAST، شجرة التركيب، الأسبقية
الفصل 4تصور ASTشجرة تركيب تفاعلية، أنواع العقد
الفصل 5التحليل الدلالي والتحسينفحص الأنواع، طي الثوابت، إزالة الكود الميت
الفصل 6تطبيق تقنيات التحسينتضمين الدوال، استخراج ثوابت الحلقة، نشر الثوابت
الفصل 7مترجم مقابل مؤول مقابل JITمقارنة ثلاثة نماذج تنفيذ

0. نظرة عامة: "رحلة الترجمة" للكود

تخيل أنك مترجم وتحتاج إلى ترجمة رواية صينية إلى الإنجليزية. لن تترجم كلمة بكلمة حرفياً، بل ستقوم بـ:

  1. تحديد الكلمات -- تقسيم الجمل إلى كلمات (التحليل المعجمي)
  2. فهم النحو -- الحكم على ما إذا كانت بنية الجملة صحيحة (التحليل النحوي)
  3. فهم الدلالة -- التأكد من أن المعنى متماسك وبدون تناقضات (التحليل الدلالي)
  4. التحسين والصقل -- جعل الترجمة أكثر طبيعية وسلاسة (تحسين الكود)
  5. إنتاج الترجمة -- كتابة النسخة الإنجليزية النهائية (توليد الكود)

المترجم البرمجي يفعل نفس الشيء تماماً، لكنه يترجم لغات البرمجة.

Compiler Principles: The Art of TranslationHow code becomes machine instructions
A compiler is like a translator, turning human-readable code into machine-readable instructions
The Complete Code Translation Pipeline
1
Lexical analysis
Break code into individual words called tokens
int age = 25 → [int, age, =, 25]
2
Syntax analysis
Check grammar rules and build a syntax tree
Validate whether statement structure is correct
3
Semantic analysis
Check whether the meaning of the code is valid
Check variable definitions and type compatibility
4
Intermediate code generation
Generate a machine-independent intermediate representation
Generate bytecode or intermediate representation
5
Optimization
Improve code so it runs more efficiently
Constant folding and dead-code elimination
6
Target code generation
Generate machine code or target code
Generate x86 or ARM machine instructions
Lexical analysis: tokenization
int age = 25;
Keywordint
Identifierage
Operator=
Number25
Separator;
Syntax analysis: build a tree
Assignment statement
Variableage
Operator=
Number25
Compilation vs Interpretation
Compiled languages
Source code → Compiler → Machine code
C, Go, Rust
✓ Fast execution
✓ Compile once, run many times
✗ Slow compile step
Interpreted languages
Source code → Interpreter → Line-by-line execution
Python, JavaScript, PHP
✓ Fast development
✓ Cross-platform
✗ Slower execution
Compiler Optimization
Before:
x = 5 + 3 + 2
⬇️
After:
x = 10
The compiler can optimize code automatically and improve runtime efficiency

1. خط أنابيب الخطوات الست للمترجم البرمجي

يمكن تقسيم عمل المترجم إلى ست مراحل، مثل خط الإنتاج في المصنع، حيث تمرر كل مرحلة نتائجها إلى المرحلة التالية.

How a Compiler WorksA six-step journey from source code to machine code
1
Lexical analysis→ Token stream
2
Syntax analysis→ AST syntax tree
3
Semantic analysis→ Typed AST
4
Intermediate code generation→ IR (intermediate representation)
5
Code optimization→ Optimized IR
6
Target code generation→ Machine code
1Lexical analysisOutput: Token stream
Split source code into individual words called tokens, like recognizing each word in a sentence.
Recognize keywordsRecognize identifiersRecognize numbersRecognize operatorsFilter whitespace
int x = 10 + 5;
→ [int] [x] [=] [10] [+] [5] [;]
    keyword identifier operator number operator number separator
Live lexical analysis
intKeyword
xIdentifier
=Operator
10Number
+Operator
5Number
;Separator
Three Execution Models Compared
Compiled
Source Compiler Machine code CPU execution
Fast executionMust wait for compilation
C, C++, Rust, Go
Interpreted
Source Interpreter Line-by-line execution
Run immediately while writingSlower execution
Python, Ruby, PHP
JIT
Source Bytecode JIT hot path compilation Execution
Balances performance and flexibilitySlower startup
Java, JavaScript (V8)
Core idea:A compiler is like a translator: it gradually turns human-readable code into instructions the machine can run. The six stages each do one job: identify words → understand syntax → check meaning → generate IR → optimize → generate machine code.

خط أنابيب الترجمة

  1. التحليل المعجمي (Lexical Analysis): تقسيم الكود المصدري إلى وحدات Token (كلمات)
  2. التحليل النحوي (Syntax Analysis): تنظيم وحدات Token في شجرة تركيب (AST)
  3. التحليل الدلالي (Semantic Analysis): التحقق من صحة الأنواع، وما إذا كانت المتغيرات معلنة
  4. توليد الكود الوسيط (IR Generation): إنشاء تمثيل وسيط مستقل عن المنصة
  5. تحسين الكود (Optimization): جعل الكود الوسيط أكثر كفاءة
  6. توليد الكود (Code Generation): توليد كود الآلة للمنصة المستهدفة
المرحلةالمدخلاتالمخرجاتالتشبيه
التحليل المعجميتدفق أحرف الكود المصدريتدفق Tokenتقسيم الجمل إلى كلمات
التحليل النحويتدفق TokenAST (شجرة التركيب)تحليل بنية الجملة
التحليل الدلاليASTAST مع أنواعالتحقق من تماسك المعنى
الكود الوسيطAST مع أنواعIRكتابة المسودة الأولى
تحسين الكودIRIR محسّنالصقل والاختصار
توليد الكودIR محسّنكود الآلةإنتاج النسخة النهائية

2. التحليل المعجمي: تقسيم الكود إلى "كلمات"

التحليل المعجمي هو الخطوة الأولى في الترجمة. يقوم المترجم بمسح كل حرف من الكود المصدري من اليسار إلى اليمين، وتجميعها في وحدات Token (وحدات معجمية) ذات معنى.

🔤 Lexer: Split Code into Tokens

Enter a line of code and see lexical analysis results in real time

تماماً كما عندما تقرأ جملة إنجليزية، يقوم عقلك تلقائياً بتجميع الحروف في كلمات، يقوم المحلل المعجمي بتجميع الأحرف في وحدات Token:

الكود المصدري: let x = 10 + 5;

تدفق Token:
[let]   → كلمة مفتاحية (كلمة محفوظة في اللغة)
[x]     → معرّف (اسم متغير)
[=]     → عامل (تعيين)
[10]    → قيمة عددية حرفية
[+]     → عامل (جمع)
[5]     → قيمة عددية حرفية
[;]     → فاصل (نهاية العبارة)

الأنواع الخمسة لـ Token

  • الكلمات المفتاحية: كلمات خاصة محفوظة في اللغة، مثل let، if، return، function
  • المعرّفات: أسماء يحددها المبرمج، مثل أسماء المتغيرات، أسماء الدوال
  • القيم الحرفية: قيم مكتوبة مباشرة في الكود، مثل الأرقام 42، السلاسل النصية "hello"
  • العوامل: رموز تنفيذ عمليات، مثل +، -، =، ===
  • الفواصل: رموز تفصل بنية الكود، مثل ;، ،، (، )

3. التحليل النحوي: بناء شجرة التركيب (AST)

التحليل المعجمي يقسم الكود إلى وحدات Token، لكن هذه مجرد "كلمات" معزولة. مهمة التحليل النحوي هي تنظيم هذه الوحدات وفقاً للقواعد النحوية في شجرة تركيب مجردة (Abstract Syntax Tree, AST) -- التي تعكس بنية الكود وأسبقية العمليات.

التعبير: 1 + 2 * 3

شجرة التركيب:        لماذا هكذا؟
       +       لأن أسبقية *
      / \      أعلى من +، لذا
     1   *     2 * 3 ترتبط أولاً
        / \    كشجرة فرعية
       2   3

أهمية AST

AST هي "بنية البيانات الأساسية" للمترجم؛ التحليل الدلالي والتحسين وتوليد الكود لاحقاً جميعها مبنية عليها. أدوات التطوير الحديثة تستخدم AST بكثرة أيضاً:

  • ESLint: يحلل الكود كـ AST، ويتحقق من انتهاك القواعد
  • Prettier: يحلل كـ AST ثم يعاد تنسيق المخرجات
  • Babel: يحلل AST → يحوّل → يولّد كود متوافق
  • إعادة الهيكلة في IDE: إعادة تسمية آمنة للمتغيرات، استخراج الدوال بناءً على AST
بنية التركيبتسلسل Tokenعقدة AST
إعلان المتغيرlet x = 10VariableDeclaration → Identifier + Literal
استدعاء دالةadd ( 1 , 2 )CallExpression → Identifier + Arguments
عبارة شرطيةif ( a > b )IfStatement → BinaryExpression + Block

4. تصور AST: رؤية "الهيكل العظمي" للكود

أعلاه وصفنا بنية AST بالنص، لكن "الرؤية" أكثر حدسية من "القراءة". المكون التفاعلي أدناه يتيح لك اختيار تعبيرات مختلفة ومراقبة أشجار التركيب الخاصة بها في الوقت الفعلي.

🌳 AST Visualizer: See the Skeleton of Code

Choose an expression and inspect its abstract syntax tree

Syntax tree
BinaryExpression+
NumericLiteral1
BinaryExpression*
NumericLiteral2
NumericLiteral3
Parse notes
1* has higher precedence than +, so 2 * 3 groups first
22 * 3 forms a BinaryExpression subtree
31 and that subtree become the left and right operands of +
4The final + node is the root, showing the evaluation order
💡 Try AST Explorer — inspect ASTs for arbitrary code online

من خلال التصور ستكتشف أن القواعد الأساسية لـ AST هي في الواقع بسيطة:

بنية الكودعقدة جذر ASTالعقد الفرعية
1 + 2 * 3BinaryExpression (+)يسار: NumericLiteral(1)، يمين: BinaryExpression(*)
let x = 10VariableDeclarationVariableDeclarator → Identifier(x) + NumericLiteral(10)
add(a, b)CallExpressionIdentifier(add) + Arguments(a, b)

تطبيقات AST في التطوير اليومي

ربما لم تكتب مترجماً برمجياً مباشرة، لكنك تستخدم أدوات مبنية على AST كل يوم:

  • ESLint / Prettier: يحللان الكود كـ AST، ويتحققان من القواعد أو يعيدان التنسيق
  • Babel / SWC: يحللان AST → يحولان التركيب → يولدان كوداً متوافقاً
  • إعادة الهيكلة في IDE: إعادة تسمية آمنة، واستخراج الدوال بناءً على AST
  • Tree-shaking: يحلل import/export في AST، ويزيل الكود غير المستخدم

5. التحليل الدلالي وتحسين الكود

التحليل النحوي يضمن أن الكود "صحيح بنيوياً"، لكن الصحة البنيوية لا تعني أن "المعنى صحيح". التحليل الدلالي يتحقق من أن معنى الكود صالح، وتحسين الكود يجعل البرنامج يعمل بشكل أسرع.

Compilation PracticeFrom code to executable file
Input code
Compilation steps
1
Preprocess
gcc -E hello.c -o hello.i
Process #include and expand macros
2
Compile
gcc -S hello.i -o hello.s
Generate assembly code
3
Assemble
gcc -c hello.s -o hello.o
Generate object file
4
Link
gcc hello.o -o hello
Generate executable file
Generated files
📄
hello.c
Source code file
📝
hello.i
Preprocessed file
⚙️
hello.s
Assembly code file
📦
hello.o
Object file
🚀
hello
Executable file
Common compiler tools
GCC
GNU Compiler Collection
Clang
LLVM C/C++ compiler
MSVC
Microsoft Visual C++

4.1 التحليل الدلالي: التحقق من صحة "المعنى"

محتوى الفحصمثالالنتيجة
فحص الأنواعint x = "hello"الأنواع غير متوافقة
فحص النطاقاستخدام متغير غير معلن yالمتغير غير موجود
استنتاج الأنواع1 + 2.0استنتاج النتيجة كـ float
فحص المعلماتadd(1, 2, 3) لكن الدالة تقبل معلمتين فقطعدد المعلمات غير متطابق

الأخطاء التي رأيتها تأتي في الغالب من التحليل الدلالي

  • TypeError: Cannot read properties of undefined -- فحص الأنواع
  • ReferenceError: x is not defined -- فحص النطاق
  • Expected 2 arguments, but got 3 -- فحص المعلمات

4.2 تحسين الكود: جعل البرنامج أسرع

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

تقنية التحسينقبلبعدالمبدأ
طي الثوابتx = 10 + 5x = 15حساب النتيجة مباشرة وقت الترجمة
إزالة الكود الميتif (false) { ... }حذف مباشركود لن يُنفذ أبداً
نشر الثوابتx = 15; y = x * 2y = 30استبدال مباشر بالقيم المعروفة
استخراج ثوابت الحلقةحساب متكرر len = arr.length داخل الحلقةنقل خارج الحلقةتجنب الحسابات المتكررة

6. تطبيق تقنيات التحسين: كيف يجعل المترجم الكود أسرع

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

⚡ Compiler Optimization: Make Code Faster Automatically

Choose an optimization technique and see how the compiler improves code

📝 Before optimization
const width = 10
const height = 20
const area = width * height  // computed at runtime
console.log(area)
Compiler optimization
🚀 After optimization
const area = 200  // computed during compilation
console.log(200)
How Constant folding works
The compiler sees that width and height are constants, so it computes 10 * 20 = 200 during compilation. Runtime no longer needs a multiplication.
Performance gain:
30%

المترجمات الحديثة ومحركات JIT (مثل V8 و GCC و LLVM) تطبق تلقائياً عشرات التحسينات. كمطور، لا تحتاج إلى القيام بذلك يدوياً، لكن فهمها يساعدك على:

  • كتابة كود أسهل في التحسين: مثلاً، استخدام const بدلاً من let، يمكن للمترجم طي الثوابت بسهولة أكبر
  • فهم اختلافات الأداء: لماذا الدوال الصغيرة أسرع من الكبيرة؟ لأن المترجم يمكنه تضمينها (inlining)
  • تجنب "إلغاء التحسين": بعض أنماط الكتابة تمنع تحسين المترجم، مثل eval() و with
تقنية التحسينشرط التفعيلالتأثير على الأداءماذا يمكن للمطور فعله
طي الثوابتتعبيرات جميعها ثوابتإزالة الحساب وقت التشغيلاستخدام تصريحات const أكثر
إزالة الكود الميتكود لا يمكن الوصول إليه أو نتيجته غير مستخدمةتقليل حجم الكودتنظيف الكود غير المستخدم في الوقت المناسب
استخراج ثوابت الحلقةحساب ثابت داخل الحلقةتقليل الحسابات المتكررةالاستخراج اليدوي أيضاً عادة جيدة
تضمين الدوالدوال صغيرة تُستدعى بشكل متكررإزالة تكلفة الاستدعاءإبقاء الدوال صغيرة ومركزة
نشر الثوابتقيمة المتغير قابلة للتحديد وقت الترجمةسلسلة الحساب بالكامل تُزالاستخدام الثوابت بدلاً من الأرقام السحرية

7. مترجم مقابل مؤول مقابل JIT

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

🔄 Compiled vs Interpreted vs JIT

Click an execution mode to see how code moves from source to running program

📝
Source code
main.c
⚙️
Compiler
Full compilation
📦
Machine code
Binary executable
🚀
Run directly
CPU runs it directly
Run speed
Very fast
Startup
Slow; compile first
Portability
Recompile required
Representative languages:CC++RustGo
البُعدمترجممؤولJIT (الترجمة في الوقت الفعلي)
العمليةترجمة الكل إلى كود الآلة أولاً، ثم التنفيذالقراءة والتنفيذ سطراً بسطر، الترجمة الفوريةالتأويل أولاً، ثم ترجمة الكود الساخن
سرعة التنفيذالأسرعالأبطأمتوسطة (الكود الساخن قريب من المترجم)
سرعة البدءبطيئة (تحتاج ترجمة)سريعة (تنفيذ مباشر)متوسطة (تحتاج تسخين)
متعدد المنصاتيحتاج إعادة ترجمةمتعدد المنصات بطبيعتهمتعدد المنصات
لغات تمثيليةC, Rust, GoPython, RubyJavaScript (V8), Java

لماذا JavaScript سريع جداً؟

يقوم مترجم JIT في محرك V8 بمراقبة الكود الذي يُنفذ بشكل متكرر (الكود الساخن)، ثم يترجمه إلى كود آلة محسّن بشكل كبير. لذلك على الرغم من أن JavaScript "لغة مؤولة"، إلا أن أداءه في V8 يمكن أن يقارب اللغات المترجمة. هذا هو أيضاً الأساس الذي يمكّن Node.js من العمل على الخادم.


الملخص

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

مراجعة النقاط الرئيسية في هذا الفصل:

  1. المترجم هو مترجم: يحول الكود المقروء بشرياً إلى تعليمات قابلة للتنفيذ آلياً
  2. خط أنابيب من ست خطوات: التحليل المعجمي → التحليل النحوي → التحليل الدلالي → الكود الوسيط → التحسين → توليد الكود
  3. التحليل المعجمي يقسم إلى Token: يقسم تدفق الأحرف إلى وحدات ذات معنى مثل الكلمات المفتاحية، والمعرفات، والعوامل
  4. التحليل النحوي يبني AST: ينظم Token في بنية شجرة وفقاً للقواعد النحوية، معكوساً أسبقية العمليات
  5. التحليل الدلالي يضمن الصحة: فحص الأنواع، فحص النطاق؛ معظم الأخطاء التي رأيتها تأتي من هنا
  6. المترجم يتحسن تلقائياً: تقنيات مثل طي الثوابت، وإزالة الكود الميت، وتضمين الدوال تجعل الكود أسرع تلقائياً
  7. ثلاثة نماذج تنفيذ: المترجم الأسرع، والمؤول الأكثر مرونة، و JIT يجمع بين الاثنين

قراءة إضافية