İngilizceFransızcaİspanyolca

OnWorks favicon'u

perlinterp - Bulutta Çevrimiçi

Perlinterp'i OnWorks ücretsiz barındırma sağlayıcısında Ubuntu Online, Fedora Online, Windows çevrimiçi emülatörü veya MAC OS çevrimiçi emülatörü üzerinden çalıştırın

Bu, Ubuntu Online, Fedora Online, Windows çevrimiçi öykünücüsü veya MAC OS çevrimiçi öykünücüsü gibi birden fazla ücretsiz çevrimiçi iş istasyonumuzdan birini kullanarak OnWorks ücretsiz barındırma sağlayıcısında çalıştırılabilen perlinterp komutudur.

Program:

ADI


perlinterp - Perl yorumlayıcısına genel bakış

TANIM


Bu belge, Perl yorumlayıcısının C düzeyinde nasıl çalıştığına dair bir genel bakış sağlar.
kodu, ilgili C kaynak kodu dosyalarına yönelik işaretçiler ile birlikte.

ELEMENTLER OF L' TERCÜMAN


Yorumlayıcının çalışmasının iki ana aşaması vardır: kodu dahili olarak derlemek.
temsil veya bayt kodu ve ardından yürütme. Perlguts'ta "Derlenmiş kod" açıklıyor
derleme aşamasının tam olarak nasıl gerçekleştiği.

Perl'in işleminin kısa bir dökümü:

Başlangıç
Eylem şurada başlıyor: kalıcı.c. (veya miniperlmain.c miniperl için) Bu çok üst düzey
kod, tek bir ekrana sığacak kadar ve perlembed'de bulunan kodu andırıyor; çoğu
gerçek eylemin içinde gerçekleşir perl.c

kalıcı.c "ExtUtils::Miniperl" tarafından oluşturulur miniperlmain.c zamanında, yani sen
Bunu takip etmek için perl yapmalıdır.

İlk olarak, kalıcı.c biraz bellek ayırır ve bunlar boyunca bir Perl yorumlayıcısı oluşturur.
hatları:

1 PERL_SYS_INIT3(&argc,&argv,&env);
2
3 eğer (!PL_do_undump) {
4 my_perl = perl_alloc();
5 ise (!my_perl)
6 çıkış(1);
7 perl_construct(perl'im);
8 PL_perl_destruct_level = 0;
9}

1. satır bir makrodur ve tanımı işletim sisteminize bağlıdır. 3. satır
global bir değişken olan "PL_do_undump" referansları - Perl'deki tüm global değişkenler şununla başlar
"PL_". Bu, geçerli çalışan programın "-u" bayrağıyla oluşturulup oluşturulmadığını söyler.
perl ve sonra boşaltmak, bu da aklı başında herhangi bir bağlamda yanlış olacağı anlamına gelir.

4. satır bir işlevi çağırır perl.c Perl yorumlayıcısı için bellek ayırmak için. bu oldukça
basit işlev ve bunun cesareti şöyle görünür:

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

Burada, daha sonra göreceğimiz Perl'in sistem soyutlamasının bir örneğini görüyorsunuz:
"PerlMem_malloc" ya sisteminizin "malloc"udur ya da Perl'in kendi "malloc"udur.
malloc.c bu seçeneği yapılandırma zamanında seçtiyseniz.

Daha sonra, 7. satırda, yorumlayıcıyı perl_construct kullanarak oluşturuyoruz. perl.c; bu
Perl'in ihtiyaç duyduğu tüm özel değişkenleri, yığınları vb. ayarlar.

Şimdi komut satırı seçeneklerini Perl'e iletiyoruz ve ona gitmesini söylüyoruz:

çıkış durumu = perl_parse(my_perl, xs_init, argc, argv, (char **)NULL);
if (!çıkış durumu)
perl_run(perl'im);

çıkış durumu = perl_destruct(perl'im);

perl_free(perl'im);

"perl_parse" aslında, içinde tanımlandığı gibi "S_parse_body" etrafındaki bir sarmalayıcıdır. perl.c, Hangi
komut satırı seçeneklerini işler, statik olarak bağlantılı XS modüllerini kurar,
programı ve onu ayrıştırmak için "yyparse" çağırır.

ayrıştırma
Bu aşamanın amacı Perl kaynağını alıp bir op ağacına dönüştürmektir. Göreceğiz
bunlardan biri daha sonra neye benziyor. Kesin konuşmak gerekirse, burada olan üç şey var.

"yyparse", ayrıştırıcı, içinde yaşıyor perly.c, orijinalini okumaktan daha iyi olsanız da
YACC girişi perly.y. (Evet, Virginia, orada is Perl için bir YACC dilbilgisi!)
ayrıştırıcı, kodunuzu almak ve "anlamak", cümlelere bölmek, karar vermektir.
hangi işlenenlerin hangi operatörlerle birlikte gittiği vb.

Ayrıştırıcıya, girdilerinizi belirteçlere ayıran lexer tarafından asil bir şekilde yardım edilir ve
her belirtecin ne tür bir şey olduğuna karar verir: bir değişken adı, bir operatör, bir bareword, bir
alt program, bir çekirdek fonksiyon vb. Sözlüğe ana giriş noktası "yylex" dir,
ve bu ve bununla ilişkili rutinler şurada bulunabilir: toke.c. Perl diğerleri gibi değil
bilgisayar dilleri; bazen bağlama son derece duyarlıdır, çalışması zor olabilir
bir şeyin ne tür bir jeton olduğu veya bir jetonun nerede bittiği. Böyle olunca çok var
belirteç ve ayrıştırıcı arasındaki etkileşim, bu da oldukça korkutucu olabilir.
alışkın değil.

Ayrıştırıcı bir Perl programını anladıkça, aşağıdakiler için bir işlem ağacı oluşturur.
yürütme sırasında gerçekleştirmek için tercüman. Birbirini oluşturan ve birbirine bağlayan rutinler
çeşitli işlemler bulunabilir operasyon cve daha sonra incelenecektir.

Optimizasyon
Şimdi ayrıştırma aşaması tamamlandı ve bitmiş ağaç, aşağıdaki işlemleri temsil ediyor:
Perl yorumlayıcısının programımızı yürütmek için gerçekleştirmesi gerekiyor. Ardından, Perl bir kuru çalışma yapar
ağaç üzerinde optimizasyon aranıyor: "3 + 4" gibi sabit ifadeler
şimdi hesaplanır ve optimize edici ayrıca herhangi bir çoklu işlemin değiştirilip değiştirilemeyeceğini de görür.
tek biriyle. Örneğin, dünyayı kapmak yerine $foo değişkenini getirmek için
*foo ve skaler bileşene bakarak, optimize edici op ağacını bir
doğrudan söz konusu skaleri arayan fonksiyon. Ana optimize edici, "dikizlemek"
operasyon cve birçok operasyonun kendi optimizasyon işlevleri vardır.

Koşu
Şimdi nihayet gitmeye hazırız: Perl bayt kodunu derledik ve geriye kalan tek şey
çalıştırmaktır. Gerçek yürütme, içindeki "runops_standard" işlevi tarafından yapılır. çalıştır.c; Daha
özellikle, bu üç masum görünen çizgi tarafından yapılır:

while ((PL_op = PL_op->op_ppaddr(aTHX))) {
PERL_ASYNC_CHECK();
}

Bunun Perl versiyonuyla daha rahat olabilirsiniz:

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

Belki de değil. Her neyse, her işlem,
işlemi gerçekten gerçekleştirecek olan işlev. Bu işlev bir sonraki
sırayla op - bu, bir sonraki op'u dinamik olarak seçen "if" gibi şeylere izin verir
işlem esnasında. "PERL_ASYNC_CHECK", sinyaller gibi şeylerin kesintiye uğramasını sağlar
gerekirse yürütme.

Çağrılan gerçek işlevler PP kodu olarak bilinir ve dört dosya arasında yayılırlar:
pp_hot.c en sık kullanılan ve yüksek düzeyde optimize edilmiş "sıcak" kodu içerir, pp_sys.c
sisteme özgü tüm işlevleri içerir, pp_ctl.c işlevleri içerir
kontrol yapılarını ("if", "while" ve benzeri) uygulamak ve ppc her şeyi içerir
Başka. Bunlar, eğer isterseniz, Perl'in yerleşik fonksiyonları ve operatörleri için C kodudur.

Her "pp_" işlevinin bir sonraki işleme bir işaretçi döndürmesinin beklendiğini unutmayın. aramalar
perl alt öğeleri (ve değerlendirme blokları) aynı runops döngüsü içinde işlenir ve tüketilmez
C yığınında fazladan boşluk. Örneğin, "pp_entersub" ve "pp_entertry" sadece bir
adresini içeren bağlam yığınına "CxSUB" veya "CxEVAL" blok yapısı
alt çağrıyı veya değerlendirmeyi takiben op. Daha sonra bu alt veya değerlendirmenin ilk op'unu döndürürler.
blok ve böylece bu alt veya bloğun yürütülmesi devam eder. Daha sonra bir "pp_leavesub" veya
"pp_leavetry" op, "CxSUB" veya "CxEVAL"'i açar, geri dönüş işlemini ondan alır ve
geri verir.

Istisna teslim
Perl'in istisna teslimi (yani "ölmek" vb.), düşük seviyenin üzerine inşa edilmiştir.
"setjmp()"/"longjmp()" C-kütüphane işlevleri. Bunlar temel olarak,
mevcut PC ve SP kayıtları ve daha sonra bunları geri yükleme; yani bir "longjmp()" devam ediyor
önceki bir "setjmp()" işleminin yapıldığı koddaki nokta, C'de daha fazla herhangi bir şeyle
yığın kayboluyor. Bu nedenle kodun değerleri her zaman "SAVE_FOO" yerine "SAVE_FOO" kullanarak kaydetmesi gerekir.
otomatik değişkenlerde.

Perl çekirdeği, "JMPENV_PUSH" ve "JMPENV_JUMP" makrolarında "setjmp()" vb. NS
perl istisnalarının temel kuralı, "çıkış" ve "öl"ün ("değerlendirme" yokluğunda) gerçekleştirmesidir.
a JMPENV_JUMP(2), "değerlendirme" içinde "ölmek" bir JMPENV_JUMP(3).

"Perl_parse()", "perl_run()" ve "call_sv(cv, G_EVAL)" gibi perl giriş noktalarında
her biri bir "JMPENV_PUSH" yapar, ardından bir runops döngüsü veya her neyse girin ve mümkün olanı ele alın
istisna döner. 2 dönüş için, yığınları patlatma gibi son temizleme gerçekleştirilir ve
"CHECK" veya "END" bloklarını çağırmak. Diğer şeylerin yanı sıra, kapsam temizleme hala bu şekilde
bir "çıkış" sırasında meydana gelir.

Bir "kalıp" bağlam yığınında bir "CxEVAL" bloğu bulabilirse, yığın şuraya açılır:
bu seviye ve o bloktaki dönüş işlemi "PL_restartop"a atanır; sonra bir
JMPENV_JUMP(3) gerçekleştirilir. Bu normalde kontrolü korumaya geri verir. durumda
"perl_run" ve "call_sv" için, boş olmayan bir "PL_restartop", runop'lara yeniden girişi tetikler
döngü. Bir "değerlendirme" içinde "ölmek" veya "vırmak"ın normal yoludur.

Bazen işlemler, bağlama, sıralama veya aşırı yükleme gibi bir iç runops döngüsü içinde yürütülür.
kod. Bu durumda şöyle bir şey

alt FETCH {değerlendirme {öl}}

"perl_run" da bir longjmp'nin korumaya geri dönmesine neden olur ve her iki runops döngüsünü de açar,
ki bu açıkça yanlıştır. Bundan kaçınmanın bir yolu, kravat kodunun bir
İç runops döngüsünde "FETCH" yürütmeden önce "JMPENV_PUSH", ancak verimlilik için
nedenler, Perl aslında sadece "CATCH_SET(TRUE)" kullanarak bir bayrak ayarlar. "pp_require",
"pp_entereval" ve "pp_entertry" işlemleri bu bayrağı kontrol eder ve doğruysa "docatch" derler,
hangi bir "JMPENV_PUSH" yapar ve kodu yürütmek yerine yeni bir runops seviyesi başlatır.
geçerli döngüde yapıyor.

Başka bir optimizasyon olarak, "FETCH" de değerlendirme bloğundan çıkışta,
bloğu izleyen kod hala iç döngüde yürütülür. Bir istisna olduğunda
yükseltilmiş, "docatch", "CxEVAL"in "JMPENV" seviyesini "PL_top_env" ile karşılaştırır ve eğer
farklıdırlar, sadece istisnayı yeniden atar. Bu şekilde herhangi bir iç döngü atılır.

İşte bir örnek.

1: değerlendirme { tie @a, 'A' };
2: alt A::TIEARRAY {
3: değerlendir {öl };
4: ölmek;
5: }

Bu kodu çalıştırmak için "perl_run" çağrılır, bu da bir "JMPENV_PUSH" yapar ve ardından bir runops girer.
döngü. Bu döngü, değerlendirmenin bir "CxEVAL" itmesiyle, 1. satırda değerlendirme ve bağlama işlemlerini yürütür.
bağlam yığını üzerine.

"pp_tie", bir "CATCH_SET(TRUE)" yapar, ardından işlemi yürütmek için ikinci bir runops döngüsü başlatır.
"TIEARRAY" gövdesi. 3. satırda giriş işlemini yürüttüğünde, "CATCH_GET" doğrudur, yani
"pp_entertry", "JMPENV_PUSH" yapan ve üçüncü bir runops döngüsü başlatan "docatch" öğesini çağırır,
bu daha sonra kalıp işlemini yürütür. Bu noktada C çağrı yığını şöyle görünür:

Perl_pp_die
Perl_runops # üçüncü döngü
S_docatch_body
S_docatch
Perl_pp_entertry
Perl_runops # ikinci döngü
S_call_body
Perl_call_sv
Perl_pp_tie
Perl_runops # ilk döngü
S_run_body
perl_run
ana

ve "-Dstv" ile gösterildiği gibi bağlam ve veri yığınları şöyle görünür:

Yığın 0: ANA
CX 0: BLOK =>
CX 1: DEĞERLENDİRME => AV() PV("A"\0)
retop=ayrılmak
Yığın 1: SİHİR
CX 0: ALT =>
retop=(boş)
CX 1: DEĞERLENDİRME => *
retop=sonraki durum

Kalıp, bağlam yığınından ilk "CxEVAL" öğesini çıkarır, ondan "PL_restartop" öğesini ayarlar,
JMPENV_JUMP(3) ve kontrol en üstteki "docatch" a döner. Bu daha sonra başka bir üçüncü-
4. satırda nextstate, pushmark ve die operasyonlarını yürüten level runops level.
ikinci "pp_die"nin çağrıldığı nokta, C çağrı yığını tam olarak yukarıdaki gibi görünür,
artık bir iç değerlendirme içinde olmasak da; bunun nedeni optimizasyon
daha önce bahsedilmiştir. Ancak, bağlam yığını şimdi şuna benzer, yani en üstteki CxEVAL ile
patladı:

Yığın 0: ANA
CX 0: BLOK =>
CX 1: DEĞERLENDİRME => AV() PV("A"\0)
retop=ayrılmak
Yığın 1: SİHİR
CX 0: ALT =>
retop=(boş)

4. satırdaki kalıp, bağlam yığınını CxEVAL'e geri getirerek onu şu şekilde bırakır:

Yığın 0: ANA
CX 0: BLOK =>

Her zamanki gibi, "PL_restartop", "CxEVAL"den çıkarılır ve bir JMPENV_JUMP(3) yapıldı, hangi
C yığınını dokümana geri döndürür:

S_docatch
Perl_pp_entertry
Perl_runops # ikinci döngü
S_call_body
Perl_call_sv
Perl_pp_tie
Perl_runops # ilk döngü
S_run_body
perl_run
ana

Bu durumda, "CxEVAL" içinde kaydedilen "JMPENV" seviyesi,
şu anki, "docatch" sadece bir JMPENV_JUMP(3) ve C yığını şu şekilde gevşer:

perl_run
ana

"PL_restartop" boş olmadığından, "run_body" yeni bir runops döngüsü ve yürütme başlatır
Devam ediyor.

İÇ DEĞİŞKEN TÜRLERİ
Şimdiye kadar, size Perl'in iç yapısı hakkında bilgi veren perlguts'a bir göz atmış olmalısınız.
değişken türleri: SV'ler, HV'ler, AV'ler ve diğerleri. Değilse, şimdi yapın.

Bu değişkenler yalnızca Perl uzayı değişkenlerini temsil etmek için değil, aynı zamanda herhangi bir değişkeni temsil etmek için de kullanılır.
koddaki sabitler ve bazı yapılar tamamen Perl'e dahildir. Sembol
örneğin tablo, sıradan bir Perl karmasıdır. Kodunuz olduğu gibi bir SV ile temsil edilir.
ayrıştırıcıya oku; aradığınız herhangi bir program dosyası, sıradan Perl dosya tanıtıcıları aracılığıyla açılır,
ve benzerleri.

Çekirdek Devel::Peek modülü, bir Perl programından SV'leri incelememize izin verir. bakalım, için
örneğin, Perl'in "merhaba" sabitine nasıl davrandığı.

% perl -MDevel::Peek -e 'Dump("merhaba")'
1 SV = PV(0xa041450) 0xa04ecbc'de
2 REFCNT = 1
3 BAYRAK = (POK,ONLYOKU,pPOK)
4 PV = 0xa0484e0 "merhaba"\0
5 KÜR = 5
6 UZUNLUK = 6

"Devel::Peek" çıktısını okumak biraz alıştırma gerektirir, o yüzden satır satır inceleyelim.

Satır 1, bellekte 0xa04ecbc'de yaşayan bir SV'ye baktığımızı söylüyor. SV'lerin kendileri
çok basit yapılardır, ancak daha karmaşık bir yapıya işaret ederler. İçinde
bu durumda, 0xa041450 konumunda bir dize değeri tutan bir yapı olan bir PV'dir. Hat
2 referans sayısıdır; Bu verilere başka referans yok, bu yüzden 1'dir.

Satır 3, bu SV'nin bayraklarıdır - onu PV olarak kullanmakta sorun yoktur, salt okunur bir SV'dir (çünkü
bu bir sabittir) ve veriler dahili olarak bir PV'dir. Sonraki içeriğimiz var
0xa0484e0 konumundan başlayan dize.

5. satır bize dizinin mevcut uzunluğunu verir - bunun değil içerir
boş sonlandırıcı. 6. satır, dizenin uzunluğu değil, şu anda dizinin uzunluğudur.
tahsis edilmiş arabellek; dize büyüdükçe, Perl otomatik olarak kullanılabilir depolama alanını genişletir
"SvGROW" adlı bir rutin aracılığıyla.

Bu miktarların herhangi birini C'den çok kolay bir şekilde alabilirsiniz; adına "Sv" eklemeniz yeterli
snippet'te gösterilen alan ve değeri döndürecek bir makronuz var:
"SvCUR(sv)", dizenin geçerli uzunluğunu döndürür, "SvREFCOUNT(sv)",
başvuru sayısı, "SvPV(sv, len)", dizenin kendisini uzunluğuyla birlikte döndürür, vb.
Bu özellikleri işlemek için daha fazla makro, perlgut'larda bulunabilir.

"sv_catpvn"den bir PV'yi manipüle etmeye bir örnek verelim. sv.c

1 boşluk
2 Perl_sv_catpvn(pTHX_SV *sv, const char *ptr, STRLEN len)
3 {
4 STRLEN;
5 karakter *önemsiz;

6 önemsiz = SvPV_force(sv, tlen);
7 SvGROW(sv, uzun + uzun + 1);
8 if (ptr == önemsiz)
9 puan = SvPVX(sv);
10 Taşı(ptr,SvPVX(sv)+tlen,len,char);
11 SvCUR(sv) += uzun;
12 *SvEND(sv) = '\0';
13 (geçersiz)SvPOK_only_UTF8(sv); /* işaretçiyi doğrula */
14 SvTAINT(sv);
15}

Bu, PV'nin sonuna "len" uzunluğunda bir "ptr" dizesi ekleyen bir işlevdir.
"sv" içinde saklanır. 6. satırda yaptığımız ilk şey, SV'nin vardır geçerli bir PV,
bir PV'yi zorlamak için "SvPV_force" makrosunu çağırarak. Bir yan etki olarak, "tlen"
PV'nin mevcut değeri ve PV'nin kendisi "önemsiz" duruma döndürülür.

7. satırda, SV'nin eski dizeyi barındırmak için yeterli alana sahip olduğundan emin oluruz,
yeni dize ve boş sonlandırıcı. "LEN" yeterince büyük değilse, "SvGROW"
bize yer ayırın.

Şimdi, "önemsiz" eklemeye çalıştığımız dizeyle aynıysa, dizeyi alabiliriz
doğrudan SV'den; "SvPVX", SV'deki PV'nin adresidir.

Satır 10, asıl sıralamayı yapar: "Taşı" makrosu, bir bellek yığınını etrafta hareket ettirir: biz
"ptr" dizesini PV'nin sonuna taşıyın - bu, PV'nin başlangıcı artı akımıdır
uzunluk. "char" türündeki "len" baytları taşıyoruz. Bunu yaptıktan sonra Perl'e söylememiz gerekiyor.
"CUR" değerini yeni uzunluğu yansıtacak şekilde değiştirerek dizeyi genişlettik. "SvEND" bir makrodur
bu bize dizgenin sonunu verir, yani bunun bir "\0" olması gerekir.

13. satır bayrakları yönetir; PV'yi değiştirdiğimiz için herhangi bir IV veya NV değeri
artık geçerli olabilir: "$a=10; $a.="6";" varsa 10'un eski IV'ünü kullanmak istemiyoruz.
"SvPOK_only_utf8", "SvPOK_only"nin UTF-8 ile uyumlu özel bir sürümüdür.
IOK ve NOK işaretlerini kapatır ve POK'u açar. Son "SvTAINT", aklayan bir makrodur.
kusur modu açıksa kusurlu veriler.

AV'ler ve HV'ler daha karmaşıktır, ancak SV'ler açık ara en yaygın değişken türüdür.
etrafa atılmış. Bunları nasıl manipüle ettiğimize dair bir şeyler gördükten sonra, devam edelim ve
op ağacının nasıl oluşturulduğu.

OP AĞAÇLAR


İlk olarak, op ağacı nedir? Op ağacı, dosyanızın ayrıştırılmış temsilidir.
programı, ayrıştırma bölümümüzde gördüğümüz gibi ve bu,
Perl, "Running" bölümünde gördüğümüz gibi programınızı yürütmeye devam eder.

İşlem, Perl'in gerçekleştirebileceği temel bir işlemdir: tüm yerleşik işlevler ve
operatörler op'tur ve yorumlayıcının kavramlarıyla ilgilenen bir dizi op vardır.
dahili ihtiyaçlar - bir bloğa girip çıkmak, bir ifadeyi bitirmek, bir değişkeni getirmek,
ve benzerleri.

İşlem ağacı iki şekilde bağlantılıdır: arasında iki "yol" olduğunu hayal edebilirsiniz.
o, ağacı geçebileceğiniz iki emir. İlk olarak, ayrıştırma sırası,
ayrıştırıcı kodu anladı ve ikinci olarak, yürütme sırası Perl'e hangi sıranın gerçekleştirileceğini söyler
içindeki işlemler.

İşlem ağacını incelemenin en kolay yolu, ayrıştırmayı bitirdikten sonra Perl'i durdurmaktır.
ağaçtan atmasını sağlayın. Bu tam olarak derleyicinin B::Terse,
B::Özlü ve B::Debug yapar.

Perl'in "$a = $b + $c" ifadesini nasıl gördüğüne bir bakalım:

% perl -MO=Terse -e '$a=$b+$c'
1 LISTOP (0x8179888) bırak
2 OP (0x81798b0) girin
3 COP (0x8179850) sonraki durum
4 BINOP (0x8179828) atama
5 BINOP (0x8179800) [1] ekleyin
6 UNOP (0x81796e0) boş [15]
7 SVOP (0x80fafe0) gvsv GV (0x80fa4cc) *b
8 UNOP (0x81797e0) boş [15]
9 SVOP (0x8179700) gvsv GV (0x80efeb0) *c
10 UNOP (0x816b4f0) boş [15]
11 SVOP (0x816dcf0) gvsv GV (0x80fa460) *a

Ortadan, 4. satırdan başlayalım. Bu bir BINOP, ikili bir operatördür.
konum 0x8179828. Söz konusu belirli operatör "sassign" - skaler atama -
ve onu uygulayan kodu "pp_sassign" işlevinde bulabilirsiniz. pp_hot.c.
bir ikili operatör, iki çocuğu vardır: "$b+$c" sonucunu sağlayan ekleme operatörü,
5. satırda en üstte ve sol taraf 10. satırda.

Satır 10 boş işlemdir: bu tam olarak hiçbir şey yapmaz. Bunun orada ne işi var? Eğer görürsen
null op, ayrıştırmadan sonra bir şeyin optimize edildiğinin bir işaretidir. biz olarak
"Optimizasyon" bölümünde bahsedilen optimizasyon aşaması bazen iki işlemi
bir, örneğin bir skaler değişken alırken. Bu olduğunda, yeniden yazmak yerine
op ağacı ve sarkan işaretçileri temizlemek, sadece değiştirmek daha kolaydır
null op ile gereksiz işlem. Başlangıçta, ağaç şöyle görünürdü:

10 SVOP (0x816b4f0) rv2sv [15]
11 SVOP (0x816dcf0) gv GV (0x80fa460) *a

Yani, ana sembol tablosundan "a" girişini getirin ve ardından skalere bakın.
bileşeni: "gvsv" ("pp_gvsv" içine pp_hot.c) bu iki şeyi yapmak olur.

Sağ taraf, 5. satırdan başlayarak az önce gördüğümüze benzer:
"add" op ("pp_add" ayrıca pp_hot.c) iki "gvsv"yi bir araya getirin.

Bu ne hakkında?

1 LISTOP (0x8179888) bırak
2 OP (0x81798b0) girin
3 COP (0x8179850) sonraki durum

"Gir" ve "ayrıl", kapsam belirleme operasyonlarıdır ve görevleri, her
bir blok girdiğiniz ve bir bloktan ayrıldığınız zaman: sözcüksel değişkenler düzenlenir, referanssız değişkenler
yok edilir, vb. Her programın ilk üç satırı olacaktır: "ayrılmak" bir
list ve alt öğeleri bloktaki tüm ifadelerdir. İfadeler şu şekilde sınırlandırılmıştır:
"nextstate", yani bir blok, gerçekleştirilecek işlemlerle birlikte "nextstate" işlemlerinin bir koleksiyonudur.
her ifade için "nextstate" nin çocuklarıdır. "enter" tek bir işlemdir ve
işaretleyici olarak işlev görür.

Perl programı yukarıdan aşağıya şöyle ayrıştırdı:

Programı
|
Açıklama
|
=
/\
/\
$ bir +
/\
$b $c

Ancak, imkansız yapmak işlemleri şu sırayla gerçekleştirin:
Örneğin, $b ve $c değerlerini bir araya getirmeden önce. Yani, diğer iş parçacığı
op ağacından geçer, yürütme sırasıdır: her operasyonun bir "op_next" alanı vardır.
çalıştırılacak bir sonraki işlemi işaret eder, bu nedenle bu işaretçileri takip etmek bize perl'in nasıl yürütüldüğünü söyler
kod. "exec" seçeneğini "B::Terse" olarak kullanarak ağacı bu sırayla geçebiliriz:

% perl -MO=Terse,exec -e '$a=$b+$c'
1 OP (0x8179928) girin
2 COP (0x81798c8) sonraki durum
3 SVOP (0x81796c8) gvsv GV (0x80fa4d4) *b
4 SVOP (0x8179798) gvsv GV (0x80efeb0) *c
5 BINOP (0x8179878) [1] ekleyin
6 SVOP (0x816dd38) gvsv GV (0x80fa468) *a
7 BINOP (0x81798a0) ataması
8 LISTOP (0x8179900) bırak

Bu muhtemelen bir insan için daha mantıklıdır: bir blok girin, bir ifade başlatın. Almak
$b ve $c değerlerini toplayın ve bunları bir araya toplayın. $a'yı bulun ve birini diğerine atayın. Sonra
ayrılmak.

Perl'in ayrıştırma işleminde bu op ağaçlarını oluşturma şekli şu şekilde çözülebilir:
incelenmesi perly.y, YACC dilbilgisi. Ağacı inşa etmek için ihtiyacımız olan parçayı alalım
"$a = $b + $c" için

1 dönem : dönem ASSIGNOP terimi
2 { $$ = newASSIGNOP(OPf_STACKED, $1, $2, $3); }
3 | ADDOP terimi
4 { $$ = yeniBINOP($2, 0, skaler($1), skaler($3)); }

BNF gramerlerini okumaya alışkın değilseniz, şu şekilde çalışır: Kesin beslenirsiniz.
genellikle büyük harfle biten belirteç tarafından yapılan şeyler. Burada, "ADDOP" sağlanır
belirteç kodunuzda "+" gördüğünde. "=" için kullanıldığında "ASSIGNOP" sağlanır
atama. Bunlar "terminal sembolleridir", çünkü onlardan daha basitini elde edemezsiniz.

Yukarıdaki snippet'in birinci ve üçüncü satırları olan dilbilgisi, size daha fazlasını nasıl oluşturacağınızı söyler.
karmaşık formlar. Bu karmaşık formlar, "terminal olmayan semboller" genellikle daha alt sıralara yerleştirilir.
durum. Buradaki "terim", tek bir ifadeyi temsil eden, terminal olmayan bir semboldür.

Dilbilgisi size şu kuralı verir: kolonun solundaki şeyi yapabilirsiniz
sağdaki her şeyi sırayla görüyorsanız. Buna "azaltma" denir ve
ayrıştırmanın amacı girdiyi tamamen azaltmaktır. Yapabileceğiniz birkaç farklı yol var
dikey çubuklarla ayrılmış bir indirgeme gerçekleştirin: yani, "term" ve ardından "=" ve ardından
"terim" bir "terim" oluşturur ve "+" ve ardından "terim" gelen "terim" de bir terim oluşturabilir.
"Terim".

Yani, aralarında "=" veya "+" olan iki terim görürseniz, onları tek bir terime dönüştürebilirsiniz.
ifade. Bunu yaptığınızda, bir sonraki satırdaki bloktaki kodu yürütürsünüz:
bkz. "=", 2. satırdaki kodu yapacaksınız. "+" seçeneğini görürseniz 4. satırdaki kodu yapacaksınız.
op ağacına katkıda bulunan bu kod.

| ADDOP terimi
{ $$ = newBINOP($2, 0, skaler($1), skaler($3)); }

Bunun yaptığı, yeni bir ikili op oluşturmak ve ona bir dizi değişken beslemek. NS
değişkenler jetonlara atıfta bulunur: $1 girdideki ilk jeton, ikinci jeton $2 ve böylece
açık - normal ifade geri referanslarını düşünün. $$, bu indirimden döndürülen işlemdir.
Bu nedenle, yeni bir ikili operatör oluşturmak için "newBINOP" diyoruz. "newBINOP" için ilk parametre,
içinde bir işlev operasyon c, işlem türüdür. Bu bir toplama operatörüdür, bu yüzden türün şöyle olmasını istiyoruz:
"ADDOP". Bunu doğrudan belirtebiliriz, ancak tam orada ikinci belirteç olarak.
girdi, bu yüzden 2 $ kullanıyoruz. İkinci parametre operasyonun bayraklarıdır: 0, "özel bir şey yok" anlamına gelir.
Sonra eklenecek şeyler: skaler bağlamda ifademizin sol ve sağ tarafı.

YIĞINLAR


Perl "addop" gibi bir şey çalıştırdığında, sonuçlarını bir sonraki operasyona nasıl iletir?
Cevap, yığınların kullanılmasıdır. Perl'in şeyleri depolamak için bir dizi yığını vardır.
şu anda üzerinde çalışıyoruz ve burada en önemli üçüne bakacağız.

Tartışma yığın
Argümanlar PP koduna geçirilir ve "ST" argüman yığını kullanılarak PP kodundan döndürülür.
Argümanları ele almanın tipik yolu, onları yığından çıkarmak, onlarla nasıl başa çıkacağınızı anlamaktır.
dilek ve ardından sonucu yığına geri itin. Bu, örneğin, kosinüs
operatör çalışır:

NV değeri;
değer = POPn;
değer = Perl_cos(değer);
XPUSHn(değer);

Aşağıda Perl'in makrolarını düşündüğümüzde bunun daha zor bir örneğini göreceğiz. "POPn" verir
siz yığındaki en üstteki SV'nin NV'si (kayan nokta değeri): "cos($x)" içindeki $x. Bizden sonra
kosinüsü hesaplayın ve sonucu bir NV olarak geri itin. "XPUSHn" içindeki "X",
gerekirse yığın genişletilmelidir - burada gerekli olamaz, çünkü biliyoruz
Az önce bir tanesini kaldırdığımız için yığında bir öğeye daha yer var! "XPUSH*"
makrolar en azından güvenliği garanti eder.

Alternatif olarak, yığınla doğrudan oynayabilirsiniz: "SP" size ilk öğeyi verir.
yığının sizin bölümünüz ve "TOP*" size en iyi SV/IV/NV/etc'yi verir. yığın üzerinde. Yani,
örneğin, bir tamsayının tekli olumsuzunu yapmak için:

SETI(-TOPi);

Sadece üst yığın girişinin tamsayı değerini olumsuzlamasına ayarlayın.

Çekirdekteki argüman yığını işleme, XSUB'lardakiyle tamamen aynıdır - bkz.
yığında kullanılan makroların daha uzun açıklaması için perlxstut, perlxs ve perlguts
manipülasyonu.

işaret yığın
Yukarıda "yığındaki payınız" diyorum çünkü PP kodu mutlaka tamamını almıyor
kendi kendine yığın: işleviniz başka bir işlevi çağırırsa, yalnızca
çağrılan işlevi hedefleyen ve (mutlaka) kendi başınıza almasına izin vermeyen argümanlar
veri. Bunu yapmamızın yolu, her birine maruz kalan "sanal" bir yığın altı elde etmektir.
işlev. İşaret yığını, her biri tarafından kullanılabilen argüman yığınındaki konumlara yer imlerini tutar.
işlev. Örneğin, bağlı bir değişkenle uğraşırken, (dahili olarak "P" ile bir şey
magic) Perl, bağlı değişkenlere erişim için yöntemleri çağırmalıdır. Ancak, ihtiyacımız var
yönteme maruz kalan argümanları orijinale maruz kalan argümana ayırın
işlev - mağaza veya getirme veya ne olursa olsun. İşte kabaca nasıl bağlanmış "itme"
uygulanır; "av_push" bölümüne bakın Av.c:

1 PUSHMARK(SP);
2 GENİŞLET(SP,2);
3 PUSH(SvTIED_obj((SV*)av, mg));
4 PUSH(val);
5 BAŞVURU;
6 GİRİŞ;
7 call_method("PUSH", G_SCALAR|G_DISCARD);
8 İZİN;

Uygulama için tüm uygulamayı inceleyelim:

1 PUSHMARK(SP);

Yığın işaretçisinin mevcut durumunu işaret yığınının üzerine itin. Bu öyle ki, ne zaman
argüman yığınına öğe eklemeyi bitirdik, Perl kaç tane şey eklediğimizi biliyor
son günlerde.

2 GENİŞLET(SP,2);
3 PUSH(SvTIED_obj((SV*)av, mg));
4 PUSH(val);

Argüman yığınına iki öğe daha ekleyeceğiz: bağlı bir diziniz olduğunda,
"PUSH" alt yordamı nesneyi ve itilecek değeri alır ve tam olarak bu
burada - "SvTIED_obj" ile alınan bağlı nesne ve SV "val" değeri var.

5 BAŞVURU;

Sonra Perl'e global yığın işaretçisini dahili değişkenimizden güncellemesini söyleriz: "dSP"
bize sadece yerel bir kopya verdi, global referansı değil.

6 GİRİŞ;
7 call_method("PUSH", G_SCALAR|G_DISCARD);
8 İZİN;

"ENTER" ve "LEAVE" bir kod bloğunu yerelleştirir - tüm değişkenlerin
toparlanır, yerelleştirilen her şey önceki değerini döndürür, vb.
Bunları bir Perl bloğunun "{" ve "}" öğeleri olarak düşünün.

Sihirli yöntem çağrısını gerçekten yapmak için Perl uzayında bir alt program çağırmamız gerekir:
"call_method" bununla ilgilenir ve perlcall'da açıklanmıştır. "PUSH" diyoruz
skaler bağlamda yöntemi ve dönüş değerini atacağız. NS çağrı_yöntemi()
işlev, işaret yığınının en üst öğesini kaldırır, bu nedenle arayanın yapması gereken hiçbir şey yoktur.
Temizlemek.

İndirim yığın
C'nin yerel kapsam kavramı yoktur, bu nedenle Perl bir tane sağlar. "ENTER" ifadesini gördük ve
"LEAVE", kapsam belirleme parantezleri olarak kullanılır; kaydetme yığını, aşağıdakilerin C eşdeğerini uygular:
örnek:

{
yerel $foo = 42;
...
}

Kaydetme yığınının nasıl kullanılacağına ilişkin bilgi için perlguts'taki "Değişiklikleri yerelleştirme" konusuna bakın.

MİLYONLARCA OF MAKROS


Perl kaynağı hakkında farkedeceğiniz bir şey, onun makrolarla dolu olmasıdır. Bazıları var
Anlaması en zor şey makroların yaygın kullanımı olarak adlandırılırken, diğerleri bunu
açıklık. Bir örnek alalım, toplama operatörünü uygulayan kod:

1 kişi(pp_add)
2 {
3 dSP; dATARGET; tryAMAGICbin(ekle,opASSIGN);
4 {
5 dPOPTOPnnrl_ul;
6 SETn( sol + sağ);
7 DÖNÜŞ;
8}
9}

Buradaki her satır (tabii ki parantezler dışında) bir makro içerir. İlk satır kümeleri
Perl'in PP kodu için beklediği gibi işlev bildirimini yukarı; satır 3 değişkeni ayarlar
argüman yığını ve hedef için bildirimler, işlemin dönüş değeri.
Son olarak, toplama işleminin aşırı yüklenip yüklenmediğini görmeye çalışır; eğer öyleyse, uygun
alt program denir.

5. satır başka bir değişken bildirimidir - tüm değişken bildirimleri "d" ile başlar - bu
argüman yığınının tepesinden iki NV (dolayısıyla "nn") çıkar ve bunları
"sağ" ve "sol" değişkenleri, dolayısıyla "rl". Bunlar, toplama işleminin iki işlenenidir.
Şebeke. Ardından, dönüş değerinin NV'sini toplama sonucuna ayarlamak için "SETn" diyoruz.
iki değer. Bu yapılır, geri döneriz - "RETURN" makrosu, dönüş değerimizin olduğundan emin olur
düzgün bir şekilde işlenir ve bir sonraki operatörü ana çalıştırma döngüsüne geri çalıştırması için geçiririz.

Bu makroların çoğu perlapi'de açıklanmıştır ve daha önemli olanlardan bazıları şunlardır:
perlxs'te de açıklanmıştır. "Arka Plan ve
"[pad]THX_?" makroları hakkında bilgi için perlgut'larda PERL_IMPLICIT_CONTEXT".

DAHA İLERİ OKUMA


Perl dahilileri hakkında daha fazla bilgi için lütfen "Internals" bölümünde listelenen belgelere bakın.
ve C Dil Arayüzü" perl'de.

onworks.net hizmetlerini kullanarak perlinterp'i çevrimiçi kullanın


Ücretsiz Sunucular ve İş İstasyonları

Windows ve Linux uygulamalarını indirin

Linux komutları

Ad