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

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

רקורסיה שמאלית מיידית, כלומר רקורסיה של הצורה , ניתן להסיר בדרך הבאה. ראשית, אנו מקבצים את כללי A:

כאשר אף אחת מהשורות לא מתחילה ב-A. ואז נחליף את מערכת הכללים הזו ב

כאשר A" הוא לא-טרמינל חדש. ניתן לגזור את אותן שרשראות מ-A-לא-טרמינל כמו קודם, אבל עכשיו אין רקורסיה שמאלה. הליך זה מסיר הכל באופן מיידי שמאל רקורסיות, אך הרקורסיה השמאלית הכרוכה בשני שלבים או יותר אינה מוסרת. לְהַלָן אלגוריתם 4.8מאפשר לך להסיר הכל שמאל רקורסיותמתוך דקדוק.

אלגוריתם 4.8. הֲסָרָה רקורסיה שמאלה.

כְּנִיסָה. COP-דקדוק G ללא כללים אלקטרוניים (מהצורה A -> ה).

יְצִיאָה. COP-דקדוק G" בלי רקורסיה שמאלה, שווה ערך ל-G.

שיטה. בצע את שלבים 1 ו-2.

(1) סדר את הלא-טרמינלים של הדקדוק G בסדר שרירותי.

(2) בצע את ההליך הבא:

לאחר האיטרציה (i-1) של הלולאה החיצונית בשלב 2 לכל כלל של הטופס , שבו ק< i, выполняется s >ק. כתוצאה מכך, באיטרציה הבאה (ב-i), הלולאה הפנימית (ב-j) מגדילה ברצף את הגבול התחתון ב-m בכל כלל עד m >= i. לאחר מכן, לאחר הסרת המיידי רקורסיה שמאלהעבור A i -חוקים, m הופך להיות גדול מ-i.

אלגוריתם 4.8ישים אם לדקדוק אין כללים e - (כללים בצורת A -> e). זמין בדקדוק e - חוקים ניתן למחוק מראש. הדקדוק המתקבל בלי רקורסיה שמאלהיכול להיות שיש כללים אלקטרוניים.

פירוק שמאל

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

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

אלגוריתם 4.9. פירוק שמאלי של הדקדוק.

כְּנִיסָה. COP-דקדוק G.

יְצִיאָה. CF-דקדוק G המבוסס על שמאל", שווה ערך ל-G.

שיטה. עבור כל A שאינו מסוף, מצא את הקידומת הארוכה ביותר המשותפת לשתיים או יותר מהחלופות שלו. אם , כלומר קיימת קידומת משותפת לא טריוויאלית, החלף את כל כללי A

כאשר z מציין את כל החלופות שאינן מתחילות ב-.

כאשר A" הוא חדש שאינו מסוף. החל טרנספורמציה זו עד שלא תהיה לשתי חלופות קידומת משותפת.

דוגמה 4.9. שקול שוב את הדקדוק של הצהרות מותנות מ דוגמה 4.6:

S -> אם E אז S | אם E אז S אחרת S | א E -> ב

לאחר הפירוק השמאלי, הדקדוק מקבל את הצורה

S -> אם E אז SS" | a S" -> אחרת S | ה E -> ב

למרבה הצער, הדקדוק נשאר מעורפל, ולכן אינו דקדוק LL(1).

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


אורז. 4.4.

מתחילים וממשיכים ב-k מסופים שהוזכרו.

דקדוק נקרא LL(k)-דקדוק אם הוא LL(k)-דקדוק עבור כמה k.

דוגמה 4.7. שקול דקדוק G = ((S, A, B), (0, 1, a, b), P, S), כאשר P מורכב מחוקים

S -> א | ב, א -> אאב | 0, B -> aBbb | 1.

כאן . G אינו דקדוק LL(k) עבור כל k. באופן אינטואיטיבי, אם נתחיל בקריאת מחרוזת ארוכה מספיק של תווים a , איננו יודעים איזה מהכללים S -> A ו-S -> B הוחל ראשון עד שנתקל ב-0 או 1.

אם נפנה להגדרה המדויקת של הדקדוק של LL (k), קבענו ו- y = a k 1b 2k . ואז המסקנות

תואמים למסקנות (1) ו-(2) של ההגדרה. K התווים הראשונים של המחרוזות x ו-y תואמים. עם זאת, המסקנה שקרית. מכיוון ש-k נבחר כאן באופן שרירותי, G אינו דקדוק LL.

השלכות הגדרת LL(k)-דקדוק

משפט 4.6. דקדוק CS הוא LL(k)-דקדוק אם ורק אם עבור שני כללים שוניםומצומת R ריק לכל אלה , מה .

הוכחה. כּוֹרַח. נניח ועמידה בתנאי המשפט, ומכיל x . ואז, לפי ההגדרה של FIRST, עבור כמה y ו-z יש מסקנות

(שימו לב שכאן השתמשנו בעובדה ש-N אינו מכיל טרמינלים חסרי תועלת, כפי שמניחים עבור כל הדקדוקים הנחשבים.) אם |x|< k ; то y = z = e . Так как , то G не LL (k)- грамматика .

הלימה. נניח ש-G אינו דקדוק של LL(k).

ואז יש שתי מסקנות

שהמחרוזות x ו- y תואמות במיקומי k הראשונים, אבל . לכן, והם כללים שונים מפ' ומכל אחת מהקבוצות ו מכיל את המחרוזת FIRST k (x) התואמת למחרוזת FIRST k (y) .

דוגמה 4.8. דקדוק G המורכב משני כללים S -> aS | a , לא יהיה LL(1)-דקדוק, שכן

FIRST 1 (aS) = FIRST 1 (a) = א.

באופן אינטואיטיבי, ניתן להסביר זאת באופן הבא: כאשר מנתחים מחרוזת שמתחילה בתו a , אנו רואים רק את התו הראשון הזה, איננו יודעים איזה מהכללים S -> aS או S -> a יש להחיל על S . מצד שני, G הוא דקדוק של LL(2). ואכן, בסימון המשפט שהוצג זה עתה, אם , ואז A = S ו- . מכיוון שרק שני כללים שצוינו ניתנים עבור S, אז ו. מכיוון ש-FIRST2(aS) = aa ו-FIRST2(a) = a, אז לפי המשפט האחרון G הוא LL (2)-דקדוק.

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

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

רקורסיה שמאלית מיידית, כלומר רקורסיה של הצורה , ניתן להסיר בדרך הבאה. ראשית, אנו מקבצים את כללי A:

כאשר אף אחת מהשורות לא מתחילה ב-A. אז נחליף את מערכת הכללים הזו ב

כאשר A" הוא לא-טרמינל חדש. ניתן לגזור את אותן שרשראות מ-Non-Terminal A כמו בעבר, אך כעת אין רקורסיה שמאלית. הליך זה מסיר את כל הרקורסיה השמאלית המיידית, אך אינו מסיר רקורסיה שמאלית הכוללת שניים או יותר שלבים. ניתן להלן אלגוריתם 4.8מאפשר לך להסיר את כל הרקורסיות השמאליות מהדקדוק.

אלגוריתם 4.8. הסרת רקורסיה שמאלית.

כְּנִיסָה. COP-דקדוק G ללא כללים אלקטרוניים (מהצורה A -> ה).

יְצִיאָה. COP-דקדוק G" ללא רקורסיה שמאלית, שווה ערך ל-G .

שיטה. בצע את שלבים 1 ו-2.

(1) סדר את הלא-טרמינלים של הדקדוק G בסדר שרירותי.

(2) בצע את ההליך הבא:

לאחר האיטרציה (i-1) של הלולאה החיצונית בשלב 2 לכל כלל של הטופס , שבו ק< i , выполняется s >ק . כתוצאה מכך, באיטרציה הבאה (ב-i), הלולאה הפנימית (ב-j) מגדילה ברצף את הגבול התחתון ב-m בכל כלל עד m >= i . לאחר מכן, לאחר הסרת הרקורסיה השמאלית המיידית עבור כללי A i, m הופך להיות גדול מ-i.

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

נניח שעלינו לבנות מנתח עבור השפה שנוצרת על ידי הדקדוק הבא:

הה + ט | הט | ט

T→T*F | ת/ו | ו

ומספר | (ה)

הגדר טרמינלים FIRST(T)שייכים גם לסט FIRST(E+T), כך שלא ניתן לקבוע באופן חד משמעי את רצף הקריאות לפרוצדורה שיש לבצע בעת ניתוח מחרוזת הקלט. הבעיה היא שהלא טרמינלי המתרחשת במיקום הראשון של הצד הימני של כלל שגם הצד השמאלי שלו ה. במצב כזה, הלא טרמינלי הנקרא ישירות שמאלי-רקורסיבי.

לא טרמינלי א COP-דקדוקים Gשקוראים לו נותר רקורסיבי אם יש לדקדוק גזירה א =>* אוי.

דקדוק שיש לו לפחות כלל שמאלי-רקורסיבי אחד לא יכול להיות LL(1)-דקדוק.

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

    1. שמאל אלגוריתם חיסול רקורסיבי

לתת G = (N, T, P, S)– CS-דקדוק וכלל א → אוי 1 | אוי 2 | … | אוי נ | v 1 | v 2 | … | v Mמייצג את כל הכללים מ פמֵכִיל אבצד שמאל, ואף אחת מהשרשרות v אנילא מתחיל עם לא טרמינלי א.

בואו נוסיף לסט נאחר שאינו מסוף א"ולהחליף את הכללים המכילים אבצד שמאל, אל הדברים הבאים:

א → v 1 | v 2 | … | v M | v 1 א' | v 2 א' | … | v M א"

א' → w 1 | w 2 | … | w נ | w 1 א' | w 2 א' | …| w נ א"

ניתן להוכיח שהדקדוק המתקבל שווה ערך לזה המקורי.

כתוצאה מיישום טרנספורמציה זו על הדקדוק הנ"ל המתאר ביטויים אריתמטיים, אנו מקבלים את הדקדוק הבא:

הט | TE"

ה" → + ט | + TE"

טו | FT"

ט"→ * ו | * FT"

ו → (ה) | מספר

קל להראות שלדקדוק שנוצר יש את המאפיין LL(1).

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

לדוגמה,

S → אם E אז S אחרת S

סאם ה לאחר מכן ס

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

סאם ה לאחר מכן ס ס

ס" →

ס"→ אַחֵר ס

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

    1. 9.1.4. ירידה רקורסיבית עם חזרה לאחור.

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

לשם כך, המנתח המילוני מיוצג כאובייקט שיש לו, בנוסף לשיטות המסורתיות לִסְרוֹק, הַבָּאוכו', יש גם בנאי העתקה. לאחר מכן, בכל המצבים שבהם עלולה להיווצר אי בהירות, לפני תחילת הניתוח, יש לזכור את המצב הנוכחי של המנתח המילוני (כלומר, ליצור עותק של המנתח המילוני) ולהמשיך לנתח את הטקסט, בהתחשב בכך שאנו עוסקים בראשון האפשרי. בנייה במצב זה. אם הניתוח הזה נכשל, אז שחזר את המצב של lexer ונסה לנתח שוב את אותו קטע עם הדקדוק הבא, וכן הלאה.אם כל הניתוחים נכשלים, מדווחת על שגיאה.

שיטת הניתוח הזו היא פוטנציאלית איטית יותר מירידה רקורסיבית ללא חזרה לאחור, אך במקרה זה ניתן לשמור את הדקדוק בצורתו המקורית ולחסוך את המאמץ של המתכנת.

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

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

בדקדוק ז' שנדון לעיל, רקורסיה ישירה קיימת בכלל:<чс>-»<чс><цифра>, ובדקדוק השווה ז" - בכלל: ת-וותף.

כדי שהרקורסיה לא תהיה אינסופית, כדי שהסמל הלא-טרמינלי של הדקדוק המשתתף בה, חייבים להיות גם כללים אחרים המגדירים אותה, עוקפים את עצמה, ומאפשרים הימנעות מהגדרה רקורסיבית אינסופית (אחרת סמל זה פשוט לא היה צריך בדקדוק). כללים אלו הם<чс>-»<цифра>- בדקדוק ז' ו-ת->ו - בדקדוק ז'".

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


הגדרה של דקדוק. צורת האקוסה-מאורה "ZO /

אם תנסה להגדיר מהו מספר, אז אתה יכול להתחיל עם העובדה שכל ספרה בפני עצמה היא מספר. יתר על כן, אתה יכול לראות שכל שתי ספרות הן גם מספר, ואז שלוש ספרות וכו'. אם אתה בונה הגדרה של מספר בצורה זו, אז היא לעולם לא תושלם (במתמטיקה, קיבולת הסיביות של מספר אינה מוגבל בכל דבר). עם זאת, ניתן לראות שבכל פעם שאנו יוצרים מספר חדש, אנו פשוט מוסיפים את האדיפרה מימין (שכן אנו רגילים לכתוב משמאל לימין) לסדרת המספרים שכבר נכתבה. וסדרת הספרות הזו, החלה מספרה אחת, היא גם מספר בתורו. אז ניתן לבנות את ההגדרה למושג "מספר" כך: "מספר הוא כל ספרה, או מספר אחר שמצורפת לו כל ספרה מימין". זה מה שמהווה את הבסיס לכללי הדקדוק ז' ו-ז' ובא לידי ביטוי בשורה השנייה של הכללים בכללים<чс>-><цифра> [ <чс><цифра>ו-T->F | TF. כללים אחרים בדקדוקים אלו מאפשרים להוסיף סימן למספר (שורת הכללים הראשונה) ולהגדיר את המושג "ספרה" (שורת הכללים השלישית). הם אלמנטריים ואינם דורשים הסבר.



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

דרכים אחרות להגדרת דקדוקים

צורת Backus-Naur היא דרך נוחה, מנקודת מבט פורמלית, אך לא תמיד מובנת לכתיבת דקדוקים פורמליים. הגדרות רקורסיביות טובות לניתוח פורמלי של שרשראות שפה, אך אינן נוחות מנקודת מבט אנושית. למשל, אילו כללים<чс>-><цифра> | <чс><цифра>לשקף את היכולת לבנות מספר כדי להוסיף כל מספר של ספרות בצד ימין, החל מאחת, אינה ברורה ודורשת הסבר נוסף.

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

רישום כללי דקדוק

באמצעות מטא-תווים

הקלטת כללי דקדוק באמצעות תווים מטא מרמזת שתווים מיוחדים יכולים להופיע במחרוזת כללי הדקדוק - meta-


358 פרק 9. שפות פורמליות ודקדוקים

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

□ סוגריים פירושם של כל המחרוזות הרשומות בתוכם
תווים במקום נתון של כלל הדקדוק יכולים להיות רק אחד
לְהַנֵץ;

□ סוגריים מרובעים פירושם שהמחרוזת המצוינת בהם יכולה להיפגש
שיה, וייתכן שלא יתרחשו במקום הזה כללי הדקדוק (כלומר,
יכול להיות בו פעם אחת או לא פעם אחת);

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

□ פסיק משמש להפרדת מחרוזות של תווים בתוך סיבוב
סוֹגְרַיִם;

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

כך אמורים להיראות הכללים של דקדוק G שנדונו לעיל אם הם נכתבים באמצעות מטא-תווים:

<число> -» [(+.-)]<цифра>{<цифра>}

<цифра> ->0|1|2|3|4|5|6|7|8|9

השורה השנייה של הכללים אינה זקוקה להערות, והכלל הראשון כתוב כך: "מספר הוא מחרוזת תווים שעשויה להתחיל בתווים + או -, חייבת להכיל ספרה אחת נוספת, שעשויה להיות אחריה רצף של כל מספר של ספרות." בניגוד לצורת Backus-Naur, בצורת כתיבה בעזרת מטא-תווים, כפי שניתן לראות, ראשית, סמל לא-טרמינלי לא ברור מוסר מהדקדוק<чс>, ושנית, ניתן היה לבטל לחלוטין את הרקורסיה. דקדוק מובן יותר.

צורת המטא של כללים היא דרך נוחה ומובנת לייצג כללי דקדוק. במקרים רבים, הוא מאפשר לך להיפטר לחלוטין מהרקורסיה, ולהחליף אותה בסמל האיטרציה () (סוגרים מתולתלים). כפי שיתברר מהחומר הבא, צורה זו משמשת לרוב לאחד מסוגי הדקדוקים - דקדוקים רגילים.

כתיבת כללי דקדוק בצורה גרפית

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


הגדרה של דקדוק. טופס Backus-Naur 359

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

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

□ נקודת כניסה (לא מצוינת בשום צורה בתרשים, היא רק מתחילה
קשת הקלט של הגרף);

□ סמל לא מסוף (מסומן על ידי מלבן בתרשים, שבו
ייעוד הסמל מוזן);

□ שרשרת של סמלים סופיים (מסומנים בתרשים באמצעות אליפסה, עיגול
או מלבן עם קצוות מעוגלים, שבתוכו רשום
לְהַנֵץ);

□ נקודת עיגון (מסומנת בנקודה מודגשת או במילוי
מעגל);

□ נקודת יציאה (לא מסומנת בשום צורה, היא רק כוללת את קשת היציאה של הגרף).

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

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

המחרוזת המתקבלת, בתורה, עשויה להכיל תווים שאינם מסוף. על מנת להחליפם בשרשראות של סמלי טרמינלים, יש לשקול שוב את התרשימים התואמים להם. וכך הלאה עד שרק דמויות מסוף נשארות בשרשרת. ברור שכדי לבנות שרשרת של סמלים של שפה נתונה, יש להתחיל בתרשים של סמל המטרה של הדקדוק.

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



פרק 9


השיטה די פשוטה. ניתן לראות זאת בקלות אם מסתכלים על התיאור של המושג "מספר" מהדקדוק G באמצעות הדיאגרמות באיור. 9.1.

