مرجع الاستخدام
مفاهيم عامة
الأصناف الأساسية للمتغيرات
التراكيب والمؤثرات
أوامر التحكم
الدالّات
أصناف المستخدم
استنباط الأصناف
المؤشرات
السندات
المصفوفات
تمثيل الأصناف
الكائنات المؤقتة
رزم الأوامر
حجز وتهيئة الكائنات ديناميكيا
الألقاب
الوحدات
الماكروهات
شجرة البنية المجردة (AST)
المعالجة التمهيدية
دمج التعريفات
شمل مكتبات أو ملفات مصدرية
الوحدة: الـعملية (Process)

يضم هذا المرجع تعليمات البرمجة الإجرائية باستخدام مكتبة نمط البرمجة المعياري (Standard Programming Paradigm).

مفاهيم عامة


المعرّفات (identifiers)

المعرّفات تستخدم للإشارة إلى دالة أو متغير أو صنف وتبدأ بحرف هجائي عربي أو انجليزي أو بالرمز _. يمكن للمعرفات أن تحتوي أيضاً على الأرقام 0-9 وعلى الحركات والشدّة أيضاً.

الفصل بين الجمل

تستخدم الفاصلة المنقوطة للفصل بين الجمل بطريقة مشابهة للفاصلة الإعتيادية التي تفصل بين الحدود داخل القوائم. بمعنى آخر فإن الفاصلة المنقوطة ليست جزءاً من الجملة ويُمكن إهمالها إن لم يأت بعدها جملة أخرى.

الأقواس الهلالية والأقواس المعقوفة

تستخدم الأقواس الهلالية في الأمور التي تُعالج أثناء تنفيذ البرنامج مثل جمع الحدود داخل التراكيب أو إرسال المدخلات إلى الدالّات، بينما تستخدم الأقواس المعقوفة في الأمور التي تُعالج أثناء الترجمة مثل تحديد نوع المؤشر أو المصفوفة. بمعنى آخر، إن كانت المعلومة مرسلةً إلى المترجم نفسه تُسخدم الأقواس المعقوفة، وإلا فالأقواس الهلالية.

الأقواس الحاصرة

تستخدم الأقواس الحاصرة لحصر مجموعة من الجمل في كتلة واحدة. وتستخدم هذه الكتل في جواب الجمل الشرطية ومتون الحلقات والهياكل والدالات والوحدات.

التعريفات

كل التعريفات تتم باستخدام الأمر "عرّف" (def) ويأخذ الصيغة التالية:
  عرّف <معرِّف> : <تعريف>
  def <identifier> : <definition>
التعريف يمكن أن يكون اسم أحد الأصناف وينتج عن التعريف متغير من ذلك الصنف، أو يكون أحد الأوامر التعريفية الأخرى مثل الدالات والهياكل. المثال التالي يعرّف متغيراً من صنف الأعداد الصحيحة:
  عرّف م : العدد_الصحيح
  def i : Int
يمكن أيضاً تعريف الثوابت بنفس الطريقة وذلك بوضع القيمة نفسها بدل الصنف ويمكن هذا مع الأعداد الصحيحة والعائمة بالإضافة إلى سلاسل المحارف، كما في المثال التالي:
  عرّف البسملة: "بسم الله الرحمن الرحيم"؛
  عرف النسبة_الثابتة: 3.141592؛
  عرف عدد_أيام_الاسبوع: 7؛
  def hello: "Hello World";
  def pi: 3.141592;
  def daysPerWeek: 7;

الهيكل العام للبرنامج

كل برنامج بلغة الأسُس يجب أن يشمل المكتبة "libalusus_spp.so" إما بشكل مباشر أو بشكل غير مباشر وذلك لأن هذه المكتبة هي التي تحتوي على تعريفات الأوامر الأساسية المتعلقة بالبرمجة الإجرائية. لذلك يبدأ كل برنامج بالأمر "اشمل" أو "import".
  import "libalusus_spp.so";
غالباً لن تحتاج لشمل مكتبة البناء المعيارية مباشرة، وإنما تشمل ملفات تعريفية أخرى تقوم بدورها بشمل مكتبة البناء المعيارية. على سبيل المثال:
  import "Srl/Console.alusus";
أو:
  اشمل "مـتم/طـرفية.أسس"؛
يحتوي كل برنامج على تعريف لوحدة وهذه الوحدة هي التي تحتوي على تعريفات البرنامج.
  def HelloWorld : module
  {
    ...
  }
  عرّف الـبسملة : وحدة
  {
    ...
  }
بعد ذلك يجب أن يضم البرنامج الأمر نفّذ (run) لتنفيذ البرنامج. السبب في ذلك يكمن في قدرة المبرمج على الاختيار بين التنفيذ المباشر (JIT compilation) أو التحويل إلى ملف تشغيلي (عبر الأمر build). خاصية التحويل إلى ملف تنفيذي ليست مكتملة بعد وسيتم إكمالها في إصدار آخر. الأمر "نفّذ" يكتب بالشكل التالي:
  نفّذ <اسم_وحدة>.<اسم_الدالة>؛
  run <module_name>.<function_name>

التعليقات

التعليقات في لغة الأسُس تبدأ بالرمز // وتنتهي عند نهاية السطر، أي أنها مشابهة للتعليقات مفردة السطر (single line comment) في لغة سي++ أو جافا.

المبدلات

يمكن وسم عناصر الشفرة المصدرية بمبدلات تستخدم لاحقا في مجالات مختلفة. المبدل يمكن أن يغير طريقة ترجمة وتنفيذ الشفرة الموسومة ويمكن أن لا يفعل سوى إضافة بيانات وصفية. المبدل له النسق التالي:
  @<كلمة_تعريفية> <تعريف_العنصر_المراد_وسمه>
  @<كلمة_تعريفية>[<معطيات>] <تعريف_العنصر_المراد_وسمه>
  @<keyword> <def_of_element_to_be_tagged>
  @<keyword>[<arguments>] <def_of_element_to_be_tagged>
يعتمد المترجم على مبدلات معرفة مسبقا لإضافة خواص معينة على التعريفات المختلفة كما هو موضح في مواضع عدة من هذه الوثيقة. وبإمكان المستخدم إضافة مبدلات خاصة وقراءتها لاحقا باستخدام دالة `نـبم.هات_محارف_مبدل`.
  @مبدلي["معطى1"، "معطى2"] دالة دالتي { ... }؛
  @mymodifier["arg1", "arg2"] func myFunc { ... };

الأصناف الأساسية للمتغيرات


  • صـحيح (Int)
    عدد صحيح. يتم تحديد عدد البتات بين أقواس معقوفة. تحديد عدد البتات اختياري وبدونه يكون العدد الافتراضي للبتات 32.
    صـحيح // 32 بتة
    صـحيح[1]
    صـحيح[8]
    صـحيح[16]
    صـحيح[32]
    صـحيح[64]
    Int // 32 bits
    Int[1]
    Int[8]
    Int[16]
    Int[32]
    Int[64]

  • صـحيح_متكيف (ArchInt)
    عدد صحيح بعدد بتات مطابقة لمعمارية النظام، أي بحجم 32 بت على أنظمة 32 بت وبحجم 64 بت على أنظمة 64 بت.

  • طـبيعي (Word)
    عدد صحيح موجب. يتم تحديد عدد البتات بين أقواس معقوفة. تحديد عدد البتات اختياري وبدونه يكون العدد الافتراضي للبتات 32.
    طـبيعي // 32 بتة
    طـبيعي[1]
    طـبيعي[8]
    طـبيعي[16]
    طـبيعي[32]
    طـبيعي[64]
    Word // 32 bits
    Word[1]
    Word[8]
    Word[16]
    Word[32]
    Word[64]

  • طـبيعي_متكيف (ArchWord)
    عدد صحيح موجب بعدد بتات مطابقة لمعمارية النظام، أي بحجم 32 بت على أنظمة 32 بت وبحجم 64 بت على أنظمة 64 بت.

  • عـائم (Float)
    عدد عائم. يتم تحديد عدد البتات بين أقواس معقوفة. تحديد عدد البتات اختياري وبدونه يكون العدد الافتراضي للبتات 32.
    عـائم // 32 بتة
    عـائم[32]
    عـائم[64]
    Float // 32 bits
    Float[32]
    Float[64]

  • مـحرف (Char)
    محرف، وهو مجرد لقب للصنف `طـبيعي[8]`.

  • ثـنائي (Bool)
    قيمية ثنائية، وهو مجرب لقب للصنف `طـبيعي[1]`.

  • مصفوفة (array)
    مصفوفة من المتغيرات. تُعرف بتحديد صنف المتغيرات وعدد الخانات بين أقواس معقوفة كما يلي:
    مصفوفة[الصنف، عدد_الخانات]
    array[the_type, element_count]

  • مؤشر (ptr)
    مؤشر إلى متغير. صنف المتغير يحدد بين أقواس معقوفة، كما يلي:
    مؤشر[الصنف]
    ptr[the_type]

التراكيب والمؤثرات


المؤثرات

فيما يلي قائمة المؤثرات مرتبة تصاعدياً حسب الأسبقية، أي أن العناصر الأولى بأسبقية أقل:
ملاحظة: بعض هذه المؤثرات غير مدعومة بعد وسيتم دعمها في إصدار لاحق.
  • مؤثرات التعيين
    = تحديد قيمة جديدة
    += إضافة قيمة للقيمة الحالية
    -= طرح قيمة من القيمة الحالية
    *= ضرب القيمة الحالية بقيمة أخرى
    /= تقسيم القيمة الحالية على قيمة أخرى
    %= تقسيم القيمة الحالية على قيمة أخرى والاحتفاظ بالباقي بدل نتيجة القسمة
    &= تطبيق عملية 'و' المنطقية على القيمة الحالية
    |= تطبيق عملية 'أو' المنطقية على القيمة الحالية
    $= تطبيق عملية xor المنطقية على القيمة الحالية
    <<= تزحيف جميع البتّات يساراً مراتباَ بتعداد القيمة المعطاة
    =>> تزحيف جميع البتّات يميناً مراتباً بتعداد القيمة المعطاة
  • مؤثرات العمليات المنطقية
    و عملية `و` المنطقية، ويمكن كتابتها and ايضاً
    أو عملية `أو` المنطقية، ويمكن كتابتها or أيضاً
    || صيغة رمزية ل or
    && صيغة رمزية ل and
  • مؤثرات المقارنة
    == فحص مساواة
    != فحص عدم المساواة
    < فحص أصغر
    > فحص أكبر
    <= فحص أصغر أو يساوي
    >= فحص أكبر أو يساوي
  • مؤثرات الجمع والطرح
    +
    -
  • مؤثرات الضرب والقسمة
    *
    /
    % تحصيل باقي القسمة
  • مؤثرات العمليات البتّية
    هذه المؤثرات لتطبيق عمليات منطقية على مستوى البتّات، أي تطبيق العمليات على كل بت وما يقابله، بالإضافة إلى عمليات تزحيف البتّات.
    | عملية 'أو'
    $ عملية xor
    & عملية 'و'
    << تزحيف البتّات يميناً مراتباً بتعداد القيمة المعطاة
    >> تزحيف البتّات يساراً مراتباً بتعداد القيمة المعطاة
  • المؤثرات الأحادية السابقة
    ++ زيادة بواحد
    -- إنقاص بواحد
    + إشارة الرقم الموجب
    - إشارة الرقم السالب
    ! علامة النفي البتّية (عكس قيمة البتّات)
    !! علامة النفي المنطقية
  • المؤثرات الأحادية اللاحقة
    ++ زيادة بواحد
    -- إنقاص بواحد
  • مؤثرات أخرى
    . الولوج إلى أحد أعضاء عنصر
    .{} رزمة الأوامر

التحكم بالأسبقية

