زنجیرهابزار (Toolchain) زبان برنامهنویسی سلام یک پروندهی اجرایی است، salam، که کامپایلر و تقریباً همهی ابزارهای پیرامونش را در خود دارد. سرور زبان، قالببند، اجراکنندهی آزمون، REPL، سازندهی مستندات، راهانداز خطایاب (Debugger) و بررسیگر حافظه همه زیرفرمانهای همین یک برنامهاند، پس همیشه دربارهی زبان با کامپایلر همنظرند و ویرایشگرها همان پیامهایی را نشان میدهند که در خط فرمان میبینید. افزونه برای یازده ویرایشگر، یک دستور زبان tree-sitter و یک ویرایشگر برخط این مجموعه را کامل میکنند.
این مقاله ابزارهایی را که یک برنامهنویس هر روز به کار میبرد مرور میکند، نشان میدهد چه چیزی چاپ میکنند و با کمبودهایی تمام میشود که یک زنجیرهابزار جوان هنوز دارد. همهی نمونهها به سلام فارسیاند و خروجیها همان چیزی است که ابزارها برای پروندهی فارسی چاپ میکنند.
یک پروندهی اجرایی، یک منبع حقیقت
در بیشتر اکوسیستمهای زبان، ابزارها جدا از هم رشد میکنند: کامپایلر را یک تیم مینویسد، قالببند را تیمی دیگر، و سرور زبان بخشهایی از بررسیگر گونه را دوباره پیاده میکند. ابزارهای سلام به جای این، کد را با کامپایلر شریکاند. salam lsp همان تجزیهگر و تحلیل معنایی کامپایلر را اجرا میکند، salam format همان قالببندی است که سرور زبان صدا میزند، و افزونههای ویرایشگر فهرست کلیدواژههایشان را از جدولهای کامپایلر میگیرند. نتیجهاش عملی است: وقتی یک نسخه کلیدواژه یا پیام خطای تازهای اضافه میکند، همهی ابزارها همان لحظه آن را میشناسند، هم به انگلیسی و هم به فارسی.
| کار | فرمان |
|---|---|
| پیدا کردن خطا بدون ساختن | salam check app.salam |
| اعمال اصلاحهای امن | salam check app.salam --fix |
| قالببندی کد | salam format . |
| اجرای آزمونها | salam test |
| خط فرمان تعاملی | salam cli (عمومی) یا salam page (زبان صفحه) |
| خطایابی | salam debug app.salam |
| خطاهای حافظه | salam memcheck app.salam |
| مستندات رابط برنامهنویسی | salam doc |
| سرور زبان | salam lsp |
پیامهای خطای کامپایلر سلام و اصلاح خودکار
نخستین ابزار خود کامپایلر است. salam check هر کاری را که ساخت پیش از تولید کد انجام میدهد اجرا میکند، بستههای واردشده هم شامل آن میشوند، و وقتی خطایی چاپ کند با کد ۱ خارج میشود؛ همین آن را به حلقهی سریع روزمره تبدیل میکند. پیامها کدهای ثابت دارند و هر جا کامپایلر بتواند بفهمد منظورتان چه بوده، یک اصلاح هم پیشنهاد میکند. این پروندهی آزمونی است که نام یک تابع را حدس زده است:
خطا[خطا۰۱۲]: بستهی 'رشته' تابع صادرشدهای به نام 'کوچک کردن' ندارد؛ منظورتان 'کوچک' بود؟
--> نامک_test.salam:5:25
|
5 | برگشت رشته.جایگزینی(رشته.کوچککردن(رشته.پیرایش(عنوان))، " "، "-")
| ^
|
= اصلاح: تغییر به 'کوچک'کدِ خطا۰۱۲ همان E012 در برنامههای انگلیسی است؛ برای پروندهی فارسی، هم پیام و هم کد به فارسی چاپ میشوند. بعضی اصلاحها آنقدر امناند که میشود خودکار اعمالشان کرد: واردسازیهای جاافتاده یا بیاستفاده، ناپایایی که هرگز لازم نبوده، و کلیدواژههایی که از زبانهای دیگر قرض گرفته شدهاند، مثل return یا for. salam check --fix اینها را درجا اعمال و دوباره بررسی میکند. روی پروندهای که ناپایا جمع := ۱۰ را تعریف کرده و هرگز تغییرش نداده بود، کامپایلر اول این را گزارش کرد:
خطا[خطا۰۶۹]: متغیر 'جمع' با کلیدواژهی 'ناپایا' تعریف شده اما هرگز تغییر نکرده است؛ 'ناپایا' را حذف کنید یا آن را 'پایا' تعریف کنید
--> جمع.salam:2:5
|
2 | ناپایا جمع := ۱۰
| ^
|
= اصلاح: حذف 'ناپایا'و --fix چاپ کرد جمع.salam: 1 اصلاح انجام شد و جمع.salam: بدون خطا. دو گزینهی دیگر برای ابزارسازی مهماند: --error-format=json هر پیام را همراه با اصلاحهایش به شکل ویرایشهایی با جابهجایی بایتی بیرون میدهد، و --error-lang=fa پیامهای یک برنامهی انگلیسی را به فارسی چاپ میکند، و برعکس. مقالهی کامپایلر سلام چگونه کار میکند توضیح میدهد این بررسیها کجا اجرا میشوند.
سرور زبان سلام: salam lsp
salam lsp یک سرور پروتکل سرور زبان (Language Server Protocol) است که از نسخهی ۰٫۵٫۸ درون کامپایلر ساخته شده است. هر ویرایشگری که کلاینت LSP داشته باشد این امکانات را از آن میگیرد:
- خطاها و هشدارها همزمان با تایپ، با کدها و متن راهنمای کامپایلر؛
- نمایش اعلان، گونه و توضیح مستندات با بردن نشانگر روی نام (hover)، کتابخانهی استاندارد هم شامل آن است؛
- رفتن به تعریف، اعلان و تعریف گونه، حتی درون
std/؛ - یافتن ارجاعها، برجستهسازی و تغییر نام در کل فضای کاری؛
- سلسلهمراتب فراخوانی، پیادهسازیهای میانجیها و سلسلهمراتب گونه؛
- تکمیل خودکار برای نامهای در دسترس، عضوهای ساختار و بسته، کلیدواژهها و قطعهکدهای بلوک، بهعلاوهی راهنمای امضای تابع؛
- نکتههای درونخطی (inlay hints) که گونهی استنتاجشده را پس از
نام := مقدارنشان میدهند؛ - رنگآمیزی معنایی که نامهای چندکلمهای را میفهمد، تاکردن کد، طرح کلی، نمادهای فضای کاری و انتخاب هوشمند؛
- قالببندی با قالببند کامپایلر، و اصلاحهای کامپایلر بهعنوان کنشهای کد (code actions).
پیامها از زبان ویرایشگر پیروی میکنند، پس با محیط فارسی پیامهای خطا و متن hover فارسی میآیند و کدهای خطا همان میمانند. گزینههایی مثل stdlibPath، defines برای کامپایل شرطی و switchLint در initializationOptions قرار میگیرند. راهاندازی در بیشتر ویرایشگرها چند خط است. برای نمونه در Helix:
[language-server.salam]
command = "salam"
args = ["lsp"]
[[language]]
name = "salam"
scope = "source.salam"
file-types = ["salam"]
language-servers = ["salam"]و در Sublime Text با بستهی LSP:
{
"clients": {
"salam": {
"enabled": true,
"command": ["salam", "lsp"],
"selector": "source.salam"
}
}
}پروندهی docs/LSP.md در مخزن پیکربندی VS Code، Neovim، Emacs (eglot و lsp-mode)، محیطهای JetBrains از راه LSP4IJ، Kate و چند ویرایشگر دیگر را دارد.
افزونه برای یازده ویرایشگر
پوشهی extensions/ افزونههایی برای VS Code، Vim و Neovim، Sublime Text، Emacs، محیطهای JetBrains، Kate و KDevelop، Geany، gedit و گنوم (GtkSourceView)، Notepad++، GNU nano و micro دارد، بهعلاوهی یک دستور زبان tree-sitter. همهی آنها هر دو مجموعهی کلیدواژهی انگلیسی و فارسی را رنگآمیزی میکنند، حرفهای خط عربیپایه را جزو نویسههای شناسه میدانند و رقمهای ASCII، فارسی و عربیهندی را میپذیرند.
هیچکدام فهرست کلیدواژهی جداگانهای نگه نمیدارد. یک مولد که به سلام نوشته شده، املای کلیدواژهها را از compiler/langpack.salam و نام گونهها و متدها را از تحلیلگر معنایی میخواند، همهی پروندههای نحو را مینویسد، و اگر پروندههای ثبتشده در مخزن با خروجی مولد فرق داشته باشند CI شکست میخورد. افزونهی Visual Studio Code که در بازارچهی VS Code منتشر شده، فرمانهای اجرا و ساخت، قطعهکدها، سرور زبان و خطایابی را اضافه میکند. فهرست افزونهها در صفحهی ابزارها آمده است.
قالببندی کد: salam format
salam format منبع سلام را به سبک استاندارد بازنویسی میکند: تورفتگی چهار فاصلهای بهطور پیشفرض، فاصله پس از ویرگول و دور حاشیهنویسی گونه، و وقتی فقط جابهجایی محض لازم باشد، مرتب کردن دوبارهی اعلانهای سطح بالا تا قاعدههای ترتیب کامپایلر رعایت شود (واردسازیها، ثابتها، متغیرهای سراسری، گونهها، تابعها). با این پرونده:
تابع مساحت(پ:صحیح،ب:صحیح):صحیح:
برگشت پ*ب
پایان
تابع ریشه:
سرچاپ مساحت(۳،۴)
پایانچاپ کرد قالببندی شد نامرتب.salam و این را نوشت:
تابع مساحت(پ: صحیح، ب: صحیح): صحیح:
برگشت پ*ب
پایان
تابع ریشه:
سرچاپ مساحت(۳، ۴)
پایان--check پروندههایی را که قالببندی لازم دارند بدون دست زدن به آنها گزارش میکند و با کد ۱ خارج میشود؛ این همان چیزی است که در CI میگذارید. --tabs، --indent=N، --exclude=PATTERN و پروندهی .salamfmtignore نیازهای معمول پروژه را پوشش میدهند، و --minify توضیحات و تورفتگی را حذف میکند و دنبالهی توکنها را دستنخورده نگه میدارد.
نوشتن و اجرای آزمون در سلام: salam test
salam test به هیچ پیکربندیای نیاز ندارد. پوشه را میپیماید، هر پروندهی *_test.salam و *.test.salam را پیدا میکند، هر کدام را در پوشهی موقت خودش کامپایل میکند و پروندهها را موازی اجرا میکند، بهطور پیشفرض یکی برای هر هستهی پردازنده. خروجی به ترتیب مرتبشدهی پروندهها چاپ میشود، پس دو اجرا متن یکسانی میدهند. پروندههای آزمون سه نوعاند.
آزمونهای واحد تابعهاییاند که نامشان با test_ (در فارسی آزمون_) شروع میشود و از بستهی آزمایش (testing) استفاده میکنند. اجراکننده تابع ریشه را خودش برایتان مینویسد:
واردسازی آزمایش
واردسازی رشته
تابع نامک(عنوان: رشته): رشته:
برگشت رشته.جایگزینی(رشته.کوچک(رشته.پیرایش(عنوان))، " "، "-")
پایان
تابع آزمون_کوچککردن():
آزمایش.ادعای برابری رشته(نامک("Hello World")، "hello-world")
پایان
تابع آزمون_پیرایش():
آزمایش.ادعای برابری رشته(نامک(" Salam ")، "salam")
پایاناجرای salam test در آن پوشه این را چاپ کرد:
=== RUN آزمون_کوچک کردن
--- PASS: آزمون_کوچک کردن
=== RUN آزمون_پیرایش
--- PASS: آزمون_پیرایش
ok نامک_test.salam: 2 tests passed
PASS: 1 test filesنیمفاصلهی کوچککردن در خروجی به فاصله تبدیل شده است، چون در نامهای سلام فاصله و نیمفاصله یکیاند.
آزمونهای کل برنامه تابع ریشه دارند و دستورهای توضیحی بالای پرونده بررسی میکنند چه چیزی چاپ میشود و برنامه با چه کدی خارج میشود. این پرونده یک برنامهی معمولی است که میتوانید اجرایش کنید، و salam test هم بررسیاش میکند:
// انتظار-خط: 15 فیزباز
// انتظار-نه: خطا
// انتظار-کد: ۰
تابع برچسب(ن: صحیح): رشته:
اگر ن % ۱۵ == ۰:
برگشت "فیزباز"
وگرنه ن % ۳ == ۰:
برگشت "فیز"
وگرنه ن % ۵ == ۰:
برگشت "باز"
پایان
برگشت "ساده"
پایان
تابع ریشه:
هر ن در [۳، ۵، ۷، ۱۵]:
سرچاپ ن، برچسب(ن)
پایان
پایانآزمونهای بررسی کامپایل با // انتظار-کد-خطا: E059 (در انگلیسی // EXPECT-CODE: E059) ادعا میکنند که کامپایلر یک پیام مشخص را گزارش میکند؛ پیامهای خطای خود کامپایلر به همین روش آزمایش میشوند. دستورهای دیگر آزمون را به یک سیستمعامل یا معماری محدود میکنند، مهلت تعیین میکنند یا خروجی را با یک پرونده مقایسه میکنند، و همانطور که در نمونهی بالا دیدید، هر دستور املای فارسی دارد. --run=NAME آزمونها را پالایش میکند، --format=teamcity و --report=FILE خوراک سیستمهای CI را فراهم میکنند، و --interp آزمونها را به جای کامپایل، روی مفسر اجرا میکند.
خط فرمان تعاملی REPL: salam cli
salam cli یک خط فرمان تعاملی باز میکند که تعریفها را میان خطها نگه میدارد و برای امتحان رفتار یک عبارت به کار میآید. گزینهی --lang=fa آن را برای سلام فارسی آماده میکند. یک جلسهی کوتاه:
Salam general REPL (lang=fa). ':help' for commands.
salam> ع := ۲۱
salam> سرچاپ ع * ۲
42
salam> تابع مربع(ن: صحیح): صحیح:
... برگشت ن * ن
... پایان
defined مربع.
salam> سرچاپ مربع(۹)
81تعریف دوبارهی یک تابع با همان نام جایگزینش میکند. salam page خط فرمان دومی برای زبان صفحه باز میکند. برای آزمایش سریع بدون نصب هیچ چیز، ویرایشگر برخط کامپایلر را به شکل WebAssembly در مرورگر اجرا میکند، و salam exec یک پرونده را بدون هیچ زنجیرهابزار C روی مفسر اجرا میکند؛ مقالهی salam run در برابر salam build این را شرح میدهد.
خطایابی و بررسی حافظه
salam build -g اطلاعات خطایابی DWARF را در برنامه جاسازی میکند (بهطور پیشفرض نسخهی ۵، و -gdwarf-4 برای خطایابهای قدیمیتر) و دستورهای خط تولید میکند، پس خطایاب سطرهای منبع سلام را نشان میدهد. salam debug app.salam به همین شکل میسازد و GDB یا LLDB را راه میاندازد. در VS Code، فشردن F5 در یک پروندهی سلام از salam dap میگذرد؛ پلی برای پروتکل آداپتور خطایابی (Debug Adapter Protocol) که gdb یا lldb-dap را با نقطههای توقف، اجرای گامبهگام و متغیرهای محلی در منبع سلام به کار میاندازد. این امکان هنوز روی ویندوز در دسترس نیست؛ آنجا با -g بسازید و مستقیم از یک خطایاب استفاده کنید.
برای خطاهای حافظه، salam memcheck app.salam با نمادهای خطایابی میسازد و اگر Valgrind نصب باشد برنامه را زیر آن اجرا میکند، وگرنه با AddressSanitizer از راه gcc یا clang میسازد. روی یک برنامهی کوچک، خلاصهی هیپ را گزارش کرد و با salam memcheck: no memory errors detected. تمام شد. پیش از همهی اینها، بررسیگر مالکیت کامپایلر دربارهی استفاده پس از آزادسازی، آزادسازی دوباره و اشارهگرهایی که از روی فراخوانیای که ممکن است وکتور را بزرگ کند نگه داشته شدهاند هشدار میدهد، و بررسیهای بازه، اندیسها، سرریزها و تقسیم بر صفرهایی را که بتواند ثابت کند علامت میزند. هر دو بهطور پیشفرض روشناند و در زمان اجرا هیچ هزینهای ندارند؛ مقالهی مدیریت حافظه در سلام آنها را توضیح میدهد.
نمایهسازی، مستندات و کمبودها
--time-report چاپ میکند خود کامپایلر، مرحلهبهمرحله، زمانش را کجا گذرانده و کندترین پروندههای ورودی کداماند، و --time-trace یک ردگیری Chrome مینویسد که در Perfetto باز میشود. اینها ابزار زمان کامپایلاند. نمایهساز (Profiler) برای برنامههای خودتان هنوز بخشی از زنجیرهابزار نیست: سند طراحی یک فرمان salam prof و یک چارچوب salam bench را توصیف میکند و هر دو را «طراحیشده اما پیادهنشده» علامت زده است. تا آن زمان، از نمایهسازهای سیستم (perf، Instruments، callgrind در Valgrind) روی برنامهای که با -g ساخته شده استفاده کنید.
salam doc یک پروژه را به یک صفحهی HTML خودبسنده از رابط برنامهنویسی عمومیاش تبدیل میکند. توضیحات مستندات، رشتهای از خطهای // یا ///، یا یک بلوک /** */، درست بالای یک اعلان هستند و برچسبهای @en و @fa اجازه میدهند یک توضیح هر دو زبان را داشته باشد، با یک کلید تغییر زبان در صفحهی ساختهشده.
چه چیزهایی کم است
فهرستی منصفانه برای زنجیرهابزاری که هنوز به نسخهی ۱٫۰ نرسیده:
- نمایهساز برنامه از خود ندارد، همانطور که گفته شد.
- خطایابی در Visual Studio Code روی ویندوز از راه
salam dapدر دسترس نیست. - ویرایشگرهای کوچکتر امکانات کمتری میگیرند. Kate، gedit، Notepad++، nano و micro رنگآمیزی دارند؛ امکانات سرور زبان به این بستگی دارد که ویرایشگر کلاینت LSP داشته باشد.
- یکپارچگی با ابزارهای شخص ثالث کمتر است. برخلاف Go یا Rust، اکوسیستمی از لینترها، سرویسهای پوشش کد یا افزونههای IDE که دیگران نوشته باشند وجود ندارد. بهویژه گزارش پوشش کد (Code Coverage) وجود ندارد.
ابزارهایی که هستند به شکلی کمنظیر با هم سازگارند، چون خودشان کامپایلرند. برای تصویر بزرگتر از اینکه بستهها، مستندات و مشارکتکنندگان چطور کنار هم قرار میگیرند، مقالهی ساختن اکوسیستم متنباز برای یک زبان را ببینید، و برای عادتهایی که کامپایلر را ساکت نگه میدارند، درس بهترین شیوهها را.
پرسشهای پرتکرار دربارهی ابزارهای زبان سلام
آیا زبان سلام سرور زبان (LSP) دارد؟
بله. salam lsp درون کامپایلر ساخته شده، پس نصب چیز اضافهای لازم نیست. به هر ویرایشگری که کلاینت LSP داشته باشد پیامهای خطا، تکمیل خودکار، hover، پیمایش کد، تغییر نام، قالببندی و اصلاح سریع میدهد، به انگلیسی و فارسی.
کدام ویرایشگرها از زبان سلام پشتیبانی میکنند؟
VS Code، Vim و Neovim، Sublime Text، Emacs، محیطهای JetBrains، Kate و KDevelop، Geany، gedit، Notepad++، nano و micro در مخزن افزونه دارند و یک دستور زبان tree-sitter هم هست. ویرایشگرهایی مثل Helix فقط با سرور زبان کار میکنند.
در زبان سلام چطور آزمون واحد (Unit Test) بنویسیم؟
تابعهای آزمون_ (یا test_ در برنامهی انگلیسی) را در پروندهای بگذارید که نامش به _test.salam ختم میشود، از ادعاهای بستهی آزمایش استفاده کنید و salam test را اجرا کنید. پروندههایی که تابع ریشه دارند را میتوان با توضیحهای // انتظار از روی خروجیشان آزمود.
آیا میتوان کد سلام را گامبهگام خطایابی کرد؟
بله. با -g بسازید و از GDB یا LLDB استفاده کنید، یا salam debug را اجرا کنید که هر دو کار را انجام میدهد. کاربران Visual Studio Code کلید F5 را میزنند که روی لینوکس و مک از آداپتور salam dap استفاده میکند.