feat: initial commit

This commit is contained in:
attilavs2 2025-03-02 22:28:05 +01:00
commit 4399f5bae9
6 changed files with 353 additions and 0 deletions

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
build/
build-win/
build-tmp/
*.amd64
*.exe
*.swp

63
Makefile Normal file
View file

@ -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

175
spec.md Normal file
View file

@ -0,0 +1,175 @@
# fLisp
- S-expressions:
`(<function> [arg0 ... argN])`
Will call function with optional args arg0-argN
`([*]<variable> [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] <name> [size])` :
Declares a global variable of optional type type and name name
if type is of format `:vec:<type>`, 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] <name>)`:
Same as var, but variable is of local scope (recommended)
- `(if <cond> [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 <cond> [expressions])`:
while cond evalutates to true, will execute optional expressions
- `(fn (<name> [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 <lib0> [... 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
- `(+ <a> <b>)`:
Returns a+b, if they are both strings they will be concatenated
- `(- <a> <b>)`:
Returns a-b
- `(* <a> <b>)`:
Returns a×b
- `(/ <a> <b>)`:
Returns a/b
- `(% <a> <b>)`:
Returns a%b
- `(< <a> <b>)`:
Returns 1 if a < b, else 0
- ̀`(<= <a> <b>)`:
Returns 1 if a <= b, else 0
- `(= <a> <b>)̀̀`:
Returns 1 if a == b, else 0
- `(>= <a> <b>)`:
Returns 1 if a >= b, else 0
- `(> <a> <b>)`:
Returns 1 if a > b, else 0
## console functions
- `(input)`:
Will try to get input from the user, returns it as a string
- `(write <var>)`:
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

81
src/byte_defs.h Normal file
View file

@ -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);

11
src/main.c Normal file
View file

@ -0,0 +1,11 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "types.h"
int main(){
return 0;
}

17
src/types.h Normal file
View file

@ -0,0 +1,17 @@
#include <stdint.h>
#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__))