تستخدم الأقواس الهلالية للتحكم بالأسبقية في التراكيب. على سبيل المثال التركيب التالي ينفذ عملية الضرب قبل الجمع:
  س = ص + ع * م
بينما التركيب التالي ينفذ الجمع قبل الضرب:
  س = (ص + ع) * م

الرموز الحرفية

بالإضافة للمتغيرات، بالإمكان استخدام الرموز الحرفي في التراكيب. وهذه قائمة الرموز الحرفية المدعومة في لغة الأسُس:
  • الأعداد الصحيحة
    تكتب كعدد عشري وتُمثل ب32 بت. مثلا: 132
  • أعداد الفاصلة العائمة
    تكتب كعدد حقيقي وتُمثل ب32 بت. مثلا: 1.32
  • سلاسل المحارف
    سلاسل المحارف تُحصر بعلامتي اقتباس، كما في المثال التالي:
      "بسم الله الرحمن الرحيم"
    يمكن كتابة الرموز الخاصة باستخدام علامة \ وفيما يلي قائمة الرموز الخاصة:
    \n بداية سطر جديد
    \r رجوع إلى بداية السطر
    \t إدراج علامة تاب (tab)
    \" إدراج علامة اقتباس
    \\ إدراج علامة \

أوامر التحكم


الجمل الشرطية

تكتب الجمل الشرطية بالصيغة التالية:
  إذا <تركيب شرطي> <جملة>
  إذا <تركيب شرطي> { <مجموعة جمل> }
  إذا <تركيب شرطي> <جملة_أو_كتلة> وإلا <جملة_أو_كتلة>
  if <condition expression> <statement>
  if <condition expression> { <group of statements> }
  if <condition expression> <statement_or_block> else <statement_or_block>
ليس مشروطاً حصر تركيب الشرط بأقواس هلالية لكن ذلك متاح.

الحلقات

تكتب الحلقات بالصيغ التالية:
  بينما <تركيب شرطي> <جملة>
  بينما <تركيب شرطي> { <مجموعة جمل> }
  لكل <تهيئة عداد>، <تركيب شرطي>، <تحديث عداد> <جملة>
  لكل <تهيئة عداد>، <تركيب شرطي>، <تحديث عداد> { <مجموعة جمل> }
  while <condition expression> <statement>
  while <condition expression> { <group of statements> }
  for <counter initialization>, <condition expression>, <counter update> <statement>
  for <counter initialization>, <condition expression>, <counter update> { <group of statement> }
مثلما هو الحال مع الجمل الشرطية، ليس مشترطاً حصر التركيب الشرطي في "بينما" أو التراكيب الثلاث المتعلقة بالعداد في "لكل" بأقواس هلالية، لكن ذلك متاح. مثال:
  بينما ن!=0 ن=قم_بعملية()؛
  بينما ن!=0 { ن = قم_بعملية() }؛
  لكل ع=0، ع<10، ع++ اطبع("%d\ج"، ع)؛
  لكل (ع=0، ع<10، ع++) اطبع("%d\ج"، ع)؛
  لكل ع=0، ع<10، ع++ { اطبع("%d\ج"، ع) }؛
  while r!=0 r=performOperation();
  while r!=0 { r = performOperation() }
  for i=0, i<10, i++ print("%d\n", i);
  for (i=0, i<10, i++) print("%d\n", i);
  for i=0, i<10, i++ { print("%d\n", i) };

الأمر `أكمل` (continue)

يستخدم لتجاوز ما تبقى من الدورة الحالية للحلقة والبدء بدورة جديدة. يمكن تحديد رقم الحلقة المراد تجاوز دورتها في حالة الحلقات المتداخلة.
  أكمل؛ // بدء دورة جديدة من الحلقة الحالية.
  أكمل 2؛ // الخروج من الحلقة الحالية وبدء دورة جديدة من الحلقة الخارجية.
  continue;
  continue 2;

الأمر `اقطع` (break)

الخروج من الحلقة. يمكن تحديد رقم الحلقة المراد الخروج منها في حالة الحلقات المتداخلة.
  اقطع؛ // الخروج من الحلقة الحالية.
  اقطع 2؛ // الخروج من الحلقة الحالية والخارجية
  break;
  break 2;

الدالّات


تعرّف الدالات باستخدام الأمر "دالّة" كتعريف في الأمر "عرّف":
  عرّف <اسم_الدالة> : دالّة (<معطيات>) => <صنف_النتيجة> { <متن_الدالة> }
  def <func name> : function (<arguments>) => <return_type> { <function_body> }
وتكون المعطيات بالصيغة التالية:
  <اسم_المعطى>:<نوع_المعطى>، <اسم_المعطى>:<نوع_المعطى> ...
  <arg_name>:<arg_type>, <arg_name>:<arg_type> ...
كما في المثال التالي:
  عرف مضروب : دالّة (ع:العدد_الصحيح) => العدد_الصحيح
  {
    إذا ع==1 ارجع 1؛
    ارجع ع * مضروب(ع-1)؛
  }
  def factorial : function (i:Int) => Int
  {
    if i==1 return 1;
    return i*factorial(i-1);
  }
يمكن أيضًا استخدام الصيغة المختصرة دون الحاجة للأمر "عرف" كما يلي:
  دالة <اسم_الدالة> (<معطيات>) => <صنف_النتيجة> { <متن_الدالة> }
  function <func name> (<arguments>) => <return_type> { <function_body> }
كما في المثال التالي:
  دالة مضروب (ع:العدد_الصحيح) => العدد_الصحيح
  {
    إذا ع==1 أرجع 1؛
    أرجع ع * مضروب(ع-1)؛
  }
  function factorial (i:Int) => Int
  {
    if i==1 return 1;
    return i*factorial(i-1);
  }
استدعاء الدوال يتم باستخدام اسم الدالة يليه قائمة المعطيات بين قوسين هلاليين. إن لم تكن للدالة أي معطيات فيجب إتباع اسمها بقوسين هلاليين فارغين.
  س = إقرأ_رقماً()؛
  ص = إقرأ_رقماً()؛
  ارسم_نقطة(س، ص)؛
  x = readNumber();
  y = readNumber();
  drawPoint(x, y);

المعطيات المرنة

يمكن تعريف دالة بمعطيات مرنة (variadic function) ما يسمح للمستخدم باستدعاء الدالة بعدد غير ثابت من المعطيات. بخلاف لغات أخرى كلمة السي، يمكن في الأسس تحديد صنف هذه المعطيات كما يمكن تحديد عدد أدنى وأعلى من المعطيات. تحديد المعطيات كمعطيات مرنة يتم باستخدام المؤثر ... عند تعريف صنف المعطى. أي أن تسبيق صنف المعطى بـ... يجعل ذلك المعطى مرنا ما يمكن المستخدم من تمرير عدد غير محدد من ذلك الصنف.
  <اسم_مجموعة_المعطيات>: ...<صنفها>
  <اسم_مجموعة_المعطيات>: ...[<صنفها>، <العدد_الأدنى>، <العدد_الأعلى>]
  <arg_group_name>: ...<args_type>
  <arg_group_name>: ...[<args_type>, <min_count>, <max_count>]
أمثلة:
  // دالة تستقبل عددا غير محدد من العناصر بصنف غير محدد.
  دالة اطبع (بنية: مؤشر[مصفوفة[محرف]]، عناصر: ...أيما) { ... }

  // دالة تستقبل عددا غير محدد من العناصر صنفها عائم.
  دالة اطبع (عدد: صحيح، عناصر: ...عائم) { ... }

  // دالة تستقبل عناصر صنفها عائم يتراوح عددها بين 2 و 5.
  دالة اطبع (عدد: صحيح، عناصر: ...[عائم، 2، 5]) { ... }
  // Function receiving unspecified number of args with unspecified type.
  function print (format: ptr[array[char]], ...any) { ... }

  // Function receiving unspecified number of args with type Float.
  function print (count: Int, args: ...Float) { ... }

  // Function receiving Float args counting between 2 and 5.
  function print (count: Int, args: ...[Float, 2, 5]) { ... }

استدعاء دالة مرنة المعطيات
استدعاء دالة مرنة المعطيات يتم بنفس طريقة استدعاء أي دالة أخرى، لكن إذا كانت الدالة تستلم عدد المعطيات متبوعا بالمعطيات المرنة فيمكن استخدام الأقواس المزهرة {} والتي يحولها مترجم الأسس تلقائيا إلى عدد المعطيات متبوعا بتلك المعطيات، كما في المثال:
  دالة اطبع (عدد: صحيح، عناصر: ...عائم) { ... }

  اطبع({ 5.5، 3.7، 1.0 })؛
  // الاستدعاء السابق مطابق للاستدعاء التالي، مترجم الأسس يضيف عدد العناصر (3)
  // تلقائيا في بداية الاستدعاء.
  اطبع(3، 5.5، 3.7، 1.0)؛
  function print (count: Int, args: ...Float) { ... }

  print({ 5.5, 3.7, 1.0 });
  // The upper call is identical to the one below; Alusus automatically inserts
  // the count (3) before the args.
  print(3, 5.5, 3.7, 1.0);

استخدام المعطيات المرنة داخل الدالة
الوصول إلى المعطيات المرنة يتم عبر المؤثر ~المعطى_التالي (~next_arg) على اسم مجموعة المعطيات وإعطائه صنف المعطى. المؤثر يحتاج صنف المعطى لأن التعريف قد لا يحدد صنفا للمعطيات وبالتالي يحتاج المستخدم أن يحدد الصنف بنفسه اعتمادا على معطيات أخرى مثل سلسلة محارف تحدد بنية المعطيات المرنة كما هو الحال مع دالة printf في السي. من المهم الملاحظة أن كل استخدام للمؤثر ~المعطى_التالي يسحب عنصرا من مجموعة المعطيات، أي أن الولوج للمعطيات يتم بشكل تسلسلي ولا يمكن الولوج لنفس العنصر عدة مرات أو الولوج بشكل عشوائي. كما أن تحديد عدد العناصر والوقوف بعد سحب آخر عنصر مسؤولية المبرمج حيث أن الأسس لا تملك طريقة تعرف بها عدد العناصر المتبقية، ولذلك يحتاج المستخدم لاستقبال لإضافة عدد العناصر كمعطى أولي في الدالة كما هو الحال مع الأمثلة أعلاه. المثال التالي لدالة تطبع معطيات صنفها صحيح أو عائم ويتم تحديد عدد وصنف المعطيات في المعطى الأولي للدالة:
  دالة اطبع (بنية: مؤشر[محرف]، عناصر: ...أيما) {
    بينما بنية~محتوى != 0 {
      إذا بنية~محتوى == '#' اطبع_عددا_صحيحا(عناصر~المعطى_التالي[صحيح])
      وإلا اطبع_عددا_عائما(عناصر~المعطى_التالي[عائم])؛
      بنية = بنية + 1؛
    }
  }

  اطبع("#$##$"، 5، 5.5، 8، 7، 2.3)؛
  function print (format: ptr[Char], args: ...any) {
    while format~cnt != 0 {
      if format~cnt == '#' printInteger(args~next_arg[Int])
      else printFloat(args~next_arg[Float]);
      format = format + 1;
    }
  }

  print("#$##$", 5, 5.5, 8, 7, 2.3);

الدالات الضمنية

الدالات الضمنية هي الدالات التي تُعرف ضمن تركيب دون أن يكون لها اسم، وتستخدم عادة عند الحاجة لتمرير مؤشر على دالة كمعطى لدالة أخرى. تعريف هذه الدالات يكون بكتابتها بطريقة عادية ضمن أي تركيب دون إعطائها إسمًا، وفي هذه الحالة ستترجم القيمة إلى مؤشر تلك الدالة، كما في المثال التالي:
دالة رتب (
    قيود: سند[مـصفوفة[قـيد]]،
    قارن: مؤشر[دالة (سند[قـيد]، سند[قـيد])]
) {
    عرف م: صحيح؛
    عرف ن: صحيح؛
    لكل م = 0، م < قيود.هات_الطول() - 1، ++م {
        لكل ن = م + 1، ن < قيود.هات_الطول()، ن++ {
            إذا قارن(قيود(م)، قيود(ن)) {
                // بدل م مكان ن
            }
        }
    }
}

