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


von Nils Höller (Gast)


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

von Andreas S. (andreas) (Admin) Benutzerseite


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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Nils Höller (Gast)


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:
1
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.)

von yalu (Gast)


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.

von yalu (Gast)


Lesenswert?

xbuffer = (u_char *) MEM_TEST_BUF_ADDR;

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

von Nils Höller (Gast)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> standardkonformes C  auch vor 99?

Selbst weit vor C90!

von yalu (Gast)


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.

von Nils Höller (Gast)


Lesenswert?

Danke nochmal,

ich habe jetzt den Fehler gefunden.

Hierzu den ganzen Anfang
1
u_char mem_test_checkMem(void) {
2
    volatile u_char *xbuffer;
3
    xbuffer = (u_char *) MEM_TEST_BUF_ADDR;
4
    u_short i=0; 
5
    u_char j=0; u_char data;
6
    u_char errors=0;
7
    u_char return_val=0;
8
    data = 0;
9
    for (i = 0; i < MEM_TEST_BUF_SIZE; i++) {
10
        xbuffer[i] = data;
11
        if (xbuffer[i] != data) {
12
          _print_error(i, xbuffer[i], data);
13
          return_val = 1;
14
          if (errors++ > MAX_NR_ERRORS-2) break;
15
        }
16
    }
17
    data = 1;
18
    for (j=0; j<8; j++) {
19
      errors = 0;
20
      for (i = 0; i < MEM_TEST_BUF_SIZE; i++) {
21
        xbuffer[i] = data;
22
        if (xbuffer[i] != data) {
23
           _print_error(i, xbuffer[i], data);
24
           return_val = 1;
25
           if (errors++ > MAX_NR_ERRORS-2) break;
26
        }
27
      }
28
      data = data*2;
29
    }
30
    return return_val;
31
}

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

von Nils Höller (Gast)


Lesenswert?

Mit diesem Methodenbeginn funktioniert das Parsen einwandfrei:
1
u_char mem_test_checkMem(void) {
2
    volatile u_char *xbuffer = (u_char *) MEM_TEST_BUF_ADDR;
3
    u_short i=0; 
4
    u_char j=0; u_char data;
5
    u_char errors=0;
6
    u_char return_val=0;
7
.....

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

von Andreas K. (a-k)


Lesenswert?

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

von Nils Höller (Gast)


Lesenswert?

Ein weiteres Problem tritt bei solchen Stellen auf:
1
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?

von Andreas K. (a-k)


Lesenswert?

#define __attribute__(x)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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

Durch Lesen des GCC-Manuals.

> #define __attribute__(x)

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

von Nils Höller (Gast)


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
1
 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
1
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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Nils Höller wrote:

> Der Ausdruck
>
1
 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:
1
 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

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

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

von Klaus F. (kfalser)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Nils Höller (Gast)


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?

von Nils Höller (Gast)


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Klaus F. (kfalser)


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

von Andreas K. (a-k)


Lesenswert?

Dass der Präprozessor das macht ist mir auch neu. Traditionell macht das 
der Compiler.

von Klaus F. (kfalser)


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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.