معايير لغة الأسُس البرمجية

ملاحظة: الإصدار الحالي من لغة الأسس متوافق مع هذه المعايير بشكل غير كامل. هذه المعايير فيها بعض التفاصيل التي ما زالت نظرية ولم تطبق بعد.

إفتح الكل   أغلق الكل

معايير القلب القواعدية

الخطوط العريضة للتركيب النحوي

فيما يلي بعض الخطوط العريضة التي يتميز بها التركيب النحوي للغة الأسُس:
  • قواعد اللغة حساسة لحالة الأحرف، أي أن a لا يساوي A.
  • تُفصَل الجمل بالفارزة المنقوطة ويمكن لجملة واحدة أن تمتد إلى أكثر من سطر واحد. الفارزة المنقوطة ليست جزءاً من الجملة أي أنها غير ضرورية بعد آخر جملة من مجموعة جمل.
  • تستخدم الأقواس الحاصرة {} لإنشاء مجموعات من الجمل.
  • تمرر المعطيات باستخدام الأقواس وهناك نوعين من هذه الأقواس، الهلالية () والمعقوفة []. القلب لا يفرض أي قيود حول طريقة استخدام هذه الأقواس لكن الأقواس المعقوفة خُصصت لمعطيات الترجمة، أي المعطيات التي تُعالج أثناء الترجمة، بينما خصصت الأقواس الأخرى لمعطيات التنفيذ، أي المعطيات التي تُرسل بين أجزاء البرنامج أثناء التنفيذ.
  • تُفصل المعطيات باستخدام الفارزة.
  • الملاحظات تُعلَّم باستخدام الرمزين /* و */ ويُمكن لهذه الملاحظات الامتداد على أكثر من سطر واحد، بينما ملاحظات السطر الواحد تُعلَّم بالرمز //.
  • تُعلَّم المحارف بعلامة الاقتباس المفردة، بينما تُعلَّم سلاسل الحارف باستخدام علامة الاقتباس المزدوجة.
  • تُستخدم الأقواس الهلالية لتجميع العمليات لغرض فرض الأسبقيات وتجنب الغموض الإعرابي.
  • تعامل النقطة معاملة المؤثر الثنائي فلا تختلف عند القلب عن غيرها من المؤثرات الثنائية سوى بالأسبقية، لكنها خُصصت للإشارة إلى العناصر الداخلية.

تعريفات قواعدية مساعدة

keywords
تعريف لصنف يُستخدم لحمل مجموعة من الكلمات التعريفية.

keywords : type list[string]

filter
تعريف لصنف يُستخدم لحمل قيمة المرشّحات في متن القواعد. يمكن للقيمة أما أن تكون عدداً صحيحاً، أو مصفوفة من الأعداد الصحيحة.

filter : type integer || list[integer]

valid_subject
تعريف لصنف المؤشرات المسموح بها كحد من حدود التركيب أو الأمر أو الجملة.

valid_subject : type prule[Subject || Subject.Parameter || Command || Expression || Main.Statement || Set]

Keyword
القاعدة الإعرابية للكلمة التعريفية. تستخدم هذه الكلمات التعريفية في تعريف الأوامر.

Keyword : prule as (@unique kwd:string)=>{ lexer.Identifier(kwd) }

KeywordGroup
القاعدة الإعرابية لمجموعة من الكلمات التعريفية. تستخدم هذه القاعدة لتمكين الأمر الواحد من التبديل بين أكثر من كلمة تعريفية واحدة. على سبيل المثال يمكن للأمر أن يستخدم ترجمات مختلفة لنفس الكلمة التعريفية.

KeywordGroup : prule as (kwds:keywords)=>{ alternate (kwds:k)->( Keyword(k) ) }

Constants
قاعدة إعرابية لعملية تبادل بين مجموعة من الثوابت. تستخدم هذه القاعدة من قبل القواعد التي تسمح بأكثر من ثابت واحد في موضع معين. على سبيل المثال العمليات الرياضية تسمح بأكثر من مؤثر واحد بين الحدّين.

Constants : prule as (kwds:keywords)=>{ alternate (kwds:k)->( lexer.Constant(k) ) }

المعايير الترميزية

ترميز المحارف

تُكتب البرامج المصدرية بالترميز الموحد (unicode) باستخدام صيغة UTF-8. والمرمز حساس لحالة الأحرف (case sensitive) لذلك فإن كلمة command تختلف عن المرمز عن Command. وبصورة عالمة، فإن المرمز يميز المحارف باستخدام قيمتها الترميزية وبالتالي فإن حرفين بقيمتين مختلفتين يعاملان كحرفين مختلفين حتى وإن رُسما بنفس الشكل.

عملية الترميز

اختيار الترميز الملائم

عندما تُطابق المحارف المدخلة أكثر من قاعدة ترميزية واحدة يتم اختيار أكثرها ملائمة حسب المعايير التالية وبنفس الأسبقية:
  • إذا كانت أطوال الرموز مختلفة يؤخذ أطولها. على سبيل المثال إذا طابقت المحارف المدخلة الرمزين "int" و "int32" يؤخذ الأخير.
  • إذا كان أحد الرموز من الثوابت يؤخذ على حساب الرموز غير الثابتة. على سبيل المثال القاعدة int لها أسبقية على القاعدة identifierفي المثال التالي:
    int: "int".
    identifier: letter {letter}.
    
  • إذا لم ينطبق المعياران أعلاه على القواعد الترميزية المعنية فيتم اختيار القاعدة المذكورة أولاً في مكتبة القواعد داخل القلب.

نهاية الرموز

يتم استبيان نهاية الرمز عند استلام أي حرف لا يطابق قاعدة الرمز.

المحارف المهملة

تعرف المحارف المهملة بنفس طريقة تعريف القواعد الترميزية ويقوم المرمّز بمعالجتها بنفس الطريقة أيضاً. الفرق الوحيد بينهما أن المرمّز لا يُرسل الرموز المهملة إلى المعرب. يمكن تعريف عدد غير محدد من القواعد المهملة.

المحارف اليتيمة

عندما يستلم المرمّز محارفاً لا تطابق أي قاعدة ترميزية أو أي قاعدة محارف مهملة فإنه يصدر إشعار خطأ للمستخدم.

مجموعات المحارف

يستخدم مرمّز لغة الأسُس مجموعات المحارف التالية:
BinDigit : char '0'..'1';
OctDigit : char '0'..'7';
DecDigit : char '0'..'9';
HexDigit : char '0'..'9', 'a'..'f', 'A'..'F';
Letter : char 'a'..'z', 'A'..'Z', '_', 0h0620..0h065F, 0h066E..0h06DC;
AnyCharNoEs : char !('\\');
AnyCharNoEsOrSingleQuote : char !("\\'");
AnyCharNoEsOrDoubleQuote : char !("\\\"");
Spacing : char " \n\r\t";
ملاحظة
سيتم تعديل تعريف Letter مستقبلاً لتمكين استخدام لغات غير الإنجليزية في كتابة الشفرة المصدرية.

المحارف المهملة

يُهمل المرمّز المحارف الغير مرئية كمحارف المسافة ونهاية السطر، وهي المحارف التي تحتويها مجموعة المحارف Spacing بالإضافة للملاحظات، وحسب القاعدة التالية:
Spacing : char " \n\r\t";
ignore { Spacing * (1,endless) };
@minimum ignore { "/*" + any*(0,endless) + "*/" };
@minimum ignore { "//" + any*(0,endless) + "\n" }

المحارف المركبة

يدعم مرمّز لغة الأسُس المجموعات التالية من المحارف المركبة (escape sequences):

المحارف اللامرئية

\nسطر جديد (linefeed)
\rرجوع إلى بداية السطر (carriage return)
\tعلامة التبويب (tab)
\bمسح للخلف (backspace)
\0نهاية سلسلة محارف (string terminator)
سطر جديد (linefeed)
رجوع إلى بداية السطر (carriage return)
علامة التبويب (tab)

المحارف الاستثنائية

\'علامة اقتباس مفردة (single quote)
\"علامة اقتباس مزدوجة (double quote)
\\شارحة مائلة (backslash)
\!الرمز !. يستخدم في الرموز الحرفية (literals) الشخصية حيث يستخدم الرمز ! لتحديد نهاية الرمز

محارف التعبيرات النمطية

\[
\]
\(
\)
\*
\+
\.
\?
\|
\^
\$

إدخال شفرة المحرف يدوياً

\cDDلإدخال شفرة بحجم بايت، أي 8 بتّات
\uDDDDلإدخال شفرة بحجم بايتين، أي16 بتّاً
\wDDDDDDDDلإدخال شفرة بحجم 4 بايتات، أي 32 بتّاً

التعريف القواعدي

escapeSequences : keywords = (
  // invisible characters:
  "\\n", "\\r", "\\t", "\\b", "\\f", "\\0",
  // special characters:
  "\\\'", "\\\"", "\\\\", "\\!",
  // regular expressions characters:
  "\\[", "\\]", "\\(", "\\)", "\\*", "\\+",
  "\\.", "\\?", "\\|", "\\^", "\\$");

AnyCharNoEs : char !('\\');
AnyCharNoEsOrSingleQuote : char !("\\'");
AnyCharNoEsOrDoubleQuote : char !("\\\"");

@inner EsCharWithSingleQuote : trule as {
  AnyCharNoEsOrDoubleQuote || EsCodeSequence ||
  alternate (escapeSequences:es)->( es )
};
@inner EsCharWithDoubleQuote : trule as {
  AnyCharNoEsOrSingleQuote || EsCodeSequence ||
  alternate (escapeSequences:es)->( es )
};
@inner EsCharWithQuotes : trule as {
  AnyCharNoEs || EsCodeSequence || alternate (escapeSequences:es)->( es )
};
@inner EsCodeSequence : trule as {
  '\\' + ('c' + HexDigit*(2,2) ||
          'u' + HexDigit*(4,4) ||
          'w' + HexDigit*(8,8) ||
          'n' || 't' || 'r' || '"' || '\'' || '\\' || 'ج' || 'ت' || 'ر')
};

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

يمكن تقسيم الرموز المسبقة التعريف إلى ثلاثة أنواع: معرِّفات وثوابت وحرفيات (literals).

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

المعرِّفات تبدأ بحرف أبجدي أو بشارحة سفلية وتحتوي على عدد غير محدد من المحارف. يمكن للمعرِّفات احتواء الأرقام أيضاً.

التركيب النحوي

identifier : letter { letter | decDigit }.
letter : 'a'..'z' | 'A'..'Z' | '_' | 0h0620..0h065F | 0h066E..0h06DC.
decDigit : '0'..'9'.

التعريف القواعدي

Identifier : trule as { Letter + (Letter || DecDigit)*(0,endless) };
Letter : char 'a'..'z', 'A'..'Z', '_', 0h0620..0h065F, 0h066E..0h06DC;
DecDigit : char '0'..'9';

الثوابت

الثوابت تُعرّف بقالب قواعدي بسيط ويتم إنشاء طبعة من هذا القالب لكل من ثوابت اللغة. كل ثابت من هذه الثواب يمثل بسلسلة محارف تُمرر للقالب القواعدي كمدخلات.

التعريف القواعدي

Constant : token as (@unique cnst:string)=>{ cnst };

مجموعات الثوابت

يمكن تقسيم ثوابت اللغة إلى المجموعات التالية:
مؤثرات التعيين= += -= *= /= %= &= |= $= <<= >>=
مؤثرات المقارنة== != < > <= >=
مؤثرات الجمع والطرح+ -
مؤثرات القسمة والضرب* / %
مؤثرات التعاملات البتّية| $ & << >>
مؤثرات التعاملات المنطقية|| $$ && or nor xor xnor and nand
مؤثرات التعاملات الأحادية المسبقة++ -- + - ! !! not
مؤثرات التعاملات الأحادية المؤخرة++ --
مؤثرات الربط العليا. -> .> <.
مؤثرات الربط الثانية=> .. ..> <..
مؤثرات الربط الثالثة: :> <:
مؤثرات الربط السفلى:: ::> <:: in
مؤثر الشرط?
ثوابت التنقيط (punctuators)( ) { } [ ] @ ~ \ ;

ملاحظة
الثوابت الظاهرة في أكثر من مجموعة من المجموعات السابقة تُعامل من قبل المرمّز كثابت واحد. على سبيل المثال، يوجد في المرمّز تعريف واحد للرمز ++ وليس تعريفين رغم ظهوره مرتين أعلاه.

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

الأعداد الصحيحة

يمكن للأعداد الصحيحة أن تكون عشرية أو ثنائية أو ثمانية أو ستة عشرية ويميز نوع العدد بما يسبقه من بادئة كما يلي:
الأعداد الثنائية0b
الأعداد الثمانية0o
الأعداد الستة عشرية0h
في حال خلو العدد من بادئة فإن العدد عشري. يتم تحديد ما إذا كان العدد طبيعياً (عدد صحيح موجب) بتذييله ب u وإلا فإن العدد صحيح (موجب أو سالب). آخر جزء من العدد، وهو جزء اختياري أيضاً، يحدد الوحدة التخزينية للعدد، أي يحدد عدد البتّات لذلك العدد، ويحدد ذلك بكتابة الحرف i يتبعه عدد بتّات ذلك العدد.

التركيب النحوي

[prefix] digit {digit} ["u"] ["i" num].

التعريف القواعدي

BinDigit : char '0'..'1';
OctDigit : char '0'..'7';
DecDigit : char '0'..'9';
HexDigit : char '0'..'9', 'a'..'f', 'A'..'F';