// رتب تصاعديا
رتب(قيود، دالة (ق1: سند[قـيد]، ق2: سند[قـيد]) {
    أرجع ق1.س > ق2.س؛
})؛

// رتب تنازليا
رتب(قيود، دالة (ق1: سند[قـيد]، ق2: سند[قـيد]) {
    أرجع ق1.س < ق2.س؛
})؛
func sort (
    records: ref[Array[Record]],
    compare: ptr[func (ref[Record], ref[Record])]
) {
    def i: Int;
    def j: Int;
    for i = 0, i < records.getLength() - 1, ++i {
        for j = i + 1, j < records.getLength(), ++j {
            if compare(records(i), records(j)) {
                // Replace i with j;
            }
        }
    }
}

// Sort ascending.
sort(records, func (r1: ref[Record], r2: ref[Record]) {
    return r1.x > r2.x;
});

// Sort descending.
sort(records, func (r1: ref[Record], r2: ref[Record]) {
    return r1.x < r2.x;
});
يمكن للدالات الضمنية الولوج إلى المتغيرات العمومية خارجها، لكن لا يمكنها الولوج إلى المتغيرات المحلية ضمن دالة خارجية تحوي الدالة الضمنية، وذلك لأن الدالة الخارجية قد ينتهي تنفيذها وتُزال متغيراتها من الذاكرة قبل استدعاء الدالة الضمنية. للولوج إلى متغيرات محلية ضمن دالة خارجية يحتاج المستخدم لاستخدام المغلفات بدل الدالات الضمنية.

أصناف المستخدم


تعرّف الهياكل باستخدام الأمر "صنف" كتعريف في الأمر "عرّف":
  عرّف <اسم_الصنف> : صنف { <مجموعة_تعريفات> }
  def <type_name> : class { <definition_statements> }
بعد تعريف الصنف يصبح الصنف متوفراً لتعريف المتغيرات. الولوج إلى عناصر الهيكل يتم باستخدام مؤثر النقطة. مثال:
  عرّف النقطة : صنف {
    عرّف س : العدد_الحقيقي؛
    عرّف ص : العدد_الحقيقي؛
  }؛
  .
  .
  عرّف ن : النقطة؛
  ن.س = إقرأ_قيمة()؛
  ن.ص = إقرأ_قيمة()؛
  def Point : class {
    def x : Float;
    def y : Float
  };
  .
  .
  def p : Point;
  p.x = readValue();
  p.y = readValue();
يمكن أيضًا استخدام الصيغة المختصرة التي تغنيك عن استعمال الأمر "عرف"، كما يلي:
  صنف <اسم_الصنف> { <مجموعة_تعريفات> }
  class <type_name> { <definition_statements> }
كما في المثال التالي:
  صنف النقطة {
    عرّف س : العدد_الحقيقي؛
    عرّف ص : العدد_الحقيقي؛
  }؛
  .
  .
  عرّف ن : النقطة؛
  ن.س = إقرأ_قيمة()؛
  ن.ص = إقرأ_قيمة()؛
  class Point {
    def x : Float;
    def y : Float
  };
  .
  .
  def p : Point;
  p.x = readValue();
  p.y = readValue();

قوالب الأصناف

يمكن تعريف قوالب الأصناف بتعريف معطيات يستخدمها الصنف في متنه ويتم تمرير هذا المعطى أثناء تعريف متغير من ذلك الصنف. التعريف والاستخدام يأخذان الصيغة التالية:
  صنف <اسم_الصنف> [<تعريف_معطيات_القالب>] { <متن_الصنف> }

  عرف <اسم_المتغير>: <اسم_الصنف>[<معطيات_القالب>]؛
  class <type_name> [<template_arg_defs>] { <type_body> }

  def <var_name>: <type_name>[<template_args>];
كما في المثال التالي:
  صنف نـقطة [نوع: صنف] {
    عرف س: نوع؛
    عرف ص: نوع؛
  }؛

  عرف موقع_صحيح: نـقطة[صحيح]؛
  عرف موقع_عائم: نـقطة[عائم]؛
  class Point [T: type] {
    def x: T;
    def y: T;
  };

  def intPoint: Point[int];
  def floatPoint: Point[float];
معطيات القوالب يمكن أن تكون من خمسة أنواع:
  • صنف type
  • دالة function
  • صحيح integer
  • محارف string
  • شبم ast

المعطيات من نوع `شبم` (ast) يمكن استخدامها في المعالجة التمهيدية للصنف، فهي تمكن المستخدم من تمرير شفرة مصدرية كمعطى للقالب، ثم أثناء المعالجة التمهيدية يمكن قراءة ذلك المعطى والتعامل معه بأي طريقة كانت ومن ثم حشر الشفرة المعطاة أو شفرة مشتقة منها داخل طبعة القالب الناتجة. من الجدير ملاحظ أن المعالجة التمهيدية لقوالب الأصناف تتكرر مع كل طبعة من ذلك الصنف.

من الممكن أيضا تحديد قيم مبدئية لمعطيات القوالب، كما في المثال التالي:
  صنف نـقطة [نوع: صنف، القيمة_المبدئية: صحيح = 0] {
    عرف س: نوع = القيمة_المبدئية؛
    عرف ص: نوع = القيمة_المبدئية؛
  }

  عرف موقع1: نـقطة[صحيح]؛ // س و ص سيكونان 0.
  عرف موقع2: نـقطة[صحيح، 1]؛ // س و ص سيكونان 1.
  class Point [T: type, V: integer = 0] {
    def x: T = V;
    def y: T = V;
  }

  def point1: Point[Int]; // x and y will be 0.
  def point2: Point[Int, 1]; // x and y will be 1.

الوظائف (methods)

الوظائف هي دالات تعرف داخل الأصناف ويتم استدعاؤها باستخدام كائن من ذلك الصنف. تُعرف الوظائف بتعريف دالة داخل متن الصنف مع إضافة المبدل `@عضو` (@member) لتلك الدالة وجعل أول معطى لتلك الدالة سندًا لذلك الصنف.
صنف <اسم_الصنف> {
  @عضو دالة <اسم_الوظيفة> (هذا: سند[هذا_الصنف]، <تعريفات_المعطيات>): <صنف_الإرجاع> {
    <متن_الوظيفة>
  }؛
}؛

<اسم_الكائن>.<اسم_الوظيفة>(<المعطيات>)؛
class <type_name> {
  @member func <method_name> (this: ref[this_type], <arg_definitions>): <ret_type> {
    <method_body>
  };
};

<object_name>.<method_name>(<arguments>);
داخل الوظيفة يمكنك الوصول إلى الكائن المرتبط بذلك الاستدعاء باستخدام المعطى الأول للدالة (هذا) كما لو كان المستخدم قد مرر ذلك الكائن إلى الدالة يدويًا. مثال:
  صنف نـقطة {
    عرف س: عائم؛
    عرف ص: عائم؛
    @عضو دالة هات_المسافة (هذا: سند[هذا_الصنف]): عائم {
      ارجع ريـاضيات.جذر(هذا.س * هذا.س + هذا.ص * هذا.ص)؛
    }؛
  }؛
  عرف ن: نـقطة؛
  ن.س = 3؛
  ن.ص = 4؛
  اطبع(ن.هات_المسافة())؛ // سيطبع 5
  class Point {
    def x: float;
    def y: float;
    @member func getDistance (this: ref[this_type]):float {
      return Math.sqrt(this.x * this.x + this.y * this.y);
    };
  }:
  def p: Point;
  p.x = 3;
  p.y = 4;
  print(p.getDistance()); // prints 5
يمكن أيضًا تعريف الوظائف باستخدام الأمر `عملية` (handler) وهو مجرد تجميل لغوي للطريقة المذكورة أعلاه:
صنف <اسم_الصنف> {
  عملية هذا.<اسم_الوظيفة> (<تعريفات_المعطيات>): <صنف_الإرجاع> {
    <متن_الوظيفة>
  }؛
}؛

<اسم_الكائن>.<اسم_الوظيفة>(<المعطيات>)؛
class <type_name> {
  handler this.<method_name> (<arg_definitions>): <ret_type> {
    <method_body>
  };
};

<object_name>.<method_name>(<arguments>);
مثال:
  صنف نـقطة {
    عرف س: عائم؛
    عرف ص: عائم؛
    عملية هذا.هات_المسافة (): عائم {
      ارجع ريـاضيات.جذر(هذا.س * هذا.س + هذا.ص * هذا.ص)؛
    }؛
  }؛
  عرف ن: نـقطة؛
  ن.س = 3؛
  ن.ص = 4؛
  اطبع(ن.هات_المسافة())؛ // سيطبع 5
  class Point {
    def x: float;
    def y: float;
    handler this.getDistance ():float {
      return Math.sqrt(this.x * this.x + this.y * this.y);
    };
  }:
  def p: Point;
  p.x = 3;
  p.y = 4;
  print(p.getDistance()); // prints 5

الخصال (properties)

الخصال وظائف لكنها تختلف بأن استدعاءها يتم باستخدام أحد المؤثرات التي يحددها تعريف الخصلة بدل استدعائها كدالة، كما هو موضح في المثال لاحقًا. تُعرف الخصال كما تُعرف الوظائف، لكن بإضافة المؤثر `@عملية` (@operation) إلى التعريف، كما يلي:
صنف <اسم_الصنف> {
  @عضو
  @عملية["<المؤثر>"]
  دالة <اسم_الخصلة> (هذا: سند[هذا_الصنف]، <تعريف_المعطى>): <صنف_الإرجاع> {
    <متن_الخصلة>
  }؛
}؛

<اسم_الكائن>.<اسم_الوظيفة> <المؤثر> <المعطيات>؛
class <type_name> {
  @member
  @operation["<operator>"]
  func <method_name> (this: ref[this_type], <arg_def>): <ret_type> {
    <method_body>
  };
};

<object_name>.<method_name> <operator> <arguments>;
المثال التالي يوضح تعريف عملية = على خصلة:
  صنف مـدة {
    عرف بداية: عائم؛
    عرف نهاية: عائم؛
    @عضو @عملية["="] دالة طول (هذا: سند[هذا_الصنف]، ط: عائم) {
      هذا.نهاية = هذا.بداية + ط؛
    }؛
  }؛
  عرف م: مـدة؛
  م.بداية = 10؛
  م.طول = 50؛ // ستوضع القيمة 60 في م.نهاية
  class Period {
    def start: float;
    def end: float;
    @member @operation["="] func length (this: ref[this_type], l:float) {
      this.end = this.start + l;
    };
  }:
  def p: Period;
  p.start = 10;
  p.length = 50; // p.end will be set to 60
في المثال أعلاه عُرفت عملية التعيين على الخصلة `طول` (length) دون العمليات الأخرى، هذا يعني أن أي من العمليات الأخرى لا يمكن تطبيقها على هذه الخصلة، بما فيها محاولة قراءة الخصلة. للتمكن من قراءة الخصلة نحتاج لتعريف عملية جديدة للخصلة مع تحديد معطى العملية كسلسلة محارف فارغة بدل سلسلة محارف تحتوي المؤثر المطلوب. مثال:
  صنف مـدة {
    عرف بداية: عائم؛
    عرف نهاية: عائم؛
    @عضو @عملية[""] دالة طول (هذا: سند[هذا_الصنف]): عائم {
      أرجع هذا.نهاية - هذا.بداية؛
    }؛
  }؛
  عرف م: مـدة؛
  م.بداية = 10؛
  م.نهاية = 60؛
  اطبع_عائم(م.طول)؛ // سيطبع 50
  class Period {
    def start: float;
    def end: float;
    @member @operation[""] func length (this: ref[this_type]): float {
      return this.end - this.start;
    };
  }:
  def p: Period;
  p.start = 10;
  p.end = 60;
  printFloat(p.length) // prints 50
