diff interps/sceql/sceql-0.1.c @ 996:859f9b4339e6

<Gregor> tar xf egobot.tar.xz
author HackBot
date Sun, 09 Dec 2012 19:30:08 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/interps/sceql/sceql-0.1.c	Sun Dec 09 19:30:08 2012 +0000
@@ -0,0 +1,404 @@
+/*
+	The Sceql specification is not set in stone, so this
+interpreter may change in the future. Version 0.1 of the specification
+is implemented here.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#define NODESIZE  1000
+#define LOOPDEPTH 1000
+
+typedef unsigned char byte;
+
+typedef struct byteqnode
+{
+	byte qdata[NODESIZE];
+	int start; /* the first byte in use in qdata */
+	int end;   /* the last byte in use in qdata */
+	struct byteqnode *next;
+} byteqnode;
+
+typedef struct
+{
+	int len;
+	byteqnode *head, *tail;
+} bytequeue;
+
+void error(char *complaint)
+{
+	fprintf(stderr, "%s\n", complaint);
+	exit(1);
+}
+
+/* create a queue with a zero byte in it */
+bytequeue *queue_create(void)
+{
+	bytequeue *myq;
+	myq = malloc(sizeof (bytequeue));
+	if (!myq)
+		error("Out of memory");
+	myq->len = 1;
+	myq->head = myq->tail = malloc(sizeof (byteqnode));
+	if (!myq->head)
+		error("Out of memory");
+	myq->tail->next = NULL;
+	myq->tail->start = myq->tail->end = 0;
+	myq->tail->qdata[0] = 0;
+	return myq;
+}
+
+/* add a byte onto the back of the queue */
+void queue_enqueue(bytequeue *q, byte val)
+{
+	byteqnode *node;
+	node = q->tail;
+	node->end++;
+	if (node->end == NODESIZE)
+	{
+		node = q->tail = node->next = malloc(sizeof (byteqnode));
+		if (!node)
+			error("Out of memory");
+		node->next = NULL;
+		node->start = node->end = 0;
+	}
+	node->qdata[node->end] = val;
+	q->len++;
+}
+
+byte queue_peek(bytequeue *q);
+
+/* take a byte from the front of the queue */
+byte queue_dequeue(bytequeue *q)
+{
+	byte result;
+
+	result = queue_peek(q);
+
+	q->head->start++;
+	if (q->head->start == NODESIZE)
+	{
+		byteqnode *node = q->head->next;
+		free(q->head);
+		q->head = node;
+		/* q->head->start should already be 0 */
+	}
+	q->len--;
+	return result;
+}
+
+/* peek at a byte from the front of the queue */
+byte queue_peek(bytequeue *q)
+{
+	if (q->len == 0)
+		error("peeking at an empty queue");
+	return q->head->qdata[q->head->start];
+}
+
+/* shove a new byte at the front of the queue replacing what's there */
+void queue_shove(bytequeue *q, byte b)
+{
+	if (q->len == 0)
+		error("shoving into an empty queue");
+	q->head->qdata[q->head->start] = b;
+}
+
+void queue_delete(bytequeue *q)
+{
+	byteqnode *node;
+	while (q->head)
+	{
+		node = q->head->next;
+		free(q->head);
+		q->head = node;
+	}
+	free(q);
+}
+
+#ifdef DEBUGGING
+void queue_debug(bytequeue *q)
+{
+	int i;
+	i = q->len;
+	if (i > 0)
+	{
+		byte b;
+		b = queue_dequeue(q);
+		printf("%d", b);
+		queue_enqueue(q, b);
+	}
+	for (i--; i > 0; i--)
+	{
+		byte b;
+		b = queue_dequeue(q);
+		printf(", %d", b);
+		queue_enqueue(q, b);
+	}
+	putchar('\n');
+}
+#endif
+
+typedef enum
+{
+	IN_NEXT   = 0x01,
+	IN_DEC    = 0x02,
+	IN_INPUT  = 0x03,
+	IN_OUTPUT = 0x04,
+	IN_INC    = 0x05,
+	IN_GROW   = 0x06,
+	IN_BEGIN  = 0x10,
+	IN_END    = 0x11
+#ifdef DEBUGGING
+	,IN_DEBUG  = 0xff
+#endif
+} sceql_intype;
+
+typedef struct
+{
+	sceql_intype in;
+	int r;
+} sceql_instruction;
+
+typedef struct
+{
+	sceql_instruction *code;
+	int len;
+} sceql_program;
+
+sceql_intype sceql_getinstruction(FILE *f)
+{
+	static sceql_intype uc2i[UCHAR_MAX + 1] = { IN_BEGIN };
+	int c;
+	sceql_intype i;
+	if (uc2i[0] == IN_BEGIN)
+	{
+		uc2i[0] = 0;
+		for (c = 1; c <= (int) UCHAR_MAX; c++)
+		{
+			i = 0;
+			if (c == '\\')
+				i = IN_BEGIN;
+			else if (c == '/')
+				i = IN_END;
+			else if (c == '=')
+				i = IN_NEXT;
+			else if (c == '-')
+				i = IN_DEC;
+			else if (c == '_')
+				i = IN_INC;
+			else if (c == '!')
+				i = IN_GROW;
+			else if (c == '&')
+				i = IN_INPUT;
+			else if (c == '*')
+				i = IN_OUTPUT;
+#ifdef DEBUGGING
+			else if (c == '`')
+				i = IN_DEBUG;
+#endif
+			uc2i[c] = i;
+		}
+	}
+	for (;;)
+	{
+		c = fgetc(f);
+		if (c < 0 || c > 255)
+			return 0;
+		if ((i = uc2i[c]))
+			break;
+	}
+	return i;
+}
+
+void sceql_load(sceql_program *p, FILE *f)
+{
+	sceql_instruction *code;
+	sceql_intype in;
+	unsigned long filesize;
+	int last = 0, depth = 0, begin[LOOPDEPTH];
+
+	fseek(f, 0, SEEK_END);
+	filesize = ftell(f);
+	if (filesize > INT_MAX)
+		error("File too long");
+	fseek(f, 0, SEEK_SET);
+
+	code = p->code = malloc(filesize * sizeof (sceql_instruction));
+	if (!code)
+		error("Not enough memory");
+
+	if (!(in = sceql_getinstruction(f)))
+		error("Program contains no instructions");
+	if (in == IN_END)
+		error("Unbalanced slashes");
+	if (in == IN_BEGIN)
+		begin[depth++] = 0;
+	code[0].in = in;
+	code[0].r = 1;
+
+	while ((in = sceql_getinstruction(f)))
+	{
+		if (in == IN_BEGIN)
+		{
+			last++;
+			if (depth < LOOPDEPTH)
+				begin[depth] = last;
+			depth++;
+		}
+		else if (in == IN_END && depth <= LOOPDEPTH)
+		{
+			if (!depth)
+				error("Unbalanced slashes");
+			last++;
+			code[last].r = last - begin[--depth];
+			code[begin[depth]].r = code[last].r + 1;
+		}
+		else if (in == IN_END)
+		{
+			int mydepth = depth, start;
+			for (start = last++; start >= 0; start--)
+			{
+				if (code[start].in == IN_BEGIN)
+				{
+					if (mydepth == depth)
+						break;
+					mydepth--;
+				}
+				else if (code[start].in == IN_END)
+					mydepth++;
+			}
+			code[last].r = last - start;
+			code[start].r = code[last].r + 1;
+			depth--;
+		}
+		else
+		{
+			if (in == code[last].in)
+			{
+				code[last].r++;
+				continue;
+			}
+			code[++last].r = 1;
+		}
+		code[last].in = in;
+	}
+	fclose(f);
+	if (depth)
+		error("Unbalanced slashes");
+	p->len = last + 1;
+}
+
+void sceql_run(sceql_program *p)
+{
+	bytequeue *q;
+	sceql_instruction *ip, *end;
+
+	ip = p->code;
+	end = ip + p->len; 
+
+	q = queue_create();
+
+	do
+	{
+		register sceql_intype in;
+		register int r;
+
+		in = ip->in;
+		r = ip->r;
+
+		/* run the instruction "in": not just once, but "r" times */
+
+		if (in == IN_NEXT)
+		{
+			register byte b;
+			for (; r; r--)
+			{
+				b = queue_dequeue(q);
+				queue_enqueue(q, b);
+			}
+		}
+
+		/* it just so happens that we can increment or decrement
+		   r times with a one-liner */
+		else if (in == IN_INC)
+			queue_shove(q, queue_peek(q) + r);
+		else if (in == IN_DEC)
+			queue_shove(q, queue_peek(q) - r);
+
+		else if (in == IN_GROW)
+		{
+			for (; r; r--)
+				queue_enqueue(q, 0);
+		}
+		else if (in == IN_OUTPUT)
+		{
+			register int c;
+			for (; r; r--)
+			{
+				c = queue_dequeue(q);
+				putchar(c);
+				queue_enqueue(q, c);
+			}
+		}
+		else if (in == IN_INPUT)
+		{
+			register int c;
+			for (; r; r--)
+			{
+				c = getchar();
+				if (c == EOF)
+					c = 0;
+				queue_enqueue(q, c);
+			}
+		}
+		else if (in == IN_BEGIN)
+		{
+			register byte b;
+			if (!(b = queue_peek(q)))
+			{
+				ip += r;
+				continue;
+			}
+		}
+#ifdef DEBUGGING
+		else if (in == IN_DEBUG)
+			queue_debug(q);
+#endif
+		else /* in == IN_END */
+		{
+			ip -= r;
+			continue;
+		}
+		ip++;
+	} while (ip < end);
+
+	queue_delete(q);
+}
+
+void sceql_freeup(sceql_program *p)
+{
+	free(p->code);
+}
+
+void sceql_do(FILE *f)
+{
+	sceql_program prog;
+	sceql_load(&prog, f);
+	sceql_run(&prog);
+	sceql_freeup(&prog);
+}
+
+int main(int argc, char *argv[])
+{
+	FILE *codesrc = stdin;
+	if (argc != 2)
+		error("usage: sceql [sourcefile.sceql]");
+	if (!(codesrc = fopen(argv[1], "r")))
+	{
+		error("Unable to open file");
+	}
+	sceql_do(codesrc);
+	return 0;
+}