قواعد مترجم
مترجم سلام خیلی از اشتباهها را پیش از اجرای برنامه میگیرد. هر پیام میگوید چه چیزی کجا اشتباه است و معمولاً راه درست کردنش را هم میگوید. این درس قاعدههای پشت پیامهایی را که بیشتر میبینید یکییکی مرور میکند.
چرا اینقدر سختگیر؟
هر خطایی که مترجم بگیرد، خطایی است که هرگز به دست کاربر نمیرسد. برای همین سلام چیزهایی را خطا میداند که در زبانهای دیگر فقط هشدارند: متغیری که کسی نمیخواندش، ناپایایی که هیچوقت عوض نمیشود، شاخهای که هرگز اجرا نمیشود. هشدارها روی هم جمع میشوند و نادیده گرفته میشوند، ولی خطا همان لحظه درست میشود؛ پس برنامهی سلامی که ترجمه شود، چنین باقیماندههایی ندارد.
خواندن پیام خطا
یک پیام معمولی این شکلی است:
[SEMANTIC][ERROR] E013: نمیتوان متغیر تغییرناپذیر 'جمع' را دوباره مقداردهی کرد؛ برای این کار آن را 'ناپایا' تعریف کنید، یا بهجای آن به عناصر یا فیلدهایش مقدار دهید (app.salam:3:5)
E013کد خطاست. این کد در فارسی و انگلیسی یکی است، پس برای جستوجو همین را به کار ببرید.- پس از آن مشکل میآید و بعد از نقطهویرگول، راه معمول درست کردنش. پیام را تا آخر بخوانید؛ اغلب همان راهحل کافی است.
- آخر خط نام پرونده، شمارهی خط و ستون است: خط ۳، ستون ۵ از
app.salam.
اشتباه در شکل کد، مثل جاافتادن پایان یا واژهای که در سلام نیست، را تجزیهگر گزارش میدهد. این پیامها خود خط را با یک نشانه زیر جای اشتباه نشان میدهند، مثل خطا: '&&' عملگر سلام نیست؛ به جای آن 'و' بنویسید. پروندهی فارسی پیامهایش را به فارسی میگیرد.
اول نخستین خطا را درست کنید. یک اشتباه میتواند خطاهای دیگری هم پایینتر بسازد: اگر نامی را غلط بنویسید، مترجم هم نام ناشناخته را گزارش میدهد (E001) و هم متغیری را که منظورتان بود و حالا بیاستفاده مانده (E059).
هیچ چیز بیاستفاده
متغیر، ورودی، تابع خصوصی یا واردسازیای که هیچجا به کار نرود خطاست: E059، E062، E066 و E082. معمولاً راه درست حذف آن است، یا اینکه بفهمید یادتان رفته به کارش ببرید. شروع کردن نام با _ خطا را ساکت میکند، ولی آن را برای تنها حالت واقعیاش نگه دارید: امضایی که دست شما نیست. اینجا هر پاسخگو باید کاربر و متن را بگیرد و فریاد به کاربر نیازی ندارد:
میانجی پاسخگو:
تابع پاسخ(کاربر: رشته، متن: رشته): رشته
پایان
ساختار پژواک:
همگانی تابع پاسخ(کاربر: رشته، متن: رشته): رشته:
برگشت کاربر + " گفت " + متن
پایان
پایان
ساختار فریاد:
همگانی تابع پاسخ(_کاربر: رشته، متن: رشته): رشته:
برگشت متن + "!"
پایان
پایان
تابع اجرا(پ: پویا پاسخگو):
سرچاپ پ.پاسخ("سارا"، "سلام")
پایان
تابع ریشه:
اجرا(پژواک {})
اجرا(فریاد {})
پایانبدون _ مترجم میگفت E062: پارامتر استفادهنشده 'کاربر'، و خود پیامش میگوید _ را فقط وقتی به کار ببرید که امضا ثابت است.
ناپایا فقط جایی که چیزی عوض میشود
متغیری که با := ساخته شود دوباره مقدار نمیگیرد (E013). وقتی باید عوض شود ناپایا اضافه کنید، و فقط همان وقت: ناپایایی که هرگز عوض نشود هم خطاست (E069). افزودن به وکتور یا نگاشت دادهای را عوض میکند که به آن اشاره دارند، نه خود متغیر را، پس بیفزا بدون ناپایا هم کار میکند. مقداری که در تمام برنامه ثابت است پایا است.
پایا سقف := ۳
تابع ریشه:
نامها := وکتور {} برگردان وکتور<رشته>
دیرکن نامها.آزادکن()
نامها.بیفزا("سارا")
نامها.بیفزا("رضا")
نامها.بیفزا("مینا")
نامها.بیفزا("علی")
ناپایا نشانداده := ۰
هر ن در نامها:
اگر نشانداده == سقف:
بشکن
پایان
سرچاپ ن
نشانداده++
پایان
سرچاپ نشانداده، "از"، نامها.طول()، "نام نشان داده شد"
پایانگونهها هیچوقت حدس زده نمیشوند
سلام پشت سر شما گونهای را به گونهی دیگر تبدیل نمیکند. گذاشتن رشته جای صحیح در مقداردهی E002 و در صدا زدن تابع E012 است، و شرط باید واقعاً منطقی باشد: اگر کالاها: با یک عدد E021 است، پس بنویسید اگر کالاها > ۰:. دو شاخهی ?: هم باید یک گونه داشته باشند. با برگردان صریحاً تبدیل کنید، ولی نه وقتی مقدار همان گونه را دارد: ن برگردان صحیح روی یک صحیح تبدیل بیفایده است، E093.
همهی مسیرها بررسی میشوند
مترجم هر مسیر درون تابع را دنبال میکند. تابعی که مقدار برمیگرداند باید در همهی مسیرها برگرداند (E071: تابع 'علامت' ممکن است بدون بازگرداندن مقداری از گونه 'رشته' به پایان بدنهی خود برسد)، همخوان روی جداشمار باید همهی عضوها را پوشش دهد یا وگرنه داشته باشد (E080 عضوهای جاافتاده را نام میبرد)، و کدی که پس از برگشت بیاید هرگز اجرا نمیشود (E070). همینطور است شرطی که همیشه نادرست است (E068)، شاخهی خالی اگر (E060) و دستوری که مقدارش دور ریخته میشود، مثل ن + ۱ تنها در یک خط (E110).
جداشمار چراغ: قرمز، زرد، سبز پایان
تابع کار(چ: چراغ): رشته:
برگشت همخوان چ:
قرمز: "بایست" پایان
زرد: "آهسته" پایان
سبز: "برو" پایان
پایان
پایان
تابع علامت(ن: صحیح): رشته:
اگر ن > ۰:
برگشت "مثبت"
وگرنه ن < ۰:
برگشت "منفی"
پایان
برگشت "صفر"
پایان
تابع ریشه:
سرچاپ کار(چراغ.زرد)، کار(چراغ.سبز)
سرچاپ علامت(-۴)، علامت(۰)، علامت(۹)
پایاناگر بعدها عضو تازهای به چراغ اضافه شود، هر همخوانی که آن را پوشش ندهد دیگر ترجمه نمیشود، و این دقیقاً همان جاهایی است که باید سری بزنید.
مقدارها پیش از اجرای برنامه بررسی میشوند
مترجم مقدارهایی را هم که در تابع جریان دارند دنبال میکند: شمارنده در هر دور حلقه چه مقداری دارد، یک وکتور پس از افزودنها چند عضو دارد، و آیا یک اشارهگر هنوز میتواند پوچ باشد. وقتی خطی بیگمان اشتباه پیش میرود، ترجمه با خطایی میایستد که آن مقدار را نام میبرد: اندیسی بیرون از محدوده (E146: اندیس خارج از محدوده است: 'i' برابر ۳ است، اما 'xs' ۳ عضو دارد (در دور ۴ حلقهی خط ۳))، عدد علامتداری که از گونهاش سرریز میکند (E148)، تقسیم بر مقداری که ۰ است (E150) یا خواندن از اشارهگر پوچ (E152). وقتی فقط برای برخی مقدارها اشتباه پیش میرود، به جای خطا هشدار میگیرید (W147، W149، W151، W153)، و بررسیای مانند اگر i < xs.len(): پیش از آن خط برای خاموش کردنش کافی است. مقدارهایی که مترجم نمیبیند، مانند یک پارامتر یا خروجی یک تابع، هرگز حدس زده نمیشوند.
نمونهای که به خاطر سپردنش میارزد: تکرار 0 تا n در i: تا خود n میشمارد، پس تکرار 0 تا xs.len() در i: یک عضو پس از پایان را میخواند. به جای آن با تکرار xs.len() در i: یا هر i, x در xs: بشمارید.
خطا با پرتاب خطا، تلاش و گرفتن خطا، و کد خروج ریشه
تابعی که ممکن است شکست بخورد این را در امضایش میگوید: خطادار پس از گونهی برگشتی. شکست را با پرتاب خطا اعلام میکند که یک خطا (یا فقط یک پیام) میگیرد. هر فراخوانی چنین تابعی نشانه دارد: تلاش جلوی فراخوانی خطا را به صدازننده میفرستد که آن هم باید خطادار باشد؛ بلوک تلاش: ... گرفتن خطا خ: ... پایان خطا را رسیدگی میکند و فراخوانیهای درونش نشانه نمیخواهند. تلاش ر() گرفتن خطا مقدار به جای آن یک مقدار جایگزین میدهد. چیزی پنهان نیست: در هر خط میبینید خطا از کجا ممکن است بیاید، و وقتی خطایی رد میشود دیرکن باز هم اجرا میشود.
تابع سن از(متن: رشته): صحیح خطادار:
ن := متن.به صحیح()
اگر ن <= ۰ یا ن > ۱۵۰:
پرتاب خطا خطا.جدید(نوع خطا.خارج از بازه، "«" + متن + "» سن نیست")
پایان
برگشت ن
پایان
تابع بزرگترین(متنها: وکتور<رشته>): صحیح خطادار:
ناپایا بیشینه := ۰
هر متن در متنها:
سن := تلاش سن از(متن)
اگر سن > بیشینه:
بیشینه = سن
پایان
پایان
برگشت بیشینه
پایان
تابع ریشه:
هر متن در ["42"، "abc"]:
تلاش:
سرچاپ متن، "سن درستی است:"، سن از(متن)
گرفتن خطا خ:
سرچاپ خ.پیام
پایان
پایان
جایگزین := تلاش سن از("900") گرفتن خطا ۱۸
سرچاپ "با مقدار جایگزین:"، جایگزین
ناپایا متنها := وکتور {} برگردان وکتور<رشته>
متنها.بیفزا("30")
متنها.بیفزا("45")
تلاش:
سرچاپ "بزرگترین:"، بزرگترین(متنها)
گرفتن خطا خ:
نادرستسرچاپ "بزرگترین سن پیدا نشد:"، خ.پیام
برگشت ۱
پایان
پایانجا انداختن نشانه خطای ترجمهی E155 است و تلاش در تابعی که خطادار نیست E156. یک اشکال برنامهنویسی، مثل نمایهی بیرون از محدوده، مقدار خطا نیست: برنامه را متوقف میکند و گرفتن خطا آن را نمیبیند. بستهی خطا نوعهای خطا و آنچه یک خطا با خود دارد را فهرست میکند.
مقداری که ریشه برمیگرداند کد خروج برنامه است: برگشت ۱ شکست را به پوسته خبر میدهد و نبودن برگشت یعنی ۰. صدا زدن سیستم عامل.خروج درون ریشه خطای E109 است، چون پاکسازیهایی را که ریشه با دیرکن گذاشته جا میاندازد.
رایجترین خطاها
| کد | آنچه نوشتهاید | پیام مترجم | راه درست |
|---|---|---|---|
E001 | نامی که غلط نوشته شده | شناسهی ناشناخته 'شمر' | املای نام را درست کنید |
E002 | عدد = "پنج" | نمیتوان 'رشته' را به 'صحیح' نسبت داد | گونهی درست را بگذارید یا با برگردان تبدیل کنید |
E012 | دوبرابر("۴")، جمع(۱) | آرگومان ۱: نمیتوان 'رشته' را به 'صحیح' پاس داد؛ 'جمع' به ۲ آرگومان نیاز دارد، اما ۱ دریافت شد | ورودیها را با تعریف تابع جور کنید |
E013 | جمع := ۰ سپس جمع = ۵ | نمیتوان متغیر تغییرناپذیر 'جمع' را دوباره مقداردهی کرد | آن را ناپایا تعریف کنید |
E017 | نام.فریاد()، ح.موجودی | رشته متدی به نام 'فریاد' ندارد؛ فیلد 'موجودی' در ساختار 'حساب' خصوصی است | روشی را صدا بزنید که وجود دارد، یا عضو را همگانی کنید |
E021 | اگر کالاها: | شرط 'اگر' باید بولی باشد، اما 'صحیح' دریافت شد | مقایسه بنویسید: اگر کالاها > ۰: |
E059 | عدد := ۵ و بهکارنبردن آن | متغیر استفادهنشده 'عدد' | به کارش ببرید یا حذفش کنید |
E060 | اگر با بدنهی خالی | بدنهی 'اگر' خالی است | بدنه را پر کنید، یا شرط را با وارونه برعکس کنید |
E062 | ورودیای که بدنه نمیخواندش | پارامتر استفادهنشده 'بلندی' | حذفش کنید، یا برای امضای ثابت از _ استفاده کنید |
E066 | تابع خصوصیای که صدا زده نمیشود | تابع 'کمکی' استفاده نشده است | صدایش بزنید یا حذفش کنید |
E068 | اگر نادرست: | شرط 'اگر' همیشه نادرست است: بدنهی آن هرگز اجرا نمیشود | شاخهی مرده را حذف کنید |
E069 | ناپایا عدد := ۵ بدون تغییر | متغیر 'عدد' با کلیدواژهی 'ناپایا' تعریف شده اما هرگز تغییر نکرده است | ناپایا را بردارید |
E070 | دستوری پس از برگشت | کد دستنیافتنی | حذفش کنید یا بالاتر ببریدش |
E071 | مسیری بدون برگشت | تابع 'علامت' ممکن است بدون بازگرداندن مقداری از گونه 'رشته' به پایان بدنهی خود برسد | یک برگشت پایانی بگذارید |
E080 | همخوانی که عضوی را جا انداخته | همخوان ناقص روی جداشمار 'چراغ': عضو(های) زرد پوشش داده نشده است | عضو را اضافه کنید یا شاخهی وگرنه بگذارید |
E082 | واردسازی ریاضی بیاستفاده | بستهی واردشدهی 'ریاضی' استفاده نشده است | واردسازی را حذف کنید |
E084 | پایا پس از یک تابع | پایا «نرخ» باید پیش از هر تعریف تابع یا گونه اعلام شود | آن را به بالای پرونده ببرید |
E087 | ساختار پس از یک تابع | «نقطه» باید پیش از هر تعریف تابعی اعلام شود | گونهها را بالای تابعها بگذارید |
E090 | متغیری همنام یک تابع | 'جمع' از قبل نام یک تابع در این فایل است | یکی را تغییر نام دهید |
E093 | ن برگردان صحیح روی یک صحیح | تبدیل بیفایده | تبدیل را حذف کنید |
E108 | واردسازی پس از فراخوانی | «واردسازی رشته» باید پیش از همهی فراخوانیها بیاید | واردسازیها را اول بنویسید |
E109 | سیستم عامل.خروج(۱) در ریشه | در «ریشه» تابع «سیستم عامل.خروج» را صدا نزنید | بنویسید برگشت ۱ |
E110 | ن + ۱ تنها در یک خط | این دستور هیچ اثری ندارد | نتیجه را در متغیری بگذارید یا خط را حذف کنید |
E162 | ن = ن + ۱، ن += ۱، ن = ن + ۵، ن = ن | 'ن = ن + ۱' مجاز نیست: بنویسید 'ن++'؛ 'ن = ن + ۵' مجاز نیست: بنویسید 'ن += ۵'؛ 'ن = ن' کاری انجام نمیدهد: این دستور را بردارید | برای یکی زیاد کردن ن++، برای هر مقدار دیگر ن += ۵ بنویسید و دستورهایی را که چیزی عوض نمیکنند بردارید |
چند اشتباه را تجزیهگر میگیرد و کد ندارند: && و || (بنویسید و و یا)، تعریف با گونه مثل ن: صحیح = ۲ (بنویسید ن := ۲)، و واژهی رزروشده بهجای نام، مثل و که همیشه به معنای «and» است.
چند راهنمایی
- اول نخستین پیام را درست کنید و دوباره بسازید؛ خیلی از پیامهای بعدی همراهش میروند.
- هر پیام را تا آخر بخوانید. بخش پس از نقطهویرگول معمولاً همان راهحل است.
- برای پنهان کردن خطا سراغ
_یاهمگانینروید. مترجم به کدی اشاره میکند که مرده، نیمهکاره یا سر جای خودش نیست. - بهجای متن پیام، کد آن را جستوجو کنید، مثل
E071؛ کد در هر زبانی یکی است. - پیش از دنبال کردن خطاهای ترتیب،
salam formatرا اجرا کنید؛ خیلی از تعریفها را خودش سر جایشان میگذارد.