Tolk (computing) - Interpreter (computing)

W3sDesign Tolkedesignmønster UML

Inden for datalogi er en tolk et edb -program, der direkte udfører instruktioner, der er skrevet i et programmerings- eller scriptsprog , uden at de tidligere skal være blevet samlet til et maskinsprogsprogram . En tolk bruger generelt en af ​​følgende strategier til programkørsel:

  1. Parse den kildekoden og udføre sin adfærd direkte;
  2. Oversæt kildekoden til en effektiv mellemrepræsentation eller objektkode og udfør det med det samme;
  3. Udfør eksplicit lagret forudkompileret kode, der er lavet af en compiler, som er en del af tolksystemet.

Tidlige versioner af Lisp programmeringssprog og minicomputer og mikrocomputer BASIC dialekter ville være eksempler på den første type. Perl , Raku , Python , MATLAB og Ruby er eksempler på den anden, mens UCSD Pascal er et eksempel på den tredje type. Kildeprogrammer kompileres på forhånd og gemmes som maskineuafhængig kode, som derefter forbindes i løbetid og udføres af en tolk og/eller kompilator (for JIT- systemer). Nogle systemer, såsom Smalltalk og moderne versioner af BASIC og Java , kombinerer muligvis også to og tre. Tolke af forskellige typer er også blevet konstrueret til mange sprog, der traditionelt er forbundet med kompilering, såsom Algol , Fortran , Cobol , C og C ++ .

Selvom tolkning og kompilering er de to vigtigste midler, hvormed programmeringssprog implementeres, udelukker de ikke hinanden, da de fleste tolkesystemer også udfører nogle oversættelsesarbejder, ligesom kompilatorer. Udtrykkene " fortolket sprog " eller " kompileret sprog " betyder, at den kanoniske implementering af dette sprog er henholdsvis en tolk eller en kompilator. Et sprog på højt niveau er ideelt set en abstraktion uafhængig af bestemte implementeringer.

Historie

Tolke blev brugt allerede i 1952 til at lette programmeringen inden for computerens begrænsninger på det tidspunkt (f.eks. Mangel på programlagerplads eller ingen indbygget understøttelse af floating point -numre). Tolke blev også brugt til at oversætte mellem maskinsprog på lavt niveau, hvilket gjorde det muligt at skrive kode til maskiner, der stadig var under opbygning og testet på computere, der allerede eksisterede. Det første fortolkede sprog på højt niveau var Lisp . Lisp blev først implementeret i 1958 af Steve Russell på en IBM 704 -computer. Russell havde læst John McCarthys papir og indså (til McCarthys overraskelse), at Lisp eval -funktionen kunne implementeres i maskinkode. Resultatet var en fungerende Lisp -tolk, der kunne bruges til at køre Lisp -programmer eller mere korrekt "evaluere Lisp -udtryk".

Kompilatorer versus tolke

En illustration af forbindelsesprocessen. Objektfiler og statiske biblioteker samles til et nyt bibliotek eller eksekverbar

Programmer, der er skrevet på et sprog på højt niveau, udføres enten direkte af en slags tolk eller konverteres til maskinkode af en compiler (og assembler og linker ), som CPU'en kan udføre.

Mens kompilatorer (og montører) generelt producerer maskinkode, der kan eksekveres direkte af computerhardware, kan de ofte (valgfrit) producere en mellemform kaldet objektkode . Dette er dybest set den samme maskinspecifikke kode, men udvidet med en symboltabel med navne og tags for at gøre eksekverbare blokke (eller moduler) identificerbare og flytbare. Kompilerede programmer vil typisk bruge byggesten (funktioner), der opbevares i et bibliotek med sådanne objektkodemoduler. En linker bruges til at kombinere (præfabrikerede) biblioteksfiler med objektfilerne i applikationen for at danne en enkelt eksekverbar fil. De objektfiler, der bruges til at generere en eksekverbar fil, produceres således ofte på forskellige tidspunkter og nogle gange endda af forskellige sprog (i stand til at generere det samme objektformat).

