diff options
| author | Federico Angelilli <code@fedang.net> | 2024-06-05 15:38:09 +0200 |
|---|---|---|
| committer | Federico Angelilli <code@fedang.net> | 2024-06-05 15:38:09 +0200 |
| commit | 31757b5cca25297be90fe092176a3296c2dc4f80 (patch) | |
| tree | 23b4e1eae229b6c34ad3cc60ddd5c471f4d98e7d | |
| parent | c3d59b49767602ef2e53cd93f0f963e981af6cdd (diff) | |
Use stream for parsing sexp
| -rw-r--r-- | any_sexp.h | 272 | ||||
| -rw-r--r-- | test/sexp.c | 5 |
2 files changed, 153 insertions, 124 deletions
@@ -28,8 +28,11 @@ typedef struct any_sexp { #define ANY_SEXP_ERROR ((any_sexp_t){ ANY_SEXP_TAG_ERROR }) #define ANY_SEXP_GET_TAG(sexp) ((sexp).tag) +#define ANY_SEXP_GET_CONS(sexp) ((sexp).cons) +#define ANY_SEXP_GET_SYMBOL(sexp) ((sexp).symbol) #else + typedef void *any_sexp_t; #define ANY_SEXP_NIL (any_sexp_t)NULL @@ -38,6 +41,9 @@ typedef void *any_sexp_t; #define ANY_SEXP_UNTAG(sexp) (any_sexp_t)((uintptr_t)(sexp) & 0x0fffffffffffffff) #define ANY_SEXP_TAG(sexp, tag) (any_sexp_t)((uintptr_t)(sexp) | ((uintptr_t)tag << 60)) #define ANY_SEXP_GET_TAG(sexp) (((uintptr_t)(sexp) >> 60) & 0xf) +#define ANY_SEXP_GET_CONS(sexp) ((any_sexp_cons_t *)ANY_SEXP_UNTAG(sexp)) +#define ANY_SEXP_GET_SYMBOL(sexp) (((char *)ANY_SEXP_UNTAG(sexp)) + #endif #define ANY_SEXP_IS_TAG(sexp, tag) (ANY_SEXP_GET_TAG(sexp) == (tag)) @@ -46,18 +52,38 @@ typedef void *any_sexp_t; #define ANY_SEXP_IS_CONS(sexp) (ANY_SEXP_IS_TAG(sexp, ANY_SEXP_TAG_CONS)) #define ANY_SEXP_IS_QUOTE(sexp) (ANY_SEXP_IS_TAG(sexp, ANY_SEXP_TAG_QUOTE)) +// NOTE: Use it only after checking the tag! +#define ANY_SEXP_GET_CAR(sexp) (ANY_SEXP_GET_CONS(sexp)->car) +#define ANY_SEXP_GET_CDR(sexp) (ANY_SEXP_GET_CONS(sexp)->cdr) + typedef struct any_sexp_cons { any_sexp_t car; any_sexp_t cdr; } any_sexp_cons_t; +#ifndef ANY_SEXP_SYMBOL_MAX +#define ANY_SEXP_SYMBOL_MAX 256 +#endif + +typedef char (*any_sexp_getchar_t)(void *stream); + typedef struct { const char *source; size_t length; size_t cursor; +} any_sexp_string_stream_t; + +char any_sexp_string_getc(any_sexp_string_stream_t *stream); + +typedef struct { + any_sexp_getchar_t getc; + void *stream; + char c; } any_sexp_parser_t; -void any_sexp_parser_init(any_sexp_parser_t *parser, const char *source, size_t length); +void any_sexp_parser_init(any_sexp_parser_t *parser, any_sexp_getchar_t getc, void *stream); + +void any_sexp_parser_init_string(any_sexp_parser_t *parser, any_sexp_string_stream_t *stream, const char *source, size_t length); bool any_sexp_parser_eof(any_sexp_parser_t *parser); @@ -87,34 +113,13 @@ void any_sexp_free(any_sexp_t sexp); #define ANY_SEXP_FREE free #endif -void any_sexp_parser_init(any_sexp_parser_t *parser, const char *source, size_t length) -{ - parser->source = source; - parser->length = length; - parser->cursor = 0; -} - -bool any_sexp_parser_eof(any_sexp_parser_t *parser) -{ - return parser->cursor >= parser->length; -} - -void any_sexp_skip(any_sexp_parser_t *parser) -{ - while (!any_sexp_parser_eof(parser)) { -#ifndef ANY_SEXP_NO_COMMENT - if (parser->source[parser->cursor] == ';') { - while (!any_sexp_parser_eof(parser) && parser->source[parser->cursor] != '\n') - parser->cursor++; - } +#ifndef ANY_SEXP_CHAR_COMMENT +#define ANY_SEXP_CHAR_COMMENT ';' #endif - if (!isspace(parser->source[parser->cursor])) - return; - - parser->cursor++; - } -} +#ifndef ANY_SEXP_CHAR_QUOTE +#define ANY_SEXP_CHAR_QUOTE '\'' +#endif // Extended alphabetic characters // ! $ % & * + - . / : < = > ? @ ^ _ ~ @@ -137,90 +142,143 @@ static inline bool any_sexp_issym(char c) } } -static any_sexp_t any_sexp_parser_symbol(any_sexp_parser_t *parser) +static inline char any_sexp_parser_advance(any_sexp_parser_t *parser) { - size_t cursor = parser->cursor; - - while (!any_sexp_parser_eof(parser) && any_sexp_issym(parser->source[parser->cursor])) - parser->cursor++; - - size_t length = parser->cursor - cursor; - char *sym = ANY_SEXP_MALLOC(length + 1); - memcpy(sym, parser->source + cursor, length); - sym[length] = '\0'; + parser->c = parser->getc(parser->stream); + return parser->c; +} -#ifndef ANY_SEXP_NO_BOXING - return ANY_SEXP_TAG(sym, ANY_SEXP_TAG_SYMBOL); -#else - any_sexp_t sexp = { - .tag = ANY_SEXP_TAG_SYMBOL, - .symbol = sym, - }; - return sexp; +static void any_sexp_parser_skip(any_sexp_parser_t *parser) +{ + while (!any_sexp_parser_eof(parser)) { +#ifndef ANY_SEXP_NO_COMMENT + if (parser->c == ANY_SEXP_CHAR_COMMENT) { + while (!any_sexp_parser_eof(parser) && parser->c != '\n') + any_sexp_parser_advance(parser); + } #endif + if (!isspace(parser->c)) + return; + + any_sexp_parser_advance(parser); + } } -static any_sexp_t any_sexp_parser_quote(any_sexp_parser_t *parser) +void any_sexp_parser_init(any_sexp_parser_t *parser, any_sexp_getchar_t getc, void *stream) { - // TODO: Maybe this is not the most efficient way... + parser->getc = getc; + parser->stream = stream; + any_sexp_parser_advance(parser); +} - parser->cursor++; - any_sexp_t *quote = ANY_SEXP_MALLOC(sizeof(any_sexp_t)); - if (quote == NULL) return ANY_SEXP_ERROR; - *quote = any_sexp_parser_next(parser); +void any_sexp_parser_init_string(any_sexp_parser_t *parser, any_sexp_string_stream_t *stream, const char *source, size_t length) +{ + stream->source = source; + stream->length = length; + stream->cursor = 0; + any_sexp_parser_init(parser, (any_sexp_getchar_t)any_sexp_string_getc, stream); +} -#ifndef ANY_SEXP_NO_BOXING - return (any_sexp_t )ANY_SEXP_TAG(quote, ANY_SEXP_TAG_QUOTE); -#else - any_sexp_t sexp = { - .tag = ANY_SEXP_TAG_QUOTE, - .quote = quote, - }; - return sexp; -#endif +bool any_sexp_parser_eof(any_sexp_parser_t *parser) +{ + return parser->c == EOF; } any_sexp_t any_sexp_parser_next(any_sexp_parser_t *parser) { - any_sexp_skip(parser); + any_sexp_parser_skip(parser); - if (any_sexp_parser_eof(parser)) - return ANY_SEXP_ERROR; + // Symbol + if (any_sexp_issym(parser->c)) { + char buffer[ANY_SEXP_SYMBOL_MAX + 1]; + size_t length = 0; - if (any_sexp_issym(parser->source[parser->cursor])) - return any_sexp_parser_symbol(parser); + do { + if (length < ANY_SEXP_SYMBOL_MAX) + buffer[length++] = parser->c; - if (parser->source[parser->cursor] == '\'') - return any_sexp_parser_quote(parser); + any_sexp_parser_advance(parser); + } while (any_sexp_issym(parser->c)); - if (parser->source[parser->cursor] != '(') - return ANY_SEXP_ERROR; + char *symbol = ANY_SEXP_MALLOC(length + 1); + if (symbol == NULL) + return ANY_SEXP_ERROR; - parser->cursor++; - any_sexp_t sexp = ANY_SEXP_NIL; + memcpy(symbol, buffer, length); + symbol[length] = '\0'; - any_sexp_skip(parser); - while (!any_sexp_parser_eof(parser) && parser->source[parser->cursor] != ')') { +#ifndef ANY_SEXP_NO_BOXING + return ANY_SEXP_TAG(symbol, ANY_SEXP_TAG_SYMBOL); +#else + any_sexp_t sexp = { + .tag = ANY_SEXP_TAG_SYMBOL, + .symbol = symbol, + }; + return sexp; +#endif + } - any_sexp_t sub = any_sexp_parser_next(parser); - sexp = any_sexp_cons(sub, sexp); // reversed +#ifndef ANY_SEXP_NO_QUOTE + // Quote + if (parser->c == ANY_SEXP_CHAR_QUOTE) { + any_sexp_parser_advance(parser); - if (ANY_SEXP_IS_ERROR(sub) || ANY_SEXP_IS_ERROR(sexp)) { - any_sexp_free(sexp); + any_sexp_t *quote = ANY_SEXP_MALLOC(sizeof(any_sexp_t)); + if (quote == NULL) return ANY_SEXP_ERROR; + + *quote = any_sexp_parser_next(parser); + +#ifndef ANY_SEXP_NO_BOXING + return (any_sexp_t )ANY_SEXP_TAG(quote, ANY_SEXP_TAG_QUOTE); +#else + any_sexp_t sexp = { + .tag = ANY_SEXP_TAG_QUOTE, + .quote = quote, + }; + return sexp; +#endif + } +#endif + + // List + if (parser->c == '(') { + any_sexp_parser_advance(parser); + any_sexp_parser_skip(parser); + + any_sexp_t sexp = ANY_SEXP_NIL; + while (!any_sexp_parser_eof(parser) && parser->c != ')') { + any_sexp_t sub = any_sexp_parser_next(parser); + sexp = any_sexp_cons(sub, sexp); // reversed + + if (ANY_SEXP_IS_ERROR(sub) || ANY_SEXP_IS_ERROR(sexp)) { + any_sexp_free(sexp); + return ANY_SEXP_ERROR; + } + + any_sexp_parser_skip(parser); } - any_sexp_skip(parser); + any_sexp_parser_advance(parser); + return any_sexp_reverse(sexp); } - parser->cursor++; - return any_sexp_reverse(sexp); + return ANY_SEXP_ERROR; +} + +char any_sexp_string_getc(any_sexp_string_stream_t *stream) +{ + if (stream->cursor >= stream->length) + return EOF; + + return stream->source[stream->cursor++]; } any_sexp_t any_sexp_cons(any_sexp_t car, any_sexp_t cdr) { any_sexp_cons_t *cons = ANY_SEXP_MALLOC(sizeof(any_sexp_cons_t)); - if (cons == NULL) return ANY_SEXP_ERROR; + if (cons == NULL) + return ANY_SEXP_ERROR; cons->car = car; cons->cdr = cdr; @@ -240,26 +298,14 @@ any_sexp_t any_sexp_car(any_sexp_t sexp) { if (!ANY_SEXP_IS_CONS(sexp)) return ANY_SEXP_ERROR; - -#ifndef ANY_SEXP_NO_BOXING - any_sexp_cons_t *cons = (any_sexp_cons_t *)ANY_SEXP_UNTAG(sexp); - return cons->car; -#else - return sexp.cons->car; -#endif + return ANY_SEXP_GET_CAR(sexp); } any_sexp_t any_sexp_cdr(any_sexp_t sexp) { if (!ANY_SEXP_IS_CONS(sexp)) return ANY_SEXP_ERROR; - -#ifndef ANY_SEXP_NO_BOXING - any_sexp_cons_t *cons = (any_sexp_cons_t *)ANY_SEXP_UNTAG(sexp); - return cons->cdr; -#else - return sexp.cons->cdr; -#endif + return ANY_SEXP_GET_CDR(sexp); } any_sexp_t any_sexp_reverse(any_sexp_t sexp) @@ -270,32 +316,19 @@ any_sexp_t any_sexp_reverse(any_sexp_t sexp) if (!ANY_SEXP_IS_CONS(sexp)) return ANY_SEXP_ERROR; -#ifndef ANY_SEXP_NO_BOXING - any_sexp_cons_t *cons = (any_sexp_cons_t *)ANY_SEXP_UNTAG(sexp); - any_sexp_t prev = ANY_SEXP_NIL, next = ANY_SEXP_NIL; + any_sexp_t cons = sexp, + prev = ANY_SEXP_NIL, + next = ANY_SEXP_NIL; - while (cons != NULL) { + while (ANY_SEXP_GET_CONS(cons) != NULL) { if (!ANY_SEXP_IS_NIL(cons) && !ANY_SEXP_IS_CONS(cons)) return ANY_SEXP_ERROR; - next = (any_sexp_cons_t *)ANY_SEXP_UNTAG(cons->cdr); - cons->cdr = prev; - prev = ANY_SEXP_TAG(cons, ANY_SEXP_TAG_CONS); - cons = next; - } -#else - any_sexp_t cons = sexp, prev = ANY_SEXP_NIL, next = ANY_SEXP_NIL; - - while (cons.cons != NULL) { - if (!ANY_SEXP_IS_NIL(cons) && !ANY_SEXP_IS_CONS(cons)) - return ANY_SEXP_ERROR; - - memcpy(&next, &cons.cons->cdr, sizeof(any_sexp_t)); - memcpy(&cons.cons->cdr, &prev, sizeof(any_sexp_t)); + memcpy(&next, &ANY_SEXP_GET_CDR(cons), sizeof(any_sexp_t)); + memcpy(&ANY_SEXP_GET_CDR(cons), &prev, sizeof(any_sexp_t)); memcpy(&prev, &cons, sizeof(any_sexp_t)); memcpy(&cons, &next, sizeof(any_sexp_t)); } -#endif return prev; } @@ -350,17 +383,11 @@ void any_sexp_free(any_sexp_t sexp) case ANY_SEXP_TAG_CONS: any_sexp_free(any_sexp_car(sexp)); any_sexp_free(any_sexp_cdr(sexp)); -#ifndef ANY_SEXP_NO_BOXING - ANY_SEXP_FREE(ANY_SEXP_UNTAG(sexp)); -#endif + ANY_SEXP_FREE(ANY_SEXP_GET_CONS(sexp)); break; case ANY_SEXP_TAG_SYMBOL: -#ifndef ANY_SEXP_NO_BOXING - ANY_SEXP_FREE(ANY_SEXP_UNTAG(sexp)); -#else - ANY_SEXP_FREE(sexp.symbol); -#endif + ANY_SEXP_FREE(ANY_SEXP_GET_SYMBOL(sexp)); break; case ANY_SEXP_TAG_QUOTE: @@ -369,6 +396,7 @@ void any_sexp_free(any_sexp_t sexp) ANY_SEXP_FREE(ANY_SEXP_UNTAG(sexp)); #else any_sexp_free(*sexp.quote); + ANY_SEXP_FREE(sexp.quote); #endif break; } diff --git a/test/sexp.c b/test/sexp.c index c71d4fa..3edceb0 100644 --- a/test/sexp.c +++ b/test/sexp.c @@ -14,8 +14,9 @@ int main() "'symbol 'another 'a\n" "'(a b c) '('a) ''x\n"; + any_sexp_string_stream_t stream; any_sexp_parser_t parser; - any_sexp_parser_init(&parser, s, strlen(s)); + any_sexp_parser_init_string(&parser, &stream, s, strlen(s)); any_sexp_t sexp = any_sexp_parser_next(&parser); while (!ANY_SEXP_IS_ERROR(sexp)) { @@ -25,7 +26,7 @@ int main() sexp = any_sexp_parser_next(&parser); } - printf("%zu\n", sizeof(any_sexp_t)); + //printf("%zu\n", sizeof(any_sexp_t)); return 0; } |
