1 / 26

ניתוח תחבירי ( Parsing ) - המשך

ניתוח תחבירי ( Parsing ) - המשך. Wilhelm, and Maurer – Chapter 8 Aho, Sethi, and Ullman – Chapter 4. תזכורת: סוגי הניתוח התחבירי. top-down – מהשורש לעלים (נקרא גם – "ניתוח תחזית" – predictive )

Download Presentation

ניתוח תחבירי ( Parsing ) - המשך

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. ניתוח תחבירי (Parsing) - המשך Wilhelm, and Maurer – Chapter 8 Aho, Sethi, and Ullman – Chapter 4

  2. תזכורת: סוגי הניתוח התחבירי • top-down – מהשורש לעלים (נקרא גם – "ניתוח תחזית" – predictive) • bottom-up – מהעלים לשורש – מעבירים למחסנית, או מחליפים צד ימין בסימן מהצד השמאלי של חוק הדקדוק (shift reduce) s  x y s x y

  3. תזכורת: Recursive Descent • אלגוריתם Recursive Descent מתרגם דקדוק לקוד באופן הבא: • עבור כל nonterminal מגדירים פונקציה. • המנתח מתחיל לפעול מהפונקציה המתאימה ל-nonterminal הראשון. • כל פונקציה מחקה את החוק שעבורו הוגדרה, באופן הבא: • terminal מתורגם לקריאת הלקסמה המתאימה מהקלט. • nonterminal מתורגם להפעלת הפונקציה המתאימה לו. • אם ישנם כמה חוקי גזירה עבור אותו nonterminal, בוחרים ביניהם בעזרת lookahead.

  4. תזכורת: כתיבת פונקציות בהתאם לדקדוק void E() { if (lookahead  {TRUE, FALSE}) LIT(); else if (lookahead = LPAREN) match(LPARENT); E(); OP(); E(); match(RPAREN); else if (lookahead = NOT) match(NOT); E(); else error; } E → LIT | ( E OP E ) | not E LIT → true | false OP → and | or | xor void LIT() { if (lookahead = TRUE) match(TRUE); else if (lookahead = FALSE) match(FALSE); else error; } void OP() { if (lookahead = AND) match(AND); else if (lookahead = OR) match(OR); else if (lookahead = XOR) match(XOR); else error; }

  5. תזכורת: בעיות • איך מתגברים על כללי-ε, או כללים המתחילים ב- ε? • אם יש ε ב-FIRST, זו הופכת להיות ברירת המחדל. • מה קורה עם רקורסיה שמאלית? E → E A E | ( E ) | – E | id A → + | – | * | / | ^ • מחליפים את הדקדוק. • לכל דקדוק עם רקורסיה שמאלית מיידית יש דקדוק שקול נטול רקורסיה שמאלית.

  6. איך זה עוזר לנו? • יופי – יש לנו קוד עם פונקציה מתאימה לכל כלל גזירה. • הקוד מופעל בהתאם לגזירת הקלט מהדקדוק הנתון. • אבל איך זה עוזר לנו לקבל, למשל, עץ גזירה?

  7. הוספת פעולות במהלך הגזירה • בכל פעם שנקראת אחת הפונקציות (למשל, E(), LIT() ו-OP() בדוגמא שלנו), פירוש הדבר ש"איתרנו" צעד בגזירה. • בכל צעד כזה ניתן לבצע פעולות שונות! • בפרט, ניתן די בקלות לבנות עץ בעזרת הפעולות הללו.

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

  9. הוספת פעולות במהלך הגזירה Node E() { result = new Node(); if (lookahead  {TRUE, FALSE}) // E → LIT result.addChild(LIT()); else if (lookahead = LPAREN) // E → ( E OP E ) result.addChild(match(LPARENT)); result.addChild(E());result.addChild(OP()); result.addChild(E()); result.addChild(match(RPAREN)); else if (lookahead = NOT) // E → not E result.addChild(match(NOT));result.addChild(E()); else error; return result; }

  10. הוספת פעולות במהלך הגזירה • ואז, למשל: input = “(not true and false)”; Node treeRoot = E(); E ( E OP E ) LIT not LIT and true false

  11. הוספת פעולות במהלך הגזירה • כאמור, בפועל בד"כ לא באמת בונים עץ. • ייצוג של התוכנית כולה בזכרון יכול להיות מסובך ו"כבד". • אבל באופן שקול, ניתן לבצע כל פעולה שהיא בפונקציות השונות המתקבלות באלגוריתם RD . • פעולות אלה תייצרנה, בסופו של דבר, את הפלט של ה-parser.

  12. התאמת הדקדוק ל-RD • לא כל דקדוק מתאים ל-RD. • הבעיה הקשה: רקורסיה שמאלית. • כזכור, קל לבטל רקורסיה שמאלית ישירה: • מה לגבי רקורסיה שמאלית עקיפה? S → Aa | b A → Ac | Sd | ε

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

  14. הקטנת הצורך ב-lookahead בעזרת Left Factoring • בעיה נוספת של RD היא התנגשויות ב-FIRST של כללי גזירה שונים לאותו משתנה. • הפתרון: Left Factoring – פירוק שמאלי, אלגוריתם המפיק דקדוק חלופי ללא הבעיה. • למשל:

  15. אלגוריתם Left Factoring

  16. וזהו? • קיימות טרנספורמציות המייצרות דקדוק ללא רקורסיה שמאלית וללא התנגשויות ב-FIRST מדקדוקים רבים. • אפשר לגזור כל דקדוק שעבר "טיפול" כזה בהצלחה בעזרת RD. • מסקנה: אפשר לגזור שפות רבות ושונות בעזרת RD. • לשם מה אנו זקוקים לאלגוריתמים אחרים?

  17. אלגוריתם LL(1)

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

  19. שפות LL(k) • שפה נקראת LL(k) אם אפשר לגזור אותה בעזרת LL(k) parser. • אלגוריתם RD גוזר שפות LL(k) עבור k בלתי-חסום. • בדרך-כלל גוזרים שפות LL(k) בעזרת אלגוריתמים מבוססי-טבלה. אלגוריתמים אלו הם הידועים בשם LL(k) parsers.

  20. אלגוריתם LL(1) קלט Parser טבלת מעברים מחסנית פלט

  21. טבלת המעברים • ב-LL(1) משתמשים בטבלה המכתיבה, עבור כל מצב נתון, באיזה כלל גזירה להשתמש. • שורות הטבלה: כללי גזירה. • עמודות הטבלה: אסימונים אפשריים בקלט. • תוכן הטבלה: חוקי גזירה.

  22. למשל... (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

  23. האלגוריתם • אתחול המחסנית: המשתנה הראשון בדקדוק, ו-$ (סימן לסוף הקלט). • המחסנית יכולה להכיל אסימונים או משתנים. "$" הוא אסימון מיוחד, לצורך זה. • אם בראש המחסנית יש אסימון: • אם האסימון הבא בקלט אינו זהה: שגיאה. • אם הוא תואם את הקלט: צרוך את תו הקלט; הסר את האסימון מהמחסנית. (אם האסימון הוא $, סיימנו). • אם בראש המחסנית יש משתנה: • מצא את התא בטבלה המתאים למשתנה זה ולתו שבראש הקלט. • אם התא ריק:שגיאה. • אחרת:הסר את המשתנה מראש המחסנית; הוסף למחסנית את צד ימין של כלל הגזירה שנמצא בטבלה, לפי סדר – החל באסימון/משתנה הימני ביותר וכלה באסימון/משתנה השמאלי ביותר (הוא ישאר בראש המחסנית).

  24. בניית הטבלה • ... בתרגול.

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

  26. אלגוריתמים LL(k) • עבור k>1, הטבלה הנדרשת לאלגוריתם LL(k) היא (במקרה הגרוע) בעלת סיבוכיות אקספוננציאלית. • לכן, עד לא מזמן האמינו שלא יהיה מעשי לבנות parsers לשפות תכנות "אמיתיות" בעזרת אלגוריתם זה. • לכן yacc, bison וחברים מבוססים על אלגוריתמים אחרים. • בתחילת שנות התשעים הדגימו חוקרים מאוניברסיטת Purdue (ארה"ב) שהמקרה הגרוע הוא למעשה נדיר, וניתן לבנות parsers פרקטיים עם LL(k). • הכלי שפיתחו נקרא כיום ANTLR. • כלים אחרים המבוססים על LL(k): JavaCC (משמש לבניית מהדרים ב-Java, כולל מהדר javac עצמו), SableCC (גם הוא ב-Java), ואחרים.

More Related