Download presentation
Presentation is loading. Please wait.
Published byΛωΐς Ρόκας Modified over 6 years ago
1
תורת הקומפילציה הרצאה 1 מבוא; ניתוח לקסיקלי
2
תורת הקומפילציה 236360 מרצה: ד"ר שירלי גינסברג מתרגלים
שעת קבלה – יום שני 9:30-10:30 בתיאום מראש, טאוב 318 מתרגלים קירה רדינסקי, מתרגלת אחראית גלעד קותיאל רן צמח
3
אדמיניסטרציה אתר: http://webcourse.cs.technion.ac.il/236360
25% - תרגילי בית 5% תרגילים "יבשים", רשות (מגן) 20% תרגילים "רטובים", חובה 75% - מבחן סוף הקורס ציון נכשל במבחן גורר כשלון בקורס, ללא קשר לציון התרגילים. קדם: אוטומטים ושפות פורמליות מועד ג' למילואימניקים (בלבד). העתקות...
4
ספרות ספר עיקרי ספר משני
A.V. Aho, M. S. Lam, R. Sethi, and J.D. Ullman – “Compilers – Principles, Techniques, and Tools”, Addison-Wesley, 2007 ספר משני R. Wilhelm, and D. Maurer – “Compiler Design”, Addison-Wesley, 1995
5
ספרות-המשך ספר נוסף K.D.Cooper and L.Torczon
“Engineering A Compiler”, Morgan Kaufmann, 2004
6
פרס טיורינג March 10, 2009: ACM has named Barbara Liskov the recipient of the 2008 ACM A.M. Turing Award for her contributions to practical and theoretical foundations of programming language and system design.
7
מורכבות משלב תחומים רבים ממדעי המחשב משלב מחקר תיאורטי ומעשי
קומפילציה שפות תכנות הנדסת תכנה מבנה מחשבים מערכות הפעלה שפות פורמליות אלגוריתמים מבני נתונים משלב תחומים רבים ממדעי המחשב משלב מחקר תיאורטי ומעשי
8
קומפילציה (הידור) – מושגי יסוד
זמן קומפילציה זמן ריצה compiler source program target program target program input output
9
אינטרפרטציה )= פרשנות(
source program interpreter output input שימושים: שפות סקריפט שבהם כל שורה קוראת לביצוע של משהו מורכב (אין טעם ליעל) שפות מאד גבוהות (APL, Mathlab) שבהן הנתונים מורכבים ואינם ידועים בעת הקומפילציה
10
דוגמא למבנה מודרני חשוב - Java
source program Bytecode is standard, machine-independent Translator (javac) Intermediate program (bytecode) Java Virtual Machine output input Virtual machine- תלוי מכונה. מבצע טוב כפי יכולתו על המכונה הנתונה. Just In Time -- תוך כדי פרשנות התכנית, ה-interpreter מבצע קומפילציה של חלקי תוכנית על מנת שהמשך הביצוע יהיה מהיר יותר.
11
הידור / אינטרפרטציה – הכללות
Source to Source Virtual Machine Pre-processors C ++ program translator C program compiler Java program Java bytecode program with embedded pre-pocessing statements (e.g., #include, macros) preprocessor “pure” program
12
שימושים של קופיילרים יעד קוד מכונה: SPARC, P690, IA32 שפת אסמבלי
קוד עבור אינטרפרטר: Java Virtual Machine, P-Code, … שפות עבוד טקסט: PostScript, TeX, html, RTF, … תוכנה למכשור מעגל חשמלי קלט שפות תכנות:C, Pascal, Assembler,... שפות לעיבוד טקסט: PostScript, TeX, html, RTF,… שפות scripting: C-shell, emacs, perl, Hypercard… שפות שאילתה לעבוד נתונים (SQL) שפות לתיאור חומרה (VHDL) שפות בקרה
13
כלים נוספים לעיבוד שפה – דוגמא
Skeletal source program Preprocessor Source program Target assembly program Assembler Relocateable machine code Loader/Link-editor Absolute machine code איסוף מודולים, פתיחת מקרואים. compiler כתובות יחסיות, הפניות לספריה Library, releasable, object files כתובות יחסיות, לכל התוכנית Link: כתובות אבסולוטיות Load:
14
קומפילציה – חשיבות התחום
לפיתוח שמושים מתקדמים ניצול של כלים לפיתוח קומפיילרים להקלת מאמץ הפיתוח קריאה (ו"הבנה") אוטומטית של קבצי קלט למפתחי תוכנה הבנה של האבחנה בין זמן קומפילציה לזמן ריצה הבנה של מה יעשה קומפיילר עם התוכנה שלכם (שיפור זמן ריצה, התראות של שגיאות) שימוש נכון במבנים שונים של שפות התכנות ניצול נכון של ארכיטקטורת המחשב
15
קומפילציה – חשיבות התחום
לאנשי שפות תכנות תמיכה יעילה בשפה חדשה לאנשי ארכיטקטורה של מחשבים הבנה טובה של האיזון העדין שבין חומרה לתכנה הבנה מעמיקה מה יעשה קומפיילר (כלומר: מה תעשה כל תוכנה) עם החומרה שלכם
16
תורת הקומפילציה – תוכן הקורס
מבוא מבנה הקומפיילר ניתוח מילוני (lexical analysis) ניתוח תחבירי (parsing) ניתוח סמנטי יצירת קוד סביבת זמן הריצה (טיפול ברוטינות, ניהול זיכרון, וכו') נושאים מתקדמים: אופטימיזציות, ניתוח סטטי, Data-flow analysis, לא נדבר על: קומפיילרים Just-In-Time ו-Virtual Machines,
17
מבנה הקומפיילר – תמונה כללית
Wilhelm and Maurer – Chapter 6 Aho, Sethi, and Ullman – Chapter 1 Cooper and Torczon – Chapter 1
18
קומפיילר – כקופסא שחורה
int a, b; a = 2; b = a*2 + 1; מתרגם משפת המקור לשפת היעד תוכנית זהה סמנטית (עושה אותו דבר) כיוון שהתירגום מסובך, חילקו אותו לשלבים מודולריים סטנדרטיים. source code target code SET R1,2 STORE #0,R1 SHIFT R1,1 STORE #1,R1 ADD R1,1 STORE #2,R1
19
קומפיילר – חלוקה גסה תוכנית מקור analysis Front End יצוג ביניים
תוכנית מטרה analysis Front End יצוג ביניים code optimization ייצוג ביניים Back End code generation
20
מרכיבי הקומפיילר – חלוקה עדינה
Character Stream Intermediate Representation Lexical Analyzer Machine-Independent Code Optimization Token Stream Syntax Analyzer Intermediate Representation Syntax Tree Code Generator Semantic Analyzer Target Machine Code Decorated Syntax Tree Machine-Dependent Code Optimization Intermediate Code Generator Target Machine Code
21
מרכיבי הקומפיילר – Front End
Character Stream קל, טיפול בביטויים רגולריים Lexical Analyzer Token Stream יותר מסובך, רקורסיבי, עבודה עם דקדוק חסר הקשר. Syntax Analyzer Syntax Tree יותר מסובך, רקורסיבי, דורש עליה וירידה בעץ של הגזירה. Semantic Analyzer Decorated Syntax Tree השטחת העץ. חלק האופטימיזציות נח לעשות על עץ, וחלק על קוד Intermediate Code Generator
22
דוגמה לתפקידי החלקים
23
דוגמה לתפקידי החלקים
24
טבלת הסמלים מבנה נתונים לכל identifier. שומר את התכונות שלו (attributes) ומאפשר גישה מהירה. לדוגמא: מיקום בזיכרון, טיפוס, scope. הטבלה נבנית במהלך הניתוח. למשל: הניתוח הלקסיקלי לא מגלה מה הטיפוס. רק השלב של ייצור הקוד יודע את מיקום המשתנה בזיכרון, וכיו"ב.
25
הודעות שגיאה חלק חשוב של תהליך הקומפילציה
כל שלב מגלה שגיאות ברמת הניתוח שלו של התוכנית. מודיע על השגיאה ומנסה כמיטב יכולתו להמשיך בקומפילציה
26
יתרון חשוב של מודולריות
... שפה ב שפה א analysis n analysis 2 analysis 1 machine independent optimization machine dependent optimization code generation machine dependent optimization code generation … מכונה m מכונה 1
27
שפת ביניים שפה קלה לייצור, שקל לייצר ממנה קוד אמיתי, ונוחה לעבודה לאופטימיזציה. אנו נשתמש בצורה הנקראת three-address code שבה כל תא בזיכרון יכול לשמש פרמטר לכל פעולה. temp1 := inttoreal(60) temp2 := id3 * temp1 temp3 := id2 + temp2 id1 := temp3
28
שלב האופטימיזציה יצירת קוד הביניים נעשית בצורה אוטומטית.
הקוד ניתן לשיפור temp1 := inttoreal(60) temp2 := id3 * temp1 temp3 := id2 + temp2 id1 := temp3 temp1 := id3 * 60.0 id1 := id2 + temp1
29
machine independent optimization
שלב האופטימיזציה Decorated Syntax Tree machine independent optimization דוגמאות constant propogation common subexpressions dead code elimination
30
אופטימיזציה לא ניתן לייצר קוד אופטימלי (NP-שלם או לא ניתן להכרעה, תלוי מה מנסים לעשות...). לכן משתמשים בהיוריסטיקות. חובה לשמור על נכונות; צריך לשפר (כמעט תמיד); חישוב האופטימיזציה צריך להיות יעיל. שיפורים: מהירות, מקום, אנרגיה, וכו'. החלק שתופס זמן מירבי בתהליך הקומפילציה. השאלה העיקרית: כמה זמן שווה להשקיע באופטימיזציה עבור ייעול תוכנית המטרה. התשובה מאוד משתנה עבור JIT.
31
אופטימיזציה מודרנית האתגר העיקרי הוא לנצל פלטפורמות מודרניות
Multicores פקודות ווקטוריות היררכית זיכרונות האם הקוד מועבר ברשת (Java Bytecode)?
32
שלב הסינתזה ((back-end
decorated syntax tree target program address assignmet code generation peephole optimizer machine dependent optimization
33
ייצור קוד (Code Generation)
שלב אחרון: ייצור קוד של שפת המטרה. משימה חשובה: החלטה על מיקום המשתנים בזיכרון. האתגר העיקרי: לאיזה משתנים יוקצו רגיסטרים באיזה שלב של התוכנית? פעולות על רגיסטרים מהירות משמעותית מפעולות על הזיכרון. השמה אופטימלית למקרה הגרוע ביותר היא קשה (NP-קשה), היוריסטיקות מאד טובות קיימות.
34
ה- back-end, דוגמא int a, b; a = 2; b = a*2 + 1; (front end) a = 2
temp1 = a*2 b = temp1 + 1 address assignment a0 temp1 1 b2 code generation a = 2 temp1 = a << 1 b = temp1 + 1 machine indep. optimization SET R1,2 STORE #0,R1 LOAD R1,#0 SHIFT R1,1 STORE #1,R1 LOAD R1,#1 ADD R1,1 STORE #2,R1 SET R1,2 STORE #0,R1 SHIFT R1,1 STORE #1,R1 ADD R1,1 STORE #2,R1 machine-dependent optimization
35
כלים חלק משמעותי מהמאמץ בכתיבת קומפיילרים חוזר על עצמו בין קומפיילרים שונים. כלים מיועדים לחסוך את החלק החוזר. Parser generators: חלקם מתבססים על אלגוריתמים מורכבים. Lexical generator (עבור ניתוח לקסיקאלי) Syntax-directed translation engines מעבר על העץ האבסטרקטי ויצירת קוד ביניים ממנו ע"י ביצוע פעולה על כל צומת. Data Flow Engines עבור אופטימיזציה.
36
לסיכום קומפיילר מתרגם משפת מקור לשפת יעד (ובדרך מספק בדיקת שגיאות חשובה). על מנת להקטין את הסיבוכיות התירגום מחולק למודולים ברורים ונפרדים. שלב ה-front-end מביא את התוכנית לייצוג ביניים שנוח לעבוד איתו באופן אוטומטי. שלב ה-back-end מבצע אופטימיזציות ומייצר קוד של שפת המטרה. בהמשך – נתחיל בהבנת ה-front-end.
37
ניתוח לקסיקלי Wilhelm, and Maurer – Chapter 7
Aho, Sethi, and Ullman – Chapter 3 Cooper and Torczon – Chapter 2
38
front-end שלב הניתוח תוכנית מקור Back end Lexical analysis
token string symbol table error messages syntax analysis syntax tree semantic analysis decorated syntax tree בקומפיילרים רבים כל השלבים של ה-front-end משולבים זה בזה בקומפיילרים רבים ייצוג הביניים הוא decorated syntax tree
39
מנתח לקסיקלי – אינטרקציה עם ה-parser
תוכנית מקור Lexical Analyzer error message manager get next token token parser
40
מדוע מפרידים ניתוח לקסיקלי?
פישוט הניתוח הלקסיקלי (והגדרת השפה) ופישוט הניתוח הסינטקטי מודולריות שימוש חוזר יעילות – ההפרדה מקלה לממש אלגוריתמים יעודיים יעילים.
41
מושגים בסיסיים lexeme = לקסמה (לפעמים נקרא גם symbol): סדרת אותיות המופרדת משאר התוכנית באופן מוסכם (למשל ע"י רווח או נקודה). לדוגמא:counter . Pattern: כלל המגדיר אוסף של מחרוזות. (לדוגמא: כל המחרוזות שמתחילות באות וממשיכות באות או מספר). token = אסימון: זוג של שם ותכונות. למשל: (identifier, {name=“counter”,code_location=3}). שם ה-token נקבע עפ"י ה-pattern שהלקסמה מקיימת.
42
תפקידי ה- lexical analyzer
קריאת הקלט, והפרדתו ליחידות לקסיקליות (לקסמות) דוגמאות: integer, real, boolean, string , שמות משתנים, הערות, סימנים מיוחדים (למשל =<). זיהוי ה-patterns, מיונם ויצירת tokens עם שמות ותכונות. טיפול ב- keywords -- מילות מפתח שאינן שמורות, זיהוי reserved words, לפעמים מממש גם preprocessing, (include files ו- macros). ספירת מספר שורות (למה?) דיווח על שגיאות: symbols לא חוקיים. הדפסת פלט (התוכנית בשילוב הודעות שגיאה), עשוי לכלול pretty printing. (יש לעקוב אחרי מספרי השורות והעמודות בהם מופיעים ה- tokens)
43
Typical Tokens (Patterns)
Token אחד לכל מילה שמורה בשפה (למשל if, else, וכיו"ב) Token אחד לכל אופרטור. לפעמים הם ארוזים במשפחות. למשל משפחת ההשוואות: comparisons = < or > or <= or >= or == or != . Token אחד המייצג את כל שמות המשתנים (identifier). למשל: counter, score, employee. Token אחד או יותר המייצגים קבועים (בד"כ מספרים ומחרוזות). למשל: 35, 27.34, 23e5, “Billie Holiday” . Token אחד לכל סימן פיסוק. למשל: ( ) , ; { } .
44
משפחות של סמלים (Patterns)
symbol class תוכנית המשתמש ID foo n_14 last NUM REAL e67 5.5e-10 IF if COMMA , NOTEQ != LPAREN ( RPAREN )
45
טיפול מיוחד במחרוזות שאינן סמלים
תוכנית המשתמש comment /* ignored */ preprocessor directive #include <foo.h> macro #define NUMS 5, 6 white space \t \n
46
תכונות תכונות של token ישמשו לשלבים הבאים של הקומפילציה או להודעות שגיאה. token נפוץ לדוגמא הוא שם משתנה (identifier). תכונות סטנדרטיות שנרצה לשמור עבורו יכללו: שם המשתנה (הלקסמה) מיקום המשתנה בתוכנית (עבור הודעות שגיאה) טיפוס המשתנה התכונות האלו נשמרות בד"כ בטבלת הסמלים, ולכן ב-token מספיק לרשום את מספר הכניסה בטבלה.
47
מעבר מלקסמות ל-tokens
48
דוגמא עבור הביטוי e = m * c ** 2 :, נעביר את ה-tokens הבאים:
< id , pointer to symbol table entry for e > < assignment > < id , pointer to symbol table entry for m> < multiplication > < id , pointer to symbol table entry for c> < exponentiation > < number , integer value 2 >
49
הקושי ב-lexical analysis – דוגמאות
בשפת פסקל קל: Const pi = ; ב-Fortran הרווחים אינם נחשבים. לכן ההחלטה על סיום לקסמה יותר מורכבת. למשל, do 5 I = 1.2 אבל: do I = 1 , 2 כאן מודגש הצורך ב- lookahead (שפות מודרניות מתוכננות כך שלא תיווצרנה בעיות כאלו.) ב-Pascal ו- Ada 1. ו אינם נחשבים שברים, כי זה מקשה את הזיהוי של
50
ניסוח הtokens- המותרים בשפה
נרצה דרך סטנדרטית להכריז מהם הסימבולים החוקיים בשפה. לשם כך נשתמש בביטויים רגולריים. אוטומטים המבטאים את הביטויים הרגולריים ישמשו לסריקת הקלט וזיהוי ה-tokens. תזכורת לתיאוריה...
51
אלפבית, מילים, וכו' – אלפבית (דוגמא: ={i,m,b,a} )
מילה מעל היא מחרוזת סופית של אותיות מ- . אורך המילה הוא מספר האותיות בה. דוגמאות: abba, aba, ima, abbabbabba המילה ריקה תסומן ב- 0 – אוסף המילים באורך אפס n – אוסף המילים באורך n *– אוסף כל המילים (הסגור של Kleene) + – אוסף המילים באורך 1 או יותר x.y – מילה המהווה שרשור של x ו- y (בד"כ משמיטים את אופרטור השרשור וכותבים x y) suffix, prefix, substring
52
שפות מעל שפה – תת קבוצה של * דוגמא: L1={ima, aba, bamba} או L2 היא "כל המילים שמכילות את האות a". יהיו L, L1, L2 שפות L1 L2 – איחוד שפות L1 L2 = {x1 x2 | x1 L1, x2 L2 } – שרשור שפות L – המשלים של L (המילים ב-* שאינן ב- L) Ln – שרשור של L לעצמו n פעמים L* – הסגור של L – שרשור L לעצמו מספר כלשהו של פעמים
53
שפות מעל דוגמא: תהי L קבוצת כל האותיות וגם הסימן _ . תהי D קבוצת כל הספרות. אז ניתן לתאר את השמות החוקיים של משתנים בשפת C כ- L(L D)* . עבור תיאור ה-patterns, הביטויים החשובים הם: איחוד, שרשור, וסגור. על-מנת לציין את המחרוזות האפשריות המתקבלות המציאו את מושג הביטויים הרגולריים.
54
ביטויים רגולריים, שפות רגולריות
הוא ביטוי רגולרי מעל המתאר את השפה הריקה הוא ביטוי רגולרי מעל המתאר את השפה L() = { } לכל a , a הוא ביטוי רגולרי המתאר את השפה L(a) = { a } אם pו- q הם ביטויים רגולריים המתארים את השפות הרגולריות P ו- Q, אזי: p | qהוא ביטוי רגולרי המתאר את השפה הרגולרית P U Q p q הוא ביטוי רגולרי המתאר את השפה P Q p* הוא ביטוי רגולרי המתאר את P* ( p ) הוא ביטוי רגולרי שהשפה שלו P (כלומר, מותר לשים סוגריים). הערה: על מנת לפשט את הסימונים, * הוא בעל הקדימות הגבוהה ביותר, אח"כ שרשור, ואח"כ איחוד. ( a | bc* ) כל שפה הניתנת לכתיבה כביטוי רגולרי היא שפה רגולרית.
55
תרגיל תהי Σ={a,b}. L(a|b) = ? L( (a|b) (a|b) ) = ? L( (a|b)* ) = ?
56
דוגמא תהי Σ={a,b}. L(a|b) = {a,b} L( (a|b) (a|b) ) = {aa,ab,ba,bb}
L( (a|b)* ) = {ε, a, b, aa, ab, ba, bb, aaa, aab, aba, …}
57
תיאור symbols באמצאות ביטויים רגולריים
דוגמא: מספר שלם או שבור ( 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ) ( 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 )* ( | . ( 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ) ( | E ( 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ) ( 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 )* ) ) שיטת תיאור זאת קשה לשימוש. לכן נשתמש בשמות של קבוצות.
58
תיאור symbols בעזרת שמות ותחומים
על-מנת לקצר את התיאור נשתמש בשמות לביטויים ונשתמש בהם כאילו היו משתנים רגילים. למשל: letter = a | b | … | z | A | B | … | Z letter_ = letter | _ digit = 0 | 1 | 2 | … | 9 id = letter_ ( letter_ | digit )* נשתמש גם במקף לציין תחום ברור. לדוגמא: letter = a-z | A-Z digit = 0-9 ונשתמש ב- + במקום כוכבית לציין שהסגור מכיל לפחות מופע אחד: binary-number = (0|1)+
59
דוגמאות digit = 0-9 digits = digit+
number = digits (Є | .digits (Є | e (Є|+|-) digits ) ) letter = a-z | A-Z id = letter (letter | digit)* if = if then = then else = else relop = < | > | <= | >= | = | <> ws = ( blank | tab | newline ) - את זה לא נעביר לשלב הבא אלא נתעלם
60
זיהוי ביטויים רגולריים – על ידי אוטומט סופי
אוטומט – , Q , δ, q0 , F ) M = ( – א"ב Q– קבוצה סופית של מצבים q0 Q – מצב התחלתי F Q – קבוצת המצבים הסופיים δ : Q Q פונקצית המעברים state control קלט
61
אוטומט דטרמיניסטי ואי-דטרמיניסטי
, Q , δ, q0 , F ) M = ( הוא אוטומט דטרמיניסטי (סופי) אם: Q → Q : δ היא פונקציה חלקית עבורה: אין מעברי לכל מצב q ואות a יש לכל היותר מעבר למצב יחיד כלומר: לכל מילה w, האוטומט יגיע למצב מסוים יחיד, או ייתקע. קבלה: אם המצב שאליו מגיעים סופי, הקלט "מתקבל". , Q , δ, q0 , F ) M = ( הוא אוטומט אי-דטרמיניסטי (סופי) אם: Q ( {}) → 2Q : δ היא פונקציה חלקית. (ייתכנו מעברי- , ויותר ממעבר אפשרי אחד לאות ומצב נתונים.) לכל מילה w, האוטומט יכול להגיע למספר כלשהו של מצבים או להיתקע. אם אחד מהם סופי, הקלט מתקבל.
62
שיטת העבודה נהפוך את הביטויים הרגולריים לאוטומט אי-דטרמיניסטי
נהפוך את האוטומט האי-דטרמיניסטי לדטרמיניסטי משפט: לכל אוטומט לא דטרמיניסטי קיים אוטומט דטרמיניסטי שקול במקרה הגרוע גודל האוטומט המתקבל אקספוננציאלי בגודל האוטומט המקורי. אך בפועל (עבור זיהוי לקסמות) מתקבל אוטומט סביר. נריץ את האוטומט הדטרמיניסטי על הקלט לזיהוי הביטוי המתאים. באוטומטים למדנו על אוטומטים שרק מזהים קלט (כן או לא)... אבל כשתוכנית מריצה אוטומט על קלט אין סיבה שלא תאסוף מידע בדרך (בפרט, אותיות הקלט שנסקרו עד עתה, מספר השורה בקוד וכיו"ב).
63
מביטויים רגולריים לאוטומט לא דטרמיניסטי
שלב 1: הצב (את סימני הקבוצות כגון 0-9) וקבל סדרת ביטויים רגולריים טהורים R1, R2, …, Rm. שלב 2: בנה אוטומט לא דטרמיניסטי Mi לכל ביטוי רגולרי Ri שלב 3: בנה אוטומט משולב M המזהה את כל הביטויים הרגולריים לתשומת לב: ייתכן שישנם כמה דרכים לזהות את הלקסמה הבאה בתור, אנו נעדיף בד"כ את הארוכה ביותר. למשל thenutcracker כשם משתנה עדיף על then המילה השמורה. אבל באותו אורך, then כמילה השמורה עדיף על then כשם משתנה.
64
סימונים
65
מביטוי רגולרי לאוטומט אי-דטרמיניסטי שקול
66
המצבים ההתחלתיים והמקבלים במכונות המקוריות הופכים למצבים רגילים לאחר הבניה.
69
בהינתן אוטומט לכל pattern, איך נבצע את הניתוח הלקסיקלי ?
70
נרכיב את כל האוטומטים שהתקבלו לאוטומט אחד גדול עם מצב סופי ייחודי לכל pattern אפשרי:
71
שיטה נאיבית: ננסה כל אחד מהאוטומטים.
72
שיטה יותר יעילה היא שילוב כל האוטומטים לאחד, מעבר לאוטומט דטרמיניסטי, והרצה ארוכה ככל האפשר עד שנתקעים. כשנתקעים, זוכרים את המקום האחרון שבו התקבלה מחרוזת ואותו מחזירים. דוגמא:
73
מעבר לאוטומט דטרמיניסטי:
74
דוגמאות:
75
Lookahead הבעיה: לעיתים צריך לסרוק מספר אותיות קדימה על מנת להחליט מהו ה-symbol אותו נחזיר. דוגמא: המילה השמורה IF ב- FORTRAN יכולה להיות גם שם של משתנה. “if(i,j)=3” שונה מ-"if (condition) then …” . תאור lookahead עבור המנתח: אות מיוחדת (למשל - /) המציינת את מקום תחילת ה-lookahead. לזיהוי if נוכל לציין שאנו מצפים ל- "if ( * ) then” אבל רוצים שרק ה-if יוחזר. ונרשום “if / ( * ) then”. הערה: בפסקל ו-ADA מספיק lookahead של 2 אותיות. I F ( ) letter start 1 2 3 4 5 6 any
76
ה-lexical analyzer: מפורמליזם לתוכנה
symbol specification scanner generator scanner scanning table Input program scanner token stream scanning table driver החלק הראשון מבוצע ע"י כותב הקומפיילר, החלק השני הופך להיות השלב הראשון בקומפיילר. יתרונות המפרט – קצר יחסית אפשר לוודא שהמפרט תקין (לא אוטומטית!) התוכנה קלה לתחזוקה לשיטת ה- generation שימושים נוספים בקומפילציה ובשטחים נוספים
77
ה-lexical analyzer: מפורמליזם לתוכנה
תאור ה- token - ביטויים רגולריים מבנה ה- scanner – אוטומט סופי ה-driver הוא תוכנית המריצה אוטומט לפי טבלת המעברים והקלט. ה- scanning table היא טבלת המעברים.
78
תהליך בניית ה-lexical analyzer
בניית תיאור ה-tokens האפשריים כביטויים רגולריים. החלטה על תכונות שיישמרו בעת זיהוי token מכל סוג מעבר לאוטומט דטרמניסטי כולל המתאר את כל הביטויים האפשריים (טבלת מעברים) + שמירת תכונות. נעשה באופן אוטומטי ע"י scanner generator. הפלט של ה-scanner generator משמש את ה-driver של הניתוח הלקסיקלי. ה-driver והטבלאות של השפה הופכים להיות החלק הראשון של הקומפיילר.
79
טיפול בשגיאות בשלב הניתוח הלקסיקלי יש שגיאות רבות שלא ניתן לגלות.
למשל: עבור הביטוי fi ( a == f(x) ) …, קשה לדעת אם fi הוא שיבוש של if, או שם רוטינה. בשלב יותר מאוחר של האנליזה נגלה זאת. בשלב זה פשוט נעביר token עבור identifier. אבל לפעמים יש שגיאה ברורה כבר בשלב זה – כאשר לא ניתן לשייך את הלקסמה שהתגלתה לשום pattern. מה עושים? השיטה הקלה ביותר: לסלק אותיות מהקלט עד שמתגלה התחלה של לקסמה חוקית. אפשרויות נוספות: לסלק אות אחת, להוסיף אות אחת, להחליף אות אחת, להחליף סדר של שתי אותיות סמוכות. המטרה: לאפשר המשך הקומפילציה עם מינימום הפרעה. הקושי – התפשטות השגיאות.
80
סיכום בתרגולים ובתרגיל הבית תכירו כלי אחד כזה: Lex
לרוב השפות ניתן לכתוב ביטויים רגולריים לכל הלקסמות הרלוונטיות. משלבים את כל הביטויים לאוטומט אי-דטרמיניסטי יחיד הופכים את האוטומט לדטרמיניסטי מריצים לגילוי לקסמה אחר לקסמה, אוספים מידע בדרך (למשל שם המשתנה) מודיעים על שגיאות ומנסים לתקן. לרוב שפות התכנות ניתן לבנות מנתח לקסיקלי בקלות. יוצאי דופן: Fortran, PL/1. ישנם כלים שבונים מנתח לקסיקלי בהינתן ביטויים רגולריים. יש להם שימושים גם מחוץ לתחום הקומפיילרים, במקומות בהם דרוש ניתוח אוטומטי של קלטים, למשל, מערכות שאילתות. בתרגולים ובתרגיל הבית תכירו כלי אחד כזה: Lex
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.