climp

Dirty interpreter for the limp programming language in C

git clone https://git.8pit.net/climp.git

  1#include <stdio.h>
  2#include <ctype.h>
  3#include <string.h>
  4#include <stddef.h>
  5#include <stdlib.h>
  6#include <stdarg.h>
  7#include <pthread.h>
  8#include <semaphore.h>
  9
 10#include <sys/queue.h>
 11
 12#include "scanner.h"
 13#include "util.h"
 14
 15static token septok = { .type = -1, .text = NULL, .line = -1 };
 16
 17void* lexvar(scanner *scr);
 18void* lexdigit(scanner *scr);
 19void* lexspace(scanner *scr);
 20void* lexassign(scanner *scr);
 21
 22char
 23nextch(scanner *scr)
 24{
 25	if (scr->pos >= scr->inlen)
 26		return -1;
 27
 28	return scr->input[scr->pos++];
 29}
 30
 31char
 32peekch(scanner *scr)
 33{
 34	char nxt = nextch(scr);
 35	if (nxt != -1) scr->pos--;
 36	return nxt;
 37}
 38
 39void
 40ignore(scanner *scr)
 41{
 42	scr->start = scr->pos;
 43}
 44
 45void
 46emit(scanner *scr, tok_t tkt)
 47{
 48	size_t siz = scr->pos - scr->start + 1;
 49	token *tok;
 50	char *dest;
 51
 52	dest = emalloc(siz * sizeof(char*));
 53	tok = emalloc(sizeof(*tok));
 54
 55	strncpy(dest, &scr->input[scr->start], siz - 1);
 56	dest[siz - 1] = '\0';
 57
 58	tok->line = scr->line;
 59	tok->text = dest;
 60	tok->type = tkt;
 61
 62	sem_wait(scr->emptysem);
 63	pthread_mutex_lock(scr->qmutex);
 64	TAILQ_INSERT_BEFORE(&septok, tok, toks);
 65	pthread_mutex_unlock(scr->qmutex);
 66	sem_post(scr->fullsem);
 67
 68	scr->start = scr->pos;
 69}
 70
 71void
 72errf(scanner *scr, char *msg, ...)
 73{
 74	int slen = 1;
 75	va_list ap;
 76	token *tok;
 77	char *dest;
 78
 79	va_start(ap, msg);
 80	slen += vsnprintf(NULL, 0, msg, ap);
 81	dest = emalloc(slen * sizeof(char*));
 82
 83	vsnprintf(dest, slen, msg, ap);
 84	va_end(ap);
 85
 86	tok = emalloc(sizeof(*tok));
 87	tok->line = scr->start;
 88	tok->text = dest;
 89	tok->type = TOK_ERROR;
 90
 91	sem_wait(scr->emptysem);
 92	pthread_mutex_lock(scr->qmutex);
 93	TAILQ_INSERT_BEFORE(&septok, tok, toks);
 94	pthread_mutex_unlock(scr->qmutex);
 95	sem_post(scr->fullsem);
 96
 97	scr->start = scr->pos;
 98}
 99
