+
+struct EvalResult
+car(void *param, Gc *gc, struct Scope *scope, struct Expr args)
+{
+ (void) param;
+ trace_assert(gc);
+ trace_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);
+ }
+
+ 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);
+ }
+
+ long int *p = va_arg(args_list, long int *);
+ if (p != NULL) {
+ *p = x.atom->num;
+ }
+ } break;
+
+ case 's': {
+ if (!string_p(x)) {
+ va_end(args_list);
+ return wrong_argument_type(gc, "stringp", x);
+ }
+
+ const char **p = va_arg(args_list, const char**);
+ if (p != NULL) {
+ *p = x.atom->str;
+ }
+ } break;
+
+ case 'q': {
+ if (!symbol_p(x)) {
+ va_end(args_list);
+ return wrong_argument_type(gc, "symbolp", x);
+ }
+
+ const char **p = va_arg(args_list, const char**);
+ if (p != NULL) {
+ *p = x.atom->sym;
+ }
+ } break;
+
+ case 'e': {
+ struct Expr *p = va_arg(args_list, struct Expr*);
+ *p = x;
+ } break;
+
+ case '*': {
+ struct Expr *p = va_arg(args_list, struct Expr*);
+ if (p != NULL) {
+ *p = xs;
+ }
+ xs = NIL(gc);
+ } break;
+ }
+
+ format++;
+ if (!nil_p(xs)) {
+ xs = CDR(xs);
+ }
+ }
+
+ if (*format == '*' && nil_p(xs)) {
+ struct Expr *p = va_arg(args_list, struct Expr*);
+ if (p != NULL) {
+ *p = NIL(gc);
+ }
+ format++;
+ }
+
+ if (*format != 0 || !nil_p(xs)) {
+ va_end(args_list);
+ return wrong_number_of_arguments(gc, i);
+ }
+
+ va_end(args_list);
+ return eval_success(NIL(gc));
+}