תורת הקומפילציה 236360 הרצאה 2 ניתוח תחבירי ( Parsing )

Preview:

DESCRIPTION

תורת הקומפילציה 236360 הרצאה 2 ניתוח תחבירי ( Parsing ). Wilhelm, and Maurer – Chapter 8 Aho, Sethi, and Ullman – Chapter 4 Cooper and Torczon – Chapter 3. בשבוע הבא לא תתקיים הרצאה. במילים אחרות, אין צורך להגיע לאודיטוריום 2 בבוקר יום חמישי הבא. front-end שלב הניתוח. תוכנית מקור - PowerPoint PPT Presentation

Citation preview

236360תורת הקומפילציה 2הרצאה (Parsingניתוח תחבירי )

Wilhelm, and Maurer – Chapter 8Aho, Sethi, and Ullman – Chapter 4Cooper and Torczon – Chapter 3

בשבוע הבא לא תתקיים הרצאה

במילים אחרות, אין צורך להגיע בבוקר יום חמישי הבא. 2לאודיטוריום

front-endשלב הניתוח תוכנית מקור

Back end

Lexical Analysis

syntax analysisParsing

semantic analysis

token string

parse tree

decorated syntax tree

symboltable

errormessages

תוכנית מקור

Lexical analysis

parser

tokenget next token

parserהאינטרקציה בין המנתח לקסיקלי וה-

errormessagemanager

ניתוח סינטקטי

להבין את המבנה של התוכנית.המטרה:

בנויה מפונקציות, כל פונקציה בנויה Cלמשל: תוכנית מהצהרות ופקודות, כל פקודה בנויה מביטויים וכו'.

