ภาษาอังกฤษอาหารฝรั่งเศสสเปน

ไอคอน Fav ของ OnWorks

perlinterp - ออนไลน์ในคลาวด์

เรียกใช้ perlinterp ในผู้ให้บริการโฮสต์ฟรีของ OnWorks ผ่าน Ubuntu Online, Fedora Online, โปรแกรมจำลองออนไลน์ของ Windows หรือโปรแกรมจำลองออนไลน์ของ MAC OS

นี่คือคำสั่ง perlinterp ที่สามารถเรียกใช้ในผู้ให้บริการโฮสต์ฟรีของ OnWorks โดยใช้หนึ่งในเวิร์กสเตชันออนไลน์ฟรีของเรา เช่น Ubuntu Online, Fedora Online, โปรแกรมจำลองออนไลน์ของ Windows หรือโปรแกรมจำลองออนไลน์ของ MAC OS

โครงการ:

ชื่อ


perlinterp - ภาพรวมของล่าม Perl

DESCRIPTION


เอกสารนี้ให้ภาพรวมเกี่ยวกับวิธีการทำงานของล่าม Perl ที่ระดับ C
รหัสพร้อมกับตัวชี้ไปยังไฟล์ซอร์สโค้ด C ที่เกี่ยวข้อง

ELEMENTS OF DIE ล่าม


การทำงานของล่ามมีสองขั้นตอนหลัก: รวบรวมรหัสเข้าภายใน
การแทนค่าหรือ bytecode แล้วดำเนินการ "โค้ดที่คอมไพล์แล้ว" ในภาษา Perlguts อธิบาย
ขั้นตอนการรวบรวมเกิดขึ้นได้อย่างไร

นี่คือรายละเอียดสั้น ๆ ของการทำงานของ Perl:

การเริ่มต้น
การดำเนินการเริ่มต้นใน เพิร์ลเมน.ซี. (หรือ มินิเพิร์ลเมน.ซี สำหรับ miniperl) นี่เป็นระดับที่สูงมาก
รหัสมากพอที่จะใส่ได้บนหน้าจอเดียว และมันคล้ายกับรหัสที่พบใน perlembed; ที่สุด
ของการกระทำจริงเกิดขึ้นใน เพิร์ล.ซี

เพิร์ลเมน.ซี ถูกสร้างขึ้นโดย "ExtUtils::Miniperl" จาก มินิเพิร์ลเมน.ซี ในเวลา ดังนั้นคุณ
ควรทำ Perl ให้ทำตามนี้

ประการแรก เพิร์ลเมน.ซี จัดสรรหน่วยความจำบางส่วนและสร้างล่าม Perl ตามเหล่านี้
บรรทัด:

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" หรือไม่
เพื่อ perl แล้ว unumpซึ่งหมายความว่ามันจะเป็นเท็จในบริบทที่เหมาะสม

บรรทัดที่ 4 เรียกใช้ฟังก์ชันใน เพิร์ล.ซี เพื่อจัดสรรหน่วยความจำสำหรับล่าม Perl มันค่อนข้างเ
ฟังก์ชันเรียบง่าย และความกล้าของมันมีลักษณะดังนี้:

my_perl = (PerlInterpreter*)PerlMem_malloc (ขนาดของ (PerlInterpreter));

คุณจะเห็นตัวอย่างการแยกระบบของ Perl ซึ่งเราจะเห็นในภายหลัง:
"PerlMem_malloc" อาจเป็น "malloc" ของระบบของคุณ หรือ "malloc" ของ Perl ตามที่กำหนดไว้ใน
มอลลอค.ซี หากคุณเลือกตัวเลือกนั้นในเวลาที่กำหนด

ต่อไป ในบรรทัดที่ 7 เราสร้างล่ามโดยใช้ perl_construct เช่นกันใน เพิร์ล.ซี; นี้
ตั้งค่าตัวแปรพิเศษทั้งหมดที่ Perl ต้องการ สแต็ค และอื่นๆ

ตอนนี้เราส่ง Perl ตัวเลือกบรรทัดคำสั่งและบอกให้ไป:

exitstatus = perl_parse (my_perl, xs_init, argc, argv, (ถ่าน **) ​​NULL);
ถ้า (!exitstatus)
perl_run(my_perl);

สถานะทางออก = perl_destruct(my_perl);

perl_free(my_perl);

"perl_parse" จริงๆ แล้วเป็น wrapper รอบ "S_parse_body" ตามที่กำหนดไว้ใน เพิร์ล.ซีซึ่ง
ประมวลผลตัวเลือกบรรทัดคำสั่ง ตั้งค่าโมดูล XS ที่เชื่อมโยงแบบคงที่ เปิด
โปรแกรมและเรียก "yyparse" เพื่อแยกวิเคราะห์

วจีวิภาค
จุดมุ่งหมายของขั้นตอนนี้คือการนำ Perl source มาแปลงเป็น op tree เราจะได้เห็น
สิ่งที่ดูเหมือนในภายหลัง พูดอย่างเคร่งครัด มีสามสิ่งที่เกิดขึ้นที่นี่

"yyparse", parser, อาศัยอยู่ใน เพอร์ลี่.คแม้ว่าคุณจะอ่านต้นฉบับดีกว่า
อินพุต YACC ใน Perly.y. (ใช่ เวอร์จิเนีย ที่นั่น is ไวยากรณ์ YACC สำหรับ Perl!) งานของ
parser คือการนำโค้ดของคุณและ "เข้าใจ" มา แบ่งเป็นประโยค ตัดสินใจ
ตัวถูกดำเนินการใดไปกับตัวดำเนินการใดเป็นต้น

parser ได้รับความช่วยเหลืออย่างสูงส่งโดย lexer ซึ่งแบ่งข้อมูลที่คุณป้อนเป็นโทเค็น และ
ตัดสินใจว่าแต่ละโทเค็นคืออะไร: ชื่อตัวแปร, โอเปอเรเตอร์, คำเปล่า, a
รูทีนย่อย ฟังก์ชันหลัก และอื่นๆ ประเด็นหลักในการเข้าสู่ lexer คือ "yylex"
และนั่นและกิจวัตรที่เกี่ยวข้องสามารถพบได้ใน โทค.ซี. Perl ไม่เหมือนที่อื่นมาก
ภาษาคอมพิวเตอร์ มีความอ่อนไหวต่อบริบทสูงในบางครั้ง อาจเป็นเรื่องยากที่จะฝึกฝน
โทเค็นประเภทใด หรือโทเค็นสิ้นสุดที่ใด แบบนี้ก็มีมากมาย
การทำงานร่วมกันระหว่าง tokeniser และ parser ซึ่งอาจดูน่ากลัวหากคุณ
ไม่คุ้นเคยกับมัน