En simpel tolk skrevet i et sprog på lavt niveau (f.eks. Samling ) kan have lignende maskinkodeblokke, der implementerer funktioner på sproget på højt niveau, og som udføres, når en funktions indtastning i en opslagstabel peger på den kode. Imidlertid anvender en tolk, der er skrevet på et sprog på højt niveau, typisk en anden tilgang, såsom at generere og derefter gå et parse-træ , eller ved at generere og udføre mellemliggende software-definerede instruktioner eller begge dele.

Således omdanner både kompilatorer og tolke generelt kildekode (tekstfiler) til tokens, begge kan (eller ikke) generere et parsetræ, og begge kan generere øjeblikkelige instruktioner (for en stakmaskine , firdobbelt kode eller på andre måder). Den grundlæggende forskel er, at et kompilersystem, herunder en (indbygget eller separat) linker, genererer et enkeltstående maskinkodeprogram , mens et tolksystem i stedet udfører de handlinger, der er beskrevet af programmet på højt niveau.

En kompiler kan således foretage næsten alle konverteringerne fra kildekodesemantik til maskinniveau en gang for alle (dvs. indtil programmet skal ændres), mens en tolk skal udføre noget af dette konverteringsarbejde hver gang en erklæring eller funktion udføres . Men i en effektiv tolk er meget af oversættelsesarbejdet (herunder analyse af typer og lignende) udregnet og udført kun første gang et program, modul, funktion eller endda udsagn køres, og ligner derfor meget hvordan en compiler fungerer. Imidlertid kører et kompileret program stadig meget hurtigere under de fleste omstændigheder, dels fordi kompilatorer er designet til at optimere kode og kan få rigelig tid til dette. Dette gælder især for enklere sprog på højt niveau uden (mange) dynamiske datastrukturer, kontroller eller typekontrol .

I traditionel kompilering kan den eksekverbare output fra linkerne (.exe -filer eller .dll -filer eller et bibliotek, se billede) typisk flyttes, når den køres under et generelt operativsystem, ligesom objektkodemodulerne er, men med den forskel, at denne flytning udføres dynamisk ved løbetid, dvs. når programmet indlæses til udførelse. På den anden side er kompilerede og sammenkædede programmer til små integrerede systemer typisk statisk allokeret, ofte hårdt kodet i en NOR -flashhukommelse , da der ofte ikke er noget sekundært lager og intet operativsystem i denne forstand.

Historisk set har de fleste tolke-systemer haft en selvstændig editor indbygget. Dette bliver mere og mere almindeligt også for kompilatorer (dengang ofte kaldet en IDE ), selvom nogle programmører foretrækker at bruge en editor efter eget valg og køre kompilatoren, linkeren og andre værktøjer manuelt. Historisk set går kompilatorer forud for tolke, fordi hardware på det tidspunkt ikke kunne understøtte både tolk og fortolket kode og datidens typiske batchmiljø begrænsede fordelene ved fortolkning.

Udviklingscyklus

Under softwareudviklingscyklussen foretager programmører hyppige ændringer af kildekoden. Når du bruger en kompilator, hver gang der foretages en ændring af kildekoden, skal de vente på, at kompilatoren oversætter de ændrede kildefiler og forbinder alle de binære kodefiler sammen, før programmet kan udføres. Jo større program, jo ​​længere ventetid. Derimod venter en programmør, der bruger en tolk, meget mindre ventetid, da tolken normalt bare skal oversætte koden, der arbejdes med, til en mellemrepræsentation (eller slet ikke oversætte den), hvilket kræver meget mindre tid, før ændringerne kan foretages testet. Effekter er tydelige ved at gemme kildekoden og genindlæse programmet. Kompileret kode debugges generelt mindre, da redigering, kompilering og sammenkædning er sekventielle processer, der skal udføres i den rigtige sekvens med et ordentligt sæt kommandoer. Af denne grund har mange kompilatorer også et udøvende hjælpemiddel, kendt som en Make -fil og et program. Make -filen viser kommandolinjer til kompiler og linker og programkildekodefiler, men kan tage en simpel kommandolinjemenuindgang (f.eks. "Make 3"), der vælger den tredje gruppe (sæt) instruktioner, derefter udsteder kommandoerne til kompilatoren og linker, der fodrer de angivne kildekodefiler.

