حسین کعبی: وقتی فیگو را در جام جهانی زدم....
با توجه به زبان برنامهنویسی و طریقه عملکرد برنامه مبدل، نامهای مختلفی برای آنها در نظر گرفته شده است.
همانطور که پیش از این نیز گفتیم، اگر زبان برنامهنویسی، اسمبلی باشد، نرمافزاری که برای تبدیل متن برنامه به زبان ماشین به کار میرود، اسمبلر نام دارد. اسمبلر از ابتدای برنامه شروع به خواندن میکند و پس از خواندن یک خط و تشخیص دستور مورد نظر، آن را به یک دستور زبان ماشین تبدیل میکند. هر خط و دستور زبان اسمبلی، دقیقا معادل یک دستور زبان ماشین است. البته ممکن است اسمبلر برای تبدیل کل برنامه به نمونه معادل زبان ماشین مجبور باشد دو بار برنامه را از ابتدا تا انتها مرور کند. معمولا در دستورهای پرشی، چون مشخص نیست که برنامه دقیقا باید به چه آدرسی پرش کند، اسمبلر جای آن دستور را خالی میگذارد و بعد از یک بار مرور کامل متن برنامه و استخراج و محاسبه همه آدرسها، در دور دوم، دستوراتی را که در دور اول از آنها صرفنظر کرده بود، در جای خود قرار میدهد. به این ترتیب فایلی به نام object code با پسوند obj .* تولید میکند. البته این فایل تنها در صورتی ساخته میشود که اسمبلر طی مرور برنامه با هیچگونه خطایی مواجه نشده باشد. فایل حاصل به وسیله نرمافزار دیگری به نام linker مستقیما به فایل اجرایی تبدیل میشود.
مفسر و کامپایلر
مفسر یا interpreter در اصطلاح رایانه به نرمافزاری گفته میشود که برنامه نوشته شده به یک زبان برنامهنویسی را خط به خط خوانده و در همان لحظه اجرا میکند. مترجم یا کامپایلر (compiler) نیز نرمافزاری است که کل متن برنامه را خوانده و پس از ترجمه یک فایل اجرایی تولید میکند. تفاوت اصلی مفسر و کامپایلر نیز در این است که مفسر هر خط را که خواند، همان لحظه اجرا میکند، اما کامپایلر پیش از اجرای کل برنامه باید ابتدا تمام متن را خوانده و ترجمه کند و پس از انجام موفقیتآمیز این عمل فایل اجرایی را تولید کند. البته مفسر و کامپایلر دو مفهوم کاملا جدا از هم و با مرز مشخص نیستند زیرا بسیاری از کامپایلرها و مفسرها، در برخی امور مانند دیگری عمل میکنند.
یک مفسر میتواند متن برنامه را بدون ساخت فایل اجرایی مستقیما اجرا کند. برخی از مفسرها هم متن برنامه ر ا به یک کد میانی بهینه تبدیل میکنند و بلافاصله آن را اجرا میکنند. زبانهایی چون MATLAB, Python, Perl و Ruby از جمله زبانهایی هستند که به روش دوم عمل میکنند.
برخی از مفسرها هم کد میانی ساخته شده از کل برنامه را در قالب یک فایل به کاربر تحویل میدهند تا از آن پس آن را اجرا کند. زبانهایی مثل جاوا از این روش استفاده میکنند. در این روش، یک کامپایلر نیز برای ساخت کد میانی به مفسر کمک میکند و در واقع در این روش کامپایلر بخشی از سیستم مفسر به شمار میرود.
باید توجه داشت که کد میانی، به زبان ماشین نیست و یک کد قراردادی برای همان مفسر است که برخلاف object code، کاملا مستقل از ماشین است. به عنوان مثال کدهای میانی تولید شده توسط جاوا میتوانند روی هر ماشین و سیستمعاملی اجرا شوند. با این شرط که از مفسر مناسب و مخصوص آن ماشین استفاده شود. اما کد کاملا مستقل از ماشین بوده و میتواند بدون کوچکترین تغییری به سیستم دیگر منتقل شود. برخی از زبانها مثل زبان Smalltalk ممکن است دو روش اخیر را با هم ترکیب کنند.
اصطلاح «زبان مفسری» یا «زبان کامپایلر» صرفا به این معنی است که استاندارد پیادهسازی آن زبان براساس مفسر یا کامپایلر است. وگرنه یک زبان سطح بالا اساسا یک مفهوم است که مستقل از طریقه پیادهسازی آن است.
کارایی
اشکال اصلی مفسرها در مقایسه با کامپایلرها به سرعت اجرای آنها مربوط میشود. برنامهای که کامپایل شده است به فایل اجرایی exe و کد ماشین تبدیل شده است. یک بار برنامه به طور کامل کامپایل میشود و به هر تعداد دلخواه اجرا میشود. در این حالت برنامه حداکثر سرعت اجرا را خواهد داشت. در مقابل این کار، مفسرها هر بار برنامه را باید از روی متن اصلی اجرا کنند و یا در حالات دیگر از کد میانی استفاده کنند که با اینکه سرعت عمل در حالت دوم بیشتر است، اما در هر دو حالت سرعت اجرا از برنامه کامپایل شده کمتر خواهد بود. تفاوت سرعت اجرا در برنامههای کوچک بین مفسر و کامپایلر چندان محسوس و چشمگیر نیست. اما در برخی برنامههای پیچیدهتر، این تفاوت به وضوح حس میشود. البته زمان کامپایل کردن یک برنامه به طور کامل از زمان تفسیر آن بیشتر خواهد بود. اما باید توجه داشت که عمل کامپایل تنها یک بار انجام میشود و به هر تعداد دفعه اجرا میشود. در صورتی که عمل تفسیر باید برای هر بار اجرا انجام شود. ضمنا در اشکالزدایی برنامه، چرخه ویرایش تفسیر اشکالزدایی زمان بسیار کمتری از چرخه ویرایش کامپایل اجرا اشکالزدایی میگیرد.
در مفسرها، دسترسی به متغیرها نیز کندتر از کامپایلرهاست. زیرا نگاشت متغیرها به محل ذخیرهسازی هر بار باید به طور مکرر در زمان اجرا انجام شوند که در کامپایلر این کار تنها یک با انجام میشود و آن هم در ابتدای اجرای برنامه است. همه اینها یک هزینه زمانی به مفسر تحمیل میکنند که به آن «سربار زمانی» گفته میشود.
سیستم برخی زبانها مانندLisp طوری است که میتوان هم از امکان کامپایل و هم تفسیر استفاده کرد. به عنوان مثال، زمانی که یک تابع به طور کامل توسط مفسر پیادهسازی و اشکالزدایی شد، میتوان آن را کامپایل کرد تا از امتیاز سرعت اجرای آن به هنگام پیادهسازی سایر بخشهای برنامه بهره برد.
مفسرBytecode
برخی زبانها مانندEmacs lisp کامپایلری دارند که این کامپایلر، برنامه را به کدهای زبان ماشین تبدیل نمیکند. بلکه به کدی موسوم به bytecode تبدیل میکند که بسیار فشرده و بهینهسازی شده متن اصلی برنامه است. این کد نیز مستقل از ماشین است.
این کد کامپایل شده، در استفادههای بعدی میتواند توسط مفسر bytecode تفسیر شود. مفسر bytecode، نوعی مفسر است که خود با استفاده از زبان c نوشته شده است.
این مفسر به جای آنکه کد اصلی برنامه را تفسیر کند، وظیفه تفسیرهای bytecode را به عهده دارد.
مواجه شدن با خطا
هنگامی که از یک زبان کامپایلری استفاده میکنیم باید تمام برنامه از نظر گرامری درست باشد تا کامپایلر بدون اعلام خطا، فایل اجرایی را تولید کند و ما بتوانیم برنامه را اجرا کنیم. حتی اگر یک خطا در برنامه وجود داشته باشد، کامپایلر فایل نهایی را نخواهد ساخت تا برنامهنویس آن رفع کند. اما در مفسر، اینگونه نیست. برنامه از ابتدا آغاز به اجرا میشود. خط به خط پیش میرود و نتیجه را تولید میکند. هر زمان که با خطایی برخورد کرد، همان جا متوقف شده و اعلام خطا میکند.
کامپایلر همه بخشهای برنامه و درون همه ساختارها را بررسی میکند. اما مفسر تنها خطی که باید اجرا شود را بررسی میکند و اجرا میکند و ممکن است شرایط طوری شود که یک خط از برنامه هیچگاه اجرا نشود. حال اگر آن خط اشکال بزرگی هم داشته باشد، مفسر هیچگاه آن اشکال را ندیده و برنامه هیچگاه با خطا مواجه نمیشود.
اجرای گام به گام
هنگامی که برنامه یک خطای منطقی دارد به درستی کامپایل میشود و فایل اجرایی تولید میشود اما آنطور که انتظار میرود عمل نمیکند. برای یافتن این خطا باید برنامه را خط به خط بررسی کرد و نتایج مقطعی برنامه را ثبت کرد تا محل وقوع خطا کشف شود. معمولاIDE ها ابزاری به نام trace دارند که این امکان را به برنامهنویس میدهند که برنامه را خط به خط اجرا کرده و محتوای متغیرها را زیر نظر بگیرد. برنامههای مفسری که مشکلی در اجرای خط به خط برنامه ندارند، فقط کافی است که بعد از اجرای هر خط مکث کنند و برای اجرای خط بعد منتظر فرمان شما باشند. اما سوالی که مطرح است آن است که این کار در زبانهای کامپایلری چگونه انجام میشود؟ پاسخ آن است که برای فراهم آوردن چنین امکانی، کامپایلر باید یک بار به طور کامل برنامه را کامپایل کند و فایل اجرایی را نیز تولید کند. حال با امکان در نظر گرفته شده میتوان در هر مرحله از اجرای برنامه که مربوط به یک خط از متن اصلی است وقفهای ایجاد میشود تا برنامهنویس بتواند شرایط لحظهای را برای پی بردن به محل خطا کشف کند.
پارسا ستودهنیا
حسین کعبی: وقتی فیگو را در جام جهانی زدم....