צורה פשוטה אך מדויקת לניסוח מבנה של תוכנית )בשפת תוכנית ספציפית( היא ע"י דקדוק חסר הקשר.

אנו נתעניין במחלקות של דקדוקים ח"ה שניתן לנתח ביעילות.

...פרטים בהמשך

, יוודא שהם מקיימים tokens יקרא את רצף ה-parserה-את הדקדוק )או יתריע על שגיאות(, ויבנה את עץ הגזירה של

התוכנית.

דקדוק חסר הקשר

G=(V, T, P, S)V – nonterminalsמשתנים ,

T – terminals ,טרמינלים ,tokens

Pחוקי גזירה –

P = V (V U T)*

Sמשתנה תחילי –

S V

דקדוק ח"ה עבור ביטוי אריתמטידוגמא:

א: ל' ימּון מ) ס*

סימון מקוצר:

{E, A} V =

{ ) , ( , - , id , +, - , , / , ^} T =

A +, , { E E A E

P =

A ‒, , E ) E (

A , , E - E

A ^} A /, , E id

E S =

E A E | ) E ( | - E | id E

+ | ‒ | | / | ^ A

שפה חסרת הקשר

– סדרה של החלפות של אותיות לא טרמינליות תוך שימוש בחוקי גזירההגזירה

– אוסף ביטויים הנגזרים מהמצב התחילי והמכילים טרמינלים שפהבלבד

E A E E

id A E

id + E

id + id

סימון הגזירה

שפה חסרת הקשר

– סדרה של החלפות של אותיות לא טרמינליות תוך שימוש בחוקי גזירההגזירה

– אוסף ביטויים הנגזרים מהמצב התחילי והמכילים טרמינלים שפהבלבד

– תוצאת סדרת גזירות בה נותרו (sentential formתבנית פסוקית ))אולי( לא-טרמינלים

E * id + E

– גזירה בה מוחלף בכל שלב הסימן השמאלי ביותר גזירה שמאלית)באופן דומה – גזירה ימנית(

ניתן לגזירה (רב שלבית)

E

E EA

E

id

E A E

E

E A E

E

id +

E A E

E

id + id

bottom-upדוגמא לגזירה

נתון דקדוקS → a A c B eA → A b | bB → d

a b b c d eקלט – a b b c d e

נבחר בשמאלית

a A b c d eנבחר בשמאלית

a A c d e

a A c B e

A A B אלטרנטיבות 3

A

B

S

A B אלטרנטיבות 3

bottom-upדוגמא לגזירה

נתון דקדוקS → a A c B eA → A b | bB → d

a b b c d eקלט – a b b c d e

נבחר בשמאלית

a A b c d eנבחר בשמאלית

a A c d e

a A c B e

A A B אלטרנטיבות 3

A

B

S

A B אלטרנטיבות 3

bottom-upדוגמא לגזירה

נתון דקדוקS → a A c B eA → A b | bB → d

a b b c d eקלט – a b b c d e

נבחר בשמאלית

a A b c d eנבחר בשמאלית

a A c d e

a A c B e

A A B אלטרנטיבות 3

A

B

S

A B אלטרנטיבות 3

bottom-upדוגמא לגזירה

נתון דקדוקS → a A c B eA → A b | bB → d

a b b c d eקלט – a b b c d e

נבחר בשמאלית

a A b c d eנבחר בשמאלית

a A c d e

a A c B e

A A B אלטרנטיבות 3

A

B

S

A B אלטרנטיבות 3

bottom-upדוגמא לגזירה

נתון דקדוקS → a A c B eA → A b | bB → d

a b b c d eקלט – a b b c d e

נבחר בשמאלית

a A b c d eנבחר בשמאלית

a A c d e

a A c B e

A A B אלטרנטיבות 3

A

B

S

A B אלטרנטיבות 3

האם קיבלנו גזירה ימנית או שמאלית?

קיבלנו גזירה ימנית

S a A c B e a A c d e a A b c d e a b b c d e

מלמטה תמיד בחרנו בכלל השמאלי ביותר, ולכן הגזירה המתקבלת )מלמעלה( בוחרת קודם בכלל הימני ביותר, כלומר ימנית.

נראה גם בעתיד ניתוחי תחביריים שהולכים על הקלט משמאל לימין והם עובדים בשיטת LRויוצרים גזירה ימנית. ניתוחים אלה מסומנים כ-

. bottom-upה-

ניתוחים שהולכים על הקלט משמאל לימין ומייצרים גזירה שמאלית . top-down והם עובדים לפי LLמסומנים ב-

ביטוי רגולרי מול דקדוק חסר הקשר

שפות חסרות הקשר יותר חזקות משפות רגולריות. בפרט, כל מבנה שניתן לבטא ע"י ביטוי רגולרי יכול להיות מתואר ע"י דקדוק.

ההיפך לא נכון )למשל?(

מדוע לא לעשות הכל בדקדוק? .כרגיל: הפרדה, מודולריות, פישוט אין טעם להפעיל כלים חזקים )ופחות יעילים( על ביטויים רגולריים

שקל לנתח.

, identifiersביטוים רגולרים שימושיים עבור תאור מבנים לקסיקלים כ- קבועים, מילות מפתח וכו'

דקדוקים שימושיים עבור מבנים מקוננים כסוגרים מאוזנים, התאמת begin-end, if-then-else'וכו ,

סוגי הניתוח התחבירי

כל דקדוק חסר-הקשר שקול לאוטומט מחסנית )אי-דטרמיניסטי(. .אבל – לא שמיש כי לא שקול לאוטומט מחסנית דטרמיניסטי

מתאים לגזירת כל Cocke-Younger-Kasami האלגוריתם באופן כללי:. אנו נעבוד עם אלגוריתמים לינאריים.O(n3)דקדוק אבל בסיבוכיות

...חישבו על תוכנית של מאות אלפי שורות קוד

המטרהפענוח ובנית עץ הגזירה תוך מעבר בודד על הקלט, משמאל לימין בכל שלב, הקלטw מתחלק ל x y

החלק שטרם נקרא חלק הקלט שקראנו

*

0

n

סוגי הניתוח התחבירי )המשך(

top-down – "מהשורש לעלים )נקרא גם – "ניתוח תחזית – predictive)

