kuroko: integrated language runtime support
This commit is contained in:
parent
a627c732d5
commit
4291a61a92
|
@ -44,3 +44,4 @@
|
|||
/common-uefi-riscv64
|
||||
/decompressor-build
|
||||
/stage1.stamp
|
||||
/kuroko
|
||||
|
|
|
@ -25,6 +25,7 @@ fi
|
|||
[ -d freestanding-headers ] || git clone https://github.com/mintsuki/freestanding-headers.git $SHALLOW_CLONE_FLAG
|
||||
[ -d limine-efi ] || git clone https://github.com/limine-bootloader/limine-efi.git $SHALLOW_CLONE_FLAG
|
||||
[ -d libgcc-binaries ] || git clone https://github.com/mintsuki/libgcc-binaries.git $SHALLOW_CLONE_FLAG
|
||||
[ -d kuroko ] || git clone https://github.com/kuroko-lang/kuroko.git $SHALLOW_CLONE_FLAG
|
||||
|
||||
mkdir -p build-aux
|
||||
cp "$(automake --print-libdir)/install-sh" build-aux
|
||||
|
|
|
@ -70,6 +70,9 @@ override CPPFLAGS_FOR_TARGET := \
|
|||
-MMD \
|
||||
-MP
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/./kuroko/%.o: override CFLAGS_FOR_TARGET += -DKRK_STATIC_ONLY -DKRK_DISABLE_THREADS -DKRK_NO_FILESYSTEM -DKRK_NO_CALLGRIND -DKRK_NO_SOURCE_IN_TRACEBACK -DKRK_NO_FLOAT -DKRK_NO_GC_TRACING -Wno-shadow -Wno-unused-parameter -Wno-misleading-indentation
|
||||
$(call MKESCAPE,$(BUILDDIR))/./kuroko/%.o: override CPPFLAGS_FOR_TARGET += -I'$(call SHESCAPE,$(BUILDDIR))/../kuroko-inc'
|
||||
|
||||
ifeq ($(TARGET),bios)
|
||||
override CFLAGS_FOR_TARGET += \
|
||||
-fno-PIE \
|
||||
|
@ -200,7 +203,7 @@ ifeq ($(TARGET),uefi-riscv64)
|
|||
-z text
|
||||
endif
|
||||
|
||||
override C_FILES := $(shell find . -type f -name '*.c')
|
||||
override C_FILES := $(shell find . -xtype f -name '*.c')
|
||||
ifeq ($(TARGET),bios)
|
||||
override ASMX86_FILES := $(shell find . -type f -name '*.asm_x86')
|
||||
override ASM32_FILES := $(shell find . -type f -name '*.asm_ia32')
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <stdint.h>
|
||||
#include <config.h>
|
||||
#include <console.h>
|
||||
#include <kuroko-limine.h>
|
||||
#include <menu.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <lib/print.h>
|
||||
|
@ -18,6 +19,7 @@ static void console_help(void) {
|
|||
"exit -- Exit Limine console.\n"
|
||||
"clear -- Clear the console.\n"
|
||||
"%s"
|
||||
"kuroko -- Start a Kuroko repl.\n"
|
||||
"lsvol -- List volumes.\n"
|
||||
"version -- Print version.\n"
|
||||
"copyright -- Print copyright.\n"
|
||||
|
@ -55,6 +57,8 @@ void console(void) {
|
|||
print(LIMINE_COPYRIGHT "\n");
|
||||
print("Limine is distributed under the terms of the BSD-2-Clause license.\n");
|
||||
print("There is ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n");
|
||||
} else if (strcmp(prompt, "kuroko") == 0) {
|
||||
limine_krk_enter_repl();
|
||||
} else if (*prompt != 0) {
|
||||
print("Invalid command: `%s`.\n", prompt);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <fs/file.h>
|
||||
#include <lib/elf.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <kuroko-limine.h>
|
||||
#include <menu.h>
|
||||
#include <pxe/pxe.h>
|
||||
#include <pxe/tftp.h>
|
||||
|
@ -130,6 +131,8 @@ noreturn void stage3_common(void) {
|
|||
init_io_apics();
|
||||
#endif
|
||||
|
||||
limine_krk_init();
|
||||
|
||||
term_notready();
|
||||
|
||||
menu(true);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef __KUROKO_LIMINE_H__
|
||||
#define __KUROKO_LIMINE_H__
|
||||
|
||||
void limine_krk_init(void);
|
||||
void limine_krk_enter_repl(void);
|
||||
void limine_krk_cleanup(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/builtins.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/chunk.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/compiler.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/debug.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/exceptions.c
|
|
@ -0,0 +1,198 @@
|
|||
/**
|
||||
* @file limine-os.c
|
||||
* @brief Limine bindings for os, time, fileio modules.
|
||||
*
|
||||
*/
|
||||
#include <config.h>
|
||||
#include <menu.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/config.h>
|
||||
#include <lib/time.h>
|
||||
#include <lib/readline.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <kuroko/vm.h>
|
||||
#include <kuroko/util.h>
|
||||
|
||||
extern int krk_exitRepl;
|
||||
extern void krk_repl(void);
|
||||
|
||||
KRK_Function(uname) {
|
||||
KrkValue result = krk_dict_of(0, NULL, 0);
|
||||
krk_push(result);
|
||||
krk_attachNamedObject(AS_DICT(result), "sysname", (KrkObj*)S("Limine"));
|
||||
krk_attachNamedObject(AS_DICT(result), "release", (KrkObj*)S(LIMINE_VERSION));
|
||||
krk_attachNamedObject(AS_DICT(result), "nodename", (KrkObj*)S(""));
|
||||
|
||||
krk_attachNamedObject(AS_DICT(result), "version", (KrkObj*)
|
||||
#if defined(BIOS)
|
||||
S("BIOS")
|
||||
#elif defined(UEFI)
|
||||
S("UEFI")
|
||||
#else
|
||||
S("")
|
||||
#endif
|
||||
);
|
||||
|
||||
krk_attachNamedObject(AS_DICT(result), "machine", (KrkObj*)
|
||||
#if defined(__x86_64__)
|
||||
S("x86-64")
|
||||
#elif defined(__aarch64__)
|
||||
S("aarch64")
|
||||
#elif defined(__i386__)
|
||||
S("i386")
|
||||
#elif defined(__riscv64)
|
||||
S("riscv64")
|
||||
#else
|
||||
S("unknown")
|
||||
#endif
|
||||
);
|
||||
|
||||
return krk_pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* def config_get_value(key: str, index: int = 0, config: str = None) -> str
|
||||
*/
|
||||
KRK_Function(config_get_value) {
|
||||
char * config = NULL;
|
||||
char * key = NULL;
|
||||
size_t index = 0;
|
||||
if (!krk_parseArgs("s|Nz", (const char*[]){"key", "index", "config"}, &key, &index, &config)) return NONE_VAL();
|
||||
char * value = config_get_value(config, index, key);
|
||||
|
||||
if (!value) {
|
||||
return NONE_VAL();
|
||||
}
|
||||
|
||||
return OBJECT_VAL(krk_copyString(value, strlen(value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* def boot(config: str) -> does not return
|
||||
*/
|
||||
KRK_Function(boot) {
|
||||
char * config = NULL;
|
||||
if (!krk_parseArgs("s", (const char*[]){"config"}, &config)) return NONE_VAL();
|
||||
|
||||
boot(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* def getchar() -> int
|
||||
*/
|
||||
KRK_Function(getchar) {
|
||||
return INTEGER_VAL(getchar());
|
||||
}
|
||||
|
||||
/**
|
||||
* def readline(prefix="",bufsize=1024) -> str
|
||||
*/
|
||||
KRK_Function(readline) {
|
||||
int size = 1024;
|
||||
char * prefix = "";
|
||||
if (!krk_parseArgs("|si", (const char*[]){"prefix","bufsize"}, &prefix, &size)) return NONE_VAL();
|
||||
|
||||
char * buf = malloc(size);
|
||||
readline(prefix,buf,size);
|
||||
krk_push(OBJECT_VAL(krk_copyString(buf,strlen(buf))));
|
||||
free(buf);
|
||||
|
||||
return krk_pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builtins
|
||||
*/
|
||||
KRK_Function(exit) {
|
||||
krk_exitRepl = 1;
|
||||
return NONE_VAL();
|
||||
}
|
||||
|
||||
KRK_Function(clear) {
|
||||
print("\e[2J\e[H");
|
||||
return NONE_VAL();
|
||||
}
|
||||
|
||||
void krk_module_init_os(void) {
|
||||
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_attachNamedObject(&vm.modules, "os", (KrkObj*)module);
|
||||
krk_attachNamedObject(&module->fields, "__name__", (KrkObj*)S("os"));
|
||||
krk_attachNamedValue(&module->fields, "__file__", NONE_VAL());
|
||||
BIND_FUNC(module,uname);
|
||||
|
||||
/* Let's also make a Limine-specific module */
|
||||
KrkInstance * limine = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_attachNamedObject(&vm.modules, "limine", (KrkObj*)limine);
|
||||
krk_attachNamedObject(&limine->fields, "__name__", (KrkObj*)S("limine"));
|
||||
krk_attachNamedValue(&limine->fields, "__file__", NONE_VAL());
|
||||
BIND_FUNC(limine,config_get_value);
|
||||
BIND_FUNC(limine,boot);
|
||||
BIND_FUNC(limine,getchar);
|
||||
BIND_FUNC(limine,readline);
|
||||
|
||||
/* And some builtins */
|
||||
|
||||
BIND_FUNC(vm.builtins,exit);
|
||||
BIND_FUNC(vm.builtins,clear);
|
||||
}
|
||||
|
||||
KRK_Function(time) {
|
||||
/* We don't have floats, so just return an int; 48 bits should be enough */
|
||||
return INTEGER_VAL(time());
|
||||
}
|
||||
|
||||
void krk_module_init_time(void) {
|
||||
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_attachNamedObject(&vm.modules, "time", (KrkObj*)module);
|
||||
krk_attachNamedObject(&module->fields, "__name__", (KrkObj*)S("time"));
|
||||
krk_attachNamedValue(&module->fields, "__file__", NONE_VAL());
|
||||
BIND_FUNC(module,time);
|
||||
}
|
||||
|
||||
void krk_module_init_fileio(void) {
|
||||
/* Nope. Maybe later. */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the Kuroko VM state and prep the @c limine module.
|
||||
*/
|
||||
void limine_krk_init(void) {
|
||||
krk_initVM(0);
|
||||
krk_startModule("__main__");
|
||||
krk_interpret(
|
||||
"if True:\n"
|
||||
" import limine\n"
|
||||
" def config(modules=[],**kwargs):\n"
|
||||
" let args = [f'{k.upper()}={v}' for k,v in kwargs.items()]\n"
|
||||
" for module in modules:\n"
|
||||
" args.append(f'MODULE_PATH={module}')\n"
|
||||
" return '\\n'.join(args)\n"
|
||||
" limine.config = config\n",
|
||||
"<stdin>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start a repl session.
|
||||
*/
|
||||
void limine_krk_enter_repl(void) {
|
||||
/* Print version number. */
|
||||
krk_interpret(
|
||||
"if True:\n"
|
||||
" import kuroko\n"
|
||||
" print(f'Kuroko {kuroko.version} ({kuroko.builddate}) with {kuroko.buildenv}')\n"
|
||||
" print('Type `help` for guidance, `exit()` to quit.')\n",
|
||||
"<stdin>");
|
||||
|
||||
/* Run the repl */
|
||||
krk_repl();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cleanup VM resources.
|
||||
*/
|
||||
void limine_krk_cleanup(void) {
|
||||
/* Free the VM resources; we never actually release the heap, so this doesn't really do anything,
|
||||
* but the heap is shared through multiple Kuroko sessions, so freeing it is still useful. */
|
||||
krk_freeVM();
|
||||
}
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <mm/pmm.h>
|
||||
|
||||
static void * sbrk(size_t s) {
|
||||
return ext_mem_alloc_type_aligned(s, MEMMAP_BOOTLOADER_RECLAIMABLE, 4096);
|
||||
}
|
||||
|
||||
#if defined(__x86_64__) || defined(__aarch64__) || defined(__riscv64)
|
||||
#define NUM_BINS 10U /* Number of bins, total, under 64-bit. */
|
||||
#define SMALLEST_BIN_LOG 3U /* Logarithm base two of the smallest bin: log_2(sizeof(int32)). */
|
||||
#else
|
||||
#define NUM_BINS 11U /* Number of bins, total, under 32-bit. */
|
||||
#define SMALLEST_BIN_LOG 2U /* Logarithm base two of the smallest bin: log_2(sizeof(int32)). */
|
||||
#endif
|
||||
#define BIG_BIN (NUM_BINS - 1) /* Index for the big bin, (NUM_BINS - 1) */
|
||||
#define SMALLEST_BIN (1UL << SMALLEST_BIN_LOG) /* Size of the smallest bin. */
|
||||
|
||||
#define PAGE_SIZE 0x1000 /* Size of a page (in bytes), should be 4KB */
|
||||
#define PAGE_MASK (PAGE_SIZE - 1) /* Block mask, size of a page * number of pages - 1. */
|
||||
|
||||
#define BIN_MAGIC 0xDEFAD00D
|
||||
|
||||
static void * __attribute__ ((malloc)) klmalloc(uintptr_t size);
|
||||
static void * __attribute__ ((malloc)) klrealloc(void * ptr, uintptr_t size);
|
||||
static void * __attribute__ ((malloc)) klcalloc(uintptr_t nmemb, uintptr_t size);
|
||||
static void klfree(void * ptr);
|
||||
|
||||
void * __attribute__ ((malloc)) malloc(uintptr_t size) {
|
||||
return klmalloc(size);
|
||||
}
|
||||
|
||||
void * __attribute__ ((malloc)) realloc(void * ptr, uintptr_t size) {
|
||||
return klrealloc(ptr, size);
|
||||
}
|
||||
|
||||
void * __attribute__ ((malloc)) calloc(uintptr_t nmemb, uintptr_t size) {
|
||||
return klcalloc(nmemb, size);
|
||||
}
|
||||
|
||||
void free(void * ptr) {
|
||||
klfree(ptr);
|
||||
}
|
||||
|
||||
static inline uintptr_t __attribute__ ((always_inline, pure)) klmalloc_adjust_bin(uintptr_t bin)
|
||||
{
|
||||
if (bin <= (uintptr_t)SMALLEST_BIN_LOG)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
bin -= SMALLEST_BIN_LOG + 1;
|
||||
if (bin > (uintptr_t)BIG_BIN) {
|
||||
return BIG_BIN;
|
||||
}
|
||||
return bin;
|
||||
}
|
||||
|
||||
static inline uintptr_t __attribute__ ((always_inline, pure)) klmalloc_bin_size(uintptr_t size) {
|
||||
uintptr_t bin = sizeof(size) * 8 - __builtin_clzl(size);
|
||||
bin += !!(size & (size - 1));
|
||||
return klmalloc_adjust_bin(bin);
|
||||
}
|
||||
|
||||
typedef struct _klmalloc_bin_header {
|
||||
struct _klmalloc_bin_header * next; /* Pointer to the next node. */
|
||||
void * head; /* Head of this bin. */
|
||||
uintptr_t size; /* Size of this bin, if big; otherwise bin index. */
|
||||
uint32_t bin_magic;
|
||||
} klmalloc_bin_header;
|
||||
|
||||
typedef struct _klmalloc_bin_header_head {
|
||||
klmalloc_bin_header * first;
|
||||
} klmalloc_bin_header_head;
|
||||
|
||||
static klmalloc_bin_header_head klmalloc_bin_head[NUM_BINS - 1]; /* Small bins */
|
||||
|
||||
static inline void __attribute__ ((always_inline)) klmalloc_list_decouple(klmalloc_bin_header_head *head, klmalloc_bin_header *node) {
|
||||
klmalloc_bin_header *next = node->next;
|
||||
head->first = next;
|
||||
node->next = NULL;
|
||||
}
|
||||
|
||||
static inline void __attribute__ ((always_inline)) klmalloc_list_insert(klmalloc_bin_header_head *head, klmalloc_bin_header *node) {
|
||||
node->next = head->first;
|
||||
head->first = node;
|
||||
}
|
||||
|
||||
static inline klmalloc_bin_header * __attribute__ ((always_inline)) klmalloc_list_head(klmalloc_bin_header_head *head) {
|
||||
return head->first;
|
||||
}
|
||||
|
||||
static void * klmalloc_stack_pop(klmalloc_bin_header *header) {
|
||||
void *item = header->head;
|
||||
uintptr_t **head = header->head;
|
||||
uintptr_t *next = *head;
|
||||
header->head = next;
|
||||
return item;
|
||||
}
|
||||
|
||||
static void klmalloc_stack_push(klmalloc_bin_header *header, void *ptr) {
|
||||
uintptr_t **item = (uintptr_t **)ptr;
|
||||
*item = (uintptr_t *)header->head;
|
||||
header->head = item;
|
||||
}
|
||||
|
||||
static inline int __attribute__ ((always_inline)) klmalloc_stack_empty(klmalloc_bin_header *header) {
|
||||
return header->head == NULL;
|
||||
}
|
||||
|
||||
static void * __attribute__ ((malloc)) klmalloc(uintptr_t size) {
|
||||
if (__builtin_expect(size == 0, 0))
|
||||
return NULL;
|
||||
unsigned int bucket_id = klmalloc_bin_size(size);
|
||||
|
||||
if (bucket_id < BIG_BIN) {
|
||||
klmalloc_bin_header * bin_header = klmalloc_list_head(&klmalloc_bin_head[bucket_id]);
|
||||
if (!bin_header) {
|
||||
bin_header = (klmalloc_bin_header*)sbrk(PAGE_SIZE);
|
||||
bin_header->bin_magic = BIN_MAGIC;
|
||||
bin_header->head = (void*)((uintptr_t)bin_header + sizeof(klmalloc_bin_header));
|
||||
klmalloc_list_insert(&klmalloc_bin_head[bucket_id], bin_header);
|
||||
uintptr_t adj = SMALLEST_BIN_LOG + bucket_id;
|
||||
uintptr_t i, available = ((PAGE_SIZE - sizeof(klmalloc_bin_header)) >> adj) - 1;
|
||||
|
||||
uintptr_t **base = bin_header->head;
|
||||
for (i = 0; i < available; ++i) {
|
||||
base[i << bucket_id] = (uintptr_t *)&base[(i + 1) << bucket_id];
|
||||
}
|
||||
base[available << bucket_id] = NULL;
|
||||
bin_header->size = bucket_id;
|
||||
}
|
||||
uintptr_t ** item = klmalloc_stack_pop(bin_header);
|
||||
if (klmalloc_stack_empty(bin_header)) {
|
||||
klmalloc_list_decouple(&(klmalloc_bin_head[bucket_id]),bin_header);
|
||||
}
|
||||
return item;
|
||||
} else {
|
||||
uintptr_t pages = (size + sizeof(klmalloc_bin_header)) / PAGE_SIZE + 1;
|
||||
klmalloc_bin_header * bin_header = (klmalloc_bin_header*)sbrk(PAGE_SIZE * pages);
|
||||
bin_header->bin_magic = BIN_MAGIC;
|
||||
bin_header->size = pages * PAGE_SIZE - sizeof(klmalloc_bin_header);
|
||||
bin_header->head = NULL;
|
||||
return (void*)((uintptr_t)bin_header + sizeof(klmalloc_bin_header));
|
||||
}
|
||||
}
|
||||
|
||||
static void klfree(void *ptr) {
|
||||
if (__builtin_expect(ptr == NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((uintptr_t)ptr % PAGE_SIZE == 0) {
|
||||
ptr = (void *)((uintptr_t)ptr - 1);
|
||||
}
|
||||
|
||||
klmalloc_bin_header * header = (klmalloc_bin_header *)((uintptr_t)ptr & (uintptr_t)~PAGE_MASK);
|
||||
|
||||
if (header->bin_magic != BIN_MAGIC)
|
||||
return;
|
||||
|
||||
uintptr_t bucket_id = header->size;
|
||||
if (bucket_id > (uintptr_t)NUM_BINS) {
|
||||
bucket_id = BIG_BIN;
|
||||
klmalloc_bin_header *bheader = (klmalloc_bin_header*)header;
|
||||
pmm_free(header, bheader->size + sizeof(klmalloc_bin_header));
|
||||
} else {
|
||||
if (klmalloc_stack_empty(header)) {
|
||||
klmalloc_list_insert(&klmalloc_bin_head[bucket_id], header);
|
||||
}
|
||||
klmalloc_stack_push(header, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void * __attribute__ ((malloc)) klrealloc(void *ptr, uintptr_t size) {
|
||||
if (__builtin_expect(ptr == NULL, 0))
|
||||
return klmalloc(size);
|
||||
|
||||
if (__builtin_expect(size == 0, 0))
|
||||
{
|
||||
free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
klmalloc_bin_header * header_old = (void *)((uintptr_t)ptr & (uintptr_t)~PAGE_MASK);
|
||||
if (header_old->bin_magic != BIN_MAGIC) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uintptr_t old_size = header_old->size;
|
||||
if (old_size < (uintptr_t)BIG_BIN) {
|
||||
old_size = (1UL << (SMALLEST_BIN_LOG + old_size));
|
||||
}
|
||||
|
||||
if (old_size == size) return ptr;
|
||||
|
||||
void * newptr = klmalloc(size);
|
||||
if (__builtin_expect(newptr != NULL, 1)) {
|
||||
memcpy(newptr, ptr, (old_size < size) ? old_size : size);
|
||||
klfree(ptr);
|
||||
return newptr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void * __attribute__ ((malloc)) klcalloc(uintptr_t nmemb, uintptr_t size) {
|
||||
void *ptr = klmalloc(nmemb * size);
|
||||
if (ptr) memset(ptr,0x00,nmemb * size);
|
||||
return ptr;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/memory.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/methods.h
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_base.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_bytes.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_dict.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_function.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_gen.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_list.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_long.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_numeric.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_range.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_set.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_slice.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_str.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_tuple.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/obj_typing.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/object.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/opcode_enum.h
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/opcodes.h
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/parseargs.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/private.h
|
|
@ -0,0 +1,464 @@
|
|||
/**
|
||||
* @file repl.c
|
||||
* @brief General-purpose Kuroko repl using rline, with tab completion.
|
||||
*
|
||||
* This is a generic repl. Initialize the @c __main__ module and then
|
||||
* call @c krk_repl() to run a repl session. You can bind a builtin
|
||||
* @c exit() function to set @c krk_exitRepl to 1 to exit the repl;
|
||||
* the repl will also terminate if rline detects an EOF condition.
|
||||
*/
|
||||
#include <kuroko/vm.h>
|
||||
#include <kuroko/scanner.h>
|
||||
#include <kuroko/util.h>
|
||||
#include <rline.h>
|
||||
|
||||
#define PROMPT_MAIN ">>> "
|
||||
#define PROMPT_BLOCK " > "
|
||||
|
||||
int krk_exitRepl = 0;
|
||||
|
||||
void krk_printResult(KrkValue result) {
|
||||
if (IS_NONE(result)) return;
|
||||
krk_attachNamedValue(&vm.builtins->fields, "_", result);
|
||||
KrkClass * type = krk_getType(result);
|
||||
const char * formatStr = " \033[1;90m=> %s\033[0m\n";
|
||||
if (type->_reprer) {
|
||||
krk_push(result);
|
||||
result = krk_callDirect(type->_reprer, 1);
|
||||
} else if (type->_tostr) {
|
||||
krk_push(result);
|
||||
result = krk_callDirect(type->_tostr, 1);
|
||||
}
|
||||
if (!IS_STRING(result)) {
|
||||
printf(" \033[1;91m=> Unable to produce representation for value.\033[0m\n");
|
||||
} else {
|
||||
printf(formatStr, AS_CSTRING(result));
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
static KrkValue findFromProperty(KrkValue current, KrkToken next) {
|
||||
KrkValue member = OBJECT_VAL(krk_copyString(next.start, next.literalWidth));
|
||||
krk_push(member);
|
||||
KrkValue value = krk_valueGetAttribute_default(current, AS_CSTRING(member), NONE_VAL());
|
||||
krk_pop();
|
||||
return value;
|
||||
}
|
||||
|
||||
static char * syn_krk_keywords[] = {
|
||||
"and","class","def","else","for","if","in","import","del",
|
||||
"let","not","or","return","while","try","except","raise",
|
||||
"continue","break","as","from","elif","lambda","with","is",
|
||||
"pass","assert","yield","finally","async","await",
|
||||
"True","False","None",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void tab_complete_func(rline_context_t * c) {
|
||||
/* Figure out where the cursor is and if we should be completing anything. */
|
||||
if (c->offset) {
|
||||
size_t stackIn = krk_currentThread.stackTop - krk_currentThread.stack;
|
||||
/* Copy up to the cursor... */
|
||||
char * tmp = malloc(c->offset + 1);
|
||||
memcpy(tmp, c->buffer, c->offset);
|
||||
tmp[c->offset] = '\0';
|
||||
/* and pass it to the scanner... */
|
||||
KrkScanner scanner = krk_initScanner(tmp);
|
||||
/* Logically, there can be at most (offset) tokens, plus some wiggle room. */
|
||||
KrkToken * space = malloc(sizeof(KrkToken) * (c->offset + 2));
|
||||
int count = 0;
|
||||
do {
|
||||
space[count++] = krk_scanToken(&scanner);
|
||||
} while (space[count-1].type != TOKEN_EOF && space[count-1].type != TOKEN_ERROR);
|
||||
|
||||
/* If count == 1, it was EOF or an error and we have nothing to complete. */
|
||||
if (count == 1) {
|
||||
goto _cleanup;
|
||||
}
|
||||
|
||||
/* Otherwise we want to see if we're on an identifier or a dot. */
|
||||
int base = 2;
|
||||
int n = base;
|
||||
if (space[count-base].type == TOKEN_DOT) {
|
||||
/* Dots we need to look back at the previous tokens for */
|
||||
n--;
|
||||
base--;
|
||||
} else if (space[count-base].type >= TOKEN_IDENTIFIER && space[count-base].type <= TOKEN_WITH) {
|
||||
/* Something alphanumeric; only for the last element */
|
||||
} else {
|
||||
/* Some other symbol */
|
||||
goto _cleanup;
|
||||
}
|
||||
|
||||
/* Work backwards to find the start of this chain of identifiers */
|
||||
while (n < count) {
|
||||
if (space[count-n-1].type != TOKEN_DOT) break;
|
||||
n++;
|
||||
if (n == count) break;
|
||||
if (space[count-n-1].type != TOKEN_IDENTIFIER) break;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n <= count) {
|
||||
/* Now work forwards, starting from the current globals. */
|
||||
KrkValue root = OBJECT_VAL(krk_currentThread.module);
|
||||
int isGlobal = 1;
|
||||
while (n > base) {
|
||||
/* And look at the potential fields for instances/classes */
|
||||
KrkValue next = findFromProperty(root, space[count-n]);
|
||||
if (IS_NONE(next)) {
|
||||
/* If we hit None, we found something invalid (or literally hit a None
|
||||
* object, but really the difference is minimal in this case: Nothing
|
||||
* useful to tab complete from here. */
|
||||
if (!isGlobal) goto _cleanup;
|
||||
/* Does this match a builtin? */
|
||||
if (!krk_tableGet_fast(&vm.builtins->fields,
|
||||
krk_copyString(space[count-n].start,space[count-n].literalWidth), &next) || IS_NONE(next)) {
|
||||
goto _cleanup;
|
||||
}
|
||||
}
|
||||
isGlobal = 0;
|
||||
root = next;
|
||||
n -= 2; /* To skip every other dot. */
|
||||
}
|
||||
|
||||
if (isGlobal && n < count && (space[count-n-1].type == TOKEN_IMPORT || space[count-n-1].type == TOKEN_FROM)) {
|
||||
KrkInstance * modules = krk_newInstance(vm.baseClasses->objectClass);
|
||||
root = OBJECT_VAL(modules);
|
||||
krk_push(root);
|
||||
for (size_t i = 0; i < vm.modules.capacity; ++i) {
|
||||
KrkTableEntry * entry = &vm.modules.entries[i];
|
||||
if (IS_KWARGS(entry->key)) continue;
|
||||
krk_attachNamedValue(&modules->fields, AS_CSTRING(entry->key), NONE_VAL());
|
||||
}
|
||||
}
|
||||
|
||||
/* Now figure out what we're completing - did we already have a partial symbol name? */
|
||||
int length = (space[count-base].type == TOKEN_DOT) ? 0 : (space[count-base].length);
|
||||
isGlobal = isGlobal && (length != 0);
|
||||
|
||||
/* Collect up to 256 of those that match */
|
||||
char * matches[256];
|
||||
int matchCount = 0;
|
||||
|
||||
/* Take the last symbol name from the chain and get its member list from dir() */
|
||||
|
||||
for (;;) {
|
||||
KrkValue dirList = krk_dirObject(1,(KrkValue[]){root},0);
|
||||
krk_push(dirList);
|
||||
if (!IS_INSTANCE(dirList)) {
|
||||
fprintf(stderr,"\nInternal error while tab completting.\n");
|
||||
goto _cleanup;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < AS_LIST(dirList)->count; ++i) {
|
||||
KrkString * s = AS_STRING(AS_LIST(dirList)->values[i]);
|
||||
krk_push(OBJECT_VAL(s));
|
||||
KrkToken asToken = {.start = s->chars, .literalWidth = s->length};
|
||||
KrkValue thisValue = findFromProperty(root, asToken);
|
||||
krk_push(thisValue);
|
||||
if (IS_CLOSURE(thisValue) || IS_BOUND_METHOD(thisValue) || IS_NATIVE(thisValue)) {
|
||||
size_t allocSize = s->length + 2;
|
||||
char * tmp = malloc(allocSize);
|
||||
size_t len = snprintf(tmp, allocSize, "%s(", s->chars);
|
||||
s = krk_takeString(tmp, len);
|
||||
krk_pop();
|
||||
krk_push(OBJECT_VAL(s));
|
||||
} else {
|
||||
krk_pop();
|
||||
}
|
||||
|
||||
/* If this symbol is shorter than the current submatch, skip it. */
|
||||
if (length && (int)s->length < length) continue;
|
||||
|
||||
/* See if it's already in the matches */
|
||||
int found = 0;
|
||||
for (int i = 0; i < matchCount; ++i) {
|
||||
if (!strcmp(matches[i], s->chars)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) continue;
|
||||
|
||||
if (!memcmp(s->chars, space[count-base].start, length)) {
|
||||
matches[matchCount] = s->chars;
|
||||
matchCount++;
|
||||
if (matchCount == 255) goto _toomany;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the object we were scanning was the current module,
|
||||
* then we should also throw the builtins into the ring.
|
||||
*/
|
||||
if (isGlobal && AS_OBJECT(root) == (KrkObj*)krk_currentThread.module) {
|
||||
root = OBJECT_VAL(vm.builtins);
|
||||
continue;
|
||||
} else if (isGlobal && AS_OBJECT(root) == (KrkObj*)vm.builtins) {
|
||||
KrkInstance * fakeKeywordsObject = krk_newInstance(vm.baseClasses->objectClass);
|
||||
root = OBJECT_VAL(fakeKeywordsObject);
|
||||
krk_push(root);
|
||||
for (char ** keyword = syn_krk_keywords; *keyword; keyword++) {
|
||||
krk_attachNamedValue(&fakeKeywordsObject->fields, *keyword, NONE_VAL());
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_toomany:
|
||||
|
||||
/* Now we can do things with the matches. */
|
||||
if (matchCount == 1) {
|
||||
/* If there was only one, just fill it. */
|
||||
rline_insert(c, matches[0] + length);
|
||||
rline_place_cursor();
|
||||
} else if (matchCount) {
|
||||
/* Otherwise, try to find a common substring among them... */
|
||||
int j = length;
|
||||
while (1) {
|
||||
char m = matches[0][j];
|
||||
if (!m) break;
|
||||
int diff = 0;
|
||||
for (int i = 1; i < matchCount; ++i) {
|
||||
if (matches[i][j] != m) {
|
||||
diff = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (diff) break;
|
||||
j++;
|
||||
}
|
||||
/* If no common sub string could be filled in, we print the list. */
|
||||
if (j == length) {
|
||||
/* First find the maximum width of an entry */
|
||||
int maxWidth = 0;
|
||||
for (int i = 0; i < matchCount; ++i) {
|
||||
if ((int)strlen(matches[i]) > maxWidth) maxWidth = strlen(matches[i]);
|
||||
}
|
||||
/* Now how many can we fit in a screen */
|
||||
int colsPerLine = rline_terminal_width / (maxWidth + 2); /* +2 for the spaces */
|
||||
fprintf(stderr, "\n");
|
||||
int column = 0;
|
||||
for (int i = 0; i < matchCount; ++i) {
|
||||
fprintf(stderr, "%-*s ", maxWidth, matches[i]);
|
||||
column += 1;
|
||||
if (column >= colsPerLine) {
|
||||
fprintf(stderr, "\n");
|
||||
column = 0;
|
||||
}
|
||||
}
|
||||
if (column != 0) fprintf(stderr, "\n");
|
||||
} else {
|
||||
/* If we do have a common sub string, fill in those characters. */
|
||||
for (int i = length; i < j; ++i) {
|
||||
char tmp[2] = {matches[0][i], '\0'};
|
||||
rline_insert(c, tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_cleanup:
|
||||
free(tmp);
|
||||
free(space);
|
||||
krk_currentThread.stackTop = &krk_currentThread.stack[stackIn];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void krk_repl(void) {
|
||||
krk_exitRepl = 0;
|
||||
while (!krk_exitRepl) {
|
||||
size_t lineCapacity = 8;
|
||||
size_t lineCount = 0;
|
||||
char ** lines = ALLOCATE(char *, lineCapacity);
|
||||
size_t totalData = 0;
|
||||
int valid = 1;
|
||||
char * allData = NULL;
|
||||
int inBlock = 0;
|
||||
int blockWidth = 0;
|
||||
rline_exp_set_prompts(PROMPT_MAIN, "", 4, 0);
|
||||
rline_exit_string="exit";
|
||||
rline_exp_set_syntax("krk");
|
||||
rline_exp_set_tab_complete_func(tab_complete_func);
|
||||
|
||||
while (1) {
|
||||
char buf[4096] = {0};
|
||||
if (inBlock) {
|
||||
rline_exp_set_prompts(PROMPT_BLOCK, "", 4, 0);
|
||||
rline_preload = malloc(blockWidth + 1);
|
||||
for (int i = 0; i < blockWidth; ++i) {
|
||||
rline_preload[i] = ' ';
|
||||
}
|
||||
rline_preload[blockWidth] = '\0';
|
||||
}
|
||||
rline_scroll = 0;
|
||||
if (rline(buf, 4096) == 0) {
|
||||
valid = 0;
|
||||
krk_exitRepl = 1;
|
||||
break;
|
||||
}
|
||||
if (krk_currentThread.flags & KRK_THREAD_SIGNALLED) {
|
||||
/* We should actually be properly raising the interrupt and printing it with an empty traceback, but whatever. */
|
||||
krk_currentThread.flags &= ~(KRK_THREAD_SIGNALLED); /* Clear signal flag */
|
||||
printf("KeyboardInterrupt\n");
|
||||
valid = 0;
|
||||
break;
|
||||
}
|
||||
if (buf[strlen(buf)-1] != '\n') {
|
||||
valid = 0;
|
||||
break;
|
||||
}
|
||||
if (lineCapacity < lineCount + 1) {
|
||||
size_t old = lineCapacity;
|
||||
lineCapacity = GROW_CAPACITY(old);
|
||||
lines = GROW_ARRAY(char *,lines,old,lineCapacity);
|
||||
}
|
||||
int i = lineCount++;
|
||||
lines[i] = strdup(buf);
|
||||
size_t lineLength = strlen(lines[i]);
|
||||
totalData += lineLength;
|
||||
int isSpaces = 1;
|
||||
int countSpaces = 0;
|
||||
for (size_t j = 0; j < lineLength; ++j) {
|
||||
if (lines[i][j] != ' ' && lines[i][j] != '\n') {
|
||||
isSpaces = 0;
|
||||
break;
|
||||
}
|
||||
countSpaces += 1;
|
||||
}
|
||||
if (lineLength > 1 && lines[i][lineLength-2] == ':') {
|
||||
inBlock = 1;
|
||||
blockWidth = countSpaces + 4;
|
||||
continue;
|
||||
} else if (lineLength > 1 && lines[i][lineLength-2] == '\\') {
|
||||
inBlock = 1;
|
||||
continue;
|
||||
} else if (inBlock && lineLength != 1) {
|
||||
if (isSpaces) {
|
||||
free(lines[i]);
|
||||
totalData -= lineLength;
|
||||
lineCount--;
|
||||
break;
|
||||
}
|
||||
blockWidth = countSpaces;
|
||||
continue;
|
||||
} else if (lineLength > 1 && lines[i][countSpaces] == '@') {
|
||||
inBlock = 1;
|
||||
blockWidth = countSpaces;
|
||||
continue;
|
||||
}
|
||||
if (isSpaces && !i) valid = 0;
|
||||
break;
|
||||
}
|
||||
if (valid) {
|
||||
allData = malloc(totalData + 1);
|
||||
allData[0] = '\0';
|
||||
}
|
||||
for (size_t i = 0; i < lineCount; ++i) {
|
||||
if (valid) strcat(allData, lines[i]);
|
||||
rline_history_insert(strdup(lines[i]));
|
||||
rline_scroll = 0;
|
||||
free(lines[i]);
|
||||
}
|
||||
FREE_ARRAY(char *, lines, lineCapacity);
|
||||
if (valid) {
|
||||
KrkValue result = krk_interpret(allData, "<stdin>");
|
||||
krk_printResult(result);
|
||||
krk_resetStack();
|
||||
}
|
||||
(void)blockWidth;
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
/* repl without rline */
|
||||
void krk_repl(void) {
|
||||
krk_exitRepl = 0;
|
||||
while (!krk_exitRepl) {
|
||||
size_t lineCapacity = 8;
|
||||
size_t lineCount = 0;
|
||||
char ** lines = ALLOCATE(char *, lineCapacity);
|
||||
size_t totalData = 0;
|
||||
int valid = 1;
|
||||
char * allData = NULL;
|
||||
int inBlock = 0;
|
||||
int blockWidth = 0;
|
||||
|
||||
while (1) {
|
||||
char buf[4096] = {0};
|
||||
print(">>> ");
|
||||
readline("", buf, 4096);
|
||||
if (krk_currentThread.flags & KRK_THREAD_SIGNALLED) {
|
||||
/* We should actually be properly raising the interrupt and printing it with an empty traceback, but whatever. */
|
||||
krk_currentThread.flags &= ~(KRK_THREAD_SIGNALLED); /* Clear signal flag */
|
||||
printf("KeyboardInterrupt\n");
|
||||
valid = 0;
|
||||
break;
|
||||
}
|
||||
if (buf[strlen(buf)-1] != '\n') {
|
||||
valid = 0;
|
||||
break;
|
||||
}
|
||||
if (lineCapacity < lineCount + 1) {
|
||||
size_t old = lineCapacity;
|
||||
lineCapacity = GROW_CAPACITY(old);
|
||||
lines = GROW_ARRAY(char *,lines,old,lineCapacity);
|
||||
}
|
||||
int i = lineCount++;
|
||||
lines[i] = strdup(buf);
|
||||
size_t lineLength = strlen(lines[i]);
|
||||
totalData += lineLength;
|
||||
int isSpaces = 1;
|
||||
int countSpaces = 0;
|
||||
for (size_t j = 0; j < lineLength; ++j) {
|
||||
if (lines[i][j] != ' ' && lines[i][j] != '\n') {
|
||||
isSpaces = 0;
|
||||
break;
|
||||
}
|
||||
countSpaces += 1;
|
||||
}
|
||||
if (lineLength > 1 && lines[i][lineLength-2] == ':') {
|
||||
inBlock = 1;
|
||||
blockWidth = countSpaces + 4;
|
||||
continue;
|
||||
} else if (lineLength > 1 && lines[i][lineLength-2] == '\\') {
|
||||
inBlock = 1;
|
||||
continue;
|
||||
} else if (inBlock && lineLength != 1) {
|
||||
if (isSpaces) {
|
||||
free(lines[i]);
|
||||
totalData -= lineLength;
|
||||
lineCount--;
|
||||
break;
|
||||
}
|
||||
blockWidth = countSpaces;
|
||||
continue;
|
||||
} else if (lineLength > 1 && lines[i][countSpaces] == '@') {
|
||||
inBlock = 1;
|
||||
blockWidth = countSpaces;
|
||||
continue;
|
||||
}
|
||||
if (isSpaces && !i) valid = 0;
|
||||
break;
|
||||
}
|
||||
if (valid) {
|
||||
allData = malloc(totalData + 1);
|
||||
allData[0] = '\0';
|
||||
}
|
||||
for (size_t i = 0; i < lineCount; ++i) {
|
||||
if (valid) strcat(allData, lines[i]);
|
||||
free(lines[i]);
|
||||
}
|
||||
FREE_ARRAY(char *, lines, lineCapacity);
|
||||
if (valid) {
|
||||
KrkValue result = krk_interpret(allData, "<stdin>");
|
||||
krk_printResult(result);
|
||||
krk_resetStack();
|
||||
}
|
||||
(void)blockWidth;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/scanner.c
|
|
@ -0,0 +1,568 @@
|
|||
/**
|
||||
* @file stubs.c
|
||||
* @brief Implementations of libc functions needed under EFI and BIOS.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <lib/print.h>
|
||||
#include <lib/term.h>
|
||||
void print_(char *str) {
|
||||
FOR_TERM(flanterm_write(TERM, str, strlen(str)));
|
||||
}
|
||||
|
||||
void abort(void) {print_("ABORT\n"); while(1); }
|
||||
void exit(int status) {print_("EXIT\n"); while(1); }
|
||||
|
||||
char * strchr(const char * s, int c) {
|
||||
while (*s && *s != c) s++;
|
||||
return *s == c ? (char*)s : NULL;
|
||||
}
|
||||
|
||||
char * strrchr(const char * s, int c) {
|
||||
size_t l = strlen(s);
|
||||
for (size_t i = 0; i < l; ++i) {
|
||||
if (s[l-i-1] == c) {
|
||||
return (char*)&s[l-i-1];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) {
|
||||
if (!nmemb) return;
|
||||
if (!size) return;
|
||||
char * tmp = malloc(size);
|
||||
for (size_t i = 0; i < nmemb-1; ++i) {
|
||||
for (size_t j = 0; j < nmemb-1; ++j) {
|
||||
void * left = (char *)base + size * j;
|
||||
void * right = (char *)base + size * (j + 1);
|
||||
if (compar(left,right) > 0) {
|
||||
memcpy(tmp, right, size);
|
||||
memcpy(right, left, size);
|
||||
memcpy(left, tmp, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
static int isspace(int c) {
|
||||
return c == ' ';
|
||||
}
|
||||
|
||||
static int is_valid(int base, char c) {
|
||||
if (c < '0') return 0;
|
||||
if (base <= 10) {
|
||||
return c < ('0' + base);
|
||||
}
|
||||
|
||||
if (c >= 'a' && c < 'a' + (base - 10)) return 1;
|
||||
if (c >= 'A' && c < 'A' + (base - 10)) return 1;
|
||||
if (c >= '0' && c <= '9') return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int convert_digit(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
return c - 'a' + 0xa;
|
||||
}
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return c - 'A' + 0xa;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define LONG_MAX 2147483647
|
||||
#define LONG_LONG_MAX 0x7FFFffffFFFFffffULL
|
||||
|
||||
#define strtox(max, type) \
|
||||
if (base < 0 || base == 1 || base > 36) { \
|
||||
return max; \
|
||||
} \
|
||||
while (*nptr && isspace(*nptr)) nptr++; \
|
||||
int sign = 1; \
|
||||
if (*nptr == '-') { \
|
||||
sign = -1; \
|
||||
nptr++; \
|
||||
} else if (*nptr == '+') { \
|
||||
nptr++; \
|
||||
} \
|
||||
if (base == 16) { \
|
||||
if (*nptr == '0') { \
|
||||
nptr++; \
|
||||
if (*nptr == 'x') { \
|
||||
nptr++; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (base == 0) { \
|
||||
if (*nptr == '0') { \
|
||||
base = 8; \
|
||||
nptr++; \
|
||||
if (*nptr == 'x') { \
|
||||
base = 16; \
|
||||
nptr++; \
|
||||
} \
|
||||
} else { \
|
||||
base = 10; \
|
||||
} \
|
||||
} \
|
||||
type val = 0; \
|
||||
while (is_valid(base, *nptr)) { \
|
||||
val *= base; \
|
||||
val += convert_digit(*nptr); \
|
||||
nptr++; \
|
||||
} \
|
||||
if (endptr) { \
|
||||
*endptr = (char *)nptr; \
|
||||
} \
|
||||
if (sign == -1) { \
|
||||
return -val; \
|
||||
} else { \
|
||||
return val; \
|
||||
}
|
||||
|
||||
long int strtol(const char *nptr, char **endptr, int base) {
|
||||
strtox(LONG_MAX, unsigned long int);
|
||||
}
|
||||
|
||||
long long int strtoll(const char *nptr, char **endptr, int base) {
|
||||
strtox(LONG_LONG_MAX, unsigned long long int);
|
||||
}
|
||||
|
||||
#define ULONG_MAX ((unsigned long)(-1))
|
||||
unsigned long int strtoul(const char *nptr, char **endptr, int base) {
|
||||
strtox(ULONG_MAX, unsigned long int);
|
||||
}
|
||||
|
||||
int atoi(const char * c) {
|
||||
return strtol(c,NULL,10);
|
||||
}
|
||||
|
||||
FILE * stdout = NULL;
|
||||
FILE * stderr = NULL;
|
||||
FILE * stdin = NULL;
|
||||
|
||||
int fputc(int c, FILE * stream) {
|
||||
if (stream == stdout) {
|
||||
char tmp[2] = {c,0};
|
||||
print_(tmp);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int fputs(const char * s, FILE * stream) {
|
||||
while (*s) {
|
||||
fputc(*s,stream);
|
||||
s++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int puts(const char * s) {
|
||||
fputs(s,stdout);
|
||||
fputc('\n',stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define OUT(c) do { callback(userData, (c)); written++; } while (0)
|
||||
static size_t print_dec(unsigned long long value, unsigned int width, int (*callback)(void*,char), void * userData, int fill_zero, int align_right, int precision) {
|
||||
size_t written = 0;
|
||||
unsigned long long n_width = 1;
|
||||
unsigned long long i = 9;
|
||||
if (precision == -1) precision = 1;
|
||||
|
||||
if (value == 0) {
|
||||
n_width = 0;
|
||||
} else {
|
||||
unsigned long long val = value;
|
||||
while (val >= 10UL) {
|
||||
val /= 10UL;
|
||||
n_width++;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_width < (unsigned long long)precision) n_width = precision;
|
||||
|
||||
int printed = 0;
|
||||
if (align_right) {
|
||||
while (n_width + printed < width) {
|
||||
OUT(fill_zero ? '0' : ' ');
|
||||
printed += 1;
|
||||
}
|
||||
|
||||
i = n_width;
|
||||
char tmp[100];
|
||||
while (i > 0) {
|
||||
unsigned long long n = value / 10;
|
||||
long long r = value % 10;
|
||||
tmp[i - 1] = r + '0';
|
||||
i--;
|
||||
value = n;
|
||||
}
|
||||
while (i < n_width) {
|
||||
OUT(tmp[i]);
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
i = n_width;
|
||||
char tmp[100];
|
||||
while (i > 0) {
|
||||
unsigned long long n = value / 10;
|
||||
long long r = value % 10;
|
||||
tmp[i - 1] = r + '0';
|
||||
i--;
|
||||
value = n;
|
||||
printed++;
|
||||
}
|
||||
while (i < n_width) {
|
||||
OUT(tmp[i]);
|
||||
i++;
|
||||
}
|
||||
while (printed < (long long)width) {
|
||||
OUT(fill_zero ? '0' : ' ');
|
||||
printed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hexadecimal to string
|
||||
*/
|
||||
static size_t print_hex(unsigned long long value, unsigned int width, int (*callback)(void*,char), void* userData, int fill_zero, int alt, int caps, int align) {
|
||||
size_t written = 0;
|
||||
int i = width;
|
||||
|
||||
unsigned long long n_width = 1;
|
||||
unsigned long long j = 0x0F;
|
||||
while (value > j && j < UINT64_MAX) {
|
||||
n_width += 1;
|
||||
j *= 0x10;
|
||||
j += 0x0F;
|
||||
}
|
||||
|
||||
if (!fill_zero && align == 1) {
|
||||
while (i > (long long)n_width + 2*!!alt) {
|
||||
OUT(' ');
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (alt) {
|
||||
OUT('0');
|
||||
OUT(caps ? 'X' : 'x');
|
||||
}
|
||||
|
||||
if (fill_zero && align == 1) {
|
||||
while (i > (long long)n_width + 2*!!alt) {
|
||||
OUT('0');
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
i = (long long)n_width;
|
||||
while (i-- > 0) {
|
||||
char c = (caps ? "0123456789ABCDEF" : "0123456789abcdef")[(value>>(i*4))&0xF];
|
||||
OUT(c);
|
||||
}
|
||||
|
||||
if (align == 0) {
|
||||
i = width;
|
||||
while (i > (long long)n_width + 2*!!alt) {
|
||||
OUT(' ');
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
/*
|
||||
* vasprintf()
|
||||
*/
|
||||
size_t xvasprintf(int (*callback)(void *, char), void * userData, const char * fmt, va_list args) {
|
||||
char * s;
|
||||
int precision = -1;
|
||||
size_t written = 0;
|
||||
for (const char *f = fmt; *f; f++) {
|
||||
if (*f != '%') {
|
||||
OUT(*f);
|
||||
continue;
|
||||
}
|
||||
++f;
|
||||
unsigned int arg_width = 0;
|
||||
int align = 1; /* right */
|
||||
int fill_zero = 0;
|
||||
int big = 0;
|
||||
int alt = 0;
|
||||
int always_sign = 0;
|
||||
while (1) {
|
||||
if (*f == '-') {
|
||||
align = 0;
|
||||
++f;
|
||||
} else if (*f == '#') {
|
||||
alt = 1;
|
||||
++f;
|
||||
} else if (*f == '*') {
|
||||
arg_width = (char)va_arg(args, int);
|
||||
++f;
|
||||
} else if (*f == '0') {
|
||||
fill_zero = 1;
|
||||
++f;
|
||||
} else if (*f == '+') {
|
||||
always_sign = 1;
|
||||
++f;
|
||||
} else if (*f == ' ') {
|
||||
always_sign = 2;
|
||||
++f;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (*f >= '0' && *f <= '9') {
|
||||
arg_width *= 10;
|
||||
arg_width += *f - '0';
|
||||
++f;
|
||||
}
|
||||
if (*f == '.') {
|
||||
++f;
|
||||
precision = 0;
|
||||
if (*f == '*') {
|
||||
precision = (int)va_arg(args, int);
|
||||
++f;
|
||||
} else {
|
||||
while (*f >= '0' && *f <= '9') {
|
||||
precision *= 10;
|
||||
precision += *f - '0';
|
||||
++f;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*f == 'l') {
|
||||
big = 1;
|
||||
++f;
|
||||
if (*f == 'l') {
|
||||
big = 2;
|
||||
++f;
|
||||
}
|
||||
}
|
||||
if (*f == 'j') {
|
||||
big = (sizeof(uintmax_t) == sizeof(unsigned long long) ? 2 :
|
||||
sizeof(uintmax_t) == sizeof(unsigned long) ? 1 : 0);
|
||||
++f;
|
||||
}
|
||||
if (*f == 'z') {
|
||||
big = (sizeof(size_t) == sizeof(unsigned long long) ? 2 :
|
||||
sizeof(size_t) == sizeof(unsigned long) ? 1 : 0);
|
||||
++f;
|
||||
}
|
||||
if (*f == 't') {
|
||||
big = (sizeof(ptrdiff_t) == sizeof(unsigned long long) ? 2 :
|
||||
sizeof(ptrdiff_t) == sizeof(unsigned long) ? 1 : 0);
|
||||
++f;
|
||||
}
|
||||
/* fmt[i] == '%' */
|
||||
switch (*f) {
|
||||
case 's': /* String pointer -> String */
|
||||
{
|
||||
size_t count = 0;
|
||||
if (big) {
|
||||
return written;
|
||||
} else {
|
||||
s = (char *)va_arg(args, char *);
|
||||
if (s == NULL) {
|
||||
s = "(null)";
|
||||
}
|
||||
if (precision >= 0) {
|
||||
while (*s && precision > 0) {
|
||||
OUT(*s++);
|
||||
count++;
|
||||
precision--;
|
||||
if (arg_width && count == arg_width) break;
|
||||
}
|
||||
} else {
|
||||
while (*s) {
|
||||
OUT(*s++);
|
||||
count++;
|
||||
if (arg_width && count == arg_width) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (count < arg_width) {
|
||||
OUT(' ');
|
||||
count++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'c': /* Single character */
|
||||
OUT((char)va_arg(args,int));
|
||||
break;
|
||||
case 'p':
|
||||
alt = 1;
|
||||
if (sizeof(void*) == sizeof(long long)) big = 2;
|
||||
/* fallthrough */
|
||||
case 'X':
|
||||
case 'x': /* Hexadecimal number */
|
||||
{
|
||||
unsigned long long val;
|
||||
if (big == 2) {
|
||||
val = (unsigned long long)va_arg(args, unsigned long long);
|
||||
} else if (big == 1) {
|
||||
val = (unsigned long)va_arg(args, unsigned long);
|
||||
} else {
|
||||
val = (unsigned int)va_arg(args, unsigned int);
|
||||
}
|
||||
written += print_hex(val, arg_width, callback, userData, fill_zero, alt, !(*f & 32), align);
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
case 'd': /* Decimal number */
|
||||
{
|
||||
long long val;
|
||||
if (big == 2) {
|
||||
val = (long long)va_arg(args, long long);
|
||||
} else if (big == 1) {
|
||||
val = (long)va_arg(args, long);
|
||||
} else {
|
||||
val = (int)va_arg(args, int);
|
||||
}
|
||||
if (val < 0) {
|
||||
OUT('-');
|
||||
val = -val;
|
||||
} else if (always_sign) {
|
||||
OUT(always_sign == 2 ? ' ' : '+');
|
||||
}
|
||||
written += print_dec(val, arg_width, callback, userData, fill_zero, align, precision);
|
||||
}
|
||||
break;
|
||||
case 'u': /* Unsigned ecimal number */
|
||||
{
|
||||
unsigned long long val;
|
||||
if (big == 2) {
|
||||
val = (unsigned long long)va_arg(args, unsigned long long);
|
||||
} else if (big == 1) {
|
||||
val = (unsigned long)va_arg(args, unsigned long);
|
||||
} else {
|
||||
val = (unsigned int)va_arg(args, unsigned int);
|
||||
}
|
||||
written += print_dec(val, arg_width, callback, userData, fill_zero, align, precision);
|
||||
}
|
||||
break;
|
||||
case '%': /* Escape */
|
||||
OUT('%');
|
||||
break;
|
||||
default: /* Nothing at all, just dump it */
|
||||
OUT(*f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
struct CBData {
|
||||
char * str;
|
||||
size_t size;
|
||||
size_t written;
|
||||
};
|
||||
|
||||
static int cb_sprintf(void * user, char c) {
|
||||
struct CBData * data = user;
|
||||
if (data->size > data->written + 1) {
|
||||
data->str[data->written] = c;
|
||||
data->written++;
|
||||
if (data->written < data->size) {
|
||||
data->str[data->written] = '\0';
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vsnprintf(char *str, size_t size, const char *format, va_list ap) {
|
||||
struct CBData data = {str,size,0};
|
||||
int out = xvasprintf(cb_sprintf, &data, format, ap);
|
||||
cb_sprintf(&data, '\0');
|
||||
return out;
|
||||
}
|
||||
|
||||
int snprintf(char * str, size_t size, const char * format, ...) {
|
||||
struct CBData data = {str,size,0};
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int out = xvasprintf(cb_sprintf, &data, format, args);
|
||||
va_end(args);
|
||||
cb_sprintf(&data, '\0');
|
||||
return out;
|
||||
}
|
||||
|
||||
static int cb_fprintf(void * user, char c) {
|
||||
fputc(c,(FILE*)user);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fprintf(FILE *stream, const char * fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int out = xvasprintf(cb_fprintf, stream, fmt, args);
|
||||
va_end(args);
|
||||
return out;
|
||||
}
|
||||
|
||||
char * strdup(const char * src) {
|
||||
char * out = malloc(strlen(src)+1);
|
||||
char * c = out;
|
||||
while (*src) {
|
||||
*c = *src;
|
||||
c++; src++;
|
||||
}
|
||||
*c = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
char *strstr(const char *haystack, const char *needle) {
|
||||
size_t s = strlen(needle);
|
||||
const char * end = haystack + strlen(haystack);
|
||||
|
||||
while (haystack + s <= end) {
|
||||
if (!memcmp(haystack,needle,s)) return (char*)haystack;
|
||||
haystack++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char * strcat(char *dest, const char *src) {
|
||||
char * end = dest;
|
||||
while (*end != '\0') {
|
||||
++end;
|
||||
}
|
||||
while (*src) {
|
||||
*end = *src;
|
||||
end++;
|
||||
src++;
|
||||
}
|
||||
*end = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
void ___chkstk_ms(void) { }
|
||||
|
||||
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE * stream) {
|
||||
for (size_t i = 0; i < size * nmemb; ++i) {
|
||||
fputc(((char*)ptr)[i], stream);
|
||||
}
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
void fflush(FILE * f) {
|
||||
/* do nothing */
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/sys.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/table.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/threads.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/value.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/vm.c
|
|
@ -0,0 +1 @@
|
|||
../../kuroko/src/wcwidth._h
|
|
@ -91,6 +91,8 @@ int getchar_internal(uint8_t scancode, uint8_t ascii, uint32_t shift_state) {
|
|||
}
|
||||
}
|
||||
|
||||
if (ascii == '\t') return ascii;
|
||||
|
||||
// Guard against non-printable values
|
||||
if (ascii < 0x20 || ascii > 0x7e) {
|
||||
return -1;
|
||||
|
@ -310,7 +312,7 @@ again:
|
|||
}
|
||||
|
||||
if (kd.Key.ScanCode == 0x08) {
|
||||
return '\b';
|
||||
return GETCHAR_DELETE;
|
||||
}
|
||||
|
||||
if (kd.Key.ScanCode == SCAN_ESC) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdnoreturn.h>
|
||||
#include <config.h>
|
||||
#include <kuroko-limine.h>
|
||||
#include <menu.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/misc.h>
|
||||
|
@ -815,6 +816,10 @@ refresh:
|
|||
}
|
||||
set_cursor_pos_helper(terms[0]->cols - 13, 3);
|
||||
print("\e[32mC\e[0m Console");
|
||||
|
||||
set_cursor_pos_helper(terms[0]->cols - 24, 3);
|
||||
print("\e[32mK\e[0m Kuroko");
|
||||
|
||||
set_cursor_pos_helper(x, y);
|
||||
}
|
||||
|
||||
|
@ -928,6 +933,12 @@ timeout_aborted:
|
|||
console();
|
||||
goto refresh;
|
||||
}
|
||||
case 'k':
|
||||
case 'K': {
|
||||
reset_term();
|
||||
limine_krk_enter_repl();
|
||||
goto refresh;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
#define assert(...)
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct dirent {
|
||||
int d_ino;
|
||||
char d_name[256];
|
||||
} dirent;
|
||||
|
||||
typedef void * DIR;
|
||||
|
||||
extern DIR * opendir(const char * name);
|
||||
extern int closedir(DIR * dirp);
|
||||
extern struct dirent * readdir(DIR *dirp);
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* EFI is 32-bit long, 64-bit long long
|
||||
*/
|
||||
|
||||
#define PRIi8 "i"
|
||||
#define PRIi16 "i"
|
||||
#define PRIi32 "i"
|
||||
#define PRIi64 "lli"
|
||||
|
||||
#define PRIx8 "x"
|
||||
#define PRIx16 "x"
|
||||
#define PRIx32 "x"
|
||||
#define PRIx64 "llx"
|
||||
|
||||
#define PRIu8 "u"
|
||||
#define PRIu16 "u"
|
||||
#define PRIu32 "u"
|
||||
#define PRIu64 "llu"
|
||||
|
||||
#define PRId8 "d"
|
||||
#define PRId16 "d"
|
||||
#define PRId32 "d"
|
||||
#define PRId64 "lld"
|
|
@ -0,0 +1 @@
|
|||
../kuroko/src/kuroko
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
struct rline_callback;
|
||||
|
||||
typedef struct {
|
||||
char * buffer;
|
||||
struct rline_callback * callbacks;
|
||||
int collected;
|
||||
int requested;
|
||||
int newline;
|
||||
int cancel;
|
||||
int offset;
|
||||
int tabbed;
|
||||
int quiet;
|
||||
} rline_context_t;
|
||||
|
||||
typedef void (*rline_callback_t)(rline_context_t * context);
|
||||
|
||||
typedef struct rline_callback {
|
||||
rline_callback_t tab_complete;
|
||||
rline_callback_t redraw_prompt;
|
||||
rline_callback_t special_key;
|
||||
rline_callback_t key_up;
|
||||
rline_callback_t key_down;
|
||||
rline_callback_t key_left;
|
||||
rline_callback_t key_right;
|
||||
rline_callback_t rev_search;
|
||||
} rline_callbacks_t;
|
||||
|
||||
typedef enum {
|
||||
/* Base colors */
|
||||
RLINE_STYLE_MAIN,
|
||||
RLINE_STYLE_ALT,
|
||||
/* Syntax flags */
|
||||
RLINE_STYLE_KEYWORD,
|
||||
RLINE_STYLE_STRING,
|
||||
RLINE_STYLE_COMMENT,
|
||||
RLINE_STYLE_TYPE,
|
||||
RLINE_STYLE_PRAGMA,
|
||||
RLINE_STYLE_NUMERAL,
|
||||
} rline_style_t;
|
||||
|
||||
extern int rline(char * buffer, int buf_size);
|
||||
extern int rline_exp_set_prompts(char * left, char * right, int left_width, int right_width);
|
||||
extern int rline_exp_set_shell_commands(char ** cmds, int len);
|
||||
extern int rline_exp_set_tab_complete_func(rline_callback_t func);
|
||||
extern int rline_exp_set_syntax(char * name);
|
||||
extern void rline_history_insert(char * str);
|
||||
extern void rline_history_append_line(char * str);
|
||||
extern char * rline_history_get(int item);
|
||||
extern char * rline_history_prev(int item);
|
||||
extern void rline_place_cursor(void);
|
||||
extern void rline_set_colors(rline_style_t style);
|
||||
extern void rline_insert(rline_context_t * context, const char * what);
|
||||
extern int rline_terminal_width;
|
||||
|
||||
#define RLINE_HISTORY_ENTRIES 128
|
||||
extern char * rline_history[RLINE_HISTORY_ENTRIES];
|
||||
extern int rline_history_count;
|
||||
extern int rline_history_offset;
|
||||
extern int rline_scroll;
|
||||
extern char * rline_exit_string;
|
||||
extern char * rline_preload;
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
typedef void * FILE;
|
||||
|
||||
extern FILE * stdout;
|
||||
extern FILE * stderr;
|
||||
extern FILE * stdin;
|
||||
|
||||
extern int fprintf(FILE *stream, const char * fmt, ...);
|
||||
extern int snprintf(char * str, size_t size, const char * format, ...);
|
||||
extern int fputc(int c, FILE * stream);
|
||||
extern int vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
||||
extern int puts(const char * s);
|
||||
|
||||
#define printf(...) fprintf(stdout, __VA_ARGS__)
|
||||
|
||||
extern int fgetc(FILE * stream);
|
||||
extern FILE * fopen(const char * pathname, const char * mode);
|
||||
extern int fclose(FILE * stream);
|
||||
extern size_t fread(void * ptr, size_t size, size_t nmemb, FILE * stream);
|
||||
extern size_t fwrite(void * ptr, size_t size, size_t nmemb, FILE * stream);
|
||||
extern int fseek(FILE * stream, long offset, int whence);
|
||||
extern long ftell(FILE * stream);
|
||||
extern void fflush(FILE * stream);
|
||||
#define SEEK_SET 1
|
||||
#define SEEK_END 2
|
||||
struct stat {
|
||||
int pad;
|
||||
};
|
||||
extern int stat(const char*,struct stat*);
|
||||
extern int errno;
|
||||
extern char * strerror(int errnum);
|
||||
extern int feof(FILE * stream);
|
||||
#define ferror(o) (0)
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
extern void * realloc(void * ptr, size_t size);
|
||||
extern void free(void *ptr);
|
||||
extern void * malloc(size_t size);
|
||||
extern void * calloc(size_t nmemb, size_t size);
|
||||
extern double strtod(const char *nptr, char **endptr);
|
||||
extern long int strtol(const char *nptr, char **endptr, int base);
|
||||
extern long long int strtoll(const char *nptr, char **endptr, int base);
|
||||
extern unsigned long int strtoul(const char *nptr, char **endptr, int base);
|
||||
|
||||
extern void qsort(void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *));
|
||||
|
||||
extern void abort(void);
|
||||
extern void exit(int status);
|
||||
extern int atoi(const char * c);
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef __SIZE_TYPE__
|
||||
# if defined(__x86_64__) || defined(__aarch64__)
|
||||
typedef unsigned long long size_t;
|
||||
# else
|
||||
typedef unsigned long size_t;
|
||||
# endif
|
||||
#else
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
#endif
|
||||
|
||||
#ifndef ssize_t
|
||||
# if defined(__x86_64__) || defined(__aarch64__)
|
||||
typedef long long ssize_t;
|
||||
# else
|
||||
typedef long ssize_t;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
extern void * memcpy(void * restrict dest, const void * restrict src, size_t n);
|
||||
extern void * memset(void * dest, int c, size_t n);
|
||||
extern int strcmp(const char * l, const char * r);
|
||||
extern size_t strlen(const char *s);
|
||||
extern int memcmp(const void * vl, const void * vr, size_t n);
|
||||
extern char * strdup(const char * src);
|
||||
extern void * memmove(void * dest, const void * src, size_t n);
|
||||
extern char * strcat(char *dest, const char *src);
|
||||
extern char *strstr(const char *haystack, const char *needle);
|
||||
extern char * strchr(const char * s, int c);
|
||||
extern char * strrchr(const char * s, int c);
|
||||
extern char * strcpy(char * restrict dest, const char * restrict src);
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
struct timespec {
|
||||
int tv_sec;
|
||||
int tv_nsec;
|
||||
};
|
||||
|
||||
#define CLOCK_MONOTONIC 0
|
||||
#define clock_gettime(a,b)
|
Loading…
Reference in New Issue