Syntax (Programmiersprachen)

Satzstellung markieren und Eingerichteter Stil werden häufig verwendet, um Programmierern zu helfen, Elemente des Quellcode zu erkennen. Dies Python Code verwendet farbcodiertes Hervorhebung.

Im Informatik, das Syntax von a Computer Sprache ist der Satz von Regeln, die die Kombinationen von Symbolen definieren, die als korrekt strukturiert betrachtet werden Aussagen oder Ausdrücke in dieser Sprache. Dies gilt beides auf Programmiersprachen, wo das Dokument repräsentiert Quellcodeund zu Markup -Sprachen, wo das Dokument Daten darstellt.

Die Syntax einer Sprache definiert ihre Oberflächenform.[1] Textbasiert Computersprachen basieren auf Sequenzen von Figuren, während visuelle Programmiersprachen basieren auf dem räumlichen Layout und Verbindungen zwischen Symbolen (die textuell oder grafisch sein können). Dokumente, die syntaktisch ungültig sind Syntax-Fehler. Bei der Gestaltung der Syntax einer Sprache kann ein Designer zunächst Beispiele für legale und illegale aufschreiben Saiten, bevor Sie versuchen, die allgemeinen Regeln aus diesen Beispielen herauszufinden.[2]

Syntax bezieht sich daher auf die bilden des Code und steht im Gegensatz zu mit Semantik - das Bedeutung. Bei der Verarbeitung von Computersprachen erfolgt die semantische Verarbeitung im Allgemeinen nach der syntaktischen Verarbeitung. In einigen Fällen ist jedoch eine semantische Verarbeitung für eine vollständige syntaktische Analyse erforderlich, und diese werden zusammen oder zusammen durchgeführt oder gleichzeitig. In einem CompilerDie syntaktische Analyse umfasst die Frontend, während Semantische Analyse umfasst die Backend (und mittleres Ende, wenn diese Phase unterschieden wird).

Syntaxniveaus

Die Computersprache Syntax unterscheidet sich im Allgemeinen in drei Ebenen:

  • Wörter - die lexikalische Ebene, bestimmt, wie sich Zeichenformen bilden Token;
  • Phrasen - die Grammatikniveau, eng gesehen, um zu bestimmen, wie Pokens Phrasen bilden;
  • Kontext - Bestimmen Sie, auf welche Objekte oder Variablennamen sich beziehen, wenn die Typen gültig sind usw.

Die Unterscheidung auf diese Weise ergibt die Modularität, sodass jede Ebene separat und oft unabhängig beschrieben und verarbeitet werden kann. Zunächst verwandelt ein Lexer die lineare Zeichenfolge von Zeichen in eine lineare Sequenz von Token; Dies ist als "bekannt"lexikalische Analyse"oder" Lexing ". Zweitens verwandelt der Parser die lineare Sequenz von Token in einen hierarchischen Syntaxbaum; dies ist als" bekannt als "Parsing"Grenzweise. Drittens löst die Kontextanalyse Namen und Überprüftypen auf. Diese Modularität ist manchmal möglich, aber in vielen realen Sprachen hängt ein früherer Schritt von einem späteren Schritt ab-zum Beispiel. Der Lexer -Hack In C liegt die Tokenisierung vom Kontext ab. Selbst in diesen Fällen wird die syntaktische Analyse häufig als Apparat an diesem idealen Modell angesehen.

Die Parsingbühne selbst kann in zwei Teile unterteilt werden: die Baum analysieren, oder "Betonsyntaxbaum", der durch die Grammatik bestimmt wird, aber im Allgemeinen viel zu detailliert für den praktischen Gebrauch und die Zusammenfassung Syntaxbaum (AST), was dies zu einer verwendbaren Form vereinfacht. Die AST- und Kontextanalyseschritte können als Form der semantischen Analyse betrachtet werden, da sie der Syntax oder als informelle manuelle Implementierungen syntaktischer Regeln, die schwierig oder unangenehm sind, offiziell zu beschreiben oder formell zu implementieren, als informelle implementierende syntaktische Regeln Sinn und Interpretation hinzufügen.

