ฉันทำงานอย่างช้าๆเพื่อจบปริญญาของฉันและภาคเรียนนี้คือ Compilers 101 เรากำลังใช้Dragon Bookอยู่ ไม่นานในหลักสูตรและเรากำลังพูดถึงการวิเคราะห์คำศัพท์และวิธีการที่จะสามารถนำไปใช้ผ่านทางออปติคัลไฟไนต์ จำกัด (ต่อไปนี้คือ DFA) ตั้งค่าสถานะ lexer ต่างๆกำหนดช่วงการเปลี่ยนภาพเป็นต้น
แต่ทั้งศาสตราจารย์และหนังสือเสนอให้นำพวกมันไปใช้ผ่านตารางการเปลี่ยนแปลงซึ่งมีขนาดเท่ากับอาร์เรย์ 2 มิติขนาดใหญ่ (สถานะที่ไม่ใช่เทอร์มินัลต่างๆเป็นมิติเดียวและสัญลักษณ์อินพุตที่เป็นไปได้อื่น ๆ ) และคำสั่งสวิตช์ เช่นเดียวกับส่งไปยังตารางการเปลี่ยนแปลงหากอยู่ในสถานะที่ไม่ใช่ขั้ว
ทฤษฎีนี้เป็นสิ่งที่ดีและดี แต่ในฐานะคนที่เขียนโค้ดจริง ๆ มาหลายสิบปี มันไม่สามารถทดสอบได้มันไม่สามารถบำรุงรักษาได้ไม่สามารถอ่านได้และมันเป็นความเจ็บปวดและครึ่งหนึ่งในการแก้ไขข้อบกพร่อง ยิ่งไปกว่านั้นฉันยังไม่เห็นว่ามันจะเป็นประโยชน์จากระยะไกลได้อย่างไรหากภาษานั้นมีความสามารถใน UTF การมีรายการตารางการเปลี่ยนแปลงนับล้านรายการต่อรัฐที่มินัลนั้นได้รับความไม่รีบร้อน
ดังนั้นการจัดการคืออะไร? เหตุใดหนังสือที่ชัดเจนถึงเรื่องที่บอกว่าทำแบบนี้?
ฟังก์ชั่นโอเวอร์เฮดเรียกได้มากขนาดนั้นจริงเหรอ? นี่เป็นสิ่งที่ใช้งานได้ดีหรือจำเป็นเมื่อไวยากรณ์ไม่ทราบล่วงหน้า (นิพจน์ทั่วไป?) หรือบางทีสิ่งที่จัดการกับทุกกรณีแม้ว่าวิธีแก้ปัญหาที่เฉพาะเจาะจงมากขึ้นจะทำงานได้ดีขึ้นสำหรับไวยากรณ์ที่เฉพาะเจาะจงมากขึ้น?
( หมายเหตุ:เป็นไปได้ที่ซ้ำกัน " ทำไมใช้วิธีการ OO แทนคำสั่งสวิตช์ขนาดใหญ่? " อยู่ใกล้ แต่ฉันไม่สนใจ OO วิธีการใช้งานหรือแม้แต่วิธีการเตือนแบบ saner ด้วยฟังก์ชันแบบสแตนด์อโลนก็ดี)
[a-zA-Z]+
และเพื่อเห็นแก่ตัวอย่างให้พิจารณาภาษาที่มีเพียงตัวบ่งชี้และตัวบ่งชี้เหล่านั้น ในการติดตั้ง DFA คุณจะได้รับ:
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;
}
(แม้ว่าสิ่งที่จะจัดการกับจุดสิ้นสุดของไฟล์อย่างถูกต้อง)
เปรียบเทียบกับสิ่งที่ฉันคาดหวัง:
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
refactored ออกเป็นฟังก์ชั่นของตัวเองเมื่อคุณมีหลายปลายทางจากจุดเริ่มต้นของ DFA