# لغة الحساب المصغرة (التذاكر 15 و16 و17)

لغة صغيرة مكتوبة بالكامل بلغة ASPL. تقرأ برنامجا واحدا من ملف، وتحوّله
إلى رموز ثم تحلّله، وتنفّذه بحساب صحيح ومتغيرات مسماة وتحكم في التدفق،
وتبلّغ عن أخطاء المصدر مع مواضع الأسطر والأعمدة بالوحدات القياسية
(scalar)، وتولّد مصدر ASPL قابلا للتشغيل بمخرجات مطابقة. التذكرة 15
قدّمت تفسير التعبيرات؛ والتذكرة 16 وسّعت البرنامج نفسه بالمتغيرات
والتحكم في التدفق؛ والتذكرة 17 توسّعه بتوليد مصدر ASPL.

## الملفات

- `mini-arith.aspl` — التطبيق في ملف واحد: المحوّل إلى رموز، والمحلّل،
  وتخزين عقد الصياغة، وجدول المتغيرات، والمفسّر، ومولّد مصدر ASPL،
  ومشغّل الملف والعملية.
- `calc.txt` — مثال تعبير صحيح: `(2 + 3) * 4 - 10 / 2`، نتيجته `15`
  (صيغة التذكرة 15، وما تزال مقبولة).
- `calc-error.txt` — مثال تعبير مشوّه: `2 + * 3`، يفشل عند السطر 1
  والعمود 5.
- `calc-unicode.txt` — مثال مختلط المحارف: معرّف بحروف عربية متبوع
  برمز تعبيري، يفشل بالرمز `invalid-character` عند السطر 1 والعمود 6
  (بالعدّ القياسي؛ العدّ بالبايتات كان سيذكر العمود 11).
- `calc-name.txt` — مثال المعرّف: `7 + متغير`، يفشل بالرمز
  `syntax-error` عند السطر 1 والعمود 5. التعبير المجرّد يحتفظ بسلوك
  التذكرة 15: المعرّفات تحوّل إلى رموز مع الاحتفاظ بتهجئتها لكنها
  مرفوضة في موضع التعبير.
- `demo.txt` — عرض العبارات: يصرّح بـ `i` و`مجموع` و`limit`، ويجمع 1
  حتى 10 في حلقة `while`، ويختار فرع `if` عند `مجموع == 55`، ويطبع `55`.
- `branch.txt` — مثال فرع `else` بفواصل `.`: عند `x = 3` يُؤخذ الجانب
  `else` فيطبع `2`، فلا يُنفَّذ جانب `then` أبدا.
- `undeclared.txt` — خطأ متغير: إسناد إلى `y` مع التصريح بـ `x` فقط،
  يفشل بالرمز `undeclared-variable` عند السطر 2 والعمود 1 مع ذكر `y`.
- `duplicate.txt` — خطأ متغير: تصريح مكرر بـ `x`، يفشل بالرمز
  `duplicate-variable` عند السطر 1 والعمود 8 مع ذكر `x`.
- `badstmt.txt` — عبارة مشوّهة: `x = ;` تفشل بالرمز `syntax-error`
  عند السطر 2 والعمود 5.

## القواعد

```text
program     := expression | var_section? statement+
expression  := term (("+" | "-") term)*
term        := factor (("*" | "/" | "%") factor)*
factor      := "-" factor | primary
primary     := number | variable | "(" expression ")"
var_section := ("var" name ("," name)* (";" | "."))+
statement   := assignment | print_statement | if_statement | while_statement
assignment  := name "=" expression (";" | ".")
print_statement := "print" expression (";" | ".")
if_statement    := "if" "(" condition ")" "{" statement* "}"
                   ("else" "{" statement* "}")?
while_statement := "while" "(" condition ")" "{" statement* "}"
condition   := expression ("==" | "!=" | "<" | "<=" | ">" | ">=") expression
```

البرنامج الذي يبدأ رمزه الأول بمعرّف يُحلَّل بصيغة العبارات (مع قسم
`var` اختياري في المقدمة)؛ وغير ذلك يُحلَّل كتعبير مجرّد بصيغة التذكرة
15. التعبير المجرّد يطبع قيمته مباشرة، بينما صيغة العبارات لا تطبع إلا
عبر عبارات `print`.

