scc

Simple C99 Compiler
Log | Files | Refs | README | LICENSE

commit b8e51de07999a695f7e66bcfa1b270e61896dd5d
parent 34f9e3e9ce1c23c5d0c4d8bf5a9728495604d629
Author: Roberto E. Vargas Caballero <k0ga@shike2.com>
Date:   Tue,  6 Oct 2015 09:13:15 +0200

Merge remote-tracking branch 'origin/master'

Diffstat:
cc1/Makefile | 3+++
cc1/cc1.h | 12+++++++++---
cc1/cpp.c | 144++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
cc1/expr.c | 41+++++++++++++++++++++++++++++++++--------
cc1/fold.c | 5+++++
cc1/lex.c | 59++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
cc1/main.c | 22++++++++++++++++------
cc1/symbol.c | 98+++++++++----------------------------------------------------------------------
cc1/tests/test031.c | 33+++++++++++++++++++++++++++++++++
cc1/tests/test032.c | 25+++++++++++++++++++++++++
cc1/tests/test033.c | 17+++++++++++++++++
cc1/tests/update.sh | 24+++++++++++++++++++++---
inc/cc.h | 7+++++--
lib/Makefile | 2+-
lib/debug.c | 20++++++++++++++++++++
15 files changed, 381 insertions(+), 131 deletions(-)

