summaryrefslogtreecommitdiffstats
path: root/dimension/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'dimension/parse.c')
-rw-r--r--dimension/parse.c231
1 files changed, 229 insertions, 2 deletions
diff --git a/dimension/parse.c b/dimension/parse.c
index 2c1b67c..c868d95 100644
--- a/dimension/parse.c
+++ b/dimension/parse.c
@@ -20,17 +20,244 @@
#include "parse.h"
#include "tokenize.h"
#include "utility.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <locale.h>
+
+static int
+dmnsn_parse_expect(dmnsn_token_type type, const dmnsn_array *tokens,
+ unsigned int *ip)
+{
+ dmnsn_token token;
+
+ if (*ip < dmnsn_array_size(tokens)) {
+ dmnsn_array_get(tokens, *ip, &token);
+ if (token.type != type) {
+ dmnsn_diagnostic(token.filename, token.line, token.col,
+ "Expected '%s', found '%s'",
+ dmnsn_token_name(type),
+ dmnsn_token_name(token.type));
+ return 1;
+ }
+ } else {
+ fprintf(stderr, "Expected '%s', found end of file\n",
+ dmnsn_token_name(type));
+ return 1;
+ }
+
+ ++*ip;
+ return 0;
+}
+
+static int
+dmnsn_parse_float(const dmnsn_array *tokens, unsigned int *ip,
+ dmnsn_array *astree)
+{
+ unsigned int i = *ip;
+
+ if (i >= dmnsn_array_size(tokens)) {
+ fprintf(stderr, "Expected '%s' or '%s', found end of file\n",
+ dmnsn_token_name(DMNSN_T_INTEGER),
+ dmnsn_token_name(DMNSN_T_FLOAT));
+ return 1;
+ }
+
+ int negative = 0;
+ dmnsn_token token;
+ dmnsn_array_get(tokens, i, &token);
+
+ if (token.type == DMNSN_T_MINUS) {
+ negative = 1;
+
+ ++i;
+ if (i >= dmnsn_array_size(tokens)) {
+ fprintf(stderr, "Expected '%s' or '%s', found end of file\n",
+ dmnsn_token_name(DMNSN_T_INTEGER),
+ dmnsn_token_name(DMNSN_T_FLOAT));
+ return 1;
+ }
+ dmnsn_array_get(tokens, i, &token);
+ }
+
+ double *value = malloc(sizeof(double));
+ if (!value)
+ dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't allocate room for float.");
+
+ if (token.type == DMNSN_T_INTEGER || token.type == DMNSN_T_FLOAT) {
+ *value = strtod(token.value, NULL);
+ ++i;
+ } else {
+ fprintf(stderr, "Expected '%s' or '%s', found '%s'\n",
+ dmnsn_token_name(DMNSN_T_INTEGER),
+ dmnsn_token_name(DMNSN_T_FLOAT),
+ dmnsn_token_name(token.type));
+ return 1;
+ }
+
+ if (negative)
+ *value *= -1.0;
+
+ dmnsn_astnode astnode;
+ astnode.type = DMNSN_AST_FLOAT;
+ astnode.children = NULL;
+ astnode.ptr = value;
+
+ dmnsn_array_push(astree, &astnode);
+ *ip = i;
+ return 0;
+}
+
+static int
+dmnsn_parse_vector(const dmnsn_array *tokens, unsigned int *ip,
+ dmnsn_array *astree)
+{
+ unsigned int i = *ip;
+
+ if (dmnsn_parse_expect(DMNSN_T_LESS, tokens, &i) != 0)
+ return 1;
+
+ dmnsn_astnode astnode;
+ astnode.type = DMNSN_AST_VECTOR;
+ astnode.children = dmnsn_new_array(sizeof(dmnsn_astnode));
+ astnode.ptr = NULL;
+
+ /* First element */
+ if (dmnsn_parse_float(tokens, &i, astnode.children) != 0)
+ goto bailout;
+
+ if (dmnsn_parse_expect(DMNSN_T_COMMA, tokens, &i) != 0)
+ goto bailout;
+
+ /* Second element */
+ if (dmnsn_parse_float(tokens, &i, astnode.children) != 0)
+ goto bailout;
+
+ if (dmnsn_parse_expect(DMNSN_T_COMMA, tokens, &i) != 0)
+ goto bailout;
+
+ /* Third element */
+ if (dmnsn_parse_float(tokens, &i, astnode.children) != 0)
+ goto bailout;
+
+ if (dmnsn_parse_expect(DMNSN_T_GREATER, tokens, &i) != 0)
+ goto bailout;
+
+ dmnsn_array_push(astree, &astnode);
+ *ip = i;
+ return 0;
+
+ bailout:
+ dmnsn_delete_astree(astnode.children);
+ return 1;
+}
+
+static int
+dmnsn_parse_box(const dmnsn_array *tokens, unsigned int *ip,
+ dmnsn_array *astree)
+{
+ unsigned int i = *ip;
+
+ if (dmnsn_parse_expect(DMNSN_T_BOX, tokens, &i) != 0)
+ return 1;
+ if (dmnsn_parse_expect(DMNSN_T_LBRACE, tokens, &i) != 0)
+ return 1;
+
+ dmnsn_astnode astnode;
+ astnode.type = DMNSN_AST_BOX;
+ astnode.children = dmnsn_new_array(sizeof(dmnsn_astnode));
+ astnode.ptr = NULL;
+
+ /* First corner */
+ if (dmnsn_parse_vector(tokens, &i, astnode.children) != 0)
+ goto bailout;
+
+ if (dmnsn_parse_expect(DMNSN_T_COMMA, tokens, &i) != 0)
+ goto bailout;
+
+ /* Second corner */
+ if (dmnsn_parse_vector(tokens, &i, astnode.children) != 0)
+ goto bailout;
+
+ if (dmnsn_parse_expect(DMNSN_T_RBRACE, tokens, &i) != 0)
+ goto bailout;
+
+ dmnsn_array_push(astree, &astnode);
+ *ip = i;
+ return 0;
+
+ bailout:
+ dmnsn_delete_astree(astnode.children);
+ return 1;
+}
dmnsn_array *
-dmnsn_parse(dmnsn_array *tokens)
+dmnsn_parse(const dmnsn_array *tokens)
{
+ unsigned int i;
+ dmnsn_array *astree = dmnsn_new_array(sizeof(dmnsn_astnode));
+ dmnsn_token token;
+
+ /* Save the current locale */
+ char *lc_ctype = strdup(setlocale(LC_CTYPE, NULL));
+ char *lc_numeric = strdup(setlocale(LC_NUMERIC, NULL));
+
+ /* Set the locale to `C' to make strtoul(), etc. consistent */
+ setlocale(LC_CTYPE, "C");
+ setlocale(LC_NUMERIC, "C");
+
+ for (i = 0; i < dmnsn_array_size(tokens); ++i) {
+ dmnsn_array_get(tokens, i, &token);
+
+ if (token.type == DMNSN_T_BOX) {
+ if (dmnsn_parse_box(tokens, &i, astree) != 0) {
+ dmnsn_diagnostic(token.filename, token.line, token.col,
+ "Invalid box",
+ dmnsn_token_name(DMNSN_T_BOX),
+ dmnsn_token_name(token.type));
+ goto bailout;
+ }
+ } else {
+ dmnsn_diagnostic(token.filename, token.line, token.col,
+ "Expected '%s', found '%s'",
+ dmnsn_token_name(DMNSN_T_BOX),
+ dmnsn_token_name(token.type));
+ goto bailout;
+ }
+ }
+
+ /* Restore the original locale */
+ setlocale(LC_CTYPE, lc_ctype);
+ setlocale(LC_NUMERIC, lc_numeric);
+ free(lc_ctype);
+ free(lc_numeric);
+
+ return astree;
+
+ bailout:
+ /* Restore the original locale */
+ setlocale(LC_CTYPE, lc_ctype);
+ setlocale(LC_NUMERIC, lc_numeric);
+ free(lc_ctype);
+ free(lc_numeric);
+
+ dmnsn_delete_astree(astree);
return NULL;
}
void
dmnsn_delete_astree(dmnsn_array *astree)
{
- dmnsn_delete_array(astree);
+ unsigned int i;
+ dmnsn_astnode node;
+
+ if (astree) {
+ for (i = 0; i < dmnsn_array_size(astree); ++i) {
+ dmnsn_array_get(astree, i, &node);
+ dmnsn_delete_astree(node.children);
+ free(node.ptr);
+ }
+ dmnsn_delete_array(astree);
+ }
}
void