يمكن أيضا استخدام الأمر `عملية` (handler) لتبسيط كتابة هذه الخصال، كما يلي:
  صنف مـدة {
    عرف بداية: عائم؛
    عرف نهاية: عائم؛
    عملية هذا.طول = عائم {
        هذا.نهاية = هذا.بداية + قيمة؛
        أرجع قيمة؛
    }؛
    عملية هذا.طول: عائم {
      أرجع هذا.نهاية - هذا.بداية؛
    }؛
  }؛
  عرف م: مـدة؛
  م.بداية = 10؛
  م.طول = 50؛ // قيمة م.نهاية ستكون 60
  اطبع_عائم(م.طول)؛ // سيطبع 50
  class Period {
    def start: float;
    def end: float;
    handler this.length = float {
        this.end = this.start + value;
        return value;
    };
    handler this.length: float {
      return this.end - this.start;
    };
  }:
  def p: Period;
  p.start = 10;
  p.length = 50; // p.end will be 60;
  printFloat(p.length) // prints 50
لاحظ في المثال أعلاه أن عملية التعيين فيها عبارة إرجاع، وذلك لأن الأمر `عملية` يُعرف تلقائيا صنف إرجاع لدالة الخصلة بصنف يطابق صنف المعطى.

العناصر المشتركة

يمكن تعريف متغيرات ودالات مشتركة داخل أصناف المستخدم، وهذه العناصر المشتركة تكون غير مرتبطة بأي كائن من هذا الصنف وإنما تتصرف كأنها دالات ومتغيرات عمومية وفرقها الوحيد عن المتغيرات والدالات العمومية أنها معرفة داخل مجال مختلف ولاستدعائها تحتاج أن تسبق اسمها باسم ذلك الصنف، كما في المثال التالي:
  صنف نـقطة {
    @مشترك عرف عدد: صحيح = 0؛
    عرف س: صحيح؛
    عرف ص: صحيح؛
    دالة أنشئ_نقطة (ا: صحيح، ب: صحيح): نـقطة {
      ++عدد؛
      س = ا؛ // خطأ
      ص = ب؛ // خطأ
      عرف ن: نـقطة؛
      ن.س = ا؛
      ن.ص = ب؛
      ارجع ن؛
    }؛
  }؛
  عرف ن: نـقطة = نـقطة.أنشئ_نقطة(1، 2)؛
  اطبع(نـقطة.عدد)؛ // يطبع 1
  اطبع(ن.عدد)؛ // خطأ
  اطبع(نـقطة.س)؛ // خطأ
  class Point {
    @shared def count: int;
    def x: int;
    def y: int;
    func getPoint (a: int, b: int): Point {
      ++count;
      x = a; // error
      y = b; // error
      def p: Point;
      p.x = a;
      p.y = b;
      return p;
    };
  }:
  def p: Point = Point.getPoint(1, 2);
  print(Point.count); // prints 1
  print(p.count); // error
  print(Point.x); // error
كما هو موضح في المثال أعلاه، تعريف المتغيرات المشتركة يحتاج للمبدل `@مشترك` (@shared) بينما تعريف الدوال المشتركة لا يحتاج لأي مبدل خاص لأن أي دالة تعرف داخل صنف تكون مشتركة ما لم يُضف لتعريفها المبدل `@عضو` (@member).

مؤشرات الوظائف

تعريف مؤشر على دالة بمبدل `@عضو` (@member) يجعله مؤشرًا على وظيفة، أي أنك لن تستطيع تعيين قيمة المؤشر ليؤشر على دالة اعتيادية وإنما يجب لقيمة المؤشر أن تشير إلى وظيفة في نفس الصنف. المثال التالي يوضح الأمر:
  صنف صـنفي {
    @عضو دالة افعل (هذا: سند[هذا_الصنف]، ص: صحيح) { ... }
    عرف مد: مؤشر[@عضو دالة (ص: صحيح)]؛
  }

  عرف ص: صـنفي؛
  ص.مد = صـنفي.افعل~مؤشر؛ // صحيح
  ص.مد(4)؛

  دالة افعل2 (ص: صحيح) { ... }
  ص.مد = افعل2~مؤشر؛ // خاطئ
  class MyType {
    @member func doSomething (this: ref[this_type], j: Int) { ... }
    def pf: ptr[@member func (Int)];
  }

  def mt: MyType;
  mt.pf = MyType.doSomething~ptr; // correct
  mt.pf(5);

  func doSomething2 (j: Int) { ... }
  mt.pf = doSomething2~ptr; // error
مؤشرات الوظائف مفيدة لبعض خصائص البرمجة الكائنية مثل تعددية الأشكال الموروثة (polymorphism). لاحظ أن الوظائف يجب أن تبدأ بالمعطى `هذا` (this) وإلا فلن يكون استدعاء الدالة ممكنًا.
لكن ماذا لو أردنا تعريف مؤشر على دالة عامة غير مرتبطة بالكائن الذي يحتوي المؤشر؟ تعريف مؤشر على دالة بلا مبدل `@عضو` يؤدي الغرض، كما في المثال التالي:
  صنف صـنفي {
    @عضو دالة افعل (هذا: سند[هذا_الصنف]، ص: صحيح) { ... }
    عرف مد: مؤشر[دالة (ص: صحيح)]؛
  }

  عرف ص: صـنفي؛
  ص.مد = صـنفي.افعل~مؤشر؛ // خاطئ

  دالة افعل2 (ص: صحيح) { ... }
  ص.مد = افعل2~مؤشر؛ // صحيح
  class MyType {
    @member func doSomething (this: ref[this_type], j: Int) { ... }
    def pf: ptr[func (Int)];
  }

  def mt: MyType;
  mt.pf = MyType.doSomething~ptr; // error

  func doSomething2 (j: Int) { ... }
  mt.pf = doSomething2~ptr; // correct
يمكن استخدام الأمر `عملية` (handler) لتبسيط تعريف مؤشرات الوظائف، حيث أن تعريف مؤشر الوظيفة يشبه تعريف الوظيفة مع اختلاف واحد وهو إضافة الكلمة المفتاحية `كمؤشر` (as_ptr) في نهاية التعريف قبل متن الدالة، كما في المثال التالي:
  صنف صـنفي {
    عملية هذا.مو(ص: صحيح) كمؤشر { ... }؛
  }
  class MyType {
    handler this.mp(i: Int) as_ptr { ... };
  }
التعريف أعلاه يعرف المؤشر ويعرف في نفس الوقت الدالة ويقوم أيضًا بتعيين قيمة المؤشر أثناء تهيئة الكائن ليشير إلى الدالة.
يمكن أيضًا استخدام `عملية` لتعريف المؤشر فقط دون دالة يشير إليها، كما يلي:
  صنف صـنفي {
    عملية هذا.مو(ص: صحيح) كمؤشر؛
  }
  class MyType {
    handler this.mp(i: Int) as_ptr;
  }
وفي حالة توفر المؤشر مسبقًا والرغبة فقط بتعريف الدالة وتهيئة المؤشر الموجود مسبقًا ليشير إلى الدالة تستخدم الصيغة التالية:
  صنف صـنفي {
    عملية هذا.مو(ص: صحيح) حدد_مؤشر { ... }؛
  }
  class MyType {
    handler this.mp(i: Int) set_ptr { ... };
  }
تعريف المؤشر بشكل منفصل عن تحديد قيمته مفيد في البرمجة الكائنية، وتحديدًا في تعددية الأشكال الموروثة (polymorphism)، حيث يُعرف المؤشر في الصنف الأصل وتحدد قيمته في الفرع.

تخصيص تهيئة الكائنات

يمكن للمستخدم تخصيص عملية تهيئة الكائنات باستخدام الأمر `عملية` (handler) وهذه صيغته:
  عملية هذا~هيئ (<معطيات>) { <متن_الدالة> }؛
  handler this~init (<argument_definitions>) { <body> };
عند تعريف هذه العملية داخل الصنف يقوم المترجم باستدعاء هذه الدالة كلما احتاج لتهيئة كائن جديد. يمكن تعريف معطيات لهذه الدالة إذا أردت تهيئة الكائن من كائن آخر كما هو الحال عند إرجاع هذا الكائن من دالة كقيمة أو استلامه في الدالة كمعطى. فعند تهيئة العنصر في إحدى هذه الحالات يقوم المترجم باستدعاء هذه الدالة وتمرير العنصر الأصلي كي تقوم الدلة بنسخه لـ`هذا`. أما في حالة عدم تخصيص عملية التهيئة فإن المترجم في هذه الحالات يقوم بنسخ ذاكرة الكائن.
ملاحظة: عند تعريف أي عملية لتخصيص التهيئة يمتنع المترجم عن التهيئة الافتراضية في كل الحالات وبالتالي تحتاج لتعريف كل حالات التهيئة، أي حالة التهيئة بدون معطيات وحالة التهيئة من كائن آخر.
المثال التالي يوضح تخصيص التهيئة:
  صنف نـقطة {
    عرف س: صحيح؛
    عرف ص: صحيح؛

    عملية هذا~هيئ() {
      هذا.س = 0؛
      هذا.ص = 0؛
    }؛
    عملية هذا~هيئ(مصدر: سند[نـقطة]) {
      هذا.س = مصدر.س؛
      هذا.ص = مصدر.ص؛
    }؛
  }؛

  عرف ن: نـقطة: // يستدعي عملية التهيئة دون معطيات.
  دالة هات_نقطة (): نـقطة {
    عرف ن: نقطة؛
    ارجع ن: // يستدعي عملية التهيئة بالمعطى لتهيئة العنصر الذي سيستلم ن
  }؛
  class Point {
    def x: int;
    def y: int;

    handler this~init() {
      this.x = 0;
      this.y = 0;
    };
    handler this~init(src: ref[Point]) {
      this.x = src.x;
      this.y = src.y;
    };
  }:

  def p: Point; // calls this~init()
  func getPoint (): Point {
    def p: Point;
    return p; // calls this~init(src)
  };
عند وجود عمليات تهيئة بمعطيات فإن تعريف المتغير يحتاج لتمرير هذه المعطيات، ويكون التعريف كالتالي:
  عرف <اسم_المتغير>: <صنف_المتغير>(<معطيات>)؛
  def <var_name>: <var_type>(<arguments>);
المثال التالي يوضح تهيئة بمعطيات:
  صنف نـقطة {
    عرف س: صحيح؛
    عرف ص: صحيح؛

    عملية هذا~هيئ(س: صحيح، ص: صحيح) {
      هذا.س = س؛
      هذا.ص = ص؛
    }؛
  }؛

  عرف ن: نـقطة(5، 10)؛
  class Point {
    def x: int;
    def y: int;

    handler this~init(x: int, y: int) {
      this.x = x;
      this.y = y;
    };
  }:

  def p: Point(5, 10);
يمكن أيضا تخصيص التهيئة بدون معطيات بكتابة عمليات تلك التهيئة مباشرة داخل متن الصنف، وهذا مطابق لتعريف `عملية هذا~هيئ()`، كما في المثال التالي:
  صنف نـقطة {
    عرف س: صحيح؛
    عرف ص: صحيح؛

    هذا.س = 0؛ // مطابق لفعلها داخل عملية هذا~هيئ()
    هذا.ص = 0؛ // مطابق لفعلها داخل عملية هذا~هيئ()

    عملية هذا~هيئ(مصدر: سند[نـقطة]) {
      هذا.س = مصدر.س؛
      هذا.ص = مصدر.ص؛
    }؛
  }؛

  عرف ن: نـقطة: // يستدعي عملية التهيئة دون معطيات.
  دالة هات_نقطة (): نـقطة {
    عرف ن: نقطة؛
    ارجع ن: // يستدعي عملية التهيئة بالمعطى لتهيئة العنصر الذي سيستلم ن
  }؛
  class Point {
    def x: int;
    def y: int;

    this.x = 0;
    this.y = 0;

    handler this~init(src: ref[Point]) {
      this.x = src.x;
      this.y = src.y;
    };
  }:
ملاحظة: إذا احتوى الصنف على متغيرات من صنف ذي تهيئة مخصصة، فإن الصنف الخارجي يعتبر ذا تهيئة مخصصة أيضا حتى لو لم يعرف المستخدم علميات تهيئة لذلك الصنف. السبب في ذلك أن المترجم تلقائيا يعرف دالات تهيئة لتهيئة العناصر الداخلية.

تخصيص إتلاف الكائنات

مثلما هو الحال مع تهيئة الكائنات، يمكن للمستخدم تخصيص عملية إتلاف الكائن وذلك بإضافة التعريف التالي إلى الصنف:
  عملية هذا~أتلف () { <متن_الدالة> }؛
  handler this~terminate () { <body> };
عند خروج التنفيذ من أي مجال فإن المترجم يقوم تلقائيا باستدعاء هذه الدالة لكل العناصر المعرفة ضمن ذلك المجال. يمكن استخدام هذه الدالة لتحرير أي موارد تم حجزها من قبل ذلك الكائن.

تسلسل تهيئة الكائنات

عند تهيئة كائن ذي تهيئة مخصصة فإن المترجم يبدأ أولا بتهيئة كل متغير من متغيرات الكائن تهيئة كاملة قبل أن يستدعي دالة التهيئة التابعة للكائن نفسه. والتسلسل نفسه يتبع عند تهيئة كل متغير من المتغيرات إذا كان هو الآخر يحتوي على متغيرات بتهيئة مخصصة. بمعنى آخر التهيئة تتم دائما من الأطراف إلى الجذر، أما عملية الإتلاف فتكون بشكل عكسي، أي يبدأ الإتلاف من الجذر إلى الأطراف، أي بتنفيذ دالة الإتلاف للكائن الرئيسي ثم للعناصر الداخلية ثم العناصر الأعمق وهكذا بشكل مشابه لخوارزية بحث العمق أولا.

تخصيص المؤثرات

يمكن للمستخدم أيضا تخصيص المؤثرات المطبقة على الكائنات باستخدام الأمر `عملية` (handler) بطريقة مشابهة لتخصيص التهيئة مع استخدام المؤثر المطلوب بدل الأمر `~هيئ`. المثال التالي يوضح تخصيص مؤثر المساواة على كائن:
  صنف نـص {
    ...
    عملية هذا = مؤشر[مصفوفة[محرف]] {
      هذا.انسخ_محارف(قيمة)؛
    }؛
    عملية هذا == مؤشر[مصفوفة[محرف]] {
      ارجع هذا.قارن(قيمة)؛
    }؛
  }؛

  عرف ن: نـص؛
  ن = "بسم الله"؛
  إذا ن == "بسم الله" اطبع("النص مطابق")؛
  class String {
    ...
    handler this = ptr[array[char]] {
      this.copyChars(value);
    };
    handler this == ptr[array[char]] {
      return this.compare(value);
    };
  };

  def s: String;
  s = "hello world";
  if s == "hello world" print("text is identical");
يمكن باستخدام هذه الطريقة تخصيص كل المؤثرات، أي تخصيص أي من مؤثرات المقارنة أو العمليات الرياضية أو العمليات البتية كما يمكن أيضا تخصيص الأقواس كما في المثال التالي:
  صنف نـص {
    ...
    عملية هذا(موقع: صحيح): محرف {
      ارجع هذا.هات_محرف(موقع)؛
    }؛
  }؛

  عرف ن: نـص؛
  ن = "!@#$"؛
  اطبع(ن(1))؛ // سيطبع @
  class String {
    ...
    handler this(pos: int): char {
      return this.getChar(pos);
    };
  };

  def s: String;
  s = "!@#$";
  print(s(1)); // prints @

تخصيص إنشاء المتغيرات المؤقتة

يمكن تخصيص عملية إنشاء المتغيرات المؤقتة، والتي يتم إنشاؤها بإتباع اسم الصنف بأقواس. تخصيص هذه العملية يتم بتخصيص مؤثر الأقواس على الصنف ذاته وليس على كائن من ذاك الصنف، أي مؤثر الأقواس على `هذا_الصنف` (this_type) بدل `هذا` (this). المثال التالي يوضح الطريقة:
صنف قـيد {
    ...
    عملية هذا_الصنف(): سند[قـيد] {
        عرف س: سند[قـيد]؛
        س~مؤشر = ذاكـرة.احجز(قـيد~حجم)~مثل[مؤشر[قـيد]]؛
        س~هيئ()؛
        أرجع س؛
    }
}

دالتي(قـيد())؛ // سيستدعي العملية المخصصة وسينشئ الكائن ديناميكيا
    // بدل إنشاء متغير مؤقت على المكدس.
class Record {
    ...
    handler this_type(): ref[Record] {
        def r: ref[Record];
        r~ptr = Memory.alloc(Record~size)~cast[ptr[Record]];
        r~init();
        return r;
    }
};

myFunc(Record()); // Will call the custom operation and allocate dynamically
    // on the heap instead of creating a temp var on the heap.

حقن التعريفات

خاصية حقن التعريفات تمكن المستخدم من جعل عناصر تعريف معين متوفرة مباشرة في المجال الخارجي، وهذه الخاصية من لبنات البناء الأساسية التي يمكن استخدامها لتوفير خاصيات أخرى مثل الوراثة (inheritance) أو المؤشرات الذكية أو غيرها. كل ما تحتاج لجعل عناصر تعريف متوفرة داخل مجال الصنف الحاوي أن تَسِم التعريف بالمبدل @حقنة (injection) كما في المثال التالي:
  صنف داخـلي {
    عرف س: صحيح؛
    دالة اطبع_س { ... }؛
  }؛

  صنف خـارجي {
    @حقنة عرف د: داخـلي؛
    عرف ص: صحيح؛
    دالة اطبع_ص { ... }؛
  }؛

  عرف خ: خـارجي؛
  خ.س = 1؛ // يحولها المترجم إلى خ.د.س
  خ.ص = 2؛
  خ.اطبع_س()؛ // يحولها المترجم إلى خ.د.اطبع_س()
  خ.اطبع_ص()؛
  class Inner {
    def x: Int;
    func printX { ... };
  };

  class Outer {
    @injection def i: Inner;
    def y: Int;
    func printY { ... };
  };

  def o: Outer;
  o.x = 1; // compiler translates it to o.i.x
  o.y = 2;
  o.printX(); // compiler translates it to o.i.printX()
  o.printY();

استنباط الأصناف


يمكن استنباط معلومات عن الأصناف أثناء الترجمة باستخدام المؤثرات التالية:

مؤثر ~صنف (~type)

باستخدام هذا المؤثر يمكن استنباط صنف متغير معين كما في المثال التالي:
  عرف س: صحيح[64]؛
  عرف ص: س~صنف؛ // صنف ص هنا مطابق لصنف س، وهو عدد صحيح[64].

  عرف م: مؤشر؛
  م = س~مثل[مؤشر]؛
  م = س~مثل[م~صنف]؛ // مطابقة للجملة أعلاه.
  def x: Int[64];
  def y: x~type; // y here has the same type as x, which is Int[64].

  def p: ptr;
  p = x~cast[ptr];
  p = x~cast[p~type]; // Equivalent to the upper statement.
هذا المؤثر مفيد بشكل أساسي في القوالب والماكروهات حيث صنف المتغير قد لا يكون معروفا إلا عند استخدام القالب أو الماكرو.

مؤثر ~حجم (~size)

يستخدم هذا المؤثر لمعرفة حجم صنف أو متغير في الذاكرة، أي عدد البايتات التي يستهلكها الصنف في الذاكرة. يمكن استخدام هذا المؤثر على صنف أو متغير أو حتى على تركيب، كما في الأمثلة التالية:
  عرف س: صحيح[16]؛
  عرف ص: صحيح[32]؛
  دالة هات_صحيح (): صحيح { ... }؛

  طـرفية.اطبع(س~حجم)؛ // يطبع 2.
  طـرفية.اطبع(ص~حجم)؛ // يطبع 4.
  طـرفية.اطبع(صـحيح[64]~حجم)؛ // يطبع 8
  طـرفية.اطبع(هات_صحيح()~حجم)؛ // يطبع 4؛
  طـرفية.اطبع((س + ص)~حجم)؛ // يطبع 4؛
  def x: Int[16];
  def y: Int[32];
  func getInt (): Int { ... };

  Console.print(x~size); // Prints 2.
  Console.print(y~size); // Prints 4.
  Console.print(Int[64]~size); // Prints 8.
  Console.print(getInt()~size); // Prints 4.
  Console.print((x + y)~size); // Prints 4.

المؤشرات


المؤشرات تستخدم للإشارة إلى مواقع في الذاكرة والتحكم بمحتويات تلك المواقع. تُعرّف المؤشرات باستخدام الصنف "مؤشر" (ptr) متبوعاً بقوسين معقوفين بينهما صنف محتوى الذاكرة المشار إليه بالمؤشر:
  عرّف <اسم_المؤشر> : مؤشر[<صنف_المحتوى>]
  def <ptr_name> : ptr[<content_type>]
يمكن الولوج إلى المحتوى المشار إليه بالمؤشر عن طريق المؤثر "~محتوى" (~cnt) ويمكن الحصول على موقع أي متغير عن طريق المؤثر "~مؤشر" (~ptr) كما في المثال التالي:
  عرّف م : مؤشر[العدد_الصحيح]؛
  عرّف س : العدد_الصحيح؛
  س = 5؛
  م = س~مؤشر؛
  م~محتوى = 1؛
  // قيمة س الآن 1 وليس 5.
  def p : ptr[Int];
  def x : Int;
  x = 5;
  p = x~ptr;
  p~cnt = 1;
  // x is now equal to 1, not 5.
يمكن تطبيق عمليات الجمع والطرح على المؤشرات، وفي هذه الحالة فإن مقدار الزيادة لقيمة المؤشر ستكون من مضاعفات حجم الصنف الذي يشير إليه المؤشر. على سبيل المثال، إذا أضفت 1 لمؤشر على عدد صحيح فإن قيم المؤشر ستزداد بمقدار حجم العدد الصحيح، أي بمقدار 4 بايتات في حالة العدد الصحيح بصيغة 32 بت.
  عرّف م1: مؤشر[صحيح[32]] = ...؛
  عرف م2: مؤشر[محرف] = ...؛
  م1 = م1 + 1؛ // سيزداد بمقدار 4.
  م1 = م1 + 5؛ // سيزداد بمقدار 4 * 5، أي 20.
  م2 = م2 + 1؛ // سيزداد بمقدار 1.
  م2 = م2 + 5؛ // سيزداد بمقدار 5.
  def p1: ptr[Int[32]] = ...;
  def p2: ptr[Char] = ...;
  p1 = p1 + 1; // Incremented by 4.
  p1 = p1 + 5; // Incremented by 5 * 4.
  p2 = p2 + 1; // Incremented by 1.
  p2 = p2 + 5; // Incremented by 5.

السندات


السندات مشابهة لعمل المؤشرات إلا أنها أبسط في التعامل من المؤشرات حيث لا تتطلب منك سوى تعريف المتغير على أنه سند ومن ثم التعامل معه بنفس طريقة التعامل مع متغيرات اعتيادية، اي الوصول إلى المحتوى دون الحاجة لاستخدام المؤثر `~محتوى`.
  عرّف <اسم_السند> : سند[<صنف_المحتوى>]
  def <ref_name> : ref[<content_type>]