IntLiteral : trule as {
  (DecIntLiteral || BinIntLiteral || OctIntLiteral || HexIntLiteral) +
  ("u" || "U")*(0,1) + (("i" || "I") + DecIntLiteral)*(0,1)
};
@inner DecIntLiteral : trule as { DecDigit*(1,endless) };
@inner BinIntLiteral : trule as { ("0b" || "0B") + BinDigit*(1,endless) };
@inner OctIntLiteral : trule as { ("0o" || "0O") + OctDigit*(1,endless) };
@inner HexIntLiteral : trule as { ("0h" || "0H") + HexDigit*(1,endless) };

الاعداد الحقيقية

تكتب الأعداد الحقيقية بالطريقة العشرية فقط، وتكتب بعدد صحيح يحتوي الفاصلة العشرية متبوعاً اختيارياً بالحرف e تليه قيمة الرفع، متبوعاً اختيارياً بالحرف f يليه حجم الوحدة التخزينية للعدد.

التركيب النحوي

num "." num ["e" ["+"|"-"] num] ["f" num]

التعريف القواعدي

DecDigit : char '0'..'9';

FloatLiteral : trule as {
  DecDigit*(1,endless) + FloatPostfix ||
  DecDigit*(1,endless) + FloatExponent + FloatPostfix*(0,1) ||
  DecDigit*(1,endless) + "." + DecDigit*(1,endless) +
    FloatExponent*(0,1) + FloatPostfix*(1,1)
};
@inner FloatExponent : token as { ("e" || "E") + ("+" || "-")*(0,1) + DecDigit*(1,endless) };
@inner FloatPostfix : token as { ("f" || "F") + DecDigit*(0,endless) };

المحارف

يكتب ثابت المحرف كمحرف واحد أو محرف مركب محاط بعلامتي اقتباس فردية. يُمكن تذييل الثابت بما يحدد الصيغة الترميزية للمحرف. إن لم تحدد صيغة الترميز فإن الصيغة الإفتراضية ستكون UTF-8. تحديد الترميز يكون كما يلي:
uUTF-8 LE
u8UTF-8 LE
u16UTF-16 LE
u32UTF-32 LE
aASCII

التركيب النحوي

"'" esCharWithDoubleQuote "'" [charCodePostfix].

التعريف القواعدي

charCodes : list[string] = ("u", "u8", "u16", "u32", "a");
escapeSequences : keywords = (
  // invisible characters:
  "\\n", "\\r", "\\t", "\\b", "\\f", "\\0",
  // special characters:
  "\\\'", "\\\"", "\\\\", "\\!",
  // regular expressions characters:
  "\\[", "\\]", "\\(", "\\)", "\\*", "\\+",
  "\\.", "\\?", "\\|", "\\^", "\\$");

AnyCharNoEsOrSingleQuote : char !("\\'");

CharLiteral : trule as { "'" + EsCharWithDoubleQuote + "'" + CharCodePostfix*(0,1) };
@inner CharCodePostfix : trule as { alternate (charCodes:cc)->( cc ) };
@inner EsCharWithDoubleQuote : trule as {
  AnyCharNoEsOrSingleQuote || EsCodeSequence || alternate (escapeSequences:es)->( es )
};
@inner EsCodeSequence : trule as {
  '\\' + ('c' + HexDigit*(2,2) ||
          'u' + HexDigit*(4,4) ||
          'w' + HexDigit*(8,8) ||
          'n' || 't' || 'r' || '"' || '\'' || '\\' || 'ج' || 'ت' || 'ر')
};

سلاسل المحارف

يكتب ثابت سلاسل المحارف كسلسلة محارف محاطة بعلامتي اقتباس مزدوج. يمكن تذييل الثابت بما يحدد الصيغة الترميزية للمحارف. إن لم تحدد صيغة الترميز فإن الصيغة الإفتراضية ستكون UTF-8. تحديد الترميز يكون كما يلي:
uUTF-8 LE
u8UTF-8 LE
u16UTF-16 LE
u32UTF-32 LE
aASCII

التركيب النحوي

"\"" {esCharWithSingleQuote} "\"" [charCodePostfix].

التعريف القواعدي

charCodes : list[string] = ("u", "u8", "u16", "u32", "a");
escapeSequences : keywords = (
  // invisible characters:
  "\\n", "\\r", "\\t", "\\b", "\\f", "\\0",
  // special characters:
  "\\\'", "\\\"", "\\\\", "\\!",
  // regular expressions characters:
  "\\[", "\\]", "\\(", "\\)", "\\*", "\\+",
  "\\.", "\\?", "\\|", "\\^", "\\$");

AnyCharNoEsOrDoubleQuote : char !("\\\"");

StringLiteral : trule as {
  StringLiteralPart + (Spacing*(0,endless) + StringLiteralPart)*(0,endless) +
  CharCodePostfix*(0,1)
};
@inner StringLiteralPart : trule as { "\"" + EsCharWithSingleQuote*(0,endless) + "\"" };
@inner CharCodePostfix : trule as { alternate (charCodes:cc)->( cc ) };
@inner EsCharWithSingleQuote : trule as {
  AnyCharNoEsOrDoubleQuote || EsCodeSequence || alternate (escapeSequences:es)->( es )
};
@inner EsCodeSequence : token as {
  '\\' + ('c' + HexDigit*(2,2) ||
          'u' + HexDigit*(4,4) ||
          'w' + HexDigit*(8,8) ||
          'n' || 't' || 'r' || '"' || '\'' || '\\' || 'ج' || 'ت' || 'ر')
};

الرموز الشخصية

الرموز الشخصية صممت لتمكين المستخدم من إضافة أنواع رموز جديدة بشكل فعّال (ديناميكي). على سبيل المثال، يمكن للمستخدم إضافة نوع جديد من الرموز الحرفية لتحمل معلومات بصيغة XML. يبدأ الرمز الشخصي بشعار البداية، تليه سلسلة محارف تليها شعار النهاية. شعار البداية يبدأ بعلامة ! تليها كلمة تعريفية تحدد نوع الثابت الحرفي يليها الرمز <. أما شعار النهاية فيبدأ بالرمز > يليه اختيارياً سلسلة محارف لتحديد مواصفات إضافية يليها الرمز !. كما هو الحال مع سلاسل المحارف، يمكن استخدام المحارف المركبة في الرموز الشخصية ما بين شعاري البداية والنهاية.

التركيب النحوي

"!" prefix "<" {esChar} ">" [postfix] "!".

التعريف القواعدي

escapeSequences : keywords = (
  // invisible characters:
  "\\n", "\\r", "\\t", "\\b", "\\f", "\\0",
  // special characters:
  "\\\'", "\\\"", "\\\\", "\\!",
  // regular expressions characters:
  "\\[", "\\]", "\\(", "\\)", "\\*", "\\+",
  "\\.", "\\?", "\\|", "\\^", "\\$");

AnyCharNoEs : char !('\\');

CustomLiteral : trule as (@unique prefix:keywords,postfix:keywords)=>{
  "!" + alternate (prefix:p)->( p ) + "<" + EsCharWithQuotes*(0,endless) +
  (">!" + Spacing*(0,endless) + "!<" + EsCharWithQuotes*(0,endless))*(0,endless) +
  ">" + (alternate (postfix:p)->( p ))*(0,1) + "!"
};
@inner EsCharWithQuotes : trule as {
  AnyCharNoEs || EsCodeSequence || alternate (escapeSequences:es)->( es )
};
@inner EsCodeSequence : trule as {
  '\\' + ('c' + HexDigit*(2,2) ||
          'u' + HexDigit*(4,4) ||
          'w' + HexDigit*(8,8) ||
          'n' || 't' || 'r' || '"' || '\'' || '\\' || 'ج' || 'ت' || 'ر')
};

المعايير الإعرابية

البرامج والجمل

البرنامج في لغة الأسُس يتكون من مجموعة من الجمل (statement). كل جملة تتكون من حدّ (subject) واحد أو سلسلة من الحدود. تتقبل الجملة صيغاً مختلفة من سلاسل الحدود، كل صيغة من هذه الصيغ تسمى عبارة (phrase).
يتم الفصل بين الجمل باستخدام الفارزة المنقوطة ولا تكون الفارزة المنقوطة جزءاً من الجملة ولذلك لا يُشترط أن ينتهي البرنامج (أو أي مجموعة من الجمل) بالفارزة المنقوطة.
تم جمع تعريفات الجمل والعبارات في حزمة واحدة باسم Main والغرض من هذه الأسلوب تسهيل اشتقاق أنواع أخرى من الجمل ومجموعات الجمل.
يعرّف القلب ثلاثة عبارات افتراضية:
  • ExpPhrase: تبدأ هذه العبارة بتركيب تليه سلسلة غير محددة من الأوامر الذيلية وهي مجموعة الأوامر التي لا تظهر في بداية الجملة.
  • CmdPhrase: تبدأ هذه العبارة بأمر افتتاحي تليه سلسلة غير محددة من الأوامر الذيلية. الأوامر الافتتاحية هي مجموعة الأوامر التي تظهر في بداية الجملة ولا تظهر في مكان آخر.
  • SoloCmdPhrase: تحتوي هذه العبارة على أمر منفرد واحد. الأوامر المنفردة هي التي تظهر فقط وحيدة في الجملة.

التركيب النحوي

Program : StatementList.
StatementList : Statement { ";" Statement }.
Statement : (Phrase | Phrase | ...).
Phrase : { Subject }.

التعريف القواعدي

Program : prule as { root.Main };

Main : module {
  start StatementList;

  StatementList : prule as {
    Statement*(0, 1) + (lexer.Constant(";") + Statement*(0, 1))*(0, endless)
  };

  Statement : @limit[user.parent==self,child.terms==self] prule
    prefix self.id, DefaultModifier
    as (phrases:list[prule[Phrase]]=(CmdPhrase, SoloCmdPhrase, ExpPhrase)=>{
      alternate (phrases:phrase)->( phrase )
  };

  Phrase : prule as (subjects:list[map[prd:valid_subject, min:integer, max:integer, pty:integer]])=>{
      concat (subjects:s)->( @priority(s.pty,0) s.prd*(s.min,s.max) )
  };

  // Default phrases.
  CmdPhrase : prule ref Phrase(subjects=((prd=LeadingCmdGroup,min=1,max=1),
                                         (prd=TrailingCmdGroup,min=0,max=endless)));
  SoloCmdPhrase : prule ref Phrase(sections=((prd=SoloCmdGroup,min=1,max=1)));
  ExpPhrase : prule ref Phrase(subjects=((prd=root.Expression,min=1,max=1),
                                         (prd=TrailingCmdGroup,min=0,max=endless)));
}

التراكيب

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

التركيب النحوي

للمؤثرات التراكيب النحوية التالية:
[unaryOp] operand.
operand [unaryOp].
operand1 {binaryOp operand2}.

المؤثرات

كل المؤثرات لها نفس التركيب النحوي المذكور أعلاه لكنها تختلف في أسبقياتها التي تحدد كيفية إعراب التركيب وتحويله إلى شجرة بيانية. على سبيل المثال المؤثر * له الأسبقية دائماً على المؤثر + وبالتالي يكون ظهوره في شجرة البيانات أعمق من المؤثر +. الأسبقيات بين المؤثرات ثابتة في لغة الأسُس التي تفرض هذه الأسبقية على من يشتق تراكيبه الخاصة. فيما يلي قائمة المؤثرات مرتبة تصاعدياً حسب الأسبقية:
  • مؤثرات الربط السفلى
    تُستخدم للربط بين حدّين مختلفين بأسبقية سفلى دون أن تقدم معنى محدداً لهذا الربط، بل يترك الأمر لمطوري القواعد المخصصة لتحديد المعنى.
    ::
    ::>
    <::
    inتستخدم لتحديد جزء من كل كما هو الحال في أمر foreach.
  • المؤثر الشرطي '?'
    يستخدم المؤثر الشرطي ? لإنشاء تركيب شرطي فيسبقه شرط ويليه جواب الشرط.
  • مؤثر القوائم ','
    يستخدم لجمع عدد غير محدد من الحدود في قائمة واحدة.
  • مؤثرات الربط الثالثة
    تُستخدم للربط بين حدّين مختلفين بأسبقية أعلى من مؤثرات الربط السفلى لكنها أدنى من مؤثرات الربط الثانية والعليا. يترك لمطوري القواعد المخصصة تحديد معنى هذه المؤثرات.
    :
    :>
    <:
  • مؤثرات التعيين
    تستخدم لتعيين قيمة جديدة لحدّ.
    =تحديد قيمة جديدة
    +=إضافة قيمة للقيمة الحالية
    -=طرح قيمة من القيمة الحالية
    *=ضرب القيمة الحالية بقيمة أخرى
    /=تقسيم القيمة الحالية على قيمة أخرى
    ÷=تقسيم القيمة الحالية على قيمة أخرى
    %=تقسيم القيمة الحالية على قيمة أخرى والاحتفاظ بالباقي بدل نتيجة القسمة
    &=تطبيق عملية 'و' المنطقية على القيمة الحالية
    |=تطبيق عملية 'أو' المنطقية على القيمة الحالية
    $=تطبيق عملية xor المنطقية على القيمة الحالية
    <<=تزحيف جميع البتّات يساراً مراتباَ بتعداد القيمة المعطاة
    =>>تزحيف جميع البتّات يميناً مراتباً بتعداد القيمة المعطاة
  • مؤثرات العمليات المنطقية
    أوعملية 'أو'
    orعملية 'أو'
    ||صيغة رمزية ل or
    norعملية 'أو' معكوسة
    xorعملية xor
    $$صيغة رمزية ل xor
    xnorعملية xor معكوسة
    وعملية 'و'
    andعملية 'و'
    &&صيغة رمزية ل and
    nandعملية 'و' معكوسة
  • مؤثرات المقارنة
    ==فحص مساواة
    !=فحص عدم المساواة
    <فحص أصغر
    >فحص أكبر
    <=فحص أصغر أو يساوي
    >=فحص أكبر أو يساوي
  • مؤثرات الربط الثانية
    تُستخدم للربط بين حدّين مختلفين بأسبقية أعلى من مؤثرات الربط الثالثة لكنها أدنى من مؤثرات الربط العليا. يترك لمطوري القواعد المخصصة تحديد معنى هذه المؤثرات.
    =>
    ..
    ..>
    <..
  • مؤثرات الجمع والطرح
    +
    -
  • مؤثرات الضرب والقسمة
    *
    /
    %تحصيل باقي القسمة
  • مؤثرات العمليات البتّية
    هذه المؤثرات لتطبيق عمليات منطقية على مستوى البتّات، أي تطبيق العمليات على كل بت وما يقابله، بالإضافة إلى عمليات تزحيف البتّات.
    |عملية 'أو'
    $عملية xor
    &عملية 'و'
    <<تزحيف البتّات يميناً مراتباً بتعداد القيمة المعطاة
    >>تزحيف البتّات يساراً مراتباً بتعداد القيمة المعطاة
  • المؤثرات الأحادية السابقة
    ++زيادة بواحد
    --إنقاص بواحد
    +إشارة الرقم الموجب
    -إشارة الرقم السالب
    !علامة النفي البتّية (عكس قيمة البتّات)
    !!علامة النفي المنطقية
  • المؤثرات الأحادية اللاحقة
    ++زيادة بواحد
    --إنقاص بواحد
  • مؤثرات تمرير المعطيات
    تشمل الأقواس الإعتيادية والأقواس المعقوفة (المربعة). تأتي الأقواس بعد الحد المعني وتُستخدم لتمرير معطيات معينة إلى ذلك الحد. ليس هناك تحديد لنوع الحد المعني أو نوع المعطيات الممررة أو ماتعنيه. أدرجت هذه المؤثرات ضمن قائمة المؤثرات الأحادية لإمكانية افتقارها لأي معطى حيث يمكن إلحاق حدّ معين بقوسين فارغين.
    operand "(" [expression] ")"
    operand "[" [expression] ")"
    
  • مؤثرات الربط العليا
    تُستخدم للربط بين حدّين مختلفين بأسبقية عليا. يترك لمطوري القواعد المخصصة تحديد معنى بعض هذه المؤثرات.
    .يُستخدم للربط بين حاوية وعنصر ينتمي لها، كما هو الحال عند الإشارة إلى أحد عناصر كائن ما في البرمجة كائنية المنحى.
    ->يستخدم كأسلوب آخر للربط بين حاوية وعنصر ينتمي لها.
    .>
    <.
  • مؤثر التلدة '~'
    يُستخدم لتطبيق عمليات مختلفة على الحد الذي يسبقه. هذا المؤثر لا يليه إلا أمر من مجموعة محددة من الأوامر تُضاف بشكل فعّال (ديناميكي) لأغراض شتّى. على سبيل المثال يمكن تعريف أمر ptr للحصول على موقع متغير معين في الذاكرة فيصبح الحصول على موقع ذلك العنصر يتم بكتابة: a~ptr
    في المثال أعلاه a هو المتغير بينما ptr أمر معرّف من قبل مكتبة البناء للحصول على موقع المتغير في الذاكرة.
ملاحظة: مؤثرات تمرير المعطيات والتلدة ومؤثرات الربط العليا لها جميعاً نفس الأسبقية. المثال التالي يوضح هذه المسألة:
1) myvar.mysubvar(params)
2) myvar(params).mysubvar
في المثال الأول أعلاه الجزء '(params)' يُطبق على ناتج 'myvar.mysubvar' بينما في المثال الثاني الجزء '.mysubvar' يُطبق على ناتج 'myvar(params)'.