Die Ebenen entsprechen im Allgemeinen den Niveaus in der Chomsky -Hierarchie. Worte sind in a Regelmäßige Sprache, angegeben in der lexikalische Grammatik, was eine Typ-3-Grammatik ist, allgemein als angegeben als Reguläre Ausdrücke. Sätze sind in a Kontextfreie Sprache (CFL), im Allgemeinen a deterministische kontextfreie Sprache (DCFL), angegeben in a Phrasenstruktur Grammatik, was eine Typ-2-Grammatik ist, allgemein als angegeben als Produktionsregeln in Backus -Naur -Form (BNF). Die Phrase -Grammatiken werden oft in viel eingeschränkteren Grammatiken als voll spezifiziert Kontextfreie Grammatiken, um sie leichter zu analysieren; während LR Parser kann jede DCFL in linearer Zeit analysieren, die einfache Lalr Parser und noch einfacher LL Parser sind effizienter, können aber nur Grammatiken analysieren, deren Produktionsregeln eingeschränkt sind. Im Prinzip kann die Kontextstruktur durch a beschrieben werden Kontextsensitive Grammatikund automatisch analysiert mit Mitteln wie z. Grammatiken zuschreibenIm Allgemeinen erfolgt dieser Schritt jedoch manuell durch Namensauflösung Regeln und Geben Sie die Überprüfung einund über a implementiert Symboltabelle Das speichert Namen und Typen für jeden Umfang.

Es wurden Tools geschrieben, die automatisch einen Lexer aus einer lexikalischen Spezifikation in regulären Ausdrücken und einem Parser aus der in BNF geschriebenen Phrase -Grammatik erzeugen: Dies ermöglicht eine Verwendung deklarative Programmierung, anstatt prozedurale oder funktionelle Programmierung zu haben. Ein bemerkenswertes Beispiel ist das Lex-yacc Paar. Diese erzeugen automatisch a Beton Syntaxbaum; Der Parser -Schriftsteller muss dann manuell Code schreiben, der beschreibt, wie dies in eine konvertiert wird abstrakt Syntaxbaum. Die Kontextanalyse wird im Allgemeinen auch manuell implementiert. Trotz der Existenz dieser automatischen Tools wird die Parsen häufig aus verschiedenen Gründen manuell implementiert-möglicherweise ist die Phrasenstruktur nicht kontextfrei, oder eine alternative Implementierung verbessert die Leistung oder Fehlermeldung oder ermöglicht die Leichtigkeit der Grammatik. Parser werden oft in funktionalen Sprachen geschrieben, wie z. Haskelloder in Skriptsprachen, wie z. Python oder Perl, oder in C oder C ++.

Beispiele für Fehler

Als Beispiel, (Fügen Sie 1 1 hinzu) ist ein syntaktisch gültiges LISP -Programm (unter der Annahme, dass die Funktion 'hinzufügen' existiert, sonst nennen die Namensauflösung fehl), was 1 und 1 hinzufügt.

(_ 1 1) Lexikaler Fehler: '_' ist nicht gültig (1 1 Parsing -Fehler hinzufügen: Fehlende Schließen ')' ''

Beachten Sie, dass der Lexer nicht in der Lage ist, den ersten Fehler zu identifizieren - alles, was er weiß, ist, dass nach der Erzeugung des Token Left_Paren "(" Der Rest des Programms ist ungültig, da keine Wortregel mit "_" beginnt. Der zweite Fehler wird erkannt In der Parsing -Phase: Der Parser hat die Produktionsregel "Listen" aufgrund des '(' Tokens (als einziger Match) identifiziert und kann daher eine Fehlermeldung geben; im Allgemeinen kann es sein zweideutig.

Typ-Fehler und nicht deklarierte variable Fehler werden manchmal als Syntaxfehler angesehen, wenn sie bei der Kompilierungszeit erkannt werden (was normalerweise beim Kompilieren starker Sprachen der Fall ist) semantisch Fehler stattdessen.[3][4][5]

Als Beispiel der Python -Code

'a' + 1

Enthält einen Typfehler, da es einem Ganzzahl ein Zapfenliteral ein String -Literal hinzufügt. Typ-Fehler dieser Art können zur Kompilierungszeit erkannt werden Der Compiler verwendet eine Parsing -Regel, die alle Ausdrücke des Formulars "Literaloridentifier + Literaloridentifier" ermöglicht, und dann wird der Fehler während der Kontextanalyse erfasst (wenn Typprüfung auftritt). In einigen Fällen wird diese Validierung vom Compiler nicht durchgeführt, und diese Fehler werden nur zur Laufzeit erkannt.

In einer dynamisch getippten Sprache, bei der der Typ nur zur Laufzeit ermittelt werden kann, können viele Typfehler nur zur Laufzeit erkannt werden. Zum Beispiel der Python -Code

a + b

ist auf Phrasenebene syntaktisch gültig, aber die Richtigkeit der Arten von A und B kann nur zur Laufzeit bestimmt werden, da Variablen keine Typen in Python haben, nur Werte. Während es nicht einverstanden ist Statische Semantik Fehler), Typ Fehler, die nur zur Programmausführungszeit erkannt werden können, werden immer eher als semantisch als als Syntaxfehler angesehen.

Syntaxdefinition

