changeset 15:f6177d3ed5c2

handle directives
author David A. Holland
date Sun, 19 Dec 2010 21:42:01 -0500
parents 5045b9678bb0
children 9dda765ee85c
files Makefile directive.c directive.h eval.h files.c macro.h main.c
diffstat 7 files changed, 596 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sun Dec 19 19:51:36 2010 -0500
+++ b/Makefile	Sun Dec 19 21:42:01 2010 -0500
@@ -1,7 +1,7 @@
 #	$NetBSD$
 
 PROG=	tradcpp
-SRCS=	main.c files.c place.c array.c utils.c
+SRCS=	main.c files.c directive.c  place.c array.c utils.c
 WARNS=	5
 
 .include <bsd.prog.mk>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/directive.c	Sun Dec 19 21:42:01 2010 -0500
@@ -0,0 +1,423 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils.h"
+#include "mode.h"
+#include "place.h"
+#include "files.h"
+#include "directive.h"
+#include "macro.h"
+#include "eval.h"
+
+static const char ws[] = " \t\f\v";
+
+struct ifstate {
+	struct ifstate *prev;
+	struct place startplace;
+	bool curtrue;
+	bool evertrue;
+	bool seenelse;
+};
+
+static struct ifstate *ifstate;
+
+////////////////////////////////////////////////////////////
+// common parsing bits
+
+static
+void
+oneword(const char *what, struct place *p2, char *line)
+{
+	size_t pos;
+
+	pos = strcspn(line, ws);
+	if (line[pos] != '\0') {
+		p2->column += pos;
+		complain(p2, "Garbage after %s argument", what);
+		complain_fail();
+		line[pos] = '\0';
+	}
+}
+
+////////////////////////////////////////////////////////////
+// if handling
+
+static
+struct ifstate *
+ifstate_create(struct ifstate *prev, struct place *p, bool startstate)
+{
+	struct ifstate *is;
+
+	is = domalloc(sizeof(*is));
+	is->prev = prev;
+	if (p != NULL) {
+		is->startplace = *p;
+	} else {
+		place_setbuiltin(&is->startplace, 1);
+	}
+	is->curtrue = startstate;
+	is->evertrue = is->curtrue;
+	is->seenelse = false;
+	return is;
+}
+
+static
+void
+ifstate_destroy(struct ifstate *is)
+{
+	free(is);
+}
+
+static
+void
+ifstate_push(struct place *p, bool startstate)
+{
+	ifstate = ifstate_create(ifstate, p, startstate);
+}
+
+static
+void
+ifstate_pop(void)
+{
+	struct ifstate *is;
+
+	is = ifstate;
+	ifstate = ifstate->prev;
+	ifstate_destroy(is);
+}
+
+static
+void
+d_if(struct place *p, struct place *p2, char *line, size_t len)
+{
+	char *expr;
+	bool val;
+
+	expr = macroexpand(p2, line, len, true);
+	val = eval(expr);
+	ifstate_push(p, val);
+	free(expr);
+}
+
+static
+void
+d_ifdef(struct place *p, struct place *p2, char *line, size_t len)
+{
+	oneword("#ifdef", p2, line);
+	ifstate_push(p, macro_isdefined(line));
+}
+
+static
+void
+d_ifndef(struct place *p, struct place *p2, char *line, size_t len)
+{
+	oneword("#ifndef", p2, line);
+	ifstate_push(p, !macro_isdefined(line));
+}
+
+static
+void
+d_elif(struct place *p, struct place *p2, char *line, size_t len)
+{
+	char *expr;
+
+	if (ifstate->seenelse) {
+		complain(p, "#elif after #else");
+		complain_fail();
+	}
+
+	if (ifstate->evertrue) {
+		ifstate->curtrue = false;
+	} else {
+		expr = macroexpand(p2, line, len, true);
+		ifstate->curtrue = eval(expr);
+		ifstate->evertrue = ifstate->curtrue;
+		free(expr);
+	}
+}
+
+static
+void
+d_else(struct place *p, struct place *p2, char *line, size_t len)
+{
+	if (ifstate->seenelse) {
+		complain(p, "Multiple #else directives in one conditional");
+		complain_fail();
+	}
+
+	ifstate->curtrue = !ifstate->evertrue;
+	ifstate->evertrue = true;
+	ifstate->seenelse = true;
+}
+
+static
+void
+d_endif(struct place *p, struct place *p2, char *line, size_t len)
+{
+	if (ifstate->prev == NULL) {
+		complain(p, "Unmatched #endif");
+		complain_fail();
+	} else {
+		ifstate_pop();
+	}
+}
+
+////////////////////////////////////////////////////////////
+// macros
+
+static
+void
+d_define(struct place *p, struct place *p2, char *line, size_t len)
+{
+	size_t pos;
+	struct place p3;
+
+	/*
+	 * line may be:
+	 *    macro expansion
+	 *    macro(arg, arg, ...) expansion
+	 */
+
+	pos = strcspn(line, " \t\f\v(");
+	if (line[pos] == '(') {
+		pos++;
+		pos = pos + strcspn(line+pos, "()");
+		if (line[pos] == '(') {
+			p2->column += pos;
+			complain(p2, "Left parenthesis in macro parameters");
+			complain_fail();
+			return;
+		}
+		if (line[pos] != ')') {
+			p2->column += pos;
+			complain(p2, "Unclosed macro parameter list");
+			complain_fail();
+			return;
+		}
+		pos++;
+		if (!strchr(ws, line[pos])) {
+			p2->column += pos;
+			complain(p2, "Trash after macro parameter list");
+			complain_fail();
+			return;
+		}
+		line[pos++] = '\0';
+	} else if (line[pos] == '\0') {
+		/* nothing */
+	} else {
+		line[pos++] = '\0';
+	}
+
+	pos += strspn(line+pos, ws);
+
+	p3 = *p2;
+	p3.column += pos;
+	macro_define(p2, line, &p3, line + pos);
+}
+
+static
+void
+d_undef(struct place *p, struct place *p2, char *line, size_t len)
+{
+	oneword("#undef", p2, line);
+	macro_undef(line);
+}
+
+////////////////////////////////////////////////////////////
+// includes
+
+static
+bool
+tryinclude(struct place *p, char *line, size_t len)
+{
+	if (len > 2 && line[0] == '"' && line[len-1] == '"') {
+		line[len-1] = '\0';
+		file_readquote(p, line+1);
+		return true;
+	}
+	if (len > 2 && line[0] == '<' && line[len-1] == '>') {
+		line[len-1] = '\0';
+		file_readbracket(p, line+1);
+		return true;
+	}
+	return false;
+}
+
+static
+void
+d_include(struct place *p, struct place *p2, char *line, size_t len)
+{
+	char *text;
+
+	if (tryinclude(p, line, len)) {
+		return;
+	}
+	text = macroexpand(p2, line, len, false);
+	if (tryinclude(p, text, strlen(text))) {
+		free(text);
+		return;
+	}
+	free(text);
+	complain(p, "Illegal #include directive");
+	complain_fail();
+}
+
+static
+void
+d_line(struct place *p, struct place *p2, char *line, size_t len)
+{
+	/* XXX */
+	complain(p, "Sorry, no #line yet");
+}
+
+////////////////////////////////////////////////////////////
+// messages
+
+static
+void
+d_warning(struct place *p, struct place *p2, char *line, size_t len)
+{
+	char *msg;
+
+	msg = macroexpand(p2, line, len, false);
+	complain(p, "#warning: %s", msg);
+	if (mode.werror) {
+		complain_fail();
+	}
+	free(msg);
+}
+
+static
+void
+d_error(struct place *p, struct place *p2, char *line, size_t len)
+{
+	char *msg;
+
+	msg = macroexpand(p2, line, len, false);
+	complain(p, "#error: %s", msg);
+	complain_fail();
+	free(msg);
+}
+
+////////////////////////////////////////////////////////////
+// other
+
+static
+void
+d_pragma(struct place *p, struct place *p2, char *line, size_t len)
+{
+	complain(p, "#pragma %s", line);
+	complain_fail();
+}
+
+////////////////////////////////////////////////////////////
+// directive table
+
+static const struct {
+	const char *name;
+	bool ifskip;
+	void (*func)(struct place *, struct place *, char *line, size_t len);
+} directives[] = {
+	{ "define",  true,  d_define },
+	{ "elif",    false, d_elif },
+	{ "else",    false, d_else },
+	{ "endif",   false, d_endif },
+	{ "error",   true,  d_error },
+	{ "if",      false, d_if },
+	{ "ifdef",   false, d_ifdef },
+	{ "ifndef",  false, d_ifndef },
+	{ "include", true,  d_include },
+	{ "line",    true,  d_line },
+	{ "pragma",  true,  d_pragma },
+	{ "undef",   true,  d_undef },
+	{ "warning", true,  d_warning },
+};
+static const unsigned numdirectives = HOWMANY(directives);
+
+static
+size_t
+notrailingws(char *buf, size_t len)
+{
+	while (len > 0 && strchr(ws, buf[len-1])) {
+		buf[--len] = '\0';
+	}
+	return len;
+}
+
+static
+void
+directive_gotdirective(struct place *p, char *line, size_t linelen)
+{
+	struct place p2;
+	size_t len, skip;
+	unsigned i;
+
+	p2 = *p;
+	for (i=0; i<numdirectives; i++) {
+		len = strlen(directives[i].name);
+		if (!strncmp(line, directives[i].name, len) &&
+		    strchr(ws, line[len])) {
+			if (directives[i].ifskip && !ifstate->curtrue) {
+				return;
+			}
+			skip = len + strspn(line+len, ws);
+			p2.column += skip;
+			line += skip;
+			linelen -= skip;
+			linelen = notrailingws(line, linelen);
+			directives[i].func(p, &p2, line, linelen);
+			return;
+		}
+	}
+	skip = strcspn(line, ws);
+	complain(p, "Unknown directive #%.*s", (int)skip, line);
+	complain_fail();
+}
+
+void
+directive_gotline(struct place *p, char *line, size_t len)
+{
+	size_t skip;
+
+	/* check if we have a directive line */
+	skip = strspn(line, ws);
+	if (line[skip] == '#') {
+		skip = skip + 1 + strspn(line + skip + 1, ws);
+		p->column += skip;
+		directive_gotdirective(p, line+skip, len-skip);
+	} else if (ifstate->curtrue) {
+		macro_sendline(p, line, len);
+	}
+}
+
+
+void
+directive_goteof(struct place *p)
+{
+	while (ifstate->prev != NULL) {
+		complain(p, "Missing #endif");
+		complain(&ifstate->startplace, "...opened at this point");
+		complain_failed();
+		ifstate_pop();
+	}
+	macro_sendeof(p);
+}
+
+////////////////////////////////////////////////////////////
+// module initialization
+
+void
+directive_init(void)
+{
+	ifstate = ifstate_create(NULL, NULL, true);
+}
+
+void
+directive_cleanup(void)
+{
+	assert(ifstate->prev == NULL);
+	ifstate_destroy(ifstate);
+	ifstate = NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/directive.h	Sun Dec 19 21:42:01 2010 -0500
@@ -0,0 +1,10 @@
+#include <stddef.h>
+
+struct place;
+
+void directive_init(void);
+void directive_cleanup(void);
+
+void directive_gotline(struct place *p, char *line, size_t len);
+void directive_goteof(struct place *p);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eval.h	Sun Dec 19 21:42:01 2010 -0500
@@ -0,0 +1,1 @@
+bool eval(char *expr);
--- a/files.c	Sun Dec 19 19:51:36 2010 -0500
+++ b/files.c	Sun Dec 19 21:42:01 2010 -0500
@@ -7,8 +7,10 @@
 #include <err.h>
 
 #include "array.h"
+#include "mode.h"
 #include "place.h"
 #include "files.h"
+#include "directive.h"
 
 struct incdir {
 	const char *name;
@@ -84,8 +86,133 @@
 ////////////////////////////////////////////////////////////
 // parsing
 
+static
+size_t
+findnl(const char *buf, size_t start, size_t limit)
+{
+	size_t i;
+
+	for (i=start; i<limit; i++) {
+		if (buf[i] == '\n') {
+			return i;
+		}
+	}
+	return limit;
+}
+
+static
 void
-file_read(const struct placefile *pf, int fd);
+file_read(const struct placefile *pf, int fd, const char *name)
+{
+	struct place linestartplace, nextlinestartplace, ptmp;
+	size_t bufend, bufmax, linestart, lineend, nextlinestart, tmp;
+	ssize_t result;
+	bool ateof = false;
+	char *buf;
+
+	place_setfilestart(&linestartplace, pf);
+	nextlinestartplace = linestartplace;
+
+	bufmax = 128;
+	bufend = 0;
+	linestart = 0;
+	lineend = 0;
+	buf = domalloc(bufmax);
+
+	while (1) {
+		if (lineend >= bufend) {
+			/* do not have a whole line in the buffer; read more */
+			if (linestart > 0 && bufend > linestart) {
+				/* slide to beginning of buffer */
+				memmove(buf, buf+linestart, bufend-linestart);
+				bufend -= linestart;
+				lineend -= linestart;
+				linestart = 0;
+			}
+			if (bufend >= bufmax) {
+				/* need bigger buffer */
+				bufmax *= 2;
+				buf = dorealloc(buf, bufmax);
+			}
+
+			if (ateof) {
+				/* don't read again, in case it's a socket */
+				result = 0;
+			} else {
+				result = read(fd, buf+bufend, bufmax - bufend);
+			}
+
+			if (result == -1) {
+				/* read error */
+				warn("%s", name);
+				complain_fail();
+			} else if (result == 0 && bufend == linestart) {
+				/* eof */
+				ateof = true;
+				break;
+			} else if (result == 0) {
+				/* eof in middle of line */
+				ateof = true;
+				ptmp = linestartplace;
+				ptmp.column += bufend - linestart;
+				complain(&ptmp, "No newline at end of file");
+				if (mode.werror) {
+					complain_fail();
+				}
+				assert(bufend < bufmax);
+				lineend = bufend++;
+				buf[lineend] = '\n';
+			} else {
+				tmp = bufend;
+				bufend += (size_t)result;
+				lineend = findnl(buf, tmp, bufend);
+			}
+			/* loop in case we still don't have a whole line */
+			continue;
+		}
+
+		/* have a line */
+		assert(buf[lineend] == '\n');
+		buf[lineend] = '\0';
+		nextlinestart = lineend+1;
+		nextlinestartplace.line++;
+
+		/* check for CR/NL */
+		if (lineend > 0 && buf[lineend-1] == '\r') {
+			buf[lineend-1] = '\0';
+			lineend--;
+		}
+
+		/* check for continuation line */
+		if (lineend > 0 && buf[lineend-1]=='\\') {
+			lineend--;
+			tmp = nextlinestart - lineend;
+			if (bufend > nextlinestart) {
+				memmove(buf+lineend, buf+nextlinestart,
+					bufend - nextlinestart);
+			}
+			bufend -= tmp;
+			nextlinestart -= tmp;
+			lineend = findnl(buf, lineend, bufend);
+			/* might not have a whole line, so loop */
+			continue;
+		}
+
+		/* line now goes from linestart to lineend */
+		assert(buf[lineend] == '\0');
+		if (lineend > linestart) {
+			directive_gotline(&linestartplace,
+					  buf+linestart, lineend-linestart);
+		}
+
+		linestart = nextlinestart;
+		lineend = findnl(buf, linestart, bufend);
+		linestartplace = nextlinestartplace;
+	}
+
+	directive_goteof(&linestartplace);
+	free(buf);
+}
 
 ////////////////////////////////////////////////////////////
 // path search
@@ -120,11 +247,13 @@
 {
 	int fd;
 
+	/* XXX check for non-regular files */
+
 	fd = open(file, O_RDONLY);
 	if (fd < 0) {
 		return -1;
 	}
-	/* XXX: do we need to do anything here or is this function pointless?*/
+
 	return fd;
 }
 
@@ -147,8 +276,8 @@
 		fd = file_tryopen(file);
 		if (fd >= 0) {
 			pf = place_addfile(place, file, id->issystem);
+			file_read(pf, fd, file);
 			free(file);
-			file_read(pf, fd);
 			close(fd);
 			return;
 		}
@@ -184,6 +313,6 @@
 		die();
 	}
 	pf = place_addfile(place, name, false);
-	file_read(pf, fd);
+	file_read(pf, fd, name);
 	close(fd);
 }