رمز نهاية التركيب

الرمز \ اختياري ويستخدم لتحديد نهاية التركيب. الغرض منه إزالة الغموض إن وجد حول النقطة التي ينتهي عندها التركيب. المثال التالي يوضح حالة غموض تتطلب استخدام هذا الرمز:
if a (b,c,d)-e
if a\ (b,c,d)-e  // prevents (b,c,d)-e from being part of the condition.
if a(b,c,d)\ -e  // makes (a,b,c) part of the condition and leaves -e out.

فرض أسبقيات مختلفة

يمكن استخدام الأقواس لوضع تركيب كامل كحدً في تركيب آخر وذلك لفرض أسبقيات مختلفة عن الأسبقية الإفتراضية. على سبيل المثال:
a = b + c * d;
a = (b + c) * d;
في المثال الأول تجمع b مع ناتج ضرب c و d بينما في المثال الثاني تُضرب d بناتج جمع c و b.

التعريفات القواعدية

TokenData : module {
  // Predefined Operator Lists
  assignmentOpList : keywords = ("=", "+=", "-=", "*=", "/=", "÷=", "%=", "&=", "|=", "$=",
                                 "<<=", ">>=");
  comparisonOpList : keywords = ("==", "!=", "<", ">", "<=", ">=");
  addOpList : keywords = ("+", "-");
  mulOpList : keywords = ("*", "/", "%");
  bitwiseOpList : keywords = ("|", "$", "&", "<<", ">>");
  logOpList : keywords = ("||", "$$", "&&", "أو", "or", "nor", "xor", "xnor", "و", "and", "nand");
  prefixOpList : keywords = ("++", "--", "+", "-", "!", "!!", "not");
  postfixOpList : keywords = ("++", "--");
  linkOpList : keywords = ("->", ".", ".>", "<.");
  lowLinkOpList : keywords = ("=>", "..", "..>", "<..");
  lowerLinkOpList : keywords = (":", ":>", "<:");
  lowestLinkOpList : keywords = ("::", "::>", "<::", "in")
};

// Expression :
// operand {binaryOp operand}.
// [unaryOp] operand.
// operand [unaryOp].
// operand {FunctionalOp}.
Expression : module {
  prefix self.id, DefaultModifier;
  start Exp;

  Exp : prule as { LowestLinkExp + (@priority(in,0) lexer.Constant("\\")*(0,1)) };
  LowestLinkExp : prule prefix self.id as (enable:integer=endless)=>{
    ConditionalExp + (@priority(in,0) (LowestLinkOp + ConditionalExp)*(0,enable))
  };
  ConditionalExp : prule prefix self.id  as (enable:integer[0<=n<=1]=1)=>{
    ListExp + (@priority(in,0) (lexer.Constant("?") + ListExp)*(0,enable))
  };
  ListExp : prule prefix self.id as (enable:integer=endless)=>{
    (@priority(in,0) lexer.Constant(",")*(0,enable)) + LowerLinkExp +
    (@priority(in,0) (lexer.Constant(",") + LowerLinkExp*(0,1))*(0,enable))
  };
  LowerLinkExp : prule prefix self.id as (enable:integer=endless)=>{
    AssignmentExp + (@priority(in,0) (LowerLinkOp + AssignmentExp)*(0,enable))
  };
  AssignmentExp : prule prefix self.id as (enable:integer=endless)=>{
    LogExp + (@priority(in,0) (AssignmentOp + LogExp)*(0,enable))
  };
  LogExp : prule prefix self.id as (enable:integer=endless)=>{
    ComparisonExp + (@priority(in,0) (LogOp + ComparisonExp)*(0,enable))
  };
  ComparisonExp : prule prefix self.id as (enable:integer=endless)=>{
    LowLinkExp + (@priority(in,0) (ComparisonOp + LowLinkExp)*(0,enable))
  };
  LowLinkExp : prule prefix self.id as (enable:integer=endless)=>{
    AddExp + (@priority(in,0) (LowLinkOp + AddExp)*(0,enable))
  };
  AddExp : prule prefix self.id as (enable:integer=endless)=>{
    MulExp + (@priority(in,0) (AddOp + MulExp)*(0,enable))
  };
  MulExp : prule prefix self.id as (enable:integer=endless)=>{
    BitwiseExp + (@priority(in,0) (MulOp + BitwiseExp)*(0,enable))
  };
  BitwiseExp : prule prefix self.id as (enable:integer=endless)=>{
    UnaryExp + (@priority(in,0) (BitwiseOp + UnaryExp)*(0,enable))
  };
  UnaryExp : prule prefix self.id as (
    enable1:integer[0<=n<=1]=1, enable2:integer[0<=n<=1]=1
  )=>{
    (@priority(in,0) PrefixOp*(0,enable1)) + FunctionalExp +
    (@priority(in,0) PostfixOp*(0,enable2))
  };
  FunctionalExp : prule prefix self.id as (
    operand:valid_subject=root.Subject, fltr:filter=null, dup:integer=endless,
    pty:integer=in
  )=>{
    operand + (@priority(pty,0) (@filter(fltr) ParamPassExp ||
                                               TildeExp ||
                                               LinkExp(operand))*(0,dup))
  };

  // ParamPassExp : "(" [Expression] ")" | "[" [Expression] "]".
  ParamPassExp : prule as (
    expr:prule[Expression||Statement]=root.Expression, fltr:filter=null
  )=>{
    @filter(fltr) lexer.Constant("(") + expr*(0,1) + lexer.Constant(")") ||
                  lexer.Constant("[") + expr*(0,1) + lexer.Constant("]")
  };

  // TildeExp : "~" Subject.
  TildeExp : prule as (operand:valid_subject=root.TildeSubject)=>{
    lexer.Constant("~") + operand
  };

  // LinkExp : LinkOp operand.
  LinkExp : prule as (operand:valid_subject=root.Subject)=>{ LinkOp + operand };

  // Operators :
  AssignmentOp : prule as { root.Constants(root.TokenData.assignmentOpList) };
  ComparisonOp : prule as { root.Constants(root.TokenData.comparisonOpList) };
  AddOp : prule as { root.Constants(root.TokenData.addOpList) };
  MulOp : prule as { root.Constants(root.TokenData.mulOpList) };
  BitwiseOp : prule as { root.Constants(root.TokenData.bitwiseOpList) };
  LogOp : prule as { root.Constants(root.TokenData.logOpList) };
  PrefixOp : prule as { root.Constants(root.TokenData.prefixOpList) };
  PostfixOp : prule as { root.Constants(root.TokenData.postfixOpList) };
  LinkOp : prule as { root.Constants(root.TokenData.linkOpList) };
  LowLinkOp : prule as { root.Constants(root.TokenData.lowLinkOpList) };
  LowerLinkOp : prule as { root.Constants(root.TokenData.lowerLinkOpList) };
  LowestLinkOp : prule as { root.Constants(root.TokenData.lowestLinkOpList) }
};

تعريفات تراكيب مخصصة

القلب يعرّف أيضاً مجموعة من التراكيب المخصصة لبعض الأغراض العامة والمتاحة لمطوري مكتبات البناء:
// An expression that blocks sets.
NoSetExpression : module inherits Expression {
  FunctionalExp.operand = root.NoSetSubject;
  LinkExp.operand = root.NoSetSubject;
};

// An expression that doesn't allow brackets.
NoBracketExpression : module inherits Expression {
  FunctionalExp.{operand=root.NoBracketSubject; fltr=(0,1,1); dup=endless; pty=in};
  LinkExp.operand = root.NoBracketSubject;
};

// An expression that allows only identifiers with link operators.
FullIdExpression : module inherits Expression {
  start FunctionalExp;
  FunctionalExp.{operand=IdSubject; fltr=2; dup=endless; pty=in};
  LinkExp.operand = root.IdSubject;
};

أمثلة

الأمثلة التالية توضح بعض التراكيب وكيفية إعرابيها:
statement:        a = b + c * d
translates into:  store(a, add(b, multiply(c, d)))

statement:        a > b ? c, d
translates into:  decide(greaterThan(a, b), list(c, d))

statement:        a:int, b:float, c:char
translates into:  list(link(a,int), link(b,float), link(c,char))

statement:        a = myclass.myfunction(5, 2)
translates into:  store(a, bracketOp(dot(myclass, myfunction), list(5, 2)))

الأوامر

الأوامر تبدأ بكلمة تعريفية تُستخدم لتمييز الأوامر عن بعضها، تليها إختيارياً معطياتٌ. الأوامر تُشتق من أحد قالبين معرفين مسبقاً في القلب، ويُترك لكاتب مكتبة البناء اختيار القالب الأنسب.

التركيب النحوي

Cmd : keyword {Subject}.
MultiCmd : {keyword {Subject}}.
القالب Cmd يبدأ بكلمة تعريفية يليها عدد غير محدد من الحدود. يمكن التحكم عن طريق معطيات القالب بخيارات التكرار لكل حد من هذه الحدود. القالب MultiCmd عبارة عن سلسلة مقاطع كل منها مشابه للقالب Cmd ويمكن التحكم بخيارات التكرار لكل مقطع من هذه المقاطع، بالإضافة لإمكانية التحكم لكل حد في كل من هذه المقاطع. في حقيقة الأمر، القالب Cmd عبارة عن نسخة مبسطة من القالب MultiCmd، أي أن بالإمكان الاستعاضة عن Cmd ب MultiCmd.

ملاحظة: تجنب الغموض الإعرابي من مسؤولية كاتب مكتبة البناء. الغموض قد ينتج من استخدام نفس الكلمة التعريفية في أكثر من أمر وقد ينتج أيضاً بسبب الحدود الاختيارية. على سبيل المثال، قد ينتج الغموض من حدّي تراكيب متتاليين أولهما اختياري.

التعريفات القواعدية

// Root Command
Command : @limit[child.terms==(Cmd||MultiCmd)] prule
  prefix self.id, DefaultModifier;

// Cmd : keyword {Subject}.
Cmd : @limit[user.parent==root.Command] prule
  as (kwd:keywords, args:list[map[prd:valid_subject, min:integer, max:integer, pty:integer]])=>{
    root.KeywordGroup(kwd) + concat (args:a)->( @priority(a.pty,0) a.prd*(a.min,a.max) )
  };