Fordeling

En kompilator konverterer kildekode til binær instruktion for en specifik processors arkitektur, hvilket gør den mindre bærbar . Denne konvertering foretages kun én gang i udviklerens miljø, og derefter kan den samme binære distribueres til brugerens maskiner, hvor den kan udføres uden yderligere oversættelse. En krydskompilator kan generere binær kode til brugermaskinen, selvom den har en anden processor end den maskine, hvor koden er kompileret.

Et fortolket program kan distribueres som kildekode. Det skal oversættes til hver sidste maskine, hvilket tager mere tid, men gør programfordelingen uafhængig af maskinens arkitektur. Imidlertid er portabiliteten af ​​fortolket kildekode afhængig af, at målmaskinen faktisk har en passende tolk. Hvis tolken skal leveres sammen med kilden, er den samlede installationsproces mere kompleks end levering af en monolitisk eksekverbar, da tolken selv er en del af det, der skal installeres.

Den kendsgerning, at fortolket kode let kan læses og kopieres af mennesker, kan være bekymret fra ophavsrettens synspunkt . Imidlertid findes der forskellige systemer til kryptering og tilsløring . Levering af mellemkode, f.eks. Bytecode, har en lignende effekt som tilsløring, men bytecode kan dekodes med en dekompiler eller demonterer .

Effektivitet

Den største ulempe ved tolke er, at et fortolket program typisk kører langsommere, end hvis det var blevet udarbejdet . Forskellen i hastigheder kan være lille eller stor; ofte en størrelsesorden og nogle gange mere. Det tager generelt længere tid at køre et program under en tolk end at køre den kompilerede kode, men det kan tage mindre tid at tolke det end den samlede tid, der kræves for at kompilere og køre det. Dette er især vigtigt, når prototyper og tester kode, når en rediger-fortolk-debug-cyklus ofte kan være meget kortere end en rediger-kompilér-kør-debug-cyklus.

At tolke kode er langsommere end at køre den kompilerede kode, fordi tolken skal analysere hver sætning i programmet hver gang den udføres og derefter udføre den ønskede handling, hvorimod den kompilerede kode bare udfører handlingen inden for en fast kontekst bestemt af kompilationen. Denne runtime- analyse er kendt som "fortolkende overliggende". Adgang til variabler er også langsommere i en tolk, fordi kortlægningen af ​​identifikatorer til lagringssteder skal udføres gentagne gange i løbetid frem for på kompileringstidspunktet .

Der er forskellige kompromiser mellem udviklingshastigheden ved brug af en tolk og udførelseshastigheden ved brug af en compiler. Nogle systemer (f.eks. Nogle Lisps ) tillader fortolket og kompileret kode at ringe til hinanden og dele variabler. Det betyder, at når en rutine er blevet testet og fejlfindet under tolken, kan den kompileres og dermed drage fordel af hurtigere udførelse, mens andre rutiner udvikles. Mange tolke udfører ikke kildekoden som den er, men konverterer den til en mere kompakt intern form. Mange BASIC tolke erstatte søgeord med enkelt byte poletter , som kan bruges til at finde den instruktion i et spring tabel . Nogle få tolke, f.eks. PBASIC- tolken, opnår endnu højere niveauer af programkomprimering ved at bruge en bitorienteret snarere end en byteorienteret programhukommelsesstruktur, hvor kommandotokener fylder måske 5 bit, nominelt lagres "16-bit" -konstanter i en kode med variabel længde, der kræver 3, 6, 10 eller 18 bit, og adresseoperander inkluderer en "bitforskydning". Mange BASIC -tolke kan gemme og læse deres egen tokeniserede interne repræsentation tilbage.

En tolk kan godt bruge den samme leksikale analysator og parser som kompilatoren og derefter fortolke det resulterende abstrakte syntakstræ . Eksempel på datatypedefinitioner for sidstnævnte og en legetøjstolk til syntakstræer, der er hentet fra C -udtryk, er vist i boksen.

Regression