เนื่องจาก parser เข้าใจโปรแกรม Perl มันจึงสร้างแผนผังของการดำเนินงานสำหรับ
ล่ามเพื่อดำเนินการในระหว่างการดำเนินการ กิจวัตรที่สร้างและเชื่อมโยงกัน
จะพบการดำเนินการต่างๆ ใน สหกรณ์และจะได้รับการตรวจสอบในภายหลัง

การเพิ่มประสิทธิภาพ
ตอนนี้ ขั้นตอนการแยกวิเคราะห์เสร็จสมบูรณ์แล้ว และทรีที่เสร็จสิ้นแล้วแสดงถึงการดำเนินการที่
ล่าม Perl จำเป็นต้องดำเนินการเพื่อรันโปรแกรมของเรา ต่อไป Perl จะทำแห้ง
เหนือต้นไม้มองหาการเพิ่มประสิทธิภาพ: นิพจน์คงที่เช่น "3 + 4" จะเป็น
คำนวณแล้วและเครื่องมือเพิ่มประสิทธิภาพจะดูว่าสามารถเปลี่ยนการทำงานหลายอย่างได้หรือไม่
กับหนึ่งเดียว ตัวอย่างเช่น ในการดึงตัวแปร $foo แทนที่จะจับ glob
*foo และดูที่องค์ประกอบสเกลาร์ เครื่องมือเพิ่มประสิทธิภาพจะเล่นซอต้นไม้ op เพื่อใช้a
ฟังก์ชันที่ค้นหาสเกลาร์ที่เป็นปัญหาโดยตรง เครื่องมือเพิ่มประสิทธิภาพหลักคือ "peep" ใน
สหกรณ์และ ops จำนวนมากมีฟังก์ชันการเพิ่มประสิทธิภาพของตัวเอง

เล่น
ในที่สุดเราก็พร้อมแล้ว: เราได้คอมไพล์โค้ด Perl byte แล้ว และสิ่งที่เหลืออยู่
กำลังทำงานอยู่ การดำเนินการจริงทำได้โดยฟังก์ชัน "runops_standard" ใน รัน.ค; มากกว่า
โดยเฉพาะอย่างยิ่ง มันถูกทำโดยสามเส้นที่ดูไร้เดียงสา:

ในขณะที่ ((PL_op = PL_op->op_ppaddr(aTHX))) {
PERL_ASYNC_CHECK();
}

คุณอาจสบายใจกับเวอร์ชัน Perl มากขึ้น:

PERL_ASYNC_CHECK() ในขณะที่ $Perl::op = &{$Perl::op->{function}};

ดีอาจจะไม่ อย่างไรก็ตาม แต่ละ op มีตัวชี้ฟังก์ชันซึ่งกำหนด
ฟังก์ชั่นที่จะใช้งานจริง ฟังก์ชันนี้จะคืนค่าใน next
op ตามลำดับ - อนุญาตให้ทำสิ่งต่าง ๆ เช่น "if" ซึ่งเลือก op ถัดไปแบบไดนามิก
ในเวลาทำงาน "PERL_ASYNC_CHECK" ทำให้แน่ใจว่าสิ่งต่าง ๆ เช่นสัญญาณขัดจังหวะ
การดำเนินการหากจำเป็น

ฟังก์ชันจริงที่เรียกว่ารหัส PP และกระจายระหว่างสี่ไฟล์:
pp_hot.c มีรหัส "ร้อน" ซึ่งใช้บ่อยที่สุดและปรับให้เหมาะสมที่สุด pp_sys.ค
มีฟังก์ชันเฉพาะระบบทั้งหมด pp_ctl.c ประกอบด้วยฟังก์ชันซึ่ง
ใช้โครงสร้างการควบคุม ("ถ้า", "ในขณะที่" และสิ่งที่คล้ายคลึงกัน) และ pp.c มีทุกอย่าง
อื่น. ถ้าคุณต้องการ โค้ด C สำหรับฟังก์ชันและตัวดำเนินการในตัวของ Perl

โปรดทราบว่าแต่ละฟังก์ชัน "pp_" คาดว่าจะส่งคืนตัวชี้ไปยัง op ถัดไป โทรไป
perl subs (และบล็อก eval) ได้รับการจัดการภายใน runops loop เดียวกันและไม่ใช้
พื้นที่พิเศษบนกอง C ตัวอย่างเช่น "pp_entersub" และ "pp_entertry" เพียงแค่กด a
บล็อก "CxSUB" หรือ "CxEVAL" บนสแต็กบริบทซึ่งมีที่อยู่ของ
op ติดตามการโทรย่อยหรือ eval จากนั้นพวกเขาจะส่งคืน op แรกของ sub หรือ eval
บล็อก และการดำเนินการต่อจากส่วนย่อยหรือบล็อกนั้น ต่อมา "pp_leavesub" หรือ
"pp_leavetry" op แสดง "CxSUB" หรือ "CxEVAL" ดึงข้อมูล op ที่กลับมาจากมันและ
ส่งคืน

ข้อยกเว้น มอบหมาย
การส่งข้อยกเว้นของ Perl (เช่น "ตาย" เป็นต้น) สร้างขึ้นจากระดับล่าง
"setjmp()"/"longjmp()" ฟังก์ชันไลบรารี C โดยพื้นฐานแล้วสิ่งเหล่านี้เป็นวิธีการจับภาพ
PC และ SP ปัจจุบันลงทะเบียนและกู้คืนในภายหลัง เช่น "longjmp()" ดำเนินต่อไปที่
ชี้ในรหัสที่ทำ "setjmp()" ก่อนหน้านี้โดยมีอะไรเพิ่มเติมในC
สแต็คสูญหาย นี่คือเหตุผลที่โค้ดควรบันทึกค่าโดยใช้ "SAVE_FOO" แทน
ในตัวแปรอัตโนมัติ

