mikrocontroller.net

Forum: Compiler & IDEs [AVR] Grammatik für AVR C (avr-gcc)


Autor: Nils Höller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich sitze gerade an der Generierung eines Parsers für C Programme meines 
Microcontrollers (bzw. Sensorknoten). Ich verwende als Compiler 
normalerweise avr-gcc. Der verwendete Parser Generator ist JavaCC. 
Hierfür habe ich eine Grammatik für ANSI C und eigene Grammatiken für 
Spracherweiterungen, die ich in meine AVR C Programme mit einfließen 
lassen will. Ich arbeite also an einem eigenen Präcompiler, welcher den 
AVR C Code lässt wie er ist und meine Spracherweiterungen (XML , XPATH 
...) in AVR C Code transformiert.

Mein Parser, der rein auf ANSI C basiert, wirft immer wieder Exceptions 
für korrekten AVR C Code, da der Sprachumfang vom avr-gcc nicht 
identisch ist. (Ist ja klar :-) )

Bevor ich mich selber daran mache, die Grammatik zu erweitern, wollte 
ich daher fragen, ob jmd. eine fertige Grammatik für AVR C kennt.
In welcher Form sie vorliegt ist zunächst egal (gerne in JavaCC, Yacc, 
ANTLR etc.) Hauptsache es ist der gedachte korrekte Sprachumfang (nicht 
mehr und nicht weniger :-) ) Includes können evtl. nicht beachtet 
werden, obwohl ich diese zur statischen Typüberprüfung benötige.


Kann mir jmd. helfen?

Vielen Dank

Nils

Autor: Andreas Schwarz (andreas) (Admin) Benutzerseite Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Syntaktisch sollte sich AVR-GCC-Code nicht von Standard-C unterscheiden. 
Wo genau gibt es Probleme?

Aber musst du überhaupt den kompletten Code parsen? Sehr viel einfacher 
wäre es, mit regulären Ausdrücken gezielte Ersetzungen vorzunehmen. Wie 
sehen deine Spracherweiterungen denn aus?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der originale Parser ist letztlich in den Quellen vom GCC enthalten
und in Bison (GNU's Yacc-Pendant) geschrieben.  Aber der ist alles in
allem nicht wirklich klein.

Autor: Nils Höller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich verwende zum Beispiel XML Konstrukte, die wiederum herkömmliche C 
Variablen in sich aufnehmen können. Da ich eine statische Typüberprüfung 
durchführe, benötige ich also die verwendeten Variablen aus dem C Code 
(zumindest deren Typ). Die Typüberprüfung wiederum basiert auf einem 
gegebenen XML Schema (oder DTD). Sinn der ganzen Aufgabe ist es XML 
direkt bei der Programmierung nutzen zu können, wobei statisch validiert 
wird. Das hat besonderen Nutzen bei Programmen mit XML Austausch (SOAP 
Nachrichten etc.) Desweiteren sollen Xpath Ausdrücke direkt im C Code 
verwendet werden können.


Was den Sprachumfang angeht, hier mal ein Beispiel, was von meinem 
Parser nicht erkannt wird, aber in avr-gcc Programmen klappt:
xbuffer = (u_char *) MEM_TEST_BUF_ADDR;

Hier wirft der Parser eine Exception, da nach dem Stern kein 
Variablenname erscheint. (auch wenn man MEM_TEST.... auflöst.)

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Willst du die GCC-Erweeiterungen gegenüber ANSI-C (mittlerweile C90
genannt) wirklich nutzen?

Wenn nicht, kannst du die Erweiterungen mit den Compiler-Optionen

  -ansi -pedantic

deaktivieren. Alles, was nicht dem Standard entspricht, wird dann als
Fehler angesehen.

Wenn du die GCC-Erweiterungen benutzen möchtest, kommst du nicht
umhin, den original GCC-Parser bzw. dessen Grammatik zu verwenden. Der
Parser der aktuellen Version (ich glaube, seit 4.0) ist handgemacht
(LL(1) per rekursivem Abstieg), da bin ich nicht sicher, ob es eine
parsergeneratorlesbare Form der Grammatik gibt. Prinzipiell kannst du
den Parser aber auch so nehmen, wie er ist. Schau dir mal
gcc/c-parser.c an). Das hätte den Vorteil, dass du bei neuen
GCC-Versionen mit deiner Software immer schnell wieder up-to-date
bist.

Die älteren Versionen verwenden einen tabellengesteuerten
LR(1)-Parser, der von bison (yacc-Ersatz) generiert wird. Die Datei
heißt dort c-parse.y.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
xbuffer = (u_char *) MEM_TEST_BUF_ADDR;

