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>910#include <sys/queue.h>1112#include "scanner.h"13#include "util.h"1415static token septok = { .type = -1, .text = NULL, .line = -1 };1617void* lexvar(scanner *scr);18void* lexdigit(scanner *scr);19void* lexspace(scanner *scr);20void* lexassign(scanner *scr);2122char23nextch(scanner *scr)24{25 if (scr->pos >= scr->inlen)26 return -1;2728 return scr->input[scr->pos++];29}3031char32peekch(scanner *scr)33{34 char nxt = nextch(scr);35 if (nxt != -1) scr->pos--;36 return nxt;37}3839void40ignore(scanner *scr)41{42 scr->start = scr->pos;43}4445void46emit(scanner *scr, tok_t tkt)47{48 size_t siz = scr->pos - scr->start + 1;49 token *tok;50 char *dest;5152 dest = emalloc(siz * sizeof(char*));53 tok = emalloc(sizeof(*tok));5455 strncpy(dest, &scr->input[scr->start], siz - 1);56 dest[siz - 1] = '\0';5758 tok->line = scr->line;59 tok->text = dest;60 tok->type = tkt;6162 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);6768 scr->start = scr->pos;69}7071void72errf(scanner *scr, char *msg, ...)73{74 int slen = 1;75 va_list ap;76 token *tok;77 char *dest;7879 va_start(ap, msg);80 slen += vsnprintf(NULL, 0, msg, ap);81 dest = emalloc(slen * sizeof(char*));8283 vsnprintf(dest, slen, msg, ap);84 va_end(ap);8586 tok = emalloc(sizeof(*tok));87 tok->line = scr->start;88 tok->text = dest;89 tok->type = TOK_ERROR;9091 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);9697 scr->start = scr->pos;98}99100void*101lexany(void *pscr)102{103 scanner *scr = (scanner*)pscr;104 char nxt;105106 if ((nxt = nextch(scr)) == -1) {107 emit(scr, TOK_EOF);108 return NULL;109 }110111 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 }146147 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 }154155 errf(scr, "Invalid character '%c'", nxt);156 return NULL;157}158159void*160lexassign(scanner *scr) {161 char c;162163 if ((c = peekch(scr)) == '=') {164 nextch(scr);165 emit(scr, TOK_ASSIGN);166 } else {167 errf(scr, "Expected '=' after ':' got '%c'", c);168 }169170 return lexany(scr);171}172173void*174lexvar(scanner *scr)175{176 while (isalpha(peekch(scr)))177 nextch(scr);178179 emit(scr, TOK_VAR);180 return lexany(scr);181}182183void*184lexdigit(scanner *scr)185{186 while (isdigit(peekch(scr)))187 nextch(scr);188189 emit(scr, TOK_DIG);190 return lexany(scr);191}192193void*194lexspace(scanner *scr)195{196 while (isspace(peekch(scr)))197 nextch(scr);198199 ignore(scr);200 return lexany(scr);201}202203void204freescr(scanner *scr)205{206 token *tok, *nxt;207208 if (!scr)209 return;210211 pthread_join(*scr->thread, NULL); /* TODO stop the thread instead. */212 if (pthread_mutex_destroy(scr->qmutex))213 die("pthread_mutex_destroy failed");214215 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 }222223 if (sem_destroy(scr->fullsem) ||224 sem_destroy(scr->emptysem))225 die("sem_destroy failed");226227 free(scr->fullsem);228 free(scr->emptysem);229 free(scr->qmutex);230 free(scr->input);231 free(scr);232}233234scanner*235scanstr(char *str)236{237 scanner *scr;238239 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));248249 scr->fullsem = emalloc(sizeof(sem_t));250 scr->emptysem = emalloc(sizeof(sem_t));251252 /* 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");256257 TAILQ_INIT(&scr->qhead);258 TAILQ_INSERT_TAIL(&scr->qhead, &septok, toks);259260 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");264265 return scr;266}267268token*269nxttok(scanner *scr)270{271 token *tok;272273 if (scr->eof) return NULL;274275 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);282283 if (tok->type == TOK_EOF)284 scr->eof = 1;285286 return tok;287}