aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico Angelilli <code@fedang.net>2024-06-05 15:38:09 +0200
committerFederico Angelilli <code@fedang.net>2024-06-05 15:38:09 +0200
commit31757b5cca25297be90fe092176a3296c2dc4f80 (patch)
tree23b4e1eae229b6c34ad3cc60ddd5c471f4d98e7d
parentc3d59b49767602ef2e53cd93f0f963e981af6cdd (diff)
Use stream for parsing sexp
-rw-r--r--any_sexp.h272
-rw-r--r--test/sexp.c5
2 files changed, 153 insertions, 124 deletions
diff --git a/any_sexp.h b/any_sexp.h
index 516b9e5..0c9357d 100644
--- a/any_sexp.h
+++ b/any_sexp.h
@@ -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;
}