อะไรคือข้อดีและข้อเสียเฉพาะของแต่ละวิธีในการใช้งานไวยากรณ์ภาษาโปรแกรม
ทำไม / เมื่อไหร่ที่ฉันควรจะม้วนตัวเอง? ทำไม / เมื่อไรฉันจึงควรใช้เครื่องกำเนิดไฟฟ้า?
อะไรคือข้อดีและข้อเสียเฉพาะของแต่ละวิธีในการใช้งานไวยากรณ์ภาษาโปรแกรม
ทำไม / เมื่อไหร่ที่ฉันควรจะม้วนตัวเอง? ทำไม / เมื่อไรฉันจึงควรใช้เครื่องกำเนิดไฟฟ้า?
คำตอบ:
มีสามตัวเลือกจริงๆทั้งสามตัวเลือกจะดีกว่าในสถานการณ์ที่แตกต่างกัน
สมมติว่าคุณถูกขอให้สร้างเครื่องมือแยกวิเคราะห์สำหรับรูปแบบข้อมูลโบราณบางส่วนในขณะนี้ หรือคุณต้องการโปรแกรมแยกวิเคราะห์ของคุณให้เร็ว หรือคุณต้องการเครื่องมือแยกวิเคราะห์เพื่อให้บำรุงรักษาได้ง่าย
ในกรณีเหล่านี้คุณน่าจะใช้ตัวสร้าง parser ได้ดีที่สุด คุณไม่ต้องไปยุ่งกับรายละเอียดคุณไม่ต้องใช้โค้ดที่ซับซ้อนมากมายในการทำงานอย่างถูกต้องคุณเพียงแค่เขียนไวยากรณ์ที่อินพุตจะยึดติดเขียนโค้ดการจัดการและ presto: ตัวแยกวิเคราะห์แบบทันที
ข้อดีมีความชัดเจน:
มีสิ่งหนึ่งที่คุณต้องระวังด้วยเครื่องมือแยกวิเคราะห์: บางครั้งสามารถปฏิเสธไวยากรณ์ของคุณ สำหรับภาพรวมของประเภทที่แตกต่างกันของ parsers และวิธีการที่พวกเขาสามารถกัดคุณคุณอาจต้องการที่จะเริ่มต้นที่นี่ ที่นี่คุณสามารถดูภาพรวมของการใช้งานจำนวนมากและประเภทของไวยากรณ์ที่พวกเขายอมรับ
เครื่องมือสร้าง Parser นั้นดี แต่พวกเขาไม่ได้เป็นมิตรกับผู้ใช้ โดยทั่วไปคุณจะไม่สามารถให้ข้อความแสดงข้อผิดพลาดที่ดีได้และไม่สามารถให้การกู้คืนข้อผิดพลาดได้ บางทีภาษาของคุณแปลกมากและนักแยกวิเคราะห์ปฏิเสธไวยากรณ์ของคุณหรือคุณต้องการการควบคุมมากกว่าตัวสร้างให้คุณ
ในกรณีเหล่านี้การใช้ตัวแยกวิเคราะห์ recursive-descent ที่เขียนด้วยมือน่าจะดีที่สุด ในขณะที่ทำให้ถูกต้องอาจมีความซับซ้อนคุณสามารถควบคุม parser ของคุณได้อย่างสมบูรณ์เพื่อให้คุณสามารถทำสิ่งดี ๆ ทุกประเภทที่คุณไม่สามารถทำกับเครื่องกำเนิดไฟฟ้า parser เช่นข้อความแสดงข้อผิดพลาดและการกู้คืนข้อผิดพลาด (ลองลบเครื่องหมายอัฒภาคทั้งหมด : คอมไพเลอร์ C # จะบ่น แต่จะตรวจพบข้อผิดพลาดอื่น ๆ ส่วนใหญ่ต่อไปโดยไม่คำนึงถึงการมีอัฒภาค)
ตัวแยกวิเคราะห์ที่เขียนด้วยมือมักจะทำงานได้ดีกว่าตัวแยกวิเคราะห์ซึ่งสมมติว่าคุณภาพของตัวแยกวิเคราะห์สูงพอ ในทางกลับกันถ้าคุณไม่สามารถเขียนโปรแกรมแยกวิเคราะห์ที่ดีได้ - โดยปกติจะเกิดจากการขาดประสบการณ์ความรู้หรือการออกแบบ (มักจะรวมกัน) ประสิทธิภาพจะช้าลง สำหรับ lexers ตรงกันข้ามจะเป็นจริง: lexers ที่สร้างขึ้นโดยทั่วไปใช้การค้นหาตารางทำให้เร็วกว่า (ส่วนใหญ่) เขียนด้วยมือ
การศึกษาที่ชาญฉลาดการเขียนโปรแกรมแยกวิเคราะห์ของคุณจะสอนคุณมากกว่าการใช้เครื่องกำเนิดไฟฟ้า คุณต้องเขียนโค้ดที่ซับซ้อนมากขึ้นเรื่อย ๆ รวมทั้งคุณต้องเข้าใจอย่างชัดเจนถึงวิธีการแยกวิเคราะห์ภาษา ในทางกลับกันถ้าคุณต้องการเรียนรู้วิธีการสร้างภาษาของคุณเอง (ดังนั้นให้ได้รับประสบการณ์ในการออกแบบภาษา) ตัวเลือกที่ 1 หรือตัวเลือกที่ 3 เป็นที่นิยม: ถ้าคุณกำลังพัฒนาภาษามันอาจจะเปลี่ยนไปมาก และตัวเลือกที่ 1 และ 3 ช่วยให้คุณมีเวลาได้ง่ายขึ้น
นี่คือเส้นทางที่ฉันกำลังเดิน: คุณเขียนเครื่องมือแยกวิเคราะห์ของคุณเอง ในขณะที่ไม่เอาจริงเอาจังการทำเช่นนี้อาจจะสอนคุณมากที่สุด
เพื่อให้แนวคิดแก่คุณว่าการทำโครงการแบบนี้เกี่ยวข้องกับอะไรฉันจะบอกคุณเกี่ยวกับความก้าวหน้าของฉัน
เครื่องกำเนิด lexer
ฉันสร้างตัวสร้าง lexer ของตัวเองก่อน ฉันมักจะออกแบบซอฟแวร์เริ่มต้นด้วยวิธีการใช้รหัสดังนั้นฉันคิดว่าฉันต้องการใช้รหัสของฉันและเขียนรหัสชิ้นนี้ (ใน C #):
Lexer<CalculatorToken> calculatorLexer = new Lexer<CalculatorToken>(
new List<StringTokenPair>()
{ // This is just like a lex specification:
// regex token
new StringTokenPair("\\+", CalculatorToken.Plus),
new StringTokenPair("\\*", CalculatorToken.Times),
new StringTokenPair("(", CalculatorToken.LeftParenthesis),
new StringTokenPair(")", CalculatorToken.RightParenthesis),
new StringTokenPair("\\d+", CalculatorToken.Number),
});
foreach (CalculatorToken token in
calculatorLexer.GetLexer(new StringReader("15+4*10")))
{ // This will iterate over all tokens in the string.
Console.WriteLine(token.Value);
}
// Prints:
// 15
// +
// 4
// *
// 10
คู่ของสตริงโทเค็นอินพุตถูกแปลงเป็นโครงสร้างแบบเรียกซ้ำที่อธิบายถึงนิพจน์ทั่วไปที่แสดงโดยใช้แนวคิดของสแต็กเลขคณิต จากนั้นจะถูกแปลงเป็น NFA (หุ่นยนต์ จำกัด nondeterministic) ซึ่งจะถูกแปลงเป็น DFA (เครื่องจักร จำกัด แน่นอนอัตโนมัติ) จากนั้นคุณสามารถจับคู่สตริงกับ DFA
ด้วยวิธีนี้คุณจะได้รับความคิดที่ดีว่า lexers ทำงานอย่างไร นอกจากนี้หากคุณทำในสิ่งที่ถูกต้องผลลัพธ์ที่ได้จากเครื่องกำเนิด lexer ของคุณอาจเร็วพอ ๆ กับการใช้งานระดับมืออาชีพ คุณจะไม่สูญเสียความหมายใด ๆ เมื่อเทียบกับตัวเลือก 2 และไม่มากเมื่อเทียบกับตัวเลือก 1
ฉันปรับใช้ตัวสร้าง lexer ของฉันด้วยรหัสเพียง 1600 บรรทัด รหัสนี้ทำงานด้านบน แต่ก็ยังสร้าง lexer ได้ทันทีทุกครั้งที่คุณเริ่มโปรแกรม: ฉันจะเพิ่มรหัสเพื่อเขียนลงดิสก์ในบางจุด
หากคุณต้องการทราบวิธีการเขียน lexer ของคุณเองนี่เป็นจุดเริ่มต้นที่ดี
ตัวแยกวิเคราะห์
จากนั้นคุณเขียนตัวแยกวิเคราะห์ของคุณ ฉันอ้างถึงที่นี่อีกครั้งสำหรับภาพรวมเกี่ยวกับ parsers ประเภทต่าง ๆ - เป็นกฎง่ายๆยิ่งพวกเขาสามารถแยกวิเคราะห์ยิ่งช้าพวกเขา
ความเร็วไม่ใช่ปัญหาสำหรับฉันฉันเลือกที่จะใช้เครื่องมือแยกวิเคราะห์ Earley การใช้งานขั้นสูงของตัวแยกวิเคราะห์ Earley แสดงให้เห็นว่าช้ากว่าตัวแยกวิเคราะห์ชนิดอื่นประมาณสองเท่า
ในทางกลับกันสำหรับความเร็วที่คุณจะได้รับความสามารถในการแยกประเภทของไวยากรณ์ใด ๆแม้กระทั่งคนที่ไม่ชัดเจน ซึ่งหมายความว่าคุณไม่ต้องกังวลว่า parser ของคุณจะมีการวนซ้ำทางซ้ายหรือไม่หรือความขัดแย้งแบบกะ - ลดคืออะไร นอกจากนี้คุณยังสามารถกำหนดไวยากรณ์ได้ง่ายขึ้นโดยใช้ไวยากรณ์ที่กำกวมหากไม่สำคัญว่าผลการแยกวิเคราะห์แบบใดเช่นมันไม่สำคัญว่าคุณแยกวิเคราะห์ 1 + 2 + 3 เป็น (1 + 2) +3 หรือ 1 + (2 + 3)
นี่คือสิ่งที่ชิ้นส่วนของรหัสโดยใช้ตัวแยกวิเคราะห์ของฉันสามารถมีลักษณะ:
Lexer<CalculatorToken> calculatorLexer = new Lexer<CalculatorToken>(
new List<StringTokenPair>()
{
new StringTokenPair("\\+", CalculatorToken.Plus),
new StringTokenPair("\\*", CalculatorToken.Times),
new StringTokenPair("(", CalculatorToken.LeftParenthesis),
new StringTokenPair(")", CalculatorToken.RightParenthesis),
new StringTokenPair("\\d+", CalculatorToken.Number),
});
Grammar<IntWrapper, CalculatorToken> calculator
= new Grammar<IntWrapper, CalculatorToken>(calculatorLexer);
// Declaring the nonterminals.
INonTerminal<IntWrapper> expr = calculator.AddNonTerminal<IntWrapper>();
INonTerminal<IntWrapper> term = calculator.AddNonTerminal<IntWrapper>();
INonTerminal<IntWrapper> factor = calculator.AddNonTerminal<IntWrapper>();
// expr will be our head nonterminal.
calculator.SetAsMainNonTerminal(expr);
// expr: term | expr Plus term;
calculator.AddProduction(expr, term.GetDefault());
calculator.AddProduction(expr,
expr.GetDefault(),
CalculatorToken.Plus.GetDefault(),
term.AddCode(
(x, r) => { x.Result.Value += r.Value; return x; }
));
// term: factor | term Times factor;
calculator.AddProduction(term, factor.GetDefault());
calculator.AddProduction(term,
term.GetDefault(),
CalculatorToken.Times.GetDefault(),
factor.AddCode
(
(x, r) => { x.Result.Value *= r.Value; return x; }
));
// factor: LeftParenthesis expr RightParenthesis
// | Number;
calculator.AddProduction(factor,
CalculatorToken.LeftParenthesis.GetDefault(),
expr.GetDefault(),
CalculatorToken.RightParenthesis.GetDefault());
calculator.AddProduction(factor,
CalculatorToken.Number.AddCode
(
(x, s) => { x.Result = new IntWrapper(int.Parse(s));
return x; }
));
IntWrapper result = calculator.Parse("15+4*10");
// result == 55
(โปรดทราบว่า IntWrapper เป็นเพียง Int32 ยกเว้นว่า C # ต้องการให้เป็นคลาสดังนั้นฉันจึงต้องแนะนำคลาส wrapper)
ฉันหวังว่าคุณจะเห็นว่ารหัสข้างต้นมีประสิทธิภาพมาก: ไวยากรณ์ใด ๆ ที่คุณสามารถเกิดขึ้นกับสามารถแยกวิเคราะห์ คุณสามารถเพิ่มบิตของรหัสโดยพลการในไวยากรณ์ที่สามารถทำงานจำนวนมากได้ หากคุณจัดการเพื่อให้การทำงานทั้งหมดนี้คุณสามารถใช้รหัสผลลัพธ์อีกครั้งเพื่อทำงานหลายอย่างได้อย่างง่ายดายเพียงจินตนาการการสร้างล่ามบรรทัดคำสั่งโดยใช้รหัสชิ้นนี้
หากคุณไม่เคยเคยเขียน parser ฉันอยากจะแนะนำให้คุณทำมัน มันสนุกและคุณเรียนรู้วิธีการทำงานของสิ่งต่าง ๆ และคุณเรียนรู้ที่จะชื่นชมความพยายามของตัวแยกวิเคราะห์และตัวสร้าง lexer ช่วยให้คุณประหยัดจากการทำในครั้งต่อไปที่คุณต้องใช้ตัวแยกวิเคราะห์
ฉันขอแนะนำให้คุณลองอ่านhttp://compilers.iecc.com/crenshaw/เนื่องจากมันมีทัศนคติที่ไม่คุ้นเคยกับวิธีการทำเช่นนั้น
ข้อดีของการเขียนตัวแยกวิเคราะห์โคตรแบบเรียกซ้ำของคุณเองคือคุณสามารถสร้างข้อความแสดงข้อผิดพลาดคุณภาพสูงบนข้อผิดพลาดทางไวยากรณ์ เมื่อใช้ตัวแยกวิเคราะห์คุณสามารถสร้างข้อผิดพลาดและเพิ่มข้อความข้อผิดพลาดที่กำหนดเองได้ในบางจุด แต่ตัวแยกวิเคราะห์แบบไม่ตรงกับพลังของการควบคุมการแยกวิเคราะห์อย่างสมบูรณ์
ข้อดีอีกอย่างของการเขียนของคุณเองก็คือการแยกวิเคราะห์การแสดงที่ง่ายกว่านั้นไม่มีการโต้ตอบกับไวยากรณ์ของคุณแบบหนึ่งต่อหนึ่ง
หากไวยากรณ์ของคุณได้รับการแก้ไขและข้อความแสดงข้อผิดพลาดมีความสำคัญให้พิจารณาการหมุนของคุณเองหรืออย่างน้อยก็ใช้ parser generator ที่ให้ข้อความผิดพลาดที่คุณต้องการ หากไวยากรณ์ของคุณเปลี่ยนแปลงอยู่ตลอดเวลาคุณควรพิจารณาใช้ตัวแยกวิเคราะห์แทน
Bjarne Stroustrup พูดถึงวิธีที่เขาใช้ YACC สำหรับการติดตั้ง C ++ เป็นครั้งแรก (ดูThe Design and Evolution of C ++ ) ในกรณีแรกนั้นเขาหวังว่าเขาจะเขียนโปรแกรมวิเคราะห์คำซ้ำลงไปแทน!
ตัวเลือก 3: ไม่มี (ตัวสร้าง parser ของคุณเอง)
เพียงเพราะมีเหตุผลที่จะไม่ใช้ANTLR , bison , Coco / R , Grammatica , JavaCC , Lemon , Parboiled , SableCC , Quexและอื่น ๆ - นั่นไม่ได้หมายความว่าคุณควรม้วน parser + lexer ของคุณเองทันที
ระบุว่าทำไมเครื่องมือเหล่านี้ไม่ดีพอ - ทำไมพวกเขาถึงไม่ยอมให้คุณไปถึงเป้าหมาย?
หากคุณไม่แน่ใจว่าสิ่งแปลกประหลาดในไวยากรณ์ที่คุณกำลังทำอยู่นั้นไม่เหมือนใครคุณไม่ควรสร้าง parser + lexer ที่กำหนดเองเพียงตัวเดียว ให้สร้างเครื่องมือที่จะสร้างสิ่งที่คุณต้องการ แต่สามารถใช้เพื่อตอบสนองความต้องการในอนาคตจากนั้นปล่อยเป็นซอฟต์แวร์ฟรีเพื่อป้องกันไม่ให้คนอื่นมีปัญหาเช่นเดียวกับคุณ
การแยกวิเคราะห์คำของคุณเองบังคับให้คุณคิดโดยตรงเกี่ยวกับความซับซ้อนของภาษาของคุณ หากภาษาแยกวิเคราะห์ได้ยากอาจเป็นเรื่องยากที่จะเข้าใจ
มีความสนใจอย่างมากในเครื่องกำเนิดไฟฟ้า parser ในวันแรกแรงบันดาลใจจากไวยากรณ์ภาษาที่ซับซ้อน (บางคนอาจพูดว่า "ถูกทรมาน") JOVIAL เป็นตัวอย่างที่ไม่ดีเป็นพิเศษ: มันต้องใช้สองสัญลักษณ์ lookahead ในเวลาที่ทุกอย่างอื่นต้องการสัญลักษณ์มากที่สุด สิ่งนี้ทำให้การแยกวิเคราะห์สำหรับคอมไพเลอร์ JOVIAL ยากกว่าที่คาดการณ์ไว้ (เนื่องจากพลวัตทั่วไป / ฟอร์ตเวิร์ ธ แผนกเรียนรู้วิธีที่ยากเมื่อพวกเขาจัดหาคอมไพเลอร์ JOVIAL สำหรับโปรแกรม F-16)
วันนี้เชื้อสายแบบเรียกซ้ำเป็นวิธีที่นิยมใช้กันทั่วโลกเพราะง่ายกว่าสำหรับนักเขียนคอมไพเลอร์ คอมไพเลอร์เชื้อสายแบบเรียกซ้ำนั้นให้รางวัลอย่างง่ายต่อการออกแบบภาษาที่สะอาดซึ่งมันง่ายกว่ามากในการเขียนตัวแยกวิเคราะห์แบบสืบเชื้อสายซ้ำสำหรับภาษาที่ง่ายและสะอาดกว่าภาษาที่ซับซ้อนและยุ่งเหยิง
ในที่สุด: คุณได้พิจารณาการฝังภาษาของคุณใน LISP และให้ล่าม LISP ทำการแปลอย่างหนักสำหรับคุณหรือไม่? AutoCAD ทำเช่นนั้นและพบว่าทำให้ชีวิตของพวกเขาง่ายขึ้นมาก มีล่าม LISP น้ำหนักเบาอยู่สองสามตัวบางอันฝังอยู่ได้
ผมเคยเขียน parser สำหรับการประยุกต์ใช้ในเชิงพาณิชย์ครั้งและฉันใช้yacc มีต้นแบบการแข่งขันที่ผู้พัฒนาเขียนทั้งหมดด้วยมือใน C ++ และทำงานช้าลงประมาณห้าเท่า
สำหรับ lexer สำหรับ parser นี้ฉันเขียนมันด้วยมือทั้งหมด มันต้องใช้เวลา - ขอโทษมันเป็นเกือบ 10 ปีที่ผ่านมาดังนั้นผมจึงไม่จำมันได้อย่างแม่นยำ - ประมาณ 1000 เส้นในC
เหตุผลที่ฉันเขียน lexer ด้วยมือคือไวยากรณ์อินพุตของ parser มันเป็นข้อกำหนดบางอย่างที่โปรแกรมแยกวิเคราะห์ของฉันต้องปฏิบัติตามเมื่อเทียบกับสิ่งที่ฉันออกแบบ (แน่นอนฉันจะออกแบบมันแตกต่างกันและดีกว่า!) ไวยากรณ์นั้นขึ้นอยู่กับบริบทอย่างรุนแรงและแม้แต่การพึ่งพาอาศัยความหมายในบางสถานที่ ตัวอย่างเช่นเครื่องหมายอัฒภาคอาจเป็นส่วนหนึ่งของโทเค็นในที่เดียว แต่ตัวคั่นในที่อื่น - ขึ้นอยู่กับการตีความความหมายขององค์ประกอบบางอย่างที่ถูกแยกวิเคราะห์ก่อนหน้านี้ ดังนั้นฉัน "ฝัง" การพึ่งพาความหมายเช่นนี้ใน lexer ที่เขียนด้วยมือและทำให้ฉันมีBNFที่ค่อนข้างตรงไปตรงมาและง่ายต่อการใช้ใน yacc
เพิ่มในการตอบสนองต่อMacneil : yacc ให้สิ่งที่เป็นนามธรรมที่ทรงพลังมากซึ่งทำให้โปรแกรมเมอร์คิดในแง่ของเทอร์มินัลไม่ใช่เทอร์มินัลการผลิตและสิ่งต่าง ๆ เช่นนั้น นอกจากนี้เมื่อใช้งานyylex()
ฟังก์ชั่นมันช่วยให้ฉันมุ่งเน้นที่การคืนโทเค็นปัจจุบันและไม่ต้องกังวลกับสิ่งที่เกิดขึ้นก่อนหรือหลังมัน โปรแกรมเมอร์ C ++ ทำงานกับระดับตัวอักษรโดยไม่ได้รับประโยชน์จากนามธรรมและจบลงด้วยการสร้างอัลกอริทึมที่ซับซ้อนและมีประสิทธิภาพน้อยลง เราสรุปได้ว่าความเร็วที่ช้าลงนั้นไม่เกี่ยวข้องกับ C + + หรือไลบรารีใด ๆ เราวัดความเร็วในการแจงบริสุทธิ์ด้วยไฟล์ที่โหลดในหน่วยความจำ หากเรามีปัญหาการบัฟเฟอร์ไฟล์ yacc จะไม่เป็นเครื่องมือของเราในการเลือกแก้ไข
นอกจากนี้ยังต้องการเพิ่ม : นี่ไม่ใช่สูตรสำหรับการเขียนโปรแกรมแยกวิเคราะห์โดยทั่วไปเป็นเพียงตัวอย่างของวิธีการทำงานในสถานการณ์เฉพาะ
ขึ้นอยู่กับสิ่งที่คุณต้องแยกวิเคราะห์ คุณสามารถม้วนตัวเองเร็วกว่าที่คุณจะสามารถเรียนรู้จาก lexer ได้หรือไม่? สิ่งที่จะแยกวิเคราะห์แบบคงที่พอที่คุณจะไม่เสียใจในการตัดสินใจในภายหลัง? คุณพบว่าการใช้งานที่มีอยู่ซับซ้อนเกินไป? ถ้าเป็นเช่นนั้นขอให้สนุกกับการกลิ้งของคุณเอง แต่ถ้าคุณไม่ได้หลบโค้งการเรียนรู้
เมื่อเร็ว ๆ นี้ฉันมาชอบตัวแยกวิเคราะห์มะนาวซึ่งเป็นเนื้อหาที่ง่ายและง่ายที่สุดที่ฉันเคยใช้ เพื่อประโยชน์ในการทำสิ่งต่าง ๆ ที่ง่ายต่อการบำรุงรักษาฉันแค่ใช้สิ่งนั้นเพื่อความต้องการส่วนใหญ่ SQLite ใช้เช่นเดียวกับโครงการที่มีชื่อเสียงอื่น ๆ
แต่ฉันไม่สนใจ lexers เลยนอกจากพวกเขาจะไม่ได้รับเมื่อฉันต้องการใช้หนึ่ง (ดังนั้นมะนาว) คุณอาจจะเป็นและถ้าเป็นเช่นนั้นทำไมไม่ทำอย่างใดอย่างหนึ่ง? ฉันมีความรู้สึกว่าคุณจะกลับมาใช้สิ่งที่มีอยู่ได้ แต่เกาถ้าคุณต้อง :)
ขึ้นอยู่กับว่าเป้าหมายของคุณคืออะไร
คุณกำลังพยายามเรียนรู้วิธีการทำงานของ parsers / compilers จากนั้นเขียนของคุณเองตั้งแต่เริ่มต้น นั่นเป็นวิธีเดียวที่คุณจะได้เรียนรู้ที่จะซาบซึ้งในสิ่งที่พวกเขาทำ ฉันเขียนหนึ่งเดือนที่ผ่านมาและมันเป็นประสบการณ์ที่น่าสนใจและมีค่าโดยเฉพาะอย่างยิ่ง 'ah ดังนั้นนั่นเป็นเหตุผลว่าทำไมภาษา X จึงใช้ช่วงเวลานี้ ... '
คุณต้องการรวบรวมบางสิ่งบางอย่างเข้าด้วยกันอย่างรวดเร็วเพื่อให้แอปพลิเคชันอยู่ในกำหนดเวลาหรือไม่? จากนั้นอาจใช้เครื่องมือแยกวิเคราะห์
คุณต้องการบางสิ่งบางอย่างที่คุณต้องการขยายในอีก 10, 20 หรือ 30 ปีหรือไม่ เขียนของคุณเองและใช้เวลาของคุณ มันจะคุ้มค่ามาก
คุณเคยพิจารณาแนวทางการปรับแต่งภาษามาร์ตินฟาวเลอร์หรือไม่? ข้อความจากบทความ
การเปลี่ยนแปลงที่ชัดเจนที่สุดที่การปรับแต่งภาษาทำให้สมการคือความง่ายในการสร้าง DSL ภายนอก คุณไม่ต้องเขียนโปรแกรมแยกวิเคราะห์อีกต่อไป คุณต้องกำหนดรูปแบบนามธรรม - แต่จริงๆแล้วเป็นขั้นตอนการสร้างแบบจำลองข้อมูลที่ค่อนข้างตรงไปตรงมา นอกจากนี้ DSL ของคุณยังได้รับ IDE ที่ทรงพลัง - แม้ว่าคุณจะต้องใช้เวลาในการกำหนดตัวแก้ไขนั้น เครื่องกำเนิดไฟฟ้ายังคงเป็นสิ่งที่คุณต้องทำและความรู้สึกของฉันคือมันไม่ง่ายกว่าที่เคยเป็นมา แต่การสร้างเครื่องกำเนิดสำหรับ DSL ที่ดีและเรียบง่ายเป็นหนึ่งในส่วนที่ง่ายที่สุดของการฝึก
อ่านอย่างนั้นฉันจะบอกว่าวันที่เขียน parser ของคุณเองไปแล้วและดีกว่าที่จะใช้ห้องสมุดที่มีอยู่ เมื่อคุณเชี่ยวชาญไลบรารีแล้ว DSL ทั้งหมดที่คุณสร้างขึ้นในอนาคตจะได้รับประโยชน์จากความรู้นั้น นอกจากนี้คนอื่น ๆ ไม่จำเป็นต้องเรียนรู้วิธีการแยกวิเคราะห์
แก้ไขเพื่อครอบคลุมความคิดเห็น (และคำถามที่แก้ไขแล้ว)
ข้อดีของการกลิ้งของคุณเอง
ดังนั้นในระยะสั้นคุณควรม้วนตัวเองเมื่อคุณต้องการเจาะลึกเข้าไปในลำไส้ของปัญหาที่ยากมากที่คุณรู้สึกว่ามีแรงจูงใจอย่างมากที่จะเชี่ยวชาญ
ข้อดีของการใช้ห้องสมุดของคนอื่น
ดังนั้นหากคุณต้องการผลลัพธ์สุดท้ายอย่างรวดเร็วให้ใช้ห้องสมุดของคนอื่น
โดยรวมแล้วสิ่งนี้จะเป็นตัวเลือกว่าคุณต้องการเป็นเจ้าของปัญหามากแค่ไหน ถ้าคุณต้องการมันทั้งหมดแล้วม้วนของคุณเอง
ข้อได้เปรียบที่สำคัญในการเขียนของคุณคือคุณจะรู้วิธีการเขียนของคุณเอง ข้อได้เปรียบที่ยิ่งใหญ่ในการใช้เครื่องมือเช่น yacc คือคุณจะรู้วิธีใช้เครื่องมือนี้ ฉันเป็นแฟนของยอดไม้สำหรับการสำรวจครั้งแรก
ทำไมไม่แยกเครื่องมือสร้างตัวแยกวิเคราะห์โอเพ่นซอร์สและทำให้เป็นของคุณเอง? หากคุณไม่ได้ใช้ parser generators รหัสของคุณจะยากมากหากคุณทำการเปลี่ยนแปลงไวยากรณ์ของภาษาของคุณอย่างมาก
ในตัวแยกวิเคราะห์ของฉันฉันใช้นิพจน์ทั่วไป (ฉันหมายถึงสไตล์ Perl) เพื่อทำเครื่องหมายและใช้ฟังก์ชั่นอำนวยความสะดวกบางอย่างเพื่อเพิ่มความสามารถในการอ่านโค้ด อย่างไรก็ตามรหัส parser สร้างได้เร็วโดยการทำตารางรัฐและยาวswitch
- case
s ซึ่งอาจเพิ่มขนาดรหัสที่มาจนกว่าคุณจะ.gitignore
ให้พวกเขา
นี่คือสองตัวอย่างของตัวแยกวิเคราะห์ที่ฉันเขียน:
https://github.com/SHiNKiROU/DesignScript - ภาษาเบสิกขั้นพื้นฐานเพราะฉันขี้เกียจเกินกว่าจะเขียน lookaheads ในสัญกรณ์อาร์เรย์ฉันเสียสละคุณภาพข้อความข้อผิดพลาด https://github.com/SHiNKiROU/ExprParser - เครื่องคิดเลขสูตร สังเกตุเห็นถึงเทคนิคการ metaprogramming แปลก ๆ
"ฉันควรใช้ 'วงล้อ' ที่ผ่านการทดสอบและผ่านการทดสอบแล้วหรือนำกลับมาใช้ใหม่หรือไม่"