Forum: Compiler & IDEs LEX, YACC Problem..


von Holm T. (Gast)


Lesenswert?

Hi.

Ich möchte einen simplen Kommandozeilenparser mit lex und yacc basteln 
aber trotz Lesens diverser (online) Literatur, Manuals und auch 
Beispielcode
habe ich wohl einen Knoten im Kopf der mich hindert vorwärts zu kommen.
Ich habe mir 2 Bücher bestellt die hoffentlich heute kommen...

Ich möchte simple Kommandozeilen wie z.B. diese hier parsen und 
Variablen setzen und lesen:

set meas dac 0 offset 33<lf>

oder halt

get meas dac 0 offset<lf>

Dazu habe ich folgendes lex file gebastelt:
1
%{
2
#include <stdio.h>
3
#include "y.tab.h"
4
5
#ifndef YYERRCODE
6
#define YYERRCODE 256
7
#endif
8
9
extern int yylval;
10
extern int tmpint;
11
extern char tmpchr;
12
%}
13
%%
14
acloops                 return TOKACLOO;
15
autocal                 return TOKAUTOC;
16
adc                     return TOKADC;
17
calib                   return TOKCALIB;
18
cycle                   return TOKCYC;
19
ctrl                    return TOKCTRL;
20
dispb10                 return TOKDBEL10;
21
dismod                  return TOKDISPMD;
22
dac                     return TOKDAC;
23
eeprom                  return TOKEEPROM;
24
offset                  return TOKOFFSET;
25
order                   return TOKORDER;
26
solenoid                return TOKSOL;
27
staples                 return TOKSTAPLES;
28
staple                  return TOKSTAP;
29
smooth                  return TOKSMOOTH;
30
saving                  return TOKSAV;
31
slope                   return TOKSLOPE;
32
set                     return TOKSET;
33
get                     return TOKGET;
34
rounding                return TOKROUND;
35
rtc                     return TOKRTC;
36
median                  return TOKMEDIAN;
37
mwcorr                  return TOKMWCOR;
38
meas                    return TOKMEAS;
39
tcoff                   return TOKTCOFF;
40
tcalib                  return TOKTCALIB;
41
warmup                  return TOKWARM;
42
[abc][01]               yylval=(int)((yytext[0]-'a')<<1)|yytext[1]-'0'; return N
43
UMBER;
44
on|off                  yylval=(!strcmp(yytext,"on")); return STATE;
45
[0-9]+                  yylval=atoi(yytext); return NUMBER;
46
\r                      /* ignore carriage return */;
47
[ \t]+                  /* ignore whitespace */;
48
\n                      return TOKNL;
49
.                       printf("unrecognized char"); return YYERRCODE;
50
%%

und den folgenden Spielzeugparser:
1
%{
2
#include <stdio.h>
3
#include <string.h>
4
int     yylex();
5
int     yyparse();
6
;
7
#define DEBUG
8
#ifdef DEBUG
9
#       define DPRINTF(...) fprintf(stdout, __VA_ARGS__)
10
#else
11
#       define DPRINTF(...) /* nothing */
12
#endif
13
14
int     tmpval;
15
int     dac_0_offset;
16
int     dac_1_offset;
17
int     dac_0_slope;
18
int     dac_1_slope;
19
int     dac_order;
20
int     adc_order;
21
int     solenoid;
22
int     smoothing;
23
int     rounding;
24
int     staple;
25
int     staples;
26
int     autocal;
27
int     warmup=20;
28
int     saving;
29
int     acloops;
30
int     cycle=6;
31
int     mwcorr;
32
int     tcoff;
33
int     tcalib;
34
35
struct  meas {
36
        int     offset;
37
        int     slope;
38
        int     median;
39
};
40
41
struct meas mfield[6];
42
43
 
44
void yyerror(const char *str)
45
{
46
        fprintf(stderr,"error: %s\n",str);
47
}
48
 
49
int yywrap()
50
{
51
        return 1;
52
} 
53
54
void promptl()
55
{
56
        printf("L>");
57
}
58
void promptok()
59
{
60
        printf("ok>");
61
}
62
  
63
int main()
64
{
65
        while(1)
66
                yyparse();
67
} 
68
69
%}
70
71
%token NUMBER STATE TOKSET TOKGET TOKCTRL TOKMEAS TOKCALIB TOKDAC TOKADC
72
%token TOKSOL TOKAUTOC TOKWARM TOKDBEL10 TOKDISPMD TOKRTC TOKEEPROM
73
%token TOKOFFSET TOKSLOPE TOKORDER TOKSMOOTH TOKROUND TOKSTAP TOKSTAPLES
74
%token TOKSAV TOKACLOO TOKCYC TOKMWCOR TOKTCOFF TOKTCALIB TOKNL TOKMEDIAN
75
76
77
%%
78
commands: /* empty */ { promptl(); } |
79
        command { promptok(); }