แกน Perl ล้อม "setjmp ()" ฯลฯ ในมาโคร "JMPENV_PUSH" และ "JMPENV_JUMP" ดิ
กฎพื้นฐานของข้อยกเว้น Perl คือ "ออก" และ "ตาย" (ในกรณีที่ไม่มี "eval")
a JMPENV_JUMP(2) ในขณะที่ "ตาย" ภายใน "eval" ทำ JMPENV_JUMP(3)

ที่จุดเริ่มต้นไปยัง perl เช่น "perl_parse()", "perl_run()" และ "call_sv(cv, G_EVAL)"
แต่ละตัวทำ "JMPENV_PUSH" จากนั้นป้อน runops loop หรืออะไรก็ตามและจัดการได้
ผลตอบแทนข้อยกเว้น สำหรับการส่งคืน 2 ครั้ง จะมีการดำเนินการล้างขั้นสุดท้าย เช่น popping stacks และ
เรียกบล็อค "CHECK" หรือ "END" เหนือสิ่งอื่นใด นี่คือวิธีการล้างขอบเขตที่ยังคง
เกิดขึ้นในระหว่างการ "ออก"

หาก "die" สามารถค้นหาบล็อก "CxEVAL" บนคอนเท็กซ์สแต็กได้ สแต็กจะถูกเปิดขึ้น
ระดับนั้นและการดำเนินการส่งคืนในบล็อกนั้นถูกกำหนดเป็น "PL_restartop"; แล้ว a
JMPENV_JUMP(3) ดำเนินการแล้ว โดยปกติแล้วจะส่งการควบคุมกลับไปที่การ์ด ในกรณี
ของ "perl_run" และ "call_sv", "PL_restartop" ที่ไม่ใช่ค่าว่างจะทริกเกอร์การเข้าสู่ runops อีกครั้ง
ห่วง เป็นเรื่องปกติที่ "ตาย" หรือ "บ่น" ถูกจัดการภายใน "eval"

บางครั้ง ops จะถูกดำเนินการภายในลูป runops ภายใน เช่น tie, sort หรือ overload
รหัส. ในกรณีนี้ บางอย่างเช่น

ดึงข้อมูลย่อย { eval { ตาย } }

จะทำให้ longjmp กลับไปที่การ์ดใน "perl_run" โดยเปิดลูป runops ทั้งสองอัน
ซึ่งไม่ถูกต้องอย่างชัดเจน วิธีหนึ่งที่จะหลีกเลี่ยงสิ่งนี้คือให้ผูกรหัสทำ
"JMPENV_PUSH" ก่อนดำเนินการ "FETCH" ในลูป runops ภายใน แต่เพื่อประสิทธิภาพ
เหตุผล Perl ในความเป็นจริงเพียงแค่ตั้งค่าสถานะโดยใช้ "CATCH_SET(TRUE)" "pp_require",
"pp_entereval" และ "pp_entertry" ops ตรวจสอบแฟล็กนี้และหากเป็นจริงจะเรียกว่า "docatch"
ซึ่งทำ "JMPENV_PUSH" และเริ่มระดับ runops ใหม่เพื่อรันโค้ดแทนที่จะเป็น
ทำในลูปปัจจุบัน

เพื่อเป็นการเพิ่มประสิทธิภาพเพิ่มเติม เมื่อออกจากบล็อก eval ใน "FETCH" การดำเนินการของ
รหัสที่ตามหลังบล็อกจะยังคงอยู่ในวงใน เมื่อข้อยกเว้นคือ
ยกขึ้น "docatch" เปรียบเทียบระดับ "JMPENV" ของ "CxEVAL" กับ "PL_top_env" และถ้า
พวกเขาต่างกันเพียงแค่โยนข้อยกเว้นอีกครั้ง ด้วยวิธีนี้ลูปภายในจะแตกออก

นี่คือตัวอย่าง

1: eval { เสมอ @a, 'A' };
2: ย่อย A::TIEARRAY {
3: eval { ตาย };
4: ตาย;
5: }

ในการรันโค้ดนี้ จะมีการเรียก "perl_run" ซึ่งทำ "JMPENV_PUSH" จากนั้นจึงเข้าสู่ runops
ห่วง ลูปนี้รัน eval และ tie ops ในบรรทัดที่ 1 โดยที่ eval นั้นกด "CxEVAL"
บนสแต็กบริบท

"pp_tie" ทำ "CATCH_SET(TRUE)" จากนั้นเริ่มลูป runops ที่สองเพื่อดำเนินการ
เนื้อหาของ "TIEARRAY" เมื่อมันรัน entertry op ในบรรทัดที่ 3 "CATCH_GET" จะเป็นจริง ดังนั้น
"pp_entertry" เรียก "docatch" ซึ่งทำ "JMPENV_PUSH" และเริ่มลูป runops ที่สาม
ซึ่งจะดำเนินการตาย op ณ จุดนี้ C call stack จะมีลักษณะดังนี้:

Perl_pp_die
Perl_runops # วงที่สาม
S_doatch_body
S_doatch
Perl_pp_entertry
Perl_runops # วนรอบที่สอง
S_call_body
Perl_call_sv
Perl_pp_tie
Perl_runops # ลูปแรก
S_run_body
perl_run
หลัก

และบริบทและข้อมูลกองดังที่แสดงโดย "-Dstv" มีลักษณะดังนี้:

กอง 0: หลัก
CX 0: บล็อค =>
CX 1: EVAL => AV() PV("A"\0)
retop=ออกจาก
กองที่ 1: มายากล
CX 0: SUB =>
รีท็อป=(null)
CX 1: EVAL => *
retop = สถานะถัดไป

แม่พิมพ์เปิด "CxEVAL" ตัวแรกออกจากบริบทสแต็กตั้งค่า "PL_restartop" จากนั้นทำ
JMPENV_JUMP(3) และการควบคุมกลับสู่ด้านบน "docatch" จากนั้นจะเริ่มอีกสาม-
ระดับ runops ระดับซึ่งดำเนินการ nextstate, pushmark และ die ops ในบรรทัดที่ 4 ที่
ชี้ให้เห็นว่า "pp_die" ตัวที่สองถูกเรียก สแต็คการโทร C มีลักษณะเหมือนกับด้านบน
แม้ว่าเราจะไม่ได้อยู่ในการประเมินภายในอีกต่อไป นี่เป็นเพราะการเพิ่มประสิทธิภาพ
กล่าวถึงก่อนหน้านี้ อย่างไรก็ตาม บริบทสแต็กตอนนี้มีลักษณะเช่นนี้ เช่น CxEVAL . ด้านบน
โผล่:

กอง 0: หลัก
CX 0: บล็อค =>
CX 1: EVAL => AV() PV("A"\0)
retop=ออกจาก
กองที่ 1: มายากล
CX 0: SUB =>
รีท็อป=(null)

ตายในบรรทัดที่ 4 จะแสดงบริบทสแต็กกลับลงไปที่ CxEVAL โดยปล่อยให้เป็น:

กอง 0: หลัก
CX 0: บล็อค =>

ตามปกติ "PL_restartop" จะถูกแยกจาก "CxEVAL" และ a JMPENV_JUMP(3) เสร็จแล้วซึ่ง
แตก C stack กลับไปที่ docatch:

S_doatch
Perl_pp_entertry
Perl_runops # วนรอบที่สอง
S_call_body
Perl_call_sv
Perl_pp_tie
Perl_runops # ลูปแรก
S_run_body
perl_run
หลัก

ในกรณีนี้ เนื่องจากระดับ "JMPENV" ที่บันทึกใน "CxEVAL" นั้นแตกต่างจาก
อันปัจจุบัน "doatch" ก็แค่ทำ JMPENV_JUMP(3) และกอง C คลี่คลายไปที่:

perl_run
หลัก

เนื่องจาก "PL_restartop" ไม่เป็นค่าว่าง "run_body" จึงเริ่มต้นลูป runops ใหม่และดำเนินการ
อย่างต่อเนื่อง

ภายใน ตัวแปร ประเภท
ตอนนี้คุณควรได้ดู perlguts ซึ่งบอกคุณเกี่ยวกับ Perl's internal
ประเภทตัวแปร: SVs, HVs, AVs และส่วนที่เหลือ ถ้าไม่ทำอย่างนั้นตอนนี้

ตัวแปรเหล่านี้ไม่เพียงแต่ใช้แทนตัวแปร Perl-space เท่านั้น แต่ยังใช้ใดๆ อีกด้วย
ค่าคงที่ในโค้ด เช่นเดียวกับโครงสร้างบางส่วนภายใน Perl อย่างสมบูรณ์ สัญลักษณ์
ตัวอย่างเช่น ตาราง เป็นแฮช Perl ธรรมดา รหัสของคุณแสดงโดย SV เนื่องจากเป็น
อ่านใน parser; ไฟล์โปรแกรมใด ๆ ที่คุณเรียกเปิดผ่านไฟล์ Perl ธรรมดา
เป็นต้น

โมดูลหลัก Devel::Peek ช่วยให้เราตรวจสอบ SV จากโปรแกรม Perl มาดูกันสำหรับ
ตัวอย่างเช่น Perl ปฏิบัติต่อค่าคงที่ "สวัสดี" อย่างไร

% perl -MDevel::Peek -e 'Dump("สวัสดี")'
1 เอสวี = PV(0xa041450) ที่ 0xa04ecbc
2 รีเอฟซีเอ็นที = 1
3 ธง = (ป๊อก อ่านอย่างเดียว 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)" จะคืนค่าสตริงตามความยาวของสตริง และอื่นๆ
พบมาโครเพิ่มเติมเพื่อจัดการคุณสมบัติเหล่านี้ได้ใน Perlgut

มาดูตัวอย่างการจัดการ PV จาก "sv_catpvn" ใน เอสวีซี

1 โมฆะ
2 Perl_sv_catpvn(pTHX_ SV *sv, const char *ptr, STRLEN เลน)
3 {
4 STRLEN เทเลน;
5 ตัวอักษร *ขยะ;

6 ขยะ = SvPV_force(sv, tlen);
7 SvGROW(sv, tlen + เลน + 1);
8 ถ้า (ptr == ขยะ)
9 พอยต์ = SvPVX(sv);
10 ย้าย(ptr,SvPVX(sv)+tlen,len,ถ่าน);
11 SvCUR(sv) += เลน;
12 *SvEND(sv) = '\0';
13 (เป็นโมฆะ)SvPOK_only_UTF8(sv); /* ตรวจสอบตัวชี้ */
14 สวีเทน(สวี);
15}

นี่คือฟังก์ชันที่เพิ่มสตริง "ptr" ของความยาว "len" ที่ส่วนท้ายของ PV
เก็บไว้ใน "sv" สิ่งแรกที่เราทำในบรรทัดที่ 6 คือต้องแน่ใจว่า SV มี PV ที่ถูกต้อง
โดยการเรียกมาโคร "SvPV_force" เพื่อบังคับ PV ผลข้างเคียง "tlen" ถูกตั้งค่าเป็น
มูลค่าปัจจุบันของ PV และ PV เองจะถูกส่งกลับเป็น "ขยะ"

ในบรรทัดที่ 7 เราตรวจสอบให้แน่ใจว่า SV จะมีที่ว่างเพียงพอสำหรับสตริงเก่า
สตริงใหม่และตัวสิ้นสุด null ถ้า "LEN" ไม่ใหญ่พอ "SvGROW" จะ
จัดสรรพื้นที่ให้เรา

ทีนี้ ถ้า "ขยะ" เหมือนกับสตริงที่เรากำลังพยายามเพิ่ม เราสามารถคว้าสตริงนั้นได้
โดยตรงจาก SV; "SvPVX" คือที่อยู่ของ PV ใน SV

บรรทัดที่ 10 ทำ catenation จริง: มาโคร "ย้าย" จะย้ายหน่วยความจำบางส่วนไปรอบๆ: เรา
ย้ายสตริง "ptr" ไปที่จุดสิ้นสุดของ PV - นั่นคือจุดเริ่มต้นของ PV บวกกับปัจจุบัน
ระยะเวลา. เรากำลังย้ายไบต์ "len" ของประเภท "char" เสร็จแล้วต้องบอก Perl
เราได้ขยายสตริงโดยเปลี่ยน "CUR" เพื่อสะท้อนความยาวใหม่ "SvEND" เป็นมาโคร
ซึ่งทำให้เราสิ้นสุดสตริง จึงต้องเป็น "\0"