// MultiCmd : {keyword {Subject}}.
MultiCmd : @limit[user.parent==root.Command] prule
  as (sections:list[map[kwd:keywords, min:integer, max:integer, pty:integer,
                        args:list[map[prd:valid_subject, min:integer, max:integer, pty:integer]]
                       ]])=>{
    concat (sections:s)->(
      @priority(s.pty,0) (root.KeywordGroup(s.kwd) + concat (s.args:a)->(
        @priority(a.pty,0) a.arg*(a.min, a.max)
      ))*(s.min, s.max)
    )
  };
التعريف Command هو جذر كل الأوامر وكل أمر يجب أن يُشتق منه بشكل مباشر أو غير مباشر. فيما يلي قائمة المعطيات الداخلة في تعريف القالبين:
  • kwd: الكلمة التعريفية للأمر، أو قائمة بتلك الكلمة التعريفية مع مرادفاتها.
  • min: الحد الأدنى من التكرار. قيمة 0 تجعل الظهور اختياري.
  • max: الحد الأعلى للتكرار. قيمة endless تعني غياب الحد الأعلى.
  • pty: الأسبقية في عملية التكرار. قيمة 1 تعني أن الأسبقية للتكرار بينما قيمة 0 تعطي الأسبقية للإهمال.
  • prd: مؤشر للتعريف القواعدي للحد.

أمثلة

1

Cmd:      keyword {Subject}.
gotoCmd:  "goto" Expression.
          goto mystart;

2

MultiCmd:  {keyword {Subject}}.
ifCmd:     "if" Expression Statement "else" Statement.
           if i==5 DoSomething() else DoSomethingElse();
المثال أعلاه يُعرب إلى:
ifCmd
  Expression: i==5
  Statement:  DoSomething()
  Statement:  DoSomethingElse()

الحدود

الحدّ يمكن أن يكون قيمة ثابتة أو اسم متغير أو تركيب أو أمر أو جملة أو مجموعة من الجمل. يمكن للحد أن يُحصر بين أقواس اعتيادية أو أقواس معقوفة. جُمعت التعريفات القواعدية للحدّ في حزمة واحدة يمكن الاشتقاق منها للتحكم فيما يُسمح به من أصناف في ذلك الحد والتحكم فيما إن كانت الأقواس مسموحة أو مفروضة أو ممنوعة.

التركيب النحوي

Subject : (Parameter | Command | Expression | Statement | Set) |
          "(" (Parameter | Command | Expression | Statement | Set) ")" |
          "[" (Parameter | Command | Expression | Statement | Set) "]".
Parameter : Identifier | Literal.
Literal : IntLiteral | FloatLiteral | CharLiteral | StringLiteral | CustomLiteral.

التعريفات القواعدية

Subject : module {
  prefix self.id, DefaultModifier;
  start Subject1;

  Subject1 : prule as (
    sbj1:list[prule[
      Parameter||root.Command||root.Expression||root.Main.Statement||root.Set
    ]] = (Parameter, SubjectCommandGroup, root.Set),
    sbj2:list[prule[
      Parameter||root.Command||root.Expression||root.Main.Statement||root.Set
    ]] = (root.Expression),
    sbj3:list[prule[
      Parameter||root.Command||root.Expression||root.Main.Statement||root.Set
    ]] = (root.Expression),
    fltr:filter,
    frc2:integer = 0,
    frc3:integer = 0
  )=>{
    @filter(fltr)
      alternate (sbj1:s)->( s ) ||
      lexer.Constant("(") + (alternate (sbj2:s)->( s ))*(frc2,1) + lexer.Constant(")") ||
      lexer.Constant("[") + (alternate (sbj3:s)->( s ))*(frc3,1) + lexer.Constant("]")
  };
  Subject2 : @limit[user.owner==Subject] prule as (
    sbj:prule[Parameter||root.Command||root.Expression||root.Main.Statement||root.Set],
    fltr:filter,
    frc:integer
  )=>{
    @filter(fltr) sbj ||
                  lexer.Constant("(") + sbj*(frc,1) + lexer.Constant(")") ||
                  lexer.Constant("[") + sbj*(frc,1) + lexer.Constant("]")
  };

  // Commands
  SubjectCommand : prule inherits root.Command prefix self.id;
  SubjectCommandGroup : prule_group[SubjectCommand];

  // Parameter
  Parameter : prule prefix self.id as (fltr:filter=null, cnsts:keywords=null)=>{
    @filter(fltr) lexer.Identifier || Literal || root.Constants(cnsts)
  };

  // Literal
  Literal : prule as (fltr:filter=null)=>{
    @filter(fltr) lexer.IntLiteral || lexer.FloatLiteral || lexer.CharLiteral ||
                  lexer.StringLiteral || lexer.CustomLiteral
  }
}
الفرق بين Subject1 و Subject2 هو أن الأول يسمح بأكثر من صنف واحد بينما يقتصر الأخير على صنف واحد يُسمح به في هذا الحد. التعريف SubjectCommandGroup حاوية لمجموعة الأوامر المسموح ظهورها كحد. فيما يلي قائمة المعطيات:
  • sbj, sbj1, sbj2, sbj3: مؤشر أو مصفوفة من المؤشرات للأصناف الجائزة في الحد.
  • frc, frc2, frc3: تحديد ما إذا كان الصنف الذي داخل الأقواس اختيارياً. يُسمح بالقيم 0 و 1 في هذا المتغير. قيمة 1 تعني أن ما بين الأقواس ليس اختيارياً.
  • fltr: مرشّح لنوع الأقواس المسموح، وفي حالة Parameter و Literal فمرشح للأصناف المسموح بها.

تعريفات حدود مخصصة

القلب يعرّف أيضاً مجموعة من الحدود المخصصة لبعض الأغراض العامة والمتاحة لمطوري مكتبات البناء:
// The default Tilde subject.
TildeSubject : module inherits Subject {
  Subject1.{sbj1=(SubjectCommandGroup);
            sbj2=(root.Expression); frc2=0;
            sbj3=(root.Expression); frc3=0};
  SubjectCommandGroup = ();
};

// A subject that doesn't allow sets.
NoSetSubject : module inherits Subject {
  Subject1.{sbj1=(Parameter, SubjectCommandGroup);
            sbj2=(root.Expression); frc2=0;
            sbj3=(root.Expression); frc3=0}
};

// A subject that doesn't allow brackets.
NoBracketSubject : module inherits Subject {
  Subject1.{sbj1=(Parameter, SubjectCommandGroup, root.Set);
            sbj2=(); frc2=0;
            sbj3=(); frc3=0}
};

// A subject that allows only identifiers.
IdSubject : module inherits Subject {
  start Parameter;
  Parameter.{fltr=0; cnsts=null}
};

// A subject that allows full identifiers (with . or -> operators).
FullIdSubject : module inherits Subject {
  start Subject2;
  Subject2.{obj=root.FullIdExpression; fltr=0; frc=0}
};

ModifierSubject : module inherits Subject {
  Subject1.{sbj1=(SubjectCommandGroup);
            sbj2=(root.Expression); frc2=0;
            sbj3=(root.Expression); frc3=0};
  SubjectCommandGroup = (UseModifierCmd);

  // @use modifier.
  UseModifierCmd : prule ref root.Cmd(kwd=("use"),
                                       args=((UseModifierSubject)));
  UseModifierSubject : module inherits root.FullIdSubject {
    Subject2.{fltr=2, frc=1}
  }
};

المجموعات

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

التركيب النحوي

Set : "{" StatementList "}".

التعريف القواعدي

Set : @limit[child.terms==self,user.parent==self] prule prefix DefaultModifier as (
  stmt:prule[StatementList]=root.Main
)=>{
  lexer.Constant("{") + stmt + lexer.Constant("}")
};

المبدّلات

المبدّلات عناصر قواعدية تحمل معلومات وصفية تُضاف إلى ما يأتي بعدها وتستخدم من قبل مكتبات البناء لأغراض مختلفة. عرّفت المبدلات كقواعد موازية وبالتالي فقد عُرفت بشكل منفصل عن القواعد التي تستخدمها. المبدّلات عبارة عن الرمز @ يليه حدّ واحد (ModifierSubject). يشير هذا الحدّ إلى:
  • مجموعة من الأوامر التي تستخدم لتعريف المعلومات الوصفية. يُمكن لمكتبات البناء تعريف أوامرها الخاصة وإضافتها لهذه المجموعة.
  • قوسين اعتياديين أو مربعين يحتويان على تركيب.
تم تعريف البعد الإعرابي الخاص بالمبدّلات ليتفرع عن بداية أي قاعدة إعرابية، أي يفحص قاعدة المبدلات عن مدخل كل قاعدة إعرابية. عند الإنتهاء من إعراب المبدل يتم تنفيذ دالّة تعمل على إضافة المعلومات الوصفية إلى طابور المعلومات الوصفية مع تحديد قيمة targets وهي القيمة التي تستخدم لمزاوجة المعلومات الوصفية مع القواعد الإعرابية المعنية بها. تُقرأ قيمة targets من معلومات وصفية مرتبطة بالتعريف الإعرابي لأمر المبدّل وفي حالة المبدلات ذات التراكيب فتُستخدم القيمة DefaultModifier. تحدد الحزم والقواعد الإعرابية المعلومات الوصفية المرغوب استلامها باستخدام الأمر prefix والذي يظهر في الكثير من التعريفات القواعدية. التعريفات التي لا تحتوي على الأمر prefix لا تستلم أي معلومات وصفية. في القواعد المسبقة التعريف يُستخدم الاسم التعريفي للقاعدة الإعرابية كقيمة للأمر prefix.

التركيب النحوي

Modifier : "@" Subject |
           "@<" Subject.
هناك صيغتان لكتابة المبدّل. الصيفة الأولى تعني أن الأسبقية في مزاوجة المعلومات الوصفية مع قاعدة إعرابية تكون للقاعدة الإعرابية الداخلية بينما تعطي الصيغة الثانية الأسبقية للقاعدة الخارجية. على سبيل المثال، لو كانت القاعدة Subject تشير إلى القاعدة Parameter وكلتا القاعدتين تقبلان مبدّلاً معيناً فإن استخدام الصيغة الأولى يُعطي المبدّل للقاعدة Parameter بينما تعطي الصيغة الثانية المبدّل للقاعدة Subject.

التعريفات القواعدية

Modifiers : module {
  Expression: module inherits root.Expression {
    start FunctionalExp;

    FunctionalExp.vars.{
      operand = root.Modifiers.Subject;
      dup = 1;
      filter2 = 2;
    };
  };

  Subject : module inherits root.Subject {
    SubjectCommandGroup = ();
  };

  CmdGroup : prule_group[LeadingCommand];

  Phrase: prule as {
    module.CmdGroup || module.Expression
  };

  LeadingModifier: prule inherits module.Phrase;
  TrailingModifier: prule inherits module.Phrase;
};

dimension {
  entryTokenText: "@";
  diverge : atRuleEntry;
  start: root.Modifiers.LeadingModifier;
  onExit : function (dt:ParsingData) {
    if dt.data.data.id == root.ModifierSubject.UseModifierCmd
      // Grammar overriding modifier.
      override dt.data.data.data
    else if dt.route == 0
      // Command based modifiers (specifies their own targets).
      addPrefix data=dt.data, type=dt.data.id,
                targets=prule(dt.data.id).targets
    else
      // Expression based modifiers (uses a default target).
      addPrefix data=dt.data, type=dt.data.id,
                targets=DefaultModifier
  }
};

dimension {
  entryTokenText: "@<";
  diverge : atRuleEntry;
  start: root.Modifiers.TrailingModifier;
  onExit : function (dt:ParsingData) {
    if dt.route == 0
      // Command based modifiers (specifies their own targets).
      addPostfix data=dt.data, type=dt.data.id,
                targets=prule(dt.data.id).targets
    else
      // Expression based modifiers (uses a default target).
      addPostfix data=dt.data, type=dt.data.id,
                targets=DefaultModifier
  }
};

الأوامر الأساسية

اشمل (import)

يستخدم هذا الأمر لكل أنواع الاستيراد سواء استيراد ملف شفرة مصدرية أو مكتبة ساكنة أو مكتبة فعّالة. يسمح القلب بإضافة سوائس جديدة لعملية الاستيراد تسمح باستيراد أنواع أخرى من الملفات.

التركيب النحوي

اشـمل: "اشمل" تـركيب.
Import : "import" Expression.

التعريف القواعدي

ImportCmd : prule inherits SoloCommand
  ref root.Cmd(kwd=("import", "اشمل"), args=((prd=root.Expression, min=1, max=1, pty=in)));

عرف (def)

يستخدم هذا الأمر لإنشاء تعريف جديد يتكون من اسم وقيمته.

التركيب النحوي

عـرف: ("عرف" | "عرّف") تـركيب.
Def : "def" Expression.

المبدّلات المقبولة

يتقبل الأمر "عرف" المبدل @دمج (@merge) الذي يؤدي إلى دمج التعريف بأي تعريف مسبق بنفس الإسم في نفس المجال.

التعريف القواعدي

DefCmd : prule inherits SoloCommand
  ref root.Cmd(kwd=("def", "عرف", "عرّف"), args=((prd=root.Expression, min=1, max=1, pty=in)));

use

يستخدم هذا الأمر لتفعيل مجموعة من القواعد في المجال الذي يظهر فيه. بمعنى آخر، يمكن تحميل مجموعة من القواعد دون المباشرة بتفعيلها حتى يصادف المترجم هذا الأمر. يتوفر هذا الأمر أيضاً بصيغة مبدّل يسمح بتفعيل مجموعة القواعد فقط على الجملة التي يطبَّق عليها المبدّل.

التركيب النحوي

UseCmd : "use" FullIdSubject [IdSubject].
UseModifierCmd : "use" "[" FullIdSubject "]".