قبل استخدام السند تحتاج لتحديد قيمة المؤشر لذلك السند ويختلف ذلك فيما لو كان السند أحد معطيات دالة أم لا. إذا كان السند معطى لدالة فكل ما تحتاج لفعله تمرير متغير من صنف محتوى السند لتلك الدالة ويتولى المترجم تلقائيا تمرير مؤشر المتغير واستخدامه لذلك السند كما في المثال التالي:
  دالة ضاعف (سم: سند[صحيح]) { سم *= 2 }؛

  عرف م: صحيح = 5؛
  افعل(م)؛
  // الآن م == 10
  func twice (ri: ref[int]) { ri *= 2 };

  def i: int = 5;
  twice(i);
  // now i == 10
في حالة كون السند معرفا كمتغير اعتيادي وليس معطى لدالة فتحتاج لتحديد مؤشره يدويا كما في المثال التالي:
  عرف سم: سند[صحيح]؛
  عرف م: صحيح؛
  سم~مؤشر = م~مؤشر؛
  سم = 3؛
  // الآن م == 3
  def ri: ref[Int];
  def i: Int;
  ri~ptr = i~ptr;
  ri = 3;
  // now i == 3
بالإمكان أيضا استخدام المؤثر `~مؤشر` لجعل السند يشير إلى حجز ديناميكي للذاكرة:
  عرف سم: سند[صـنفي]؛
  سم~مؤشر = ذاكـرة.احجز(صـنفي~حجم)~مثل[مؤشر[صـنفي]]؛
  def r: ref[MyType];
  r~ptr = Memory.alloc(MyType~size)~cast[ptr[MyType]];
كما يمكنك تعريف سند لسند كما في المثال:
  عرف سسم: سند[سند[صحيح]]؛
  عرف سم: سند[صحيح]؛
  عرف م: صحيح؛
  سم~مؤشر = م~مؤشر؛
  سسم~مؤشر~مؤشر = سم~مؤشر~مؤشر؛
  سسم = 3؛
  // الآن م == 3
  def rri: ref[ref[Int]];
  def ri: ref[Int];
  def i: Int;
  ri~ptr = i~ptr;
  rri~ptr~ptr = ri~ptr~ptr;
  rri = 3;
  // now i == 3
لاحظ أن المؤثر `~مؤشر` يبدأ دائما من المحتوى. بمعنى آخر لو عرفنا سسص على أنه `سند[سند[صحيح]]` وعرفنا سص على أنه `سند[صحيح]` فإن `سسص~مؤشر` و `سص~مؤشر` كلاهما يرجعان مؤشرا على صحيح. كما أن العمليات التي نطبقها على السند دائما تطبق على المحتوى بغض النظر عن عمق السند، لذا فإن `سسص = 5` و `سص = 5` كلاهما يعدلان المحتوى رغم أن الأول سند مزدوج.

السندات المؤقتة

إذا كانت الدالة تستقبل سندا فلا يمكن استدعاؤها باستخدام قيمة. على سبيل المثال، إذا كانت الدالة ا ترجع قيمة وكانت الدالة ب تستقبل سند لنفس الصنف، فلا يمكن تمرير القيمة المرجعة من ا كمعطى للدالة ب. هذا الأمر مقصود لتجنب الأخطاء غير المقصودة والتي قد تؤدي إلى segmentation fault (لأن وجود القيمة في الذاكرة مؤقت وبالتالي فالاحتفاظ بسند لتلك القيمة سيؤدلي لاحقاً إلى ولوج لذاكرة غير مرخصة). لكن في بعض الحالات قد يكون استلام سند على قيمة مؤقتة آمنًا لأن الحاجة لتلك القيمة المؤقتة تنتهي بعد الخروج من الدالة. في هذه الحالات يمكن تعريف السند على أنه سند مؤقت وفي هذه الحالة سيقوم المترجم تلقائيًا بتمرير سند حتى لو أعطيناه قيمة وليس متغيرًا (سيقوم في هذه الحالة بتحويل القيمة إلى سند تلقائيًا). مثال:
  دالة استلم_سند1 (س: سند[صحيح]) { ... }
  دالة استلم_سند2 (س: سند_مؤقت[صحيح]) { ... }

  استلم_سند1(7ص32)؛ // خطأ. الدالة تحتاج إلى متغير وليس قيمة.
  استلم_سند2(7ص32)؛ // مقبول. سيحول المترجم هذه القيمة تلقائيًا إلى سند.
  func receiveRef1 (r: ref[Int]) { ... }
  func receiveRef2 (r: temp_ref[Int]) { ... }

  receiveRef1(7i32); // Error. The functions needs a variable, not a value.
  receiveRef2(7i32); // Accepted. The compiler will automatically generate a reference out of this value.

مؤثر ~عطل_التتبع (~no_deref)

في بعض الحالات قد نحتاج لمنع المترجم من تتبع السند إلى القيمة، كما في الحالات التي نحتاج فيها لتغيير السند نفسه وليس القيمة التي يؤشر إليها. في هذه الحالات نستخدم المؤثر ~عطل_التتبع لإخبار المترجم أننا نريد تعديل السند نفسه وليس القيمة. كما في المثال التالي:
  عرف ع: صحيح؛
  عرف س: سند[صحيح]؛

  س = ع؛ // سيؤدي لتغيير القيمة المخزونة في الموقع الذي يشير إليه س.
  س~عطل_التتبع = ع؛ // سيجعل س يشير إلى ع.
  def i: Int;
  def r: ref[Int];

  r = i; // Will change the value pointed to by r.
  r~no_deref = i; // Will make r point to i.
استخدام مؤثر ~عطل_التتبع على متغير غير سند ليس له أي تأثير لكنه لا يؤدي إلى خطأ.
  عرف ع: صحيح؛
  ع~عطل_التتبع = 7؛ // مطابق لـ: ع = 7؛
  def i: Int;
  i~no_deref = 7; // Equivalent to: i = 7;
هذا المؤثر مهم جدا في حالة القوالب. بدون استخدام هذا المؤثر فإن القوالب ستتصرف بصورة مختلفة عند استخدامها مع السندات عما سيكون تصرفها في حالة استخدامها مع أصناف أخرى غير السندات. لاحظ المثال التالي:
  صنف صـنف1 [ن: صنف] {
    عرف س: ن؛
    س = 0؛
  }
  عرف ك: صـنف1[صحيح]؛ // لا مشكلة. سيصفر قيمة س.
  عرف ل: صـنف1[سند[صحيح]]؛ // سيؤدي إلى segfault لأنه سيحاول تصفير موقع عشوائي لأن س سند.


  صنف صـنف2 [ن: صنف] {
    عرف س: ن؛
    س~عطل_التتبع = 0؛
  }
  عرف م: صـنف2[صحيح]؛ // لا مشكلة. سيصفر قيمة س.
  عرف ن: صـنف2[سند[صحيح]]؛ // لا مشكلة. سيصفر قيمة س، اي قيمة المؤشر.
  class Tp1 [T: type] {
    def x: T;
    x = 0;
  }
  def i: Tp1[Int]; // No problem, x will be set to 0.
  def j: Tp1[ref[Int]]; // Causes segfault for updating a random location in memory.

  class Tp2 [T: type] {
    def x: T;
    x~no_deref = 0;
  }
  def k: Tp2[Int]; // No problem, value of x will be set to 0.
  def l: Tp2[ref[Int]]; // No problem, value of x will be set to 0, i.e. the pointer value.

المصفوفات


تعرّف المصفوفات باستخدام الصنف "مصفوفة" (array) متبوعاً بأقواس معقوفة تحتوي صنف عناصر المصفوفة وعددها:
  عرّف <اسم_المصفوفة> : مصفوفة[<صنف_العناصر>، <عدد_العناصر>]
  def <array_name> : array[<element_type>, <element_count>]
يمكن الدخول إلى عناصر المصفوفة بإعطاء رقم العنصر المعني بين قوسين معقوفين. مثال:
  عرّف مصفوفتي : مصفوفة[صحيح، 10]؛
  عرّف ع : صحيح؛
  لكل ع=0، ع<10، ع++ {
    مصفوفتي[ع] = مضروب(ع)
  }
  def myArray : array[Int, 10];
  def i : Int;
  for i=0, i<10, i++ {
    myArray[i] = factorial(i)
  }

تمثيل الأصناف


يمكن تمثيل المتغيرات بصنف غير صنفها الحقيقي باستخدام المؤثر "~مثّل" (~cast) متبوعاً بقوسين معقوفين بينهما الصنف المراد تمثيله، كما في المثال التالي:
  عرّف ح : عـائم؛
  إطبع_عددا_صحيحا(ح~مثّل[صحيح])؛
  def f : Float;
  printInteger(f~cast[Int]);
في الوقت الحالي عملية التمثيل محدودة ومازالت قيد التطوير، لكنها قريباً ستدعم تمثيل الأصناف دون قيود، مثل تمثيل عدد صحيح كمؤشر أو تمثيل مؤشر لصنف ما كمؤشر لصنف آخر.

الكائنات المؤقتة


يمكن إنشاء الكائنات بشكل مؤقت دون ربطها بمتغير وذلك باستخدام أقواس فارغة أو مصحوبة بمعطيات التهيئة، كما في المثال:
  ارسم_نقطة(نـقطة())؛
  ارسم_نقطة(نـقطة(5، 10))؛
  drawPoint(Point());
  drawPoint(Point(5, 10));
في المثال أعلاه يُنشأ كائن مؤقت من صنف `نـقطة` أثناء استدعاء دالة `ارسم_نقطة`، ويُتلف هذا الكائن تلقائيًا بعد الانتهاء من تنفيذ الجملة. إتلاف الكائنات المؤقتة يتم دائمًا بعد الانتهاء من تنفيذ الجملة كاملة (التي يُنشأ فيها الكائن المؤقت) وليس بعد انتهاء تنفيذ الجزء الذي يستخدم الكائن المؤقت. على سبيل المثال:
  اطبع(احسب_المسافة(نـقطة(5، 10)، نـقطة(20، 25)))؛
  print(calculateDistance(Point(5, 10), Point(20, 25)));
في المثال أعلاه يتم إتلاف الكائنين المؤقتين بعد الانتهاء من تنفيذ الجملة كلها، أي بعد الانتهاء من تنفيذ دالة الطباعة وليس بعد الانتهاء من تنفيذ دالة `احسب_المسافة`.

رزم الأوامر


تمكن هذه الخاصية المستخدم من تنفيذ مجموعة من الأوامر على كائن دون الخروج من السياق الحالي للتنفيذ ودون تكرار اسم الكائن وذلك باستخدام مؤثر النقطة متبوعًا برزمة من الجمل بين قوسين حاصرين، كما في المثال التالي: المقصود بعدم الخروج من سياق التنفيذ الحالي أن البرنامج ينفذ رزمة الأوامر بشكل اعتراضي قبل أن يعود ليكمل التنفيذ من حيث كان قبل تنفيذ الرزمة.
  صنف نـقطة {
    عرف س: صـحيح؛
    عرف ص: صـحيح؛
  }
  
  عرف ن: نـقطة؛
  ن.{ س = 5؛ ص = 10 }؛
  // الجملة أعلاه مطابقة ل:
  // ن.س = 5؛
  // ن.ص = 10؛
  class Point {
    def x: Int;
    def y: Int;
  }
  
  def p: Point;
  p.{ x = 5; y = 10 };
  // Upper statement is equivalent to:
  // p.x = 5;
  // p.y = 10;