bottom-up מהעלים לשורש – מעבירים למחסנית, או מחליפים צד – (. shift reduceימין בסימן מהצד השמאלי של חוק הדקדוק )נקרא גם

*

0

n

x y

s

x y

s

top-downנתחיל בניתוח

נתונים לנו דקדוק ומילה.

רוצים להחליט ע"י מעבר על המילה, כיצד המשתנה התחילי S נגזר, ואח"כ תוך-כדי סריקת המילה, כיצד ממשיכים לגזור

כל משתנה עד שמגיעים לטרמינלים.

בדקדוק כללי תיתכן יותר מאפשרות אחת לגזור את המשתנה השמאלי ביותר לפי האות הבאה בקלט.

אנו נשתדל לעבוד עם דקדוקים שבהם תהיה רק אפשרות קטן כדי להחליט מהו כלל lookaheadאחת, אולי נצטרך

הגזירה המתאים הבא.

לנתחדקדוק שקל )מאד(

התבוננו בדקדוק:E → LIT | ( E OP E ) | not ELIT → true | falseOP → and | or | xor

בכל שלב של הגזירה, אם מסתכלים על משתנה שצריך לגזור והאות הבאה

בקלט, ברור מה כלל הגזירה שצריך להפעיל !

למשל איך נגזרת המילה not ( not true or false ) ?

E

E

E E

not

OP

LITnot

true

( )

or false

E => not E => not ( E OP E ) =>not ( not E OP E ) =>not ( not LIT OP E ) =>not ( not true OP E ) =>not ( not true or E ) =>not ( not true or LIT ) =>not ( not true or false )

LIT

מתרגם דקדוק באופן הבא:Recursive Descentאלגוריתם .מטרה: להתחיל במשתנה התחילי ולמצוא גזירה( עבור כל משתנה בדקדוקnonterminal.מגדירים פונקציה )-המנתח מתחיל לפעול מהפונקציה המתאימה לnonterminal

התחילי.:כל פונקציה מחקה את החוק שעבורו הוגדרה, באופן הבא

terminal מתורגם לקריאת האסימון המתאים מהקלט ובדיקת התאמה.

nonterminal.מתורגם להפעלת הפונקציה המתאימה לו אם ישנם כמה חוקי גזירה עבור אותוnonterminal בוחרים ביניהם ,

.lookaheadבעזרת

תוך כדי הפעלת פונקציות: top-downניתוח Recursive Descent

matchפונקציית עזר:

void match(token t) {if ( current == t )

current = next_token();else

error;}

פונקציה זו משמשת כדי לקרוא טרמינלים ולבדוק התאמה.

מחזיק את התו )האסימון( שעליו currentהמשתנה מסתכלים כרגע בקלט.

כתיבת פונקציות בהתאם לדקדוק

למשל, עבור הדקדוק:

E → LIT | ( E OP E ) | not ELIT → true | falseOP → and | or | xor

OP ו-E, LITנגדיר שלוש פונקציות:

כתיבת פונקציות בהתאם לדקדוק

void E() {if (current {TRUE, FALSE}) // E → LIT

LIT();else if (current = LPAREN) // E → ( E OP E )

match(LPARENT); E(); OP(); E(); match(RPAREN);else if (current = NOT) // E → not E

match(NOT); E();else

error;}

}

כתיבת פונקציות בהתאם לדקדוק

void LIT() {if (current = TRUE)

match(TRUE);else if (current = FALSE)

match(FALSE);else

error;}

כתיבת פונקציות בהתאם לדקדוק

void OP() {if (current = AND)

match(AND);else if (current = OR)

match(OR);else if (current = XOR)

match(XOR);else

error;}

סך-הכל: כתיבת פונקציות בהתאם לדקדוק

