ניתוח לקסיקלי ו- Java: חלק 1

ניתוח וניתוח לקסיקלי

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

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

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

המנתחים המילוניים של ג'אווה

מפרט שפת Java, גרסה 1.0.2, מגדיר שתי מחלקות מנתח לקסיקליות, StringTokenizerו- StreamTokenizer. משמותיהם ניתן להסיק StringTokenizerהמשתמש Stringבאובייקטים כקלטו StreamTokenizerומשתמש InputStreamבאובייקטים.

כיתת StringTokenizer

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

כמנתח לקסיקלי, StringTokenizerניתן להגדירו רשמית כמוצג להלן.

[~ delim1, delim2, ..., delim N ] :: אסימון

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

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

היישומון שלהלן הוא StringTokenizerמתאמן פשוט . המקור ליישומון StringTokenizer נמצא כאן. כדי להשתמש ביישומון, הקלד טקסט כלשהו שיש לנתח באזור מחרוזת הקלט, ואז הקלד מחרוזת המורכבת מדמויות מפרידות באזור מחרוזת הפרדה. לבסוף, לחץ על ה- Tokenize! לַחְצָן. התוצאה תופיע ברשימת האסימונים מתחת למחרוזת הקלט ותארגן כאסימון אחד בכל שורה.

אתה זקוק לדפדפן המותאם ל- Java כדי לראות יישומון זה.

קחו כדוגמה מחרוזת, "a, b, d", שהועברה StringTokenizerלאובייקט שנבנה עם פסיק (,) כתו המפריד. אם תשים ערכים אלה ביישומון המתאמן לעיל תראה Tokenizerשהאובייקט מחזיר את המיתרים "a", "b" ו- "d". אם הכוונה שלך הייתה לציין שפרמטר אחד חסר, ייתכן שהופתעת שלא תראה שום אינדיקציה לכך ברצף האסימונים. היכולת לזהות אסימונים חסרים מופעלת על ידי הבוליאן Return Separator שניתן להגדיר בעת יצירת Tokenizerאובייקט. כאשר פרמטר זה מוגדר בעת Tokenizerבנייתו, מוחזר גם כל מפריד. לחץ על תיבת הסימון עבור מפריד החזרה ביישומון שלמעלה, והשאיר את המחרוזת והמפריד לבד. עכשיו הTokenizerמחזיר "a, פסיק, ב, פסיק, פסיק וד '." על ידי ציון שאתה מקבל שתי תווי מפריד ברצף, אתה יכול לקבוע כי אסימון "null" נכלל במחרוזת הקלט.

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

/ ** * נתח פרמטר של הטופס "10,20,30" כ- TGB של RGB לערך צבע. * / 1 getColor צבע (שם מחרוזת) {2 נתוני מחרוזת; 3 StringTokenizer st; 4 int אדום, ירוק, כחול; 5 6 נתונים = getParameter (שם); 7 אם (נתונים == null) 8 להחזיר null; 9 10 st = StringTokenizer חדש (נתונים, ","); 11 נסה {12 אדום = Integer.parseInt (st.nextToken ()); 13 ירוק = Integer.parseInt (st.nextToken ()); 14 כחול = Integer.parseInt (st.nextToken ()); 15} לתפוס (חריג ה) {16 להחזיר אפס; // (ERROR STATE) לא הצליחה לנתח אותה 17} 18 להחזיר צבע חדש (אדום, ירוק, כחול); // (END STATE) נעשה. 19}

הקוד שלמעלה מיישם ניתוח פשוט מאוד שקורא את המחרוזת "מספר, מספר, מספר" ומחזיר Colorאובייקט חדש . בשורה 10, הקוד יוצר StringTokenizerאובייקט חדש שמכיל את נתוני הפרמטרים (נניח ששיטה זו היא חלק מיישומון), ורשימת תווים מפרידה המורכבת מפסיקים. ואז בשורות 12, 13, 14, כל אסימון מופק מהמחרוזת ומומר למספר בשיטת Integer parseInt. המרות אלה מוקפות try/catchבחסימה למקרה שמחרוזות המספר לא היו מספרים תקפים או Tokenizerשההטלה היא חריגה מכיוון שנגמרו לה האסימונים. אם כל המספרים עוברים המרה, מגיעים למצב הסיום Colorואובייקט מוחזר; אחרת מגיעים למצב השגיאה ומחזירים null .

אחת התכונות של StringTokenizerהכיתה היא שהוא מוערם בקלות. בדוק את השיטה getColorשלמטה, שהיא שורות 10 עד 18 של השיטה הנ"ל.

/ ** * מנתח גוון צבע "r, g, b" Colorלאובייקט AWT . * / 1 getColor צבע (נתוני מחרוזת) {2 int אדום, ירוק, כחול; 3 StringTokenizer st = StringTokenizer חדש (נתונים, ","); 4 נסה {5 אדום = Integer.parseInt (st.nextToken ()); 6 ירוק = Integer.parseInt (st.nextToken ()); 7 כחול = Integer.parseInt (st.nextToken ()); 8} לתפוס (חריג e) {9 להחזיר null; // (שגיאת מדינה) לא הצליחה לנתח אותה 10} 11 להחזיר צבע חדש (אדום, ירוק, כחול); // (END STATE) נעשה. 12}

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

/ ** * לנתח קבוצה של צבעים "r1, g1, b1: r2, g2, b2: ...: rn, gn, bn" לתוך * מערך של אובייקטים בצבע AWT. * / 1 צבע [] getColors (נתוני מחרוזת) {2 וקטור הצטברות = וקטור חדש (); 3 צבעי צבע, תוצאה []; 4 StringTokenizer st = StringTokenizer חדש (נתונים, ":"); 5 בעוד (st.hasMoreTokens ()) {6 cl = getColor (st.nextToken ()); 7 אם (cl! = Null) {8 accum.addElement (cl); 9} אחר {10 System.out.println ("שגיאה - צבע רע."); 11} 12} 13 אם (accum.size () == 0) 14 להחזיר null; 15 תוצאה = צבע חדש [accum.size ()]; 16 עבור (int i = 0; i <accum.size (); i ++) {17 result [i] = (Color) accum.elementAt (i); 18} 19 תוצאה חוזרת; 20}

בשיטה לעיל, השונה רק מעט getColorמהשיטה, הקוד בשורות 4 עד 12 יוצר חדש Tokenizerלחילוץ אסימונים המוקפים בתו המעי הגס (:). כפי שניתן לקרוא בתגובת התיעוד של השיטה, בשיטה זו מצפים כי גבעולי צבע יופרדו על ידי נקודתיים. כל קריאה nextTokenשל StringTokenizerהכיתה תחזור אסימון חדש עד שהחוט מוצה. האסימונים שיוחזרו יהיו מחרוזות המספרים המופרדות באמצעות פסיקים; מחרוזות האסימון הללו מוזנות getColor, ואז מחלץ צבע משלושת המספרים. יצירת StringTokenizerאובייקט חדש באמצעות אסימון שהוחזר על ידי StringTokenizerאובייקט אחר מאפשרת לקוד המנתח שכתבנו להיות קצת יותר מתוחכם לגבי האופן בו הוא מפרש את קלט המחרוזת.

עד כמה שזה שימושי, בסופו של דבר תמצו את יכולות StringTokenizerהכיתה ותצטרכו לעבור לאחיו הגדול StreamTokenizer.

כיתת StreamTokenizer

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

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

  • תווים של חלל לבן - המשמעות הלקסיקלית שלהם מוגבלת להפרדה בין מילים

  • תווי מילים - עליהם להיות מצטברים כאשר הם צמודים לדמות מילה אחרת

  • תווים רגילים - יש להחזיר אותם מיד למנתח

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

מדינה קֶלֶט פעולה מדינה חדשה
לְהִתְבַּטֵל דמות מילה לדחוף את הדמות לאחור לִצְבּוֹר
אופי רגיל דמות החזרה לְהִתְבַּטֵל
אופי חלל לבן לצרוך אופי לְהִתְבַּטֵל
לִצְבּוֹר דמות מילה הוסף למילה הנוכחית לִצְבּוֹר
אופי רגיל

להחזיר את המילה הנוכחית

לדחוף את הדמות לאחור

לְהִתְבַּטֵל
אופי חלל לבן

להחזיר את המילה הנוכחית

לצרוך אופי

לְהִתְבַּטֵל

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

The first example is number processing. Certain character sequences can be interpreted as representing a numerical value. For example, the sequence of characters 1, 0, 0, ., and 0 adjacent to each other in the input stream represent the numerical value 100.0. When all of the digit characters (0 through 9), the dot character (.), and the minus (-) character are specified as being part of the word set, the StreamTokenizer class can be told to interpret the word it is about to return as a possible number. Setting this mode is achieved by calling the parseNumbers method on the tokenizer object that you instantiated (this is the default). If the analyzer is in the accumulate state, and the next character would not be part of a number, the currently accumulated word is checked to see if it is a valid number. If it is valid, it is returned, and the scanner moves to the next appropriate state.

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