المقصود بعدم الخروج من سياق التنفيذ الحالي أن البرنامج ينفذ رزمة الأوامر بشكل اعتراضي قبل أن يعود ليكمل التنفيذ من حيث كان قبل تنفيذ الرزمة. على سبيل المثال:
  ارسم_نقطة(ن.{ س = 5؛ ص = 10 })؛
  drawPoint(p.{ x = 5; y = 10 });
في المثال أعلاه تُنفذ رزمة الأوامر على المتغير ن قبل تمريره إلى الدالة `ارسم_نقطة`، وبالتالي فإن الجملة مطابقة لـ:
  ن.س = 5؛
  ن.ص = 10؛
  ارسم_نقطة(ن)؛
  p.x = 5;
  p.y = 10;
  drawPoint(p);
يمكن أيضًا تنفيذ رزمة الأوامر على كائن مؤقت، كما في المثال التالي:
  ارسم_نقطة(نـقطة().{ س = 5؛ ص = 10 })؛
  drawPoint(Point().{ x = 5; y = 10 });
في داخل رزمة الأوامر، الكلمة المفتاحية `هذا` (this) تشير إلى الكائن الذي تُنفذ عليه رزمة الأوامر، وبالتالي يمكنك كتابة الجملة أعلاه بهذه الطريقة:
  نـقطة().{ س = 5؛ ص = 10؛ ارسم_نقطة(هذا) }؛
  Point().{ x = 5; y = 10; drawPoint(this) };
يمكن أيضًا استخدام رزمة الأوامر مع الأصناف الأساسية، كما في المثال التالي:
  اطبع("أدخل رقمًا: ")؛
  عرف ر: صحيح(أدخل_صحيح())؛
  اطبع("الرقم %s.\ج"، مؤشر[محرف]().{
      إذا ر > 0 هذا = "موجب"
      وإلا إذا ر < 0 هذا = "سالب"
      وإلا هذا = "صفر"
  })؛
  print("Enter a number: ");
  def i: Int(getInt());
  print("Number is %s.\n", ptr[Char]().{
      if i > 0 this = "positive"
      else if i < 0 this = "negative"
      else this = "zero"
  });

حجز وتهيئة الكائنات ديناميكيا


يمكن للمستخدم أن يهيئ الكائنات المنشأة ديناميكيًا باستخدام الأمر `~هيئ` (~init) كما في المثال التالي:
  صنف نـقطة {
    عملية هذا~هيئ() { ... }؛
    عملية هذا~أتلف() { ... }؛
    ...
  }؛

  عرف ن: سند[نـقطة]؛
  ن~مؤشر = ذاكـرة.احجز(نـقطة~حجم)~مثل[مؤشر[نـقطة]]؛
  ن~هيئ()؛
  class Point {
    handler this~init() { ... };
    handler this~terminate() { ... };
    ...
  };

  def p: ref[Point];
  p~ptr = Memory.alloc(Point~size)~cast[ptr[Point]];
  p~init();
الفرق بين استخدام الأمر `~هيئ` واستدعاء دالة مستخدم عادية مخصصة للتهيئة أن استخدام `~هيئ` يضمن لك أيضا تهيئة المتغيرات الداخلية لهذا الصنف (إذا كانت هي الأخرى ذات تهيئة مخصصة). أي أن الأمر `~هيئ` يضمن تهيئة شجرة المتغيرات كاملة بكل الأعماق بدل أن تحتاج أن تهيئ كل متغير يدويا. مثلاً، إذا كان الصنف نـقطة يحتوي على متغير من صنف مخصص التهيئة ويحتوي هو الآخر على متغيرات من أصناف مخصصة التهيئة فإن الأمر `~هيئ` يضمن لك تهيئة كل هذه العناصر وبالتسلسل المطلوب.
كما هو الحال ما تهيئة الكائنات، يمكن للمستخدم استدعاء دالة الإتلاف يدويا لإتلاف الكائنات المحجوزة ديناميكيا، وبهذه الصيغة:
  ن~أتلف()؛
  p~terminate();

الألقاب


لتيسير استخدام المكتبات المكتوبة بالانجليزية داخل برامج مكتوبة بالعربية (أو العكس) يمكن إنشاء ألقاب للدالّات والأصناف والمتغيرات وذلك باستخدام الأمر "لقب" (alias) كتعريف للأمر "عرّف". بعد ذلك يمكن استخدام الأسم الأصلي أو اللقب كيفما يشاء المستخدم فكلاهما يشيران إلى نفس المعرّف. على سبيل المثال الصنف "العدد_الصحيح" هو لقب للصنف "Int" والدالة "اطبع" هي لقب للدالة "printf" وقد تم تعريف هذه الألقاب مسبقاً في الملف "متم.أسس" كما يلي:
  عرّف العدد_الصحيح : لقب int؛
  عرّف اطبع : لقب printf؛
إنشاء الألقاب لا يشترط أن يكون بين لغتين، فيمكن تعريف لقب انجليزي لمعرّف انجليزي على سبيل المثال، كما في الصنف Int الذي هو لقب لـint.

الوحدات


الوحدة مجال يمكن وضع التعريفات داخله. يمكن للوحدة أن تحتوي دالات أو متغيرات أو أصناف كما يمكن للوحدة أن تحتوي وحدات أخرى. تساعد الوحدات في تجنب الاصطدام الناتج من التشابه بين أسماء التعريفات حيث أن التعريفات داخل أي وحدة غير مرئية داخل الوحدات الأخرى ما لم تتم الإشارة إليها بشكل صريح.
يمكن تعريف الوحدة باستخدام الأمر `وحدة` (module) كما يلي:
  عرّف <اسم_الوحدة> : وحدة { <التعريفات> }؛
  def <module_name> : module { <definitions> };
يمكن الوصول إلى تعريفات داخل وحدة أخرى بأحد الطرق التالية:
  • أن تكون الوحدة الحالية نفسها داخل الوحدة التي تحتوي التعريف المطلوب كما في المثال التالي:
      عرف الـخارجية: وحدة {
        عرف م: صـحيح؛
    
        عرف الـداخلية: وحدة {
          عرف اطبع_م: دالة {
            اطبع(م)؛
          }
        }
      }
      def Outer: module {
        def v: Int;
    
        def Inner: module {
          def printV: function {
            print(v);
          }
        }
      }
  • أن يتم ذكر المجال الكامل للتعريف المراد الوصول إليه، ابتداءًا من أي مجال مشترك كما في المثال التالي:
      عرف الـخارجية: وحدة {
        عرف الـداخلية1: وحدة {
          عرف م: صـحيح؛
        }؛
    
        عرف الـداخلية2: وحدة {
          عرف اطبع_م: دالة {
            اطبع(الـداخلية1.م)؛
          }
        }
      }
      def Outer: module {
        def Inner1: module {
          def v: Int;
        };
    
        def Inner2: module {
          def printV: function {
            print(Inner1.v);
          }
        }
      }
  • باستخدام الأمر `استخدم` (use). يستخدم هذا الأمر لجعل مجال معين متوفرًا داخل المجال الحالي وصيغته كما يلي:
      استخدم <المجال_الكامل_للوحدة_المستهدفة>؛
      use <full_path_of_targetted_module>;
    هذا الأمر مفيد لتجنب الحاجة لتكرار ذكر المجال الكامل. يمكن استخدام هذا الأمر داخل وحدة أخرى أو داخل دالة ولا يمكن استخدامه في المجال الرئيسي خارج الوحدات. المثال التالي يوضح استخدام الأمر:
      عرف الـخارجية: وحدة {
        عرف الـداخلية1: وحدة {
          عرف الـداخلية2: وحدة {
            عرف م: صـحيح؛
          }؛
        }؛
    
        استخدم الـداخلية1.الـداخلية2؛
    
        عرف اطبع_م: دالة {
          اطبع(م)؛
        }
      }
      def Outer: module {
        def Inner1: module {
          def Inner2: module {
            def v: Int;
          };
        };
    
        use Inner1.Inner2;
    
        def printV: function {
          print(v);
        }
      }
يمكن أيضًا استخدام الصيغة المختصرة التي تغنيك عن الأمر "عرف":
  وحدة <اسم_الوحدة> { <التعريفات> }؛
  module <module_name> { <definitions> };
كما في المثال التالي:
  وحدة الـخارجية {
    وحدة الـداخلية1 {
      وحدة الـداخلية2 {
        عرف م: صـحيح؛
      }؛
    }؛
  }
  module Outer {
    module Inner1 {
      module Inner2 {
        def v: Int;
      };
    };
  }

الماكروهات


الماكرو هو مجموعة من الأوامر يمكن تكرارها بسهولة في أي مكان من البرنامج. يتم تعريف الماكرو كما يلي:
  عرّف <اسم_الماكرو> : ماكرو [<قائمة_المدخلات>] <متن_الماكرو>
  def <macro_name> : macro [<arg_list>] <macro_body>
متن الماكرو يمكن أن يكون سطراً واحداً أو مجموعة أسطر بين أقواس حاصرة. ثم يُستخدم الماكرو لاحقاً في البرنامج بكتابة اسمه متبوعاً بالمعطيات بين أقواس معقوفة، كما في المثال التالي:
  عرّف تربيع: ماكرو [م] م * م؛
  .
  .
  ص = تربيع[س]؛
  def power2: macro [n] n * n;
  .
  .
  s = power2[a];
يمكن كذلك استخدام الصيغة المختصرة التي تغنيك عن استخدام الأمر "عرف":
  ماكرو <اسم_الماكرو> [<قائمة_المدخلات>] <متن_الماكرو>
  macro <macro_name> [<arg_list>] <macro_body>
كما في المثال التالي:
  ماكرو تربيع [م] م * م؛
  .
  .
  ص = تربيع[س]؛
  macro power2 [n] n * n;
  .
  .
  s = power2[a];

القوالب في المعرفات وسلاسل المحارف

يمكن استخدام القوالب مع المعرفات وسلاسل المحارف داخل الماكرو ويتم ذلك في المعرفات بحصر القسم المتغير من المعرف بين شارحتين سفليتين متتاليتين من كل جانب، أما في سلاسل المحارف فيتم حصر القسم المتغير بين قوسين حاصرين مزدوجين، كما في المثال التالي:
  عرّف اطبع_متغيرات: ماكرو [م] {
    اطبع("{{م}}1 = %d\ج"، __م__1)؛
    اطبع("{{م}}2 = %d\ج"، __م__2)؛
  }؛
  .
  .
  س1 = 5؛
  س2 = 6؛
  اطبع_متغيرات[س]؛

  // سيطبع التالي:
  //  س1 = 5
  //  س2 = 6
  def print_vars: macro [v] {
    print("{{v}}1 = %d\n", __v__1);
    print("{{v}}2 = %d\n", __v__2);
  };
  .
  .
  s1 = 5;
  s2 = 6;
  print_vars[s];

  // will print:
  //  s1 = 5
  //  s2 = 6

الفرق بين ماكرو لغة الأسُس وماكرو لغة السي

الماكرو في لغة الأسُس مختلف عن نظيره في لغة السي في طريقة المعالجة. في لغة السي الماكرو يعالج نصياً في مرحلة تسبق الإعراب بينما في لغة الأسُس الماكرو يعالج أثناء مرحلة الإعراب نفسها وهذا يؤدي إلى الفروقات التالية:
  • في لغة الأسُس الماكرو تأثيره محدود ضمن مجال تعريفه بعكس الماكر في لغة السي حيث يكون غير محدود بأي مجال. بمعنى آخر، في لغة الأسُس إذا عرفت ماكرو داخل مجال معين وحاولت استخدامه داخل مجال آخر فسيمنعك المترجم من ذلك.
  • يمكن في لغة الأسُس تعريف عدة ماكروهات بنفس الاسم طالما أنها معرفة ضمن مجالات مختلفة، وهذا غير ممكن في لغة السي.
  • في لغة الأسُس الأخطاء الإعرابية داخل الماكرو يتم استشعارها مباشرة بعكس لغة السي حيث لا تُستشعر الأخطاء الإعرابية إلا بعد انتهاء معالجة الماكرو والبدء بمرحلة الإعراب.
  • في لغة الأسُس لا يمكن تعريف ماكرو يحتوي متنه على أجزاء غير مكتملة من القواعد بعكس لغة السي التي تتيح ذلك. مثلاً، في لغة السي يمكن تعريف ماكرو يحتوي على جزء غير مكتمل من القواعد ويقوم المستخدم يتجميع ماكروهات متعددة للحصول على برنامج صحيح قواعدياً وهذا الأمر غير ممكن في لغة الأسُس التي تمنع أن يكون متن أي ماكرو غير مكتمل قواعدياً.