diff --git a/cc1/Makefile b/cc1/Makefile @@ -12,6 +12,9 @@ $(OBJS) : cc1.h ../inc/cc.h ../inc/sizes.h arch/$(ARCH)/arch.h cc1: $(OBJS) ../lib/libcc.a $(CC) $(LDFLAGS) $(OBJS) ../lib/libcc.a -o $@ +cpp: cc1 + ln -f cc1 cpp + test: cd tests && ./chktest.sh diff --git a/cc1/cc1.h b/cc1/cc1.h @@ -46,6 +46,10 @@ struct limits { } min; }; +struct keyword { + char *str; + unsigned char token, value; +}; struct type { unsigned char op; /* type builder operator */ @@ -345,9 +349,9 @@ extern Symbol *nextsym(Symbol *sym, int ns); extern Symbol *install(int ns, Symbol *sym); extern Symbol *newsym(int ns); extern void pushctx(void), popctx(void); -extern void ikeywords(void); -extern void delmacro(Symbol *sym); +extern void killsym(Symbol *sym); extern Symbol *newlabel(void); +extern void keywords(struct keyword *key, int ns); /* stmt.c */ extern void compound(Symbol *lbreak, Symbol *lcont, Caselist *lswitch); @@ -396,6 +400,8 @@ extern void icpp(void); extern bool cpp(void); extern bool expand(char *begin, Symbol *sym); extern void incdir(char *dir); +extern void outcpp(void); +extern Symbol *defmacro(char *s); /* * Definition of global variables @@ -407,7 +413,7 @@ extern unsigned short yylen; extern int cppoff, disexpand; extern unsigned cppctx; extern Input *input; -extern int lexmode, namespace; +extern int lexmode, namespace, onlycpp; extern unsigned curctx; extern Symbol *curfun, *zero, *one; diff --git a/cc1/cpp.c b/cc1/cpp.c @@ -25,10 +25,23 @@ static char **dirinclude; unsigned cppctx; int disexpand; -static Symbol * +Symbol * defmacro(char *s) { - return install(NS_CPP, lookup(NS_CPP, s)); + char *p, *q; + Symbol *sym; + size_t len; + + if ((p = strchr(s, '=')) != NULL) { + *p++='\0'; + len = strlen(p); + q = xmalloc(len+4); + sprintf(q, "-1#%s", p); + p = q; + } + sym = install(NS_CPP, lookup(NS_CPP, s)); + sym->u.s = p; + return sym; } void @@ -37,12 +50,27 @@ icpp(void) static char sdate[17], stime[14]; struct tm *tm; time_t t; - char **bp, *list[] = { + static char **bp, *list[] = { "__STDC__", "__STDC_HOSTED__", "__SCC__", NULL }; + static struct keyword keys[] = { + {"define", DEFINE, DEFINE}, + {"include", INCLUDE, INCLUDE}, + {"line", LINE, LINE}, + {"ifdef", IFDEF, IFDEF}, + {"if", IF, IF}, + {"elif", ELIF, ELIF}, + {"else", ELSE, ELSE}, + {"ifndef", IFNDEF, IFNDEF}, + {"endif", ENDIF, ENDIF}, + {"undef", UNDEF, UNDEF}, + {"pragma", PRAGMA, PRAGMA}, + {"error", ERROR, ERROR}, + {NULL, 0, 0} + }; t = time(NULL); tm = localtime(&t); @@ -57,6 +85,7 @@ icpp(void) for (bp = list; *bp; ++bp) defmacro(*bp)->u.s = "-1#1"; + keywords(keys, NS_CPPCLAUSES); } static void @@ -145,21 +174,39 @@ parsepars(char *buffer, char **listp, int nargs) return 1; } +/* FIXME: characters in the definition break the macro definition */ static size_t copymacro(char *buffer, char *s, size_t bufsiz, char *arglist[]) { - char prevc, c, *arg, *bp = buffer; + char prevc, c, *p, *arg, *bp = buffer; + size_t size; for (prevc = '\0'; c = *s; prevc = c, ++s) { if (c != '@') { - if (c == '#') + switch (c) { + case '$': + while (bp[-1] == ' ') + --bp, ++bufsiz; + while (s[1] == ' ') + ++s; + case '#': + continue; + case '\"': + for (p = s; *++s != '"'; ) + /* nothing */; + size = s - p + 1; + if (size > bufsiz) + goto expansion_too_long; + memcpy(bp, p, size); + bufsiz -= size; + bp += size; continue; + // case '\''; + } if (bufsiz-- == 0) goto expansion_too_long; *bp++ = c; } else { - size_t size; - if (prevc == '#') bufsiz -= 2; arg = arglist[atoi(++s)]; @@ -194,7 +241,9 @@ expand(char *begin, Symbol *sym) char *arglist[NR_MACROARG], arguments[INPUTSIZ], buffer[BUFSIZE]; macroname = sym->name; - if (!(sym->flags & ISDECLARED)) { + if ((sym->flags & ISDECLARED) == 0) { + if (namespace == NS_CPP && !strcmp(sym->name, "defined")) + return 0; /* we found a 'defined in an #if */ /* * This case happens in #if were macro not defined must * be expanded to 0 @@ -216,12 +265,12 @@ expand(char *begin, Symbol *sym) if (!parsepars(arguments, arglist, atoi(s))) return 0; for (n = 0; n < atoi(s); ++n) - DBG("MACRO par%d:%s\n", n, arglist[n]); + DBG("MACRO par%d:%s", n, arglist[n]); elen = copymacro(buffer, s+3, INPUTSIZ-1, arglist); substitute: - DBG("MACRO '%s' expanded to :'%s'\n", macroname, buffer); + DBG("MACRO '%s' expanded to :'%s'", macroname, buffer); rlen = strlen(input->p); /* rigth length */ llen = begin - input->line; /* left length */ ilen = input->p - begin; /* invocation length */ @@ -241,7 +290,7 @@ substitute: input->p = input->begin = begin; if (!(sym->flags & ISDECLARED)) - delmacro(sym); + killsym(sym); return 1; } @@ -285,6 +334,11 @@ getdefs(Symbol *args[NR_MACROARG], int nargs, char *bp, size_t bufsiz) size_t len; int prevc = 0, ispar; + if (yytoken == '$') { + cpperror("'##' cannot appear at either end of a macro expansion"); + return 0; + } + for (;;) { ispar = 0; if (yytoken == IDEN && nargs >= 0) { @@ -308,10 +362,15 @@ getdefs(Symbol *args[NR_MACROARG], int nargs, char *bp, size_t bufsiz) cpperror("too long macro"); return 0; } - memcpy(bp, yytext, len); - bp += len; - bufsiz -= len; - if ((prevc = yytoken) != '#') + if (yytoken == '$') { + *bp++ = '$'; + --bufsiz; + } else { + memcpy(bp, yytext, len); + bp += len; + bufsiz -= len; + } + if ((prevc = yytoken) != '#') *bp++ = ' '; next(); } @@ -342,7 +401,7 @@ define(void) free(sym->u.s); } else { sym = install(NS_CPP, sym); - sym->flags |= ISDECLARED; + sym->flags |= ISDECLARED|ISSTRING; } namespace = NS_IDEN; /* Avoid polution in NS_CPP */ @@ -353,11 +412,11 @@ define(void) if (!getdefs(args, n, buff+3, LINESIZ-3)) goto delete; sym->u.s = xstrdup(buff); - DBG("MACRO '%s' defined as '%s'\n", sym->name, buff); + DBG("MACRO '%s' defined as '%s'", sym->name, buff); return; delete: - delmacro(sym); + killsym(sym); } void @@ -531,7 +590,7 @@ ifclause(int negate, int isifdef) next(); status = (sym->flags & ISDECLARED) != 0; if (!status) - delmacro(sym); + killsym(sym); } else { /* TODO: catch recovery here */ if ((expr = iconstexpr()) == NULL) { @@ -589,6 +648,7 @@ static void elif(void) { elseclause(); + --cppctx; cppif(); } @@ -614,7 +674,7 @@ undef(void) error("no macro name given in #undef directive"); return; } - delmacro(yylval.sym); + killsym(yylval.sym); next(); } @@ -670,3 +730,47 @@ cpp(void) return 1; } + +void +outcpp(void) +{ + char c, *s, *t; + + for (next(); yytoken != EOFTOK; next()) { + if (yytoken != CONSTANT || *yytext != '"') { + printf("%s ", yytext); + continue; + } + for (s = yylval.sym->u.s; c = *s; ++s) { + switch (c) { + case '\n': + t = "\\n"; + goto print_str; + case '\v': + t = "\\v"; + goto print_str; + case '\b': + t = "\\b"; + goto print_str; + case '\t': + t = "\\t"; + goto print_str; + case '\a': + t = "\\a"; + print_str: + fputs(t, stdout); + break; + case '\\': + putchar('\\'); + default: + if (!isprint(c)) + printf("\\x%x", c); + else + putchar(c); + break; + } + } + } + putchar('\n'); +} + diff --git a/cc1/expr.c b/cc1/expr.c @@ -496,6 +496,32 @@ negation(char op, Node *np) } } +static Symbol * +notdefined(Symbol *sym) +{ + int isdef; + + if (namespace == NS_CPP && !strcmp(sym->name, "defined")) { + disexpand = 1; + next(); + expect('('); + sym = yylval.sym; + expect(IDEN); + expect(')'); + + isdef = (sym->flags & ISDECLARED) != 0; + sym = newsym(NS_IDEN); + sym->type = inttype; + sym->flags |= ISCONSTANT; + sym->u.i = isdef; + disexpand = 0; + return sym; + } + errorp("'%s' undeclared", yytext); + sym->type = inttype; + return install(sym->ns, yylval.sym); +} + /************************************************************* * grammar functions * *************************************************************/ @@ -505,17 +531,18 @@ primary(void) Node *np; Symbol *sym; + sym = yylval.sym; switch (yytoken) { case CONSTANT: - np = constnode(yylval.sym); + np = constnode(sym); next(); break; case IDEN: - sym = yylval.sym; - if ((sym->flags & ISDECLARED) == 0) { - errorp("'%s' undeclared", yytext); - sym->type = inttype; - install(sym->ns, yylval.sym); + if ((sym->flags & ISDECLARED) == 0) + sym = notdefined(sym); + if (sym->flags & ISCONSTANT) { + np = constnode(sym); + break; } sym->flags |= ISUSED; np = varnode(sym); @@ -566,8 +593,6 @@ arguments(Node *np) arg = convert(arg, doubletype, 1); break; } - if (arg->type->op == INT) - arg = promote(arg); par = node(OPAR, arg->type, par, arg); continue; } diff --git a/cc1/fold.c b/cc1/fold.c @@ -480,6 +480,11 @@ change_to_comma: return NULL; } +/* + * TODO: transform simplify in a recursivity + * function, because we are losing optimization + * chances + */ Node * simplify(int op, Type *tp, Node *lp, Node *rp) { diff --git a/cc1/lex.c b/cc1/lex.c @@ -39,6 +39,44 @@ allocinput(char *fname, FILE *fp) void ilex(char *fname) { + static struct keyword keys[] = { + {"auto", SCLASS, AUTO}, + {"break", BREAK, BREAK}, + {"_Bool", TYPE, BOOL}, + {"case", CASE, CASE}, + {"char", TYPE, CHAR}, + {"const", TQUALIFIER, CONST}, + {"continue", CONTINUE, CONTINUE}, + {"default", DEFAULT, DEFAULT}, + {"do", DO, DO}, + {"double", TYPE, DOUBLE}, + {"else", ELSE, ELSE}, + {"enum", TYPE, ENUM}, + {"extern", SCLASS, EXTERN}, + {"float", TYPE, FLOAT}, + {"for", FOR, FOR}, + {"goto", GOTO, GOTO}, + {"if", IF, IF}, + {"inline", TQUALIFIER, INLINE}, + {"int", TYPE, INT}, + {"long", TYPE, LONG}, + {"register", SCLASS, REGISTER}, + {"restrict", TQUALIFIER, RESTRICT}, + {"return", RETURN, RETURN}, + {"short", TYPE, SHORT}, + {"signed", TYPE, SIGNED}, + {"sizeof", SIZEOF, SIZEOF}, + {"static", SCLASS, STATIC}, + {"struct", TYPE, STRUCT}, + {"switch", SWITCH, SWITCH}, + {"typedef", SCLASS, TYPEDEF}, + {"union", TYPE, UNION}, + {"unsigned", TYPE, UNSIGNED}, + {"void", TYPE, VOID}, + {"volatile", TQUALIFIER, VOLATILE}, + {"while", WHILE, WHILE}, + {NULL, 0, 0}, + }; FILE *fp; if (!fname) { @@ -52,6 +90,7 @@ ilex(char *fname) } allocinput(fname, fp); *input->begin = '\0'; + keywords(keys, NS_KEYWORD); } bool @@ -176,6 +215,10 @@ repeat: bool moreinput(void) { + static char file[FILENAME_MAX]; + static unsigned nline; + char *s; + repeat: if (!readline()) return 0; @@ -187,6 +230,19 @@ repeat: goto repeat; } + if (onlycpp) { + putchar('\n'); + if (strcmp(file, input->fname)) { + strcpy(file, input->fname); + s = "#line %u %s\n"; + } else if (nline+1 != input->nline) { + s = "#line %u\n"; + } else { + s = ""; + } + nline = input->nline; + printf(s, nline, file); + } input->begin = input->p; return 1; } @@ -525,6 +581,7 @@ operator(void) case '*': t = follow('=', MUL_EQ, '*'); break; case '/': t = follow('=', DIV_EQ, '/'); break; case '!': t = follow('=', NE, '!'); break; + case '#': t = follow('#', '$', '#'); break; case '-': t = minus(); break; case '+': t = plus(); break; case '.': t = dot(); break; @@ -576,7 +633,7 @@ next(void) yytoken = operator(); exit: - DBG("TOKEN %s\n", yytext); + DBG("TOKEN %s", yytext); return yytoken; } diff --git a/cc1/main.c b/cc1/main.c @@ -12,8 +12,8 @@ int warnings; jmp_buf recover; -static char *output; -static int onlycpp; +static char *output, *arg0; +int onlycpp; static void clean(void) @@ -27,7 +27,9 @@ clean(void) static void usage(void) { - fputs("usage: cc1 [-w] [-o output] [input]\n", stderr); + fprintf(stderr, + "usage: %s [-E] [-Dmacro[=value]] [-Idir] [-w] [-d] [-o output] [input]\n", + arg0); exit(1); } @@ -38,6 +40,10 @@ main(int argc, char *argv[]) atexit(clean); + arg0 = (cp = strrchr(*argv, '/')) ? cp+1 : *argv; + if (!strcmp(arg0, "cpp")) + onlycpp = 1; + for (;;) { nextiter: --argc, ++argv; @@ -51,6 +57,12 @@ main(int argc, char *argv[]) case 'E': onlycpp = 1; break; + case 'D': + defmacro(cp+1); + goto nextiter; + case 'd': + DBGON(); + break; case 'I': incdir(cp+1); goto nextiter; @@ -72,12 +84,10 @@ main(int argc, char *argv[]) usage(); icpp(); - ikeywords(); ilex(*argv); if (onlycpp) { - for (next(); yytoken != EOFTOK; next()) - printf("%s ", yytext); + outcpp(); } else { for (next(); yytoken != EOFTOK; decl()) /* nothing */; diff --git a/cc1/symbol.c b/cc1/symbol.c @@ -76,7 +76,7 @@ pushctx(void) error("too much nested blocks"); } -static void +void killsym(Symbol *sym) { short f; @@ -87,14 +87,14 @@ killsym(Symbol *sym) free(sym->u.s); if (sym->ns == NS_TAG) sym->type->defined = 0; - if ((name = sym->name) != NULL) { - unlinkhash(sym); + unlinkhash(sym); + if ((name = sym->name) != NULL && sym->ns != NS_CPP) { if ((f & (ISUSED|ISGLOBAL|ISDECLARED)) == ISDECLARED) warn("'%s' defined but not used", name); if ((f & ISDEFINED) == 0 && sym->ns == NS_LABEL) errorp("label '%s' is not defined", name); - free(name); } + free(name); free(sym); } @@ -168,7 +168,6 @@ linksym(Symbol *sym) { Symbol *p, *prev; - sym->flags |= ISDECLARED; switch (sym->ns) { case NS_CPP: return sym; @@ -198,7 +197,6 @@ linkhash(Symbol *sym) Symbol **h, *p, *prev; h = &htab[hash(sym->name)]; - for (prev = p = *h; p; prev = p, p = p->hash) { if (p->ctx <= sym->ctx) break; @@ -214,16 +212,14 @@ linkhash(Symbol *sym) if (sym->ns != NS_CPP) sym->id = newid(); + sym->flags |= ISDECLARED; return linksym(sym); } Symbol * newsym(int ns) { - Symbol *sym; - - sym = linksym(allocsym(ns, NULL)); - return sym; + return linksym(allocsym(ns, NULL)); } Symbol * @@ -253,15 +249,6 @@ lookup(int ns, char *name) return allocsym(ns, name); } -void -delmacro(Symbol *sym) -{ - unlinkhash(sym); - free(sym->name); - free(sym->u.s); - free(sym); -} - Symbol * nextsym(Symbol *sym, int ns) { @@ -296,77 +283,14 @@ install(int ns, Symbol *sym) } void -ikeywords(void) +keywords(struct keyword *key, int ns) { - static struct { - char *str; - unsigned char token, value; - } *bp, keywords[] = { - {"auto", SCLASS, AUTO}, - {"break", BREAK, BREAK}, - {"_Bool", TYPE, BOOL}, - {"case", CASE, CASE}, - {"char", TYPE, CHAR}, - {"const", TQUALIFIER, CONST}, - {"continue", CONTINUE, CONTINUE}, - {"default", DEFAULT, DEFAULT}, - {"do", DO, DO}, - {"double", TYPE, DOUBLE}, - {"else", ELSE, ELSE}, - {"enum", TYPE, ENUM}, - {"extern", SCLASS, EXTERN}, - {"float", TYPE, FLOAT}, - {"for", FOR, FOR}, - {"goto", GOTO, GOTO}, - {"if", IF, IF}, - {"inline", TQUALIFIER, INLINE}, - {"int", TYPE, INT}, - {"long", TYPE, LONG}, - {"register", SCLASS, REGISTER}, - {"restrict", TQUALIFIER, RESTRICT}, - {"return", RETURN, RETURN}, - {"short", TYPE, SHORT}, - {"signed", TYPE, SIGNED}, - {"sizeof", SIZEOF, SIZEOF}, - {"static", SCLASS, STATIC}, - {"struct", TYPE, STRUCT}, - {"switch", SWITCH, SWITCH}, - {"typedef", SCLASS, TYPEDEF}, - {"union", TYPE, UNION}, - {"unsigned", TYPE, UNSIGNED}, - {"void", TYPE, VOID}, - {"volatile", TQUALIFIER, VOLATILE}, - {"while", WHILE, WHILE}, - {NULL, 0, 0}, - }, cppclauses[] = { - {"define", DEFINE, DEFINE}, - {"include", INCLUDE, INCLUDE}, - {"line", LINE, LINE}, - {"ifdef", IFDEF, IFDEF}, - {"if", IF, IF}, - {"elif", ELIF, ELIF}, - {"else", ELSE, ELSE}, - {"ifndef", IFNDEF, IFNDEF}, - {"endif", ENDIF, ENDIF}, - {"undef", UNDEF, UNDEF}, - {"pragma", PRAGMA, PRAGMA}, - {"error", ERROR, ERROR}, - {NULL, 0, 0} - }, *list[] = { - keywords, - cppclauses, - NULL - }, **lp; Symbol *sym; - int ns = NS_KEYWORD; - for (lp = list; *lp; ++lp) { - for (bp = *lp; bp->str; ++bp) { - sym = linkhash(allocsym(ns, bp->str)); - sym->token = bp->token; - sym->u.token = bp->value; - } - ns = NS_CPPCLAUSES; + for ( ; key->str; ++key) { + sym = linkhash(allocsym(ns, key->str)); + sym->token = key->token; + sym->u.token = key->value; } /* * Remove all the predefined symbols from * the symbol list. It diff --git a/cc1/tests/test031.c b/cc1/tests/test031.c @@ -0,0 +1,33 @@ + +/* +name: TEST031 +description: Test concatenation in preprocessor +output: +F5 I +G6 F5 main +{ +\ +A7 I foo +A8 I bar +A9 I foobar + A9 A7 A8 +I :I + A9 A7 A8 +I :I + r #I0 +} +*/ + +#define CAT(x,y) x ## y +#define XCAT(x,y) CAT(x,y) +#define FOO foo +#define BAR bar + +int +main(void) +{ + int foo, bar, foobar; + + CAT(foo,bar) = foo + bar; + XCAT(FOO,BAR) = foo + bar; + return 0; +} + diff --git a/cc1/tests/test032.c b/cc1/tests/test032.c @@ -0,0 +1,25 @@ + +/* +name: TEST032 +description: test special characters @ and $ in macro definitions +output: +F3 I +G4 F3 main +{ +\ +A6 P p + A6 "54686973206973206120737472696E672024206F722023206F72202323616E64206974206973206F6B2021 'P :P + r A6 #P0 !I +} +*/ + +#define M1(x) "This is a string $ or # or ##" ## #x + +int +main(void) +{ + char *p = M1(and it is ok!); + + return p != 0; +} + diff --git a/cc1/tests/test033.c b/cc1/tests/test033.c @@ -0,0 +1,17 @@ +/* +name: TEST033 +description: test for #if defined() +output: +G1 I c +*/ + +#if defined(FOO) +int a; +#elif !defined(FOO) && defined(BAR) +int b; +#elif !defined(FOO) && !defined(BAR) +int c; +#else +int d; +#endif + diff --git a/cc1/tests/update.sh b/cc1/tests/update.sh @@ -1,8 +1,26 @@ #!/bin/sh +update() +{ + (echo '/^output/+;/^\*\//-c' + ../cc1 -I./ -w $1 2>&1 + printf ".\nw\n") | ed -s $1 +} + + +case $# in +1) + update $1 + exit + ;; +*) + echo "usage: update.sh [test]" >&2 + exit 1 + ;; +esac + + for i in *.c do - (echo '/^output/+;/^\*\//-c' - ../cc1 -w $i 2>&1 - printf ".\nw\n") | ed -s $i + update $i done diff --git a/inc/cc.h b/inc/cc.h @@ -8,9 +8,12 @@ typedef unsigned bool; #endif #ifndef NDEBUG -#define DBG(...) fprintf(stderr, __VA_ARGS__) +extern int debug; +#define DBG(fmt, ...) dbg(fmt, __VA_ARGS__) +#define DBGON() (debug = 1) #else #define DBG(...) +#define DBGON() #endif #define L_INT8 'C' @@ -39,8 +42,8 @@ typedef unsigned bool; #define L_EXTERN 'X' extern void die(const char *fmt, ...); +extern void dbg(const char *fmt, ...); extern void *xmalloc(size_t size); extern void *xcalloc(size_t nmemb, size_t size); extern char *xstrdup(const char *s); extern void *xrealloc(void *buff, register size_t size); - diff --git a/lib/Makefile b/lib/Makefile @@ -1,6 +1,6 @@ include ../config.mk -OBJS = die.o xcalloc.o xmalloc.o xrealloc.o xstrdup.o +OBJS = die.o xcalloc.o xmalloc.o xrealloc.o xstrdup.o debug.o all: libcc.a diff --git a/lib/debug.c b/lib/debug.c @@ -0,0 +1,20 @@ + +#include <stdarg.h> +#include <stdio.h> + +#include "../inc/cc.h" + +int debug; + +void +dbg(const char *fmt, ...) +{ + if (!debug) + return; + va_list va; + va_start(va, fmt); + vfprintf(stderr, fmt, va); + putc('\n', stderr); + va_end(va); + return; +}