diff directive.c @ 15:f6177d3ed5c2

handle directives
author David A. Holland
date Sun, 19 Dec 2010 21:42:01 -0500
parents
children 9dda765ee85c
line wrap: on
line diff
--- /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;
+}