W skrócie
Języki programowania składają się ze składni, która reprezentuje program jako ciągi znaków, oraz semantyki, która jest zamierzonym znaczeniem programu.
Języki formalne są składnią bez znaczenia. Ma to na celu zbadanie struktury zestawów ciągów zdefiniowanych formalnie, zwykle bez przypisywania znaczenia tym ciągom.
Wyrażenia regularne i inne formalizmy (takie jak gramatyka bezkontekstowa) są używane do definiowania języków formalnych, używanych jako składniowy składnik programowania i języków naturalnych, tj. Do reprezentowania zdań w uporządkowany sposób. Inne mechanizmy są używane do powiązania tej struktury z semantyką języków programowania.
Wiele jest tutaj znacznie uproszczonych, szczególnie w odniesieniu do języka naturalnego.
Z wieloma szczegółami
Aby odpowiedzieć na twoje pytanie, powinniśmy zacząć od początku. Język w zwykłym znaczeniu to nieformalnie sposób przekazywania informacji lub pomysłów. W języku zwykle rozróżnia się składnię i semantykę. Semantyka jest tym, o czym chcesz rozmawiać / pisać. informacje, które chcesz przekazać. Składnia to sposób, w jaki ją przekazujesz, tj. Konwencjonalna reprezentacja, którą można wymieniać między ludźmi, a teraz także między ludźmi i urządzeniami lub między urządzeniami (komputerami).
Zazwyczaj używasz tego słowa, dog
aby przekazać ideę psa. Słowo dog
składa się z trzech liter lub innego równoważnego dźwięku i ma być reprezentacją jakiegoś zwierzęcia. Kluczową ideą jest to, że komunikacja odbywa się poprzez przedstawienie tego, co ma zostać przekazane. Struktury reprezentacji są zwykle nazywane składnią, a reprezentowana jest semantyką. Dotyczy to mniej więcej języka naturalnego, a także języków programowania.
Słowa to byty syntaktyczne, które reprezentują mniej więcej podstawowe pojęcia semantyczne. Ale te podstawowe pojęcia należy łączyć na różne sposoby, aby nadać bardziej złożone znaczenie. Piszemy,
the dog
aby przekazać, że mamy na myśli konkretnego psa, i the dog bites the cat
przekazać bardziej złożony pomysł. Ale sposób, w jaki słowa są zorganizowane, musi zostać ustalony przez reguły, abyśmy mogli stwierdzić, który z psów i kot rzeczywiście gryzie drugie.
Mamy więc takie zasady, sentence -> subject verb complement
które powinny pasować do zdań i powiedzieć nam, w jaki sposób wyrażane są pomysły związane z każdą częścią. Reguły te są regułami składniowymi, ponieważ mówią nam, w jaki sposób ma być zorganizowana reprezentacja naszej wiadomości. Sama subject
może być zdefiniowana przez regułę subject -> article noun
i tak dalej.
2 x + 1 = 23x123
equation -> expression "=" expression
expression -> expression "+" expression
expression -> number
Struktura języków programowania jest taka sama. Języki programowania są semantycznie wyspecjalizowane w wyrażaniu obliczeń, które należy wykonać, a nie w wyrażaniu problemów do rozwiązania, dowodu twierdzeń lub przyjaznych relacji między zwierzętami. Ale to główna różnica.
Reprezentacje stosowane w składni to zwykle ciągi znaków lub dźwięków w językach mówionych. Semantyka zwykle należy do dziedziny abstrakcyjnej lub ewentualnie do rzeczywistości, ale wciąż jest abstrakcyjna w naszych procesach myślowych lub do dziedziny behawioralnej urządzeń. Komunikacja wymaga zakodowania informacji / idei w składni, która jest przesyłana i dekodowana przez odbiornik. Wynik jest następnie interpretowany w jakikolwiek sposób przez odbiorcę.
Widzimy więc głównie składnię i jej strukturę. Powyższy przykład jest tylko jednym z najczęstszych sposobów definiowania łańcuchów składniowych i ich organizacji strukturalnej. Są inni. Dla danego języka niektórym ciągom można przypisać strukturę, o których mówi się, że należą do języka, podczas gdy inne nie.
To samo dotyczy słów. Niektóre sekwencje liter (lub dźwięków) są prawidłowymi słowami, a inne nie.
Języki formalne to tylko składnia bez semantyki. Określają za pomocą zestawu reguł, jakie sekwencje można konstruować, używając podstawowych elementów alfabetu. Zasady mogą być bardzo zmienne, czasem złożone. Ale języki formalne są używane do wielu celów matematycznych poza komunikacją językową, zarówno naturalną, jak i do programowania. Zestaw reguł definiujących ciągi w języku nazywa się gramatyką. Ale istnieje wiele innych sposobów definiowania języków.
W praktyce język składa się z dwóch poziomów. Poziom leksykalny definiuje słowa zbudowane z alfabetu znaków. Poziom składniowy definiuje zdania lub programy zbudowane z alfabetu słów (a ściślej z rodzin słów, aby pozostały alfabetem skończonym). Jest to z konieczności nieco uproszczone.
Struktura słów jest dość prosta w większości języków (programistycznych lub naturalnych), dlatego zwykle definiuje się je za pomocą tego, co zwykle uważa się za najprostszy rodzaj języka formalnego: zwykłe języki. Można je zdefiniować za pomocą wyrażeń regularnych (regexp) i dość łatwo można je zidentyfikować za pomocą zaprogramowanych urządzeń zwanych automatami skończonymi. W przypadku języków programowania przykładami słowa są identyfikator, liczba całkowita, ciąg, liczba rzeczywista, słowo zastrzeżone, takie jak if
lub repeat
, symbol interpunkcyjny lub otwarty nawias. Przykładami rodzin słów są identyfikator, ciąg, liczba całkowita.
Poziom składniowy jest zwykle definiowany przez nieco bardziej złożony typ języka formalnego: języki bezkontekstowe, używając słów jako alfabetu. Reguły, które widzieliśmy powyżej, to bezkontekstowe reguły dla języka naturalnego. W przypadku języków programowania reguły mogą być:
statement -> assignment
statement -> loop
loop -> "while" expression "do" statement
assignment -> "identifier" "=" expression
expression -> "identifier"
expression -> "integer"
expression -> expression "operator" expression
Dzięki takim regułom możesz pisać:
while aaa /= bbb do aaa = aaa + bbb / 6
który jest stwierdzeniem.
Sposób, w jaki został wytworzony, może być reprezentowany przez strukturę drzewa zwaną drzewem parsowania lub drzewem składni (tutaj niepełne):
statement
|
_______________ loop _______________
/ / \ \
"while" expression "do" statement
__________|_________ |
/ | \ assignment
expression "operator" expression _______|_______
| | | / | \
"identifier" "/=" "identifier" "identifier" "=" expression
| | | |
aaa bbb aaa ... ...
Nazwy pojawiające się po lewej stronie reguły są nazywane nieterminalami, podczas gdy słowa te nazywane są także terminali, ponieważ znajdują się w alfabecie dla języka (powyżej poziomu leksykalnego). Nieterminalne reprezentują różne struktury składniowe, których można użyć do skomponowania programu.
Takie reguły nazywane są bezkontekstowe, ponieważ terminale nieterminalne można dowolnie zastąpić dowolnymi odpowiadającymi im regułami, niezależnie od kontekstu, w którym się pojawiają. Zestaw reguł definiujących język nazywa się gramatyką bezkontekstową.
W rzeczywistości istnieją ograniczenia dotyczące tego, kiedy identyfikatory muszą być najpierw zadeklarowane, lub gdy wyrażenie musi spełniać ograniczenia typu. Ale takie ograniczenie można uznać za semantyczne, a nie składniowe. W rzeczywistości niektórzy specjaliści umieszczają je w tak
zwanej semantyce statycznej .
Przy danym zdaniu, dowolnym programie znaczenie tego zdania wyodrębnia się, analizując strukturę podaną przez drzewo analizy dla tego zdania. Dlatego bardzo ważne jest opracowanie algorytmów, zwanych parserami, które mogą odzyskać strukturę drzewa odpowiadającą programowi, jeśli zostanie mu dany program.
Parser poprzedza analizator leksykalny, który rozpoznaje słowa i określa rodzinę, do której należą. Następnie sekwencja słów lub elementów leksykalnych jest przekazywana do analizatora składni, który pobiera strukturę drzewa. Na podstawie tej struktury kompilator może następnie określić, w jaki sposób wygenerować kod, który jest jego semantyczną częścią przetwarzania programu po stronie kompilatora.
Analizator składni kompilatora może faktycznie zbudować strukturę danych odpowiadającą parsowaniu i przekazać go do późniejszych etapów procesu kompilacji, ale nie musi tego robić. Uruchomienie algorytmu analizującego sprowadza się do opracowania strategii obliczeniowej do eksploracji drzewa składni, które jest niejawne w tekście programu. To drzewo składni / parsowania może, ale nie musi być wyjaśnione w tym procesie, w zależności od strategii kompilacji (liczba etapów). Konieczne jest jednak to, że ostatecznie istnieje co najmniej jedno oddolne badanie drzewa parsowania, bez względu na to, czy zostało to wyjaśnione, czy pozostawione jako ukryte w strukturze obliczeniowej.
Powodem tego jest intuicyjnie to, że standardowym formalnym sposobem definiowania semantyki związanej ze strukturą drzewa składniowego jest tak zwany homomorfizm. Nie bój się wielkiego słowa. Chodzi o to, aby wziąć pod uwagę znaczenie całości zbudowane na podstawie znaczenia części, na podstawie operatora, który je łączy
Na przykład zdanie the dog bites the cat
można analizować za pomocą reguły sentence -> subject verb complement
. Znając rozumieniu 3 poddrzew subject
, verb
i complement
, zasadę, że komponuje im mówi, że przedmiotem robi akcję, a kot jest tym, który zostaje ugryziony.
To jest tylko intuicyjne wyjaśnienie, ale można je sformalizować. Semantyka jest konstruowana w górę od składników. Ale to kryje wiele komplikacji.
Wewnętrzne działanie kompilatora można rozłożyć na kilka etapów. Rzeczywisty kompilator może działać etapy, wykorzystując reprezentacje pośrednie. Może także łączyć niektóre etapy. Zależy to od zastosowanej technologii i złożoności kompilacji danego języka.