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

معمولا ورودی اصلی را کد مبدا و خروجی را کد شی گوییم. خروجی این برنامه ممکن است برای پردازش شدن توسط برنامه دیگری مثل Linker مناسب باشد یا فایل متنی باشد که انسان نیز بتواند آنرا بخواند. مهمترین علت استفاده از ترجمه کد مبدا، ایجاد برنامه اجرایی می باشد. اصطلاح همگردان (compiler) اصولا برای برنامه هایی به کار میرود که کد مبدا را از یک زبان سطح بالا به زبانی سطح پایین تری مثل اسمبلی یا زبان سطح ماشین ترجمه می کند. برعکس برنامه ای که زبان سطح پایین را به بالاتر تبدیل می کند را decompiler گوییم.

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

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

کامپیوتر های اولیه از کامپایلر ها استفاده نمی کردند، چرا که این کامپیوتر ها حافظه کوچکی داشتند و با برنامه های کوتاه سر و کار داشتیم. کاربران مجبور بودند کد باینری یا دسیمال برنامه ها را به طور مستقیم و با کمک نوار های مغناطیسی به سیستم وارد کنند. اما برنامه نویس ها زیاد این وضعیت را تحمل نکردند و به فکر تولید برنامه ای افتادند که کاراکتر های الفبایی(واژه های اختصاری) را به تعدادی دستور که قابل اجرا توسط ماشین باشد تبدیل کند. در این وضعیت بود که زبان های اسمبلی و کامپایلر های اولیه با نام اسمبلر به وجود آمد. در اواخر دهه 90 بود که ماشین های وابسطه به زبانهای زبانهای برنامه نویسی رونق گرفتند. متعاقبا کامپایلرهای آزمایشی ایجاد شدند. FORTRAN به سرپرستی John Backus در شرکت IBM به عنوان اولین کامپایلر کامل را در سال 1957 تولید شد.

کوبول اولین زبان کامپایلی با معماری چندگانه در سال 1960 تولید شد. در طی دهه 60 کامپایلر های زیادی تولید شد اما بر روی کیفیت کامپایلر ها کمتر فکر می شد. همزمان با تکامل زبان های برنامه سازی و افزایش قدرت کامپیوتر ها، کامپایلرها هرچه بیشتر پیچیده می شدند. یک کامپایلر خود برنامه ای است که توسط زبان پیاده ساز تولید شده است. اولین کامپایلر خود محور که می توانست کد خود را کامپایل کند برای زبان Lisp و توسط Hart و Levin در سال 1962 و در دانشگاه MIT ایجاد شد. در دهه 70 از زبانهای سطح بالایی مثل Pascal و C جهت نوشتن کامپایلر ها استفاده شد.

ساخت کامپایلر های خود محور دارای مشکل راه اندازی است، چونکه هر کامپایلری باید توسط کامپایلر نوشته شده ای به زبان دیگر کامپایل شود یا برای این مشکل دست به دامن مفسری بشود. ساختار کامپایلر ها و کامپایلر بهینه ساز امروزه بخشی از برنامه درسی دانشجویان کامپیوتر است. برخی کامپایلر ها به منظور آموزشی برای زبان های برنامه نویسی تولید می گردد. مثلا کامپایلر PL/0 توسط Niklaus Wirth برای آموزش در دهه 1970 به کار رفت.

به علت سادگی و دلایل زیر هنوز برای آموزش مورد استفاده قرار می گیرد:

  • توسعه گام به گام برنامه
  • به کار گیری پارسر های بازگشتی
  • استفاده از EBNF جهت تعریف نحو زبان
  • استفاده از P-Code در جریان تولید کد خروجی قابل حمل
  • نمایش T-diagram جهت تعارف رسمی

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

از 1945تا1960:تولید کد:

در این دوره ,زبانها به تدریج به وجود آمدند و ماشینها چندان متعارف نبودند . مسئله این بود که چگونه باید کدی را برای یک ماشین تولید کرد . با توجه به اینکه برنامه نویسی به زبان اسمبلی رواج داشت , این مسئله وخیمتر شد. استفاده از کامپایلر , برنامه نویسی خودکار نامیده شد . طرفداران زبانهای سطح بالا می ترسیدند که کد تولید شده نسبت به زبان اسمبلی کارایی چندان نداشته باشد. اولین کامپایلر فرترن(شریدان 1959) به خوبی بهینه سازی شد

از 1960تا1975 :تجزیه کردن:

در دهه های 1960و1970 زبانهای برنامه سازی جدید به وجود آمدند و طراحان زبان معتقد بودند که طراحی سریع کامپایلر برای زبان جدید , مهمتر از وجود کامپایلری با کد کارآمد است .بدین ترتیب , در ساخت کامپایلر به پردازشگر جلویی تاکید شده است . در همین زمان , مطالعه زبانهای رسمی , تکنیکهای قدرتمندی را برای ساخت پردازشگر جلوی , بخصوص تولید تجزیه کننده به وجود آورد

از 1975 تاکنون :تولید کد و بهینه سازی کد:

از 1975 تاکنون , تعداد زبانهای جدید و انواع ماشین مختلف کاهش یافت در نتیجه نیاز به کامپایلرهای سریع و ساده یا سریع و ناقص برای زبانها یا ماشینهای جدید , کاهش یافت . بزرگترین آشفتگی در طراحی زبان و ماشین خاتمه یافت و افراد خواستار کامپایلرهای قابل اعتماد , کارآمد و با واسط کاربر مناسب شدند . بدین ترتیب , توجه کیفی به کد بیشتر شد زیرا با تغیر اندکی که در ساختار ماشینها ایجاد می شود , طول عمر کدها افزایش می یابد.در همین دوره , مدلهایی در برنامه نویسی به وجود آمدند که برنامه نویسی تابعی , منطقی و توزیعی نمونه های از این مدلها هستند, خواسته های زمان اجرای این زبانها نسبت به زبانهای دستور, افزایش یافت .

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

راه های مختلفی جهت دسته بندی کامپایلر ها وجود دارد مثلا می توان آنها را با توجه به ورودی، خروجی، ساختار داخلی و یا رفتار زمان اجرای آن تقسیم بندی کرد.

کامپایلرهای Native و cross:

اکثر کامپایلرها به دو دسته Native و Cross تقسیم می شوند. کامپایلرهایی که به منظور اجرای برنامه ها کدهای باینری را تولید می کنند، کامپایلر هایی با کد محلی یا Native گوییم چرا که تنها در کامپیوترهای یک نوع با سیستم عامل های یکسان قابل به کارگیری است. از طرف دیگر ممکن است کامپایلرها کدهای باینری را تولید کنند که در سیستم های مختلف قابل اجرا باشد. به این دسته از کامپایلر ها که وابستگی به سخت افزار ندارند، کامپایلر های عبوری یا Cross گوییم. برای این نوع کاپایلر ها تنها کافی است برای بار اول سخت افزار را به آن معرفی نمود. بنابراین می توان نتیجه گرفت که کامپایلرهای عبوری مفیدتر هستند. این تقسیم بندی برای مفسرها به کار نمی رود جونکه آنها از نمایش دودویی برای اجرای کد خود استفاده نمی کنند. ماشین های مجازی در هیچ یک از این دسته بندی ها نمی گنجد. هر گاه در ماشین های مجازی یکسان قابل اجرا باشد می توان آنرا Native و هرگاه کامپایلر قادر به تولید خروجی برای پلت فورم های مختلف باشد آنرا Cross گوییم.

کامپایلرهای تک فاز و چند فاز:

فاز بندی کامپایلر ها که در پشت زمینه به محدودیت های منابع سخت افزاری وابسته است. در نتیجه کامپایلر ها به مجموعه برنامه های کوچکتر تقسیم می شوند هر یک بخشی از عمل ترجمه یا آنالیز را برعهده می گیرند. کامپایل تک فازی به نظر مفید می آید، چراکه سریعتر است. زبان پاسکال از این امکان استفاده می کند. اما مشکل اینجا است که اگر اعلان جلوتر از دستور به کارگیری باشد، چه کار باید کرد؟ برای حل این مشکل میتوان در فاز اول اعلان ها را مشخص کرد و در فاز بعد عمل ترجمه را انجام داد. عیب دیگر کامپایلر تک فازی دشواری بهینه سازی کدهای زبان سطح بالا می باشد. همگردان یک گذره (One-Pass Compiler) کامپایلری است که برای تولید کد ماشین، تنها یک مرتبه متن برنامه را می  خواند. دستور برخی زبان ها به گونه ای است که تولید همگردان یک گذره برای آنها غیر ممکن است. مجموعه همگردان های گنو یا Gnu complier colection یا به صورت مخفف GCC مجموعه ای از همگردان های آزاد برای زبان های برنامه نویسی است. تقسم بندی کامپایلر ها به برنامه های کوچکتر تکنیکی است که همچنان مورد بحث محققان است. در این نوع دسته بندی کامپایلر ها، انواع دیگری نیز وجود دارد:

**کامپایلر مبدا به مبدا(source-to-source compiler)**که کدی با زبان سطح بالا را دریافت می کند و خروجی آن نیز زبان سطح بالا می باشد. مثلا موازی سازی خودکار کامپایلر در مواردی که به طور تکراری در برنامه ورودی وجود دارد و سپس تغییر شکل دادن کد و نوشتن کد یا ساختار زبانی موازی(برابر)با آن.(همچون دستور DOALL در فورترن) .