E → LIT | ) E OP E ( | not ELIT → true | falseOP → and | or | xor

void E)( {if )current {TRUE, FALSE}( LIT)(;else if )current = LPAREN( match)LPARENT(;

E)(; OP)(; E)(;match)RPAREN(;

else if )current = NOT( match)NOT(; E)(;else error;

}

void LIT)( {if )current = TRUE( match)TRUE(;else if )current = FALSE( match)FALSE(;else error;

}

void OP)( {if )current = AND( match)AND(;else if )current = OR( match)OR(;else if )current = XOR( match)XOR(;else error;

}

סך-הכל: כתיבת פונקציות בהתאם לדקדוק

E → LIT | ) E OP E ( | not ELIT → true | falseOP → and | or | xor

void E)( {if )current {TRUE, FALSE}( LIT)(;else if )current = LPAREN( match)LPARENT(;

E)(; OP)(; E)(;match)RPAREN(;

else if )current = NOT( match)NOT(; E)(;else error;

}

void LIT)( {if )current = TRUE( match)TRUE(;else if )current = FALSE( match)FALSE(;else error;

}

void OP)( {if )current = AND( match)AND(;else if )current = OR( match)OR(;else if )current = XOR( match)XOR(;else error;

}

הפונקציות האלו נראות כאילו הן לא עושות כלום

(חוץ מלזהות שגיאות), אבל ניתן כמובן להוסיף לכל אחת קוד נוסף, למשל שבונה את העץ כך שיוחזר העץ המלא

של הגזירה ביציאה מן הרקורסיה.

הוספת פעולות במהלך הגזירה

)( E(), LITבכל פעם שנקראת אחת הפונקציות )למשל, )( בדוגמא שלנו(, פירוש הדבר ש"איתרנו" צעד בגזירה.OPו-

בכל צעד כזה ניתן לבצע פעולות שונות.

הפעולות האלו נקראות "פעולות סמנטיות" ונרחיב עליהן בשיעור נפרד.

בפרט, ניתן לבנות את עץ הגזירה: כל פונקציה תחזיר רשומה מסוגNode.)צומת בעץ( .כל רשומה כזו מכילה רשימה של בנים-או ל( בכל קריאה לפונקציה אחרתmatch מוסיפים את ,)

שנבנה כעת.Nodeתוצאת הקריאה ל-

הוספת פעולות במהלך הגזירהNode E() {

result = new Node(); result.name = “E”;if (current {TRUE, FALSE}) // E → LIT

result.addChild(LIT());else if (current = LPAREN) // E → ( E OP E )

result.addChild(match(LPARENT));result.addChild(E());result.addChild(OP()); result.addChild(E());result.addChild(match(RPAREN));

else if (current = NOT) // E → not Eresult.addChild(match(NOT));result.addChild(E());

else error;return result;

}

ואם נחזור לדוגמא

אז, למשל:

input = “(not true and false)”;Node treeRoot = E();

E

( E OP E )

not LIT

falsetrue

and LIT

Recursive Descentגנרי

יש פרוצדורה הנראית Aנחזור לרגע לצורה הבסיסית. לכל משתנה דקדוק כך:

Void A() {Choose an A-production, A -> X1X2…Xk; for (i=1; i≤ k; i++) {

if (Xi is a nonterminal) call procedure Xi();

elseif (Xi == current)advance input;

elsereport error;

}}

איך מבצעים את הבחירה של כלל הגזירה? •ניתן פשוט לנסות אותם אחד אחר השני ולהמשיך באופן רקורסיבי עם •

backtracking .אבל זה יכול להיות מאד יקר .

איך מחליטים בין הכללים?

של אסימון יחיד.lookaheadבדוגמה שראינו הספיק

פורמלית:

– רשימת האסימונים שהם ראשונים FIRST(α), נגדיר: A→αעבור כלל באחת או יותר גזירות אפשריות הנובעות מכלל זה.

:Eבדוגמה שלנו, עבור הכללים של FIRST(LIT) = { true, false }FIRST( ( E OP E ) ) = { ( }FIRST(not E) = { not }

אין שום חיתוך בין הקבוצות השונות ולכן ניתן מיד לדעת מה לגזור עם lookahead .של אסימון יחיד

איך מחליטים בין הכללים?

nonterminal-ים עבור כללים שונים של FIRSTאם יש חיתוך בין ה- עמוק יותר.lookahead להשתמש ב-אונתון, צריך לתקן את הדקדוק

מחלקת הדקדוקים שעבורם ניתן לקבוע את כלל הגזירה הבא בכל פעם . LL(k) אסימונים נקראת k של lookaheadע"י

( של דקדוקי Parsingניתוח תחבירי )LL)1(

היא המחלקה של דקדוקים שניתן LL(1)המחלקה של אסימון אחד. יש look-aheadלגזור עם

דקדוקים כאלו לשפות תכנות רבות.

רקורסיה דוגמא לבעיה הניתנת לפיתרון:שמאלית

שלא ניתן top-downרקורסיה שמאלית יוצרת בעיית זיהוי עבור ניתוח חסום. lookaheadלפתור בעזרת

A -> AaB | aC לא ניתן לדעת איזה מהכללים להפעיל.

עשוי להיתקע בלולאה אינסופית! (backtracking)ויותר: משנים את הדקדוק.

.לכל דקדוק עם רקורסיה שמאלית יש דקדוק מקביל בלעדיה

A -> aCA’A’ -> aBA’ | ε

אלימינציה של רקורסיה שמאלית

נחליף את הכללים ביטול רקורסיה ישירה:

A → Aα1 | Aα2 | ··· | Aαn | β1 | β2 | ··· | βn

בכללים

A → β1A’ | β2A’ | ··· | βnA’

A’ → α1A’ | α2A’| ··· | αnA’ | Є אבל יש גם רקורסיה עקיפה. למשל:

S → Aa | b

A → Ac | Sd | Є ועבורה האלגוריתם מעט יותר מורכב.

אלגוריתם להעלמת רקורסיה שמאלת )עקיפה וישירה( מדקדוק

שאולי יש בו רקורסיה שמאלית, ללא מעגלים, וללא כללי G דקדוק קלט:ε.

דקדוק שקול ללא רקורסיה שמאלית.פלט:

. A → Єדוגמא לכלל אפסילון:

;. A → B; B → A דוגמא למעגל: ניתן לבטל כללי אפסילון ומעגלים בדקדוק )באופן אוטומטי(.

רעיון האלגוריתם לסילוק רקורסיה שמאלית: נסדר את המשתנים לפי סדר A1, A2, …, Anכלשהו:

נדאג שכל כלל שלו יהיה מהצורה Aiנעבור על המשתנים לפי הסדר, ולכל

Ai → Ajβ with j > i .מדוע זה מספיק?

אלגוריתם להעלמת רקורסיה שמאלת )עקיפה וישירה( מדקדוק

Input: Grammar G possibly left-recursive, no cycles, no ε productions.Output: An equivalent grammar with no left-recursionMethod: Arrange the nonterminals in some order A1, A2, …, An

for i:=1 to n do begin for s:=1 to i-1 do begin

replace each production of the form Ai → Asβ

by the productions Ai → d1β |d2β|…|dkβ

where As -> d1 | d2 | …| dk are all the current As-

productions; end

eliminate immediate left recursion among the Ai-

productionsend

ניתוח האלגוריתם

< t מקיים Ak → Atβנראה שבסיום האלגוריתם כל חוק גזירה מהצורה k.

Ai כלשהו )עם s: כשגומרים את הלולאה הפנימית עבור 1שמורה מתחילים בטרמינלים, או Aiבלולאה החיצונית( אז כל כללי הגזירה של

. j>s עבורם Ajבמשתנים

, כל כללי הגזירה שלו מתחילים Ai: כשמסיימים עם המשתנה 2שמורה או בטרמינלים. j>i עבורם Ajבמשתנים

. s ו-iהוכחת שתי השמורות יחד באינדוקציה על

: בסיום האלגוריתם אין רקורסיה שמאלית בין המשתנים מסקנההמקוריים )ישירה או עקיפה(.

. 2נובע משמורה

לגבי המשתנים החדשים, הם תמיד מופיעים כימניים ביותר, ולכן לעולם לא יהיו מעורבים ברקורסיה שמאלית.

הערות

?εמדוע לא עובד אם יש כלל

ייעלם מהתחלת הכלל A4 אז אנו דואגים שה-A5 → A4A3 כי אם יש:. A5 → A6A3, ואז יכול להתקבל A6ויוחלף, למשל ב-

ואז השמורות לא תקיפות A5 → A3, אז בעצם ניתן לגזור A6 → εאבל אם ועלולים לקבל רקורסיה שמאלית עקיפה.

בעיקרון, טיפלנו רק במשתנה השמאלי ואסור לו להיעלם!

Left בעזרת lookaheadהקטנת הצורך ב-Factoring

מכילה את קבוצת הטרמינלים Ai → β עבור כלל גזירה firstהקבוצה

. βשעשויים להופיע ראשונים בגזירה כלשהי של

של FIRST היא התנגשויות ב-Recursive Descentבעיה נוספת של כללי גזירה שונים לאותו משתנה.

, אלגוריתם המפיק דקדוק פירוק שמאלי – Left Factoringהפתרון: חלופי ללא הבעיה.

אינטואיציה: כאשר לא ברור כרגע איך להחליט בין שני כללים, ניצור כלל משותף לתחילת הגזירה ונפריד לשני כללים בהמשך. למשל:

S → if E then S S’ | TS’→ else S | ε

S → if E then S else S | if E then S | T

Left Factoring אלגוריתם

Input: Grammar G Output: An equivalent left-factored grammar

Method: For each nonterminal A find the longest (non empty) prefix α common to two or more of its production rules. Namely: A → α b1 | α b2 | …| α bn. Replace all the A productions

A → α b1 | α b2 | …| α bn

byA → α A’A’ → b1 | b2 | … | bn (A’ is a new nonterminal)

Repeatedly apply this transformation until no such common prefix exists.

עוד טרנספורמציות ?

קיימות עוד טרנספורמציות שמטרתן לייצר דקדוק ללא התנגשויות . FIRSTב-

של שפות רבות.top-downהטרנספורמציות הללו מאפשרות גזירת

Recursiveאפשר לגזור כל דקדוק שעבר "טיפול" כזה בהצלחה בעזרת Descent.

ישנן תכונות של שפות שלא ניתנות לזיהוי ע"י שום דקדוק חסר הקשר. שכל משתנה יוגדר לפני השימוש בו. Java ו-Cלמשל, הדרישה של

w2w | w is in (0|1)* אבסטרקציה של הבעיה: בדיקת דרישות כאלו תיכלל בניתוח הסמנטי.

האם בשבוע הבא תתקיים הרצאה ?

לא.

)LL)1אלגוריתם

LL(k)מחלקת הדקדוקים

אם הוא ניתן לגזירה:LL(k)דקדוק הוא במחלקה top-down,( סורקת את הקלט משמאלL,לימין )( מניבה את הגזירה השמאליתL,ביותר )-וזקוקה לlookahead בגודל k.

. LL(k)אם יש לה דקדוק LL(k)שפה היא

.LL(1)המקרה הפשוט ביותר הוא אלגוריתם

אלגוריתם הגזירה

Recursive באמצעות LL(1)דיברנו על מציאת גזירה למילה בדקדוק Descent שהוא אלגוריתם כללי לגזירת top-down.

חסרונות: ,משתמש ברקורסיה .תכונות הדקדוק מתוארות באריכות באמצעות קוד

נרצה טבלה המתארת את הגזירה, ונסלק רקורסיה. סילוק רקורסיה חוסך מקום ומאפשר לטפל בהתפוצצות המחסנית

של זמן הריצה. הרקורסיה מוחלפת במחסנית המכילה את התבנית שנותר לגזור.

LL(k) ידועים בשם LL(k)אלגוריתמים מבוססי-טבלה לניתוח שפות parsers.

טבלת המעברים

תתארנה עבור כל מצב נתון, באיזה כלל גזירה להשתמש.LL(1)טבלאות

שורות הטבלה: משתנים.

עמודות הטבלה: אסימונים אפשריים בקלט.

תוכן הטבלה: חוקי גזירה.

...למשל

$ xor or and false true not ( )

1 1 3 2 E

5 4 LIT

8 7 6 OP

(1) E → LIT(2) E → ) E OP E ( (3) E → not E(4) LIT → true(5) LIT → false(6) OP → and(7) OP → or(8) OP → xor

משתמש בטבלה ומחסניתLL(1)אלגוריתם

מחסנית

Parser

קלט

פלט

טבלת מעברים

נשתמש במחסנית לשמור את התבנית

הפסוקית שעוד נותר לגזור.

a * c + b $

מבנה נתונים בזמן ריצת האלגוריתם:

if ( E ) then Stmt else Stmt ; Stmts; } $