80
        ;
81
82
command: 
83
        value_set
84
        |
85
        value_get
86
        |
87
        error '\n'      { YYABORT; }
88
        ;
89
90
value_set:      // set meas dac 0 offset 0
91
        TOKSET TOKMEAS TOKDAC NUMBER TOKOFFSET NUMBER TOKNL
92
        {
93
                DPRINTF("\tDAC %d offset set to %d\n",$4,$6);
94
                if($4==0)
95
                        dac_0_offset=$6;
96
                else
97
                        dac_1_offset=$6;
98
        }
99
        ;
100
        |       // set meas dac 0 slope 0
101
        TOKSET TOKMEAS TOKDAC NUMBER TOKSLOPE NUMBER TOKNL
102
        {
103
                DPRINTF("\tDAC %d slope set to %d\n",$4,$6);
104
                if($4==0)
105
                        dac_0_slope=$6;
106
                else
107
                        dac_1_slope=$6;
108
        }
109
110
111
value_get:      // get meas dac 0 offset
112
        TOKGET TOKMEAS TOKDAC NUMBER TOKOFFSET TOKNL
113
        {
114
                if($4==0)
115
                        printf("\tDAC 0 OFFSET actual= %d\n",dac_0_offset);
116
                else
117
                        printf("\tDAC 1 OFFSET actual= %d\n",dac_1_offset);
118
                        
119
        }
120
        ;
121
        |       // get meas dac 0 slope
122
        TOKGET TOKMEAS TOKDAC NUMBER TOKSLOPE TOKNL
123
        {
124
                if($4==0)
125
                        printf("\tDAC 0 SLOPE actual=%d\n",dac_0_slope);
126
                else
127
                        printf("\tDAC 1 SLOPE actual=%d\n",dac_1_slope);
128
        }

Das ist ne gekürzte Variante, länger wäre noch langweiliger...

Mein Problem dabei ist nicht das das nicht funktioniert, sondern das 
Ganze gegen Unfug abzudichten, Ich möchte bei Syntaxfehlern Meldungen, 
und nach Fehlern gerne das die fehlerhafte Zeile verworfen wird und 
wieder ein Prompt erscheint. Nach einem einzelnen Newline hätte ich 
gerne einen neuen Prompt..
Ich habe jetzt schon seit ein paar Tagen die lustigsten Spielchen durch, 
aber ich kriege das nicht hin..was mache ich falsch?

Gruß,

Holm

von udok (Gast)


Lesenswert?

Du verwendest lex und yacc, das machst du falsch :-)

Die Buffern ihren I/O und sind nicht für
Kommandozeilenverwendung gedacht.
Das lässt sich irgendwie abdrehen, eventuell mit setvbuf,
ich glaube aber, dass die Algorithmen dahinter ziemlich
viele Token vorrausschauen, wird ja oft als Vorteil gesehen.

von Holm T. (Gast)


Lesenswert?

udok schrieb:
> Du verwendest lex und yacc, das machst du falsch :-)


Hmm...

>
> Die Buffern ihren I/O und sind nicht für
> Kommandozeilenverwendung gedacht.
> Das lässt sich irgendwie abdrehen, eventuell mit setvbuf,
> ich glaube aber, dass die Algorithmen dahinter ziemlich
> viele Token vorrausschauen, wird ja oft als Vorteil gesehen.

