diff --git a/.gitignore b/.gitignore index 76900774..ce5fad40 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,2 @@ -# lone binary -./lone - -# generated files and code -NR.list -NR.c +# build tree +build/ diff --git a/GNUmakefile b/GNUmakefile index a1ff75e1..79d467b2 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,5 +1,10 @@ # SPDX-License-Identifier: AGPL-3.0-or-later +MAKEFLAGS += --no-builtin-variables --no-builtin-rules + +CC := cc +CFLAGS := -Wall -Wextra -Wpedantic -Os + ifdef TARGET ifndef UAPI $(error UAPI must be defined when cross compiling) @@ -11,30 +16,70 @@ else TARGET := $(shell uname -m) endif -override ARCH := $(TARGET) -override ARCH.c := arch/$(ARCH).c +source_to_object = $(patsubst $(directories.source)/%.c,$(directories.build.objects)/%.o,$(1)) +source_to_prerequisite = $(patsubst $(directories.source)/%.c,$(directories.build.prerequisites)/%.d,$(1)) -CFLAGS := -Wall -Wextra -Wpedantic -Os -override directories := $(if $(UAPI),-isystem $(UAPI)) -override definitions := -D LONE_ARCH=$(ARCH) -D LONE_ARCH_SOURCE='"$(ARCH.c)"' -D LONE_NR_SOURCE='"NR.c"' -override essential_flags := $(definitions) -ffreestanding -nostdlib -Wl,-elone_start -static -fno-omit-frame-pointer -fshort-enums -override CC := $(strip $(CC) $(directories)) +ARCH := $(TARGET) -lone : lone.c NR.c $(ARCH.c) - $(CC) $(essential_flags) $(CFLAGS) -o $@ $< +directories.build := build/$(ARCH) +directories.build.objects := $(directories.build)/objects +directories.build.prerequisites := $(directories.build)/prerequisites +directories.build.include := $(directories.build)/include +directories.create := -phony += clean -clean: - rm -f lone NR.list NR.c +directories.include := include architecture/$(ARCH)/include $(directories.build.include) +directories.source := source -phony += test -test: lone - scripts/test.bash +files.sources := $(shell find $(directories.source) -type f) -NR.list: scripts/NR.filter - $(CC) -E -dM -include linux/unistd.h - < /dev/null | scripts/NR.filter > $@ +targets.phony := +targets.NR.list := $(directories.build)/NR.list +targets.NR.c := $(directories.build.include)/lone/NR.c +targets.NR := $(targets.NR.list) $(targets.NR.c) +targets.objects := $(call source_to_object,$(files.sources)) +targets.lone := $(directories.build)/lone +targets.prerequisites := $(call source_to_prerequisite,$(files.sources)) + +directories.create += $(dir $(targets.lone) $(targets.objects) $(targets.prerequisites) $(targets.NR)) + +flags.definitions := -D LONE_ARCH=$(ARCH) +flags.include_directories := $(foreach directory,$(directories.include),-I $(directory)) +flags.system_include_directories := $(if $(UAPI),-isystem $(UAPI)) +flags.prerequisites_generation = -MMD -MF $(call source_to_prerequisite,$(<)) +flags.common := -static -ffreestanding -nostdlib -fno-omit-frame-pointer -fshort-enums -flto +flags.object = $(flags.system_include_directories) $(flags.include_directories) $(flags.prerequisites_generation) $(flags.definitions) $(flags.common) +flags.lone = $(flags.common) -Wl,-elone_start + +$(directories.build.objects)/%.o: $(directories.source)/%.c | directories + $(strip $(CC) $(flags.object) $(CFLAGS) -o $@ -c $<) + +$(targets.lone): $(targets.objects) | directories + $(strip $(CC) $(flags.lone) $(CFLAGS) -o $@ $^) -NR.c: NR.list scripts/NR.generate +$(call source_to_object,source/lone/modules/linux.c): $(targets.NR.c) + +$(targets.NR.c): $(targets.NR.list) scripts/NR.generate scripts/NR.generate < $< > $@ -.PHONY: $(phony) +$(targets.NR.list): scripts/NR.filter + $(CC) -E -dM -include linux/unistd.h - < /dev/null | scripts/NR.filter > $@ + +targets.phony += lone +lone: $(targets.lone) + +targets.phony += clean +clean: + rm -rf $(directories.build) + +targets.phony += test +test: $(targets.lone) + scripts/test.bash $< + +targets.phony += directories +directories: + mkdir -p $(sort $(directories.create)) + +.PHONY: $(targets.phony) +.DEFAULT_GOAL := lone + +sinclude $(targets.prerequisites) diff --git a/README.md b/README.md index 83b1542e..de9d8072 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,11 @@ Any of the following commands can be used: make clean lone UAPI=/alternative/linux/uapi/headers make clean lone TARGET=x86_64 UAPI=/linux/uapi/headers/x86_64 +Currently supported targets: + + - `x86_64` + - `aarch64` + ## Testing Lone has an automated test suite that exercises language features. @@ -32,7 +37,7 @@ Any of the following commands can be used to run it: make test make clean test - scripts/test.bash + scripts/test.bash [lone-executable [test-suite-directory]] New tests are added by creating directories inside `test/`, forming an arbitrary directory tree which determines the test name. @@ -61,30 +66,118 @@ the successful status code `0` is expected. ## Project structure - lone/ # The lone repository - ├── arch/ # Architecture-specific code, one file each - │ ├── aarch64.c # System calls and process start for aarch64 - │ └── x86_64.c # System calls and process start for x86_64 - ├── scripts/ # Small support programs for development - │ ├── NR.filter # Extracts system call definitions from compiler output - │ ├── NR.generate # Generates C structure initializers for system call names and numbers - │ ├── test.bash # The automated testing script - │ └── test.new # The new test case creation script - ├── test/ # The lone test suite - │ └── arbitrary/tree/ # Arbitrary tree, determines test name, leaves contain test files - │ ├── arguments # Arguments passed, one per line - │ ├── environment # Environment variables set, one per line - │ ├── input # Standard input - │ ├── output # Expected standard output - │ ├── error # Expected standard error - │ └── status # Expected exit status - ├── GNUmakefile # The GNU Make file - ├── LICENSE.AGPLv3 # GNU Affero General Public License version 3, full license text - ├── lone # The lone executable produced by make - ├── lone.c # The lone C source code - ├── README.md # This README file - ├── .gdbinit # GDB visualization functions for lone's data structures - └── .github/ # GitHub-specific data - └── workflows/ # GitHub Actions workflows - ├── codeql.yml # Automated code quality checker - └── lone.yml # Automated building and testing + lone/ # The lone repository + ├── build/ # The build tree + ├── include/ # Header files + ├── source/ # Source files + ├── architecture/ # Architecture-specific tree + ├── scripts/ # Development tools and test suite + ├── test/ # The lone test suite + ├── GNUmakefile # The build system + ├── LICENSE.AGPLv3 # Full license text of the GNU AGPLv3 + ├── README.md # This README file + ├── .gdbinit # GDB visualization functions for lone's data structures + └── .github/ # GitHub-specific data + + lone/include/ # Added to compiler include directories + └── lone/ # Lone namespace + ├── hash/ # Hash function implementations + │ └── fnv_1a.h # Fowler–Noll–Vo hash function + ├── lisp/ # Lone lisp language features + │ ├── constants.h # Constants like nil and true + │ ├── evaluator.h # Evaluates lone values + │ ├── printer.h # Writes lone values into text + │ └── reader.h # Reads text into lone values + ├── memory/ # Lone's memory subsystem + │ ├── allocator.h # General memory block allocator + │ ├── functions.h # Memory moving and filling functions + │ ├── garbage_collector.h # The lone garbage collector + │ └── heap.h # The lone value heap + ├── modules/ # Intrinsic lone modules + │ ├── intrinsic.h # Bulk initializer for all built-in modules + │ ├── linux.h # Linux system calls and process parameters + │ ├── list.h # List functions + │ ├── lone.h # Lone language primitives + │ ├── math.h # Mathematical functions + │ └── text.h # Text manipulation functions + ├── struct/ # Lone structure definitions + │ ├── auxiliary.h # Auxiliary vector elements + │ ├── bytes.h # Memory segments of known size + │ ├── function.h # Reusable code blocks + │ ├── heap.h # Heap from where values are allocated + │ ├── lisp.h # Lone lisp interpreter + │ ├── list.h # Linked list of lone values + │ ├── memory.h # Memory blocks managed by lone + │ ├── module.h # Modules + │ ├── pointer.h # Typed pointers + │ ├── primitive.h # Functions implemented in C + │ ├── reader.h # Reader state and buffer + │ ├── table.h # Hash table with prototypal inheritance + │ ├── value.h # Tagged and flagged union of all value types + │ └── vector.h # Contiguous arrays of lone values + ├── value/ # Functions for each type of value + │ ├── bytes.h # Creation and transfer functions + │ ├── function.h # Function and closure instantiation + │ ├── integer.h # Integer value creation and parsing + │ ├── list.h # List construction and processing + │ ├── module.h # Module value creation + │ ├── pointer.h # Typed pointer value creation + │ ├── primitive.h # Primitive C function binding creation + │ ├── symbol.h # Symbol creation and interning + │ ├── table.h # Hash table creation and operations + │ ├── text.h # Text value creation and C string transfers + │ └── vector.h # Vector creation and operations + ├── definitions.h # Defined constants and macros + ├── hash.h # General hashing functions + ├── linux.h # Linux system calls used by lone + ├── lisp.h # Lone lisp interpreter initialization + ├── memory.h # Lone memory subsystem initialization + ├── modules.h # Module loading, search, path management + ├── structures.h # Includes all lone structure definitions + ├── types.h # Basic type definitions and forward declarations + ├── utilities.h # Useful functions + └── value.h # Blank slate lone value creation + + lone/source/ # Lone lisp implementation source code + ├── lone/ # Matches the structure or the include/ directory + └── lone.c # The main lone function + + lone/architecture/ + └── $ARCH/ + └── include/ # Added to compiler include directories + └── lone/architecture/ + ├── linux/ + │ ├── entry_point.c # Process start code + │ └── system_calls.c # Linux system call stubs + └── garbage_collector.c # Register spilling code + + lone/build/ + └── $ARCH/ # The targeted architecture + ├── include/ # Added to compiler include directories + │ └── lone/ + │ └── NR.c # Generated Linux system call table initializers + ├── objects/ # Compiled object files; mirrors source tree structure + ├── prerequisites/ # Prerequisite files; mirrors source tree structure + ├── NR.list # List of system calls found on the targeted Linux UAPI + └── lone # The built lone lisp freestanding executable + + lone/scripts/ + ├── NR.filter # Extracts system call definitions from compiler output + ├── NR.generate # Generates C structure initializers for system call names and numbers + ├── test.bash # The automated test suite script + └── test.new # The new test case creation script + + lone/test/ + └── arbitrary/tree/ # Arbitrary tree, determines test name, leaves contain test files + ├── arguments # Arguments passed, one per line + ├── environment # Environment variables set, one per line + ├── input # Standard input + ├── output # Expected standard output + ├── error # Expected standard error + └── status # Expected exit status + + lone/.github/ + ├── workflows/ # GitHub Actions workflows + │ ├── codeql.yml # Automated code quality checker + │ └── lone.yml # Automated building and testing + └── FUNDING.yml # Funding information diff --git a/arch/aarch64.c b/arch/aarch64.c deleted file mode 100644 index 0288c3cd..00000000 --- a/arch/aarch64.c +++ /dev/null @@ -1,234 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ - -#include - -/** - * - * architecture: aarch64 - * register-size: 64 bits - * stack-alignment: 16 bytes - * system-call: x0 = "svc 0" [x8] x0 x1 x2 x3 x4 x5 - * - * https://github.com/ARM-software/abi-aa - * https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst - * - **/ - -static long -system_call_0(long n) -{ - register long x8 __asm__("x8") = n; - register long x0 __asm__("x0"); - - __asm__ volatile - ("svc 0" - - : "=r" (x0) - : "r" (x8) - : "cc", "memory"); - - return x0; -} - -static long -system_call_1(long n, long _1) -{ - register long x8 __asm__("x8") = n; - register long x0 __asm__("x0") = _1; - - __asm__ volatile - ("svc 0" - - : "+r" (x0) - : "r" (x8) - : "cc", "memory"); - - return x0; -} - -static long -system_call_2(long n, long _1, long _2) -{ - register long x8 __asm__("x8") = n; - register long x0 __asm__("x0") = _1; - register long x1 __asm__("x1") = _2; - - __asm__ volatile - ("svc 0" - - : "+r" (x0) - : "r" (x1), - "r" (x8) - : "cc", "memory"); - - return x0; -} - -static long -system_call_3(long n, long _1, long _2, long _3) -{ - register long x8 __asm__("x8") = n; - register long x0 __asm__("x0") = _1; - register long x1 __asm__("x1") = _2; - register long x2 __asm__("x2") = _3; - - __asm__ volatile - ("svc 0" - - : "+r" (x0) - : "r" (x1), "r" (x2), - "r" (x8) - : "cc", "memory"); - - return x0; -} - -static long -system_call_4(long n, long _1, long _2, long _3, long _4) -{ - register long x8 __asm__("x8") = n; - register long x0 __asm__("x0") = _1; - register long x1 __asm__("x1") = _2; - register long x2 __asm__("x2") = _3; - register long x3 __asm__("x3") = _4; - - __asm__ volatile - ("svc 0" - - : "+r" (x0) - : "r" (x1), "r" (x2), "r" (x3), - "r" (x8) - : "cc", "memory"); - - return x0; -} - -static long -system_call_5(long n, long _1, long _2, long _3, long _4, long _5) -{ - register long x8 __asm__("x8") = n; - register long x0 __asm__("x0") = _1; - register long x1 __asm__("x1") = _2; - register long x2 __asm__("x2") = _3; - register long x3 __asm__("x3") = _4; - register long x4 __asm__("x4") = _5; - - __asm__ volatile - ("svc 0" - - : "+r" (x0) - : "r" (x1), "r" (x2), "r" (x3), "r" (x4), - "r" (x8) - : "cc", "memory"); - - return x0; -} - -static long -system_call_6(long n, long _1, long _2, long _3, long _4, long _5, long _6) -{ - register long x8 __asm__("x8") = n; - register long x0 __asm__("x0") = _1; - register long x1 __asm__("x1") = _2; - register long x2 __asm__("x2") = _3; - register long x3 __asm__("x3") = _4; - register long x4 __asm__("x4") = _5; - register long x5 __asm__("x5") = _6; - - __asm__ volatile - ("svc 0" - - : "+r" (x0) - : "r" (x1), "r" (x2), "r" (x3), "r" (x4), "r" (x5), - "r" (x8) - : "cc", "memory"); - - return x0; -} - -/** - * Registers may contain pointers to garbage collector roots. - * They must be spilled onto the stack so that they can be marked. - * Link register is the only architectural register, others are conventional. - * Nearly all of arm64's registers may be used as scratch or result registers. - * Probably best to just save all 30 of them just in case. - **/ -typedef long lone_registers[30]; -extern void lone_save_registers(lone_registers); - -__asm__ -( - -".global lone_save_registers" "\n" -".type lone_save_registers,@function" "\n" - -"lone_save_registers:" "\n" // x0 = &lone_registers -"stp x0, x1, [x0, #0 ]" "\n" -"stp x2, x3, [x0, #16 ]" "\n" -"stp x4, x5, [x0, #32 ]" "\n" -"stp x6, x7, [x0, #48 ]" "\n" -"stp x8, x9, [x0, #64 ]" "\n" -"stp x10, x11, [x0, #80 ]" "\n" -"stp x12, x13, [x0, #96 ]" "\n" -"stp x14, x15, [x0, #112]" "\n" -"stp x16, x17, [x0, #128]" "\n" -"stp x18, x19, [x0, #144]" "\n" -"stp x20, x21, [x0, #160]" "\n" -"stp x22, x23, [x0, #176]" "\n" -"stp x24, x25, [x0, #192]" "\n" -"stp x26, x27, [x0, #208]" "\n" -"stp x28, x29, [x0, #224]" "\n" -"ret" "\n" - -); - -/** - * - * initial stack layout - logical - * - * sp → 0 | argc - * 1 | argv - * argv + *argc + 1 | envp - * &(*envp++ == 0) + 1 | auxv - * - * initial stack layout - bytes - * - * sp → 0 | argc - * 8 | argv - * argv + 8 * (*argc + 1) | envp - * &(*envp++ == 0) + 8 | auxv - * - **/ -__asm__ -( - -".global lone_start" "\n" // place lone_start in the symbol table -"lone_start:" "\n" // program entry point - - // compute argc, argv, envp and auxv -"ldr x0, [sp]" "\n" // argc: x0 = *sp -"add x1, sp, 8" "\n" // argv: x1 = sp + 8 -"add x2, x0, 1" "\n" // x2 = argc + 1 -"lsl x2, x2, 3" "\n" // x2 = x2 * 8 -"add x2, x1, x2" "\n" // envp: x2 = argv + x2 -"mov x3, x2" "\n" // x3 = envp -"0:" "\n" // null finder loop: -"ldr x8, [x3], 8" "\n" // x8 = *x3 - // x3 = x3 + 8 -"cbnz x8, 0b" "\n" // goto loop if x8 != 0 - // auxv: x3 - -"and sp, x1, -16" "\n" // ensure 16 byte alignment - -"bl lone" "\n" // call lone; returns status code in x0 - -#define S2(s) #s -#define S(s) S2(s) - -"mov x8, " S(__NR_exit) "\n" // ensure clean process termination -"svc 0" "\n" // exit with returned status code - -#undef S2 -#undef S - -); diff --git a/architecture/aarch64/include/lone/architecture/garbage_collector.c b/architecture/aarch64/include/lone/architecture/garbage_collector.c new file mode 100644 index 00000000..d5b05ef5 --- /dev/null +++ b/architecture/aarch64/include/lone/architecture/garbage_collector.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +/** + * Registers may contain pointers to garbage collector roots. + * They must be spilled onto the stack so that they can be marked. + * Link register is the only architectural register, others are conventional. + * Nearly all of arm64's registers may be used as scratch or result registers. + * Probably best to just save all 30 of them just in case. + **/ +typedef long lone_registers[30]; +extern void lone_save_registers(lone_registers); + +__asm__ +( + +".global lone_save_registers" "\n" +".type lone_save_registers,@function" "\n" + +"lone_save_registers:" "\n" // x0 = &lone_registers +"stp x0, x1, [x0, #0 ]" "\n" +"stp x2, x3, [x0, #16 ]" "\n" +"stp x4, x5, [x0, #32 ]" "\n" +"stp x6, x7, [x0, #48 ]" "\n" +"stp x8, x9, [x0, #64 ]" "\n" +"stp x10, x11, [x0, #80 ]" "\n" +"stp x12, x13, [x0, #96 ]" "\n" +"stp x14, x15, [x0, #112]" "\n" +"stp x16, x17, [x0, #128]" "\n" +"stp x18, x19, [x0, #144]" "\n" +"stp x20, x21, [x0, #160]" "\n" +"stp x22, x23, [x0, #176]" "\n" +"stp x24, x25, [x0, #192]" "\n" +"stp x26, x27, [x0, #208]" "\n" +"stp x28, x29, [x0, #224]" "\n" +"ret" "\n" + +); diff --git a/architecture/aarch64/include/lone/architecture/linux/entry_point.c b/architecture/aarch64/include/lone/architecture/linux/entry_point.c new file mode 100644 index 00000000..e74b5fc5 --- /dev/null +++ b/architecture/aarch64/include/lone/architecture/linux/entry_point.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include + +/** + * + * initial stack layout - logical + * + * sp → 0 | argc + * 1 | argv + * argv + *argc + 1 | envp + * &(*envp++ == 0) + 1 | auxv + * + * initial stack layout - bytes + * + * sp → 0 | argc + * 8 | argv + * argv + 8 * (*argc + 1) | envp + * &(*envp++ == 0) + 8 | auxv + * + **/ +__asm__ +( + +".global lone_start" "\n" // place lone_start in the symbol table +"lone_start:" "\n" // program entry point + + // compute argc, argv, envp and auxv +"ldr x0, [sp]" "\n" // argc: x0 = *sp +"add x1, sp, 8" "\n" // argv: x1 = sp + 8 +"add x2, x0, 1" "\n" // x2 = argc + 1 +"lsl x2, x2, 3" "\n" // x2 = x2 * 8 +"add x2, x1, x2" "\n" // envp: x2 = argv + x2 +"mov x3, x2" "\n" // x3 = envp +"0:" "\n" // null finder loop: +"ldr x8, [x3], 8" "\n" // x8 = *x3 + // x3 = x3 + 8 +"cbnz x8, 0b" "\n" // goto loop if x8 != 0 + // auxv: x3 + +"and sp, x1, -16" "\n" // ensure 16 byte alignment + +"bl lone" "\n" // call lone; returns status code in x0 + +#define S2(s) #s +#define S(s) S2(s) + +"mov x8, " S(__NR_exit) "\n" // ensure clean process termination +"svc 0" "\n" // exit with returned status code + +#undef S2 +#undef S + +); diff --git a/architecture/aarch64/include/lone/architecture/linux/system_calls.c b/architecture/aarch64/include/lone/architecture/linux/system_calls.c new file mode 100644 index 00000000..0edeb227 --- /dev/null +++ b/architecture/aarch64/include/lone/architecture/linux/system_calls.c @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include + +/** + * + * architecture: aarch64 + * register-size: 64 bits + * stack-alignment: 16 bytes + * system-call: x0 = "svc 0" [x8] x0 x1 x2 x3 x4 x5 + * + * https://github.com/ARM-software/abi-aa + * https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst + * + **/ + +long linux_system_call_0(long n) +{ + register long x8 __asm__("x8") = n; + register long x0 __asm__("x0"); + + __asm__ volatile + ("svc 0" + + : "=r" (x0) + : "r" (x8) + : "cc", "memory"); + + return x0; +} + +long linux_system_call_1(long n, long _1) +{ + register long x8 __asm__("x8") = n; + register long x0 __asm__("x0") = _1; + + __asm__ volatile + ("svc 0" + + : "+r" (x0) + : "r" (x8) + : "cc", "memory"); + + return x0; +} + +long linux_system_call_2(long n, long _1, long _2) +{ + register long x8 __asm__("x8") = n; + register long x0 __asm__("x0") = _1; + register long x1 __asm__("x1") = _2; + + __asm__ volatile + ("svc 0" + + : "+r" (x0) + : "r" (x1), + "r" (x8) + : "cc", "memory"); + + return x0; +} + +long linux_system_call_3(long n, long _1, long _2, long _3) +{ + register long x8 __asm__("x8") = n; + register long x0 __asm__("x0") = _1; + register long x1 __asm__("x1") = _2; + register long x2 __asm__("x2") = _3; + + __asm__ volatile + ("svc 0" + + : "+r" (x0) + : "r" (x1), "r" (x2), + "r" (x8) + : "cc", "memory"); + + return x0; +} + +long linux_system_call_4(long n, long _1, long _2, long _3, long _4) +{ + register long x8 __asm__("x8") = n; + register long x0 __asm__("x0") = _1; + register long x1 __asm__("x1") = _2; + register long x2 __asm__("x2") = _3; + register long x3 __asm__("x3") = _4; + + __asm__ volatile + ("svc 0" + + : "+r" (x0) + : "r" (x1), "r" (x2), "r" (x3), + "r" (x8) + : "cc", "memory"); + + return x0; +} + +long linux_system_call_5(long n, long _1, long _2, long _3, long _4, long _5) +{ + register long x8 __asm__("x8") = n; + register long x0 __asm__("x0") = _1; + register long x1 __asm__("x1") = _2; + register long x2 __asm__("x2") = _3; + register long x3 __asm__("x3") = _4; + register long x4 __asm__("x4") = _5; + + __asm__ volatile + ("svc 0" + + : "+r" (x0) + : "r" (x1), "r" (x2), "r" (x3), "r" (x4), + "r" (x8) + : "cc", "memory"); + + return x0; +} + +long linux_system_call_6(long n, long _1, long _2, long _3, long _4, long _5, long _6) +{ + register long x8 __asm__("x8") = n; + register long x0 __asm__("x0") = _1; + register long x1 __asm__("x1") = _2; + register long x2 __asm__("x2") = _3; + register long x3 __asm__("x3") = _4; + register long x4 __asm__("x4") = _5; + register long x5 __asm__("x5") = _6; + + __asm__ volatile + ("svc 0" + + : "+r" (x0) + : "r" (x1), "r" (x2), "r" (x3), "r" (x4), "r" (x5), + "r" (x8) + : "cc", "memory"); + + return x0; +} diff --git a/architecture/x86_64/include/lone/architecture/garbage_collector.c b/architecture/x86_64/include/lone/architecture/garbage_collector.c new file mode 100644 index 00000000..eb33d69c --- /dev/null +++ b/architecture/x86_64/include/lone/architecture/garbage_collector.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +/** + * Registers may contain pointers to garbage collector roots. + * They must be spilled onto the stack so that they can be marked. + * The x86_64 provides 16 general purpose registers. + **/ +typedef long lone_registers[16]; +extern void lone_save_registers(lone_registers); + +__asm__ +( + +".global lone_save_registers" "\n" +".type lone_save_registers,@function" "\n" + +"lone_save_registers:" "\n" // rdi = &lone_registers +"mov %rax, 0(%rdi)" "\n" +"mov %rbx, 8(%rdi)" "\n" +"mov %rcx, 16(%rdi)" "\n" +"mov %rdx, 24(%rdi)" "\n" +"mov %rsp, 32(%rdi)" "\n" +"mov %rbp, 40(%rdi)" "\n" +"mov %rsi, 48(%rdi)" "\n" +"mov %rdi, 56(%rdi)" "\n" +"mov %r8, 64(%rdi)" "\n" +"mov %r9, 72(%rdi)" "\n" +"mov %r10, 80(%rdi)" "\n" +"mov %r11, 88(%rdi)" "\n" +"mov %r12, 96(%rdi)" "\n" +"mov %r13, 104(%rdi)" "\n" +"mov %r14, 112(%rdi)" "\n" +"mov %r15, 120(%rdi)" "\n" +"ret" "\n" + +); diff --git a/architecture/x86_64/include/lone/architecture/linux/entry_point.c b/architecture/x86_64/include/lone/architecture/linux/entry_point.c new file mode 100644 index 00000000..8e1ac0bb --- /dev/null +++ b/architecture/x86_64/include/lone/architecture/linux/entry_point.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include + +/** + * + * initial stack layout - logical + * + * sp → 0 | argc + * 1 | argv + * argv + *argc + 1 | envp + * &(*envp++ == 0) + 1 | auxv + * + * initial stack layout - bytes + * + * sp → 0 | argc + * 8 | argv + * argv + (*argc * 8) + 8 | envp + * &(*envp++ == 0) + 8 | auxv + * + **/ +__asm__ +( + +".global lone_start" "\n" // place lone_start in the symbol table +"lone_start:" "\n" // program entry point + + // compute argc, argv, envp and auxv + +"pop %rdi" "\n" // argc: rdi = pop +"mov %rsp, %rsi" "\n" // argv: rsi = sp +"lea 8(%rsi, %rdi, 8), %rdx" "\n" // envp: rdx = rsi + (rdi * 8) + 8 + +"lea 0(%rdx), %rcx" "\n" // rcx = rdx +"0:" "\n" // loop: +"add $8, %rcx" "\n" // rcx = rcx + 9 +"cmpq $0, -8(%rcx)" "\n" // *(rcx - 8) == 0 ? +"jnz 0b" "\n" // loop if not zero + // rcx - 8 == 0 + // auxv: rcx + + // x86_64 SysV ABI requirements: +"xor %rbp, %rbp" "\n" // zero the deepest stack frame +"and $-16, %rsp" "\n" // ensure 16 byte stack alignment + +"call lone" "\n" // call lone +"mov %rax, %rdi" "\n" // status code returned in rax + +#define S2(s) #s +#define S(s) S2(s) + +"mov $" S(__NR_exit) ", %rax" "\n" // ensure clean process termination +"syscall" "\n" // exit with returned status code + +#undef S2 +#undef S + +); diff --git a/arch/x86_64.c b/architecture/x86_64/include/lone/architecture/linux/system_calls.c similarity index 52% rename from arch/x86_64.c rename to architecture/x86_64/include/lone/architecture/linux/system_calls.c index aadb1e54..50fb557e 100644 --- a/arch/x86_64.c +++ b/architecture/x86_64/include/lone/architecture/linux/system_calls.c @@ -139,93 +139,3 @@ static long system_call_6(long number, long _1, long _2, long _3, long _4, long return rax; } - -/** - * Registers may contain pointers to garbage collector roots. - * They must be spilled onto the stack so that they can be marked. - * The x86_64 provides 16 general purpose registers. - **/ -typedef long lone_registers[16]; -extern void lone_save_registers(lone_registers); - -__asm__ -( - -".global lone_save_registers" "\n" -".type lone_save_registers,@function" "\n" - -"lone_save_registers:" "\n" // rdi = &lone_registers -"mov %rax, 0(%rdi)" "\n" -"mov %rbx, 8(%rdi)" "\n" -"mov %rcx, 16(%rdi)" "\n" -"mov %rdx, 24(%rdi)" "\n" -"mov %rsp, 32(%rdi)" "\n" -"mov %rbp, 40(%rdi)" "\n" -"mov %rsi, 48(%rdi)" "\n" -"mov %rdi, 56(%rdi)" "\n" -"mov %r8, 64(%rdi)" "\n" -"mov %r9, 72(%rdi)" "\n" -"mov %r10, 80(%rdi)" "\n" -"mov %r11, 88(%rdi)" "\n" -"mov %r12, 96(%rdi)" "\n" -"mov %r13, 104(%rdi)" "\n" -"mov %r14, 112(%rdi)" "\n" -"mov %r15, 120(%rdi)" "\n" -"ret" "\n" - -); - -/** - * - * initial stack layout - logical - * - * sp → 0 | argc - * 1 | argv - * argv + *argc + 1 | envp - * &(*envp++ == 0) + 1 | auxv - * - * initial stack layout - bytes - * - * sp → 0 | argc - * 8 | argv - * argv + (*argc * 8) + 8 | envp - * &(*envp++ == 0) + 8 | auxv - * - **/ -__asm__ -( - -".global lone_start" "\n" // place lone_start in the symbol table -"lone_start:" "\n" // program entry point - - // compute argc, argv, envp and auxv - -"pop %rdi" "\n" // argc: rdi = pop -"mov %rsp, %rsi" "\n" // argv: rsi = sp -"lea 8(%rsi, %rdi, 8), %rdx" "\n" // envp: rdx = rsi + (rdi * 8) + 8 - -"lea 0(%rdx), %rcx" "\n" // rcx = rdx -"0:" "\n" // loop: -"add $8, %rcx" "\n" // rcx = rcx + 9 -"cmpq $0, -8(%rcx)" "\n" // *(rcx - 8) == 0 ? -"jnz 0b" "\n" // loop if not zero - // rcx - 8 == 0 - // auxv: rcx - - // x86_64 SysV ABI requirements: -"xor %rbp, %rbp" "\n" // zero the deepest stack frame -"and $-16, %rsp" "\n" // ensure 16 byte stack alignment - -"call lone" "\n" // call lone -"mov %rax, %rdi" "\n" // status code returned in rax - -#define S2(s) #s -#define S(s) S2(s) - -"mov $" S(__NR_exit) ", %rax" "\n" // ensure clean process termination -"syscall" "\n" // exit with returned status code - -#undef S2 -#undef S - -); diff --git a/include/lone/definitions.h b/include/lone/definitions.h new file mode 100644 index 00000000..fd9ac1b4 --- /dev/null +++ b/include/lone/definitions.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_DEFINITIONS_HEADER +#define LONE_DEFINITIONS_HEADER + +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ bits = 32 | bits = 64 │ + │ digits = ceil(bits * log10(2)) = 10 | 20 │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +#if __BITS_PER_LONG == 64 + #define DECIMAL_DIGITS_PER_LONG 20 +#elif __BITS_PER_LONG == 32 + #define DECIMAL_DIGITS_PER_LONG 10 +#else + #error "Unsupported architecture" +#endif + +#ifndef LONE_BUFFER_SIZE + #define LONE_BUFFER_SIZE 4096 +#endif + +#ifndef LONE_MEMORY_SIZE + #define LONE_MEMORY_SIZE (1024 * 1024) +#endif + +#ifndef LONE_ALIGNMENT + #define LONE_ALIGNMENT 16 +#endif + +#define LONE_PRIMITIVE(name) \ +struct lone_value *lone_primitive_ ## name \ +( \ + struct lone_lisp *lone, \ + struct lone_value *module, \ + struct lone_value *environment, \ + struct lone_value *arguments, \ + struct lone_value *closure \ +) + +#endif /* LONE_DEFINITIONS_HEADER */ diff --git a/include/lone/hash.h b/include/lone/hash.h new file mode 100644 index 00000000..0a56a8ad --- /dev/null +++ b/include/lone/hash.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_HASH_HEADER +#define LONE_HASH_HEADER + +#include +#include + +void lone_hash_initialize(struct lone_lisp *lone, struct lone_bytes random); +size_t lone_hash(struct lone_lisp *lone, struct lone_value *value); + +#endif /* LONE_HASH_HEADER */ diff --git a/include/lone/hash/fnv_1a.h b/include/lone/hash/fnv_1a.h new file mode 100644 index 00000000..00e65ee4 --- /dev/null +++ b/include/lone/hash/fnv_1a.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_HASH_FNV_1A_HEADER +#define LONE_HASH_FNV_1A_HEADER + +#include + +#include +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ https://en.wikipedia.org/wiki/FNV_hash │ + │ https://datatracker.ietf.org/doc/draft-eastlake-fnv/ │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +#if __BITS_PER_LONG == 64 + #define FNV_PRIME 0x00000100000001B3UL + #define FNV_OFFSET_BASIS 0xCBF29CE484222325UL +#elif __BITS_PER_LONG == 32 + #define FNV_PRIME 0x01000193UL + #define FNV_OFFSET_BASIS 0x811C9DC5 +#else + #error "Unsupported architecture" +#endif + +void lone_hash_fnv_1a_initialize(struct lone_lisp *lone, struct lone_bytes random); + +unsigned long +__attribute__((pure)) +lone_hash_fnv_1a(struct lone_bytes data, unsigned long offset_basis); + +#endif /* LONE_HASH_FNV_1A_HEADER */ diff --git a/include/lone/linux.h b/include/lone/linux.h new file mode 100644 index 00000000..f6c0f85a --- /dev/null +++ b/include/lone/linux.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_LINUX_HEADER +#define LONE_LINUX_HEADER + +#include +#include +#include + +long linux_system_call_0(long n); +long linux_system_call_1(long n, long _1); +long linux_system_call_2(long n, long _1, long _2); +long linux_system_call_3(long n, long _1, long _2, long _3); +long linux_system_call_4(long n, long _1, long _2, long _3, long _4); +long linux_system_call_5(long n, long _1, long _2, long _3, long _4, long _5); +long linux_system_call_6(long n, long _1, long _2, long _3, long _4, long _5, long _6); + +void +__attribute__((noreturn)) +linux_exit(int code); + +long +__attribute__((tainted_args)) +linux_openat(int dirfd, unsigned char *path, int flags); + +long +__attribute__((tainted_args)) +linux_close(int fd); + +ssize_t +__attribute__((fd_arg_read(1), tainted_args)) +linux_read(int fd, const void *buffer, size_t count); + +ssize_t +__attribute__((fd_arg_write(1), tainted_args)) +linux_write(int fd, const void *buffer, size_t count); + +#endif /* LONE_LINUX_HEADER */ diff --git a/include/lone/lisp.h b/include/lone/lisp.h new file mode 100644 index 00000000..f4681d1e --- /dev/null +++ b/include/lone/lisp.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_LISP_HEADER +#define LONE_LISP_HEADER + +#include + +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ The lone lisp structure represents the lone lisp interpreter. │ + │ A pointer to this structure is passed to nearly every function. │ + │ It must be initialized before everything else since the memory │ + │ allocation system is not functional without it. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +void lone_lisp_initialize(struct lone_lisp *lone, struct lone_bytes memory, size_t heap_size, void *stack, struct lone_bytes random); + +#endif /* LONE_LISP_HEADER */ diff --git a/include/lone/lisp/constants.h b/include/lone/lisp/constants.h new file mode 100644 index 00000000..ea115293 --- /dev/null +++ b/include/lone/lisp/constants.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_LISP_CONSTANTS_HEADER +#define LONE_LISP_CONSTANTS_HEADER + +#include + +struct lone_value *lone_nil(struct lone_lisp *lone); +struct lone_value *lone_true(struct lone_lisp *lone); + +#endif /* LONE_LISP_CONSTANTS_HEADER */ diff --git a/include/lone/lisp/evaluator.h b/include/lone/lisp/evaluator.h new file mode 100644 index 00000000..63577e76 --- /dev/null +++ b/include/lone/lisp/evaluator.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_LISP_EVALUATOR_HEADER +#define LONE_LISP_EVALUATOR_HEADER + +#include + +/* ╭────────────────────────┨ LONE LISP EVALUATOR ┠─────────────────────────╮ + │ │ + │ The heart of the language. This is what actually executes code. │ + │ │ + │ Evaluator features: │ + │ │ + │ ◦ Symbol resolution │ + │ ◦ Application of arguments │ + │ ◦ Functions │ + │ ◦ Evaluated and unevaluated arguments │ + │ ◦ Evaluated and unevaluated result │ + │ ◦ Variadic │ + │ ◦ Primitives │ + │ ◦ Evaluated and unevaluated arguments │ + │ ◦ Evaluated and unevaluated result │ + │ ◦ Vectors │ + │ ◦ Indexing │ + │ ◦ Assignment │ + │ ◦ Tables │ + │ ◦ Lookup │ + │ ◦ Assignment │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_evaluate( + struct lone_lisp *lone, + struct lone_value *module, + struct lone_value *environment, + struct lone_value *value +); + +struct lone_value *lone_evaluate_all( + struct lone_lisp *lone, + struct lone_value *module, + struct lone_value *environment, + struct lone_value *list +); + +struct lone_value *lone_evaluate_module( + struct lone_lisp *lone, + struct lone_value *module, + struct lone_value *value +); + +struct lone_value *lone_apply( + struct lone_lisp *lone, + struct lone_value *module, + struct lone_value *environment, + struct lone_value *applicable, + struct lone_value *arguments +); + +#endif /* LONE_LISP_EVALUATOR_HEADER */ diff --git a/include/lone/lisp/printer.h b/include/lone/lisp/printer.h new file mode 100644 index 00000000..ddeabca8 --- /dev/null +++ b/include/lone/lisp/printer.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_LISP_PRINTER_HEADER +#define LONE_LISP_PRINTER_HEADER + +#include + +/* ╭─────────────────────────┨ LONE LISP PRINTER ┠──────────────────────────╮ + │ │ + │ Transforms lone lisp objects into text and writes them out. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +void lone_print(struct lone_lisp *lone, struct lone_value *value, int file_descriptor); + +#endif /* LONE_LISP_PRINTER_HEADER */ diff --git a/include/lone/lisp/reader.h b/include/lone/lisp/reader.h new file mode 100644 index 00000000..01dfed28 --- /dev/null +++ b/include/lone/lisp/reader.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_LISP_READER_HEADER +#define LONE_LISP_READER_HEADER + +#include + +/* ╭─────────────────────────┨ LONE LISP READER ┠───────────────────────────╮ + │ │ + │ The reader's job is to transform input into lone lisp values. │ + │ It accomplishes the task by reading input from a given file │ + │ descriptor and then lexing and parsing the results. │ + │ │ + │ The lexer or tokenizer transforms a linear stream of characters │ + │ into a linear stream of tokens suitable for parser consumption. │ + │ This gets rid of insignificant whitespace and reduces the size │ + │ of the parser's input significantly. │ + │ │ + │ It consists of an input buffer, its current position in it │ + │ as well as two functions: │ + │ │ + │ ◦ peek(k) which returns the character at i+k │ + │ ◦ consume(k) which advances i by k positions │ + │ │ + │ The parser transforms a linear sequence of tokens into a nested │ + │ sequence of lisp objects suitable for evaluation. │ + │ Its main task is to match nested structures such as lists. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +void lone_reader_initialize(struct lone_lisp *lone, struct lone_reader *reader, size_t buffer_size, int file_descriptor); +void lone_reader_finalize(struct lone_lisp *lone, struct lone_reader *reader); +struct lone_value *lone_read(struct lone_lisp *lone, struct lone_reader *reader); + +#endif /* LONE_LISP_READER_HEADER */ diff --git a/include/lone/memory.h b/include/lone/memory.h new file mode 100644 index 00000000..1ce98994 --- /dev/null +++ b/include/lone/memory.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_MEMORY_HEADER +#define LONE_MEMORY_HEADER + +#include + +#include + +void lone_memory_initialize( + struct lone_lisp *lone, + struct lone_bytes memory, + size_t heap_size, + void *stack +); + +#endif /* LONE_MEMORY_HEADER */ diff --git a/include/lone/memory/allocator.h b/include/lone/memory/allocator.h new file mode 100644 index 00000000..d8312b6b --- /dev/null +++ b/include/lone/memory/allocator.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_MEMORY_ALLOCATOR_HEADER +#define LONE_MEMORY_ALLOCATOR_HEADER + +#include +#include + +size_t +__attribute__((const)) +lone_align(size_t size, size_t alignment); + +void * +__attribute__((malloc, alloc_size(2), alloc_align(3))) +lone_allocate_aligned(struct lone_lisp *lone, size_t requested_size, size_t alignment); + +void * +__attribute__((malloc, alloc_size(2), assume_aligned(LONE_ALIGNMENT))) +lone_allocate(struct lone_lisp *lone, size_t requested_size); + +void * +__attribute__((alloc_size(3))) +lone_reallocate(struct lone_lisp *lone, void *pointer, size_t size); + +void lone_deallocate(struct lone_lisp *lone, void *pointer); + +#endif /* LONE_MEMORY_ALLOCATOR_HEADER */ diff --git a/include/lone/memory/functions.h b/include/lone/memory/functions.h new file mode 100644 index 00000000..5a990b96 --- /dev/null +++ b/include/lone/memory/functions.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_MEMORY_FUNCTIONS_HEADER +#define LONE_MEMORY_FUNCTIONS_HEADER + +#include + +void lone_memory_move(void *from, void *to, size_t count); +void lone_memory_set(void *to, unsigned char byte, size_t count); +void lone_memory_zero(void *to, size_t count); +size_t lone_c_string_length(char *c_string); + +#endif /* LONE_MEMORY_FUNCTIONS_HEADER */ diff --git a/include/lone/memory/garbage_collector.h b/include/lone/memory/garbage_collector.h new file mode 100644 index 00000000..ded7beb1 --- /dev/null +++ b/include/lone/memory/garbage_collector.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_MEMORY_GARBAGE_COLLECTOR_HEADER +#define LONE_MEMORY_GARBAGE_COLLECTOR_HEADER + +#include + +void lone_garbage_collector(struct lone_lisp *lone); + +#endif /* LONE_MEMORY_GARBAGE_COLLECTOR_HEADER */ diff --git a/include/lone/memory/heap.h b/include/lone/memory/heap.h new file mode 100644 index 00000000..05931dad --- /dev/null +++ b/include/lone/memory/heap.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include + +#ifndef LONE_MEMORY_HEAP_HEADER +#define LONE_MEMORY_HEAP_HEADER + +void lone_heap_initialize(struct lone_lisp *lone, size_t heap_size); +struct lone_value *lone_heap_allocate_value(struct lone_lisp *lone); +void lone_deallocate_dead_heaps(struct lone_lisp *lone); + +#endif /* LONE_MEMORY_HEAP_HEADER */ diff --git a/include/lone/modules.h b/include/lone/modules.h new file mode 100644 index 00000000..f9e86f22 --- /dev/null +++ b/include/lone/modules.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_MODULES_HEADER +#define LONE_MODULES_HEADER + +#include + +#include + +/* ╭───────────────────────────┨ LONE / MODULES ┠───────────────────────────╮ + │ │ + │ Module importing, exporting and loading operations. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_module_null(struct lone_lisp *lone); +struct lone_value *lone_module_for_name(struct lone_lisp *lone, struct lone_value *name); +struct lone_value *lone_module_load(struct lone_lisp *lone, struct lone_value *name); +void lone_module_load_null_from_file_descriptor(struct lone_lisp *lone, int file_descriptor); +void lone_module_load_null_from_standard_input(struct lone_lisp *lone); +void lone_module_path_push(struct lone_lisp *lone, struct lone_value *directory); +void lone_module_path_push_c_string(struct lone_lisp *lone, char *directory); +void lone_module_path_push_va_list(struct lone_lisp *lone, size_t count, va_list directories); +void lone_module_path_push_all(struct lone_lisp *lone, size_t count, ...); + +void lone_export( + struct lone_lisp *lone, + struct lone_value *module, + struct lone_value *symbol +); + +void lone_set_and_export( + struct lone_lisp *lone, + struct lone_value *module, + struct lone_value *symbol, + struct lone_value *value +); + +struct lone_value *lone_primitive_export( + struct lone_lisp *lone, + struct lone_value *module, + struct lone_value *environment, + struct lone_value *arguments, + struct lone_value *closure +); + +struct lone_value *lone_primitive_import( + struct lone_lisp *lone, + struct lone_value *module, + struct lone_value *environment, + struct lone_value *arguments, + struct lone_value *closure +); + +#endif /* LONE_MODULES_HEADER */ diff --git a/include/lone/modules/intrinsic.h b/include/lone/modules/intrinsic.h new file mode 100644 index 00000000..4e6feebf --- /dev/null +++ b/include/lone/modules/intrinsic.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_MODULES_INTRINSIC_HEADER +#define LONE_MODULES_INTRINSIC_HEADER + +#include + +/* ╭─────────────────────┨ LONE / MODULES / INTRINSIC ┠─────────────────────╮ + │ │ + │ Initialization for built-in modules with essential functionality. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +void lone_modules_intrinsic_initialize( + struct lone_lisp *lone, + int argument_count, + char **argument_vector, + char **environment, + struct auxiliary *auxiliary_vector +); + +#endif /* LONE_MODULES_INTRINSIC_HEADER */ diff --git a/include/lone/modules/linux.h b/include/lone/modules/linux.h new file mode 100644 index 00000000..f2fc55a3 --- /dev/null +++ b/include/lone/modules/linux.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_MODULES_LINUX_HEADER +#define LONE_MODULES_LINUX_HEADER + +#include +#include + +/* ╭────────────────────────┨ LONE / MODULE / LINUX ┠───────────────────────╮ + │ │ + │ Linux system calls and process parameters. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +void lone_module_linux_initialize(struct lone_lisp *lone, int argc, char **argv, char **envp, struct auxiliary *auxv); + +LONE_PRIMITIVE(linux_system_call); + +#endif /* LONE_MODULES_LINUX_HEADER */ diff --git a/include/lone/modules/list.h b/include/lone/modules/list.h new file mode 100644 index 00000000..aa1000be --- /dev/null +++ b/include/lone/modules/list.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_MODULES_LIST_HEADER +#define LONE_MODULES_LIST_HEADER + +#include +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ List operations. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +void lone_module_list_initialize(struct lone_lisp *lone); + +LONE_PRIMITIVE(list_construct); +LONE_PRIMITIVE(list_first); +LONE_PRIMITIVE(list_rest); +LONE_PRIMITIVE(list_map); +LONE_PRIMITIVE(list_reduce); +LONE_PRIMITIVE(list_flatten); + +#endif /* LONE_MODULES_LIST_HEADER */ diff --git a/include/lone/modules/lone.h b/include/lone/modules/lone.h new file mode 100644 index 00000000..9898c299 --- /dev/null +++ b/include/lone/modules/lone.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_MODULES_LONE_HEADER +#define LONE_MODULES_LONE_HEADER + +#include +#include + +/* ╭───────────────────┨ LONE LISP PRIMITIVE FUNCTIONS ┠────────────────────╮ + │ │ + │ Lone lisp functions implemented in C. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +void lone_module_lone_initialize(struct lone_lisp *lone); + +LONE_PRIMITIVE(lone_begin); +LONE_PRIMITIVE(lone_when); +LONE_PRIMITIVE(lone_unless); +LONE_PRIMITIVE(lone_if); +LONE_PRIMITIVE(lone_let); +LONE_PRIMITIVE(lone_set); +LONE_PRIMITIVE(lone_quote); +LONE_PRIMITIVE(lone_quasiquote); +LONE_PRIMITIVE(lone_lambda); +LONE_PRIMITIVE(lone_lambda_bang); +LONE_PRIMITIVE(lone_lambda_star); +LONE_PRIMITIVE(lone_is_list); +LONE_PRIMITIVE(lone_is_vector); +LONE_PRIMITIVE(lone_is_table); +LONE_PRIMITIVE(lone_is_symbol); +LONE_PRIMITIVE(lone_is_text); +LONE_PRIMITIVE(lone_is_integer); +LONE_PRIMITIVE(lone_is_identical); +LONE_PRIMITIVE(lone_is_equivalent); +LONE_PRIMITIVE(lone_is_equal); +LONE_PRIMITIVE(lone_print); + +#endif /* LONE_MODULES_LONE_HEADER */ diff --git a/include/lone/modules/math.h b/include/lone/modules/math.h new file mode 100644 index 00000000..e9fb6ac5 --- /dev/null +++ b/include/lone/modules/math.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_MODULES_MATH_HEADER +#define LONE_MODULES_MATH_HEADER + +#include +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Built-in mathematical and numeric operations. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +void lone_module_math_initialize(struct lone_lisp *lone); + +LONE_PRIMITIVE(math_add); +LONE_PRIMITIVE(math_subtract); +LONE_PRIMITIVE(math_multiply); +LONE_PRIMITIVE(math_divide); +LONE_PRIMITIVE(math_is_less_than); +LONE_PRIMITIVE(math_is_less_than_or_equal_to); +LONE_PRIMITIVE(math_is_greater_than); +LONE_PRIMITIVE(math_is_greater_than_or_equal_to); +LONE_PRIMITIVE(math_sign); +LONE_PRIMITIVE(math_is_zero); +LONE_PRIMITIVE(math_is_positive); +LONE_PRIMITIVE(math_is_negative); + +#endif /* LONE_MODULES_MATH_HEADER */ diff --git a/include/lone/modules/text.h b/include/lone/modules/text.h new file mode 100644 index 00000000..3c54ac01 --- /dev/null +++ b/include/lone/modules/text.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_MODULES_TEXT_HEADER +#define LONE_MODULES_TEXT_HEADER + +#include +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Text operations. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +void lone_module_text_initialize(struct lone_lisp *lone); + +LONE_PRIMITIVE(text_join); +LONE_PRIMITIVE(text_concatenate); + +#endif /* LONE_MODULES_TEXT_HEADER */ diff --git a/include/lone/struct/auxiliary.h b/include/lone/struct/auxiliary.h new file mode 100644 index 00000000..917ae9df --- /dev/null +++ b/include/lone/struct/auxiliary.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_AUXILIARY_HEADER +#define LONE_STRUCT_AUXILIARY_HEADER + +#include + +#include + +struct auxiliary { + long type; + union { + char *c_string; + void *pointer; + long integer; + } as; +}; + +#endif /* LONE_STRUCT_AUXILIARY_HEADER */ diff --git a/include/lone/struct/bytes.h b/include/lone/struct/bytes.h new file mode 100644 index 00000000..e855d25e --- /dev/null +++ b/include/lone/struct/bytes.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_BYTES_HEADER +#define LONE_STRUCT_BYTES_HEADER + +#include + +struct lone_bytes { + size_t count; + unsigned char *pointer; +}; + +#endif /* LONE_STRUCT_BYTES_HEADER */ diff --git a/include/lone/struct/function.h b/include/lone/struct/function.h new file mode 100644 index 00000000..d1a8f7d9 --- /dev/null +++ b/include/lone/struct/function.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_FUNCTION_HEADER +#define LONE_STRUCT_FUNCTION_HEADER + +#include + +/* https://dl.acm.org/doi/10.1145/947941.947948 + * https://user.ceng.metu.edu.tr/~ucoluk/research/lisp/lispman/node24.html + */ +struct lone_function_flags { + bool evaluate_arguments: 1; + bool evaluate_result: 1; + bool variable_arguments: 1; +}; + +struct lone_function { + struct lone_value *arguments; /* the bindings */ + struct lone_value *code; /* the lambda */ + struct lone_value *environment; /* the closure */ + struct lone_function_flags flags; /* how to evaluate & apply */ +}; + +#endif /* LONE_STRUCT_FUNCTION_HEADER */ diff --git a/include/lone/struct/heap.h b/include/lone/struct/heap.h new file mode 100644 index 00000000..e229fd90 --- /dev/null +++ b/include/lone/struct/heap.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_HEAP_HEADER +#define LONE_STRUCT_HEAP_HEADER + +#include +#include + +struct lone_heap { + struct lone_heap *next; + size_t count; + struct lone_value values[]; +}; + +#endif /* LONE_STRUCT_HEAP_HEADER */ diff --git a/include/lone/struct/lisp.h b/include/lone/struct/lisp.h new file mode 100644 index 00000000..d0581f94 --- /dev/null +++ b/include/lone/struct/lisp.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_LISP_HEADER +#define LONE_STRUCT_LISP_HEADER + +#include + +/* ╭───────────────────────┨ LONE LISP INTERPRETER ┠────────────────────────╮ + │ │ + │ The lone lisp interpreter is composed of all internal state │ + │ necessary to process useful programs. It includes memory, │ + │ references to all allocated objects, a table of interned │ + │ symbols, references to constant values such as nil and │ + │ a table of loaded modules and the top level null module. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_memory; +struct lone_heap; + +struct lone_lisp { + struct { + void *stack; + struct lone_memory *general; + struct lone_heap *heaps; + } memory; + struct lone_value *symbol_table; + struct { + struct lone_value *nil; + struct lone_value *truth; + } constants; + struct { + struct lone_value *loaded; + struct lone_value *null; + struct lone_value *top_level_environment; + struct lone_value *path; + } modules; + struct { + struct { + unsigned long offset_basis; + } fnv_1a; + } hash; +}; + +#endif /* LONE_STRUCT_LISP_HEADER */ diff --git a/include/lone/struct/list.h b/include/lone/struct/list.h new file mode 100644 index 00000000..24814946 --- /dev/null +++ b/include/lone/struct/list.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_LIST_HEADER +#define LONE_STRUCT_LIST_HEADER + +#include + +struct lone_list { + struct lone_value *first; + struct lone_value *rest; +}; + +#endif /* LONE_STRUCT_LIST_HEADER */ diff --git a/include/lone/struct/memory.h b/include/lone/struct/memory.h new file mode 100644 index 00000000..4e6ac949 --- /dev/null +++ b/include/lone/struct/memory.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_MEMORY_HEADER +#define LONE_STRUCT_MEMORY_HEADER + +#include + +/* ╭────────────────────┨ LONE LISP MEMORY ALLOCATION ┠─────────────────────╮ + │ │ + │ Lone is designed to work without any dependencies except Linux, │ + │ so it does not make use of even the system's C library. │ + │ In order to bootstrap itself in such harsh conditions, │ + │ it must be given some memory to work with. │ + │ │ + │ Lone manages its own memory with a block-based allocator. │ + │ Memory blocks are allocated on a first fit basis. │ + │ They will be split into smaller units when allocated │ + │ and merged together with free neighbors when deallocated. │ + │ │ + │ Memory blocks are segments prefixed by a block descriptor │ + │ that tracks its size, allocation status as well as pointers │ + │ to surrounding memory blocks. │ + │ │ + │ Lone employs a very simple mark-and-sweep garbage collector. │ + │ It starts by marking all values reachable by the interpreter │ + │ in its current state. Then it walks the list of all values, │ + │ deallocating any unmarked object it finds as well as any │ + │ memory they happen own based on the value's type. │ + │ │ + │ Like memory blocks, lone values are prefixed by a header │ + │ structure containing metadata such as its marked state │ + │ as well as a pointer to the next object in the list. │ + │ │ + │ Since these headers are prefixed to pointers returned by │ + │ allocation and value creation functions, it is simple to │ + │ calculate their locations given a pointer to a memory block │ + │ or lone value: simply subtract the header's size from it. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +struct lone_memory { + struct lone_memory *prev, *next; + int free; + size_t size; + unsigned char pointer[]; +}; + +#endif /* LONE_STRUCT_MEMORY_HEADER */ diff --git a/include/lone/struct/module.h b/include/lone/struct/module.h new file mode 100644 index 00000000..6e56b02f --- /dev/null +++ b/include/lone/struct/module.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_MODULE_HEADER +#define LONE_STRUCT_MODULE_HEADER + +#include + +struct lone_module { + struct lone_value *name; + struct lone_value *environment; + struct lone_value *exports; +}; + +#endif /* LONE_STRUCT_MODULE_HEADER */ diff --git a/include/lone/struct/pointer.h b/include/lone/struct/pointer.h new file mode 100644 index 00000000..789b1a9f --- /dev/null +++ b/include/lone/struct/pointer.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_POINTER_HEADER +#define LONE_STRUCT_POINTER_HEADER + +#include + +struct lone_pointer { + enum lone_pointer_type type; + void *address; +}; + +#endif /* LONE_STRUCT_POINTER_HEADER */ diff --git a/include/lone/struct/primitive.h b/include/lone/struct/primitive.h new file mode 100644 index 00000000..d6164f57 --- /dev/null +++ b/include/lone/struct/primitive.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_PRIMITIVE_HEADER +#define LONE_STRUCT_PRIMITIVE_HEADER + +#include + +struct lone_primitive { + struct lone_value *name; + lone_primitive function; + struct lone_value *closure; + struct lone_function_flags flags; /* primitives always accept variable arguments */ +}; + +#endif /* LONE_STRUCT_PRIMITIVE_HEADER */ diff --git a/include/lone/struct/reader.h b/include/lone/struct/reader.h new file mode 100644 index 00000000..20185b8b --- /dev/null +++ b/include/lone/struct/reader.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_READER_HEADER +#define LONE_STRUCT_READER_HEADER + +#include + +struct lone_reader { + int file_descriptor; + struct { + struct lone_bytes bytes; + struct { + size_t read; + size_t write; + } position; + } buffer; + int error; +}; + +#endif /* LONE_STRUCT_READER_HEADER */ diff --git a/include/lone/struct/table.h b/include/lone/struct/table.h new file mode 100644 index 00000000..b835d925 --- /dev/null +++ b/include/lone/struct/table.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_TABLE_HEADER +#define LONE_STRUCT_TABLE_HEADER + +#include + +struct lone_table_entry { + struct lone_value *key; + struct lone_value *value; +}; + +struct lone_table { + size_t count; + size_t capacity; + struct lone_table_entry *entries; + struct lone_value *prototype; +}; + +#endif /* LONE_STRUCT_TABLE_HEADER */ diff --git a/include/lone/struct/value.h b/include/lone/struct/value.h new file mode 100644 index 00000000..53fc405d --- /dev/null +++ b/include/lone/struct/value.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_VALUE_HEADER +#define LONE_STRUCT_VALUE_HEADER + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct lone_value { + struct { + bool live: 1; + bool marked: 1; + bool should_deallocate_bytes: 1; + }; + + enum lone_type type; + + union { + struct lone_module module; + struct lone_function function; + struct lone_primitive primitive; + struct lone_list list; + struct lone_vector vector; + struct lone_table table; + struct lone_bytes bytes; /* also used by texts and symbols */ + struct lone_pointer pointer; + long integer; + }; +}; + +#endif /* LONE_STRUCT_VALUE_HEADER */ diff --git a/include/lone/struct/vector.h b/include/lone/struct/vector.h new file mode 100644 index 00000000..d22a2390 --- /dev/null +++ b/include/lone/struct/vector.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCT_VECTOR_HEADER +#define LONE_STRUCT_VECTOR_HEADER + +#include + +struct lone_vector { + struct lone_value **values; + size_t count; + size_t capacity; +}; + +#endif /* LONE_STRUCT_VECTOR_HEADER */ diff --git a/include/lone/structures.h b/include/lone/structures.h new file mode 100644 index 00000000..570f4c37 --- /dev/null +++ b/include/lone/structures.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_STRUCTURES_HEADER +#define LONE_STRUCTURES_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* LONE_STRUCTURES_HEADER */ diff --git a/include/lone/types.h b/include/lone/types.h new file mode 100644 index 00000000..f290902f --- /dev/null +++ b/include/lone/types.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_TYPES_HEADER +#define LONE_TYPES_HEADER + +#include + +#include + +typedef __kernel_size_t size_t; +typedef __kernel_ssize_t ssize_t; + +/* ╭──────────────────────────┨ LONE LISP TYPES ┠───────────────────────────╮ + │ │ + │ Lone implements dynamic data types as a tagged union. │ + │ Supported types are: │ + │ │ + │ ◦ Module the isolated programming environment type │ + │ ◦ Function the reusable executable expressions type │ + │ ◦ Primitive the built-in C subroutine type │ + │ ◦ List the linked list and tree type │ + │ ◦ Vector the contiguous array of values type │ + │ ◦ Table the hash table, prototype and object type │ + │ ◦ Symbol the keyword and interned string type │ + │ ◦ Text the UTF-8 encoded text type │ + │ ◦ Bytes the binary data and low level string type │ + │ ◦ Integer the signed integer type │ + │ ◦ Pointer the memory addressing and dereferencing type │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +enum lone_type { + LONE_MODULE, + LONE_FUNCTION, + LONE_PRIMITIVE, + LONE_LIST, + LONE_VECTOR, + LONE_TABLE, + LONE_SYMBOL, + LONE_TEXT, + LONE_BYTES, + LONE_INTEGER, + LONE_POINTER, +}; + +enum lone_pointer_type { + LONE_TO_UNKNOWN, + + LONE_TO_U8, LONE_TO_I8, + LONE_TO_U16, LONE_TO_I16, + LONE_TO_U32, LONE_TO_I32, + LONE_TO_U64, LONE_TO_I64, +}; + +struct lone_value; +struct lone_module; +struct lone_function; +struct lone_function_flags; +struct lone_list; +struct lone_vector; +struct lone_table; +struct lone_text; +struct lone_bytes; +struct lone_pointer; + +struct lone_lisp; +struct lone_memory; +struct lone_reader; + +struct auxiliary; + +typedef bool (*lone_predicate)(struct lone_value *); +typedef bool (*lone_comparator)(struct lone_value *, struct lone_value *); + +typedef struct lone_value *(*lone_primitive)(struct lone_lisp *lone, + struct lone_value *module, + struct lone_value *environment, + struct lone_value *arguments, + struct lone_value *closure); + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Type predicate functions. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +bool lone_has_same_type(struct lone_value *x, struct lone_value *y); +bool lone_is_module(struct lone_value *value); +bool lone_is_function(struct lone_value *value); +bool lone_is_primitive(struct lone_value *value); +bool lone_is_applicable(struct lone_value *value); +bool lone_is_list(struct lone_value *value); +bool lone_is_vector(struct lone_value *value); +bool lone_is_table(struct lone_value *value); +bool lone_is_nil(struct lone_value *value); +bool lone_has_bytes(struct lone_value *value); +bool lone_is_bytes(struct lone_value *value); +bool lone_is_text(struct lone_value *value); +bool lone_is_symbol(struct lone_value *value); +bool lone_is_integer(struct lone_value *value); +bool lone_is_pointer(struct lone_value *value); + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Comparison and equality functions. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +bool lone_is_identical(struct lone_value *x, struct lone_value *y); +bool lone_is_equivalent(struct lone_value *x, struct lone_value *y); +bool lone_is_equal(struct lone_value *x, struct lone_value *y); +bool lone_list_is_equal(struct lone_value *x, struct lone_value *y); +bool lone_vector_is_equal(struct lone_value *x, struct lone_value *y); +bool lone_table_is_equal(struct lone_value *x, struct lone_value *y); +bool lone_integer_is_less_than(struct lone_value *x, struct lone_value *y); +bool lone_integer_is_less_than_or_equal_to(struct lone_value *x, struct lone_value *y); +bool lone_integer_is_greater_than(struct lone_value *x, struct lone_value *y); +bool lone_integer_is_greater_than_or_equal_to(struct lone_value *x, struct lone_value *y); +bool lone_bytes_equals(struct lone_bytes x, struct lone_bytes y); +bool lone_bytes_equals_c_string(struct lone_bytes bytes, char *c_string); + +#endif /* LONE_TYPES_HEADER */ diff --git a/include/lone/utilities.h b/include/lone/utilities.h new file mode 100644 index 00000000..f0c3105f --- /dev/null +++ b/include/lone/utilities.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_UTILITIES_HEADER +#define LONE_UTILITIES_HEADER + +#include + +#include + +struct lone_value *lone_apply_predicate(struct lone_lisp *lone, struct lone_value *arguments, lone_predicate function); +struct lone_value *lone_apply_comparator(struct lone_lisp *lone, struct lone_value *arguments, lone_comparator function); +struct lone_bytes lone_join(struct lone_lisp *lone, struct lone_value *separator, struct lone_value *arguments, lone_predicate is_valid); +struct lone_bytes lone_concatenate(struct lone_lisp *lone, struct lone_value *arguments, lone_predicate is_valid); +struct lone_bytes lone_get_auxiliary_random(struct auxiliary *value); + +#endif /* LONE_UTILITIES_HEADER */ + diff --git a/include/lone/value.h b/include/lone/value.h new file mode 100644 index 00000000..b2a698d5 --- /dev/null +++ b/include/lone/value.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_VALUE_HEADER +#define LONE_VALUE_HEADER + +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Constructor for a general lone value. │ + │ Completely uninitialized. │ + │ Meant for other constructors to use. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +struct lone_value *lone_value_create(struct lone_lisp *lone); + +#endif /* LONE_VALUE_HEADER */ diff --git a/include/lone/value/bytes.h b/include/lone/value/bytes.h new file mode 100644 index 00000000..d177ccd0 --- /dev/null +++ b/include/lone/value/bytes.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_VALUE_BYTES_HEADER +#define LONE_VALUE_BYTES_HEADER + +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Lone bytes values are initialized with a pointer to a memory │ + │ block of known size. It can take ownership of the block │ + │ through the transfer functions or it can make a copy of it │ + │ via the create functions. │ + │ │ + │ Transferring memory blocks allows control over deallocation. │ + │ Disabling deallocation on garbage collection allows pointing to │ + │ data such as statically allocated buffers and C string literals. │ + │ │ + │ Copies will automatically include a hidden trailing null │ + │ byte to ease compatibility with code expecting C strings. │ + │ It's impossible to escape from them since system calls use them. │ + │ Transferred buffers should also contain that null byte │ + │ but the lone bytes type currently has no way to enforce this. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_bytes_transfer(struct lone_lisp *lone, unsigned char *pointer, size_t count, bool should_deallocate); +struct lone_value *lone_bytes_transfer_bytes(struct lone_lisp *lone, struct lone_bytes bytes, bool should_deallocate); +struct lone_value *lone_bytes_create(struct lone_lisp *lone, unsigned char *pointer, size_t count); + +#endif /* LONE_VALUE_BYTES_HEADER */ diff --git a/include/lone/value/function.h b/include/lone/value/function.h new file mode 100644 index 00000000..9eef23ac --- /dev/null +++ b/include/lone/value/function.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_VALUE_FUNCTION_HEADER +#define LONE_VALUE_FUNCTION_HEADER + +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Lone functions represent a body of executable lone lisp code. │ + │ They have a list of argument names to be bound during function │ + │ application, a list of expressions to be evaluated when called │ + │ and a closure: a reference to the environment it was defined in. │ + │ │ + │ To apply a function is to create a new environment with its │ + │ argument names bound to the given arguments and then evaluate │ + │ the function's expressions in the context of that environment. │ + │ │ + │ The function flags control how the function is applied. │ + │ It may be configured to receive evaluated or unevaluated │ + │ arguments as well as to evaluate the result automatically. │ + │ These features allow code manipulation and generation. │ + │ It may also be configured to be variadic: all arguments │ + │ are collected into a list and passed as a single argument. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_function_create( + struct lone_lisp *lone, + struct lone_value *arguments, + struct lone_value *code, + struct lone_value *environment, + struct lone_function_flags flags +); + +#endif /* LONE_VALUE_FUNCTION_HEADER */ diff --git a/include/lone/value/integer.h b/include/lone/value/integer.h new file mode 100644 index 00000000..cfd45646 --- /dev/null +++ b/include/lone/value/integer.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_VALUE_INTEGER_HEADER +#define LONE_VALUE_INTEGER_HEADER + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Lone integers are currently signed fixed-length integers. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_integer_create(struct lone_lisp *lone, long integer); +struct lone_value *lone_integer_parse(struct lone_lisp *lone, unsigned char *digits, size_t count); + +#endif /* LONE_VALUE_INTEGER_HEADER */ diff --git a/include/lone/value/list.h b/include/lone/value/list.h new file mode 100644 index 00000000..443d0d1c --- /dev/null +++ b/include/lone/value/list.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_VALUE_LIST_HEADER +#define LONE_VALUE_LIST_HEADER + +#include + +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Lone lists are pairs of lone values: first and rest. │ + │ Usually first is the element while rest is another pair. │ + │ This explains their names. Also called car and cdr. │ + │ │ + │ Although often the case, rest need not be another pair. │ + │ Any other object may be set: (1 . 2); first = 1, rest = 2. │ + │ So rest could also be named second. │ + │ │ + │ A list with null first and rest pointers is known as nil. │ + │ It is provided as a constant by the lone interpreter. │ + │ Their presence in the rest of a list marks its end. │ + │ New nil values may be created by C code that builds lists. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_list_create(struct lone_lisp *lone, struct lone_value *first, struct lone_value *rest); +struct lone_value *lone_list_create_nil(struct lone_lisp *lone); +struct lone_value *lone_nil(struct lone_lisp *lone); + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ List manipulation functions. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_list_first(struct lone_value *value); +struct lone_value *lone_list_rest(struct lone_value *value); +struct lone_value *lone_list_set_first(struct lone_value *list, struct lone_value *value); +struct lone_value *lone_list_set_rest(struct lone_value *list, struct lone_value *rest); +struct lone_value *lone_list_append(struct lone_lisp *lone, struct lone_value *list, struct lone_value *value); +struct lone_value *lone_list_build(struct lone_lisp *lone, size_t count, ...); +struct lone_value *lone_list_to_vector(struct lone_lisp *lone, struct lone_value *list); +struct lone_value *lone_list_flatten(struct lone_lisp *lone, struct lone_value *list); +struct lone_bytes lone_join(struct lone_lisp *lone, struct lone_value *separator, struct lone_value *arguments, lone_predicate is_valid); +struct lone_bytes lone_concatenate(struct lone_lisp *lone, struct lone_value *arguments, lone_predicate is_valid); + +#endif /* LONE_VALUE_LIST_HEADER */ diff --git a/include/lone/value/module.h b/include/lone/value/module.h new file mode 100644 index 00000000..76011e35 --- /dev/null +++ b/include/lone/value/module.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_VALUE_MODULE_HEADER +#define LONE_VALUE_MODULE_HEADER + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Lone modules are named isolated environments for evaluation. │ + │ │ + │ The lisp interpreter contains a top level environment. │ + │ This environment contains only the import/export primitives. │ + │ New modules have clean environments which inherit from it. │ + │ This allows complete control over the symbols and the namespace. │ + │ │ + │ Each module corresponds roughly to one .ln file on disk. │ + │ These module files are text files containing lone lisp code │ + │ which may import or export symbols from or to other modules. │ + │ The lone interpreter's import primitive will search for files │ + │ to load in conventional locations, enabling library development. │ + │ │ + │ A special nameless module known as the null module │ + │ contains code read in from standard input. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +struct lone_value *lone_module_create(struct lone_lisp *lone, struct lone_value *name); + +#endif /* LONE_VALUE_MODULE_HEADER */ diff --git a/include/lone/value/pointer.h b/include/lone/value/pointer.h new file mode 100644 index 00000000..b4404616 --- /dev/null +++ b/include/lone/value/pointer.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_VALUE_POINTER_HEADER +#define LONE_VALUE_POINTER_HEADER + +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Lone pointers do not own the data they point to. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_pointer_create(struct lone_lisp *lone, void *pointer, enum lone_pointer_type type); + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Pointer dereferencing functions. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_pointer_dereference(struct lone_lisp *lone, struct lone_value *pointer); + +#endif /* LONE_VALUE_POINTER_HEADER */ diff --git a/include/lone/value/primitive.h b/include/lone/value/primitive.h new file mode 100644 index 00000000..935e41e6 --- /dev/null +++ b/include/lone/value/primitive.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_VALUE_PRIMITIVE_HEADER +#define LONE_VALUE_PRIMITIVE_HEADER + +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Primitives are lone functions implemented in C. │ + │ They are always variadic and must check their arguments. │ + │ All of them must follow the primitive function prototype. │ + │ They also have closures which are pointers to arbitrary data. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_primitive_create( + struct lone_lisp *lone, + char *name, + lone_primitive function, + struct lone_value *closure, + struct lone_function_flags flags +); + +#endif /* LONE_VALUE_PRIMITIVE_HEADER */ diff --git a/include/lone/value/symbol.h b/include/lone/value/symbol.h new file mode 100644 index 00000000..e4915e86 --- /dev/null +++ b/include/lone/value/symbol.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_VALUE_SYMBOL_HEADER +#define LONE_VALUE_SYMBOL_HEADER + +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Lone symbols are like lone texts but are interned in a table. │ + │ Symbol table interning deduplicates them in memory, │ + │ enabling fast identity-based comparisons via pointer equality. │ + │ However, this means they won't be garbage collected. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_symbol_transfer(struct lone_lisp *lone, unsigned char *text, size_t length, bool should_deallocate); +struct lone_value *lone_symbol_transfer_bytes(struct lone_lisp *lone, struct lone_bytes bytes, bool should_deallocate); +struct lone_value *lone_symbol_create(struct lone_lisp *lone, unsigned char *text, size_t length); +struct lone_value *lone_intern(struct lone_lisp *lone, unsigned char *bytes, size_t count, bool should_deallocate); +struct lone_value *lone_intern_c_string(struct lone_lisp *lone, char *c_string); + +#endif /* LONE_VALUE_SYMBOL_HEADER */ diff --git a/include/lone/value/table.h b/include/lone/value/table.h new file mode 100644 index 00000000..7e95b167 --- /dev/null +++ b/include/lone/value/table.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_VALUE_TABLE_HEADER +#define LONE_VALUE_TABLE_HEADER + +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Lone tables are openly addressed, linearly probed hash tables. │ + │ Currently, lone tables use the FNV-1a hashing algorithm. │ + │ They also strive to maintain a load factor of at most 0.5: │ + │ tables will be rehashed once they're above half capacity. │ + │ They do not use tombstones to delete keys. │ + │ │ + │ Tables are able to inherit from another table: missing keys │ + │ are also looked up in the parent table. This is currently used │ + │ to implement nested environments but will also serve as a │ + │ prototype-based object system as in Javascript and Self. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_table_create(struct lone_lisp *lone, size_t capacity, struct lone_value *prototype); + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Hash table functions. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_table_get(struct lone_lisp *lone, struct lone_value *table, struct lone_value *key); +void lone_table_set(struct lone_lisp *lone, struct lone_value *table, struct lone_value *key, struct lone_value *value); +void lone_table_delete(struct lone_lisp *lone, struct lone_value *table, struct lone_value *key); + +#endif /* LONE_VALUE_TABLE_HEADER */ diff --git a/include/lone/value/text.h b/include/lone/value/text.h new file mode 100644 index 00000000..fa9f0cbc --- /dev/null +++ b/include/lone/value/text.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_VALUE_TEXT_HEADER +#define LONE_VALUE_TEXT_HEADER + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Lone texts are lone's strings and represent UTF-8 encoded text. │ + │ Transfer and creation functions work like lone bytes. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_text_transfer(struct lone_lisp *lone, unsigned char *text, size_t length, bool should_deallocate); +struct lone_value *lone_text_transfer_bytes(struct lone_lisp *lone, struct lone_bytes bytes, bool should_deallocate); +struct lone_value *lone_text_create(struct lone_lisp *lone, unsigned char *text, size_t length); +struct lone_value *lone_text_create_from_c_string(struct lone_lisp *lone, char *c_string); + +#endif /* LONE_VALUE_TEXT_HEADER */ diff --git a/include/lone/value/vector.h b/include/lone/value/vector.h new file mode 100644 index 00000000..9fda933e --- /dev/null +++ b/include/lone/value/vector.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#ifndef LONE_VALUE_VECTOR_HEADER +#define LONE_VALUE_VECTOR_HEADER + +#include + +#include + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Lone vectors are simple dynamic arrays of lone values. │ + │ They grow automatically as elements are added. │ + │ Any index may be used regardless of current length: │ + │ all the elements remain unset as the array grows. │ + │ Unset elements are null pointers which are currently │ + │ converted to nil automatically but might one day serve │ + │ as an undefined value like in other languages. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +struct lone_value *lone_vector_create(struct lone_lisp *lone, size_t capacity); + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Functions for vectors, lone's dynamic arrays. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +void lone_vector_resize(struct lone_lisp *lone, struct lone_value *vector, size_t new_capacity); +struct lone_value *lone_vector_get_value_at(struct lone_lisp *lone, struct lone_value *vector, size_t i); +struct lone_value *lone_vector_get(struct lone_lisp *lone, struct lone_value *vector, struct lone_value *index); +void lone_vector_set_value_at(struct lone_lisp *lone, struct lone_value *vector, size_t i, struct lone_value *value); +void lone_vector_set(struct lone_lisp *lone, struct lone_value *vector, struct lone_value *index, struct lone_value *value); +void lone_vector_push(struct lone_lisp *lone, struct lone_value *vector, struct lone_value *value); +void lone_vector_push_va_list(struct lone_lisp *lone, struct lone_value *vector, size_t count, va_list arguments); +void lone_vector_push_all(struct lone_lisp *lone, struct lone_value *vector, size_t count, ...); +struct lone_value *lone_vector_build(struct lone_lisp *lone, size_t count, ...); +bool lone_vector_contains(struct lone_value *vector, struct lone_value *value); + +#endif /* LONE_VALUE_VECTOR_HEADER */ diff --git a/lone.c b/lone.c deleted file mode 100644 index 63f541c3..00000000 --- a/lone.c +++ /dev/null @@ -1,3893 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ - -/* ╭─────────────────────────────┨ LONE LISP ┠──────────────────────────────╮ - │ │ - │ The standalone Linux Lisp │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -#include -#include -#include - -#include -#include -#include -#include -#include - -typedef __kernel_size_t size_t; -typedef __kernel_ssize_t ssize_t; - -#include LONE_ARCH_SOURCE - -static void __attribute__((noreturn)) linux_exit(int code) -{ - system_call_1(__NR_exit, code); - __builtin_unreachable(); -} - -static long __attribute__((tainted_args)) linux_openat(int dirfd, unsigned char *path, int flags) -{ - return system_call_4(__NR_openat, dirfd, (long) path, flags, 0); -} - -static long __attribute__((tainted_args)) linux_close(int fd) -{ - return system_call_1(__NR_close, fd); -} - -static ssize_t __attribute__((fd_arg_read(1), tainted_args)) linux_read(int fd, const void *buffer, size_t count) -{ - return system_call_3(__NR_read, fd, (long) buffer, (long) count); -} - -static ssize_t __attribute__((fd_arg_write(1), tainted_args)) linux_write(int fd, const void *buffer, size_t count) -{ - return system_call_3(__NR_write, fd, (long) buffer, (long) count); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ bits = 32 | bits = 64 │ - │ digits = ceil(bits * log10(2)) = 10 | 20 │ - │ │ - │ https://en.wikipedia.org/wiki/FNV_hash │ - │ https://datatracker.ietf.org/doc/draft-eastlake-fnv/ │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -#if __BITS_PER_LONG == 64 - #define DECIMAL_DIGITS_PER_LONG 20 - #define FNV_PRIME 0x00000100000001B3UL - #define FNV_OFFSET_BASIS 0xCBF29CE484222325UL -#elif __BITS_PER_LONG == 32 - #define DECIMAL_DIGITS_PER_LONG 10 - #define FNV_PRIME 0x01000193UL - #define FNV_OFFSET_BASIS 0x811C9DC5 -#else - #error "Unsupported architecture" -#endif - -#ifndef LONE_BUFFER_SIZE - #define LONE_BUFFER_SIZE 4096 -#endif - -#ifndef LONE_MEMORY_SIZE - #define LONE_MEMORY_SIZE (1024 * 1024) -#endif - -#ifndef LONE_ALIGNMENT - #define LONE_ALIGNMENT 16 -#endif - -/* ╭──────────────────────────┨ LONE LISP TYPES ┠───────────────────────────╮ - │ │ - │ Lone implements dynamic data types as a tagged union. │ - │ Supported types are: │ - │ │ - │ ◦ Module the isolated programming environment type │ - │ ◦ Function the reusable executable expressions type │ - │ ◦ Primitive the built-in C subroutine type │ - │ ◦ List the linked list and tree type │ - │ ◦ Vector the contiguous array of values type │ - │ ◦ Table the hash table, prototype and object type │ - │ ◦ Symbol the keyword and interned string type │ - │ ◦ Text the UTF-8 encoded text type │ - │ ◦ Bytes the binary data and low level string type │ - │ ◦ Integer the signed integer type │ - │ ◦ Pointer the memory addressing and dereferencing type │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -enum lone_type { - LONE_MODULE, - LONE_FUNCTION, - LONE_PRIMITIVE, - LONE_LIST, - LONE_VECTOR, - LONE_TABLE, - LONE_SYMBOL, - LONE_TEXT, - LONE_BYTES, - LONE_INTEGER, - LONE_POINTER, -}; - -struct lone_bytes { - size_t count; - unsigned char *pointer; -}; - -struct lone_list { - struct lone_value *first; - struct lone_value *rest; -}; - -struct lone_vector { - struct lone_value **values; - size_t count; - size_t capacity; -}; - -struct lone_table_entry { - struct lone_value *key; - struct lone_value *value; -}; - -struct lone_table { - size_t count; - size_t capacity; - struct lone_table_entry *entries; - struct lone_value *prototype; -}; - -/* https://dl.acm.org/doi/10.1145/947941.947948 - * https://user.ceng.metu.edu.tr/~ucoluk/research/lisp/lispman/node24.html - */ -struct lone_function_flags { - bool evaluate_arguments: 1; - bool evaluate_result: 1; - bool variable_arguments: 1; -}; - -struct lone_function { - struct lone_value *arguments; /* the bindings */ - struct lone_value *code; /* the lambda */ - struct lone_value *environment; /* the closure */ - struct lone_function_flags flags; /* how to evaluate & apply */ -}; - -struct lone_lisp; -typedef struct lone_value *(*lone_primitive)(struct lone_lisp *lone, - struct lone_value *module, - struct lone_value *environment, - struct lone_value *arguments, - struct lone_value *closure); - -struct lone_primitive { - struct lone_value *name; - lone_primitive function; - struct lone_value *closure; - struct lone_function_flags flags; /* primitives always accept variable arguments */ -}; - -struct lone_module { - struct lone_value *name; - struct lone_value *environment; - struct lone_value *exports; -}; - -enum lone_pointer_type { - LONE_TO_UNKNOWN, - - LONE_TO_U8, LONE_TO_I8, - LONE_TO_U16, LONE_TO_I16, - LONE_TO_U32, LONE_TO_I32, - LONE_TO_U64, LONE_TO_I64, -}; - -struct lone_pointer { - enum lone_pointer_type type; - void *address; -}; - -struct lone_value { - struct { - bool live: 1; - bool marked: 1; - bool should_deallocate_bytes: 1; - }; - - enum lone_type type; - - union { - struct lone_module module; - struct lone_function function; - struct lone_primitive primitive; - struct lone_list list; - struct lone_vector vector; - struct lone_table table; - struct lone_bytes bytes; /* also used by texts and symbols */ - struct lone_pointer pointer; - long integer; - }; -}; - -typedef bool (*lone_predicate)(struct lone_value *); -typedef bool (*lone_comparator)(struct lone_value *, struct lone_value *); - -/* ╭───────────────────────┨ LONE LISP INTERPRETER ┠────────────────────────╮ - │ │ - │ The lone lisp interpreter is composed of all internal state │ - │ necessary to process useful programs. It includes memory, │ - │ references to all allocated objects, a table of interned │ - │ symbols, references to constant values such as nil and │ - │ a table of loaded modules and the top level null module. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -struct lone_lisp { - struct { - void *stack; - struct lone_memory *general; - struct lone_heap *heaps; - } memory; - struct lone_value *symbol_table; - struct { - struct lone_value *nil; - struct lone_value *truth; - } constants; - struct { - struct lone_value *loaded; - struct lone_value *null; - struct lone_value *top_level_environment; - struct lone_value *path; - } modules; - struct { - struct { - unsigned long offset_basis; - } fnv_1a; - } hash; -}; - -/* ╭────────────────────┨ LONE LISP MEMORY ALLOCATION ┠─────────────────────╮ - │ │ - │ Lone is designed to work without any dependencies except Linux, │ - │ so it does not make use of even the system's C library. │ - │ In order to bootstrap itself in such harsh conditions, │ - │ it must be given some memory to work with. │ - │ │ - │ Lone manages its own memory with a block-based allocator. │ - │ Memory blocks are allocated on a first fit basis. │ - │ They will be split into smaller units when allocated │ - │ and merged together with free neighbors when deallocated. │ - │ │ - │ Memory blocks are segments prefixed by a block descriptor │ - │ that tracks its size, allocation status as well as pointers │ - │ to surrounding memory blocks. │ - │ │ - │ Lone employs a very simple mark-and-sweep garbage collector. │ - │ It starts by marking all values reachable by the interpreter │ - │ in its current state. Then it walks the list of all values, │ - │ deallocating any unmarked object it finds as well as any │ - │ memory they happen own based on the value's type. │ - │ │ - │ Like memory blocks, lone values are prefixed by a header │ - │ structure containing metadata such as its marked state │ - │ as well as a pointer to the next object in the list. │ - │ │ - │ Since these headers are prefixed to pointers returned by │ - │ allocation and value creation functions, it is simple to │ - │ calculate their locations given a pointer to a memory block │ - │ or lone value: simply subtract the header's size from it. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -struct lone_memory { - struct lone_memory *prev, *next; - int free; - size_t size; - unsigned char pointer[]; -}; - -struct lone_heap { - struct lone_heap *next; - size_t count; - struct lone_value values[]; -}; - -static void lone_memory_move(void *from, void *to, size_t count) -{ - unsigned char *source = from, *destination = to; - - if (source >= destination) { - /* destination is at or behind source, copy forwards */ - while (count--) { *destination++ = *source++; } - } else { - /* destination is ahead of source, copy backwards */ - source += count; destination += count; - while (count--) { *--destination = *--source; } - } -} - -static void lone_memory_split(struct lone_memory *block, size_t used) -{ - size_t excess = block->size - used; - - /* split block if there's enough space to allocate at least 1 byte */ - if (excess >= sizeof(struct lone_memory) + 1) { - struct lone_memory *new = (struct lone_memory *) __builtin_assume_aligned(block->pointer + used, LONE_ALIGNMENT); - new->next = block->next; - new->prev = block; - new->free = 1; - new->size = excess - sizeof(struct lone_memory); - block->next = new; - block->size -= excess + sizeof(struct lone_memory); - } -} - -static void lone_memory_coalesce(struct lone_memory *block) -{ - struct lone_memory *next; - - if (block && block->free) { - next = block->next; - if (next && next->free) { - block->size += next->size + sizeof(struct lone_memory); - next = block->next = next->next; - if (next) { next->prev = block; } - } - } -} - -static size_t __attribute__((const)) lone_next_power_of_2(size_t n) -{ - size_t next = 1; - while (next < n) { next *= 2; } - return next; -} - -static size_t __attribute__((const)) lone_next_power_of_2_multiple(size_t n, size_t m) -{ - m = lone_next_power_of_2(m); - return (n + m - 1) & (~(m - 1)); -} - -static size_t __attribute__((const)) lone_align(size_t size, size_t alignment) -{ - return lone_next_power_of_2_multiple(size, alignment); -} - -static void * __attribute__((malloc, alloc_size(2), alloc_align(3))) lone_allocate_aligned(struct lone_lisp *lone, size_t requested_size, size_t alignment) -{ - size_t needed_size = requested_size + sizeof(struct lone_memory); - struct lone_memory *block; - - needed_size = lone_align(needed_size, alignment); - - for (block = lone->memory.general; block; block = block->next) { - if (block->free && block->size >= needed_size) - break; - } - - if (!block) { linux_exit(-1); } - - block->free = 0; - lone_memory_split(block, needed_size); - - return block->pointer; -} - -static void * __attribute__((malloc, alloc_size(2), assume_aligned(LONE_ALIGNMENT))) lone_allocate(struct lone_lisp *lone, size_t requested_size) -{ - return lone_allocate_aligned(lone, requested_size, LONE_ALIGNMENT); -} - -static void lone_deallocate(struct lone_lisp *lone, void * pointer) -{ - struct lone_memory *block = ((struct lone_memory *) pointer) - 1; - block->free = 1; - - lone_memory_coalesce(block); - lone_memory_coalesce(block->prev); -} - -static void * __attribute__((alloc_size(3))) lone_reallocate(struct lone_lisp *lone, void *pointer, size_t size) -{ - struct lone_memory *old = ((struct lone_memory *) pointer) - 1, - *new = ((struct lone_memory *) lone_allocate(lone, size)) - 1; - - if (pointer) { - lone_memory_move(old->pointer, new->pointer, new->size < old->size ? new->size : old->size); - lone_deallocate(lone, pointer); - } - - return new->pointer; -} - -static struct lone_heap *lone_allocate_heap(struct lone_lisp *lone, size_t count) -{ - size_t i, size = sizeof(struct lone_heap) + (sizeof(struct lone_value) * count); - struct lone_heap *heap = lone_allocate(lone, size); - heap->next = 0; - heap->count = count; - for (i = 0; i < count; ++i) { - heap->values[i].live = false; - heap->values[i].marked = false; - } - return heap; -} - -static struct lone_value *lone_allocate_from_heap(struct lone_lisp *lone) -{ - struct lone_value *element; - struct lone_heap *heap, *prev; - size_t i; - - for (prev = lone->memory.heaps, heap = prev; heap; prev = heap, heap = heap->next) { - for (i = 0; i < heap->count; ++i) { - element = &heap->values[i]; - - if (!element->live) { - goto resurrect; - } - } - } - - heap = lone_allocate_heap(lone, lone->memory.heaps[0].count); - prev->next = heap; - element = &heap->values[0]; - -resurrect: - element->live = true; - return element; -} - -static void lone_deallocate_dead_heaps(struct lone_lisp *lone) -{ - struct lone_heap *prev = lone->memory.heaps, *heap = prev->next; - size_t i; - - while (heap) { - for (i = 0; i < heap->count; ++i) { - if (heap->values[i].live) { /* at least one live object */ goto next_heap; } - } - - /* no live objects */ - prev->next = heap->next; - lone_deallocate(lone, heap); - heap = prev->next; - continue; -next_heap: - prev = heap; - heap = heap->next; - } -} - -static void lone_mark_value(struct lone_value *value) -{ - if (!value || !value->live || value->marked) { return; } - - value->marked = true; - - switch (value->type) { - case LONE_MODULE: - lone_mark_value(value->module.name); - lone_mark_value(value->module.environment); - lone_mark_value(value->module.exports); - break; - case LONE_FUNCTION: - lone_mark_value(value->function.arguments); - lone_mark_value(value->function.code); - lone_mark_value(value->function.environment); - break; - case LONE_PRIMITIVE: - lone_mark_value(value->primitive.name); - lone_mark_value(value->primitive.closure); - break; - case LONE_LIST: - lone_mark_value(value->list.first); - lone_mark_value(value->list.rest); - break; - case LONE_VECTOR: - for (size_t i = 0; i < value->vector.count; ++i) { - lone_mark_value(value->vector.values[i]); - } - break; - case LONE_TABLE: - lone_mark_value(value->table.prototype); - for (size_t i = 0; i < value->table.capacity; ++i) { - lone_mark_value(value->table.entries[i].key); - lone_mark_value(value->table.entries[i].value); - } - break; - case LONE_SYMBOL: - case LONE_TEXT: - case LONE_BYTES: - case LONE_POINTER: - case LONE_INTEGER: - /* these types do not contain any other values to mark */ - break; - } -} - -static void lone_mark_known_roots(struct lone_lisp *lone) -{ - lone_mark_value(lone->symbol_table); - lone_mark_value(lone->constants.nil); - lone_mark_value(lone->constants.truth); - lone_mark_value(lone->modules.loaded); - lone_mark_value(lone->modules.null); - lone_mark_value(lone->modules.top_level_environment); - lone_mark_value(lone->modules.path); -} - -static bool lone_points_within_range(void *pointer, void *start, void *end) -{ - return start <= pointer && pointer < end; -} - -static bool lone_points_to_general_memory(struct lone_lisp *lone, void *pointer) -{ - struct lone_memory *general = lone->memory.general; - return lone_points_within_range(pointer, general->pointer, general->pointer + general->size); -} - -static bool lone_points_to_heap(struct lone_lisp *lone, void *pointer) -{ - struct lone_heap *heap; - - if (!lone_points_to_general_memory(lone, pointer)) { return false; } - - for (heap = lone->memory.heaps; heap; heap = heap->next) { - if (lone_points_within_range(pointer, heap->values, heap->values + heap->count)) { return true; } - } - - return false; -} - -static void lone_find_and_mark_stack_roots(struct lone_lisp *lone) -{ - void *bottom = lone->memory.stack, *top = __builtin_frame_address(0), *tmp; - void **pointer; - - if (top < bottom) { - tmp = bottom; - bottom = top; - top = tmp; - } - - pointer = bottom; - - while (pointer++ < top) { - if (lone_points_to_heap(lone, *pointer)) { - lone_mark_value(*pointer); - } - } -} - -static void lone_mark_all_reachable_values(struct lone_lisp *lone) -{ - lone_registers registers; /* stack space for registers */ - lone_save_registers(registers); /* spill registers on stack */ - - lone_mark_known_roots(lone); /* precise */ - lone_find_and_mark_stack_roots(lone); /* conservative */ -} - -static void lone_kill_all_unmarked_values(struct lone_lisp *lone) -{ - struct lone_value *value; - struct lone_heap *heap; - size_t i; - - for (heap = lone->memory.heaps; heap; heap = heap->next) { - for (i = 0; i < heap->count; ++i) { - value = &heap->values[i]; - - if (!value->live) { continue; } - - if (!value->marked) { - switch (value->type) { - case LONE_BYTES: - case LONE_TEXT: - case LONE_SYMBOL: - if (value->should_deallocate_bytes) { - lone_deallocate(lone, value->bytes.pointer); - } - break; - case LONE_VECTOR: - lone_deallocate(lone, value->vector.values); - break; - case LONE_TABLE: - lone_deallocate(lone, value->table.entries); - break; - case LONE_MODULE: - case LONE_FUNCTION: - case LONE_PRIMITIVE: - case LONE_LIST: - case LONE_INTEGER: - case LONE_POINTER: - /* these types do not own any additional memory */ - break; - } - - value->live = false; - } - - value->marked = false; - } - } -} - -static void lone_garbage_collector(struct lone_lisp *lone) -{ - lone_mark_all_reachable_values(lone); - lone_kill_all_unmarked_values(lone); - lone_deallocate_dead_heaps(lone); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Initializers and creation functions for lone's types. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_value_create(struct lone_lisp *lone) -{ - return lone_allocate_from_heap(lone); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Lone bytes values are initialized with a pointer to a memory │ - │ block of known size. It can take ownership of the block │ - │ through the transfer functions or it can make a copy of it │ - │ via the create functions. Ownership means the block is │ - │ deallocated when the value is garbage collected, │ - │ so it is advisable to copy data not owned by lone │ - │ such as C string literals. │ - │ │ - │ Copies will automatically include a hidden trailing null │ - │ byte to ease compatibility with code expecting C strings. │ - │ Transferred buffers should also contain that null byte │ - │ but the lone bytes type currently has no way to enforce this. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_bytes_transfer(struct lone_lisp *lone, unsigned char *pointer, size_t count, bool should_deallocate) -{ - struct lone_value *value = lone_value_create(lone); - value->type = LONE_BYTES; - value->bytes.count = count; - value->bytes.pointer = pointer; - value->should_deallocate_bytes = should_deallocate; - return value; -} - -static struct lone_value *lone_bytes_transfer_bytes(struct lone_lisp *lone, struct lone_bytes bytes, bool should_deallocate) -{ - return lone_bytes_transfer(lone, bytes.pointer, bytes.count, should_deallocate); -} - -static struct lone_value *lone_bytes_create(struct lone_lisp *lone, unsigned char *pointer, size_t count) -{ - unsigned char *copy = lone_allocate(lone, count + 1); - lone_memory_move(pointer, copy, count); - copy[count] = '\0'; - return lone_bytes_transfer(lone, copy, count, true); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Lone lists are pairs of lone values: first and rest. │ - │ Usually first is the element while rest is another pair. │ - │ This explains their names. Also called car and cdr. │ - │ │ - │ Although often the case, rest need not be another pair. │ - │ Any other object may be set: (1 . 2); first = 1, rest = 2. │ - │ So rest could also be named second. │ - │ │ - │ A list with null first and rest pointers is known as nil. │ - │ It is provided as a constant by the lone interpreter. │ - │ Their presence in the rest of a list marks its end. │ - │ New nil values may be created by C code that builds lists. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_list_create(struct lone_lisp *lone, struct lone_value *first, struct lone_value *rest) -{ - struct lone_value *value = lone_value_create(lone); - value->type = LONE_LIST; - value->list.first = first; - value->list.rest = rest; - return value; -} - -static struct lone_value *lone_list_create_nil(struct lone_lisp *lone) -{ - return lone_list_create(lone, 0, 0); -} - -static struct lone_value *lone_nil(struct lone_lisp *lone) -{ - return lone->constants.nil; -} - -static struct lone_value *lone_true(struct lone_lisp *lone) -{ - return lone->constants.truth; -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Lone functions represent a body of executable lone lisp code. │ - │ They have a list of argument names to be bound during function │ - │ application, a list of expressions to be evaluated when called │ - │ and a closure: a reference to the environment it was defined in. │ - │ │ - │ To apply a function is to create a new environment with its │ - │ argument names bound to the given arguments and then evaluate │ - │ the function's expressions in the context of that environment. │ - │ │ - │ The function flags control how the function is applied. │ - │ It may be configured to receive evaluated or unevaluated │ - │ arguments as well as to evaluate the result automatically. │ - │ These features allow code manipulation and generation. │ - │ It may also be configured to be variadic: all arguments │ - │ are collected into a list and passed as a single argument. │ - │ │ - │ Primitives are lone functions implemented in C. │ - │ They are always variadic and must check their arguments. │ - │ All of them must follow the primitive function prototype. │ - │ They also have closures which are pointers to arbitrary data. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_function_create(struct lone_lisp *lone, struct lone_value *arguments, struct lone_value *code, struct lone_value *environment, struct lone_function_flags flags) -{ - struct lone_value *value = lone_value_create(lone); - value->type = LONE_FUNCTION; - value->function.arguments = arguments; - value->function.code = code; - value->function.environment = environment; - value->function.flags = flags; - return value; -} - -static struct lone_value *lone_intern_c_string(struct lone_lisp *, char *); - -static struct lone_value *lone_primitive_create(struct lone_lisp *lone, char *name, lone_primitive function, struct lone_value *closure, struct lone_function_flags flags) -{ - struct lone_value *value = lone_value_create(lone); - value->type = LONE_PRIMITIVE; - value->primitive.name = lone_intern_c_string(lone, name); - value->primitive.function = function; - value->primitive.closure = closure; - value->primitive.flags = flags; - value->primitive.flags.variable_arguments = 1; - return value; -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Lone vectors are simple dynamic arrays of lone values. │ - │ They grow automatically as elements are added. │ - │ Any index may be used regardless of current length: │ - │ all the elements remain unset as the array grows. │ - │ Unset elements are null pointers which are currently │ - │ converted to nil automatically but might one day serve │ - │ as an undefined value like in other languages. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_vector_create(struct lone_lisp *lone, size_t capacity) -{ - struct lone_value *value = lone_value_create(lone); - value->type = LONE_VECTOR; - value->vector.capacity = capacity; - value->vector.count = 0; - value->vector.values = lone_allocate(lone, capacity * sizeof(*value->vector.values)); - for (size_t i = 0; i < value->vector.capacity; ++i) { value->vector.values[i] = 0; } - return value; -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Lone tables are openly addressed, linearly probed hash tables. │ - │ Currently, lone tables use the FNV-1a hashing algorithm. │ - │ They also strive to maintain a load factor of at most 0.5: │ - │ tables will be rehashed once they're above half capacity. │ - │ They do not use tombstones to delete keys. │ - │ │ - │ Tables are able to inherit from another table: missing keys │ - │ are also looked up in the parent table. This is currently used │ - │ to implement nested environments but will also serve as a │ - │ prototype-based object system as in Javascript and Self. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_table_create(struct lone_lisp *lone, size_t capacity, struct lone_value *prototype) -{ - struct lone_value *value = lone_value_create(lone); - value->type = LONE_TABLE; - value->table.prototype = prototype; - value->table.capacity = capacity; - value->table.count = 0; - value->table.entries = lone_allocate(lone, capacity * sizeof(*value->table.entries)); - - for (size_t i = 0; i < capacity; ++i) { - value->table.entries[i].key = 0; - value->table.entries[i].value = 0; - } - - return value; -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Lone integers are currently signed fixed-length integers. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_integer_create(struct lone_lisp *lone, long integer) -{ - struct lone_value *value = lone_value_create(lone); - value->type = LONE_INTEGER; - value->integer = integer; - return value; -} - -static struct lone_value *lone_integer_parse(struct lone_lisp *lone, unsigned char *digits, size_t count) -{ - size_t i = 0; - long integer = 0; - - switch (*digits) { case '+': case '-': ++i; break; } - - while (i < count) { - integer *= 10; - integer += digits[i++] - '0'; - } - - if (*digits == '-') { integer *= -1; } - - return lone_integer_create(lone, integer); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Lone pointers do not own the data they point to. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_pointer_create(struct lone_lisp *lone, void *pointer, enum lone_pointer_type type) -{ - struct lone_value *value = lone_value_create(lone); - value->type = LONE_POINTER; - value->pointer.type = type; - value->pointer.address = pointer; - return value; -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Lone texts are lone's strings and represent UTF-8 encoded text. │ - │ Transfer and creation functions work like lone bytes. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_text_transfer(struct lone_lisp *lone, unsigned char *text, size_t length, bool should_deallocate) -{ - struct lone_value *value = lone_bytes_transfer(lone, text, length, should_deallocate); - value->type = LONE_TEXT; - return value; -} - -static struct lone_value *lone_text_transfer_bytes(struct lone_lisp *lone, struct lone_bytes bytes, bool should_deallocate) -{ - return lone_text_transfer(lone, bytes.pointer, bytes.count, should_deallocate); -} - -static struct lone_value *lone_text_create(struct lone_lisp *lone, unsigned char *text, size_t length) -{ - struct lone_value *value = lone_bytes_create(lone, text, length); - value->type = LONE_TEXT; - return value; -} - -static size_t lone_c_string_length(char *c_string) -{ - size_t length = 0; - if (!c_string) { return 0; } - while (c_string[length++]); - return length - 1; -} - -static struct lone_value *lone_text_create_from_c_string(struct lone_lisp *lone, char *c_string) -{ - return lone_text_transfer(lone, (unsigned char *) c_string, lone_c_string_length(c_string), false); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Lone symbols are like lone texts but are interned in a table. │ - │ Symbol table interning deduplicates them in memory, │ - │ enabling fast identity-based comparisons via pointer equality. │ - │ However, this means they won't be garbage collected. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_symbol_transfer(struct lone_lisp *lone, unsigned char *text, size_t length, bool should_deallocate) -{ - struct lone_value *value = lone_bytes_transfer(lone, text, length, should_deallocate); - value->type = LONE_SYMBOL; - return value; -} - -static struct lone_value *lone_symbol_transfer_bytes(struct lone_lisp *lone, struct lone_bytes bytes, bool should_deallocate) -{ - return lone_symbol_transfer(lone, bytes.pointer, bytes.count, should_deallocate); -} - -static struct lone_value *lone_symbol_create(struct lone_lisp *lone, unsigned char *text, size_t length) -{ - struct lone_value *value = lone_bytes_create(lone, text, length); - value->type = LONE_SYMBOL; - return value; -} - -static bool lone_is_nil(struct lone_value *); -static struct lone_value *lone_table_get(struct lone_lisp *, struct lone_value *, struct lone_value *); -static void lone_table_set(struct lone_lisp *, struct lone_value *, struct lone_value *, struct lone_value *); - -static struct lone_value *lone_intern(struct lone_lisp *lone, unsigned char *bytes, size_t count, bool should_deallocate) -{ - struct lone_value *key, *value; - - key = should_deallocate? lone_symbol_create(lone, bytes, count) : lone_symbol_transfer(lone, bytes, count, should_deallocate); - value = lone_table_get(lone, lone->symbol_table, key); - - if (lone_is_nil(value)) { - value = key; - lone_table_set(lone, lone->symbol_table, key, value); - } - - return value; -} - -static struct lone_value *lone_intern_c_string(struct lone_lisp *lone, char *c_string) -{ - return lone_intern(lone, (unsigned char *) c_string, lone_c_string_length(c_string), false); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Lone modules are named isolated environments for evaluation. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_module_create(struct lone_lisp *lone, struct lone_value *name) -{ - struct lone_value *value = lone_value_create(lone); - value->type = LONE_MODULE; - value->module.name = name; - value->module.environment = lone_table_create(lone, 64, lone->modules.top_level_environment); - value->module.exports = lone_vector_create(lone, 16); - return value; -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ The lone lisp structure represents the lone lisp interpreter. │ - │ A pointer to this structure is passed to nearly every function. │ - │ It must be initialized before everything else since the memory │ - │ allocation system is not functional without it. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_primitive_import(struct lone_lisp *, struct lone_value *, struct lone_value *, struct lone_value *, struct lone_value *); -static struct lone_value *lone_primitive_export(struct lone_lisp *, struct lone_value *, struct lone_value *, struct lone_value *, struct lone_value *); -static void lone_hash_initialize(struct lone_lisp *, struct lone_bytes); - -static void lone_lisp_initialize(struct lone_lisp *lone, struct lone_bytes memory, size_t heap_size, void *stack, struct lone_bytes random) -{ - struct lone_function_flags flags = { .evaluate_arguments = 0, .evaluate_result = 0, .variable_arguments = 1 }; - struct lone_value *import, *export; - - lone->memory.stack = stack; - - lone->memory.general = (struct lone_memory *) __builtin_assume_aligned(memory.pointer, LONE_ALIGNMENT); - lone->memory.general->prev = lone->memory.general->next = 0; - lone->memory.general->free = 1; - lone->memory.general->size = memory.count - sizeof(struct lone_memory); - - lone->memory.heaps = lone_allocate_heap(lone, heap_size); - - lone_hash_initialize(lone, random); - - /* basic initialization done, can now use value creation functions */ - - lone->symbol_table = lone_table_create(lone, 256, 0); - lone->constants.nil = lone_list_create_nil(lone); - lone->constants.truth = lone_intern_c_string(lone, "true"); - - lone->modules.loaded = lone_table_create(lone, 32, 0); - lone->modules.top_level_environment = lone_table_create(lone, 8, 0); - lone->modules.path = lone_vector_create(lone, 8); - - import = lone_primitive_create(lone, "import", lone_primitive_import, 0, flags); - export = lone_primitive_create(lone, "export", lone_primitive_export, 0, flags); - lone_table_set(lone, lone->modules.top_level_environment, lone_intern_c_string(lone, "import"), import); - lone_table_set(lone, lone->modules.top_level_environment, lone_intern_c_string(lone, "export"), export); - lone->modules.null = lone_module_create(lone, 0); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Type predicate functions. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static bool lone_has_same_type(struct lone_value *x, struct lone_value *y) -{ - return x->type == y->type; -} - -static bool lone_is_module(struct lone_value *value) -{ - return value->type == LONE_MODULE; -} - -static bool lone_is_function(struct lone_value *value) -{ - return value->type == LONE_FUNCTION; -} - -static bool lone_is_primitive(struct lone_value *value) -{ - return value->type == LONE_PRIMITIVE; -} - -static bool lone_is_applicable(struct lone_value *value) -{ - return lone_is_function(value) || lone_is_primitive(value); -} - -static bool lone_is_list(struct lone_value *value) -{ - return value->type == LONE_LIST; -} - -static bool lone_is_vector(struct lone_value *value) -{ - return value->type == LONE_VECTOR; -} - -static bool lone_is_table(struct lone_value *value) -{ - return value->type == LONE_TABLE; -} - -static bool lone_is_nil(struct lone_value *value) -{ - return lone_is_list(value) && value->list.first == 0 && value->list.rest == 0; -} - -static bool lone_has_bytes(struct lone_value *value) -{ - return value->type == LONE_TEXT || value->type == LONE_SYMBOL || value->type == LONE_BYTES; -} - -static bool lone_is_bytes(struct lone_value *value) -{ - return value->type == LONE_BYTES; -} - -static bool lone_is_text(struct lone_value *value) -{ - return value->type == LONE_TEXT; -} - -static bool lone_is_symbol(struct lone_value *value) -{ - return value->type == LONE_SYMBOL; -} - -static bool lone_is_integer(struct lone_value *value) -{ - return value->type == LONE_INTEGER; -} - -static bool lone_is_pointer(struct lone_value *value) -{ - return value->type == LONE_POINTER; -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Comparison and equality functions. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static bool lone_is_identical(struct lone_value *x, struct lone_value *y) -{ - return x == y; -} - -static bool lone_bytes_equals(struct lone_bytes x, struct lone_bytes y); - -static bool lone_is_equivalent(struct lone_value *x, struct lone_value *y) -{ - if (lone_is_identical(x, y)) { return true; } - if (!lone_has_same_type(x, y)) { return false; } - - switch (x->type) { - case LONE_SYMBOL: - case LONE_TEXT: - case LONE_BYTES: - return lone_bytes_equals(x->bytes, y->bytes); - case LONE_INTEGER: - return x->integer == y->integer; - case LONE_POINTER: - return x->pointer.address == y->pointer.address; - - case LONE_MODULE: case LONE_FUNCTION: case LONE_PRIMITIVE: - case LONE_LIST: case LONE_VECTOR: case LONE_TABLE: - return lone_is_identical(x, y); - } -} - -static bool lone_is_equal(struct lone_value *, struct lone_value *); - -static bool lone_list_is_equal(struct lone_value *x, struct lone_value *y) -{ - return lone_is_equal(x->list.first, y->list.first) && lone_is_equal(x->list.rest, y->list.rest); -} - -static bool lone_vector_is_equal(struct lone_value *x, struct lone_value *y) -{ - size_t i; - - if (x->vector.count != y->vector.count) return false; - - for (i = 0; i < x->vector.count; ++i) { - if (!lone_is_equal(x->vector.values[i], y->vector.values[i])) { - return false; - } - } - - return true; -} - -static bool lone_table_is_equal(struct lone_value *x, struct lone_value *y) -{ - return lone_is_identical(x, y); -} - -static bool lone_is_equal(struct lone_value *x, struct lone_value *y) -{ - if (lone_is_identical(x, y)) { return true; } - if (!lone_has_same_type(x, y)) { return false; } - - switch (x->type) { - case LONE_LIST: - return lone_list_is_equal(x, y); - case LONE_VECTOR: - return lone_vector_is_equal(x, y); - case LONE_TABLE: - return lone_table_is_equal(x, y); - - case LONE_MODULE: case LONE_FUNCTION: case LONE_PRIMITIVE: - case LONE_SYMBOL: case LONE_TEXT: case LONE_BYTES: - case LONE_INTEGER: case LONE_POINTER: - return lone_is_equivalent(x, y); - } -} - -static bool lone_integer_is_less_than(struct lone_value *x, struct lone_value *y) -{ - if (!(lone_is_integer(x) && lone_is_integer(y))) { /* can't compare non-integers */ linux_exit(-1); } - - if (x->integer < y->integer) { return true; } - else { return false; } -} - -static bool lone_integer_is_less_than_or_equal_to(struct lone_value *x, struct lone_value *y) -{ - if (!(lone_is_integer(x) && lone_is_integer(y))) { /* can't compare non-integers */ linux_exit(-1); } - - if (x->integer <= y->integer) { return true; } - else { return false; } -} - -static bool lone_integer_is_greater_than(struct lone_value *x, struct lone_value *y) -{ - if (!(lone_is_integer(x) && lone_is_integer(y))) { /* can't compare non-integers */ linux_exit(-1); } - - if (x->integer > y->integer) { return true; } - else { return false; } -} - -static bool lone_integer_is_greater_than_or_equal_to(struct lone_value *x, struct lone_value *y) -{ - if (!(lone_is_integer(x) && lone_is_integer(y))) { /* can't compare non-integers */ linux_exit(-1); } - - if (x->integer >= y->integer) { return true; } - else { return false; } -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ List manipulation functions. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static inline struct lone_value *lone_list_first(struct lone_value *value) -{ - return value->list.first; -} - -static inline struct lone_value *lone_list_rest(struct lone_value *value) -{ - return value->list.rest; -} - -static struct lone_value *lone_list_set_first(struct lone_value *list, struct lone_value *value) -{ - return list->list.first = value; -} - -static struct lone_value *lone_list_set_rest(struct lone_value *list, struct lone_value *rest) -{ - return list->list.rest = rest; -} - -static struct lone_value *lone_list_append(struct lone_lisp *lone, struct lone_value *list, struct lone_value *value) -{ - lone_list_set_first(list, value); - return lone_list_set_rest(list, lone_list_create_nil(lone)); -} - -static struct lone_value *lone_list_build(struct lone_lisp *lone, size_t count, ...) -{ - struct lone_value *list = lone_list_create_nil(lone), *head = list, *argument; - va_list arguments; - size_t i; - - va_start(arguments, count); - - for (i = 0; i < count; ++i) { - argument = va_arg(arguments, struct lone_value *); - head = lone_list_append(lone, head, argument); - } - - va_end(arguments); - - return list; -} - -static void lone_vector_push(struct lone_lisp *, struct lone_value *, struct lone_value *); - -static struct lone_value *lone_list_to_vector(struct lone_lisp *lone, struct lone_value *list) -{ - struct lone_value *vector = lone_vector_create(lone, 16), *head; - - for (head = list; !lone_is_nil(head); head = lone_list_rest(head)) { - lone_vector_push(lone, vector, lone_list_first(head)); - } - - return vector; -} - -static struct lone_value *lone_list_flatten(struct lone_lisp *lone, struct lone_value *list) -{ - struct lone_value *flattened = lone_list_create_nil(lone), *head, *flat_head, *return_head, *first; - - for (head = list, flat_head = flattened; !lone_is_nil(head); head = lone_list_rest(head)) { - first = lone_list_first(head); - - if (lone_is_list(first)) { - return_head = lone_list_flatten(lone, first); - - for (/* return_head */; !lone_is_nil(return_head); return_head = lone_list_rest(return_head)) { - flat_head = lone_list_append(lone, flat_head, lone_list_first(return_head)); - } - - } else { - flat_head = lone_list_append(lone, flat_head, first); - } - } - - return flattened; -} - -static bool lone_bytes_equals(struct lone_bytes x, struct lone_bytes y) -{ - if (x.count != y.count) return false; - for (size_t i = 0; i < x.count; ++i) if (x.pointer[i] != y.pointer[i]) return false; - return true; -} - -static inline int lone_bytes_equals_c_string(struct lone_bytes bytes, char *c_string) -{ - struct lone_bytes c_string_bytes = { lone_c_string_length(c_string), (unsigned char *) c_string }; - return lone_bytes_equals(bytes, c_string_bytes); -} - -static struct lone_bytes lone_join(struct lone_lisp *lone, struct lone_value *separator, struct lone_value *arguments, lone_predicate is_valid) -{ - struct lone_value *head, *argument; - unsigned char *joined; - size_t total = 0, position = 0; - - if (!is_valid) { is_valid = lone_has_bytes; } - if (is_valid != lone_has_bytes && is_valid != lone_is_bytes && - is_valid != lone_is_text && is_valid != lone_is_symbol) { - /* invalid predicate function */ linux_exit(-1); - } - - if (separator && !lone_is_nil(separator)) { - if (!is_valid(separator)) { linux_exit(-1); } - } - - for (head = arguments; head && !lone_is_nil(head); head = lone_list_rest(head)) { - argument = lone_list_first(head); - if (!is_valid(argument)) { linux_exit(-1); } - - total += argument->bytes.count; - if (separator && !lone_is_nil(separator)) { - if (!lone_is_nil(lone_list_rest(head))) { total += separator->bytes.count; } - } - } - - joined = lone_allocate(lone, total + 1); - - for (head = arguments; head && !lone_is_nil(head); head = lone_list_rest(head)) { - argument = lone_list_first(head); - - lone_memory_move(argument->bytes.pointer, joined + position, argument->bytes.count); - position += argument->bytes.count; - - if (separator && !lone_is_nil(separator)) { - if (!lone_is_nil(lone_list_rest(head))) { - lone_memory_move(separator->bytes.pointer, joined + position, separator->bytes.count); - position += separator->bytes.count; - } - } - } - - joined[total] = '\0'; - - return (struct lone_bytes) { .count = total, .pointer = joined }; -} - -static struct lone_bytes lone_concatenate(struct lone_lisp *lone, struct lone_value *arguments, lone_predicate is_valid) -{ - return lone_join(lone, 0, arguments, is_valid); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Functions for vectors, lone's dynamic arrays. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static void lone_vector_resize(struct lone_lisp *lone, struct lone_value *vector, size_t new_capacity) -{ - struct lone_value **new = lone_allocate(lone, new_capacity * sizeof(struct lone_value *)); - size_t i; - - for (i = 0; i < new_capacity; ++i) { - new[i] = i < vector->vector.count? vector->vector.values[i] : 0; - } - - lone_deallocate(lone, vector->vector.values); - - vector->vector.values = new; - vector->vector.capacity = new_capacity; - if (vector->vector.count > new_capacity) { vector->vector.count = new_capacity; } -} - -static struct lone_value *lone_vector_get_value_at(struct lone_lisp *lone, struct lone_value *vector, size_t i) -{ - struct lone_value *value = i < vector->vector.capacity? vector->vector.values[i] : lone_nil(lone); - return value? value : lone_nil(lone); -} - -static struct lone_value *lone_vector_get(struct lone_lisp *lone, struct lone_value *vector, struct lone_value *index) -{ - if (!lone_is_integer(index)) { /* only integer indexes supported */ linux_exit(-1); } - return lone_vector_get_value_at(lone, vector, (size_t) index->integer); -} - -static void lone_vector_set_value_at(struct lone_lisp *lone, struct lone_value *vector, size_t i, struct lone_value *value) -{ - if (i >= vector->vector.capacity) { lone_vector_resize(lone, vector, i * 2); } - vector->vector.values[i] = value; - if (++i > vector->vector.count) { vector->vector.count = i; } -} - -static void lone_vector_set(struct lone_lisp *lone, struct lone_value *vector, struct lone_value *index, struct lone_value *value) -{ - if (!lone_is_integer(index)) { /* only integer indexes supported */ linux_exit(-1); } - lone_vector_set_value_at(lone, vector, (size_t) index->integer, value); -} - -static void lone_vector_push(struct lone_lisp *lone, struct lone_value *vector, struct lone_value *value) -{ - lone_vector_set_value_at(lone, vector, vector->vector.count, value); -} - -static void lone_vector_push_va_list(struct lone_lisp *lone, struct lone_value *vector, size_t count, va_list arguments) -{ - struct lone_value *argument; - size_t i; - - for (i = 0; i < count; ++i) { - argument = va_arg(arguments, struct lone_value *); - lone_vector_push(lone, vector, argument); - } -} - -static void lone_vector_push_all(struct lone_lisp *lone, struct lone_value *vector, size_t count, ...) -{ - va_list arguments; - - va_start(arguments, count); - lone_vector_push_va_list(lone, vector, count, arguments); - va_end(arguments); -} - -static struct lone_value *lone_vector_build(struct lone_lisp *lone, size_t count, ...) -{ - struct lone_value *vector = lone_vector_create(lone, count); - va_list arguments; - - va_start(arguments, count); - lone_vector_push_va_list(lone, vector, count, arguments); - va_end(arguments); - - return vector; -} - -static bool lone_vector_contains(struct lone_value *vector, struct lone_value *value) -{ - size_t i; - - for (i = 0; i < vector->vector.count; ++i) { - if (lone_is_equal(value, vector->vector.values[i])) { - return true; - } - } - - return false; -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Hash table functions. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static unsigned long __attribute__((pure)) fnv_1a(struct lone_bytes data, unsigned long offset_basis) -{ - unsigned long hash = offset_basis; - unsigned char *bytes = data.pointer; - size_t count = data.count; - - while (count--) { - hash ^= *bytes++; - hash *= FNV_PRIME; - } - - return hash; -} - -static void lone_hash_initialize(struct lone_lisp *lone, struct lone_bytes random) -{ - lone->hash.fnv_1a.offset_basis = fnv_1a(random, FNV_OFFSET_BASIS); -} - -static size_t lone_hash_recursively(struct lone_value *key, unsigned long hash) -{ - struct lone_bytes bytes; - - if (!key) { /* a null key is probably a bug */ linux_exit(-1); } - - bytes.pointer = (unsigned char *) &key->type; - bytes.count = sizeof(key->type); - hash = fnv_1a(bytes, hash); - - if (lone_is_nil(key)) { return hash; } - - switch (key->type) { - case LONE_MODULE: - case LONE_FUNCTION: - case LONE_PRIMITIVE: - case LONE_VECTOR: - case LONE_TABLE: - linux_exit(-1); - case LONE_LIST: - hash = lone_hash_recursively(key->list.first, hash); - hash = lone_hash_recursively(key->list.rest, hash); - return hash; - case LONE_SYMBOL: - case LONE_TEXT: - case LONE_BYTES: - bytes = key->bytes; - break; - case LONE_INTEGER: - bytes.pointer = (unsigned char *) &key->integer; - bytes.count = sizeof(key->integer); - break; - case LONE_POINTER: - bytes.pointer = (unsigned char *) &key->pointer; - bytes.count = sizeof(key->pointer); - break; - } - - hash = fnv_1a(bytes, hash); - - return hash; -} - -static size_t lone_hash(struct lone_lisp *lone, struct lone_value *value) -{ - return lone_hash_recursively(value, lone->hash.fnv_1a.offset_basis); -} - -static unsigned long lone_table_compute_hash_for(struct lone_lisp *lone, struct lone_value *key, size_t capacity) -{ - return lone_hash(lone, key) % capacity; -} - -static size_t lone_table_entry_find_index_for(struct lone_lisp *lone, struct lone_value *key, struct lone_table_entry *entries, size_t capacity) -{ - size_t i = lone_table_compute_hash_for(lone, key, capacity); - - while (entries[i].key && !lone_is_equal(entries[i].key, key)) { - i = (i + 1) % capacity; - } - - return i; -} - -static int lone_table_entry_set(struct lone_lisp *lone, struct lone_table_entry *entries, size_t capacity, struct lone_value *key, struct lone_value *value) -{ - size_t i = lone_table_entry_find_index_for(lone, key, entries, capacity); - struct lone_table_entry *entry = &entries[i]; - - if (entry->key) { - entry->value = value; - return 0; - } else { - entry->key = key; - entry->value = value; - return 1; - } -} - -static void lone_table_resize(struct lone_lisp *lone, struct lone_value *table, size_t new_capacity) -{ - size_t old_capacity = table->table.capacity, i; - struct lone_table_entry *old = table->table.entries, - *new = lone_allocate(lone, new_capacity * sizeof(*new)); - - for (i = 0; i < new_capacity; ++i) { - new[i].key = 0; - new[i].value = 0; - } - - for (i = 0; i < old_capacity; ++i) { - if (old[i].key) { - lone_table_entry_set(lone, new, new_capacity, old[i].key, old[i].value); - } - } - - lone_deallocate(lone, old); - table->table.entries = new; - table->table.capacity = new_capacity; -} - -static void lone_table_set(struct lone_lisp *lone, struct lone_value *table, struct lone_value *key, struct lone_value *value) -{ - if (table->table.count >= table->table.capacity / 2) { - lone_table_resize(lone, table, table->table.capacity * 2); - } - - if (lone_table_entry_set(lone, table->table.entries, table->table.capacity, key, value)) { - ++table->table.count; - } -} - -static struct lone_value *lone_table_get(struct lone_lisp *lone, struct lone_value *table, struct lone_value *key) -{ - size_t capacity = table->table.capacity, i; - struct lone_table_entry *entries = table->table.entries, *entry; - struct lone_value *prototype = table->table.prototype; - - i = lone_table_entry_find_index_for(lone, key, entries, capacity); - entry = &entries[i]; - - if (entry->key) { - return entry->value; - } else if (prototype && !lone_is_nil(prototype)) { - return lone_table_get(lone, prototype, key); - } else { - return lone_nil(lone); - } -} - -static void lone_table_delete(struct lone_lisp *lone, struct lone_value *table, struct lone_value *key) -{ - size_t capacity = table->table.capacity, i, j, k; - struct lone_table_entry *entries = table->table.entries; - - i = lone_table_entry_find_index_for(lone, key, entries, capacity); - - if (!entries[i].key) { return; } - - j = i; - while (1) { - j = (j + 1) % capacity; - if (!entries[j].key) { break; } - k = lone_table_compute_hash_for(lone, entries[j].key, capacity); - if ((j > i && (k <= i || k > j)) || (j < i && (k <= i && k > j))) { - entries[i] = entries[j]; - i = j; - } - } - - entries[i].key = 0; - entries[i].value = 0; - --table->table.count; -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Pointer dereferencing functions. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_pointer_dereference(struct lone_lisp *lone, struct lone_value *pointer) -{ - enum lone_pointer_type type; - void *address; - - if (!lone_is_pointer(pointer)) { /* can't dereference this value */ linux_exit(-1); } - - type = pointer->pointer.type; - address = pointer->pointer.address; - - switch (type) { - case LONE_TO_U8: - return lone_integer_create(lone, *((uint8_t *) address)); - case LONE_TO_I8: - return lone_integer_create(lone, *((int8_t *) address)); - case LONE_TO_U16: - return lone_integer_create(lone, *((uint16_t *) address)); - case LONE_TO_I16: - return lone_integer_create(lone, *((int16_t *) address)); - case LONE_TO_U32: - return lone_integer_create(lone, *((uint32_t *) address)); - case LONE_TO_I32: - return lone_integer_create(lone, *((int32_t *) address)); - case LONE_TO_U64: - return lone_integer_create(lone, (long) *((uint64_t *) address)); - case LONE_TO_I64: - return lone_integer_create(lone, *((int64_t *) address)); - case LONE_TO_UNKNOWN: - /* cannot dereference pointer to unknown type */ linux_exit(-1); - } -} - -/* ╭─────────────────────────┨ LONE LISP READER ┠───────────────────────────╮ - │ │ - │ The reader's job is to transform input into lone lisp values. │ - │ It accomplishes the task by reading input from a given file │ - │ descriptor and then lexing and parsing the results. │ - │ │ - │ The lexer or tokenizer transforms a linear stream of characters │ - │ into a linear stream of tokens suitable for parser consumption. │ - │ This gets rid of insignificant whitespace and reduces the size │ - │ of the parser's input significantly. │ - │ │ - │ It consists of an input buffer, its current position in it │ - │ as well as two functions: │ - │ │ - │ ◦ peek(k) which returns the character at i+k │ - │ ◦ consume(k) which advances i by k positions │ - │ │ - │ The parser transforms a linear sequence of tokens into a nested │ - │ sequence of lisp objects suitable for evaluation. │ - │ Its main task is to match nested structures such as lists. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -struct lone_reader { - int file_descriptor; - struct { - struct lone_bytes bytes; - struct { - size_t read; - size_t write; - } position; - } buffer; - int error; -}; - -static void lone_reader_initialize(struct lone_lisp *lone, struct lone_reader *reader, size_t buffer_size, int file_descriptor) -{ - reader->file_descriptor = file_descriptor; - reader->buffer.bytes.count = buffer_size; - reader->buffer.bytes.pointer = lone_allocate(lone, buffer_size); - reader->buffer.position.read = 0; - reader->buffer.position.write = 0; - reader->error = 0; -} - -static void lone_reader_finalize(struct lone_lisp *lone, struct lone_reader *reader) -{ - lone_deallocate(lone, reader->buffer.bytes.pointer); -} - -static size_t lone_reader_fill_buffer(struct lone_lisp *lone, struct lone_reader *reader) -{ - unsigned char *buffer = reader->buffer.bytes.pointer; - size_t size = reader->buffer.bytes.count, position = reader->buffer.position.write, - allocated = size, bytes_read = 0, total_read = 0; - ssize_t read_result = 0; - - while (1) { - read_result = linux_read(reader->file_descriptor, buffer + position, size); - - if (read_result < 0) { - linux_exit(-1); - } - - bytes_read = (size_t) read_result; - total_read += bytes_read; - position += bytes_read; - - if (bytes_read == size) { - allocated += size; - buffer = lone_reallocate(lone, buffer, allocated); - } else { - break; - } - } - - reader->buffer.bytes.pointer = buffer; - reader->buffer.bytes.count = allocated; - reader->buffer.position.write = position; - return total_read; -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ The peek(k) function returns the k-th element from the input │ - │ starting from the current input position, with peek(0) being │ - │ the current character and peek(k) being look ahead for k > 1. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static unsigned char *lone_reader_peek_k(struct lone_lisp *lone, struct lone_reader *reader, size_t k) -{ - size_t read_position = reader->buffer.position.read, - write_position = reader->buffer.position.write, - bytes_read; - - if (read_position + k >= write_position) { - // we'd overrun the buffer because there's not enough input - // fill it up by reading more first - bytes_read = lone_reader_fill_buffer(lone, reader); - if (bytes_read <= k) { - // wanted at least k bytes but got less - return 0; - } - } - - return reader->buffer.bytes.pointer + read_position + k; -} - -static unsigned char *lone_reader_peek(struct lone_lisp *lone, struct lone_reader *reader) -{ - return lone_reader_peek_k(lone, reader, 0); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ The consume(k) function advances the input position by k. │ - │ This progresses through the input, consuming it. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static void lone_reader_consume_k(struct lone_reader *reader, size_t k) -{ - reader->buffer.position.read += k; -} - -static void lone_reader_consume(struct lone_reader *reader) -{ - lone_reader_consume_k(reader, 1); -} - -static int lone_reader_match_byte(unsigned char byte, unsigned char target) -{ - if (target == ' ') { - switch (byte) { - case ' ': - case '\t': - case '\n': - return 1; - default: - return 0; - } - } else if (target == ')' || target == ']' || target == '}') { - return byte == ')' || byte == ']' || byte == '}'; - } else if (target >= '0' && target <= '9') { - return byte >= '0' && byte <= '9'; - } else { - return byte == target; - } -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Analyzes a number and adds it to the tokens list if valid. │ - │ │ - │ ([+-]?[0-9]+)[)]} \n\t] │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_reader_consume_number(struct lone_lisp *lone, struct lone_reader *reader) -{ - unsigned char *current, *start = lone_reader_peek(lone, reader); - if (!start) { return 0; } - size_t end = 0; - - switch (*start) { - case '+': case '-': - lone_reader_consume(reader); - ++end; - break; - default: - break; - } - - if ((current = lone_reader_peek(lone, reader)) && lone_reader_match_byte(*current, '1')) { - lone_reader_consume(reader); - ++end; - } else { return 0; } - - while ((current = lone_reader_peek(lone, reader)) && lone_reader_match_byte(*current, '1')) { - lone_reader_consume(reader); - ++end; - } - - if (current && !lone_reader_match_byte(*current, ')') && !lone_reader_match_byte(*current, ' ')) { return 0; } - - return lone_integer_parse(lone, start, end); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Analyzes a symbol and adds it to the tokens list if valid. │ - │ │ - │ (.*)[)]} \n\t] │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_reader_consume_symbol(struct lone_lisp *lone, struct lone_reader *reader) -{ - unsigned char *current, *start = lone_reader_peek(lone, reader); - if (!start) { return 0; } - size_t end = 0; - - while ((current = lone_reader_peek(lone, reader)) && !lone_reader_match_byte(*current, ')') && !lone_reader_match_byte(*current, ' ')) { - lone_reader_consume(reader); - ++end; - } - - return lone_intern(lone, start, end, true); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Analyzes a string and adds it to the tokens list if valid. │ - │ │ - │ (".*")[)]} \n\t] │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_reader_consume_text(struct lone_lisp *lone, struct lone_reader *reader) -{ - size_t end = 0; - unsigned char *current, *start = lone_reader_peek(lone, reader); - if (!start || *start != '"') { return 0; } - - // skip leading " - ++start; - lone_reader_consume(reader); - - while ((current = lone_reader_peek(lone, reader)) && *current != '"') { - lone_reader_consume(reader); - ++end; - } - - // skip trailing " - ++current; - lone_reader_consume(reader); - - if (!lone_reader_match_byte(*current, ')') && !lone_reader_match_byte(*current, ' ')) { return 0; } - - return lone_text_create(lone, start, end); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Analyzes a single character token, │ - │ characters that the parser deals with specially. │ - │ These include single quotes and opening and closing brackets. │ - │ │ - │ (['()[]{}]) │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_reader_consume_character(struct lone_lisp *lone, struct lone_reader *reader) -{ - unsigned char *bracket = lone_reader_peek(lone, reader); - if (!bracket) { return 0; } - - switch (*bracket) { - case '(': case ')': - case '[': case ']': - case '{': case '}': - case '\'': case '`': - case '.': - lone_reader_consume(reader); - return lone_intern(lone, bracket, 1, true); - default: - return 0; - } -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ The lone lisp lexer receives as input a single lone bytes value │ - │ containing the full source code to be processed and it outputs │ - │ a lone list of each lisp token found in the input. For example: │ - │ │ - │ lex ← lone_bytes = [ (abc ("zxc") ] │ - │ lex → lone_list = { ( → abc → ( → "zxc" → ) } │ - │ │ - │ Note that the list is linear and parentheses are not matched. │ - │ The lexical analysis algorithm can be summarized as follows: │ - │ │ - │ ◦ Skip all whitespace until it finds something │ - │ ◦ Fail if tokens aren't separated by spaces or ) at the end │ - │ ◦ If found sign before digits tokenize signed number │ - │ ◦ If found digit then look for more digits and tokenize │ - │ ◦ If found " then find the next " and tokenize │ - │ ◦ If found ( or ) just tokenize them as is without matching │ - │ ◦ Tokenize everything else unmodified as a symbol │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_lex(struct lone_lisp *lone, struct lone_reader *reader) -{ - struct lone_value *token = 0; - unsigned char *c; - - while ((c = lone_reader_peek(lone, reader))) { - if (lone_reader_match_byte(*c, ' ')) { - lone_reader_consume(reader); - continue; - } else { - unsigned char *c1; - - switch (*c) { - case '+': case '-': - if ((c1 = lone_reader_peek_k(lone, reader, 1)) && lone_reader_match_byte(*c1, '1')) { - token = lone_reader_consume_number(lone, reader); - } else { - token = lone_reader_consume_symbol(lone, reader); - } - break; - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - token = lone_reader_consume_number(lone, reader); - break; - case '"': - token = lone_reader_consume_text(lone, reader); - break; - case '(': case ')': - case '[': case ']': - case '{': case '}': - case '\'': case '`': - case '.': - token = lone_reader_consume_character(lone, reader); - break; - default: - token = lone_reader_consume_symbol(lone, reader); - break; - } - - if (token) { - break; - } else { - goto lex_failed; - } - } - } - - return token; - -lex_failed: - linux_exit(-1); -} - -static struct lone_value *lone_parse(struct lone_lisp *, struct lone_reader *, struct lone_value *); - -static struct lone_value *lone_parse_vector(struct lone_lisp *lone, struct lone_reader *reader) -{ - struct lone_value *vector = lone_vector_create(lone, 32), *value; - size_t i = 0; - - while (1) { - value = lone_lex(lone, reader); - - if (!value) { /* end of input */ reader->error = 1; return 0; } - if (lone_is_symbol(value) && *value->bytes.pointer == ']') { - /* complete vector: [], [ x ], [ x y ] */ - break; - } - - value = lone_parse(lone, reader, value); - - lone_vector_set_value_at(lone, vector, i++, value); - } - - return vector; -} -static struct lone_value *lone_parse_table(struct lone_lisp *lone, struct lone_reader *reader) -{ - struct lone_value *table = lone_table_create(lone, 32, 0), *key, *value; - - while (1) { - key = lone_lex(lone, reader); - - if (!key) { /* end of input */ reader->error = 1; return 0; } - if (lone_is_symbol(key) && *key->bytes.pointer == '}') { - /* complete table: {}, { x y } */ - break; - } - - key = lone_parse(lone, reader, key); - - value = lone_lex(lone, reader); - - if (!value) { /* end of input */ reader->error = 1; return 0; } - if (lone_is_symbol(value) && *value->bytes.pointer == '}') { - /* incomplete table: { x }, { x y z } */ - reader->error = 1; - return 0; - } - - value = lone_parse(lone, reader, value); - - lone_table_set(lone, table, key, value); - } - - return table; -} - -static struct lone_value *lone_parse_list(struct lone_lisp *lone, struct lone_reader *reader) -{ - struct lone_value *list = lone_list_create_nil(lone), *first = list, *prev = 0, *next; - - while (1) { - next = lone_lex(lone, reader); - if (!next) { reader->error = 1; return 0; } - - if (lone_is_symbol(next)) { - if (*next->bytes.pointer == ')') { break; } - else if (*next->bytes.pointer == '.') { - if (!prev) { reader->error = 1; return 0; } - - next = lone_lex(lone, reader); - if (!next) { reader->error = 1; return 0; } - - lone_list_set_rest(prev, lone_parse(lone, reader, next)); - - next = lone_lex(lone, reader); - if (!next || !lone_is_symbol(next) || *next->bytes.pointer != ')') { reader->error = 1; return 0; } - - break; - } - } - - prev = list; - list = lone_list_append(lone, list, lone_parse(lone, reader, next)); - } - - return first; -} - -static struct lone_value *lone_parse_special_character(struct lone_lisp *lone, struct lone_reader *reader, char character) -{ - struct lone_value *symbol, *value, *form; - char *c_string; - - switch (character) { - case '\'': - c_string = "quote"; - break; - case '`': - c_string = "quasiquote"; - break; - default: - /* invalid special character */ linux_exit(-1); - } - - symbol = lone_intern_c_string(lone, c_string); - value = lone_parse(lone, reader, lone_lex(lone, reader)); - form = lone_list_create(lone, value, lone_nil(lone)); - - return lone_list_create(lone, symbol, form); -} - -static struct lone_value *lone_parse(struct lone_lisp *lone, struct lone_reader *reader, struct lone_value *token) -{ - char character; - - if (!token) { return 0; } - - // lexer has already parsed atoms - // parser deals with nested structures - switch (token->type) { - case LONE_SYMBOL: - character = *token->bytes.pointer; - - switch (character) { - case '(': - return lone_parse_list(lone, reader); - case '[': - return lone_parse_vector(lone, reader); - case '{': - return lone_parse_table(lone, reader); - case ')': case ']': case '}': - goto parse_failed; - case '\'': case '`': - return lone_parse_special_character(lone, reader, character); - default: - return token; - } - case LONE_INTEGER: - case LONE_TEXT: - return token; - case LONE_MODULE: - case LONE_FUNCTION: - case LONE_PRIMITIVE: - case LONE_LIST: - case LONE_VECTOR: - case LONE_TABLE: - case LONE_BYTES: - case LONE_POINTER: - /* unexpected value type from lexer */ - goto parse_failed; - } - -parse_failed: - /* parse failed */ linux_exit(-1); -} - -static struct lone_value *lone_read(struct lone_lisp *lone, struct lone_reader *reader) -{ - return lone_parse(lone, reader, lone_lex(lone, reader)); -} - -/* ╭────────────────────────┨ LONE LISP EVALUATOR ┠─────────────────────────╮ - │ │ - │ The heart of the language. This is what actually executes code. │ - │ Currently supports resolving variable references. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_evaluate(struct lone_lisp *, struct lone_value *, struct lone_value *, struct lone_value *); - -static struct lone_value *lone_evaluate_module(struct lone_lisp *lone, struct lone_value *module, struct lone_value *value) -{ - return lone_evaluate(lone, module, module->module.environment, value); -} - -static struct lone_value *lone_evaluate_form_index(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *collection, struct lone_value *arguments) -{ - struct lone_value *(*get)(struct lone_lisp *, struct lone_value *, struct lone_value *); - void (*set)(struct lone_lisp *, struct lone_value *, struct lone_value *, struct lone_value *); - struct lone_value *key, *value; - - switch (collection->type) { - case LONE_VECTOR: - get = lone_vector_get; - set = lone_vector_set; - break; - case LONE_TABLE: - get = lone_table_get; - set = lone_table_set; - break; - case LONE_MODULE: case LONE_FUNCTION: case LONE_PRIMITIVE: - case LONE_BYTES: case LONE_SYMBOL: case LONE_TEXT: - case LONE_LIST: case LONE_INTEGER: case LONE_POINTER: - linux_exit(-1); - } - - if (lone_is_nil(arguments)) { /* need at least the key: (collection) */ linux_exit(-1); } - key = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - if (lone_is_nil(arguments)) { - /* table get: (collection key) */ - return get(lone, collection, lone_evaluate(lone, module, environment, key)); - } else { - /* at least one argument */ - value = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - if (lone_is_nil(arguments)) { - /* table set: (collection key value) */ - set(lone, collection, - lone_evaluate(lone, module, environment, key), - lone_evaluate(lone, module, environment, value)); - return value; - } else { - /* too many arguments given: (collection key value extra) */ - linux_exit(-1); - } - } -} - -static struct lone_value *lone_apply(struct lone_lisp *, struct lone_value *, struct lone_value *, struct lone_value *, struct lone_value *); -static struct lone_value *lone_apply_function(struct lone_lisp *, struct lone_value *, struct lone_value *, struct lone_value *, struct lone_value *); -static struct lone_value *lone_apply_primitive(struct lone_lisp *, struct lone_value *, struct lone_value *, struct lone_value *, struct lone_value *); - -static struct lone_value *lone_evaluate_form(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *list) -{ - struct lone_value *first = lone_list_first(list), *rest = lone_list_rest(list); - - /* apply arguments to a lone value */ - first = lone_evaluate(lone, module, environment, first); - switch (first->type) { - case LONE_FUNCTION: - case LONE_PRIMITIVE: - return lone_apply(lone, module, environment, first, rest); - case LONE_VECTOR: - case LONE_TABLE: - return lone_evaluate_form_index(lone, module, environment, first, rest); - case LONE_MODULE: - case LONE_LIST: - case LONE_SYMBOL: - case LONE_TEXT: - case LONE_BYTES: - case LONE_INTEGER: - case LONE_POINTER: - /* first element not an applicable type */ linux_exit(-1); - } -} - -static struct lone_value *lone_evaluate(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *value) -{ - if (value == 0) { return 0; } - if (lone_is_nil(value)) { return value; } - - switch (value->type) { - case LONE_LIST: - return lone_evaluate_form(lone, module, environment, value); - case LONE_SYMBOL: - return lone_table_get(lone, environment, value); - case LONE_MODULE: - case LONE_FUNCTION: - case LONE_PRIMITIVE: - case LONE_VECTOR: - case LONE_TABLE: - case LONE_INTEGER: - case LONE_POINTER: - case LONE_BYTES: - case LONE_TEXT: - return value; - } -} - -static struct lone_value *lone_evaluate_all(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *list) -{ - struct lone_value *evaluated = lone_list_create_nil(lone), *head; - - for (head = evaluated; !lone_is_nil(list); list = lone_list_rest(list)) { - head = lone_list_append(lone, head, lone_evaluate(lone, module, environment, lone_list_first(list))); - } - - return evaluated; -} - -static struct lone_value *lone_apply(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *applicable, struct lone_value *arguments) -{ - if (!lone_is_applicable(applicable)) { /* given function is not an applicable type */ linux_exit(-1); } - - if (lone_is_function(applicable)) { - return lone_apply_function(lone, module, environment, applicable, arguments); - } else { - return lone_apply_primitive(lone, module, environment, applicable, arguments); - } -} - -static struct lone_value *lone_apply_function(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *function, struct lone_value *arguments) -{ - struct lone_value *new_environment = lone_table_create(lone, 16, function->function.environment), - *names = function->function.arguments, *code = function->function.code, - *value = lone_nil(lone); - - if (function->function.flags.evaluate_arguments) { arguments = lone_evaluate_all(lone, module, environment, arguments); } - - if (function->function.flags.variable_arguments) { - if (lone_is_nil(names) || !lone_is_nil(lone_list_rest(names))) { - /* must have exactly one argument: the list of arguments */ - linux_exit(-1); - } - - lone_table_set(lone, new_environment, lone_list_first(names), arguments); - } else { - while (1) { - if (lone_is_nil(names) != lone_is_nil(arguments)) { - /* argument number mismatch: ((lambda (x) x) 10 20), ((lambda (x y) y) 10) */ - linux_exit(-1); - } else if (lone_is_nil(names) && lone_is_nil(arguments)) { - break; - } - - lone_table_set(lone, new_environment, lone_list_first(names), lone_list_first(arguments)); - - names = lone_list_rest(names); - arguments = lone_list_rest(arguments); - } - } - - while (1) { - if (lone_is_nil(code)) { break; } - value = lone_list_first(code); - value = lone_evaluate(lone, module, new_environment, value); - code = lone_list_rest(code); - } - - if (function->function.flags.evaluate_result) { value = lone_evaluate(lone, module, environment, value); } - - return value; -} - -static struct lone_value *lone_apply_primitive(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *primitive, struct lone_value *arguments) -{ - struct lone_value *result; - if (primitive->primitive.flags.evaluate_arguments) { arguments = lone_evaluate_all(lone, module, environment, arguments); } - result = primitive->primitive.function(lone, module, environment, arguments, primitive->primitive.closure); - if (primitive->primitive.flags.evaluate_result) { result = lone_evaluate(lone, module, environment, result); } - return result; -} - -/* ╭─────────────────────────┨ LONE LISP PRINTER ┠──────────────────────────╮ - │ │ - │ Transforms lone lisp objects into text in order to write it out. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static void lone_print(struct lone_lisp *, struct lone_value *, int); - -static void lone_print_integer(int fd, long n) -{ - static char digits[DECIMAL_DIGITS_PER_LONG + 1]; /* digits, sign */ - char *digit = digits + DECIMAL_DIGITS_PER_LONG; /* work backwards */ - size_t count = 0; - int is_negative; - - if (n < 0) { - is_negative = 1; - n *= -1; - } else { - is_negative = 0; - } - - do { - *--digit = '0' + (n % 10); - n /= 10; - ++count; - } while (n > 0); - - if (is_negative) { - *--digit = '-'; - ++count; - } - - linux_write(fd, digit, count); -} - -static void lone_print_pointer(struct lone_lisp *lone, struct lone_value *pointer, int fd) -{ - if (pointer->pointer.type == LONE_TO_UNKNOWN) { - lone_print_integer(fd, (intptr_t) pointer->pointer.address); - } else { - lone_print(lone, lone_pointer_dereference(lone, pointer), fd); - } -} - -static void lone_print_bytes(struct lone_lisp *lone, struct lone_value *bytes, int fd) -{ - size_t count = bytes->bytes.count; - if (count == 0) { linux_write(fd, "bytes[]", 7); return; } - - static unsigned char hexadecimal[] = "0123456789ABCDEF"; - size_t size = 2 + count * 2; // size required: "0x" + 2 characters per input byte - unsigned char *text = lone_allocate(lone, size); - unsigned char *byte = bytes->bytes.pointer; - size_t i; - - text[0] = '0'; - text[1] = 'x'; - - for (i = 0; i < count; ++i) { - unsigned char low = (byte[i] & 0x0F) >> 0; - unsigned char high = (byte[i] & 0xF0) >> 4; - text[2 + (2 * i + 0)] = hexadecimal[high]; - text[2 + (2 * i + 1)] = hexadecimal[low]; - } - - linux_write(fd, "bytes[", 6); - linux_write(fd, text, size); - linux_write(fd, "]", 1); - - lone_deallocate(lone, text); -} - -static void lone_print_list(struct lone_lisp *lone, struct lone_value *list, int fd) -{ - if (list == 0 || lone_is_nil(list)) { return; } - - struct lone_value *first = list->list.first, - *rest = list->list.rest; - - lone_print(lone, first, fd); - - if (lone_is_list(rest)) { - if (!lone_is_nil(rest)) { - linux_write(fd, " ", 1); - lone_print_list(lone, rest, fd); - } - } else { - linux_write(fd, " . ", 3); - lone_print(lone, rest, fd); - } -} - -static void lone_print_vector(struct lone_lisp *lone, struct lone_value *vector, int fd) -{ - size_t n = vector->vector.count, i; - struct lone_value **values = vector->vector.values; - - if (vector->vector.count == 0) { linux_write(fd, "[]", 2); return; } - - linux_write(fd, "[ ", 2); - - for (i = 0; i < n; ++i) { - lone_print(lone, values[i], fd); - linux_write(fd, " ", 1); - } - - linux_write(fd, "]", 1); -} - -static void lone_print_table(struct lone_lisp *lone, struct lone_value *table, int fd) -{ - size_t n = table->table.capacity, i; - struct lone_table_entry *entries = table->table.entries; - - if (table->table.count == 0) { linux_write(fd, "{}", 2); return; } - - linux_write(fd, "{ ", 2); - - for (i = 0; i < n; ++i) { - struct lone_value *key = entries[i].key, - *value = entries[i].value; - - - if (key) { - lone_print(lone, key, fd); - linux_write(fd, " ", 1); - lone_print(lone, value, fd); - linux_write(fd, " ", 1); - } - } - - linux_write(fd, "}", 1); -} - -static void lone_print_function(struct lone_lisp *lone, struct lone_value *function, int fd) -{ - struct lone_value *arguments = function->function.arguments, - *code = function->function.code; - - linux_write(fd, "(𝛌 ", 6); - lone_print(lone, arguments, fd); - - while (!lone_is_nil(code)) { - linux_write(fd, "\n ", 3); - lone_print(lone, lone_list_first(code), fd); - code = lone_list_rest(code); - } - - linux_write(fd, ")", 1); -} - -static void lone_print_hash_notation(struct lone_lisp *lone, char *descriptor, struct lone_value *value, int fd) -{ - linux_write(fd, "#<", 2); - linux_write(fd, descriptor, lone_c_string_length(descriptor)); - linux_write(fd, " ", 1); - lone_print(lone, value, fd); - linux_write(fd, ">", 1); -} - -static void lone_print(struct lone_lisp *lone, struct lone_value *value, int fd) -{ - if (value == 0) { return; } - if (lone_is_nil(value)) { linux_write(fd, "nil", 3); return; } - - switch (value->type) { - case LONE_MODULE: - lone_print_hash_notation(lone, "module", value->module.name, fd); - break; - case LONE_PRIMITIVE: - lone_print_hash_notation(lone, "primitive", value->primitive.name, fd); - break; - case LONE_FUNCTION: - lone_print_function(lone, value, fd); - break; - case LONE_LIST: - linux_write(fd, "(", 1); - lone_print_list(lone, value, fd); - linux_write(fd, ")", 1); - break; - case LONE_VECTOR: - lone_print_vector(lone, value, fd); - break; - case LONE_TABLE: - lone_print_table(lone, value, fd); - break; - case LONE_BYTES: - lone_print_bytes(lone, value, fd); - break; - case LONE_SYMBOL: - linux_write(fd, value->bytes.pointer, value->bytes.count); - break; - case LONE_TEXT: - linux_write(fd, "\"", 1); - linux_write(fd, value->bytes.pointer, value->bytes.count); - linux_write(fd, "\"", 1); - break; - case LONE_INTEGER: - lone_print_integer(fd, value->integer); - break; - case LONE_POINTER: - lone_print_pointer(lone, value, fd); - break; - } -} - -/* ╭───────────────────┨ LONE LISP PRIMITIVE FUNCTIONS ┠────────────────────╮ - │ │ - │ Lone lisp functions implemented in C. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_primitive_begin(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *value; - - for (value = lone_nil(lone); !lone_is_nil(arguments); arguments = lone_list_rest(arguments)) { - value = lone_list_first(arguments); - value = lone_evaluate(lone, module, environment, value); - } - - return value; -} - -static struct lone_value *lone_primitive_when(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *test; - - if (lone_is_nil(arguments)) { /* test not specified: (when) */ linux_exit(-1); } - test = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - - if (!lone_is_nil(lone_evaluate(lone, module, environment, test))) { - return lone_primitive_begin(lone, module, environment, arguments, closure); - } - - return lone_nil(lone); -} - -static struct lone_value *lone_primitive_unless(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *test; - - if (lone_is_nil(arguments)) { /* test not specified: (unless) */ linux_exit(-1); } - test = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - - if (lone_is_nil(lone_evaluate(lone, module, environment, test))) { - return lone_primitive_begin(lone, module, environment, arguments, closure); - } - - return lone_nil(lone); -} - -static struct lone_value *lone_primitive_if(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *value, *consequent, *alternative = 0; - - if (lone_is_nil(arguments)) { /* test not specified: (if) */ linux_exit(-1); } - value = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - - if (lone_is_nil(arguments)) { /* consequent not specified: (if test) */ linux_exit(-1); } - consequent = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - - if (!lone_is_nil(arguments)) { - alternative = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - if (!lone_is_nil(arguments)) { /* too many values (if test consequent alternative extra) */ linux_exit(-1); } - } - - if (!lone_is_nil(lone_evaluate(lone, module, environment, value))) { - return lone_evaluate(lone, module, environment, consequent); - } else if (alternative) { - return lone_evaluate(lone, module, environment, alternative); - } - - return lone_nil(lone); -} - -static struct lone_value *lone_primitive_let(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *bindings, *first, *second, *rest, *value, *new_environment; - - if (lone_is_nil(arguments)) { /* no variables to bind: (let) */ linux_exit(-1); } - bindings = lone_list_first(arguments); - if (!lone_is_list(bindings)) { /* expected list but got something else: (let 10) */ linux_exit(-1); } - - new_environment = lone_table_create(lone, 8, environment); - - while (1) { - if (lone_is_nil(bindings)) { break; } - first = lone_list_first(bindings); - if (!lone_is_symbol(first)) { /* variable names must be symbols: (let ("x")) */ linux_exit(-1); } - rest = lone_list_rest(bindings); - if (lone_is_nil(rest)) { /* incomplete variable/value list: (let (x 10 y)) */ linux_exit(-1); } - second = lone_list_first(rest); - value = lone_evaluate(lone, module, new_environment, second); - lone_table_set(lone, new_environment, first, value); - bindings = lone_list_rest(rest); - } - - value = lone_nil(lone); - - while (!lone_is_nil(arguments = lone_list_rest(arguments))) { - value = lone_evaluate(lone, module, new_environment, lone_list_first(arguments)); - } - - return value; -} - -static struct lone_value *lone_primitive_set(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *variable, *value; - - if (lone_is_nil(arguments)) { - /* no variable to set: (set) */ - linux_exit(-1); - } - - variable = lone_list_first(arguments); - if (!lone_is_symbol(variable)) { - /* variable names must be symbols: (set 10) */ - linux_exit(-1); - } - - arguments = lone_list_rest(arguments); - if (lone_is_nil(arguments)) { - /* value not specified: (set variable) */ - value = lone_nil(lone); - } else { - /* (set variable value) */ - value = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - } - - if (!lone_is_nil(arguments)) { /* too many arguments */ linux_exit(-1); } - - value = lone_evaluate(lone, module, environment, value); - lone_table_set(lone, environment, variable, value); - - return value; -} - -static struct lone_value *lone_primitive_quote(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - if (!lone_is_nil(lone_list_rest(arguments))) { /* too many arguments: (quote x y) */ linux_exit(-1); } - return lone_list_first(arguments); -} - -static struct lone_value *lone_primitive_quasiquote(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *list, *head, *current, *element, *result, *first, *rest, *unquote, *splice; - bool escaping, splicing; - - if (!lone_is_nil(lone_list_rest(arguments))) { /* too many arguments: (quasiquote x y) */ linux_exit(-1); } - - unquote = lone_intern_c_string(lone, "unquote"); - splice = lone_intern_c_string(lone, "unquote*"); - head = list = lone_list_create_nil(lone); - arguments = lone_list_first(arguments); - - for (current = arguments; !lone_is_nil(current); current = lone_list_rest(current)) { - element = lone_list_first(current); - - if (lone_is_list(element)) { - first = lone_list_first(element); - rest = lone_list_rest(element); - - if (lone_is_equivalent(first, unquote)) { - escaping = true; - splicing = false; - } else if (lone_is_equivalent(first, splice)) { - escaping = true; - splicing = true; - } else { - escaping = false; - splicing = false; - } - - if (escaping) { - first = lone_list_first(rest); - rest = lone_list_rest(rest); - - if (!lone_is_nil(rest)) { /* too many arguments: (quasiquote (unquote x y) (unquote* x y)) */ linux_exit(-1); } - - result = lone_evaluate(lone, module, environment, first); - - if (splicing) { - if (lone_is_list(result)) { - for (/* result */; !lone_is_nil(result); result = lone_list_rest(result)) { - head = lone_list_append(lone, head, lone_list_first(result)); - } - } else { - head = lone_list_append(lone, head, result); - } - - } else { - head = lone_list_append(lone, head, result); - } - - } else { - head = lone_list_append(lone, head, element); - } - - } else { - head = lone_list_append(lone, head, element); - } - } - - return list; -} - -static struct lone_value *lone_primitive_lambda_with_flags(struct lone_lisp *lone, struct lone_value *environment, struct lone_value *arguments, struct lone_function_flags flags) -{ - struct lone_value *bindings, *code; - - bindings = lone_list_first(arguments); - if (!lone_is_list(bindings)) { /* parameters not a list: (lambda 10) */ linux_exit(-1); } - - code = lone_list_rest(arguments); - - return lone_function_create(lone, bindings, code, environment, flags); -} - -static struct lone_value *lone_primitive_lambda(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_function_flags flags = { - .evaluate_arguments = 1, - .evaluate_result = 0, - .variable_arguments = 0, - }; - - return lone_primitive_lambda_with_flags(lone, environment, arguments, flags); -} - -static struct lone_value *lone_primitive_lambda_bang(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_function_flags flags = { - .evaluate_arguments = 0, - .evaluate_result = 0, - .variable_arguments = 0, - }; - - return lone_primitive_lambda_with_flags(lone, environment, arguments, flags); -} - -static struct lone_value *lone_primitive_lambda_star(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_function_flags flags = { - .evaluate_arguments = 1, - .evaluate_result = 0, - .variable_arguments = 1, - }; - - return lone_primitive_lambda_with_flags(lone, environment, arguments, flags); -} - -static struct lone_value *lone_apply_predicate(struct lone_lisp *lone, struct lone_value *arguments, lone_predicate function) -{ - if (lone_is_nil(arguments) || !lone_is_nil(lone_list_rest(arguments))) { /* predicates accept exactly one argument */ linux_exit(-1); } - return function(lone_list_first(arguments)) ? lone_true(lone) : lone_nil(lone); -} - -static struct lone_value *lone_primitive_is_list(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_predicate(lone, arguments, lone_is_list); -} - -static struct lone_value *lone_primitive_is_vector(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_predicate(lone, arguments, lone_is_vector); -} - -static struct lone_value *lone_primitive_is_table(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_predicate(lone, arguments, lone_is_table); -} - -static struct lone_value *lone_primitive_is_symbol(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_predicate(lone, arguments, lone_is_symbol); -} - -static struct lone_value *lone_primitive_is_text(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_predicate(lone, arguments, lone_is_text); -} - -static struct lone_value *lone_primitive_is_integer(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_predicate(lone, arguments, lone_is_integer); -} - -static struct lone_value *lone_apply_comparator(struct lone_lisp *lone, struct lone_value *arguments, lone_comparator function) -{ - struct lone_value *argument, *next; - - while (1) { - if (lone_is_nil(arguments)) { break; } - argument = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - next = lone_list_first(arguments); - - if (next && !function(argument, next)) { return lone_nil(lone); } - } - - return lone_true(lone); -} - -static struct lone_value *lone_primitive_is_identical(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_comparator(lone, arguments, lone_is_identical); -} - -static struct lone_value *lone_primitive_is_equivalent(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_comparator(lone, arguments, lone_is_equivalent); -} - -static struct lone_value *lone_primitive_is_equal(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_comparator(lone, arguments, lone_is_equal); -} - -static struct lone_value *lone_primitive_print(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - while (!lone_is_nil(arguments)) { - lone_print(lone, lone_list_first(arguments), 1); - linux_write(1, "\n", 1); - arguments = lone_list_rest(arguments); - } - - return lone_nil(lone); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Built-in mathematical and numeric operations. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_primitive_integer_operation(struct lone_lisp *lone, struct lone_value *arguments, char operation, long accumulator) -{ - struct lone_value *argument; - - if (lone_is_nil(arguments)) { /* wasn't given any arguments to operate on: (+), (-), (*) */ goto return_accumulator; } - - do { - argument = lone_list_first(arguments); - if (!lone_is_integer(argument)) { /* argument is not a number */ linux_exit(-1); } - - switch (operation) { - case '+': accumulator += argument->integer; break; - case '-': accumulator -= argument->integer; break; - case '*': accumulator *= argument->integer; break; - default: /* invalid primitive integer operation */ linux_exit(-1); - } - - arguments = lone_list_rest(arguments); - - } while (!lone_is_nil(arguments)); - -return_accumulator: - return lone_integer_create(lone, accumulator); -} - -static struct lone_value *lone_primitive_add(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_primitive_integer_operation(lone, arguments, '+', 0); -} - -static struct lone_value *lone_primitive_subtract(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *first; - long accumulator; - - if (!lone_is_nil(arguments) && !lone_is_nil(lone_list_rest(arguments))) { - /* at least two arguments, set initial value to the first argument: (- 100 58) */ - first = lone_list_first(arguments); - if (!lone_is_integer(first)) { /* argument is not a number */ linux_exit(-1); } - accumulator = first->integer; - arguments = lone_list_rest(arguments); - } else { - accumulator = 0; - } - - return lone_primitive_integer_operation(lone, arguments, '-', accumulator); -} - -static struct lone_value *lone_primitive_multiply(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_primitive_integer_operation(lone, arguments, '*', 1); -} - -static struct lone_value *lone_primitive_divide(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *dividend, *divisor; - - if (lone_is_nil(arguments)) { /* at least the dividend is required, (/) is invalid */ linux_exit(-1); } - dividend = lone_list_first(arguments); - if (!lone_is_integer(dividend)) { /* can't divide non-numbers: (/ "not a number") */ linux_exit(-1); } - arguments = lone_list_rest(arguments); - - if (lone_is_nil(arguments)) { - /* not given a divisor, return 1/x instead: (/ 2) = 1/2 */ - return lone_integer_create(lone, 1 / dividend->integer); - } else { - /* (/ x a b c ...) = x / (a * b * c * ...) */ - divisor = lone_primitive_integer_operation(lone, arguments, '*', 1); - return lone_integer_create(lone, dividend->integer / divisor->integer); - } -} - -static struct lone_value *lone_primitive_is_less_than(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_comparator(lone, arguments, lone_integer_is_less_than); -} - -static struct lone_value *lone_primitive_is_less_than_or_equal_to(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_comparator(lone, arguments, lone_integer_is_less_than_or_equal_to); -} - -static struct lone_value *lone_primitive_is_greater_than(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_comparator(lone, arguments, lone_integer_is_greater_than); -} - -static struct lone_value *lone_primitive_is_greater_than_or_equal_to(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_apply_comparator(lone, arguments, lone_integer_is_greater_than_or_equal_to); -} - -static struct lone_value *lone_primitive_sign(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *value; - if (lone_is_nil(arguments)) { /* no arguments: (sign) */ linux_exit(-1); } - value = lone_list_first(arguments); - if (!lone_is_nil(lone_list_rest(arguments))) { /* too many arguments: (sign 1 2 3) */ linux_exit(-1); } - - if (lone_is_integer(value)) { - return lone_integer_create(lone, value->integer > 0? 1 : value->integer < 0? -1 : 0); - } else { - linux_exit(-1); - } -} - -static struct lone_value *lone_primitive_is_zero(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *value = lone_primitive_sign(lone, module, environment, arguments, closure); - if (lone_is_integer(value) && value->integer == 0) { return value; } - else { return lone_nil(lone); } -} - -static struct lone_value *lone_primitive_is_positive(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *value = lone_primitive_sign(lone, module, environment, arguments, closure); - if (lone_is_integer(value) && value->integer > 0) { return value; } - else { return lone_nil(lone); } -} - -static struct lone_value *lone_primitive_is_negative(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *value = lone_primitive_sign(lone, module, environment, arguments, closure); - if (lone_is_integer(value) && value->integer < 0) { return value; } - else { return lone_nil(lone); } -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Text operations. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_primitive_join(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_text_transfer_bytes(lone, lone_join(lone, lone_list_first(arguments), lone_list_rest(arguments), lone_is_text), true); -} - -static struct lone_value *lone_primitive_concatenate(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_text_transfer_bytes(lone, lone_concatenate(lone, arguments, lone_is_text), true); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ List operations. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_primitive_construct(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *first, *rest; - - if (lone_is_nil(arguments)) { /* no arguments given: (construct) */ linux_exit(-1); } - - first = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - if (lone_is_nil(arguments)) { /* only one argument given: (construct first) */ linux_exit(-1); } - - rest = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - if (!lone_is_nil(arguments)) { /* more than two arguments given: (construct first rest extra) */ linux_exit(-1); } - - return lone_list_create(lone, first, rest); -} - -static struct lone_value *lone_primitive_first(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *argument; - if (lone_is_nil(arguments)) { linux_exit(-1); } - argument = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - if (lone_is_nil(argument)) { linux_exit(-1); } - if (!lone_is_nil(arguments)) { linux_exit(-1); } - return lone_list_first(argument); -} - -static struct lone_value *lone_primitive_rest(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *argument; - if (lone_is_nil(arguments)) { linux_exit(-1); } - argument = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - if (lone_is_nil(argument)) { linux_exit(-1); } - if (!lone_is_nil(arguments)) { linux_exit(-1); } - return lone_list_rest(argument); -} - -static struct lone_value *lone_primitive_list_map(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *function, *list, *results, *head; - - if (lone_is_nil(arguments)) { /* arguments not given */ linux_exit(-1); } - function = lone_list_first(arguments); - if (!lone_is_applicable(function)) { /* not given an applicable value */ linux_exit(-1); } - arguments = lone_list_rest(arguments); - list = lone_list_first(arguments); - if (!lone_is_list(list)) { /* can only map functions to lists */ linux_exit(-1); } - arguments = lone_list_rest(arguments); - if (!lone_is_nil(arguments)) { /* too many arguments given */ linux_exit(-1); } - - results = lone_list_create_nil(lone); - - for (head = results; !lone_is_nil(list); list = lone_list_rest(list)) { - arguments = lone_list_create(lone, lone_list_first(list), lone_nil(lone)); - head = lone_list_append(lone, head, lone_apply(lone, module, environment, function, arguments)); - } - - return results; -} - -static struct lone_value *lone_primitive_list_reduce(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *function, *list, *result; - - if (lone_is_nil(arguments)) { /* arguments not given */ linux_exit(-1); } - function = lone_list_first(arguments); - if (!lone_is_applicable(function)) { /* not given an applicable value */ linux_exit(-1); } - arguments = lone_list_rest(arguments); - result = lone_list_first(arguments); - arguments = lone_list_rest(arguments); - list = lone_list_first(arguments); - if (!lone_is_list(list)) { /* can only map functions to lists */ linux_exit(-1); } - arguments = lone_list_rest(arguments); - if (!lone_is_nil(arguments)) { /* too many arguments given */ linux_exit(-1); } - - for (/* list */; !lone_is_nil(list); list = lone_list_rest(list)) { - arguments = lone_list_build(lone, 2, result, lone_list_first(list)); - result = lone_apply(lone, module, environment, function, arguments); - } - - return result; -} - -static struct lone_value *lone_primitive_flatten(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - return lone_list_flatten(lone, arguments); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Module importing, exporting and loading operations. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -struct lone_import_specification { - struct lone_value *module; /* module value to import from */ - struct lone_value *symbols; /* list of symbols to import */ - struct lone_value *environment; /* environment to import symbols to */ - - bool prefixed; /* whether to prefix symbols */ -}; - -static struct lone_value *lone_prefix_module_name(struct lone_lisp *lone, struct lone_value *module, struct lone_value *symbol) -{ - struct lone_value *arguments = lone_list_flatten(lone, lone_list_build(lone, 2, module->module.name, symbol)), - *dot = lone_intern_c_string(lone, "."); - - return lone_symbol_transfer_bytes(lone, lone_join(lone, dot, arguments, lone_has_bytes), true); -} - -static void lone_import_specification(struct lone_lisp *lone, struct lone_import_specification *spec) -{ - size_t i; - struct lone_value *module = spec->module, *symbols = spec->symbols, *environment = spec->environment, *exports = module->module.exports, - *symbol, *value; - - /* bind either the exported or the specified symbols: (import (module)), (import (module x f)) */ - for (i = 0; i < symbols->vector.count; ++i) { - symbol = lone_vector_get_value_at(lone, symbols, i); - if (!lone_is_symbol(symbol)) { /* name not a symbol: (import (module 10)) */ linux_exit(-1); } - - if (symbols != exports && !lone_vector_contains(exports, symbol)) { - /* attempt to import private symbol */ linux_exit(-1); - } - - value = lone_table_get(lone, module->module.environment, symbol); - - if (spec->prefixed) { - symbol = lone_prefix_module_name(lone, spec->module, symbol); - } - - lone_table_set(lone, environment, symbol, value); - } -} - -static struct lone_value *lone_module_load(struct lone_lisp *lone, struct lone_value *name); - -static void lone_primitive_import_form(struct lone_lisp *lone, struct lone_import_specification *spec, struct lone_value *argument) -{ - struct lone_value *name; - - if (lone_is_nil(argument)) { /* nothing to import: (import ()) */ linux_exit(-1); } - - switch (argument->type) { - case LONE_SYMBOL: - /* (import module) */ - name = argument; - argument = lone_nil(lone); - break; - case LONE_LIST: - /* (import (module)), (import (module symbol)) */ - name = lone_list_first(argument); - argument = lone_list_rest(argument); - break; - case LONE_MODULE: - case LONE_FUNCTION: case LONE_PRIMITIVE: - case LONE_TEXT: case LONE_BYTES: - case LONE_VECTOR: case LONE_TABLE: - case LONE_INTEGER: case LONE_POINTER: - /* not a supported import argument type */ linux_exit(-1); - } - - spec->module = lone_module_load(lone, name); - if (lone_is_nil(spec->module)) { /* module not found: (import non-existent), (import (non-existent)) */ linux_exit(-1); } - - spec->symbols = lone_is_nil(argument)? spec->module->module.exports : lone_list_to_vector(lone, argument); - - lone_import_specification(lone, spec); -} - -static struct lone_value *lone_primitive_import(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_import_specification spec; - struct lone_value *prefixed = lone_intern_c_string(lone, "prefixed"), - *unprefixed = lone_intern_c_string(lone, "unprefixed"), - *argument; - - if (lone_is_nil(arguments)) { /* nothing to import: (import) */ linux_exit(-1); } - - spec.environment = environment; - spec.prefixed = false; - - for (/* argument */; !lone_is_nil(arguments); arguments = lone_list_rest(arguments)) { - argument = lone_list_first(arguments); - if (lone_is_list(argument)) { - lone_primitive_import_form(lone, &spec, argument); - } else if (lone_is_symbol(argument)) { - if (lone_is_equivalent(argument, prefixed)) { spec.prefixed = true; } - else if (lone_is_equivalent(argument, unprefixed)) { spec.prefixed = false; } - } else { - /* invalid import argument */ linux_exit(-1); - } - } - - return lone_nil(lone); -} - -static void lone_export(struct lone_lisp *lone, struct lone_value *module, struct lone_value *symbol) -{ - if (!lone_is_symbol(symbol)) { /* only symbols can be exported */ linux_exit(-1); } - lone_vector_push(lone, module->module.exports, symbol); -} - -static void lone_set_and_export(struct lone_lisp *lone, struct lone_value *module, struct lone_value *symbol, struct lone_value *value) -{ - lone_export(lone, module, symbol); - lone_table_set(lone, module->module.environment, symbol, value); -} - -static struct lone_value *lone_primitive_export(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) -{ - struct lone_value *head, *symbol; - - for (head = arguments; !lone_is_nil(head); head = lone_list_rest(head)) { - symbol = lone_list_first(head); - - lone_export(lone, module, symbol); - } - - return lone_nil(lone); -} - -/* ╭────────────────────────────────────────────────────────────────────────╮ - │ │ - │ Linux primitive functions for issuing system calls. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static inline long lone_value_to_linux_system_call_number(struct lone_lisp *lone, struct lone_value *linux_system_call_table, struct lone_value *value) -{ - switch (value->type) { - case LONE_INTEGER: - return value->integer; - case LONE_BYTES: - case LONE_TEXT: - case LONE_SYMBOL: - return lone_table_get(lone, linux_system_call_table, value)->integer; - case LONE_MODULE: - case LONE_FUNCTION: - case LONE_PRIMITIVE: - case LONE_LIST: - case LONE_VECTOR: - case LONE_TABLE: - case LONE_POINTER: - linux_exit(-1); - } -} - -static inline long lone_value_to_linux_system_call_argument(struct lone_value *value) -{ - switch (value->type) { - case LONE_INTEGER: return value->integer; - case LONE_POINTER: return (long) value->pointer.address; - case LONE_BYTES: case LONE_TEXT: case LONE_SYMBOL: return (long) value->bytes.pointer; - case LONE_PRIMITIVE: return (long) value->primitive.function; - case LONE_FUNCTION: case LONE_LIST: case LONE_VECTOR: case LONE_TABLE: case LONE_MODULE: linux_exit(-1); - } -} - -static struct lone_value *lone_primitive_linux_system_call(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *linux_system_call_table) -{ - struct lone_value *argument; - long result, number, args[6]; - unsigned char i; - - if (lone_is_nil(arguments)) { /* need at least the system call number */ linux_exit(-1); } - argument = lone_list_first(arguments); - number = lone_value_to_linux_system_call_number(lone, linux_system_call_table, argument); - arguments = lone_list_rest(arguments); - - for (i = 0; i < 6; ++i) { - if (lone_is_nil(arguments)) { - args[i] = 0; - } else { - argument = lone_list_first(arguments); - args[i] = lone_value_to_linux_system_call_argument(argument); - arguments = lone_list_rest(arguments); - } - } - - if (!lone_is_nil(arguments)) { /* too many arguments given */ linux_exit(-1); } - - result = system_call_6(number, args[0], args[1], args[2], args[3], args[4], args[5]); - - return lone_integer_create(lone, result); -} - -/* ╭─────────────────────────┨ LONE LINUX PROCESS ┠─────────────────────────╮ - │ │ - │ Code to access all the parameters Linux passes to its processes. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -struct auxiliary { - long type; - union { - char *c_string; - void *pointer; - long integer; - } as; -}; - -static struct lone_bytes lone_get_auxiliary_random_bytes(struct auxiliary *value) -{ - struct lone_bytes random = { 0, 0 }; - - for (/* value */; value->type != AT_NULL; ++value) { - if (value->type == AT_RANDOM) { - random.pointer = value->as.pointer; - random.count = 16; - } - } - - return random; -} - -static void lone_auxiliary_value_to_table(struct lone_lisp *lone, struct lone_value *table, struct auxiliary *auxiliary_value) -{ - struct lone_value *key, *value; - switch (auxiliary_value->type) { - case AT_BASE_PLATFORM: - key = lone_intern_c_string(lone, "base-platform"); - value = lone_text_create_from_c_string(lone, auxiliary_value->as.c_string); - break; - case AT_PLATFORM: - key = lone_intern_c_string(lone, "platform"); - value = lone_text_create_from_c_string(lone, auxiliary_value->as.c_string); - break; - case AT_HWCAP: - key = lone_intern_c_string(lone, "hardware-capabilities"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - case AT_HWCAP2: - key = lone_intern_c_string(lone, "hardware-capabilities-2"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - case AT_FLAGS: - key = lone_intern_c_string(lone, "flags"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - case AT_NOTELF: - key = lone_intern_c_string(lone, "not-ELF"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - case AT_BASE: - key = lone_intern_c_string(lone, "interpreter-base-address"); - value = lone_pointer_create(lone, auxiliary_value->as.pointer, LONE_TO_UNKNOWN); - break; - case AT_ENTRY: - key = lone_intern_c_string(lone, "entry-point"); - value = lone_pointer_create(lone, auxiliary_value->as.pointer, LONE_TO_UNKNOWN); - break; - case AT_SYSINFO_EHDR: - key = lone_intern_c_string(lone, "vDSO"); - value = lone_pointer_create(lone, auxiliary_value->as.pointer, LONE_TO_UNKNOWN); - break; - case AT_PHDR: - key = lone_intern_c_string(lone, "program-headers-address"); - value = lone_pointer_create(lone, auxiliary_value->as.pointer, LONE_TO_UNKNOWN); - break; - case AT_PHENT: - key = lone_intern_c_string(lone, "program-headers-entry-size"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - case AT_PHNUM: - key = lone_intern_c_string(lone, "program-headers-count"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - case AT_EXECFN: - key = lone_intern_c_string(lone, "executable-file-name"); - value = lone_text_create_from_c_string(lone, auxiliary_value->as.c_string); - break; - case AT_EXECFD: - key = lone_intern_c_string(lone, "executable-file-descriptor"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - case AT_UID: - key = lone_intern_c_string(lone, "user-id"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - case AT_EUID: - key = lone_intern_c_string(lone, "effective-user-id"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - case AT_GID: - key = lone_intern_c_string(lone, "group-id"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - case AT_EGID: - key = lone_intern_c_string(lone, "effective-group-id"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - case AT_PAGESZ: - key = lone_intern_c_string(lone, "page-size"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; -#ifdef AT_MINSIGSTKSZ - case AT_MINSIGSTKSZ: - key = lone_intern_c_string(lone, "minimum-signal-delivery-stack-size"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; -#endif - case AT_CLKTCK: - key = lone_intern_c_string(lone, "clock-tick"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - case AT_RANDOM: - key = lone_intern_c_string(lone, "random"); - value = lone_bytes_create(lone, auxiliary_value->as.pointer, 16); - break; - case AT_SECURE: - key = lone_intern_c_string(lone, "secure"); - value = lone_integer_create(lone, auxiliary_value->as.integer); - break; - default: - key = lone_intern_c_string(lone, "unknown"); - value = lone_list_create(lone, - lone_integer_create(lone, auxiliary_value->type), - lone_integer_create(lone, auxiliary_value->as.integer)); - } - - lone_table_set(lone, table, key, value); -} - -static struct lone_value *lone_auxiliary_vector_to_table(struct lone_lisp *lone, struct auxiliary *auxiliary_values) -{ - struct lone_value *table = lone_table_create(lone, 32, 0); - size_t i; - - for (i = 0; auxiliary_values[i].type != AT_NULL; ++i) { - lone_auxiliary_value_to_table(lone, table, &auxiliary_values[i]); - } - - return table; -} - -static struct lone_value *lone_environment_to_table(struct lone_lisp *lone, char **c_strings) -{ - struct lone_value *table = lone_table_create(lone, 64, 0), *key, *value; - char *c_string_key, *c_string_value, *c_string; - - for (/* c_strings */; *c_strings; ++c_strings) { - c_string = *c_strings; - c_string_key = c_string; - c_string_value = ""; - - while (*c_string++) { - if (*c_string == '=') { - *c_string = '\0'; - c_string_value = c_string + 1; - break; - } - } - - key = lone_text_create_from_c_string(lone, c_string_key); - value = lone_text_create_from_c_string(lone, c_string_value); - lone_table_set(lone, table, key, value); - } - - return table; -} - -static struct lone_value *lone_arguments_to_list(struct lone_lisp *lone, int count, char **c_strings) -{ - struct lone_value *arguments = lone_list_create_nil(lone), *head; - int i; - - for (i = 0, head = arguments; i < count; ++i) { - head = lone_list_append(lone, head, lone_text_create_from_c_string(lone, c_strings[i])); - } - - return arguments; -} - -static void lone_fill_linux_system_call_table(struct lone_lisp *lone, struct lone_value *linux_system_call_table) -{ - size_t i; - - static struct linux_system_call { - char *symbol; - int number; - } linux_system_calls[] = { - - /* huge generated array initializer with all the system calls found on the host platform */ - #include LONE_NR_SOURCE - - }; - - for (i = 0; i < (sizeof(linux_system_calls)/sizeof(linux_system_calls[0])); ++i) { - lone_table_set(lone, linux_system_call_table, - lone_intern_c_string(lone, linux_system_calls[i].symbol), - lone_integer_create(lone, linux_system_calls[i].number)); - } -} - -/* ╭─────────────────────────┨ LONE LISP MODULES ┠──────────────────────────╮ - │ │ - │ Built-in modules containing essential functionality. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -static struct lone_value *lone_module_null(struct lone_lisp *lone) -{ - return lone->modules.null; -} - -static struct lone_value *lone_module_name_to_key(struct lone_lisp *lone, struct lone_value *name) -{ - struct lone_value *head; - - switch (name->type) { - case LONE_SYMBOL: - return lone_list_create(lone, name, lone_nil(lone)); - case LONE_LIST: - for (head = name; !lone_is_nil(head); head = lone_list_rest(head)) { - if (!lone_is_symbol(lone_list_first(head))) { - linux_exit(-1); - } - } - return name; - case LONE_MODULE: - return lone_module_name_to_key(lone, name->module.name); - case LONE_TEXT: case LONE_BYTES: - case LONE_FUNCTION: case LONE_PRIMITIVE: - case LONE_VECTOR: case LONE_TABLE: - case LONE_INTEGER: case LONE_POINTER: - linux_exit(-1); - } -} - -static struct lone_value *lone_module_for_name(struct lone_lisp *lone, struct lone_value *name, bool *not_found) -{ - struct lone_value *module; - - name = lone_module_name_to_key(lone, name); - module = lone_table_get(lone, lone->modules.loaded, name); - *not_found = false; - - if (lone_is_nil(module)) { - module = lone_module_create(lone, name); - lone_table_set(lone, lone->modules.loaded, name, module); - *not_found = true; - } - - return module; -} - -static int lone_module_search(struct lone_lisp *lone, struct lone_value *symbols) -{ - struct lone_value *slash = lone_intern_c_string(lone, "/"), *ln = lone_intern_c_string(lone, ".ln"); - struct lone_value *arguments, *package, *search_path; - unsigned char *path; - size_t i; - long result; - - symbols = lone_module_name_to_key(lone, symbols); - package = lone_list_first(symbols); - - for (i = 0; i < lone->modules.path->vector.count; ++i) { - search_path = lone->modules.path->vector.values[i]; - arguments = lone_list_build(lone, 3, search_path, package, symbols); - arguments = lone_list_flatten(lone, arguments); - arguments = lone_text_transfer_bytes(lone, lone_join(lone, slash, arguments, lone_has_bytes), true); - arguments = lone_list_build(lone, 2, arguments, ln); - path = lone_concatenate(lone, arguments, lone_has_bytes).pointer; - - result = linux_openat(AT_FDCWD, path, O_RDONLY | O_CLOEXEC); - - lone_deallocate(lone, path); - - switch (result) { - case -ENOENT: - case -EACCES: case -EPERM: - case -ENOTDIR: case -EISDIR: - case -EINVAL: case -ENAMETOOLONG: - case -EMFILE: case -ENFILE: - case -ELOOP: - continue; - case -ENOMEM: case -EFAULT: - linux_exit(-1); - } - - return (int) result; - } - - linux_exit(-1); /* module not found */ -} - -static void lone_module_load_from_file_descriptor(struct lone_lisp *lone, struct lone_value *module, int file_descriptor) -{ - struct lone_value *value; - struct lone_reader reader; - - lone_reader_initialize(lone, &reader, LONE_BUFFER_SIZE, file_descriptor); - - while (1) { - value = lone_read(lone, &reader); - if (!value) { if (reader.error) { linux_exit(-1); } else { break; } } - - value = lone_evaluate_module(lone, module, value); - } - - lone_reader_finalize(lone, &reader); - lone_garbage_collector(lone); -} - -static struct lone_value *lone_module_load(struct lone_lisp *lone, struct lone_value *name) -{ - struct lone_value *module; - bool not_found; - int file_descriptor; - - module = lone_module_for_name(lone, name, ¬_found); - - if (not_found) { - file_descriptor = lone_module_search(lone, name); - lone_module_load_from_file_descriptor(lone, module, file_descriptor); - linux_close(file_descriptor); - } - - return module; -} - -static void lone_builtin_module_linux_initialize(struct lone_lisp *lone, int argc, char **argv, char **envp, struct auxiliary *auxv) -{ - struct lone_value *name = lone_module_name_to_key(lone, lone_intern_c_string(lone, "linux")), - *module = lone_module_create(lone, name), - *linux_system_call_table = lone_table_create(lone, 1024, 0), - *count, *arguments, *environment, *auxiliary_values, - *primitive; - - struct lone_function_flags flags = { .evaluate_arguments = true, .evaluate_result = false, .variable_arguments = true }; - primitive = lone_primitive_create(lone, "linux_system_call", lone_primitive_linux_system_call, linux_system_call_table, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "system-call"), primitive); - - lone_set_and_export(lone, module, lone_intern_c_string(lone, "system-call-table"), linux_system_call_table); - - lone_fill_linux_system_call_table(lone, linux_system_call_table); - - count = lone_integer_create(lone, argc); - arguments = lone_arguments_to_list(lone, argc, argv); - environment = lone_environment_to_table(lone, envp); - auxiliary_values = lone_auxiliary_vector_to_table(lone, auxv); - - lone_set_and_export(lone, module, lone_intern_c_string(lone, "argument-count"), count); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "arguments"), arguments); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "environment"), environment); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "auxiliary-values"), auxiliary_values); - - lone_table_set(lone, lone->modules.loaded, name, module); -} - -static void lone_builtin_module_math_initialize(struct lone_lisp *lone) -{ - struct lone_value *name = lone_module_name_to_key(lone, lone_intern_c_string(lone, "math")), - *module = lone_module_create(lone, name), - *primitive; - - struct lone_function_flags flags = { .evaluate_arguments = true, .evaluate_result = false, .variable_arguments = true }; - - primitive = lone_primitive_create(lone, "add", lone_primitive_add, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "+"), primitive); - - primitive = lone_primitive_create(lone, "subtract", lone_primitive_subtract, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "-"), primitive); - - primitive = lone_primitive_create(lone, "multiply", lone_primitive_multiply, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "*"), primitive); - - primitive = lone_primitive_create(lone, "divide", lone_primitive_divide, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "/"), primitive); - - primitive = lone_primitive_create(lone, "is_less_than", lone_primitive_is_less_than, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "<"), primitive); - - primitive = lone_primitive_create(lone, "is_less_than_or_equal_to", lone_primitive_is_less_than_or_equal_to, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "<="), primitive); - - primitive = lone_primitive_create(lone, "is_greater_than", lone_primitive_is_greater_than, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, ">"), primitive); - - primitive = lone_primitive_create(lone, "is_greater_than_or_equal_to", lone_primitive_is_greater_than_or_equal_to, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, ">="), primitive); - - primitive = lone_primitive_create(lone, "sign", lone_primitive_sign, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "sign"), primitive); - - primitive = lone_primitive_create(lone, "is_zero", lone_primitive_is_zero, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "zero?"), primitive); - - primitive = lone_primitive_create(lone, "is_positive", lone_primitive_is_positive, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "positive?"), primitive); - - primitive = lone_primitive_create(lone, "is_negative", lone_primitive_is_negative, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "negative?"), primitive); - - lone_table_set(lone, lone->modules.loaded, name, module); -} - -static void lone_builtin_module_text_initialize(struct lone_lisp *lone) -{ - struct lone_value *name = lone_module_name_to_key(lone, lone_intern_c_string(lone, "text")), - *module = lone_module_create(lone, name), - *primitive; - - struct lone_function_flags flags = { .evaluate_arguments = true, .evaluate_result = false, .variable_arguments = true }; - - primitive = lone_primitive_create(lone, "join", lone_primitive_join, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "join"), primitive); - - primitive = lone_primitive_create(lone, "concatenate", lone_primitive_concatenate, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "concatenate"), primitive); - - lone_table_set(lone, lone->modules.loaded, name, module); -} - -static void lone_builtin_module_list_initialize(struct lone_lisp *lone) -{ - struct lone_value *name = lone_module_name_to_key(lone, lone_intern_c_string(lone, "list")), - *module = lone_module_create(lone, name), - *primitive; - - struct lone_function_flags flags = { .evaluate_arguments = true, .evaluate_result = false, .variable_arguments = true }; - - primitive = lone_primitive_create(lone, "construct", lone_primitive_construct, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "construct"), primitive); - - primitive = lone_primitive_create(lone, "first", lone_primitive_first, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "first"), primitive); - - primitive = lone_primitive_create(lone, "rest", lone_primitive_rest, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "rest"), primitive); - - primitive = lone_primitive_create(lone, "map", lone_primitive_list_map, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "map"), primitive); - - primitive = lone_primitive_create(lone, "reduce", lone_primitive_list_reduce, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "reduce"), primitive); - - primitive = lone_primitive_create(lone, "flatten", lone_primitive_flatten, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "flatten"), primitive); - - lone_table_set(lone, lone->modules.loaded, name, module); -} - -static void lone_builtin_module_lone_initialize(struct lone_lisp *lone) -{ - struct lone_value *name = lone_module_name_to_key(lone, lone_intern_c_string(lone, "lone")), - *module = lone_module_create(lone, name), - *primitive; - - struct lone_function_flags flags = { .evaluate_arguments = false, .evaluate_result = false, .variable_arguments = true }; - - primitive = lone_primitive_create(lone, "begin", lone_primitive_begin, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "begin"), primitive); - - primitive = lone_primitive_create(lone, "when", lone_primitive_when, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "when"), primitive); - - primitive = lone_primitive_create(lone, "unless", lone_primitive_unless, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "unless"), primitive); - - primitive = lone_primitive_create(lone, "if", lone_primitive_if, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "if"), primitive); - - primitive = lone_primitive_create(lone, "let", lone_primitive_let, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "let"), primitive); - - primitive = lone_primitive_create(lone, "set", lone_primitive_set, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "set"), primitive); - - primitive = lone_primitive_create(lone, "quote", lone_primitive_quote, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "quote"), primitive); - - primitive = lone_primitive_create(lone, "quasiquote", lone_primitive_quasiquote, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "quasiquote"), primitive); - - primitive = lone_primitive_create(lone, "lambda", lone_primitive_lambda, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "lambda"), primitive); - - primitive = lone_primitive_create(lone, "lambda_bang", lone_primitive_lambda_bang, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "lambda!"), primitive); - - primitive = lone_primitive_create(lone, "lambda_star", lone_primitive_lambda_star, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "lambda*"), primitive); - - flags = (struct lone_function_flags) { .evaluate_arguments = true, .evaluate_result = false, .variable_arguments = true }; - - primitive = lone_primitive_create(lone, "print", lone_primitive_print, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "print"), primitive); - - primitive = lone_primitive_create(lone, "is_list", lone_primitive_is_list, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "list?"), primitive); - - primitive = lone_primitive_create(lone, "is_vector", lone_primitive_is_vector, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "vector?"), primitive); - - primitive = lone_primitive_create(lone, "is_table", lone_primitive_is_table, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "table?"), primitive); - - primitive = lone_primitive_create(lone, "is_symbol", lone_primitive_is_symbol, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "symbol?"), primitive); - - primitive = lone_primitive_create(lone, "is_text", lone_primitive_is_text, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "text?"), primitive); - - primitive = lone_primitive_create(lone, "is_integer", lone_primitive_is_integer, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "integer?"), primitive); - - primitive = lone_primitive_create(lone, "is_identical", lone_primitive_is_identical, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "identical?"), primitive); - - primitive = lone_primitive_create(lone, "is_equivalent", lone_primitive_is_equivalent, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "equivalent?"), primitive); - - primitive = lone_primitive_create(lone, "is_equal", lone_primitive_is_equal, module, flags); - lone_set_and_export(lone, module, lone_intern_c_string(lone, "equal?"), primitive); - - lone_table_set(lone, lone->modules.loaded, name, module); -} - -static void lone_modules_initialize(struct lone_lisp *lone, int argc, char **argv, char **envp, struct auxiliary *auxv) -{ - lone_builtin_module_linux_initialize(lone, argc, argv, envp, auxv); - lone_builtin_module_lone_initialize(lone); - lone_builtin_module_math_initialize(lone); - lone_builtin_module_text_initialize(lone); - lone_builtin_module_list_initialize(lone); - - lone_vector_push_all(lone, lone->modules.path, 4, - - lone_text_create_from_c_string(lone, "."), - lone_text_create_from_c_string(lone, "~/.lone/modules"), - lone_text_create_from_c_string(lone, "~/.local/lib/lone/modules"), - lone_text_create_from_c_string(lone, "/usr/lib/lone/modules") - - ); -} - -/* ╭───────────────────────┨ LONE LISP ENTRY POINT ┠────────────────────────╮ - │ │ - │ Linux places argument, environment and auxiliary value arrays │ - │ on the stack before jumping to the entry point of the process. │ - │ Architecture-specific code collects this data and passes it to │ - │ the lone function which begins execution of the lisp code. │ - │ │ - │ During early initialization, lone has no dynamic memory │ - │ allocation capabilities and so this function statically │ - │ allocates 64 KiB of memory for the early bootstrapping process. │ - │ │ - ╰────────────────────────────────────────────────────────────────────────╯ */ -long lone(int argc, char **argv, char **envp, struct auxiliary *auxv) -{ - void *stack = __builtin_frame_address(0); - static unsigned char __attribute__((aligned(LONE_ALIGNMENT))) bytes[LONE_MEMORY_SIZE]; - struct lone_bytes memory = { sizeof(bytes), bytes }, random = lone_get_auxiliary_random_bytes(auxv); - struct lone_lisp lone; - - lone_lisp_initialize(&lone, memory, 1024, stack, random); - lone_modules_initialize(&lone, argc, argv, envp, auxv); - - lone_module_load_from_file_descriptor(&lone, lone_module_null(&lone), 0); - - return 0; -} diff --git a/scripts/NR.filter b/scripts/NR.filter index 8f1cee8b..3ad43415 100755 --- a/scripts/NR.filter +++ b/scripts/NR.filter @@ -1,3 +1,4 @@ #!/usr/bin/bash +# SPDX-License-Identifier: AGPL-3.0-or-later grep __NR_ | sed 's/#define //g' | cut -d ' ' -f 1 diff --git a/scripts/NR.generate b/scripts/NR.generate index cf29ad22..063eb76e 100755 --- a/scripts/NR.generate +++ b/scripts/NR.generate @@ -1,4 +1,5 @@ #!/usr/bin/bash +# SPDX-License-Identifier: AGPL-3.0-or-later while read -r NR; do printf '{ "%s", %s },\n' "${NR#__NR_}" "${NR}" diff --git a/scripts/test.bash b/scripts/test.bash index 29f7eb28..b642e911 100755 --- a/scripts/test.bash +++ b/scripts/test.bash @@ -1,6 +1,7 @@ #!/usr/bin/bash +# SPDX-License-Identifier: AGPL-3.0-or-later -lone=${1:-./lone} +lone=${1:-lone} tests_directory=${2:-test} code=0 diff --git a/scripts/test.new b/scripts/test.new index de697020..66be5257 100755 --- a/scripts/test.new +++ b/scripts/test.new @@ -1,4 +1,5 @@ #!/usr/bin/bash +# SPDX-License-Identifier: AGPL-3.0-or-later if [[ -z "${1}" ]]; then 1>&2 printf "Test name not specified\n" diff --git a/source/lone.c b/source/lone.c new file mode 100644 index 00000000..3e79bc59 --- /dev/null +++ b/source/lone.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +/* ╭─────────────────────────────┨ LONE LISP ┠──────────────────────────────╮ + │ │ + │ The standalone Linux Lisp │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +#include +#include +#include +#include +#include +#include + +#include +#include + +/* ╭───────────────────────┨ LONE LISP ENTRY POINT ┠────────────────────────╮ + │ │ + │ Linux places argument, environment and auxiliary value arrays │ + │ on the stack before jumping to the entry point of the process. │ + │ Architecture-specific code collects this data and passes it to │ + │ the lone function which begins execution of the lisp code. │ + │ │ + │ During early initialization, lone has no dynamic memory │ + │ allocation capabilities and so this function statically │ + │ allocates 64 KiB of memory for the early bootstrapping process. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ + +#include + +long lone(int argc, char **argv, char **envp, struct auxiliary *auxv) +{ + void *stack = __builtin_frame_address(0); + static unsigned char __attribute__((aligned(LONE_ALIGNMENT))) bytes[LONE_MEMORY_SIZE]; + struct lone_bytes memory = { sizeof(bytes), bytes }, random = lone_get_auxiliary_random(auxv); + struct lone_lisp lone; + + lone_lisp_initialize(&lone, memory, 1024, stack, random); + lone_modules_intrinsic_initialize(&lone, argc, argv, envp, auxv); + lone_module_path_push_all(&lone, 4, + + ".", + "~/.lone/modules", + "~/.local/lib/lone/modules", + "/usr/lib/lone/modules" + + ); + + lone_module_load_null_from_standard_input(&lone); + + return 0; +} diff --git a/source/lone/hash.c b/source/lone/hash.c new file mode 100644 index 00000000..4c6b61e2 --- /dev/null +++ b/source/lone/hash.c @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include + +#include + +#include +#include + +void lone_hash_initialize(struct lone_lisp *lone, struct lone_bytes random) +{ + lone_hash_fnv_1a_initialize(lone, random); +} + +static size_t lone_hash_recursively(struct lone_value *key, unsigned long hash) +{ + struct lone_bytes bytes; + + if (!key) { /* a null key is probably a bug */ linux_exit(-1); } + + bytes.pointer = (unsigned char *) &key->type; + bytes.count = sizeof(key->type); + hash = lone_hash_fnv_1a(bytes, hash); + + if (lone_is_nil(key)) { return hash; } + + switch (key->type) { + case LONE_MODULE: + case LONE_FUNCTION: + case LONE_PRIMITIVE: + case LONE_VECTOR: + case LONE_TABLE: + linux_exit(-1); + case LONE_LIST: + hash = lone_hash_recursively(key->list.first, hash); + hash = lone_hash_recursively(key->list.rest, hash); + return hash; + case LONE_SYMBOL: + case LONE_TEXT: + case LONE_BYTES: + bytes = key->bytes; + break; + case LONE_INTEGER: + bytes.pointer = (unsigned char *) &key->integer; + bytes.count = sizeof(key->integer); + break; + case LONE_POINTER: + bytes.pointer = (unsigned char *) &key->pointer; + bytes.count = sizeof(key->pointer); + break; + } + + hash = lone_hash_fnv_1a(bytes, hash); + + return hash; +} + +size_t lone_hash(struct lone_lisp *lone, struct lone_value *value) +{ + return lone_hash_recursively(value, lone->hash.fnv_1a.offset_basis); +} diff --git a/source/lone/hash/fnv_1a.c b/source/lone/hash/fnv_1a.c new file mode 100644 index 00000000..726327f8 --- /dev/null +++ b/source/lone/hash/fnv_1a.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include + +#include +#include + +unsigned long __attribute__((pure)) lone_hash_fnv_1a(struct lone_bytes data, unsigned long offset_basis) +{ + unsigned long hash = offset_basis; + unsigned char *bytes = data.pointer; + size_t count = data.count; + + while (count--) { + hash ^= *bytes++; + hash *= FNV_PRIME; + } + + return hash; +} + +void lone_hash_fnv_1a_initialize(struct lone_lisp *lone, struct lone_bytes random) +{ + lone->hash.fnv_1a.offset_basis = lone_hash_fnv_1a(random, FNV_OFFSET_BASIS); +} diff --git a/source/lone/linux.c b/source/lone/linux.c new file mode 100644 index 00000000..fa175209 --- /dev/null +++ b/source/lone/linux.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include + +#include + +void linux_exit(int code) +{ + linux_system_call_1(__NR_exit, code); + __builtin_unreachable(); +} + +long linux_openat(int dirfd, unsigned char *path, int flags) +{ + return linux_system_call_4(__NR_openat, dirfd, (long) path, flags, 0); +} + +long linux_close(int fd) +{ + return linux_system_call_1(__NR_close, fd); +} + +ssize_t linux_read(int fd, const void *buffer, size_t count) +{ + return linux_system_call_3(__NR_read, fd, (long) buffer, (long) count); +} + +ssize_t linux_write(int fd, const void *buffer, size_t count) +{ + return linux_system_call_3(__NR_write, fd, (long) buffer, (long) count); +} diff --git a/source/lone/lisp.c b/source/lone/lisp.c new file mode 100644 index 00000000..c072745e --- /dev/null +++ b/source/lone/lisp.c @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +void lone_lisp_initialize(struct lone_lisp *lone, struct lone_bytes memory, size_t heap_size, void *stack, struct lone_bytes random) +{ + struct lone_function_flags flags = { .evaluate_arguments = 0, .evaluate_result = 0, .variable_arguments = 1 }; + struct lone_value *import, *export; + + lone_memory_initialize(lone, memory, heap_size, stack); + + lone_hash_initialize(lone, random); + + /* basic initialization done, can now use value creation functions */ + + lone->symbol_table = lone_table_create(lone, 256, 0); + lone->constants.nil = lone_list_create_nil(lone); + lone->constants.truth = lone_intern_c_string(lone, "true"); + + lone->modules.loaded = lone_table_create(lone, 32, 0); + lone->modules.top_level_environment = lone_table_create(lone, 8, 0); + lone->modules.path = lone_vector_create(lone, 8); + + import = lone_primitive_create(lone, "import", lone_primitive_import, 0, flags); + export = lone_primitive_create(lone, "export", lone_primitive_export, 0, flags); + lone_table_set(lone, lone->modules.top_level_environment, lone_intern_c_string(lone, "import"), import); + lone_table_set(lone, lone->modules.top_level_environment, lone_intern_c_string(lone, "export"), export); + lone->modules.null = lone_module_create(lone, 0); +} diff --git a/source/lone/lisp/constants.c b/source/lone/lisp/constants.c new file mode 100644 index 00000000..6808dded --- /dev/null +++ b/source/lone/lisp/constants.c @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include + +#include + +struct lone_value *lone_nil(struct lone_lisp *lone) +{ + return lone->constants.nil; +} + +struct lone_value *lone_true(struct lone_lisp *lone) +{ + return lone->constants.truth; +} diff --git a/source/lone/lisp/evaluator.c b/source/lone/lisp/evaluator.c new file mode 100644 index 00000000..456adecd --- /dev/null +++ b/source/lone/lisp/evaluator.c @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include + +#include +#include +#include + +#include +#include + +#include + +static struct lone_value *lone_evaluate_form_index(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *collection, struct lone_value *arguments) +{ + struct lone_value *(*get)(struct lone_lisp *, struct lone_value *, struct lone_value *); + void (*set)(struct lone_lisp *, struct lone_value *, struct lone_value *, struct lone_value *); + struct lone_value *key, *value; + + switch (collection->type) { + case LONE_VECTOR: + get = lone_vector_get; + set = lone_vector_set; + break; + case LONE_TABLE: + get = lone_table_get; + set = lone_table_set; + break; + case LONE_MODULE: case LONE_FUNCTION: case LONE_PRIMITIVE: + case LONE_BYTES: case LONE_SYMBOL: case LONE_TEXT: + case LONE_LIST: case LONE_INTEGER: case LONE_POINTER: + linux_exit(-1); + } + + if (lone_is_nil(arguments)) { /* need at least the key: (collection) */ linux_exit(-1); } + key = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + if (lone_is_nil(arguments)) { + /* table get: (collection key) */ + return get(lone, collection, lone_evaluate(lone, module, environment, key)); + } else { + /* at least one argument */ + value = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + if (lone_is_nil(arguments)) { + /* table set: (collection key value) */ + set(lone, collection, + lone_evaluate(lone, module, environment, key), + lone_evaluate(lone, module, environment, value)); + return value; + } else { + /* too many arguments given: (collection key value extra) */ + linux_exit(-1); + } + } +} + +static struct lone_value *lone_evaluate_form(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *list) +{ + struct lone_value *first = lone_list_first(list), *rest = lone_list_rest(list); + + /* apply arguments to a lone value */ + first = lone_evaluate(lone, module, environment, first); + switch (first->type) { + case LONE_FUNCTION: + case LONE_PRIMITIVE: + return lone_apply(lone, module, environment, first, rest); + case LONE_VECTOR: + case LONE_TABLE: + return lone_evaluate_form_index(lone, module, environment, first, rest); + case LONE_MODULE: + case LONE_LIST: + case LONE_SYMBOL: + case LONE_TEXT: + case LONE_BYTES: + case LONE_INTEGER: + case LONE_POINTER: + /* first element not an applicable type */ linux_exit(-1); + } +} + +struct lone_value *lone_evaluate(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *value) +{ + if (value == 0) { return 0; } + if (lone_is_nil(value)) { return value; } + + switch (value->type) { + case LONE_LIST: + return lone_evaluate_form(lone, module, environment, value); + case LONE_SYMBOL: + return lone_table_get(lone, environment, value); + case LONE_MODULE: + case LONE_FUNCTION: + case LONE_PRIMITIVE: + case LONE_VECTOR: + case LONE_TABLE: + case LONE_INTEGER: + case LONE_POINTER: + case LONE_BYTES: + case LONE_TEXT: + return value; + } +} + +struct lone_value *lone_evaluate_all(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *list) +{ + struct lone_value *evaluated = lone_list_create_nil(lone), *head; + + for (head = evaluated; !lone_is_nil(list); list = lone_list_rest(list)) { + head = lone_list_append(lone, head, lone_evaluate(lone, module, environment, lone_list_first(list))); + } + + return evaluated; +} + +struct lone_value *lone_evaluate_module(struct lone_lisp *lone, struct lone_value *module, struct lone_value *value) +{ + return lone_evaluate(lone, module, module->module.environment, value); +} + +static struct lone_value *lone_apply_function(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *function, struct lone_value *arguments) +{ + struct lone_value *new_environment = lone_table_create(lone, 16, function->function.environment), + *names = function->function.arguments, *code = function->function.code, + *value = lone_nil(lone); + + if (function->function.flags.evaluate_arguments) { arguments = lone_evaluate_all(lone, module, environment, arguments); } + + if (function->function.flags.variable_arguments) { + if (lone_is_nil(names) || !lone_is_nil(lone_list_rest(names))) { + /* must have exactly one argument: the list of arguments */ + linux_exit(-1); + } + + lone_table_set(lone, new_environment, lone_list_first(names), arguments); + } else { + while (1) { + if (lone_is_nil(names) != lone_is_nil(arguments)) { + /* argument number mismatch: ((lambda (x) x) 10 20), ((lambda (x y) y) 10) */ + linux_exit(-1); + } else if (lone_is_nil(names) && lone_is_nil(arguments)) { + break; + } + + lone_table_set(lone, new_environment, lone_list_first(names), lone_list_first(arguments)); + + names = lone_list_rest(names); + arguments = lone_list_rest(arguments); + } + } + + while (1) { + if (lone_is_nil(code)) { break; } + value = lone_list_first(code); + value = lone_evaluate(lone, module, new_environment, value); + code = lone_list_rest(code); + } + + if (function->function.flags.evaluate_result) { value = lone_evaluate(lone, module, environment, value); } + + return value; +} + +static struct lone_value *lone_apply_primitive(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *primitive, struct lone_value *arguments) +{ + struct lone_value *result; + if (primitive->primitive.flags.evaluate_arguments) { arguments = lone_evaluate_all(lone, module, environment, arguments); } + result = primitive->primitive.function(lone, module, environment, arguments, primitive->primitive.closure); + if (primitive->primitive.flags.evaluate_result) { result = lone_evaluate(lone, module, environment, result); } + return result; +} + +struct lone_value *lone_apply(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *applicable, struct lone_value *arguments) +{ + if (!lone_is_applicable(applicable)) { /* given function is not an applicable type */ linux_exit(-1); } + + if (lone_is_function(applicable)) { + return lone_apply_function(lone, module, environment, applicable, arguments); + } else { + return lone_apply_primitive(lone, module, environment, applicable, arguments); + } +} diff --git a/source/lone/lisp/printer.c b/source/lone/lisp/printer.c new file mode 100644 index 00000000..d0e96dbb --- /dev/null +++ b/source/lone/lisp/printer.c @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +static void lone_print_integer(int fd, long n) +{ + static char digits[DECIMAL_DIGITS_PER_LONG + 1]; /* digits, sign */ + char *digit = digits + DECIMAL_DIGITS_PER_LONG; /* work backwards */ + size_t count = 0; + int is_negative; + + if (n < 0) { + is_negative = 1; + n *= -1; + } else { + is_negative = 0; + } + + do { + *--digit = '0' + (n % 10); + n /= 10; + ++count; + } while (n > 0); + + if (is_negative) { + *--digit = '-'; + ++count; + } + + linux_write(fd, digit, count); +} + +static void lone_print_pointer(struct lone_lisp *lone, struct lone_value *pointer, int fd) +{ + if (pointer->pointer.type == LONE_TO_UNKNOWN) { + lone_print_integer(fd, (intptr_t) pointer->pointer.address); + } else { + lone_print(lone, lone_pointer_dereference(lone, pointer), fd); + } +} + +static void lone_print_bytes(struct lone_lisp *lone, struct lone_value *bytes, int fd) +{ + size_t count = bytes->bytes.count; + if (count == 0) { linux_write(fd, "bytes[]", 7); return; } + + static unsigned char hexadecimal[] = "0123456789ABCDEF"; + size_t size = 2 + count * 2; // size required: "0x" + 2 characters per input byte + unsigned char *text = lone_allocate(lone, size); + unsigned char *byte = bytes->bytes.pointer; + size_t i; + + text[0] = '0'; + text[1] = 'x'; + + for (i = 0; i < count; ++i) { + unsigned char low = (byte[i] & 0x0F) >> 0; + unsigned char high = (byte[i] & 0xF0) >> 4; + text[2 + (2 * i + 0)] = hexadecimal[high]; + text[2 + (2 * i + 1)] = hexadecimal[low]; + } + + linux_write(fd, "bytes[", 6); + linux_write(fd, text, size); + linux_write(fd, "]", 1); + + lone_deallocate(lone, text); +} + +static void lone_print_list(struct lone_lisp *lone, struct lone_value *list, int fd) +{ + if (list == 0 || lone_is_nil(list)) { return; } + + struct lone_value *first = list->list.first, + *rest = list->list.rest; + + lone_print(lone, first, fd); + + if (lone_is_list(rest)) { + if (!lone_is_nil(rest)) { + linux_write(fd, " ", 1); + lone_print_list(lone, rest, fd); + } + } else { + linux_write(fd, " . ", 3); + lone_print(lone, rest, fd); + } +} + +static void lone_print_vector(struct lone_lisp *lone, struct lone_value *vector, int fd) +{ + size_t n = vector->vector.count, i; + struct lone_value **values = vector->vector.values; + + if (vector->vector.count == 0) { linux_write(fd, "[]", 2); return; } + + linux_write(fd, "[ ", 2); + + for (i = 0; i < n; ++i) { + lone_print(lone, values[i], fd); + linux_write(fd, " ", 1); + } + + linux_write(fd, "]", 1); +} + +static void lone_print_table(struct lone_lisp *lone, struct lone_value *table, int fd) +{ + size_t n = table->table.capacity, i; + struct lone_table_entry *entries = table->table.entries; + + if (table->table.count == 0) { linux_write(fd, "{}", 2); return; } + + linux_write(fd, "{ ", 2); + + for (i = 0; i < n; ++i) { + struct lone_value *key = entries[i].key, + *value = entries[i].value; + + + if (key) { + lone_print(lone, key, fd); + linux_write(fd, " ", 1); + lone_print(lone, value, fd); + linux_write(fd, " ", 1); + } + } + + linux_write(fd, "}", 1); +} + +static void lone_print_function(struct lone_lisp *lone, struct lone_value *function, int fd) +{ + struct lone_value *arguments = function->function.arguments, + *code = function->function.code; + + linux_write(fd, "(𝛌 ", 6); + lone_print(lone, arguments, fd); + + while (!lone_is_nil(code)) { + linux_write(fd, "\n ", 3); + lone_print(lone, lone_list_first(code), fd); + code = lone_list_rest(code); + } + + linux_write(fd, ")", 1); +} + +static void lone_print_hash_notation(struct lone_lisp *lone, char *descriptor, struct lone_value *value, int fd) +{ + linux_write(fd, "#<", 2); + linux_write(fd, descriptor, lone_c_string_length(descriptor)); + linux_write(fd, " ", 1); + lone_print(lone, value, fd); + linux_write(fd, ">", 1); +} + +void lone_print(struct lone_lisp *lone, struct lone_value *value, int fd) +{ + if (value == 0) { return; } + if (lone_is_nil(value)) { linux_write(fd, "nil", 3); return; } + + switch (value->type) { + case LONE_MODULE: + lone_print_hash_notation(lone, "module", value->module.name, fd); + break; + case LONE_PRIMITIVE: + lone_print_hash_notation(lone, "primitive", value->primitive.name, fd); + break; + case LONE_FUNCTION: + lone_print_function(lone, value, fd); + break; + case LONE_LIST: + linux_write(fd, "(", 1); + lone_print_list(lone, value, fd); + linux_write(fd, ")", 1); + break; + case LONE_VECTOR: + lone_print_vector(lone, value, fd); + break; + case LONE_TABLE: + lone_print_table(lone, value, fd); + break; + case LONE_BYTES: + lone_print_bytes(lone, value, fd); + break; + case LONE_SYMBOL: + linux_write(fd, value->bytes.pointer, value->bytes.count); + break; + case LONE_TEXT: + linux_write(fd, "\"", 1); + linux_write(fd, value->bytes.pointer, value->bytes.count); + linux_write(fd, "\"", 1); + break; + case LONE_INTEGER: + lone_print_integer(fd, value->integer); + break; + case LONE_POINTER: + lone_print_pointer(lone, value, fd); + break; + } +} diff --git a/source/lone/lisp/reader.c b/source/lone/lisp/reader.c new file mode 100644 index 00000000..07bb8c76 --- /dev/null +++ b/source/lone/lisp/reader.c @@ -0,0 +1,482 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +void lone_reader_initialize(struct lone_lisp *lone, struct lone_reader *reader, size_t buffer_size, int file_descriptor) +{ + reader->file_descriptor = file_descriptor; + reader->buffer.bytes.count = buffer_size; + reader->buffer.bytes.pointer = lone_allocate(lone, buffer_size); + reader->buffer.position.read = 0; + reader->buffer.position.write = 0; + reader->error = 0; +} + +void lone_reader_finalize(struct lone_lisp *lone, struct lone_reader *reader) +{ + lone_deallocate(lone, reader->buffer.bytes.pointer); +} + +static size_t lone_reader_fill_buffer(struct lone_lisp *lone, struct lone_reader *reader) +{ + unsigned char *buffer = reader->buffer.bytes.pointer; + size_t size = reader->buffer.bytes.count, position = reader->buffer.position.write, + allocated = size, bytes_read = 0, total_read = 0; + ssize_t read_result = 0; + + while (1) { + read_result = linux_read(reader->file_descriptor, buffer + position, size); + + if (read_result < 0) { + linux_exit(-1); + } + + bytes_read = (size_t) read_result; + total_read += bytes_read; + position += bytes_read; + + if (bytes_read == size) { + allocated += size; + buffer = lone_reallocate(lone, buffer, allocated); + } else { + break; + } + } + + reader->buffer.bytes.pointer = buffer; + reader->buffer.bytes.count = allocated; + reader->buffer.position.write = position; + return total_read; +} + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ The peek(k) function returns the k-th element from the input │ + │ starting from the current input position, with peek(0) being │ + │ the current character and peek(k) being look ahead for k > 1. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +static unsigned char *lone_reader_peek_k(struct lone_lisp *lone, struct lone_reader *reader, size_t k) +{ + size_t read_position = reader->buffer.position.read, + write_position = reader->buffer.position.write, + bytes_read; + + if (read_position + k >= write_position) { + // we'd overrun the buffer because there's not enough input + // fill it up by reading more first + bytes_read = lone_reader_fill_buffer(lone, reader); + if (bytes_read <= k) { + // wanted at least k bytes but got less + return 0; + } + } + + return reader->buffer.bytes.pointer + read_position + k; +} + +static unsigned char *lone_reader_peek(struct lone_lisp *lone, struct lone_reader *reader) +{ + return lone_reader_peek_k(lone, reader, 0); +} + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ The consume(k) function advances the input position by k. │ + │ This progresses through the input, consuming it. │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +static void lone_reader_consume_k(struct lone_reader *reader, size_t k) +{ + reader->buffer.position.read += k; +} + +static void lone_reader_consume(struct lone_reader *reader) +{ + lone_reader_consume_k(reader, 1); +} + +static int lone_reader_match_byte(unsigned char byte, unsigned char target) +{ + if (target == ' ') { + switch (byte) { + case ' ': + case '\t': + case '\n': + return 1; + default: + return 0; + } + } else if (target == ')' || target == ']' || target == '}') { + return byte == ')' || byte == ']' || byte == '}'; + } else if (target >= '0' && target <= '9') { + return byte >= '0' && byte <= '9'; + } else { + return byte == target; + } +} + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Analyzes a number and adds it to the tokens list if valid. │ + │ │ + │ ([+-]?[0-9]+)[)]} \n\t] │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +static struct lone_value *lone_reader_consume_number(struct lone_lisp *lone, struct lone_reader *reader) +{ + unsigned char *current, *start = lone_reader_peek(lone, reader); + if (!start) { return 0; } + size_t end = 0; + + switch (*start) { + case '+': case '-': + lone_reader_consume(reader); + ++end; + break; + default: + break; + } + + if ((current = lone_reader_peek(lone, reader)) && lone_reader_match_byte(*current, '1')) { + lone_reader_consume(reader); + ++end; + } else { return 0; } + + while ((current = lone_reader_peek(lone, reader)) && lone_reader_match_byte(*current, '1')) { + lone_reader_consume(reader); + ++end; + } + + if (current && !lone_reader_match_byte(*current, ')') && !lone_reader_match_byte(*current, ' ')) { return 0; } + + return lone_integer_parse(lone, start, end); +} + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Analyzes a symbol and adds it to the tokens list if valid. │ + │ │ + │ (.*)[)]} \n\t] │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +static struct lone_value *lone_reader_consume_symbol(struct lone_lisp *lone, struct lone_reader *reader) +{ + unsigned char *current, *start = lone_reader_peek(lone, reader); + if (!start) { return 0; } + size_t end = 0; + + while ((current = lone_reader_peek(lone, reader)) && !lone_reader_match_byte(*current, ')') && !lone_reader_match_byte(*current, ' ')) { + lone_reader_consume(reader); + ++end; + } + + return lone_intern(lone, start, end, true); +} + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Analyzes a string and adds it to the tokens list if valid. │ + │ │ + │ (".*")[)]} \n\t] │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +static struct lone_value *lone_reader_consume_text(struct lone_lisp *lone, struct lone_reader *reader) +{ + size_t end = 0; + unsigned char *current, *start = lone_reader_peek(lone, reader); + if (!start || *start != '"') { return 0; } + + // skip leading " + ++start; + lone_reader_consume(reader); + + while ((current = lone_reader_peek(lone, reader)) && *current != '"') { + lone_reader_consume(reader); + ++end; + } + + // skip trailing " + ++current; + lone_reader_consume(reader); + + if (!lone_reader_match_byte(*current, ')') && !lone_reader_match_byte(*current, ' ')) { return 0; } + + return lone_text_create(lone, start, end); +} + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ Analyzes a single character token, │ + │ characters that the parser deals with specially. │ + │ These include single quotes and opening and closing brackets. │ + │ │ + │ (['()[]{}]) │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +static struct lone_value *lone_reader_consume_character(struct lone_lisp *lone, struct lone_reader *reader) +{ + unsigned char *bracket = lone_reader_peek(lone, reader); + if (!bracket) { return 0; } + + switch (*bracket) { + case '(': case ')': + case '[': case ']': + case '{': case '}': + case '\'': case '`': + case '.': + lone_reader_consume(reader); + return lone_intern(lone, bracket, 1, true); + default: + return 0; + } +} + +/* ╭────────────────────────────────────────────────────────────────────────╮ + │ │ + │ The lone lisp lexer receives as input a single lone bytes value │ + │ containing the full source code to be processed and it outputs │ + │ a lone list of each lisp token found in the input. For example: │ + │ │ + │ lex ← lone_bytes = [ (abc ("zxc") ] │ + │ lex → lone_list = { ( → abc → ( → "zxc" → ) } │ + │ │ + │ Note that the list is linear and parentheses are not matched. │ + │ The lexical analysis algorithm can be summarized as follows: │ + │ │ + │ ◦ Skip all whitespace until it finds something │ + │ ◦ Fail if tokens aren't separated by spaces or ) at the end │ + │ ◦ If found sign before digits tokenize signed number │ + │ ◦ If found digit then look for more digits and tokenize │ + │ ◦ If found " then find the next " and tokenize │ + │ ◦ If found ( or ) just tokenize them as is without matching │ + │ ◦ Tokenize everything else unmodified as a symbol │ + │ │ + ╰────────────────────────────────────────────────────────────────────────╯ */ +static struct lone_value *lone_lex(struct lone_lisp *lone, struct lone_reader *reader) +{ + struct lone_value *token = 0; + unsigned char *c; + + while ((c = lone_reader_peek(lone, reader))) { + if (lone_reader_match_byte(*c, ' ')) { + lone_reader_consume(reader); + continue; + } else { + unsigned char *c1; + + switch (*c) { + case '+': case '-': + if ((c1 = lone_reader_peek_k(lone, reader, 1)) && lone_reader_match_byte(*c1, '1')) { + token = lone_reader_consume_number(lone, reader); + } else { + token = lone_reader_consume_symbol(lone, reader); + } + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + token = lone_reader_consume_number(lone, reader); + break; + case '"': + token = lone_reader_consume_text(lone, reader); + break; + case '(': case ')': + case '[': case ']': + case '{': case '}': + case '\'': case '`': + case '.': + token = lone_reader_consume_character(lone, reader); + break; + default: + token = lone_reader_consume_symbol(lone, reader); + break; + } + + if (token) { + break; + } else { + goto lex_failed; + } + } + } + + return token; + +lex_failed: + linux_exit(-1); +} + +static struct lone_value *lone_parse(struct lone_lisp *, struct lone_reader *, struct lone_value *); + +static struct lone_value *lone_parse_vector(struct lone_lisp *lone, struct lone_reader *reader) +{ + struct lone_value *vector = lone_vector_create(lone, 32), *value; + size_t i = 0; + + while (1) { + value = lone_lex(lone, reader); + + if (!value) { /* end of input */ reader->error = 1; return 0; } + if (lone_is_symbol(value) && *value->bytes.pointer == ']') { + /* complete vector: [], [ x ], [ x y ] */ + break; + } + + value = lone_parse(lone, reader, value); + + lone_vector_set_value_at(lone, vector, i++, value); + } + + return vector; +} +static struct lone_value *lone_parse_table(struct lone_lisp *lone, struct lone_reader *reader) +{ + struct lone_value *table = lone_table_create(lone, 32, 0), *key, *value; + + while (1) { + key = lone_lex(lone, reader); + + if (!key) { /* end of input */ reader->error = 1; return 0; } + if (lone_is_symbol(key) && *key->bytes.pointer == '}') { + /* complete table: {}, { x y } */ + break; + } + + key = lone_parse(lone, reader, key); + + value = lone_lex(lone, reader); + + if (!value) { /* end of input */ reader->error = 1; return 0; } + if (lone_is_symbol(value) && *value->bytes.pointer == '}') { + /* incomplete table: { x }, { x y z } */ + reader->error = 1; + return 0; + } + + value = lone_parse(lone, reader, value); + + lone_table_set(lone, table, key, value); + } + + return table; +} + +static struct lone_value *lone_parse_list(struct lone_lisp *lone, struct lone_reader *reader) +{ + struct lone_value *list = lone_list_create_nil(lone), *first = list, *prev = 0, *next; + + while (1) { + next = lone_lex(lone, reader); + if (!next) { reader->error = 1; return 0; } + + if (lone_is_symbol(next)) { + if (*next->bytes.pointer == ')') { break; } + else if (*next->bytes.pointer == '.') { + if (!prev) { reader->error = 1; return 0; } + + next = lone_lex(lone, reader); + if (!next) { reader->error = 1; return 0; } + + lone_list_set_rest(prev, lone_parse(lone, reader, next)); + + next = lone_lex(lone, reader); + if (!next || !lone_is_symbol(next) || *next->bytes.pointer != ')') { reader->error = 1; return 0; } + + break; + } + } + + prev = list; + list = lone_list_append(lone, list, lone_parse(lone, reader, next)); + } + + return first; +} + +static struct lone_value *lone_parse_special_character(struct lone_lisp *lone, struct lone_reader *reader, char character) +{ + struct lone_value *symbol, *value, *form; + char *c_string; + + switch (character) { + case '\'': + c_string = "quote"; + break; + case '`': + c_string = "quasiquote"; + break; + default: + /* invalid special character */ linux_exit(-1); + } + + symbol = lone_intern_c_string(lone, c_string); + value = lone_parse(lone, reader, lone_lex(lone, reader)); + form = lone_list_create(lone, value, lone_nil(lone)); + + return lone_list_create(lone, symbol, form); +} + +static struct lone_value *lone_parse(struct lone_lisp *lone, struct lone_reader *reader, struct lone_value *token) +{ + char character; + + if (!token) { return 0; } + + // lexer has already parsed atoms + // parser deals with nested structures + switch (token->type) { + case LONE_SYMBOL: + character = *token->bytes.pointer; + + switch (character) { + case '(': + return lone_parse_list(lone, reader); + case '[': + return lone_parse_vector(lone, reader); + case '{': + return lone_parse_table(lone, reader); + case ')': case ']': case '}': + goto parse_failed; + case '\'': case '`': + return lone_parse_special_character(lone, reader, character); + default: + return token; + } + case LONE_INTEGER: + case LONE_TEXT: + return token; + case LONE_MODULE: + case LONE_FUNCTION: + case LONE_PRIMITIVE: + case LONE_LIST: + case LONE_VECTOR: + case LONE_TABLE: + case LONE_BYTES: + case LONE_POINTER: + /* unexpected value type from lexer */ + goto parse_failed; + } + +parse_failed: + /* parse failed */ linux_exit(-1); +} + +struct lone_value *lone_read(struct lone_lisp *lone, struct lone_reader *reader) +{ + return lone_parse(lone, reader, lone_lex(lone, reader)); +} diff --git a/source/lone/memory.c b/source/lone/memory.c new file mode 100644 index 00000000..6c75dcb5 --- /dev/null +++ b/source/lone/memory.c @@ -0,0 +1,18 @@ +#include +#include +#include + +#include +#include + +void lone_memory_initialize(struct lone_lisp *lone, struct lone_bytes memory, size_t heap_size, void *stack) +{ + lone->memory.stack = stack; + + lone->memory.general = (struct lone_memory *) __builtin_assume_aligned(memory.pointer, LONE_ALIGNMENT); + lone->memory.general->prev = lone->memory.general->next = 0; + lone->memory.general->free = 1; + lone->memory.general->size = memory.count - sizeof(struct lone_memory); + + lone_heap_initialize(lone, heap_size); +} diff --git a/source/lone/memory/allocator.c b/source/lone/memory/allocator.c new file mode 100644 index 00000000..609031d1 --- /dev/null +++ b/source/lone/memory/allocator.c @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include +#include + +#include +#include + +static size_t __attribute__((const)) lone_next_power_of_2(size_t n) +{ + size_t next = 1; + while (next < n) { next *= 2; } + return next; +} + +static size_t __attribute__((const)) lone_next_power_of_2_multiple(size_t n, size_t m) +{ + m = lone_next_power_of_2(m); + return (n + m - 1) & (~(m - 1)); +} + +size_t __attribute__((const)) lone_align(size_t size, size_t alignment) +{ + return lone_next_power_of_2_multiple(size, alignment); +} + +static void lone_memory_split(struct lone_memory *block, size_t used) +{ + size_t excess = block->size - used; + + /* split block if there's enough space to allocate at least 1 byte */ + if (excess >= sizeof(struct lone_memory) + 1) { + struct lone_memory *new = (struct lone_memory *) __builtin_assume_aligned(block->pointer + used, LONE_ALIGNMENT); + new->next = block->next; + new->prev = block; + new->free = 1; + new->size = excess - sizeof(struct lone_memory); + block->next = new; + block->size -= excess + sizeof(struct lone_memory); + } +} + +static void lone_memory_coalesce(struct lone_memory *block) +{ + struct lone_memory *next; + + if (block && block->free) { + next = block->next; + if (next && next->free) { + block->size += next->size + sizeof(struct lone_memory); + next = block->next = next->next; + if (next) { next->prev = block; } + } + } +} + +void * lone_allocate_aligned(struct lone_lisp *lone, size_t requested_size, size_t alignment) +{ + size_t needed_size = requested_size + sizeof(struct lone_memory); + struct lone_memory *block; + + needed_size = lone_align(needed_size, alignment); + + for (block = lone->memory.general; block; block = block->next) { + if (block->free && block->size >= needed_size) + break; + } + + if (!block) { linux_exit(-1); } + + block->free = 0; + lone_memory_split(block, needed_size); + + return block->pointer; +} + +void * lone_allocate(struct lone_lisp *lone, size_t requested_size) +{ + return lone_allocate_aligned(lone, requested_size, LONE_ALIGNMENT); +} + +void * lone_reallocate(struct lone_lisp *lone, void *pointer, size_t size) +{ + struct lone_memory *old = ((struct lone_memory *) pointer) - 1, + *new = ((struct lone_memory *) lone_allocate(lone, size)) - 1; + + if (pointer) { + lone_memory_move(old->pointer, new->pointer, new->size < old->size ? new->size : old->size); + lone_deallocate(lone, pointer); + } + + return new->pointer; +} + +void lone_deallocate(struct lone_lisp *lone, void *pointer) +{ + struct lone_memory *block = ((struct lone_memory *) pointer) - 1; + block->free = 1; + + lone_memory_coalesce(block); + lone_memory_coalesce(block->prev); +} diff --git a/source/lone/memory/functions.c b/source/lone/memory/functions.c new file mode 100644 index 00000000..e79cf354 --- /dev/null +++ b/source/lone/memory/functions.c @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include + +void lone_memory_move(void *from, void *to, size_t count) +{ + unsigned char *source = from, *destination = to; + + if (source >= destination) { + /* destination is at or behind source, copy forwards */ + while (count--) { *destination++ = *source++; } + } else { + /* destination is ahead of source, copy backwards */ + source += count; destination += count; + while (count--) { *--destination = *--source; } + } +} + +void lone_memory_set(void *to, unsigned char byte, size_t count) +{ + unsigned char *memory = to; + size_t i; + + for (i = 0; i < count; ++i) { + memory[i] = byte; + } +} + +void lone_memory_zero(void *to, size_t count) +{ + lone_memory_set(to, 0, count); +} + +size_t lone_c_string_length(char *c_string) +{ + size_t length = 0; + if (!c_string) { return 0; } + while (c_string[length++]); + return length - 1; +} + +/* Compilers emit calls to mem* functions even with -nostdlib */ +void *memset(void *to, int byte, size_t count) +{ + lone_memory_set(to, (unsigned char) byte, count); + return to; +} diff --git a/source/lone/memory/garbage_collector.c b/source/lone/memory/garbage_collector.c new file mode 100644 index 00000000..c1f2a784 --- /dev/null +++ b/source/lone/memory/garbage_collector.c @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +static void lone_mark_value(struct lone_value *value) +{ + if (!value || !value->live || value->marked) { return; } + + value->marked = true; + + switch (value->type) { + case LONE_MODULE: + lone_mark_value(value->module.name); + lone_mark_value(value->module.environment); + lone_mark_value(value->module.exports); + break; + case LONE_FUNCTION: + lone_mark_value(value->function.arguments); + lone_mark_value(value->function.code); + lone_mark_value(value->function.environment); + break; + case LONE_PRIMITIVE: + lone_mark_value(value->primitive.name); + lone_mark_value(value->primitive.closure); + break; + case LONE_LIST: + lone_mark_value(value->list.first); + lone_mark_value(value->list.rest); + break; + case LONE_VECTOR: + for (size_t i = 0; i < value->vector.count; ++i) { + lone_mark_value(value->vector.values[i]); + } + break; + case LONE_TABLE: + lone_mark_value(value->table.prototype); + for (size_t i = 0; i < value->table.capacity; ++i) { + lone_mark_value(value->table.entries[i].key); + lone_mark_value(value->table.entries[i].value); + } + break; + case LONE_SYMBOL: + case LONE_TEXT: + case LONE_BYTES: + case LONE_POINTER: + case LONE_INTEGER: + /* these types do not contain any other values to mark */ + break; + } +} + +static void lone_mark_known_roots(struct lone_lisp *lone) +{ + lone_mark_value(lone->symbol_table); + lone_mark_value(lone->constants.nil); + lone_mark_value(lone->constants.truth); + lone_mark_value(lone->modules.loaded); + lone_mark_value(lone->modules.null); + lone_mark_value(lone->modules.top_level_environment); + lone_mark_value(lone->modules.path); +} + +static bool lone_points_within_range(void *pointer, void *start, void *end) +{ + return start <= pointer && pointer < end; +} + +static bool lone_points_to_general_memory(struct lone_lisp *lone, void *pointer) +{ + struct lone_memory *general = lone->memory.general; + return lone_points_within_range(pointer, general->pointer, general->pointer + general->size); +} + +static bool lone_points_to_heap(struct lone_lisp *lone, void *pointer) +{ + struct lone_heap *heap; + + if (!lone_points_to_general_memory(lone, pointer)) { return false; } + + for (heap = lone->memory.heaps; heap; heap = heap->next) { + if (lone_points_within_range(pointer, heap->values, heap->values + heap->count)) { return true; } + } + + return false; +} + +static void lone_find_and_mark_stack_roots(struct lone_lisp *lone) +{ + void *bottom = lone->memory.stack, *top = __builtin_frame_address(0), *tmp; + void **pointer; + + if (top < bottom) { + tmp = bottom; + bottom = top; + top = tmp; + } + + pointer = bottom; + + while (pointer++ < top) { + if (lone_points_to_heap(lone, *pointer)) { + lone_mark_value(*pointer); + } + } +} + +static void lone_mark_all_reachable_values(struct lone_lisp *lone) +{ + lone_registers registers; /* stack space for registers */ + lone_save_registers(registers); /* spill registers on stack */ + + lone_mark_known_roots(lone); /* precise */ + lone_find_and_mark_stack_roots(lone); /* conservative */ +} + +static void lone_kill_all_unmarked_values(struct lone_lisp *lone) +{ + struct lone_value *value; + struct lone_heap *heap; + size_t i; + + for (heap = lone->memory.heaps; heap; heap = heap->next) { + for (i = 0; i < heap->count; ++i) { + value = &heap->values[i]; + + if (!value->live) { continue; } + + if (!value->marked) { + switch (value->type) { + case LONE_BYTES: + case LONE_TEXT: + case LONE_SYMBOL: + if (value->should_deallocate_bytes) { + lone_deallocate(lone, value->bytes.pointer); + } + break; + case LONE_VECTOR: + lone_deallocate(lone, value->vector.values); + break; + case LONE_TABLE: + lone_deallocate(lone, value->table.entries); + break; + case LONE_MODULE: + case LONE_FUNCTION: + case LONE_PRIMITIVE: + case LONE_LIST: + case LONE_INTEGER: + case LONE_POINTER: + /* these types do not own any additional memory */ + break; + } + + value->live = false; + } + + value->marked = false; + } + } +} + +void lone_garbage_collector(struct lone_lisp *lone) +{ + lone_mark_all_reachable_values(lone); + lone_kill_all_unmarked_values(lone); + lone_deallocate_dead_heaps(lone); +} diff --git a/source/lone/memory/heap.c b/source/lone/memory/heap.c new file mode 100644 index 00000000..a5f20eaa --- /dev/null +++ b/source/lone/memory/heap.c @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include + +#include +#include + +static struct lone_heap *lone_allocate_heap(struct lone_lisp *lone, size_t count) +{ + size_t i, size = sizeof(struct lone_heap) + (sizeof(struct lone_value) * count); + struct lone_heap *heap = lone_allocate(lone, size); + heap->next = 0; + heap->count = count; + for (i = 0; i < count; ++i) { + heap->values[i].live = false; + heap->values[i].marked = false; + } + return heap; +} + +static struct lone_value *lone_allocate_from_heap(struct lone_lisp *lone) +{ + struct lone_value *element; + struct lone_heap *heap, *prev; + size_t i; + + for (prev = lone->memory.heaps, heap = prev; heap; prev = heap, heap = heap->next) { + for (i = 0; i < heap->count; ++i) { + element = &heap->values[i]; + + if (!element->live) { + goto resurrect; + } + } + } + + heap = lone_allocate_heap(lone, lone->memory.heaps[0].count); + prev->next = heap; + element = &heap->values[0]; + +resurrect: + element->live = true; + return element; +} + +void lone_deallocate_dead_heaps(struct lone_lisp *lone) +{ + struct lone_heap *prev = lone->memory.heaps, *heap = prev->next; + size_t i; + + while (heap) { + for (i = 0; i < heap->count; ++i) { + if (heap->values[i].live) { /* at least one live object */ goto next_heap; } + } + + /* no live objects */ + prev->next = heap->next; + lone_deallocate(lone, heap); + heap = prev->next; + continue; +next_heap: + prev = heap; + heap = heap->next; + } +} + +struct lone_value *lone_heap_allocate_value(struct lone_lisp *lone) +{ + return lone_allocate_from_heap(lone); +} + +void lone_heap_initialize(struct lone_lisp *lone, size_t heap_size) +{ + lone->memory.heaps = lone_allocate_heap(lone, heap_size); +} diff --git a/source/lone/modules.c b/source/lone/modules.c new file mode 100644 index 00000000..2cd4d1f0 --- /dev/null +++ b/source/lone/modules.c @@ -0,0 +1,319 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct lone_value *lone_module_null(struct lone_lisp *lone) +{ + return lone->modules.null; +} + +static struct lone_value *lone_module_name_to_key(struct lone_lisp *lone, struct lone_value *name) +{ + struct lone_value *head; + + switch (name->type) { + case LONE_SYMBOL: + return lone_list_create(lone, name, lone_nil(lone)); + case LONE_LIST: + for (head = name; !lone_is_nil(head); head = lone_list_rest(head)) { + if (!lone_is_symbol(lone_list_first(head))) { + linux_exit(-1); + } + } + return name; + case LONE_MODULE: + return lone_module_name_to_key(lone, name->module.name); + case LONE_TEXT: case LONE_BYTES: + case LONE_FUNCTION: case LONE_PRIMITIVE: + case LONE_VECTOR: case LONE_TABLE: + case LONE_INTEGER: case LONE_POINTER: + linux_exit(-1); + } +} + +static struct lone_value *lone_module_get_or_create(struct lone_lisp *lone, struct lone_value *name, bool *not_found) +{ + struct lone_value *module; + + name = lone_module_name_to_key(lone, name); + module = lone_table_get(lone, lone->modules.loaded, name); + if (not_found) { + *not_found = false; + } + + if (lone_is_nil(module)) { + module = lone_module_create(lone, name); + lone_table_set(lone, lone->modules.loaded, name, module); + if (not_found) { + *not_found = true; + } + } + + return module; +} + +struct lone_value *lone_module_for_name(struct lone_lisp *lone, struct lone_value *name) +{ + return lone_module_get_or_create(lone, name, 0); +} + +static int lone_module_search(struct lone_lisp *lone, struct lone_value *symbols) +{ + struct lone_value *slash = lone_intern_c_string(lone, "/"), *ln = lone_intern_c_string(lone, ".ln"); + struct lone_value *arguments, *package, *search_path; + unsigned char *path; + size_t i; + long result; + + symbols = lone_module_name_to_key(lone, symbols); + package = lone_list_first(symbols); + + for (i = 0; i < lone->modules.path->vector.count; ++i) { + search_path = lone->modules.path->vector.values[i]; + arguments = lone_list_build(lone, 3, search_path, package, symbols); + arguments = lone_list_flatten(lone, arguments); + arguments = lone_text_transfer_bytes(lone, lone_join(lone, slash, arguments, lone_has_bytes), true); + arguments = lone_list_build(lone, 2, arguments, ln); + path = lone_concatenate(lone, arguments, lone_has_bytes).pointer; + + result = linux_openat(AT_FDCWD, path, O_RDONLY | O_CLOEXEC); + + lone_deallocate(lone, path); + + switch (result) { + case -ENOENT: + case -EACCES: case -EPERM: + case -ENOTDIR: case -EISDIR: + case -EINVAL: case -ENAMETOOLONG: + case -EMFILE: case -ENFILE: + case -ELOOP: + continue; + case -ENOMEM: case -EFAULT: + linux_exit(-1); + } + + return (int) result; + } + + linux_exit(-1); /* module not found */ +} + +static void lone_module_load_from_file_descriptor(struct lone_lisp *lone, struct lone_value *module, int file_descriptor) +{ + struct lone_value *value; + struct lone_reader reader; + + lone_reader_initialize(lone, &reader, LONE_BUFFER_SIZE, file_descriptor); + + while (1) { + value = lone_read(lone, &reader); + if (!value) { if (reader.error) { linux_exit(-1); } else { break; } } + + value = lone_evaluate_module(lone, module, value); + } + + lone_reader_finalize(lone, &reader); + lone_garbage_collector(lone); +} + +struct lone_value *lone_module_load(struct lone_lisp *lone, struct lone_value *name) +{ + struct lone_value *module; + bool not_found; + int file_descriptor; + + module = lone_module_get_or_create(lone, name, ¬_found); + + if (not_found) { + file_descriptor = lone_module_search(lone, name); + lone_module_load_from_file_descriptor(lone, module, file_descriptor); + linux_close(file_descriptor); + } + + return module; +} + +void lone_module_load_null_from_file_descriptor(struct lone_lisp *lone, int file_descriptor) +{ + lone_module_load_from_file_descriptor(lone, lone_module_null(lone), file_descriptor); +} + +void lone_module_load_null_from_standard_input(struct lone_lisp *lone) +{ + lone_module_load_null_from_file_descriptor(lone, 0); +} + +void lone_export(struct lone_lisp *lone, struct lone_value *module, struct lone_value *symbol) +{ + if (!lone_is_symbol(symbol)) { /* only symbols can be exported */ linux_exit(-1); } + lone_vector_push(lone, module->module.exports, symbol); +} + +void lone_set_and_export(struct lone_lisp *lone, struct lone_value *module, struct lone_value *symbol, struct lone_value *value) +{ + lone_export(lone, module, symbol); + lone_table_set(lone, module->module.environment, symbol, value); +} + +struct lone_value *lone_primitive_export(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) +{ + struct lone_value *head, *symbol; + + for (head = arguments; !lone_is_nil(head); head = lone_list_rest(head)) { + symbol = lone_list_first(head); + + lone_export(lone, module, symbol); + } + + return lone_nil(lone); +} + +struct lone_import_specification { + struct lone_value *module; /* module value to import from */ + struct lone_value *symbols; /* list of symbols to import */ + struct lone_value *environment; /* environment to import symbols to */ + + bool prefixed; /* whether to prefix symbols */ +}; + +static struct lone_value *lone_prefix_module_name(struct lone_lisp *lone, struct lone_value *module, struct lone_value *symbol) +{ + struct lone_value *arguments = lone_list_flatten(lone, lone_list_build(lone, 2, module->module.name, symbol)), + *dot = lone_intern_c_string(lone, "."); + + return lone_symbol_transfer_bytes(lone, lone_join(lone, dot, arguments, lone_has_bytes), true); +} + +static void lone_import_specification(struct lone_lisp *lone, struct lone_import_specification *spec) +{ + size_t i; + struct lone_value *module = spec->module, *symbols = spec->symbols, *environment = spec->environment, *exports = module->module.exports, + *symbol, *value; + + /* bind either the exported or the specified symbols: (import (module)), (import (module x f)) */ + for (i = 0; i < symbols->vector.count; ++i) { + symbol = lone_vector_get_value_at(lone, symbols, i); + if (!lone_is_symbol(symbol)) { /* name not a symbol: (import (module 10)) */ linux_exit(-1); } + + if (symbols != exports && !lone_vector_contains(exports, symbol)) { + /* attempt to import private symbol */ linux_exit(-1); + } + + value = lone_table_get(lone, module->module.environment, symbol); + + if (spec->prefixed) { + symbol = lone_prefix_module_name(lone, spec->module, symbol); + } + + lone_table_set(lone, environment, symbol, value); + } +} + +static void lone_primitive_import_form(struct lone_lisp *lone, struct lone_import_specification *spec, struct lone_value *argument) +{ + struct lone_value *name; + + if (lone_is_nil(argument)) { /* nothing to import: (import ()) */ linux_exit(-1); } + + switch (argument->type) { + case LONE_SYMBOL: + /* (import module) */ + name = argument; + argument = lone_nil(lone); + break; + case LONE_LIST: + /* (import (module)), (import (module symbol)) */ + name = lone_list_first(argument); + argument = lone_list_rest(argument); + break; + case LONE_MODULE: + case LONE_FUNCTION: case LONE_PRIMITIVE: + case LONE_TEXT: case LONE_BYTES: + case LONE_VECTOR: case LONE_TABLE: + case LONE_INTEGER: case LONE_POINTER: + /* not a supported import argument type */ linux_exit(-1); + } + + spec->module = lone_module_load(lone, name); + if (lone_is_nil(spec->module)) { /* module not found: (import non-existent), (import (non-existent)) */ linux_exit(-1); } + + spec->symbols = lone_is_nil(argument)? spec->module->module.exports : lone_list_to_vector(lone, argument); + + lone_import_specification(lone, spec); +} + +struct lone_value *lone_primitive_import(struct lone_lisp *lone, struct lone_value *module, struct lone_value *environment, struct lone_value *arguments, struct lone_value *closure) +{ + struct lone_import_specification spec; + struct lone_value *prefixed = lone_intern_c_string(lone, "prefixed"), + *unprefixed = lone_intern_c_string(lone, "unprefixed"), + *argument; + + if (lone_is_nil(arguments)) { /* nothing to import: (import) */ linux_exit(-1); } + + spec.environment = environment; + spec.prefixed = false; + + for (/* argument */; !lone_is_nil(arguments); arguments = lone_list_rest(arguments)) { + argument = lone_list_first(arguments); + if (lone_is_list(argument)) { + lone_primitive_import_form(lone, &spec, argument); + } else if (lone_is_symbol(argument)) { + if (lone_is_equivalent(argument, prefixed)) { spec.prefixed = true; } + else if (lone_is_equivalent(argument, unprefixed)) { spec.prefixed = false; } + } else { + /* invalid import argument */ linux_exit(-1); + } + } + + return lone_nil(lone); +} + +void lone_module_path_push(struct lone_lisp *lone, struct lone_value *directory) +{ + lone_vector_push(lone, lone->modules.path, directory); +} + +void lone_module_path_push_c_string(struct lone_lisp *lone, char *directory) +{ + lone_module_path_push(lone, lone_text_create_from_c_string(lone, directory)); +} + +void lone_module_path_push_va_list(struct lone_lisp *lone, size_t count, va_list directories) +{ + struct lone_value *directory; + size_t i; + + for (i = 0; i < count; ++i) { + directory = va_arg(directories, struct lone_value *); + lone_module_path_push(lone, directory); + } +} + +void lone_module_path_push_all(struct lone_lisp *lone, size_t count, ...) +{ + va_list directories; + + va_start(directories, count); + lone_module_path_push_va_list(lone, count, directories); + va_end(directories); +} diff --git a/source/lone/modules/intrinsic.c b/source/lone/modules/intrinsic.c new file mode 100644 index 00000000..a94e41b6 --- /dev/null +++ b/source/lone/modules/intrinsic.c @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include + +#include +#include +#include +#include +#include + +void lone_modules_intrinsic_initialize(struct lone_lisp *lone, int argc, char **argv, char **envp, struct auxiliary *auxv) +{ + lone_module_linux_initialize(lone, argc, argv, envp, auxv); + lone_module_lone_initialize(lone); + lone_module_math_initialize(lone); + lone_module_text_initialize(lone); + lone_module_list_initialize(lone); +} diff --git a/source/lone/modules/linux.c b/source/lone/modules/linux.c new file mode 100644 index 00000000..1354bde7 --- /dev/null +++ b/source/lone/modules/linux.c @@ -0,0 +1,287 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +static void lone_auxiliary_value_to_table(struct lone_lisp *lone, struct lone_value *table, struct auxiliary *auxiliary_value) +{ + struct lone_value *key, *value; + switch (auxiliary_value->type) { + case AT_BASE_PLATFORM: + key = lone_intern_c_string(lone, "base-platform"); + value = lone_text_create_from_c_string(lone, auxiliary_value->as.c_string); + break; + case AT_PLATFORM: + key = lone_intern_c_string(lone, "platform"); + value = lone_text_create_from_c_string(lone, auxiliary_value->as.c_string); + break; + case AT_HWCAP: + key = lone_intern_c_string(lone, "hardware-capabilities"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + case AT_HWCAP2: + key = lone_intern_c_string(lone, "hardware-capabilities-2"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + case AT_FLAGS: + key = lone_intern_c_string(lone, "flags"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + case AT_NOTELF: + key = lone_intern_c_string(lone, "not-ELF"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + case AT_BASE: + key = lone_intern_c_string(lone, "interpreter-base-address"); + value = lone_pointer_create(lone, auxiliary_value->as.pointer, LONE_TO_UNKNOWN); + break; + case AT_ENTRY: + key = lone_intern_c_string(lone, "entry-point"); + value = lone_pointer_create(lone, auxiliary_value->as.pointer, LONE_TO_UNKNOWN); + break; + case AT_SYSINFO_EHDR: + key = lone_intern_c_string(lone, "vDSO"); + value = lone_pointer_create(lone, auxiliary_value->as.pointer, LONE_TO_UNKNOWN); + break; + case AT_PHDR: + key = lone_intern_c_string(lone, "program-headers-address"); + value = lone_pointer_create(lone, auxiliary_value->as.pointer, LONE_TO_UNKNOWN); + break; + case AT_PHENT: + key = lone_intern_c_string(lone, "program-headers-entry-size"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + case AT_PHNUM: + key = lone_intern_c_string(lone, "program-headers-count"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + case AT_EXECFN: + key = lone_intern_c_string(lone, "executable-file-name"); + value = lone_text_create_from_c_string(lone, auxiliary_value->as.c_string); + break; + case AT_EXECFD: + key = lone_intern_c_string(lone, "executable-file-descriptor"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + case AT_UID: + key = lone_intern_c_string(lone, "user-id"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + case AT_EUID: + key = lone_intern_c_string(lone, "effective-user-id"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + case AT_GID: + key = lone_intern_c_string(lone, "group-id"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + case AT_EGID: + key = lone_intern_c_string(lone, "effective-group-id"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + case AT_PAGESZ: + key = lone_intern_c_string(lone, "page-size"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; +#ifdef AT_MINSIGSTKSZ + case AT_MINSIGSTKSZ: + key = lone_intern_c_string(lone, "minimum-signal-delivery-stack-size"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; +#endif + case AT_CLKTCK: + key = lone_intern_c_string(lone, "clock-tick"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + case AT_RANDOM: + key = lone_intern_c_string(lone, "random"); + value = lone_bytes_create(lone, auxiliary_value->as.pointer, 16); + break; + case AT_SECURE: + key = lone_intern_c_string(lone, "secure"); + value = lone_integer_create(lone, auxiliary_value->as.integer); + break; + default: + key = lone_intern_c_string(lone, "unknown"); + value = lone_list_create(lone, + lone_integer_create(lone, auxiliary_value->type), + lone_integer_create(lone, auxiliary_value->as.integer)); + } + + lone_table_set(lone, table, key, value); +} + +static struct lone_value *lone_auxiliary_vector_to_table(struct lone_lisp *lone, struct auxiliary *auxiliary_vector) +{ + struct lone_value *table = lone_table_create(lone, 32, 0); + size_t i; + + for (i = 0; auxiliary_vector[i].type != AT_NULL; ++i) { + lone_auxiliary_value_to_table(lone, table, &auxiliary_vector[i]); + } + + return table; +} + +static struct lone_value *lone_environment_to_table(struct lone_lisp *lone, char **c_strings) +{ + struct lone_value *table = lone_table_create(lone, 64, 0), *key, *value; + char *c_string_key, *c_string_value, *c_string; + + for (/* c_strings */; *c_strings; ++c_strings) { + c_string = *c_strings; + c_string_key = c_string; + c_string_value = ""; + + while (*c_string++) { + if (*c_string == '=') { + *c_string = '\0'; + c_string_value = c_string + 1; + break; + } + } + + key = lone_text_create_from_c_string(lone, c_string_key); + value = lone_text_create_from_c_string(lone, c_string_value); + lone_table_set(lone, table, key, value); + } + + return table; +} + +static struct lone_value *lone_arguments_to_list(struct lone_lisp *lone, int count, char **c_strings) +{ + struct lone_value *arguments = lone_list_create_nil(lone), *head; + int i; + + for (i = 0, head = arguments; i < count; ++i) { + head = lone_list_append(lone, head, lone_text_create_from_c_string(lone, c_strings[i])); + } + + return arguments; +} + +static void lone_fill_linux_system_call_table(struct lone_lisp *lone, struct lone_value *linux_system_call_table) +{ + size_t i; + + static struct linux_system_call { + char *symbol; + int number; + } linux_system_calls[] = { + + /* huge generated array initializer with all the system calls found on the host platform */ + #include + + }; + + for (i = 0; i < (sizeof(linux_system_calls)/sizeof(linux_system_calls[0])); ++i) { + lone_table_set(lone, linux_system_call_table, + lone_intern_c_string(lone, linux_system_calls[i].symbol), + lone_integer_create(lone, linux_system_calls[i].number)); + } +} + +void lone_module_linux_initialize(struct lone_lisp *lone, int argc, char **argv, char **envp, struct auxiliary *auxv) +{ + struct lone_value *name = lone_intern_c_string(lone, "linux"), + *module = lone_module_for_name(lone, name), + *linux_system_call_table = lone_table_create(lone, 1024, 0), + *count, *arguments, *environment, *auxiliary_vector, + *primitive; + + struct lone_function_flags flags = { .evaluate_arguments = true, .evaluate_result = false, .variable_arguments = true }; + primitive = lone_primitive_create(lone, "linux_system_call", lone_primitive_linux_system_call, linux_system_call_table, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "system-call"), primitive); + + lone_set_and_export(lone, module, lone_intern_c_string(lone, "system-call-table"), linux_system_call_table); + + lone_fill_linux_system_call_table(lone, linux_system_call_table); + + count = lone_integer_create(lone, argc); + arguments = lone_arguments_to_list(lone, argc, argv); + environment = lone_environment_to_table(lone, envp); + auxiliary_vector = lone_auxiliary_vector_to_table(lone, auxv); + + lone_set_and_export(lone, module, lone_intern_c_string(lone, "argument-count"), count); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "arguments"), arguments); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "environment"), environment); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "auxiliary-vector"), auxiliary_vector); + + lone_table_set(lone, lone->modules.loaded, name, module); +} + +static inline long lone_value_to_linux_system_call_number(struct lone_lisp *lone, struct lone_value *linux_system_call_table, struct lone_value *value) +{ + switch (value->type) { + case LONE_INTEGER: + return value->integer; + case LONE_BYTES: + case LONE_TEXT: + case LONE_SYMBOL: + return lone_table_get(lone, linux_system_call_table, value)->integer; + case LONE_MODULE: + case LONE_FUNCTION: + case LONE_PRIMITIVE: + case LONE_LIST: + case LONE_VECTOR: + case LONE_TABLE: + case LONE_POINTER: + linux_exit(-1); + } +} + +static inline long lone_value_to_linux_system_call_argument(struct lone_value *value) +{ + switch (value->type) { + case LONE_INTEGER: return value->integer; + case LONE_POINTER: return (long) value->pointer.address; + case LONE_BYTES: case LONE_TEXT: case LONE_SYMBOL: return (long) value->bytes.pointer; + case LONE_PRIMITIVE: return (long) value->primitive.function; + case LONE_FUNCTION: case LONE_LIST: case LONE_VECTOR: case LONE_TABLE: case LONE_MODULE: linux_exit(-1); + } +} + +LONE_PRIMITIVE(linux_system_call) +{ + struct lone_value *linux_system_call_table = closure, *argument; + long result, number, args[6]; + unsigned char i; + + if (lone_is_nil(arguments)) { /* need at least the system call number */ linux_exit(-1); } + argument = lone_list_first(arguments); + number = lone_value_to_linux_system_call_number(lone, linux_system_call_table, argument); + arguments = lone_list_rest(arguments); + + for (i = 0; i < 6; ++i) { + if (lone_is_nil(arguments)) { + args[i] = 0; + } else { + argument = lone_list_first(arguments); + args[i] = lone_value_to_linux_system_call_argument(argument); + arguments = lone_list_rest(arguments); + } + } + + if (!lone_is_nil(arguments)) { /* too many arguments given */ linux_exit(-1); } + + result = linux_system_call_6(number, args[0], args[1], args[2], args[3], args[4], args[5]); + + return lone_integer_create(lone, result); +} diff --git a/source/lone/modules/list.c b/source/lone/modules/list.c new file mode 100644 index 00000000..7ec56365 --- /dev/null +++ b/source/lone/modules/list.c @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +void lone_module_list_initialize(struct lone_lisp *lone) +{ + struct lone_value *name = lone_intern_c_string(lone, "list"), + *module = lone_module_for_name(lone, name), + *primitive; + + struct lone_function_flags flags = { .evaluate_arguments = true, .evaluate_result = false, .variable_arguments = true }; + + primitive = lone_primitive_create(lone, "construct", lone_primitive_list_construct, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "construct"), primitive); + + primitive = lone_primitive_create(lone, "first", lone_primitive_list_first, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "first"), primitive); + + primitive = lone_primitive_create(lone, "rest", lone_primitive_list_rest, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "rest"), primitive); + + primitive = lone_primitive_create(lone, "map", lone_primitive_list_map, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "map"), primitive); + + primitive = lone_primitive_create(lone, "reduce", lone_primitive_list_reduce, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "reduce"), primitive); + + primitive = lone_primitive_create(lone, "flatten", lone_primitive_list_flatten, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "flatten"), primitive); + + lone_table_set(lone, lone->modules.loaded, name, module); +} + + +LONE_PRIMITIVE(list_construct) +{ + struct lone_value *first, *rest; + + if (lone_is_nil(arguments)) { /* no arguments given: (construct) */ linux_exit(-1); } + + first = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + if (lone_is_nil(arguments)) { /* only one argument given: (construct first) */ linux_exit(-1); } + + rest = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + if (!lone_is_nil(arguments)) { /* more than two arguments given: (construct first rest extra) */ linux_exit(-1); } + + return lone_list_create(lone, first, rest); +} + +LONE_PRIMITIVE(list_first) +{ + struct lone_value *argument; + if (lone_is_nil(arguments)) { linux_exit(-1); } + argument = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + if (lone_is_nil(argument)) { linux_exit(-1); } + if (!lone_is_nil(arguments)) { linux_exit(-1); } + return lone_list_first(argument); +} + +LONE_PRIMITIVE(list_rest) +{ + struct lone_value *argument; + if (lone_is_nil(arguments)) { linux_exit(-1); } + argument = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + if (lone_is_nil(argument)) { linux_exit(-1); } + if (!lone_is_nil(arguments)) { linux_exit(-1); } + return lone_list_rest(argument); +} + +LONE_PRIMITIVE(list_map) +{ + struct lone_value *function, *list, *results, *head; + + if (lone_is_nil(arguments)) { /* arguments not given */ linux_exit(-1); } + function = lone_list_first(arguments); + if (!lone_is_applicable(function)) { /* not given an applicable value */ linux_exit(-1); } + arguments = lone_list_rest(arguments); + list = lone_list_first(arguments); + if (!lone_is_list(list)) { /* can only map functions to lists */ linux_exit(-1); } + arguments = lone_list_rest(arguments); + if (!lone_is_nil(arguments)) { /* too many arguments given */ linux_exit(-1); } + + results = lone_list_create_nil(lone); + + for (head = results; !lone_is_nil(list); list = lone_list_rest(list)) { + arguments = lone_list_create(lone, lone_list_first(list), lone_nil(lone)); + head = lone_list_append(lone, head, lone_apply(lone, module, environment, function, arguments)); + } + + return results; +} + +LONE_PRIMITIVE(list_reduce) +{ + struct lone_value *function, *list, *result; + + if (lone_is_nil(arguments)) { /* arguments not given */ linux_exit(-1); } + function = lone_list_first(arguments); + if (!lone_is_applicable(function)) { /* not given an applicable value */ linux_exit(-1); } + arguments = lone_list_rest(arguments); + result = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + list = lone_list_first(arguments); + if (!lone_is_list(list)) { /* can only map functions to lists */ linux_exit(-1); } + arguments = lone_list_rest(arguments); + if (!lone_is_nil(arguments)) { /* too many arguments given */ linux_exit(-1); } + + for (/* list */; !lone_is_nil(list); list = lone_list_rest(list)) { + arguments = lone_list_build(lone, 2, result, lone_list_first(list)); + result = lone_apply(lone, module, environment, function, arguments); + } + + return result; +} + +LONE_PRIMITIVE(list_flatten) +{ + return lone_list_flatten(lone, arguments); +} diff --git a/source/lone/modules/lone.c b/source/lone/modules/lone.c new file mode 100644 index 00000000..eeaf4196 --- /dev/null +++ b/source/lone/modules/lone.c @@ -0,0 +1,400 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +void lone_module_lone_initialize(struct lone_lisp *lone) +{ + struct lone_value *name = lone_intern_c_string(lone, "lone"), + *module = lone_module_for_name(lone, name), + *primitive; + + struct lone_function_flags flags = { .evaluate_arguments = false, .evaluate_result = false, .variable_arguments = true }; + + primitive = lone_primitive_create(lone, "begin", lone_primitive_lone_begin, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "begin"), primitive); + + primitive = lone_primitive_create(lone, "when", lone_primitive_lone_when, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "when"), primitive); + + primitive = lone_primitive_create(lone, "unless", lone_primitive_lone_unless, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "unless"), primitive); + + primitive = lone_primitive_create(lone, "if", lone_primitive_lone_if, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "if"), primitive); + + primitive = lone_primitive_create(lone, "let", lone_primitive_lone_let, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "let"), primitive); + + primitive = lone_primitive_create(lone, "set", lone_primitive_lone_set, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "set"), primitive); + + primitive = lone_primitive_create(lone, "quote", lone_primitive_lone_quote, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "quote"), primitive); + + primitive = lone_primitive_create(lone, "quasiquote", lone_primitive_lone_quasiquote, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "quasiquote"), primitive); + + primitive = lone_primitive_create(lone, "lambda", lone_primitive_lone_lambda, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "lambda"), primitive); + + primitive = lone_primitive_create(lone, "lambda_bang", lone_primitive_lone_lambda_bang, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "lambda!"), primitive); + + primitive = lone_primitive_create(lone, "lambda_star", lone_primitive_lone_lambda_star, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "lambda*"), primitive); + + flags = (struct lone_function_flags) { .evaluate_arguments = true, .evaluate_result = false, .variable_arguments = true }; + + primitive = lone_primitive_create(lone, "print", lone_primitive_lone_print, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "print"), primitive); + + primitive = lone_primitive_create(lone, "is_list", lone_primitive_lone_is_list, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "list?"), primitive); + + primitive = lone_primitive_create(lone, "is_vector", lone_primitive_lone_is_vector, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "vector?"), primitive); + + primitive = lone_primitive_create(lone, "is_table", lone_primitive_lone_is_table, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "table?"), primitive); + + primitive = lone_primitive_create(lone, "is_symbol", lone_primitive_lone_is_symbol, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "symbol?"), primitive); + + primitive = lone_primitive_create(lone, "is_text", lone_primitive_lone_is_text, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "text?"), primitive); + + primitive = lone_primitive_create(lone, "is_integer", lone_primitive_lone_is_integer, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "integer?"), primitive); + + primitive = lone_primitive_create(lone, "is_identical", lone_primitive_lone_is_identical, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "identical?"), primitive); + + primitive = lone_primitive_create(lone, "is_equivalent", lone_primitive_lone_is_equivalent, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "equivalent?"), primitive); + + primitive = lone_primitive_create(lone, "is_equal", lone_primitive_lone_is_equal, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "equal?"), primitive); + + lone_table_set(lone, lone->modules.loaded, name, module); +} + + +LONE_PRIMITIVE(lone_begin) +{ + struct lone_value *value; + + for (value = lone_nil(lone); !lone_is_nil(arguments); arguments = lone_list_rest(arguments)) { + value = lone_list_first(arguments); + value = lone_evaluate(lone, module, environment, value); + } + + return value; +} + +LONE_PRIMITIVE(lone_when) +{ + struct lone_value *test; + + if (lone_is_nil(arguments)) { /* test not specified: (when) */ linux_exit(-1); } + test = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + + if (!lone_is_nil(lone_evaluate(lone, module, environment, test))) { + return lone_primitive_lone_begin(lone, module, environment, arguments, closure); + } + + return lone_nil(lone); +} + +LONE_PRIMITIVE(lone_unless) +{ + struct lone_value *test; + + if (lone_is_nil(arguments)) { /* test not specified: (unless) */ linux_exit(-1); } + test = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + + if (lone_is_nil(lone_evaluate(lone, module, environment, test))) { + return lone_primitive_lone_begin(lone, module, environment, arguments, closure); + } + + return lone_nil(lone); +} + +LONE_PRIMITIVE(lone_if) +{ + struct lone_value *value, *consequent, *alternative = 0; + + if (lone_is_nil(arguments)) { /* test not specified: (if) */ linux_exit(-1); } + value = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + + if (lone_is_nil(arguments)) { /* consequent not specified: (if test) */ linux_exit(-1); } + consequent = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + + if (!lone_is_nil(arguments)) { + alternative = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + if (!lone_is_nil(arguments)) { /* too many values (if test consequent alternative extra) */ linux_exit(-1); } + } + + if (!lone_is_nil(lone_evaluate(lone, module, environment, value))) { + return lone_evaluate(lone, module, environment, consequent); + } else if (alternative) { + return lone_evaluate(lone, module, environment, alternative); + } + + return lone_nil(lone); +} + +LONE_PRIMITIVE(lone_let) +{ + struct lone_value *bindings, *first, *second, *rest, *value, *new_environment; + + if (lone_is_nil(arguments)) { /* no variables to bind: (let) */ linux_exit(-1); } + bindings = lone_list_first(arguments); + if (!lone_is_list(bindings)) { /* expected list but got something else: (let 10) */ linux_exit(-1); } + + new_environment = lone_table_create(lone, 8, environment); + + while (1) { + if (lone_is_nil(bindings)) { break; } + first = lone_list_first(bindings); + if (!lone_is_symbol(first)) { /* variable names must be symbols: (let ("x")) */ linux_exit(-1); } + rest = lone_list_rest(bindings); + if (lone_is_nil(rest)) { /* incomplete variable/value list: (let (x 10 y)) */ linux_exit(-1); } + second = lone_list_first(rest); + value = lone_evaluate(lone, module, new_environment, second); + lone_table_set(lone, new_environment, first, value); + bindings = lone_list_rest(rest); + } + + value = lone_nil(lone); + + while (!lone_is_nil(arguments = lone_list_rest(arguments))) { + value = lone_evaluate(lone, module, new_environment, lone_list_first(arguments)); + } + + return value; +} + +LONE_PRIMITIVE(lone_set) +{ + struct lone_value *variable, *value; + + if (lone_is_nil(arguments)) { + /* no variable to set: (set) */ + linux_exit(-1); + } + + variable = lone_list_first(arguments); + if (!lone_is_symbol(variable)) { + /* variable names must be symbols: (set 10) */ + linux_exit(-1); + } + + arguments = lone_list_rest(arguments); + if (lone_is_nil(arguments)) { + /* value not specified: (set variable) */ + value = lone_nil(lone); + } else { + /* (set variable value) */ + value = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + } + + if (!lone_is_nil(arguments)) { /* too many arguments */ linux_exit(-1); } + + value = lone_evaluate(lone, module, environment, value); + lone_table_set(lone, environment, variable, value); + + return value; +} + +LONE_PRIMITIVE(lone_quote) +{ + if (!lone_is_nil(lone_list_rest(arguments))) { /* too many arguments: (quote x y) */ linux_exit(-1); } + return lone_list_first(arguments); +} + +LONE_PRIMITIVE(lone_quasiquote) +{ + struct lone_value *list, *head, *current, *element, *result, *first, *rest, *unquote, *splice; + bool escaping, splicing; + + if (!lone_is_nil(lone_list_rest(arguments))) { /* too many arguments: (quasiquote x y) */ linux_exit(-1); } + + unquote = lone_intern_c_string(lone, "unquote"); + splice = lone_intern_c_string(lone, "unquote*"); + head = list = lone_list_create_nil(lone); + arguments = lone_list_first(arguments); + + for (current = arguments; !lone_is_nil(current); current = lone_list_rest(current)) { + element = lone_list_first(current); + + if (lone_is_list(element)) { + first = lone_list_first(element); + rest = lone_list_rest(element); + + if (lone_is_equivalent(first, unquote)) { + escaping = true; + splicing = false; + } else if (lone_is_equivalent(first, splice)) { + escaping = true; + splicing = true; + } else { + escaping = false; + splicing = false; + } + + if (escaping) { + first = lone_list_first(rest); + rest = lone_list_rest(rest); + + if (!lone_is_nil(rest)) { /* too many arguments: (quasiquote (unquote x y) (unquote* x y)) */ linux_exit(-1); } + + result = lone_evaluate(lone, module, environment, first); + + if (splicing) { + if (lone_is_list(result)) { + for (/* result */; !lone_is_nil(result); result = lone_list_rest(result)) { + head = lone_list_append(lone, head, lone_list_first(result)); + } + } else { + head = lone_list_append(lone, head, result); + } + + } else { + head = lone_list_append(lone, head, result); + } + + } else { + head = lone_list_append(lone, head, element); + } + + } else { + head = lone_list_append(lone, head, element); + } + } + + return list; +} + +static struct lone_value *lone_primitive_lambda_with_flags(struct lone_lisp *lone, struct lone_value *environment, struct lone_value *arguments, struct lone_function_flags flags) +{ + struct lone_value *bindings, *code; + + bindings = lone_list_first(arguments); + if (!lone_is_list(bindings)) { /* parameters not a list: (lambda 10) */ linux_exit(-1); } + + code = lone_list_rest(arguments); + + return lone_function_create(lone, bindings, code, environment, flags); +} + +LONE_PRIMITIVE(lone_lambda) +{ + struct lone_function_flags flags = { + .evaluate_arguments = 1, + .evaluate_result = 0, + .variable_arguments = 0, + }; + + return lone_primitive_lambda_with_flags(lone, environment, arguments, flags); +} + +LONE_PRIMITIVE(lone_lambda_bang) +{ + struct lone_function_flags flags = { + .evaluate_arguments = 0, + .evaluate_result = 0, + .variable_arguments = 0, + }; + + return lone_primitive_lambda_with_flags(lone, environment, arguments, flags); +} + +LONE_PRIMITIVE(lone_lambda_star) +{ + struct lone_function_flags flags = { + .evaluate_arguments = 1, + .evaluate_result = 0, + .variable_arguments = 1, + }; + + return lone_primitive_lambda_with_flags(lone, environment, arguments, flags); +} + +LONE_PRIMITIVE(lone_is_list) +{ + return lone_apply_predicate(lone, arguments, lone_is_list); +} + +LONE_PRIMITIVE(lone_is_vector) +{ + return lone_apply_predicate(lone, arguments, lone_is_vector); +} + +LONE_PRIMITIVE(lone_is_table) +{ + return lone_apply_predicate(lone, arguments, lone_is_table); +} + +LONE_PRIMITIVE(lone_is_symbol) +{ + return lone_apply_predicate(lone, arguments, lone_is_symbol); +} + +LONE_PRIMITIVE(lone_is_text) +{ + return lone_apply_predicate(lone, arguments, lone_is_text); +} + +LONE_PRIMITIVE(lone_is_integer) +{ + return lone_apply_predicate(lone, arguments, lone_is_integer); +} + +LONE_PRIMITIVE(lone_is_identical) +{ + return lone_apply_comparator(lone, arguments, lone_is_identical); +} + +LONE_PRIMITIVE(lone_is_equivalent) +{ + return lone_apply_comparator(lone, arguments, lone_is_equivalent); +} + +LONE_PRIMITIVE(lone_is_equal) +{ + return lone_apply_comparator(lone, arguments, lone_is_equal); +} + +LONE_PRIMITIVE(lone_print) +{ + while (!lone_is_nil(arguments)) { + lone_print(lone, lone_list_first(arguments), 1); + linux_write(1, "\n", 1); + arguments = lone_list_rest(arguments); + } + + return lone_nil(lone); +} diff --git a/source/lone/modules/math.c b/source/lone/modules/math.c new file mode 100644 index 00000000..bdaf058c --- /dev/null +++ b/source/lone/modules/math.c @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +void lone_module_math_initialize(struct lone_lisp *lone) +{ + struct lone_value *name = lone_intern_c_string(lone, "math"), + *module = lone_module_for_name(lone, name), + *primitive; + + struct lone_function_flags flags = { .evaluate_arguments = true, .evaluate_result = false, .variable_arguments = true }; + + primitive = lone_primitive_create(lone, "add", lone_primitive_math_add, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "+"), primitive); + + primitive = lone_primitive_create(lone, "subtract", lone_primitive_math_subtract, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "-"), primitive); + + primitive = lone_primitive_create(lone, "multiply", lone_primitive_math_multiply, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "*"), primitive); + + primitive = lone_primitive_create(lone, "divide", lone_primitive_math_divide, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "/"), primitive); + + primitive = lone_primitive_create(lone, "is_less_than", lone_primitive_math_is_less_than, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "<"), primitive); + + primitive = lone_primitive_create(lone, "is_less_than_or_equal_to", lone_primitive_math_is_less_than_or_equal_to, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "<="), primitive); + + primitive = lone_primitive_create(lone, "is_greater_than", lone_primitive_math_is_greater_than, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, ">"), primitive); + + primitive = lone_primitive_create(lone, "is_greater_than_or_equal_to", lone_primitive_math_is_greater_than_or_equal_to, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, ">="), primitive); + + primitive = lone_primitive_create(lone, "sign", lone_primitive_math_sign, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "sign"), primitive); + + primitive = lone_primitive_create(lone, "is_zero", lone_primitive_math_is_zero, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "zero?"), primitive); + + primitive = lone_primitive_create(lone, "is_positive", lone_primitive_math_is_positive, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "positive?"), primitive); + + primitive = lone_primitive_create(lone, "is_negative", lone_primitive_math_is_negative, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "negative?"), primitive); + + lone_table_set(lone, lone->modules.loaded, name, module); +} + +static struct lone_value *lone_primitive_integer_operation(struct lone_lisp *lone, struct lone_value *arguments, char operation, long accumulator) +{ + struct lone_value *argument; + + if (lone_is_nil(arguments)) { /* wasn't given any arguments to operate on: (+), (-), (*) */ goto return_accumulator; } + + do { + argument = lone_list_first(arguments); + if (!lone_is_integer(argument)) { /* argument is not a number */ linux_exit(-1); } + + switch (operation) { + case '+': accumulator += argument->integer; break; + case '-': accumulator -= argument->integer; break; + case '*': accumulator *= argument->integer; break; + default: /* invalid primitive integer operation */ linux_exit(-1); + } + + arguments = lone_list_rest(arguments); + + } while (!lone_is_nil(arguments)); + +return_accumulator: + return lone_integer_create(lone, accumulator); +} + +LONE_PRIMITIVE(math_add) +{ + return lone_primitive_integer_operation(lone, arguments, '+', 0); +} + +LONE_PRIMITIVE(math_subtract) +{ + struct lone_value *first; + long accumulator; + + if (!lone_is_nil(arguments) && !lone_is_nil(lone_list_rest(arguments))) { + /* at least two arguments, set initial value to the first argument: (- 100 58) */ + first = lone_list_first(arguments); + if (!lone_is_integer(first)) { /* argument is not a number */ linux_exit(-1); } + accumulator = first->integer; + arguments = lone_list_rest(arguments); + } else { + accumulator = 0; + } + + return lone_primitive_integer_operation(lone, arguments, '-', accumulator); +} + +LONE_PRIMITIVE(math_multiply) +{ + return lone_primitive_integer_operation(lone, arguments, '*', 1); +} + +LONE_PRIMITIVE(math_divide) +{ + struct lone_value *dividend, *divisor; + + if (lone_is_nil(arguments)) { /* at least the dividend is required, (/) is invalid */ linux_exit(-1); } + dividend = lone_list_first(arguments); + if (!lone_is_integer(dividend)) { /* can't divide non-numbers: (/ "not a number") */ linux_exit(-1); } + arguments = lone_list_rest(arguments); + + if (lone_is_nil(arguments)) { + /* not given a divisor, return 1/x instead: (/ 2) = 1/2 */ + return lone_integer_create(lone, 1 / dividend->integer); + } else { + /* (/ x a b c ...) = x / (a * b * c * ...) */ + divisor = lone_primitive_integer_operation(lone, arguments, '*', 1); + return lone_integer_create(lone, dividend->integer / divisor->integer); + } +} + +LONE_PRIMITIVE(math_is_less_than) +{ + return lone_apply_comparator(lone, arguments, lone_integer_is_less_than); +} + +LONE_PRIMITIVE(math_is_less_than_or_equal_to) +{ + return lone_apply_comparator(lone, arguments, lone_integer_is_less_than_or_equal_to); +} + +LONE_PRIMITIVE(math_is_greater_than) +{ + return lone_apply_comparator(lone, arguments, lone_integer_is_greater_than); +} + +LONE_PRIMITIVE(math_is_greater_than_or_equal_to) +{ + return lone_apply_comparator(lone, arguments, lone_integer_is_greater_than_or_equal_to); +} + +LONE_PRIMITIVE(math_sign) +{ + struct lone_value *value; + if (lone_is_nil(arguments)) { /* no arguments: (sign) */ linux_exit(-1); } + value = lone_list_first(arguments); + if (!lone_is_nil(lone_list_rest(arguments))) { /* too many arguments: (sign 1 2 3) */ linux_exit(-1); } + + if (lone_is_integer(value)) { + return lone_integer_create(lone, value->integer > 0? 1 : value->integer < 0? -1 : 0); + } else { + linux_exit(-1); + } +} + +LONE_PRIMITIVE(math_is_zero) +{ + struct lone_value *value = lone_primitive_math_sign(lone, module, environment, arguments, closure); + if (lone_is_integer(value) && value->integer == 0) { return value; } + else { return lone_nil(lone); } +} + +LONE_PRIMITIVE(math_is_positive) +{ + struct lone_value *value = lone_primitive_math_sign(lone, module, environment, arguments, closure); + if (lone_is_integer(value) && value->integer > 0) { return value; } + else { return lone_nil(lone); } +} + +LONE_PRIMITIVE(math_is_negative) +{ + struct lone_value *value = lone_primitive_math_sign(lone, module, environment, arguments, closure); + if (lone_is_integer(value) && value->integer < 0) { return value; } + else { return lone_nil(lone); } +} diff --git a/source/lone/modules/text.c b/source/lone/modules/text.c new file mode 100644 index 00000000..a06fdbca --- /dev/null +++ b/source/lone/modules/text.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +void lone_module_text_initialize(struct lone_lisp *lone) +{ + struct lone_value *name = lone_intern_c_string(lone, "text"), + *module = lone_module_for_name(lone, name), + *primitive; + + struct lone_function_flags flags = { .evaluate_arguments = true, .evaluate_result = false, .variable_arguments = true }; + + primitive = lone_primitive_create(lone, "join", lone_primitive_text_join, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "join"), primitive); + + primitive = lone_primitive_create(lone, "concatenate", lone_primitive_text_concatenate, module, flags); + lone_set_and_export(lone, module, lone_intern_c_string(lone, "concatenate"), primitive); + + lone_table_set(lone, lone->modules.loaded, name, module); +} + +LONE_PRIMITIVE(text_join) +{ + return lone_text_transfer_bytes(lone, lone_join(lone, lone_list_first(arguments), lone_list_rest(arguments), lone_is_text), true); +} + +LONE_PRIMITIVE(text_concatenate) +{ + return lone_text_transfer_bytes(lone, lone_concatenate(lone, arguments, lone_is_text), true); +} diff --git a/source/lone/types.c b/source/lone/types.c new file mode 100644 index 00000000..9bf986e6 --- /dev/null +++ b/source/lone/types.c @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include + +#include + +bool lone_has_same_type(struct lone_value *x, struct lone_value *y) +{ + return x->type == y->type; +} + +bool lone_is_module(struct lone_value *value) +{ + return value->type == LONE_MODULE; +} + +bool lone_is_function(struct lone_value *value) +{ + return value->type == LONE_FUNCTION; +} + +bool lone_is_primitive(struct lone_value *value) +{ + return value->type == LONE_PRIMITIVE; +} + +bool lone_is_applicable(struct lone_value *value) +{ + return lone_is_function(value) || lone_is_primitive(value); +} + +bool lone_is_list(struct lone_value *value) +{ + return value->type == LONE_LIST; +} + +bool lone_is_vector(struct lone_value *value) +{ + return value->type == LONE_VECTOR; +} + +bool lone_is_table(struct lone_value *value) +{ + return value->type == LONE_TABLE; +} + +bool lone_is_nil(struct lone_value *value) +{ + return lone_is_list(value) && value->list.first == 0 && value->list.rest == 0; +} + +bool lone_has_bytes(struct lone_value *value) +{ + return value->type == LONE_TEXT || value->type == LONE_SYMBOL || value->type == LONE_BYTES; +} + +bool lone_is_bytes(struct lone_value *value) +{ + return value->type == LONE_BYTES; +} + +bool lone_is_text(struct lone_value *value) +{ + return value->type == LONE_TEXT; +} + +bool lone_is_symbol(struct lone_value *value) +{ + return value->type == LONE_SYMBOL; +} + +bool lone_is_integer(struct lone_value *value) +{ + return value->type == LONE_INTEGER; +} + +bool lone_is_pointer(struct lone_value *value) +{ + return value->type == LONE_POINTER; +} + +bool lone_is_identical(struct lone_value *x, struct lone_value *y) +{ + return x == y; +} + +bool lone_is_equivalent(struct lone_value *x, struct lone_value *y) +{ + if (lone_is_identical(x, y)) { return true; } + if (!lone_has_same_type(x, y)) { return false; } + + switch (x->type) { + case LONE_SYMBOL: + case LONE_TEXT: + case LONE_BYTES: + return lone_bytes_equals(x->bytes, y->bytes); + case LONE_INTEGER: + return x->integer == y->integer; + case LONE_POINTER: + return x->pointer.address == y->pointer.address; + + case LONE_MODULE: case LONE_FUNCTION: case LONE_PRIMITIVE: + case LONE_LIST: case LONE_VECTOR: case LONE_TABLE: + return lone_is_identical(x, y); + } +} + +bool lone_list_is_equal(struct lone_value *x, struct lone_value *y) +{ + return lone_is_equal(x->list.first, y->list.first) && lone_is_equal(x->list.rest, y->list.rest); +} + +bool lone_vector_is_equal(struct lone_value *x, struct lone_value *y) +{ + size_t i; + + if (x->vector.count != y->vector.count) return false; + + for (i = 0; i < x->vector.count; ++i) { + if (!lone_is_equal(x->vector.values[i], y->vector.values[i])) { + return false; + } + } + + return true; +} + +bool lone_table_is_equal(struct lone_value *x, struct lone_value *y) +{ + return lone_is_identical(x, y); +} + +bool lone_is_equal(struct lone_value *x, struct lone_value *y) +{ + if (lone_is_identical(x, y)) { return true; } + if (!lone_has_same_type(x, y)) { return false; } + + switch (x->type) { + case LONE_LIST: + return lone_list_is_equal(x, y); + case LONE_VECTOR: + return lone_vector_is_equal(x, y); + case LONE_TABLE: + return lone_table_is_equal(x, y); + + case LONE_MODULE: case LONE_FUNCTION: case LONE_PRIMITIVE: + case LONE_SYMBOL: case LONE_TEXT: case LONE_BYTES: + case LONE_INTEGER: case LONE_POINTER: + return lone_is_equivalent(x, y); + } +} + +bool lone_integer_is_less_than(struct lone_value *x, struct lone_value *y) +{ + if (!(lone_is_integer(x) && lone_is_integer(y))) { /* can't compare non-integers */ linux_exit(-1); } + + if (x->integer < y->integer) { return true; } + else { return false; } +} + +bool lone_integer_is_less_than_or_equal_to(struct lone_value *x, struct lone_value *y) +{ + if (!(lone_is_integer(x) && lone_is_integer(y))) { /* can't compare non-integers */ linux_exit(-1); } + + if (x->integer <= y->integer) { return true; } + else { return false; } +} + +bool lone_integer_is_greater_than(struct lone_value *x, struct lone_value *y) +{ + if (!(lone_is_integer(x) && lone_is_integer(y))) { /* can't compare non-integers */ linux_exit(-1); } + + if (x->integer > y->integer) { return true; } + else { return false; } +} + +bool lone_integer_is_greater_than_or_equal_to(struct lone_value *x, struct lone_value *y) +{ + if (!(lone_is_integer(x) && lone_is_integer(y))) { /* can't compare non-integers */ linux_exit(-1); } + + if (x->integer >= y->integer) { return true; } + else { return false; } +} + +bool lone_bytes_equals(struct lone_bytes x, struct lone_bytes y) +{ + if (x.count != y.count) return false; + for (size_t i = 0; i < x.count; ++i) if (x.pointer[i] != y.pointer[i]) return false; + return true; +} + +bool lone_bytes_equals_c_string(struct lone_bytes bytes, char *c_string) +{ + struct lone_bytes c_string_bytes = { lone_c_string_length(c_string), (unsigned char *) c_string }; + return lone_bytes_equals(bytes, c_string_bytes); +} diff --git a/source/lone/utilities.c b/source/lone/utilities.c new file mode 100644 index 00000000..5e319dcb --- /dev/null +++ b/source/lone/utilities.c @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +struct lone_value *lone_apply_predicate(struct lone_lisp *lone, struct lone_value *arguments, lone_predicate function) +{ + if (lone_is_nil(arguments) || !lone_is_nil(lone_list_rest(arguments))) { /* predicates accept exactly one argument */ linux_exit(-1); } + return function(lone_list_first(arguments)) ? lone_true(lone) : lone_nil(lone); +} + +struct lone_value *lone_apply_comparator(struct lone_lisp *lone, struct lone_value *arguments, lone_comparator function) +{ + struct lone_value *argument, *next; + + while (1) { + if (lone_is_nil(arguments)) { break; } + argument = lone_list_first(arguments); + arguments = lone_list_rest(arguments); + next = lone_list_first(arguments); + + if (next && !function(argument, next)) { return lone_nil(lone); } + } + + return lone_true(lone); +} + +struct lone_bytes lone_join(struct lone_lisp *lone, struct lone_value *separator, struct lone_value *arguments, lone_predicate is_valid) +{ + struct lone_value *head, *argument; + unsigned char *joined; + size_t total = 0, position = 0; + + if (!is_valid) { is_valid = lone_has_bytes; } + if (is_valid != lone_has_bytes && is_valid != lone_is_bytes && + is_valid != lone_is_text && is_valid != lone_is_symbol) { + /* invalid predicate function */ linux_exit(-1); + } + + if (separator && !lone_is_nil(separator)) { + if (!is_valid(separator)) { linux_exit(-1); } + } + + for (head = arguments; head && !lone_is_nil(head); head = lone_list_rest(head)) { + argument = lone_list_first(head); + if (!is_valid(argument)) { linux_exit(-1); } + + total += argument->bytes.count; + if (separator && !lone_is_nil(separator)) { + if (!lone_is_nil(lone_list_rest(head))) { total += separator->bytes.count; } + } + } + + joined = lone_allocate(lone, total + 1); + + for (head = arguments; head && !lone_is_nil(head); head = lone_list_rest(head)) { + argument = lone_list_first(head); + + lone_memory_move(argument->bytes.pointer, joined + position, argument->bytes.count); + position += argument->bytes.count; + + if (separator && !lone_is_nil(separator)) { + if (!lone_is_nil(lone_list_rest(head))) { + lone_memory_move(separator->bytes.pointer, joined + position, separator->bytes.count); + position += separator->bytes.count; + } + } + } + + joined[total] = '\0'; + + return (struct lone_bytes) { .count = total, .pointer = joined }; +} + +struct lone_bytes lone_concatenate(struct lone_lisp *lone, struct lone_value *arguments, lone_predicate is_valid) +{ + return lone_join(lone, 0, arguments, is_valid); +} + +struct lone_bytes lone_get_auxiliary_random(struct auxiliary *value) +{ + struct lone_bytes random = { 0, 0 }; + + for (/* value */; value->type != AT_NULL; ++value) { + if (value->type == AT_RANDOM) { + random.pointer = value->as.pointer; + random.count = 16; + } + } + + return random; +} diff --git a/source/lone/value.c b/source/lone/value.c new file mode 100644 index 00000000..762fb860 --- /dev/null +++ b/source/lone/value.c @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include + +struct lone_value *lone_value_create(struct lone_lisp *lone) +{ + return lone_heap_allocate_value(lone); +} diff --git a/source/lone/value/bytes.c b/source/lone/value/bytes.c new file mode 100644 index 00000000..f8b2452a --- /dev/null +++ b/source/lone/value/bytes.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include +#include + +#include + +struct lone_value *lone_bytes_transfer(struct lone_lisp *lone, unsigned char *pointer, size_t count, bool should_deallocate) +{ + struct lone_value *value = lone_value_create(lone); + value->type = LONE_BYTES; + value->bytes.count = count; + value->bytes.pointer = pointer; + value->should_deallocate_bytes = should_deallocate; + return value; +} + +struct lone_value *lone_bytes_transfer_bytes(struct lone_lisp *lone, struct lone_bytes bytes, bool should_deallocate) +{ + return lone_bytes_transfer(lone, bytes.pointer, bytes.count, should_deallocate); +} + +struct lone_value *lone_bytes_create(struct lone_lisp *lone, unsigned char *pointer, size_t count) +{ + unsigned char *copy = lone_allocate(lone, count + 1); + lone_memory_move(pointer, copy, count); + copy[count] = '\0'; + return lone_bytes_transfer(lone, copy, count, true); +} diff --git a/source/lone/value/function.c b/source/lone/value/function.c new file mode 100644 index 00000000..3dd8cb72 --- /dev/null +++ b/source/lone/value/function.c @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include + +#include +#include + +struct lone_value *lone_function_create( + struct lone_lisp *lone, + struct lone_value *arguments, + struct lone_value *code, + struct lone_value *environment, + struct lone_function_flags flags) +{ + struct lone_value *value = lone_value_create(lone); + value->type = LONE_FUNCTION; + value->function.arguments = arguments; + value->function.code = code; + value->function.environment = environment; + value->function.flags = flags; + return value; +} diff --git a/source/lone/value/integer.c b/source/lone/value/integer.c new file mode 100644 index 00000000..a37261cf --- /dev/null +++ b/source/lone/value/integer.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include + +#include + +struct lone_value *lone_integer_create(struct lone_lisp *lone, long integer) +{ + struct lone_value *value = lone_value_create(lone); + value->type = LONE_INTEGER; + value->integer = integer; + return value; +} + +struct lone_value *lone_integer_parse(struct lone_lisp *lone, unsigned char *digits, size_t count) +{ + size_t i = 0; + long integer = 0; + + switch (*digits) { case '+': case '-': ++i; break; } + + while (i < count) { + integer *= 10; + integer += digits[i++] - '0'; + } + + if (*digits == '-') { integer *= -1; } + + return lone_integer_create(lone, integer); +} diff --git a/source/lone/value/list.c b/source/lone/value/list.c new file mode 100644 index 00000000..9d042abb --- /dev/null +++ b/source/lone/value/list.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct lone_value *lone_list_create(struct lone_lisp *lone, struct lone_value *first, struct lone_value *rest) +{ + struct lone_value *value = lone_value_create(lone); + value->type = LONE_LIST; + value->list.first = first; + value->list.rest = rest; + return value; +} + +struct lone_value *lone_list_create_nil(struct lone_lisp *lone) +{ + return lone_list_create(lone, 0, 0); +} + +struct lone_value *lone_list_first(struct lone_value *value) +{ + return value->list.first; +} + +struct lone_value *lone_list_rest(struct lone_value *value) +{ + return value->list.rest; +} + +struct lone_value *lone_list_set_first(struct lone_value *list, struct lone_value *value) +{ + return list->list.first = value; +} + +struct lone_value *lone_list_set_rest(struct lone_value *list, struct lone_value *rest) +{ + return list->list.rest = rest; +} + +struct lone_value *lone_list_append(struct lone_lisp *lone, struct lone_value *list, struct lone_value *value) +{ + lone_list_set_first(list, value); + return lone_list_set_rest(list, lone_list_create_nil(lone)); +} + +struct lone_value *lone_list_build(struct lone_lisp *lone, size_t count, ...) +{ + struct lone_value *list = lone_list_create_nil(lone), *head = list, *argument; + va_list arguments; + size_t i; + + va_start(arguments, count); + + for (i = 0; i < count; ++i) { + argument = va_arg(arguments, struct lone_value *); + head = lone_list_append(lone, head, argument); + } + + va_end(arguments); + + return list; +} + +struct lone_value *lone_list_to_vector(struct lone_lisp *lone, struct lone_value *list) +{ + struct lone_value *vector = lone_vector_create(lone, 16), *head; + + for (head = list; !lone_is_nil(head); head = lone_list_rest(head)) { + lone_vector_push(lone, vector, lone_list_first(head)); + } + + return vector; +} + +struct lone_value *lone_list_flatten(struct lone_lisp *lone, struct lone_value *list) +{ + struct lone_value *flattened = lone_list_create_nil(lone), *head, *flat_head, *return_head, *first; + + for (head = list, flat_head = flattened; !lone_is_nil(head); head = lone_list_rest(head)) { + first = lone_list_first(head); + + if (lone_is_list(first)) { + return_head = lone_list_flatten(lone, first); + + for (/* return_head */; !lone_is_nil(return_head); return_head = lone_list_rest(return_head)) { + flat_head = lone_list_append(lone, flat_head, lone_list_first(return_head)); + } + + } else { + flat_head = lone_list_append(lone, flat_head, first); + } + } + + return flattened; +} diff --git a/source/lone/value/module.c b/source/lone/value/module.c new file mode 100644 index 00000000..8b2f81cf --- /dev/null +++ b/source/lone/value/module.c @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include +#include + +#include +#include +#include + +struct lone_value *lone_module_create(struct lone_lisp *lone, struct lone_value *name) +{ + struct lone_value *value = lone_value_create(lone); + value->type = LONE_MODULE; + value->module.name = name; + value->module.environment = lone_table_create(lone, 64, lone->modules.top_level_environment); + value->module.exports = lone_vector_create(lone, 16); + return value; +} diff --git a/source/lone/value/pointer.c b/source/lone/value/pointer.c new file mode 100644 index 00000000..4688b96d --- /dev/null +++ b/source/lone/value/pointer.c @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include +#include + +#include +#include + +struct lone_value *lone_pointer_create(struct lone_lisp *lone, void *pointer, enum lone_pointer_type type) +{ + struct lone_value *value = lone_value_create(lone); + value->type = LONE_POINTER; + value->pointer.type = type; + value->pointer.address = pointer; + return value; +} + +struct lone_value *lone_pointer_dereference(struct lone_lisp *lone, struct lone_value *pointer) +{ + enum lone_pointer_type type; + void *address; + + if (!lone_is_pointer(pointer)) { /* can't dereference this value */ linux_exit(-1); } + + type = pointer->pointer.type; + address = pointer->pointer.address; + + switch (type) { + case LONE_TO_U8: + return lone_integer_create(lone, *((uint8_t *) address)); + case LONE_TO_I8: + return lone_integer_create(lone, *((int8_t *) address)); + case LONE_TO_U16: + return lone_integer_create(lone, *((uint16_t *) address)); + case LONE_TO_I16: + return lone_integer_create(lone, *((int16_t *) address)); + case LONE_TO_U32: + return lone_integer_create(lone, *((uint32_t *) address)); + case LONE_TO_I32: + return lone_integer_create(lone, *((int32_t *) address)); + case LONE_TO_U64: + return lone_integer_create(lone, (long) *((uint64_t *) address)); + case LONE_TO_I64: + return lone_integer_create(lone, *((int64_t *) address)); + case LONE_TO_UNKNOWN: + /* cannot dereference pointer to unknown type */ linux_exit(-1); + } +} diff --git a/source/lone/value/primitive.c b/source/lone/value/primitive.c new file mode 100644 index 00000000..0b674a3f --- /dev/null +++ b/source/lone/value/primitive.c @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include + +#include +#include + +struct lone_value *lone_intern_c_string(struct lone_lisp *, char *); + +struct lone_value *lone_primitive_create( + struct lone_lisp *lone, + char *name, + lone_primitive function, + struct lone_value *closure, + struct lone_function_flags flags) +{ + struct lone_value *value = lone_value_create(lone); + value->type = LONE_PRIMITIVE; + value->primitive.name = lone_intern_c_string(lone, name); + value->primitive.function = function; + value->primitive.closure = closure; + value->primitive.flags = flags; + value->primitive.flags.variable_arguments = 1; + return value; +} + diff --git a/source/lone/value/symbol.c b/source/lone/value/symbol.c new file mode 100644 index 00000000..d134c462 --- /dev/null +++ b/source/lone/value/symbol.c @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include + +#include +#include + +struct lone_value *lone_symbol_transfer(struct lone_lisp *lone, unsigned char *text, size_t length, bool should_deallocate) +{ + struct lone_value *value = lone_bytes_transfer(lone, text, length, should_deallocate); + value->type = LONE_SYMBOL; + return value; +} + +struct lone_value *lone_symbol_transfer_bytes(struct lone_lisp *lone, struct lone_bytes bytes, bool should_deallocate) +{ + return lone_symbol_transfer(lone, bytes.pointer, bytes.count, should_deallocate); +} + +struct lone_value *lone_symbol_create(struct lone_lisp *lone, unsigned char *text, size_t length) +{ + struct lone_value *value = lone_bytes_create(lone, text, length); + value->type = LONE_SYMBOL; + return value; +} + +struct lone_value *lone_intern(struct lone_lisp *lone, unsigned char *bytes, size_t count, bool should_deallocate) +{ + struct lone_value *key, *value; + + key = should_deallocate? lone_symbol_create(lone, bytes, count) : lone_symbol_transfer(lone, bytes, count, should_deallocate); + value = lone_table_get(lone, lone->symbol_table, key); + + if (lone_is_nil(value)) { + value = key; + lone_table_set(lone, lone->symbol_table, key, value); + } + + return value; +} + +struct lone_value *lone_intern_c_string(struct lone_lisp *lone, char *c_string) +{ + return lone_intern(lone, (unsigned char *) c_string, lone_c_string_length(c_string), false); +} diff --git a/source/lone/value/table.c b/source/lone/value/table.c new file mode 100644 index 00000000..53b75f14 --- /dev/null +++ b/source/lone/value/table.c @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include +#include + +#include +#include + +#include + +struct lone_value *lone_table_create(struct lone_lisp *lone, size_t capacity, struct lone_value *prototype) +{ + struct lone_value *value = lone_value_create(lone); + value->type = LONE_TABLE; + value->table.prototype = prototype; + value->table.capacity = capacity; + value->table.count = 0; + value->table.entries = lone_allocate(lone, capacity * sizeof(*value->table.entries)); + + for (size_t i = 0; i < capacity; ++i) { + value->table.entries[i].key = 0; + value->table.entries[i].value = 0; + } + + return value; +} + +static unsigned long lone_table_compute_hash_for(struct lone_lisp *lone, struct lone_value *key, size_t capacity) +{ + return lone_hash(lone, key) % capacity; +} + +static size_t lone_table_entry_find_index_for(struct lone_lisp *lone, struct lone_value *key, struct lone_table_entry *entries, size_t capacity) +{ + size_t i = lone_table_compute_hash_for(lone, key, capacity); + + while (entries[i].key && !lone_is_equal(entries[i].key, key)) { + i = (i + 1) % capacity; + } + + return i; +} + +static int lone_table_entry_set(struct lone_lisp *lone, struct lone_table_entry *entries, size_t capacity, struct lone_value *key, struct lone_value *value) +{ + size_t i = lone_table_entry_find_index_for(lone, key, entries, capacity); + struct lone_table_entry *entry = &entries[i]; + + if (entry->key) { + entry->value = value; + return 0; + } else { + entry->key = key; + entry->value = value; + return 1; + } +} + +static void lone_table_resize(struct lone_lisp *lone, struct lone_value *table, size_t new_capacity) +{ + size_t old_capacity = table->table.capacity, i; + struct lone_table_entry *old = table->table.entries, + *new = lone_allocate(lone, new_capacity * sizeof(*new)); + + for (i = 0; i < new_capacity; ++i) { + new[i].key = 0; + new[i].value = 0; + } + + for (i = 0; i < old_capacity; ++i) { + if (old[i].key) { + lone_table_entry_set(lone, new, new_capacity, old[i].key, old[i].value); + } + } + + lone_deallocate(lone, old); + table->table.entries = new; + table->table.capacity = new_capacity; +} + +void lone_table_set(struct lone_lisp *lone, struct lone_value *table, struct lone_value *key, struct lone_value *value) +{ + if (table->table.count >= table->table.capacity / 2) { + lone_table_resize(lone, table, table->table.capacity * 2); + } + + if (lone_table_entry_set(lone, table->table.entries, table->table.capacity, key, value)) { + ++table->table.count; + } +} + +struct lone_value *lone_table_get(struct lone_lisp *lone, struct lone_value *table, struct lone_value *key) +{ + size_t capacity = table->table.capacity, i; + struct lone_table_entry *entries = table->table.entries, *entry; + struct lone_value *prototype = table->table.prototype; + + i = lone_table_entry_find_index_for(lone, key, entries, capacity); + entry = &entries[i]; + + if (entry->key) { + return entry->value; + } else if (prototype && !lone_is_nil(prototype)) { + return lone_table_get(lone, prototype, key); + } else { + return lone_nil(lone); + } +} + +void lone_table_delete(struct lone_lisp *lone, struct lone_value *table, struct lone_value *key) +{ + size_t capacity = table->table.capacity, i, j, k; + struct lone_table_entry *entries = table->table.entries; + + i = lone_table_entry_find_index_for(lone, key, entries, capacity); + + if (!entries[i].key) { return; } + + j = i; + while (1) { + j = (j + 1) % capacity; + if (!entries[j].key) { break; } + k = lone_table_compute_hash_for(lone, entries[j].key, capacity); + if ((j > i && (k <= i || k > j)) || (j < i && (k <= i && k > j))) { + entries[i] = entries[j]; + i = j; + } + } + + entries[i].key = 0; + entries[i].value = 0; + --table->table.count; +} diff --git a/source/lone/value/text.c b/source/lone/value/text.c new file mode 100644 index 00000000..1a9cd867 --- /dev/null +++ b/source/lone/value/text.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include +#include + +#include + +struct lone_value *lone_text_transfer(struct lone_lisp *lone, unsigned char *text, size_t length, bool should_deallocate) +{ + struct lone_value *value = lone_bytes_transfer(lone, text, length, should_deallocate); + value->type = LONE_TEXT; + return value; +} + +struct lone_value *lone_text_transfer_bytes(struct lone_lisp *lone, struct lone_bytes bytes, bool should_deallocate) +{ + return lone_text_transfer(lone, bytes.pointer, bytes.count, should_deallocate); +} + +struct lone_value *lone_text_create(struct lone_lisp *lone, unsigned char *text, size_t length) +{ + struct lone_value *value = lone_bytes_create(lone, text, length); + value->type = LONE_TEXT; + return value; +} + +struct lone_value *lone_text_create_from_c_string(struct lone_lisp *lone, char *c_string) +{ + return lone_text_transfer(lone, (unsigned char *) c_string, lone_c_string_length(c_string), false); +} + diff --git a/source/lone/value/vector.c b/source/lone/value/vector.c new file mode 100644 index 00000000..2137f8ad --- /dev/null +++ b/source/lone/value/vector.c @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +#include +#include +#include +#include +#include + +#include +#include + +struct lone_value *lone_vector_create(struct lone_lisp *lone, size_t capacity) +{ + struct lone_value *value = lone_value_create(lone); + value->type = LONE_VECTOR; + value->vector.capacity = capacity; + value->vector.count = 0; + value->vector.values = lone_allocate(lone, capacity * sizeof(*value->vector.values)); + for (size_t i = 0; i < value->vector.capacity; ++i) { value->vector.values[i] = 0; } + return value; +} + +void lone_vector_resize(struct lone_lisp *lone, struct lone_value *vector, size_t new_capacity) +{ + struct lone_value **new = lone_allocate(lone, new_capacity * sizeof(struct lone_value *)); + size_t i; + + for (i = 0; i < new_capacity; ++i) { + new[i] = i < vector->vector.count? vector->vector.values[i] : 0; + } + + lone_deallocate(lone, vector->vector.values); + + vector->vector.values = new; + vector->vector.capacity = new_capacity; + if (vector->vector.count > new_capacity) { vector->vector.count = new_capacity; } +} + +struct lone_value *lone_vector_get_value_at(struct lone_lisp *lone, struct lone_value *vector, size_t i) +{ + struct lone_value *value = i < vector->vector.capacity? vector->vector.values[i] : lone_nil(lone); + return value? value : lone_nil(lone); +} + +struct lone_value *lone_vector_get(struct lone_lisp *lone, struct lone_value *vector, struct lone_value *index) +{ + if (!lone_is_integer(index)) { /* only integer indexes supported */ linux_exit(-1); } + return lone_vector_get_value_at(lone, vector, (size_t) index->integer); +} + +void lone_vector_set_value_at(struct lone_lisp *lone, struct lone_value *vector, size_t i, struct lone_value *value) +{ + if (i >= vector->vector.capacity) { lone_vector_resize(lone, vector, i * 2); } + vector->vector.values[i] = value; + if (++i > vector->vector.count) { vector->vector.count = i; } +} + +void lone_vector_set(struct lone_lisp *lone, struct lone_value *vector, struct lone_value *index, struct lone_value *value) +{ + if (!lone_is_integer(index)) { /* only integer indexes supported */ linux_exit(-1); } + lone_vector_set_value_at(lone, vector, (size_t) index->integer, value); +} + +void lone_vector_push(struct lone_lisp *lone, struct lone_value *vector, struct lone_value *value) +{ + lone_vector_set_value_at(lone, vector, vector->vector.count, value); +} + +void lone_vector_push_va_list(struct lone_lisp *lone, struct lone_value *vector, size_t count, va_list arguments) +{ + struct lone_value *argument; + size_t i; + + for (i = 0; i < count; ++i) { + argument = va_arg(arguments, struct lone_value *); + lone_vector_push(lone, vector, argument); + } +} + +void lone_vector_push_all(struct lone_lisp *lone, struct lone_value *vector, size_t count, ...) +{ + va_list arguments; + + va_start(arguments, count); + lone_vector_push_va_list(lone, vector, count, arguments); + va_end(arguments); +} + +struct lone_value *lone_vector_build(struct lone_lisp *lone, size_t count, ...) +{ + struct lone_value *vector = lone_vector_create(lone, count); + va_list arguments; + + va_start(arguments, count); + lone_vector_push_va_list(lone, vector, count, arguments); + va_end(arguments); + + return vector; +} + +bool lone_vector_contains(struct lone_value *vector, struct lone_value *value) +{ + size_t i; + + for (i = 0; i < vector->vector.count; ++i) { + if (lone_is_equal(value, vector->vector.values[i])) { + return true; + } + } + + return false; +} diff --git a/test/linux/arguments/0/input b/test/linux/arguments/0/input index f643740d..5bd842b9 100644 --- a/test/linux/arguments/0/input +++ b/test/linux/arguments/0/input @@ -1,3 +1,3 @@ -(import (lone print) (linux arguments)) +(import (lone print) (list rest) (linux arguments)) -(print arguments) +(print (rest arguments)) diff --git a/test/linux/arguments/0/output b/test/linux/arguments/0/output index da9d9f35..607602cf 100644 --- a/test/linux/arguments/0/output +++ b/test/linux/arguments/0/output @@ -1 +1 @@ -("./lone") +nil diff --git a/test/linux/arguments/1/input b/test/linux/arguments/1/input index f643740d..5bd842b9 100644 --- a/test/linux/arguments/1/input +++ b/test/linux/arguments/1/input @@ -1,3 +1,3 @@ -(import (lone print) (linux arguments)) +(import (lone print) (list rest) (linux arguments)) -(print arguments) +(print (rest arguments)) diff --git a/test/linux/arguments/1/output b/test/linux/arguments/1/output index 562a1e49..9a682049 100644 --- a/test/linux/arguments/1/output +++ b/test/linux/arguments/1/output @@ -1 +1 @@ -("./lone" "argument") +("argument") diff --git a/test/linux/arguments/2/input b/test/linux/arguments/2/input index f643740d..5bd842b9 100644 --- a/test/linux/arguments/2/input +++ b/test/linux/arguments/2/input @@ -1,3 +1,3 @@ -(import (lone print) (linux arguments)) +(import (lone print) (list rest) (linux arguments)) -(print arguments) +(print (rest arguments)) diff --git a/test/linux/arguments/2/output b/test/linux/arguments/2/output index c3e9118c..1e417347 100644 --- a/test/linux/arguments/2/output +++ b/test/linux/arguments/2/output @@ -1 +1 @@ -("./lone" "1" "2") +("1" "2")