المحلل اللغوي LL

المحلل اللغوي LL LL parser
محلل LL هو محلل من أعلى إلى أسفل تصميم أعلى أسفل وأسفل أعلى لمجموعة فرعية من قواعد السياق الحرcontext-free grammar]]s]] فإنه يقوم بتحليل المدخلات من اليسار إلى اليمين، ويبني الاستخلاص أقصى اليسار من الجملة (وبالتالي محلل LL، مقارنة مع محللLR). فئة القواعد التي تحلل على النحو هذا يقال عليه قواعد LL. 
ما تبقى من هذه المقالة يصف نوع الجدول الذي يستند إليه المحلل، والبديل هو محلل نزول تكراريالترميز التكراري النموذجي الذي يتم ترميزه عادة باليد (وإن لم يكن دائما، وانظر على سبيل المثال ANTLR لـ (*)LL مولد محلل نزول تكراري).
ويسمى محلل LL محلل (k) LL إذا كان يستخدم الرموز K للاستباق عند تحليل الجملة. إذا كان هذا المحلل موجود لقواعد معينة، ويمكنه تحليل جمل هذه القواعد بدون تراجع فهو يسمى قواعد LL(k). اللغة التي لديها قواعد LL(k) تسمى لغة LL(k). وهناك لغات LL(k+n) التي ليست لغات LL(k). [1] والنتيجة لذلك هو أن ليس كل لغات السياق الحر هي لغات LL(k).
قواعد [[LL (1) LL(grammars شعبية جدا لأن المحلل LL المقابل يحتاج فقط لإلقاء نظرة على الرمز التالي لاتخاذ قرارات التحليل. الغات التي تستند إلى قواعد مع قيمة مرتفعة من k اعتبرت تقليديا [who?] [من?] صعبة التحليل، رغم أن هذا يعتبر نوعا ما غير صحيح الآن، نظرا لتوافر والتوسع في نطاق الاستخدام [citation needed] لمولدات المحلل الذي يدعم قواعد LL(k) لـ k الأعتباطى.
ويسمى محلل LL محلل (*)LL محلل إذا لم يقتصر على الرموز K للاستباق، ولكن يمكنه اتخاذ قرارات التحليل عن طريق التعرف على ما إذا كانت الرموز التالية تنتمي إلى لغة عادية (على سبيل المثال عن طريق استخدام أوتوماتيكية محدودة القطعية.
و هناك خلاف بين "المدرسة الأوروبية" لتصميم اللغة، والذين يفضلون الاستناد على قواعد LL، وبين " مدرسة الولايات المتحدة "، الذين يفضلون في الغالب الاستناد على قواعد [[LR. citation needed وهذا يرجع إلى حد كبير لتقاليد التدريس وتفاصيل وصف أساليب وأدوات في بعض الكتب المدرسية ؛ التأثير الآخر قد يكون نيكلاوس ويرث في أي تي أتش زيوريخ في سويسرا، الذي بحوه وصف عدد من الطرق لتحسين لغات ومترجمينLL(1)..

تنفيذ المحلل في C++

ما يلي يتبع تنفيذ C++ على أساس جدول محلل LL للغة في المثال التالي :
#include <iostream>#include <map>#include <stack>enum Symbols {
// the symbols:
// Terminal symbols:
TS_L_PARENS,// (
TS_R_PARENS,//)
TS_A,// a
TS_PLUS,// +
TS_EOS,// $, in this case corresponds to '\0'
TS_INVALID,// invalid token

// Non-terminal symbols:
NTS_S,// S
NTS_F};/* Converts a valid token to the corresponding terminal symbol*/enum Symbols lexer(char c){
switch(c)
{
case '(':
return TS_L_PARENS;
break;

case ')':
return TS_R_PARENS;
break;

case 'a':
return TS_A;
break;

case '+':
return TS_PLUS;
break;

case '\0':// this will act as the $ terminal symbol
return TS_EOS;
break;

default:
return TS_INVALID;
break;
}}int main(int argc, char **argv){
using namespace std;

if (argc < 2)
{
cout << "usage:\n\tll '(a+a)'" << endl;
return 0;
}

map< enum Symbols, map<enum Symbols, int> > table; // LL parser table, maps < non-terminal, terminal> pair to action
stack<enum Symbols>ss;// symbol stack
char *p;// input buffer

// initialize the symbols stack
ss.push(TS_EOS);// terminal, $
ss.push(NTS_S);// non-terminal, S

// initialize the symbol stream cursor
p = &argv[1][0];

// setup the parsing table
table[NTS_S][TS_L_PARENS] = 2;table[NTS_S][TS_A] = 1;
table[NTS_F][TS_A] = 3;

while(ss.size() > 0)
{
if(lexer(*p) == ss.top())
{
cout << "Matched symbols: " << lexer(*p) << endl;
p++;
ss.pop();
}
else
{
cout << "Rule " << table[ss.top()][lexer(*p)] << endl;
switch(table[ss.top()][lexer(*p)])
{
case 1:// 1. S → F
ss.pop();
ss.push(NTS_F);// F
break;

        case 2:// 2. S → (S + F)
ss.pop();
ss.push(TS_R_PARENS);//)
ss.push(NTS_F);// F
ss.push(TS_PLUS);// +
ss.push(NTS_S);// S
ss.push(TS_L_PARENS);// (
break;

 case 3:// 3. F → a
ss.pop();
ss.push(TS_A);// a
break;

default:
cout << "parsing table defaulted" << endl;
return 0;
break;
}
}
}

cout << "finished parsing" << endl;

return 0;}

بناء جدول المحلل LL(1)

من أجل ملء جدول التحليل، يجب علينا ان نقرر القاعدة التي يجب أن يختارها المحلل إذا رأى المحطة الغير طرفية A أعى المكدس من ورمزa على تدفق المدخلات. فمن السهل أن نرى أن هذه القاعدة ينبغي أن تكون على شكل A → w واللغة الموافقة ل w يجب أن يكون لديها على الأقل سلسلة واحدة تبدءا من a لهذا الغرض نحدد أول مجموعة من w، كتبت هنا Fi(w)، كمجموعة من المحطات الطرفية التي يمكن العثور عليها في بداية أي سلسلة فيw، بإضافة ε إذا كانت السلسلة الفارغة أيضا تنتمي إلى w يعطى القواعد مع الأحكام A1 → w1,..., An → wn، يمكننا حساب Fi(wi) وFi(Ai) لكل قاعدة كما يلي:
1. نبدأ كلFi(wi) وFi(Ai) مع المجموعة الفارغة
2. نضيف Fi(wi) على Fi(Ai) لأي حكم Ai → wi، حيث Fi يتم تعريفها على النحو التالي :
oFi(a w') = { a } for every terminal a لكل محطة طرفية
oFi(A w') = Fi(A) for every nonterminal A with ε not in Fi(A) لكل محطة طرفية a مع ε ليست في 
oFi(A w') = Fi(A) \ { ε } ∪ Fi(w') for every nonterminal A with ε in Fi(A) لكل محطة غير طرفية aمع ε في
oFi(ε) = { ε }
3. أضف Fi(wi) إلى Fi(Ai) لكل قاعدة Ai → wi
4. كرر الخطوات 2 و3 حتى كافة مجموعات Fi تبقى متساوية.
وللأسف، فإن المجموعات الأولى ليست كافية لحساب جدول التحليل، لأن الجزء اليمين w من القاعدة قد يكتب أخيراً لسلسلة فارغة. لذلك فإن المحلل ينبغي أن يستخدم القاعدة A → w إذا ε في Fi(w) وترا على تدفق الإدخال الرمز الذي يمكن أن يتبع a ولذلك نحن بحاجة أيضا المجموعة التابعة لـ A، تكتب هنا Fo(A)، التي تعرف على أنها مجموعة من المحطات الطرفية a ومن هذا القبيل هناك سلسلة من الرموز αAaβ التي يمكن استخلاصها من رمز البداية. ويمكن أن يتم احتساب المجموعات التابعة للمحطات الغير طرفية في القواعد على النحو التالي :
1. نبدأ كل Fo(Ai) مع المجموعة الفارغة
2. إذا كان هناك قاعدة للنموذج Aj → wAiw'، إذا
       * إذا كانت المحطة الطرفية a  في Fi(w')، قم بإضافة a إلى Fo(Ai)
       * إذا كانت ε في Fi(w')، قم بإضافة a إلى Fo(Ai)
3. كرر الخطوة 2 حتى كل مجموعات Fo تبقى متساوية.
الآن يمكن أن نحدد بالضبط أي القواعد التي سوف ترد في جدول التحليل. إذا T[A, a] تشير إلى الدخول في الجدول لمحطة غير طرفية A وطرفية a، إذا
T[A،a]يحتوي على مادة A → w إذا وفقط إذا كانت
a في Fi(w) أو 
ε في Fi(w) وFo(A)
إذا كان الجدول يحتوي على قاعدة واحدة على أكثر في كل خلية من خلاياه ،إذا المحلل سوف يعرف دائما أي قاعدة يجب أن يستخدمها وبالتالي يمكن تحليل السلاسل بدون تراجع. في هذه الحالة على وجه التحديد القاعدة تسمى قاعدة LL(1).

بناء جدول تحليل LL(k)

حتى منتصف التسعينات، وكان يعتقد على نطاق واسع أن LL(k) تحليل لـ (k > 1) كان غير عملياً [citation needed]، إذ أن جدول التحليل سيكون حجمه هائل في k في أسوأ الأحوال. وهذا المفهوم تغيير تدريجيا بعد إصدار PCCTS في حوالي عام 1992، عندما اتضح أنه يمكن تحليل العديد من لغات البرمجة بكفاءة من خلال المحلل LL(k) من دون التسبب في أعمال أسوأ الحالات للمحلل اللغوي. وعلاوة على ذلك، في بعض حالات تحليل LL مجدية حتى مع الاستباق غير المحدود. على النقيض من ذلك، مولدات المحلل التقليدي، مثل yacc يستخدم جدول تحليل LALR(1) لبناء محلل LR المقيد مع استباق رمز ثابت واحد.

التعارضات

كما هو موضح في المقدمة، محللات(1) LL تتعرف على اللغات ذات قواعد(1) LL، والتي هي حالة خاصة من قواعد السياق الحر (CFG's)؛ محللات(1) LL لا يمكن أن تتعرف على كل لغات السياق الحر. لغات(1) LLهي بالضبط تلك التي يتم التعرف عليها بتأكيدية التشغيل الذاتي التنازلى deterministic pushdown automata الذي يقتصر على حالة واحدة. من أجل CFG أن تكون قاعدة (1) LL، يجب أن لا تنشأ تعارضات معينة، التي وصفناها في هذا القسم.

المصطلحات

لتكونA محطة غير طرفية. أولاً A (تعرف لتكون) مجموعة من المحطات الطرفية التي يمكن أن تظهر في المركز الأول لأي سلسلة مستمدة من A. يتبع (A) الاتحاد الفوقي أولاً (B) حيث B هي أي محطة غير طرفية التي تليA  مباشرة في الجانب الأيمن من قاعدة الإنتاج.