100void*
101lexany(void *pscr)
102{
103	scanner *scr = (scanner*)pscr;
104	char nxt;
105
106	if ((nxt = nextch(scr)) == -1) {
107		emit(scr, TOK_EOF);
108		return NULL;
109	}
110	
111	switch (nxt) {
112	case '\n':
113		scr->line++;
114		ignore(scr);
115		return lexany(scr);
116	case ';':
117		emit(scr, TOK_SEMICOLON);
118		return lexany(scr);
119	case ':':
120		return lexassign(scr);
121	case '(':
122		emit(scr, TOK_LBRACKET);
123		return lexany(scr);
124	case ')':
125		emit(scr, TOK_RBRACKET);
126		return lexany(scr);
127	case '%':
128		emit(scr, TOK_DIVIDE);
129		return lexany(scr);
130	case '*':
131		emit(scr, TOK_MULTI);
132		return lexany(scr);
133	case '+':
134		emit(scr, TOK_PLUS);
135		return lexany(scr);
136	case '-':
137		emit(scr, TOK_MINUS);
138		return lexany(scr);
139	case '?':
140		emit(scr, TOK_QUESTION);
141		return lexany(scr);
142	case '!':
143		emit(scr, TOK_EXCLAMATION);
144		return lexany(scr);
145	}
146
147	if (isalpha(nxt)) {
148		return lexvar(scr);
149	} else if (isdigit(nxt)) {
150		return lexdigit(scr);
151	} else if (isspace(nxt)) {
152		return lexspace(scr);
153	}
154
155	errf(scr, "Invalid character '%c'", nxt);
156	return NULL;
157}
158
159void*
160lexassign(scanner *scr) {
161	char c;
162
163	if ((c = peekch(scr)) == '=') {
164		nextch(scr);
165		emit(scr, TOK_ASSIGN);
166	} else {
167		errf(scr, "Expected '=' after ':' got '%c'", c);
168	}
169
170	return lexany(scr);
171}
172
173void*
174lexvar(scanner *scr)
175{
176	while (isalpha(peekch(scr)))
177		nextch(scr);
178
179	emit(scr, TOK_VAR);
180	return lexany(scr);
181}
182
183void*
184lexdigit(scanner *scr)
185{
186	while (isdigit(peekch(scr)))
187		nextch(scr);
188
189	emit(scr, TOK_DIG);
190	return lexany(scr);
191}
192
193void*
194lexspace(scanner *scr)
195{
196	while (isspace(peekch(scr)))
197		nextch(scr);
198
199	ignore(scr);
200	return lexany(scr);
201}
202
203void
204freescr(scanner *scr)
205{
206	token *tok, *nxt;
207
208	if (!scr)
209		return;
210
211	pthread_join(*scr->thread, NULL); /* TODO stop the thread instead. */
212	if (pthread_mutex_destroy(scr->qmutex))
213		die("pthread_mutex_destroy failed");
214
215	free(scr->thread);
216	TAILQ_FOREACH_SAFE(tok, &scr->qhead, toks, nxt) {
217		if (tok != &septok) {
218			free(tok->text);
219			free(tok);
220		}
221	}
222
223	if (sem_destroy(scr->fullsem) ||
224			sem_destroy(scr->emptysem))
225		die("sem_destroy failed");
226
227	free(scr->fullsem);
228	free(scr->emptysem);
229	free(scr->qmutex);
230	free(scr->input);
231	free(scr);
232}
233
234scanner*
235scanstr(char *str)
236{
237	scanner *scr;
238
239	scr = emalloc(sizeof(*scr));
240	scr->eof    = 0;
241	scr->pos    = 0;
242	scr->line   = 1;
243	scr->start  = 0;
244	scr->input  = estrdup(str);
245	scr->inlen  = strlen(str);
246	scr->qmutex = emalloc(sizeof(pthread_mutex_t));
247	scr->thread = emalloc(sizeof(pthread_t));
248
249	scr->fullsem  = emalloc(sizeof(sem_t));
250	scr->emptysem = emalloc(sizeof(sem_t));
251
252	/* TODO adjust initial emptysem value */
253	if (sem_init(scr->fullsem, 0, 0)
254			|| sem_init(scr->emptysem, 0, 250))
255		die("sem_init failed");
256
257	TAILQ_INIT(&scr->qhead);
258	TAILQ_INSERT_TAIL(&scr->qhead, &septok, toks);
259
260	if (pthread_mutex_init(scr->qmutex, NULL))
261		die("pthread_mutex_init failed");
262	if (pthread_create(scr->thread, NULL, lexany, (void*)scr))
263		die("pthread_create failed");
264
265	return scr;
266}
267
268token*
269nxttok(scanner *scr)
270{
271	token *tok;
272
273	if (scr->eof) return NULL;
274
275	sem_wait(scr->fullsem);
276	pthread_mutex_lock(scr->qmutex);
277	tok = TAILQ_FIRST(&scr->qhead);
278	TAILQ_REMOVE(&scr->qhead, tok, toks);
279	TAILQ_INSERT_AFTER(&scr->qhead, &septok, tok, toks);
280	pthread_mutex_unlock(scr->qmutex);
281	sem_post(scr->emptysem);
282
283	if (tok->type == TOK_EOF)
284		scr->eof = 1;
285
286	return tok;
287}