บรรทัดที่ 13 ปรับเปลี่ยนธง; เนื่องจากเราได้เปลี่ยน PV ค่า IV หรือ NV ใดๆ จะไม่
ถูกต้องอีกต่อไป: ถ้าเรามี "$a=10; $a.="6";" เราไม่ต้องการใช้ IV เดิมของ 10
"SvPOK_only_utf8" คือ "SvPOK_only" เวอร์ชันที่ทราบ UTF-8 แบบพิเศษ ซึ่งเป็นมาโครที่จะเปลี่ยน
ปิดแฟล็ก IOK และ NOK และเปิด POK "SvTAINT" สุดท้ายคือมาโครที่ฟอก
ข้อมูลเสียถ้าเปิดโหมดเทนต์

AV และ HV นั้นซับซ้อนกว่า แต่ SV นั้นเป็นตัวแปรประเภททั่วไปมากที่สุด
โยนไปรอบ ๆ เมื่อได้เห็นวิธีจัดการกับสิ่งเหล่านี้แล้ว เรามาดูต่อกันที่
วิธีการสร้าง op tree

OP TREES


อย่างแรกเลย ต้นไม้ op คืออะไร? ต้นไม้ op เป็นตัวแทนการแยกวิเคราะห์ของคุณ
โปรแกรม ดังที่เราเห็นในหัวข้อการแยกวิเคราะห์ และมันเป็นลำดับของการดำเนินการที่
Perl ดำเนินการตามโปรแกรมของคุณ ดังที่เราเห็นใน "การทำงาน"

op คือการดำเนินการพื้นฐานที่ Perl สามารถทำได้: ฟังก์ชันในตัวทั้งหมดและ
ตัวดำเนินการคือ ops และมีชุดของ ops ที่จัดการกับแนวคิดของล่าม
ต้องการภายใน - การเข้าและออกจากบล็อก, การสิ้นสุดคำสั่ง, การดึงตัวแปร,
เป็นต้น

op tree เชื่อมต่อกันในสองวิธี: คุณสามารถจินตนาการได้ว่ามี "เส้นทาง" สองทางผ่าน
มันสองคำสั่งที่คุณสามารถสำรวจต้นไม้ได้ ขั้นแรก ลำดับการแยกวิเคราะห์สะท้อนให้เห็นว่า
parser เข้าใจโค้ด และประการที่สอง คำสั่งดำเนินการบอก perl ว่าคำสั่งใดที่จะดำเนินการ
การดำเนินงานใน

วิธีที่ง่ายที่สุดในการตรวจสอบทรี op คือหยุด Perl หลังจากแยกวิเคราะห์เสร็จแล้ว
เอาไปทิ้งต้นไม้ นี่คือสิ่งที่คอมไพเลอร์แบ็กเอนด์ B::Terse
B::กระชับและ B::ดีบักทำ

มาดูกันว่า Perl เห็น "$a = $b + $c" อย่างไร:

% perl -MO=Terse -e '$a=$b+$c'
1 LISTOP (0x8179888) ลาออก
2 OP (0x81798b0) เข้าสู่
3 COP (0x8179850) สถานะถัดไป
4 BINOP (0x8179828) มอบหมาย
5 BINOP (0x8179800) เพิ่ม [1]
6 UNOP (0x81796e0) โมฆะ [15]
7 SVOP (0x80fafe0) gvsv GV (0x80fa4cc) *ข
8 UNOP (0x81797e0) โมฆะ [15]
9 SVOP (0x8179700) gvsv GV (0x80efeb0) *ค
10 UNOP (0x816b4f0) โมฆะ [15]
11 SVOP (0x816dcf0) gvsv GV (0x80fa460) *ก

เริ่มจากตรงกลางที่บรรทัดที่ 4 นี่คือ BINOP ซึ่งเป็นตัวดำเนินการไบนารีซึ่งอยู่ที่
ที่ตั้ง 0x8179828. ตัวดำเนินการเฉพาะที่เป็นปัญหาคือ "sassign" - การกำหนดสเกลาร์ -
และคุณสามารถค้นหาโค้ดที่นำไปใช้ในฟังก์ชัน "pp_sassign" ใน pp_hot.c. ในขณะที่
ตัวดำเนินการไบนารี มันมีลูกสองคน: ตัวดำเนินการ add ให้ผลลัพธ์ของ "$b+$c"
อยู่บนสุดของบรรทัดที่ 5 และด้านซ้ายมืออยู่ในบรรทัดที่ 10

บรรทัดที่ 10 เป็นค่าว่าง: สิ่งนี้ไม่ทำอะไรเลย นั่นทำอะไรอยู่น่ะ? ถ้าคุณเห็น
null op เป็นสัญญาณว่ามีบางอย่างได้รับการปรับให้เหมาะสมหลังจากแยกวิเคราะห์ ในขณะที่เรา
ที่กล่าวถึงใน "การเพิ่มประสิทธิภาพ" บางครั้งขั้นตอนการเพิ่มประสิทธิภาพจะแปลงการดำเนินการสองรายการเป็น
ตัวอย่างเช่น เมื่อดึงตัวแปรสเกลาร์ เมื่อสิ่งนี้เกิดขึ้น แทนที่จะเขียนใหม่
ต้นไม้ op และการทำความสะอาดตัวชี้ที่ห้อยอยู่นั้นง่ายกว่าเพียงแค่เปลี่ยน
การดำเนินการซ้ำซ้อนกับ null op เดิมทีต้นไม้จะมีลักษณะดังนี้:

10 เอสวีโอพี (0x816b4f0) rv2sv [15]
11 SVOP (0x816dcf0) กรัมวี GV (0x80fa460) *ก

นั่นคือดึงรายการ "a" จากตารางสัญลักษณ์หลัก แล้วดูที่สเกลาร์
ส่วนประกอบของมัน: "gvsv" ("pp_gvsv" เป็น pp_hot.c) เกิดขึ้นเพื่อทำทั้งสองสิ่งนี้

ทางขวามือ เริ่มต้นที่บรรทัดที่ 5 คล้ายกับที่เราเพิ่งเห็นคือ เรามี
"add" op ("pp_add" อยู่ใน .ด้วย pp_hot.c) เพิ่ม "gvsv" สองอันเข้าด้วยกัน

เดี๋ยวนะ นี่มันเรื่องอะไรกัน?

1 LISTOP (0x8179888) ลาออก
2 OP (0x81798b0) เข้าสู่
3 COP (0x8179850) สถานะถัดไป

