]> git.lizzy.rs Git - nothing.git/blobdiff - src/script/expr.c
(#395) Make region print eval errors on stdout
[nothing.git] / src / script / expr.c
index 51c809efe5cde4b416522564e9fdf3a071af452c..bf25ba17f8d2cb5340f655f51cc9d714154cb0f9 100644 (file)
@@ -1,10 +1,13 @@
 #include <assert.h>
+#include <ctype.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "script/expr.h"
+#include "script/gc.h"
+#include "str.h"
 
 struct Expr atom_as_expr(struct Atom *atom)
 {
@@ -26,11 +29,76 @@ struct Expr cons_as_expr(struct Cons *cons)
     return expr;
 }
 
-struct Expr create_expr_from_str(const char *str)
+struct Expr void_expr(void)
 {
-    /* TODO(#283): create_expr_from_str is not implemented */
-    assert(str);
-    return atom_as_expr(0);
+    struct Expr expr = {
+        .type = EXPR_VOID
+    };
+
+    return expr;
+}
+
+void print_atom_as_sexpr(FILE *stream, struct Atom *atom)
+{
+    assert(atom);
+
+    switch (atom->type) {
+    case ATOM_SYMBOL:
+        fprintf(stream, "%s", atom->sym);
+        break;
+
+    case ATOM_NUMBER:
+        fprintf(stream, "%ld", atom->num);
+        break;
+
+    case ATOM_STRING:
+        fprintf(stream, "\"%s\"", atom->str);
+        break;
+
+    case ATOM_NATIVE:
+        fprintf(stream, "<native>");
+        break;
+    }
+}
+
+void print_cons_as_sexpr(FILE *stream, struct Cons *head)
+{
+    assert(head);
+
+    struct Cons *cons = head;
+
+    fprintf(stream, "(");
+    print_expr_as_sexpr(stream, cons->car);
+
+    while (cons->cdr.type == EXPR_CONS) {
+        cons = cons->cdr.cons;
+        fprintf(stream, " ");
+        print_expr_as_sexpr(stream, cons->car);
+    }
+
+    if (cons->cdr.atom->type != ATOM_SYMBOL ||
+        strcmp("nil", cons->cdr.atom->sym) != 0) {
+        fprintf(stream, " . ");
+        print_expr_as_sexpr(stream, cons->cdr);
+    }
+
+    fprintf(stream, ")");
+}
+
+void print_expr_as_sexpr(FILE *stream, struct Expr expr)
+{
+    switch (expr.type) {
+    case EXPR_ATOM:
+        print_atom_as_sexpr(stream, expr.atom);
+        break;
+
+    case EXPR_CONS:
+        print_cons_as_sexpr(stream, expr.cons);
+        break;
+
+    case EXPR_VOID:
+        break;
+    }
 }
 
 void destroy_expr(struct Expr expr)
@@ -43,10 +111,13 @@ void destroy_expr(struct Expr expr)
     case EXPR_CONS:
         destroy_cons(expr.cons);
         break;
+
+    case EXPR_VOID:
+        break;
     }
 }
 
