#include <assert.h>
#include <math.h>
#include <string.h>
+#include <stdarg.h>
+#include <stdbool.h>
#include "./builtins.h"
#include "./expr.h"
return result;
}
-static struct EvalResult length(Gc *gc, struct Expr obj)
+struct EvalResult
+wrong_argument_type(Gc *gc, const char *type, struct Expr obj)
{
- if (!list_p(obj)) {
- return eval_failure(list(gc, 3,
- SYMBOL(gc, "wrong-argument-type"),
- SYMBOL(gc, "listp"),
- obj));
- }
+ return eval_failure(
+ list(gc, 3,
+ SYMBOL(gc, "wrong-argument-type"),
+ SYMBOL(gc, type),
+ obj));
+}
- return eval_success(NUMBER(gc, length_of_list(obj)));
+struct EvalResult
+wrong_number_of_arguments(Gc *gc, long int count)
+{
+ return eval_failure(
+ CONS(gc,
+ SYMBOL(gc, "wrong-number-of-arguments"),
+ NUMBER(gc, count)));
+}
+
+struct EvalResult
+not_implemented(Gc *gc)
+{
+ return eval_failure(SYMBOL(gc, "not-implemented"));
}
static struct EvalResult eval_atom(Gc *gc, struct Scope *scope, struct Atom *atom)
return eval_success(atom_as_expr(atom));
case ATOM_SYMBOL: {
- if (nil_p(atom_as_expr(atom))) {
- return eval_success(atom_as_expr(atom));
- }
-
struct Expr value = get_scope_value(scope, atom_as_expr(atom));
if (nil_p(value)) {
args));
}
-static struct EvalResult plus_op(Gc *gc, struct Expr args)
-{
- long int result = 0.0f;
-
- while (!nil_p(args)) {
- if (args.type != EXPR_CONS) {
- return eval_failure(CONS(gc,
- SYMBOL(gc, "expected-cons"),
- args));
- }
-
- if (args.cons->car.type != EXPR_ATOM ||
- args.cons->car.atom->type != ATOM_NUMBER) {
- return eval_failure(CONS(gc,
- SYMBOL(gc, "expected-number"),
- args.cons->car));
- }
-
- result += args.cons->car.atom->num;
- args = args.cons->cdr;
- }
-
- return eval_success(atom_as_expr(create_number_atom(gc, result)));
-}
-
static struct EvalResult call_lambda(Gc *gc,
struct Scope *scope,
struct Expr lambda,
static struct EvalResult call_callable(Gc *gc,
struct Scope *scope,
- struct Expr callable,
- struct Expr args) {
- if (callable.type == EXPR_ATOM && callable.atom->type == ATOM_NATIVE) {
- return ((NativeFunction)callable.atom->native.fun)(callable.atom->native.param, gc, scope, args);
+ struct Expr callable_expr,
+ struct Expr args_expr) {
+ struct EvalResult callable_result = eval(gc, scope, callable_expr);
+ if (callable_result.is_error) {
+ return callable_result;
+ }
+
+ struct EvalResult args_result = symbol_p(callable_expr) && is_special(callable_expr.atom->sym)
+ ? eval_success(args_expr)
+ : eval_all_args(gc, scope, args_expr);
+
+ if (args_result.is_error) {
+ return args_result;
+ }
+
+ if (callable_result.expr.type == EXPR_ATOM &&
+ callable_result.expr.atom->type == ATOM_NATIVE) {
+ return ((NativeFunction)callable_result.expr.atom->native.fun)(
+ callable_result.expr.atom->native.param, gc, scope, args_result.expr);
}
- return call_lambda(gc, scope, callable, args);
+ return call_lambda(gc, scope, callable_result.expr, args_result.expr);
}
-static struct EvalResult eval_funcall(Gc *gc, struct Scope *scope, struct Cons *cons)
+
+struct EvalResult eval_block(Gc *gc, struct Scope *scope, struct Expr block)
{
- assert(cons);
- (void) scope;
+ assert(gc);
+ assert(scope);
- if (symbol_p(cons->car)) {
- if (strcmp(cons->car.atom->sym, "+") == 0) {
- struct EvalResult args = eval_all_args(gc, scope, cons->cdr);
- if (args.is_error) {
- return args;
- }
- return plus_op(gc, args.expr);
- } else if (strcmp(cons->car.atom->sym, "set") == 0) {
- struct Expr args = cons->cdr;
- struct EvalResult n = length(gc, args);
+ if (!list_p(block)) {
+ return wrong_argument_type(gc, "listp", block);
+ }
- if (n.is_error) {
- return n;
- }
+ struct Expr head = block;
+ struct EvalResult eval_result = eval_success(NIL(gc));
+
+ while (cons_p(head)) {
+ eval_result = eval(gc, scope, CAR(head));
+ if (eval_result.is_error) {
+ return eval_result;
+ }
+
+ head = CDR(head);
+ }
+
+ return eval_result;
+}
+
+struct EvalResult eval(Gc *gc, struct Scope *scope, struct Expr expr)
+{
+ switch(expr.type) {
+ case EXPR_ATOM:
+ return eval_atom(gc, scope, expr.atom);
+
+ case EXPR_CONS:
+ return call_callable(gc, scope, expr.cons->car, expr.cons->cdr);
+
+ default: {}
+ }
+
+ return eval_failure(CONS(gc,
+ SYMBOL(gc, "unexpected-expression"),
+ expr));
+}
+
+struct EvalResult
+car(void *param, Gc *gc, struct Scope *scope, struct Expr args)
+{
+ (void) param;
+ assert(gc);
+ assert(scope);
+
+ struct Expr xs = NIL(gc);
+
+ struct EvalResult result = match_list(gc, "e", args, &xs);
+ if (result.is_error) {
+ return result;
+ }
+
+ if (nil_p(xs)) {
+ return eval_success(xs);
+ }
+
+ if (!cons_p(xs)) {
+ return wrong_argument_type(gc, "consp", xs);
+ }
- if (n.expr.atom->num != 2) {
- return eval_failure(list(gc, 3,
- SYMBOL(gc, "wrong-number-of-arguments"),
- SYMBOL(gc, "set"),
- NUMBER(gc, n.expr.atom->num)));
+ return eval_success(CAR(xs));
+}
+
+struct EvalResult
+match_list(struct Gc *gc, const char *format, struct Expr xs, ...)
+{
+ va_list args_list;
+ va_start(args_list, xs);
+
+ long int i = 0;
+ for (i = 0; *format != 0 && !nil_p(xs); ++i) {
+ if (!cons_p(xs)) {
+ va_end(args_list);
+ return wrong_argument_type(gc, "consp", xs);
+ }
+
+ struct Expr x = CAR(xs);
+
+ switch (*format) {
+ case 'd': {
+ if (!number_p(x)) {
+ va_end(args_list);
+ return wrong_argument_type(gc, "numberp", x);
}
- struct Expr name = args.cons->car;
- if (!symbol_p(name)) {
- return eval_failure(list(gc, 3,
- SYMBOL(gc, "wrong-type-argument"),
- SYMBOL(gc, "symbolp"),
- name));
+ long int *p = va_arg(args_list, long int *);
+ if (p != NULL) {
+ *p = x.atom->num;
}
+ } break;
- struct EvalResult value = eval(gc, scope, args.cons->cdr.cons->car);
- if (value.is_error) {
- return value;
+ case 's': {
+ if (!string_p(x)) {
+ va_end(args_list);
+ return wrong_argument_type(gc, "stringp", x);
}
- set_scope_value(gc, scope, name, value.expr);
+ const char **p = va_arg(args_list, const char**);
+ if (p != NULL) {
+ *p = x.atom->str;
+ }
+ } break;
- return eval_success(value.expr);
- } else if (strcmp(cons->car.atom->sym, "quote") == 0) {
- /* TODO(#334): quote does not check the amout of it's arguments */
- return eval_success(cons->cdr.cons->car);
- } else if (strcmp(cons->car.atom->sym, "begin") == 0) {
- struct Expr head = CDR(cons_as_expr(cons));
+ case 'q': {
+ if (!symbol_p(x)) {
+ va_end(args_list);
+ return wrong_argument_type(gc, "symbolp", x);
+ }
- struct EvalResult eval_result = eval_success(NIL(gc));
+ const char **p = va_arg(args_list, const char**);
+ if (p != NULL) {
+ *p = x.atom->sym;
+ }
+ } break;
- while (cons_p(head)) {
- eval_result = eval(gc, scope, CAR(head));
- if (eval_result.is_error) {
- return eval_result;
- }
+ case 'e': {
+ struct Expr *p = va_arg(args_list, struct Expr*);
+ *p = x;
+ } break;
- head = CDR(head);
+ case '*': {
+ struct Expr *p = va_arg(args_list, struct Expr*);
+ if (p != NULL) {
+ *p = xs;
}
+ xs = NIL(gc);
+ } break;
+ }
- return eval_result;
- } else if (strcmp(cons->car.atom->sym, "lambda") == 0) {
- /* TODO(#335): lambda special form doesn't check if it forms a callable object */
- return eval_success(cons_as_expr(cons));
+ format++;
+ if (!nil_p(xs)) {
+ xs = CDR(xs);
}
}
- struct EvalResult r = eval_all_args(gc, scope, cons_as_expr(cons));
+ if (*format == '*' && nil_p(xs)) {
+ struct Expr *p = va_arg(args_list, struct Expr*);
+ if (p != NULL) {
+ *p = NIL(gc);
+ }
+ format++;
+ }
- if (r.is_error) {
- return r;
+ if (*format != 0 || !nil_p(xs)) {
+ va_end(args_list);
+ return wrong_number_of_arguments(gc, i);
}
- return call_callable(gc, scope, r.expr.cons->car, r.expr.cons->cdr);
+ va_end(args_list);
+ return eval_success(NIL(gc));
}
-struct EvalResult eval(Gc *gc, struct Scope *scope, struct Expr expr)
+static struct Expr
+format_list_rec(Gc *gc, const char *format, va_list args)
{
- switch(expr.type) {
- case EXPR_ATOM:
- return eval_atom(gc, scope, expr.atom);
+ assert(gc);
+ assert(format);
- case EXPR_CONS:
- return eval_funcall(gc, scope, expr.cons);
+ if (*format == 0) {
+ return NIL(gc);
+ }
- default: {}
+ switch (*format) {
+ case 'd': {
+ long int p = va_arg(args, long int);
+ return CONS(gc, NUMBER(gc, p),
+ format_list_rec(gc, format + 1, args));
}
- return eval_failure(CONS(gc,
- SYMBOL(gc, "unexpected-expression"),
- expr));
+ case 's': {
+ const char* p = va_arg(args, const char*);
+ return CONS(gc, STRING(gc, p),
+ format_list_rec(gc, format + 1, args));
+ }
+
+ case 'q': {
+ const char* p = va_arg(args, const char*);
+ return CONS(gc, SYMBOL(gc, p),
+ format_list_rec(gc, format + 1, args));
+ }
+
+ case 'e': {
+ struct Expr p = va_arg(args, struct Expr);
+ return CONS(gc, p, format_list_rec(gc, format + 1, args));
+ }
+
+ default: {
+ fprintf(stderr, "Wrong format parameter: %c\n", *format);
+ assert(0);
+ }
+ }
+}
+
+struct Expr
+format_list(Gc *gc, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ struct Expr result = format_list_rec(gc, format, args);
+ va_end(args);
+
+ return result;
}