From 4399f5bae97fc3bf4084e87e102b59547142fd18 Mon Sep 17 00:00:00 2001 From: attilavs2 Date: Sun, 2 Mar 2025 22:28:05 +0100 Subject: [PATCH] feat: initial commit --- .gitignore | 6 ++ Makefile | 63 +++++++++++++++++ spec.md | 175 ++++++++++++++++++++++++++++++++++++++++++++++++ src/byte_defs.h | 81 ++++++++++++++++++++++ src/main.c | 11 +++ src/types.h | 17 +++++ 6 files changed, 353 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 spec.md create mode 100644 src/byte_defs.h create mode 100644 src/main.c create mode 100644 src/types.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51d1a0b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +build/ +build-win/ +build-tmp/ +*.amd64 +*.exe +*.swp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2692ad4 --- /dev/null +++ b/Makefile @@ -0,0 +1,63 @@ +# /!\ Warning /!\ +# Parallel build doesn't work with w64devkit + +OUTNAME = flisp + +CFLAGS = -O0 -Wall -Wextra -g -pipe + +CC = gcc + +LDFLAGS_UNIX = -lm +LDFLAGS_WIN = -lm + +# ===== End of what should be edited ===== + +OUTPUT = "${OUTNAME}.amd64" + +LDFLAGS = $(LDFLAGS_UNIX) + +BUILD_DIR = build +SRC_DIR = src + +OBJS = $(patsubst $(SRC_DIR)/%.c,build-tmp/%.o,$(wildcard $(SRC_DIR)/*.c)) + +all: | builddir build builddir2 + +windef: + $(eval OUTPUT = "$(OUTNAME).exe") + $(eval BUILD_DIR = build-win) + $(eval LDFLAGS = $(LDFLAGS_WIN)) + +builddir: + - mkdir $(BUILD_DIR) + - mkdir build-tmp + - rm -rf $(wildcard build-tmp/*) + - mv $(wildcard $(BUILD_DIR)/*.o) build-tmp/ + +build-tmp/%.o : $(SRC_DIR)/%.c + ${CC} -c $< -o $@ ${CFLAGS} + +build: $(OBJS) + ${CC} ${CFLAGS} -o ${OUTPUT} $(OBJS) ${LDFLAGS} + +builddir2: + mv $(OBJS) $(BUILD_DIR) + rm -rf build-tmp + +win: | windef all + +testwin: win + ./$(OUTPUT) + +test: all + @echo "===== Program output =====" + @./${OUTPUT} + +clean: + - rm -rf ${BUILD_DIR} + - rm -rf build-win + - rm -rf build-tmp + - rm "$(OUTNAME).amd64" "$(OUTNAME).exe" + +.NOTPARALLEL: builddir builddir2 +.PHONY: all test clean win windef builddir builddir2 testwin diff --git a/spec.md b/spec.md new file mode 100644 index 0000000..d2fa423 --- /dev/null +++ b/spec.md @@ -0,0 +1,175 @@ +# fLisp + +- S-expressions: + `( [arg0 ... argN])` + Will call function with optional args arg0-argN + `([*] [arg | *arg] [arg2])` + If arg is given, it will assign arg to variable, then return variable + If assigning to a vector or string : + - If `*` is specified before variable, it will replace the vector + - Otherwise, if only arg is given, it will return the element at position + arg of variable. If arg2 is given, it will set the element at position + arg of variable to arg2. + Examples: + ``` + fLisp : (let:vec:int a 5) + C : int a[5]; + fLisp : (a 0 69) + C : a[0] = 69; + fLisp : (let:int x)(x (a 0)) + C : int x = a[0]; + ``` + For vectors and strings, it's a shallow copy (data is the same) unless a `*` + is specified before [arg] + +- Compiled to bytecode + +In this document : +- <...> : + Mandatory argument +- [...] : + Optional argument +- | : + Or +## Core types + +- null : + Represents a null value - always evaluates to false +- int : + 32 bit signed integer, evaluates to false if zero and true otherwise +- fix : + 16:16 bit fixed point signed integer, same evaluation rules as ints +- float : + 32bit IEEE floating point value, same evaluation as ints (within + `FL_ESPILOǸ̀`) +- str: + ASCII character string - always evaluates to false +- fn: + Function, function "variables" are inherently of type fn - handle with care ! +- vec : + Vectors of any type, + +## Core functions + +- `(var[:type] [size])` : + Declares a global variable of optional type type and name name + if type is of format `:vec:`, the variable will be a vector + of type type, and optional size size (otherwise size 0) + (if type isn't specified it is inferred) + +- `(let[:type] )`: + Same as var, but variable is of local scope (recommended) + +- `(if [expressions])`: + If cond evaluates to true, will execute optional expressions + +- `(else [expressions])`: + Must follow a if, will execute if the if's cond evaluates to false + +- `(while [expressions])`: + while cond evalutates to true, will execute optional expressions + +- `(fn ( [arg0[:type] ... argN[:type]]) [expressions])` : + Declares a function of name name, optional args arg0-argN with optional + types (recommended to explicit them), that executes optional expressions + The name cannot be a variable + +- `(import [... libN])` : + Import the functions and variables from lib0-libN + +For the following numerical functions, values must be scalars (except + and =, +where they may be strings), and of the same type +- `(+ )`: + Returns a+b, if they are both strings they will be concatenated + +- `(- )`: + Returns a-b + +- `(* )`: + Returns a×b + +- `(/ )`: + Returns a/b + +- `(% )`: + Returns a%b + +- `(< )`: + Returns 1 if a < b, else 0 + +- ̀`(<= )`: + Returns 1 if a <= b, else 0 + +- `(= )̀̀`: + Returns 1 if a == b, else 0 + +- `(>= )`: + Returns 1 if a >= b, else 0 + +- `(> )`: + Returns 1 if a > b, else 0 + +## console functions +- `(input)`: + Will try to get input from the user, returns it as a string +- `(write )`: + Will try to cast var to string then print it to the console +- `(newline)`: + Will print a newline to the console (newline characters aren't escaped with + write) + +## gint functions + +TODO + +# Bytecode + +- 8 byte Value : + 2 bytes tag, 6 bytes value itself +- 4 byte bytecode ops: + 1 byte op, 3 byte args (named 1-3) + +## Bytecode types + +- `T_Null̀̀` -> null +- `T_Int` -> int +- `T_Fix` -> fix +- `T_Float` -> float +- `T_Str` -> string : + - If shorter than 4 chars (+ `\0`), is a "compact string" + - Otherwise, gets converted to a vector, inherently of chars +- `T_Fn` -> fn / function + +## Bytecode operations : + +if just 1, 2 or 3, means the value at pos 1,2,3 of the stack +if #1,#2,#3, the immediate value of 1,2,3 +if 1:2 / 1:2:3, means stitching 1,2,3 together as a 2 or 3 byte value +- `add` : 1+2 -> stack +- `sub` : 1-2 -> stack +- `mul` : 1×2 -> stack +- `div` : 1/2 -> stack +- `mod` : 1%2 -> stack +- `call` : calls 1 +- `ret` : returns to caller +- `mov` : 1 -> 2 +- `copy` : deepcopy of 1 -> 2 +- `get` : value at pos 2 of vector 1 -> stack +- `set` : set value at pos 2 of vector 1 to 3 +- `gt` : 1>2 -> stack +- `ge` : 1>=2 -> stack +- `eq` : 1==2 -> stack +- `jmp` : Jumps to bytecode at relative 2:3 +- `brt` : Jumps to bytecode at relative 2:3 if 1 +- `cast` : Casts 1 to the type of 2 -> 1 +- `fcast` : Casts 1 to the type of 2:3 -> 1 +- `init` : value of type 2:3 and size 1 -> stack +- `fcall` : calls function of hash 1:2:3 +- `pop` : pop value 1 +- `popl` : pops the last #2 values on stack, starting from #1 +- `cst0` : 2:3 -> LSW of 3 +- `cst1` : 2:3 -> MSW of 3 +- `iter` : Iterates on vector 1, keeping track with int value at 2, and sets + int value at 3 to 1 when done + to 1 + diff --git a/src/byte_defs.h b/src/byte_defs.h new file mode 100644 index 0000000..ee82ef8 --- /dev/null +++ b/src/byte_defs.h @@ -0,0 +1,81 @@ +#include "types.h" + +#pragma once + +enum OpTypes { + OP_add = 0, + OP_sub = 1, + OP_mul = 2, + OP_div = 3, + OP_mod = 4, + OP_call = 5, + OP_ret = 6, + OP_mov = 7, + OP_copy = 8, + OP_get = 9, + OP_set = 10, + OP_gt = 11, + OP_ge = 12, + OP_eq = 13, + OP_jmp = 14, + OP_brt = 15, + OP_cast = 16, + OP_fcast = 17, + OP_init = 18, + OP_fcall = 19, + OP_pop = 20, + OP_popl = 21, + OP_cst0 = 22, + OP_cst1 = 23, + OP_iter = 24 +}; + +typedef struct { + + u8 type; // OpTypes + u8 o1; + u8 o2; + u8 o3; + +} PACKED Opcode; + +typedef struct { + + u8 __padding[2]; + u32 value; + +} PACKED Value4B; + +typedef struct { + + u16 len; + u32 pos; // Offset from stack allocator + +} PACKED ValueArray; + +typedef struct { + + u8 len; + union { + char str[5]; + struct { + u8 __padding; + u32 pos; + }; + }; + +} PACKED ValueStr; + +typedef struct { + + uint is_array : 1; + uint type : 15; + + PACKED union { + Value4B v4B; + ValueArray varray; + }; + +} PACKED Value; + +_Static_assert(sizeof(Value) == 8); diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d3ce630 --- /dev/null +++ b/src/main.c @@ -0,0 +1,11 @@ +#include +#include +#include +#include + +#include "types.h" + +int main(){ + + return 0; +} diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..d2f2c2d --- /dev/null +++ b/src/types.h @@ -0,0 +1,17 @@ +#include + +#pragma once + +typedef int8_t i8; +typedef uint8_t u8; +typedef int16_t i16; +typedef uint16_t u16; +typedef int32_t i32; +typedef uint32_t u32; +typedef int64_t i64; +typedef uint64_t u64; + +typedef unsigned int uint; + +#define PACKED __attribute__((__packed__)) +