الإنجليزيةالفرنسيةالإسبانية

OnWorks فافيكون

perlinterp - عبر الإنترنت في السحابة

قم بتشغيل perlinterp في موفر الاستضافة المجاني OnWorks عبر Ubuntu Online أو Fedora Online أو محاكي Windows عبر الإنترنت أو محاكي MAC OS عبر الإنترنت

هذا هو الأمر perlinterp الذي يمكن تشغيله في موفر الاستضافة المجاني OnWorks باستخدام إحدى محطات العمل المجانية المتعددة عبر الإنترنت مثل Ubuntu Online أو Fedora Online أو محاكي Windows عبر الإنترنت أو محاكي MAC OS عبر الإنترنت

برنامج:

اسم


perlinterp - نظرة عامة على مترجم Perl

الوصف


يقدم هذا المستند نظرة عامة حول كيفية عمل مترجم Perl على مستوى C
التعليمات البرمجية، بالإضافة إلى مؤشرات إلى ملفات التعليمات البرمجية المصدر C ذات الصلة.

عناصر OF ال مترجم


يتكون عمل المترجم من مرحلتين رئيسيتين: تجميع الكود في الداخل
التمثيل، أو الرمز الثانوي، ومن ثم تنفيذه. يشرح "الكود المترجم" في perlguts
بالضبط كيف تحدث مرحلة التجميع.

فيما يلي تفصيل قصير لعملية بيرل:

الشركة الناشئة
يبدأ العمل في بيرلمان.ج. (أو miniperlmain.c لـ miniperl) هذا مستوى عالٍ جدًا
رمز كافٍ لوضعه على شاشة واحدة، وهو يشبه الكود الموجود في perlembed؛ معظم
من العمل الحقيقي يحدث في بيرل.c

بيرلمان.ج يتم إنشاؤه بواسطة "ExtUtils::Miniperl" من miniperlmain.c في الوقت المحدد، لذلك أنت
ينبغي أن تجعل بيرل لمتابعة هذا على طول.

أولا، بيرلمان.ج يخصص بعض الذاكرة ويبني مترجم بيرل، على طول هذه
خطوط:

1 PERL_SYS_INIT3(&argc,&argv,&env);
2
3 إذا (!PL_do_undump) {
4 my_perl = perl_alloc();
5 إذا (!my_perl)
6 خروج(1)
7 perl_construct(my_perl);
8 PL_perl_destruct_level = 0;
9}

السطر 1 هو ماكرو، ويعتمد تعريفه على نظام التشغيل لديك. السطر 3
مراجع "PL_do_undump"، متغير عام - جميع المتغيرات العامة في Perl تبدأ بـ
"PL_". يخبرك هذا ما إذا كان البرنامج الجاري تشغيله قد تم إنشاؤه باستخدام العلامة "-u".
إلى بيرل وبعد ذلك تفريغ، مما يعني أنه سيكون كاذبًا في أي سياق عاقل.

السطر 4 يستدعي وظيفة في بيرل.c لتخصيص الذاكرة لمترجم بيرل. انها تماما أ
وظيفة بسيطة، والشجاعة منها تبدو كما يلي:

my_perl = (PerlInterpreter*)PerlMem_malloc(sizeof(PerlInterpreter));

هنا ترى مثالاً لتجريد نظام بيرل، والذي سنراه لاحقًا:
"PerlMem_malloc" هو إما "malloc" الخاص بنظامك، أو "malloc" الخاص بـ Perl كما هو محدد في
malloc.c إذا قمت بتحديد هذا الخيار في وقت التكوين.

بعد ذلك، في السطر 7، نقوم ببناء المترجم باستخدام perl_construct، أيضًا في بيرل.c. هذه
يقوم بإعداد كافة المتغيرات الخاصة التي يحتاجها Perl، والمكدسات، وما إلى ذلك.

الآن نقوم بتمرير خيارات سطر الأوامر لـ Perl، ونطلب منه المضي قدمًا:

exitstatus = perl_parse(my_perl, xs_init, argc, argv, (char **)NULL);
إذا (! حالة الخروج)
perl_run(my_perl);

حالة الخروج = perl_destruct(my_perl);

بيرل_فري(my_perl);

"perl_parse" هو في الواقع غلاف حول "S_parse_body"، كما هو محدد في بيرل.c، التي
يعالج خيارات سطر الأوامر، ويقوم بإعداد أي وحدات XS مرتبطة بشكل ثابت، ويفتح ملف
البرنامج ويستدعي "yyparse" لتحليله.

توزيع
الهدف من هذه المرحلة هو أخذ مصدر Perl وتحويله إلى شجرة العمليات. سوف نرى
كيف يبدو أحد هؤلاء لاحقًا. بالمعنى الدقيق للكلمة، هناك ثلاثة أشياء تحدث هنا.

"yyparse"، المحلل اللغوي، يعيش في بيرلي.ج، على الرغم من أنه من الأفضل أن تقرأ النص الأصلي
مدخلات YACC perly.y. (نعم، فيرجينيا، هناك is قواعد YACC لـ Perl!) وظيفة
المحلل اللغوي هو أخذ الكود الخاص بك و "فهمه" وتقسيمه إلى جمل واتخاذ القرار
ما هي المعاملات التي تتوافق مع أي عوامل تشغيل وما إلى ذلك.

يتم مساعدة المحلل اللغوي بشكل نبيل من قبل المعجم، الذي يقوم بتقطيع مدخلاتك إلى رموز مميزة، و
يقرر نوع الشيء الذي يمثله كل رمز مميز: اسم متغير، عامل تشغيل، كلمة مجردة، أ
روتين فرعي، وظيفة أساسية، وما إلى ذلك. نقطة الدخول الرئيسية إلى المعجم هي "yylex"،
ويمكن العثور على ذلك والإجراءات المرتبطة به في toke.c. بيرل ليس مثل الآخرين
لغات الكمبيوتر؛ إنها حساسة للغاية للسياق في بعض الأحيان، وقد يكون من الصعب العمل بها
ما هو نوع الرمز المميز لشيء ما، أو أين ينتهي الرمز المميز. على هذا النحو، هناك الكثير من
التفاعل بين مُرمز الرموز والمحلل، والذي يمكن أن يصبح مخيفًا جدًا إذا كنت كذلك
لم تستخدم لذلك.

