The C/C++ Parser Generator (v. 2.x)-- Example 3 (MC2 .DEF Parser Parser
Posted 04-23-2015 at 03:56 PM by rainbowsally
Updated 05-02-2015 at 10:24 PM by rainbowsally (typo in title)
Updated 05-02-2015 at 10:24 PM by rainbowsally (typo in title)
[fixed bad link to parser stuff Apr 24, 2015 -rs]
What a bother it is to continually upload Makefiles. Let's let Son Of MC2 do it for us.
Today's Features:
The BIG download, the low level parser files, are packed up and base64-ed here.
http://www.linuxquestions.org/questi...-brains-36510/
If you put all the files from these examples into a directory named src and use the mc2.def created by the 'real' mc2 makefile creator, it will generate a makefile that will compile and link ALL of the files -- automagically.
Run mc2-multi (with no args) again each time you add or remove files from the src directory to update the makefile.
Note: The makefile at the bottom was created with this little utility. And mc2.def files are easy and intiutive to work with... if you have all the parts defined.
WARNING: If you don't have all the demo files, this makefile may choke. Type 'make -k' or do whatever you have to to get mc2-multi built, and from then on things should be ok, or at least easily solvable. Build it. Check the --help switch and good luck.
file: src/mc2-multi.cpp
purpose: source file
file: src/common/mc2_multi_str.dat
purpose: source file
file: mc2.def
purpose: Makefile template and Demo test
file: Makefile
purpose: Builds all the files
What a bother it is to continually upload Makefiles. Let's let Son Of MC2 do it for us.
Today's Features:
- A makefile creator to compile and link all files in your SRC directory.
- Stringing together of C and boolean parser elements (see main())
- Working with strings, reading values by name from files, and stuff like that.
The BIG download, the low level parser files, are packed up and base64-ed here.
http://www.linuxquestions.org/questi...-brains-36510/
If you put all the files from these examples into a directory named src and use the mc2.def created by the 'real' mc2 makefile creator, it will generate a makefile that will compile and link ALL of the files -- automagically.
Run mc2-multi (with no args) again each time you add or remove files from the src directory to update the makefile.
Note: The makefile at the bottom was created with this little utility. And mc2.def files are easy and intiutive to work with... if you have all the parts defined.
WARNING: If you don't have all the demo files, this makefile may choke. Type 'make -k' or do whatever you have to to get mc2-multi built, and from then on things should be ok, or at least easily solvable. Build it. Check the --help switch and good luck.
file: src/mc2-multi.cpp
purpose: source file
Code:
// mc2-multi -- parser for mc2.def files that are type MULTI #include <stdio.h> #include <malloc.h> #include <string.h> #include "parser.h" #include "common/parser.cpp" // don't need a lib void dbg(){} // for a non-moving breakpoint int usage(int errcode); // returns false if error occurred bool load_filenames(char** pfilenames, int max_names, int* pn_names, cstr filespec); void free_filenames(char** filenames, int n_names); void textfile_load(cstr* pinbuf, int* pinlen, cstr filename); int textfile_save(cstr filename, vstr text, int textlen); bool read_value(vstr buf, cstr name); void make_objdir(cstr path); // the parser entry point bool create_makefile(cstr* cfilenames, int n_cfilenames, cstr* hfilenames, int n_hfilenames); int main(int argc, char** argv) { dbg(); int errcode = 1; ///////////////////////////////// // input checks if(argc > 1) // help or version { if(streq(argv[1], "--help")) return usage(0); else if(streq(argv[1], "--version")) return print_version(); else return usage(1); } ///////////////////////////////// // init inbuf and parser int inlen; cstr inbuf; textfile_load(&inbuf, &inlen, "mc2.def"); if(!inlen) { fprintf(stderr, "Can't open mc2.def in current directory\n"); return 1; } int outlen = 16 * 1024; vstr outbuf = (vstr)malloc(outlen); char* cfilenames[256]; int n_cfilenames; char* hfilenames[256]; int n_hfilenames; // We need the global obj but can't name it 'obj'. PARSER_OBJ o; parser_init(&o, inbuf, inlen, outbuf, outlen); bool ok = true; // pre-parse it to get data and create OBJDIR char tmpbuf[256]; char srcdir[256]; char objdir[256]; char outname[256]; static char errmsg[256]; do { SAVE_STATIC_ERRMSG(errmsg); sprintf(errmsg, "Sorry kids, we can only do type MULTI mc2.def files"); ok &= read_value(outname, "OUTNAME"); if(! streq(outname, "MULTI")) { ok = false; break; } sprintf(errmsg, "loading dir data"); ok &= read_value(srcdir, "SRCDIR") && 0 != sprintf(tmpbuf, "s/*.c*", srcdir) && load_filenames(cfilenames, 256, &n_cfilenames, tmpbuf) && 0 != sprintf(tmpbuf, "s/*.h", srcdir) && load_filenames(hfilenames, 256, &n_hfilenames, tmpbuf) && read_value(objdir, "OBJDIR") ; if(!ok) break; make_objdir(objdir); ok &= (n_cfilenames != 0) && (n_hfilenames != 0) ; RESTORE_STATIC_ERRMSG(); // parse it again to write the Makefile ok &= create_makefile((cstr*)cfilenames, n_cfilenames, (cstr*)hfilenames, n_hfilenames); }while(0); // do if(ok) textfile_save("Makefile", obj.outbuf, obj.op - obj.outbuf); else parse_error(); // unload free_filenames(cfilenames, n_cfilenames); free_filenames(hfilenames, n_hfilenames); if(inbuf) free((vstr)inbuf); if(outbuf) free(outbuf); return errcode = !ok; } int usage(int errcode) { #include "common/mc2_multi_str.dat" fprintf( errcode == 0 ? stdout : stderr, "s", usage_str); return errcode; } bool write_header(); bool update_static_errmsg(vstr old_errmsg, cstr new_errmsg); bool find_end_defs(cstr* end_pos); bool write_mc2_defs(cstr end_defs); bool write_make_defs(cstr* cfilenames, int n_cfilenames, cstr* hfilenames, int n_hfilenames); bool write_rules(cstr* cfilenames, int n_cfilenames); bool write_user_targets(); // the main parser entry point bool create_makefile(cstr* cfilenames, int n_cfilenames, cstr* hfilenames, int n_hfilenames) { // Here's our job description: // // add header ## Makefile created with makefile creator 'mc2-multi' // DEFINITIONS: ends at the first target def // ignore the OUTNAME definition // remove Makefile from EXT_ALL and remove empty EXT_ALL // write MAIN = $(MAIN_FILES) // write SRC = \ list of hfiles // write HDR = \ list of cpp files // write OBJ = \ list of .cpp -> .o files // MAIN_FILES = list of .cpp files minus ext // RULES // write all: $(EXT_ALL) $(MAIN) $(HDR) $(SRC) // ignore Makefile target because it depens on the real mc2 app // for each cpp file // $(BINDIR)/mc2-multi: $(OBJDIR)/mc2-multi.o // @echo // @echo "Linking mc2-multi" // $(LINK) $(BINDIR)/mc2-multi $(OBJDIR)/mc2-multi.o $(LDFLAGS) $(LIB) // $(POST) // // $(OBJDIR)/mc2-multi.o: $(SRCDIR)/mc2-multi.cpp $(HDR) // @echo // @echo "Compiling mc2-multi" // $(COMPILE) $(OBJDIR)/mc2-multi.o $(SRCDIR)/mc2-multi.cpp $(CFLAGS) $(INCLUDE) // ADDITIONAL TARGETS: // copy mc2.def removing Makefile target and replacing \n with \t if no ';' on the line. bool ok = true; static char errmsg[256]; SAVE_STATIC_ERRMSG(errmsg); // sets a new pointer // cstr x = obj.outbuf; cstr end_defs; // end of definitons section //////////////////////////////////////////// // header and definitions sections strcpy(errmsg, "writing header"); ok = write_header() && update_static_errmsg(errmsg, "finding end of definitions section") && find_end_defs(&end_defs) && update_static_errmsg(errmsg, "writing user definitions section") && write_mc2_defs(end_defs) ; // printf("%s\n", x); x = obj.op; //////////////////////////////////////////// // file lists ok &= update_static_errmsg(errmsg, "writing file list definitions") && write_make_defs(cfilenames, n_cfilenames, hfilenames, n_hfilenames) ; // printf("%s\n", x); x = obj.op; //////////////////////////////////////////// // main target rules section ok = update_static_errmsg(errmsg, "writing rules section") && write_rules(cfilenames, n_cfilenames) ; // printf("%s\n", x); x = obj.op; //////////////////////////////////////////// // additional user defined targets & rules ok = update_static_errmsg(errmsg, "writing user defined targets & rules") && write_user_targets() ; // printf("%s\n", x); x = obj.op; if(ok) RESTORE_STATIC_ERRMSG(); return obj.state = ok; } ////////////////////////////////////////////////////////////// // misc utils // ignoring system() returns is such a bother int _system(cstr cmd) { return system(cmd); return system(cmd); } int pipe_read(char** filenames, int* pn_lines, cstr cmdstr) { int err = 0; int i = 0; char buf[4096]; FILE* p = popen(cmdstr, "r"); if(!p) return 1; while(1) { fgets(buf, 4096, p); if(feof(p)) break; buf[strlen(buf) - 1] = 0; // remove newline filenames[i++] = strdup(buf); } filenames[i] = 0; // null terminate the list *pn_lines = i; pclose(p); return err; } // returns non-zero if error occurred. bool load_filenames(char** filenames, int max_names, int* pn_files, cstr filespec) { bool ok = true; char cmdstr[256]; sprintf(cmdstr, "ls %s", filespec); ok = ! pipe_read(filenames, pn_files, cmdstr); if(!ok) { *pn_files = 0; return obj.state = false; } int n_files = *pn_files; // remove files not legitimate c or c++ names. int offs = 0; int len; for(int i = 0; i < *pn_files; i++) { if(offs) filenames[i - offs] = filenames[i]; char* p = strrchr(filenames[i], '.'); if(p) { len = strlen(p); if( (len > 4) || (p[len-1] == '~') ) { free(filenames[i]); filenames[i] = 0; offs += 1; } } } n_files -= offs; filenames[n_files] = 0; *pn_files = n_files; return obj.state = true; } void free_filenames(char** filenames, int n_names) { if( ( filenames == 0 ) || ( filenames[0] == 0 ) || ( n_names == 0 ) ) return; for(int i = 0; i < n_names; i++) { vstr s = filenames[i]; if(s) free(s); filenames[i] = 0; } } void textfile_load(cstr* pinbuf, int* pinlen, cstr filename) { *pinlen = 0; *pinbuf = 0; int inlen; vstr(inbuf); FILE* ifile = fopen(filename, "r"); if(!ifile) return; fseek(ifile, 0, SEEK_END); inlen = ftell(ifile); fseek(ifile, 0, SEEK_SET); inbuf = (vstr)malloc(inlen+1); if(!inbuf) { fclose(ifile); return; } fread(inbuf, 1, inlen, ifile); fclose(ifile); *pinbuf = inbuf; *pinlen = inlen; } int textfile_save(cstr filename, vstr text, int textlen) { FILE* fp = fopen(filename, "w"); if(!fp) return 1; fwrite(text, 1, textlen, fp); fclose(fp); return 0; } /////////////////////////////////////// // parser funcs bool write_header() { bool ok = true; write_str( "## Makefile created with makefile creator 'mc2-multi' (parser demo)\n" "\n" ) ; return obj.state = ok; } bool update_static_errmsg(vstr old_errmsg, cstr new_errmsg) { bool ok = true; // update_errpos(); NOT DEFINED IN PARSER - WOOPS! strcpy(old_errmsg, new_errmsg); UPDATE_STATIC_ERRMSG(); return obj.state = ok; } bool find_end_defs(cstr* end_pos) { bool ok = true; // don't save result SAVE_PTRS(); // find end of first target definition while(not_eoi()) { ok = skip_to(":"); if(is_str(":=")) skip_1(); else break; } if(!ok) return obj.state = false; // back up to beginning of first target definition while( !is_soi() && not_newline() && skip_n(-1) ) ; // loop ok = not_eoi() && skip_1() && (*end_pos = obj.ip) // not zero ; // undo RESTORE_PTRS(); return obj.state = ok; } static cstr* create_mainfilenames(cstr* cfilenames, int n_cfilenames) { // the array as well as the names will need to be freed. if((cfilenames == 0) || (n_cfilenames == 0)) return 0; int n_mainfilenames = n_cfilenames; vstr* mainfilenames = (vstr*)malloc((n_mainfilenames + 1) * sizeof(vstr)); for(int i = 0; i < n_mainfilenames; i++) { // extract basename minus ext cstr src = cfilenames[i]; cstr cp = strrchr(src, '/'); if(cp) cp++; vstr dest = strdup(cp); vstr vp = strrchr(dest, '.'); if(vp) *vp = 0; mainfilenames[i] = dest; } mainfilenames[n_mainfilenames] = 0; // terminate return (cstr*)mainfilenames; } void delete_mainfilenames(cstr* mainfilenames, int n_mainfilenames) { for(int i = 0; i < n_mainfilenames; i++) free((void*)mainfilenames[i]); free(mainfilenames); } bool write_mc2_defs(cstr end_defs) { bool ok = true; write_str( "################################################################\n" "## User Defined Vars\n" "\n" ); // copy literally but remove EXT_ALL = Makefile, or try // just removing Makefile because EXT_ALL may still // be useful. This simple parse does not check to see if // the name is in a comment or any other harmless context. while(not_eoi() && (obj.ip != end_defs)) { ok = skip_str("Makefile") || copy_1() ; } return obj.state = ok; } static bool write_end_list() { return obj.state = write_str(" #############\n\n"); } static bool write_filelist(cstr* names, int n_names, cstr dir, cstr ext) { bool ok = true; char buf[256]; const char* p; for(int i = 0; not_eoi() && ok && i < n_names; i++) { // we need only the file name and optional extension. cstr name = names[i]; if(ext == 0) ext = ""; p = strrchr(name, '/'); if(p) name = p + 1; sprintf(buf, " %s/%s%s \\\n", dir, name, ext); ok = write_str(buf) ; } return obj.state = ok; } bool write_make_defs(cstr* cfilenames, int n_cfilenames, cstr* hfilenames, int n_hfilenames) { bool ok = true; ///////////////////////////////////////// // setup for writing all file names // names for MAIN_FILES are derived from cfiles and names for OBJ are derived // from MAIN_FILES here. int n_mainfilenames = n_cfilenames; cstr* mainfilenames = create_mainfilenames(cfilenames, n_mainfilenames); ok = write_str( "################################################################\n" "## File Lists Section\n" "\n" ) && write_str("MAIN = $(MAIN_FILES)\n\n") && write_str("SRC = \\\n") && write_filelist(cfilenames, n_cfilenames, "$(SRCDIR)", 0) && write_end_list() && write_str("HDR = \\\n") && write_filelist(hfilenames, n_hfilenames, "$(SRCDIR)", 0) && write_end_list() && write_str("OBJ = \\\n") && write_filelist(mainfilenames, n_mainfilenames, "$(OBJDIR)", ".o") && write_end_list() && write_str("MAIN_FILES = \\\n") && write_filelist(mainfilenames, n_mainfilenames, "$(BINDIR)", 0) && write_end_list() ; delete_mainfilenames(mainfilenames, n_mainfilenames); return obj.state = ok; } bool write_rules(cstr* cfilenames, int n_cfilenames) { bool ok = true; cstr binname; cstr cname; char buf[256]; int n_mainfilenames = n_cfilenames; cstr* mainfilenames = create_mainfilenames(cfilenames, n_mainfilenames); ok = write_str ( "################################################################\n" "## Rules Section\n" "\n" "all: $(EXT_ALL) $(MAIN) $(HDR) $(SRC)\n" "\n" ) ; for(int i = 0; not_eoi() && ok && (i < n_mainfilenames); i++) { binname = mainfilenames[i]; cname = cfilenames[i]; cstr p = strrchr(cname, '/'); if(p) cname = p + 1; // write link commands sprintf(buf, "$(BINDIR)/%s: $(OBJDIR)/%s.o\n" "\t@echo\n" "\t@echo \"Linking %s\"\n" "\t$(LINK) $(BINDIR)/%s $(OBJDIR)/%s.o $(LDFLAGS) $(LIB)\n" "\t$(POST)\n\n", binname, binname, binname, binname, binname); ok &= write_str(buf); // write compile commands sprintf(buf, "$(OBJDIR)/%s.o: $(SRCDIR)/%s $(HDR)\n" "\t@echo\n" "\t@echo \"Compiling %s\"\n" "\t$(COMPILE) $(OBJDIR)/%s.o $(SRCDIR)/%s $(CFLAGS) $(INCLUDE)\n\n", binname, cname, binname, binname, cname ); ok &= write_str(buf); } delete_mainfilenames(mainfilenames, n_mainfilenames); return obj.state = ok; } bool is_target() { // the line has a colon but not followd by "=" bool ok = true; SAVE_PTRS(); // find colon before a newline, not followed by an '=' sign while(not_eoi() && not_str("\n") && not_str(":")) skip_1(); ok = skip_str(":") && not_str("=") ; RESTORE_PTRS(); return obj.state = ok; } bool is_empty_line() { bool ok = true; SAVE_PTRS(); while(not_eoi() && is_blank()) skip_1(); ok = is_newline(); RESTORE_PTRS(); return obj.state = ok; } bool skip_blank() { return obj.state = is_blank() && skip_1(); } bool indent_line() { bool ok = true; ok = do_while(skip_blank) && write_str("\t"); return obj.state = ok; } bool write_user_targets() { bool ok = true; // this is the easy part. copy targets (lines with a colon and no = sign // as-is otherwise replace \n and whitespaces with a \n\t. while(not_eoi() && ok) { ok = ( ( is_target() || ( is_empty_line() && do_while(skip_blank) ) ) || indent_line() ) && copy_past("\n") ; } return obj.state = ok; } bool read_value(vstr buf, cstr name) { bool ok = true; SAVE_PTRS(); skip_past(name); skip_past("="); while(skip_ws()) ; //loop ok &= do_until(is_ws, copy_1); strcpy(buf, obj.outbuf); RESTORE_PTRS(); return obj.state = ok; } void make_objdir(cstr path) { char cmd[256]; sprintf(cmd, "mkdir -p %s 2>/dev/null", path); _system(cmd); }
file: src/common/mc2_multi_str.dat
purpose: source file
Code:
// This file was created with txt2str const char* usage_str = "Usage: mc2-to-makefile\n" "\n" " Use this in the folder with your mc2.def file to decode an mc2.def \n" " (type MULTI) to create a makefile for all parser projects in the \n" " ./src directory.\n" "\n" " This is not the same as the full mc2 application. It only parses\n" " type MULTI definitions. That is, each file in the SRC dir must\n" " be a main() file. If you need additional files they can be \n" " #included but this simple mc2 parser doesn't do libs or create\n" " additional utilities.\n" "\n" " The main purpose for this app is to make Makefiles for the parser\n" " tests but if you don't want the real mc2 app, this will at least \n" " generate Makefiles.\n" ;
file: mc2.def
purpose: Makefile template and Demo test
Code:
# mc2.def template created with Makefile Creator 'mc2' # sandbox path and other new variables PREFIX = $(HOME)/usr BUILDDIR := $(PWD) OUTNAME = MULTI EXT_ALL = Makefile SRCDIR = src OBJDIR = o BINDIR = . # what COMPILE should do COMPILE = g++ -c -o # COMPILE <output_file> ... CFLAGS = -Wall -Wno-comment -g3 # debug # CFLAGS = -Wall -O2 # optimized INCLUDE = -I $(SRCDIR) -I. -I /usr/include # what LINK should do LINK = g++ -o # LINK <output_file> ... LDFLAGS = LIB = -L/usr/lib -L$(PREFIX)/lib Makefile: mc2.def @mc2 -u @sleep 2 semiclean: $(EXT_SEMICLEAN) @rm -f $(OBJ) @rm -f *~ */*~ */*/*~ */*/*/*~ @rm -f *.kdevelop.filelist *.kdevelop.pcs *.kdevses Doxyfile strip: @strip $(MAIN_FILES) @make semiclean clean: $(EXT_CLEAN) @rm -f $(MAIN_FILES) @rm -f $(OBJ) @rm -f *.kdevelop.pcs *.kdevses @rm -f *~ */*~ */*/*~ */*/*/*~ tmp.mak force: # used to force execution
file: Makefile
purpose: Builds all the files
Code:
## Makefile created with makefile creator 'mc2-multi' (parser demo) ################################################################ ## User Defined Vars # mc2.def template created with Creator 'mc2' # sandbox path and other new variables PREFIX = $(HOME)/usr BUILDDIR := $(PWD) OUTNAME = MULTI EXT_ALL = SRCDIR = src OBJDIR = o BINDIR = . # what COMPILE should do COMPILE = g++ -c -o # COMPILE <output_file> ... CFLAGS = -Wall -Wno-comment -g3 # debug # CFLAGS = -Wall -O2 # optimized INCLUDE = -I $(SRCDIR) -I. -I /usr/include # what LINK should do LINK = g++ -o # LINK <output_file> ... LDFLAGS = LIB = -L/usr/lib -L$(PREFIX)/lib ################################################################ ## File Lists Section MAIN = $(MAIN_FILES) SRC = \ $(SRCDIR)/mc2-multi.cpp \ $(SRCDIR)/super-dosex.cpp \ $(SRCDIR)/to-camel.cpp \ ############# HDR = \ $(SRCDIR)/parser.h \ ############# OBJ = \ $(OBJDIR)/mc2-multi.o \ $(OBJDIR)/super-dosex.o \ $(OBJDIR)/to-camel.o \ ############# MAIN_FILES = \ $(BINDIR)/mc2-multi \ $(BINDIR)/super-dosex \ $(BINDIR)/to-camel \ ############# ################################################################ ## Rules Section all: $(EXT_ALL) $(MAIN) $(HDR) $(SRC) $(BINDIR)/mc2-multi: $(OBJDIR)/mc2-multi.o @echo @echo "Linking mc2-multi" $(LINK) $(BINDIR)/mc2-multi $(OBJDIR)/mc2-multi.o $(LDFLAGS) $(LIB) $(POST) $(OBJDIR)/mc2-multi.o: $(SRCDIR)/mc2-multi.cpp $(HDR) @echo @echo "Compiling mc2-multi" $(COMPILE) $(OBJDIR)/mc2-multi.o $(SRCDIR)/mc2-multi.cpp $(CFLAGS) $(INCLUDE) $(BINDIR)/super-dosex: $(OBJDIR)/super-dosex.o @echo @echo "Linking super-dosex" $(LINK) $(BINDIR)/super-dosex $(OBJDIR)/super-dosex.o $(LDFLAGS) $(LIB) $(POST) $(OBJDIR)/super-dosex.o: $(SRCDIR)/super-dosex.cpp $(HDR) @echo @echo "Compiling super-dosex" $(COMPILE) $(OBJDIR)/super-dosex.o $(SRCDIR)/super-dosex.cpp $(CFLAGS) $(INCLUDE) $(BINDIR)/to-camel: $(OBJDIR)/to-camel.o @echo @echo "Linking to-camel" $(LINK) $(BINDIR)/to-camel $(OBJDIR)/to-camel.o $(LDFLAGS) $(LIB) $(POST) $(OBJDIR)/to-camel.o: $(SRCDIR)/to-camel.cpp $(HDR) @echo @echo "Compiling to-camel" $(COMPILE) $(OBJDIR)/to-camel.o $(SRCDIR)/to-camel.cpp $(CFLAGS) $(INCLUDE) Makefile: mc2.def @mc2 -u @sleep 2 semiclean: $(EXT_SEMICLEAN) @rm -f $(OBJ) @rm -f *~ */*~ */*/*~ */*/*/*~ @rm -f *.kdevelop.filelist *.kdevelop.pcs *.kdevses Doxyfile strip: @strip $(MAIN_FILES) @make semiclean clean: $(EXT_CLEAN) @rm -f $(MAIN_FILES) @rm -f $(OBJ) @rm -f *.kdevelop.pcs *.kdevses @rm -f *~ */*~ */*/*~ */*/*/*~ tmp.mak force: # used to force execution
Total Comments 0