"เข้า" และ "ออก" เป็นการกำหนดขอบเขต และหน้าที่ของพวกเขาคือดูแลทำความสะอาดทุก ๆ
เวลาที่คุณเข้าและออกจากบล็อก: ตัวแปรศัพท์ถูกจัดระเบียบ ตัวแปรที่ไม่ได้อ้างอิง
ถูกทำลายเป็นต้น. ทุกโปรแกรมจะมีสามบรรทัดแรก: "leave" คือ a
รายการและลูก ๆ ของมันคือคำสั่งทั้งหมดในบล็อก งบคั่นด้วย
"nextstate" ดังนั้นบล็อกจึงเป็นชุดของ "nextstate" ops โดยที่ ops ที่จะดำเนินการ
สำหรับแต่ละคำสั่งที่เป็นลูกของ "สถานะถัดไป" "ป้อน" เป็น op เดียวที่
ทำหน้าที่เป็นเครื่องหมาย

นั่นเป็นวิธีที่ Perl แยกวิเคราะห์โปรแกรมจากบนลงล่าง:

โครงการ
|
คำแถลง
|
=
-
-
$a+
-
$ข$ค

อย่างไรก็ตาม มันเป็นไปไม่ได้ที่จะ ดำเนินการ การดำเนินการตามลำดับนี้ คุณต้องหา
ค่าของ $b และ $c ก่อนที่คุณจะรวมเข้าด้วยกัน เป็นต้น ดังนั้นอีกกระทู้ที่
วิ่งผ่านทรี op เป็นลำดับการดำเนินการ: แต่ละ op มีฟิลด์ "op_next" ซึ่ง
ชี้ไปที่ op ถัดไปที่จะเรียกใช้ ดังนั้นการติดตามตัวชี้เหล่านี้จะบอกเราว่า perl ดำเนินการอย่างไร
รหัส. เราสามารถสำรวจต้นไม้ตามลำดับนี้โดยใช้ตัวเลือก "exec" เป็น "B::Terse":

% perl -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) *ค
5 BINOP (0x8179878) เพิ่ม [1]
6 SVOP (0x816dd38) gvsv GV (0x80fa468) *ก
7 BINOP (0x81798a0) มอบหมาย
8 LISTOP (0x8179900) ลาออก

สิ่งนี้น่าจะเหมาะสมกว่าสำหรับมนุษย์: เข้าสู่บล็อก เริ่มคำสั่ง รับ
ค่าของ $b และ $c แล้วนำมารวมกัน ค้นหา $a และกำหนดหนึ่งให้กับอีกคนหนึ่ง แล้ว
ออกจาก.

วิธีที่ Perl สร้างทรี op เหล่านี้ในกระบวนการแยกวิเคราะห์สามารถคลี่คลายได้โดย
การตรวจสอบ Perly.y, ไวยากรณ์ YACC มาเอาชิ้นส่วนที่เราต้องสร้างต้นไม้กันเถอะ
สำหรับ "$a = $b + $c"

1 เทอม : เทอม ASSIGNOP เทอม
2 { $$ = ใหม่ASSIGNOP(OPf_STACKED, $1, $2, $3); }
3 | เทอม ADDOP เทอม
4 { $$ = newBINOP($2, 0, สเกลาร์($1), สเกลาร์($3)); }

หากคุณไม่คุ้นเคยกับการอ่านไวยากรณ์ BNF นี่คือวิธีการ: คุณได้รับอาหารบางอย่าง
โดย tokeniser ซึ่งโดยทั่วไปจะลงท้ายด้วยตัวพิมพ์ใหญ่ ที่นี่ "ADDOP" มีให้
เมื่อ tokeniser เห็น "+" ในโค้ดของคุณ "ASSIGNOP" มีให้เมื่อใช้ "=" สำหรับ
มอบหมาย นี่คือ "สัญลักษณ์เทอร์มินัล" เพราะคุณไม่สามารถทำได้ง่ายกว่านี้

ไวยากรณ์ในบรรทัดที่ XNUMX และ XNUMX ของตัวอย่างด้านบนจะบอกวิธีสร้างเพิ่มเติม
รูปแบบที่ซับซ้อน รูปแบบที่ซับซ้อนเหล่านี้ "สัญลักษณ์ที่ไม่ใช่เทอร์มินัล" โดยทั่วไปจะอยู่ด้านล่าง
กรณี. "term" ในที่นี้คือสัญลักษณ์ที่ไม่ใช่เทอร์มินัล ซึ่งแสดงถึงนิพจน์เดียว

ไวยากรณ์ให้กฎต่อไปนี้แก่คุณ: คุณสามารถสร้างสิ่งที่อยู่ทางซ้ายของทวิภาค
ถ้าคุณเห็นทุกสิ่งทางด้านขวาตามลำดับ สิ่งนี้เรียกว่า "การลด" และ
จุดมุ่งหมายของการแยกวิเคราะห์คือการลดอินพุตให้สมบูรณ์ มีหลายวิธีที่คุณสามารถ
ทำการย่อโดยคั่นด้วยแถบแนวตั้ง: ดังนั้น "term" ตามด้วย "=" ตามด้วย
"term" ทำให้ "term" และ "term" ตามด้วย "+" ตามด้วย "term" ยังสามารถทำให้ a
"ภาคเรียน".

ดังนั้น หากคุณเห็นคำศัพท์สองคำที่มี "=" หรือ "+" ระหว่างคำทั้งสอง คุณสามารถเปลี่ยนให้เป็นคำเดียวได้
การแสดงออก. เมื่อคุณทำเช่นนี้ คุณจะรันโค้ดในบล็อกในบรรทัดถัดไป: if you
เห็น "=" คุณจะทำโค้ดในบรรทัดที่ 2 ถ้าคุณเห็น "+" คุณจะทำโค้ดในบรรทัดที่ 4
รหัสนี้ซึ่งมีส่วนช่วยในทรี op

| เทอม ADDOP เทอม
{ $$ = newBINOP($2, 0, สเกลาร์($1), สเกลาร์($3)); }