Fortolkning kan ikke bruges som den eneste udførelsesmetode: selvom en tolk selv kan tolkes og så videre, er der brug for et direkte udført program et sted i bunden af ​​stakken, fordi koden, der fortolkes, ikke per definition er det samme som maskinkoden, som CPU'en kan udføre.

Variationer

Bytecode -tolke

Der er et spektrum af muligheder mellem tolkning og kompilering, afhængigt af analysemængden, før programmet udføres. For eksempel er Emacs Lisp kompileret til bytecode , som er en stærkt komprimeret og optimeret repræsentation af Lisp -kilden, men ikke er maskinkode (og derfor ikke knyttet til nogen bestemt hardware). Denne "kompilerede" kode fortolkes derefter af en bytecodetolk (selv skrevet i C ). Den kompilerede kode i dette tilfælde er maskinkode til en virtuel maskine , som ikke implementeres i hardware, men i bytecode -tolken. Sådanne kompilerende tolke kaldes undertiden også kompretere . I en bytecode -tolk starter hver instruktion med en byte, og derfor har bytecode -tolke op til 256 instruktioner, selvom ikke alle kan bruges. Nogle bytecodes kan tage flere bytes og kan være vilkårligt komplicerede.

Kontroltabeller - der ikke nødvendigvis nogensinde behøver at passere gennem en kompileringsfase - dikterer passende algoritmisk kontrolflow via tilpassede tolke på lignende måde som bytecode -tolke.

Gevindtolke med gevind

Gevindkodetolke ligner bytecode -tolke, men i stedet for bytes bruger de pointere. Hver "instruktion" er et ord, der peger på en funktion eller en instruktionssekvens, eventuelt efterfulgt af en parameter. Den trådede kodetolker looper enten med at hente instruktioner og kalde de funktioner, de peger på, eller henter den første instruktion og hopper til den, og hver instruktionssekvens slutter med en hentning og spring til den næste instruktion. I modsætning til bytecode er der ingen effektiv grænse for antallet af andre instruktioner end den tilgængelige hukommelse og adresserum. Det klassiske eksempel på gevindkode er Forth -koden, der bruges i Open Firmware -systemer: kildesproget kompileres til "F -kode" (en bytecode), som derefter fortolkes af en virtuel maskine .

Abstrakt syntaks træ fortolkere

I spektret mellem fortolkning og kompilering er en anden tilgang at transformere kildekoden til et optimeret abstrakt syntakstræ (AST), derefter udføre programmet efter denne træstruktur eller bruge det til at generere native kode just-in-time . I denne tilgang skal hver sætning parses bare én gang. Som en fordel i forhold til bytecode bevarer AST den globale programstruktur og forholdet mellem udsagn (som går tabt i en bytecode -repræsentation), og når den komprimeres, giver den en mere kompakt repræsentation. Således er anvendelse af AST blevet foreslået som et bedre mellemformat til just-in-time-kompilatorer end bytecode. Det giver også systemet mulighed for at udføre bedre analyser under runtime.

For tolke forårsager en AST imidlertid mere overhead end en bytecode -tolk på grund af noder relateret til syntaks, der ikke udfører noget nyttigt arbejde, af en mindre sekventiel repræsentation (kræver passage af flere pointer) og af overhead, der besøger træet.

Just-in-time kompilering

Yderligere sløring af sondringen mellem tolke, bytecode-tolke og kompilering er just-in-time (JIT) -samling, en teknik, hvor den mellemliggende repræsentation kompileres til native maskinkode ved runtime. Dette giver effektiviteten ved at køre native kode på bekostning af opstartstid og øget hukommelsesbrug, når bytecode eller AST først kompileres. Den tidligste offentliggjorte JIT -kompilator tilskrives generelt arbejde på LISP af John McCarthy i 1960. Adaptiv optimering er en komplementær teknik, hvor tolken profilerer det kørende program og samler dets hyppigst udførte dele til indfødt kode. Sidstnævnte teknik er et par årtier gammel og optræder på sprog som Smalltalk i 1980'erne.