Ja, es gibt diese Mechanismen, yacc löffelt 3 Tokens bis er aus dem 
Fehlerstatus wieder heraus kommt und man kann ihn resynchronisieren..was 
mir allerdings mit '\n' als Synchronisationscharakter nicht recht 
gelingen will.
Das Puffern der Eingabe macht mir wenig Sorgen, ich übertrage die 
Kommandos
sowieso Zeilenweise und durch '\n' abgeschlossen. Mir reicht es, wenn 
nach einer fehlerfreien Zeile ein OK kommt und nach einer 
Fehlerbehafteten ein ERROR, aber bitte nicht 3.7 Stück davon. Außerdem 
hätte ich gerne nach einer leeren Eingabezeile wieder ein OK (der 
Versuch das zu basteln ist mit promptl() im Parser zu sehen, L für 
..leer....

Warum soll die Verwendung von lex und yacc falsch sein? Die wurden mal 
für solche Zwecke gemacht, ich unterfordere sie eher...

Gruß,
Holm

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


Lesenswert?

udok schrieb:
> Die Buffern ihren I/O und sind nicht für Kommandozeilenverwendung
> gedacht.
> Das lässt sich irgendwie abdrehen

Meiner Erinnerung nach hat flex dafür irgendeine Option.

Yep, -I.  Die Doku sagt dazu:
1
'flex' scanners default to 'interactive' unless you use the '-Cf'
2
or '-CF' table-compression options (*note Performance::).  That's
3
because if you're looking for high-performance you should be using
4
one of these options, so if you didn't, 'flex' assumes you'd rather
5
trade off a bit of run-time performance for intuitive interactive
6
behavior.

Sollte also kein grundlegendes Problem sein.  Habe auch schon mal
(spaßeshalber) einen kleinen BASIC-Interpreter für AVRs mit flex und
byacc geschrieben; das funktionierte durchaus, auch interaktiv.

von Holm T. (Gast)


Lesenswert?

...hab das -I ausprobiert...kein Unterschied. So richtig verwunderlich 
isses aber nicht:

" Flex scanners default to interactive unless you use the -Cf or
  -CF table-compression options (see below). "

Hmm...

..sehe gerade Jörg hat das auch geschrieben. Es sollte also prinzipiell 
funktionieren.

..muß erst mal was Andres machen.

Gruß,

Holm

von Karl der erste von oben (Gast)


Lesenswert?

Ich hab nicht alles gelesen oder verstanden. Daher bitte um Nachsicht, 
aber man kann den Buffer von flex zur Laufzeit flushen. Yy_flush_buffer. 
Dann muss er neu einlesen.
Ich benutze das weil ich verschiedene Sachen aus einer Datei parsen mir 
und dazu Lexer und Parser tausche. Beim Wechsel muss die Datei an der 
richtigen Stelle stehen und der ziel-lexer muss geflusht sein damit es 
geht.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Holm T. schrieb:
> Mein Problem dabei ist nicht das das nicht funktioniert, sondern das
> Ganze gegen Unfug abzudichten, Ich möchte bei Syntaxfehlern Meldungen,
> und nach Fehlern gerne ...

Das dürfte mit Bison / YACC nicht so einfach sein.  Zum Beispiel werden 
entsprechende Parser im GCC seit langem nicht mehr durch Generatoren 
erzeugt sondern sind handgeklöppelt.  Ein Grund dafür ist, dass es fast 
unmöglich war, sinnvolle Diagnostics zu bekommen bzw. sinnvolle 
Diagnostics und weiteres Verhalten nur in unbefriedigendem Umfang ode 
nicht mit vertretbarem Aufwand oder Wartbarkeitsverlust zu realisieren 
war.

Zwar verwendest zu eine deutlich einfachere Sprache als die von GCC zu 
parsenden, aber dennoch erhälst du besser zu kontrollierendes Verhalten, 
insbesondere bei Syntaxproblemen, wohl am einfachstem mit eigenem 
Parser.

Die Hacks und Tricks, die in einem Parser-Generator nötig wären, werden 
einen Großteil des Comforts, den der Einsatz eines solchen Generators 
bieten soll, zunichte gemacht.

von Holm T. (Gast)


Lesenswert?

..naja..ganz so schlimm isses ja wohl auch wieder nicht und ein Projekt 
in der Größe eines GCC habe ich nicht vor.
Jörg (Tm) hat den Parser von avrdude auch mit lex und yacc 
gebastelt..scheint doch zu funktionieren :-)

Ich habe Jörg über meinen Krempel mal drüber gucken lassen und das 
funktioniert jetzt schon ganz brauchbar. Mal sehen zu welcher Größe das 
sich auswächst, im Endeffekt soll das mal auf einen Xmega.

Gruß,
Holm

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


Lesenswert?

Für den Xmega solltest du aber meine AVR-Patches für flex und byacc
benutzen, damit er die Tabellen nicht in den RAM sondern in den Flash
wirft.

https://www.avrfreaks.net/comment/366056#comment-366056

von Possetitjel (Gast)


Lesenswert?

Johann L. schrieb:

> Die Hacks und Tricks, die in einem Parser-Generator
> nötig wären, werden einen Großteil des Comforts, den
> der Einsatz eines solchen Generators bieten soll,
> zunichte gemacht.

Könntest Du dafür ein, zwei Beispiele geben, die auch
für einen Nicht-Compilerbauer verständlich sind?

von Holm T. (Gast)


Lesenswert?

Jörg W. schrieb:
> Für den Xmega solltest du aber meine AVR-Patches für flex und byacc
> benutzen, damit er die Tabellen nicht in den RAM sondern in den Flash
> wirft.
>
> https://www.avrfreaks.net/comment/366056#comment-366056

Ok, das sehe ich mir an.. hatte sowieso vor den Output in dieser 
Richtung zu überarbeiten. Wenn das schon "aus der Dose" gibt, ist das ne 
feine Sache!

Gruß,

Holm

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.