מחסנית:

top

if ( id < id ) then id = id + num else break ;id = id * id ;

Remaining Input:

גזירה בהינתן טבלה

( התחילי, ו-$ )סימן לסוף nonterminalאתחול המחסנית: המשתנה )הקלט(.

( המחסנית יכולה להכיל אסימוניםterminals או משתנים. "$" הוא )אסימון מיוחד, לצורך סימון סוף הקלט.

אם בראש המחסנית יש אסימון:.אם האסימון הבא בקלט אינו זהה: שגיאה אם הוא תואם את הקלט: עבור לאסימון הקלט הבא; הסר את

האסימון מהמחסנית. )אם האסימון הוא $, סיימנו(.אם בראש המחסנית יש משתנה:

.מצא את התא בטבלה המתאים למשתנה זה ולתו שבראש הקלט.אם התא ריק: שגיאה אחרת: הסר את המשתנה מראש המחסנית; הוסף למחסנית את צד

ימין של כלל הגזירה שנמצא בטבלה, לפי סדר – החל באסימון/משתנה הימני ביותר וכלה באסימון/משתנה השמאלי ביותר

)הוא ישאר בראש המחסנית(.

בניית הטבלה

, ובעזרתם את follows ו-Firstבתרגול תראו/ראיתם איך בונים את הטבלה.

דוגמא טריביאלית )בתרגול דוגמאות רבות(

דקדוק:A → aAb | c

a b c

A A → aAb (error) A → cטבלה:

:aacbbנריץ את האלגוריתם על המילה $, ומתחילים מאות הקלט הראשונה. Aאיתחול מחסנית: •

שארית הקלט המחסנית כלל מתאים

aacbb$ A$ A → aAb

aacbb$ aAb$ התאמת טרמינל

acbb$ Ab$ A → aAb

acbb$ aAbb$ התאמת טרמינל

cbb$ Abb$ A → c

cbb$ cbb$ התאמת טרמינל

LL(k)אלגוריתמים

היא )במקרה הגרוע( LL(k), הטבלה הנדרשת לאלגוריתם k>1עבור .kבעלת סיבוכיות אקספוננציאלית ב-

מעשיים parsersלכן, עד לא מזמן האמינו שלא יהיה מעשי לבנות -ים יותר גדולים. k עבור LL(k)לדקדוקי ,

)ארה"ב( Purdueבתחילת שנות התשעים הדגימו חוקרים מאוניברסיטת פרקטיים עם parsersשהמקרה הגרוע הוא למעשה נדיר, וניתן לבנות

