#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)
{
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(struct Atom *atom)
+{
+ assert(atom);
+
+ switch (atom->type) {
+ case ATOM_SYMBOL:
+ printf("%s", atom->sym);
+ break;
+
+ case ATOM_NUMBER:
+ printf("%ld", atom->num);
+ break;
+
+ case ATOM_STRING:
+ printf("\"%s\"", atom->str);
+ break;
+
+ case ATOM_NATIVE:
+ printf("<native>");
+ break;
+ }
+}
+
+void print_cons_as_sexpr(struct Cons *head)
+{
+ assert(head);
+
+ struct Cons *cons = head;
+
+ printf("(");
+ print_expr_as_sexpr(cons->car);
+
+ while (cons->cdr.type == EXPR_CONS) {
+ cons = cons->cdr.cons;
+ printf(" ");
+ print_expr_as_sexpr(cons->car);
+ }
+
+ if (cons->cdr.atom->type != ATOM_SYMBOL ||
+ strcmp("nil", cons->cdr.atom->sym) != 0) {
+ printf(" . ");
+ print_expr_as_sexpr(cons->cdr);
+ }
+
+ printf(")");
+}
+
+void print_expr_as_sexpr(struct Expr expr)
+{
+ switch (expr.type) {
+ case EXPR_ATOM:
+ print_atom_as_sexpr(expr.atom);
+ break;
+
+ case EXPR_CONS:
+ print_cons_as_sexpr(expr.cons);
+ break;
+
+ case EXPR_VOID:
+ break;
+ }
}
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) {
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;
+}
- if (str == NULL) {
- free(atom);
- return NULL;
+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;
+ }
+
+ 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)
switch (atom->type) {
case ATOM_SYMBOL:
case ATOM_STRING: {
- free(atom->text);
+ free(atom->str);
} break;
+ case ATOM_NATIVE:
case ATOM_NUMBER: {
/* Nothing */
} break;
free(atom);
}
-void print_expr_as_sexpr(struct Expr expr)
+static int atom_as_sexpr(struct Atom *atom, char *output, size_t n)
+{
+ 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)
{
- (void) expr;
- /* TODO(#284): print_expr_as_sexpr is not implemented */
+ 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;
}