شجرة البنية المجردة (Abstract Syntax Tree)


الشفرة المصدرية بعد إعرابها تُحول إلى شجرة من البيانات تسمى شجرة البيانات المجردة (Abstract Syntax Tree) ويمكن للمستخدم الوصول لهذه البيانات. الوصول لهذه البيانات مفيد في بعض التعاملات مع المترجم مثل ترجمة دالة إلى شفرة تنفيذية أو ما شابه، كما أنه مفيد في تطوير خصائص جديدة عن طريق توليد شفرة جديدة برمجيا أو قراءة شفرة برمجيا لأي غرض كان. يمكن الوصول لشجرة البنية المجردة بطريقتين:
  • استخدام المؤثر ~شبم (~ast) على أي عنصر من الشفرة المصدرية للحصول على شجرة ذلك العنصر.
      مدير_البناء.أنشء_ملفا_رقميا_لعنصر(دالتي~شبم)
    
      buildMgr.buildObjectFileForElement(myFunction~ast)
    
  • استخدام الإيعاز شبم (ast) وإعطائه شفرة مصدرية للحصول على شجرة تلك الشفرة.
      مدير_شبم.احشر_شبم(شبم { ع = 0 }، ...)
    
      astMgr.insertAst(ast { i = 0 }, ...)
    
    اي أن الفرق بين إيعاز `شبم` والمؤثر `~شبم` أن الإيعاز يمكنك من كتابة الشفرة مباشرة ضمن الإيعاز بدل كتابتها ضمن تعريف ثم استخدام المؤثر ~شبم على اسم ذلك التعريف.

    مبدئيًا، يقوم المترجم بالمعالجة التمهيدية لمعطى الأمر `شبم` قبل استخدامه في مواقع أخرى من الشفرة المصدرية. على سبيل المثال، لنفرض أنك تستخدم الأمر `شبم` لكتابة شفرة مصدرية يتم حشرها باستخدام دالة `احشر_شبم`، والشفرة المصدرية المرغوب حشرها (معطى شبم) تحتوي على الأمر `تمهيد`. مبدئيًا، يعالج المترجم عبارة التمهيد ضمن معطى `شبم` نفسه، أي قبل تنفيذ الشفرة التي تحتوي استدعاء `احشر_شبم`. أحيانًا تحتاج لتأخير المعالجة التمهيدية لتتم بعد حشر الشفرة، أي بعد الانتهاء من تنفيذ دالة `احشر_شبم`. لفعل ذلك يمكن إضافة المبدل `@بلا_تمهيد` (@no_preprocess) للأمر `شبم`، كما في المثال التالي:

      ماكرو م [اسم] "__{{اسم}}__"؛
      مدير_شبم.احشر_شبم(شبم م[تجربة])؛ // سيحشر: "__تجربة__"
      مدير_شبم.احشر_شبم(@بلا_تمهيد شبم م[تجربة])؛ // سيحشر: م[تجربة]
    
      macro m [name] "__{{name}}__";
      astMgr.insertAst(ast m[test]); // inserts: "__test__"
      astMgr.insertAst(@no_preprocess ast m[test]); // inserts: m[test]
    

المعالجة التمهيدية


المعالجة التمهيدية تمكن المستخدم من تنفيذ شفرة مباشرة قبل ترجمة أي دالة أو صنف، وتمكن المستخدم من إنشاء وإدخال شفرة جديدة برمجيًا. تفيد هذه الخاصية في توليد شفرة برمجية اعتمادا على تعريف أبسط، وبالتالي تطوير اللغة وإضافة خاصيات جديدة بطريقة بسيطة. فيما يلي صيغة عبارة `تمهيد`:
  تمهيد <متن_التمهيد>
  preprocess <preprocess_body>
داخل متن التمهيد يمكن للمستخدم كتابة شفرة بلغة الأسس نفسها دون قيود ويمكنه إنشاء وإدخال شفرة جديدة باستخدام الكائن `نـبم.مدير_شبم` (Spp.astMgr). يمكن الاطلاع على دليل المكتبات التنفيذية لمعلومات مفصلة حول الوحدة `نـبم`. المثال التالي يوضح إنشاء مجموعة عبارات تعيين لمتغيرات يتم الاستعلام عنها بطريقة ديناميكية:
  تمهيد {
      عرف متغيرات: مـصفوفة[نـص] = جد_أسماء_المتغيرات(...)؛
      عرف ع: صحيح؛
      لكل ع = 0، ع < متغيرات.هات_الطول()، ++ع {
          نـبم.مدير_شبم.احشر_شبم(
              شبم { هذا.اسم = 0 }،
              تـطبيق[نـص، سند[الـقلب.أسـاسيات.كـائن_بهوية]]()
                  .حدد(نـص("اسم")، الـقلب.أسـاسيات.نـص_بهوية(متغيرات(ع)))
          )؛
      }
  }
  preprocess {
      def vars: Array[String] = findVarNames(...);
      def i: Int;
      for i = 0, i < vars.getLength(), ++i {
          Spp.astMgr.insertAst(
              ast { this.name = 0 },
              Map[String, ref[Core.Basic.TiObject]]()
                  .set(String("name"), Core.Basic.TiStr(vars(i)))
          );
      }
  }
يمكن لعبارات `تمهيد` أن تكون متداخلة، أي يمكن لمتن عبارة تمهيد أن يحتوي على عبارة تمهيد أخرى. في هذه الحالة ينفذ المترجم عبارة التمهيد الداخلية والتي بدورها تحشر شفرة داخل متن عبارة التمهيد الخارجية. بعد الانتهاء من تنفيذ العبارة الداخلية تُترجم عبارة التمهيد الخارجية ثم تنفذ، وهكذا.

دمج التعريفات


بعض التعريفات يمكن تجزئتها إلى عدة أجزاء تدمج لاحقاً عند البناء في تعريف واحد. تمكن هذه الخاصية المستخدم من توزيع التعريفات الكبيرة مثل الوحدات إلى عدة ملفات كما تمكن أيضاً من إلحاق إضافات إلى تعريفات مسبقة. يتم الدمج باستخدام المبدل `@دمج` (@merge) كما في المثال التالي:
  عرف نـقطة: صنف {
    عرف س: صـحيح؛
  }؛
  .
  .
  @دمج عرف نـقطة: صنف {
    عرف ص: صحيح؛
  }؛
  .
  .
  عرف ابدأ: دالة {
    عرف ن: نـطقة؛
    ن.س = 10؛
    ن.ص = 12؛
    .
    .
  }؛
  def Point: class {
    def x: Int;
  };
  .
  .
  @merge def Point: class {
    def y: Int;
  };
  .
  .
  def start: function {
    def p: Point;
    p.x = 10;
    p.y = 12;
    .
    .
  };
من الممكن استخدام هذا المبدل لإلحاق إضافة على طبعة من قالب صنف، لكنك تحتاج أولاً لتعريف لقب لتلك الطبعة كي تتمكن من الدمج معها، كما في المثال التالي:
  صنف نـقطة [الـصنف: صنف] {
    عرف س: الـصنف؛
    عرف ص: الـصنف؛
  }

  عرف نـقطة_صحيح: لقب نـقطة[صحيح]؛

  @دمج صنف نـقطة_صحيح {
    دالة اطبع { ... } // ستضاف هذه الدالة إلى نـقطة[صحيح] دون بقية طبعات القالب.
  }
  class Point [T: type] {
    def x: T;
    def y: T;
  }

  def IntPoint: alias Point[Int];

  @merge class IntPoint {
    func print { ... } // This function will be added to Point[Int] only, not all template instances.
  }

شمل مكتبات أو ملفات مصدرية


الأمر "اشمل" (import) يستخدم لتحميل ملف مصدري أو مكتبة واستخدامها في البرنامج. الأمر يميز تلقائياً بين الملف المصدري والمكتبة. في الوقت الحالي يدعم هذا الأمر ثلاث أنواع من الملفات: ملفات مصدرية ومكتبات متحركة (dynamic) عامة ومكتبات بناء مثل libalusus_spp. يمكن تحميل أي مكتبة متحركة مهما كانت اللغة التي كتبت بها وعند تحميلها تكون كل دالّاتها العمومية متوفرة للاستخدام من داخل برنامج الأسُس، لكن ستحتاج لتعريف تلك الدوال يدوياً باستخدام الأمر `دالة` مع إضافة المبدل `@تصدير` (@expname) كما هو مبين في المثال التالي:
  اشمل "libmath.so"؛
  عرف جا: @تصدير[sin] دالة (ر: عـائم) => عـائم؛
  .
  .
  ج = جا(ز)؛
  import "libmath.so";
  def sin: @expname[sin] function (f: Float) => Float;
  .
  .
  s = sin(r);
يمكن اختصار الامتداد في عبارة اشمل، حيث أن الـقلب سيضيف الامتداد تلقائيًا. على سبيل المثال، يمكن اختصار:
  اشمل "مـتم/نـص.أسس"؛
  import "Srl/String.alusus";
إلى:
  اشمل "مـتم/نـص"؛
  import "Srl/String";
ويمكن أيضًا اختصار الامتداد والبادئة في أسماء المكتبات. فمثلا يمكن اختصار التالي:
  اشمل "libmath.so"؛
  import "libmath.so";
إلى:
  اشمل "math"؛
  import "math";
في الحالة أعلاه فإن القلب سيحاول تحميل الملف "math.alusus" فإن لم يجد فسيحاول تحميل "libmath.so" إن كان على نظام لينكس، وإن كان على نظام ماك أو إس فسيحاول بدلاً من ذلك تحميل "libmath.dylib".
في بعض الحالات قد تحتاج لإعطاء عدة خيارات لاسم الملف وتترك للقلب انتقاء أول خيار ينجح في تحميله. توفير عدة خيارات مفيد لدعم عدة أنظمة تشغيل بنفس الشفرة المصدرية. توفير خيارات متعددة يتم باستخدام `أو` ضمن عبارة `اشمل` كما في المثال التالي:
  اشمل "libmath.so" أو "libmath.so.0"؛
  import "libmath.so" or "libmath.so.0";
يمكن أيضًا استخدام المؤثر `||` بدلا من `أو` في المثال أعلاه.

الوحدة: الـعملية (Process)


عند تشغيل الأسس وأثناء تنفيذ المباشر للبرنامج (أي دون بناء مسبق) فإن الأسس تعرف وحدة `الـعملية` (Process) التي تحتوي على المتغيرات العمومية التالية:

  • عدد_المعطيات (argCount): صحيح
    عدد المعطيات الممرة لمترجم الأسس في سطر الأوامر.

  • المعطيات (args): مؤشر[مصفوفة[مؤشر[مصفوفة[محرف]]]]
    قائمة المعطيات الممرة لمترجم الأسس في سطر الأوامر.

  • اللغة (language): مؤشر[مصفوفة[محرف]]
    سلسلة محارف لوصف لغة لنظام الحالية. على سبيل المثال ar إذا كانت لغة النظام هي العربية.

  • النظام (platform): مؤشر[مصفوفة[محرف]]
    سلسلة محارف لوصف نظام التشغيل الحالي. قيمته إما أن تكون linux أو windows أو macos.