I am new to GNU autotools and in my project lex and yacc parsers are used.Including them as source in makefile.am produes following error :
configure.in :
...
AC_CHECK_PROGS(YACC,bison yacc,none)
if test "x$YACC" = "xbison"; then
YACC="$YACC -y"
fi
AC_CHECK_PROGS(LEX,flex,none)
...
makefile.am :
## $Id
AUTOMAKE_OPTIONS=foreign no-dependencies
include $(srcdir)/Makefile_defs
dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor
dynamicpreprocessor_LTLIBRARIES = libsf_appid_preproc.la
libsf_appid_preproc_la_LDFLAGS = -export-dynamic -module #XCCFLAGS#
if SO_WITH_STATIC_LIB
libsf_appid_preproc_la_LIBADD = ../libsf_dynamic_preproc.la
../libsf_dynamic_utils.la $(LUA_LIBS)
else
nodist_libsf_appid_preproc_la_SOURCES = \
../include/sf_dynamic_preproc_lib.c \
../include/sf_ip.c \
../include/sfPolicyUserData.c \
../include/sfxhash.c \
../include/sfghash.c \
../include/sflsq.c \
../include/sfhashfcn.c \
../include/sfmemcap.c \
../include/sfprimetable.c
libsf_appid_preproc_la_LIBADD = $(LUA_LIBS)
endif
libsf_appid_preproc_la_CFLAGS = -DDYNAMIC_PREPROC_CONTEXT -DSTATIC=static $(LUA_CFLAGS)
libsf_appid_preproc_la_SOURCES = $(APPID_SOURCES)
all-local: $(LTLIBRARIES)
$(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES
In Makefile_defs :
APPID_SRC_DIR = ${top_srcdir}/src/dynamic-preprocessors/appid
...
APPID_SOURCES = \
$(APPID_SRC_DIR)/vfml/fc45.lex \
$(APPID_SRC_DIR)/vfml/fc45.y \
...
when i run the program i get following error :
libsf_appid_preproc.so: undefined symbol: FC45SetFile
While FC45SetFile() is already defined in fc45.lex file.
fc45.lex :
%{
#include "fc45.tab.h"
//#include "vfml.h"
#include <string.h>
#include <stdlib.h>
/* HERE doesn't match strings starting with numbers other than 0 right */
char string_buf[4000]; /* BUG - maybe check for strings that are too long? */
char *string_buf_ptr;
void FC45FinishString(void);
extern int gLineNumber;
%}
%x str_rule
%%
<str_rule,INITIAL>\|[^\n]* ;
[\ \t\r]+ ;
\n gLineNumber++;
\. { return '.';}
, { return ',';}
: { return ':';}
ignore { return tIgnore; }
continuous { return tContinuous; }
discrete { return tDiscrete; }
[^:?,\t\n\r\|\.\\\ ] string_buf_ptr = string_buf; unput(yytext[0]); BEGIN(str_rule);
<str_rule>[:,?] FC45FinishString(); unput(yytext[0]); return tString;
<str_rule>\.[\t\r\ ] FC45FinishString(); unput(yytext[1]); unput(yytext[0]); return tString;
<str_rule>\.\n FC45FinishString(); unput(yytext[1]); unput(yytext[0]); return tString; gLineNumber++;
<str_rule><<EOF>> {
int len = strlen(string_buf);
// printf("eof rule.\n");
if(len == 1 && string_buf[0] == '.') {
//printf(" period at end of file\n");
return '.';
} else if(string_buf[len - 1] == '.') {
// printf(" period: %s - unput .\n", string_buf);
FC45FinishString(); unput('.'); return tString;
} else {
// printf(" no-period: %s\n", string_buf);
FC45FinishString(); return tString;
}
}
<str_rule>\\: *string_buf_ptr++ = ':';
<str_rule>\\\? *string_buf_ptr++ = '?';
<str_rule>\\, *string_buf_ptr++ = ',';
<str_rule>\\. *string_buf_ptr++ = '.';
<str_rule>\n *string_buf_ptr++ = ' '; gLineNumber++;
<str_rule>[ \t\r]+ *string_buf_ptr++ = ' ';
<str_rule>[^:?,\t\n\r\|\.\\\ ]+ {
char *yptr = yytext;
while(*yptr) {
*string_buf_ptr++ = *yptr++;
}
}
%%
int fc45wrap(void) {
return 1;
}
void FC45SetFile(FILE *file) {
fc45in = file;
yyrestart(fc45in);
}
void FC45FinishString(void) {
int len;
char *tmpStr;
BEGIN(INITIAL);
*string_buf_ptr = '\0';
len = strlen(string_buf);
/* remove any ending spaces */
while(string_buf[len - 1] == ' ') {
string_buf[len - 1] = '\0';
len--;
}
tmpStr = MNewPtr(len + 1);
strncpy(tmpStr, string_buf, len + 1);
fc45lval.string = tmpStr;
string_buf[0] = '\0';
}
fc45.y :
%{
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "ExampleSpec1.h"
//#include "AttributeTracker.c"
//#include "vfml.h"
%}
%{
int fc45lex(void);
int fc45error(const char *);
/* HERE figure out how to give better error messages */
/* BUG needs a \n at the end of the names file */
/* These tmps are allocated at the begining of parsing and then
used during parsing. For example, so that we can simply
add terrains to an area while parsing. After parsing a
statement, the associated tmp is added to the appropriate
global list, and a new tmp is allocated. Finally, at the
end of parsing, all the tmps are freed
*/
ExampleSpecPtr exampleSpec;
AttributeSpecPtr attributeSpec;
int gLineNumber;
%}
%union {
int integer;
float f;
char *string;
}
%token <integer> tInteger
%token <string> tString
%token tIgnore tContinuous tDiscrete tEOF
%%
ExampleSpec: ClassList '.' AttributeList;
ClassList: ClassList ',' ClassSpec | ClassSpec /* ending */;
ClassSpec: tString { ExampleSpecAddClass(exampleSpec, $1); };
AttributeList: AttributeList AttributeSpec | /* ending */;
AttributeSpec: tString ':' AttributeInfo '.' {
AttributeSpecSetName(attributeSpec, $1);
ExampleSpecAddAttributeSpec(exampleSpec, attributeSpec);
attributeSpec = AttributeSpecNew();
};
AttributeInfo: tIgnore {
AttributeSpecSetType(attributeSpec, asIgnore);} |
tContinuous {
AttributeSpecSetType(attributeSpec, asContinuous);} |
tDiscrete tString {
AttributeSpecSetType(attributeSpec, asDiscreteNoName);
AttributeSpecSetNumValues(attributeSpec, atoi($2)); } |
AttributeValueNameList {
AttributeSpecSetType(attributeSpec, asDiscreteNamed); };
AttributeValueNameList: AttributeValueNameList ',' tString {
AttributeSpecSetNumValues(attributeSpec,
AttributeSpecGetNumValues(attributeSpec) + 1);
AttributeSpecAddValue(attributeSpec, $3); } |
tString {
AttributeSpecSetNumValues(attributeSpec,
AttributeSpecGetNumValues(attributeSpec) + 1);
AttributeSpecAddValue(attributeSpec, $1); };
%%
void FC45SetFile(FILE *file);
int fc45error(const char *msg) {
fprintf(stderr, "%s line %d\n", msg, gLineNumber);
return 0;
}
ExampleSpecPtr ParseFC45(const char *file) {
FILE *input;
input = fopen(file, "r");
if(input == 0) {
return 0;
}
FC45SetFile(input);
exampleSpec = ExampleSpecNew();
attributeSpec = AttributeSpecNew();
gLineNumber = 0;
if(fc45parse()) {
/* parse failed! */
fprintf(stderr, "Error in parsing: %s\n", file);
}
fclose(input);
/* free the left over attribute spec */
AttributeSpecFree(attributeSpec);
return exampleSpec;
}
I've searched Internet for the solution and was unable to come up with any.
Hope someone recognizes the problem and has a quick solution to it. Any help will be appreciated.
I just ran through a simple example, adding the following Autotools files:
configure.ac:
AC_PREREQ([2.69])
AC_INIT([example], [0.1a], [example#example.com])
AC_CONFIG_SRCDIR([ex1.l])
# Used only to shorten the otherwise lengthy compilation line in the output below.
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign])
# I used C instead of C++.
AC_PROG_CC
AM_PROG_LEX
AC_PROG_YACC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
Makefile.am (taken pretty much straight from the Automake manual):
BUILT_SOURCES = ex1.h
AM_YFLAGS = -d
bin_PROGRAMS = ex1
ex1_SOURCES = ex1.l ex1.y
It turned out that you can't name the files with the same base file name. In my case, I had ex1.l and ex1.y. Using ex1_SOURCES = ex1.l ex1.y resulted in the following output when I invoked make:
make[1]: Entering directory '/home/kit/ex1'
/bin/bash ./ylwrap ex1.l lex.yy.c ex1.c -- flex
make[1]: Leaving directory '/home/kit/ex1'
make all-am
make[1]: Entering directory '/home/kit/ex1'
gcc -DHAVE_CONFIG_H=1 -I. -g -O2 -MT ex1.o -MD -MP -MF .deps/ex1.Tpo -c -o ex1.o ex1.c
ex1.l:5:17: fatal error: ex1.h: No such file or directory
#include "ex1.h"
^
compilation terminated.
Makefile:378: recipe for target 'ex1.o' failed
make[1]: *** [ex1.o] Error 1
make[1]: Leaving directory '/home/kit/ex1'
Makefile:281: recipe for target 'all' failed
make: *** [all] Error 2
Note the fact that flex was invoked in the second line, but bison/yacc was not. What's the reason? Well, the ylwrap script is the reason:
ex1.c: ex1.l
ex1.c: ex1.y
Because the script renames the output of ex1.l from "lex.yy.c" to "ex1.c", the makefile thinks that ex1.c is already built, so it won't do anything with the bison/yacc file, which means that ex1.h isn't built either.
You can't disable the ylwrap script, but you can work around it: simply rename your flex source file and change the references to the file name in your Makefile.in and configure.ac files as necessary. You shouldn't need to rename your bison/yacc source file because that would mean changing every #include "fc45.h" to #include "fc45_g.h" (or whatever you renamed the file to) in every C file as well as your flex source file.
The problem is that neither make nor automake know anything about the non-standard .lex file extension, so when you have a source file that ends in .lex, they don't know what to do with it. You could create rules to handle .lex, but it's probably much easier to just rename the file with a .l file extension, which they know how to handle.
Related
I've several files with main functions in C, for example, I've files called show.c, delete.c add.c (...). I also have a file, called interpreter.c, which may call one of the files, for example delete.c. Most of these file implement a main function, like the delete.c:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
int main (int argc, char *argv[])
{
int fd, rm;
char *caminho = argv[1]; // argumento inserido no terminal
char caminhod[30]="../TPSOFinal/";
strcat(caminhod,argv[1]);
fd = open(caminhod, O_RDONLY);
rm=unlink(caminhod);
// Verifica se o caminho inserido no input existe
if(rm == 0){
write(1,"Ficheiro eliminado!!!\n", 22);
return 0;
}
else{
write(1,"Erro ao eliminar ficheiro !!!\n", 29);
perror("Erro");
}
return 0;
close(fd);
}
The interpreter:
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <readline/readline.h>
#include <readline/history.h>
#define LER_BUFFER 1024
#define TBUFF 64
#define DELIM "\t\r\n\a"
int mostra(char **args);
int conta(char **args);
int acrescenta(char **args);
int apaga(char **args);
int informa(char **args);
int lista(char **args);
int manual(char **args);
int termina(char **args);
char *comando[] =
{
"mostra <caminho>",
"conta <caminho>",
"acrescenta <caminho> <caminho destino>",
"apaga <caminho>",
"informa <caminho>",
"lista <caminho>",
"manual",
"termina",
" ",
};
int (*fcomandos[]) (char**) =
{
&mostra,
&conta,
&acrescenta,
&apaga,
&informa,
&lista,
&manual,
&termina
};
int ncomandos()
{
return sizeof(comando)/sizeof(char*);
}
void processa(char *linha, char **argv)
{
while(*linha != '\0')
{
while(*linha == ' ' || *linha == '\t' || *linha == '\n')
{
*linha++ = '\0'; //troca caracteres especiais
}
*argv++ = linha; //guarda posição
while (*linha != '\0' && *linha != ' ' && *linha != '\t' && *linha != '\n')
{
linha++;
}
}
*argv = NULL;
}
char *lerlinha (void)
{
char *linha = NULL;
ssize_t tam = 0;
getline (&linha, &tam, stdin);
return linha;
}
char **separa (char *linha)
{
int tam = TBUFF, pos = 0;
char **palavras = malloc (tam *sizeof(char*));
char *palavra;
if (!palavras)
{
perror("Erro");
exit(EXIT_FAILURE);
}
palavra = strtok (linha, DELIM);
while (palavra != NULL)
{
palavras [pos] = palavra;
pos ++;
if (pos >= tam)
{
perror ("Erro");
}
}
palavra = strtok(NULL, DELIM);
palavras [pos] = NULL;
return palavras;
}
int launch (char **args)
{
pid_t pid, wpid;
int estado;
pid = fork();
if (pid == 0)
{
if(execvp(args[0],args)==-1){ perror ("Erro!"); }
exit (EXIT_FAILURE);
}
if (pid <0)
{
perror ("Erro!");
}
else
{
do{wpid = waitpid(pid, &estado, WUNTRACED);}
while (!WIFEXITED(estado)&& !WIFSIGNALED(estado));
}
return 1;
}
//Testa se os comandos existem
int mostra (char **args)
{
if (args[1] == NULL)
{
perror("sem argumentos ");
}
else if (chdir (args[1]) != 0)
{
perror ("Erro!");
}
return 1;
}
int conta ( char ** args)
{
if (args[1] == NULL)
{
perror("Sem argumentos ");
}
else if (chdir (args[1])!= 0)
{
perror ("Erro!");
}
return 1;
}
// Manual dos comandos
int manual (char **args)
{
int i;
printf("\n\nMiguel Oliveira\n");
printf("10260 - LESI\n");
printf("Sistemas Operativos e Sistemas Distribuidos\n");
printf("\nLista de Comandos\n");
for (i=0; i<ncomandos(); i++)
{
printf("%s\n", comando[i]);
}
return 1;
}
int termina (char **args)
{
return 0;
}
//Executa os comandos
int executar (char **args)
{
int i;
if (args[0] == NULL)
{
return 1;
}
for (i=0; i<ncomandos(); i++)
{
if (strcmp(args[0], comando[i])==0)
{
return (*fcomandos[i])(args);
}
}
return launch(args);
}
//Interpretador
void interpretador (void)
{
char *linha;
char **args;
int estado;
do
{
printf("%% ");
linha = lerlinha();
args = separa(linha);
estado = executar(args);
free(linha);
free(args);
} while (estado);
}
int main (void)
{
interpretador();
return EXIT_SUCCESS;
}
I've tried to research for similar problems, and i've found some little possible solutions, but cannot solve my problem, as show on bottom GCC compile mistake
You do not "call source files"; source files define functions and variables, and when compiled, ones defined in different files can use each other if they have a declaration (in a header file, usually) or a pointer (via dynamic link methods, like POSIX dlsym()).
Consider this minimal example. First, example.c:
#include <stdlib.h>
#include <stdio.h>
/* We expect someone else to define these */
extern int one(void);
int main(void)
{
printf("one() returned %d.\n", one());
return EXIT_SUCCESS;
}
and helper.c:
int one(void)
{
return 2; /* TODO: It's not one! */
}
You compile each source file to an object file:
gcc -Wall -O2 -c example.c
gcc -Wall -O2 -c helper.c
and then you link them to an executable program:
gcc -Wall -O2 example.o helper.o -o program
which you can run using
./program
Normally, each C source file that provides functions or variables usable outside that file, declares them in a header file. Here's a better example.
degrees.h
#ifndef DEGREES_H
#define DEGREES_H
double radians_to_degrees(double);
double degrees_to_radians(double);
#endif /* DEGREES_H */
The #ifndef, #define, and #endif are used as guards, so that if you #include the file more than once, the functions get declared only once. (The compiler will complain if it sees multiple declarations. Plus, we don't need to use extern here.)
The implementation of the above is then in degrees.c,
#ifndef PI
#define PI 3.14159265358979323846
#endif
double degrees_to_radians(double degrees)
{
return degrees * PI / 180.0;
}
double radians_to_degrees(double radians)
{
return radians * 180.0 / PI;
}
In a program myprog.c in the same project, you would use the above thus:
#include <stdlib.h>
#include <stdio.h>
#include "degrees.h"
int main(void)
{
printf("45 degrees is %.6f radians.\n", degrees_to_radians(45.0));
printf("2 radians is %.3f degrees.\n", radians_to_degrees(2.0));
return EXIT_SUCCESS;
}
and again you'd compile first the two source files to object files,
gcc -Wall -O2 -c degrees.c
gcc -Wall -O2 -c myprog.c
and then link together to a program, say myprog,
gcc -Wall -O2 degrees.o myprog.o -o myprog
which you can then run:
./myprog
It is also possible to compile and link the functions and variables declared in degrees.h to a static (libdegrees.a) or a dynamic (libdegrees.so) library, and install the header file to the standard location, so that your program could instead use #include <degrees.h> and the program link to the library via -ldegrees, but that is better left until you are well comfortable working with multiple files.
Until then, you might find the following Makefile useful
CC := gcc
CFLAGS := -Wall -O2
LDFLAGS :=
PROGS := myprog
all: clean $(PROGS)
clean:
rm -f *.o $(PROGS)
%.o: %.c
$(CC) $(CFLAGS) -c $^
myprog: degrees.o myprog.o
$(CC) $(CFLAGS) $^ -o $#
You can add multiple programs in the PROGS line, separated by spaces, and copy the myprog: lines for each, listing the object files that program needs.
With this, all you need to compile the program is to type make.
This forum eats Tabs, and Makefiles need indentation to use those. So, if you just copy-paste that to a file, it won't work. You can fix it, though, by running
sed -e 's|^ *|\t|' -i Makefile
which removes all initial spaces on each line with a tab in file Makefile.
If you use separate libraries, typically libm (#include <math.h>), you just need to add -lm (dash ell em) to the LDFLAGS line. If you eventually play with dynamic linking, that's -ldl.
If you were to write a graphical program using Gtk+, you'd append `pkg-config --cflags gtk+-3.0` (including the backticks `) to the CFLAGS line, and `pkg-config --libs gtk+-3.0` to the LDFLAGS line, and #include <gtk/gtk.h> to your program.
I am preparing a yacc/lex test program. The lexer is intended to read integer numbers (long), float numbers (double) and date times in a specific format(YYYYMMDD HHMM).
lexer.l
%{
#include <time.h>
#include "grammar.h"
void read_float_number(void);
void read_integer_number(void);
void read_date_YYYYMMDD_HHMM(void);
void yyerror(const char* msg);
%}
%%
/* SKIP BLANKS AND TABS */
[\t ] { ; }
/* YYYYMMDD HHMM DATE */
[12][09][0-9][0-9][0-1][0-9][0-3][0-9][ ][0-2][0-9][0-5][0-9] { read_date_YYYYMMDD_HHMM(); return DATETIME; }
/* FLOAT NUMBER */
[0-9]+\.[0-9]+ { read_float_number(); return FLOAT_NUMBER; }
/* INTEGER NUMBER */
[0-9]+ { read_integer_number(); return INTEGER_NUMBER; }
%%
/* READ FLOAT NUMBER */
void read_float_number(void) {
sscanf(yytext, "%lf", &yylval.float_number);
}
/* READ INTEGER NUMBER */
void read_integer_number(void) {
sscanf(yytext, "%ld", &yylval.integer_number);
}
/* READ YYYYMMDD HHMM DATE */
void read_date_YYYYMMDD_HHMM(void) {
/* DATETIME STRUCT TM */
struct tm dt;
char buffer[80];
/* READ VALUES */
sscanf(yytext, "%4d%2d%2d %2d%2d", &dt.tm_year, &dt.tm_mon, &dt.tm_mday, &dt.tm_hour, &dt.tm_min);
/* NORMALIZE VALUES */
dt.tm_year = dt.tm_year - 1900; /* NORMALIZE YEAR */
dt.tm_mon = dt.tm_mon - 1; /* NORMALIZE MONTH */
dt.tm_isdst = -1; /* NO INFORMATION ABOUT DST */
mktime(&dt); /* NORMALIZE STRUCT TM */
/* PRINT DATETIME */
strftime(buffer, 80, "%c %z %Z\n", &dt);
printf("%s\n", buffer);
/* COPY STRUCT TM TO YACC RETURN VALUE */
memcpy(&dt, &yylval.datetime, sizeof(dt));
}
/* YYERROR */
void yyerror(const char* msg) {
fprintf(stderr, "yyerror %s\n", msg);
exit(1);
}
grammar.y
The grammar is intended to parse this kind of lines (DATETIME,FLOAT,FLOAT,INTEGER):
20191201 17000,1.102290,1.102470,0
%{
#include <time.h>
#include <stdio.h>
%}
%union {
struct tm datetime; /* DATE TIME VALUES */
double float_number; /* 8 BYTES DOUBLE VALUE */
long integer_number; /* 8 BYTES INTEGER VALUE */
}
%token <datetime> DATETIME
%token <float_number> FLOAT_NUMBER
%token <integer_number> INTEGER_NUMBER
%%
lastbid_lastask: DATETIME ',' FLOAT_NUMBER ',' FLOAT_NUMBER ',' INTEGER_NUMBER { printf("MATCH %lf %lf %ld\n", $3, $5, $7); }
;
%%
int main(int argc, char *argv[]) {
yyparse();
return 0;
}
The makefile to build everything is as follows:
CCFLAGS = -std=c89 -c
YFLAGS = -d # Forces generation of y.tab.h
OBJS = lexer.o grammar.o
TARGET = readfile
readfile: $(OBJS)
cc $(OBJS) -std=c89 -ll -o $(TARGET)
grammar.h grammar.o: grammar.y
yacc $(YFLAGS) -ogrammar.c grammar.y
cc $(CCFLAGS) grammar.c
lexer.o: lexer.l grammar.h
lex -olexer.c lexer.l
cc $(CCFLAGS) lexer.c
clean:
rm -f $(OBJS) grammar.[ch] lexer.c
I run readfile but after parsing the DATETIME lex seems to invoke yyerror:
% ./readfile
20191201 170003296,1.102290,1.102470,0
Mon Feb 17 22:20:00 2020 +0100 CET
yyerror syntax error
Same for numbers:
% ./readfile
45.45
yyerror syntax error
% ./readfile
45
yyerror syntax error
But not for arbitrary text:
% ./readfile
abc
abc
Why is lex invoking yyerror? What is missing in the lex parsing code?
As far as I can see, your lexer never returns a ',' token. By default, (f)lex scanners print unrecognised characters to stdout, as, for example, in your test with input abc. However, the unrecognised comma is not shown in your output because the stdout buffer was not flushed before exit() was called in yyerror().
In any event, we usually put a fallback rule as the last rule in the scanner specification:
. { return yytext[0]; }
That guarantees that any unrecognised character will be passed through to the parser as a quoted single-character token. If the parser does not expect that token, it will raise a syntax error immediately.
This question is something of a trick C question or a trick clang/gcc question. I'm not sure which.
I phrased it like I did because the final array is in main.c, but the structs that are in the array are defined in C modules.
The end goal of what I am trying to do is to be able to define structs in seperate C modules and then have those structs be available in a contiguous array right from program start. I do not want to use any dynamic code to declare the array and put in the elements.
I would like it all done at compile or link time -- not at run time.
I'm looking to end up with a monolithic blob of memory that gets setup right from program start.
For the sake of the Stack Overflow question, I thought it would make sense if I imagined these as "drivers" (like in the Linux kernel) Going with that...
Each module is a driver. Because the team is complex, I do not know how many drivers there will ultimately be.
Requirements:
Loaded into contiguous memory (an array)
Loaded into memory at program start
installed by the compiler/linker, not dynamic code
a driver exists because source code exists for it (no dynamic code to load them up)
Avoid cluttering up the code
Here is a contrived example:
// myapp.h
//////////////////////////
struct state
{
int16_t data[10];
};
struct driver
{
char name[255];
int16_t (*on_do_stuff) (struct state *state);
/* other stuff snipped out */
};
// drivera.c
//////////////////////////
#include "myapp.h"
static int16_t _on_do_stuff(struct state *state)
{
/* do stuff */
}
static const struct driver _driver = {
.name = "drivera",
.on_do_stuff = _on_do_stuff
};
// driverb.c
//////////////////////////
#include "myapp.h"
static int16_t _on_do_stuff(struct state *state)
{
/* do stuff */
}
static const struct driver _driver = {
.name = "driverb",
.on_do_stuff = _on_do_stuff
};
// driverc.c
//////////////////////////
#include "myapp.h"
static int16_t _on_do_stuff(struct state *state)
{
/* do stuff */
}
static const struct driver _driver = {
.name = "driverc",
.on_do_stuff = _on_do_stuff
};
// main.c
//////////////////////////
#include <stdio.h>
static struct driver the_drivers[] = {
{drivera somehow},
{driverb somehow},
{driverc somehow},
{0}
};
int main(void)
{
struct state state;
struct driver *current = the_drivers;
while (current != 0)
{
printf("we are up to %s\n", current->name);
current->on_do_stuff(&state);
current += sizeof(struct driver);
}
return 0;
}
This doesn't work exactly.
Ideas:
On the module-level structs, I could remove the static const keywords, but I'm not sure how to get them into the array at compile time
I could move all of the module-level structs to main.c, but then I would need to remove the static keyword from all of the on_do_stuff functions, and thereby clutter up the namespace.
In the Linux kernel, they somehow define kernel modules in separate files and then through linker magic, they are able to be loaded into monolithics
Use a dedicated ELF section to "collect" the data structures.
For example, define your data structure in info.h as
#ifndef INFO_H
#define INFO_H
#ifndef INFO_ALIGNMENT
#if defined(__LP64__)
#define INFO_ALIGNMENT 16
#else
#define INFO_ALIGNMENT 8
#endif
#endif
struct info {
long key;
long val;
} __attribute__((__aligned__(INFO_ALIGNMENT)));
#define INFO_NAME(counter) INFO_CAT(info_, counter)
#define INFO_CAT(a, b) INFO_DUMMY() a ## b
#define INFO_DUMMY()
#define DEFINE_INFO(data...) \
static struct info INFO_NAME(__COUNTER__) \
__attribute__((__used__, __section__("info"))) \
= { data }
#endif /* INFO_H */
The INFO_ALIGNMENT macro is the alignment used by the linker to place each symbol, separately, to the info section. It is important that the C compiler agrees, as otherwise the section contents cannot be treated as an array. (You'll obtain an incorrect number of structures, and only the first one (plus every N'th) will be correct, the rest of the structures garbled. Essentially, the C compiler and the linker disagreed on the size of each structure in the section "array".)
Note that you can add preprocessor macros to fine-tune the INFO_ALIGNMENT for each of the architectures you use, but you can also override it for example in your Makefile, at compile time. (For GCC, supply -DINFO_ALIGNMENT=32 for example.)
The used attribute ensures that the definition is emitted in the object file, even though it is not referenced otherwise in the same data file. The section("info") attribute puts the data into a special info section in the object file. The section name (info) is up to you.
Those are the critical parts, otherwise it is completely up to you how you define the macro, or whether you define it at all. Using the macro is easy, because one does not need to worry about using unique variable name for the structure. Also, if at least one member is specified, all others will be initialized to zero.
In the source files, you define the data objects as e.g.
#include "info.h"
/* Suggested, easy way */
DEFINE_INFO(.key = 5, .val = 42);
/* Alternative way, without relying on any macros */
static struct info foo __attribute__((__used__, __section__("info"))) = {
.key = 2,
.val = 1
};
The linker provides symbols __start_info and __stop_info, to obtain the structures in the info section. In your main.c, use for example
#include "info.h"
extern struct info __start_info[];
extern struct info __stop_info[];
#define NUM_INFO ((size_t)(__stop_info - __start_info))
#define INFO(i) ((__start_info) + (i))
so you can enumerate all info structures. For example,
int main(void)
{
size_t i;
printf("There are %zu info structures:\n", NUM_INFO);
for (i = 0; i < NUM_INFO; i++)
printf(" %zu. key=%ld, val=%ld\n", i,
__start_info[i].key, INFO(i)->val);
return EXIT_SUCCESS;
}
For illustration, I used both the __start_info[] array access (you can obviously #define SOMENAME __start_info if you want, just make sure you do not use SOMENAME elsewhere in main.c, so you can use SOMENAME[] as the array instead), as well as the INFO() macro.
Let's look at a practical example, an RPN calculator.
We use section ops to define the operations, using facilities defined in ops.h:
#ifndef OPS_H
#define OPS_H
#include <stdlib.h>
#include <errno.h>
#ifndef ALIGN_SECTION
#if defined(__LP64__) || defined(_LP64)
#define ALIGN_SECTION __attribute__((__aligned__(16)))
#elif defined(__ILP32__) || defined(_ILP32)
#define ALIGN_SECTION __attribute__((__aligned__(8)))
#else
#define ALIGN_SECTION
#endif
#endif
typedef struct {
size_t maxsize; /* Number of values allocated for */
size_t size; /* Number of values in stack */
double *value; /* Values, oldest first */
} stack;
#define STACK_INITIALIZER { 0, 0, NULL }
struct op {
const char *name; /* Operation name */
const char *desc; /* Description */
int (*func)(stack *); /* Implementation */
} ALIGN_SECTION;
#define OPS_NAME(counter) OPS_CAT(op_, counter, _struct)
#define OPS_CAT(a, b, c) OPS_DUMMY() a ## b ## c
#define OPS_DUMMY()
#define DEFINE_OP(name, func, desc) \
static struct op OPS_NAME(__COUNTER__) \
__attribute__((__used__, __section__("ops"))) = { name, desc, func }
static inline int stack_has(stack *st, const size_t num)
{
if (!st)
return EINVAL;
if (st->size < num)
return ENOENT;
return 0;
}
static inline int stack_pop(stack *st, double *to)
{
if (!st)
return EINVAL;
if (st->size < 1)
return ENOENT;
st->size--;
if (to)
*to = st->value[st->size];
return 0;
}
static inline int stack_push(stack *st, double val)
{
if (!st)
return EINVAL;
if (st->size >= st->maxsize) {
const size_t maxsize = (st->size | 127) + 129;
double *value;
value = realloc(st->value, maxsize * sizeof (double));
if (!value)
return ENOMEM;
st->maxsize = maxsize;
st->value = value;
}
st->value[st->size++] = val;
return 0;
}
#endif /* OPS_H */
The basic set of operations is defined in ops-basic.c:
#include "ops.h"
static int do_neg(stack *st)
{
double temp;
int retval;
retval = stack_pop(st, &temp);
if (retval)
return retval;
return stack_push(st, -temp);
}
static int do_add(stack *st)
{
int retval;
retval = stack_has(st, 2);
if (retval)
return retval;
st->value[st->size - 2] = st->value[st->size - 1] + st->value[st->size - 2];
st->size--;
return 0;
}
static int do_sub(stack *st)
{
int retval;
retval = stack_has(st, 2);
if (retval)
return retval;
st->value[st->size - 2] = st->value[st->size - 1] - st->value[st->size - 2];
st->size--;
return 0;
}
static int do_mul(stack *st)
{
int retval;
retval = stack_has(st, 2);
if (retval)
return retval;
st->value[st->size - 2] = st->value[st->size - 1] * st->value[st->size - 2];
st->size--;
return 0;
}
static int do_div(stack *st)
{
int retval;
retval = stack_has(st, 2);
if (retval)
return retval;
st->value[st->size - 2] = st->value[st->size - 1] / st->value[st->size - 2];
st->size--;
return 0;
}
DEFINE_OP("neg", do_neg, "Negate current operand");
DEFINE_OP("add", do_add, "Add current and previous operands");
DEFINE_OP("sub", do_sub, "Subtract previous operand from current one");
DEFINE_OP("mul", do_mul, "Multiply previous and current operands");
DEFINE_OP("div", do_div, "Divide current operand by the previous operand");
The calculator expects each value and operand to be a separate command-line argument for simplicity. Our main.c contains operation lookup, basic usage, value parsing, and printing the result (or error):
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "ops.h"
extern struct op __start_ops[];
extern struct op __stop_ops[];
#define NUM_OPS ((size_t)(__stop_ops - __start_ops))
static int do_op(stack *st, const char *opname)
{
struct op *curr_op;
if (!st || !opname)
return EINVAL;
for (curr_op = __start_ops; curr_op < __stop_ops; curr_op++)
if (!strcmp(opname, curr_op->name))
break;
if (curr_op >= __stop_ops)
return ENOTSUP;
return curr_op->func(st);
}
static int usage(const char *argv0)
{
struct op *curr_op;
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
fprintf(stderr, " %s RPN-EXPRESSION\n", argv0);
fprintf(stderr, "\n");
fprintf(stderr, "Where RPN-EXPRESSION is an expression using reverse\n");
fprintf(stderr, "Polish notation, and each argument is a separate value\n");
fprintf(stderr, "or operator. The following operators are supported:\n");
for (curr_op = __start_ops; curr_op < __stop_ops; curr_op++)
fprintf(stderr, "\t%-14s %s\n", curr_op->name, curr_op->desc);
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
int main(int argc, char *argv[])
{
stack all = STACK_INITIALIZER;
double val;
size_t i;
int arg, err;
char dummy;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
return usage(argv[0]);
for (arg = 1; arg < argc; arg++)
if (sscanf(argv[arg], " %lf %c", &val, &dummy) == 1) {
err = stack_push(&all, val);
if (err) {
fprintf(stderr, "Cannot push %s to stack: %s.\n", argv[arg], strerror(err));
return EXIT_FAILURE;
}
} else {
err = do_op(&all, argv[arg]);
if (err == ENOTSUP) {
fprintf(stderr, "%s: Operation not supported.\n", argv[arg]);
return EXIT_FAILURE;
} else
if (err) {
fprintf(stderr, "%s: Cannot perform operation: %s.\n", argv[arg], strerror(err));
return EXIT_FAILURE;
}
}
if (all.size < 1) {
fprintf(stderr, "No result.\n");
return EXIT_FAILURE;
} else
if (all.size > 1) {
fprintf(stderr, "Multiple results:\n");
for (i = 0; i < all.size; i++)
fprintf(stderr, " %.9f\n", all.value[i]);
return EXIT_FAILURE;
}
printf("%.9f\n", all.value[0]);
return EXIT_SUCCESS;
}
Note that if there were many operations, constructing a hash table to speed up the operation lookup would make a lot of sense.
Finally, we need a Makefile to tie it all together:
CC := gcc
CFLAGS := -Wall -O2 -std=c99
LDFLAGS := -lm
OPS := $(wildcard ops-*.c)
OPSOBJS := $(OPS:%.c=%.o)
PROGS := rpncalc
.PHONY: all clean
all: clean $(PROGS)
clean:
rm -f *.o $(PROGS)
%.o: %.c
$(CC) $(CFLAGS) -c $^
rpncalc: main.o $(OPSOBJS)
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $#
Because this forum does not preserve Tabs, and make requires them for indentation, you probably need to fix the indentation after copy-pasting the above. I use sed -e 's|^ *|\t|' -i Makefile
If you compile (make clean all) and run (./rpncalc) the above, you'll see the usage information:
Usage: ./rpncalc [ -h | --help ]
./rpncalc RPN-EXPRESSION
Where RPN-EXPRESSION is an expression using reverse
Polish notation, and each argument is a separate value
or operator. The following operators are supported:
div Divide current operand by the previous operand
mul Multiply previous and current operands
sub Subtract previous operand from current one
add Add current and previous operands
neg Negate current operand
and if you run e.g. ./rpncalc 3.0 4.0 5.0 sub mul neg, you get the result 3.000000000.
Now, let's add some new operations, ops-sqrt.c:
#include <math.h>
#include "ops.h"
static int do_sqrt(stack *st)
{
double temp;
int retval;
retval = stack_pop(st, &temp);
if (retval)
return retval;
return stack_push(st, sqrt(temp));
}
DEFINE_OP("sqrt", do_sqrt, "Take the square root of the current operand");
Because the Makefile above compiles all C source files beginning with ops- in to the final binary, the only thing you need to do is recompile the source: make clean all. Running ./rpncalc now outputs
Usage: ./rpncalc [ -h | --help ]
./rpncalc RPN-EXPRESSION
Where RPN-EXPRESSION is an expression using reverse
Polish notation, and each argument is a separate value
or operator. The following operators are supported:
sqrt Take the square root of the current operand
div Divide current operand by the previous operand
mul Multiply previous and current operands
sub Subtract previous operand from current one
add Add current and previous operands
neg Negate current operand
and you have the new sqrt operator available.
Testing e.g. ./rpncalc 1 1 1 1 add add add sqrt yields 2.000000000, as expected.
I'm new to flex and bison so bear with me. I'm trying to use yylloc in yyerror to print out where the error occurs along with the filename. I know that this requires me to redefine YYLTPYE to include a char* filename that I can use to keep track of the filename. According to the Flex and Bison book I have, it recommends that I use the YY_USER_ACTION macro to initialize the YYLTYPE in the .l file, so I included the following in it,
#define YY_USER_ACTION yylloc.filename = filename; yylloc.hel = 0; \
yylloc.first_line = yylloc.last_line = yylineno; \
yylloc.first_column = yycolumn; yylloc.last_column = yycolumn+yyleng-1; \
yycolumn += yyleng;
but when I try to compile the project, I get the error that yylloc is undeclared.
I've tried the solution offered by Chris Dodd in this question, but it hasn't helped me to resolve the issue. Any and all help in resolving this error is much apprecaited.
Here's the full code in .l:
%option noyywrap nodefault yylineno case-insensitive
%{
#include "need.h"
#include "numbers.tab.h"
int yycolumn = 1;
#define YY_USER_ACTION yylloc.filename = filename; yylloc.hel = 0; \
yylloc.first_line = yylloc.last_line = yylineno; \
yylloc.first_column = yycolumn; yylloc.last_column = yycolumn+yyleng-1; \
yycolumn += yyleng;
%}
Integers [-]?(0|[1-9][0-9]*)
Float [.][0-9]+
Exp [eE][-]?(0|[1-9][0-9]*)
Octal [-]?(00|0[1-7][0-7]*)
Hexa [-]?(0[xX][0-9A-F]+)
tomsNotNumbers [^ \t\n\v\f\r]+
%%
{Integers}{Float}?{Exp}? {
printf("%s is a number.\n", yytext);
possibleNumbers++; // increment by 1 as an input was given -M
actualNumbers++; // increment by 1 as an input did match our pattern -M
}
{Octal} {
printf("%s is a number.\n", yytext);
possibleNumbers++; // increment by 1 as an input was given -M
actualNumbers++; // increment by 1 as an input did match our pattern -M
}
{Hexa} {
printf("%s is a number.\n", yytext);
possibleNumbers++; // increment by 1 as an input was given -M
actualNumbers++; // increment by 1 as an input did match our pattern -M
}
{tomsNotNumbers} {
printf("%s is not a number.\n", yytext);
yyerror(warning, "This isn't a number.");
possibleNumbers++; // increment by 1 as an input was given -M
failedNumbers++; // increment by 1 as the input has failed to match our patterns -M
}
[\n] /*Do nothing for newline*/
. /*Do nothing for anything else*/
%%
.y is just empty for now, only has an include for need.h and one for .tab.h
The need.h:
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
int possibleNumbers = 0;
int actualNumbers = 0;
int failedNumbers = 0;
typedef struct YYLTYPE
{
int first_line;
int first_column;
int last_line;
int last_column;
char *filename; /* use to keep track of which file we're currently in */
int hel; /* no errors = 0, warning = 1, error = 2, fatal = 3 */
} YYLTYPE;
char *name; /*using for test purposes*/
# define YYLTYPE_IS_DECLARED 1
# define YYLLOC_DEFAULT(Current, Rhs, N) \
do \
if (N) \
{ \
(Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
(Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
(Current).last_line = YYRHSLOC (Rhs, N).last_line; \
(Current).last_column = YYRHSLOC (Rhs, N).last_column; \
(Current).filename = YYRHSLOC (Rhs, 1).filename; \
(Current).hel = YYRHSLOC (Rhs, 1).hel; \
} \
else \
{ /* empty RHS */ \
(Current).first_line = (Current).last_line = YYRHSLOC (Rhs, 0).last_line; \
(Current).first_column = (Current).last_column = YYRHSLOC (Rhs, 0).last_column; \
(Current).filename = NULL; \
(Current).hel = 0; \
} \
while (0)
typedef enum errorSeverity
{
warning = 1, error, fatal
} errorLevel;
void yyerror(errorLevel errlvl, char *s, ...)
{
va_list ap;
va_start(ap, s);
char *errLvls[3] = {"Warning", "Error", "Fatal"};
fprintf(stderr, "%s: %s: , %n", name, errLvls[errlvl - 1], yylloc.first_line);
vfprintf(stderr, s, ap);
fprintf(stderr, "\n");
}
main(int argc, char **argv)
{
printf("argv[0] = %s, argv[1] = %s.\n", argv[0], argv[1]);
if(argc > 1)
{
if((yyin = fopen(argv[1], "r")) == NULL)
{
perror(argv[1]);
exit(1);
}
name = argv[1];
} else
name = "(stdin)";
printf("Filename1: %s", name);
yylex();
printf("Filename2: %s", name);
// print out the report. -M
printf("Out of %d possible numbers, there were %d numbers, and %d not numbers.\n", possibleNumbers, actualNumbers, failedNumbers);
}
Since yylloc is normally defined in the bison-generated parser, not having a bison input file is going to be a bit of a nuisance.
Bison will define yylloc in the generated parser, and place a declaration in the generated header file, if:
You include the directive %locations in the bison prologue, or
You reference a location (#n for some n) in any bison action.
It is generally preferred to add the directive in case there is no explicit reference to a location in any rule.
As Chris Dodd says in the linked question, it is important to include the definition of YYLTYPE before #includeing the bison-generated header file. Alternatively, you could insert the definition of the structure, or an appropriate #include, directly in the bison prologue in a %code requires section. %code requires sections are copied to the generated header, so that will obviate the need to worry about the definition in the flex file.
By the way, I think you meant to use YY_USER_INIT to initialize yylloc. The expansion of YY_USER_INIT is executed only once, before the flex scanner's own initialization. The expansion of YY_USER_ACTION is executed before every scanner action (including empty actions), and is likely to be of use to update the yylloc structure with the current token.
I want to create a read-eval-print loop using flex/bison parser. Trouble is, the flex generated lexer wants input of type FILE* and i would like it to be char*. Is there anyway to do this?
One suggestion has been to create a pipe, feed it the string and open the file descriptor and send to the lexer. This is fairly simple but it feels convoluted and not very platform independent. Is there a better way?
The following routines are available for setting up input buffers for scanning in-memory strings instead of files (as yy_create_buffer does):
YY_BUFFER_STATE yy_scan_string(const char *str): scans a NUL-terminated string`
YY_BUFFER_STATE yy_scan_bytes(const char *bytes, int len): scans len bytes (including possibly NULs) starting at location bytes
Note that both of these functions create, return a corresponding YY_BUFFER_STATE handle (which you must delete with yy_delete_buffer() when done with it) so yylex() scan a copy of the string or bytes. This behavior may be desirable since yylex() modifies the contents of the buffer it is scanning).
If you want avoid the copy (and yy_delete_buffer) using:
YY_BUFFER_STATE yy_scan_buffer(char *base, yy_size_t size)
sample main:
int main() {
yy_scan_buffer("a test string");
yylex();
}
See this section of Flex's manual for information on how to scan in-memory buffers, such as strings.
flex can parse char * using any one of three functions: yy_scan_string(),
yy_scan_buffer(), and yy_scan_bytes() (see the documentation). Here's an example of the first:
typedef struct yy_buffer_state * YY_BUFFER_STATE;
extern int yyparse();
extern YY_BUFFER_STATE yy_scan_string(char * str);
extern void yy_delete_buffer(YY_BUFFER_STATE buffer);
int main(){
char string[] = "String to be parsed.";
YY_BUFFER_STATE buffer = yy_scan_string(string);
yyparse();
yy_delete_buffer(buffer);
return 0;
}
The equivalent statements for yy_scan_buffer() (which requires a doubly null-terminated string):
char string[] = "String to be parsed.\0";
YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string));
My answer reiterates some of the information provided by #dfa and #jlholland, but neither of their answers' code seemed to be working for me.
Here is what I needed to do :
extern yy_buffer_state;
typedef yy_buffer_state *YY_BUFFER_STATE;
extern int yyparse();
extern YY_BUFFER_STATE yy_scan_buffer(char *, size_t);
int main(int argc, char** argv) {
char tstr[] = "line i want to parse\n\0\0";
// note yy_scan_buffer is is looking for a double null string
yy_scan_buffer(tstr, sizeof(tstr));
yy_parse();
return 0;
}
you cannot extern the typedef, which make sense when you think about it.
The accepted answer is incorrect. It will cause memory leaks.
Internally, yy_scan_string calls yy_scan_bytes which, in turn, calls yy_scan_buffer.
yy_scan_bytes allocates memory for a COPY of the input buffer.
yy_scan_buffer works directly upon the supplied buffer.
With all three forms, you MUST call yy_delete_buffer to free the flex buffer-state information (YY_BUFFER_STATE).
However, with yy_scan_buffer, you avoid the internal allocation/copy/free of the internal buffer.
The prototype for yy_scan_buffer does NOT take a const char* and you MUST NOT expect the contents to remain unchanged.
If you allocated memory to hold your string, you are responsible for freeing it AFTER you call yy_delete_buffer.
Also, don't forget to have yywrap return 1 (non-zero) when you're parsing JUST this string.
Below is a COMPLETE example.
%%
<<EOF>> return 0;
. return 1;
%%
int yywrap()
{
return (1);
}
int main(int argc, const char* const argv[])
{
FILE* fileHandle = fopen(argv[1], "rb");
if (fileHandle == NULL) {
perror("fopen");
return (EXIT_FAILURE);
}
fseek(fileHandle, 0, SEEK_END);
long fileSize = ftell(fileHandle);
fseek(fileHandle, 0, SEEK_SET);
// When using yy_scan_bytes, do not add 2 here ...
char *string = malloc(fileSize + 2);
fread(string, fileSize, sizeof(char), fileHandle);
fclose(fileHandle);
// Add the two NUL terminators, required by flex.
// Omit this for yy_scan_bytes(), which allocates, copies and
// apends these for us.
string[fileSize] = '\0';
string[fileSize + 1] = '\0';
// Our input file may contain NULs ('\0') so we MUST use
// yy_scan_buffer() or yy_scan_bytes(). For a normal C (NUL-
// terminated) string, we are better off using yy_scan_string() and
// letting flex manage making a copy of it so the original may be a
// const char (i.e., literal) string.
YY_BUFFER_STATE buffer = yy_scan_buffer(string, fileSize + 2);
// This is a flex source file, for yacc/bison call yyparse()
// here instead ...
int token;
do {
token = yylex(); // MAY modify the contents of the 'string'.
} while (token != 0);
// After flex is done, tell it to release the memory it allocated.
yy_delete_buffer(buffer);
// And now we can release our (now dirty) buffer.
free(string);
return (EXIT_SUCCESS);
}
Other-way, you can redefine function YY_INPUT in lex file, and then set your string to LEX's input. As below:
#undef YY_INPUT
#define YY_INPUT(buf) (my_yyinput(buf))
char my_buf[20];
void set_lexbuf(char *org_str)
{ strcpy(my_buf, org_str); }
void my_yyinput (char *buf)
{ strcpy(buf, my_buf); }
In your main.c, before scanning, you need to set lex's buffer first:
set_lexbuf(your_string);
scanning...
here is a small example for using bison / flex as a parser inside your cpp code for parsing string and changing a string value according to it
(few parts of the code were removed so there might be irrelevant parts there.)
parser.y :
%{
#include "parser.h"
#include "lex.h"
#include <math.h>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int yyerror(yyscan_t scanner, string result, const char *s){
(void)scanner;
std::cout << "yyerror : " << *s << " - " << s << std::endl;
return 1;
}
%}
%code requires{
#define YY_TYPEDEF_YY_SCANNER_T
typedef void * yyscan_t;
#define YYERROR_VERBOSE 0
#define YYMAXDEPTH 65536*1024
#include <math.h>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
}
%output "parser.cpp"
%defines "parser.h"
%define api.pure full
%lex-param{ yyscan_t scanner }
%parse-param{ yyscan_t scanner } {std::string & result}
%union {
std::string * sval;
}
%token TOKEN_ID TOKEN_ERROR TOKEN_OB TOKEN_CB TOKEN_AND TOKEN_XOR TOKEN_OR TOKEN_NOT
%type <sval> TOKEN_ID expression unary_expression binary_expression
%left BINARY_PRIO
%left UNARY_PRIO
%%
top:
expression {result = *$1;}
;
expression:
TOKEN_ID {$$=$1; }
| TOKEN_OB expression TOKEN_CB {$$=$2;}
| binary_expression {$$=$1;}
| unary_expression {$$=$1;}
;
unary_expression:
TOKEN_NOT expression %prec UNARY_PRIO {result = " (NOT " + *$2 + " ) " ; $$ = &result;}
;
binary_expression:
expression expression %prec BINARY_PRIO {result = " ( " + *$1+ " AND " + *$2 + " ) "; $$ = &result;}
| expression TOKEN_AND expression %prec BINARY_PRIO {result = " ( " + *$1+ " AND " + *$3 + " ) "; $$ = &result;}
| expression TOKEN_OR expression %prec BINARY_PRIO {result = " ( " + *$1 + " OR " + *$3 + " ) "; $$ = &result;}
| expression TOKEN_XOR expression %prec BINARY_PRIO {result = " ( " + *$1 + " XOR " + *$3 + " ) "; $$ = &result;}
;
%%
lexer.l :
%{
#include <string>
#include "parser.h"
%}
%option outfile="lex.cpp" header-file="lex.h"
%option noyywrap never-interactive
%option reentrant
%option bison-bridge
%top{
/* This code goes at the "top" of the generated file. */
#include <stdint.h>
}
id ([a-zA-Z][a-zA-Z0-9]*)+
white [ \t\r]
newline [\n]
%%
{id} {
yylval->sval = new std::string(yytext);
return TOKEN_ID;
}
"(" {return TOKEN_OB;}
")" {return TOKEN_CB;}
"*" {return TOKEN_AND;}
"^" {return TOKEN_XOR;}
"+" {return TOKEN_OR;}
"!" {return TOKEN_NOT;}
{white}; // ignore white spaces
{newline};
. {
return TOKEN_ERROR;
}
%%
usage :
void parse(std::string& function) {
string result = "";
yyscan_t scanner;
yylex_init_extra(NULL, &scanner);
YY_BUFFER_STATE state = yy_scan_string(function.c_str() , scanner);
yyparse(scanner,result);
yy_delete_buffer(state, scanner);
yylex_destroy(scanner);
function = " " + result + " ";
}
makefile:
parser.h parser.cpp: parser.y
# /usr/local/bison/2.7.91/bin/bison -y -d parser.y
lex.h lex.cpp: lexer.l
# /usr/local/flex/2.5.39/bin/flex lexer.l
clean:
- \rm -f *.o parser.h parser.cpp lex.h lex.cpp
There's this funny code in libmatheval:
/* Redefine macro to redirect scanner input from string instead of
* standard input. */
#define YY_INPUT( buffer, result, max_size ) \
{ result = input_from_string (buffer, max_size); }