Das ist standardkonformes C. Vielleicht hat dein Parser einen Bug?

Autor: Nils Höller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank,

das sind schonmal viele gute Infos, die ich verdauen muss :-)

Ich habe hier noch eine ANTLR GCC Grammatik.

Die Sprachen GCC und AVR-GCC sind also identisch?

Dann würde das ja reichen.

Zu obigen Code:

standardkonformes C  auch vor 99?

Also vielen Dank noch mal

Nils

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> standardkonformes C  auch vor 99?

Selbst weit vor C90!

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Die Sprachen GCC und AVR-GCC sind also identisch?

GCC ist ein (oder genau genommen mehrere) Compiler, der Fontends für
verschiedene Sprachen (C, C++, Fortran usw.) und Backends für
verschiedene Prozessoren (x86, AVR usw.) hat. Der AVR-GCC ist ein GCC,
der so konfiguriert ist, dass er C in AVR-Code übersetzt. Die C-Syntax
ist aber vom gewählten Backend unabhängig.

> standardkonformes C  auch vor 99?

Standardkonform, seit es C gibt. Das ist einfach eine Zuweisung eines
Werts (vielleicht ein int), der vorher in einen u_char-Pointer
umgewandelt wird. Damit ein Compiler nicht meckert, müssen natürlich
xbuffer, u_char und MEM_TEST_BUF_ADDR vorher deklariert worden sein.

Autor: Nils Höller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke nochmal,

ich habe jetzt den Fehler gefunden.

Hierzu den ganzen Anfang

u_char mem_test_checkMem(void) {
    volatile u_char *xbuffer;
    xbuffer = (u_char *) MEM_TEST_BUF_ADDR;
    u_short i=0; 
    u_char j=0; u_char data;
    u_char errors=0;
    u_char return_val=0;
    data = 0;
    for (i = 0; i < MEM_TEST_BUF_SIZE; i++) {
        xbuffer[i] = data;
        if (xbuffer[i] != data) {
          _print_error(i, xbuffer[i], data);
          return_val = 1;
          if (errors++ > MAX_NR_ERRORS-2) break;
        }
    }
    data = 1;
    for (j=0; j<8; j++) {
      errors = 0;
      for (i = 0; i < MEM_TEST_BUF_SIZE; i++) {
        xbuffer[i] = data;
        if (xbuffer[i] != data) {
           _print_error(i, xbuffer[i], data);
           return_val = 1;
           if (errors++ > MAX_NR_ERRORS-2) break;
        }
      }
      data = data*2;
    }
    return return_val;
} 

Das Problem liegt darin, dass in meiner Grammatik zuerst alle 
Variablendeklarationen kommen müssen ehe der weitere Code kommt.
Ich bin mir nicht sicher, ob ein anderer Weg erst seit 99 offiziell 
Standard ist?! Wenn ich nur die ersten beiden Zeilen der Methode im 
Block habe, funktioniert das parsen einwandfrei.

Daher werde ich mich an die Grammatik setzen müssen. Dann dürfte das 
aber kein Problem mehr sein.

Vielen Dank

Nils