التعريف القواعدي

UseCmd : prule inherits SoloCommand
  ref root.Cmd(kwd=("use"), args=((prd=root.FullIdSubject, min=1, max=1, pty=in),
                                  (prd=root.IdSubject, min=0, max=1, pty=in)))
  handle onProductionEnd : function (dt:ParsingData) {
    if dt.data(1) == null
      override dt.data(0)
    else
      defGrammar dt.data(1), dt.data(0)
  };
UseModifierCmd : prule ref root.Cmd(kwd=("use"),
                                    args=((UseModifierSubject)));
UseModifierSubject : module inherits root.FullIdSubject {
  Subject2.{fltr=2, frc=1}
}

أمثلة

استخدام مبدّل use لتفعيل مجموعة قواعد ضمن جملة واحدة:
@use[myGrammar] myGrammarCmd "...";
استخدام الأمر use لتفعيل مجموعة قواعد ضمن المجال الحالي:
use myGrammar;
myGrammarCmd "...";
استخدام الأمر use لإنشاء اختصار لمجموعة القواعد يمكن استخدامه لاحقاً في مبدّل use:
use myGrammar g;
@use[g] myGrammarCmd "...";

الإشعارات

يُطلق القلب إشعارات مختلفة للإشعار عن أخطاء أو ماشابه. يحتوي الإشعار على المعلومات التالية:
  • Code: شفرة لتمييز الإشعار بشكل فريد.
  • Severity: حدة أو درجة خطورة الإشعار. القيم المحتملة لهذه المعلومة هي:
    • 0: أشد الإشعارات حدّة. تشير إلى خطأ لا يستطيع معه القلب من الاستمرار.
    • 1: تشير إلى خطأ يستطيع القلب معه الاستمرار بعد إهمال الجزء الذي يحتوي الخطأ.
    • 2: تحذير مهم يجب على المستخدم الانتباه إليه، لكن لم يُهمل أي جزء من البرنامج الخاضع للترجمة.
    • 3: تحذير يمكن للمستخدم إهماله.
    • 4: إسترعاء انتباه المستخدم، لكن المستخدم لا يحتاج لاتخاذ أي إجراء.
  • Description: شرح لمعنى الإشعار.
  • Filename: إسم الملفِّ مصدرِ الإشعار.
  • Line: رقم السطرِ مصدرِ الإشار.
  • Column: رقم العمودِ مصدرِ الإشعار.
فيما يلي قائمة بالإشعارات التي يطلقها القلب أثناء عملية الترجمة:
الشفرة (code) الشدة (severity) الوصف
CL1001 1 يطلق هذا الإشعار عند مصادفة المرمّز لمحارف لا تنتمي لأي قاعدة ترميزية.
CL1002 1 صوان المحارف المدخلة ممتلئ. الرمز أطول من سعة الصوان وقد يتم تقسيم الرمز إلى عدة رموز.
CL2001 2 صوان المحارف المدخلة ممتلئ. الرمز أطول من سعة الصوان ما يؤدي إلى إقتطاع بعض المحارف من الرمز.
CP1001 1 خطأ إعرابي. سيتم تجاوز الجملة الخاطئة إلى التي تليها.
CP1002 1 المعرب أنهى عملية الإعراب قبل إكمال القاعدة الإعرابية الجذرية.
CP1003 1 المعرب استلم رمزاً بعد أن أنهى عملية الإعراب بإكمال القاعدة الإعرابية الجذرية.
CP2004 2 الغموض الإعرابي يتسبب في تشعب المعرب إلى وضعيات متعددة.
CP1005 1 مبدّل غير متوقع. واجه المعرب مبدّلاً يم تتقبله أي قاعدة إعرابية.
CD1001 1 التعريف يفتقر إلى التركيب الصحيح. لم يجد المعرب تركيب الربط بـ":" المطلوب للتعريف.
CD1002 1 التعريف يفتقر إلى اسم.
CD1003 1 أمر تعريف غير صالح.
CD1004 1 محاولة دمج تعريفين غير متوافقين.
CG1001 1 خطأ مجهول.
CG1003 1 فشل الاستيراد. فشل تحميل الملف المعني.

المتن القواعدي الكامل

////////////////////////////////////////////////////////////////////////////
// Type Definitions

// A list of keywords.
keywords : type list[string];

// A value for the filter modifier.
filter : type integer || list[integer];

// A list of productions that can be used as subjects.
valid_subject : type prule[Subject || Subject.Parameter || Command || Expression || Main.Statement || Set];


////////////////////////////////////////////////////////////////////////////
// Token Data Definitions

TokenData : module {
  // The set of postfixes used to specify character coding (ascii, unicode, etc.).
  charCodes : list[string] = ("u", "u8", "u16", "u32", "a");

  // Escape Sequences
  escapeSequences : keywords = (
    // invisible characters:
    "\\n", "\\r", "\\t", "\\b", "\\f", "\\0",
    // special characters:
    "\\\'", "\\\"", "\\\\", "\\!",
    // regular expressions characters:
    "\\[", "\\]", "\\(", "\\)", "\\*", "\\+",
    "\\.", "\\?", "\\|", "\\^", "\\$");

  // Predefined Operator Lists
  assignmentOpList : keywords = ("=", "+=", "-=", "*=", "/=", "÷=", "%=", "&=", "|=", "$=",
                                 "<<=", ">>=");
  comparisonOpList : keywords = ("==", "!=", "<", ">", "<=", ">=");
  addOpList : keywords = ("+", "-");
  mulOpList : keywords = ("*", "/", "%");
  bitwiseOpList : keywords = ("|", "$", "&", "<<", ">>");
  logOpList : keywords = ("||", "$$", "&&", "أو", "or", "nor", "xor", "xnor", "و", "and", "nand");
  prefixOpList : keywords = ("++", "--", "+", "-", "!", "!!", "not");
  postfixOpList : keywords = ("++", "--");
  linkOpList : keywords = ("->", ".", ".>", "<.");
  lowLinkOpList : keywords = ("=>", "..", "..>", "<..");
  lowerLinkOpList : keywords = (":", ":>", "<:");
  lowestLinkOpList : keywords = ("::", "::>", "<::", "in")
};


////////////////////////////////////////////////////////////////////////////
// Lexer Definitions

lexer LexerDefs;

LexerDefs : module {
  ////////////////////////////////////////////////////////////////////////
  // Character Group Definitions

  BinDigit : char '0'..'1';
  OctDigit : char '0'..'7';
  DecDigit : char '0'..'9';
  HexDigit : char '0'..'9', 'a'..'f', 'A'..'F';
  Letter : char 'a'..'z', 'A'..'Z', '_', 0h0620..0h065F, 0h066E..0h06DC;
  AnyCharNoEs : char !('\\');
  AnyCharNoEsOrSingleQuote : char !("\\'");
  AnyCharNoEsOrDoubleQuote : char !("\\\"");
  Spacing : char " \n\r\t";

  ////////////////////////////////////////////////////////////////////////
  // Token Definitions

  // Identifiers
  // letter {letter|decDigit}.
  Identifier : trule as { Letter + (Letter || DecDigit)*(0,endless) };

  // Integer Literals
  // [prefix] digit {digit} ["u"] ["i" num].
  IntLiteral : trule as {
    (DecIntLiteral || BinIntLiteral || OctIntLiteral || HexIntLiteral) +
    ("u" || "U")*(0,1) + (("i" || "I") + DecIntLiteral)*(0,1)
  };
  @inner DecIntLiteral : trule as { DecDigit*(1,endless) };
  @inner BinIntLiteral : trule as { ("0b" || "0B") + BinDigit*(1,endless) };
  @inner OctIntLiteral : trule as { ("0o" || "0O") + OctDigit*(1,endless) };
  @inner HexIntLiteral : trule as { ("0h" || "0H") + HexDigit*(1,endless) };

  // Float Literals
  // num "." num ["e" ["+"|"-"] num] ["f" num].
  FloatLiteral : trule as {
    DecDigit*(1,endless) + FloatPostfix ||
    DecDigit*(1,endless) + FloatExponent + FloatPostfix*(0,1) ||
    DecDigit*(1,endless) + "." + DecDigit*(1,endless) +
      FloatExponent*(0,1) + FloatPostfix*(1,1)
  };
  @inner FloatExponent : trule as { ("e" || "E") + ("+" || "-")*(0,1) + DecDigit*(1,endless) };
  @inner FloatPostfix : trule as { ("f" || "F") + DecDigit*(0,endless) };

  // Character And String Literals
  // charLiteral: "'" esCharWithDoubleQuote "'" [charCodePostfix].
  // stringLiteral: "\"" {esCharWithSingleQuote} "\"" [charCodePostfix].
  CharLiteral : trule as { "'" + EsCharWithDoubleQuote + "'" + CharCodePostfix*(0,1) };
  StringLiteral : trule as {
    StringLiteralPart + (Spacing*(0,endless) + StringLiteralPart)*(0,endless) +
    CharCodePostfix*(0,1)
  };
  @inner StringLiteralPart : trule as { "\"" + EsCharWithSingleQuote*(0,endless) + "\"" };
  @inner CharCodePostfix : trule as { alternate (root.TokenData.charCodes:cc)->( cc ) };

  // Custom Literals
  // "!" prefix "<" {esCharWithQuotes} ">" [postfix] "!".
  CustomLiteral : trule as (@unique prefix:keywords,postfix:keywords)=>{
    "!" + alternate (prefix:p)->( p ) + "<" + EsCharWithQuotes*(0,endless) +
    (">!" + Spacing*(0,endless) + "!<" + EsCharWithQuotes*(0,endless))*(0,endless) +
    ">" + (alternate (postfix:p)->( p ))*(0,1) + "!"
  };

  // String Characters
  @inner EsCharWithSingleQuote : trule as {
    AnyCharNoEsOrDoubleQuote || EsCodeSequence ||
    alternate (root.TokenData.escapeSequences:es)->( es )
  };
  @inner EsCharWithDoubleQuote : trule as {
    AnyCharNoEsOrSingleQuote || EsCodeSequence ||
    alternate (root.TokenData.escapeSequences:es)->( es )
  };
  @inner EsCharWithQuotes : trule as {
    AnyCharNoEs || EsCodeSequence || alternate (root.TokenData.escapeSequences:es)->( es )
  };
  @inner EsCodeSequence : trule as {
    '\\' + ('c' + HexDigit*(2,2) ||
            'u' + HexDigit*(4,4) ||
            'w' + HexDigit*(8,8) ||
            'n' || 't' || 'r' || '"' || '\'' || '\\' || 'ج' || 'ت' || 'ر')
  };

  // Constants
  Constant : trule as (@unique cnst:string)=>{ cnst };

  ////////////////////////////////////////////////////////////////////////
  // Ignore spacing (' ', '\n', '\r', '\t') when they don't belong to any token

  ignore { Spacing*(1,endless) };
  @minimum ignore { "/*" + any*(0,endless) + "*/" };
  @minimum ignore { "//" + any*(0,endless) + "\n" }
};


////////////////////////////////////////////////////////////////////////////
// Parser Productions
// NOTE:
//  The syntax written in comments is just an approximation of the actual syntax.
//  This commented syntax is written just to give the reader a simplified outline
//  of the actual syntax of the element.

start Program;

//////****** Main ******//////

// Program : StatementList.
Program : prule as { root.Main };

// Keywords and Constants
KeywordGroup : prule as (kwds:keywords)=>{ alternate (kwds:k)->( Keyword(k) ) };
Keyword : prule as (@unique kwd:string)=>{ lexer.Identifier(kwd) };
Constants : prule as (kwds:keywords)=>{ alternate (kwds:k)->( lexer.Constant(k) ) };