عندما يفهم المحلل اللغوي برنامج Perl، فإنه يبني شجرة عمليات لـ
مترجم لأداء أثناء التنفيذ. الروتين الذي يبني ويربط معًا
العمليات المختلفة يمكن العثور عليها في مرجع سابق، وسيتم فحصها لاحقا.

التحسين
الآن اكتملت مرحلة التحليل، وتمثل الشجرة النهائية العمليات التي
يحتاج مترجم Perl إلى تنفيذ برنامجنا. بعد ذلك، يقوم بيرل بإجراء تجربة تجريبية
فوق الشجرة بحثًا عن التحسينات: ستكون التعبيرات الثابتة مثل "3 + 4".
يتم حسابه الآن، وسيرى المُحسِّن أيضًا ما إذا كان يمكن استبدال أي عمليات متعددة
مع واحد. على سبيل المثال، لجلب المتغير $foo، بدلاً من الاستيلاء على الكرة الأرضية
*foo وبالنظر إلى المكون العددي، يقوم المُحسِّن بتلاعب شجرة op لاستخدام a
الوظيفة التي تبحث مباشرة عن العددية المعنية. المحسن الرئيسي هو "زقزقة" في
مرجع سابقوالعديد من العمليات لها وظائف التحسين الخاصة بها.

الركض
نحن الآن جاهزون أخيرًا للانطلاق: لقد قمنا بتجميع كود Perl بايت، وكل ما تبقى للقيام به
يتم تشغيله. يتم التنفيذ الفعلي بواسطة وظيفة "runops_standard" في تشغيل.ج؛ أكثر
على وجه التحديد، يتم ذلك من خلال هذه الخطوط الثلاثة البريئة المظهر:

بينما ((PL_op = PL_op->op_ppaddr(aTHX))) {
PERL_ASYNC_CHECK();
}

قد تكون أكثر راحة مع إصدار Perl لذلك:

PERL_ASYNC_CHECK() while $Perl::op = &{$Perl::op->{function}};

حسنا، ربما لا. على أية حال، تحتوي كل عملية على مؤشر وظيفة، والذي ينص على
الوظيفة التي ستقوم بالفعل بتنفيذ العملية. هذه الوظيفة سوف ترجع في اليوم التالي
op في التسلسل - وهذا يسمح لأشياء مثل "if" التي تختار العملية التالية ديناميكيًا
في وقت التشغيل. يتأكد "PERL_ASYNC_CHECK" من مقاطعة أشياء مثل الإشارات
التنفيذ إذا لزم الأمر.

تُعرف الوظائف الفعلية التي يتم استدعاؤها باسم PP code، وهي موزعة بين أربعة ملفات:
pp_hot.c يحتوي على الكود "السريع" الذي يتم استخدامه في أغلب الأحيان ويتم تحسينه بدرجة كبيرة، pp_sys.c
يحتوي على كافة الوظائف الخاصة بالنظام، pp_ctl.c يحتوي على الوظائف التي
تنفيذ هياكل التحكم ("إذا"، "بينما" وما شابه ذلك) و ص.ج يحتوي على كل شيء
آخر. هذه هي، إذا أردت، رمز C للوظائف والمشغلات المضمنة في Perl.

لاحظ أنه من المتوقع أن تقوم كل دالة "pp_" بإرجاع مؤشر إلى العملية التالية. يدعو ل
تتم معالجة غواصات Perl (وكتل التقييم) ضمن نفس حلقة التشغيل ولا تستهلك
مساحة إضافية على المكدس C. على سبيل المثال، "pp_entersub" و"pp_entertry" فقط اضغط على a
بنية الكتلة "CxSUB" أو "CxEVAL" على مكدس السياق الذي يحتوي على عنوان ملف
المرجع بعد المكالمة الفرعية أو التقييم. ثم يقومون بإرجاع العملية الأولى لذلك الفرعي أو التقييم
block، وبالتالي يستمر تنفيذ تلك القطعة الفرعية أو الكتلة. في وقت لاحق، "pp_leavesub" أو
تقوم عملية "pp_leavetry" بإبراز "CxSUB" أو "CxEVAL"، واسترداد عملية الإرجاع منه، و
يعيدها.

استثناء تسليم
تم إنشاء تسليم الاستثناءات الخاص بـ Perl (أي "الموت" وما إلى ذلك) أعلى المستوى المنخفض
وظائف مكتبة C "setjmp()"/"longjmp()". توفر هذه بشكل أساسي طريقة لالتقاط ملف
سجلات الكمبيوتر الشخصي وSP الحالية واستعادتها لاحقًا؛ على سبيل المثال، يستمر "longjmp()" في
نقطة في الكود حيث تم تنفيذ "setjmp()" السابقة، مع أي شيء آخر في C
كومة يجري فقدانها. ولهذا السبب يجب على الكود دائمًا حفظ القيم باستخدام "SAVE_FOO" بدلاً من "SAVE_FOO".
في المتغيرات التلقائية

