نکات متفرقه
• کار با کاراکترها و رشتهها
رشتهها میتوانند در فرمت packed یا unpacked باشند. در فرمت packed، هر cell معمولاً چهار کاراکتر را نگه میدارد (در پیادهسازیهای معمول، یک cell 32 بیت است و یک کاراکتر 8 بیت). در این پیکربندی، اولین کاراکتر در یک "بسته" از چهار کاراکتر، بالاترین بایت یک cell است و چهارمین کاراکتر در پایینترین بایت هر cell قرار دارد.
یک رشته باید در یک آرایه ذخیره شود. برای یک رشته unpacked، آرایه باید به اندازه کافی بزرگ باشد تا همه کاراکترهای رشته به علاوه یک cell صفر پایانی را در خود جای دهد. یعنی، در مثال زیر، متغیر ustring به عنوان داشتن پنج cell تعریف شده است، که فقط به اندازه کافی برای نگهداری رشتهای که با آن مقداردهی اولیه شده است، کافی است:
لیستینگ: رشته unpacked
new ustring[5] = "test"
در یک رشته packed، هر cell شامل چندین کاراکتر است و رشته با یک کاراکتر صفر پایان مییابد. عملگر char به اعلان اندازه آرایه برای نگهداری تعداد مورد نیاز کاراکترها کمک میکند. مثال زیر به اندازه کافی cell برای نگهداری پنج کاراکتر packed تخصیص میدهد. در یک پیادهسازی معمول، دو cell در آرایه وجود خواهد داشت.
لیستینگ: رشته packed
new pstring[5 char] = !"test"
به عبارت دیگر، عملگر char عملوند سمت چپ خود را بر تعداد بایتهایی که در یک cell جا میشوند تقسیم میکند و به بالا گرد میکند. باز هم، در یک پیادهسازی معمول، این به معنای تقسیم بر چهار و گرد کردن به بالا است.
شما میتوانید روالهایی طراحی کنید که با رشتهها در هر دو فرمت packed و unpacked کار کنند. برای فهمیدن اینکه آیا یک رشته packed است یا unpacked، به اولین cell یک رشته نگاه کنید. اگر مقدار آن منفی یا بالاتر از حداکثر مقدار ممکن یک کاراکتر unpacked باشد، رشته یک رشته packed است. در غیر این صورت یک رشته unpacked است.
قطعه کد زیر اگر رشته ورودی packed باشد true و در غیر این صورت false برمیگرداند:
لیستینگ: تابع ispacked
bool: ispacked(string[])
return !(0 <= string[0] <= ucharmax)
یک رشته unpacked با یک cell کاملاً صفر پایان مییابد. پایان یک رشته packed با فقط یک کاراکتر صفر مشخص میشود. از آنجا که ممکن است تا چهار کاراکتر در یک cell 32 بیتی وجود داشته باشد، این کاراکتر صفر ممکن است در هر یک از چهار موقعیت در "بسته" رخ دهد. عملگر یک کاراکتر را از یک cell در یک آرایه استخراج میکند. اساساً، از عملگر شاخص cell ("[ ]") برای رشتههای unpacked و عملگر شاخص کاراکتر ("") برای کار روی رشتههای packed استفاده میکنید.
به عنوان مثال، یک روال که طول هر رشته (packed یا unpacked) را بر حسب کاراکتر برمیگرداند:
لیستینگ: تابع my strlen
my_strlen(string[])
{
new len = 0
if (ispacked(string))
while (string{len} != EOS) /* دریافت کاراکتر از بسته */
++len
else
while (string[len] != EOS) /* دریافت cell */
++len
return len
}
اگر توابعی را برای کار منحصراً روی رشتههای packed یا unpacked ایجاد میکنید، ایده خوبی است که یک assertion برای اجرای این شرط اضافه کنید:
لیستینگ: تابع strupper
strupper(string[])
{
assert ispacked(string)
for (new i=0; string{i} != EOS; ++i)
string{i} = toupper(string{i})
}
اگرچه، در پاراگرافهای قبلی فرض کردهایم که یک cell 32 بیت عرض دارد و یک کاراکتر 8 بیت است، اما نباید به این موضوع اعتماد کرد. اندازه یک cell وابسته به پیادهسازی است؛ مقادیر حداکثر و حداقل در ثابتهای از پیش تعریف شده cellmax و cellmin قرار دارند. ثابتهای از پیش تعریف شده مشابهی برای کاراکترها وجود دارد. با این حال، میتوان با اطمینان فرض کرد که هم اندازه یک کاراکتر به بایت و هم اندازه یک cell به بایت توانهای دو هستند.
عملگر char به شما امکان میدهد تعیین کنید چند کاراکتر packed در یک cell جا میشوند. به عنوان مثال:
#if 4 char == 1
/* کدی که فرض میکند 4 کاراکتر packed در هر cell */
#elseif 4 char == 2
/* کدی که فرض میکند 2 کاراکتر packed در هر cell */
#elseif 4 char == 4
/* کدی که فرض میکند 1 کاراکتر packed در هر cell */
#else
#assert 0 /* اندازه cell/کاراکتر پشتیبانی نشده */
#endif
• بینالمللیسازی
مثالهای برنامهنویسی در این راهنما از زبان انگلیسی برای تمام خروجیها (پیامها، پیغامها، ...) و یک مجموعه کاراکتر لاتین استفاده کردهاند. این لزوماً اینطور نیست؛ به عنوان مثال، میتوانید اولین برنامه "hello world" در صفحه 5 را به این صورت تغییر دهید:
لیستینگ: "hello world" به زبان یونانی
main()
printf "˙ ˙\n"
PAWN پشتیبانی اولیه از الفباهای غیر لاتین دارد، اما فقط کاراکترهای غیر لاتین را در رشتهها و ثابتهای کاراکتری میپذیرد. زبان PAWN نیاز دارد که تمام کلمات کلیدی و نمادها (نام توابع، متغیرها، تگها و سایر عناصر) در مجموعه کاراکتر ascii کدگذاری شوند.
برای زبانهایی که مجموعه کاراکتر مورد نیاز آنها نسبتاً کوچک است، یک راه حل رایج استفاده از یک مجموعه کاراکتر ascii گسترش یافته 8 بیتی است (مجموعه کاراکتر ascii 7 بیتی است و 128 کاراکتر دارد). 128 کد بالایی مجموعه گسترش یافته شامل گلیفهای مخصوص زبان هستند. برای زبانهای اروپای غربی، یک مجموعه کاراکتر شناخته شده "Latin-1" است، که به عنوان ISO 8859-1 استاندارد شده است —همان مجموعه همچنین با نام "codepage 1252" شناخته میشود، حداقل برای Microsoft Windows.∗ Codepageها برای بسیاری از زبانها تعریف شدهاند؛ به عنوان مثال، ISO 8859-2 ("Latin-2") دارای گلیفهایی است که در اروپای مرکزی و شرقی استفاده میشود، و ISO 8859-7 شامل الفبای یونانی در نیمه بالایی مجموعه ascii گسترش یافته است.
متأسفانه، انتخاب codepage میتواند گیجکننده باشد، زیرا فروشندگان سیستمهای عامل معمولاً codepageهای خود را بدون توجه به آنچه قبلاً وجود داشته ایجاد کردهاند. در نتیجه، برای اکثر مجموعههای کاراکتر، چندین codepage ناسازگار وجود دارد.
∗ Codepage 1252 دقیقاً همان Latin-1 نیست؛ Microsoft مجموعه استاندارد شده را گسترش داد تا گلیفهایی را در موقعیتهای کدی که Latin-1 آنها را به عنوان "رزرو شده" علامتگذاری کرده بود، شامل شود.
به عنوان مثال، codepage 1253 برای Microsoft Windows نیز الفبای یونانی را کدگذاری میکند، اما با ISO 8859-7 ناسازگار است. هنگام نوشتن متون به زبان یونانی، اکنون بررسی اینکه چه کدگذاری استفاده میشود مهم میشود، زیرا بسیاری از برنامههای Microsoft Windows از هر دو پشتیبانی میکنند.
وقتی مجموعه کاراکتر برای یک زبان از 256 گلیف بیشتر باشد، یک codepage کافی نیست. به طور سنتی، تکنیک codepage با رزرو کردن کدهای "shift" خاص در مجموعه کاراکتر پایه که به یک مجموعه جدید از گلیفها تغییر میکنند، گسترش یافته است. سپس کاراکتر بعدی گلیف خاص را نشان میدهد. در واقع، گلیف اکنون با یک شاخص 2 بایتی شناسایی میشود. از طرف دیگر، برخی کاراکترها (به ویژه مجموعه ascii 7 بیتی) هنوز میتوانند با یک بایت واحد نشان داده شوند. استاندارد "Shift-JIS"، برای مجموعه کاراکتر ژاپنی، مثالی برای کدگذاری طول متغیر است.
Codepageها هنگام تبادل اسناد یا دادهها با افرادی که در مناطقی با codepage متفاوت هستند، یا هنگام استفاده از زبانهای مختلف در یک سند، مشکلساز میشوند. Codepageهایی که از کاراکترهای "shift" استفاده میکنند مسئله را پیچیدهتر میکنند، زیرا پردازش متن اکنون باید در نظر بگیرد که یک کاراکتر ممکن است یک یا دو بایت بگیرد. اسکن کردن یک رشته از راست به چپ حتی ممکن است غیرممکن شود، زیرا یک بایت ممکن است یک گلیف از مجموعه پایه ("unshifted") را نشان دهد یا ممکن است یک گلیف از یک مجموعه shifted باشد -در حالت اخیر بایت قبلی مجموعه shift را نشان میدهد، اما معنای کاراکتر قبلی به کاراکتر قبل از آن بستگی دارد.
استاندارد ISO/IEC 10646 "مجموعه کاراکتر جهانی" (UCS) هدف بلندپروازانهای دارد که در نهایت تمام کاراکترهای مورد استفاده در تمام زبانهای نوشتاری جهان را با استفاده از یک مجموعه کاراکتر 31 بیتی شامل شود. این هر دو مشکل مربوط به codepageها و مجموعههای کاراکتر "shifted" را حل میکند. با این حال، نهاد ISO/IEC نتوانست یک استاندارد را به موقع تولید کند، و بنابراین یک کنسرسیوم عمدتاً از تولیدکنندگان نرمافزار آمریکایی شروع به کار موازی روی یک مجموعه کاراکتر سادهشده 16 بیتی به نام "Unicode" کردند. منطق پشت Unicode این بود که
کاراکترهای انتزاعی را کدگذاری میکند، نه گلیفها، و بنابراین 65,536 کد کافی خواهد بود.† در عمل، اما، Unicode گلیفها را کدگذاری میکند و نه مدت زیادی پس از ظهور آن، مشخص شد که 65,536 نقطه کد کافی نخواهد بود. برای مقابله با این مشکل، نسخههای بعدی Unicode با چندین "صفحه" و کدهای ویژهای که یک صفحه را انتخاب میکنند، گسترش یافتند. ترکیب یک انتخابکننده صفحه و اشارهگر کد داخل آن صفحه "جفت جایگزین" نامیده میشود.
† اگر Unicode کاراکترها را کدگذاری میکند، یک "فونت Unicode" یک تناقض در اصطلاحات است —زیرا یک فونت گلیفها را کدگذاری میکند.
اولین 65,536 نقطه کد در "صفحه چندزبانه پایه" (BMP) قرار دارند و کاراکترهای این مجموعه به یک انتخابکننده صفحه نیاز ندارند.
اساساً، معرفی جفتهای جایگزین در استاندارد Unicode معادل کدهای shift مجموعههای کاراکتر قبلی است —و برخی از مشکلاتی را که Unicode قصد داشت حل کند، به همراه دارد. کدگذاری UCS-4 توسط ISO/IEC 10646 جفتهای جایگزین ندارد/نیاز ندارد.
پشتیبانی از Unicode/UCS-4 در برنامهها و سیستمهای عامل (میزبان) به دو روش مختلف ظاهر شده است: یا نمایش داخلی کاراکترها چند بایتی است (معمولاً 16 بیتی، یا 2 بایتی)، یا برنامه رشتهها را به صورت داخلی در فرمت UTF-8 ذخیره میکند، و این رشتهها فقط هنگام نمایش یا چاپ به گلیفهای مناسب تبدیل میشوند. نسخههای اخیر Microsoft Windows به صورت داخلی از Unicode استفاده میکنند؛ سیستم عامل Plan-9 پیشگام رویکرد کدگذاری UTF-8 بود، که اکنون به طور گسترده در Unix/Linux استفاده میشود. مزیت کدگذاری UTF-8 به عنوان یک نمایش داخلی این است که از نظر فیزیکی یک کدگذاری 8 بیتی است، و بنابراین با تقریباً تمام پایگاههای داده، فرمتهای فایل و کتابخانههای موجود سازگار است. این نیاز به نقاط ورودی دوگانه برای توابعی که پارامترهای رشتهای میگیرند را دور میزند —همانطور که در Microsoft Windows وجود دارد، جایی که بسیاری از توابع در یک نسخه "A"nsi و یک نسخه "W"ide وجود دارند. یک معایب UTF-8 این است که یک کدگذاری با طول متغیر است، و بسیاری از عملیات رشتهای در حافظه بنابراین ناشیانه (و ناکارآمد) هستند. با این حال، با ظهور جفتهای جایگزین، Unicode نیز اکنون به یک کدگذاری با طول متغیر تبدیل شده است.
زبان PAWN نیاز دارد که کلمات کلیدی و نامهای نمادهای آن در ascii باشند، و اجازه میدهد کاراکترهای غیر ascii در رشتهها وجود داشته باشند. پنج راه وجود دارد که یک برنامه میزبان میتواند از کاراکترهای غیر ascii در رشتهها و لیترالهای کاراکتری پشتیبانی کند:
1 پشتیبانی از codepageها: در این استراتژی تمام پیچیدگی انتخاب گلیفها و فونتهای صحیح به برنامه میزبان واگذار میشود. پشتیبانی از codepage بر اساس فایلهای نگاشت codepage با فرمت فایل "جداول نگاشت متقابل" توزیع شده توسط کنسرسیوم Unicode است.
2 پشتیبانی از Unicode یا UCS-4 و اجازه دادن به کامپایلر PAWN برای تبدیل اسکریپتهایی که با استفاده از یک codepage نوشته شدهاند به کاراکترهای "عریض": برای این استراتژی، باید #pragma codepage را تنظیم کنید یا از گزینه معادل کامپایلر استفاده کنید. کامپایلر فقط کاراکترها را در رشتههای unpacked به درستی ترجمه میکند.
3 پشتیبانی از Unicode یا UCS-4 و اجازه دادن به کامپایلر PAWN برای تبدیل اسکریپتهای کدگذاری شده در UTF-8 به کاراکترهای "عریض": وقتی فایل منبع برای کامپایلر PAWN در کدگذاری UTF-8 است، کامپایلر کاراکترها را به Unicode/UCS-4 در رشتههای unpacked گسترش میدهد.
4 پشتیبانی از کدگذاری UTF-8 به صورت داخلی (در برنامه میزبان) و نوشتن فایل منبع به UTF-8 نیز: همه رشتهها اکنون باید رشتههای packed باشند تا از تبدیل آنها توسط کامپایلر جلوگیری شود.
برای اکثر استراتژیهای بینالمللیسازی، همانطور که میبینید، برنامه میزبان نیاز به پشتیبانی از Unicode یا UCS-4 دارد. به عنوان یک نکته جانبی، کامپایلر PAWN جفتهای جایگزین Unicode تولید نمیکند. اگر کاراکترهای خارج از BMP مورد نیاز باشند
و برنامه میزبان (یا سیستم عامل) از کدگذاری کامل UCS-4 پشتیبانی نکند، برنامه میزبان باید cell کاراکتر 32 بیتی ارائه شده توسط کامپایلر PAWN را به یک جفت جایگزین تقسیم کند.
کامپایلر PAWN یک فایل منبع را به عنوان یک فایل متنی کدگذاری شده UTF-8 میپذیرد —به صفحه 168 مراجعه کنید. وقتی فایل منبع در کدگذاری UTF-8 است، کاراکترهای "عریض" در یک رشته unpacked به عنوان کاراکترهای چند بایتی Unicode/UCS-4 ذخیره میشوند؛ کاراکترهای عریض در یک رشته packed در کدگذاری UTF-8 باقی میمانند. برای نوشتن فایلهای منبع در کدگذاری UTF-8، شما البته به یک ویرایشگر (برنامهنویسی) نیاز دارید که از UTF-8 پشتیبانی کند. ترجمه codepage برای فایلهایی که در کدگذاری UTF-8 هستند اعمال نمیشود.
برای یک کاراکتر Unicode گاهبهگاه در یک رشته لیترال، یک جایگزین این است که از یک دنباله escape استفاده کنید. از آنجا که جداول کاراکتر Unicode معمولاً با شاخصهای گلیف هگزادسیمال مستند میشوند، دنباله xhhh; احتمالاً مشخصات راحتتری برای یک کاراکتر تصادفی Unicode است. به عنوان مثال، دنباله escape "\x2209" نشاندهنده کاراکتر "6∈" است.
موارد بسیار بیشتری در بینالمللیسازی وجود دارد که فراتر از پشتیبانی اولیه برای مجموعههای کاراکتر گسترش یافته است، مانند قالببندی فیلدهای تاریخ و زمان، جهت خواندن (چپ به راست یا راست به چپ) و ترجمه پیامهای سیستم بر اساس محل. مجموعه ابزار PAWN این مسائل را به برنامه میزبان واگذار میکند.
• کار با تگها
سیستم نام تگ برای افزودن یک مکانیزم "بررسی استفاده" به PAWN اختراع شد. یک تگ یک "هدف" از یک مقدار یا متغیر را نشان میدهد، و کامپایلر PAWN یک پیام تشخیصی صادر میکند هنگامی که تگ یک عبارت با تگ مورد نیاز برای زمینه عبارت مطابقت نداشته باشد.
بسیاری از زبانهای برنامهنویسی مدرن انواع متغیر ارائه میدهند، جایی که یک نوع طرح حافظه و هدف متغیر را مشخص میکند. زبان برنامهنویسی
سپس برابری نوع را بررسی میکند؛ زبان پاسکال در بررسی برابری نوع بسیار سختگیر است، در حالی که زبان برنامهنویسی C بخشندهتر است. زبان PAWN انواع ندارد: همه متغیرها اندازه و طرح یک cell را دارند، اگرچه نمایشهای بیتی در cell ممکن است به هدف متغیر بستگی داشته باشد. به طور خلاصه:
-
یک نوع، طرح حافظه و محدوده متغیرها و نتایج تابع را مشخص میکند
-
یک نام تگ، هدف متغیرها، ثابتها و نتایج تابع را برچسبگذاری میکند
تگها در PAWN عمدتاً اختیاری هستند. یک برنامه که با نامهای تگ در اعلانهای متغیر و ثابت "تقویت" شده است، هنگامی که همه نامهای تگ حذف شوند، به طور یکسان عمل خواهد کرد. یک استثنا توسط عملگرهای تعریف شده توسط کاربر تشکیل میشود: کامپایلر PAWN از تگهای عملوندها برای انتخاب بین هر عملگر تعریف شده توسط کاربر و عملگر استاندارد استفاده میکند.
قطعه زیر سه متغیر را اعلام میکند و سه انتساب انجام میدهد، که دو مورد از آنها یک پیام تشخیصی "عدم تطابق تگ" میدهند:
لیستینگ: مقایسه سیب با پرتقال
new apple:elstar /* متغیر "elstar" با تگ "apple" */
new orange:valencia /* متغیر "valencia" با تگ "orange" */
new x /* متغیر بدون تگ "x" */
elstar = valencia /* عدم تطابق تگ */
elstar = x /* عدم تطابق تگ */
x = valencia /* قابل قبول */
انتساب اول باعث یک پیام تشخیصی "عدم تطابق تگ" میشود زیرا یک متغیر با تگ "orange" را به یک متغیر با تگ "apple" اختصاص میدهد. انتساب دوم مقدار بدون تگ x را در یک متغیر با تگ قرار میدهد، که باز هم باعث یک پیام تشخیصی میشود. هنگامی که متغیر بدون تگ در سمت چپ عملگر انتساب باشد، مانند انتساب سوم، هیچ پیام هشدار یا خطایی وجود ندارد. از آنجا که متغیر x بدون تگ است، میتواند یک مقدار با هر تگ ضعیف را بپذیرد.
همان مکانیزم برای ارسال متغیرها یا عبارات به توابع به عنوان عملوندهای تابع اعمال میشود —برای یک مثال صفحه 78 را ببینید. به طور خلاصه، هنگامی که یک تابع یک نام تگ خاص را برای یک آرگومان انتظار دارد، باید یک عبارت/ متغیر با یک تگ مطابق به آن تابع ارسال کنید؛ اما اگر تابع یک آرگومان بدون تگ را انتظار داشته باشد، میتوانید آرگومانهایی با هر تگ ضعیف ارسال کنید.
در مواقعی، لازم است تگ یک عبارت را به طور موقت تغییر دهید. به عنوان مثال، با اعلانهای قطعه کد قبلی، اگر بخواهید سیبها را با پرتقالها مقایسه کنید (تحقیقات اخیر نشان میدهد که مقایسه سیبها با پرتقالها آنقدر که باور عمومی میپندارد، بیمعنی نیست)، میتوانید از:
if (apple:valencia < elstar)
valencia = orange:elstar
عبارت آزمون دستور if (بین پرانتزها) متغیر valencia را با متغیر elstar مقایسه میکند. برای جلوگیری از یک پیام تشخیصی "عدم تطابق تگ"، یک لغو تگ apple: روی valencia قرار میدهد —پس از آن، عبارات در سمت چپ و راست عملگر > دارای همان نام تگ هستند: "apple:". خط دوم، انتساب elstar به valencia، تگ elstar را با orange: قبل از انتساب لغو میکند. در یک انتساب، نمیتوانید تگ مقصد را لغو کنید؛ یعنی، سمت چپ عملگر =. نوشتن "apple:valencia = elstar" خطا است. در انتساب، valencia یک "lvalue" است و نمیتوانید تگ یک lvalue را لغو کنید.
همانطور که قبلاً نشان داده شد، هنگامی که سمت چپ یک انتساب یک متغیر بدون تگ دارد، عبارت در سمت راست ممکن است هر نام تگ ضعیفی داشته باشد. هنگامی که به عنوان یک lvalue استفاده میشود، یک متغیر بدون تگ با همه نامهای تگ ضعیف سازگار است. یا بهتر است بگوییم، یک تگ ضعیف هنگامی که به یک متغیر بدون تگ اختصاص داده میشود یا هنگامی که به یک تابع که یک آرگومان بدون تگ را انتظار دارد ارسال میشود، به آرامی حذف میشود. هنگامی که یک نام تگ الگوی بیت یک cell را نشان میدهد، حذف آرام یک تگ ضعیف میتواند خطاها را پنهان کند. به عنوان مثال، قطعه زیر دارای خطایی است که بلافاصله آشکار نیست:
لیستینگ: روش بد استفاده از تگها
#pragma rational float
new limit = -5.0
new value = -1.0
if (value < limit)
printf("Value %f below limit %f\n", value, limit)
else
printf("Value above limit\n")
از طریق "#pragma rational"، همه اعداد گویا تگ نام "float" را دریافت میکنند و این اعداد در فرمت 4 بایتی IEEE 754 کدگذاری میشوند. قطعه دو متغیر، limit و value را اعلام میکند، که هر دو بدون تگ هستند (این خطا است). اگرچه مقادیر لیترال -5.0 و -1.0 به طور ضمنی با float: تگ شدهاند، این تگ ضعیف هنگامی که مقادیر به نمادهای بدون تگ limit و value اختصاص داده میشوند، به آرامی حذف میشود. اکنون، دستور if value را با limit به عنوان اعداد صحیح، با استفاده از عملگر استاندارد < مقایسه میکند (یک عملگر تعریف شده توسط کاربر برای مقایسه دو مقدار کدگذاری شده IEEE 754 مناسبتر خواهد بود). هنگام اجرا، این قطعه کد به ما میگوید که "Value -1.000000 below limit -5.000000" —که البته نادرست است.
برای جلوگیری از چنین خطاهای ظریفی که تشخیص داده نمیشوند، باید از تگهای قوی استفاده کنید. یک تگ قوی صرفاً یک نام تگ است که با یک حرف بزرگ شروع میشود، مانند Float: به جای float:. یک تگ قوی هرگز به طور خودکار "حذف" نمیشود، اما هنوز هم ممکن است به صراحت لغو شود. در زیر یک قطعه کد اصلاح شده با تغییرات پیشنهادی است:
لیستینگ: تگهای قوی ایمنتر هستند
#pragma rational Float
new Float:limit = -5.0
new Float:value = -1.0
if (value < limit)
printf("Value %f below limit %f\n", _:value, _:limit)
else
printf("Value above limit\n")
فراموش کردن نام تگ Float: در اعلان متغیرهای limit یا value بلافاصله یک پیام تشخیصی "عدم تطابق تگ" میدهد، زیرا مقادیر لیترال -5.0 و -1.0 اکنون یک نام تگ قوی دارند.
printf یک تابع همه منظوره است که میتواند رشتهها و مقادیر را در فرمتهای مختلف چاپ کند. برای اینکه همه منظوره باشد، printf آرگومانهایی با هر نام تگ ضعیف را میپذیرد، چه apple:، چه orange:، یا چیز دیگری. تابع printf این کار را با پذیرش آرگومانهای بدون تگ انجام میدهد —تگهای ضعیف هنگامی که یک آرگومان بدون تگ مورد انتظار است، حذف میشوند. تگهای قوی، با این حال، هرگز حذف نمیشوند، و در قطعه بالا (که از تعریف اصلی printf استفاده میکند)، من نیاز داشتم یک لغو تگ خالی، "_:"، قبل از متغیرهای value و limit در اولین فراخوانی printf قرار دهم.
یک جایگزین برای حذف تگ عبارات با نامهای تگ قوی در توابع همه منظوره وجود دارد: تنظیم تعریف تابع برای پذیرش هم همه تگهای ضعیف و هم یک مجموعه انتخابی از نامهای تگ قوی. تعریف اصلی printf (از فایل console.inc) این است:
native printf(const format[], ...);
با اضافه کردن هم یک تگ Float: و هم یک تگ خالی در جلوی سه نقطه ("...")، printf آرگومانهایی با نام تگ Float:، آرگومانهای بدون نام تگ و آرگومانهایی که یک نام تگ ضعیف دارند را میپذیرد. برای مشخص کردن نامهای تگ چندگانه، همه نامهای تگ بدون دو نقطه پایانیشان را بین آکولادها با یک کاما که نامهای تگ را جدا میکند، قرار دهید (مثال زیر را ببینید). لازم است تگ خالی را به لیست نامهای تگ اضافه کنید، زیرا در غیر این صورت printf فقط آرگومانهایی با نام تگ Float: را میپذیرد. در زیر تعریف جدید تابع printf است:
native printf(const format[], {Float, _}: ...);
تگهای چندگانه به شما امکان میدهند یک تابع واحد بنویسید که cellهایی با یک زیرمجموعه دقیقاً مشخص شده از تگها (قوی و/یا ضعیف) را میپذیرد. در حالی که یک آرگومان تابع ممکن است پذیرش آرگومانهای واقعی با تگهای متنوع را بپذیرد، یک متغیر میتواند فقط یک تگ واحد داشته باشد —و یک آرگومان تابع رسمی یک متغیر محلی در بدنه تابع است. در حضور تگهای چندگانه، آرگومان تابع رسمی تگی را میگیرد که اول در لیست تگ در اعلان آرگومان تابع ذکر شده است.
در مواقعی، ممکن است بخواهید بررسی کنید که یک آرگومان تابع واقعی چه تگی داشت، هنگامی که آرگومان تگهای چندگانه را میپذیرد. بررسی تگ آرگومان رسمی (در بدنه تابع) بیفایده است، زیرا همیشه اولین تگ در لیست تگ در اعلان آرگومان تابع را خواهد داشت. شما میتوانید تگ آرگومان واقعی را با اضافه کردن یک آرگومان اضافی به تابع بررسی کنید، و مقدار پیشفرض آن را "tagof" آرگومان مورد نظر قرار دهید. مشابه عملگر sizeof، عملگر tagof یک معنای ویژه دارد هنگامی که در یک مقدار پیشفرض یک آرگومان تابع اعمال میشود: عبارت در نقطه فراخوانی تابع ارزیابی میشود، به جای تعریف تابع. این بدان معناست که "مقدار پیشفرض" آرگومان تابع تگ واقعی پارامتر ارسال شده به تابع است.
در داخل بدنه تابع، میتوانید تگ را با تگهای شناخته شده مقایسه کنید با، دوباره، استفاده از عملگر tagof.
• اتصال خطوط
PAWN یک زبان با فرمت آزاد است، اما دستورالعملهای تجزیهکننده باید در یک خط واحد باشند. رشتهها نیز نمیتوانند روی چندین خط قرار گیرند. هنگامی که این ناراحتکننده است، میتوانید از یک کاراکتر بکاسلش ("") در انتهای یک خط برای "چسباندن" آن خط با خط بعدی استفاده کنید.
به عنوان مثال:
#define max_path max_drivename + max_directorystring + \
max_filename + max_extension
همچنین از کاراکتر اتصال برای بریدن رشتههای لیترال طولانی روی چندین خط استفاده میکنید. توجه داشته باشید که "" تمام فضاهای خالی پس از خود را میخورد و فضاهای خالی ابتدایی در خط بعدی را نیز. مثال زیر "Hello world" را با یک فاصله بین دو کلمه چاپ میکند (زیرا یک فاصله بین "Hello" و بکاسلش وجود دارد):
print("Hello \
world")
• یک برنامه که کد منبع خود را تولید میکند
یک معیار عجیب، کمی آکادمیک، برای کمی کردن "بیانپذیری" یک زبان برنامهنویسی اندازه کوچکترین برنامهای است که، هنگام اجرا، کد منبع خود را دوباره تولید میکند. منطق پشت این معیار این است که هرچه برنامه خود-تولیدکننده کوتاهتر باشد، زبان باید انعطافپذیرتر و بیانپذیرتر باشد. برنامههایی از این نوع برای بسیاری از زبانهای برنامهنویسی ایجاد شدهاند —گاهی به طور تعجبآوری کوچک، مانند زبانهایی که قابلیتهای بازتابی داخلی دارند.
برنامههای خود-تولیدکننده "quine" نامیده میشوند، به افتخار فیلسوف ویلارد ون اورمن کواین که عبارات خود-ایجادکننده در زبان طبیعی نوشت. کار ون اورمن کواین از طریق کتابهای "گودل، اشر، باخ" و "موضوعات متافیزیکی" توسط داگلاس هافستادتر به خوبی شناخته شد.
quine PAWN در مثال زیر است؛ این بر اساس "C" quine معروف مدلسازی شده است (که از آن تغییرات زیادی وجود دارد). با 77 کاراکتر، در میان کوچکترین نسخهها برای کلاس زبانهای برنامهنویسی دستوری است، و اندازه را میتوان با حذف چهار کاراکتر "فاصله" که برای خوانایی باقی ماندهاند، به 73 کاراکتر کاهش داد.
لیستینگ: quine.p
new s[]="new s[]=%c%s%c; main() printf s,34,s,34"; main() printf s,34,s,34
به یادداشت برنامه جداگانه برای توابع بومی پیشنهادی که هم روی رشتههای packed و هم unpacked کار میکنند، مراجعه کنید
EOS: ثابت از پیش تعریف شده برای نشان دادن پایان رشته؛ مقدار آن '\0' است
ثابتهای از پیش تعریف شده: 102
رشتههای Packed و unpacked: 99
دنباله escape: 99
نامهای تگ: 68
عملگرهای تعریف شده توسط کاربر: 86
قوانین بیشتر نام تگ: 68
lvalue (تعریف ~): 104
دستورالعملها: 77
دستورالعملها: 117