123 lines
3.8 KiB
C
123 lines
3.8 KiB
C
#include "arctic.h"
|
|
|
|
#include <string.h>
|
|
|
|
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;
|
|
}
|
|
}
|