blob: ad7314adfc80cf445587e6c1c5a9b3fc1a7e30da [file] [log] [blame]
#pragma once
#include "error.h"
#include "istream.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdarg.h>
#define INT_MASK 0b11
#define INT_TAG 0b00
#define CHAR_MASK 0xff
#define CHAR_TAG 0b00001111
#define BOOL_MASK 0b1111111
#define BOOL_TAG 0b0011111
#define HEAP_MASK 0b111
#define CONS_TAG 0b001
#define CLASS_TAG 0b010
#define STRING_TAG 0b011
#define SYMBOL_TAG 0b101
#define CLOSURE_TAG 0b110
struct cons;
/// Represents a Lisp value
typedef unsigned int value_t;
struct cons
{
value_t car, cdr;
/// Line of the input file from where this was parsed, 0 if it was created
/// in Lisp.
int line;
/// Description of where the cons was parsed from, or NULL if generated in
/// code.
char *name;
};
/**
* Represents how many arguments a function takes.
*/
struct args
{
/// The minimum valid number of arguments
int num_required;
/// The number of optional values
int num_optional;
/// Does this function accept variadic arguments? If `true`, any arguments
/// after the required and optional arguments will be `cons`-ed to a list
/// and passed as a final argument.
bool variadic;
/// The default values for the optional arguments, as expressions. These
/// should be evaluated at the call site. They are known not to reference
/// anything that could clash with scope at the call site.
struct optional_argument
{
/// The default value of this argument
value_t value;
/// The name of this argument as a symbol
value_t name;
} optional_arguments[];
};
struct closure
{
/// How many arguments does this closure take
struct args *args;
/// How many free variables does it capture (i.e. length of `data`)
int num_captured;
/// The function pointer itself
void *function;
/// This will be passed in edi.
value_t data[];
};
/// Default pool (no pool)
#define NO_POOL 0
/**
* The max used pool number, don't touch this.
*/
extern unsigned char max_pool;
/**
* Current allocation pool, default 0 (no pool)
*/
extern unsigned char current_pool;
// It is integral that this be 16 bytes long so that whatever follows it is
// still aligned to 4 bits.
struct alloc
{
/**
* One of the type tags, eg CONS_TAG, etc
*/
unsigned int type_tag; // 4
struct alloc *prev, *next; // + 8
/**
* Zero if this is not part of a release pool, pool number otherwise.
*/
unsigned char pool; // + 1
/**
* Reserved for the GC.
*/
unsigned int mark : 24; // + 3 = 16
// Whatever else
};
extern struct alloc *first_a, *last_a;
struct cons_alloc
{
struct alloc alloc;
struct cons cons;
};
struct closure_alloc
{
struct alloc alloc;
struct closure closure;
};
/**
* Create a new allocation pool.
*/
unsigned char make_pool();
/**
* Set the allocation pull
* @returns the old pool, you should reset this later with pop_pool.
*/
unsigned char push_pool(unsigned char pool);
/**
* Set the allocation pool and throw away the old value.
*/
void pop_pool(unsigned char pool);
void add_to_pool(value_t form);
void del_alloc(struct alloc *alloc);
/**
* @returns true if pool is still alive (in scope).
*/
bool pool_alive(unsigned char pool);
bool startswith(struct istream *s, char *pattern);
struct error readsym(struct istream *is, value_t *val) WARN_UNUSED;
struct error readstr(struct istream *is, value_t *val) WARN_UNUSED;
struct error readlist(struct istream *is, value_t *val) WARN_UNUSED;
struct error readint(struct istream *is, value_t *val) WARN_UNUSED;
/**
* Read a quoted form, including `'` (quote) `\`` (backquote) and `,` (unquote)
*/
struct error readquote(struct istream *is, value_t *val) WARN_UNUSED;
value_t intval(int i);
value_t strval(char *str);
value_t symval(char *str);
value_t cons(value_t car, value_t cdr);
value_t merge2(value_t front, value_t back);
struct error read1(struct istream *is, value_t *val) WARN_UNUSED;
value_t read(struct istream *is);
value_t readn(struct istream *is);
value_t car(value_t v);
value_t cdr(value_t v);
/// Return a pointer to the "nil" tail of the list, or NULL if you do
/// something stupid.
value_t *nilptr(value_t val);
value_t *carref(value_t v);
value_t *cdrref(value_t v);
/// @returns the `index`-th `cdr`
value_t cxdr(value_t v, int index);
/// @returns a reference to the `index`-th `cdr`
value_t *cxdrref(value_t *v, int index);
value_t deep_copy(value_t val);
int cons_line(value_t val);
char *cons_file(value_t val);
bool integerp(value_t v);
bool symbolp(value_t v);
bool stringp(value_t v);
bool consp(value_t v);
bool listp(value_t v);
bool nilp(value_t v);
bool heapp(value_t v);
bool closurep(value_t v);
int length(value_t v);
value_t elt(value_t v, int index);
void printval(value_t v, int depth);
bool symstreq(value_t sym, char *str);
value_t create_closure(void *code, struct args *args, int ncaptures);
/**
* Set the `index`th capture variable of `closure`. This should really only be
* called when creating a new closure.
*/
void set_closure_capture_variable(int index, value_t value, value_t closure);
extern value_t nil;
extern value_t t;