Baum analysieren von Python -Code mit Inset Tokenisierung

Die Syntax von Textprogrammiersprachen wird normalerweise unter Verwendung einer Kombination von definiert Reguläre Ausdrücke (zum lexikalisch Struktur) und Backus -Naur -Form (zum grammatikalisch Struktur) induktiv spezifizieren syntaktische Kategorien (Nicht -Terminale) und Terminal Symbole. Syntaktische Kategorien werden durch Regeln definiert, die genannt werden Produktionen, die die Werte angeben, die zu einer bestimmten syntaktischen Kategorie gehören.[1] Terminalsymbole sind die konkreten Zeichen oder Zeichenfolgen von Zeichen (zum Beispiel Schlüsselwörter wie zum Beispiel definieren, wenn, Lassen, oder Leere) von welchen syntaktisch gültigen Programmen konstruiert werden.

Eine Sprache kann unterschiedliche äquivalente Grammatiken haben, wie z. B. äquivalente reguläre Ausdrücke (auf lexikalischen Ebenen) oder unterschiedliche Phrasenregeln, die dieselbe Sprache erzeugen. Die Verwendung einer breiteren Kategorie von Grammatik, wie z. B. LR -Grammatiken, kann kürzere oder einfachere Grammatiken im Vergleich zu eingeschränkteren Kategorien wie LL -Grammatik ermöglichen, die möglicherweise längere Grammatiken mit mehr Regeln erfordern. Unterschiedliche, aber äquivalente Phrasengrammatik ergeben unterschiedliche analysende Bäume, obwohl die zugrunde liegende Sprache (Satz gültiger Dokumente) gleich ist.

Beispiel: Lisp S-Expressions

Unten ist eine einfache Grammatik, definiert unter Verwendung der Notation regulärer Ausdrücke und Erweiterte Backus -Naur -Form. Es beschreibt die Syntax von S-Expressionen, eine Datensyntax der Programmiersprache Lispeln, was Produktionen für die syntaktischen Kategorien definiert Ausdruck, Atom, Nummer, Symbol, und aufführen:

Ausdruck = Atom | aufführen Atom = Nummer | Symbol  Nummer = [+-]?['0'-'9']+ Symbol = ['EIN'-'Z']['EIN'-'Z''0' '-'9'].* aufführen = '(', Ausdruck*, ')' 

Diese Grammatik gibt Folgendes an:

  • ein Ausdruck ist entweder an Atom oder ein aufführen;
  • ein Atom ist entweder a Nummer oder ein Symbol;
  • a Nummer ist eine ungebrochene Sequenz einer oder mehrerer Dezimalstellen, die optional ein Plus- oder Minuszeichen vorausgeht;
  • a Symbol ist ein Brief, gefolgt von null oder mehr von Zeichen (ohne Whitespace); und
  • a aufführen ist ein übereinstimmendes Paar Klammern mit null oder mehr Ausdrücke im Inneren.

Hier sind die Dezimalstellen, die oberen und unteren Case-Zeichen und Klammern terminale Symbole.

Die folgenden Beispiele für gut geformte Token-Sequenzen in dieser Grammatik: '12345','()','(A B C232 (1))''

Komplexe Grammatiken

Die Grammatik, die zum Angeben einer Programmiersprache erforderlich ist Chomsky -Hierarchie. Die Phrase-Grammatik der meisten Programmiersprachen kann unter Verwendung einer Typ-2-Grammatik angegeben werden, d. H. Sie sind es Kontextfreie Grammatiken,[6] Die Gesamtsyntax ist zwar kontextsensitiv (aufgrund variabler Erklärungen und verschachtelter Bereiche), daher Typ-1. Es gibt jedoch Ausnahmen, und für einige Sprachen ist die Phrase-Grammatik Typ-0 (Turing-Complete).

In einigen Sprachen wie Perl und lispen ermöglicht die Spezifikation (oder Implementierung) der Sprache Konstrukte, die während der Parsingphase ausgeführt werden. Darüber hinaus haben diese Sprachen Konstrukte, die es dem Programmierer ermöglichen, das Verhalten des Parsers zu verändern. Diese Kombination verwischt effektiv die Unterscheidung zwischen Analyse und Ausführung und macht eine Syntaxanalyse zu unentschlossenes Problem In diesen Sprachen bedeutet die Parsingphase möglicherweise nicht. In Perl beispielsweise ist es möglich, Code während des Parsens mit a auszuführen START Anweisung und Perl -Funktionsprototypen können die syntaktische Interpretation und möglicherweise sogar die syntaktische Validität des verbleibenden Codes verändern.[7] Umgangssprachlich wird dies als "nur Perl können Perl analysieren" bezeichnet (da der Code während der Parsen ausgeführt werden muss und die Grammatik ändern kann) oder stärker "sogar Perl kann Perl analysieren" (weil sie nicht negativ ist). Ebenso Lisp Makros eingeführt von der Defmacro Die Syntax wird auch während der Parsen ausgeführt, was bedeutet, dass ein Lisp-Compiler ein ganzes Lisp-Laufzeitsystem vorhanden sein muss. Im Gegensatz dazu sind C -Makros lediglich Zeichenfolge und erfordern keine Codeausführung.[8][9]

Syntax gegen Semantik

Die Syntax einer Sprache beschreibt die Form eines gültigen Programms, enthält jedoch keine Informationen über die Bedeutung des Programms oder die Ergebnisse der Ausführung dieses Programms. Die Bedeutung einer Kombination von Symbolen wird von Semantik behandelt (entweder formell oder hart codiert in a Referenzimplementierung). Nicht alle syntaktisch korrekten Programme sind semantisch korrekt. Viele syntaktisch korrekte Programme werden nach den Regeln der Sprache dennoch schlecht geformt. und kann (abhängig von der Sprachspezifikation und der Klang der Implementierung) zu einem Fehler bei der Übersetzung oder Ausführung führen. In einigen Fällen können solche Programme ausstellen undefiniertes Verhalten. Selbst wenn ein Programm in einer Sprache gut definiert ist, kann es immer noch eine Bedeutung haben, die von der Person, die es geschrieben hat, nicht beabsichtigt ist.

Verwendung Natürliche Sprache Beispielsweise ist es möglicherweise nicht möglich, einem grammatikalisch korrekten Satz eine Bedeutung zuzuweisen, oder der Satz kann falsch sein:

  • "Farblose grüne ideen schlafen wütend. "ist grammatikalisch gut gebildet, hat aber keine allgemein akzeptierte Bedeutung.
  • "John ist ein verheirateter Junggeselle." ist grammatikalisch gut gebildet, drückt aber eine Bedeutung aus, die nicht wahr sein kann.

Das folgende C -Sprachfragment ist syntaktisch korrekt, führt jedoch eine Operation durch, die nicht semantisch definiert ist (weil p ist ein Null Zeiger, die Operationen p->real und p->im keine Bedeutung haben):

 Komplex *p = NULL;  Komplex ABS_P = sqrt (p->real * p->real + p->ich bin * p->ich bin); 

Als einfacheres Beispiel,

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

ist syntaktisch gültig, aber nicht semantisch definiert, da es eine verwendet nicht initialisierte Variable. Auch wenn Compiler für einige Programmiersprachen (z. B. Java und C#) nicht initialisierte variable Fehler dieser Art erkennen würden, sollten sie als angesehen werden als semantisch Fehler anstelle von Syntaxfehlern.[5][10]

Siehe auch

Um die Syntax verschiedener Programmiersprachen schnell zu vergleichen, schauen Sie sich die Liste der Liste an "Hallo Welt!" Programm Beispiele:

Verweise

  1. ^ a b Friedman, Daniel P.; Mitchell Zauberstab; Christopher T. Haynes (1992). Grundlagen von Programmiersprachen (1. Aufl.). Die MIT -Presse. ISBN 0-262-06145-7.
  2. ^ Smith, Dennis (1999). Entwerfen von Wartungssoftware. Springer Science & Business Media.
  3. ^ Aho, Alfred V.; Monica S. Lam; Ravi Sethi; Jeffrey D. Ullman (2007). Compiler: Prinzipien, Techniken und Werkzeuge (2. Aufl.). Addison Wesley. ISBN 0-321-48681-1.Abschnitt 4.1.3: Syntaxfehlerbehandlung, S. 194–195.
  4. ^ Louden, Kenneth C. (1997). Compiler -Konstruktion: Prinzipien und Praxis. Brooks/Cole. ISBN 981-243-694-4. Übung 1.3, S. 27–28.
  5. ^ a b Semantische Fehler in Java
  6. ^ Michael Sipser (1997). Introduction to the Theory of Computation. PWS Publishing. ISBN 0-534-94728-x. Abschnitt 2.2: Pushdown -Automaten, S. 101–114.
  7. ^ Die folgenden Diskussionen geben Beispiele:
  8. ^ "Eine Einführung in gemeinsame Lisp -Makros". Apl.jhu.edu. 1996-02-08. Archiviert von das Original Am 2013-08-06. Abgerufen 2013-08-17.
  9. ^ "Das Common Lisp -Kochbuch - Makros und Backquote". Cl-cookbook.sourceforge.net. 2007-01-16. Abgerufen 2013-08-17.
  10. ^ Ausgabe von Syntax oder Semantik?

Externe Links