Just-in-time-kompilering har opnået almindelig opmærksomhed blandt sprogimplementører i de seneste år, med Java , .NET Framework , de fleste moderne JavaScript- implementeringer og Matlab, der nu inkluderer JIT-kompilatorer.

Skabelonfortolker

At gøre sondringen mellem kompilatorer og tolke endnu engang endnu mere uklar er et specielt tolkedesign kendt som en skabelontolk. I stedet for at implementere udførelsen af ​​kode i kraft af en stor switch -sætning, der indeholder alle mulige mulige bytekoder, mens en skabelontolker opererer på en softwarestak eller en trægang, opretholder en lang række bytecode (eller enhver effektiv mellemrepræsentation) direkte til tilsvarende native maskine instruktioner, der kan udføres på værtshardwaren som nøgleværdipar, kendt som en "skabelon". Når det særlige kodesegment udføres, indlæser tolken simpelthen opcode -kortlægningen i skabelonen og kører den direkte på hardwaren. På grund af sit design ligner skabelontolken meget stærkt en just-in-time compiler frem for en traditionel tolk, men det er teknisk set ikke et JIT på grund af det faktum, at den blot oversætter kode fra sproget til native opkald en opcode på en tid i stedet for at oprette optimerede sekvenser af CPU -eksekverbare instruktioner fra hele kodesegmentet. På grund af fortolkerens enkle design ved simpelthen at viderestille opkald direkte til hardwaren i stedet for at implementere dem direkte, er det meget hurtigere end alle andre typer, selv bytecolke -tolke, og i et omfang mindre tilbøjelige til fejl, men som en afvejning er vanskeligere at vedligeholde på grund af at tolken skal understøtte oversættelse til flere forskellige arkitekturer i stedet for en platformuafhængig virtuel maskine/stak. Til dato er den eneste skabelonfortolkerimplementering af et sprog, der findes, tolken inden for HotSpot/OpenJDK Java Virtual Machine -referenceimplementering.

Selvfortolker

En selvfortolker er en programmeringssprogstolk skrevet i et programmeringssprog, der kan tolke sig selv; et eksempel er en BASIC -tolk skrevet i BASIC. Selvfortolkere er relateret til selvværtskompilatorer .

Hvis der ikke findes en kompilator til sproget, der skal tolkes, kræver oprettelse af en selvfortolker implementering af sproget på et værtsprog (som kan være et andet programmeringssprog eller en assembler ). Ved at have en første tolk som denne bootstarter systemet, og der kan udvikles nye versioner af tolken på selve sproget. Det var på denne måde, at Donald Knuth udviklede TANGLE -tolken til sproget WEB i den industrielle standard TeX -sætningssystem .

Definition af et edb-sprog sker normalt i forhold til en abstrakt maskine (såkaldt operationel semantik ) eller som en matematisk funktion ( denotationssemantik ). Et sprog kan også defineres af en tolk, hvor semantikken i værtsproget er givet. En selvfortolkers definition af et sprog er ikke velbegrundet (det kan ikke definere et sprog), men en selvfortolker fortæller en læser om et sprogs udtryksfuldhed og elegance. Det gør det også muligt for tolken at fortolke sin kildekode, det første skridt mod reflekterende fortolkning.

En vigtig designdimension i implementeringen af ​​en selvfortolker er, om en funktion i det fortolkede sprog er implementeret med den samme funktion i fortolkerens værtsprog. Et eksempel er, om en lukning i et Lisp -lignende sprog implementeres ved hjælp af lukninger på tolksproget eller implementeres "manuelt" med en datastruktur, der eksplicit lagrer miljøet. Jo flere funktioner implementeret af den samme funktion på værtsproget, jo mindre kontrol har tolkens programmør; en anden adfærd for håndtering af taloverløb kan ikke realiseres, hvis de aritmetiske operationer delegeres til tilsvarende operationer på værtsproget.

