35da21a7e6ddbcadee50427c31e5d6de5018fd30 — Stephen Brennan 2 years ago b966a7c
Add utilities for mutating and constructing lists
6 files changed, 148 insertions(+), 0 deletions(-)

M Makefile
M doc/embedding.rst
M inc/funlisp.h
M src/util.c
A tools/example_list_append.c
@@ 9,6 9,12 @@ project will use [Semantic Versioning](https://semver.org) for non-beta releases
changes may be made on minor version bumps, and feature additions / bug-fixes
will be made on patch versions.

## [Unreleased]

### Added
- Added `lisp_list_append()` and `lisp_list_set_{left|right}` for mutating and
  constructing lists more easily.

## [1.0.0] - 2018-08-16

First 1.0 release. The API should now be stable enough for you to use!

M Makefile => Makefile +3 -0
@@ 35,6 35,9 @@ bin/call_lisp: tools/call_lisp.o bin/libfunlisp.a
bin/funlisp: tools/funlisp.o bin/libfunlisp.a
	$(CC) $(CFLAGS) -ledit $^ -o $@

bin/example_list_append: tools/example_list_append.o bin/libfunlisp.a
	$(CC) $(CFLAGS) -ledit $^ -o $@

clean: FORCE
	rm -rf bin/* src/*.o tools/*.o src/*.gcda src/*.gcno

M doc/embedding.rst => doc/embedding.rst +43 -0
@@ 307,6 307,49 @@ be registered multiple times as multiple Funlisp functions. It also allows the C
function to access additional context, which it may not have been able to get
through the context object attached to the runtime.

Building Lists

Many parts of the funlisp API require constructing lists. As has been mentioned
earlier, funlisp "list" types are singly linked lists. Each node's ``left``
pointer points to the data contained by the node, and the ``right`` pointer
points to the next node in the list. ``nil``, returned by
:c:func:`lisp_nil_new()`, is the empty list, and every list is terminated by it.

You have several options available to you for constructing lists. First is
:c:func:`lisp_list_new()`, which allows you to create a new list given a left
and a right pointer. This may be used for constructing single list nodes. It may
also be used to construct a list in reverse, starting with the last node and
continuing to the first one.  You may also directly set the left and right
pointer of a list node, using :c:func:`lisp_list_set_left()` and
:c:func:`lisp_list_set_right()` respectively.

.. warning::

   Note that lisp lists are not mutable. In Lisp, any modification to list
   results in a new one. Thus, you should never modify list nodes, unless you
   have just created them yourself and are constructing a full list.

   Also note that lisp lists' ``left`` and ``right`` pointers should never
   contain ``null`` -- this will certainly cause a segmentation fault when they
   are used. However, you can set these pointers to ``null`` during the
   construction of a list, so long as the end result is a valid list with no

The simplest option is :c:func:`lisp_list_append()`. This function allows you to
construct a list forwards (rather than reverse). It requires double pointers to
the head and tail of the list (where tail is the last non-nil item in the list).
See the generated documentation for full information, but below is a fully
working example of using it:

.. literalinclude:: ../tools/example_list_append.c
  :language: C

Finally, there are a few specialized options for constructing lists.
:c:func:`lisp_singleton_list()` allows you to construct a list with one item.
:c:func:`lisp_list_of_strings()` is useful for converting the argv and argc of a
C main function into program arguments for a funlisp program.

Advanced Topics

M inc/funlisp.h => inc/funlisp.h +44 -0
@@ 401,6 401,15 @@ int lisp_list_length(lisp_list *list);
lisp_value *lisp_list_get_left(lisp_list *l);

 * Set the left item of a list node.
 * @warning Lisp lists are not mutable! This should only be used during
 * construction of lists.
 * @param l list node to set
 * @param left item to set the left pointer to
void lisp_list_set_left(lisp_list *l, lisp_value *left);

 * Retrieve the right item of a list node / sexp
 * @param l list to retrieve from
 * @return right item of list node

@@ 408,6 417,41 @@ lisp_value *lisp_list_get_left(lisp_list *l);
lisp_value *lisp_list_get_right(lisp_list *l);

 * Set the right item of a list node.
 * @warning Lisp lists are not mutable! This should only be used during
 * construction of lists.
 * @param l list node to set
 * @param right item to set the right pointer to
void lisp_list_set_right(lisp_list *l, lisp_value *right);

 * Append @a itemto the end of a list. This routine accepts double pointers to
 * the head and tail of a list, so that it can update them if they change.
 * To create a list, you can append onto ``nil``. After that, you may continue
 * appending onto the list. Here is a complete example:
 * @code
 * lisp_list *head, *tail;
 * head = tail = lisp_nil_new(rt);
 * lisp_list_append(rt, &head, &tail, (lisp_value*) lisp_integer_new(rt, 1));
 * lisp_list_append(rt, &head, &tail, (lisp_value*) lisp_integer_new(rt, 2));
 * lisp_list_append(rt, &head, &tail, (lisp_value*) lisp_integer_new(rt, 3));
 * lisp_print(stdout, (lisp_value*) head);
 * // prints (1 2 3 )
 * @endcode
 * @param rt runtime
 * @param head double pointer to the first item in the list (or nil, if
 * appending to an empty list)
 * @param tail double pointer to the last non-nil item in the list (or nil, if
 * appending to an empty list)
 * @param item the value to append
void lisp_list_append(lisp_runtime *rt, lisp_list **head, lisp_list **tail, lisp_value *item);

 * Return a nil instance. Nil is simply a "special" ::lisp_list, with left and
 * right both set to NULL. It is used to terminate lists. For example, the list
 * ``'(a b)`` is internally: ``lisp_list(a, lisp_list(b, lisp_list(NULL, NULL)))``

M src/util.c => src/util.c +22 -0
@@ 338,6 338,28 @@ lisp_value *lisp_list_get_right(lisp_list *l)
	return l->right;

void lisp_list_set_left(lisp_list *l, lisp_value *left)
	l->left = left;

void lisp_list_set_right(lisp_list *l, lisp_value *right)
	l->right = right;

void lisp_list_append(lisp_runtime *rt, lisp_list **head, lisp_list **tail, lisp_value *item)
	if (lisp_nil_p((lisp_value*) *head)) {
		*head = lisp_list_new(rt, item, (lisp_value*) *head);
		*tail = *head;
	} else {
		(*tail)->right = (lisp_value*)lisp_list_new(
				rt, item, lisp_nil_new(rt));
		*tail = (lisp_list*) (*tail)->right;

lisp_integer *lisp_integer_new(lisp_runtime *rt, int n)
	lisp_integer *integer = (lisp_integer *) lisp_new(rt, type_integer);

A tools/example_list_append.c => tools/example_list_append.c +30 -0
@@ 0,0 1,30 @@
 * example_list_append.c: example demonstrating how to append to a list
 * Stephen Brennan <stephen@brennan.io>

#include <stdio.h>

#include "funlisp.h"

int main(int argc, char **argv)
	lisp_runtime *rt;
	lisp_list *head, *tail;

	(void) argc; /* unused parameters */
	(void) argv;

	rt = lisp_runtime_new();

	head = tail = (lisp_list*) lisp_nil_new(rt);
	lisp_list_append(rt, &head, &tail, (lisp_value*)lisp_integer_new(rt, 1));
	lisp_list_append(rt, &head, &tail, (lisp_value*)lisp_integer_new(rt, 2));
	lisp_list_append(rt, &head, &tail, (lisp_value*)lisp_integer_new(rt, 3));
	lisp_print(stdout, (lisp_value*)head);
	fprintf(stdout, "\n");

	return 0;