کامپایلر (Compiler) نرم افزاری برای تبدیل کد منبع (Source Code) به کد شی (Object Code) است. به عبارت دیگر میتوان گفت که کامپایلر کدهای نوشته شده به زبان سطح بالا (نزدیک به زبان انسان) توسط برنامه نویسان را به زبان دودویی ماشین تبدیل میکند. انجام این مرحله از اجرای برنامهها و استفاده از کامپایلر به این دلیل الزامی است که کامپیوترها تنها قادر به اجرای کدهای دودویی هستند و لذا کدهای سطح بالا باید به زبان ماشین ترجمه شوند. اینجا به این سوال پاسخ داده شده است که کامپایلر چیست و سایر نکات و مباحث مهم پیرامون مفهوم کامپایلر در برنامه نویسی شرح داده میشوند.
کامپایلر چیست و چکار می کند ؟
کامپایلر یک برنامه نرم افزاری است که کدهای نوشته شده توسط برنامه نویس را به یک زبان پایه ماشین یعنی همان زبان قابل فهم توسط سخت افزار تبدیل میکند و به این ترتیب کدها را برای کامپیوتر خوانا و قابل اجرا میشوند. به عبارت دیگر، منبع کد سطح بالا که توسط توسعه دهنده با استفاده از زبان برنامه نویسی سطح بالا نوشته شده، توسط کامپایلر به کدهای سطح پایین یا همان «Object Code» تبدیل میشوند تا نتیجه برای پردازنده (سختافزار) قابل درک شود.
برخی از کامپایلرها زبان سطح بالا را در مرحله میانی (Intermediate Step) ابتدا به زبان اسمبلی (Assembly Language) تبدیل میکنند. در حالی که برخی از آنها زبان سطح بالا را به طور مستقیم به کدهای دودویی ماشین تبدیل میکنند. به فرآیند تبدیل کد منبع به کدهای دودویی ماشین «کامپایل کردن» (Compilation) گفته میشود.
به طور رسمی، خروجی کامپایل شده، کد شی یا گاهی اوقات ماژول شی نامیده میشود. کد شی یک کد ماشینی است که پردازنده میتواند هر دستورش را هر بار انجام دهد. کامپایلرها به این دلیل به کد شی برای اجرا نیاز دارند که پردازندهها به روش سنتی عمل میکنند. پردازنده از گِیتهای منطقی (Logic Gate) برای مسیریابی سیگنالها بر روی «صفحه مدار» (Circuit Board) استفاده و سیگنالهای دودویی (Binary) بالا و پایین را برای کار با واحد منطقی ریاضی (Arithmetic Logic Unit | ALU) کامپیوتر تغییر میدهند و تنظیم میکند.
یک برنامه نویس کدهای برنامه را برای پردازنده نمیسازد، بلکه در اکثر موارد برای سادهتر شدن کار، کدها به صورت سطح بالا یعنی نزدیک به زبان انسان نوشته میشوند. کدهای سطح بالا از متغیرها، دستورات، توابع (Function)، فراخوانیها، متُدها (Method) و سایر موارد گوناگون توسط زبان دودویی ماشین برای پردازنده به زبانی قابل درک تبدیل میشوند. همه این موارد باید در فرمی قرار داده شوند که کامپیوتر بتواند آنها را درک کند و اجرای برنامه توسط آن انجام شود. مراحل انجام کار در کامپایلر دارای چهار بخش اصلی به شرح زیر است:
- اسکن کردن (Scanning): اسکنر، کاراکترها یا همان حروف را در زمانی مشخص در کد منبع میخواند و ردیابی میکند که کدام کاراکتر در کدام خط و موقعیت قرار دارد.
- تحلیل واژهای یا لغوی (Lexical Analysis): کامپایلر دنبالهای از کاراکترهای کد منبع را به رشتهای (String) از کاراکترها تبدیل میکند که به عنوان توکن (Token) شناخته میشوند. سپس این توکنها توسط قانونی مشخص با برنامهای به نام «تحلیل لغوی» به یکدیگر مرتبط شدهاند. این برنامه از یک جدول سمبلها برای ذخیره حروف در کد منبع استفاده میکند تا تطابق آنها را با توکنهای ارسالی بررسی کند.
- تحلیل نحوی (Syntactic Analysis): در این مرحله، تحلیل نحوی (سینتکس) انجام میشود. این مرحله شامل پیشپردازش توکنهای ایجاد شده در مرحله تحلیل لغوی برای بررسی مناسب بودن ترتیب توکنها جهت استفاده در کامپایلر است. ترتیب صحیح مجموعه کلمات کلیدی که باعث ایجاد نتیجه دلخواه خواهد شد، سینتکس نامیده میشود. نیاز است که کامپایلر کد منبع را برای اطمینان از صحت سینتکس بررسی کند. این مرحله میتواند به وسیله ایجاد درخت انجام شود.
- تحلیل معنایی (Semantic Analysis): این مرحله شامل چندین بخش میشود (تجزیه درخت ایجاد شده در مرحله قبل) که در ادامه ارائه شدهاند:
- ابتدا ساختمان (Structure) توکن و ترتیب آنها با توجه با دستور زبان مورد نظر بررسی میشود.
- معنای ساختمان توکن توسط تجزیه کننده (Parser) و تحلیلگر تفسیر میشود.
- در نهایت، یک کد میانی (Intermediate Code) به نام کد شی تولید خواهد شد.
کد شی شامل دستورالعملهایی است که عملکرد پردازنده را برای توکن متناظرش در هنگام مواجه با آن در برنامه نشان میدهد. در نهایت، کل کدهای برنامه برای بررسی امکانپذیری بهینهسازی، تجزیه و تفسیر میشوند.
پس از انجام بهینهسازی، توکنهای مناسب و اصلاح شده وارد کد شی میشوند تا کد شی نهایی تولید و در یک فایل ذخیره شود. در بخش بعدی مقاله «کامپایلر چیست و چکار می کند» به ارائه توضیح بیشتری پیرامون مفهوم کامپایلر همراه با مثال برای درک بهتر آن پرداخته شده است.
توضیح کامپایلر به زبان ساده همراه با مثال
بهترین مثالی که برای عملکرد کامپایلر میتوان در نظر گرفت، توضیحات کدهایی است که توسط برنامه نویسان در کدهای برنامه با استفاده از برچسبی خاص نوشته میشوند که کامپیوتر را ملزم به نادیده گرفتن آنها (متن الفبایی) در اجرای کدها میکنند. دلیل این که این توضیحات باید برچسبگذاری شوند تا خوانده نشوند این است که آنها توسط پردازنده (Processor) کامپیوتر قابل خواندن نیستند. پردازشگر نمیتواند کلمات استفاده شده در زبان انسانی و معانی آنها را متوجه شود، بنابراین نمیتواند از توضیحات کدها اطلاعاتی را برای اجرا دریافت کند.
این موضوع تا حد زیادی برای بخشهایی از کدهای برنامه که جزئی از توضیحات نیستند نیز صادق است، برای مثال فراخوانی تابعی مانند «get» برای رایانه معنایی ندارد و امکانپذیر نیست. بنابراین، کامپایلر کدهای برنامه را دریافت و آنها را به کدهای باینری برای ماشین تبدیل میکند تا عملیات منطقی پردازنده انجام شود.
اگرچه کامپایلر در برنامه نویسی مدرن بسیار مهم است، اما تنها گزینه در دسترس برای توسعه نرم افزار نیست. یکی از بهترین روشها برای پاسخ به این سوال در محاسبات مدرن که کامپایلر چیست ، یک روش جایگزین جدیدتر به نام تفسیر (Interpretation) است که نوع متفاوتی از نرم افزار به نام مفسر (Interpreter) را برای قرار دادن کدهای ماشین در زمان اجرا (Runtime) استفاده میکند.
طبق روشهای سنتی کامپایل کردن، کدهای برنامه فقط یک بار قبل از اجرا کامپایل میشوند. اما یک مفسر، کدهای برنامه را بر اساس تقاضای آن برای هر اجرا تفسیر میکند. توضیحاتی که برای کامپایلر ارائه شده است تقریباً ساده و شفاف هستند، مسئلهای که کمی این موضوع را پیچیده میکند این است که کامپایل کردن چطور اتفاق میافتد؟ همچنین باید به این موضوع پرداخته شود که کدام زبان برنامه نویسی کامپایلری (Compiled Language) و کدام زبان مفسری (Interpreted Language) است.
برای مثال، زبان برنامه نویسی «++C» معمولاً به عنوان اصلیترین زبانی شناخته میشود که از کامپایلر استفاده میکند. اگرچه، مفسر «CINT» زبان ++C به صورت ظریفی این موضوع را نقض میکند. یا مثلا زبان برنامه نویسی جاوا اسکریپت (JavaScript) بیشتر به عنوان یک زبان مفسری در نظر گرفته میشود. اما زمانی که بررسی عمیقتری در کدهای جاوا اسکریپ انجام شود، این موضوع روشن خواهد شد که بخشهایی از کدهای این زبان در جهت ارائه توضیح دقیقتری از نحوه عملکرد برنامه، کامپایل میشوند.
کارشناسان و توسعه دهندگان موارد استفاده دستورالعملهای کد بایتی (Bytecode) یا ماشین مجازی (Virtual Machine) را به روشهای مختلفی بررسی و ارائه میکنند که این روشها، «تحلیل لغوی»، «تحلیل نحوی» و «تحلیل معنایی» کامپایل کردن یا «اجرای پویا» در زمان اجرای مفسر را شامل میشوند. ظاهر کامپایلر درجا (Just In Time Compiler | JIT) به عنوان یک ابزار کامپایل زمان اجرا پویا، کامپایل و تفسیر کردن را گنگ و پیچیده میکند.
به طور کلی، مفسر یک برنامه مستقل است و کامپایل کردن روشی قدیمیتر برای تبدیل کدهای برنامه به زبان دودویی ماشین به حساب میآید. البته مانند خیلی از رویکردهای دیگر در دنیای علوم کامپیوتر، کارایی و تکامل باعث ایجاد یک رویکرد جدید ترکیبی از کامپایلرها و مفسرها برای تبدیل کدهای برنامه به کدهای دودویی ماشین شده است. در بخش بعدی مقاله «کامپایلر چیست و چکار می کند» به توضیح مختصری از تاریخچه کامپایلر پرداخته میشود.
تاریخچه کامپایلر چیست ؟
اگر از زمان شروع محاسبات با کارایی بالا یعنی از سال ۱۳۲۸ شمسی (دهه ۱۹۵۰ میلادی) بررسیهایی در زبانهای برنامه نویسی انجام شود، میتوان به این نتیجه رسید که در این مدت با استفاده از چندین زبان برنامه نویسی، کدنویسی انجام شده است که امروزه برخی از آنها از رده خارج شدهاند. در بازه سالهای ۱۳۲۸ تا ۱۳۳۸ شمسی (۱۹۵۰ تا ۱۹۶۰ میلادی) برنامه نویسان با استفاده از زبان برنامه نویسی اسمبلی کدنویسی میکردند. در آن سالها، محدودیت در حافظه سیستم و سرعت بسیار پایین آن، اجرای هر برنامه را بسیار کند میکرد.
در آن زمان سیستمها دارای حافظههای کوچکی بودند، بنابراین کوچک بودن اندازه برنامهها و کدنویسی با زبان اسمبلی کافی بود و نیاز توسعه دهندگان را برطرف میکرد. در اواخر دهه ۳۰ شمسی (دهه ۱۹۶۰ میلادی) برنامه نویسان شروع به نوشتن کدهای خود به وسیله زبانی سطح بالا مانند فرترن (FORTRAN) کردند. نوشتن برنامهها با استفاده از یک زبان سطح بالا، قابلیتهای پروژه از جمله قابلیت حمل و انتقال (Portable)، اعتماد (Reliable) و نگهداری (Maintainable) را افزایش میدهد. با توجه به افزایش ظرفیت و سرعت کامپیوترها، کدنویسی با یک زبان سطح بالا چیزی بود که اکثر برنامه نویسان به آن روی آوردند.
در دهه ۱۳۴۰ شمسی (دهه ۱۹۷۰ میلای)، اگر برنامهای زمان زیادی را برای کارهای خود صرف میکرد، کارها بخشی از سیستم عامل یا یک کتابخانه معمولی بود. همچنین به این معنی در نظر گرفته میشد که این برنامه با استفاده از زبان اسمبلی نوشته شده است. در اواخر دهه ۴۰ شمسی (دهه ۱۹۷۰ میلادی) و اوایل دهه ۵۰ شمسی (دهه ۱۹۸۰ میلادی) بهینه سازی کامپایلرها به حدی پیشرفت کرد که همه به جز حیاتیترین بخشهای برنامههای همه منظوره با استفاده از زبانهای برنامه نویسی سطح بالا نوشته میشدند.
میتوان گفت که برنامه نویسان در آن زمان به این نتیجه رسیدند که کامپایلرها کدهای بهتری نسبت به زبان اسمبلی ایجاد کردند. یکی از دلایل این موضوع این بود که کامپایلرها میتوانستند از منابع سخت افزاری مانند ثَباتها (Register) بهتر استفاده کنند. کامپایلر میتواند هر ثبات را به هر اندازه که نیاز دارد استفاده کند زیرا زمانی که یک ثبات در برنامه دیگری در حال استفاده باشد، کاملاً مشخص است.
با این حال در آن زمان معماری کامپیوترهایی که کارایی بالایی دارند در حال تکامل بودند. شرکت «Cray Research» در حال توسعه پرازشگرهای بُرداری با بالاترین طیف محاسباتی بود. اما کامپایلرها برای استفاده از این ساختارهای برداری آماده نبودند. بنابراین، برنامه نویسان مجبور بودند که کدهای خود را با استفاده از زبان برنامه نویسی اسمبلی یا فرترن تنظیم شده بنویسند تا روالهای برداری مناسب در کدها ایجاد شوند.
یعنی در زمانی که کامپایلرها در حال پیشرفت خوبی بودند، پردازشگرهای برداری باعث پسرفت آنها شدند و زمان را به عقب برگرداندند. برنامه نویسان هیچگاه مجدداً به طور کامل با زبان اسمبلی کدنویسی نکردند، اما تغییراتی که مجبور بودند روی زبان فرترن ایجاد کنند در حد زیادی بود و کدهای فرترن را کاملاً تغییر میداد. زمانی که کامپیوترهای برداری تکامل یافتند، کامپایلرهای آنها نیز در تشخیص بردارها پیشرفت کردند و مجدداً به حدی رسیدند که عملکرد بهتری نسبت به کدهایی مانند اسمبلی داشتند که توسط برنامه نویسان ایجاد میشد. همچنین، این کامپایلرهای جدید نیاز به دستورالعملهای گسترده زبانهای برنامه نویسی را کاهش دادند.
جنبش «کاهش مجموعه دستورات محاسباتی» (Reduced instruction Set Computing | RISC) منجر به افزایش وابستگی برنامه نویسی به کامپایلرها شد. حاصل این جُنبش، ایجاد پردازنده «RISC» بود و برنامه نویسی با پردازندههای اولیه «RISC» مانند «Intel i860» در مقایسه با پردازندههای «CISC» که مخفف «Complex Instruction Set Computing» است، اصلاً خوب نبود. تفاوتهای کوچکی در نحوه کدگذاری یک برنامه در زبان ماشین میتواند تأثیر قابل توجهی بر عملکرد کلی برنامه داشته باشد. همچنین، چون پردازندههای فوق اسکالر (Superscalar) توسعه یافتند، جفتهای خاصی از دستورالعملها میتوانستند به طور همزمان انجام شوند و بقیه دستورالعملها به صورت سریالی و پشت سر هم انجام میشدند.
زیرا، تعداد زیادی از پردازندههای متفاوت «RISC» تولید شدند و برنامه نویسان فرصت یادگیری تفاوتهای همه این پردازندهها را نداشتند. در نهایت میتوان گفت که کامپایلر به ابزاری بسیار مهم در چرخه طراحی پردازندهها تبدیل شده است. طراحان پردازنده، انعطافپذیری بسیار بیشتری برای اعمال انواع تغییرات دارند. در بخش بعدی مقاله «کامپایلر چیست و چکار می کند» به نقاط عطف تاریخچه کامپایلر پرداخته شده است.
نقاط عطف تاریخچه کامپایلر چیست ؟
در این بخش، نقاط عطف مهم در طراحی و ایجاد کامپایلرها به صورت زیر فهرست شدهاند:
- کلمه «کامپایلر» برای اولین بار در دهه ۱۳۳۰ شمسی (۱۹۵۰ میلادی) توسط «Grace Murray Hopper» استفاده شده است.
- اولین کامپایلر بین سالهای ۱۳۳۲ شمسی (۱۹۵۴ میلادی) تا ۱۳۳۵ شمسی (۱۹۵۷ میلادی توسط ) «John Backum» و گروهش ساخته شد.
- زبان برنامه نویسی کوبول (COBOL) اولین زبانی بود که در سال ۱۳۳۸ شمسی (۱۹۶۰ میلادی) بر روی پلتفرمهای چندگانه (Multiple Platform) کامپایل شد.
- مطالعه روی مسائل اسکن کردن و تجزیه و تحلیل در دهههای ۳۰ و ۴۰ شمسی (دهههای ۱۹۶۰ و ۱۹۷۰ میلادی) برای ارائه یک راه حل کامل در ایجاد کامپایلرها ارائه شد.
انواع کامپایلر چیست ؟
در بخشهای پیشین، مفهوم، روش عملکرد و تاریخچه کامپایلر تقریباً به طور جامع مورد بررسی قرار گرفتند، حال در این بخش، انواع گوناگون کامپایلرها معرفی شدهاند.
کامپایلرها به سه دسته اصلی زیر تقسیم میشوند:
- کامپایلر تک گذره (One or Single Pass Compiler)
- کامپایلر دو گذره (Two Pass Compiler)
- کامپایلر چند گذره (Multi pass Compiler)
اکنون به بررسی هر کدام از این انواع کامپایلرها پرداخته میشود. بخش اول به تعریف کامپایلر تک گذره اختصاص داده شده است.
کامپایلر تک گذره
در کامپایلرهای تک گذره، کد منبع به طور مستقیم به کدهای دودویی ماشین تبدیل میشود. برای مثال زبان پاسکال (Pascal) یک زبان برنامه نویسی کامپایلری به حساب میآید.
مراحل انجام کار در کامپایلرهای تک گذره در ادامه شرح داده شده است:
- کامپایلر تک گذره فقط یک بار در پیمایش برنامه استفاده میشود. یعنی، این کامپایلر فقط یک بار از هر بخش برنامه عبور میکند و هر بخش برای کدهای نهایی ماشین ترجمه میشود.
- در این نوع از کامپایلرها، هنگامی که یک خط از کد منبع پردازش میشود، بعد از آن اسکن و توکنها استخراج میشوند.
- سپس، تحلیل نحوی روی کدها انجام و ساختار درخت و برخی جداول شامل دادههای مرتبط با هر توکن ساخته میشوند.
- در نهایت و پس از تحلیل معنایی و بررسی صحت آن، کدهای دودویی ماشین ایجاد خواهند شد.
- این فرآیند روی همه خطهای کد منبع انجام و تکرار میشود تا کامپایل کل برنامه و تبدیل کدهای آن به کد دودویی ماشین انجام شود.
معمولاً همه عملکرد کامپایلر به وسیله تجزیه کننده ساخته میشود. آنها دستورالعملهایی را فراخوانی میکنند که عملکردهای مختلفی را انجام میدهند. این نوع از کامپایلرها دارای محدودیتهایی به شرح زیر هستند:
- هنگام استفاده از کامپایلرهای تک گذره، بهینه سازی به خوبی انجام نمیشود زیرا هنگام کامپایل کردن متن، عبارتهای انتخاب شده محدود هستند.
- دستور زبان (روش تبدیل کدها) در این روش به دلیل این که نمیتوان پشتیبانگیری (Backup) و پردازش انجام داد، باید محدود و ساده باشد.
در بخش بعدی مقاله «کامپایلر چیست و چکار می کند» به بررسی کامپایلرهای دو گذره پرداخته شده است.
کامپایلر دو گذره
کامپایلرهای دو گذره همانطور که از نامش مشخص است از دو مرحله اصلی گذر میکند یعنی به دو بخش (فاز) اصلی به صورت زیر تقسیم میشوند:
- فرانتاند (Front end): این بخش از کامپایلرهای دو گذره، کدهای منطقی را به نمایش میانی (Intermediate Representation | IR) نگاشت میکنند.
- بکاند (Back end): سپس در این بخش از عملکرد این کامپایلرها، نمایش میانی به کدهای هدف ماشین نگاشت میشوند.
روش کامپایلر دو گذره، فرآیند رسیدن به هدف را آسان میکند و همچنین امکان استفاده از فرانتاند چندگانه در آن وجود دارد. در ادامه این بخش، مراحل انجام دستورالعمل در آن و کامپایل شدن کدهای برنامه بررسی میشوند.
اولین گذر در کامپایلر دو گذره
- عملیات فرانتاند کامپایلر
- بخش تجزیه و تحلیل کدها
- این بخش مستقل از پلتفرم (Platform Independent) است.
در اولین گذر کامپایلر دو گذره، فازهای عملیاتی شامل تحلیل لغوی، تحلیل نحوی (تجزیه کننده)، تحلیل معنایی و تولید کدهای میانی انجام میشوند. این چهار بخش به طور کامل در مرحله فرانتاند اتفاق میافتند. در این بخش همه فازهای تجزیه و تحلیل زبان سطح بالا انجام شده است و آنها به سه آدرس برای نمایش کدها تبدیل میشوند.
فاز اول کامپایلر دو گذره، مستقل از پلتفرم است، زیرا خروجی این فاز سه آدرس برای کدها به همراه دارد که برای هر سیستمی مفید خواهد بود. نیازمندیهای این بخش، تغییر بهینهسازی کدها و تولید کدها است که در فاز دوم این نوع کامپایلر انجام میشود.
دومین گذر در کامپایلر دو گذره
-
- عملیات بکاند کامپایلر
- بخش ساخت (Synthesis Part)
- این بخش وابسته به پلتفرم (Platform Dependent) است.
این فاز از کامپایلرهای دو گذره شامل بهینه سازی کدها و تولید کدها میشود. این دو مرحله به عنوان عملیات بکاند کامپایلر انجام میشوند. این بخش ورودی را به صورت سه آدرس برای کدها دریافت و آنها را به زبانهای سطح پایین (Low level language) و اسمبلی تبدیل میکند. این فاز وابسته به پلتفرم است زیرا مرحله نهایی کامپایلرهای معمولی، نمایش میانی برنامه را به مجموعهای از دستورالعملهای قابل اجرا و وابسته به سیستم تبدیل میکنند. بخش بعدی مقاله «کامپایلر چیست و چکار می کند» به بررسی کامپایلرهای چند گذره اختصاص داده میشود.
- مقالههای پیشنهادی:
- بهینه سازی کد (Code Optimization) در طراحی کامپایلر
- تولید کد (Code Generation) در طراحی کامپایلر — راهنمای جامع
کامپایلر چند گذره
کامپایلرهای چند گذره، کد منبع و درخت سینتکس کدهای برنامه را چند بار پردازش میکنند. این نوع کامپایلر یک برنامه بزرگ را به چند برنامه کوچک تقسیم و هر کدام از آنها را به صورت جداگانه پردازش میکند. کامپایلرهای چند گذره، چندین کد میانی توسعه میدهند.
گذرها یا فازهای چندگانه این کامپایلر، خروجی فاز قبل را به عنوان ورودی فاز جدید دریافت میکنند، بنابراین، این روش به حافظه کمتری نیاز دارد. کامپایلر چند گذره با نام «کامپایلرهای گسترده» (Wide Compiler) نیز شناخته میشوند. در ادامه این بخش به بررسی مزایای کامپایلرهای چند گذره پرداخته شده است.
مزایای کامپایلرهای چند گذره
این نوع از کامپایلرها مزایای بسیاری دارند که در این بخش به شرح چند نمونه از آنها پرداخته میشود:
- مستقل بودن از ماشین (Machine Independent): از آنجایی که فازهای چندگانه شامل ساختار ماژولی هستند و همچنین تولید کدها از مراحل دیگر کامپایلر جدا شده است، فازها را میتوان برای سخت افزار یا همان ماشینهای مختلف مورد استفاده مجدد قرار داد.
- زبانهای بیانی بیشتر (Expressive Language): وجود چندین فاز نیاز به اعلامیههای رو به جلو (Forward Declaration) را از بین میبرند و امکان پیادهسازی بازگشت متقابل (Mutual Recursion) را به خوبی به وجود میآورند. مثالهایی برجسته از زبانهای برنامه نویسی که به دلیل نیاز به کامپایل شدن در یک گذر، نیازمند اعلامیههای رو به جلو هستند، شامل زبان «C» و پاسکال میشوند. همچنین برای نمونه، زبان برنامه نویسی جاوا (Java) به اعلامیههای رو به جلو نیاز ندارد.
حال پس از بررسی انواع کامپایلرها بر اساس ساختار آنها، در ادامه مقاله «کامپایلر چیست و چکار می کند» به شرح برخی از انواع دیگر کامپایلرها پرداخته میشود.
انواع دیگر کامپایلرها
کامپایلرها به سه نوع اصلی تک گذره، دو گذره و چند گذره تقسیم میشوند که در بخشهای پیشین به طور کامل مورد بررسی قرار گرفتند. حال در این بخش به چند نوع دیگر از کامپایلرها پرداخته میشود که در این دستهها قرار نمیگیرند. ابتدا در بخش بعدی به بررسی کامپایلرهای متقابل (Cross Compiler) پرداخته میشود.
کامپایلر متقابل چیست؟
کامپایلرهای متقابل، کامپایلرهایی هستند که در یک سیستم پیادهسازی میشوند و کدهای هدف مورد نظر را برای ماشینهای دیگر تولید میکنند. یعنی کامپایلر روی پلتفرم X و کدهای هدف آن روی پلتفرم Y پیادهسازی میشوند. این کامپایلر برای ساخت کدهای اجرایی در پلتفرمی غیر از پلتفرمی که کامپایلر روی آن در حال اجرا است، مورد استفاده قرار میگیرد. برای مثال، کامپایلر «Free Pascal» یک کامپایلر متقابل به حساب میآید. در بخش بعدی مقاله «کامپایلر چیست و چکار می کند» به معرفی کامپایلر بارگیری و رفتن (Load and Go Compiler) پرداخته شده است.
کامپایلر بارگیری و رفتن چیست؟
کامپایلرها معمولاً کدهای مطلقی (Absolute Code) تولید میکنند که بلافاصله پس از پایان کامپایل اجرا میشوند، یا کدهای شی تولید میکنند که توسط یک لینک دهنده بارگیری (Linking Loader) به کدهای مطلق تبدیل میشوند. اما این کامپایلر کدهای ماشین را تولید و بلافاصله آنها را پیادهسازی میکند. این بدان معناست که کامپایل کردن، اسمبل کردن یا پیوند دادن در این نوع از کامپایلرها از مرحله پیادهسازی برنامه جدا نیست. کامپایلرهای «Dartmouth BASIC» و «WATFOR» از این نوع هستند. در بخش بعدی مقاله به بررسی کامپایلر کد نخدار (Threaded Code Compiler) پرداخته شده است.
کامپایلر کد نخدار چیست؟
برنامههای دارای کدهای نخدار یا همان «Thread» به فرمی هستند که اساساً با استفاده از فراخوانی برنامههای فرعی تشکیل شدهاند. کامپایلر کدهای نخدار، رشتههای (String) موجود در کدهای منبع را با کدهای باینری داده شده حین کامپایل جایگزین میکند. در بخش بعدی به معرفی کامپایلرهای درجا پرداخته شده است.
کامپایلر درجا چیست؟
در کامپایلرهای درجا (Just In Time Compiler | JIT)، برنامهها به صورت بایتکد به کامپایلر تحویل داده و درست قبل از پیادهسازی برنامه به کدهای محلی (Native) ماشین تبدیل میشوند. کامپایلر «JIT» به صورت پیشفرض غیر فعال است و زمانی فعال میشود که متد جاوا فراخوانی شده باشد. این کامپایلر بایتکدهای متد را به کدهای محلی ماشین به صورت درجا کامپایل میکند. در کامپایلر درجا، زمانی که متدی کامپایل میشود، ماشین مجازی جاوا (Java Virtual Machine | JVM) کدهای کامپایل شده متد را به طور مستقیم به جای استفاده از مفسر، فراخوانی میکند.
این نوع از کامپایلرها، کامپایلرهای زمان اجرا (Runtime) هستند و از یک زبان برنامه نویسی میانی مانند بایتکد یا «MSIL» به مرحله پیادهسازی کدها یا کدهای ماشین محلی میرسند. این نوع از پیادهسازیها تأیید مبتنی بر نوع (Type Based Verification) را انجام میدهند که باعث ایجاد کدهای معتبرتری میشوند. بخش بعدی مقاله کامپایلر چیست و چکار می کند به معرفی کامپایلر موازیسازی (Parallelizing Compiler) اختصاص دارد.
- مقاله پیشنهادی: محیط Run-Time (زمان اجرا) در طراحی کامپایلر — راهنمای جامع
کامپایلر موازیسازی چیست؟
کامپایلر موازیسازی برنامه ورودی را به فرمی یا شکلی مناسب برای پیادهسازی خوب در معماریهای موازی تبدیل میکند. هدف از موازیسازی خودکار رهایی برنامه نویسان از موازیسازی دستی پیچیده و مستعد خطا است. کامپایلرهای «Rice Fortran D compiler» و «Polaris compiler» نمونههایی از کامپایلرهای موازیسازی هستند. بخش بعدی مقاله به کامپایلرهای افزایشی (Incremental Compiler) اختصاص داده شده است.
کامپایلر افزایشی چیست؟
کامپایلرهای افزایشی، نوعی از کامپایلرها هستند که در زمان ویرایش و تغییر کدهای منبع برنامه، فقط کدهای ویرایش شده را مجدداً کامپایل میکنند و همه کدهای برنامه را کامپایل نمیکنند. این کامپایلر وابستگی بین کدهای برنامه خروجی و کدهای منبع را ردیابی و بررسی میکند. فرآیند کامپایل افزایشی برای نگهداری کدها بسیار مفید و مؤثر است. برخی از نمونههای کامپایلرهای افزایشی «SWI-prolog» و پلتفرم «Eclipse» با کامپایلر افزایشی جاوا به شمار میروند. در بخش بعدی مقاله «کامپایلر چیست و چکار می کند» به بررسی کامپایلرهای سنتی (Traditional Compiler) پرداخته میشود.
کامپایلر سنتی چیست؟
کامپایلرهای سنتی منبع کدهای اصلی برنامه زبانهای سطح بالا را به کدهای متناظر محلی همان برنامه در ماشین یا برنامه شی تبدیل میکنند. برای مثال کامپایلرهای زبانهای ++C ،C و پاسکال از این نوع هستند. در بخش بعدی به بررسی مفسرها پرداخته میشود.
مفسر چیست؟
مفسر با کامپایلر تفاوت دارد اما هر دو برای انجام یک وظیفه مورد استفاده قرار میگیرند. مفسرها ابتدا کدهای منبع اصلی را به کدهای میانی تبدیل و سپس آنها را به کدهای ماشین ترجمه میکنند. زبانهای برنامه نویسی LISP ،SNOBOL و Java1.0 دارای مفسر هستند. در بخش بعدی مقاله به معرفی تبدیلکنندهها (Converter) پرداخته میشود.
تبدیلکننده چیست؟
با استفاده تبدیلکننده، برنامهها از یک زبان سطح بالا به زبانی دیگر کامپایل میشوند. مانند تبدیل زبان برنامه نویسی «COBOL» به زبان «++C» که با استفاده از این تبدلیکنندهها انجام میگیرد. در بخش بعدی مقاله «کامپایلر چیست و چکار می کند»، کامپایلر جلوتر از زمان (Ahead of Time Compiler | AOT) معرفی و بررسی میشود.
https://blog.faradars.org/wp-admin/post.php?post=983927&action=edit
کامپایلر جلوتر از زمان چیست؟
کامپایلرهای جلوتر از زمان، پیشکامپایلرهای کدهای محلی جاوا و دات نت (NET.) به حساب میآیند. این کامپایلرها دارای زمان اجرای کمتری نسبت به دیگر کامپایلرها هستند. در بخش بعدی کامپایلرهای باینری (دودویی) معرفی شدهاند.
کامپایلر باینری چیست؟
کامپایلرهای باینری کد شی از یک پلتفرم را به کد شی از پلتفرمی دیگر تبدیل میکنند. پس از بررسی انواع کامپایلرها در این بخش، اکنون در بخش بعدی مقاله «کامپایلر چیست و چکار می کند» توضیح مختصری از مراحل سیستمهای پردازش زبان ارائه میشود.
مراحل سیستمهای پردازش زبان
در این بخش برای بررسی نحوه کارکرد کامپایلرها در برنامه، به بررسی چند مرحلهای پرداخته میشود که عملکرد آنها در کنار کامپایلر مهم و حیاتی هستند.
ابتدا این مراحل در تصویر زیر با رعایت اولویت نشان داده شدهاند و سپس هر کدام از بخشها تعریف و بررسی میشوند.
- پیشپردازنده (Preprocessor): پیشپردازنده به عنوان بخشی از کامپایلر در نظر گرفته میشود. کاربرد این ابزار تولید ورودی برای کامپایلرها است. پیشپردازندهها با پردازش ماکرو (کلان | Macro)، تقویت (Augmentation)، گسترش زبان (Language Extension) و سایر موارد سر و کار دارند.
- مفسر (Interpreter): وظیفه مفسرها و کامپایلرها یکسان است و زبانهای سطح بالایی که کدهای آنها توسط برنامه نویسان نوشته میشوند را به کدهای زبانهای سطح پایین برای ماشین تبدیل میکنند. تفاوت اصلی بین مفسر و کامپایلر این مسئله است که مفسر کدها را خط به خط میخواند و به کدهای دودویی ماشین تبدیل میکند اما کامپایلر همه کدها را باهم میخواند و سپس کدهای دودویی ماشین را ایجاد میکند.
- اسمبلر (Assembler): اسمبلر کدهای زبان اسمبلی را به زبان قابل فهم برای ماشین یا سیستم تبدیل میکند. نتیجه خروجی اسمبلر به عنوان یک فایل شی شناخته میشود که ترکیبی از دستورالعملهای ماشین و همچنین دادههای مورد نیاز برای ذخیره این دستورالعملها در حافظه است.
- پیوند دهنده (Linker): پیوند دهنده به برنامه نویس کمک میکند تا فایلهای شی گوناگون را با هم ادغام کنند و پیوند دهند تا یک فایل اجرایی ایجاد شود. هر کدام از این فایلها ممکن است با ابزارهای جداگانه کامپایل شده باشند. وظیفه اصلی پیوند دهندهها جستجوی ماژولهای فراخوانی شده در برنامه و یافتن مکان حافظهای است که همه ماژولها میتوانند در آن ذخیره شوند.
- بارگذار (Loader): بارگیرنده یا همان لودر بخشی از سیستم عامل به حساب میآید که وظیفه بارگذاری فایلهای اجرایی در حافظه و اجرای آن فایلها را به عهده دارد. همچنین اندازه برنامههایی که فضای حافظه اضافی ایجاد میکنند را محاسبه میکند.
- کامپایلر متقابل: یک کامپایلر متقابل در طراحی کامپایلرها، پلتفرمی است که به برنامه نویس کمک میکند تا کدهای اجرایی تولید کند.
- کامپایلر منبع به منبع (Source to Source Compiler): کامپایلر منبع به منبع یک اصطلاح است و زمانی استفاده میشود که کدهای منبع یک زبان برنامه نویسی به کدهای منبع زبان برنامه نویسی دیگر تبدیل میشود.
بخش بعدی مقاله «کامپایلر چیست و چکار می کند» به ویژگیهایی از کامپایلرها اختصاص داده شده است.
ویژگیهای کامپایلر چیست ؟
کامپایلر یک ابزار مهم در برنامه نویسی کامپیوتر به حساب میآید و ویژگیهای گوناگونی دارد که در ادامه این بخش به آنها پرداخته شده است:
- کامپایلر تعداد زیادی از کدها را در زمان کمی کامپایل میکند.
- کامپایلر سرعت بالایی در هنگام کامپایل کردن کدها دارد.
- معنای صحیح کدها را حفظ میکند.
- در صورتی که در کد منبع تغییراتی ایجاد شود، تنها بخشی که تغییر در آن اعمال شده را کامپایل میکند و مجدداً همه برنامه را کامپایل نمیکند.
- زمانی که سخت افزار سیستم قطع شود، برای کامپایلرها مشکلی پیش نمیآید چون کامپایلرهای خوب با سیستم عامل در ارتباط هستند.
- کامپایلر خطاهای برنامه را به خوبی گزارش و مدیریت میکند.
در بخش بعدی این مقاله به معرفی چند نمونه از مزایای کامپایلر پرداخته میشود.
مزایای کامپایلر چیست ؟
در این بخش فهرستی از مزایای کامپایلرها در برنامه نویسی کامپیوتر ارائه شده است:
- کامپایلر کدهای برنامه مورد نظر را برای اجرا، تنها به یک تک اجرا (Single Run) تبدیل میکند.
- کامپایل کردن توسط کامپایلرها زمان کمی صرف میکنند.
- کامپایلرها بیشتر از پردازنده CPU استفاده میکنند.
- خطاهای مرتبط به تحلیل معنایی و تحلیل نحوی در کامپایلرها میتوانند به صورت همزمان بررسی شوند.
- به راحتی در زبانهای برنامه نویسی سطح بالا مانند C ،JAVA و ++C از کامپایلرها پشتیبانی میشود.
بخش بعدی مقاله «کامپایلر چیست و چکار می کند» به بررسی معایب کامپایلر اختصاص داده میشود.
معایب کامپایلر چیست ؟
کامپایلر در کنار مزایا و ویژگیهای بسیار خوبی که دارد، دارای چند عیب نیز است که در این بخش به بررسی این معایب در کامپایلر برنامه نویسی کامپیوتر پرداخته میشود.
- کامپایلر انعطافپذیر نیست.
- در کامپایلرها بومیسازی (Localization) خطا دشوار است.
- کد منبع در هر تغییری نیاز به کامپایل شدن دارد.
- کامپایلر برای اجرای سریع، نیاز دارد که کدهای ماشین صحیحی را ایجاد کند.
- ابزار کامپایلر قابل حمل و انتقال نیست.
- کامپایلر نیاز است که پیامهای تشخیصی و خطا را اعلام کند.
- ابزار کامپایلر باید بهینهسازی ثابتی داشته باشد.
بدین ترتیب در مقاله «کامپایلر چیست و چکار می کند» سعی شد به سادهترین شکل از پایهترین مفهوم به این سوال که کامپایلر چیست پاسخ داده و روش کار آن بررسی شود. در این بخش انتهایی از مقاله، برای درک بیشتر کامپایلرها، چند نمونه از فیلمهای آموزشی تم آف که حاوی آموزشهایی در رابطه با کامپایلر هستند برای علاقهمندان معرفی شدهاند.
جمعبندی
در مقاله «کامپایلر چیست و چکار می کند» ابزار نرم افزاری کامپایلر به طور کامل معرفی و از جهات گوناگون مورد بررسی قرار گرفت. در این مقاله سعی شد پس از تعریف و معرفی کامپایلرها به بررسی روش عملکرد آن و همچنین مراحل گوناگون کارکرد آن پرداخته شود. در اواسط مقاله، انواع گوناگون کامپایلرها در دو دستهبندی کلی و فرعی بررسی شدند.
همچنین مزایا و معایب کامپایلرها نیز مورد بررسی قرار گرفتند. در این مقاله این موضوع مهم ارائه شد که کامپایلرها بخش مهمی از برنامه نویسی را تشکیل میدهند و درک آنها مسئلهای ضروری برای برنامه نویسان و توسعه دهندگان به حساب میآید.