Learn Solan
From your first line to packages and permissions. Every example here was actually run, with its output below.
Getting started
What is Solan?
Solan is a programming language written in Arabic: its keywords, functions and libraries are all Arabic. It runs on a bytecode interpreter written in C with a JIT compiler, and ships as a single executable with a broad standard library (web, databases, JSON, regular expressions, compression…).
- Source files end in
.سوor.sn. - Text is written right to left, and Arabic digits work too (٣ + ٤).
- Comments:
//for one line and/* ... */for several. - The Arabic comma «،» is accepted in arguments and lists, alongside the regular comma.
Getting started
Your first program
Create a file named هلا.سو:
اطبع("هلا يا عالم")
خل اسم = "سولان"
اطبع("مرحبا ${اسم}، ٢+٣ = ${2+3}")Output
هلا يا عالم مرحبا سولان، ٢+٣ = 5
Then run it from the terminal:
solan هلا.سو| Command | What it does |
|---|---|
solan ملف.سو | Run a file |
solan | Interactive mode (try commands line by line) |
solan -e "اطبع(1+1)" | Run code straight from the terminal |
solan -v | Show the version |
Language basics
Variables and types
خل declares a variable you can change; ثابت declares a value that never changes. The نوع() function tells you the type of any value.
خل اسم = "سولان"
خل عمر = 3
ثابت باي = 3.14159
خل فعال = صح
خل لا_شيء = عدم
اطبع(نوع(اسم), نوع(عمر), نوع(فعال), نوع([1]), نوع({"أ": 1}))
اطبع("بعد سنتين عمرك ${عمر + 2}")Output
نص رقم منطقي قائمة قاموس بعد سنتين عمرك 5
| Type | Example | Note |
|---|---|---|
| text | "مرحبا" | Between quotes, and supports ${expression} inside |
| number | 42 and 3.14 | All numbers are double-precision floats |
| boolean | صح / غلط | true / false |
| list | [1, 2, 3] | |
| dictionary | {"اسم": "ليلى"} | Text keys |
| nothing | عدم | Means “no value” |
There is no implicit conversion between text and numbers. Use ${}, نص() or رقم(): رقم("42") + 1 gives 43.
Language basics
Operators
اطبع(10 / 4, 10 % 3, 2 ** 10)
خل أ = 7
اطبع(أ > 5 و أ < 10) // و
اطبع(أ == 3 او أ == 7) // او
اطبع(ليس (أ == 7)) // ليس
خل اسم = عدم
اطبع(اسم ?? "زائر") // قيمة افتراضية إن كان عدمOutput
2.5 1 1024 صح صح غلط زائر
For safe access to a property that may not exist use ?.: the expression ن?.ا gives عدم instead of an error when ن is empty.
Language basics
Conditions
خل درجة = 85
اذا درجة >= 90 {
اطبع("ممتاز")
} والا_اذا درجة >= 70 {
اطبع("جيد")
} والا {
اطبع("حاول مرة أخرى")
}Output
جيد
No parentheses are needed around the condition. To choose between many values use تبديل; a حالة can take several values separated by commas:
دالة وصف(ن) {
تبديل ن {
حالة 1، 2 { ارجع "صغير" }
حالة 3 { ارجع "ثلاثة" }
افتراضي { ارجع "غير معروف" }
}
}
اطبع(وصف(1), وصف(3), وصف(9))Output
صغير ثلاثة غير معروف
Language basics
Loops
// من 0 إلى 2
لكل س في مدى(3) { اطبع("س =", س) }
// من 0 إلى 10 بخطوة 5
لكل س في مدى(0, 10, 5) { اطبع(س) }
// طالما الشرط صحيح
خل ع = 0
طالما ع < 3 { ع += 1 }
// تكرار عدد من المرات
كرر 2 { اطبع("تكرار") }
// استمر تتخطى العنصر، وتوقف تنهي الحلقة
لكل ي في [1, 2, 3, 4, 5] {
اذا ي == 2 { استمر }
اذا ي == 4 { توقف }
اطبع(ي)
}Output
س = 0 س = 1 س = 2 0 5 تكرار تكرار 1 3
مدى is lazy: مدى(0, 30000000) inside لكل doesn’t build a list in memory. To walk a dictionary with its key and value together:
لكل ك، ق في {"أ": 1, "ب": 2} { اطبع(ك, ق) }Output
أ 1 ب 2
Language basics
Functions
دالة تحية(اسم, لغة = "ع") { ارجع "أهلا " + اسم }
اطبع(تحية("سامي"))
// دالة سهمية
خل مربع = س => س * س
اطبع(مربع(5))
// عدد مفتوح من المعاملات
دالة جمع(...ارقام) { ارجع ارقام.اطوي((م, س) => م + س, 0) }
اطبع(جمع(1, 2, 3, 4))Output
أهلا سامي 25 10
- Default parameter value:
لغة = "ع". ...ارقامgathers the remaining arguments into a list, and...قائمةat a call site spreads a list out.- Functions are ordinary values: store them in variables and pass them to other functions.
Names such as و, او, نفي, عدم and نهاية are reserved and can’t be parameter names.
Data
Lists and dictionaries
خل ق = [5, 3, 8]
ق.اضف(1)
اطبع(ق.طول, ق[0], ق[-1])
اطبع([1, 2, 3, 4, 5, 6].رشح(ي => ي % 2 == 0).حول(ي => ي * ي).مجموع())
خل مرتبة = [5, 3, 8, 1]
مرتبة.رتب()
اطبع(مرتبة, مرتبة.يحتوي(3), مرتبة.ابحث(8))Output
4 5 1 56 [1، 3، 5، 8] صح 3
| Function | What it does |
|---|---|
اضف(ع) | Append an item |
رشح(د) | Items that pass the test |
حول(د) | Transform every item |
اطوي(د, بداية) | Reduce the list to one value |
رتب() اعكس() | Sort and reverse (they modify the list itself) |
جزء(من, الى) يحتوي(ع) ابحث(ع) | Slice, membership test, index of an item |
مجموع() طول | Sum of numbers, and number of items |
Dictionaries: a missing key raises an error, so use .احصل(key, default) or the word في to test.
خل شخص = {"اسم": "ليلى", "عمر": 25}
شخص["بلد"] = "مصر"
اطبع(شخص.اسم, شخص.مفاتيح())
اطبع(شخص.احصل("هاتف", "غير مسجل"), "اسم" في شخص)Output
ليلى ["اسم"، "عمر"، "بلد"] غير مسجل صح
Destructuring and swapping values:
خل [ص, ع] = [1, 2]
[ص, ع] = [ع, ص]
اطبع(ص, ع)
خل ك = [1, 2]
خل كبيرة = [0, ...ك, 3] // نشر القائمةOutput
2 1
Data
Text
خل س = "مرحبا"
اطبع(س.قسم("ر"), س.يحتوي("رح"), س.ابدل("م", "ك"))
اطبع("اب".كرر(3), "سولان".يبدأ_ب("سو"), " نص ".نظف())
اطبع(نسق("{:.2f} ثانية", 1.23456))Output
["م"، "حبا"] صح كرحبا اباباب صح نص 1.23 ثانية
Joining text with + inside a long loop is slow (it copies each time). To build a large string use نصوص.بناء(), which is linear.
Object-oriented
Classes and inheritance
صنف حيوان {
بناء(اسم) { هذا.اسم = اسم }
صوت() { ارجع "..." }
عرف() { اطبع(هذا.اسم + " يقول " + هذا.صوت()) }
}
صنف قط يرث حيوان {
صوت() { ارجع "مياو" }
}
قط("ميمي").عرف()Output
ميمي يقول مياو
بناءis the constructor andهذاis the current object.يرثis inheritance, andالابreaches the parent class.- Create an object by calling the class like a function:
قط("ميمي").
Object-oriented
Errors
حاول {
ارمي "حدث خطأ"
} امسك (خ) {
اطبع("أمسكت:", خ)
} اخيرا {
اطبع("انتهى")
}
حاول { خل ز = 1 / 0 } امسك (خ) { اطبع(خ) }Output
أمسكت: حدث خطأ انتهى القسمة على صفر (سطر 9)
Error messages in Solan are in Arabic, show the line with a code excerpt, and suggest the right name if you mistype a function.
Object-oriented
Enums and optional types
تعداد اللون { احمر، اخضر = 5، ازرق }
اطبع(اللون.احمر, اللون.اخضر, اللون.ازرق)Output
0 5 6
Types are optional: add them when you want extra safety. Checks happen at runtime on declaration, assignment, function entry and return.
خل س: رقم = 5
دالة ضعف(أ: رقم): رقم { ارجع أ * 2 }
اطبع(ضعف(س))
خل ص: رقم = "نص" // خطأOutput
10 خطأ: خطأ في النوع: المتغير 'ص' يتوقع 'رقم' لكن وجدت 'نص' (سطر 5)
Known types: رقم, عدد_صحيح, نص, منطقي, قائمة, قاموس, دالة, عدم, اي, and class names. Combine with رقم | نص, and mark optional with نص?.
Advanced
Asynchronous programming
غير_متزامن دالة مهمة(اسم, ثواني) {
انتظر نم(ثواني)
ارجع "انتهت ${اسم}"
}
غير_متزامن دالة رئيسي() {
خل نتائج = انتظر كل_الوعود([مهمة("أ", 0.1), مهمة("ب", 0.1)])
اطبع(نتائج)
}
شغل(رئيسي)Output
["انتهت أ"، "انتهت ب"]
To spread heavy work across CPU cores use the عمال library (isolated subprocesses); for light work, انتظر عملية(function).
Libraries
Imports and the standard library
استورد "جيسون"
استورد "رياضيات"
استورد "ملفات"
خل بيانات = {"اسم": "سامي", "نقاط": [1, 2, 3]}
خل نص_ج = جيسون.ترميز(بيانات)
اطبع(نص_ج)
اطبع(جيسون.فك(نص_ج).نقاط)
اطبع(رياضيات.جذر(144), رياضيات.اكبر(3, 9))
ملفات.اكتب("مرحبا.txt", "سطر أول")
اطبع(ملفات.اقرا("مرحبا.txt"), ملفات.موجود("مرحبا.txt"))Output
{"اسم":"سامي","نقاط":[1,2,3]}
[1، 2، 3]
12 9
سطر أول صحThese libraries are built into the executable and imported by name:
| Library | Purpose |
|---|---|
شبكة ويبسوكت سوكت | HTTP, servers, WebSocket and TCP (with TLS) |
قاعدة | SQLite databases |
جيسون نمط ضغط | JSON, regular expressions, gzip/zlib |
ملفات مسارات نظام | Files, paths and the system environment |
رياضيات عشوائي ضخم زمن تواريخ | Math, big numbers, time and the Hijri calendar |
نصوص جداول ألوان عرض | Text processing, CSV, terminal colors and number formatting |
سجل اختبار وسائط عمال | Logging, tests, command-line options, parallel workers |
مجموعات ذاكرة مخطط قوالب نتيجة بث | Data structures, caching, data validation, templates, result/option, events |
Libraries
Synonyms
Every Solan keyword has synonyms that mean exactly the same, so you can write with the word you’re used to. Hamza forms (أ/إ/ا), ة/ه and diacritics don’t matter.
متغير س = 5
وظيفة ضعف(ن) { ارجع ن * 2 }
لو س > 3 { اعرض(ضعف(س)) }Output
10
| Meaning | Standard | Synonyms |
|---|---|---|
| variable | خل | متغير ليكن اجعل |
| function | دالة | وظيفة اجراء |
| condition | اذا | إذا لو في_حال |
| loop | لكل | لاجل من_اجل |
| return | ارجع | اعد رجع |
اطبع | اعرض اظهر |
To normalize code that mixes synonyms into the standard words:
solan fmt ملف.سو --قياسي -wTools
Command-line tools
| Command | What it does |
|---|---|
solan ملف.سو | Run a file |
solan --check ملف.سو | Check syntax without running |
solan fmt ملف.سو -w | Format the code and write it back |
solan --لون ملف.سو | Show code colorized (--html to export) |
solan --تتبع ملف.سو | Print every line as it executes |
solan --تصحيح ملف.سو | Interactive step-by-step debugger |
solan lsp | Language server for editors (completion and errors) |
make doctor | Check your environment before installing |
The VS Code extension for highlighting and completion lives in the editor/vscode folder of the language repo.
Tools
Permissions
You can run a script you don’t trust and decide exactly what it may do. Categories: قراءة (read), كتابة (write), شبكة (network), تنفيذ (execute), بيئة (environment), اصلي (native).
solan --اسمح-قراءة=/بيانات --اسمح-كتابة=/مخرجات سكربت.سو
solan --اسمح-شبكة=api.example.com سكربت.سو
solan --امنع=تنفيذ,شبكة سكربت.سوTools
Packages and libraries from GitHub
There is no central registry: any GitHub repository can be a library. Libraries are stored once in the user’s folder and shared across your projects. Your project holds solan.json (what you want) and solan.lock (exactly what you got).
solan init # ينشئ solan.json
solan add صاحب/مستودع # يضيف مكتبة من جيتهاب
solan add صاحب/مستودع@^1.2.0 # بنطاق إصدارات
solan install # يثبّت ما في solan.json
solan install --frozen # حسب القفل فقط (للتكامل المستمر)
solan verify # يتحقق من بصمات المكتباتVersions are git tags in the form vX.Y.Z. Then you import them by name:
{
"الاسم": "بوتي",
"النسخة": "0.1.0",
"الاعتماديات": {
"ادوات": "صاحب/مستودع@^1.2.0"
}
}Want to publish your library? Push it to GitHub with a version tag, then add it on the Libraries page here.