LL(k). הכלי שפיתחו נקרא כיוםANTLR. כלים אחרים המבוססים עלLL(k): JavaCC משמש לבניית(

)גם הוא SableCC עצמו(, javac, כולל מהדר Javaמהדרים ב-(, ואחרים.Javaב-

LL(k) or not LL(k)?

; LL(1היא שפה ב-) an(b|c)nהשפה "דקדוק טבעי":

ונקבל את הדקדוק:left-factoringנבצע

anbn|ancn ?an(a|b)n)*( מה לגבי השפות?

:LL(k)דקדוק/שפה שאינם

S1 → aS1b | aS1c | ε

S1 → aS1X | εX → b|c

S → A | BA → aAb | c

B → aBbb | d ancbn | andb2n

LL(k) or not LL(k)?

:LL(k שאיננו ב-)LL(k+1) ב-דקדוק

left-factoring)-יניב דקדוק שקול ב LL(1)).

:LL(k) שאיננה ב-LL(k+1)דקדוק/שפה ב-

S יודעים שצריך להפעיל את המעבר של c או bרק כשרואים .ε ל-

S → aSA | ε A → akbS | c

S → akb | akc

מראה גזירה )לדוגמא( של תוכנית כללית

program

Main function More Functions

More FunctionsFunction

Function

Decls Stmts

Decls Stmts

Decls Stmts

• • •

• • • •

• •

• • •

Decl Decls

Decl Decls

Decl

Stmt Stmts

StmtIdType• • • •

• •

• • •

exprid =• • •

;

;

{ }

{

{

}

}

טיפול בשגיאות – נושא חשוב

סוגי שגיאות.)"שגיאות לקסיקליות )טעויות "דפוססוגרים לא מאוזנים(.שגיאות תחביריות( .)שגיאות סמנטיות )אופרנדים מטיפוס לא מתאים"="-במקום "=="(.שגיאות לוגיות )לולאה אינסופית, אבל גם שימוש ב