کامپایلر Stage که به زبان اسمبلی برای ماشین نظری ترجمه می کند. مثلا در Prolog
ماشین پرولوگ معمولا ماشین انتزائی (WAM) خوانده می شود. بایت کدهای جاوا و Python زیر مجموعه ای از این دسته اند.

کامپایلر زمان اجرا(Just in time(JIT)) برای سیستم های Smalltalk ، Java و زبان های میانه(CIL) در محصولات NET. استفاده می شود. [بیشتر بدانید!]

زبانهای تفسیری و کامپایلی:

بسیاری از افراد زبانهای سطح بالا را به دو دسته تفسیری و کامپایلی تقسیم می کنند. کامپایلر ها و مفسر ها روی زبان ها عمل می کنند نه زبانها روی آنها! مثلا این تصور وجود دارد که الزاما BASIC تفسیر می شود و C کامپایل. اما ممکن است نمونه هایی از BASIC یا C ارائه شود که به ترتیب کامپایلری و تفسیری باشد. البته استثنا هایی نیز وجود دارد، مثلا برخی زبانها در خصوصیات خود این تقسیم بندی را مشخص کرده اند(C کامپایلری است یا SNOBOL4 و اکثر زبانهای اسکریپتی که کد منبع زمان اجرا دارند تفسیری می باشد).

**
طراحی کامپایلر ها:**

تقسیم بندی پروسه های کامپایل به مجموعه ای از فاز ها مورد حمایت پروژه کامپایلری (( تولید کامپایلرهای باکیفیت ))(PQCC) از دانشگاه Carnegie Mellon قرار گرفت. در این پروژه اصطلاحات جلو بندی، میان بندی(امروزه به ندرت به کار میرود) و عقب بندی معرفی شد. اکثر کامپایلرهای امروزی بیش از دو فاز دارند. جلوبندی معمولا با پردازش املایی و معنایی شرح داده می شود. عقب بندی شامل تبدیل نوع و بهینه سازی های مختلف می باشد. سپس کد برای آن کامپیوتر خاص تولید می شود. استفاده از جلوبندی و عقب بندی این را ممکن می کند که جلوبندی های مختلفی برای زبانهای مختلف وجود داشته باشد و عقب بندی های مختلفی نیز برای CPU های مختلف.

جلو بندی(Front end):

جلوبندی به منظور تولید کد میانی یا IR از کد مبدا استفاده می شود. جلوبندی معمولا جدول نماد ها را مدیریت نموده و یک نگاشتگر ساختمان داده ای، هر نماد را از درون کد مبدا به اطلاعات مربوط به آن مثل نوع و دامنه تعریف آن نگاشت می شود. این امر در چند فاز انجام میگردد:

خط نوسازی(Line Reconstruction): زبانهایی که اجازه تعیین فضای اختیاری برای شناسه ها را می دهند قبل از عمل تجزیه نیاز به فاز اضافی دارند که کد ورودی را به صورت متعارفی برای تجزیه گر آماده کند. Algol، Coral66، Atlas Autocode وImp نمونه هایی از این زبانه هستند که به خط نوسازی نیازمند است.

**پیش پردازش(Preprocessor):**برخی زبانها همچون C احتیاج به فاز پیش پردازش برای جایگزینی شروط کامپایل و ماکرو ها دارند.در زبان C فاز پیش پردازش شامل مرحله تحلیل لغوی می شود. [بیشتر بدانید!]

تحلیل لغوی کد متنی مبدا را به اجزای کوچکی که نشانه(token) نامیده می شود می شکند. هر نشانه واحد ساده ای از زبان است مثل کلمات کلیدی و نام نمادها. نحو نشانه ها نوعا یک زبان باقاعده است، بنابراین یک ماشین حالت متناهی که برپایه یک عبارت باقاعده بنا می شود می تواند جهت شناخت آن استفاده شود.

تحلیل نحوی شامل تجزیه کردن نشانه های مرتب جهت شناخت ساختار نحوی زبان می باشد.
تحلیل معنایی فازی است که معنای برنامه را جهت رعایت قوانین زبان بررسی می کند. یک مثال برای این فاز کنترل نوع است.

عقب بندی(Back end):

گاهی مرحله عقب بندی با مرحله تولید کد اشتباه گرفته می شود. اما می توان گفت که عقب بندی به مراحل چند گانه زیر تقسیم می شود:

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

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

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

آخرین ویرایش: 26-05-2023 ???? 09:52، توسط رضا رمضانپور