الأسبقية من الأعلى: الأقواس، ثم السالب الأحادي، ثم `*` و`/` و`%`، ثم
`+` و`-`. المعاملات في المستوى الواحد ترتبط من اليسار. المقارنات تأخذ
تعبيرين صحيحين وتنتج قيمة منطقية تُستخدم لقرار الفرع أو الحلقة فقط؛ ولا
يجوز تداخلها ولا استخدامها داخل `print`. الأعداد سلاسل أرقام ASCII
تُقرأ عبر `parse_i64`، فيعمل المدى الكامل للأعداد الصحيحة ذات 64 بت
الموقعة، ويُبلغ عن التجاوز بالرمز `number-out-of-range`. المسافات
البيضاء (المسافة والجدولة والعودة والسطر الجديد) تفصل الرموز؛ وأي محرف
آخر خارج العدد أو المعرّف هو `invalid-character`.

القسمة تقطع نحو الصفر وباقي `%` يأخذ إشارة الطرف الأيسر، تماما مثل
ASPL. يستخدم الحساب عمليات ASPL المفحوصة نفسها، فيكون التجاوز والقسمة
على صفر trap تماما كما في برنامج ASPL (بالرمزين `integer-overflow`
و`integer-zero-divisor`) بدلا من أن يصبحا قيمتين قابلتين للاسترداد.

## المتغيرات والتحكم في التدفق

- التصريحات في قسم `var` الافتتاحي العام للبرنامج فقط، سطر `var` واحد
  أو أكثر قبل أي عبارة. لا توجد نطاقات للكتل ولا تصريحات داخل أجسام
  الحلقات؛ وسطر `var` في غير المقدمة هو `syntax-error`.
- كل متغير مصرّح به مرئي لجميع العبارات اللاحقة ويبدأ من `0`. العبارات
  اللاحقة تُسند للمتغيرات المصرّحة بـ `=`.
- الكلمات المحجوزة وليست أبدا أسماء متغيرات: `var` و`print` و`if`
  و`else` و`while`.
- كل من `;` و`.` ينهي العبارات البسيطة، كما في ASPL.
- `if` تنفّذ كتلة `then` عند تحقق الشرط وكتلة `else` عند بطلانه (وقد
  تغيب `else` فلا يُنفَّذ شيء). `while` تكرر الجسم حتى يبطل الشرط
  باستخدام حلقات ASPL نفسها، فيبقى المثال الجامح قابلا للإيقاف عبر
  عناصر التحكم الحالية في التنفيذ (الإيقاف والإلغاء وحدود الزمن).

## أبجدية المعرّفات

الوحدة الأولى حرف ASCII أو `_` أو حرف عربي من U+0627 حتى U+064A.
الوحدات التالية قد تكون أيضا أرقام ASCII. هذه مجموعة فرعية صغيرة موثقة
عن قصد، ولا تدّعي ملف معرّفات ASPL الكامل. ملف `demo.txt` يصرّح بـ
`مجموع`، وأخطاء المتغيرات تقتبس التهجئة المخالفة مع الحفاظ على بايتات
مصدرها.

## التشغيل

يطلب البرنامج سطرين على الدخل القياسي: اسم ملف المصدر، ثم العملية
(`interpret` أو `generate`). التوليد يطلب سطرا ثالثا: اسم ملف الخرج.

```sh
aspl run docs/examples/mini-arith/mini-arith.aspl
# File: docs/examples/mini-arith/demo.txt
# Operation: interpret
# File: Operation: 55
```

ولّد مصدر ASPL ثم افحصه وشغّله وابنه بالأدوات المعتادة (لا يعمل أي
مصرّف داخل المثال نفسه):

```sh
aspl run docs/examples/mini-arith/mini-arith.aspl
# File: docs/examples/mini-arith/demo.txt
# Operation: generate
# Output: /tmp/demo-generated.aspl
# File: Operation: Output:
aspl check /tmp/demo-generated.aspl
aspl run /tmp/demo-generated.aspl
# 55
aspl build -o /tmp/demo-generated /tmp/demo-generated.aspl
/tmp/demo-generated
# 55
```

يصرّح البرنامج المولّد بكل متغير كربط مفهرس `m_var_N: i64` (حسب ترتيب
التصريح)، فتبقى الأسماء العربية والكلمات المحجوزة في ASPL والتعارضات
مع المؤقتات المولّدة صحيحة المعنى. دالة `start_main` لا تأخذ وسائط
وتعيد `0`، وتطبع الأعداد عبر `format_i64`، وتطابق مخرجات المفسّر لنفس
الدخل، بما في ذلك صيغة التعبير المجرّد.