Nogle sprog som Lisp og Prolog har elegante selvfortolkere. Meget forskning om selvfortolkere (især reflekterende tolke) er blevet udført på programmeringssproget Scheme , en dialekt af Lisp. Generelt tillader ethvert Turing-komplet sprog imidlertid at skrive sin egen tolk. Lisp er et sådant sprog, fordi Lisp -programmer er lister over symboler og andre lister. XSLT er et sådant sprog, fordi XSLT -programmer er skrevet i XML. Et underdomæne i metaprogrammering er skrivning af domænespecifikke sprog (DSL'er).

Clive Gifford introducerede en målekvalitet for selvfortolker (egenforholdet), grænsen for forholdet mellem computertid brugt på at køre en stak N- selvfortolkere og tid brugt til at køre en stak N -1 selvfortolkere, når N går til uendelighed. Denne værdi afhænger ikke af, hvilket program der køres.

Bogen Struktur og fortolkning af computerprogrammer præsenterer eksempler på meta-cirkulær fortolkning af Scheme og dets dialekter. Andre eksempler på sprog med en selvfortolker er Forth og Pascal .

Mikrokode

Mikrokode er en meget almindeligt anvendt teknik "der pålægger en tolk mellem hardware og arkitektonisk niveau på en computer". Som sådan er mikrokoden et lag af hardware-niveau instruktioner, der implementerer højere maskinkode instruktioner eller intern tilstand maskinsekvensering i mange digitale behandlingselementer . Mikrokode bruges i generelle centrale behandlingsenheder såvel som i mere specialiserede processorer såsom mikrokontrollere , digitale signalprocessorer , kanalstyringer , diskcontrollere , netværksgrænsefladekontroller , netværksprocessorer , grafikbehandlingsenheder og i anden hardware.

Mikrokode findes typisk i speciel højhastighedshukommelse og oversætter maskininstruktioner, angiver maskindata eller anden input til sekvenser af detaljerede kredsløbsniveauoperationer. Det adskiller maskinens instruktioner fra den underliggende elektronik, så instruktionerne kan designes og ændres mere frit. Det letter også opbygningen af ​​komplekse flertrinsinstruktioner, samtidig med at kompleksiteten af ​​computerkredsløb reduceres. At skrive mikrokode kaldes ofte mikroprogrammering, og mikrokoden i en bestemt processorimplementering kaldes undertiden et mikroprogram .

Mere omfattende mikrokodning tillader små og enkle mikroarkitekturer at efterligne mere kraftfulde arkitekturer med bredere ordlængde , flere udførelsesenheder og så videre, hvilket er en relativt enkel måde at opnå softwarekompatibilitet mellem forskellige produkter i en processorfamilie.

Computer processor

Selv en ikke -mikrokodende computerprocessor i sig selv kan betragtes som en analyserende tolk til øjeblikkelig udførelse, der er skrevet i et hardwarebeskrivelsessprog til almindelige formål som f.eks. VHDL for at oprette et system, der analyserer maskinkodeinstruktionerne og udfører dem straks.

Ansøgninger

  • Tolke bruges ofte til at udføre kommandosprog og lime sprog, da hver operator, der udføres på kommandosprog, normalt er en påkaldelse af en kompleks rutine, f.eks. En editor eller kompilator.
  • Selvmodificerende kode kan let implementeres i et tolket sprog. Dette relaterer sig til fortolkningens oprindelse i Lisp og forskning i kunstig intelligens .
  • Virtualisering . Maskinkode beregnet til en hardware -arkitektur kan køres ved hjælp af en virtuel maskine . Dette bruges ofte, når den tiltænkte arkitektur ikke er tilgængelig eller blandt andre anvendelser til at køre flere kopier.
  • Sandkasse : Mens nogle typer sandkasser er afhængige af operativsystembeskyttelse, bruges en tolk eller virtuel maskine ofte. Den egentlige hardware -arkitektur og den oprindeligt tiltænkte hardware -arkitektur er muligvis ikke den samme. Dette kan virke meningsløst, bortset fra at sandkasser ikke er tvunget til faktisk at udføre alle instruktionerne kildekoden, det behandler. Især kan den nægte at udføre kode, der overtræder eventuelle sikkerhedsbegrænsninger , den fungerer under.
  • Emulatorer til at køre computersoftware skrevet til forældet og utilgængelig hardware på mere moderne udstyr.

Se også

Referencer

eksterne links