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}