~brenns10/funlisp

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

M CHANGELOG.md
M Makefile
M doc/embedding.rst
M inc/funlisp.h
M src/util.c
A tools/example_list_append.c
M CHANGELOG.md => CHANGELOG.md +6 -0
@@ 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
   nulls.

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");

	lisp_runtime_free(rt);
	return 0;
}