สิ่งนี้ทำคือสร้างไบนารี่ออฟชั่นใหม่และป้อนตัวแปรจำนวนหนึ่ง ดิ
ตัวแปรอ้างอิงถึงโทเค็น: $1 เป็นโทเค็นแรกในอินพุต $2 วินาที และดังนั้น
on - คิดว่า backreference ของนิพจน์ทั่วไป $$ คือ op ที่ได้รับจากการลดลงนี้
ดังนั้นเราจึงเรียก "newBINOP" เพื่อสร้างตัวดำเนินการไบนารีใหม่ พารามิเตอร์แรกสำหรับ "newBINOP"
ฟังก์ชั่นใน สหกรณ์, เป็นประเภทปฏิบัติการ เป็นโอเปอเรเตอร์บวก เราจึงอยากให้เป็น
"แอดดอป". เราสามารถระบุสิ่งนี้ได้โดยตรง แต่มันอยู่ที่นั่นเป็นโทเค็นที่สองใน
อินพุตดังนั้นเราจึงใช้ $2 พารามิเตอร์ที่สองคือแฟล็กของ op: 0 หมายถึง "ไม่มีอะไรพิเศษ"
จากนั้นสิ่งที่ต้องเพิ่ม: ด้านซ้ายและด้านขวาของนิพจน์ของเรา ในบริบทสเกลาร์

กอง


เมื่อ Perl ดำเนินการบางอย่างเช่น "addop" มันจะส่งต่อผลลัพธ์ไปยัง op ถัดไปอย่างไร
คำตอบคือผ่านการใช้สแต็ค Perl มีสแต็คจำนวนมากสำหรับจัดเก็บสิ่งต่าง ๆ
กำลังดำเนินการอยู่ และเราจะมาดูสามสิ่งที่สำคัญที่สุดที่นี่

ข้อโต้แย้ง กอง
อาร์กิวเมนต์ถูกส่งไปยังโค้ด PP และส่งคืนจากโค้ด PP โดยใช้อาร์กิวเมนต์ stack, "ST"
วิธีทั่วไปในการจัดการอาร์กิวเมนต์คือ นำอาร์กิวเมนต์ออกจากสแต็ก จัดการกับอาร์กิวเมนต์อย่างไร
ต้องการแล้วดันผลลัพธ์กลับเข้าสู่สแต็ก เป็นเช่นนี้ ตัวอย่างเช่น โคไซน์
ผู้ประกอบการทำงาน:

ค่า NV;
ค่า = POPn;
ค่า = Perl_cos(ค่า);
XPUSHn(ค่า);

เราจะเห็นตัวอย่างที่ซับซ้อนกว่านี้เมื่อเราพิจารณามาโครของ Perl ด้านล่าง "POPn" ให้
คุณเป็น NV (ค่าจุดลอยตัว) ของ SV อันดับต้น ๆ บนสแต็ก: $x ใน "cos($x)" แล้วเรา
คำนวณโคไซน์และดันผลลัพธ์กลับเป็น NV "X" ใน "XPUSHn" หมายความว่า
ควรขยายสแต็กหากจำเป็น - ไม่จำเป็นที่นี่เพราะเรารู้
มีที่ว่างสำหรับอีกหนึ่งรายการในสแต็ก เนื่องจากเราเพิ่งลบไปหนึ่งรายการ! "XPUSH*"
มาโครอย่างน้อยก็รับประกันความปลอดภัย

อีกทางหนึ่ง คุณสามารถเล่นซอกับสแต็กโดยตรง: "SP" ให้องค์ประกอบแรกใน
ส่วนของคุณในสแตก และ "TOP*" จะให้ SV/IV/NV/ฯลฯ ระดับบนสุดแก่คุณ บนสแต็ก ดังนั้น,
ตัวอย่างเช่น ทำการลบล้างแบบเอกพจน์ของจำนวนเต็ม:

เซติ(-โทปิ);

เพียงตั้งค่าจำนวนเต็มของรายการสแต็กบนสุดเป็นค่าลบ

การจัดการสแต็กอาร์กิวเมนต์ในคอร์นั้นเหมือนกับใน XSUB ทุกประการ - ดู
perlxstut, perlxs และ perlguts สำหรับคำอธิบายที่ยาวขึ้นของมาโครที่ใช้ใน stack
การจัดการ

ผลิตโดย กอง
ฉันพูดว่า "ส่วนของคุณของสแต็ก" ด้านบนเพราะรหัส PP ไม่จำเป็นต้องได้ทั้งหมด
สแต็กให้กับตัวเอง: หากฟังก์ชันของคุณเรียกใช้ฟังก์ชันอื่น คุณจะต้องแสดงเฉพาะ
อาร์กิวเมนต์ที่มุ่งเป้าไปที่ฟังก์ชันที่ถูกเรียก และไม่ (จำเป็น) ปล่อยให้มันเป็นหน้าที่ของคุณเอง
ข้อมูล. วิธีที่เราทำเช่นนี้คือการมี "เสมือน" ด้านล่างของกองซ้อน เปิดเผยแต่ละ
การทำงาน. สแต็กเครื่องหมายเก็บบุ๊กมาร์กไปยังตำแหน่งในอาร์กิวเมนต์สแต็กที่แต่ละอันใช้ได้
การทำงาน. ตัวอย่างเช่น เมื่อจัดการกับตัวแปรที่ผูกไว้ (ภายใน บางสิ่งที่มี "P"
มายากล) Perl ต้องเรียกวิธีการในการเข้าถึงตัวแปรที่ผูกไว้ อย่างไรก็ตาม เราต้อง
แยกอาร์กิวเมนต์ที่เปิดเผยต่อวิธีการไปยังอาร์กิวเมนต์ที่เปิดเผยต่อต้นฉบับ
ฟังก์ชั่น - เก็บหรือดึงข้อมูลหรืออะไรก็ได้ นี่คือวิธีการผูก "ดัน" โดยประมาณ
ถูกนำไปใช้; ดู "av_push" ใน เฉลี่ย:

1 เครื่องหมาย (SP);
2 ขยาย(SP,2);
3 กด(SvTIED_obj((SV*)av, mg));
4 PUSHs(วาล);
5 พัตแบ็ค;
6 ป้อน;
7 call_method("ผลักดัน", G_SCALAR|G_DISCARD);
8 ออก;

มาตรวจสอบการใช้งานทั้งหมดเพื่อฝึกฝนกัน:

1 เครื่องหมาย (SP);

