شما از کامپایلرها ( Compilers ) چه می دانید؟
|
|||||||
اگر تا به حال برنامه نویسی کرده باشید شاید به این فکر کرده باشید که نشانه ها و اصطلاحات و کلماتی که شما در برنامه استفاده نموده اید چگونه مورد فهم سیستم عامل و یا نرم افزار نهایی و یا کلا سیستم قرار می گیرد ؟
اگر بپذیرید که کامپیوتر تنها قادر به درک مفهوم سیگنال های پذیرش و عدم پذیرش و یا همان سیگنال ها و اعداد صفر و یک است می توانید راحت تر به جواب برسید درواقع سیستم کامپیوتر شامل مدارهایی است که این مدارها فقط به دو سیگنال صفر و یک و یا فعال و غیر فعال و یا روشن و خاموش حساس است و به هیچ وجه قادر به درک الفاظ و زبان طبیعی نمی باشد و حتی از کاری که قرار است انجام بدهد نیز خبر ندارد و مدارهای الکتریکی بر اساس کدهایی که در حافظه قرار می گیرد ( کلمات حافظه ) و در نهایت پردازش هایی که توسط پردازنده در واحد کنترل و ALU بر روی آن ها صورت می دهد اعمالی انجام می شود . اما ان چه که در این جا مورد توجه است همان شکل گیری صفر و یک ها در نتیجه یک برنامه به زبان فرضا C# می باشد . این کاری است که کامپایلرها انجام می دهند .
مکانیسم کلی کار کامپایلرها به این صورت است که برنامه مبدا را خوانده و یک شکل میانی از آن ایجاد نموده و سرانجام آن را به زبان دیگری مانند اسمبلی تبدیل می کند و زبان اسمبلی نیز از شکل میانی برنامه شکل قابل فهم سیستم و یا همان صفر و یک ها را ایجاد و آن ها را در قالب Memory Word برای سیستم و سخت افزار مهیا می نماید . لذا تبدیل شکل ابتدایی برنامه مقصد به یک شکل اجرایی سیستمی از وظایف کامپایلر ها می باشد . البته باید توجه کنیم که کامپایلرها بر اساس قواعد و گرامر زبان مبدا اقدام به تولید زبان مقصد می نمایند .
کامپایلر نویسان برای سهولت در طراحی ، اجزای کامپایلر را به بخش های زیر تقسیم بندی می کنند که هر یک عملی را انجام می دهد :
الف) تحلیل گر لغوی ( Lexer ) : در واقع طولانی ترین پروسه را انجام می دهد ، با زبان مبدا مستثقیما در تعامل بوده و مستقل از زبان مقصد می باشد . تحلیل گر لغوی با خواندن زبان ورودی ان را به مجموعه ای از نشانه های قابل فهم برای تجزیه کننده تقسیم بندی می کند . میدانیم که جملات یک زبان از رشته هایی از نشانه ها تشکیل شده است و دنباله ای از این کاراکترهای ورودی که یک نشانه را تشکیل می دهند یک لغت ( Lexeme ) نامیده می شوند .
توضیحات ، فضاهای سفید و فاصله ها توسط تحلیل گر لغوی نادیده گرفته می شود ، سپس نشانه های تولیدی را در جدولی به نام جدول نمادها قرار می دهد و اشاره گری برای دسترسی پارسر به آن ها را بر می گرداند . تشخیص شناسه ها و کلمات کلیدی نیز از جمله مواردی است که Lexer باید آن ها را نیز تشخیص دهد و از سایر نشانه ها تمیز دهد .
بنابراین به طور خلاصه Lexer کاراکترها را از ورودی می خواند ، آن ها را به صورت لغت دسته بندی می کند و نشانه های ایجاد شده توسط لغت ها را به همراه مقادیر خصیصه آن ها به مرحله های بعدی کامپایلر منتقل می کند.نشانه های تولید شده در پارسر و یا تجزیه کننده مورد استفاده قرار می گیرد .
ب) تحلیل گر ساختار دستور (Parser) : پارسر بر رسی می کند که ایا می توان دنباله ای از نشانه های ایجاد شده را توسط گرامر زبان مورد نظر تولید نمود یا نه . در واقع پارسر مهم ترین عمل را در طراحی کامپایلر انجام می دهد و آن تولید دنباله از از رشته ها توسط گرامر زبان مبدا می باشد . به طور کل هنگام تحلیل ساختار دستور ، نشانه هایی که در زبان مبدا قرار دارند را به عبارت های گرامری دسته بندی می کنیم به طوری که کامپایلر بتواند مجددا با استفاده از آن ها خروجی را ترکیب بندی کند . عبارت ها
ی تولید شده را توسط درخت تجزیه نمایش می دهند که در ختی است که فرزندان هر گره عبارات سمت راست قوانین گرامر و پدر آن ها سمت چپ هر گره می باشد و گره ه های برگ مشتمل بر پایانی ها می باشند و یک دنباله از آن ها یک رشته از گرامر را نشان می دهند .
ج ) تحلیل گر معنایی (Syntax Analyzer ) : فاز تحلیل معنایی برنامه مبدا را برای پیدا کردن خطاهای معنایی بررسی کرده و اطلاعات مربوط به نوع داده ها را در درخت تجزیه حاشیه نویسی می کند مثلا بررسی می کند که یک رشته حرفی با یک عدد جمع نزده شده باشد و مانند آن . مجاز بودن نوع داده ها نیز در این بخش بررسی می شود . در واقع در زمان تحلیل معنایی ، کامپایلر ساختارهایی را کشف می کند که از نظر ساختار دستوری صحیح هستند اما در رابطه با عملی که انجام می دهند بی معنی هستند .
د) تولید کننده کد میانی : د راین بخش یک شکل میانی قابل فهم اسمبلر و یا یک نمایش میانی صریح از برنامه مبدا تولید می کند که می توان آن را برنامه ای برای یک ماشین انتزاعی در نظر گرفت ( مفهوم انتزاعی به معنای قابل فهم بودن برای انسان است نه ماشین . ) و باید دارای دو ویژگی سهولت تولید و سهولت تبدیل به برنامه مقصد باشد .
نمایش میانی می تواند شکل های گوناگونی داشته باشد مانند صفر ، یک ، دو ، سه و چهار آدرسه .در ساختار فرضا سه ادرسه در هر ثبات می توان سه ادرس را شامل عملگر ، عملوند اول و عملوند دوم قرار داد . محاسبه عبارات ، نظارت بر ساختارهای جریان کنترلی و احضار رویه ها نیز در این بخش توسط مولد کد میانی صورت می گیرد .
هـ) بهینه ساز کد(Optimizer) : کدی که تولید می کنیم باید دو شرط صرفه جویی در حافظه و در زمان اجرا را برآورده کند . گاهی وقت ها کد ما بسیار پیچیده است و با اعمال جایگزینی ها و حذف و درج ها می توان آن را ساده تر و کاراتر نمود . تغییر ساختارهای آدرس دهی نیز می تواند کد را بهینه تر سازد . سرعت اجرا نیز باید در نظر گرفته شود .
و) تولید کد : آخرین فاز کامپایلر ، تولید کد مقصد می باشد که معمولا شامل کد ماشی جابه جا پذیر یا کد اسمبلی است . تخصیص حافظه به هر یک از متغیرهای برنامه در این فاز انجام می شود . آن گاه هر یک از دستورهای میانی به یک دنباله از دستورهای ماشین که همان کار را انجام میدهند ترجمه می شوند . یک جنبه مهم آن ، جایگزینی متغیرها در ثبات ها می باشد .
یک نمونه : عبارت Statement :=initial + rate * ۶۰ را در نظر بگیرید . داریم :
Statement: =initial + rate * ۶۰àid۱:=id۲ + id۳ *۶۰
حال کد نهایی را می توان به فرم زیر در نظر گرفت که سرانجام در اسمبلر تبدیل به کد قابل فهم ماشین خواهد شد :
MOVE id۳, R۲
Mul #۶۰.۰, R۲
MOVE id۲, R۱
MOVE R۲, R۱
MOVE R۱, id۱
منبع: itc.itmavara.com