Syntaksi (ohjelmointikielet) - Syntax (programming languages)

Image
Syntaksin korostusta ja sisennystyyliä käytetään usein auttamaan ohjelmoijia tunnistamaan lähdekoodin elementtejä. Tämä Python -koodi käyttää värikoodattua korostusta.

In Computer Science , The syntaksin on tietokone kieli on sääntöjä, jotka määritellään yhdistelmiä symboleja, jotka katsotaan oikein rakenteeltaan lausuntoja tai ilmaisuja kyseisellä kielellä. Tämä koskee sekä ohjelmointikieliä , joissa asiakirja edustaa lähdekoodia , että merkintäkieliä , joissa asiakirja edustaa tietoja.

Kielen syntaksi määrittää sen pintamuodon. Tekstipohjaiset tietokonekielet perustuvat merkkisarjoihin , kun taas visuaaliset ohjelmointikielet perustuvat alueelliseen asetteluun ja symbolien välisiin yhteyksiin (jotka voivat olla teksti- tai graafisia). Syntaktisesti virheellisissä asiakirjoissa sanotaan olevan syntaksivirhe . Kielen syntaksia suunnitellessaan suunnittelija voi aloittaa kirjoittamalla esimerkkejä sekä laillisista että laittomista merkkijonoista , ennen kuin yrittää selvittää näiden sääntöjen yleiset säännöt.

Syntaksi viittaa siis koodin muotoon , ja sitä verrataan semantiikkaan - merkitykseen . Tietokonekielien käsittelyssä semanttinen käsittely tulee yleensä syntaktisen käsittelyn jälkeen; Joissakin tapauksissa semanttinen käsittely on kuitenkin välttämätöntä täydellisen syntaktisen analyysin suorittamiseksi, ja ne suoritetaan yhdessä tai samanaikaisesti . Eräässä kääntäjä , syntaktinen analyysi käsittää käyttöliittymän , kun taas semanttinen analyysi käsittää taustajärjestelmään (ja keski lopussa, jos tämä vaihe on erottaa).

Syntaksin tasot

Tietokoneen kielen syntaksi jaetaan yleensä kolmeen tasoon:

  • Sanat - leksinen taso, joka määrittää, kuinka merkit muodostavat rahakkeita ;
  • Lausekkeet - kieliopin taso, suppeasti ottaen, määrittää, kuinka rahakkeet muodostavat lauseita;
  • Konteksti - määritetään, mihin objekteihin tai muuttujiin nimet viittaavat, ovatko tyypit kelvollisia jne.

Tällä tavalla erottaminen tuottaa modulaarisuutta, jolloin jokainen taso voidaan kuvata ja käsitellä erikseen ja usein itsenäisesti. Ensiksi lekseri muuttaa lineaarisen merkkijonon lineaariseksi merkkisekvenssiksi; tätä kutsutaan " leksikaaliseksi analyysiksi " tai "lexingiksi". Toiseksi jäsennin muuttaa merkkien lineaarisen sekvenssin hierarkkiseksi syntaksipuuksi; tätä kutsutaan suppeasti " jäsentämiseksi ". Kolmanneksi asiayhteysanalyysi ratkaisee nimet ja tarkistaa tyypit. Tämä modulaarisuus on joskus mahdollista, mutta monilla reaalimaailman kielillä aikaisempi vaihe riippuu myöhemmästä vaiheesta-esimerkiksi lexerin hakkerointi C: ssä johtuu siitä, että tokenisointi riippuu kontekstista. Jopa näissä tapauksissa syntaktisen analyysin nähdään usein lähentävän tätä ihanteellista mallia.

Itse jäsentämisvaihe voidaan jakaa kahteen osaan: jäsennyspuu tai "konkreettinen syntaksipuu", joka määritetään kieliopin mukaan, mutta on yleensä liian yksityiskohtainen käytännön käyttöön, ja abstrakti syntaksipuu (AST), joka yksinkertaistaa tämä käyttökelpoiseen muotoon. AST- ja asiayhteysanalyysivaiheita voidaan pitää semanttisen analyysin muotona, koska ne lisäävät syntaksiin merkityksen ja tulkinnan tai vaihtoehtoisesti syntaktisten sääntöjen epävirallisina, manuaalisina toteutuksina, joita olisi vaikea tai hankala kuvata tai toteuttaa muodollisesti.

