~brenns10/funlisp

fa4b6f8323e322df36208e4c3ab8ac783c266b42 — Stephen Brennan 2 years ago 90e1f3d
Error numbers for better error handling
M doc/embedding.rst => doc/embedding.rst +4 -6
@@ 162,9 162,10 @@ So, a format string for the plus function would be ``"dd"``, and the format
string for the ``cons`` function is ``"**"``, because any two things may be put
together in an s-expression. If nothing else, the :c:func:`lisp_get_args()`
function can help you verify the number of arguments, if not their types. When
it fails, it returns false, which you should typically handle by returning an
error (:c:func:`lisp_error_new()`). If it doesn't fail, your function is free to
do whatever logic you'd like.
it fails, it returns false. It sets an internal interpreter error depending on
what happened (too many arguments, not enough, types didn't matche, etc). You
can handle this by simply returning NULL from your builtin.  If argument parsing
doesn't fail, your function is free to do whatever logic you'd like.

Finally, note that the signature includes a ``void *user`` parameter. This "user
context" is specified when you register the builtin function, and passed back to


@@ 199,9 200,6 @@ The current types (that you are likely to use) are:

- ``lisp_symbol``: type that represents names. Contains ``sym``, which is a
  ``char*``.
- ``lisp_error``: similar to a symbol in implementation, but represents an
  error. Has the attribute ``message`` which contains the error message. Create
  a new one with ``lisp_error_new(message)``.
- ``lisp_integer``: contains attribute ``x``, an integer. Yes, it's allocated on
  the heap.  Get over it.
- ``lisp_string``: another thing similar to a symbol in implementation, but this

M inc/funlisp.h => inc/funlisp.h +49 -13
@@ 476,15 476,6 @@ lisp_symbol *lisp_symbol_new(lisp_runtime *rt, char *string);
char *lisp_symbol_get(lisp_symbol *s);

/**
 * Return a new error. This function will copy the @a message and free the copy
 * on garbage collection (much like lisp_string_new()).
 * @param rt runtime
 * @param message message to use for creating the error
 * @return a new error
 */
lisp_value  *lisp_error_new(lisp_runtime *rt, char *message);

/**
 * Create a new integer.
 * @param rt runtime
 * @param n the integer value


@@ 592,7 583,7 @@ lisp_value *lisp_progn(lisp_runtime *rt, lisp_scope *scope, lisp_list *l);
 *
 *     lisp_integer *arg1;
 *     lisp_string *arg2;
 *     lisp_get_args(args, "dS", &arg1, &arg2);
 *     lisp_get_args(rt, args, "dS", &arg1, &arg2);
 *
 * @note The format code 'R' is special and deserves some more attention. When
 * used, it immediately ends argument processing, so it should only be used at


@@ 600,13 591,14 @@ lisp_value *lisp_progn(lisp_runtime *rt, lisp_scope *scope, lisp_list *l);
 * arguments as a list, provided that there is at least one (i.e. R will fail if
 * the rest of the args is an empty list).
 *
 * @param rt runtime
 * @param list Argument list to type check and count
 * @param format Format string
 * @param ... Destination pointer to place results
 * @retval 1 on success (true)
 * @retval 0 on failure (false)
 */
int lisp_get_args(lisp_list *list, char *format, ...);
int lisp_get_args(lisp_runtime *rt, lisp_list *list, char *format, ...);

/**
 * @}


@@ 657,16 649,27 @@ int lisp_parse_value(lisp_runtime *rt, char *input, int index, lisp_value **outp
lisp_value *lisp_parse_progn(lisp_runtime *rt, char *input);

/**
 * Parse every expression contained in @a file, and return the parsed code as a
 * ``progn`` block. This function behaves same as lisp_parse_progn(). Additional
 * errors may be raised due to I/O errors on @a file.
 * @param rt runtime
 * @param file file to parse
 * @return the code, fully parsed, within a progn block
 * @retval NULL when an error occurs (see lisp_print_error())
 */
lisp_value *lisp_parse_progn_f(lisp_runtime *rt, FILE *file);

/**
 * Parse every expressioned contained in @a input, where input is a file.
 * Behaves the same as lisp_parse_progn(), with the additional caveat that the
 * entire file is loaded into memory at once. Any error reading from the file
 * will be passed down (as a NULL return value).
 * @param rt runtime
 * @param input file to parse
 * @param input string to parse
 * @return the code, fully parsed, within a progn block
 * @retval NULL when an error occurs (see lisp_print_error())
 */
lisp_value *lisp_parse_progn(lisp_runtime *rt, FILE *input);
lisp_value *lisp_parse_progn(lisp_runtime *rt, char *input);

/**
 * Parse a file and evaluate its contents. This is roughly equivalent to:


@@ 748,6 751,39 @@ lisp_value *lisp_quote(lisp_runtime *rt, lisp_value *value);
 * @{
 */

enum lisp_errno {
	LE_ERROR=1,  /* a catch-all */
	LE_EOF,      /* end of file while parsing */
	LE_SYNTAX,   /* syntax error */
	LE_FERROR,   /* error reading file */
	LE_2MANY,    /* too many args */
	LE_2FEW,     /* not enough args */
	LE_TYPE,     /* wrong type arg in function */
	LE_NOCALL,   /* not callable */
	LE_NOEVAL,   /* not evaluate-able */
	LE_NOTFOUND, /* not found */

	LE_UNUSED__  /* don't use this, it's just for the trailing comma */
};

/**
 * Raise an error in the interpreter and return NULL.
 *
 * This function is meant to be used within code that implements builtins. When
 * an error condition is reached, functions may simply do something like this:
 *
 * @code
 * if (some_error_condition())
 *     return lisp_error(rt, LE_ERROR, "you broke something");
 * @endcode
 *
 * @param rt runtime
 * @param errno error number, for easy programatic acccess
 * @param message message to show the user
 * @return NULL
 */
lisp_value *lisp_error(lisp_runtime *rt, enum lisp_errno errno, char *message);

/**
 * Dump the execution stack to a file. This is useful if you want to print a
 * stack trace at your current location. This functionality can also be accessed

M src/builtins.c => src/builtins.c +38 -39
@@ 27,11 27,11 @@ static lisp_value *lisp_builtin_car(lisp_runtime *rt, lisp_scope *scope,
	lisp_list *arglist = (lisp_list*) lisp_eval_list(rt, scope, a);
	(void) user; /* unused */
	lisp_error_check(arglist);
	if (!lisp_get_args(arglist, "l", &firstarg)) {
		return lisp_error_new(rt, "wrong arguments to car");
	if (!lisp_get_args(rt, arglist, "l", &firstarg)) {
		return NULL;
	}
	if (lisp_list_length(firstarg) == 0) {
		return lisp_error_new(rt, "expected at least one item");
	if (lisp_nil_p((lisp_value*) firstarg)) {
		return lisp_error(rt, LE_ERROR, "car of nil list");
	}
	return firstarg->left;
}


@@ 44,10 44,9 @@ static lisp_value *lisp_builtin_cdr(lisp_runtime *rt, lisp_scope *scope,
	(void) user; /* unused */

	lisp_error_check(arglist);
	if (!lisp_get_args(arglist, "l", &firstarg)) {
		return lisp_error_new(rt, "wrong arguments to cdr");
	if (!lisp_get_args(rt, arglist, "l", &firstarg)) {
		return NULL;
	}
	/* save rv because firstarg may be deleted after decref */
	return firstarg->right;
}



@@ 58,8 57,8 @@ static lisp_value *lisp_builtin_quote(lisp_runtime *rt, lisp_scope *scope,
	lisp_list *arglist = (lisp_list*) a;
	(void) user; /* unused */
	(void) scope;
	if (!lisp_get_args(arglist, "*", &firstarg)) {
		return lisp_error_new(rt, "wrong arguments to quote");
	if (!lisp_get_args(rt, arglist, "*", &firstarg)) {
		return NULL;
	}
	return arglist->left;
}


@@ 72,8 71,8 @@ static lisp_value *lisp_builtin_cons(lisp_runtime *rt, lisp_scope *scope,
	lisp_list *new, *arglist = (lisp_list*) lisp_eval_list(rt, scope, a);
	(void) user; /* unused */
	lisp_error_check(arglist);
	if (!lisp_get_args(arglist, "**", &a1, &l)) {
		return lisp_error_new(rt, "wrong arguments to cons");
	if (!lisp_get_args(rt, arglist, "**", &a1, &l)) {
		return NULL;
	}
	new = (lisp_list*)lisp_new(rt, type_list);
	new->left = a1;


@@ 90,14 89,14 @@ static lisp_value *lisp_builtin_lambda(lisp_runtime *rt, lisp_scope *scope,
	(void) user; /* unused */
	(void) scope;

	if (!lisp_get_args(our_args, "lR", &argnames, &code)) {
		return lisp_error_new(rt, "expected argument list and code");
	if (!lisp_get_args(rt, our_args, "lR", &argnames, &code)) {
		return NULL;
	}

	it = argnames;
	while (!lisp_nil_p((lisp_value*)it)) {
		if (it->left->type != type_symbol) {
			return lisp_error_new(rt, "argument names must be symbols");
			return lisp_error(rt, LE_TYPE, "argument names must be symbols");
		}
		it = (lisp_list*) it->right;
	}


@@ 116,8 115,8 @@ static lisp_value *lisp_builtin_define(lisp_runtime *rt, lisp_scope *scope,
	lisp_value *expr, *evald;
	(void) user; /* unused */

	if (!lisp_get_args((lisp_list*)a, "s*", &s, &expr)) {
		return lisp_error_new(rt, "expected name and expression");
	if (!lisp_get_args(rt, (lisp_list*)a, "s*", &s, &expr)) {
		return NULL;
	}

	evald = lisp_eval(rt, scope, expr);


@@ 137,7 136,7 @@ static lisp_value *lisp_builtin_plus(lisp_runtime *rt, lisp_scope *scope,

	while (!lisp_nil_p((lisp_value*)args)) {
		if (args->left->type != type_integer) {
			return lisp_error_new(rt, "expect integers for addition");
			return lisp_error(rt, LE_TYPE, "expect integers for addition");
		}
		i = (lisp_integer*) args->left;
		sum += i->x;


@@ 162,7 161,7 @@ static lisp_value *lisp_builtin_minus(lisp_runtime *rt, lisp_scope *scope,
	len = lisp_list_length(args);

	if (len < 1) {
		return lisp_error_new(rt, "expected at least one arg");
		return lisp_error(rt, LE_2FEW, "expected at least one arg");
	} else if (len == 1) {
		i = (lisp_integer*) args->left;
		val = - i->x;


@@ 172,7 171,7 @@ static lisp_value *lisp_builtin_minus(lisp_runtime *rt, lisp_scope *scope,
		args = (lisp_list*)args->right;
		while (!lisp_nil_p((lisp_value*)args)) {
			if (args->left->type != type_integer) {
				return lisp_error_new(rt, "expected integer");
				return lisp_error(rt, LE_TYPE, "expected integer");
			}
			i = (lisp_integer*) args->left;
			val -= i->x;


@@ 196,7 195,7 @@ static lisp_value *lisp_builtin_multiply(lisp_runtime *rt, lisp_scope *scope,

	while (!lisp_nil_p((lisp_value*)args)) {
		if (args->left->type != type_integer) {
			return lisp_error_new(rt, "expect integers for multiplication");
			return lisp_error(rt, LE_TYPE, "expect integers for multiplication");
		}
		i = (lisp_integer*) args->left;
		product *= i->x;


@@ 220,18 219,18 @@ static lisp_value *lisp_builtin_divide(lisp_runtime *rt, lisp_scope *scope,
	len = lisp_list_length(args);

	if (len < 1) {
		return lisp_error_new(rt, "expected at least one arg");
		return lisp_error(rt, LE_2FEW, "expected at least one arg");
	}
	i = (lisp_integer*) args->left;
	val = i->x;
	args = (lisp_list*)args->right;
	while (!lisp_nil_p((lisp_value*)args)) {
		if (args->left->type != type_integer) {
			return lisp_error_new(rt, "expected integer");
			return lisp_error(rt, LE_TYPE, "expected integer");
		}
		i = (lisp_integer*) args->left;
		if (i->x == 0) {
			return lisp_error_new(rt, "divide by zero");
			return lisp_error(rt, LE_ERROR, "divide by zero");
		}
		val /= i->x;
		args = (lisp_list*) args->right;


@@ 250,8 249,8 @@ static lisp_value *lisp_builtin_cmp_util(lisp_runtime *rt, lisp_scope *scope,
	(void) user; /* unused */
	lisp_error_check(args);

	if (!lisp_get_args((lisp_list*)args, "dd", &first, &second)) {
		return lisp_error_new(rt, "expected two integers");
	if (!lisp_get_args(rt, (lisp_list*)args, "dd", &first, &second)) {
		return NULL;
	}

	result = (lisp_integer*)lisp_new(rt, type_integer);


@@ 326,8 325,8 @@ static lisp_value *lisp_builtin_if(lisp_runtime *rt, lisp_scope *scope,
	lisp_value *condition, *body_true, *body_false;
	(void) user; /* unused */

	if (!lisp_get_args((lisp_list*)a, "***", &condition, &body_true, &body_false)) {
		return lisp_error_new(rt, "expected condition and two bodies");
	if (!lisp_get_args(rt, (lisp_list*)a, "***", &condition, &body_true, &body_false)) {
		return NULL;
	}

	condition = lisp_eval(rt, scope, condition);


@@ 350,8 349,8 @@ static lisp_value *lisp_builtin_null_p(lisp_runtime *rt, lisp_scope *scope,
	args = (lisp_list*) lisp_eval_list(rt, scope, a);
	lisp_error_check(args);

	if (!lisp_get_args(args, "*", &v)) {
		return lisp_error_new(rt, "expected one argument");
	if (!lisp_get_args(rt, args, "*", &v)) {
		return NULL;
	}

	result = (lisp_integer*) lisp_new(rt, type_integer);


@@ 425,7 424,7 @@ static lisp_value *lisp_builtin_map(lisp_runtime *rt, lisp_scope *scope,
	/* Get the function from the first argument in the list. */
	f = map_args->left;
	if (map_args->right->type != type_list) {
		return lisp_error_new(rt, "need at least two arguments");
		return lisp_error(rt, LE_2FEW, "need at least two arguments");
	}
	map_args = (lisp_list*) map_args->right;
	while ((args = get_quoted_left_items(rt, map_args)) != NULL) {


@@ 467,23 466,23 @@ static lisp_value *lisp_builtin_reduce(lisp_runtime *rt, lisp_scope *scope, lisp
	length = lisp_list_length(args);

	if (length == 2) {
		if (!lisp_get_args(args, "*l", &callable, &list)) {
			return lisp_error_new(rt, "reduce: callable and list required");
		if (!lisp_get_args(rt, args, "*l", &callable, &list)) {
			return NULL;
		}
		if (lisp_list_length(list) < 2) {
			return lisp_error_new(rt, "reduce: list must have at least 2 entries");
			return lisp_error(rt, LE_2FEW, "reduce: list must have at least 2 entries");
		}
		initializer = list->left;
		list = (lisp_list*)list->right;
	} else if (length == 3) {
		if (!lisp_get_args(args, "**l", &callable, &initializer, &list)) {
			return lisp_error_new(rt, "reduce: callable, initializer, and list required");
		if (!lisp_get_args(rt, args, "**l", &callable, &initializer, &list)) {
			return NULL;
		}
		if (lisp_list_length(list) < 1) {
			return lisp_error_new(rt, "reduce: list must have at least 1 entry");
			return lisp_error(rt, LE_2FEW, "reduce: list must have at least 1 entry");
		}
	} else {
		return lisp_error_new(rt, "reduce: 2 or 3 arguments required");
		return lisp_error(rt, LE_2MANY, "reduce: 2 or 3 arguments required");
	}

	while (!lisp_nil_p((lisp_value*)list)) {


@@ 504,8 503,8 @@ static lisp_value *lisp_builtin_print(lisp_runtime *rt, lisp_scope *scope, lisp_
	args = (lisp_list*) lisp_eval_list(rt, scope, a);
	lisp_error_check(args);

	if (!lisp_get_args(args, "*", &v)) {
		return lisp_error_new(rt, "expected one argument");
	if (!lisp_get_args(rt, args, "*", &v)) {
		return NULL;
	}

	lisp_print(stdout, v);

M src/funlisp_internal.h => src/funlisp_internal.h +8 -0
@@ 60,8 60,16 @@ struct lisp_runtime {

	/* Data we use for reporting errors. This is very single-threaded of me,
	 * but it works for now.
	 * REQUIREMENTS:
	 *   - error and errno must both always be non-NULL and non-0
	 *     respectively when an error occurs
	 *   - error_line and error_stack may optionally be set
	 * You can use lisp_error() to set error, errno, and error_stack.
	 * Parsing functions typically use a macro to set error, errno, and
	 * error_line.
	 */
	char *error;
	enum lisp_errno errno;
	unsigned int error_line;
	lisp_list *error_stack;


M src/parse.c => src/parse.c +8 -6
@@ 16,7 16,7 @@
typedef struct {
	lisp_value *result;
	int index;
	int error;
	enum lisp_errno error;
} result;

#define return_result_err(v, i, e)                        \


@@ 42,7 42,7 @@ static result lisp_parse_integer(lisp_runtime *rt, char *input, int index)
	rv = sscanf(input + index, "%d%n", &v->x, &n);
	if (rv != 1) {
		rt->error = "syntax error: error parsing integer";
		return_result_err(NULL, index, 1);
		return_result_err(NULL, index, LE_SYNTAX);
	} else {
		return_result(v, index + n);
	}


@@ 105,7 105,7 @@ static result lisp_parse_string(lisp_runtime *rt, char *input, int index)
	if (!input[i]) {
		cb_destroy(&cb);
		rt->error = "unexpected eof while parsing string";
		return_result_err(NULL, i, 1);
		return_result_err(NULL, i, LE_SYNTAX);
	}
	cb_trim(&cb);
	str = (lisp_string*)lisp_new(rt, type_string);


@@ 122,7 122,7 @@ static result lisp_parse_list_or_sexp(lisp_runtime *rt, char *input, int index)
	index = skip_space_and_comments(input, index);
	if (!input[index]) {
		rt->error = "unexpected eof while parsing list";
		return_result_err(NULL, index, 1);
		return_result_err(NULL, index, LE_EOF);
	} else if (input[index] == ')') {
		return_result(lisp_nil_new(rt), index + 1);
	}


@@ 140,7 140,7 @@ static result lisp_parse_list_or_sexp(lisp_runtime *rt, char *input, int index)

		if (!input[index]) {
			rt->error = "unexpected eof while parsing list";
			return_result_err(NULL, index, 1);
			return_result_err(NULL, index, LE_EOF);
		} else if (input[index] == '.') {
			index++;
			r = lisp_parse_value_internal(rt, input, index);


@@ 177,7 177,7 @@ static result lisp_parse_symbol(lisp_runtime *rt, char *input, int index)
	}
	if (!input[index]) {
		rt->error = "unexpected eof while parsing symbol";
		return_result_err(NULL, index, 1);
		return_result_err(NULL, index, LE_EOF);
	}
	s = (lisp_symbol*)lisp_new(rt, type_symbol);
	s->sym = malloc(n + 1);


@@ 235,6 235,7 @@ int lisp_parse_value(lisp_runtime *rt, char *input, int index, lisp_value **outp
	result r = lisp_parse_value_internal(rt, input, index);
	bytes = r.index - index;
	if (r.error) {
		rt->errno = r.error;
		set_error_lineno(rt, input, r.index);
		bytes = -1;
	}


@@ 298,6 299,7 @@ lisp_value *lisp_parse_progn_f(lisp_runtime *rt, FILE *input)
	input_string = read_file(input);
	if (!input_string) {
		rt->error = "error reading from input file";
		rt->errno = LE_FERROR;
		return NULL;
	}
	result = lisp_parse_progn(rt, input_string);

M src/types.c => src/types.c +6 -6
@@ 26,7 26,7 @@ static lisp_value *eval_error(lisp_runtime *rt, lisp_scope *s, lisp_value *v)
{
	(void)s;
	(void)v;
	return lisp_error_new(rt, "cannot evaluate this object");
	return lisp_error(rt, LE_NOEVAL, "cannot evaluate this object");
}

static lisp_value *eval_same(lisp_runtime *rt, lisp_scope *s, lisp_value *v)


@@ 42,7 42,7 @@ static lisp_value *call_error(lisp_runtime *rt, lisp_scope *s, lisp_value *c,
	(void)s;
	(void)c;
	(void)v;
	return lisp_error_new(rt, "not callable!");
	return lisp_error(rt, LE_NOCALL, "not callable!");
}

static bool has_next_index_lt_state(struct iterator *iter)


@@ 190,11 190,11 @@ static lisp_value *list_eval(lisp_runtime *rt, lisp_scope *scope, lisp_value *v)
	lisp_list *list = (lisp_list*) v;

	if (lisp_nil_p(v)) {
		return lisp_error_new(rt, "Cannot call empty list");
		return lisp_error(rt, LE_NOCALL, "Cannot call empty list");
	}

	if (list->right->type != type_list) {
		return lisp_error_new(rt, "You may not call with an s-expression");
		return lisp_error(rt, LE_NOCALL, "You may not call with an s-expression");
	}
	callable = lisp_eval(rt, scope, list->left);
	lisp_error_check(callable);


@@ 500,10 500,10 @@ static lisp_value *lambda_call(lisp_runtime *rt, lisp_scope *scope,
	}

	if (!lisp_nil_p((lisp_value*)it1)) {
		return lisp_error_new(rt, "not enough arguments to lambda call");
		return lisp_error(rt, LE_2FEW, "not enough arguments to lambda call");
	}
	if (!lisp_nil_p((lisp_value*)it2)) {
		return lisp_error_new(rt, "too many arguments to lambda call");
		return lisp_error(rt, LE_2MANY, "too many arguments to lambda call");
	}

	return lisp_progn(rt, inner, lambda->code);

M src/util.c => src/util.c +15 -6
@@ 29,7 29,7 @@ lisp_value *lisp_scope_lookup(lisp_runtime *rt, lisp_scope *scope,
		if (scope->up) {
			return lisp_scope_lookup(rt, scope->up, symbol);
		} else {
			return lisp_error_new(rt, "symbol not found in scope");
			return lisp_error(rt, LE_NOTFOUND, "symbol not found in scope");
		}
	} else {
		return v;


@@ 76,7 76,7 @@ lisp_value *lisp_progn(lisp_runtime *rt, lisp_scope *scope, lisp_list *l)
	lisp_value *v;

	if (lisp_nil_p((lisp_value*)l))
		return lisp_error_new(rt, "progn: need at least one arg");
		return lisp_error(rt, LE_2FEW, "progn: need at least one arg");

	while (1) {
		v = lisp_eval(rt, scope, l->left);


@@ 134,7 134,7 @@ static lisp_type *lisp_get_type(char c)
	return NULL;
}

int lisp_get_args(lisp_list *list, char *format, ...)
int lisp_get_args(lisp_runtime *rt, lisp_list *list, char *format, ...)
{
	lisp_value **v;
	lisp_type *type;


@@ 155,13 155,21 @@ int lisp_get_args(lisp_list *list, char *format, ...)
		}
		type = lisp_get_type(*format);
		if (type != NULL && type != list->left->type) {
			rt->error = "incorrect argument type";
			rt->errno = LE_TYPE;
			return 0;
		}
		*v = list->left;
		list = (lisp_list*)list->right;
		format += 1;
	}
	if (strlen(format) != 0 || !lisp_nil_p((lisp_value*)list)) {
	if (*format != '\0') {
		rt->error = "too many arguments";
		rt->errno = LE_2MANY;
		return 0;
	} else if(!lisp_nil_p((lisp_value*)list)) {
		rt->error = "not enough arguments";
		rt->errno = LE_2FEW;
		return 0;
	}
	return 1;


@@ 364,9 372,10 @@ void lisp_dump_stack(lisp_runtime *rt, lisp_list *stack, FILE *file)
	}
}

lisp_value *lisp_error_new(lisp_runtime *rt, char *message)
lisp_value *lisp_error(lisp_runtime *rt, enum lisp_errno errno, char *message)
{
	rt->error = message;
	rt->errno = errno;
	rt->error_stack = rt->stack;
	return NULL;
}


@@ 393,7 402,7 @@ void lisp_print_error(lisp_runtime *rt, FILE *file)
	if (rt->error_line)
		fprintf(file, "at line %d: ", rt->error_line);

	fprintf(file, "%s\n\n", rt->error);
	fprintf(file, "Error %d: %s\n\n", rt->errno, rt->error);

	if (rt->error_stack)
		lisp_dump_stack(rt, rt->error_stack, file);

M tools/hello_repl.c => tools/hello_repl.c +2 -2
@@ 19,8 19,8 @@ static lisp_value *say_hello(lisp_runtime *rt, lisp_scope *scope,
	lisp_string *s;
	lisp_value *arglist = lisp_eval_list(rt, scope, a);

	if (!lisp_get_args((lisp_list*)arglist, "S", &s)) {
		return (lisp_value*)lisp_error_new(rt, "expected a string!");
	if (!lisp_get_args(rt, (lisp_list*)arglist, "S", &s)) {
		return NULL;
	}

	printf("Hello, %s! I'm %s.\n", lisp_string_get(s), from);