// This file defines the Dusk core library. ByteBuffer :: struct { size: usize data: u8 *mut } fn new_buffer(size: usize): ByteBuffer { ByteBuffer { size: size data: malloc(size) as u8 *mut } } fn free_buffer(buffer: ByteBuffer *mut) { free(buffer.data as void *mut) buffer.data = 0 as usize as u8 *mut buffer.size = 0 } fn at(buffer: ByteBuffer*, index: usize): u8 { assert(index < buffer.size) *((buffer.data as usize + index) as u8*) } fn at_mut(buffer: ByteBuffer *mut, index: usize): u8 *mut { assert(index < buffer.size) (buffer.data as usize + index) as u8 *mut } fn at(lit: compiler.StringLiteral, index: usize): u8 { assert(index < lit.length) *((lit.data as usize + index) as u8*) } // COMPILER BUG: this seemingly can't be named print(), or there will be an infinite loop. fn print_literal(lit: compiler.StringLiteral) { i: usize = 0 while i < lit.length { print(at(lit, i)) i += 1 } } fn to_c_string(lit: compiler.StringLiteral): ByteBuffer { buf := new_buffer(lit.length) *at_mut(&mut buf, lit.length) = 0 buf } fn assert(condition: bool) { if !condition { panic("assertion failed") } } fn read_file(path: i8*): ByteBuffer { switch compiler.target { .macos: unix.read_file(path) .linux: unix.read_file(path) .windows: windows.read_file(path) } } fn print(str: i8*) { switch compiler.target { .macos: unix.print(str) .linux: unix.print(str) .windows: windows.print(str) } } fn println(str: i8*) { print(str) print("\n") } fn strlen(str: i8*): usize { len: usize = 0 while *((str as usize + len) as i8*) != 0 { len += 1 } len } windows :: mod { fn read_file(path: i8*): ByteBuffer { file :: kernel32.CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, 0 as usize as SECURITY_ATTRIBUTES *mut, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 as usize as HANDLE) invalid :: -1 as isize as usize as HANDLE if file as usize == INVALID_HANDLE_VALUE() as usize { panic("unable to open file") } size := 0 as u64 succ :: kernel32.GetFileSizeEx(file, &mut size) if succ == FALSE { panic("unable to get file size") } size :: size as usize buf :: malloc(size) i := 0 as usize while i < size { bytes_to_read :: (size - i) as DWORD bytes_read := 0 as DWORD succ :: kernel32.ReadFile(file, (buf as usize + i) as void *mut, bytes_to_read, &mut bytes_read, 0 as usize as OVERLAPPED *mut) if succ == FALSE { panic("unable to read from file") } i += bytes_read as usize } kernel32.CloseHandle(file); ByteBuffer { size: size data: buf as u8 *mut } } fn print(str: i8*) { stdout :: kernel32.GetStdHandle(STD_OUTPUT_HANDLE) if stdout as usize == INVALID_HANDLE_VALUE() as usize { panic("failed to get handle to stdout") } total_chars_to_write :: strlen(str) as DWORD chars_left_to_write := total_chars_to_write while chars_left_to_write > 0 { i :: total_chars_to_write - chars_left_to_write chars_written: DWORD = 0 succ :: kernel32.WriteConsoleA(stdout, (str as usize + i as usize) as void*, chars_left_to_write, &mut chars_written, 0 as usize as void *mut) if succ == 0 { panic("failed to write to console") } chars_left_to_write -= chars_written } } // FFI code. To be cleaned up later, likely with https://github.com/dusklang/dusk-windows HWND :: void *mut HANDLE :: void *mut MB_OK :: 0 as u32 FILE_SHARE_READ :: 1 as DWORD fn INVALID_HANDLE_VALUE(): HANDLE { -1 as isize as usize as HANDLE } DWORD :: u32 STD_INPUT_HANDLE :: (-10) as i32 as DWORD STD_OUTPUT_HANDLE :: (-11) as i32 as DWORD STD_ERROR_HANDLE :: (-12) as i32 as DWORD BOOL :: i32 TRUE :: 1 FALSE :: 0 GENERIC_READ :: 2147483648 as DWORD GENERIC_WRITE :: 1073741824 as DWORD CREATE_ALWAYS :: 2 as DWORD CREATE_NEW :: 1 as DWORD OPEN_ALWAYS :: 4 as DWORD OPEN_EXISTING :: 3 as DWORD TRUNCATE_EXISTING :: 5 as DWORD FILE_ATTRIBUTE_NORMAL :: 128 as DWORD // it is not currently possible to have constant pointers, which is why this is a function for now fn NULL(): void *mut { 0 as usize as void *mut } SECURITY_ATTRIBUTES :: struct { nLength: DWORD lpSecurityDescriptor: void *mut bInheritHandle: BOOL } OVERLAPPED :: struct {} kernel32 :: extern_mod("kernel32.dll") { fn GetStdHandle(nStdHandle: DWORD): HANDLE fn WriteConsoleA(hConsoleOutput: HANDLE, lpBuffer: void*, nNumberOfCharsToWrite: DWORD, lpNumberOfCharsWritten: DWORD *mut, lpReserved: void *mut): BOOL fn ReadConsoleA(hConsoleInput: HANDLE, lpBuffer: void *mut, nNumberOfCharsToRead: DWORD, lpNumberOfCharsRead: DWORD *mut, pInputControl: void *mut): BOOL fn CreateFileA(lpFileName: i8*, dwDesiredAccess: DWORD, dwShareMode: DWORD, lpSecurityAttributes: SECURITY_ATTRIBUTES *mut, dwCreationDisposition: DWORD, dwFlagsAndAttributes: DWORD, hTemplateFile: HANDLE): HANDLE fn CloseHandle(hObject: HANDLE): BOOL fn GetFileSizeEx(hFile: HANDLE, lpFileSize: u64 *mut): BOOL fn ReadFile(hFile: HANDLE, lpBuffer: void *mut, nNumberOfBytesToRead: DWORD, lpNumberOfBytesRead: DWORD *mut, lpOverlapped: OVERLAPPED *mut): BOOL fn QueryPerformanceCounter(lpPerformanceCount: u64 *mut): BOOL fn QueryPerformanceFrequency(lpFrequency: u64 *mut): BOOL } } unix :: mod { fn read_file(path: i8*): ByteBuffer { file :: libc.fopen(path, "rb") if file as usize == 0 { panic("Failed to open file") } libc.fseek(file, 0, SEEK_END); size :: libc.ftell(file) as usize libc.fseek(file, 0, SEEK_SET); data :: malloc(size) as u8 *mut libc.fread(data as void *mut, 1, size, file); libc.fclose(file); ByteBuffer { size: size data: data } } fn print(str: i8*) { //libc.fputs(str, libc.stdin); panic("userspace print is unimplemented on non-Windows platform") } // FFI stuff fn libc_path(): compiler.StringLiteral { switch compiler.target { .macos: "libSystem.dylib" .linux: "libc.so.6" .windows: "libc is not provided on windows" } } FILE :: struct {} size_t :: usize long_int :: i64 int :: i32 char :: i8 SEEK_SET: int: 0 SEEK_END: int: 2 libc :: extern_mod(libc_path()) { fn fopen(filename: i8*, mode: char*): FILE *mut fn fread(ptr: void *mut, size: size_t, count: size_t, stream: FILE *mut): size_t fn fseek(stream: FILE *mut, offset: long_int, origin: int): int fn ftell(stream: FILE *mut): long_int fn fclose(stream: FILE *mut): int fn fputs(s: char*, stream: FILE *mut): int // TODO: support dynamically-linked variables // TODO: I think on macOS these are actually defined with differently-named symbols, so we also need to support // specifying the symbol name // stdin: FILE *mut // stdout: FILE *mut // stderr: FILE *mut } }