#include "aplwc_internal.h" struct scan_state { struct string *text; size_t offset; int skip_number_and_identifier; }; static void init_scan_state(struct scan_state *state, struct string *text) { state->text = text; state->offset = 0; state->skip_number_and_identifier = 0; } static int is_space(uint32_t c) { return c == ' '; } static int is_letter(uint32_t c) { return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')); } static int is_digit(uint32_t c) { return (c >= '0') && (c <= '9'); } static uint32_t peek(struct scan_state *state) { return string_char(state->text, state->offset); } static uint32_t advance(struct scan_state *state) { state->offset += char_length(state->text, state->offset); state->skip_number_and_identifier = 0; return peek(state); } static void skip_space(struct scan_state *state) { while(is_space(peek(state))) advance(state); } static int single_sym(struct scan_state *state, struct token *token) { uint32_t c; c = peek(state); switch(c){ case 0x2190: token->tag = TOKEN_TAG_LEFT_ARROW; break; case 0x22C4: token->tag = TOKEN_TAG_DIAMOND; break; case 0x2207: token->tag = TOKEN_TAG_DEL; break; case 0x2336: token->tag = TOKEN_TAG_IBEAM; break; case 0x2395: token->tag = TOKEN_TAG_QUAD; break; case '\n': token->tag = TOKEN_TAG_NEWLINE; break; case '(': token->tag = TOKEN_TAG_LEFT_PAREN; break; case ')': token->tag = TOKEN_TAG_RIGHT_PAREN; break; default: token->tag = TOKEN_TAG_UNKNOWN_SYM; break; } advance(state); return 1; } static int identifier(struct scan_state *state, struct token *token) { uint32_t c; if(state->skip_number_and_identifier || (!is_letter(peek(state)))) return 0; do c = advance(state); while(is_letter(c) || is_digit(c) || (c == '_')); token->tag = TOKEN_TAG_IDENTIFIER; state->skip_number_and_identifier = 1; return 1; } static int keyword(struct scan_state *state, struct token *token) { uint32_t c; if(peek(state) != ':') return 0; do c = advance(state); while(is_letter(c)); token->tag = TOKEN_TAG_KEYWORD; return 1; } static int number(struct scan_state *state, struct token *token) { uint32_t c; int has_digits; c = peek(state); if(state->skip_number_and_identifier) return 0; has_digits = 0; if(c == 0xAF) /* High minus */ c = advance(state); if(is_digit(c)){ /* integer part */ has_digits = 1; do c = advance(state); while(is_digit(c)); } if(c == '.'){ c = advance(state); if(is_digit(c)){ /* fractional part */ has_digits = 1; do c = advance(state); while(is_digit(c)); } } if(has_digits == 0) return 0; token->tag = TOKEN_TAG_NUMBER; state->skip_number_and_identifier = 1; return 1; } static int text(struct scan_state *state, struct token *token) { uint32_t c; c = peek(state); if(c != '\'') return 0; do c = advance(state); while((c != '\n') && (c != 0) && (c != '\'')); if(c == '\'') advance(state); else return 0; token->tag = TOKEN_TAG_TEXT; return 1; } static enum aplwc_rc next_token(struct scan_state *state, struct token *token) { size_t i; int (*scans[])(struct scan_state *, struct token *token) = { identifier, keyword, number, text, single_sym /* Must be last */ }; skip_space(state); token->start = state->offset; for(i = 0; i < NELEM(scans); i++){ if(scans[i](state, token)) break; state->offset = token->start; } token->end = state->offset; skip_space(state); if(token->start == token->end) return APLWC_INVALID_SYNTAX; else return APLWC_OK; } static enum aplwc_rc check(struct string *text, size_t *n) { struct scan_state state; struct token token; enum aplwc_rc ret; size_t count; init_scan_state(&state, text); for(count = 0; peek(&state); count++){ ret = next_token(&state, &token); if(ret != APLWC_OK) return ret; } *n = count; return APLWC_OK; } static void scan(struct string *text, struct token_list *tokens) { struct scan_state state; enum aplwc_rc ret; size_t count; init_scan_state(&state, text); for(count = 0; peek(&state); count++){ ret = next_token(&state, &tokens->tokens[count]); assert(ret == APLWC_OK); } } struct token_list * scan_string(struct aplwc *aplwc, struct string *text, int oneline) { enum aplwc_rc rc; struct token_list *tokens; size_t n, i; rc = check(text, &n); if(rc != APLWC_OK){ aplwc->last_error = rc; return NULL; } tokens = memory_alloc_token_list(aplwc, n); if(tokens == NULL) return NULL; tokens->text = text; tokens->length = n; scan(text, tokens); for(i = 0; oneline && (i < n); i++){ if(tokens->tokens[i].tag != TOKEN_TAG_NEWLINE) continue; for(i++; i < n; i++){ if(tokens->tokens[i].tag != TOKEN_TAG_NEWLINE) return NULL; } } return tokens; } enum aplwc_rc check_tokens(struct token_list *tokens) { size_t i; enum aplwc_rc rc; for(i = 0, rc = APLWC_OK; (rc == APLWC_OK) && (i < tokens->length); i++){ switch(tokens->tokens[i].tag){ case TOKEN_TAG_UNKNOWN_SYM: rc = APLWC_INVALID_SYNTAX; break; default: break; } } return rc; }