#include "arctic.h" #include const char ARCTIC_CODE_PAGE[97] = "0123456789ABCDEF" "GHIJKLMNOPQRSTUV" "WXYZabcdefghijkl" "mnopqrstuvwxyz_." "+-*/%\\|$@<{=}>,;" "&^![]?~:()'\"` #\n" ; /* ops with an immediate argument */ #define FOR_IMMEDIATE_OPS(X) \ X('0') X('G') X('H') X('L') X('M') X('P') X('Q') X('d') X('o') X('u') \ X('@') X('<') X('{') X('=') X('}') X('>') X(',') X(';') X('[') X(']') #define FOR_PLAIN_OPS(X) \ X('1') X('2') X('3') X('4') X('5') X('6') X('7') X('8') X('9') X('A') \ X('B') X('C') X('D') X('E') X('F') X('I') X('J') X('K') X('N') X('O') \ X('R') X('S') X('U') X('V') X('W') X('X') X('Y') X('Z') X('a') X('b') \ X('c') X('e') X('f') X('g') X('h') X('i') X('j') X('k') X('l') X('m') \ X('n') X('p') X('q') X('r') X('s') X('t') X('v') X('w') X('x') X('y') \ X('z') X('_') X('.') X('+') X('-') X('*') X('/') X('%') X('\\') X('|') \ X('&') X('^') X('!') X('?') X('(') X(')') X('`') #define CASE(X, ...) case X: #define chrncat(str, chr, n, fail) do { \ char *last; \ for (last = str; *last; last++) { \ if (last >= str + n) { \ fail; \ } \ } \ *last++ = chr; \ *last = 0; \ } while (0) enum ArcticErrorCode arctic_scan(struct ArcticScanner *scanner, char next) { switch (scanner->buf[0]) { case 0: /* initial state */ switch (next) { case ' ': return ARCTIC_OK; FOR_IMMEDIATE_OPS(CASE) case '#': case ':': case '\"': case '\'': scanner->buf[0] = next; scanner->buf[1] = 0; return ARCTIC_OK; FOR_PLAIN_OPS(CASE) case '\n': scanner->op_callback(next, 0, scanner->data); return ARCTIC_OK; default: return ARCTIC_UNEXPECTED_CHAR; } case '#': /* comment */ if (next == '\n') scanner->buf[0] = 0; return ARCTIC_OK; case '"': /* section switch */ if (next == '"') { scanner->section_callback(scanner->buf + 1, scanner->data); scanner->buf[0] = 0; } else { chrncat( scanner->buf, next, ARCTIC_BUFSIZE, return ARCTIC_BUFFER_FULL ); } return ARCTIC_OK; case ':': /* label name */ if (next == ' ' || next == '\n' || next == '#') { scanner->label_callback(scanner->buf + 1, scanner->data); scanner->buf[0] = (next == '#' ? '#' : 0); } else { chrncat( scanner->buf, next, ARCTIC_BUFSIZE, return ARCTIC_BUFFER_FULL ); } return ARCTIC_OK; case '\'': /* data injection */ if (next == '\'') { scanner->data_callback(scanner->buf + 1, scanner->data); scanner->buf[0] = 0; } else { chrncat( scanner->buf, next, ARCTIC_BUFSIZE, return ARCTIC_BUFFER_FULL ); } return ARCTIC_OK; FOR_IMMEDIATE_OPS(CASE) /* immediate ops */ if (next == ' ' || next == '\n' || next == '#') { scanner->op_callback(scanner->buf[0], scanner->buf + 1, scanner->data); scanner->buf[0] = (next == '#' ? '#' : 0); } else { chrncat( scanner->buf, next, ARCTIC_BUFSIZE, return ARCTIC_BUFFER_FULL ); } return ARCTIC_OK; default: return ARCTIC_INVALID_STATE; } }