-struct Cons *create_cons(struct Expr car, struct Expr cdr)
+struct Cons *create_cons(Gc *gc, struct Expr car, struct Expr cdr)
 {
     struct Cons *cons = malloc(sizeof(struct Cons));
     if (cons == NULL) {
@@ -56,50 +127,124 @@ struct Cons *create_cons(struct Expr car, struct Expr cdr)
     cons->car = car;
     cons->cdr = cdr;
 
+    if (gc_add_expr(gc, cons_as_expr(cons)) < 0) {
+        free(cons);
+        return NULL;
+    }
+
     return cons;
 }
 
 void destroy_cons(struct Cons *cons)
 {
-    destroy_expr(cons->car);
-    destroy_expr(cons->cdr);
     free(cons);
 }
 
-struct Atom *create_atom(enum AtomType type, ...)
+struct Atom *create_number_atom(Gc *gc, long int num)
 {
     struct Atom *atom = malloc(sizeof(struct Atom));
     if (atom == NULL) {
         return NULL;
     }
+    atom->type = ATOM_NUMBER;
+    atom->num = num;
 
-    va_list args;
-    va_start(args, type);
+    if (gc_add_expr(gc, atom_as_expr(atom)) < 0) {
+        free(atom);
+        return NULL;
+    }
 
-    switch (type) {
-    case ATOM_SYMBOL:
-    case ATOM_STRING: {
-        const char *arg_str = va_arg(args, const char *);
-        const size_t n = strlen(arg_str);
-        char *str = malloc(sizeof(char) * (n + 1));
+    return atom;
+}
+
+struct Atom *create_string_atom(Gc *gc, const char *str, const char *str_end)
+{
+    struct Atom *atom = malloc(sizeof(struct Atom));
+
+    if (atom == NULL) {
+        goto error;
+    }
 
-        if (str == NULL) {
-            free(atom);
-            return NULL;
+    atom->type = ATOM_STRING;
+    atom->str = string_duplicate(str, str_end);
+
+    if (atom->str == NULL) {
+        goto error;
+    }
+
+    if (gc_add_expr(gc, atom_as_expr(atom)) < 0) {
+        goto error;
+    }
+
+    return atom;
+
+error:
+    if (atom != NULL) {
+        if (atom->str != NULL) {
+            free(atom->str);
         }
+        free(atom);
+    }
 
-        strncpy(str, arg_str, n);
-        atom->text = str;
-    } break;
+    return NULL;
+}
 
-    case ATOM_NUMBER: {
-        atom->number = va_arg(args, int);
-    } break;
+struct Atom *create_symbol_atom(Gc *gc, const char *sym, const char *sym_end)
+{
+    struct Atom *atom = malloc(sizeof(struct Atom));
+
+    if (atom == NULL) {
+        goto error;
     }
 
-    va_end(args);
+    atom->type = ATOM_SYMBOL;
+    atom->sym = string_duplicate(sym, sym_end);
+
+    if (atom->sym == NULL) {
+        goto error;
+    }
+
+    if (gc_add_expr(gc, atom_as_expr(atom)) < 0) {
+        goto error;
+    }
 
     return atom;
+
+error:
+    if (atom != NULL) {
+        if (atom->sym != NULL) {
+            free(atom->sym);
+        }
+        free(atom);
+    }
+
+    return NULL;
+}
+
+struct Atom *create_native_atom(Gc *gc, NativeFunction fun, void *param)
+{
+    struct Atom *atom = malloc(sizeof(struct Atom));
+
+    if (atom == NULL) {
+        goto error;
+    }
+
+    atom->type = ATOM_NATIVE;
+    atom->native.fun = fun;
+    atom->native.param = param;
+
+    if (gc_add_expr(gc, atom_as_expr(atom)) < 0) {
+        goto error;
+    }
+
+    return atom;
+
+error:
+    if (atom != NULL) {
+        free(atom);
+    }
+
+    return NULL;
 }
 
 void destroy_atom(struct Atom *atom)
@@ -107,9 +252,10 @@ void destroy_atom(struct Atom *atom)
     switch (atom->type) {
     case ATOM_SYMBOL:
     case ATOM_STRING: {
-        free(atom->text);
+        free(atom->str);
     } break;
 
+    case ATOM_NATIVE:
     case ATOM_NUMBER: {
         /* Nothing */
     } break;
@@ -118,8 +264,97 @@ void destroy_atom(struct Atom *atom)
     free(atom);
 }
 
-void print_expr_as_sexpr(struct Expr expr)
+static int atom_as_sexpr(struct Atom *atom, char *output, size_t n)
 {
-    (void) expr;
-    /* TODO: print_expr_as_sexpr is not implemented */
+    assert(atom);
+    assert(output);
+
+    switch (atom->type) {
+    case ATOM_SYMBOL:
+        return snprintf(output, n, "%s", atom->sym);
+
+    case ATOM_NUMBER:
+        return snprintf(output, n, "%ld", atom->num);
+
+    case ATOM_STRING:
+        return snprintf(output, n, "\"%s\"", atom->str);
+
+    case ATOM_NATIVE:
+        return snprintf(output, n, "<native>");
+    }
+
+    return 0;
+}
+
+static int cons_as_sexpr(struct Cons *head, char *output, size_t n)
+{
+    assert(head);
+    assert(output);
+
+    /* TODO(#378): cons_as_sexpr does not handle encoding errors of snprintf */
+
+    struct Cons *cons = head;
+
+    int m = (int) n;
+
+    int c = snprintf(output, n, "(");
+    if (m - c <= c) {
+        return c;
+    }
+
+    c += expr_as_sexpr(cons->car, output + c, (size_t) (m - c));
+    if (m - c <= 0) {
+        return c;
+    }
+
+    while (cons->cdr.type == EXPR_CONS) {
+        cons = cons->cdr.cons;
+
+        c += snprintf(output + c, (size_t) (m - c), " ");
+        if (m - c <= 0) {
+            return c;
+        }
+
+        c += expr_as_sexpr(cons->car, output + c, (size_t) (m - c));
+        if (m - c <= 0) {
+            return c;
+        }
+    }
+
+    if (cons->cdr.atom->type != ATOM_SYMBOL ||
+        strcmp("nil", cons->cdr.atom->sym) != 0) {
+
+        c += snprintf(output + c, (size_t) (m - c), " . ");
+        if (m - c <= 0) {
+            return c;
+        }
+
+        c += expr_as_sexpr(cons->cdr, output + c, (size_t) (m - c));
+        if (m - c <= 0) {
+            return c;
+        }
+    }
+
+    c += snprintf(output + c, (size_t) (m - c), ")");
+    if (m - c <= 0) {
+        return c;
+    }
+
+    return c;
+}
+
+int expr_as_sexpr(struct Expr expr, char *output, size_t n)
+{
+    switch(expr.type) {
+    case EXPR_ATOM:
+        return atom_as_sexpr(expr.atom, output, n);
+
+    case EXPR_CONS:
+        return cons_as_sexpr(expr.cons, output, n);
+
+    case EXPR_VOID:
+        return 0;
+    }
+
+    return 0;
 }