mikrocontroller.net

Forum: PC-Programmierung Konstruktion einer kontextfreien Grammatik


Autor: Andreas Wilhelm (avedo)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Ich arbeite mich zur Zeit in das Thema Compilerbau ein und habe mir zu 
diesem Zweck das Dragon Book (Compiler) von Alfred V. Aho, Monica S. 
Lam, Ravi Sethi und Jeffrey D. Ullman zugelegt. Dieses arbeite ich 
gerade durch und konstruiere zur Zeit eine komplexere kontextfreie 
Grammatik, die eine einfache Sprache erkennen soll. Das ist natürlich 
nur eine kleine Übung sein.

Ich baue dabei auf eine einfache Grammatik auf, die im Rahmen dieses 
Exkurses vorgestellt wird. Bisher habe ich die Grammatik um einen 
Modulo-Operator, die logischen Operatoren "|", "^" und "&", die 
zusammengesetzten Zuweisungsoperatoren ("*=", "%=" usw.), Post- und Pre- 
Increment- und Decrement-Operatoren, eine for-Schleife, ein 
switch-case-Konstrukt, sowie um Funktionsaufrufe  und die Befehle DUMP, 
REUTRN und EXIT erweitert. Leider bin ich mir an einigen Stellen, wie 
zum Beispiel bei der Konstruktion des switch-case-Konstrukts, nichts 
sicher ob dies so korrekt ist.

Überhaupt keine Ahnung habe ich, wie und wo ich in der Grammatik das 
Typecasting, den Trinären-Operator "(bool) ? (bool) : (bool)", einen 
sizeof()-Operator, die Deklaration eingener Funktionen, sowie eine 
Fehlerbehandlung mit REPORT oder THROW einbauen muss.

Da diese Grammatik auch die Deklaration von Feldern ermöglicht, wäre es 
natürlich als kleines Bonbon noch sehr interessant, wie man Operatoren 
auf Mengen, wie union und intersect noch mit reinnehmen könnte, oder ob 
man diese auch einfach unter Funktionsaufrufe steckt und deren Namen und 
Operatoranzahl in einer Tabelle nachschlagen lässt. So würde ich es mit 
allen nicht elementaren Funktionen der von der Grammatik akzeptierten 
Sprache machen. Das bedeutet sie kommen in der Grammatik einfach nicht 
vor.

Wäre dies ein übliches vorgehen? Wie sollte man sonst den später 
entstehenden Compiler erweitern, ohne überall im Quellcode herumpfuschen 
zu müssen.

Würde mich sehr freuen, wenn sich jemand finden würde, der sich mit 
diesem Thema auskennt und Lust hat mir etwas unter die Arme zu greifen. 
Habe schon in einem anderen Forum gefragt und wurde nett darauf 
hingewiesen, dass der dort anzutreffende Programmierer-Mainstream 
(tolles Wortkonstrukt) davon nur wenig bis keine Ahnung hat.

Vielen Dank im Voraus.

Liebe Grüße,

Andreas

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe mir am leichtesten getan, wenn ich Grammatiken gleich als LL(1) 
Grammatiken aufgebaut habe.

So was

  expr -> expr + term
    | expr - term
    | term

ist nämlich IMHO nicht zu implementieren und nur als theoretisches 
Konstrukt brauchbar. Du hast nichts in der Hand was deinen Parser leiten 
würde. Welche der 3 Alternativen ist denn die richtige?
Deine Grammatik ist super, wenn es darum geht unter einen Satz die 
entsprechenden terminalen Symbole zu schreiben und sich dann von dort 
aus bottom-up bis zum obersten Symbol durchzutasten. Aber die Umkehrung, 
einen Parser zu schreiben, der sich vom obersten Symbol zu den 
terminalen Symbolen durchtasten muss und Übereinstimmung mit den im Satz 
vorliegenden Symbolen festzustellen - hmmmmm

Eine implementierbare Grammatik für Expression sieht zb in EBNF so aus


  expr ->   term { ( "+" term ) | ( "-" term ) } .
  term ->   factor { ( "*" factor ) | ( "/" factor ) } .
  factor ->  ["-"] (   identifier
                     | number
                     | "sqrt" "(" expr ")"
                   ) .


