Skip to content

Priedes interpretatora pirmās stadijas

Published: at 12:00 AM

Īsumā par to, kas notiek pašā sākumā, kad tiek kompilēts vai interpretēts kods, specifiski runājot par Priedi:

Programmēšanas valodu implementācijas lielākoties daļās divās daļās: interpretatori un kompilatori. Tie atšķirās ar to, kā tiek izpildīts kods, savukārt pirmās izpildes stadijas abiem ir vienādas un par to arī turpmākais raksts. Pēc šādiem principiem darbojās absolūts vairums kompilatoru un interpretatoru pasaulē. Šī noteikti nav pamācība, bet varu ieteikt e-grāmatu “Crafting Interpreters”

Satura rādītājs

Kā saprast programmas kodu

Pirms ķerties klāt kodam, varētu paskatīties uz matemātiskām izteiksmēm, jo tas arī ir pieraksts ar instrukcijām, kas jāveic lai nonāktu pie rezultāta.

1+2*3

Ņemot vērā matemātisko darbību secību, vispirms veicam reizināšanu un tad saskaitīšanu. Šīs izteiksmes izpildi varētu attēlot grafikā.

Matemātiskās izteiksmes ASK

Šo grafiku sauc par Abstraktās Sintakses Koku un tas tiek ģenerēts, lai tiktu vaļā no cilvēka rakstītā, koda. Tam nosaukums dots tāds, jo tas ir neatkarīgs no programmēšanas valodas sintakses(pieraksta veida). Divas valodas ar atšķirīgiem instrukciju pierakstiem ģenerēs vienādus abstraktās sintakses kokus. Kompilatora uzdevums ir likt datoram saprast instrukcijas, ko ir rakstījis cilvēks un šis ir pirmais posms tajā procesā - abstrahēties no cilvēka rakstītā, vienkārši lasāmā, koda

Turpinot ar kodu…

a = 3-2
print(1+2*a)

ASK

Te svarīgi norādīt, ka koks tiek apskatīts no kreisās puses, uz labo. Tas ir svarīgi piem. atņemšanas darbībai un programmas saknei, kur ir svarīga secība kādā tiek izpildītas instrukcijas

Kā izaudzēt Abstraktās Sintakses Koku

Pirmais solis ir saprast kādi simboli veido kodu un kāda ir to nozīme tajā

a = 3-2
print(1+2*a)

Varētu uzrakstīt šādā sarakstā:

MAINĪGĀ NOSAUKUMS "a"
ZĪME =
SKAITLIS 3
ZĪME -
SKAITLIS 2
JAUNA RINDA
FUNKCIJAS NOSAUKUMS "print"
ATVEROŠĀ IEKAVA
SKAITLIS 1
ZĪME +
SKAITLIS 2
ZĪME *
MAINĪGĀ NOSAUKUMS "a"
AIZVEROŠĀ IEKAVA

Šo sarakstu ģenerēt no koda ir Leksera uzdevums

Lekseris

Augstāk minētos simbolus turpmāk sauksim par tokeniem.

Lekseris sastāv no cikla, kas iet cauri kodam un veido augstāk minēto sarakstu. Atkal iedomājamies vienkāršu matemātisku izteiksmi.

2+1*3

Izveidot lekseri, kas var saprast šādu izteiksmi, ir diezgan elementāri tāpēc, ka katrs tokens ir viena rakstzīme tekstā, tāpēc viss, kas vajadzīgs ir cikls, kas iet cauri tekstam un pārbauda kādai rakstzīmei tas uzdūries virsū.

input = "2+1*3"

for character in input:
    if character == "+":
        print("ZĪME +")
    elif character == "*":
        print("ZĪME *")
    elif character.is_digit():
        print("CIPARS " + character)
    else:
        print("Neatpazīts simbols")

Izpildot augstāko Python kodu iegūstam tokenu sarakstu

CIPARS 2
ZĪME +
CIPARS 1
ZĪME *
CIPARS 3

Problēmas sākās, kad parādās tokeni, kas ir garāki par vienu rakstzīmi piem. daudzciparu skaitļi

2+1*34

Izlaižot šo izteiksmi caur esošo lekseri, iegūstam šādu rezultātu:

CIPARS 2
ZĪME +
CIPARS 1
ZĪME *
CIPARS 3
CIPARS 4

Tas tāpēc, ka mūsu lekseris ir ļoti tuvredzīgs. Tas ierauga ciparu un uzreiz met ārā jaunu tokenu. Šajā piemērā, protams, ir iespēja šim sarakstam izskriet cauri vēlreiz un, ieraugot vairākus blakus esošus ciparus, savienot tos skaitlī, bet glītāk būtu šo problēmu atrisināt jau lekserī. Lai to izdarītu būtu vajadzība paskatīties kas seko pēc pašreizējā tokena.

input = "2+1*3"

