From: ahmedsamyh Date: Tue, 25 Feb 2025 10:56:08 +0000 (+0500) Subject: WIP: Trying to port python -> c. X-Git-Url: https://www.git.momoyon.org/?a=commitdiff_plain;h=8dda0d9d5e44d842acc546770911f424fc349fd3;p=lang.git WIP: Trying to port python -> c. --- diff --git a/.gitignore b/.gitignore index ad5da30..413de34 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ test.sh lang.sh tags +lang diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c3c4e45 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +CC=gcc +CFLAGS=-Wall -Wextra -ggdb -I./include +LDFLAGS=-L./lib +LIBS= + +lang: main.c + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) diff --git a/include/commonlib.h b/include/commonlib.h new file mode 100644 index 0000000..b147ffb --- /dev/null +++ b/include/commonlib.h @@ -0,0 +1,622 @@ +#ifndef _COMMONLIB_H_ +#define _COMMONLIB_H_ +#include +#include +#include +#include +#include +#include +#include +#include + +// Memory allocation +#define C_MALLOC malloc +#define C_FREE free +#define C_REALLOC realloc +#define C_MEMCPY memcpy + +// Remove Prefix +#ifdef COMMONLIB_REMOVE_PREFIX +#define ASSERT c_ASSERT +#define ARRAY_LEN c_ARRAY_LEN + +#define da_append c_da_append +#define DYNAMIC_ARRAY_INITIAL_CAPACITY c_DYNAMIC_ARRAY_INITIAL_CAPACITY +// #define c_DYNAMIC_ARRAY_INITIAL_CAPACITY + +#define os_get_timedate c_os_get_timedate +#define os_file_exists c_os_file_exists + +#define log_error c_log_error +#define log_info c_log_info +#define log_warning c_log_warning + +#define slurp_file c_slurp_file +#define touch_file_if_doesnt_exist c_touch_file_if_doesnt_exist + +#define Arena c_Arena +#define Arena_make c_Arena_make +#define Arena_alloc c_Arena_alloc +#define Arena_reset c_Arena_reset +#define Arena_free c_Arena_free +#define Arena_alloc_str c_Arena_alloc_str +#define Arena_alloc_wstr c_Arena_alloc_wstr + +#define String_view c_String_view + +#define shift_args c_shift_args + +#endif // COMMONLIB_REMOVE_PREFIX + +// typedefs +typedef unsigned int uint; +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; + +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; + +typedef float float32; +typedef double float64; + +typedef wchar_t wchar; + +typedef const char* cstr; +typedef const wchar* wstr; + + +// Macros +#define c_ASSERT(condition, msg) do {\ + if (!(condition)) {\ + fprintf(stderr, "%s:%d:0 [ASSERTION FAILED] %s: %s", __FILE__, __LINE__, #condition, msg);\ + exit(1);\ + }\ + } while (0) + +#define c_ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0])) + +#define c_pop_front(xs, xsz) (assert(xsz > 0 && "Array is empty"), xsz--, *xs++) +#define c_shift_args c_pop_front + +// +// Struct pre-decls +// + +typedef struct c_Arena c_Arena; + +// +// ## Data Structures +// + +// +// Dynamic-Array +// + +// NOTE: To use c_da_append() the Dynamic-Array struct should be defined using +// DEFINE_DYNAMIC_ARRAY or have the same members as below! +// NOTE!!!: We actually don't want this since this makes the user want to +// use this macro to define dynamic arrays. But the user might want extra fields +// in the struct; So we recommend defining da structs manually like: +// ```C +// typedef struct { +// items; +// size_t count; +// size_t capacity; +// [extra fields...]; +// } +// ``` +// #define DEFINE_DYNAMIC_ARRAY(struct_name, elm_type) typedef struct { +// elm_type *items; +// size_t count; +// size_t capacity; +// } + +#define c_DYNAMIC_ARRAY_INITIAL_CAPACITY (sizeof(size_t)) + +#define c_da_append(da, elm) do {\ + if ((da).items == NULL) {\ + (da).capacity = c_DYNAMIC_ARRAY_INITIAL_CAPACITY;\ + (da).count = 0;\ + (da).items = C_MALLOC(sizeof(elm) * (da).capacity);\ + }\ + if ((da).count >= (da).capacity) {\ + (da).capacity *= 2;\ + (da).items = C_REALLOC((da).items, (da).capacity * sizeof(elm));\ + c_ASSERT((da).items != NULL, "TODO: Log error instead of asserting");\ + }\ + (da).items[(da).count++] = elm;\ + } while (0) + +#define c_da_pop_front(da) (c_ASSERT(da.count > 0, "Array is empty"), da.count--, *da.items++) + +// +// OS +// + +#if defined(__linux__) +#include +#endif + +void c_os_get_timedate(c_Arena* a); +bool c_os_file_exists(cstr filename); + +// +// Logging +// + +#define c_log_error(fmt, ...) do {\ + fprintf(stderr, "%s"fmt"\n", "[ERROR] ", ##__VA_ARGS__);\ + } while (0) +#define c_log_info(fmt, ...) do {\ + fprintf(stdout, "%s"fmt"\n", "[INFO] ", ##__VA_ARGS__);\ + } while (0) +#define c_log_warning(fmt, ...) do {\ + fprintf(stdout, "%s"fmt"\n", "[WARNING] ", ##__VA_ARGS__);\ + } while (0) + +// +// File +// + +// reads entire file and gives back the string holding the contents. (caller must be responsible for freeing the string!) +const char* c_slurp_file(const char* filename, bool* success); +void c_touch_file_if_doesnt_exist(cstr file); + +// +// ### Allocators ### +// + +// +// c_Arena +// + +#define ARENA_BUFF_INITIAL_SIZE (1024*4) + +struct c_Arena { + void* buff; + uint64 buff_size; + void* ptr; +}; + +// pass size 0 to get ARENA_BUFF_INITIAL_SIZE +c_Arena c_Arena_make(size_t size); +void* c_Arena_alloc(c_Arena* a, size_t size); +void c_Arena_reset(c_Arena* a); +void c_Arena_free(c_Arena* a); + +#define c_Arena_alloc_str(a, fmt, ...) c_Arena_alloc(&(a), sizeof(char)*stbsp_snprintf((a).ptr, (int)((a).buff_size - ((uint8*)(a).ptr - (uint8*)(a).buff)), (fmt), __VA_ARGS__)+1) +#define c_Arena_alloc_wstr(a, fmt, ...) c_Arena_alloc(&a, sizeof(char)*wprintf(a.ptr, a.buff_size - ((uint8*)a.ptr - (uint8*)a.buff), (fmt), __VA_ARGS__)+1) + +// +// String Builder +// + +typedef struct { + char* data; + size_t size; + size_t capacity; +} c_String_builder; + +void c_sb_append(c_String_builder* sb, char* data); + +// +// String view +// + +typedef struct { + char *data; + size_t count; +} c_String_view; + +#define c_SV_FMT "%.*s" +#define c_SV_ARG(sv) (int)sv.count, sv.data + +#define c_SV(cstr) (c_String_view){.data = cstr, strlen(cstr)} + +void c_sv_print_dumb(c_String_view sv); +c_String_view c_sv_from_cstr(const char* cstr); // Actually just use SV(cstr) macro... +c_String_view c_sv_lpop(c_String_view* sv, uint32 n); +c_String_view c_sv_lpop_until_predicate(c_String_view* sv, int(*predicate)(int)); +c_String_view c_sv_rpop_until_predicate(c_String_view* sv, int(*predicate)(int)); +c_String_view c_sv_lpop_until_char(c_String_view* sv, char ch); +c_String_view c_sv_rpop_until_char(c_String_view* sv, char ch); +void c_sv_lremove(c_String_view* sv, size_t n); +void c_sv_rremove(c_String_view* sv, size_t n); +void c_sv_lremove_until_char(c_String_view* sv, char ch); +void c_sv_rremove_until_char(c_String_view* sv, char ch); +void c_sv_lremove_until_char_after(c_String_view* sv, char ch); +void c_sv_rremove_until_char_after(c_String_view* sv, char ch); +void c_sv_ltrim(c_String_view* sv); +void c_sv_rtrim(c_String_view* sv); +void c_sv_trim(c_String_view* sv); +char* c_sv_to_cstr(c_String_view sv); +int32 c_sv_to_int(c_String_view sv); +uint64 c_sv_to_uint64(c_String_view sv); +uint8 c_sv_to_uint8_hex(c_String_view sv); +void* c_sv_to_ptr(c_String_view sv); +float32 c_sv_to_float(c_String_view sv); +bool c_sv_contains_char(c_String_view sv, char ch); +bool c_sv_is_hex_numbers(c_String_view sv); +bool c_sv_equals(c_String_view sv1, c_String_view sv2); + +#endif /* _COMMONLIB_H_ */ + +////////////////////////////////////////////////// +#ifdef COMMONLIB_IMPLEMENTATION +#include +#include +#include + +// My things implementation: + +// +// OS +// + +#if defined(_WIN32) || defined(__CYGWIN__) +void c_os_get_timedate(c_Arena* a) { + (void)a; + c_ASSERT(false, "Unimplemented!"); +} + +bool c_os_file_exists(cstr filename) { + (void) filename; + c_ASSERT(false, "Unimplemented!"); + return false; +} + +#elif defined(__linux__) +void c_os_get_timedate(c_Arena* a) { + (void)a; + c_ASSERT(false, "Unimplemented!"); +} + +bool c_os_file_exists(cstr filename) { + struct stat buf; + return stat(filename, &buf) == 0; +} +#endif + +// simple and dirty way to have defering in C (not recommended to use!) +#define defer(ret_val) \ + result = ret_val;\ + goto defer + +// TODO: Refactor error messages +const char *c_slurp_file(const char* filename, bool* success) { + FILE* f = fopen(filename, "rb"); + char* result = NULL; + + if (f == NULL){ + c_log_error("slurp_file::fopen(\"%s\", \"rb\") -> %s\n", filename, strerror(errno)); + defer(NULL); + } + + if (fseek(f, 0, SEEK_END) < 0) { + c_log_error("slurp_file::fseek(%s, 0, SEEK_END) -> %s\n", filename, strerror(errno)); + defer(NULL); + } + + size_t fsize = ftell(f); + + if (fsize == (size_t)-1){ + c_log_error("slurp_file::ftell(%s) -> %s\n", filename, strerror(errno)); + defer(NULL); + } + + result = C_MALLOC(sizeof(char)*(fsize+1)); + + if (result == NULL){ + c_log_error("slurp_file::malloc(%zu) -> %s\n", sizeof(char)*fsize, strerror(errno)); + defer(NULL); + } + + if (fseek(f, 0, SEEK_SET) < 0) { + c_log_error("slurp_file::fseek(%s, 0, SEEK_SET) -> %s\n", filename, strerror(errno)); + defer(NULL); + } + + if (fread((char*)result, sizeof(char), fsize, f) != fsize){ + c_log_error("slurp_file::fread(result, %zu, 1, f) -> %s\n", fsize, strerror(errno)); + defer(NULL); + } + + // set null-terminator + result[fsize] = '\0'; + + defer: + if (f) fclose(f); + *success = result != NULL; + return result; +} + +void c_touch_file_if_doesnt_exist(cstr filename) { + if (c_os_file_exists(filename)) return; + FILE* file = fopen(filename, "w"); + if (file) fclose(file); +} + +// +// ### Allocators ### +// + +// c_Arena + +c_Arena c_Arena_make(size_t size) { + c_Arena res = {0}; + res.buff_size = size == 0 ? ARENA_BUFF_INITIAL_SIZE : size; + res.buff = C_MALLOC(res.buff_size); + res.ptr = res.buff; + + c_ASSERT(res.buff, "Malloc failed?"); + + return res; +} + +void* c_Arena_alloc(c_Arena* a, size_t size) { + c_ASSERT(a->buff, "Bro pass an initialized arena!"); + + void* res = a->ptr; + a->ptr = (uint8*)a->ptr + size; + + size_t diff = (size_t)((uint8*)a->ptr - (uint8*)a->buff); + if (diff > a->buff_size) { + c_log_info("c_Arena resized from %zu to %zu", a->buff_size, a->buff_size*2); + a->buff_size *= 2; + a->buff = C_REALLOC(a->buff, a->buff_size); + a->ptr = (uint8*)a->buff + diff; + res = a->ptr; + a->ptr = (uint8*)a->ptr + size; + } + /* c_ASSERT((size_t)((uint8*)a->ptr - (uint8*)a->buff) <= a->buff_size); */ + + return res; +} + +void c_Arena_reset(c_Arena* a) { + a->ptr = a->buff; +} + +void c_Arena_free(c_Arena* a) { + C_FREE(a->buff); +} + +// +// String Builder +// + +void c_sb_append(c_String_builder* sb, char* data) { + size_t data_size = strlen(data); + if (sb->size + data_size > sb->capacity) { + sb->capacity *= 2; + sb->data = C_REALLOC(sb->data, sb->capacity); + } + + // void *memcpy(void dest[restrict .n], const void src[restrict .n], + C_MEMCPY((void *)((uintptr_t)sb->data + (uintptr_t)sb->data), data, data_size); + sb->size += data_size; +} + +// +// String view +// + +void c_sv_print_dumb(c_String_view sv){ + for (size_t i = 0; i < (size_t)sv.count; ++i){ + putc(*(sv.data+i), stdout); + } +} + +c_String_view c_sv_from_cstr(const char* cstr){ + return (c_String_view){ + .data = (char *)cstr, + .count = strlen(cstr), + }; +} + +c_String_view c_sv_lpop(c_String_view* sv, uint32 n) { + c_String_view res = {0}; + if (sv->count < n) return res; + res.data = sv->data; + res.count = n; + + sv->data += n; + sv->count -= n; + + return res; +} + +c_String_view c_sv_lpop_until_predicate(c_String_view* sv, int(*predicate)(int)){ + const char* old_sv_data = sv->data; + while (sv->count > 0 && !predicate(*sv->data)){ + sv->data++; + sv->count--; + } + + return (c_String_view){ + .data = sv->data - (sv->data - old_sv_data), + .count = (sv->data - old_sv_data), + }; +} + +c_String_view c_sv_rpop_until_predicate(c_String_view* sv, int(*predicate)(int)){ + size_t old_sv_count = sv->count; + while (sv->count > 0 && !predicate(*(sv->data+sv->count-1))){ + sv->count--; + } + + return (c_String_view){ + .data = sv->data + sv->count, + .count = old_sv_count - sv->count, + }; +} + +c_String_view c_sv_lpop_until_char(c_String_view* sv, char ch){ + const char* old_sv_data = sv->data; + while (sv->count > 0 && *sv->data != ch){ + sv->data++; + sv->count--; + } + + return (c_String_view){ + .data = sv->data - (sv->data - old_sv_data), + .count = (sv->data - old_sv_data), + }; +} + +c_String_view c_sv_rpop_until_char(c_String_view* sv, char ch){ + size_t old_sv_count = sv->count; + while (sv->count > 0 && *(sv->data+sv->count-1) != ch){ + sv->count--; + } + + return (c_String_view){ + .data = sv->data + sv->count, + .count = old_sv_count - sv->count, + }; +} + +void c_sv_lremove(c_String_view* sv, size_t n){ + if (n > sv->count) n = sv->count; + + sv->data += n; + sv->count -= n; +} + +void c_sv_rremove(c_String_view* sv, size_t n){ + if (n > sv->count) + sv->count = 0; + else + sv->count -= n; +} + +void c_sv_lremove_until_char(c_String_view* sv, char ch){ + while (sv->count > 0 && *sv->data != ch){ + sv->data++; + sv->count--; + } +} + +void c_sv_rremove_until_char(c_String_view* sv, char ch){ + while (sv->count > 0 && *(sv->data+sv->count-1) != ch){ + sv->count--; + } +} + +void c_sv_lremove_until_char_after(c_String_view* sv, char ch){ + while (sv->count > 0 && *(sv->data-1) != ch){ + sv->data++; + sv->count--; + } +} + +void c_sv_rremove_until_char_after(c_String_view* sv, char ch){ + while (sv->count > 0 && *(sv->data+sv->count) != ch){ + sv->count--; + } +} + +void c_sv_ltrim(c_String_view* sv){ + while (sv->count > 0 && isspace(*sv->data)){ + sv->data++; + sv->count--; + } +} + +void c_sv_rtrim(c_String_view* sv){ + while (sv->count > 0 && isspace(*(sv->data+sv->count-1))){ + sv->count--; + } +} + +void c_sv_trim(c_String_view* sv){ + c_sv_ltrim(sv); + c_sv_rtrim(sv); +} + +char* c_sv_to_cstr(c_String_view sv){ + char* res = (char*)calloc(1, sizeof(char)*sv.count); + C_MEMCPY(res, sv.data, sv.count); + return res; +} + +// TODO: check for failure of conversion. returns 0/0.0 on failure, so just check if the str contains zero. +int32 c_sv_to_int(c_String_view sv) { + char* str = c_sv_to_cstr(sv); + int32 res = atoi(str); + C_FREE(str); + return res; +} + +uint64 c_sv_to_uint64(c_String_view sv) { + char* str = c_sv_to_cstr(sv); + uint64 res = (uint64)atoll(str); + C_FREE(str); + return res; +} + +uint8 c_sv_to_uint8_hex(c_String_view sv) { + char* str = c_sv_to_cstr(sv); + char* end = str + sv.count; + uint8 res = (uint8)strtol(str, &end, 16); + C_FREE(str); + return res; +} + +float32 c_sv_to_float(c_String_view sv) { + char* str = c_sv_to_cstr(sv); + float32 res = (float32)atof(str); + C_FREE(str); + return res; +} + +void* c_sv_to_ptr(c_String_view sv) { + char* str = c_sv_to_cstr(sv); + char* end = NULL; + void* res = (void*)strtoull(str, &end, 16); + C_FREE(str); + return res; +} + +bool c_sv_contains_char(c_String_view sv, char ch){ + for (size_t i = 0; i < sv.count; ++i){ + if (sv.data[i] == ch) return true; + } + return false; +} + +bool c_sv_is_hex_numbers(c_String_view sv) { + char hex[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', + 'A', 'B', 'C', 'D', 'E', 'F' + }; + bool found = false; + for (size_t i = 0; i < sv.count; ++i) { + char c = sv.data[i]; + for (size_t j = 0; j < c_ARRAY_LEN(hex); ++j) { + if (hex[j] == c) { + found = true; + } + } + } + + return found; +} + +bool c_sv_equals(c_String_view sv1, c_String_view sv2) { + if (sv1.count != sv2.count) return false; + for (size_t i = 0; i < sv1.count; ++i) { + if (sv1.data[i] != sv2.data[i]) { + return false; + } + } + + return true; +} + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..6de8c60 --- /dev/null +++ b/main.c @@ -0,0 +1,120 @@ +#include + +#define COMMONLIB_IMPLEMENTATION +#define COMMONLIB_REMOVE_PREFIX +#include + +#define error log_error +#define info log_info + +void usage(const char *program) { + info("Usage: %s ", program); +} + +typedef struct { + const char *src; + size_t src_len; + size_t cur; + size_t bol; // Beginning of Line + size_t line; + const char *filename; +} Lexer; + +typedef struct { + const char *lexeme; +} Token; + +typedef struct { + Token *items; + size_t count; + size_t capacity; +} Tokens; + +Lexer make_lexer(const char *filename) { + bool ok = false; + const char *buf = slurp_file(filename, &ok); + if (!ok) { + error("Failed to open '%s'", filename); + exit(1); + } + Lexer l = { + .src = buf, + .src_len = strlen(buf), + .cur = 0, + .bol = 0, + .line = 1, + .filename = filename, + }; + + return l; +} + +bool eof(Lexer *l) { + return l->cur >= l->src_len; +} + +char current_char(Lexer *l) { + ASSERT(!eof(l), "Trying to get char after EOF"); + return l->src[l->cur]; +} + +char consume_char(Lexer *l) { + char ch = current_char(l); + l->cur += 1; + return ch; +} + +void left_trim(Lexer *l) { + while (!eof(l) && isspace(current_char(l))) { + // TODO: Care about window's \r\n.... + if (current_char(l) == '\n') { + l->line += 1; + l->bol = l->cur + 1; + } + consume_char(l); + } +} + +bool next_token(Lexer *l, Token *t_out) { + left_trim(l); + + if (eof(l)) return false; + + char ch = current_char(l); + + switch (ch) { + } + + /*info("ch: '%c'", ch);*/ + + return true; +} + +Tokens lex(Lexer *l) { + Tokens tokens = {0}; + Token t = {0}; + while (next_token(l, &t)) { + da_append(tokens, t); + } + + return tokens; +} + +int main(int argc, char **argv) { + const char *program = shift_args(argv, argc); + + if (argc <= 0) { + error("Please provide a filename!"); + usage(program); + return 1; + } + + const char *filename = shift_args(argv, argc); + + Lexer l = make_lexer(filename); + + lex(&l); + + info("OK"); + return 0; +} diff --git a/main.momo b/main.momo index 7764457..ee884b9 100644 --- a/main.momo +++ b/main.momo @@ -1,4 +1 @@ -msg != 0; -msg: string = "test"; -msg = string; -// msg: string = "Hello, World"; +int a = 0; diff --git a/main.py b/main.py index 9475540..00862a4 100644 --- a/main.py +++ b/main.py @@ -125,6 +125,9 @@ class Token: def __str__(self): return f"Token ({token_type_as_str_map[self.typ]}, '{self.lexeme}', {self.loc})" + def __repr__(self): + return self.__str__() + class Lexer: def __init__(self, filename: str): try: @@ -710,6 +713,8 @@ def main(): # Lexical Analysis tokens = lexer.lex() + print(tokens) + # TODO: Parse parser = Parser(tokens)