Mit deiner Grammatik könnte ich nicht viel anfangen, die in ein Programm 
umzusetzen ... ich wüsste nicht recht wie ich das machen soll, ohne 
ständig in Endlosrekursionen zu laufen.

Muss allerdings auch dazu sagen, dass meine Compilerbauvorlesungen schon 
lange her sind.

: Wiederhergestellt durch Moderator
Autor: Andreas Wilhelm (avedo)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Abend!

Vielen Dank für deine Antwort.

Das Umformen meiner Grammatik zu einer LL(1) oder zumindest einer 
rechtsseitigen ableitenden Grammatik ist ja möglich. Dazu kann man die 
Tabelle der First- und Follow-Mengen der einzelnen Nichtterminale 
aufstellen. Diese können auch dabei helfen eine Sprache, wie sie von mir 
konstruiert wurde korrekt zu parsen. Endlosschleifen können außerdem 
recht einfach durch die richtige Verwendung des EPSILON Terminals, des 
leeren Wortes, vermieden werden.

In deinem Beispiel sehe ich leider noch kein Unterschied zu meiner 
Grammatik oder soll dass meine in EBNF sein?

Gibt es eigentlich eine Möglichkeit zu beweisen, dass eine Grammatik das 
tut, was sie tun soll, nämlich eine bestimmte Sprache erkennt. Kann mich 
nicht an einen konkreten Beweis erinnern.

Liebe Grüße,

Andreas

PS: Werde heute oder morgen kurz die Regeln zur Umformung erläutern und 
auf meine Grammatik anwenden.

EDIT: Entschuldigt der Beitrag meines Vorredners wurde gelöscht.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas Wilhelm schrieb:

> EDIT: Entschuldigt der Beitrag meines Vorredners wurde gelöscht.

Ich wollte ihn eigentlich zurückziehen, weil ich mir nicht sicher bin, 
ob ich dir überhaupt damit helfen kann.

Der Punkt ist der: Deine Grammatik ist eine schöne Übung, aber wie 
gesagt, zur Verwendung in einem Parser IMHO so nicht zu gebrauchen. D.h. 
ich denke du machst dir hier zuviel Arbeit.

Auf der anderen Seite hab ich schon lange nichts anderes als LL(1) 
Grammatiken mehr gebaut. D.h. ich bin mit allem anderen reichlich aus 
der Übung.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

>   expr -> expr + term
>     | expr - term
>     | term
>
> ist nämlich IMHO nicht zu implementieren und nur als theoretisches
> Konstrukt brauchbar.

Diese linksrekursive Ausdrucksweise ist aber bei den Parser-Generatoren 
wie Yacc allgemein verbreitet, um nicht zu sagen geradezu typisch. 
Solange es sich im den gleichen Präfix handelt (hier "expr") entsteht 
kein Problem, weil der Parser sich noch garnicht für eine bestimmte 
Alternative entscheiden muss.

http://www.cs.man.ac.uk/~pjj/cs212/ho/node5.html#S...

Beim recursive descent parser sieht das freilich etwas anders aus.