while True:
    character = get_next_character()
    if character == "+":
        print("ZĪME +")
    elif character == "*":
        print("ZĪME *")
    elif character.is_digit():
        digits = []
        digits.append(character)
        while True:
            if see_next_character().is_digit():
                digits.append(get_next_character())
            else:
                break
        print("SKAITLIS " + digits_to_number(cipari))
    else:
        print("Neatpazīts simbols")

Šoreiz ieraugot ciparu, pārbaudām nākamo rakstzīmi. Ja tā arī ir cipars, tātad esam sastapuši daudzciparu skaitli un ejam uz priekšu par rakstzīmju sarakstu kamēr sastopam rakstzīmi, kas nav cipars. Tad visus savāktos ciparus savienojam skaitlī un sviežam ārā.

Šajā kodā ir divas funkcijas ar būtisku atšķirību.

get_next_character() atgriež nākamo rakstzīmi, kā arī noņem to no rakstzīmju saraksta. Tādējādi mēs to apstrādājam tagad.

see_next_character() atgriež nākamo rakstzīmi, bet to atstāj tālākai apskatīšanai.

Funkcija digits_to_number() apvieno ciparu sarakstu skaitlī

Tagad iegūstam pareizo tokenu sarakstu:

SKAITLIS 2
ZĪME +
SKAITLIS 1
ZĪME *
SKAITLIS 34

Apgrieztais poļu pieraksts

Apgrieztais poļu pieraksts ir matemātisku izteiksmju pieraksta veids un to varētu arī uzskatīt par alternatīvu abstraktajam sintakses kokam. Tajā tiek pierakstīti abi darbības skaitļi un tikai pēc tam darbības zīme.

2 + 1 kļūst par 2 1 +

3 − 4 + 5 kļūst par 3 4 − 5 +

Izpildīt izteiksmi apgrieztajā poļu pierakstā var, nevienā brīdi neredzot visu izteiksmi kopumā, tikai lineāri ejot cauri izteiksmei, pa vienam simbolam. Nav arī nekas jāzina par darbību secību.

Lai izpildītu šo pierakstu nepieciešams saraksts ar skaitliskām vērtībām - steks. Steka īpatnība ir tāda, ka pievienot skaitļus var tikai tā augšā un noņemt arī var tikai pašu augšējo vērtību.

Kā izpildīt darbību 3 4 − 5 +:

Kā izpildīt darbību 3 4 - sin:

Pārseris

Pārseris ir nākamā stadija pēc leksera. Tas izmanto iepriekš ģenerēto tokenu sarakstu, lai ģenerētu abstrakto sintakses koku, vai piem. apgriezto poļu pierakstu.

”Vilcienu šķirošanas” algoritms

Reāli izmantojami pārseri var būt diezgan sarežģīti un netiek rakstīti manuāli, bet gan izmantojot pārsera ģeneratorus. Arī priede tā dara, bet ir pāris vienkārši algoritmi, kas var ģenerēt apgriezto poļu pierakstu no tokenu saraksta. Tam ir dots tāds nosaukums, jo tas atgādina vilcienu šķirošanas staciju

“Vilcienu šķirošanas” algoritmā tiek izmantoti divi steki. Viens uzglabā skaitliskas vērtības, otrs uzglabā darbību zīmes un citus operatorus.

3 − 4 + 5 pārveidot apgrieztajā poļu pierakstā:

Leksera + Parsera ģenerators

Lekseri un pārseri bieži netiek rakstīti ar roku, arī Priedes gadījumā. Tā vietā izmantojot leksera + pārsera ģeneratorus. Priede izmanto atvērtā koda projektu Hime. Tas nolasa kodu un ģenerē abstraktās sintakses koku pēc noteiktas gramatikas. Katram gramatikas punktam ir nosaukums un pēc -> simbola seko simboli, kas to sastāda, | svītrai kalpojot par vai operatoru. Galā noslēdzoties ar semikolu.

exp_atom   -> NUMBER
           | '(' exp ')';

reizdal -> exp_atom
           |  reizdal '*' exp_atom
           |  reizdal '/' exp_atom;

plusminus   -> reizdal
           |  plusminus '+' reizdal
           |  plusminus '-' reizdal;

exp        -> plusminus;

Šī gramatika norāda, ka reizināšanas un dalīšanas darbības ir kā apakšsadaļa saskaitīšanas un atņemšanas darbībām. Tādējādi tiek nodrošināta pareiza matemātisko darbību secība.

Šis gramatikas pieraksts tiek izmantots, lai Hime ģenerētu lekseri un parseri, kas atrodas binārā formā. Tālāk Rust kods izmanto šos bināros failus, kopā ar Hime rust bibliotēku, lai ģenerētu abstrakto sintakses koku Priedes programmēšanas valodas kodam.

Un tālāk sākās jautrā daļa.