CCMD = $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
# Linker
-LD ?= ld
-LDFLAGS ?=
-LDLIBS ?= -lunistring
-LDCMD = $(LD) $(LDFLAGS) $(LDLIBS) -o $@ $^
+LD ?= ld
+LDFLAGS ?=
+LDLIBS ?= -lunistring
+apcLIBS ?=
+apc-dLIBS ?=
+LDCMD = $(LD) $(LDFLAGS) $(LDLIBS) $($1LIBS) -o $@ $^
# APC is built from Ragel, Bison and C source code only.
ySRC := $(shell find ./src -type f -name '*.y')
hGEN := $(ySRC:%.y=%.tab.h)
cGEN := $(strip $(ySRC:%.y=%.tab.c) $(rlSRC:%.rl=%.c))
-# Functions
-cGENDEP = $(if $(wildcard $1),$(subst src/,,$(filter-out $1 \ %:,$(shell $(CC) -MM -MG $1))),$(info <$1>))
-ldFLAGS = $(strip $(LDFLAGS) $(LDLIBS) $(VA_ARGS))
+# Deps generation function
+cGENDEP = $(if $(wildcard $1),$(subst $(dir $1),,$(filter-out $1 \ %:,$(shell $(CC) -MM -MG $1))),$(info <$1>))
-# Rules
-apcSRC := $(patsubst %.c,%.o,$(cSRC) $(cGEN))
-apc-dSRC := $(patsubst %.o,%-d.o,$(apcSRC))
+# Driver sources
+DRIVERS := apc testapc
+$(foreach drv,$(DRIVERS),\
+$(eval $(drv)SRC := $(patsubst %.c,%.o,$(filter-out $(patsubst %,src/%.c,$(filter-out $(drv),$(DRIVERS))),$(cSRC) $(cGEN))))\
+$(eval $(drv)-dSRC := $(patsubst %.o,%-d.o,$($(drv)SRC))))
+# Rules
.SECONDEXPANSION:
-apc-d apc: $$($$@SRC) | $(hGEN)
- $(strip $(LDCMD))
+$(DRIVERS:%=%-d) $(DRIVERS): $$($$@SRC) | $(hGEN)
+ $(strip $(call LDCMD,$@))
%-d.o: CFLAGS+= -Og -ggdb
%.o %-d.o: %.c $$(call cGENDEP,$$(dir $$@)%.c)
$(strip $(RLCMD))
clean: $(wildcard $(cGEN) $(hGEN) $(apcSRC) $(apc-dSRC))
- rm $^
+ $(if $^,rm $^)
--- /dev/null
+/*!@file
+ \brief APC main driver
+ \details The driver assumes the existence of a bison-generated parser,
+ referenced by the external function 'yyparse'.
+ It also assumes the existence of a lexer which must be initialized
+ before parsing, referenced by the external function 'lexer_init'
+ which assumes standard error handling.
+ All input arguments are made available through the exposed (that is,
+ non-static) array of character pointers 'cargs', which point
+ to the non-duplicated strings in 'argv' directly from the system.
+ \author Jordan Lavatai
+ \date Aug 2016
+ ----------------------------------------------------------------------------*/
+/* Standard */
+#include <stdio.h> //print
+#include <errno.h> //errors
+#include <string.h> //strndupa
+/* Posix */
+#include <stdlib.h> //exit
+#include <unistd.h> //getopt
+/* Internal */
+#include <apc/parser.tab.h> //bison
+
+const char* cargs['Z'] = {0};
+
+int main(int, char*[]);
+
+extern //bison
+int yyparse(void);
+extern //lexer.c
+int lexer_init(void);
+
+extern //apc/parser.tab.c
+YYSTYPE yylval;
+extern //lexer.c
+int lexer(void);
+
+/* Main entry from terminal
+ parses the command line and kicks off recursive scanning
+*/
+int main
+( int argc,
+ char* argv[]
+)
+#define $($)#$ //stringifier
+#define MAXSTR 255
+#define MAXERR "-%c allows at most " $(MAXSTR) " input characters\n", opt
+#define OPTS "d:o:h-"
+#define USAGE "Usage %s [-d dir_root][-o output_file][-h]\n", argv[0]
+#define USAGE_LONG \
+ "\tOptions:\n" \
+ "\t\t-d\tRoot directory to parse from \t[./]\n" \
+ "\t\t-o\tOutput filename \t\t[a.asspak]\n" \
+ "\t\t-h\tPrint this help\n"
+#define DONE -1
+{ int opt;
+
+ getopt:
+ switch (opt = getopt(argc, argv, OPTS))
+ { case DONE:
+ break;
+ case 'd' :
+ case 'o' :
+ if (strnlen(optarg, MAXSTR) != MAXSTR)
+ { cargs[opt] = optarg;
+ goto getopt;
+ }
+ fprintf(stderr, MAXERR);
+ default :
+ fprintf(stderr, USAGE);
+ exit(EXIT_FAILURE);
+ case 'h' :
+ printf(USAGE);
+ printf(USAGE_LONG);
+ exit(EXIT_SUCCESS);
+ }
+ if (lexer_init())
+ { perror("lexer");
+ exit(EXIT_FAILURE);
+ }
+ yyparse();
+ exit(EXIT_SUCCESS);
+}
+
--- /dev/null
+/*!@file
+ \brief APC test driver
+ \details This driver does what APC does, but in staggered stages with
+ additional debugging information
+ \author Jordan Lavatai
+ \date Aug 2016
+ ----------------------------------------------------------------------------*/
+/* Standard */
+#include <stdio.h> //print
+#include <errno.h> //errors
+#include <string.h> //strnlen
+#include <setjmp.h> //non-local jumps
+/* Posix */
+#include <stdlib.h> //exit
+#include <unistd.h> //getopt
+/* Internal */
+#include <apc/parser.tab.h> //bison
+#include <apc/ir.h> //ir
+
+/* Import apc.c but redefine its primary symbols for jumping */
+#define main apc_main
+#define yyparse testapc_yyparse
+#include <bin/tools/apc.c>
+#undef yyparse
+#undef main
+
+int main(int, char*[]);
+int testapc_yyparse(void);
+int test_yyparse(void);
+
+extern //bison
+int yyparse(void);
+extern //lexer.c
+int lexer_init(void);
+extern //scanner.c
+int scanner_init(void);
+extern //apc.c
+const char* cargs['Z'];
+
+extern //apc/parser.tab.c
+YYSTYPE yylval;
+extern //lexer.c
+int lexer(void);
+
+static
+jmp_buf testapc_jump;
+
+/* Ansi Term Colors */
+#define RED "\x1b[31m"
+#define GREEN "\x1b[32m"
+#define YELLOW "\x1b[33m"
+#define BLUE "\x1b[34m"
+#define MAGENTA "\x1b[35m"
+#define CYAN "\x1b[36m"
+#define CLRC "\x1b[0m" //clear current color
+
+/* Main entry from terminal
+ parses debugging options for testing apc, and calls apc_main
+*/
+int main
+( int argc,
+ char* argv[]
+)
+{ setjmp(testapc_jump);
+ apc_main(argc, argv);
+ printf(GREEN "PASS" CLRC "\n");
+ exit(EXIT_SUCCESS);
+}
+
+#define MAX_TOK 1024
+char tok_lval[MAX_TOK];
+
+/* yyparse intercept
+ tests yyparse internally, then resets the scanner and runs bison's 'yyparse'
+ implementation after validating it with 'test_yyparse'.
+*/
+int testapc_yyparse
+#ifndef YYABORT
+#define YYABORT 1
+#endif
+()
+{ static char bPassedTest = 'f';
+ if (bPassedTest == 'f')
+ { if (test_yyparse())
+ { printf("Parse test aborted\n");
+ return YYABORT;
+ }
+ bPassedTest = 't';
+ longjmp(testapc_jump,0);
+ }
+ return yyparse();
+}
+
+/* test_yyparse
+ runs 'lexer' 'PASSES' times, or until finished
+*/
+int test_yyparse
+#define PASSES 1000
+()
+{ int i, tok;
+ static char* tok_string;
+ static char tok_pattern[] = "[" RED " %9s " CLRC "][" CYAN " %-12i " CLRC "]";
+ for (i = 0; i < PASSES; i++)
+ { switch (tok = lexer())
+ #define TOFFS 9
+ #define LOFFS 27
+ #define $($)#$
+ #define TOK_CASE(T,C) \
+ case T: \
+ tok_string = $(T); \
+ tok_pattern[LOFFS] = C; \
+ break
+ { TOK_CASE(STR,'s');
+ TOK_CASE(NAME,'s');
+ TOK_CASE(REF,'x');
+ TOK_CASE(FPTR,'x');
+ TOK_CASE(NUM,'i');
+ TOK_CASE(SS,'i');
+ TOK_CASE(SSD,'i');
+ TOK_CASE(CLOPEN,'i');
+ TOK_CASE(CLCLOSE,'i');
+ default:
+ tok_string = 0;
+ tok_pattern[LOFFS] = 'i';
+ break;
+ case 0:
+ goto done;
+ }
+ if (tok_string == NULL)
+ { tok_pattern[TOFFS] = 'i';
+ printf(tok_pattern, tok, yylval.val);
+ }
+ else
+ { tok_pattern[TOFFS] = 's';
+ printf(tok_pattern, tok_string, yylval.val);
+ }
+ if (i % 4 == 0 || yylval.val == 0)
+ printf(";\n");
+ }
+ done:
+ printf(";\n" GREEN "Done" CLRC ".\n");
+ return 0;
+ error:
+ printf(";\n" RED "FAILED" CLRC ".\n");
+ return -1;
+}