<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--cudl.c167
-rw-r--r--cudl.h49
-rw-r--r--spec.txt2
-rw-r--r--test.c2
5 files changed, 187 insertions, 37 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ebfaa61
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,4 @@
+CC=tcc
+BIN=test
+
+test: test.o
diff --git a/cudl.c b/cudl.c
index 9c71d57..02e7b1d 100644
--- a/cudl.c
+++ b/cudl.c
@@ -1,36 +1,11 @@
#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "cudl.h"
-struct cudl_array_value {
- struct cudl_value *values;
- size_t length;
-};
+#define STRIP_WHITESPACE(text) while (isspace(*text)) text++
-struct cudl_map_value {
- struct cudl_map_field {
- char *key;
- struct cudl_value value;
- } *fields;
- size_t length;
-};
-
-struct cudl_value {
- union {
- char *string;
- double number;
- int boolean;
- struct array_value array;
- struct map_value map;
- } data;
- int tag;
-};
-
-#define CUDL_TAG_NULL 0
-#define CUDL_TAG_ARRAY 1
-
-#define CUDL_OK 0
-#define CUDL_ERR_OUT_OF_MEMORY 1
-#define CUDL_ERR_MISSING_VALUE 2
-#define CUDL_ERR_READING 3
+int cudl_err = CUDL_OK;
static char *fread_all(FILE *file) {
size_t size;
@@ -49,7 +24,28 @@ static char *fread_all(FILE *file) {
return buffer;
}
-void cudl_debug(struct cudl_value *value) {
+void cudl_debug(struct cudl_value value) {
+ int i;
+ switch (value.tag) {
+ case CUDL_TAG_NULL:
+ printf("%%null");
+ break;
+ case CUDL_TAG_BOOL:
+ if (value.data.boolean)
+ printf("%%true");
+ else
+ printf("%%false");
+ break;
+ case CUDL_TAG_ARRAY:
+ printf("[");
+ for (i = 0; i < value.data.array.length; i++)
+ cudl_debug(value.data.array.values[i]);
+ printf("]");
+ break;
+ default:
+ printf("UNKNOWN");
+ break;
+ }
}
/* Free all children of the value, not the value itself */
@@ -68,16 +64,115 @@ void cudl_deinit_value(struct cudl_value value) {
}
}
-int cudl_parse_from_file(FILE *file, struct cudl_value *value) {
+/* Parse a value from input and store it in value.
+ * Return the number of bytes consumed.
+ * Input must end with a null byte */
+size_t parse_value(char *input, struct cudl_value *value);
+
+static size_t parse_bool_or_null(char *input, struct cudl_value *value) {
+ if (strncmp(input, "null", 4)) {
+ value->tag = CUDL_TAG_NULL;
+ return 4;
+ }
+ if (strncmp(input, "true", 4)) {
+ value->tag = CUDL_TAG_BOOL;
+ value->data.boolean = 1;
+ return 4;
+ }
+ if (strncmp(input, "false", 5)) {
+ value->tag = CUDL_TAG_BOOL;
+ value->data.boolean = 0;
+ return 5;
+ }
+ cudl_err = CUDL_ERR_EXPECTED_BOOL_OR_NULL;
+ return 0;
+}
+
+static size_t parse_array(char *input, struct cudl_value *value) {
+ size_t length, capacity;
+ struct cudl_value *values, *newvalues;
+ int i;
+ char *original_input;
+
+ original_input = input;
+ value->tag = CUDL_TAG_ARRAY;
+ length = 0;
+ capacity = 8;
+ if ((values = malloc(capacity * sizeof(struct cudl_value))) == NULL) {
+ cudl_err = CUDL_ERR_OUT_OF_MEMORY;
+ return 0;
+ }
+
+ STRIP_WHITESPACE(input);
+ for (;;) {
+ if (*input == '\0') {
+ cudl_err = CUDL_ERR_UNMATCHED_BRACK;
+ for (i = 0; i < length; i++)
+ cudl_deinit_value(values[i]);
+ free(values);
+ return 0;
+ } else if (*input == ']') {
+ input++;
+ values = realloc(values, length * sizeof(struct cudl_value));
+ value->data.array.length = length;
+ value->data.array.values = values;
+ return input - original_input;
+ }
+ if (length >= capacity) {
+ if ((newvalues = realloc(values, 2 * capacity * sizeof(struct cudl_value))) == NULL) {
+ cudl_err = CUDL_ERR_OUT_OF_MEMORY;
+ for (i = 0; i < length; i++)
+ cudl_deinit_value(values[i]);
+ free(values);
+ return 0;
+ }
+ capacity *= 2;
+ }
+ input += parse_value(input, values + length);
+ if (cudl_err) {
+ for (i = 0; i < length; i++) {
+ cudl_deinit_value(values[i]);
+ }
+ free(values);
+ return 0;
+ }
+ length++;
+ }
+}
+
+static size_t _parse_value(char *input, struct cudl_value *value) {
+ if (*input == '%')
+ return parse_bool_or_null(++input, value) + 1;
+ if (*input == '[')
+ return parse_array(++input, value) + 1;
+ cudl_err = CUDL_ERR_UNRECOGNISED_VALUE;
+ return 0;
+}
+
+static size_t parse_value(char *input, struct cudl_value *value) {
+ char *original_input;
+ original_input = input;
+ input += _parse_value(input, value);
+ STRIP_WHITESPACE(input);
+ return input - original_input;
+}
+
+void cudl_parse_from_file(FILE *file, struct cudl_value *value) {
char *input;
if ((input = fread_all(file)) == NULL) {
if (ferror(file))
- return CUDL_ERR_READING;
+ cudl_err = CUDL_ERR_READING;
else
- return CUDL_ERR_OUT_OF_MEMORY;
+ cudl_err = CUDL_ERR_OUT_OF_MEMORY;
+ return;
}
- return cudl_parse(input, value);
+ input += cudl_parse(input, value);
+ if (*input != '\0')
+ cudl_deinit_value(*value);
+ free(input);
}
-int cudl_parse(char *input, struct cudl_value *value) {
+size_t cudl_parse(char *input, struct cudl_value *value) {
+ STRIP_WHITESPACE(input);
+ return parse_value(input, value);
}
diff --git a/cudl.h b/cudl.h
new file mode 100644
index 0000000..6586bba
--- /dev/null
+++ b/cudl.h
@@ -0,0 +1,49 @@
+#ifndef cudl_h_INCLUDED
+#define cudl_h_INCLUDED
+
+struct cudl_array_value {
+ struct cudl_value *values;
+ size_t length;
+};
+
+struct cudl_map_value {
+ struct cudl_map_field {
+ char *key;
+ struct cudl_value value;
+ } *fields;
+ size_t length;
+};
+
+struct cudl_value {
+ union {
+ char *string;
+ double number;
+ int boolean;
+ struct array_value array;
+ struct map_value map;
+ } data;
+ int tag;
+};
+
+enum {
+ CUDL_TAG_NULL;
+ CUDL_TAG_BOOL;
+ CUDL_TAG_ARRAY;
+}
+
+enum {
+ CUDL_OK = 0;
+ CUDL_ERR_OUT_OF_MEMORY;
+ CUDL_ERR_EXPECTED_VALUE;
+ CUDL_ERR_READING;
+ CUDL_ERR_EXPECTED_BOOL_OR_NULL;
+};
+
+extern int cudl_err;
+
+void cudl_debug(struct cudl_value value);
+void cudl_deinit_value(struct cudl_value value);
+void cudl_parse_from_file(FILE *file, struct cudl_value *value);
+size_t cudl_parse(char *input, struct cudl_value *value);
+
+#endif // cudl_h_INCLUDED
diff --git a/spec.txt b/spec.txt
index 1efee05..6070415 100644
--- a/spec.txt
+++ b/spec.txt
@@ -28,7 +28,7 @@ A map can be preceeded by nothing and succeeded by a ], which is not consumed.
A sequence of values, no delimeter.
-An array is preceeded by a [ and succeeded by a ]
+An array is preceeded by a [ (optionally followed by whitespace) and succeeded by a ]
## String
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..b2f9976
--- /dev/null
+++ b/test.c
@@ -0,0 +1,2 @@
+int main() {
+}