ابنِ برنامجا أصليا مولّدا بالطريقة المعتادة؛ وسلوكه مطابق تماما:

```sh
aspl build -o mini-arith docs/examples/mini-arith/mini-arith.aspl
printf 'docs/examples/mini-arith/demo.txt\ninterpret\n' | ./mini-arith
```

في ملعب المتصفح، استورد `demo.txt` عبر عناصر التحكم في الملفات، والصق
`mini-arith.aspl` في المحرر، وشغّله، وأجب `demo.txt` ثم `interpret` عند
موجهي الإدخال، وتوقع `File: Operation: 55`. وللتوليد أجب `demo.txt`
ثم `generate` ثم اسم خرج مثل `generated.aspl` عند الموجهات الثلاثة؛
تظهر البايتات المولّدة كملف افتراضي متاح عبر عناصر التنزيل المعتادة.
لا يصرّف المتصفح النص المولّد في نفس التشغيل.

## التشخيصات

الإخفاقات المعالجة تطبع سطرا واحدا وتخرج بالحالة 0:

```text
error: <code>                  # إخفاقات الملف والعملية، بلا موضع
error: <code> <line>:<column>         # إخفاقات المصدر، بموضع قياسي يبدأ من 1
error: <code> <line>:<column> <name>  # إخفاقات المتغيرات، مع التهجئة المصدرية
```

الرموز الثابتة: `not-found` و`invalid-utf8` و`not-regular-file`
و`file-too-large` و`permission-denied` و`invalid-path` و`io-error`
(ممررة من `read_text_file`)؛ وتُمرر رموز الكتابة نفسها من
`write_text_file` عند تعذّر نشر الخرج، مع الحفاظ على ملف الوجهة
السابق؛ و`invalid-character` و`invalid-number`
و`number-out-of-range` و`syntax-error` (إخفاقات المصدر، مع المواضع)؛
و`duplicate-variable` و`undeclared-variable` (إخفاقات المتغيرات، مع
المواضع والتهجئة المخالفة)؛ و`unknown-operation` (كل ما ليس `interpret`
أو `generate`). المصدر المشوّه يفشل قبل نشر أي ملف خرج.

## ملاحظات التطبيق

- تحتفظ الرموز بالنوع والتهجئة الأصلية والموضع القياسي في سجلات
  `Token` داخل `list<Token>`؛ وتعيش عقد الصياغة في `list<Node>` وتربط
  الأبناء بفهارس صحيحة (`-1` تعني لا يوجد). عقد التعبيرات تعيد استخدام
  رموز المعاملات، والمقارنات تعيد استخدام رموز `==` و`!=` و`<` و`<=`
  و`>` و`>=`، وتحميلات المتغيرات تحمل التهجئة المصدرية في حقل الاسم،
  والعبارات (الإسناد والطبع وif/else وwhile) تتسلسل عبر رابط `right`
  مع رؤوس الفروع في `left` و`extra` و`number`.
- جدول المتغيرات `list<Binding>` من سجلات الاسم/القيمة مع بحث خطي
  بالمساواة الدقيقة للبايتات. تأخذ دوال المحلّل `ParserState` (الرموز
  والموضع والعقد والأسماء المصرّحة وراية صيغة العبارات) بالقيمة وتعيد
  الحالة المحدّثة عبر `result<ParseStep>` و`result<ParserState>`؛
  والتنفيذ يمرّر قائمة الربط عبر القيم المعادة من `exec_list`
  و`exec_one`. لا خرائط تجزئة ولا سجلات متكررة ولا أسماء مستعارة
  مشتركة قابلة للتغيير.
- كل وصول `::value` محروس بفحص `::ok`؛ وكل وصول `::error` في فرع
  الفشل. تنسيق الأعداد يتم فقط عبر `format_i64`.
- يمشي المولّد قائمة عقد الصياغة المفحوصة، ويصدر اسما مفهرسا `m_var_N`
  لكل اسم مصرّح، ويضع كل تعبير وشرط بين قوسين، ويرسم `==` الخاصة بلغة
  المثال كـ `matches` في ASPL، ويطبع الأعداد كـ `print(format_i64(...))`
  مع `print("\n")`. تتراكم الأجزاء في `list<string>` ثم تُلتحم مرة
  واحدة عبر `join`، وتُنسق الأعداد عبر `format_i64`، ولا يُكتب الملف عبر
  `write_text_file` إلا بعد نجاح التحليل.