--- a/macro.h	Sun Dec 19 19:51:36 2010 -0500
+++ b/macro.h	Sun Dec 19 21:42:01 2010 -0500
@@ -1,4 +1,11 @@
 struct place;
 
-void macro_define(struct place *, const char *macro, const char *expansion);
+void macro_define(struct place *, const char *macro,
+		  struct place *, const char *expansion);
 void macro_undef(const char *macro);
+bool macro_isdefined(const char *macro);
+
+char *macroexpand(struct place *, char *buf, size_t len, bool honordefined);
+
+void macro_sendline(struct place *, char *buf, size_t len);
+void macro_sendeof(struct place *);
--- a/main.c	Sun Dec 19 19:51:36 2010 -0500
+++ b/main.c	Sun Dec 19 21:42:01 2010 -0500
@@ -12,6 +12,7 @@
 #include "mode.h"
 #include "place.h"
 #include "files.h"
+#include "directive.h"
 #include "macro.h"
 
 struct mode mode = {
@@ -57,6 +58,7 @@
 
 struct commandline_macro {
 	struct place where;
+	struct place where2;
 	const char *macro;
 	const char *expansion;
 };
@@ -79,13 +81,14 @@
 
 static
 void
-commandline_macro_add(const struct place *p,
-		      const char *macro, const char *expansion)
+commandline_macro_add(const struct place *p, const char *macro,
+		      const struct place *p2, const char *expansion)
 {
 	struct commandline_macro *cm;
 
 	cm = domalloc(sizeof(*cm));
 	cm->where = *p;
+	cm->where2 = *p2;
 	cm->macro = macro;
 	cm->expansion = expansion;
 }
@@ -94,6 +97,7 @@
 void
 commandline_def(const struct place *p, char *str)
 {
+	struct place p2;
 	char *val;
 
 	val = strchr(str, '=');
@@ -101,14 +105,21 @@
 		*val = '\0';
 		val++;
 	}
-	commandline_macro_add(p, str, val ? val : "1");
+
+	if (val) {
+		p2 = *p;
+		p2.column += strlen(str);
+	} else {
+		place_setbuiltin(&p2, 1);
+	}
+	commandline_macro_add(p, str, &p2, val ? val : "1");
 }
 
 static
 void
 commandline_undef(const struct place *p, char *str)
 {
-	commandline_macro_add(p, str, NULL);
+	commandline_macro_add(p, str, p, NULL);
 }
 
 static
@@ -122,7 +133,8 @@
 	for (i=0; i<num; i++) {
 		cm = array_get(&commandline_macros, i);
 		if (cm->expansion != NULL) {
-			macro_define(&cm->where, cm->macro, cm->expansion);
+			macro_define(&cm->where, cm->macro,
+				     &cm->where2, cm->expansion);
 		} else {
 			macro_undef(cm->macro);
 		}
@@ -138,7 +150,7 @@
 	struct place p;
 
 	place_setbuiltin(&p, num);
-	macro_define(&p, name, val);
+	macro_define(&p, name, &p, val);
 }
 
 static
@@ -859,6 +871,7 @@
 
 	place_init();
 	files_init();
+	directive_init();
 }
 
 static
@@ -867,6 +880,7 @@
 {
 	unsigned i, num;
 
+	directive_cleanup();
 	files_cleanup();
 	place_cleanup();