Docs

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:

terminal
solan هلا.سو
CommandWhat it does
solan ملف.سوRun a file
solanInteractive mode (try commands line by line)
solan -e "اطبع(1+1)"Run code straight from the terminal
solan -vShow 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
TypeExampleNote
text"مرحبا"Between quotes, and supports ${expression} inside
number42 and 3.14All 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
FunctionWhat 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:

LibraryPurpose
شبكة ويبسوكت سوكت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
MeaningStandardSynonyms
variableخلمتغير ليكن اجعل
functionدالةوظيفة اجراء
conditionاذاإذا لو في_حال
loopلكللاجل من_اجل
returnارجعاعد رجع
printاطبعاعرض اظهر

To normalize code that mixes synonyms into the standard words:

terminal
solan fmt ملف.سو --قياسي -w

Tools

Command-line tools

CommandWhat it does
solan ملف.سوRun a file
solan --check ملف.سوCheck syntax without running
solan fmt ملف.سو -wFormat 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 lspLanguage server for editors (completion and errors)
make doctorCheck 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).

terminal
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).

terminal
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:

solan.json
{
  "الاسم": "بوتي",
  "النسخة": "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.