ساختار کامپایلر چگونه است؟ کامپایلر چطوری کار میکنه؟

ادریس رنجبر

2020-07-09

مقدمه

قبل از اینکه در مورد ساختار کامپایلر (Compiler) و روش کار کردنش براتون بگم بزارید یکم در مورد اینکه اصلا کامپایلر چیه بیشتر براتون بنویسم.

کامپایلر چیه؟

کامپایلر نرم افزاری که سورس کد رو از یه زبانی به زبان دیگه تبدیل میکنه که غالبا منظور ما نرم افزاریه که از زبان های سطح بالا (High level Programming language) به زبان هایی سطح پایین مثل اسمبلی ترجمه می کنند.

In computing, a compiler is a computer program that translates computer code written in one programming language (the source language) into another language (the target language).

منبع: PC Mag Staff (28 February 2019). “Encyclopedia: Definition of Compiler”. PCMag.com. Retrieved 28 February 2017.

انواع کامپایلر ها

  • اگر برنامه ی کامپایل شده را بتوان در سستیمی با CPU متفاوت از سیستمی که برنامه در آن کامپایل شده اجرا کرد به آن نوع کامپایلر میگیم Cross-Compiler.
  • De-compiler برنامه ایه که میتونید باهاش برنامه ای رو از زبان سطح پایین به یک زبان سطح بالا ترجمه کنید (یعنی بر عکس مترجم های عادی مرسوم)
  • Source to Source Compiler یا Trans-compiler ها یکی از انوع کامپایلر ها هستند که امکان ترجمه یک کد زبان High level به زبان High level دیگه رو به ما میدن.
  • و…

ساختار کامپایلر ها

کامپایلر ها معملا یک ساختار مشترک دارند. یک سری کار ها رو به صورت متوالی انجام میدن تا کد رو از یک زبان سطح بالا (در این نوع کامپایلر های مورد بحث) به زبان ماشین تبدیل کنند. به ترتیب ساختار کامپایلر ها رو با هم دیگه مرور میکنیم و در مورد هر کدوم از مراحل کامپایل توضیحاتی میدم.

Lexical Analyzer

Lexical Analyzer در واقع به معنی واژه یابه و بهش scanner هم میگن که فاز ابتدایی کامپایلر هست. در این مرحله یک سری از خطاها هم که خطاهای Syntax هستند کشف میشن. کامنت ها و فواصل اضافی هم در همین مرحله کاملا حذف میشن و نتیجه میره به مرحله بعد. تو این مرحله کد های از سمت چپ به راست کاراکتر به کاراکتر خونده میشن و به شکل یک سری توکن معنی دار در میان که به مراحل بعد کار کامپایلر کمک می کنند.

کار های اصلی که در این مرحله اتفاق میفته:

  • کامنت ها و فواصل اضافی رو حذف میکنه
  • شناسایی کلمات بر اساس متن سورس کد ورودی
  • دسته بندی کلمات به شناسه های از پیش رزرو شده (مثل if)، ثوابت و…
  • توکن هایی که جزو زبان نیستند رو تشخیص میده

مثال: فرض کنید به کامپایلر کد زیر رو به عنوان ورودی رو بدیم:

int y = 10;

خروجی ما در مرحله اول کامپایلر به این شکله:

Keywordint
Identifiery
Assignment Operator=
Number10
مثالی از توکن ها
Token name Sample token values
identifier x, color, UP
keyword if, while, return
separator }, (, ;
operator +, <, =
literal true, 6.02e23, "music"
comment /* Retrieves user data */, // must be negative

Syntax Analyzer

بهش Parser هم میگیم. در واقع ساختار کد ها رو تشخیص میده و کد ها رو پارس میکنه. یک درخت پارس از ساختار کد ها رسم میکنه. در این مرحله Syntax بررسی میشه و خطا ها مشخص میشن. بصورت کلی تو این بخش درستی کد نوشته شده بررسی میشه و اگر خطایی از این بابت در کد ها رویت بشه گزارش میشه.

با کمک توکن های ساخته شده در مرحله قبل، بر اساس یک سری قوانین که برای هر زبان برنامه نویسی ممکنه متفاوت باشه Parse Tree رو میسازه.

یک مثال از Parse Tree

کار های اصلی که در این مرحله اتفاق میفته:

  • گرفتن توکن ها از Lexical Analyzer
  • بررسی صحت کد از جنبه ی Syntax
  • گزارش دادن ارور های سینتکس
  • ساختن یک درخت سلسله مراتبی به اسم Parse Tree

Semantic Analyzer

سمانتیک انالایزر معنی دار بودن Parse Tree رو بررسی میکنه و اگر مشکلی نداشت اصطلاحا Parse Tree رو Verify میکنه. یه سری کار های دیگه هم از جمله Type cheeking, label checking انجام میده. برای اینکه بهتر کار Semantic analyzer رو درک کنید اینطوری در نظر بگیرید که تو این مرحله کامپایلر اون درختی که شکلش رو تو مرحله قبل دیدیم بررسی میکنه و بهش از جنبه معنایی نگاه میکنه. اگر مشکلی وجود داشته خطا میده در غیر اینصورت ما رو به مرحله بعدی هدایت میکنه.

کار های اصلی که در این مرحله اتفاق میفته:

  • ذخیره سازی نوع داده ها در Parse Tree یا Symbol Table
  • بررسی نوع داده ها و در صورت عدم تطابق داده و نوع داده (اگر قابل Cast کردن نباشه) ارور Semantic نمایش داده میشه.

توضیح: اگر در مثالی شما نیاز به TypeCast باشد به صورت خودکار در صورت امکان این اتفاق در این مرحله انجام می گیرد. به عنوان مثال:

float x = 10.5;
float y = x * 2;

احتمالا متوجه شدید که در مثال بالا عدد ۲ که یک عدد صحیح یا Integer هست قبل از عملیات ضرب به صورت خودکار به یک Float یا عدد اعشار تبدیل میشه. پس اون خط چیزی شبیه این میشه:

float y = x * 2.0;

Intermediate Code Generator

Intermediate Code Generator یعنی سازنده کد میانی (سطح میانی). تا اینجای کار کامپایلر برای همه ی ماشین ها یکسانه و تفاوتی نداره. کد میانی در واقع یک کد انتزاعیه بین زبان برنامه نویسی سطح بالا و زبان ماشین.خوبی این مرحله اینه که کار رو برای ترجمه کد ها به زبان ماشین خیلی ساده تر میکنه.

کار های اصلی که در این مرحله اتفاق میفته:

  • بر اساس Semantic یک برنامه، کد سطح میانی تولید میشه
  • مقادیر رو طی فرایند ترجمه نگه میداره
  • به ترجمه کد به زبان مقصد کمک میکنه
  • حفظ ترتیب و اولویت هایی که در زبان مبدا وجود داشت

به عنوان مثال:

total = count + rate * 5

به کمک Address Code نتیجه میشه:

t1 := int_to_float(5) 
t2 := rate * t1 
t3 := count + t2
total := t3

Code Optimizer

در این پروسه، کد های میانی بهینه میشن (از نظر سرعت، کوتاه تر شدن کد ها، قدرت کم تر خواستن و… ) و خط های اضافی به نحوی حذف میشه و کد ها فشرده تر میشن. (منظور از خط های اضافی خطوط خالی و… نیست اونها که در مراحل قبل حذف شدن). این پروسه باعث میشه کد های کمتر بشن و تا حد ممکن سرعت اجرا بالاتر بره.

Target Code Generator

در این مرحله کد خروجی که برای ماشین قابل خوندنه بر اساس کد سطح میانی که یک کد غیر وابسته به نوع سخت افزار بود تولید میشه. اما کد خروجی زبان مقصد به نوع ماشین (CPU) وابسته است.

منابع

امیدوارم این مطلب در مورد ساختار کامپایلر ها براتون مفید واقع شده باشه. برای نوشتن این مطلب یک روز وقت گزاشتم پس اگر دوست داشتین حتما برای دوستانتون بفرستین و اگر خواستین با ذکر منبع منتشر کنید.
نویسنده: ادریس رنجبر

لینک حمایت از آموزش ها و پروژه های متن باز



دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *