Derecemi bitirmek için yavaşça çalışıyorum ve bu dönem Derleyiciler 101. Ejderha Kitabını kullanıyoruz . Derse kısa bir süre sonra, sözcüksel analiz ve deterministik sonlu otomatalar (bundan sonra DFA) ile nasıl uygulanabileceğinden söz ediyoruz. Çeşitli lexer durumlarınızı ayarlayın, aralarındaki geçişleri tanımlayın, vb.
Ancak hem profesör hem de kitap, onları dev bir 2d dizisine (çeşitli boyutlarda olmayan terminal durumları ve bir boyut gibi muhtemel giriş sembolleri) ve diğer tüm terminalleri idare edebilmek için olası giriş sembollerine denk gelen geçiş tabloları aracılığıyla uygulamayı önermektedir. terminal olmayan bir durumda ise geçiş tablolarına gönderilir.
Teori gayet iyi ve iyidir, ancak onlarca yıldır kod yazmış biri olarak, uygulama aşağılıktır. Test edilebilir değildir, bakımı yapılamaz, okunaklı değildir ve bu hata ayıklamak için bir acı ve bir buçuk. Daha da kötüsü, dilin UTF özelliğine sahip olması durumunda nasıl uzaktan uygulanabileceğini göremiyorum. Terminal olmayan her devlet için bir milyondan fazla geçiş tablosu girişi olması aceleyle ağırlaşıyor.
Peki anlaşma nedir? Konuyla ilgili kesin kitap neden bu şekilde yapmayı söylüyor?
Fonksiyon ek yükü gerçekten bu kadar mı çağırıyor? Bu iyi çalışan bir şey mi yoksa dilbilgisi vaktinden önce bilinmediğinde gerekli mi (normal ifadeler?)? Veya daha spesifik çözümler daha spesifik gramerler için daha iyi çalışsa bile, belki de tüm vakaları ele alan bir şey?
( not: olası yinelenen " Neden dev bir anahtar ifadesi yerine bir OO yaklaşımı kullanmalıyım? " yakın, ancak OO'yu umursamıyorum. İşlevsel bir yaklaşım veya hatta bağımsız işlevlere sahip olan zorunlu bir yaklaşım yaklaşımı iyi olabilir.)
Ve örnek olarak, sadece tanımlayıcıları olan ve bu tanımlayıcıları olan bir dili düşünün [a-zA-Z]+
. DFA uygulamasında, şöyle bir şey elde edersiniz:
private enum State
{
Error = -1,
Start = 0,
IdentifierInProgress = 1,
IdentifierDone = 2
}
private static State[][] transition = new State[][]{
///* Start */ new State[]{ State.Error, State.Error (repeat until 'A'), State.IdentifierInProgress, ...
///* IdentifierInProgress */ new State[]{ State.IdentifierDone, State.IdentifierDone (repeat until 'A'), State.IdentifierInProgress, ...
///* etc. */
};
public static string NextToken(string input, int startIndex)
{
State currentState = State.Start;
int currentIndex = startIndex;
while (currentIndex < input.Length)
{
switch (currentState)
{
case State.Error:
// Whatever, example
throw new NotImplementedException();
case State.IdentifierDone:
return input.Substring(startIndex, currentIndex - startIndex);
default:
currentState = transition[(int)currentState][input[currentIndex]];
currentIndex++;
break;
}
}
return String.Empty;
}
(yine de dosyanın sonunu doğru halledecek bir şey)
Beklediğim ile karşılaştırıldığında:
public static string NextToken(string input, int startIndex)
{
int currentIndex = startIndex;
while (currentIndex < startIndex && IsLetter(input[currentIndex]))
{
currentIndex++;
}
return input.Substring(startIndex, currentIndex - startIndex);
}
public static bool IsLetter(char c)
{
return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
}
NextToken
DFA'nın başından itibaren birden fazla varış noktanız olduğunda , kod tekrar kendi işlevine eklendiğinde.