Sözcüksel Çözümleme/Analiz (Lexical Analysis) http://www.fatih.edu.tr/~zorhan/bilm204/bilm204.html
Bir Derleyicinin Genel Yapısı Sözcüksel Çözümleme/Analiz (Lexical Analysis) Gramer İncelemesi veya Sözdizimsel Analiz (Parsing) Anlamsal Analiz (Semantic Analysis) Optimizasyon Kod Üretme (Code Generation) İlk üç madde insanların dil anlama yöntemine benzer şekilde anlaşılabilir.
if x == y then z = 1; else z = 2; Tokenlarımız şunlar olur: Sözcüksel Analiz Sözcüksel Analiz işlemi kaynak programı sözcüklere (words), ya da başka bir deyişle sembollere (tokens) ayırır. if x == y then z = 1; else z = 2; Tokenlarımız şunlar olur: if, x, ==, y, then, z, =, 1, ;, else, z, =, 2, ;
Sözdizimsel Analiz Sözcükler anlaşıldıktan sonra, sıra cümle yapısını anlamaya gelir. Sözdizimsel Analiz, cümlenin şekilsel olarak doğru olup olmadığını anlamak için kullanılır.
Programların Sözdizimsel Analizi Örneğin: If x == y then z = 1; else z = 2; if then else cümleciği x y z = 1 z = 2 == beyan ilişki if-ilişki else-beyan then-beyan
Anlamsal Analiz: Semantic Analysis Cümle yapısı anlaşıldıktan sonra, şimdi “anlamın” ne olduğunu çözmeye çalışabiliriz. Ancak derleyiciler için anlamı çözmek çok zordur. Derleyiciler tutarsızlıkları yakalamak için sınırlı bir analiz yaparlar.
Anlamsal Analiz Örneği Derleyiciler değişken bağlamanın(variable bindings) yanında pek çok anlamsal kontrol de yaparlar. Örnek: Ali left her homework at home. Tip uyuşmazlığı(“type mismatch”): her ve Ali arasında; bu iki sözcük farklı kişiler olmalı
Programları otomatik olarak değiştirerek Optimizasyon İngilizce için tam karşılığı olmamakla birlikte redaksiyona(editing) benzemektedir. Programları otomatik olarak değiştirerek Daha hızlı çalışmasını Daha az bellek kaplamasını ve Genel olarak kaynak kullanımında tutumlu davranmayı sağlamaktadır.
X = Y * 0 ifadesi X = 0 şeklinde yazılabilir. Optimizasyon Örneği X = Y * 0 ifadesi X = 0 şeklinde yazılabilir.
Günümüzde Derleyiciler Genel olarak bütün derleyiciler bu yapıya uygunluk gösterir. FORTRAN’dan bu yana oranlar değişmiştir. Önce: sözcüksel analiz, sözdizimsel analiz daha karmaşıktı ve pahalıydı. Bugün: optimizasyon diğer safhalara göre daha önemlidir; sözcüksel analiz, sözdizimsel analiz daha ucuzdur.
Derleyicilerdeki Yönelimler Optimizasyonu hız için kullanmak çok gerekli değildir. Ancak: Bilimsel programlarda İleri işlemcilerde Küçük cihazlarda (hız = daha uzun pil ömrü) gerekebilir.
Sözcüksel analizin kaba taslak anlatımı Biraz Daha Detay… Sözcüksel analizin kaba taslak anlatımı Girdi karakter dizisindeki (input string) sembolleri (token) tanımlar. Sözcüksel analizde ele alınan konular İleriye bakmak (Lookahead) Belirsizlikler (Ambiguities) Sözcük belirleme (Specify lexers/tokens) Düzgün/Düzenli ifadeler Düzgün/Düzenli ifade örnekleri
Hatırlatma: Derleyicinin Yapısı (Anlamsal Analiz Safhası Gösterilmemiştir !) Kaynak Sözcüksel Analiz Tokenlar Bugün başlangıç noktamız Sözdizimsel analiz Aradil (Interm. Dil) Optimizasyon Kod Üret. Makine Kodu
Ne yapmak istiyoruz? Örnek: Sözcüksel Analiz Ne yapmak istiyoruz? Örnek: if (i == j) z = 0; else z = 1; Girdi sadece bir dizi karakterden oluşmaktadır: \tif (i == j)\n\t\tz = 0;\n\telse\n\t\tz = 1; Amaç: Girdi karakter dizisini alt karakter dizilerine bölmektir Ve bunları görevlerine göre sınıflandırmaktır.
Sembol (Token) Nedir? Sözcüksel analiz çıktısı bir dizi semboldür(token) Bir token yapısal bir kategoridir. Türkçe: isim, fiil, sıfat, … Programlama dilleri: Identifier(belirtici), Integer(TamSayı), Keyword(AnahtarKelime), Whitespace, … Sözdizimsel analiz tokenlar arasındaki ayrımı kullanır: Belirticiler anahtar kelimelerden farklı işlenir.
Tokenlar Bir stringtir. Identifier: harfler ve rakamlardan oluşan ve harfle başlayan stringlerdir. Integer: boş olmayan rakam stringidir. Keyword: “else” , “if” , “begin”… Whitespace: boşluklar(blanks), yeni satır(newlines), ve tab ler OpenPar: sol parantez
Sözcüksel Analiz: Uygulama Bir uygulama şunları yapmalıdır: Tokenlara karşılık gelen string alt kümelerini bulmalıdır. AyrıcaTokenın değerini (lexeme) döndürmelidir.
Döndürülen Token-lexeme ikilileri: Örnek \tif (i == j)\n\t\tz = 0;\n\telse\n\t\tz = 1; Döndürülen Token-lexeme ikilileri: (Whitespace, “\t”) (Keyword, “if”) (OpenPar, “(“) (Identifier, “i”) (Relation, “==“) (Identifier, “j”) … A token is a group of characters having a collective meaning. A lexeme is a particular instant of a token. E.g. token: identifier, lexeme: pi, etc.
Sözcüksel Analiz:Uygulama Sözdizimsel analize etkisi olmayan stringleri gözardı eder. Örnek: Whitespace, Açıklama
Lookahead (İleri Bakma) İki önemli nokta: Amaç stringi parçalamaktır. Okuma soldan sağa yapılır ve bir defada bir token bulunur. “Lookahead” bir tokenın nerede bittiği ve diğerinin nerede başladığına karar vermek için gereklidir. Basit örnekte bile lookahead konuları vardır. i ve if = ve ==
Düzgün/Düzenli Diller (Regular Languages) Tokenları belirtmek için farklı yaklaşımlar vardır. Düzgün diller en yaygın olanıdır.
(S bu dilin alfabesidir.) Diller Tanım. S bir dizi karakterdir ve içindeki karakterler belirli kurallarla bir dil oluşturur. (S bu dilin alfabesidir.)
Örnek Diller alfabe= İngilizce karakterler Dil = İngilizce cümleler İngilizce karakterlerden oluşan her string İngilizce bir cümle değildir. alfabe= ASCII karakterleri Dil = C programları
Düzgün İfadeler ve Düzgün Diller Her düzgün ifade bir düzgün dil için notasyon/gösterimdir. Eğer A bir düzgün ifadeyse L(A), A ile gösterilen dili belirtmek için kullanılır.
Düzgün İfadeler ve Düzgün Diller (Devam) Atomik düzgün ifadeler tek bir karakterden oluşur. Tek karakter: ‘c’ L(‘c’) = { “c” } (her c Є ) Arka arkaya Ekleme(Concatenation): AB (A ve B düzgün ifadeler olsun.) L(AB) = { ab | a Є L(A) ve b Є L(B) } Örnek: L(‘i’ ‘f’) = { “if” } ( ‘i’ ‘f’ ‘if’ olarak kısaltılacaktır )
Düzgün İfadeler ve Düzgün Diller (Devam) Bileşim(Union) L(A | B) = { s | s Є L(A) or s Є L(B) } Örnekler: ‘if’ | ‘then‘ | ‘else’ = { “if”, “then”, “else”} ‘0’ | ‘1’ | … | ‘9’ = { “0”, “1”, …, “9” } Başka Bir Örnek: (‘0’ | ‘1’) (‘0’ | ‘1’) = { “00”, “01”, “10”, “11” }
Diğer Bileşik Düzgün İfadeler Tekrarlama (Iteration): A* L(A*) = { “” } U L(A) U L(AA) U L(AAA) U … Örnekler: ‘0’* = { “”, “0”, “00”, “000”, …} ‘1’ ‘0’* = {1 ile başlayıp 0’lar ile devam eden stringler} Epsilon: L() = { “” } * 0 or more of previous expression.
Keyword: “else” or “if” or “begin” or … Örnek: Keyword Keyword: “else” or “if” or “begin” or … ‘else’ | ‘if’ | ‘begin’ | … (Hatırlatma: ‘else’ ‘e’ ‘l’ ‘s’ ‘e’ )
Integer: Boş olmayan rakamlar stringi Örnek: Tam Sayılar Integer: Boş olmayan rakamlar stringi digit = ‘0’ | ‘1’ | ‘2’ | ‘3’ | ‘4’ | ‘5’ | ‘6’ | ‘7’ | ‘8’ | ‘9’ number = digit digit* Kısaltma: A+ = A A* + 1 or more of previous expression.
Identifier: harfler ve rakamlardan oluşan, harfle başlayan stringler. Örnek: Belirtici Identifier: harfler ve rakamlardan oluşan, harfle başlayan stringler. letter = ‘A’ | … | ‘Z’ | ‘a’ | … | ‘z’ identifier = letter (letter | digit) *
Örnek: Whitespace Whitespace: boş olmayan boşluk, yeni satır ve tab lerden oluşan stringler (‘ ‘ | ‘\t’ | ‘\n’)+
Örnek: Telefon Numaraları Düzgün ifadeler aslında çok sık kullanılır! Örneğin (510) 643-1481 (telefon no) = { 0, 1, 2, 3, …, 9, (, ), - } area = digit3 exchange = digit3 phone = digit4 number = ‘(‘ area ‘)’ exchange ‘-’ phone
Örnek: E-posta Adresleri Örneğin: zorhan@fatih.edu.tr harf = {A|B … a|b …} = harf U { ., @ } isim= harf+ adres= isim‘@’ isim(‘.’ isim)+
Özet Düzgün ifadeler pek çok faydalı dil tanımlarlar. Bir sonraki adım: Bir string s ve bir düzgün ifade R verildiğinde sorusuna cevap bulmaktır Ancak sadece evet/hayır cevabı yeterli değildir! Düzgün ifadeler bu amaç için kullanılacaktır.
Düzgün İfadeler => Sözcüksel Tanımlama. (1) Bir küme token seçin Number, Keyword, Identifier, ... Her bir token için bir düzgün ifade yazın Number = digit+ Keyword = ‘if’ | ‘else’ | … Identifier = letter (letter | digit)* OpenPar = ‘(‘ …
Düzgün İfadeler => Sözcüksel Tanımlama. (2) 3. R dilini bütün tokenları kapsayacak şekilde yazın. R = Keyword | Identifier | Number | … = R1 | R2 | R3 | … If s Є L(R) s bir tokenın lexeme değeridir. Dahası s Є L(Ri) (belirli bir “i” için) Bu “i” bulunan tokenın hangisi olduğunu gösterir.
R = Whitespace | Integer | Identifier | ‘+’ Örnek R = Whitespace | Integer | Identifier | ‘+’ “f +3 +g” stringini çözümleyin “f” Identifier’a dolayısıyla R’ye uyar “+“ “+” ya dolayısıyla R’ye uyar … token-lexeme ikilileri ise (Identifier, “f”), (‘+’, “+”), (Integer, “3”) (Whitespace, “ “), (‘+’, “+”), (Identifier, “g”) olur
Belirsizlikler (1) Algoritmada belirsizlikler vardır. Örnek: R = Whitespace | Integer | Identifier | ‘+’ “foo+3” çözümleyin “f” Identifier’a uyar Ama aynı zamanda “fo” Identifier ’a uyar, hatta “foo” da Identifier’a uyar, ama “foo+” Identifier’a uymaz Eğer Hem x1…xi L(R) ve hem de x1…xK L(R) doğru ise ne kadar girdi parçası kullanılır? “Maximal munch(en büyük lokma)” kuralı: R’a uyan mümkün olan en uzun stringi seçin
Daha Başka Belirsizlikler R = Whitespace | ‘new’ | Integer | Identifier “new foo” stringini çözümleyin “new” ‘new’ e uyar Aynı zamanda Identifier’a da uyar, hangisini seçeceğiz? Genelde, eğer x1…xi L(Rj) ve x1…xi L(Rk) ise Kural: Önce yazılanı kullanmaktır (Yani j < k ise j seçilir) Dolayısıyla ‘new’ Identifier’dan önce yazılmalıdır
Hataları Ele Alma R = Whitespace | Integer | Identifier | ‘+’ “=56” : çözümleyin Hiçbir önek(prefix) R’a uymamaktadır: ne “=“, ne “=5”, ne de “=56” Problem: İşin içinden çıkılamadı denemez … Çözüm: Son kural olarak bütün uygun olmayan stringlere uyacak bir kural eklenir. Lexer araçları şu şekilde bir gösterime izin verir: R = R1 | ... | Rn | Error Token Error diğer tokenlardan hiçbirine uygunluk çıkmazsa kullanılır.
Özet (1) A compiler is a translator whose source language is a high-level language and whose object language is close to the machine language of an actual computer. The typical compiler consists of several phases each of which passes its output to the next phase. The lexical phase (scanner) groups characters into lexical units or tokens. The input to the lexical phase is a character stream. The output is a stream of tokens. Regular expressions are used to define the tokens recognized by a scanner (or lexical analyzer). The scanner is implemented as a finite state machine. Lex and Flex are tools for generating scanners: programs which recognize lexical patterns in text. Flex is a faster version of Lex. Given an input file with the lexer rules, it will produce a C file with an implementation of a lexer for those rules. You can thus check the output of flex for how to write a lexer in C. That is, if you don't just want to use flex's lexer...
Özet (2) The parser groups tokens into syntactical units. The output of the parser is a parse tree representation of the program. Context-free grammars are used to define the program structure recognized by a parser. The parser is implemented as a push-down automata. Yacc and Bison are tools for generating parsers: programs which recognize the grammatical structure of programs. Bison is a faster version of Yacc.