Autor: Nils Höller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit diesem Methodenbeginn funktioniert das Parsen einwandfrei:
u_char mem_test_checkMem(void) {
    volatile u_char *xbuffer = (u_char *) MEM_TEST_BUF_ADDR;
    u_short i=0; 
    u_char j=0; u_char data;
    u_char errors=0;
    u_char return_val=0;
.....

Es liegt also daran, dass solange ich mich im Zustand 
"Variablendeklaration und sofortige Initialisierung" befinde, alles 
funktioniert. Wenn ich zum ersten Mal eine Zuweisung hab, gelange ich in 
den "Anweisungsblock". Ab jetzt sind keine Deklarationen mehr erlaubt.

Also gehe ich davon aus, dass die Methoden zunächst alle Deklarationen 
und dann die Anweisungen haben. Ich glaube das war mal Standard und ist 
in meinem C Buch auch so angegeben. (Wie auch bei Pascal)

ANSI C99 erlaubt wohl auch eine Durchmischung oder?

Danke Nils

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Korrekt. Vor C99/C++ kommen in jedem Block erst die Vars, dann der Code.

Autor: Nils Höller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein weiteres Problem tritt bei solchen Stellen auf:
const char bootloader_version[16] __attribute__ ((section (".bootloader-version"))) = PROGRAM_VERSION;

_attribute_ und der folgende Code wird von meinem Parser nicht 
erkannt,
stammt wohl aus GNU-C ??

Wo kann ich die Syntax von diesen "special types" finden?

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#define __attribute__(x)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wo kann ich die Syntax von diesen "special types" finden?

Durch Lesen des GCC-Manuals.

> #define __attribute__(x)

Besser:
#if !defined(__GNUC__)
#  define __attribute__(x)
#endif

Autor: Nils Höller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

so ich habe nun meine Arbeiten an einer GNU-C Grammatik für JavaCC fast 
abgeschlossen. Funktioniert alles sehr gut (ausser Includes automatisch 
verarbeiten)


Probleme bereiten mir weiterhin einige kleine Befehler.

Der Ausdruck
 sprintf(prompt, "[bt-cmd@"SADDR_FMT"]$ ", SADDR(addr)); 
lässt sich einfach nicht parsen.

avr-gcc erkennt ihn wunderbar.

Ist der Befehl nun nur in avr-gcc enthalten oder auch im reinen GnuC?

ändere ich den Befehl in
sprintf(prompt, "[bt-cmd@"+SADDR_FMT+"]$ ", SADDR(addr));
 klappt das parsen einfandfrei.

Die "" gehören aber nunmal eigentlich zu SADDR_FMT und das erstellen 
eines Tokens für bt-cmd@ hat noch nicht geklappt.

Vielleicht kann mir jmd. näheres zu diesem ungewöhnlichen Befehl sagen.


Danke

Nils

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nils Höller wrote:

> Der Ausdruck
>
 sprintf(prompt, "[bt-cmd@"SADDR_FMT"]$ ", SADDR(addr)); 
> lässt sich einfach nicht parsen.

Könnte das daran liegen, dass du keine Include-Dateien auswertest und
daher nicht weißt, dass SADDR_FMT zu einem string literal expandiert?
Nach dem Preprozessing kommt da also sowas raus wie:
 sprintf(prompt, "[bt-cmd@""%u""]$ ", ...

> Ist der Befehl nun nur in avr-gcc enthalten oder auch im reinen
> GnuC?

Es ist ISO-C90.  Stichwort: string concatenation.

Der AVR-GCC hat eigentlich keinerlei Syntaxerweiterungen gegenüber dem
GCC ,,aus der Dose''.  OK, eine hat(te) er: binäre Konstanten (mit 0b
eingeleitet), aber die sind nun mittlerweile auch im ,,richtigen'' GCC
drin.

> ändere ich den Befehl in

>
> sprintf(prompt, "[bt-cmd@"+SADDR_FMT+"]$ ", SADDR(addr));
> 
 klappt das parsen einfandfrei.

Nur dass das Ganze jetzt kein gültiges C mehr ist. ;-)

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Der Ausdruck
>  sprintf(prompt, "[bt-cmd@"SADDR_FMT"]$ ", SADDR(addr));
> lässt sich einfach nicht parsen.

Das liegt vielleicht daran, daß der C-Präprozessor die Zeichenketten 
verbindet. Der C-Compiler bekommt so etwas nie zu sehen.

Klaus

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Falser wrote:

> Das liegt vielleicht daran, daß der C-Präprozessor die Zeichenketten
> verbindet. Der C-Compiler bekommt so etwas nie zu sehen.

Falsch.

Autor: Nils Höller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und was ist richtig?


also es tritt auch ohne die @ Zeichen auf (Gehe von BTNut Applikationen 
aus).

Für mich sieht das nach fehlender "Verbindung" aus.


Was ist es nun wirklich?

Autor: Nils Höller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ups...hatte den Beitrag oben übersehen:


Ja das mit dem Include ist natürlich richtig ....

wie bereits erwähnt, bereiten die mir ja Probleme.

Werde mir noch eine Lösung einfallen lassen müssen.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Und was ist richtig?

Dass der Präprozessor zwar den Makro ersetzt, aber die verbleibende
string concatenation (also das Ablegen zweier aufeinander folgender
string literals als ein ganzes) macht der Compiler.

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In meinen K&R Ansi C Handbuch (2. Auflage) steht bei Preprozessor, Seite 
226, Phase 4 :

"Ersatzdarstellungen in Zeichenkonstanten und konstanten Zeichenketten 
(...) werden durch äquivalente Zeichen ersetzt; anschließend werden 
benachbarte konstante Zeichenketten aneinandergehängt."

Ich hätte das schon so interpretiert daß der Präprozessor die 
Zeichenketten zu einer zusammenfasst.

Klaus

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dass der Präprozessor das macht ist mir auch neu. Traditionell macht das 
der Compiler.

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich nehm's auch zurück.
Bei einen kurzen Test mit cpp habe ich gesehen, dass es zumindest der 
GNU cpp nicht macht.

Klaus

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.