في هذا المقال ، لن أتحدث عن الميزات الجديدة لمولد المحلل اللغوي - لقد وصفتها بشكل كافٍ في الأجزاء السابقة. بدلاً من ذلك ، أريد أن أخبرك بما فعلته في Core Developer Sprint الأسبوع الماضي قبل أن يتم مسح كل شيء من ذاكرتي. على الرغم من أن معظم المواد ذات الصلة بطريقة ما إلى PEG. لذلك يجب أن أعرض بعض الكود الذي يحدد الاتجاه في تنفيذ محلل PEG لبايثون 3.9.
بيثون PEG محلل سلسلة المحتوى كل عام على مدار السنوات الأربع الماضية ، كان فريق التطوير الأساسي لبيثون يجتمعون لسباق أسبوعي في مكان غريب. تتم رعاية هذه السباقات من قبل المضيف و PSF. في العامين الأولين ، قمنا بزيارة Facebook في Mountain View ، وفي العام الماضي ، كانت لدى Microsoft خط في Bellevue ، وتم اختيار Bloomberg في لندن لهذا العدو. (يجب أن أقول أنه يبدو رائعًا.) المجد للمطور الأساسي Pablo Galindo Salgado لتنظيمه!
هذه المرة جمعنا أكثر من 30 مطورًا ، بالإضافة إلى اثنين من البداويين. عمل الناس على أجزاء مختلفة: من 3.8 حاصرات إلى PEPs جديدة. آمل أن تكون مدونة PSF حول إنجازاتنا. واحدة من النقاط الرئيسية هي أن عدد PRS المفتوحة كان أقل من 1000 ، أكثر من 100 PRS كانوا ينتظرون مراجعتها. كان هناك حتى منافسة مع المتصدرين - أفضل 10 مشاركين نظموا عددًا أكبر من العلاقات العامة للأشخاص الآخرين.
بالنسبة لي ، دائمًا ما يكون السبب الرئيسي لحضور مثل هذه الأحداث هو لقاء مع أشخاص أتعاون معهم عبر الإنترنت طوال العام ، لكنهم لا أراهم إلا مرة واحدة أو مرتين في السنة. كان هذا العدو في أوروبا ، لذلك رأينا تكوينًا مختلفًا قليلاً ، وكان هذا مهمًا بشكل خاص. على الرغم من ذلك ، عملت أيضًا بطريقة مثمرة ، والتي سأتحدث عنها.
معظم وقتي في سباق السرعة ، عملت مع Pablo و Emily Morhouse على مولد محلل معتمد على PEG ، والذي آمل أن يحل يومًا ما محل مولد المحلل اللغوي القائم على pgen. ليس هذا هو نفس رمز المولد الذي كتبت عنه ، لكنه مشابه جدًا. ساهم Pablo بالفعل في هذا الإصدار.
في اليوم الأول من سباق العدو ، الاثنين ، عملت بشكل رئيسي على المادتين 7 و 8 من هذه الدورة. في البداية ، خططت لنشرها معًا ، لكن لم يكن لدي وقت بنهاية اليوم. لذلك قام بتقسيمها إلى جزأين ونشر النصف الأول المكرس لإنشاء المخطط. بعد ظهر يوم الجمعة ، وجدت أخيرًا بعض الوقت لأكمل الجزء 8. ومع ذلك ، ما زلت مضطرًا إلى حذف القصة المقطوعة لأنني ما زلت لا أملك مثالًا جيدًا.
يوم الثلاثاء ، بدأت العمل على قواعد PEG لقواعد بيثون. إنها أقرب إلى الشفرة من المواصفات التجريدية قبل إضافة الإجراءات. لقد فهمنا أننا نحتاج إلى التحقق من القواعد المتقدمة على كود Python الحقيقي. لذلك بينما كنت أنهي قواعدي النحوية ، كانت إميلي تقوم بعمل نصوص لاختبار الدفعة. بعد ذلك ، كان سير العمل لدي مثل هذا:
- قم بتشغيل برنامج نصي لاختبار بعض الدليل باستخدام رمز Python
- للتحقيق في المشكلة الأولى التي سقط
- تصحيح قواعد اللغة لحل هذه المشكلة
- كرر حتى لا توجد مشاكل
- انتقل إلى الدليل التالي
لقد بدأت برمز مشروع pgen الخاص بي. في النهاية ، تمكنت قواعدي اللغوية من تحليل جميع بنى Python المستخدمة في pgen ، وانتقلت إلى وحدات المكتبة القياسية. أولاً ، التركيز على Lib/test
، ثم Lib/asyncio
وأخيرا Lib
، أي في الواقع المكتبة القياسية بأكملها (على الأقل المكتبة المكتوبة في بيثون). بحلول نهاية الأسبوع ، تمكنت من الاحتفال: الملفات الوحيدة في المكتبة القياسية التي سقط عليها المحلل الجديد كانت ملفات ذات ترميزات سيئة. توجد فقط كبيانات اختبار للتحقق من أن مشاكل الترميز ستتم معالجتها بالطريقة الصحيحة ؛ وبعض الملفات لبيثون 2 اللازمة كحالات اختبار لـ lib2to3 .
ثم أضفت بعض التعليمات البرمجية لحساب وقت تشغيل المحلل في نص Emily ، ويبدو أن محلل PEG الجديد أسرع قليلاً من محلل pgen القديم. هذا لا يعني أن الأمور سوف تذهب شاقة أكثر! هناك أكثر من 100 قاعدة في القواعد (160 سطرًا) ، ولجعلها تنشئ AST ، سنحتاج إلى إضافة إجراء إلى كل منها (انظر الجزء 6).
في وقت سابق ، أجريت بعض التجارب لمعرفة مقدار حجم الملف الذي سيزداد بعد إضافة الإجراءات. توصلت إلى استنتاج أنه سيصبح أكبر من 2-3 مرات ، وإليك قواعد هذه التجربة:
start[mod_ty]: a=stmt* ENDMARKER{ Module(a, NULL, p->arena) } stmt[stmt_ty]: compound_stmt | simple_stmt compound_stmt[stmt_ty]: pass_stmt | if_stmt pass_stmt[stmt_ty]: a='pass' NEWLINE { _Py_Pass(EXTRA(a, a)) } if_stmt[stmt_ty]: | 'if' c=expr ':' t=suite e=[else_clause] { _Py_If(c, t, e, EXTRA(c, c)) } else_clause[asdl_seq*]: | 'elif' c=expr ':' t=suite e=[else_clause] { singleton_seq(p, _Py_If(c, t, e, EXTRA(c, c))) } | 'else' ':' s=suite { s } suite[asdl_seq*]: | a=simple_stmt { singleton_seq(p, a) } | NEWLINE INDENT b=stmt+ DEDENT { b } simple_stmt[stmt_ty]: a=expr_stmt NEWLINE { a } expr_stmt[stmt_ty]: a=expr { _Py_Expr(a, EXTRA(a, a)) } expr[expr_ty]: | l=expr '+' r=term { _Py_BinOp(l, Add, r, EXTRA(l, r)) } | l=expr '-' r=term { _Py_BinOp(l, Sub, r, EXTRA(l, r)) } | term term[expr_ty]: | l=term '*' r=factor { _Py_BinOp(l, Mult, r, EXTRA(l, r)) } | l=term '/' r=factor { _Py_BinOp(l, Div, r, EXTRA(l, r)) } | factor factor[expr_ty]: | l=primary '**' r=factor { _Py_BinOp(l, Pow, r, EXTRA(l, r)) } | primary primary[expr_ty]: | f=primary '(' e=expr ')' { _Py_Call(f, singleton_seq(p, e), NULL, EXTRA(f, e)) } | atom atom[expr_ty]: | '(' e=expr ')' { e } | NAME | NUMBER | STRING
هناك الكثير من الأشياء التي يجب أن أشرحها هنا.
- تتم كتابة الإجراءات في C. كما هو الحال في مولد Python من الجزء 6 ، كل منها عبارة عن تعبير واحد.
- يحدد النص الوارد بين قوسين معقوفين مباشرةً بعد اسم القاعدة نوع النتيجة لطريقة القاعدة المقابلة. على سبيل المثال ، تعني
atom[expr_ty]
أنه سيتم إرجاع expr_ty
لـ atom
. إذا نظرت إلى ملف Include/Python-ast.h
في مستودع CPython ، فسترى أن هذا النوع هو الهيكل المستخدم لتمثيل التعبيرات في AST الداخلي. - إذا كان البديل يحتوي على عنصر واحد فقط ، فيمكن حذف الإجراء ، لأن السلوك الافتراضي هو ببساطة إرجاع عقدة AST الناتجة. خلاف ذلك ، يجب تحديد الإجراء بشكل صريح.
- يحتاج رمز C الذي تم إنشاؤه أيضًا إلى بعض المعالجة. على سبيل المثال ، من المفترض أن يتم تضمين بعض ملفات رأس CPython. على سبيل المثال ، حيث
expr_ty
نوع expr_ty
، جيدًا ، والعديد من الأشياء الضرورية الأخرى. - يحتوي المتغير
p
على مؤشر إلى بنية Parser
المستخدمة من قبل المحلل الذي تم إنشاؤه. (ونعم ، هذا يعني أنه من الأفضل عدم تحديد عنصر واحد في قاعدة p
- وإلا لن يتم تجميع رمز C الذي تم إنشاؤه!) EXTRA(node1, node2)
هو ماكرو يتم توسيعه إلى مجموعة من الوسائط الإضافية التي يجب تمريرها إلى كل وظيفة إنشاء AST. يوفر هذا الكثير من الوقت عند كتابة إجراء ما - وإلا ، فسيتعين عليك تحديد رقم سطر البداية والنهاية وإزاحة العمود وأيضًا مؤشر على الساحة المستخدمة للتوزيع. (العقد AST ليست كائنات Python وتستخدم تخطيطًا أكثر كفاءة.)- نظرًا لوجود بعض السلوكيات المثيرة للاهتمام للمعالج المسبق C في
EXTRA()
، لا يمكننا استخدام وحدات الماكرو لإنشاء عقدة AST ، ولكن يجب استخدام الوظائف الأساسية. هذا هو السبب في أنك ترى ، على سبيل المثال ، _Py_Binop(...)
، وليس Binop(...)
. في المستقبل سوف أفكر في كيفية حلها بشكل مختلف. - بالنسبة للعناصر المكررة (
foo*
أو foo+
) ، يقوم منشئ الشفرة بإنشاء قاعدة مساعدة من النوع asdl_seq*
. هذه هي بنية البيانات التي تستخدمها AST لتمثيل التكرار. في العديد من الأماكن ، نحتاج إلى إنشاء مثل هذا التكرار من عنصر واحد فقط ، ولهذا حددنا الوظيفة الإضافية singleton_seq()
.
ربما يبدو بعض هذا غريبًا ، ولن أجادل. هذا نموذج أولي ، والغرض الرئيسي منه هو إثبات أنه من الممكن من حيث المبدأ إنشاء AST عامل باستخدام محلل تم إنشاؤه من قواعد PEG. كل هذا يعمل دون أي تغييرات على tokenizer أو مترجم bytecode الحالي. يمكن أن يقوم النموذج الأولي بترجمة تعبيرات بسيطة if
، ويمكن تجميع AST الناتج إلى رمز ثانوي وتنفيذها.
أشياء أخرى قمت بها كجزء من هذا السباق:
- أقنعت Lukasz Lang بتغيير PEP 585 (اقتراحه بشأن تلميح النوع في المستقبل) للتركيز على الأدوية الوراثية ، بدلاً من التركيز على مجموعة من الأفكار ، كما كان من قبل. يبدو PEP الجديد أفضل بكثير ، وفي اجتماع الكتابة قبل عدة أيام ، حيث حضر ممثلو مطوري أدوات التحقق من النوع Python (mypy ، pytype و Pyre) ، حصل على موافقة عامة. (ليس هذا هو نفس تأييد مجلس الإدارة!)
- ساعد يوري سيليفانوف في تطوير واجهة برمجة التطبيقات لنوع الخريطة المجمدة ، الذي أراد إضافته إلى stdlib. ساهم أيضًا العديد من المساهمين الآخرين في التصميم - أعتقد أننا أنهينا السباق من خلال بعض المجالس المليئة بالأمثلة وشظايا واجهة برمجة التطبيقات. والنتيجة هي PEP 603 ، وهي قيد المناقشة النشطة حاليًا. (ملاحظة واحدة: تنفيذ نوع البيانات المقترح موجود بالفعل في CPython ، كجزء من تنفيذ PEP 567 ، وحدة
contextvars
. هذا هو هيكل بيانات مثير للاهتمام للغاية ، Hash Array Mapped Trie
، والذي يجمع بين جدول التجزئة مع شجرة بادئة) - يوري ، كما هو الحال دائمًا ، مليء بالأفكار. كما عمل على مجموعات استثناء (فكرة من Trio ) أراد تنفيذها في Asyncio في Python 3.9. لا يبدو أن PEP موجودًا في آخر مرة نظرت فيها ، لكنني بالتأكيد أتذكر لوحة واحدة مليئة بالرسومات.
- نحن نناقش بنشاط اقتراح لوكاس لدورة إطلاق بايثون أقصر. وقد نتج عن ذلك PEP 602 ، والذي يقترح جعله سنويًا ، في أكتوبر. (هناك سبب وجيه لذلك: هذا يرجع إلى الجدول الزمني المعتاد لمؤتمرات بايثون وسباق الخيول الأساسية). يوجد على الأقل عرضان مضادان: في PEP 598 ، يقدم Nick Coglan إصدارات لمدة عامين ، مع السماح بميزات جديدة في تصحيحات ؛ يرغب Steve Dower في مشاهدة إصدارات فترة السنتين أيضًا ، ولكن بدون هذه الميزة (لم يكن هناك PEP بعد).
- اجتمع ثلاثة أعضاء من مجلس الإدارة حضروا السباق (بريت بريت ، كارول ويلينغ وأنا) وناقشوا رؤيتنا للتنمية المستقبلية لبيثون الأساسية. (لا أريد أن أتحدث عن هذا كثيرًا ، لأننا نعتزم التحدث عن ذلك في PyCon في الولايات المتحدة الأمريكية. ومع ذلك ، فربما نقترح بدء جمع التبرعات حتى تتمكن PSF من توظيف العديد من المطورين لدعم وتسريع عملية تطوير kernel).
- أجريت محادثة غداء ممتعة مع جوانا نانجي - أحد الذين حضروا الحفل. وروى قصة كيف عرفت عن الإنترنت عندما كان عمرها 8 سنوات وأخذت شقيقها الأصغر إلى مقهى للإنترنت أثناء عمل والدتهما. هناك اكتشفت جوجل والبريد الإلكتروني وعلقت هناك في الأسبوع الأول.
- كان الحدث الكحولي الرئيسي لهذا الأسبوع زوجين من كوكتيلات Zombie Apocalypse ، والتي طلبها البعض منا في بار Alchemist. يقدم في قارورة من 2 لتر من Erlenmeyer مع كمية كبيرة من الدخان المزيف الناتج عن سكب الكحول الجاف على خليط كحول منتظم ، تم تصميم كل مشروب لأربعة أشخاص.
- في ليلة الجمعة ، نقلتنا ليزا روش إلى مطعم هندي جيد بالقرب من فندقها. كان ذلك من خلال أربع محطات مترو ، والتي كانت مغامرة حقيقية (كانت ساعة الذروة ، وكادنا نفقد المسيحية التراتيل عدة مرات). كان الطعام يستحق كل هذا العناء!
- في مرحلة ما ، أخذنا صورة جماعية. يبدو مستقبلاً إلى حد ما ، لكنه في الحقيقة منظر طبيعي في لندن.

في المقالة التالية ، آمل أن أشارك بعض التقدم مع الإجراءات لإنشاء العقد AST.
ترخيص لهذه المقالة ورمز استشهد: CC BY-NC-SA 4.0