commit 1e16b4d34eda09317d77ff29a0094eeb4fedb1f9 Author: Alexander Rosenberg Date: Wed Sep 3 03:20:58 2025 -0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..737263f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/build*/ +/docs/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..61f8958 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.11) + +set(CMAKE_C_STANDARD 11) + +project( + object-lisp + VERSION 1.0 + LANGUAGES C) + +include(FetchContent) +FetchContent_Declare( + refcount + GIT_REPOSITORY https://git.zander.im/Zander671/refcount.git + GIT_TAG 4efdcc97ae3abbbfc3eb974a7880ff59522b379f) + +FetchContent_MakeAvailable(refcount) + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + include(CTest) +endif() + +add_compile_options(-fsanitize=address,leak,undefined) +add_link_options(-fsanitize=address,leak,undefined) + +add_library( + olisp + src/object.c + src/number.c + src/array.c + src/string.c + src/symbol.c + src/function.c + src/hashtable.c) +target_link_libraries(olisp PUBLIC refcount) +target_include_directories(olisp PUBLIC include/) +target_compile_options(olisp PRIVATE -Wall -Wpedantic) + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) + add_subdirectory(test/) +endif() diff --git a/include/lisp/array.h b/include/lisp/array.h new file mode 100644 index 0000000..4811c5b --- /dev/null +++ b/include/lisp/array.h @@ -0,0 +1,24 @@ +#ifndef INCLUDED_ARRAY_H +#define INCLUDED_ARRAY_H + +#include "lisp/object.h" +#include "lisp/util.h" + +LISP_BEGIN_DECLS + +DECLARE_CLASS(Array); + +struct Array { + Object base; + size_t length; + void *value; +}; +struct ArrayClass { + Class base; + Object *(*length)(Object *self); + Object *(*elt)(Object *self, Object *index); + Object *(*aset)(Object *self, Object *index, Object *value); +}; + +LISP_END_DECLS +#endif diff --git a/include/lisp/function.h b/include/lisp/function.h new file mode 100644 index 0000000..55a2a1b --- /dev/null +++ b/include/lisp/function.h @@ -0,0 +1,24 @@ +#ifndef INCLUDED_FUNCTION_H +#define INCLUDED_FUNCTION_H + +#include "lisp/hashtable.h" +#include "lisp/object.h" +#include "lisp/util.h" + +LISP_BEGIN_DECLS + +DECLARE_CLASS(Function); + +struct Function { + Object base; + Object *req_args; + Object *opt_args; + Object *kw_args; + Hashtable *arg_props; +}; +struct FunctionClass { + Class base; +}; + +LISP_END_DECLS +#endif diff --git a/include/lisp/hashtable.h b/include/lisp/hashtable.h new file mode 100644 index 0000000..17ca2ba --- /dev/null +++ b/include/lisp/hashtable.h @@ -0,0 +1,20 @@ +#ifndef INCLUDED_HASHTABLE_H +#define INCLUDED_HASHTABLE_H + +#include "lisp/object.h" +#include "lisp/util.h" + +LISP_BEGIN_DECLS + +DECLARE_CLASS(Hashtable); + +struct Hashtable { + Object base; + HTTable *table; +}; +struct HashtableClass { + Class base; +}; + +LISP_END_DECLS +#endif diff --git a/include/lisp/method_macro.h b/include/lisp/method_macro.h new file mode 100644 index 0000000..ecb3257 --- /dev/null +++ b/include/lisp/method_macro.h @@ -0,0 +1,139 @@ +#ifndef INCLUDED_METHOD_MACRO_H +#define INCLUDED_METHOD_MACRO_H + +#if __has_attribute(always_inline) +# define always_inline inline __attribute__((always_inline)) +#else +# define always_inline inline +#endif + +#if __has_attribute(unused) +# define UNUSED __attribute__((unused)) +#else +# define UNUSED +#endif + +// clang-format off +#define _ELEVENTH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, ...) a11 +#define _COUNT_ARGS(...) _ELEVENTH(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#define _GLUE(a, b) a##b +#define _GLUE2(a, b) _GLUE(a, b) +#define _IGNORE(...) +#define _REMOVE_FIRST(a1, ...) __VA_ARGS__ +#define _EVAL(...) __VA_ARGS__ +// clang-format on + +// This is needed because C11 doesn't allow empty __VA_ARGS__ +#define _GET_1_1(a1) a1 +#define _GET_1_2(a1, a2) a1 +#define _GET_1_3(a1, a2, a3) a1 +#define _GET_1_4(a1, a2, a3, a4) a1 +#define _GET_1_5(a1, a2, a3, a4, a5) a1 +#define _GET_1_6(a1, a2, a3, a4, a5, a6) a1 + +#define _GET_2_2(a1, a2) a2 +#define _GET_2_3(a1, a2, a3) a2 +#define _GET_2_4(a1, a2, a3, a4) a2 +#define _GET_2_5(a1, a2, a3, a4, a5) a2 +#define _GET_2_6(a1, a2, a3, a4, a5, a6) a2 + +#define _GET_3_3(a1, a2, a3) a3 +#define _GET_3_4(a1, a2, a3, a4) a3 +#define _GET_3_5(a1, a2, a3, a4, a5) a3 +#define _GET_3_6(a1, a2, a3, a4, a5, a6) a3 + +#define _GET_4_4(a1, a2, a3, a4) a4 +#define _GET_4_5(a1, a2, a3, a4, a5) a4 +#define _GET_4_6(a1, a2, a3, a4, a5, a6) a4 + +#define _GET_5_5(a1, a2, a3, a4, a5) a5 +#define _GET_5_6(a1, a2, a3, a4, a5, a6) a5 + +#define _GET_6_6(a1, a2, a3, a4, a5, a6) a6 + +#define _GET_NTH(n, args) \ + _EVAL(_GLUE2(_GLUE2(_GLUE2(_GET_, n), _), _COUNT_ARGS args) args) + +#define METHOD_CALLBACK_NAME(name) _##name##__as_callback + +#define _CB_DECL_ARGS_1 Object * +#define _CB_DECL_ARGS_2 Object *, _CB_DECL_ARGS_1 +#define _CB_DECL_ARGS_3 Object *, _CB_DECL_ARGS_2 +#define _CB_DECL_ARGS_4 Object *, _CB_DECL_ARGS_3 +#define _CB_DECL_ARGS_5 Object *, _CB_DECL_ARGS_4 +#define _CB_DECL_ARGS_6 Object *, _CB_DECL_ARGS_5 +#define _CB_DECL_ARGS_N(count) (_GLUE(_CB_DECL_ARGS_, count)) +#define _CB_DECL_ARGS_FOR(...) _CB_DECL_ARGS_N(_COUNT_ARGS(__VA_ARGS__)) +#define _GEN_CB_DECL(name, args) \ + Object *METHOD_CALLBACK_NAME(name) \ + _CB_DECL_ARGS_FOR args + +#define _ARG_NAME_1(args) _IGNORE _GET_NTH(1, args) +#define _ARG_NAME_2(args) _IGNORE _GET_NTH(2, args) +#define _ARG_NAME_3(args) _IGNORE _GET_NTH(3, args) +#define _ARG_NAME_4(args) _IGNORE _GET_NTH(4, args) +#define _ARG_NAME_5(args) _IGNORE _GET_NTH(5, args) +#define _ARG_NAME_6(args) _IGNORE _GET_NTH(6, args) +#define _ARG_NAME_N(n, args) _EVAL(_GLUE(_ARG_NAME_, n)(args)) + +#define _VOID_ARG_N(n, args) (void *) _ARG_NAME_N(n, args) +#define _CALL_ARGS_1(args) _VOID_ARG_N(1, args) +#define _CALL_ARGS_2(args) _CALL_ARGS_1(args), _VOID_ARG_N(2, args) +#define _CALL_ARGS_3(args) _CALL_ARGS_2(args), _VOID_ARG_N(3, args) +#define _CALL_ARGS_4(args) _CALL_ARGS_3(args), _VOID_ARG_N(4, args) +#define _CALL_ARGS_5(args) _CALL_ARGS_4(args), _VOID_ARG_N(5, args) +#define _CALL_ARGS_6(args) _CALL_ARGS_5(args), _VOID_ARG_N(6, args) +#define _CALL_ARGS_N(n, args) _GLUE(_CALL_ARGS_, n)(args) +#define _GEN_REAL_CALL(name, args) \ + METHOD_CALLBACK_NAME(name)(_CALL_ARGS_N(_COUNT_ARGS args, args)) + +#define _UNWRAP_TYPE(arg) _EVAL arg +#define _UNWRAP_ARG_N(n, args) _UNWRAP_TYPE(_GET_NTH(n, args)) +#define _UNWRAP_ARGS_1(args) _UNWRAP_ARG_N(1, args) +#define _UNWRAP_ARGS_2(args) _UNWRAP_ARGS_1(args), _UNWRAP_ARG_N(2, args) +#define _UNWRAP_ARGS_3(args) _UNWRAP_ARGS_2(args), _UNWRAP_ARG_N(3, args) +#define _UNWRAP_ARGS_4(args) _UNWRAP_ARGS_3(args), _UNWRAP_ARG_N(4, args) +#define _UNWRAP_ARGS_5(args) _UNWRAP_ARGS_4(args), _UNWRAP_ARG_N(5, args) +#define _UNWRAP_ARGS_6(args) _UNWRAP_ARGS_5(args), _UNWRAP_ARG_N(6, args) +#define _UNWRAP_ARGS_N(n, args) _GLUE(_UNWRAP_ARGS_, n)(args) +#define _UNWRAP_ARGS(args) _UNWRAP_ARGS_N(_COUNT_ARGS args, args) + +#define _METHOD_RAW(rtype, ret_stmt, name, args) \ + _GEN_CB_DECL(name, args); \ + static always_inline rtype name(_UNWRAP_ARGS(args)) { \ + ret_stmt _GEN_REAL_CALL(name, args); \ + } \ + struct __dummy_struct + +#define METHOD(rtype, name, args) _METHOD_RAW(rtype, return (rtype), name, args) +#define VOID_METHOD(name, args) _METHOD_RAW(void, , name, args) + +#define _CB_DEF_ARGS_1 Object *_internal_arg1 +#define _CB_DEF_ARGS_2 _CB_DEF_ARGS_1, Object *_internal_arg2 +#define _CB_DEF_ARGS_3 _CB_DEF_ARGS_2, Object *_internal_arg3 +#define _CB_DEF_ARGS_4 _CB_DEF_ARGS_3, Object *_internal_arg4 +#define _CB_DEF_ARGS_5 _CB_DEF_ARGS_4, Object *_internal_arg5 +#define _CB_DEF_ARGS_6 _CB_DEF_ARGS_5, Object *_internal_arg6 +#define _CB_DEF_ARGS_N(count) (_GLUE(_CB_DEF_ARGS_, count)) +#define _CB_DEF_ARGS_FOR(...) _CB_DEF_ARGS_N(_COUNT_ARGS(__VA_ARGS__)) +#define _GEN_CB_DEF(name, args) \ + Object *METHOD_CALLBACK_NAME(name) \ + _CB_DEF_ARGS_FOR args + +#define _SET_NTH_ARG(n, args) \ + UNUSED _GET_NTH(n, args) = (void *) _internal_arg##n; +#define _SET_ARGS_1(args) _SET_NTH_ARG(1, args) +#define _SET_ARGS_2(args) _SET_ARGS_1(args) _SET_NTH_ARG(2, args) +#define _SET_ARGS_3(args) _SET_ARGS_2(args) _SET_NTH_ARG(3, args) +#define _SET_ARGS_4(args) _SET_ARGS_3(args) _SET_NTH_ARG(4, args) +#define _SET_ARGS_5(args) _SET_ARGS_4(args) _SET_NTH_ARG(5, args) +#define _SET_ARGS_6(args) _SET_ARGS_5(args) _SET_NTH_ARG(6, args) +#define _SET_ARGS_N(n, args) _GLUE(_SET_ARGS_, n)(args) + +#define DEFINE_METHOD(rtype, name, args, body) \ + _GEN_CB_DEF(name, args) { \ + _SET_ARGS_N(_COUNT_ARGS args, args) \ + body \ + } + +#endif diff --git a/include/lisp/number.h b/include/lisp/number.h new file mode 100644 index 0000000..c2a76df --- /dev/null +++ b/include/lisp/number.h @@ -0,0 +1,42 @@ +#ifndef INCLUDED_NUMBER_H +#define INCLUDED_NUMBER_H + +#include "lisp/object.h" +#include "lisp/util.h" + +LISP_BEGIN_DECLS + +DECLARE_CLASS(Number); +DECLARE_CLASS(Float); +DECLARE_CLASS(Integer); + +struct Number { + Object base; +}; +struct NumberClass { + Class base; + + Object *(*add)(Object *self, Object *other); + Object *(*sub)(Object *self, Object *other); +}; + +struct Float { + Number base; + + double value; +}; +struct FloatClass { + NumberClass base; +}; + +struct Integer { + Number base; + + int64_t value; +}; +struct IntegerClass { + NumberClass base; +}; + +LISP_END_DECLS +#endif diff --git a/include/lisp/object.h b/include/lisp/object.h new file mode 100644 index 0000000..af6d31d --- /dev/null +++ b/include/lisp/object.h @@ -0,0 +1,73 @@ +#ifndef INCLUDED_OBJECT_H +#define INCLUDED_OBJECT_H + +#include "lisp/method_macro.h" +#include "lisp/util.h" + +#include +#include +#include + +LISP_BEGIN_DECLS + +#define DECLARE_CLASS(Name) \ + typedef struct Name Name; \ + typedef struct Name##Class Name##Class + +// clang-format off +#define ELEVENTH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, ...) a11 +#define COUNT_ARGS(...) ELLEVENTH(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) +// clang-format on + +#undef COUNT_ARGS +#undef TENTH + +typedef struct Object Object; +typedef struct Class Class; + +struct Object { + RefcountEntry refcount; + Class *class; + + Object **slots; +}; + +struct Class { + Object base; + Class *superclass; + HTTable *subclasses; + size_t object_size; + + HTTable *slots_indicies; + uint64_t nslots; + HTTable *methods; + + Object *(*construct)(Object *self, Object *args); +}; + +Object *make_object(Object *class, Object *args); + +DECLARE_CLASS(Pair); + +struct Pair { + Object base; + + Object *head; + Object *tail; +}; +struct PairClass { + Class base; + + Object *(*head)(Object *self); + Object *(*sethead)(Object *self, Object *new_head); + Object *(*tail)(Object *self); + Object *(*settail)(Object *self, Object *new_tail); +}; + +METHOD(Object *, head, ((Pair *) self)); +VOID_METHOD(sethead, ((Pair *) self)); +METHOD(Object *, tail, ((Pair *) self)); +VOID_METHOD(settail, ((Pair *) self)); + +LISP_END_DECLS +#endif diff --git a/include/lisp/string.h b/include/lisp/string.h new file mode 100644 index 0000000..df53ef8 --- /dev/null +++ b/include/lisp/string.h @@ -0,0 +1,20 @@ +#ifndef INCLUDED_STRING_H +#define INCLUDED_STRING_H + +#include "lisp/array.h" +#include "lisp/util.h" + +LISP_BEGIN_DECLS + +DECLARE_CLASS(String); + +struct String { + Array base; + bool borrowed; +}; +struct StringClass { + ArrayClass base; +}; + +LISP_END_DECLS +#endif diff --git a/include/lisp/symbol.h b/include/lisp/symbol.h new file mode 100644 index 0000000..fdd7998 --- /dev/null +++ b/include/lisp/symbol.h @@ -0,0 +1,32 @@ +#ifndef INCLUDED_SYMBOL_H +#define INCLUDED_SYMBOL_H + +#include "lisp/function.h" +#include "lisp/object.h" +#include "lisp/string.h" +#include "lisp/util.h" + +LISP_BEGIN_DECLS + +DECLARE_CLASS(Symbol); + +struct Symbol { + Object base; + String *name; + + Object *value; + Class *class; + Function *func; + Object *plist; +}; +struct SymbolClass { + Class base; + + Object *(*value)(Object *self); + Object *(*class)(Object *self); + Object *(*function)(Object *self); + Object *(*plist)(Object *self); +}; + +LISP_END_DECLS +#endif diff --git a/include/lisp/util.h b/include/lisp/util.h new file mode 100644 index 0000000..8db07da --- /dev/null +++ b/include/lisp/util.h @@ -0,0 +1,12 @@ +#ifndef INCLUDED_UTIL_H +#define INCLUDED_UTIL_H + +#ifdef __cplusplus +# define LISP_BEGIN_DECLS extern "C" { +# define LISP_END_DECLS } +#else +# define LISP_BEGIN_DECLS +# define LISP_END_DECLS +#endif + +#endif diff --git a/src/array.c b/src/array.c new file mode 100644 index 0000000..35d37a8 --- /dev/null +++ b/src/array.c @@ -0,0 +1 @@ +#include "lisp/array.h" diff --git a/src/function.c b/src/function.c new file mode 100644 index 0000000..5ec973b --- /dev/null +++ b/src/function.c @@ -0,0 +1 @@ +#include "lisp/function.h" diff --git a/src/hashtable.c b/src/hashtable.c new file mode 100644 index 0000000..4222637 --- /dev/null +++ b/src/hashtable.c @@ -0,0 +1 @@ +#include "lisp/hashtable.h" diff --git a/src/number.c b/src/number.c new file mode 100644 index 0000000..31968d6 --- /dev/null +++ b/src/number.c @@ -0,0 +1 @@ +#include "lisp/number.h" diff --git a/src/object.c b/src/object.c new file mode 100644 index 0000000..aab5678 --- /dev/null +++ b/src/object.c @@ -0,0 +1,11 @@ +#include "lisp/object.h" + +#include "lisp/symbol.h" + +Object *make_object(Object *class, Object *args) { + return NULL; +} + +DEFINE_METHOD(Object *, head, (Pair * self), { + return NULL; // +}) diff --git a/src/string.c b/src/string.c new file mode 100644 index 0000000..0a6b783 --- /dev/null +++ b/src/string.c @@ -0,0 +1 @@ +#include "lisp/string.h" diff --git a/src/symbol.c b/src/symbol.c new file mode 100644 index 0000000..8bd6550 --- /dev/null +++ b/src/symbol.c @@ -0,0 +1 @@ +#include "lisp/symbol.h" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..c628823 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,5 @@ +add_compile_options(-Wall -Wpedantic) +link_libraries(olisp) + +add_executable(test_object test_object.c) +add_test(NAME object COMMAND test_object) diff --git a/test/test_object.c b/test/test_object.c new file mode 100644 index 0000000..9b38cae --- /dev/null +++ b/test/test_object.c @@ -0,0 +1,5 @@ +#include + +int main(int argc, const char **argv) { + return 0; +}