Tasot vastaavat yleensä Chomsky -hierarkian tasoja . Sanat ovat tavallisella kielellä , joka on määritelty leksikkäisessä kieliopissa , joka on tyypin 3 kielioppi, joka annetaan yleensä säännöllisinä lausekkeina . Lauseet ovat joka yhteydettömän kielen (CFL), yleensä deterministinen yhteydettömän kielen (DCFL), eriteltävä lauseen rakenteessa kielioppi , joka on A-tyypin 2 kielioppi, ilmoitetaan yleensä tuotantoa koskevia sääntöjä on Backus-Naur muoto (BNF ). Lausekieliopit määritellään usein paljon rajoitetummissa kieliopissa kuin täydelliset kontekstittomat kieliopit , jotta niiden jäsentäminen olisi helpompaa. kun LR jäsennin voi jäsentää tahansa DCFL lineaarisessa ajassa, yksinkertainen LALR jäsennin ja jopa helpompaa LL jäsennin ovat tehokkaampia, mutta voi vain jäsentää kieliopeista jonka tuotantoa koskevat säännöt ovat rajalliset. Periaatteessa, asiayhteyteen rakenne voidaan kuvata kielioppia , ja automaattisesti analysoidaan kuten attribuuttikielioppi , vaikka yleensä tämä vaihe tapahtuu käsin, kautta nimi resoluutio sääntöjä ja tyyppi tarkkailun , ja toteutetaan kautta symboli taulukko joka tallentaa nimet ja tyypit kullekin alueelle.

On kirjoitettu työkaluja, jotka luovat automaattisesti lekserin säännöllisillä lausekkeilla kirjoitetusta sanasto -eritelmästä ja jäsentimen BNF: ssä kirjoitetusta lause -kieliopista: tämä mahdollistaa deklaratiivisen ohjelmoinnin käytön sen sijaan, että tarvittaisiin menettelyllisiä tai toiminnallisia ohjelmointeja. Hyvä esimerkki on lex - yacc pari. Nämä tuottavat automaattisesti konkreettisen syntaksipuun; jäsennyskirjoittajan on sitten kirjoitettava manuaalisesti koodi, joka kuvaa, miten tämä muunnetaan abstrakiksi syntaksipuuksi. Asiayhteysanalyysi toteutetaan yleensä myös manuaalisesti. Huolimatta näiden automaattisten työkalujen olemassaolosta jäsentäminen toteutetaan usein manuaalisesti eri syistä-ehkä lauserakenne ei ole kontekstiton tai vaihtoehtoinen toteutus parantaa suorituskykyä tai virheraportointia tai mahdollistaa kieliopin muuttamisen helpommin. Parsereita kirjoitetaan usein toiminnallisilla kielillä, kuten Haskell , tai skriptikielellä, kuten Python tai Perl , tai C- tai C ++ -kielellä .

Esimerkkejä virheistä

Esimerkki (add 1 1)on syntaktisesti pätevä Lisp -ohjelma (olettaen, että "add" -toiminto on olemassa, muuten nimen ratkaiseminen epäonnistuu), lisäämällä 1 ja 1. Seuraavat ovat kuitenkin virheellisiä:

(_ 1 1)    lexical error: '_' is not valid
(add 1 1   parsing error: missing closing ')'

Huomaa, että lexer ei pysty tunnistamaan ensimmäistä virhettä - se tietää vain, että tunnuksen LEFT_PAREN tuottamisen jälkeen ('' ohjelman loppuosa on virheellinen, koska sanasääntö ei ala numerolla _. Toinen virhe havaitaan jäsentämisvaiheessa: jäsennin on tunnistanut "list" -tuotantosäännön '(' -merkin (ainoana osumana) vuoksi ja voi siten antaa virheilmoituksen; yleensä se voi olla epäselvä .

Kirjoitusvirheitä ja ilmoittamattomia muuttujavirheitä pidetään toisinaan syntaksivirheinä, kun ne havaitaan käännöshetkellä (mikä yleensä tapahtuu vahvasti kirjoitettujen kielten kokoamisessa), vaikka on yleistä luokitella tällaiset virheet semanttisiin virheisiin.

Esimerkiksi Python -koodi

'a' + 1

sisältää tyyppivirheen, koska se lisää merkkijonon literaalin kokonaislukukirjeeseen. Tällaiset tyyppivirheet voidaan havaita käännöshetkellä: Ne voidaan havaita jäsentämisen (lauseanalyysin) aikana, jos kääntäjä käyttää erillisiä sääntöjä, jotka sallivat "integerLiteral + integerLiteral" mutta eivät "stringLiteral + integerLiteral", vaikka on todennäköisempää, että kääntäjä käyttää jäsentämissääntöä, joka sallii kaikki muodon "LiteralOrIdentifier + LiteralOrIdentifier" lausekkeet, ja sitten virhe havaitaan asiayhteysanalyysin aikana (kun tyyppitarkistus tapahtuu). Joissakin tapauksissa kääntäjä ei tee tätä validointia, ja nämä virheet havaitaan vain ajon aikana.

Dynaamisesti kirjoitetulla kielellä, jossa tyyppi voidaan määrittää vain ajon aikana, monet tyyppivirheet voidaan havaita vain ajon aikana. Esimerkiksi Python -koodi

a + b

on syntaktisesti pätevä lauseketasolla, mutta a- ja b -tyyppien oikeellisuus voidaan määrittää vain ajon aikana, koska muuttujilla ei ole tyyppejä Pythonissa, vain arvoilla. Vaikka ollaan eri mieltä siitä, pitäisikö kääntäjän havaitsemaa tyyppivirhettä kutsua syntaksivirheeksi ( staattisen semanttisen virheen sijasta ), kirjoitusvirheitä, jotka voidaan havaita vain ohjelman suoritushetkellä, pidetään aina semanttisina eikä syntaksivirheinä.

Syntaksin määritelmä

Image
Jäsennä Python -koodin puu , johon on lisätty tokenisaatio

Syntaksin tekstimuotoinen ohjelmointikielten määritellään yleensä yhdistämällä säännöllisiä lausekkeita (for leksikaalinen rakenne) ja Backus-Naur muoto (varten kieliopin rakenteeseen) induktiivisesti määrittää syntaktisen luokat (välikkeitä) ja terminaali symboleja. Syntaktiset luokat määritellään tuotannollisilla säännöillä , jotka määrittävät tietyn syntaktisen luokan arvot. Päätesymbolit ovat konkreettisia merkkejä tai merkkijonoja (esimerkiksi avainsanoja , kuten define , if , let tai void ), joista syntaktisesti pätevät ohjelmat rakennetaan.

Kielellä voi olla erilaisia ​​vastaavia kielioppeja, kuten vastaavat säännölliset lausekkeet (leksikätasolla) tai eri ilmaussäännöt, jotka luovat saman kielen. Laajemman kielioppiluokan, kuten LR -kieliopin, käyttäminen voi sallia lyhyemmät tai yksinkertaisemmat kieliopit verrattuna tiukempiin luokkiin, kuten LL -kielioppi, joka saattaa vaatia pidempiä kielioppeja ja enemmän sääntöjä. Eri mutta vastaavat lause kieliopit tuottavat erilaisia ​​jäsennyspuita, vaikka taustalla oleva kieli (kelvollisten asiakirjojen joukko) on sama.

Esimerkki: Lisp S-lausekkeet

Alla on yksinkertainen kielioppi, joka on määritelty käyttämällä säännöllisiä lausekkeita ja laajennettua Backus -Naur -muotoa . Se kuvaa S-lausekkeiden syntaksia, ohjelmointikielen Lisp tietosyntaksia , joka määrittelee syntaktisten luokkien lausekkeen , atomin , numeron , symbolin ja luettelon tuotannon :

expression = atom   | list
atom       = number | symbol    
number     = [+-]?['0'-'9']+
symbol     = ['A'-'Z']['A'-'Z''0'-'9'].*
list       = '(', expression*, ')'

Tämä kielioppi määrittää seuraavat asiat:

  • ilmaisu on joko atomi tai lista ;
  • atomi on joko numero tai symboli ;
  • numero on ehjä sekvenssi yhden tai useamman desimaalia, mahdollisesti edeltää plus- tai miinusmerkki;
  • symboli on kirjain, jota seuraa nolla tai useamman mitä tahansa merkkiä (lukuun ottamatta välilyönnit); ja
  • luettelo on sovitettu pari sulkuja, nolla tai useampia ilmaisuja sen sisällä.

Tässä desimaaliluvut, isot ja pienet kirjaimet sekä sulkeet ovat päätesymboleja.

Seuraavat ovat esimerkkejä hyvin muotoilluista merkkisekvensseistä tässä kieliopissa: ' 12345', ' ()', ' (A B C232 (1))'

Monimutkaiset kieliopit

Ohjelmointikielen määrittämiseen tarvittava kielioppi voidaan luokitella sen sijainnin perusteella Chomsky -hierarkiassa . Useimpien ohjelmointikielien lause kielioppi voidaan määrittää tyypin 2 kieliopilla, eli ne ovat asiayhteydettömiä kielioppeja , vaikka yleinen syntaksi on asiayhteysherkkä (muuttuvien ilmoitusten ja sisäkkäisten laajuuksien vuoksi), joten tyyppi 1. On kuitenkin poikkeuksia, ja joillakin kielillä lause kielioppi on tyyppi-0 (Turingin täydellinen).

Joillakin kielillä, kuten Perl ja Lisp, kielen määrittely (tai toteutus) sallii konstruktiot, jotka suoritetaan jäsennysvaiheen aikana. Lisäksi näillä kielillä on rakenteita, joiden avulla ohjelmoija voi muuttaa jäsentimen toimintaa. Tämä yhdistelmä hämärtää tehokkaasti eron jäsentämisen ja suorittamisen välillä ja tekee syntaksi -analyysistä ratkaisemattoman ongelman näillä kielillä, mikä tarkoittaa, että jäsennysvaihe ei ehkä pääty. Esimerkiksi Perlissä on mahdollista suorittaa koodi jäsentämisen aikana BEGINlauseella, ja Perl -funktion prototyypit voivat muuttaa syntaktista tulkintaa ja mahdollisesti jopa jäljellä olevan koodin syntaktista pätevyyttä. Puhekielessä tätä kutsutaan "vain Perl voi jäsentää Perlin" (koska koodi on suoritettava jäsennyksen aikana ja se voi muokata kielioppia), tai voimakkaammin "edes Perl ei voi jäsentää Perliä" (koska se ei ole määritettävissä). Samoin syntaksin käyttöön ottamat Lisp- makrotdefmacro suoritetaan myös jäsennyksen aikana, mikä tarkoittaa, että Lisp-kääntäjällä on oltava läsnä koko Lisp-ajonaikainen järjestelmä. Sitä vastoin C -makrot ovat vain merkkijonojen korvauksia eivätkä vaadi koodin suorittamista.

Syntaksi vastaan ​​semantiikka

Kielen syntaksi kuvaa kelvollisen ohjelman muodon, mutta ei anna tietoja ohjelman merkityksestä tai ohjelman suorittamisen tuloksista. Symbolien yhdistelmälle annetun merkityksen käsittelee semantiikka (joko muodollinen tai kovakoodattu viitetoteutuksessa ). Kaikki syntaktisesti oikeat ohjelmat eivät ole semanttisesti oikein. Monet syntaktisesti oikeat ohjelmat ovat kuitenkin huonosti muotoiltuja kielen sääntöjen mukaisesti; ja voi (kieliasetuksista ja toteutuksen luotettavuudesta riippuen) johtaa käännös- tai suoritusvirheeseen. Joissakin tapauksissa tällaisissa ohjelmissa voi esiintyä määrittelemätöntä käyttäytymistä . Vaikka ohjelma on hyvin määritelty kielellä, sillä voi silti olla merkitys, jota ei ole kirjoittanut.

Käyttämällä luonnollista kieltä esimerkkinä ei ehkä ole mahdollista antaa merkitystä kieliopillisesti oikealle lauseelle tai lause voi olla väärä:

  • " Värittömät vihreät ideat nukkuvat raivokkaasti ." on kieliopillisesti hyvin muotoiltu, mutta sillä ei ole yleisesti hyväksyttyä merkitystä.
  • "John on naimisissa oleva poikamies." on kieliopillisesti hyvin muotoiltu, mutta ilmaisee merkityksen, joka ei voi olla totta.

Seuraava C -kielifragmentti on syntaktisesti oikein, mutta suorittaa toiminnon, jota ei ole semanttisesti määritelty (koska se on nollapisteri , operaatiot ja sillä ei ole merkitystä): pp->realp->im

 complex *p = NULL;
 complex abs_p = sqrt (p->real * p->real + p->im * p->im);

Yksinkertaisempana esimerkkinä,

 int x;
 printf("%d", x);

on syntaktisesti pätevä, mutta ei semanttisesti määritelty, koska se käyttää alustamatonta muuttujaa . Vaikka joidenkin ohjelmointikielten kääntäjät (esim. Java ja C#) havaitsisivat tällaisia ​​alustamattomia muuttuvia virheitä, niitä olisi pidettävä semanttisina virheinä syntaksivirheiden sijasta.

Katso myös

Voit verrata nopeasti eri ohjelmointikielien syntaksia katsomalla "Hei, maailma!" -Luetteloa. esimerkkejä ohjelmasta :

Viitteet

Ulkoiset linkit