Main : module {
  start StatementList;

  // StatementList : Statement { ";" Statement }.
  StatementList : prule as {
    Statement*(0, 1) + (lexer.Constant(";") + Statement*(0, 1))*(0, endless)
  };

  // Statement : (Phrase | Phrase | ...).
  Statement : @limit[user.parent==self,child.terms==self] prule
    prefix self.id, DefaultModifier
    as (phrases:list[prule[Phrase]]=(CmdPhrase, SoloCmdPhrase, ExpPhrase)=>{
      alternate (phrases:phrase)->( phrase )
    };

  // Phrase : { Subject }.
  Phrase : prule as (
    subjects:list[map[prd:valid_subject, min:integer, max:integer, pty:integer]]
  )=>{
    concat (subjects:s)->( @priority(s.pty,0) s.prd*(s.min,s.max) )
  };

  // Default phrases.
  CmdPhrase : prule ref Phrase(subjects=((prd=LeadingCmdGroup,min=1,max=1),
                                         (prd=TrailingCmdGroup,min=0,max=endless)));
  SoloCmdPhrase : prule ref Phrase(subjects=((prd=SoloCmdGroup,min=1,max=1)));
  ExpPhrase : prule ref Phrase(subjects=((prd=root.Expression,min=1,max=1),
                                         (prd=TrailingCmdGroup,min=0,max=endless)));

  // Default Command Types
  LeadingCommand : prule inherits root.Command prefix self.id;
  TrailingCommand : prule inherits root.Command prefix self.id;
  SoloCommand : prule inherits root.Command prefix self.id;

  // Default Command Groups
  LeadingCmdGroup : prule_group[LeadingCommand];
  TrailingCmdGroup : prule_group[TrailingCommand];
  SoloCmdGroup : prule_group[SoloCommand] = (ImportCmd, DefCmd, UseCmd);

  // 'import' command.
  ImportCmd : prule inherits SoloCommand
    ref root.Cmd(kwd=("import", "اشمل"), args=((prd=root.Expression, min=1, max=1, pty=in)));

  // 'def' command.
  DefCmd : prule inherits SoloCommand
    ref root.Cmd(kwd=("def", "عرف", "عرّف"), args=((prd=root.Expression, min=1, max=1, pty=in)));

  // 'use' command.
  UseCmd : prule inherits SoloCommand
    ref root.Cmd(kwd=("use"), args=((prd=root.FullIdSubject, min=1, max=1, pty=in),
                                    (prd=root.IdSubject, min=0, max=1, pty=in)))
    handle onProductionEnd : function (dt:ParsingData) {
      if dt.data(1) == null
        override dt.data(0)
      else
        defGrammar dt.data(1), dt.data(0)
    };
};

//////****** Commands ******//////

// Root Command
Command : @limit[child.terms==(Cmd||MultiCmd)] prule
  prefix self.id, DefaultModifier;

// Cmd : keyword {Subject}.
Cmd : @limit[user.parent==root.Command] prule as
  (kwd:keywords, args:list[map[prd:valid_subject, min:integer, max:integer, pty:integer]])=>{
    root.KeywordGroup(kwd) + concat (args:a)->( @priority(a.pty,0) a.prd*(a.min,a.max) )
  };

// MultiCmd : {keyword {Subject}}.
MultiCmd : @limit[user.parent==root.Command] prule as
  (sections:list[map[kwd:keywords, min:integer, max:integer, pty:integer,
                     args:list[map[prd:valid_subject, min:integer, max:integer, pty:integer]]
                     ]])=>{
    concat (sections:s)->(
      @priority(s.pty,0) (root.KeywordGroup(s.kwd) + concat (s.args:a)->(
        @priority(a.pty,0) a.arg*(a.min, a.max)
      ))*(s.min, s.max)
    )
  };

//////****** Expressions ******//////

// Expression :
// operand {binaryOp operand}.
// [unaryOp] operand.
// operand [unaryOp].
// operand {FunctionalOp}.
Expression : module {
  prefix self.id, DefaultModifier;
  start Exp;

  Exp : prule as { LowestLinkExp + (@priority(in,0) lexer.Constant("\\")*(0,1)) };
  LowestLinkExp : prule prefix self.id as (enable:integer=endless)=>{
    ConditionalExp + (@priority(in,0) (LowestLinkOp + ConditionalExp)*(0,enable))
  };
  ConditionalExp : prule prefix self.id  as (enable:integer[0<=n<=1]=1)=>{
    ListExp + (@priority(in,0) (lexer.Constant("?") + ListExp)*(0,enable))
  };
  ListExp : prule prefix self.id as (enable:integer=endless)=>{
    (@priority(in,0) lexer.Constant(",")*(0,enable)) + LowerLinkExp +
    (@priority(in,0) (lexer.Constant(",") + LowerLinkExp*(0,1))*(0,enable))
  };
  LowerLinkExp : prule prefix self.id as (enable:integer=endless)=>{
    AssignmentExp + (@priority(in,0) (LowerLinkOp + AssignmentExp)*(0,enable))
  };
  AssignmentExp : prule prefix self.id as (enable:integer=endless)=>{
    LogExp + (@priority(in,0) (AssignmentOp + LogExp)*(0,enable))
  };
  LogExp : prule prefix self.id as (enable:integer=endless)=>{
    ComparisonExp + (@priority(in,0) (LogOp + ComparisonExp)*(0,enable))
  };
  ComparisonExp : prule prefix self.id as (enable:integer=endless)=>{
    LowLinkExp + (@priority(in,0) (ComparisonOp + LowLinkExp)*(0,enable))
  };
  LowLinkExp : prule prefix self.id as (enable:integer=endless)=>{
    AddExp + (@priority(in,0) (LowLinkOp + AddExp)*(0,enable))
  };
  AddExp : prule prefix self.id as (enable:integer=endless)=>{
    MulExp + (@priority(in,0) (AddOp + MulExp)*(0,enable))
  };
  MulExp : prule prefix self.id as (enable:integer=endless)=>{
    BitwiseExp + (@priority(in,0) (MulOp + BitwiseExp)*(0,enable))
  };
  BitwiseExp : prule prefix self.id as (enable:integer=endless)=>{
    UnaryExp + (@priority(in,0) (BitwiseOp + UnaryExp)*(0,enable))
  };
  UnaryExp : prule prefix self.id as (enable1:integer[0<=n<=1]=1, enable2:integer[0<=n<=1]=1)=>{
    (@priority(in,0) PrefixOp*(0,enable1)) + FunctionalExp +
    (@priority(in,0) PostfixOp*(0,enable2))
  };
  FunctionalExp : prule prefix self.id as (
    operand:valid_subject=root.Subject, fltr:filter=null, dup:integer=endless, pty:integer=in
  )=>{
    operand + (@priority(pty,0) (@filter(fltr) ParamPassExp ||
                                               TildeExp ||
                                               LinkExp(operand))*(0,dup))
  };

  // ParamPassExp : "(" [Expression] ")" | "[" [Expression] "]".
  ParamPassExp : prule as (expr:prule[Expression||Statement]=root.Expression, fltr:filter=null)=>{
    @filter(fltr) lexer.Constant("(") + expr*(0,1) + lexer.Constant(")") ||
                  lexer.Constant("[") + expr*(0,1) + lexer.Constant("]")
  };

  // TildeExp : "~" Subject.
  TildeExp : prule as (operand:valid_subject=root.TildeSubject)=>{ lexer.Constant("~") + operand };

  // LinkExp : LinkOp operand.
  LinkExp : prule as (operand:valid_subject=root.Subject)=>{ LinkOp + operand };

  // Operators :
  AssignmentOp : prule as { root.Constants(root.TokenData.assignmentOpList) };
  ComparisonOp : prule as { root.Constants(root.TokenData.comparisonOpList) };
  AddOp : prule as { root.Constants(root.TokenData.addOpList) };
  MulOp : prule as { root.Constants(root.TokenData.mulOpList) };
  BitwiseOp : prule as { root.Constants(root.TokenData.bitwiseOpList) };
  LogOp : prule as { root.Constants(root.TokenData.logOpList) };
  PrefixOp : prule as { root.Constants(root.TokenData.prefixOpList) };
  PostfixOp : prule as { root.Constants(root.TokenData.postfixOpList) };
  LinkOp : prule as { root.Constants(root.TokenData.linkOpList) };
  LowLinkOp : prule as { root.Constants(root.TokenData.lowLinkOpList) };
  LowerLinkOp : prule as { root.Constants(root.TokenData.lowerLinkOpList) };
  LowestLinkOp : prule as { root.Constants(root.TokenData.lowestLinkOpList) }
};

// An expression that blocks sets.
NoSetExpression : module inherits Expression {
  FunctionalExp.operand = root.NoSetSubject;
  LinkExp.operand = root.NoSetSubject;
};

// An expression that doesn't allow brackets.
NoBracketExpression : module inherits Expression {
  FunctionalExp.{operand=root.NoBracketSubject; fltr=(0,1,1); dup=endless; pty=in};
  LinkExp.operand = root.NoBracketSubject;
};

// An expression that allows only identifiers with link operators.
FullIdExpression : module inherits Expression {
  start FunctionalExp;
  FunctionalExp.{operand=IdSubject; fltr=2; dup=endless; pty=in};
  LinkExp.operand = root.IdSubject;
};

//////****** Subjects ******//////

// Subject : (Parameter | Command | Expression | Statement | Set) |
//           "(" (Parameter | Command | Expression | Statement | Set) ")" |
//           "[" (Parameter | Command | Expression | Statement | Set) "]".
Subject : module {
  prefix self.id, DefaultModifier;
  start Subject1;

  Subject1 : prule as (
    sbj1:list[prule[
      Parameter||root.Command||root.Expression||root.Main.Statement||root.Set
    ]] = (Parameter, SubjectCommandGroup, root.Set),
    sbj2:list[prule[
      Parameter||root.Command||root.Expression||root.Main.Statement||root.Set
    ]] = (root.Expression),
    sbj3:list[prule[
      Parameter||root.Command||root.Expression||root.Main.Statement||root.Set
    ]] = (root.Expression),
    fltr:filter=null,
    frc2:integer = 0,
    frc3:integer = 0
  )=>{
    @filter(fltr)
      alternate (sbj1:s)->( s ) ||
      lexer.Constant("(") + (alternate (sbj2:s)->( s ))*(frc2,1) + lexer.Constant(")") ||
      lexer.Constant("[") + (alternate (sbj3:s)->( s ))*(frc3,1) + lexer.Constant("]")
  };
  Subject2 : @limit[user.owner==Subject] prule as (
    sbj:prule[Parameter||root.Command||root.Expression||root.Main.Statement||root.Set],
    fltr:filter,
    frc:integer
  )=>{
    @filter(fltr) sbj ||
                  lexer.Constant("(") + sbj*(frc,1) + lexer.Constant(")") ||
                  lexer.Constant("[") + sbj*(frc,1) + lexer.Constant("]")
  };

  // Commands
  SubjectCommand : prule inherits root.Command prefix self.id;
  SubjectCommandGroup : prule_group[SubjectCommand];

  // Parameter: Identifier | Literal.
  Parameter : prule prefix self.id as (fltr:filter=null, cnsts:keywords=null)=>{
    @filter(fltr) lexer.Identifier || Literal || root.Constants(cnsts)
  };

  // Literal: IntLiteral | FloatLiteral | CharLiteral | StringLiteral | CustomLiteral.
  Literal : prule as (fltr:filter=null)=>{
    @filter(fltr) lexer.IntLiteral || lexer.FloatLiteral || lexer.CharLiteral ||
                  lexer.StringLiteral || lexer.CustomLiteral
  }
};

// The default Tilde subject.
TildeSubject : module inherits Subject {
  Subject1.{sbj1=(SubjectCommandGroup);
            sbj2=(root.Expression); frc2=0;
            sbj3=(root.Expression); frc3=0};
  SubjectCommandGroup = ();
};

// A subject that doesn't allow sets.
NoSetSubject : module inherits Subject {
  Subject1.{sbj1=(Parameter, SubjectCommandGroup);
            sbj2=(root.Expression); frc2=0;
            sbj3=(root.Expression); frc3=0}
};

// A subject that doesn't allow brackets.
NoBracketSubject : module inherits Subject {
  Subject1.{sbj1=(Parameter, SubjectCommandGroup, root.Set);
            sbj2=(); frc2=0;
            sbj3=(); frc3=0}
};

// A subject that allows only identifiers.
IdSubject : module inherits Subject {
  start Parameter;
  Parameter.{fltr=0; cnsts=null}
};

// A subject that allows full identifiers (with . or -> operators).
FullIdSubject : module inherits Subject {
  start Subject2;
  Subject2.{obj=root.FullIdExpression; fltr=0; frc=0}
};

ModifierSubject : module inherits Subject {
  Subject1.{sbj1=(SubjectCommandGroup);
            sbj2=(root.Expression); frc2=0;
            sbj3=(root.Expression); frc3=0};
  SubjectCommandGroup = (UseModifierCmd);

  // @use modifier.
  UseModifierCmd : prule ref root.Cmd(kwd=("use"),
                                      args=((UseModifierSubject)));
  UseModifierSubject : module inherits root.FullIdSubject {
    Subject2.{fltr=2, frc=1}
  }
};

//////****** Sets ******//////

// Set : "{" StatementList "}".
Set : @limit[child.terms==self,user.parent==self] prule prefix DefaultModifier as (
  stmt:prule[StatementList]=root.Main
)=>{
  lexer.Constant("{") + stmt + lexer.Constant("}")
};

//////****** Modifiers ******//////

Modifiers : module {
  Expression: module inherits root.Expression {
    start FunctionalExp;

    FunctionalExp.vars.{
      operand = root.Modifiers.Subject;
      dup = 1;
      filter2 = 2;
    };
  };

  Subject : module inherits root.Subject {
    SubjectCommandGroup = ();
  };

  CmdGroup : prule_group[LeadingCommand];

  Phrase: prule as {
    module.CmdGroup || module.Expression
  };

  LeadingModifier: prule inherits module.Phrase;
  TrailingModifier: prule inherits module.Phrase;
};

dimension {
  entryTokenText: "@";
  diverge : atRuleEntry;
  start: root.Modifiers.LeadingModifier;
  onExit : function (dt:ParsingData) {
    if dt.data.data.id == root.ModifierSubject.UseModifierCmd
      // Grammar overriding modifier.
      override dt.data.data.data
    else if dt.route == 0
      // Command based modifiers (specifies their own targets).
      addPrefix data=dt.data, type=dt.data.id,
                targets=prule(dt.data.id).targets
    else
      // Expression based modifiers (uses a default target).
      addPrefix data=dt.data, type=dt.data.id,
                targets=DefaultModifier
  }
};

dimension {
  entryTokenText: "@<";
  diverge : atRuleEntry;
  start: root.Modifiers.TrailingModifier;
  onExit : function (dt:ParsingData) {
    if dt.route == 0
      // Command based modifiers (specifies their own targets).
      addPostfix data=dt.data, type=dt.data.id,
                targets=prule(dt.data.id).targets
    else
      // Expression based modifiers (uses a default target).
      addPostfix data=dt.data, type=dt.data.id,
                targets=DefaultModifier
  }
};

معايير القلب التطبيقية

قيد الإنشاء

ملحق: صيغ التدوين

هذا الفصل يحتوي على شرح لصيغ التدوين في هذه الوثيقة.

أقسام ورموز الوثيقة

الكلمات التمييزية: تكتب الكلمات التمييزية باللون الأزرق.

الشفرات المصدرية: تكتب الشفرات المصدرية داخل مستطيل بخلفية غامقة اللون.

الأمثلة: تكتب الأمثلة داخل مستطيل مشابه للشفرات المصدرية ولكنه بخلفية بيضاء.

فصول شرح المعايير

تقسم فصول شرح معايير اللغة إلى عدة أقسام:

مقدمة: يحتوي على شرح مبسّط وسريع للعنصر المعني.

التعريف القواعدي: يحتوي على شرح القاعدة الإعرابية (parsing rule) أو القاعدة الترميزية (tokenizing rule) للعنصر المعني بشكل مفصّل. تفسّر هذه القاعدة كيفية تحليل العنصر وتمثيله رقميّاً.

التركيب النحوي (syntax): يحتوي على شرح مبسّط لتركيب هذا العنصر وكيفية استخدامه من قبل المبرمج. الغرض من هذا القسم تبسيط فهم القاعدة ولكنه لا يكفي لفهم كيفية تحليل العنصر وتمثيله رقميّاً.

الشروط: يحتوي الشروط اللازم توفرها كي يُقبل العنصر المعني من قبل المترجم.

النتيجة: يحتوي على نتيجة العملية، إن كان للعملية نتيجة. يُعلّم المتغير بعلامة ' للإشارة إلى قيمته بعد العملية. على سبيل المثال:

var' = var + 1

العملية: يحتوي على التفاصيل المعنية بطريقة التنفيذ وخطواتها، أي أسلوب عمل المترجم أثناء ترجمة أو تنفيذ العنصر المعني.

الردود: يحتوي على كافة الردود الممكنة من قبل المترجم أو المعرِب لكل حالة من الحالات.

لا يحتوي كل فصل على كل هذه الأقسام بالضرورة، بل قد يحتوي على بعضها فقط حسب الحاجة.

تدوين التراكيب النحوية

التراكيب في هذه الوثيقة تتبع صيغة EBNF وكما يلي:

العناصر غير الطرفية (non-terminals)تُكتب بكلمات مفصولة بشارحة سفلية، مع تكبير (capitalize) الحرف الأول من كلّ كلمة.
العناصر الطرفية (terminals)تُكتب بكلمات مفصولة بشارحة سفلية وباستخدام الأحرف الصغيرة.
سلسلة محارف فارغة تكتب "".
الفراغاتالفراغات غير مؤثرة.
|يُمثّل التبادل (عملية أو).
التسلسلالتسلسل يمثَّل بوضع العناصر المتسلسلة بعد بعضها دون فاصل.
( )يُستخدم لتجميع العناصر.
[ ]يُستخدم لتمثيل العناصر أو المجموعات الإختيارية.
{ }يُمثّل التكرار مع إمكانية الغياب.
:يُستخدم لفصل اسم العنصر عن تركيبه النحوي.
.يوضع في نهاية التعريف.

للتسلسل أسبقية على التبادل. على سبيل المثال، التركيبان التاليان متساويان:
a b c | d
(a b c) | d

تدوين القواعد

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

التدوين

العناصرتُكتب بكلمات مفصولة بشارحة سفلية، مع تكبير (capitalize) الحرف الأول من كلّ كلمة. يُستخدم هذا التدوين لتمثيل العناصر النهائية وغير النهائية بالإضافة إلى مجموعات المحارف.
الثوابتتُحصر الثوابت بين علامتي اقتباس ويشمل هذا العناصر النهائية الثابتة بالإضافة إلى مجموعات المحارف.
سلسلة محارف فارغة تكتب "".
المتغيّراتالمتغيرات تمثل بكلمات مفصولة بشارحة سفلية وباستخدام الأحرف الصغيرة.
الفراغاتالفراغات غير مؤثرة.
||يُمثّل التبادل (عملية أو). أي أن المُعرب يقبل أيّاً من العناصر المفصولة بهذا الرمز.
+تمثّل التسلسل. أي أن العناصر المفصولة بهذا الرمز يجب أن تأتي متسلسلة في الشفرة المصدرية.
*تمثّل التكرار. يُتبع بقائمة من عنصرين مفصولين بفارزة يمثّل الأول منهما الحد الأدنى للتكرار بينما يمثّل الآخر الحد الأعلى. لتمثيل تكرار مفتوح (دون حد أعلى) تُستخدم الكلمة 'endless'. يمكن للحذ الأعلى أن يكون صفراً ما يعني تعطيل العنصر المعني. يمكن استخدام قيمة ضمنية للحدين الأعلى والأدنى ويمكن أيضاً تمثيلهما بمتغيرات.
( )يُستخدم لتجميع العناصر، أو لتعريف متغيرات القوالب.
:يُستخدم لفصل اسم العنصر عن قاعدته النحوية.
;يُستخدم لفصل التعريفات عن بعضها.
@يُستخدم لتمييز المبدلات القواعدية.
/* */يُستخدم لكتابة التعليقات والملاحظات المتعددة الأسطر.
//يستخدم لكتابة الملاحظات المفردة السطر.

للتسلسل أسبقية على التبادل. على سبيل المثال، التركيبان التاليان متساويان:

a + b + c || d
(a + b + c) || d

التعريفات

قواعد لغة الأسُس تتكون من سلسلة من التعريفات مفصولة بفارزة منقوطة. كل تعريف من هذه التعريفات يأخذ الصيغة التالية:
Identifier ":" DefinitionCommand
الأمر التعريفي (definition command) يختلف بتركيبه حسب نوع التعريف، ولكنّه يبدأ دائماً بكلمة تمييزية تحدد نوع هذا التعريف. اسم التعريف لا يُمكن تكراره ضمن مجال واحد. فيما يلي قائمة بالأوامر التعريفية الممكنة:

type
يعرّف صنف بيانات جديد. على سبيل المثال، التعريف التالي يعرّف صنف بيانات يتكون من مصفوفة من سلاسل المحارف:

keywords : type list[string];
يمكن للصنف أن يكون اختياراً بين مجموعة من الأصناف. على سبيل المثال:
keyword : type string || list[string];
المثال أعلاه يعرّف الصنف ليكون أما سلسلة محارف أو مصفوفة من سلاسل المحارف. راجع قسم القواعد ذات المعطيات أدناه لمزيد من المعلومات عن الأصناف الجائزة.

integer, string, list, map
هذه الأوامر التعريفية تقوم بتعريف بيانات من نوع رقم صحيح (integer) أو سلسلة محارف (string) أو مصفوفة (list) أو قاموس (map).
تحديد صنف عناصر المصفوفة يتم بين قوسين مربّعين. ويُمكن أيضاً التخيير بين أكثر من صنف باستخدام ||. فيما يلي مثالان توضيحيان:

list[string]
list[string || integer]
ويتم تحديد عناصر القاموس بشكل مشابه، أي بكتابة قائمة عناصر القاموس مفصولة بفارزة ومحصورة بقوسين مربّعين. كل عنصر من عناصر القاموس يتكون من اسم وصنف مفصولين بنقطتان متعامدتان (:). كما هو الحال مع المصفوفة، يُمكن هنا التخيير بين أكثر من صنف. فيما يلي مثالان توضيحيان:
map[kwd:string, count:integer]
map[kwd:string||list[string], count:integer]
راجع قسم القواعد ذات المعطيات أدناه لمزيد من المعلومات حول الأصناف الجائزة للمصفوفة والقاموس.

char
يعرّف هذا الأمر مجموعة محارف ويُتبع الأمر char بتعريف لمجموعة المحارف المشمولة بهذا التعريف وبكون بأحد الصيغ التالية:

1. "<characters of the group>"
2. <from code> .. <to code>
3. 'f' .. 't'
4. <code>, <code>, ...
5. <character group>, <character group>, ...
6. !(<character group>)
وفيما يلي شرح لهذه الصيغ:
1. تُكتب كل المحارف المشمولة بهذه المجموعة بين علامتي الإقتباس.
2. تشمل المجموعة كل المحارف التي تنحصر شفرتها بين قيمتين.
3. مثل سابقتها، لكن تكتب المحارف نفسها كقيمتي البداية والنهاية، بدل كتابة شفرتيها.
4. تكتب كل شفرات المحارف المشمولة مفصولة بفارزة.
5. لجمع أكثر من مجموعة من المحارف في مجموعة واحدة.
6. تُستخدم للإستثناء، أي تعريف مجموعة تشمل كل المحارف باستثناء المجموعة المحددة بين القوسين.
يمكن استخدام الأمر char كصنف لمعطى يجعل هذا المعطى مؤشراً على قاعدة مجموعة محارف.

trule
يُستخدم لتعريف قاعدة ترميزية (tokenizing rule) وهو ما يُستخدم من قبل المعرِب. يُمكن للرمز أن يرث رمزا آخراً ما يجعله يحمل نفس المعرّف للمورّث، أي عندما يُطابق المرمز المدخلات مع قاعدة الرمز الوارث فإنه يُعطي الناتج قيمة المورث التعريفية بدل قيمة الوارث. لتعريف رمز يُستخدم التدوين التالي:

Id ":" "trule" ["inherits" ParentId] "as" ["(" ParamList ")" "=>"] "{" RuleBody "}".
لتعريف قاعدة تستخدم متن قاعدة أخرى (أي تستخدم المتن من قالب) يُستخدم التدوين التالي:
Id ":" "trule" ["inherits" ParentId] "ref" RuleReference ["(" ParamList ")"].
قائمة المعطيات اختيارية وسيأتي شرحها لاحقاً.
المثال التالي يعرّف رمزاً يتكون من حرف واحد أو أكثر:
Identifier : trule as { ( Letter )*(1,endless) };
يُمكن أيضاً استخدام تعريف رمزي فارغ كمظلة لمجموعة تعريفات أخرى ترث منها نفس القيمة التعريفية، كما يلي:
Id ":" ["inherits" ParentId] "trule" ";".
الإشارة داخل متن القاعدة إلى قاعدة ترميزية أخرى يعني استدعاءها أثناء عملية الترميز بصورة مشابهة لاستدعاء الدالّات لبعضها. ويُمكن تعليم القاعدة الترميزية بمبدل @inner لإعلام المرمّز أن هذا الرمز لا يأتي منفرداً وإنماً فقط ليُستدعى من متن تعريف آخر. يمكن استخدام الأمر trule كصنف لمعطى يجعل هذا المعطى مؤشراً على قاعدة ترميزية ويُتبع بإسم أو مجموعة أسماء لقواعد ترميزية تحصر المعطى بين أحد هذه الأسماء أو قاعدة مشقة من أحدها. المثال التالي يُعرف مصفوفة من المؤشرات على تعريفات ترميزية مشتقة من Constant:
consts : list[trule[Constant]];

prule
يُستخدم لتعريف قاعدة إعرابية (parsing rule). يُمكن للقاعدة الإعرابية أن ترث قاعدة أخرى ما يُعطيها كل صفاتها باستثناء ما تقوم القاعدة الوارثة بتعريفه بنفسها، أي أن التعريفات في القاعدة الوارثة تطغى على مثيلاتها في القاعدة المورّثة كما هو الحال مع الاشتقاق في البرمجة كائنية المنحى. تعريف القاعدة الإعرابية يكون بالصيغة التالية:

Id ":" "prule" ["inherits" ParentId] ["prefix" Ids] "as" ["(" ParamList ")" "=>"] "{" RuleBody "}".
لتعريف قاعدة تستخدم متن قاعدة أخرى (أي تستخدم المتن من قالب) يستخدم التدوين التالي:
Id ":" "prule" ["inherits" ParentId] ["prefix" Ids] "ref" RuleReference ["(" ParamList ")"].
قائمة المعطيات اختيارية وسيأتي شرحها لاحقاً. استخدام متن قاعدة أخرى يُعامل معاملة تضمين المتن في القاعدة، أي أنه يطغى على المتن الموروث. يُمكن أيضاً تعريف قاعدة إعرابية فارغة، أو قاعدة دون متن طالما لا تُستخدم القاعدة مباشرة في التحليل بل فقط لتوريث بعض الصفات لمجموعة من القواعد الأخرى. المثال التالي يعرّف قاعدة إعرابية لعملية جمع:
Multiply : prule as { Operand + lexer.Constant("*") + Operand };
كما هو الحال مع trule، يُمكن استخدام prule كصنف لمؤشرات على قاعدات إعرابية. المثال التالي يُعرف مصفوفة من المؤشرات على قاعدات مشتقة من Exp.
exps : list[prule[Exp]];
الكلمة التعريفية prefix تُستخدم مع التحليل متعدد الأبعاد لتحديد البيانات الإعرابية الموازية التي يمكن دمجها مع البيانات الإعرابية الناتجة من هذه القاعدة. راجع القسم "القواعد الموازية" أدناه لمزيد من التفاصيل.

prule_group
يعرّف مجموعة من القواعد الإعرابية يطبق المعرب عملية تبادل عليها عندما يُشار إلى هذه المجموعة.

Id ":" "prule_group" [ "[" ParentId "]" ].
يمكن تحديد القواعد الإعرابية الجائز إضافتها إلى المجموعة عن طريق تحديد اسم قاعدة مورِّثة، فيُحصر الاختيار في القواعد الوارثة لتلك القاعدة. على سبيل المثال:
CmdGroup : prule_group[Command];
Statement : prule as { Expression || CmdGroup };
في المثال أعلاه CmdGroup تمثل مجموعة من الأوامر ويُسمح للقاعدة Command أو أي وريث لها بالإنضمام لهذه المجموعة. القاعدة Statement تشير إلى المجموعة فيقوم المعرب بالبحث بين عناصر هذه المجموعة عن قاعدة متوافقة مع الرمز المعطى.

module
يُستخدم لتعريف حزمة من التعريفات. يُمكن للحزم أن ترث بعضها فتحتوي الحزمة الوارثة على كل تعريفات الحزمة المورّثة. تعريفات الحزمة الوارثة تطغى على التعريفات الموروثة كما هو الحال مع الاشتقاق في البرمجة كائنية المنحى. تعريف الحزمة يكون بالصيغة التالية:

Id ":" "module" ["inherits" ParentId] "{" ModuleBody "}" ";".
يُمكن لحزمة أن تعرف أحد القواعد الإعرابية كنقطة بداية وبذلك يكون جائزاً استخدام الحزمة كقاعدة إعرابية فيتم الإشارة إليها مباشرة من متن قواعد أخرى بدل الإشارة إلى قاعدة بداخلها ويتم ذلك باستخدام الأمر start الذي يستقبل بعده إسم القاعدة المعنية. المثال التالي يُعرف حزمة تحتوي ويحدد أحد قواعدها كنقطة بداية.
Exp : module {
  start Add;
  Add : prule as { Multiply + (lexer.Constant("*") + Multiply)*(0,1) };
  Multiply : prule as { root.Operand + (lexer.Constant("+") + root.Operand)*(0,1) };
}
الإشارة إلى قاعدة أو متغير ضمن حزمة أخرى يتم بتسبيق الإسم بنقطة تسبقها اسم الحزمة. الإسم root يُشير إلى المجال الرئيسي خارج أي حزمة.

null
الأمر null يُستخدم عند التوريث لإلغاء تعريف موروث عن طريق إعادة تعريفه بnull.

القواعد ذات المعطيات

تعرّف قائمة المعطيات بالصيغة التالية:
ParamList : Name ":" Type ["=" Value] {"," Name ":" Type ["=" Value]}
إن غابت قيمة المعطى من التعريف فإن الناتج يكون قالباً لا يُستخدم مباشرة وإنما بعد إنشاء قاعدة موصولة به أو مشتقة منه. المثال التالي يوضح كيفية إنشاء قالب وقاعدة موصولة به:
Cmd : prule as (kwd:string,body:prule[Expression])=>{ lexer.Constant(kwd) + body };
Print : prule ref Cmd(kwd="print", body=MyExpression);
المثال التالي يوضح تحديد قيمة للمعطيات في تعريف القاعدة:
LogicalExp : prule as (ops:list[string]=("&&", "||"))=>{
  Operand + (Constants(ops) + Operand)*(0,endless)
}
فيما يلي قائمة بأصناف المعطيات الجائزة في القواعد. تُستخدم هذه الأصناف في القواعد الترميزية والإعرابية بالإضافة لتعريفات الأصناف (الأمر type).

سلسلة محارفstring
عدد صحيحinteger
مصفوفةlist
قاموسmap
مؤشر على مجموعة محارفchar
مؤشر على قاعدة ترميزيةtrule
مؤشر على قاعدة إعرابيةprule

يُمكن أيضاً تطبيق عمليات التسلسل والتبادل على المصفوفات باستخدام الأوامر التالية:

concat
يقوم هذا الأمر بتكرار عنصر معين وتطبيق عملية التسلسل عليه (+) مع ربط كل عنصر من عناصر المصفوفة بأحد هذه التكرارات. الأمر يأخذ الصيغة التالية:

"concat" "(" ListArg ":" LoopArg ")" "=>" "(" Grammar ")".
يقوم الأمر بتحويل ListArg إلى سلسلة عناصر مع استبدال كل عنصر منها بناتج تطبيق ذلك العنصر على القالب Grammar. المثال التالي يوضّح هذه العملية:
Words : list("hello", "world");
HelloWorld : prule as { concat (Words:w)=>( w + "!" ) };
HelloWorldHardcoded : prule as { "hello" + "!" + "world" + "!" };
في هذا المثال HelloWorld مطابقة في نتيجتها ل HelloWorldHardcoded. فيقوم الأمر concat بتطبيق المقطع (w + "!") على كل عنصر من عناصر المصفوفة Words.

alternate
هذا الأمر مطابق بأسلوبه للأمر concat لكنه يطبق عملية تبادل بين عناصر المصفوفة بدل عملية التسلسل. كما في المثال التالي:

Words : list("hello", "world");
HelloWorld : prule as { alternate (Words:w)=>( w + "!" ) };
HelloWorldHardcoded : prule as { ("hello" + "!") || ("world" + "!") };

الإشارة إلى الرموز داخل القاعدة الإعرابية

الإشارة إلى الرموز يتم بتسبيقها ب .lexer متبوعاً باسم القاعدة الترميزية المعنية. يمكن حصر الرمز المقبول في قيمة محددة بدل قبول أي قيمة كانت وذلك بتضمين القيمة داخل قوسين بعد اسم القاعدة الترميزية. الأمثلة التالية توضح الحالتين:
a : prule as { lexer.Identifier };
b : prule as { lexer.Identifier("hello") }
في المثال أعلاه القاعدة الإعرابية a تقبل أي رمز من نوع Identifier بينما القاعدة b لا تقبل إلى رمزاً من نوع Identifier قيمته hello.

التوريث في الحزم

عند توريث الحزم يمكن استبدال التعريفات الموروثة بأخرى بإضافة تعريف يحمل نفس الأسم كما في المثال:
P : module {
  a : prule as { "cmd" + Expression }
};

C : module inherits P {
  a : prule as { "cmd" + Expression + Expression }
}
في هذا المثال تحتوي الحزمة C على تعريف جديد ل a يُلغي التعريف الموروث من P.
يُمكن أيضا في الحزمة الوارثة تعديل التعريفات بدل استبدالها كليّاً. على سبيل المثال يمكن استبدال قيمة المعطيات في قاعدة إعرابية دون استبدال متن القاعدة كما في المثال:
P : module {
  a : prule as (kwd:string="cmd")=>{ kwd + Expression }
};

C : module inherits P {
  a.kwd = "mycmd";
}
يُمكن جمع عمليات تعديل متعددة باستخدام الأقواس الحاصرة كما في المثال التالي:
C : module inherits P {
  a.{kwd = "mycmd"; param = MyExpression}
}

أوامر وكلمات تعريفية أخرى

بالإضافة إلى التعريفات هناك أوامر أخرى يمكن أن تظهر إلى جانب التعريفات، أي داخل متن الحزم أو ضمن المجال الرئيسي خارج الحزم:

ignore
يستخدم الأمر ignore لتعريف الرموز المهملة، أي لتعريف قاعدة ترميزية لرموز يتم إهمالها ولا تُرسل إلى المعرِب كما في تعريف الفراغات التي لا تدخل ضمن الإعراب وتأخذ الصيغة التالية:

"ignore" "{" TokenBody "}".

start
الأمر start يُعرّف نقطة البداية في الإعراب بتحديد اسم القاعدة الإعرابية التي يبدأ منها المعرِب. عند ظهور هذا الأمر داخل حزمة فإنه يحدد نقطة البداية لتلك الحزمة، أي عندما يُشار داخل متن قاعدة إعرابية إلى حزمة فإن المعرب ينتقل إلى القاعدة الإعرابية المُشار إليها بالإمر start داخل الحزمة.

lexer
هذا الأمر يشير إلى الحزمة التي تحتوي على القواعد الترميزية. تُتبع الكلمة lexer باسم الحزمة التي تحتوي على القواعد الترميزية. يتعامل المرمّز فقط مع القواعد التي داخل الحزمة المعنية ويهمل القواعد التي خارجها. يجب وجود هذا الأمر في المجال الرئيسي لتحديد الحزمة الترميزية الرئيسية أما داخل الحزم فإن وجوده أختياري ويؤدي إلى استخدام حزمة ترميزية مختلفة حال دخول المُعرب الحزمة المحتوية على هذا الأمر، والعودة إلى الحزمة الترميزية السابقة حال الخروج من تلك الحزمة والعودة للمجال السابق.

self
الكلمة التعريفية self تستخدم للإشارة إلى القاعدة المحتوية على هذه الكلمة. على سبيل المثال، التعريفان التاليان متساويان:

Multiply : prule as { Operand + (lexer.Constant("*") + self)*(0,1) };
Multiply : prule as { Operand + (lexer.Constant("*") + Multiply)*(0,1) }

المبدّلات

بالإضافة إلى الأوامر، هناك مبدّلات تُضاف إلى القواعد لتحديد ميّزات معينة لما تُضاف إليه. هذه المبّدلات تظهر فقط مرتبطة بأوامر أخرى ولا تظهر لوحدها في الجملة.

@filter
هذا المبدل يُستخدم كمرشّح لعناصر قائمة داخلة في عملية التبادل أو التسلسل. يأخذ هذا المبدل أحد هاتين الصيغتين:

"@filter" "(" number ")" <concatenation or alternation>
"@filter" "(" list ")" <concatenation or alternation>
في الصيغة الأولى يُبقي هذا المرشح على عنصر واحد من قائمة العناصر الداخلة في عملية التبادل أو التسلسل وهو العنصر ذو الموقع المقابل للرقم المُعطى. أما في الصيغة الثانية فإن المبدل يستلم قائمة من الأرقام كمعطيات بدل رقم واحد، كل عنصر من هذه القائمة يشير إلى أحد عناصر عملية التبادل أو التسلسل. على سبيل المثال:
@filter(2) Exp1 || Exp2 || Exp3 || Exp4
القاعدة في المثال أعلاه مساوية لكتابة Exp2 بمفردها.
@filter(2,1,4) Exp1 || Exp2 || Exp3 || Exp4
Exp2 || Exp1 || Exp4
وفي هذا المثال القاعدتان متطابقتان.

@priority
يُستخدم هذا المبدل لتحديد الأسبقية في أماكن التفرّع الإعرابي ويأخذ الصيغة التالية:

"@priority" "(" [priority ","] tokensToLive ")" Grammar.
يحدد هذا المبدّل قيمتين، الأولى (priority) تمثّل الفرع ذو الأسبقية الأعلى أما الثانية فتحدد عمر الفروع غير ذات الأسبقية ويحدد العمر بعدد الرموز التي يستقبلها المُعرب. عندما تمر على المعرب العدد المحدد من الرموز فإنه يقوم بإلغاء الفروع التي مازالت قائمة. إذا كانت قيمة العمر 0 فإن المعرب لا يقوم بالتفرّع بل يأخد من البداية الفرع ذو الأسبقية الأعلى ويهمل غيره.
يمكن لهذا المبّدل أن يطبَّق على عملية التكرار و التبادل كما يمكن أن يطبَّق على القاعدة الإعرابية نفسها لتحديد الأسبقية في حالة التفرع بين عدة قاعدات عند وصول المعرب إلى مجموعة إعرابية (prule_group). الأمثلة التالية توضح الحالات المختلفة:
a + (@priority(in,0) b*(0,1));     // الأسبقية للفرع b وتهمل الحالة الأخرى
a + (@priority(out,0) b*(0,1));    // الأسبقية لترك الفرع b
@priority(2,3) a || b || c || d;   // الأسبقية للفرع c مع إعطاء الفروع الأخرى فرصة مدتها 3 رموز قبل إلغائهم
@priority(5,0) prule MyCmd = x;  // أسبقية بقيمة 5 تعطى لهذه القاعدة ويؤدي ذلك لإلغاء أي قاعدة بأسبقية تقل عن 5

@limit
يُستخدم للإشارة إلى القيود المطبقة على القواعد. يُعطى هذا المبدل مجموعة من الشروط التي تطبقها القاعدة على القواعد المتعاملة معها. على سبيل المثال:

CmdTemplate : @limit[user.parent==Command] prule as (kwd:keywords)=>{ KeywordGroup(kwd) + Expression };
Command : @limit[child.terms==(CmdTemplate||CmdTemplate2)] prule;
في المثال أعلاه يشترط على أي مستخدم للقالب CmdTemplate أن يكون وريثاً للقاعدة Command. ويُشترط على ورثة Command أن يستخدموا أحد القالبين CmdTemplate و CmdTemplate2.

@unique
يُستخدم في تعريف معطيات القوالب القواعدية للإشارة إلى فرادة ذلك المعطى. يُمنع تكرار القيمة نفسها لذات المتغير في طبعات مختلفة من ذلك القالب. مثال:

Command : prule as (@unique kwd:string)=>{ kwd + Expression + ";" }
في المثال أعاله يُمنع إنشاء طبعتين من القالب Command بنفس القيمة لkwd.

@inner
يُستخدم مع تعريفات القواعد الترميزية للإشارة إلى قواعد داخلية لا تُستخدم من قبل المرمّز منفردة وإنما فقط عندما يُشار إليها من متن قاعدة أخرى.

@minimum
تستخدم مع الرموز لتحدد أن على المرمز الاكتفاء بأقصر رمز يطابق القاعدة بدل محاولة الحصول على أطول رمز.

القواعد الموازية

لتعريف قواعد موازية يُستخدم الأمر dimension ويأخذ الصيغة التالية:
dimension {
  start : <startingRule>;
  diverge : <divergePoint>;
  onExit : <exitHandler>
}
يحتوي التعريف على ثلاث قيم:
startRule: يحدد اسم القاعدة الإعرابية التي يبدأ منها البعد الجديد للإعراب.
divergePoint: يحدد النقطة التي يحاول المعرب منها دخول البعد الآخر. يمكن لهذه القيمة أن تكون atRuleEntry (للمحاولة قبل دخول أي قاعدة جديدة) أو تكون atRuleExit (للمحاولة بعد الخروج من أي قاعدة) أو تكون atNewToken (للمحاولة عند استلام كل رمز جديد من المرمّز).
exitHandler: يحدد التعامل مع البيانات الإعرابية الناتجة من إعراب البعد الآخر. تنفذ هذه الدالة بعد الإنتهاء من إعراب البعد الآخر والعودة للبعد الأساسي.

جميع الحقوق محفوظة لـ سرمد خالد عبداللّه 2018م \ 1440هـ.
نُشر هذا الملف برخصة الأسُس العامة (Alusus Public License)، الإصدار 1.0، والمضمّنة مع هذا الملف والمتوفرة أيضاً على الرابط http://alusus.net/alusus_license_1_0. يرجى قراءة الرخصة للتعرف على شروط الاستخدام والنسخ.