Autor: Simon Budig (nomis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas Wilhelm schrieb:
> Gibt es eigentlich eine Möglichkeit zu beweisen, dass eine Grammatik das
> tut, was sie tun soll, nämlich eine bestimmte Sprache erkennt. Kann mich
> nicht an einen konkreten Beweis erinnern.

Sowas gibt es durchaus, allerdings in der (Uni-)Praxis nur an mehr oder 
weniger kleinen Trivialbeispielen. Bei allen größeren Sachen (wie z.B. 
etwas was schon halbwegs wie eine Programmiersprache aussieht) hat man 
ruck-zuck das Problem, wie man denn nun definiert, was man überhaupt 
erkennen möchte. Wenn man das nicht über eine Grammatik machen kann (das 
will man ja letztlich beweisen) dann gehen einem da sehr schnell die 
Mittel aus.

Für Sprachen wie a^n b^n etc. besteht die Grammatik aus zwei bis drei 
Regeln, da hat man das schnell bewiesen, d.h. man entwickelt eine 
Konstruktionsvorschrift, die jedem Wort dieser Form eine Ableitungsfolge 
zuordnet und eben nachweist, dass diese Ableitungsfolge genau jenes Wort 
konstruiert. Umgekehrt muss man natürlich auch noch beweisen, dass jede 
(endliche) Ableitungsfolge die zur Grammatik passt in einem Wort der 
entsprechenden Sprache resultiert.

Aber beschreib mal alle gültigen C-Programme in einer Form, die sich 
nicht auf einer Grammatik abstützt. Das kannst Du nicht sinnvoll machen, 
d.h. man bekommt nichtmal ordentlich eine Behauptung hingeschrieben, die 
man dann beweisen kann.

Zu Deiner Grammatik: Die ist unnötig kompliziert, weil Du viele der 
Regeln zusammenfassen könntest, indem Du mehr Arbeit auf den (regulären) 
Tokenizer abwälzt. So kannst Du z.B. die Relations-Regeln (warum hast Du 
die überhaupt in "equality" und "rel" unterteilt?) zu einer 
zusammenfassen, die in der Mitte einen RELOP enthält. Es ist dann 
Aufgabe des lexing-Schritts, ">=", "<=", "==" etc. als RELOP zu 
erkennen. Später im Syntaxbaum kannst Du dann an einem Attribut des 
RELOP erkennen, welche Relation an dieser Stelle gemeint war.

Damit werden es nicht nur erheblich weniger Regeln, sondern es wird auch 
klarer ersichtlich, welche Präzedenz Du bei Deinen Operatoren hast.

Viele Grüße,
        Simon

Autor: Simon Budig (nomis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Diese linksrekursive Ausdrucksweise ist aber bei den Parser-Generatoren
> wie Yacc allgemein verbreitet, um nicht zu sagen geradezu typisch.
> Solange es sich im den gleichen Präfix handelt (hier "expr") entsteht
> kein Problem, weil der Parser sich noch garnicht für eine bestimmte
> Alternative entscheiden muss.
[...]
> Beim recursive descent parser sieht das freilich etwas anders aus.

Nunja, es ist aber eine Standard-Technik, linksrekursive Grammatiken 
entsprechend umzuformen, dass die Linksrekursion aufgelöst wird. Das ist 
genau das was lex und yacc machen.

Für einen recursive descent parser kann man das natürlich von Hand 
machhen. Ok, es lässt sich natürlich drüber streiten, ob das noch die 
"gleiche" Grammatik ist, das ist aber eher eine philosophische Frage: es 
ist sicher, dass die gleiche Sprache erkannt wird.

Viele Grüße,
        Simon

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas Wilhelm schrieb:

> Gibt es eigentlich eine Möglichkeit zu beweisen, dass eine Grammatik das
> tut, was sie tun soll, nämlich eine bestimmte Sprache erkennt. Kann mich
> nicht an einen konkreten Beweis erinnern.

Für einen solchen Beweis benötigst du eine formale Sprachdefinition, 
also eine Grammatik. Es läuft folglich darauf hinaus, die Äquivalenz 
zweier Grammatiken zu beweisen.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Simon Budig schrieb:

> Nunja, es ist aber eine Standard-Technik, linksrekursive Grammatiken
> entsprechend umzuformen, dass die Linksrekursion aufgelöst wird. Das ist
> genau das was lex und yacc machen.

Nur gibt es keinen Grund, sich eigens zu verknoten um eine rekursive 
Form zu vermeiden, wenn das der Parser-Generator nicht nur selber 
aufdröseln kann, sondern diese Formulierung geradezu erzwingt.

Bei Yacc/Bison wird die in EBNF mit { ... } dargestellte 
Wiederholungform eben gerade per links/rechtsrekursiver Formulierung 
dargestellt. Man also die KHB'sche Form erst wieder in die passende 
rekursive Darstellung überführen muss - und dabei noch drauf achten 
muss, dass die optisch naheliegende Rechtsrekursion hier die falsche 
wäre.

Insofern hängt die sinnvolle Spezifikation also davon ab, mit welchem 
Werkzeug man arbeitet. Bei Yacc/Bison rekursiv, bei recursive descent 
nicht (auch wenn's sprachlich naheläge ;-).

Autor: Andreas Wilhelm (avedo)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen!

Erst einmal vielen Dank für die zahlreichen Antworten.

Simon Budig schrieb:

> Für Sprachen wie a^n b^n etc. besteht die Grammatik aus zwei bis drei
> Regeln, da hat man das schnell bewiesen ...

Das kenne ich tatsächlich auch aus Vorlesungen, wie Info II, 
Theoretische Informatik oder Diskrete Mathematik. Wirklich helfen tut 
soetwas leider nicht wirklich, wenn man mal eine wirklich umfangreiche 
Grammatik vor sich hat. Da hast du wohl Recht.

Simon Budig schrieb:

> Zu Deiner Grammatik: Die ist unnötig kompliziert, weil Du viele der
> Regeln zusammenfassen könntest, ... So kannst Du z.B. die Relations-
> Regeln ... zu einer zusammenfassen, die in der Mitte einen RELOP
> enthält.

Das werde ich tun. Vielen Dank für diesen Tipp. Ich werde die neue 
Grammatik dann in nächster Zeit hier präsentieren.

Karl heinz Buchegger schrieb:

> Eine implementierbare Grammatik für Expression sieht zb in EBNF so aus

Die nächste Grammatik werde ich auch in EBNF, wie sie YACC und Co. 
verwenden, vorstellen.

Interessieren würde mich nun noch, wie ich in der alten Grammatik 
Funktionen und die Definition eigener Funktionen unterbringe. Erfasse 
ich in meiner Grammatik einfach eine Tokenfolge "ID LBRACKET params 
RBRACKET" und schaue dann im Schritt des Lexers, wie es auch Simon Budig 
für die Vergleichsoperatoren vorgeschlagen hat, den Namen und die damit 
verbundene Parameteranzahl nach?

Freue mich auf weitere Anregungen und Hilfestellungen.

Liebe Grüße,

Andreas

Autor: Simon Budig (nomis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas Wilhelm schrieb:
> Interessieren würde mich nun noch, wie ich in der alten Grammatik
> Funktionen und die Definition eigener Funktionen unterbringe. Erfasse
> ich in meiner Grammatik einfach eine Tokenfolge "ID LBRACKET params
> RBRACKET" und schaue dann im Schritt des Lexers, wie es auch Simon Budig
> für die Vergleichsoperatoren vorgeschlagen hat, den Namen und die damit
> verbundene Parameteranzahl nach?

Ja. Sobald der Benutzer selber Namen wählen kann geht das normalerweise 
über die reine Grammatik hinaus. Dann legst Du nur noch die 
syntaktischen Regeln für die Identifier fest (also z.B. welche 
Buchstaben man dafür verwenden darf).

In dem Syntaxbaum, den der Parser aufbaut, werden dann die Lexeme (also 
das, was durch den Lexer in ein Token für den Parser gewandelt wurde) 
mitgespeichert. Dadurch kann man dann beim Auswerten des Syntaxbaums bei 
einem Funktionsaufruf feststellen, wie die aufgerufene Funktion denn 
hieß und dann entsprechend in einer Symboltabelle prüfen, ob diese 
Funktion existiert und sie ggf. ausführen.

(An dieser Stelle verlässt die Korrektheit eines Programmtexts übrigens 
die Ebene der "Kontextfreiheit" - nur weil der Parser das Programm 
korrekt ableiten kann muss es nicht korrekt sein, z.B. weil eine 
Funktion aufgerufen wird, die nicht definiert wurde.)

Viele Grüße,
        Simon

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas Wilhelm schrieb:

> Interessieren würde mich nun noch, wie ich in der alten Grammatik
> Funktionen und die Definition eigener Funktionen unterbringe. Erfasse
> ich in meiner Grammatik einfach eine Tokenfolge "ID LBRACKET params
> RBRACKET" und schaue dann im Schritt des Lexers, wie es auch Simon Budig
> für die Vergleichsoperatoren vorgeschlagen hat, den Namen und die damit
> verbundene Parameteranzahl nach?

Ich habe Simon so verstanden, dass er den Lexer dazu bringen will, ein 
einziges Token für alle Vergleichsoperatoren gleicher Priorität 
abzuliefern, dessen Wert der exakte Operator ist, um die Grammatik zu 
vereinfachen. Das ist sinnvoll.

Bei einem Symbol kann es zwar durchaus sinnvoll sein, wenn der Lexer 
bereits einen Verweis auf die Symboltabelle liefert, sofern das nicht in 
Konflikt mit disjunkten Namespaces gerät, aber ein Parser wird 
üblicherweise die Parameter einer Funktion erst alle einsammeln und 
allenfalls abschliessend feststellen, ob die Anzahl aktueller Parameter 
mit der Deklaration zusammenpasst. Syntaktisch ist es also zulässig, 
mehr oder weniger Parameter anzugeben, als für die Funktion deklariert 
sind.

Autor: Andreas Wilhelm (avedo)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Danke für die Antworten.

A. K. schrieb:
> Ich habe Simon so verstanden, dass er den Lexer dazu bringen will, ein
> einziges Token für alle Vergleichsoperatoren gleicher Priorität
> abzuliefern, dessen Wert der exakte Operator ist, um die Grammatik zu
> vereinfachen. Das ist sinnvoll.

Das habe ich auch so verstanden, die Frage war ja nur, ob man ein 
ähnliches Verfahren für Standard-Funktionen der Sprache verwenden kann. 
Allerdings hast du natürlich Recht mit deinem Einwand bezüglich eines 
auftretenden Fehlers bei der Überprüfung der Parameteranzahl. Ein 
solcher Fehler tritt im Parser und nicht im Lexer auf.

Simon Budig schrieb:
> Sobald der Benutzer selber Namen wählen kann geht das normalerweise
> über die reine Grammatik hinaus.

Wie gesagt geht es mir momentan nicht um durch den Benutzer definierte 
Fuktionen, sondern um Standard-Funktionen, die Teil der Sprache sind. 
Wobei sich in diesem Zusammenhang natürlich auch die Frage stellt, ob 
diese nicht zusammengefasst werden könnten. Der Aufruf müsste dann ja 
die folgende Form haben:
call -> ID ( optparams )
  | factor
optparams -> params
  | EPSILON
params -> params , bool
  | bool

Welche Form muss aber eine Funktionsdeklaration erfüllen?
calldef -> ID ( optdefparams ) ASSIGN bool ;
  | factor
optdefparams -> defparams
  | EPSILON
defparams -> defparams , ID
  | ID

Wäre das so richtig und falls ja, wo sollte ich das einbauen?

Simon Budig schrieb:
> ... warum hast Du die überhaupt in "equality" und "rel" unterteilt?

Hier noch ein kurzer Nachtrag. Die Operatoren in equality und rel haben 
eine unterschiedliche Priorität - daher diese Unterteilung.

Bezüglich der Priorität hätte ich auch noch eine Frage. Ich habe mich an 
einer Tabelle orientiert, die die Priorisierung der Operatoren in C 
erklärt 
(http://de.wikibooks.org/wiki/C-Programmierung:_Lis...). 
Leider ist mir nicht ganz klar, wieso Präfix- und Postfix-Inkrement die 
gleiche Prioriät besitzen. Das ist meiner Meinung nach nicht wirklich 
logisch.

Liebe Grüße,

Andreas

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas Wilhelm schrieb:

> Wäre das so richtig

Ja.

> Leider ist mir nicht ganz klar, wieso Präfix- und Postfix-Inkrement die
> gleiche Prioriät besitzen. Das ist meiner Meinung nach nicht wirklich
> logisch.

Wo ist das Problem?

Autor: Andreas Wilhelm (avedo)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

A. K. schrieb:
> Wo ist das Problem?

Das Frage ich mich auch gerade. Hatte mich etwas an der Tatsache 
gestört, dass einmal vor allen anderen Aktionen der Wert erhöht wird und 
einmal danach.

A. K. schrieb:
>> Wäre das so richtig
>
> Ja.

Das ist doch schonmal gut,wobei mir gerade auffällt, dass es wie folgt 
richtiger wäre.
calldef -> ID ( optdefparams ) block ;
  | bool
optdefparams -> defparams
  | EPSILON
defparams -> defparams , ID
  | ID

calldef sollte zur Zuweisung kein Gleichheitszeichen verwenden, sondern 
es sollte einfach ein Block folgen. Außerdem sollte calldef wenn 
überhaupt auf bool und nicht auf factor zeigen. Die Frage ist nun, wo 
man es in der Grammatik einfügen müsste. Macht es nicht Sinn, es einfach 
gleich an den Anfang einzubauen? In diesem Fall sollte allerdings die 
Alternative bool wegfallen. Nachfolgend ein kleines Beispiel:
prgm -> calldefs
calldefs -> calldefs calldef
  | calldef
calldef -> ID ( optdefparams ) block
optdefparams -> defparams
  | EPSILON
defparams -> defparams , ID
  | ID

Auf diese Weise wäre alles funktionsbasiert und man könnte, ähnlich wie 
in C, einfach beim Parsen nach einer main-Funktion suchen, die dann 
ausgeführt wird.

Sollte ich das so in meine Grammatik, die ich noch einmal umgestellt, 
verkürzt und korrigiert in den Anhang gestellt habe, stellt sich mir 
noch die Frage, wie und wo ich Include-Befehle und Bedingungen (a ? b : 
c) einbauen kann. Außerdem bleibt noch die Frage, ob mein Konstrukt für 
eine Switch-Case-Anweisung korrekt ist.

Meiner Meinung nach müssten Includes in das obige Beispiel wie folgt 
eingepflegt werden.
prgm -> includes calldefs
includes -> includes include
  | EPSILON
include -> INCLUDE " STRING ";
calldefs -> calldefs calldef
  | EPSILON
calldef -> ID ( optdefparams ) block
optdefparams -> defparams
  | EPSILON
defparams -> defparams , ID
  | ID

Würde mich über weitere Rückmeldungen, Korrekturen und Anregungen 
Freuen.

Vielen Dank und liebe Grüße,

Andreas

\EDIT: Konnte Datei leider nicht anhängen, daher hier im Klartext.
G := (N, T, P, S)

S:= prgm

T := {
  "{",
  "}",
  CONST,
  EPSILON,
  ID,
  NUMBER,
  "[",
  "]",
  BASIC,
  "||",
  "&&",
  EQUOP,
  RELOP,
  ASSIGN,
  ";",
  IF,
  "(",
  ")",
  ELSE,
  WHILE,
  DO,
  SWITCH,
  BREAK,
  RETURN,
  STRING,
  DEFAULT,
  CASE,
  "|",
  "^",
  "&",
  "<<",
  ">>",
  "+",
  "-",
  "*",
  "/",
  "%",
  "~",
  "!",
  "^",
  ",",
  TRUE,
  FALSE
}

N := {
  prgm,
  block,
  decls,
  const,
  decl,
  type,
  stmts,
  stmt,
  loc,
  optexpr,
  cases,
  bool,
  join,
  bitor,
  bitxor,
  bitand,
  equality,
  rel,
  shift,
  expr,
  term,
  unary,
  call,
  optparams,
  params,
  factor
}

P := {
  prgm -> block
  block -> { decls stmts }
  decls -> decls const
    | EPSILON
  const -> CONST decl
    | decl
  decl -> type ID;
  type -> type [NUMBER]
    | BASIC
  stmts -> stmts stmt
    | EPSILON
  stmt -> loc ASSIGN bool ;
    | IF ( bool ) stmt
    | IF ( bool ) stmt ELSE stmt
    | WHILE ( bool ) stmt
    | DO stmt WHILE ( bool ) ;
    | FOR ( optexpr ; optexpr ; optexpr ) stmt
    | SWITCH ( bool ) cases
    | BREAK ;
    | RETURN bool ;
    | block
  loc -> loc [ bool ]
    | ID
  optexpr -> EPSILON
    | bool
  cases -> cases DEFAULT stmt
    | cases CASE bool : stmt
    | CASE stmt
  bool -> bool || join 
    | join
  join -> join && bitor
    | bitor
  bitor -> bitor | bitxor
    | bitxor
  bitxor -> bitxor ^ bitand
    | bitand
  bitand -> bitand & equality
    | equality
  equality -> equality EQUOP rel
    | rel
  rel -> bitor RELOP shift
    | shift
  shift -> shift << expr
    | shift >> expr
    | expr
  expr -> expr + term
    | expr - term
    | term
  term -> term * unary
    | term / unary
    | term % unary
    | unary
  unary -> + unary
    | - unary
    | ! unary
    | ~ unary
    | ++ unary
    | -- unary
    | unary ++
    | unary --
    | ( BASIC ) unary
    | SIZEOF BASIC unary
    | SIZEOF ID unary
    | call
  call -> ID ( optparams )
    | factor
  optparams -> params
    | EPSILON
  params -> params , bool
    | bool
  factor -> ( bool )
    | loc
    | NUMBER
    | STRING
    | TRUE
    | FALSE
}

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.