يلتف قلب Perl "setjmp()" وما إلى ذلك في وحدات الماكرو "JMPENV_PUSH" و"JMPENV_JUMP". ال
القاعدة الأساسية لاستثناءات بيرل هي أداء "الخروج" و"الموت" (في غياب "التقييم".
a JMPENV_JUMP(2)، في حين أن كلمة "يموت" داخل "التقييم" تفعل ذلك JMPENV_JUMP(3).

عند نقاط الدخول إلى Perl، مثل "perl_parse()" و"perl_run()" و"call_sv(cv, G_EVAL)"
يقوم كل منهم بـ "JMPENV_PUSH"، ثم أدخل حلقة runops أو أي شيء آخر، والتعامل معه ممكن
يعود الاستثناء. بالنسبة للإرجاع مرتين، يتم إجراء التنظيف النهائي، مثل فرقع الأكوام و
استدعاء كتل "التحقق" أو "النهاية". من بين أمور أخرى، هذه هي الطريقة التي لا يزال يتم بها تنظيف النطاق
يحدث أثناء "الخروج".

إذا تمكن "القالب" من العثور على كتلة "CxEVAL" في مكدس السياق، فسيتم دفع المكدس إلى
تم تعيين هذا المستوى وعملية الإرجاع في تلك الكتلة إلى "PL_restartop"؛ ثم
JMPENV_JUMP(٣) يتم. يؤدي هذا عادةً إلى إعادة التحكم إلى الحارس. في هذه الحالة
من "perl_run" و"call_sv"، يؤدي "PL_restartop" غير الفارغ إلى إعادة الدخول إلى عمليات التشغيل
حلقة. هذه هي الطريقة العادية للتعامل مع "الموت" أو "النعيق" ضمن "التقييم".

في بعض الأحيان يتم تنفيذ العمليات ضمن حلقة تشغيل داخلية، مثل التعادل أو الفرز أو التحميل الزائد
شفرة. في هذه الحالة، شيء من هذا القبيل

الجلب الفرعي {التقييم {الموت}}

من شأنه أن يتسبب في عودة longjmp مباشرة إلى الحارس في "perl_run"، مما يؤدي إلى ظهور حلقتي التشغيل،
وهو أمر غير صحيح بشكل واضح. إحدى الطرق لتجنب ذلك هي أن يقوم رمز التعادل بإجراء
"JMPENV_PUSH" قبل تنفيذ "FETCH" في حلقة التشغيل الداخلية، ولكن من أجل الكفاءة
الأسباب، بيرل في الواقع يقوم فقط بتعيين علامة باستخدام "CATCH_SET(TRUE)". "pp_require"،
تحدد العمليات "pp_entereval" و"pp_entertry" هذه العلامة، وإذا كانت صحيحة، فإنها تسمي "docatch"،
الذي يقوم بـ "JMPENV_PUSH" ويبدأ مستوى تشغيل جديد لتنفيذ التعليمات البرمجية، بدلاً من
القيام بذلك على الحلقة الحالية.

كتحسين إضافي، عند الخروج من كتلة التقييم في "FETCH"، يتم تنفيذ
لا يزال يتم تنفيذ الكود الذي يتبع الكتلة في الحلقة الداخلية. عندما يكون الاستثناء
مرفوع، "docatch" يقارن مستوى "JMPENV" لـ "CxEVAL" مع "PL_top_env" وإذا
أنها تختلف، مجرد إعادة رمي الاستثناء. بهذه الطريقة يتم تفجير أي حلقات داخلية.

هنا مثال.

1: إيفال { التعادل @ أ، 'أ' }؛
2: الفرعي أ::TIEARRAY {
3: إيفال {يموت}؛
4: يموت.
5: }

لتشغيل هذا الرمز، يتم استدعاء "perl_run"، والذي يقوم بإجراء "JMPENV_PUSH" ثم يدخل في عملية التشغيل
حلقة. تقوم هذه الحلقة بتنفيذ عمليات التقييم والربط على السطر 1، حيث يقوم التقييم بدفع "CxEVAL"
على مكدس السياق.

يقوم "pp_tie" بإجراء "CATCH_SET(TRUE)"، ثم يبدأ حلقة تشغيل ثانية لتنفيذ الأمر
جسم "TIEARRAY". عند تنفيذ عملية الإدخال في السطر 3، يكون "CATCH_GET" صحيحًا، لذا
يستدعي "pp_entertry" "docatch" الذي يقوم بإجراء "JMPENV_PUSH" ويبدأ حلقة تشغيل ثالثة،
والذي يقوم بعد ذلك بتنفيذ الأمر die Op. عند هذه النقطة، يبدو مكدس استدعاءات C كما يلي:

بيرل_pp_die
Perl_runops # الحلقة الثالثة
S_docatch_body
S_docatch
Perl_pp_entertry
Perl_runops # الحلقة الثانية
S_call_body
بيرل_call_sv
Perl_pp_tie
Perl_runops # الحلقة الأولى
S_run_body
perl_run
رئيسي

والسياق ومكدسات البيانات، كما هو موضح بواسطة "-Dstv"، تبدو كما يلي:

المكدس 0: الرئيسي
CX 0: كتلة =>
CX 1: EVAL => AV() PV("A"\0)
retop=leave
المكدس 1: السحر
CX 0: SUB =>
إعادة الكتابة =(فارغة)
CX 1: التقييم => *
retop=nextstate

يقوم القالب بإخراج أول "CxEVAL" من مكدس السياق، ويقوم بتعيين "PL_restartop" منه، ويقوم بإجراء
JMPENV_JUMP(3)، ويعود التحكم إلى أعلى "docatch". ثم يبدأ هذا ثالثًا آخر
مستوى التشغيل، الذي ينفذ الحالة التالية، وعلامة الدفع، وعمليات الموت على السطر 4. في
النقطة التي يتم استدعاء "pp_die" الثانية فيها، تبدو مكدس استدعاءات C تمامًا كما هو موضح أعلاه،
رغم أننا لم نعد ضمن تقييم داخلي؛ هذا بسبب التحسين
ذكر مسبقا. ومع ذلك، يبدو مكدس السياق الآن بهذا الشكل، أي مع CxEVAL العلوي
برزت:

المكدس 0: الرئيسي
CX 0: كتلة =>
CX 1: EVAL => AV() PV("A"\0)
retop=leave
المكدس 1: السحر
CX 0: SUB =>
إعادة الكتابة =(فارغة)

يقوم القالب الموجود في السطر 4 بإرجاع مكدس السياق إلى CxEVAL، ويتركه على النحو التالي:

المكدس 0: الرئيسي
CX 0: كتلة =>

كالعادة، يتم استخراج "PL_restartop" من ملف "CxEVAL"، وملف JMPENV_JUMP(٣) تم، أي
ينبثق مكدس C مرة أخرى إلى docatch:

S_docatch
Perl_pp_entertry
Perl_runops # الحلقة الثانية
S_call_body
بيرل_call_sv
Perl_pp_tie
Perl_runops # الحلقة الأولى
S_run_body
perl_run
رئيسي

في هذه الحالة، لأن مستوى "JMPENV" المسجل في "CxEVAL" يختلف عن مستوى
الحالي، "docatch" يقوم فقط بـ JMPENV_JUMP(3) ويتم فك المكدس C إلى:

perl_run
رئيسي

نظرًا لأن "PL_restartop" غير فارغ، فإن "run_body" يبدأ حلقة تشغيل وتنفيذ جديدة
لا يزال مستمرا.

الداخلية متغير أنواع
كان ينبغي عليك الآن إلقاء نظرة على perlguts، الذي يخبرك عن معلومات Perl الداخلية
أنواع متغيرة: SVs، HVs، AVs والباقي. إذا لم يكن الأمر كذلك، افعل ذلك الآن.

يتم استخدام هذه المتغيرات ليس فقط لتمثيل متغيرات Perl-space، ولكن أيضًا أي منها
الثوابت في الكود، بالإضافة إلى بعض الهياكل الداخلية تمامًا لـ Perl. الرمز
الجدول، على سبيل المثال، هو تجزئة بيرل عادية. يتم تمثيل الكود الخاص بك بواسطة SV كما هو
قراءة في المحلل اللغوي. يتم فتح أي ملفات برنامج تتصل بها عبر معالجات ملفات Perl العادية،
وما إلى ذلك وهلم جرا.

تتيح لنا وحدة Devel::Peek الأساسية فحص SVs من برنامج Perl. دعونا نرى، ل
على سبيل المثال، كيف يتعامل بيرل مع الثابت "hello".

% بيرل -MDevel::Peek -e 'Dump("hello")'
1 إس في = PV(0xa041450) في 0xa04ecbc
2 ريفكنت = 1
3 أعلام = (POK، للقراءة فقط، PPOK)
4 PV = 0xa0484e0 "مرحبا"\0
5 عملة كورية = 5
6 لين = 6

قراءة مخرجات "Devel::Peek" تتطلب القليل من التدريب، لذلك دعونا نستعرضها سطرًا تلو الآخر.

يخبرنا السطر 1 أننا ننظر إلى SV الذي يعيش في 0xa04ecbc في الذاكرة. SVs أنفسهم
هي هياكل بسيطة جدًا، ولكنها تحتوي على مؤشر إلى بنية أكثر تعقيدًا. في
في هذه الحالة، إنها PV، وهي بنية تحتوي على قيمة سلسلة، في الموقع 0xa041450. خط
2 هو العدد المرجعي؛ لا توجد مراجع أخرى لهذه البيانات، لذلك فهي 1.

السطر 3 عبارة عن علامات لـ SV - لا بأس في استخدامه كـ PV، فهو SV للقراءة فقط (لأنه
إنه ثابت) والبيانات عبارة عن PV داخليًا. التالي لدينا محتويات
السلسلة، بدءًا من الموقع 0xa0484e0.

السطر 5 يعطينا الطول الحالي للسلسلة - لاحظ أن هذا هو الحال ليس تشمل
فاصل فارغ. السطر 6 ليس طول السلسلة، بل طول السلسلة الحالية
المخزن المؤقت المخصص؛ مع نمو السلسلة، تقوم لغة Perl تلقائيًا بتوسيع مساحة التخزين المتوفرة
عبر روتين يسمى "SvGROW".

يمكنك الحصول على أي من هذه الكميات من C بسهولة شديدة؛ فقط أضف "Sv" إلى اسم
الحقل الموضح في المقتطف، ولديك ماكرو سيُرجع القيمة:
"SvCUR(sv)" ترجع الطول الحالي للسلسلة، وترجع "SvREFCOUNT(sv)"
عدد المرجع، "SvPV(sv, len)" يُرجع السلسلة نفسها بطولها، وهكذا.
يمكن العثور على المزيد من وحدات الماكرو لمعالجة هذه الخصائص في perlguts.

لنأخذ مثالاً على معالجة PV، من "sv_catpvn"، في SV.C

1 باطل
2 Perl_sv_catpvn(pTHX_ SV *sv, const char *ptr, STRLEN len)
3 {
4 سترلين تلين؛
5 شار * غير المرغوب فيه؛

6 غير المرغوب فيه = SvPV_force(sv, tlen);
7 SvGROW(sv, tlen + len + 1);
8 إذا (ptr == غير المرغوب فيه)
9 بتر = SvPVX(sv);
10 Move(ptr,SvPVX(sv)+tlen,len,char);
11 SvCUR(sv) += len;
12 *SvEND(sv) = '\0';
13 (باطل)SvPOK_only_UTF8(sv); /* التحقق من صحة المؤشر */
14 سفتانت(سف);
15}

هذه دالة تقوم بإضافة سلسلة "ptr" بطول "len" إلى نهاية PV
المخزنة في "SV". أول شيء نقوم به في السطر 6 هو التأكد من أن SV لديها كهروضوئية صالحة،
عن طريق استدعاء الماكرو "SvPV_force" لفرض PV. كأثر جانبي، يتم ضبط "tlen" على
القيمة الحالية للـ PV، ويتم إرجاع PV نفسها إلى "غير المرغوب فيه".

في السطر 7، نتأكد من أن SV سيكون لديه مساحة كافية لاستيعاب السلسلة القديمة،
السلسلة الجديدة والفاصل الفارغ. إذا لم يكن "LEN" كبيرًا بما يكفي، فإن "SvGROW" سيكون كذلك
إعادة تخصيص المساحة بالنسبة لنا.

الآن، إذا كانت كلمة "غير هامة" هي نفس السلسلة التي نحاول إضافتها، فيمكننا الحصول على السلسلة
مباشرة من SV. "SvPVX" هو عنوان PV في SV.

يقوم السطر 10 بالتسلسل الفعلي: يقوم الماكرو "نقل" بنقل جزء من الذاكرة: نحن
انقل السلسلة "ptr" إلى نهاية PV - وهذه هي بداية PV بالإضافة إلى تيارها
طول. نحن ننقل بايتات "len" من النوع "char". بعد القيام بذلك، علينا أن نخبر بيرل
لقد قمنا بتوسيع السلسلة، عن طريق تغيير "CUR" لتعكس الطول الجديد. "SvEND" هو ماكرو
مما يعطينا نهاية السلسلة، لذلك يجب أن يكون "\0".

السطر 13 يعالج الأعلام؛ نظرًا لأننا قمنا بتغيير PV، فلن يتم تغيير أي قيم IV أو NV
تكون صالحة: إذا كان لدينا "$a=10; $a.="6";" لا نريد استخدام الرابع القديم من 10.
"SvPOK_only_utf8" هو إصدار خاص متوافق مع UTF-8 من "SvPOK_only"، وهو ماكرو يتحول
قم بإيقاف علامتي IOK وNOK وتشغيل POK. "SvTAINT" النهائي هو ماكرو يتم غسله
البيانات الملوثة إذا تم تشغيل وضع الملوث.

تعتبر AVs وHVs أكثر تعقيدًا، لكن SVs هي أكثر أنواع المتغير شيوعًا
القيت حول. بعد أن رأينا شيئًا عن كيفية تعاملنا مع هذه الأشياء، فلنتابع وننظر إليها
كيف يتم بناء شجرة المرجع.

OP الأشجار


أولاً، ما هي شجرة العمليات على أي حال؟ شجرة المرجع هي التمثيل الذي تم تحليله لملفك
البرنامج، كما رأينا في قسمنا الخاص بالتحليل، وهو تسلسل العمليات
يقوم Perl بتنفيذ برنامجك، كما رأينا في "التشغيل".

تعد عملية التشغيل عملية أساسية يمكن لـ Perl تنفيذها: جميع الوظائف والوظائف المضمنة
العوامل هي عمليات، وهناك سلسلة من العمليات التي تتعامل مع مفاهيم المترجم
الاحتياجات داخليًا - الدخول والخروج من الكتلة، وإنهاء العبارة، وجلب المتغير،
وما إلى ذلك وهلم جرا.

ترتبط شجرة العمليات بطريقتين: يمكنك أن تتخيل أن هناك "طريقين" من خلالهما
ذلك، أمران يمكنك من خلالهما اجتياز الشجرة. أولاً، يعكس ترتيب التحليل كيف
يفهم المحلل اللغوي الكود، وثانيًا، يخبر أمر التنفيذ بيرل بالترتيب الذي يجب تنفيذه
العمليات في.

أسهل طريقة لفحص شجرة العمليات هي إيقاف لغة Perl بعد الانتهاء من التحليل
الحصول عليها لتفريغ الشجرة. هذا هو بالضبط ما يقوم به المترجم الخلفي B::Terse،
B::موجز وB::تصحيح القيام بذلك.

دعونا نلقي نظرة على كيف يرى بيرل "$a = $b + $c":

% بيرل -MO=مقتضب -e '$a=$b+$c'
1 ليستوب (0x8179888) إجازة
2 OP (0x81798b0) أدخل
3 COP (0x8179850) الحالة التالية
4 تعيين BINOP (0x8179828).
5 بينوب (0x8179800) إضافة [1]
6 UNOP (0x81796e0) لاغية [15]
7 SVOP (0x80fafe0) gvsv GV (0x80fa4cc) * ب
8 UNOP (0x81797e0) لاغية [15]
9 SVOP (0x8179700) gvsv GV (0x80efeb0) *c
10 UNOP (0x816b4f0) لاغية [15]
11 SVOP (0x816dcf0) gvsv GV (0x80fa460) *أ

لنبدأ من المنتصف، عند السطر 4. هذا هو BINOP، وهو عامل تشغيل ثنائي، موجود عند
الموقع 0x8179828. العامل المحدد المعني هو "تعيين" - تعيين عددي -
ويمكنك العثور على الكود الذي ينفذه في الدالة "pp_ssign" في pp_hot.c. كما
عامل تشغيل ثنائي، وله طفلان: عامل الإضافة، الذي يوفر نتيجة "$b+$c"،
في الأعلى على السطر 5، والجانب الأيسر على السطر 10.

السطر 10 هو المرجع الفارغ: هذا لا يفعل شيئًا على الإطلاق. ماذا يفعل هناك؟ إذا رأيت
المرجع الفارغ، إنها علامة على أنه تم تحسين شيء ما بعد التحليل. كما نحن
المذكورة في "التحسين"، تقوم مرحلة التحسين أحيانًا بتحويل عمليتين إلى
واحد، على سبيل المثال عند جلب متغير عددي. عندما يحدث هذا، بدلا من إعادة الكتابة
شجرة العمليات وتنظيف المؤشرات المتدلية، فمن الأسهل استبدالها
عملية زائدة عن الحاجة مع المرجع الفارغ. في الأصل، كانت الشجرة تبدو هكذا:

10 سفوب (0x816b4f0) rv2sv [15]
11 SVOP (0x816dcf0) جي في جي في (0x80fa460) *أ

أي، قم بإحضار الإدخال "a" من جدول الرموز الرئيسي، ثم انظر إلى العددية
المكون منه: "gvsv" ("pp_gvsv" في pp_hot.c) يحدث للقيام بكلا هذين الأمرين.

الجانب الأيمن، بدءًا من السطر 5 يشبه ما رأيناه للتو: لدينا
"add" op ("pp_add" موجود أيضًا pp_hot.c) قم بإضافة اثنين من "gvsv".

الآن، ما هذا؟

1 ليستوب (0x8179888) إجازة
2 OP (0x81798b0) أدخل
3 COP (0x8179850) الحالة التالية

"الدخول" و"المغادرة" هما عمليتا تحديد النطاق، ومهمتهما هي أداء أي عملية تنظيف في كل مرة
الوقت الذي تدخل فيه وتترك الكتلة: يتم ترتيب المتغيرات المعجمية، والمتغيرات غير المرجعية
يتم تدميرها، وما إلى ذلك. سيكون لكل برنامج تلك الأسطر الثلاثة الأولى: "المغادرة" هي عبارة عن أ
القائمة، وأبناءها هم كافة البيانات الموجودة في الكتلة. يتم تحديد العبارات بواسطة
"nextstate"، لذا فإن الكتلة عبارة عن مجموعة من عمليات "nextstate"، مع العمليات التي سيتم تنفيذها
لكل عبارة أبناء "الحالة التالية". "أدخل" هو عملية واحدة
يعمل كعلامة.

هذه هي الطريقة التي قام بها بيرل بتحليل البرنامج، من الأعلى إلى الأسفل:

البرنامج
|
ملخص الحساب
|
=
/ \
/ \
$أ+
/ \
$ ب $ ج

ومع ذلك، فإنه من المستحيل نفذ العمليات بهذا الترتيب: عليك العثور على
قيم $b و$c قبل إضافتهما معًا، على سبيل المثال. لذلك، الخيط الآخر ذلك
يتم تنفيذ الأمر من خلال شجرة العمليات: كل عملية لها حقل "op_next" الذي
يشير إلى العملية التالية التي سيتم تشغيلها، لذا فإن اتباع هذه المؤشرات يخبرنا بكيفية تنفيذ لغة Perl
الرمز. يمكننا اجتياز الشجرة بهذا الترتيب باستخدام خيار "exec" إلى "B::Terse":

% بيرل -MO=Terse,exec -e '$a=$b+$c'
1 OP (0x8179928) أدخل
2 COP (0x81798c8) الحالة التالية
3 SVOP (0x81796c8) gvsv GV (0x80fa4d4) * ب
4 SVOP (0x8179798) gvsv GV (0x80efeb0) *c
5 بينوب (0x8179878) إضافة [1]
6 SVOP (0x816dd38) gvsv GV (0x80fa468) *أ
7 تعيين BINOP (0x81798a0).
8 ليستوب (0x8179900) إجازة

ربما يكون هذا أكثر منطقية بالنسبة للإنسان: أدخل كتلة، ابدأ بيانًا. احصل على ال
قيمتي $b و$c، وأجمعهما معًا. ابحث عن $a وقم بتعيين أحدهما للآخر. ثم
غادر.

يمكن كشف الطريقة التي يبني بها بيرل هذه الأشجار في عملية التحليل
دراسة perly.y، قواعد YACC. لنأخذ القطعة التي نحتاجها لبناء الشجرة
لـ "$a = $b + $c"

1 مصطلح : مصطلح ASSIGNOP مصطلح
2 { $$ = newASSIGNOP(OPf_STACKED, $1, $2, $3); }
3 | مصطلح ADDOP
4 { $$ = newBINOP($2, 0, العددية($1), العددية($3)); }

إذا لم تكن معتادًا على قراءة القواعد النحوية لـ BNF، فهذه هي الطريقة التي تعمل بها: يتم تغذيتك بالتأكد من ذلك
الأشياء بواسطة الرمز المميز، والتي تنتهي عمومًا بأحرف كبيرة. هنا، يتم توفير "ADDOP".
عندما يرى صانع الرمز المميز "+" في التعليمات البرمجية الخاصة بك. يتم توفير "ASSIGNOP" عند استخدام "=" لـ
تعيين. هذه هي "الرموز النهائية"، لأنه لا يمكنك الحصول على أي شيء أبسط منها.

تخبرك القواعد النحوية، في السطرين الأول والثالث من المقتطف أعلاه، بكيفية بناء المزيد
أشكال معقدة. يتم وضع هذه الأشكال المعقدة، "الرموز غير الطرفية"، بشكل عام في الأسفل
قضية. "المصطلح" هنا هو رمز غير طرفي، يمثل تعبيرًا واحدًا.

تمنحك القواعد القاعدة التالية: يمكنك عمل الشيء الموجود على يسار القولون
إذا رأيت كل الأشياء على اليمين بالتسلسل. وهذا ما يسمى "التخفيض"، و
الهدف من التحليل هو تقليل المدخلات تمامًا. هناك عدة طرق مختلفة يمكنك القيام بها
إجراء تصغير، مفصولة بأشرطة عمودية: لذا، "مصطلح" متبوعًا بـ "= متبوعًا بـ
"مصطلح" يصنع "مصطلحًا"، و"مصطلحًا" متبوعًا بـ "+" متبوعًا بـ "مصطلحًا" يمكنه أيضًا إنشاء "مصطلح".
"شرط".

لذا، إذا رأيت مصطلحين بينهما علامة "=" أو "+"، فيمكنك تحويلهما إلى مصطلح واحد
تعبير. عند القيام بذلك، يمكنك تنفيذ التعليمات البرمجية الموجودة في الكتلة في السطر التالي: if you
راجع "="، فسوف تقوم بتنفيذ الكود الموجود في السطر 2. إذا رأيت "+"، فسوف تقوم بتنفيذ الكود الموجود في السطر 4. إنه
هذا الكود الذي يساهم في شجرة المرجع.

| مصطلح ADDOP
{ $$ = newBINOP($2, 0, العددية($1), العددية($3)); }

ما يفعله هذا هو إنشاء عملية ثنائية جديدة، وإطعامها بعدد من المتغيرات. ال
تشير المتغيرات إلى الرموز المميزة: $1 هو الرمز المميز الأول في الإدخال، و$2 هو الرمز الثاني، وهكذا
on - فكر في المراجع الخلفية للتعبير العادي. $$ هو المرجع الذي تم إرجاعه من هذا التخفيض.
لذلك، فإننا نسمي "newBINOP" لإنشاء عامل ثنائي جديد. المعلمة الأولى إلى "newBINOP"،
وظيفة في مرجع سابق، هو نوع المرجع. إنه عامل إضافة، لذلك نريد أن يكون النوع
"إضافة". يمكننا تحديد هذا مباشرة، ولكنه موجود كرمز ثانٍ في ملف
المدخلات، لذلك نستخدم $2. المعلمة الثانية هي أعلام العملية: 0 يعني "لا شيء خاص".
ثم الأشياء التي يجب إضافتها: الجانب الأيسر والأيمن من التعبير، في السياق العددي.

مكدسات


عندما ينفذ Perl شيئًا مثل "addop"، كيف ينقل نتائجه إلى العملية التالية؟
الجواب هو من خلال استخدام المداخن. لدى Perl عدد من الأكوام لتخزين الأشياء
نعمل عليها حاليًا، وسنلقي نظرة على أهم ثلاث منها هنا.

حجة كومة
يتم تمرير الوسائط إلى كود PP وإرجاعها من كود PP باستخدام مكدس الوسائط "ST".
الطريقة النموذجية للتعامل مع الحجج هي إخراجها من المكدس، والتعامل معها بالطريقة التي تريدها
ترغب فيه، ثم ادفع النتيجة مرة أخرى إلى المكدس. هذه هي الطريقة، على سبيل المثال، جيب التمام
يعمل المشغل:

قيمة نيفادا؛
القيمة = بوبن؛
value = Perl_cos(value);
XPUSHn(value);

سنرى مثالًا أكثر صعوبة على ذلك عندما ننظر إلى وحدات ماكرو Perl أدناه. "POPn" يعطي
لديك NV (قيمة النقطة العائمة) لأعلى SV في المكدس: $x في "cos($x)". بعدها نحن
حساب جيب التمام، ودفع النتيجة مرة أخرى باعتبارها NV. "X" في "XPUSHn" يعني أن
يجب تمديد المكدس إذا لزم الأمر - لا يمكن أن يكون ضروريًا هنا، لأننا نعلم
هناك مساحة لعنصر آخر في المكدس، بما أننا قمنا بإزالة عنصر واحد للتو! "XPUSH*"
وحدات الماكرو على الأقل تضمن السلامة.

وبدلاً من ذلك، يمكنك التلاعب بالمكدس مباشرةً: يمنحك "SP" العنصر الأول فيه
الجزء الخاص بك من المكدس، و"TOP*" يمنحك أعلى SV/IV/NV/إلخ. على المكدس. لذا،
على سبيل المثال، للقيام بالنفي الأحادي لعدد صحيح:

سيتي (-TOPi)؛

ما عليك سوى تعيين القيمة الصحيحة لإدخال المكدس العلوي على نفيها.

إن معالجة مكدس الوسيطات في النواة هي نفسها تمامًا كما هي في XSUBs - انظر
perlxstut وperlxs وperlguts للحصول على وصف أطول لوحدات الماكرو المستخدمة في المكدس
بمعالجة.

علامة كومة
أقول "الجزء الخاص بك من المكدس" أعلاه لأن كود PP لا يحصل بالضرورة على الكل
مكدس إلى نفسه: إذا كانت وظيفتك تستدعي وظيفة أخرى، فستحتاج فقط إلى الكشف عن ملف
الحجج التي تستهدف الوظيفة المطلوبة، وليس (بالضرورة) السماح لها بالحصول على الخاص بك
بيانات. الطريقة التي نقوم بها بذلك هي الحصول على الجزء السفلي "الافتراضي" من المكدس، مكشوفًا لكل منها
وظيفة. تحتفظ مجموعة العلامات بالإشارات المرجعية للمواقع الموجودة في مجموعة الوسائط التي يمكن استخدامها بواسطة كل منها
وظيفة. على سبيل المثال، عند التعامل مع متغير مرتبط، (داخليًا، شيء بحرف "P"
السحر) يجب على Perl استدعاء طرق الوصول إلى المتغيرات المرتبطة. ومع ذلك، نحن بحاجة إلى
افصل الوسائط المكشوفة للطريقة عن الوسيطة المكشوفة للأصل
وظيفة - مخزن أو جلب أو أيا كان. وإليك تقريبًا كيفية "الدفع" المربوط
تم تنفيذه؛ راجع "av_push" في av.c:

1 علامة ضغط (SP)؛
2 تمديد (SP،2)؛
3 دفعات(SvTIED_obj((SV*)av, mg));
4 دفعات (فال)؛
5 ارتداد؛
6 أدخل؛
7 call_method("PUSH", G_SCALAR|G_DISCARD);
8 إجازة؛

دعونا نفحص التنفيذ بأكمله، من أجل الممارسة:

1 علامة ضغط (SP)؛

ادفع الحالة الحالية لمؤشر المكدس إلى مكدس العلامات. هذا هو الحال عندما
لقد انتهينا من إضافة العناصر إلى مكدس الوسائط، ويعرف Perl عدد العناصر التي أضفناها
في الآونة الأخيرة.

2 تمديد (SP،2)؛
3 دفعات(SvTIED_obj((SV*)av, mg));
4 دفعات (فال)؛

سنقوم بإضافة عنصرين آخرين إلى مكدس الوسيطات: عندما يكون لديك مصفوفة مرتبطة، فإن
يستقبل الروتين الفرعي "PUSH" الكائن والقيمة المراد دفعها، وهذا هو بالضبط ما يحدث
لدينا هنا - الكائن المرتبط، الذي تم استرداده باستخدام "SvTIED_obj"، والقيمة، SV "val".

5 ارتداد؛

بعد ذلك نطلب من Perl تحديث مؤشر المكدس العام من المتغير الداخلي لدينا: "dSP"
أعطانا فقط نسخة محلية، وليس إشارة إلى العالمية.

6 أدخل؛
7 call_method("PUSH", G_SCALAR|G_DISCARD);
8 إجازة؛

يقوم "ENTER" و"LEAVE" بترجمة كتلة من التعليمات البرمجية - حيث يتأكدون من أن جميع المتغيرات موجودة
بعد ترتيبه، يتم إرجاع كل ما تم ترجمته إلى قيمته السابقة، وهكذا.
فكر فيهما على أنهما "{" و"}" لكتلة Perl.

لإجراء استدعاء الطريقة السحرية، علينا استدعاء روتين فرعي في مساحة بيرل:
"call_method" يعتني بذلك، وهو موضح في perlcall. نحن نسمي "الدفع"
الطريقة في سياق العددي، ونحن في طريقنا لتجاهل قيمة الإرجاع الخاصة به. ال call_method()
تقوم الوظيفة بإزالة العنصر العلوي من مكدس العلامات، لذلك لا يوجد شيء يمكن للمتصل القيام به
نظف.

حفظ كومة
لا تحتوي لغة C على مفهوم النطاق المحلي، لذا توفر لغة Perl واحدًا. لقد رأينا أن "أدخل" و
تُستخدم كلمة "LEAVE" كأقواس تحديد النطاق؛ تنفذ مكدس الحفظ المكافئ C لـ
مثال:

{
محلي $foo = 42؛

}

راجع "ترجمة التغييرات" في perlguts لمعرفة كيفية استخدام مكدس الحفظ.

ملايين OF ماكروس


شيء واحد ستلاحظه بخصوص مصدر Perl هو أنه مليء بوحدات الماكرو. البعض لديه
يُطلق على الاستخدام الواسع النطاق لوحدات الماكرو أنه أصعب شيء يمكن فهمه، بينما يجده الآخرون أنه يزيد من صعوبة فهمه
وضوح. لنأخذ مثالاً، الكود الذي ينفذ عامل الإضافة:

1 ب (pp_add)
2 {
3 دي إس بي ؛ مجموعة البيانات ؛ tryAMAGICbin (إضافة ، opASSIGN) ؛
4 {
5 dPOPTOPnnrl_ul;
6 سيتن (يسار + يمين)؛
7 العودة؛
8}
9}

كل سطر هنا (باستثناء الأقواس، بالطبع) يحتوي على ماكرو. يحدد السطر الأول
قم بإعداد إعلان الوظيفة كما يتوقع Perl لرمز PP؛ السطر 3 يقوم بإعداد المتغير
إعلانات مكدس الوسيطة والهدف، القيمة المرجعة للعملية.
وأخيرًا، يحاول معرفة ما إذا كانت عملية الإضافة مثقلة أم لا؛ إذا كان الأمر كذلك، المناسب
يسمى الروتين الفرعي .

السطر 5 هو إعلان متغير آخر - جميع تعريفات المتغيرات تبدأ بالحرف "d" - والذي
ينبثق من أعلى الوسيطة مكدس NVs (ومن ثم "nn") ويضعهما في ملف
المتغيرات "يمين" و"يسار"، ومن هنا جاء "rl". هذان هما المعاملان لعملية الجمع
المشغل أو العامل. بعد ذلك، نسمي "SETn" لتعيين NV لقيمة الإرجاع لنتيجة الإضافة
القيمتين. بعد ذلك، نعود - يتأكد الماكرو "RETURN" من قيمة الإرجاع لدينا
تتم معالجتها بشكل صحيح، ونمرر العامل التالي للرجوع إلى حلقة التشغيل الرئيسية.

يتم شرح معظم وحدات الماكرو هذه في perlapi، وبعضها الأكثر أهمية
وأوضح في perlxs كذلك. انتبه بشكل خاص إلى "الخلفية و
PERL_IMPLICIT_CONTEXT" في perlguts للحصول على معلومات حول وحدات الماكرو "[pad]THX_؟".

بالإضافة إلى ذلك قراءة


لمزيد من المعلومات حول الأجزاء الداخلية لـ Perl، يرجى الاطلاع على المستندات المدرجة في "Internals
وواجهة لغة C" في بيرل.

استخدم perlinterp عبر الإنترنت باستخدام خدمات onworks.net


خوادم ومحطات عمل مجانية

قم بتنزيل تطبيقات Windows و Linux

  • 1
    يونايتد آر بي إم إس
    يونايتد آر بي إم إس
    انضم إلينا في Gitter!
    https://gitter.im/unitedrpms-people/Lobby
    قم بتمكين مستودع URPMS في ملف
    النظام -
    https://github.com/UnitedRPMs/unitedrpms.github.io/bl...
    تحميل unitedrpms
  • 2
    تعزيز مكتبات C ++
    تعزيز مكتبات C ++
    يوفر Boost محمولًا مجانيًا
    مكتبات C ++ خاضعة لاستعراض الأقران. ال
    ينصب التركيز على المكتبات المحمولة التي
    تعمل بشكل جيد مع مكتبة C ++ القياسية.
    انظر http: //www.bo ...
    تنزيل Boost C ++ Libraries
  • 3
    برنامج VirtualGL
    برنامج VirtualGL
    يقوم برنامج VirtualGL بإعادة توجيه الأوامر ثلاثية الأبعاد من ملف
    تطبيق Unix / Linux OpenGL على ملف
    GPU من جانب الخادم ويقوم بتحويل ملف
    عرض صور ثلاثية الأبعاد في دفق فيديو
    مع ماذا ...
    تنزيل برنامج VirtualGL
  • 4
    libusb
    libusb
    مكتبة لتمكين مساحة المستخدم
    برامج تطبيقية للتواصل معها
    جهاز USB:٪ s. الجمهور: المطورين ، النهاية
    المستخدمون / سطح المكتب. لغة البرمجة: C.
    فئات...
    تنزيل libusb
  • 5
    جرعة كبيرة
    جرعة كبيرة
    SWIG هي أداة لتطوير البرمجيات
    يربط البرامج المكتوبة باللغتين C و
    C ++ مع مجموعة متنوعة من المستويات العالية
    لغات البرمجة. يستخدم SWIG مع
    مختلف...
    تنزيل SWIG
  • 6
    موضوع WooCommerce Nextjs React
    موضوع WooCommerce Nextjs React
    React WooCommerce theme ، الذي تم إنشاؤه باستخدام
    التالي JS و Webpack و Babel و Node و
    Express ، باستخدام GraphQL و Apollo
    عميل. متجر WooCommerce في React (
    يحتوي على: المنتجات ...
    قم بتنزيل WooCommerce Nextjs React Theme
  • أكثر "

أوامر لينكس

Ad