diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..1076ca2 --- /dev/null +++ b/.clang-format @@ -0,0 +1,3 @@ +BasedOnStyle: Google +IndentWidth: 4 +PointerAlignment: Left diff --git a/.env b/.env index e09c958..6eb74fb 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -SDK=0.2.4-alpine-3.19.1 \ No newline at end of file +SDK=0.2.5-alpine-3.19.1 diff --git a/.gitignore b/.gitignore index 10613c6..8c4be57 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ build CTestTestfile.cmake DartConfiguration.tcl *.cmake +*.ll diff --git a/CMakeLists.txt b/CMakeLists.txt index b5a44cb..f5eabfe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,9 +72,12 @@ add_custom_command(OUTPUT ${YACC_GENERATED_SOURCE_FILE} # Setup Glib 2.0 # # ------------------------------------------------ # +include(FindPkgConfig) find_package(PkgConfig REQUIRED) pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +link_libraries(PkgConfig::GLIB) + # ------------------------------------------------ # # TOML-C99 # # ------------------------------------------------ # @@ -95,6 +98,30 @@ set_target_properties(tomlc99 include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99) +link_libraries(tomlc99) + +# ------------------------------------------------ # +# Standard library # +# ------------------------------------------------ # + +add_subdirectory(lib) + +# ------------------------------------------------ # +# LLVM backend # +# ------------------------------------------------ # + +# Fetch LLVM link configuration +execute_process(COMMAND llvm-config --libs all + OUTPUT_VARIABLE LLVM_LIBS) +# Strip whitespace from output +string(STRIP "${LLVM_LIBS}" LLVM_LIBS) +# Link all targets to LLVM +link_libraries(${LLVM_LIBS}) + +execute_process(COMMAND llvm-config --includedir + OUTPUT_VARIABLE LLVM_INCLUDE_DIR) +string(STRIP "${LLVM_INCLUDE_DIR}" LLVM_INCLUDE_DIR) +include_directories(${LLVM_INCLUDE_DIR}) # ------------------------------------------------ # # Source # @@ -126,10 +153,6 @@ set_target_properties(release OUTPUT_NAME "gsc" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/release) -target_link_libraries(release PkgConfig::GLIB) - -target_link_libraries(release tomlc99) - # FIXME: cannot compile with /O2 because of /RTC1 flag if (MSVC) set(RELEASE_FLAGS) @@ -165,10 +188,6 @@ set_target_properties(debug OUTPUT_NAME "gsc" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/debug) -target_link_libraries(debug PkgConfig::GLIB) - -target_link_libraries(debug tomlc99) - if (MSVC) set(DEBUG_FLAGS /DEBUG) else() @@ -198,10 +217,6 @@ set_target_properties(check OUTPUT_NAME "gsc" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/check) -target_link_libraries(check PkgConfig::GLIB) - -target_link_libraries(check tomlc99) - if (MSVC) set(CHECK_FLAGS /DEBUG /WX) else() diff --git a/Dockerfile b/Dockerfile index bb287f7..a68c334 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -FROM servostar/gemstone:sdk-0.2.4-alpine-3.19.1 +FROM servostar/gemstone:sdk-0.2.5-alpine-3.19.1 LABEL authors="servostar" -LABEL version="0.2.4" +LABEL version="0.2.5" LABEL description="docker image for setting up the build pipeline on SDK" LABEL website="https://github.com/Servostar/gemstone" @@ -10,7 +10,9 @@ COPY --chown=lorang CMakeLists.txt /home/lorang/ COPY --chown=lorang run-check-test.sh /home/lorang/ COPY --chown=lorang .env /home/lorang/ COPY --chown=lorang run-docker-build.sh /home/lorang/ +COPY --chown=lorang run-lib-build.sh /home/lorang/ COPY --chown=lorang dep /home/lorang/dep +COPY --chown=lorang lib /home/lorang/lib COPY --chown=lorang .git /home/lorang/.git RUN cmake . diff --git a/dep/lldcl/CMakeLists.txt b/dep/lldcl/CMakeLists.txt new file mode 100644 index 0000000..c4cfbeb --- /dev/null +++ b/dep/lldcl/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.15...3.25) + +project(gemstone + VERSION 0.1.0 + DESCRIPTION "programming language compiler lld c++ layer" + LANGUAGES CXX) + +set(GEMSTONE_BINARY_DIR ${PROJECT_SOURCE_DIR}/../../bin) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# ------------------------------------------------ # +# LLVM # +# ------------------------------------------------ # + +# Fetch LLVM link configuration +execute_process(COMMAND llvm-config --libs all + OUTPUT_VARIABLE LLVM_LIBS) +# Strip whitespace from output +string(STRIP "${LLVM_LIBS}" LLVM_LIBS) +# Link all targets to LLVM +link_libraries(${LLVM_LIBS}) + +execute_process(COMMAND llvm-config --includedir + OUTPUT_VARIABLE LLVM_INCLUDE_DIR) +string(STRIP "${LLVM_INCLUDE_DIR}" LLVM_INCLUDE_DIR) +include_directories(${LLVM_INCLUDE_DIR}) + +file(GLOB_RECURSE SOURCE_FILES *.cpp) + +add_library(lldcl ${SOURCE_FILES}) +target_link_libraries(lldcl) +set_target_properties(lldcl + PROPERTIES + OUTPUT_NAME "lldcl" + ARCHIVE_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/dep) diff --git a/dep/lldcl/lldcl.cpp b/dep/lldcl/lldcl.cpp new file mode 100644 index 0000000..5591dc3 --- /dev/null +++ b/dep/lldcl/lldcl.cpp @@ -0,0 +1,50 @@ +// +// Created by servostar on 6/8/24. +// +// based on: https://github.com/llvm/llvm-project/blob/main/lld/unittests/AsLibAll/AllDrivers.cpp +// https://github.com/numba/llvmlite/blob/main/ffi/linker.cpp + +#include +#include + +/* + * Gemstone supports Windows (COFF, MinGW) and GNU/Linux (ELF) + */ + +LLD_HAS_DRIVER(coff) +LLD_HAS_DRIVER(elf) +LLD_HAS_DRIVER(mingw) +// LLD_HAS_DRIVER(macho) +// LLD_HAS_DRIVER(wasm) + +#define LLD_COFF_ELF_MINGW_DRIVER { {lld::WinLink, &lld::coff::link}, {lld::Gnu, &lld::elf::link}, {lld::MinGW, &lld::mingw::link} } + +extern "C" { + +/** + * @brief C-wrapper for lldMain which is written in C++ + * @param Argc + * @param Argv + * @param outstr + * @return + */ +int lld_main(int Argc, const char **Argv, const char **outstr) { + // StdOut + std::string stdout; + llvm::raw_string_ostream stdout_stream(stdout); + + // StdErr + std::string stderr; + llvm::raw_string_ostream stderr_stream(stderr); + + // convert arguments + std::vector Args(Argv, Argv + Argc); + + lld::Result result = lld::lldMain(Args, stdout_stream, stderr_stream, LLD_COFF_ELF_MINGW_DRIVER); + + *outstr = strdup(stderr.append(stdout).c_str()); + + return result.retCode; +} + +} // extern "C" diff --git a/examples/lib_common.c b/examples/lib_common.c new file mode 100644 index 0000000..29357f4 --- /dev/null +++ b/examples/lib_common.c @@ -0,0 +1,18 @@ +// +// Created by servostar on 6/10/24. +// + +#include + +extern void swap(float*, float*); + +int main(int argc, char* argv[]) { + float a = 2.0; + float b = 7.0; + + swap(&a, &b); + + printf("%f, %f\n", a, b); + + return 0; +} diff --git a/examples/lib_common.txt b/examples/lib_common.txt new file mode 100644 index 0000000..936ea72 --- /dev/null +++ b/examples/lib_common.txt @@ -0,0 +1,6 @@ + +fun swap(in out float: a, in out float: b) { + float: c = a + a = b + b = c +} diff --git a/examples/lib_fibonacci.c b/examples/lib_fibonacci.c new file mode 100644 index 0000000..6a422a7 --- /dev/null +++ b/examples/lib_fibonacci.c @@ -0,0 +1,18 @@ +// +// Created by servostar on 6/10/24. +// + +#include + +extern void fib(int*, int); + +int main(int argc, char* argv[]) { + + for (int i = 0; i < 7; i++) { + int r = 0; + fib(&r, i); + printf("%d\n", r); + } + + return 0; +} diff --git a/examples/lib_fibonacci.txt b/examples/lib_fibonacci.txt new file mode 100644 index 0000000..d5a02ff --- /dev/null +++ b/examples/lib_fibonacci.txt @@ -0,0 +1,17 @@ + +fun fib(in int: x, out int: c) { + + int: i = 0 + int: k = 1 + int: p = 1 + + while x < i { + int: t = k + k = k + p + p = t + + i = i + 1 + } + + c = k +} diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..8be6998 --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.15...3.25) + +project(gemstone_stdlib + VERSION 0.1.0 + DESCRIPTION "gemstone programming language standard library" + LANGUAGES C) + +set(CMAKE_C_STANDARD 23) +set(CMAKE_C_STANDARD_REQUIRED TRUE) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +include_directories(${PROJECT_SOURCE_DIR}/src) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/../bin/std") + +# add native module libraries + +file(GLOB_RECURSE STDLIB_IO_SOURCE_FILES src/io/*.c) +add_library(io ${STDLIB_IO_SOURCE_FILES}) + +file(GLOB_RECURSE STDLIB_MEM_SOURCE_FILES src/mem/*.c) +add_library(mem ${STDLIB_MEM_SOURCE_FILES}) + +file(GLOB_RECURSE STDLIB_OS_SOURCE_FILES src/os/*.c) +add_library(os ${STDLIB_OS_SOURCE_FILES}) diff --git a/lib/build.toml b/lib/build.toml new file mode 100644 index 0000000..85c5687 --- /dev/null +++ b/lib/build.toml @@ -0,0 +1,16 @@ +[project] +name = "gemstone standard library" +version = "0.1.0" +description = "Cross platform standard library for thr gemstone programming language." +license = "GPL-2.0" +authors = [ "Sven Vogel " ] + +[target.release] +root = "src/std.gem" +mode = "library" +output = "bin" +archive = "archive" +print_ast = false +print_asm = false +print_ir = false +opt = 3 diff --git a/lib/src/bool.gsc b/lib/src/bool.gsc new file mode 100644 index 0000000..682ad66 --- /dev/null +++ b/lib/src/bool.gsc @@ -0,0 +1,7 @@ + +import "def.gsc" + +type unsigned int: bool + +static bool: TRUE = 1 +static bool: FALSE = 0 diff --git a/lib/src/capi.h b/lib/src/capi.h new file mode 100644 index 0000000..0ef22ab --- /dev/null +++ b/lib/src/capi.h @@ -0,0 +1,14 @@ +// +// Created by servostar on 6/3/24. +// + +#ifndef GEMSTONE_STD_LIB_CAPI_H +#define GEMSTONE_STD_LIB_CAPI_H + +#if defined(_WIN32) || defined (_WIN64) +#define PLATFORM_WINDOWS +#elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__linux__) +#define PLATFORM_POSIX +#endif + +#endif // GEMSTONE_STD_LIB_CAPI_H diff --git a/lib/src/def.gsc b/lib/src/def.gsc new file mode 100644 index 0000000..8b57e7e --- /dev/null +++ b/lib/src/def.gsc @@ -0,0 +1,38 @@ +# Author: Sven Vogel +# Edited: 25.05.2024 +# License: GPL-2.0 + +# ,----------------------------------------. +# | Standard Type definitions | +# `----------------------------------------` + +# Unsigned integrals + +type unsigned half half int: u8 +type unsigned half int: u16 +type unsigned int: u32 +type unsigned double int: u64 +type unsigned double double int: u128 + +# Signed integrals + +type signed u8: i8 +type signed u16: i16 +type signed u32: i32 +type signed u64: i64 +type signed u128: i128 + +# IEEE-754 floating point + +type signed half float: f16 +type signed float: f32 +type signed double float: f64 +type signed double double float: f128 + +# String constant + +type ref u8: cstr + +# C style void pointer replacement + +type ref u8: ptr diff --git a/lib/src/def/api.h b/lib/src/def/api.h new file mode 100644 index 0000000..a807865 --- /dev/null +++ b/lib/src/def/api.h @@ -0,0 +1,28 @@ +// Author: Sven Vogel +// Edited: 25.05.2024 +// License: GPL-2.0 + +#ifndef GEMSTONE_STD_LIB_DEF_H_ +#define GEMSTONE_STD_LIB_DEF_H_ + +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +typedef float f32; +typedef double f64; + +typedef u8* cstr; + +typedef u8* ptr; + +#endif // GEMSTONE_STD_LIB_DEF_H_ diff --git a/lib/src/entry/entrypoint.c b/lib/src/entry/entrypoint.c new file mode 100644 index 0000000..c41e482 --- /dev/null +++ b/lib/src/entry/entrypoint.c @@ -0,0 +1,10 @@ +// +// Created by servostar on 6/10/24. +// + +extern void entry(void); + +int main(int argc, char* argv[]) { + entry(); + return 0; +} diff --git a/lib/src/io.gsc b/lib/src/io.gsc new file mode 100644 index 0000000..db8de9b --- /dev/null +++ b/lib/src/io.gsc @@ -0,0 +1,53 @@ +# Author: Sven Vogel +# Edited: 25.05.2024 +# License: GPL-2.0 + +# ,----------------------------------------. +# | Generic Input/Output | +# `----------------------------------------` + +import "def.gsc" + +# platform specific handle to an I/O device +# can be a file, buffer, window or something else +# NOTE: this reference is not meant to be dereferenced +# which can lead to errors and undefined behavior +type ptr: handle + +# Returns a handle to this processes standard input I/O handle +# -- Implementation note +# On Linux this will return 0 as is it convention under UNIX (see: https://www.man7.org/linux/man-pages/man3/stdin.3.html) +# On Windows the library will call `GetStdHandle(STD_INPUT_HANDLE)` +fun getStdinHandle(out handle: stdin) + +# Returns a handle to this processes standard input I/O handle +# -- Implementation note +# On Linux this will return 1 as is it convention under UNIX (see: https://www.man7.org/linux/man-pages/man3/stdout.3.html) +# On Windows the library will call `GetStdHandle(STD_OUTPUT_HANDLE)` +fun getStdoutHandle(out handle: stdout) + +# Returns a handle to this processes standard input I/O handle +# -- Implementation note +# On Linux this will return 1 as is it convention under UNIX (see: https://www.man7.org/linux/man-pages/man3/stderr.3.html) +# On Windows the library will call `GetStdHandle(STD_OUTPUT_HANDLE)` +fun getStderrHandle(out handle: stderr) + +# Write `len` number of bytes from `buf` into the I/O resource specified +# by `dev`. Returns the number of bytes written. +# -- Implementation note +# On Linux this will use the syscall write +# On Windows this will use the WriteFile function +fun writeBytes(in handle: dev, in ref u8: buf, in u32: len)(out u32: written) + +# Read atmost `len` bytes to `buf` from the I/O resource specified by `dev` +# Returns the number of read bytes in `written` +# -- Implementation note +# On Linux this will use the syscall read +# On Windows this will use the ReadFile function +fun readBytes(in handle: dev, in ref u8: buf, in u32: len)(out u32: read) + +# Flushes the buffers of the I/O resource specified by `dev` +# -- Implementation note +# On Linux this will use the fsync function +# On Windows this will use the FlushFileBuffers function +fun flush(in handle: dev) diff --git a/lib/src/io/api.h b/lib/src/io/api.h new file mode 100644 index 0000000..c642185 --- /dev/null +++ b/lib/src/io/api.h @@ -0,0 +1,21 @@ + +#ifndef GEMSTONE_STD_LIB_IO_H_ +#define GEMSTONE_STD_LIB_IO_H_ + +#include + +typedef ptr handle; + +void getStdinHandle(handle* stdin); + +void getStdoutHandle(handle* stdout); + +void getStderrHandle(handle* stderr); + +void writeBytes(handle dev, u8* buf, u32 len, u32* written); + +void readBytes(handle dev, u8* buf, u32 len, u32* read); + +void flush(handle dev); + +#endif //GEMSTONE_STD_LIB_IO_H_ diff --git a/lib/src/io/impl.c b/lib/src/io/impl.c new file mode 100644 index 0000000..4de453b --- /dev/null +++ b/lib/src/io/impl.c @@ -0,0 +1,75 @@ + +#include +#include + +#if defined(PLATFORM_WINDOWS) + +// Compile for Windows + +#include + +// FIXME: error in case GetStdHandle return INVALID_HANDLE_VALUE +// FIXME: error in case functions return 0 + +void getStdinHandle(handle* stdin) { + *stdin = (handle) GetStdHandle(STD_INPUT_HANDLE); +} + +void getStdoutHandle(handle* stdout) { + *stdout = (handle) GetStdHandle(STD_OUTPUT_HANDLE); +} + +void getStderrHandle(handle* stderr) { + *stderr = (handle) GetStdHandle(STD_ERROR_HANDLE); +} + +void writeBytes(handle dev, u8* buf, u32 len, u32* bytesWritten) { + WriteFile((HANDLE) dev, buf, len, bytesRead, NULL); +} + +void readBytes(handle dev, u8* buf, u32 len, u32* bytesRead) { + ReadFile((HANDLE) dev, buf, len, bytesRead, NULL); +} + +void flush(handle dev) { + FlushFileBuffers((HANDLE) dev); +} + +#elif defined(PLATFORM_POSIX) + +// Compile for Linux and BSD + +#include +#include + +// savely cast a 64-bit pointer down to a 32-bit value +// this assumes that 64-bit system will use 32-bit handles +// which are stored as 64-bit by zero extending +#define TO_INT(x) ((int)(long int)(x)) + +void getStdinHandle(handle* stdin) { + *stdin = (handle) STDIN_FILENO; +} + +void getStdoutHandle(handle* stdout) { + *stdout = (handle) STDOUT_FILENO; +} + +void getStderrHandle(handle* stderr) { + *stderr = (handle) STDERR_FILENO; +} + +void writeBytes(handle dev, u8* buf, u32 len, u32* bytesWritten) { + *bytesWritten = write(TO_INT(dev), buf, len); +} + +void readBytes(handle dev, u8* buf, u32 len, u32* bytesRead) { + *bytesRead = read(TO_INT(dev), buf, len); +} + +void flush(handle dev) { + fsync(TO_INT(dev)); +} + + +#endif diff --git a/lib/src/mem.gsc b/lib/src/mem.gsc new file mode 100644 index 0000000..0307666 --- /dev/null +++ b/lib/src/mem.gsc @@ -0,0 +1,27 @@ +# Author: Sven Vogel +# Edited: 25.05.2024 +# License: GPL-2.0 + +# ,----------------------------------------. +# | Memory Management | +# `----------------------------------------` + +import "def.gsc" + +# Allocate `len` bytes of heap memory +# Returns a pointer to the memory as `ptr` +fun heap_alloc(in u32: len)(out ref u8: ptr) + +# Rellocate `len` bytes of heap memory +# Returns a pointer to the memory as `ptr` +fun heap_realloc(in u32: len, in out ref u8: ptr) + +# Free a block of memory +fun heap_free(in ref u8: ptr) + +# Copy `len` bytes from `dst` into `src` +fun copy(in ref u8: dst, in ref u8: src, in u32: len) + +# Fill `len` bytes of `dst` with `byte` +fun fill(in ref u8: dst, in u8: byte, in u32: len) + diff --git a/lib/src/mem/api.h b/lib/src/mem/api.h new file mode 100644 index 0000000..8ca69c0 --- /dev/null +++ b/lib/src/mem/api.h @@ -0,0 +1,17 @@ + +#ifndef GEMSTONE_STD_LIB_MEM_H_ +#define GEMSTONE_STD_LIB_MEM_H_ + +#include + +void heap_alloc(u32 len, u8** ptr); + +void heap_realloc(u32 len, u8** ptr); + +void heap_free(u8* ptr); + +void copy(u8* dst, u8* src, u32 len); + +void fill(u8* dst, u8 byte, u32 len); + +#endif // GEMSTONE_STD_LIB_MEM_H_ diff --git a/lib/src/mem/impl.c b/lib/src/mem/impl.c new file mode 100644 index 0000000..9d384a8 --- /dev/null +++ b/lib/src/mem/impl.c @@ -0,0 +1,52 @@ + +#include +#include + +#if defined(PLATFORM_WINDOWS) + +#include + +#define HEAP_API_GLOBAL_FLAGS HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS + +void heap_alloc(u32 len, u8** ptr) { + HANDLE heap = GetProcessHeap(); + *ptr = HeapAlloc(heap, HEAP_API_GLOBAL_FLAGS, len); +} + +void heap_realloc(u32 len, u8** ptr) { + HANDLE heap = GetProcessHeap(); + *ptr = HeapReAlloc(heap, HEAP_API_GLOBAL_FLAGS, *ptr, len); +} + +void heap_free(u8* ptr) { + HANDLE heap = GetProcessHeap(); + HeapFree(heap, ptr); +} + +#elif defined(PLATFORM_POSIX) + +#include + +void heap_alloc(u32 len, u8** ptr) { + *ptr = malloc(len); +} + +void heap_realloc(u32 len, u8** ptr) { + *ptr = realloc(*ptr, len); +} + +void heap_free(u8* ptr) { + free(ptr); +} + +#endif + +#include + +void copy(u8* dst, u8* src, u32 len) { + memcpy(dst, src, len); +} + +void fill(u8* dst, u8 byte, u32 len) { + memset(dst, byte, len); +} \ No newline at end of file diff --git a/lib/src/os.gsc b/lib/src/os.gsc new file mode 100644 index 0000000..bfd6958 --- /dev/null +++ b/lib/src/os.gsc @@ -0,0 +1,25 @@ +# Author: Sven Vogel +# Edited: 03.06.2024 +# License: GPL-2.0 + +# ,----------------------------------------. +# | Operating System | +# `----------------------------------------` + +import "def.gsc" + +# Return a hard coded C string identifying the underlying operating system +# Will return one of the following: +# - "windows" (for Windows 7, Windows 10 and Windows 11) +# - "unix" (for GNU/Linux and BSD) +fun getPlatformName(out cstr: name) + +# Return a C string to the value of an environment varible named +# after the C string name. +fun getEnvVar(in cstr: name)(out cstr: value) + +# Set the value of an environment variable with name to value. +fun setEnvVar(in cstr: name, in cstr: value) + +# Unset a specific environment variable +fun unsetEnvVar(in cstr: name) \ No newline at end of file diff --git a/lib/src/os/api.h b/lib/src/os/api.h new file mode 100644 index 0000000..3ed7a1f --- /dev/null +++ b/lib/src/os/api.h @@ -0,0 +1,18 @@ +// +// Created by servostar on 6/3/24. +// + +#ifndef GEMSTONE_STD_LIB_OS_H +#define GEMSTONE_STD_LIB_OS_H + +#include + +void getPlatformName(cstr* name); + +void getEnvVar(cstr name, cstr* value); + +void setEnvVar(cstr name, cstr value); + +void unsetEnvVar(cstr name); + +#endif // GEMSTONE_STD_LIB_OS_H diff --git a/lib/src/os/impl.c b/lib/src/os/impl.c new file mode 100644 index 0000000..7bed097 --- /dev/null +++ b/lib/src/os/impl.c @@ -0,0 +1,36 @@ +// +// Created by servostar on 6/3/24. +// + +#include +#include + +#if defined(PLATFORM_WINDOWS) + +void getPlatformName(cstr* name) { + *name = (u8*) "windows"; +} + +#elif defined(PLATFORM_POSIX) + +void getPlatformName(cstr * name) { + *name = (u8*) "posix"; +} + +#endif + +// Implementation based on libc + +#include + +void getEnvVar(cstr name, cstr* value) { + *value = (cstr) getenv((char*) name); +} + +void setEnvVar(cstr name, cstr value) { + setenv((char*) name, (char*) value, true); +} + +void unsetEnvVar(cstr name) { + unsetenv((char*) name); +} diff --git a/lib/src/std.gsc b/lib/src/std.gsc new file mode 100644 index 0000000..cf690e3 --- /dev/null +++ b/lib/src/std.gsc @@ -0,0 +1,16 @@ +# Author: Sven Vogel +# Edited: 25.05.2024 +# License: GPL-2.0 + +# ,----------------------------------------. +# | Gemstone Standard Library | +# `----------------------------------------` + +# standard type definitions +import "def.gsc" + +# I/O operations +import "io.gsc" + +# memory management +import "mem.gsc" diff --git a/run-check-test.sh b/run-check-test.sh index bd7e5cc..1acba79 100644 --- a/run-check-test.sh +++ b/run-check-test.sh @@ -17,6 +17,8 @@ if [ ! $? -eq 0 ]; then exit 1 fi +sh -c ./run-lib-build.sh + echo "+--------------------------------------+" echo "| RUNNING CODE CHECK |" echo "+--------------------------------------+" @@ -39,4 +41,4 @@ fi echo "+--------------------------------------+" echo "| COMPLETED CHECK + TESTS SUCCESSFULLY |" -echo "+--------------------------------------+" \ No newline at end of file +echo "+--------------------------------------+" diff --git a/run-docker-build.sh b/run-docker-build.sh index aaec9c6..0028067 100755 --- a/run-docker-build.sh +++ b/run-docker-build.sh @@ -47,7 +47,7 @@ echo "+--------------------------------------+" echo "| RUNNING check test |" echo "+--------------------------------------+" -docker run servostar/gemstone:devkit-"$SDK" sh run-check-test.sh +docker run --rm --name "devkit-$SDK-check-test"- servostar/gemstone:devkit-"$SDK" sh run-check-test.sh if [ ! $? -eq 0 ]; then echo "===> failed to run build or checks" exit 1 diff --git a/run-lib-build.sh b/run-lib-build.sh new file mode 100755 index 0000000..ccca62c --- /dev/null +++ b/run-lib-build.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# Author: Sven Vogel +# Created: 25.05.2024 +# Description: Builds the standard library into bin/std + +echo "+--------------------------------------+" +echo "| CONFIGURE STD LIBRARY |" +echo "+--------------------------------------+" + +cmake lib +if [ ! $? -eq 0 ]; then + echo "===> failed to configure build" + exit 1 +fi + +echo "+--------------------------------------+" +echo "| BUILD STD LIBRARY |" +echo "+--------------------------------------+" + +cd lib || exit 1 +make -B +if [ ! $? -eq 0 ]; then + echo "===> failed to build standard library" + exit 1 +fi + +echo "+--------------------------------------+" +echo "| successfully build standard library |" +echo "+--------------------------------------+" diff --git a/sdk/Dockerfile b/sdk/Dockerfile index b5523b3..1cba1d3 100644 --- a/sdk/Dockerfile +++ b/sdk/Dockerfile @@ -1,11 +1,11 @@ FROM alpine:3.19.1 LABEL authors="servostar" -LABEL version="0.2.4" +LABEL version="0.2.5" LABEL description="base image for building the gemstone programming language compiler" LABEL website="https://github.com/Servostar/gemstone" # install dependencies -RUN apk add build-base gcc make cmake bison flex git python3 graphviz glib glib-dev +RUN apk add build-base gcc make cmake bison flex git python3 graphviz glib glib-dev llvm17-libs llvm17-dev # create user for build RUN adduser --disabled-password lorang diff --git a/src/ast/ast.c b/src/ast/ast.c index a9c1af6..7da6bf5 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -19,8 +19,7 @@ struct AST_Node_t *AST_new_node(TokenLocation location, enum AST_SyntaxElement_t // init to discrete state node->parent = NULL; - node->children = NULL; - node->child_count = 0; + node->children = mem_new_g_array(MemoryNamespaceAst, sizeof(AST_NODE_PTR)); node->kind = kind; node->value = value; node->location = location; @@ -131,17 +130,6 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { assert(owner != NULL); assert(child != NULL); - // if there are no children for now - if (owner->child_count == 0) { - DEBUG("Allocating new children array"); - owner->children = mem_alloc(MemoryNamespaceAst, sizeof(struct AST_Node_t *)); - - } else { - DEBUG("Rellocating old children array"); - const size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1); - owner->children = mem_realloc(MemoryNamespaceAst, owner->children, size); - } - if (owner->children == NULL) { PANIC("failed to allocate children array of AST node"); } @@ -152,22 +140,26 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { owner->location.col_start = min(owner->location.col_start, child->location.col_start); owner->location.line_start = min(owner->location.line_start, child->location.line_start); + if (owner->location.file == NULL) { + owner->location.file = child->location.file; + } + assert(owner->children != NULL); - owner->children[owner->child_count++] = child; + g_array_append_val(owner->children, child); } struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, const size_t idx) { DEBUG("retrvieng node %d from %p", idx, owner); assert(owner != NULL); assert(owner->children != NULL); - assert(idx < owner->child_count); + assert(idx < owner->children->len); if (owner->children == NULL) { PANIC("AST owner node has no children"); } - struct AST_Node_t *child = owner->children[idx]; + AST_NODE_PTR child = g_array_index(owner->children, AST_NODE_PTR, idx); if (child == NULL) { PANIC("child node is NULL"); @@ -179,19 +171,13 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, const size_t idx) { struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, const size_t idx) { assert(owner != NULL); assert(owner->children != NULL); - assert(idx < owner->child_count); + assert(idx < owner->children->len); - struct AST_Node_t* child = owner->children[idx]; + AST_NODE_PTR child = g_array_index(owner->children, AST_NODE_PTR, idx); + g_array_remove_index(owner->children, idx); child->parent = NULL; - owner->child_count--; - - // shift back every following element by one - for (size_t i = idx; i < owner->child_count; i++) { - owner->children[i] = owner->children[i + 1]; - } - return child; } @@ -200,8 +186,8 @@ struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_N assert(child != NULL); assert(owner->children != NULL); - for (size_t i = 0; i < owner->child_count; i++) { - if (owner->children[i] == child) { + for (size_t i = 0; i < owner->children->len; i++) { + if (g_array_index(owner->children, AST_NODE_PTR, i) == child) { return AST_remove_child(owner, i); } } @@ -221,10 +207,10 @@ void AST_delete_node(struct AST_Node_t *node) { } if (node->children != NULL) { - for (size_t i = 0; i < node->child_count; i++) { + for (size_t i = 0; i < AST_get_child_count(node); i++) { // prevent detach of children node - node->children[i]->parent = NULL; - AST_delete_node(node->children[i]); + AST_get_node(node, i)->parent = NULL; + AST_delete_node(AST_get_node(node, i)); } mem_free(node->children); } @@ -242,8 +228,8 @@ static void AST_visit_nodes_recurse2(struct AST_Node_t *root, (for_each)(root, depth); - for (size_t i = 0; i < root->child_count; i++) { - AST_visit_nodes_recurse2(root->children[i], for_each, depth + 1); + for (size_t i = 0; i < root->children->len; i++) { + AST_visit_nodes_recurse2(g_array_index(root->children, AST_NODE_PTR, i), for_each, depth + 1); } } @@ -270,8 +256,8 @@ static void AST_fprint_graphviz_node_definition(FILE* stream, const struct AST_N return; } - for (size_t i = 0; i < node->child_count; i++) { - AST_fprint_graphviz_node_definition(stream, node->children[i]); + for (size_t i = 0; i < node->children->len; i++) { + AST_fprint_graphviz_node_definition(stream, g_array_index(node->children, AST_NODE_PTR, i)); } } @@ -285,9 +271,10 @@ static void AST_fprint_graphviz_node_connection(FILE* stream, const struct AST_N return; } - for (size_t i = 0; i < node->child_count; i++) { - fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) node->children[i]); - AST_fprint_graphviz_node_connection(stream, node->children[i]); + for (size_t i = 0; i < node->children->len; i++) { + AST_NODE_PTR child = g_array_index(node->children, AST_NODE_PTR, i); + fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) child); + AST_fprint_graphviz_node_connection(stream, child); } } @@ -304,3 +291,45 @@ void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* root) { fprintf(stream, "}\n"); } + +AST_NODE_PTR AST_get_node_by_kind(AST_NODE_PTR owner, enum AST_SyntaxElement_t kind) { + for (size_t i = 0; i < owner->children->len; i++) { + AST_NODE_PTR child = AST_get_node(owner, i); + + if (child->kind == kind) { + return child; + } + } + + return NULL; +} + +void AST_merge_modules(AST_NODE_PTR dst, size_t k, AST_NODE_PTR src) { + assert(dst != NULL); + assert(src != NULL); + + size_t elements = src->children->len; + for (size_t i = 0; i < elements; i++) { + AST_insert_node(dst, k + i, AST_remove_child(src, 0)); + } + AST_delete_node(src); +} + +void AST_insert_node(AST_NODE_PTR owner, size_t idx, AST_NODE_PTR child) { + assert(owner != NULL); + assert(child != NULL); + + DEBUG("Reallocating old children array"); + + g_array_insert_val(owner->children, idx, child); +} + +size_t AST_get_child_count(AST_NODE_PTR node) { + return node->children->len; +} + +AST_NODE_PTR AST_get_last_node(AST_NODE_PTR node) { + assert(node != NULL); + + return g_array_index(node->children, AST_NODE_PTR, node->children->len - 1); +} diff --git a/src/ast/ast.h b/src/ast/ast.h index da3cd41..5c71d2c 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -89,7 +89,7 @@ enum AST_SyntaxElement_t { * - kind: The type of the node. Such as AST_Expr, AST_Add, ... * - value: A string representing an optional value. Can be a integer literal for kind AST_int */ -struct AST_Node_t { +typedef struct AST_Node_t { // parent node that owns this node struct AST_Node_t *parent; @@ -100,12 +100,9 @@ struct AST_Node_t { TokenLocation location; - // number of child nodes ownd by this node - // length of children array - size_t child_count; - // variable amount of child nodes - struct AST_Node_t **children; -}; + // children array + GArray* children; +} AST_Node; /** * Shorthand type for a single AST node @@ -199,6 +196,8 @@ struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_N [[gnu::nonnull(1)]] struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx); +AST_NODE_PTR AST_get_last_node(AST_NODE_PTR node); + /** * @brief Execute a function for every child, grandchild, ... and the supplied node as topmost ancestor * @param root the root to recursively execute a function for @@ -219,4 +218,14 @@ void AST_visit_nodes_recurse(struct AST_Node_t *root, [[gnu::nonnull(1), gnu::nonnull(2)]] void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* node); -#endif \ No newline at end of file +AST_NODE_PTR AST_get_node_by_kind(AST_NODE_PTR owner, enum AST_SyntaxElement_t kind); + +[[gnu::nonnull(1), gnu::nonnull(3)]] +void AST_merge_modules(AST_NODE_PTR dst, size_t i, AST_NODE_PTR src); + +[[gnu::nonnull(1), gnu::nonnull(3)]] +void AST_insert_node(AST_NODE_PTR owner, size_t idx, AST_NODE_PTR child); + +size_t AST_get_child_count(AST_NODE_PTR node); + +#endif diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 9829cb2..57ed113 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -103,6 +103,10 @@ TargetConfig* default_target_config() { config->output_directory = mem_strdup(MemoryNamespaceOpt, "bin"); config->optimization_level = 1; config->root_module = NULL; + config->link_search_paths = g_array_new(FALSE, FALSE, sizeof(char*)); + config->lld_fatal_warnings = FALSE; + config->gsc_fatal_warnings = FALSE; + config->import_paths = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*)); return config; } @@ -112,6 +116,16 @@ TargetConfig* default_target_config_from_args() { TargetConfig* config = default_target_config(); + gboolean fatal_warnings = is_option_set("all-fatal-warnings"); + + if (fatal_warnings || is_option_set("lld-fatal-warnings")) { + config->lld_fatal_warnings = true; + } + + if (fatal_warnings || is_option_set("gsc-fatal-warnings")) { + config->gsc_fatal_warnings = true; + } + if (is_option_set("print-ast")) { config->print_ast = true; } @@ -146,6 +160,40 @@ TargetConfig* default_target_config_from_args() { } } + // TODO: free vvvvvvvvvvvvv + char* cwd = g_get_current_dir(); + g_array_append_val(config->link_search_paths, cwd); + + if (is_option_set("link-paths")) { + const Option* opt = get_option("link-paths"); + + if (opt->value != NULL) { + + const char* start = opt->value; + const char* end = NULL; + while((end = strchr(start, ',')) != NULL) { + + const int len = end - start; + char* link_path = malloc(len + 1); + memcpy(link_path, start, len); + link_path[len] = 0; + + g_array_append_val(config->link_search_paths, link_path); + + start = end; + } + + const int len = strlen(start); + if (len > 0) { + char* link_path = malloc(len + 1); + memcpy(link_path, start, len); + link_path[len] = 0; + + g_array_append_val(config->link_search_paths, link_path); + } + } + } + GArray* files = get_non_options_after("compile"); if (files == NULL) { @@ -161,6 +209,9 @@ TargetConfig* default_target_config_from_args() { g_array_free(files, TRUE); } + char* default_import_path = mem_strdup(MemoryNamespaceOpt, "."); + g_array_append_val(config->import_paths, default_import_path); + return config; } @@ -173,17 +224,23 @@ void print_help(void) { "Compile non-project file: gsc compile [file]", "Output information: gsc