A minimalistic programming language written in C89.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

326 lines
9.4 KiB

#pragma once
#include "stddef.h"
/**
* Represents the natively defined type of integers
*/
#define INK_INTEGER 0
/**
* Represents the natively defined type of natively defined functions
*/
#define INK_NATIVE_FUNCTION 1
/**
* Represents the natively defined type of functions defined from within ink
*/
#define INK_FUNCTION 2
/**
* Represents the special coroutine state that means that it was disposed of and is ready for reuse
*/
#define INK_ROUTINE_CAN_REUSE 32
/**
* Represents the special coroutine state that means that it was terminated without errors
*/
#define INK_ROUTINE_SUCCESS 1
#ifdef __cplusplus
extern "C" {
#endif
/**
* Represents arbitrary values within ink
*/
struct elem {
int type;
int value;
};
/**
* Represents a stack-frame within the execution context
*/
struct stack_frame {
struct elem executing;
int index;
};
/**
* Represents a function within ink, defined in ink, using the homoiconic representation of ink
*/
struct fn {
char *name;
struct elem *things;
int size;
};
struct context;
/**
* Represents natively defined words within ink
*/
struct native_fn {
char *name;
void (*value)(struct context *);
};
/**
* Represents the narrow execution context of a single thread of execution within ink.
*/
struct ink_routine {
int panic;
struct elem *stack;
int capacity;
int top;
struct stack_frame *function_stack;
int function_stack_capacity;
int function_stack_top;
/**
* This user data can be set to any value convenient for the user to track a state local to the routine that is executing
*/
void *routine_userdata;
};
/**
* Contains a list of element on which garbage collection is to not be performed
*/
struct ink_collection_list {
struct elem* elements;
int count;
};
/**
* Contains the data and metadata of an embedded element including the pointer to the element
*/
struct element_slab {
void* data;
int uses;
int in_use;
};
/**
* Contains all the data for every element of any type and its garbage collection information.
*/
struct ink_type {
const char* name; /**< The name of the type */
int element_size; /**< The size of individual elements of the type, 0 for int adjacent, negative for unmanaged size */
struct element_slab* elements; /**< The elements that are still live */
int elements_top; /**< The top of the elements list */
int elements_capacity; /**< The allocated capacity of the elements list */
void (*collect)(struct context*,void*); /**< The "destructor" of the object */
struct ink_collection_list (*gc)(struct context*,void*); /**< A function that returns an in-interpreter allocated list of elem references within the object */
};
/**
* Represents a complete execution context for the ink interpreter
*/
struct context {
int panic;
void *(*inner_malloc)(size_t);
void *(*inner_realloc)(void *, size_t);
void (*inner_free)(void *);
void *(*malloc)(size_t);
void *(*realloc)(void *, size_t);
void (*free)(void *);
int (*putchar)(int);
struct ink_routine *routines;
int routines_capacity;
int routines_top;
struct ink_type *types;
int types_capacity;
int types_top;
/**
* Contains the id of the routine that is currently being manipulated
*/
int routine_current;
struct native_fn *native_words;
int native_words_capacity;
int native_words_top;
struct fn *words;
int words_capacity;
int words_top;
char **lex_reserved_words;
int lex_reserved_words_capacity;
int lex_reserved_words_top;
unsigned int steps;
unsigned int collections;
/**
* Can be set to any data that is convenient to the user to track and use within natively defined functions
*/
void *persistent_userdata;
};
/**
* Creates a routine to execute within the context
* @param ctx The context in which to create the routine
* @warning Does not set the `routine_current` of the context to the newly created routine
* @return either a negative error value or the id of the created routine
*/
int ink_make_routine(struct context *ctx);
/**
* Cleans the targeted routine id data from the context
* @param ctx The context to operate in
* @param routine The id of the routine to destroy
* @return 0 if nothing could or needed to be performed, 1 otherwise
*/
int ink_kill_routine(struct context *ctx, int routine);
/**
* Declares and defines a native function within the context
* @param ctx The context tpo operate in
* @param name The name to give to the newly declared word
* @param value A pointer to a valid word-function
* @return a negative value in case of error, 0 otherwise
*/
int ink_add_native(struct context *ctx, const char *name, void(*value)(struct context *));
/**
* Pushes a value on the current routine's value stack
* @param ctx The context to manipulate
* @param value The value to push
* @return 0 on success, a negative value on failure
*/
int ink_push(struct context *ctx, struct elem value);
/**
* Pushes a function on the execution stack of the current routine of the context
* @param ctx The context to operate in
* @param value the function that will be pushed. in the state one wants it to run from
* @return 0 on success, a negative value on failure
*/
int ink_push_fn(struct context *ctx, struct stack_frame value);
/**
* Create a context to execute routines
* @param malloc the memory allocation function, with a signature similar to the standard malloc
* @param realloc the memory allocation function, with a signature similar to the standard realloc
* @param free the memory allocation function, with a signature similar to the standard free
* @param putchar a function to print to the output character by character
* @return a pointer to a context allocated within the malloc function itself.
*/
struct context* ink_make_context(void *(*malloc)(size_t), void *(*realloc)(void *, size_t), void(*free)(void *), int(*putchar)(int));
#ifndef NOSTDLIB
/**
* Creates a context that includes the standard library of ink, as well as uses the C standard library to operate
* @return a pointer to a context allocated with malloc and with predefined functions added
*/
struct context* ink_make_default_context(void);
#endif
/**
* Steps the current routine by one execution step
* @param pContext The context of the routine to advance
* @return A negative value in case of error, 0 if execution is finished, a positive value if more steps are required to execute
*/
int ink_step(struct context *pContext);
/**
* Examine the context to see if any routines can execute
* @param pContext the context
* @return 0 if no coroutines are available, 1 otherwise
*/
int ink_can_run(struct context *pContext);
/**
* Step every routine that can be executed.
* @param pContext The context, the `routine_current` value may be modified.
* @return 0
*/
int ink_step_everyone(struct context *pContext);
/**
* Compiles the code and starts a main routine to execute it
* @param pContext The context to execute the code in
* @param buffer The buffer that contains the source as a NULL terminated string
*/
void ink_compile(struct context *pContext, const char *buffer);
/**
* Includes the standard library in the specified context
* @param ctx The context
* @return 0
*/
int ink_std_library(struct context *ctx);
/**
* Removes the top element of the function stack of the current routine of the specified context
* @param ctx the context
*/
void ink_pop_fn(struct context *ctx);
/**
* Removes the top element of the stack of the current routine of the specified context
* @param ctx the context
*/
void ink_pop(struct context *ctx);
/**
* Declares a new type that can be stored within the interpreter
* @param ctx The context in which to add the file
* @param type_name The name of the type we want to add
* @param size The size in bytes of the type to add, size of 0 mean no size, in which case the type is adjacent to C int, negative size means that the memory is not managed by the interpreter.
* @param collect A "destructor" function for the data
* @param gc A function that returns a list (allocated with the `inner_malloc`) of all the elements this element holds references to
* @return if positive, a new type id, if negative an error value
* @internal user defined type ids minimal value is 15, we keep the first 16 types as reserved, just like negative type ids
*/
int ink_new_type(
struct context* ctx,
const char* type_name,
int size,
void (*collect)(struct context*,void*),
struct ink_collection_list (*gc)(struct context*,void*)
);
/**
*
* @param ctx The context of the interpreter
* @param ref The in-interpreter reference
* @return A pointer to the created value or NULL if it can't be found or has a size of 0
*/
void* ink_get_value(struct context* ctx, struct elem ref);
/**
* Builds a native type from the provided memory by copying it using memcpy
* @param ctx The context in which we operate
* @param type_id The type_id to use
* @param ptr The pointer from which to copy
* @return The in-interpreter reference of the newly created element
*/
struct elem ink_make_native(struct context* ctx, int type_id, void* ptr);
/**
* Builds a transparent type from the provided pointer
* @param ctx The context in which we operate
* @param type_id The type_id to use
* @param ptr The pointer
* @return The in-interpreter reference of the newly created element
*/
struct elem ink_make_transparent(struct context* ctx, int type_id, void* ptr);
/**
* Launch the mark and sweep garbage collection
* @param ctx The context to clean
*/
void ink_gc(struct context* ctx);
#ifdef __cplusplus
};
#endif