#ifndef OD_PARSER_H #define OD_PARSER_H /* * Odissey. * * Advanced PostgreSQL connection pooler. */ typedef struct od_token od_token_t; typedef struct od_keyword od_keyword_t; typedef struct od_parser od_parser_t; enum { OD_PARSER_EOF, OD_PARSER_ERROR, OD_PARSER_NUM, OD_PARSER_KEYWORD, OD_PARSER_SYMBOL, OD_PARSER_STRING }; struct od_token { int type; int line; union { uint64_t num; struct { char *pointer; int size; } string; } value; }; struct od_keyword { int id; char *name; int name_len; }; #define od_keyword(name, token) \ { token, name, sizeof(name) - 1 } struct od_parser { char *pos; char *end; od_token_t backlog[4]; int backlog_count; int line; }; static inline void od_parser_init(od_parser_t *parser, char *string, int size) { parser->pos = string; parser->end = string + size; parser->line = 0; parser->backlog_count = 0; } static inline void od_parser_push(od_parser_t *parser, od_token_t *token) { assert(parser->backlog_count < 4); parser->backlog[parser->backlog_count] = *token; parser->backlog_count++; } static inline int od_parser_next(od_parser_t *parser, od_token_t *token) { /* try to use backlog */ if (parser->backlog_count > 0) { *token = parser->backlog[parser->backlog_count - 1]; parser->backlog_count--; return token->type; } /* skip white spaces and comments */ for (;;) { while (parser->pos < parser->end && isspace(*parser->pos)) { if (*parser->pos == '\n') parser->line++; parser->pos++; } if (od_unlikely(parser->pos == parser->end)) { token->type = OD_PARSER_EOF; return token->type; } if (*parser->pos != '#') break; while (parser->pos < parser->end && *parser->pos != '\n') parser->pos++; if (parser->pos == parser->end) { token->type = OD_PARSER_EOF; return token->type; } parser->line++; } /* symbols */ if (*parser->pos != '\"' && ispunct(*parser->pos)) { token->type = OD_PARSER_SYMBOL; token->line = parser->line; token->value.num = *parser->pos; parser->pos++; return token->type; } /* digit */ if (isdigit(*parser->pos)) { token->type = OD_PARSER_NUM; token->line = parser->line; token->value.num = 0; while (parser->pos < parser->end && isdigit(*parser->pos)) { token->value.num = (token->value.num * 10) + *parser->pos - '0'; parser->pos++; } return token->type; } /* keyword */ if (isalpha(*parser->pos)) { token->type = OD_PARSER_KEYWORD; token->line = parser->line; token->value.string.pointer = parser->pos; while (parser->pos < parser->end && (*parser->pos == '_' || isalpha(*parser->pos) || isdigit(*parser->pos))) parser->pos++; token->value.string.size = parser->pos - token->value.string.pointer; return token->type; } /* string */ if (*parser->pos == '\"') { token->type = OD_PARSER_STRING; token->line = parser->line; parser->pos++; token->value.string.pointer = parser->pos; while (parser->pos < parser->end && *parser->pos != '\"') { if (*parser->pos == '\n') { token->type = OD_PARSER_ERROR; return token->type; } parser->pos++; } if (od_unlikely(parser->pos == parser->end)) { token->type = OD_PARSER_ERROR; return token->type; } token->value.string.size = parser->pos - token->value.string.pointer; parser->pos++; return token->type; } /* error */ token->type = OD_PARSER_ERROR; token->line = parser->line; return token->type; } static inline od_keyword_t* od_keyword_match(od_keyword_t *list, od_token_t *token) { od_keyword_t *current = &list[0]; for (; current->name; current++) { if (current->name_len != token->value.string.size) continue; if (strncasecmp(current->name, token->value.string.pointer, token->value.string.size) == 0) return current; } return NULL; } #endif /* OD_PARSER_H */