אורז. 9.1. ייצוג גרפי של הדקדוק של מספרים עשרוניים שלמים עם סימן: בחלק העליון - למושג "מספר"; למטה - למושג "ספרה"

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

סיווג שפות ודקדוקים

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

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


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

סיווג דקדוק.

דקדוק המכיל רקורסיה שמאלית אינו דקדוק LL(1). קחו בחשבון את הכללים

אaa(רקורסיה שמאלה ב-A)

אא

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

אלִפנֵי הַסְפִירָה

בCD

גAE

ניתן להמיר דקדוק המכיל לולאה רקורסיבית שמאלית לדקדוק המכיל רק רקורסיה שמאלית ישירה, ובהמשך, על ידי הכנסת חוץ-טרמינלים נוספים, ניתן לבטל לחלוטין את הרקורסיה השמאלית (למעשה, היא מוחלפת ברקורסיה ימנית, אשר אינו מהווה בעיה ביחס ל-LL(1) -מאפיינים).

כדוגמה, שקול דקדוק עם כללים מחוללים


סaa

אbb

בcc

גדד

גה

דאז


שיש לו לולאה רקורסיבית שמאלית המערבת א ב ג ד. כדי להחליף לולאה זו ברקורסיה שמאלית ישירה, אנו מזמינים את הלא-טרמינלים באופן הבא: S, A, B, C, D.

שקול את כל כללי היצירה של הטופס

שיXj γ,

איפה שיו Xjאינם מסופים, ו γ – מחרוזת של תווים מסוף ותווים שאינם מסוף. לגבי הכללים לגביו j ≥ i, לא ננקטת כל פעולה. עם זאת, אי שוויון זה לא יכול להחזיק בכל הכללים אם יש לולאה רקורסיבית שמאלית. לפי הסדר שבחרנו, אנו עוסקים בכלל אחד:

דאז

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

דbbz

בגלל ה בקדם דבהזמנה, התהליך חוזר על עצמו, נותן את הכלל:

דccbz

לאחר מכן הוא חוזר על עצמו פעם נוספת ונותן שני כללים:

דecbz

דDdcbz

כעת הדקדוק המומר נראה כך:

סaa

אbb

בcc

גדד

גה

דDdcbz

דecbz

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

דecbz

דDdcbz

דecbz

דecbzZ

זdcbz

זdcbzZ

שימו לב שלפני ואחרי השינוי דיוצר ביטוי רגולרי

(ecbz) (dcbz)*

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

א 1, א 2,..., אר

אβ 1, אβ 2,..., אβ ס

אז ניתן להחליף את הכללים האלה בתנאים הבאים:

הוכחה לא רשמית היא שלפני ואחרי השינוי איוצר ביטוי רגולרי ( β 1 | β 2 |... | β s) ( α 1 | α 2 |... | α ר)*

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

17. המרת דקדוקים לצורת LL(1). פירוק לגורמים.

פירוק לגורמים

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

פ→ התחל ד; עםסוֹף

דד, ד

דד

עםס; עם

עםס

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

פ→ התחל ד; עםסוֹף

דד X איקס)

איקס→ , ד(על פי הכלל הראשון עבור דדקדוק מקורי עבור דעוקב, ד)

איקסε (על פי הכלל השני עבור דדקדוק מקורי עבור דכלום (מחרוזת ריקה)

עםs י(אנו מציגים עוד לא טרמינלי י)

י→ ; עם(על פי הכלל הראשון עבור גדקדוק מקורי עבור סעוקב; ג)

יε (על פי הכלל השני עבור גדקדוק מקורי עבור סכלום (מחרוזת ריקה)

באופן דומה, יצירת כללים

סaSb

סaSc

סε

ניתן להמיר על ידי פירוק לגורמים לכללים

סaSX

סε

איקסב

איקסג

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


1. פQx

2. פרי

3. שמ"ר

4. שש

5. רsRn

6. רר


שתי הקבוצות של תווי מדריך לשתי אפשרויות פלְהַכִיל ס, ומנסה "לסבול סמחוץ לסוגריים", אנו מחליפים שו רבחלקים הנכונים של כללים 1 ו-2:


פsQmx

פsRny

פqx

פry


ניתן להחליף את הכללים הבאים:


פqx

פry

פsp 1

פ 1 → Qmx

פ 1 → רני


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


פ 1 → sQmx

פ 1 → qmx

פ 1 → sRny

פ 1 → רני


פקטורינג, אנחנו מבינים