کامپایلر چیست؟
با عرض سلام خدمت کاربران سایت پی وی لرن . ساده ترین تعریف یک کامپایلر یک برنامه است که کد را در یک زبان برنامه نویسی high-level (مانند جاوا اسکریپت یا جاوا) به low-level (مانند Assembly) ترجمه می کند که به طور مستقیم توسط کامپیوتر یا برنامه دیگری مانند یک ماشین مجازی اجرا می شود.
به عنوان مثال، کامپایلر جاوا JVM (ماشین مجازی جاوا) کد Java را به Java Bytecode تبدیل می کند. مثالهای دیگر V8، موتور جاوا اسکریپت از گوگل است که کد جاوا اسکریپت را به کد ماشین یا GCC تبدیل می کند که می تواند کد نوشته شده در زبان های برنامه نویسی مانند C، C ++، Objective-C، و غیره را به کد ماشین تبدیل کند.
چه چیزی در جعبه سیاه است؟
تا کنون ما یک کامپایلر را به عنوان یک جعبه سیاه جادویی می دانستیم که حاوی حروف الفبا برای تبدیل کد high-level به کد low-level است. بیایید آن جعبه را باز کنیم و ببینیم چه چیزی در داخل است.
یک کامپایلر را می توان به ۲ قسمت تقسیم کرد.
اولین قسمت که معمولاجلوی انتهای صفحه، کد منبع ارسال شده برای خطاهای syntax، چک ها (و در صورت لزوم) نوع هر متغیر اعلام شده را اسکن می کند نامیده می شود و تضمین می کند که هر متغیر قبل از استفاده شناسایی شده است.اگر خطایی وجود داشته باشد، پیام های خطای اطلاع رسانی را برای کاربر فراهم می کند.همچنین یک ساختار داده ای به نام symbol table نشان می دهد که حاوی اطلاعات در مورد تمام نمادهای موجود در کد منبع است. در نهایت، اگر هیچ خطایی شناسایی نشود، ساختار داده های دیگر، نمایش متوسط از کد، از کد منبع ساخته شده و به عنوان ورودی به بخش دوم منتقل می شود.
بخش دوم، از نمایش حد متوسط و جدول نماد ساخته شده برای تولید کد low-level استفاده می کند.هر دو قسمت جلویی و انتهای جلویی عملیات خود را در یک مرحله از مراحل انجام می دهند. هر فاز یک ساختار داده خاص را از ساختار داده های دیگر منتشر شده توسط فاز قبل از آن ایجاد می کند.فازهای جلویی عموما عبارتند از تجزیه و تحلیل واژگان، تجزیه و تحلیل نحو، تجزیه و تحلیل معنایی و تولید کد کوتاه، در حالی که انتهای عقب شامل بهینه سازی و تولید کد می شود.
تجزیه و تحلیل Lexica
مرحله اول کامپایلر، تحلیل لفظی است. در این مرحله، کامپایلر کد منبع ارسال شده را در عناصر معنی دار به نام lexemes شکست می دهد و یک توالی از نشانه ها را از lexemes تولید می کند.یک lexeme را می توان به عنوان رشته ای قابل شناسایی منحصر به فرد در زبان برنامه نویسی منبع دانست، به عنوان مثال، کلمات کلیدی مانند: if,while یا func شناسه ها، رشته ها، اعداد، اپراتورها یا کاراکترهای تک مانند: (, ), . یا :.
یک نشانه شی توصیف یک lexeme است. همراه با مقدار lexeme (رشته ای قابل شناسایی lexeme)، حاوی اطلاعاتی مانند نوع آن (آیا کلمه کلیدی است؟ یک شناسه اپراتور؟ …) و موقعیت ( خط و / یا شماره ستون) در کد منبع که در آن ظاهر می شود.
اگر کامپایلر یک رشته از کاراکتر ها را که برای آن یک نشانه ایجاد نمی کند، روبرو شود، اجرای آن را با پرتاب یک خطا متوقف می کند. برای مثال، اگر یک رشته یا عدد ناقص یا یک شناسه نامعتبر (مانند یک علامت غیر ASCII در جاوا) روبرو شود.
تجزیه و تحلیل syntax
در طی تحلیل تجزیه و تحلیل، کامپایلر از دنباله ای از نشانه های تولید شده در طی تجزیه و تحلیل واژگان استفاده می کند تا یک ساختار داده ای مشابه درخت به نام ، AST را برای چکیده کوتاه Syntax تولید کند. AST نشان دهنده ساختار syntax و منطقی برنامه است.
تجزیه و تحلیل syntax نیز مرحله ای است که خطاهای syntax نهایی شناسایی و به کاربر در قالب پیام های اطلاع رسانی می شود. به عنوان مثال، در مثال بالا، اگرنماد } فراموش کنیم بعد از تعریف تابع مجموع، کامپایلر باید یک خطا را نشان دهد که یک missingوجود دارد و خطا باید به خط و ستون که در آن} گم شده برود.
اگر هیچ خطایی در این مرحله یافت نشد، کامپایلر به مرحله تجزیه و تحلیل Semantic حرکت می کند.
تجزیه و تحلیل Semantic
در طی تجزیه و تحلیل Semantic، کامپایلر از AST تولید شده در تجزیه و تحلیل نحوی برای بررسی اینکه آیا برنامه با تمام قواعد زبان برنامه نویسی سازگار است، استفاده می کند. تجزیه و تحلیل معنایی شامل می شود.
اگر زبان برنامه نویسی از نتیجه نوع پشتیبانی کند، کامپایلر سعی خواهد کرد تا نوع تمام عبارات untyped در برنامه را بیابد. اگر یک نوع با موفقیت نتیجه گیری شود، کامپایلر گره متناظر را در AST با اطلاعات نوعی تعریف می کند.
کامپایلر بررسی می کند که تمام مقادیر اختصاص داده شده به متغیرها و تمام استدلال های درگیر در یک عملیات دارای نوع صحیح هستند. به عنوان مثال، کامپایلر اطمینان حاصل می کند که هیچ نوع متغیری از نوع String دو مقدار خاصی را تعیین نمی کند و یا یک مقدار از نوع Bool به یک تابع پذیرفته یک پارامتر از نوع Double یا دوباره نمی رسد که ما سعی در تقسیم یک رشته نداریم توسط Int،”Hello” / 2 (مگر اینکه تعریف زبان آن را اجازه می دهد).
همراه با انجام نوع استنتاج و چک تایم، کامپایلر یک ساختار داده ای به نام جدول نماد حاوی اطلاعات در مورد تمام نمادها (یا نام ها) که در برنامه دیده می شود را نگه می دارد. کامپایلر از جدول نماد برای پاسخ به سوالات مانند این است که آیا این متغیر قبل از استفاده اعلام شده است، آیا دو متغیر با یک نام در یک دامنه وجود دارد؟ نوع این متغیر چیست؟ آیا این متغیر در دامنه فعلی موجود است؟ و خیلی بیشتر.
خروجی فاز تجزیه و تحلیل معنایی یک AST و جدول نماد است.
تولید کد واسط
پس از مرحله تجزیه و تحلیل معنایی، کامپایلر از AST استفاده می کند تا یک کد واسط و ماشین مستقل تولید کند. یکی از این موارد کد سه بعدی است.
کد سه-آدرس (۳AC)، در ساده ترین شکل، یک زبان است که در آن دستور یک تخصیص است و حداکثر ۳ operands دارد.
اکثر دستورالعمل ها در ۳AC از فرم هستندa := b <operator> c or a := b.
در تصویر بالا، یک کد ۳AC تولید شده از یک AST حاشیه ای ایجاد شده در طی تدوین تابع را نشان می دهد
func sum(n: Int): Int = {
n * (n + 1) / 2
}
تولید کد واسط فاز پایان جلویی کامپایلر را به پایان می رساند.
بهينه سازي
در مرحله بهینه سازی، فاز اول انتهای عقب، کامپایلر با استفاده از تکنیک های بهینه سازی مختلف برای بهبود کد کوتاه با ایجاد کد سریع یا کوچکتر تولید شده به عنوان مثال، بهینه سازی بسیار ساده در کد ۳AC در مثال قبلی، حذف تخصیص موقت t3: = t2 / 2 است و به طور مستقیم id2به مقدار t2 / 2 اختصاص می دهد.
تولید کد
در این مرحله آخر، کامپایلر کد میانگین بهینه شده را به کد وابسته به دستگاه، مجمع یا هر هدف دیگر به زبان low-level ترجمه می کند.
کامپایلر در مقابل مترجم
مترجمان و کامپایلرها در ساختار بسیار شبیه هستند. تفاوت اصلی این است که یک مترجم مستقیما دستورالعمل ها را در زبان برنامه نویسی منبع اجرا می کند در حالی که کامپایلر این دستورات را به کد ماشین کارآمد ترجمه می کند.یک مترجم معمولا نماینده ,واسط تولید می کند و بلافاصله آن را ارزیابی می کند. بسته به مترجم، نماینده واسط می تواند یک ASTیا یک نمایشگر low-level مستقل از دستگاه مانند کد سه بعدی باشد.