برنامههای زبان برنامهنویسی سلام میتوانند به شکل فایل اجرایی بومی، ماژول WebAssembly، جاوااسکریپت در مرورگر، گردانندهی درخواست روی Cloudflare Workers، درون یک ویرایشگر آنلاین و روی سختافزار خام بدون سیستمعامل اجرا شوند. برای کامپایل سلام به WebAssembly با --target=wasm32-wasi میسازید و برای کامپایل به جاوااسکریپت از salam js استفاده میکنید. زبان و کتابخانهی استاندارد یکی میماند، ولی هر مقصد دربارهی نخها، پروندهها، سوکتها و عددها قاعدههای خودش را دارد، و همین قاعدهها تعیین میکنند کدام هدف برای کدام کار مناسب است.
این نوشته همهی هدفهای موجود امروز را مرور میکند، فرمانها را نشان میدهد و میگوید هر کدام هنوز چه کاری از دستش برنمیآید.
برنامههای سلام کجا اجرا میشوند؟
| هدف | فرمان | محل اجرا | مناسب برای |
|---|---|---|---|
| فایل اجرایی بومی | salam build app.salam | لینوکس، مک، ویندوز (x86-64، ARM، ۳۲ بیتی) | ابزارها، سرورها، برنامههای رومیزی |
| WebAssembly (WASI) | salam build app.salam --target=wasm32-wasi | هر محیط اجرای WASI preview 1 | ماژولهای جعبهشنی، افزونهها، تابعهای لبه |
| Cloudflare Workers | ساخت WASI بهعلاوهی یک worker.js کوچک | لبهی شبکهی Cloudflare | گردانندههای HTTP نزدیک کاربران |
| جاوااسکریپت | salam js app.salam | مرورگرها و Node.js | صفحههای تعاملی، منطق سمت مرورگر |
| پروژهی وب | salam web | مرورگرها | یک صفحه و اسکریپتهای سلامش در یک ساخت |
| ویرایشگر آنلاین | editor.salamlang.ir | هر مرورگر امروزی | یادگیری، اشتراکگذاری، آزمایش سریع |
| محیط توسعهی موبایل | برنامههای اندروید و iOS | گوشی و تبلت | نوشتن و اجرای کد بدون اینترنت |
| مستقل (Freestanding) | --target=x86_64-unknown-none | بدون سیستمعامل | هستهها و برنامههای راهاندازی |
هدفهای بومی نوشتهی جداگانهای دارند: کامپایل متقابل در سلام. بقیهی این نوشته دربارهی هدفهای دیگر است.
برنامهی زیر بی هیچ تغییری روی سه تا از این هدفها اجرا میشود. با پرچمهای زمان کامپایل میگوید کجا اجرا شده است: سلام جاوااسکریپت (در کد انگلیسی SALAM_JS) فقط وقتی تعریف شده است که پشتیبان جاوااسکریپت پرونده را کامپایل کند، و سلام معماری وباسمبلی (در انگلیسی SALAM_ARCH_WASM) در ساختهای WebAssembly درست است.
تابع اول است(ن: صحیح): منطقی:
اگر ن < ۲: برگشت نادرست پایان
ناپایا مقسومعلیه := ۲
تا مقسومعلیه * مقسومعلیه <= ن:
اگر ن % مقسومعلیه == ۰: برگشت نادرست پایان
مقسومعلیه++
پایان
برگشت درست
پایان
تابع ریشه:
ناپایا یافتهها := ""
ناپایا شمار := ۰
تکرار ۲ تا ۵۰ در ن:
اگر اول است(ن):
یافتهها += " " + ن
شمار++
پایان
پایان
سرچاپ "اعداد اول کمتر از ۵۰:" + یافتهها
سرچاپ "تعداد:"، شمار
اگر تعریفشده(سلام جاوااسکریپت):
سرچاپ "در حال اجرا به شکل جاوااسکریپت"
وگرنه سلام معماری وباسمبلی:
سرچاپ "در حال اجرا به شکل وباسمبلی"
وگرنه:
سرچاپ "در حال اجرا به شکل کد بومی"
پایان
پایاناگر با salam js کامپایل و با Node اجرا شود، خط آخر در حال اجرا به شکل جاوااسکریپت است؛ اگر با --target=wasm32-wasi ساخته و با پشتیبانی WASI در Node اجرا شود، در حال اجرا به شکل وباسمبلی. دو خط اول همهجا یکسان است.
کامپایل زبان سلام به WebAssembly با WASI
WebAssembly یک قالب دودویی قابلحمل است که درون یک جعبهشنی (Sandbox) اجرا میشود. بهتنهایی هیچ راهی برای چاپ کردن، خواندن پرونده یا نگاه کردن به ساعت ندارد؛ WASI (میانجی سیستمی WebAssembly) اینها را به شکل مجموعهی کوچکی از تابعهای واردشده اضافه میکند. سلام از راه پشتیبان LLVM خود، WASI preview 1 را هدف میگیرد:
salam build primes.salam --target=wasm32-wasi --release --output=primes.wasmنسخههای رسمی کامپایلر یک sysroot برای WASI با خود دارند و پیونددهندهی LLD را درون همان فرایند اجرا میکنند، پس برای برنامهای مانند برنامهی بالا نصب هیچ چیز دیگری لازم نیست. گردشکار انتشار در هر نسخه این را میسنجد: یک برنامهی کوچک را در حالی میسازد که WASI_SDK_PATH تعریف نشده و PATH کمینه است، و سپس آن را با ماژول درونی node:wasi در Node اجرا میکند. اسکریپت اجرای آن در Node چند خط بیشتر نیست:
const { WASI } = require('node:wasi');
const fs = require('node:fs');
const wasi = new WASI({
version: 'preview1',
args: ['primes'],
returnOnExit: true,
});
WebAssembly.instantiate(
fs.readFileSync('primes.wasm'),
wasi.getImportObject(),
).then(({ instance }) => process.exit(wasi.start(instance)));ماژول برنامهی اعداد اول حدود ۳۰ کیلوبایت است. برنامههایی که بخش بیشتری از محیط اجرا را به کار میگیرند به بخش بیشتری از کتابخانهی C نیاز دارند: برنامهای که پشتهی سرور HTTP را به کار میبرد پشتیبانی نخ هم لازم دارد، و sysroot مربوط به WASI که در نسخهی ۰٫۶٫۲ جاسازی شده بود نمیتوانست چنین برنامهای را پیوند دهد (پیونددهنده نبود libpthread را گزارش میکرد). برای این برنامهها wasi-sdk را نصب کنید؛ کامپایلر آن را از راه WASI_SDK_PATH، ~/.salam/wasi-sdk یا /opt/wasi-sdk پیدا میکند.
اینکه یک ماژول WASI چه کارهایی میتواند بکند را میزبانش تعیین میکند. خروجی و خطای استاندارد، آرگومانها، متغیرهای محیطی، ساعتها و عددهای تصادفی بخشی از preview 1 هستند. پروندهها فقط وقتی دیده میشوند که میزبان یک پوشه در اختیار ماژول بگذارد، و ماژول نمیتواند خودش اتصال شبکه باز کند.
زبان سلام روی Cloudflare Workers
Cloudflare Workers میتواند ماژولهای WebAssembly را اجرا کند و سلام از همین برای بردن یک برنامهی کامل net/http به لبهی شبکه استفاده میکند. خود برنامه یک سرور وب معمولی سلام است که با net/router ساخته شده:
واردسازی شبکه.اچ تی تی پی
واردسازی شبکه.مسیریاب
تابع سلام کن(زمینه: صحیح۶۴):
کجا := اچ تی تی پی.لبه است() ? "در لبهی شبکه" : "روی یک سرور"
اچ تی تی پی.متن(زمینه، "سلام " + اچ تی تی پی.پارامتر(زمینه، "name") + "، از سلام " + کجا + "\n")
پایان
تابع ریشه:
ناپایا م := مسیریاب.مسیریاب جدید()
مسیریاب.دریافت(م، "/hello/:name"، سلام کن)
ناپایا س := اچ تی تی پی.سرور جدید(۸۰۸۰)
اچ تی تی پی.استفاده(س، م)
اچ تی تی پی.اجرا(س)
پایاناگر به شکل بومی ساخته شود، روی درگاه ۸۰۸۰ گوش میدهد. اگر با --target=wasm32-wasi ساخته شود، رفتار اچ تی تی پی.اجرا (در انگلیسی http.Run) عوض میشود: در هر فرایند دقیقاً یک درخواست را پاسخ میدهد. اسکریپت میزبان std/net/http/cloudflare/worker.js درخواست خام HTTP/1.1 را در ورودی استاندارد ماژول مینویسد و پاسخ را از توصیفگر پروندهی ۳ میخواند؛ خروجی سرچاپ به لاگهای Worker میرود و os.Env متغیرها و رازهای wrangler را میخواند. نقطهی ورود Worker سه خط است:
import { createWorker } from './worker-host.js';
import app from './build/app.wasm';
export default createWorker(app);محدودیتها از خود این مدل میآیند. هر درخواست یک نمونهی تازه میگیرد، پس متغیرهای سراسری میان درخواستها باقی نمیمانند. سیستم پروندهای در کار نیست، پس router.Static چیزی برای سرو کردن ندارد (از داراییهای ایستای Workers استفاده کنید). سوکت خروجی وجود ندارد، پس http.Get نمیتواند از درون ماژول سرور دیگری را صدا بزند؛ به جای آن چند خط fetch در worker.js این کار را انجام میدهد. فضای ذخیرهسازی Workers مانند KV، D1 و R2 ناهمگام است و باید از راه اسکریپت میزبان رد و بدل شود.
مخزن چهار برنامهی کامل Workers را در tests/en/apps/ نگه میدارد: cloudflare-worker-hello، -router، -telegram-bot و -pure، که آخری برنامهای به سبک CGI است که درخواست را از متغیرهای محیطی میخواند و اصلاً به net/http نیازی ندارد. برنامهی سبک CGI با sysroot جاسازیشدهی WASI پیوند میخورد؛ برنامههای net/http، همانطور که بالاتر گفته شد، wasi-sdk لازم دارند.
کامپایل زبان سلام به جاوااسکریپت با salam js
برای کدی که باید در زبانهی مرورگر اجرا شود و با صفحه کار کند، سلام یک پشتیبان جاوااسکریپت دارد. این پشتیبان برنامه را به یک پروندهی جاوااسکریپت تبدیل میکند که به طور پیشفرض فشرده (Minified) است، یا اگر نام خروجی به .html ختم شود، به یک صفحهی HTML خودکفا:
salam js counter.salam --output=counter.js
salam js counter.salam --output=counter.htmlبستهی dom (به فارسی دام) دسترسی کد سلام به صفحه را فراهم میکند: ساختن عنصرها، تعیین متن و ویژگیها، سبکها، کلاسها، رویدادها، زمانسنجها و قابهای پویانمایی.
واردسازی دام
ناپایا کلیکها := ۰
ناپایا برچسب := ۰ برگردان صحیح۶۴
تابع ریشه:
برچسب = دام.بساز("p")
دام.تنظیم متن(برچسب، "هنوز کلیکی نشده است")
دکمه := دام.بساز("button")
دام.تنظیم متن(دکمه، "کلیک کنید")
دام.هنگام رویداد(دکمه، "click"، ():
کلیکها++
دام.تنظیم متن(برچسب، کلیکها + " بار کلیک شد")
پایان)
دام.افزودن(دام.بدنه()، دکمه)
دام.افزودن(دام.بدنه()، برچسب)
پایاناگر به counter.html کامپایل شود، کل صفحه همراه با اسکریپتش کمتر از ۱ کیلوبایت است. کد تولیدشده جاوااسکریپت ساده است: نه WebAssembly، نه کتابخانهی زمان اجرایی که باید دانلود شود، و فراخوانیهایی مانند دام.تنظیم متن مستقیم به عملیات DOM تبدیل میشوند. پوشهی tests/en/js حدود صد برنامه دارد که با salam js کامپایل و اجرا میشوند و خروجیشان با خروجی مورد انتظار مقایسه میشود.
پشتیبان جاوااسکریپت محدودیتهایی دارد که از خود جاوااسکریپت میآیند:
- عددهای صحیح ۶۴ بیتی. عددهای جاوااسکریپت فقط تا
2^53عدد صحیح را دقیق نگه میدارند. روی این پشتیبان یکصحیح۶۴بزرگتر از این حد دقتش را از دست میدهد:9007199254740993 + 2به شکل بومی9007199254740995و در جاوااسکریپت9007199254740994چاپ میکند.صحیح۱۲۸وطبیعی۱۲۸دقیقاند، چون به BigInt تبدیل میشوند. - نخها.
نخساز(در انگلیسیspawn) روی این پشتیبان پشتیبانی نمیشود و تابعهای اتمی به خواندن و نوشتن ساده تبدیل میشوند، چون کد تولیدشده روی تنها نخ صفحه اجرا میشود. - دسترسی به سیستم. پروندهها، سوکتها و فرایندها مال هدفهای بومی و WASI هستند. در مرورگر، بستهی
domصفحه، رویدادها، زمانسنجها، ذخیرهسازی محلی، بوم (Canvas) و دسترسی عمومی به ویژگیها و متدهای هر شیء جاوااسکریپت را در اختیارتان میگذارد.
پروژههای وب: salam web و زبان صفحه
سلام یک زبان صفحه هم دارد: یک بلوک صفحه: (در انگلیسی page:) عنصرهای HTML، سبکهای CSS و رویدادها را با نحو سلام توصیف میکند و salam page build آن را به HTML، CSS و جاوااسکریپت تبدیل میکند. salam web یک پروژهی وب کامل، یعنی یک صفحه بهعلاوهی برنامههای سلامی که با script: src="app.salam" بار میکند، را در یک پروندهی .html میسازد، یا با --split در پروندههای جداگانهی .html، .css و .js. نوشتهی توسعهی وب با سلام و درسهای آموزش از متن صفحه به بعد زبان صفحه را آموزش میدهند.
ویرایشگر آنلاین سلام: کامپایلر در زبانهی مرورگر
ویرایشگر آنلاین سلام را بدون سرور و بدون نصب اجرا میکند و نمونهی خوبی از همکاری این هدفها با هم است:
- کامپایلر سلام (تحلیلگر واژگانی، تجزیهگر، تحلیل معنایی، مفسر و موتور چیدمان) با
--backend=c --emit-c --target=wasm32-unknown-emscriptenبه C تبدیل و با Emscripten به WebAssembly کامپایل میشود، همراه با نسخهی فشردهای از کتابخانهی استاندارد. - رابط کاربری خود ویرایشگر به زبان سلام نوشته شده است:
page.salamساختار و سبکها را نگه میدارد و باsalam webساخته میشود، وapp.salamکه وضعیت، رنگآمیزی نحوی، میانبرهای صفحهکلید و راهاندازی WebAssembly را بر عهده دارد، باsalam jsکامپایل میشود. - برنامههایی که در ویرایشگر نوشته میشوند در مفسرِ درون ماژول WebAssembly اجرا میشوند، چون مرورگر نمیتواند کامپایلر C اجرا کند یا یک فایل اجرایی بومی تازهساخته را بار کند.
چون برنامهها در مفسر اجرا میشوند، ویرایشگر برای یادگیری و بررسی منطق ایدهآل است، ولی کدی که نخ، FFI یا سرعت کامل بومی لازم دارد باید با کامپایلر روی دستگاه خودتان ساخته شود. نوشتهی تفاوت salam run و salam build مفسر را توضیح میدهد.
اندروید، iOS و سختافزار خام
پروژههای android/ و ios/ در مخزن، برنامههای محیط توسعهی بومی برای گوشیاند. هر دو کل کامپایلر را در خود دارند که از یک نقطهی ورود کتابخانهای به C تبدیل شده است، پس رنگآمیزی، بررسی و اجرا بدون اینترنت کار میکنند. برنامهها در مفسر اجرا میشوند: نه اندروید و نه iOS اجازه نمیدهند یک برنامه فایل اجراییای را که همین الان ساخته اجرا کند، پس salam build فرمانی برای رایانهی رومیزی میماند. برنامهی اندروید از اندروید ۵٫۰ به بعد و برنامهی iOS از iOS 15 به بعد را پشتیبانی میکند. فایل APK اندروید فقط وقتی به یک نسخه پیوست میشود که آن نسخه یکی منتشر کرده باشد.
در سوی دیگر، هدف *-unknown-none برنامهای بدون سیستمعامل، بدون libc و بدون فراخوانی سیستمی میسازد، برای هستهها و برنامههای راهاندازی. کامپایلر یک محیط اجرای کوچک با یک تخصیصدهندهی حافظه و خروجی سریال، VGA و کنسول خطایابی QEMU را پیوند میدهد، و tests/kernel/ زیر QEMU بالا میآید. این هدف نوشتهی جداگانهای دارد: سلام مستقل و سختافزار خام.
برای برنامههای رومیزی با رابط وب، سلام به جای WebAssembly از WebView سیستم استفاده میکند؛ نوشتهی برنامههای رومیزی و گرافیکی با سلام را ببینید.
کدام هدف را انتخاب کنیم؟
| نیاز شما | انتخاب |
|---|---|
| بیشترین سرعت، نخها، پروندهها، سوکتها، کتابخانههای C | ساخت بومی |
| ماژولی جعبهشنی که میزبان بتواند جاسازی و محدودش کند | wasm32-wasi |
| یک گردانندهی HTTP در لبهی شبکه بدون نگهداری سرور | wasm32-wasi روی Cloudflare Workers |
| منطقی که در مرورگر بازدیدکننده اجرا شود و صفحه را تغییر دهد | salam js |
| وبسایتی ایستا یا تعاملی که به زبان سلام نوشته شده باشد | زبان صفحه با salam page build یا salam web |
| یادگیری یا اشتراکگذاری یک تکه کد | ویرایشگر آنلاین |
| یک سیستمعامل یا برنامهی سختافزار خام | یک هدف مستقل *-none |
الگوی معقول برای یک محصول وب این است که منطق مشترک (اعتبارسنجی، قالببندی، محاسبههای خالص) را در بستههای سادهی سلام و بدون فراخوانی سیستمی نگه دارید، آن را برای سرور به شکل بومی و برای مرورگر با salam js کامپایل کنید، و کد وابسته به سکو را در لبهها و پشت اگر تعریفشده(سلام جاوااسکریپت): یا اگر سلام معماری وباسمبلی: بگذارید.
پرسشهای پرتکرار
آیا زبان سلام به WebAssembly کامپایل میشود؟
بله. salam build app.salam --target=wasm32-wasi یک ماژول WASI preview 1 میسازد. نسخههای رسمی کامپایلر sysroot و پیونددهندهی لازم برای برنامههای ساده را دارند؛ برنامههایی که پشتهی سرور HTTP را به کار میبرند به نصب wasi-sdk نیاز دارند.
آیا سلام به جاوااسکریپت کامپایل میشود؟
بله. salam js app.salam یک پروندهی جاوااسکریپت فشرده مینویسد، یا اگر خروجی به .html ختم شود، یک صفحهی HTML کامل. بستهی dom دسترسی به عنصرها، رویدادها و زمانسنجها را فراهم میکند. روی این پشتیبان، عددهای صحیح۶۴ بالاتر از 2^53 دقتشان را از دست میدهند.
آیا میشود بدون نصب هیچ چیز، سلام را در مرورگر اجرا کرد؟
بله. ویرایشگر آنلاین در editor.salamlang.ir کامپایلر و مفسر را به شکل WebAssembly درون صفحه اجرا میکند و رابط انگلیسی، فارسی و عربی دارد. برنامهها آنجا در مفسر اجرا میشوند، پس محاسبههای خیلی سنگین را بهتر است به شکل بومی بسازید.
آیا زبان سلام روی Cloudflare Workers اجرا میشود؟
بله. برنامهای با net/http و net/router که برای wasm32-wasi ساخته شده، از راه یک اسکریپت میزبان که همراه کتابخانهی استاندارد عرضه میشود، در هر نمونه یک درخواست را پاسخ میدهد. متغیرهای سراسری میان درخواستها نمیمانند و فراخوانیهای بیرونی از راه fetch در اسکریپت میزبان انجام میشوند.