דרישות.דיווח על שגיאות בצורה ברורה היחלצות מהירה משגיאות כך שאפשר יהיה לגלות שגיאות המופיעות

בהמשך.-שימור יעילות הparser.

גישות לטיפול בשגיאות

ראשית, מודיעים איפה הניתוח נתקע. בד"כ זה די קרוב לשגיאה.

הבעיה העיקרית היא ההיחלצות מהשגיאה לצורך המשך ניתוח.

panic mode השמטת קטעים מהקלט עד מציאת סימן סנכרון ברור – )למשל ";" או סוגר סוגריים(.

.פשוט, אבל מאבד חלק )לפעמים משמעותי( מהקוד

phrase-level recovery "," נסיונות תיקון מקומיים. למשל החלפת – ב ";", הורדה או הוספה של ";", וכיו"ב.

.לא יעבוד אם הטעות קרתה לפני הזיהוי

error production טיפול בשגיאות צפויות ע"י תיקון אוטומטי במסגרת - הדקדוק.

global correction מציאת השינוי המינימאלי בתוכנית שיהפוך אותה – לגזירה. יקר, לא פרקטי.

parse generatorgrammar

tokens

parser

syntax tree

parsingמפורמליזם לתוכנה :

העיקרון דומה לזה של הניתוח הלקסיקאלי.

הפורמליזם והשיטות – שוניםהפורמליזם – דקדוק חסר הקשר-האמצעי – הרצת הparsing table ע"י recursive descent לכל .

מתאים נאמר בטבלה מה להפעיל. lookaheadמשתנה ו-

parsing tables

driver

parserparsing tables

לסיכום

לאחר שמקלפים את הקליפה הלקסיקלית, מפעילים ניתוח תחבירי parser-על ה tokens .להבנת התחביר - עץ התוכנית

מבנה תוכנית חוקית מתואר בפשטות ובדייקנות ע"י דקדוק חסר-הקשר.

-top או bottom-up( עובד בשיטת parserהמנתח התחבירי )down.ומגלה אם התוכנית נגזרת מהדקדוק ואיך

משתמשת בפונקציה המתאימה לכל recursive descentשיטת ה-. look-aheadמשתנה המחליטה על גזירת המשתנה עפ"י

ניתוח דקדוק כללי הוא קשה. ראינו דוגמא לדקדוק קל מאד לניתוח.

k של look-ahead בהינתן top-down ניתנים לגזירה LL(k)דקדוקי tokens .

באמצעות טבלה )במקום רקורסיה(. LL(1)ראינו גזירת

. bottom-upבשיעורים הבאים נדבר על גזירות