ดันสถานะปัจจุบันของตัวชี้สแต็กลงบนสแต็กเครื่องหมาย ทั้งนี้เพื่อให้เมื่อ
เราได้เพิ่มรายการไปยังกองอาร์กิวเมนต์เสร็จแล้ว Perl รู้ว่าเราได้เพิ่มกี่รายการ
เมื่อเร็ว ๆ นี้

2 ขยาย(SP,2);
3 กด(SvTIED_obj((SV*)av, mg));
4 PUSHs(วาล);

เราจะเพิ่มอีกสองรายการในกองอาร์กิวเมนต์: เมื่อคุณมีอาร์เรย์ที่ผูก
รูทีนย่อย "PUSH" รับอ็อบเจ็กต์และค่าที่จะผลัก และนั่นคือสิ่งที่
เรามีที่นี่ - วัตถุที่ผูกไว้ซึ่งดึงข้อมูลด้วย "SvTIED_obj" และค่า "val" ของ SV

5 พัตแบ็ค;

ต่อไปเราจะบอกให้ Perl อัปเดตตัวชี้สแต็กส่วนกลางจากตัวแปรภายในของเรา: "dSP"
ให้สำเนาในท้องที่เท่านั้น ไม่ได้อ้างอิงถึงทั่วโลก

6 ป้อน;
7 call_method("ผลักดัน", G_SCALAR|G_DISCARD);
8 ออก;

"ENTER" และ "LEAVE" โลคัลโค้ดบล็อก - พวกเขาตรวจสอบให้แน่ใจว่าตัวแปรทั้งหมดเป็น
จัดระเบียบทุกอย่างที่ได้รับการแปลจะได้รับค่าก่อนหน้านี้ที่ส่งคืนเป็นต้น
คิดว่าพวกเขาเป็น "{" และ "}" ของบล็อก Perl

ในการเรียกเมธอดมายากลจริง ๆ เราต้องเรียกรูทีนย่อยใน Perl space:
"call_method" จัดการเรื่องนั้น และอธิบายไว้ใน perlcall เราเรียก "ดัน"
เมธอดในบริบทสเกลาร์ และเราจะทิ้งค่าส่งคืน ดิ โทร_วิธีการ()
ฟังก์ชั่นลบองค์ประกอบด้านบนของสแต็คเครื่องหมายดังนั้นจึงไม่มีอะไรสำหรับผู้โทรถึง
ทำความสะอาด.

ลด กอง
C ไม่มีแนวคิดเกี่ยวกับขอบเขตท้องถิ่น ดังนั้น Perl จึงจัดเตรียมไว้ เราเห็นแล้วว่า "ENTER" และ
"LEAVE" ถูกใช้เป็นเหล็กจัดฟัน สแต็กบันทึกใช้ C ที่เทียบเท่ากับ for
ตัวอย่าง:

{
ท้องถิ่น $foo = 42;
...
}

ดู "การแปลการเปลี่ยนแปลง" ใน perlguts สำหรับวิธีใช้บันทึกสแต็ก

ล้าน OF มาโคร


สิ่งหนึ่งที่คุณจะสังเกตเห็นเกี่ยวกับแหล่งที่มาของ Perl คือมันเต็มไปด้วยมาโคร บางคนมี
เรียกว่าการใช้มาโครอย่างแพร่หลายเป็นสิ่งที่เข้าใจยากที่สุด คนอื่นพบว่ามันเพิ่มเข้ามา
ความชัดเจน มาดูตัวอย่างกัน โค้ดที่ใช้ตัวดำเนินการบวก:

1 คน(pp_add)
2 {
3 dSP; ข้อมูล; tryAMAGICbin(เพิ่ม,opASSIGN);
4 {
5 dPOPTOPnnrl_ul;
6 SETn( ซ้าย + ขวา );
7 ผลตอบแทน;
8}
9}

ทุกบรรทัดที่นี่ (นอกเหนือจากวงเล็บปีกกา) มีมาโคร ชุดบรรทัดแรก
เพิ่มการประกาศฟังก์ชันตามที่ Perl คาดหวังสำหรับรหัส PP บรรทัดที่ 3 ตั้งค่าตัวแปร
ประกาศสำหรับกองอาร์กิวเมนต์และเป้าหมาย ค่าส่งคืนของการดำเนินการ
สุดท้ายจะพยายามดูว่าการดำเนินการเพิ่มเติมนั้นโอเวอร์โหลดหรือไม่ ถ้าใช่ ความเหมาะสม
รูทีนย่อยเรียกว่า

บรรทัดที่ 5 เป็นการประกาศตัวแปรอื่น - การประกาศตัวแปรทั้งหมดเริ่มต้นด้วย "d" - ซึ่ง
ปรากฏขึ้นจากด้านบนของอาร์กิวเมนต์ stack สอง NV (ด้วยเหตุนี้ "nn") และใส่ลงใน
ตัวแปร "right" และ "left" ดังนั้น "rl" นี่คือตัวถูกดำเนินการสองตัวที่บวก
โอเปอเรเตอร์ ต่อไปเราเรียก "SETn" เพื่อตั้งค่า NV ของค่าที่ส่งกลับเป็นผลลัพธ์ของการเพิ่ม
ทั้งสองค่า เสร็จแล้ว เรากลับมา - มาโคร "RETURN" ทำให้แน่ใจว่าค่าที่ส่งกลับของเรา
ได้รับการจัดการอย่างเหมาะสม และเราส่งโอเปอเรเตอร์ถัดไปเพื่อเรียกใช้กลับไปยังลูปการทำงานหลัก

มาโครเหล่านี้ส่วนใหญ่จะอธิบายเป็น Perlapi และที่สำคัญกว่านั้นคือ
อธิบายใน perlxs เช่นกัน ให้ความสนใจเป็นพิเศษกับ "พื้นหลังและ
PERL_IMPLICIT_CONTEXT" ใน perlguts สำหรับข้อมูลเกี่ยวกับมาโคร "[pad]THX_?"

ต่อไป การอ่าน


สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ Perl internals โปรดดูเอกสารที่ระบุไว้ที่ "Internals
และส่วนต่อประสานภาษา C" ใน Perl

ใช้ perlinterp ออนไลน์โดยใช้บริการ onworks.net


เซิร์ฟเวอร์และเวิร์กสเตชันฟรี

ดาวน์โหลดแอพ Windows & Linux

คำสั่ง Linux

Ad