remedyvm

A toy RISC virtual machine inspired by Bell Lab's `dis' and Tsoding's `bm'
git clone git://git.ethandl.dev/remedyvm
Log | Files | Refs

commit 95ab7b1fefc308baeacaeda507c3ac1af1a5c0fd
parent 25914a6e52fd416a35b4e40b4414e0cd776abe8b
Author: Ethan Long <ethandavidlong@gmail.com>
Date:   Fri, 14 Jun 2024 18:55:07 +1000

New directory structure

I will first implement everything in Rust and see where I want to go
from there.

Diffstat:
Rimplementations/rust/Cargo.toml -> Cargo.toml | 0
Dimplementations/C/README.md | 10----------
Dimplementations/C/examples/fib.rasm | 22----------------------
Dimplementations/C/examples/fib.rin | 0
Dimplementations/C/examples/fib_recursive.rasm | 26--------------------------
Dimplementations/C/nobuild.c | 99-------------------------------------------------------------------------------
Dimplementations/C/nobuild.h | 1151-------------------------------------------------------------------------------
Dimplementations/C/src/remcc.c | 477-------------------------------------------------------------------------------
Dimplementations/C/src/remcc.h | 131-------------------------------------------------------------------------------
Dimplementations/C/src/remvm_interp.c | 17-----------------
Dimplementations/C/tests/remcc_tests.c | 106-------------------------------------------------------------------------------
Rimplementations/rust/src/lib/parse.rs -> src/lib/parse.rs | 0
Rimplementations/rust/src/lib/remedy.rs -> src/lib/remedy.rs | 0
Rimplementations/rust/src/main.rs -> src/main.rs | 0
14 files changed, 0 insertions(+), 2039 deletions(-)

diff --git a/implementations/rust/Cargo.toml b/Cargo.toml diff --git a/implementations/C/README.md b/implementations/C/README.md @@ -1,10 +0,0 @@ -# C Implementation -This is the C implementation of remedyVM. - -## Building -This uses tsoding's [nobuild](https://github.com/tsoding/nobuild), to compile, run the following: -```console -$ cc ./nobuild.c -o nobuild -$ ./nobuild -``` -This should compile all the required files provided my `nobuild.c` implementation is correct. diff --git a/implementations/C/examples/fib.rasm b/implementations/C/examples/fib.rasm @@ -1,22 +0,0 @@ -; NOTE: This implementation could probably still be improved, but it will be far -; faster than fib_recursive, as it utilizes the register architecture more -; efficiently. It also uses less memory & scales O(1) with respect to memory. - -; Arg: a0 := n -; Returns: r0 := fib(n) -fib: - push [t0, t1, t2] - sub a0, a0, 3 ; a0 := a0 - 2 <=> m := n - 2 - move t0, 0 ; t0 := 0 <=> i := 0 - - move t1, 1 ; t1 := 1 <=> fib(0) = 1 - move t2, 1 ; t2 := 1 <=> fib(1) = 1 -fib_start: - add t2, t1, t2 ; t2 := t1 + t2 <=> fib(i + 2) = fib(i) + fib(i + 1) - swap t2, t1 ; (t1, t2) := (t2, t1) - add t0, t0, 1 ; t0 := t0 + 1 <=> i++ - cmp t0, a0 ; t0 vs a0? <=> i < m ? - jump.lt fib_start ; <=> if (i < m) { goto fib_start } - move r0, t1 ; r0 := t1 <=> ret := fib(n) ; as i + 2 = n - pop [t0, t1, t2] - return diff --git a/implementations/C/examples/fib.rin b/implementations/C/examples/fib.rin diff --git a/implementations/C/examples/fib_recursive.rasm b/implementations/C/examples/fib_recursive.rasm @@ -1,26 +0,0 @@ -; NOTE: This is probably the most inneficient implementation of the fibonacci -; function, the memory usage grows as O(2^n). It IS however a good stack test :) - -; Arg: a0 := n -; Returns: r0 := fib(n) -fib_rec: - cmp a0 1 ; a0 vs 1? <=> n <= 1? - jump.leq fib_base_case ; <=> if (n <= 1) { return 1 } - - push t0 - - push a0 ; stack := (a0 : stack) <=> stack := (n : stack) - sub a0, a0, 1 ; a1 := a0 - 1 <=> (n - 1) - call fib_rec ; <=> fib(n - 1) - move t0, r0 ; t0 := r0 <=> t0 := fib(n - 1) - - pop a0 ; a0 := head(stack) <=> n - sub a0, a0, 2 ; a0 := a0 - 2 <=> (n - 2) - call fib_rec ; <=> fib(n - 2) - add r0, r0, t0 ; r0 := r0 + t0 <=> ret := fib(n - 1) + fib(n - 2) - - pop t0 - return ; <=> return ret -fib_base_case: ; if (n <= 1) { return 1 } - move r0, 1 ; <=> ret := 1 - return ; <=> return ret diff --git a/implementations/C/nobuild.c b/implementations/C/nobuild.c @@ -1,99 +0,0 @@ -/* - I DID NOT MAKE THIS BUILD SYSTEM!! - All credit to Tsoding/Rexim, go check out the nobuild repo: - https://github.com/tsoding/nobuild - - Usage: - # Compile the build system - cc -o nobuild nobuild.c - # Compile the program - ./nobuild - */ -#define NOBUILD_IMPLEMENTATION -#include "./nobuild.h" - -#define CFLAGS "-Wall", "-Wextra", "-std=c99", "-pedantic" - -void build_target(const char *target) { - Cstr source_path = PATH("src", target); - Cstr dest_path = NOEXT(PATH("bin", target)); - - CMD("cc", CFLAGS, "-o", dest_path, source_path); -} - -void build(void) { - FOREACH_FILE_IN_DIR(target, "src", { - if (ENDS_WITH(target, ".c")) { - build_target(target); - } - }); -} - -void print_chain(const Chain *chain) { - INFO("input: %s", chain->input_filepath); - INFO("output: %s", chain->output_filepath); - FOREACH_ARRAY(Cmd, cmd, chain->cmds, { - INFO("cmd: %s", cmd_show(*cmd)); - }); -} - -void byte_compile_example(const char *example) { - Cstr source_path = PATH("examples", example); - Cstr dest_path = CONCAT(NOEXT(source_path), ".rin"); - - CMD("bin/remcc", source_path, dest_path); -} - -void byte_compile_examples(void) { - FOREACH_FILE_IN_DIR(example, "examples", { - if (ENDS_WITH(example, ".rasm")) { - byte_compile_example(example); - } - }); -} - -void run_example(const char *example) { - Cstr path = PATH("examples", example); - - CMD("bin/vm_interp", path); -} - -void run_examples(void) { - FOREACH_FILE_IN_DIR(example, "examples", { - if (ENDS_WITH(example, ".rin")) { - run_example(example); - } - }); -} - -void clean(void) { - FOREACH_FILE_IN_DIR(bin, "bin", { - if (!ENDS_WITH(bin, ".")) { - CMD("rm", PATH("bin", bin)); - } - }); - - FOREACH_FILE_IN_DIR(bytecode, "examples", { - if (ENDS_WITH(bytecode, ".rin")) { - CMD("rm", PATH("examples", bytecode)); - } - }); - - CMD("rm", "nobuild.old"); -} - -int main(int argc, char **argv) { - GO_REBUILD_URSELF(argc, argv); - - if (argc == 2 && strcmp(argv[1], "clean") == 0) { // Cleanup builds - clean(); - return 0; - } // else - - build(); - - byte_compile_examples(); - run_examples(); - - return 0; -} diff --git a/implementations/C/nobuild.h b/implementations/C/nobuild.h @@ -1,1151 +0,0 @@ -#ifndef NOBUILD_H_ -#define NOBUILD_H_ - -#ifndef _WIN32 -# define _POSIX_C_SOURCE 200809L -# include <sys/types.h> -# include <sys/wait.h> -# include <sys/stat.h> -# include <unistd.h> -# include <dirent.h> -# include <fcntl.h> -# define PATH_SEP "/" -typedef pid_t Pid; -typedef int Fd; -#else -# define WIN32_MEAN_AND_LEAN -# include "windows.h" -# include <process.h> -# define PATH_SEP "\\" -typedef HANDLE Pid; -typedef HANDLE Fd; -// minirent.h HEADER BEGIN //////////////////////////////////////// -// Copyright 2021 Alexey Kutepov <reximkut@gmail.com> -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// ============================================================ -// -// minirent — 0.0.1 — A subset of dirent interface for Windows. -// -// https://github.com/tsoding/minirent -// -// ============================================================ -// -// ChangeLog (https://semver.org/ is implied) -// -// 0.0.1 First Official Release - -#ifndef MINIRENT_H_ -#define MINIRENT_H_ - -#define WIN32_LEAN_AND_MEAN -#include "windows.h" - -struct dirent { - char d_name[MAX_PATH+1]; -}; - -typedef struct DIR DIR; - -DIR *opendir(const char *dirpath); -struct dirent *readdir(DIR *dirp); -int closedir(DIR *dirp); - -#endif // MINIRENT_H_ -// minirent.h HEADER END //////////////////////////////////////// - -// TODO(#28): use GetLastErrorAsString everywhere on Windows error reporting -LPSTR GetLastErrorAsString(void); - -#endif // _WIN32 - -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <string.h> -#include <errno.h> - -#define FOREACH_ARRAY(type, elem, array, body) \ - for (size_t elem_##index = 0; \ - elem_##index < array.count; \ - ++elem_##index) \ - { \ - type *elem = &array.elems[elem_##index]; \ - body; \ - } - -typedef const char * Cstr; - -int cstr_ends_with(Cstr cstr, Cstr postfix); -#define ENDS_WITH(cstr, postfix) cstr_ends_with(cstr, postfix) - -Cstr cstr_no_ext(Cstr path); -#define NOEXT(path) cstr_no_ext(path) - -typedef struct { - Cstr *elems; - size_t count; -} Cstr_Array; - -Cstr_Array cstr_array_make(Cstr first, ...); -Cstr_Array cstr_array_append(Cstr_Array cstrs, Cstr cstr); -Cstr cstr_array_join(Cstr sep, Cstr_Array cstrs); - -#define JOIN(sep, ...) cstr_array_join(sep, cstr_array_make(__VA_ARGS__, NULL)) -#define CONCAT(...) JOIN("", __VA_ARGS__) -#define PATH(...) JOIN(PATH_SEP, __VA_ARGS__) - -typedef struct { - Fd read; - Fd write; -} Pipe; - -Pipe pipe_make(void); - -typedef struct { - Cstr_Array line; -} Cmd; - -Fd fd_open_for_read(Cstr path); -Fd fd_open_for_write(Cstr path); -void fd_close(Fd fd); -void pid_wait(Pid pid); -Cstr cmd_show(Cmd cmd); -Pid cmd_run_async(Cmd cmd, Fd *fdin, Fd *fdout); -void cmd_run_sync(Cmd cmd); - -typedef struct { - Cmd *elems; - size_t count; -} Cmd_Array; - -// TODO(#1): no way to disable echo in nobuild scripts -// TODO(#2): no way to ignore fails -#define CMD(...) \ - do { \ - Cmd cmd = { \ - .line = cstr_array_make(__VA_ARGS__, NULL) \ - }; \ - INFO("CMD: %s", cmd_show(cmd)); \ - cmd_run_sync(cmd); \ - } while (0) - -typedef enum { - CHAIN_TOKEN_END = 0, - CHAIN_TOKEN_IN, - CHAIN_TOKEN_OUT, - CHAIN_TOKEN_CMD -} Chain_Token_Type; - -// A single token for the CHAIN(...) DSL syntax -typedef struct { - Chain_Token_Type type; - Cstr_Array args; -} Chain_Token; - -// TODO(#17): IN and OUT are already taken by WinAPI -#define IN(path) \ - (Chain_Token) { \ - .type = CHAIN_TOKEN_IN, \ - .args = cstr_array_make(path, NULL) \ - } - -#define OUT(path) \ - (Chain_Token) { \ - .type = CHAIN_TOKEN_OUT, \ - .args = cstr_array_make(path, NULL) \ - } - -#define CHAIN_CMD(...) \ - (Chain_Token) { \ - .type = CHAIN_TOKEN_CMD, \ - .args = cstr_array_make(__VA_ARGS__, NULL) \ - } - -// TODO(#20): pipes do not allow redirecting stderr -typedef struct { - Cstr input_filepath; - Cmd_Array cmds; - Cstr output_filepath; -} Chain; - -Chain chain_build_from_tokens(Chain_Token first, ...); -void chain_run_sync(Chain chain); -void chain_echo(Chain chain); - -// TODO(#15): PIPE does not report where exactly a syntactic error has happened -#define CHAIN(...) \ - do { \ - Chain chain = chain_build_from_tokens(__VA_ARGS__, (Chain_Token) {0}); \ - chain_echo(chain); \ - chain_run_sync(chain); \ - } while(0) - -#ifndef REBUILD_URSELF -# if _WIN32 -# if defined(__GNUC__) -# define REBUILD_URSELF(binary_path, source_path) CMD("gcc", "-o", binary_path, source_path) -# elif defined(__clang__) -# define REBUILD_URSELF(binary_path, source_path) CMD("clang", "-o", binary_path, source_path) -# elif defined(_MSC_VER) -# define REBUILD_URSELF(binary_path, source_path) CMD("cl.exe", source_path) -# endif -# else -# define REBUILD_URSELF(binary_path, source_path) CMD("cc", "-o", binary_path, source_path) -# endif -#endif - -// Go Rebuild Urself™ Technology -// -// How to use it: -// int main(int argc, char** argv) { -// GO_REBUILD_URSELF(argc, argv); -// // actual work -// return 0; -// } -// -// After your added this macro every time you run ./nobuild it will detect -// that you modified its original source code and will try to rebuild itself -// before doing any actual work. So you only need to bootstrap your build system -// once. -// -// The modification is detected by comparing the last modified times of the executable -// and its source code. The same way the make utility usually does it. -// -// The rebuilding is done by using the REBUILD_URSELF macro which you can redefine -// if you need a special way of bootstraping your build system. (which I personally -// do not recommend since the whole idea of nobuild is to keep the process of bootstrapping -// as simple as possible and doing all of the actual work inside of the nobuild) -// -#define GO_REBUILD_URSELF(argc, argv) \ - do { \ - const char *source_path = __FILE__; \ - assert(argc >= 1); \ - const char *binary_path = argv[0]; \ - \ - if (is_path1_modified_after_path2(source_path, binary_path)) { \ - RENAME(binary_path, CONCAT(binary_path, ".old")); \ - REBUILD_URSELF(binary_path, source_path); \ - Cmd cmd = { \ - .line = { \ - .elems = (Cstr*) argv, \ - .count = argc, \ - }, \ - }; \ - INFO("CMD: %s", cmd_show(cmd)); \ - cmd_run_sync(cmd); \ - exit(0); \ - } \ - } while(0) -// The implementation idea is stolen from https://github.com/zhiayang/nabs - -void rebuild_urself(const char *binary_path, const char *source_path); - -int path_is_dir(Cstr path); -#define IS_DIR(path) path_is_dir(path) - -int path_exists(Cstr path); -#define PATH_EXISTS(path) path_exists(path) - -void path_mkdirs(Cstr_Array path); -#define MKDIRS(...) \ - do { \ - Cstr_Array path = cstr_array_make(__VA_ARGS__, NULL); \ - INFO("MKDIRS: %s", cstr_array_join(PATH_SEP, path)); \ - path_mkdirs(path); \ - } while (0) - -void path_rename(Cstr old_path, Cstr new_path); -#define RENAME(old_path, new_path) \ - do { \ - INFO("RENAME: %s -> %s", old_path, new_path); \ - path_rename(old_path, new_path); \ - } while (0) - -void path_rm(Cstr path); -#define RM(path) \ - do { \ - INFO("RM: %s", path); \ - path_rm(path); \ - } while(0) - -#define FOREACH_FILE_IN_DIR(file, dirpath, body) \ - do { \ - struct dirent *dp = NULL; \ - DIR *dir = opendir(dirpath); \ - if (dir == NULL) { \ - PANIC("could not open directory %s: %s", \ - dirpath, strerror(errno)); \ - } \ - errno = 0; \ - while ((dp = readdir(dir))) { \ - const char *file = dp->d_name; \ - body; \ - } \ - \ - if (errno > 0) { \ - PANIC("could not read directory %s: %s", \ - dirpath, strerror(errno)); \ - } \ - \ - closedir(dir); \ - } while(0) - -#if defined(__GNUC__) || defined(__clang__) -// https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html -#define NOBUILD_PRINTF_FORMAT(STRING_INDEX, FIRST_TO_CHECK) __attribute__ ((format (printf, STRING_INDEX, FIRST_TO_CHECK))) -#else -#define NOBUILD_PRINTF_FORMAT(STRING_INDEX, FIRST_TO_CHECK) -#endif - -void VLOG(FILE *stream, Cstr tag, Cstr fmt, va_list args); -void INFO(Cstr fmt, ...) NOBUILD_PRINTF_FORMAT(1, 2); -void WARN(Cstr fmt, ...) NOBUILD_PRINTF_FORMAT(1, 2); -void ERRO(Cstr fmt, ...) NOBUILD_PRINTF_FORMAT(1, 2); -void PANIC(Cstr fmt, ...) NOBUILD_PRINTF_FORMAT(1, 2); - -char *shift_args(int *argc, char ***argv); - -#endif // NOBUILD_H_ - -//////////////////////////////////////////////////////////////////////////////// - -#ifdef NOBUILD_IMPLEMENTATION - -#ifdef _WIN32 -LPSTR GetLastErrorAsString(void) -{ - // https://stackoverflow.com/questions/1387064/how-to-get-the-error-message-from-the-error-code-returned-by-getlasterror - - DWORD errorMessageId = GetLastError(); - assert(errorMessageId != 0); - - LPSTR messageBuffer = NULL; - - DWORD size = - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // DWORD dwFlags, - NULL, // LPCVOID lpSource, - errorMessageId, // DWORD dwMessageId, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // DWORD dwLanguageId, - (LPSTR) &messageBuffer, // LPTSTR lpBuffer, - 0, // DWORD nSize, - NULL // va_list *Arguments - ); - - return messageBuffer; -} - -// minirent.h IMPLEMENTATION BEGIN //////////////////////////////////////// -struct DIR { - HANDLE hFind; - WIN32_FIND_DATA data; - struct dirent *dirent; -}; - -DIR *opendir(const char *dirpath) -{ - assert(dirpath); - - char buffer[MAX_PATH]; - snprintf(buffer, MAX_PATH, "%s\\*", dirpath); - - DIR *dir = (DIR*)calloc(1, sizeof(DIR)); - - dir->hFind = FindFirstFile(buffer, &dir->data); - if (dir->hFind == INVALID_HANDLE_VALUE) { - errno = ENOSYS; - goto fail; - } - - return dir; - -fail: - if (dir) { - free(dir); - } - - return NULL; -} - -struct dirent *readdir(DIR *dirp) -{ - assert(dirp); - - if (dirp->dirent == NULL) { - dirp->dirent = (struct dirent*)calloc(1, sizeof(struct dirent)); - } else { - if(!FindNextFile(dirp->hFind, &dirp->data)) { - if (GetLastError() != ERROR_NO_MORE_FILES) { - errno = ENOSYS; - } - - return NULL; - } - } - - memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name)); - - strncpy( - dirp->dirent->d_name, - dirp->data.cFileName, - sizeof(dirp->dirent->d_name) - 1); - - return dirp->dirent; -} - -int closedir(DIR *dirp) -{ - assert(dirp); - - if(!FindClose(dirp->hFind)) { - errno = ENOSYS; - return -1; - } - - if (dirp->dirent) { - free(dirp->dirent); - } - free(dirp); - - return 0; -} -// minirent.h IMPLEMENTATION END //////////////////////////////////////// -#endif // _WIN32 - -Cstr_Array cstr_array_append(Cstr_Array cstrs, Cstr cstr) -{ - Cstr_Array result = { - .count = cstrs.count + 1 - }; - result.elems = malloc(sizeof(result.elems[0]) * result.count); - memcpy(result.elems, cstrs.elems, cstrs.count * sizeof(result.elems[0])); - result.elems[cstrs.count] = cstr; - return result; -} - -int cstr_ends_with(Cstr cstr, Cstr postfix) -{ - const size_t cstr_len = strlen(cstr); - const size_t postfix_len = strlen(postfix); - return postfix_len <= cstr_len - && strcmp(cstr + cstr_len - postfix_len, postfix) == 0; -} - -Cstr cstr_no_ext(Cstr path) -{ - size_t n = strlen(path); - while (n > 0 && path[n - 1] != '.') { - n -= 1; - } - - if (n > 0) { - char *result = malloc(n); - memcpy(result, path, n); - result[n - 1] = '\0'; - - return result; - } else { - return path; - } -} - -Cstr_Array cstr_array_make(Cstr first, ...) -{ - Cstr_Array result = {0}; - - if (first == NULL) { - return result; - } - - result.count += 1; - - va_list args; - va_start(args, first); - for (Cstr next = va_arg(args, Cstr); - next != NULL; - next = va_arg(args, Cstr)) { - result.count += 1; - } - va_end(args); - - result.elems = malloc(sizeof(result.elems[0]) * result.count); - if (result.elems == NULL) { - PANIC("could not allocate memory: %s", strerror(errno)); - } - result.count = 0; - - result.elems[result.count++] = first; - - va_start(args, first); - for (Cstr next = va_arg(args, Cstr); - next != NULL; - next = va_arg(args, Cstr)) { - result.elems[result.count++] = next; - } - va_end(args); - - return result; -} - -Cstr cstr_array_join(Cstr sep, Cstr_Array cstrs) -{ - if (cstrs.count == 0) { - return ""; - } - - const size_t sep_len = strlen(sep); - size_t len = 0; - for (size_t i = 0; i < cstrs.count; ++i) { - len += strlen(cstrs.elems[i]); - } - - const size_t result_len = (cstrs.count - 1) * sep_len + len + 1; - char *result = malloc(sizeof(char) * result_len); - if (result == NULL) { - PANIC("could not allocate memory: %s", strerror(errno)); - } - - len = 0; - for (size_t i = 0; i < cstrs.count; ++i) { - if (i > 0) { - memcpy(result + len, sep, sep_len); - len += sep_len; - } - - size_t elem_len = strlen(cstrs.elems[i]); - memcpy(result + len, cstrs.elems[i], elem_len); - len += elem_len; - } - result[len] = '\0'; - - return result; -} - -Pipe pipe_make(void) -{ - Pipe pip = {0}; - -#ifdef _WIN32 - // https://docs.microsoft.com/en-us/windows/win32/ProcThread/creating-a-child-process-with-redirected-input-and-output - - SECURITY_ATTRIBUTES saAttr = {0}; - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - - if (!CreatePipe(&pip.read, &pip.write, &saAttr, 0)) { - PANIC("Could not create pipe: %s", GetLastErrorAsString()); - } -#else - Fd pipefd[2]; - if (pipe(pipefd) < 0) { - PANIC("Could not create pipe: %s", strerror(errno)); - } - - pip.read = pipefd[0]; - pip.write = pipefd[1]; -#endif // _WIN32 - - return pip; -} - -Fd fd_open_for_read(Cstr path) -{ -#ifndef _WIN32 - Fd result = open(path, O_RDONLY); - if (result < 0) { - PANIC("Could not open file %s: %s", path, strerror(errno)); - } - return result; -#else - // https://docs.microsoft.com/en-us/windows/win32/fileio/opening-a-file-for-reading-or-writing - SECURITY_ATTRIBUTES saAttr = {0}; - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - - Fd result = CreateFile( - path, - GENERIC_READ, - 0, - &saAttr, - OPEN_EXISTING, - FILE_ATTRIBUTE_READONLY, - NULL); - - if (result == INVALID_HANDLE_VALUE) { - PANIC("Could not open file %s", path); - } - - return result; -#endif // _WIN32 -} - -Fd fd_open_for_write(Cstr path) -{ -#ifndef _WIN32 - Fd result = open(path, - O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (result < 0) { - PANIC("could not open file %s: %s", path, strerror(errno)); - } - return result; -#else - SECURITY_ATTRIBUTES saAttr = {0}; - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - - Fd result = CreateFile( - path, // name of the write - GENERIC_WRITE, // open for writing - 0, // do not share - &saAttr, // default security - CREATE_NEW, // create new file only - FILE_ATTRIBUTE_NORMAL, // normal file - NULL // no attr. template - ); - - if (result == INVALID_HANDLE_VALUE) { - PANIC("Could not open file %s: %s", path, GetLastErrorAsString()); - } - - return result; -#endif // _WIN32 -} - -void fd_close(Fd fd) -{ -#ifdef _WIN32 - CloseHandle(fd); -#else - close(fd); -#endif // _WIN32 -} - -void pid_wait(Pid pid) -{ -#ifdef _WIN32 - DWORD result = WaitForSingleObject( - pid, // HANDLE hHandle, - INFINITE // DWORD dwMilliseconds - ); - - if (result == WAIT_FAILED) { - PANIC("could not wait on child process: %s", GetLastErrorAsString()); - } - - DWORD exit_status; - if (GetExitCodeProcess(pid, &exit_status) == 0) { - PANIC("could not get process exit code: %lu", GetLastError()); - } - - if (exit_status != 0) { - PANIC("command exited with exit code %lu", exit_status); - } - - CloseHandle(pid); -#else - for (;;) { - int wstatus = 0; - if (waitpid(pid, &wstatus, 0) < 0) { - PANIC("could not wait on command (pid %d): %s", pid, strerror(errno)); - } - - if (WIFEXITED(wstatus)) { - int exit_status = WEXITSTATUS(wstatus); - if (exit_status != 0) { - PANIC("command exited with exit code %d", exit_status); - } - - break; - } - - if (WIFSIGNALED(wstatus)) { - PANIC("command process was terminated by %s", strsignal(WTERMSIG(wstatus))); - } - } - -#endif // _WIN32 -} - -Cstr cmd_show(Cmd cmd) -{ - // TODO(#31): cmd_show does not render the command line properly - // - No string literals when arguments contains space - // - No escaping of special characters - // - Etc. - return cstr_array_join(" ", cmd.line); -} - -Pid cmd_run_async(Cmd cmd, Fd *fdin, Fd *fdout) -{ -#ifdef _WIN32 - // https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output - - STARTUPINFO siStartInfo; - ZeroMemory(&siStartInfo, sizeof(siStartInfo)); - siStartInfo.cb = sizeof(STARTUPINFO); - // NOTE: theoretically setting NULL to std handles should not be a problem - // https://docs.microsoft.com/en-us/windows/console/getstdhandle?redirectedfrom=MSDN#attachdetach-behavior - siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); - // TODO(#32): check for errors in GetStdHandle - siStartInfo.hStdOutput = fdout ? *fdout : GetStdHandle(STD_OUTPUT_HANDLE); - siStartInfo.hStdInput = fdin ? *fdin : GetStdHandle(STD_INPUT_HANDLE); - siStartInfo.dwFlags |= STARTF_USESTDHANDLES; - - PROCESS_INFORMATION piProcInfo; - ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); - - BOOL bSuccess = - CreateProcess( - NULL, - // TODO(#33): cmd_run_async on Windows does not render command line properly - // It may require wrapping some arguments with double-quotes if they contains spaces, etc. - cstr_array_join(" ", cmd.line), - NULL, - NULL, - TRUE, - 0, - NULL, - NULL, - &siStartInfo, - &piProcInfo - ); - - if (!bSuccess) { - PANIC("Could not create child process %s: %s\n", - cmd_show(cmd), GetLastErrorAsString()); - } - - CloseHandle(piProcInfo.hThread); - - return piProcInfo.hProcess; -#else - pid_t cpid = fork(); - if (cpid < 0) { - PANIC("Could not fork child process: %s: %s", - cmd_show(cmd), strerror(errno)); - } - - if (cpid == 0) { - Cstr_Array args = cstr_array_append(cmd.line, NULL); - - if (fdin) { - if (dup2(*fdin, STDIN_FILENO) < 0) { - PANIC("Could not setup stdin for child process: %s", strerror(errno)); - } - } - - if (fdout) { - if (dup2(*fdout, STDOUT_FILENO) < 0) { - PANIC("Could not setup stdout for child process: %s", strerror(errno)); - } - } - - if (execvp(args.elems[0], (char * const*) args.elems) < 0) { - PANIC("Could not exec child process: %s: %s", - cmd_show(cmd), strerror(errno)); - } - } - - return cpid; -#endif // _WIN32 -} - -void cmd_run_sync(Cmd cmd) -{ - pid_wait(cmd_run_async(cmd, NULL, NULL)); -} - -static void chain_set_input_output_files_or_count_cmds(Chain *chain, Chain_Token token) -{ - switch (token.type) { - case CHAIN_TOKEN_CMD: { - chain->cmds.count += 1; - } - break; - - case CHAIN_TOKEN_IN: { - if (chain->input_filepath) { - PANIC("Input file path was already set"); - } - - chain->input_filepath = token.args.elems[0]; - } - break; - - case CHAIN_TOKEN_OUT: { - if (chain->output_filepath) { - PANIC("Output file path was already set"); - } - - chain->output_filepath = token.args.elems[0]; - } - break; - - case CHAIN_TOKEN_END: - default: { - assert(0 && "unreachable"); - exit(1); - } - } -} - -static void chain_push_cmd(Chain *chain, Chain_Token token) -{ - if (token.type == CHAIN_TOKEN_CMD) { - chain->cmds.elems[chain->cmds.count++] = (Cmd) { - .line = token.args - }; - } -} - -Chain chain_build_from_tokens(Chain_Token first, ...) -{ - Chain result = {0}; - - chain_set_input_output_files_or_count_cmds(&result, first); - va_list args; - va_start(args, first); - Chain_Token next = va_arg(args, Chain_Token); - while (next.type != CHAIN_TOKEN_END) { - chain_set_input_output_files_or_count_cmds(&result, next); - next = va_arg(args, Chain_Token); - } - va_end(args); - - result.cmds.elems = malloc(sizeof(result.cmds.elems[0]) * result.cmds.count); - if (result.cmds.elems == NULL) { - PANIC("could not allocate memory: %s", strerror(errno)); - } - result.cmds.count = 0; - - chain_push_cmd(&result, first); - - va_start(args, first); - next = va_arg(args, Chain_Token); - while (next.type != CHAIN_TOKEN_END) { - chain_push_cmd(&result, next); - next = va_arg(args, Chain_Token); - } - va_end(args); - - return result; -} - -void chain_run_sync(Chain chain) -{ - if (chain.cmds.count == 0) { - return; - } - - Pid *cpids = malloc(sizeof(Pid) * chain.cmds.count); - - Pipe pip = {0}; - Fd fdin = 0; - Fd *fdprev = NULL; - - if (chain.input_filepath) { - fdin = fd_open_for_read(chain.input_filepath); - if (fdin < 0) { - PANIC("could not open file %s: %s", chain.input_filepath, strerror(errno)); - } - fdprev = &fdin; - } - - for (size_t i = 0; i < chain.cmds.count - 1; ++i) { - pip = pipe_make(); - - cpids[i] = cmd_run_async( - chain.cmds.elems[i], - fdprev, - &pip.write); - - if (fdprev) fd_close(*fdprev); - fd_close(pip.write); - fdprev = &fdin; - fdin = pip.read; - } - - { - Fd fdout = 0; - Fd *fdnext = NULL; - - if (chain.output_filepath) { - fdout = fd_open_for_write(chain.output_filepath); - if (fdout < 0) { - PANIC("could not open file %s: %s", - chain.output_filepath, - strerror(errno)); - } - fdnext = &fdout; - } - - const size_t last = chain.cmds.count - 1; - cpids[last] = - cmd_run_async( - chain.cmds.elems[last], - fdprev, - fdnext); - - if (fdprev) fd_close(*fdprev); - if (fdnext) fd_close(*fdnext); - } - - for (size_t i = 0; i < chain.cmds.count; ++i) { - pid_wait(cpids[i]); - } -} - -void chain_echo(Chain chain) -{ - printf("[INFO] CHAIN:"); - if (chain.input_filepath) { - printf(" %s", chain.input_filepath); - } - - FOREACH_ARRAY(Cmd, cmd, chain.cmds, { - printf(" |> %s", cmd_show(*cmd)); - }); - - if (chain.output_filepath) { - printf(" |> %s", chain.output_filepath); - } - - printf("\n"); -} - -int path_exists(Cstr path) -{ -#ifdef _WIN32 - DWORD dwAttrib = GetFileAttributes(path); - return (dwAttrib != INVALID_FILE_ATTRIBUTES); -#else - struct stat statbuf = {0}; - if (stat(path, &statbuf) < 0) { - if (errno == ENOENT) { - errno = 0; - return 0; - } - - PANIC("could not retrieve information about file %s: %s", - path, strerror(errno)); - } - - return 1; -#endif -} - -int path_is_dir(Cstr path) -{ -#ifdef _WIN32 - DWORD dwAttrib = GetFileAttributes(path); - - return (dwAttrib != INVALID_FILE_ATTRIBUTES && - (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); -#else - struct stat statbuf = {0}; - if (stat(path, &statbuf) < 0) { - if (errno == ENOENT) { - errno = 0; - return 0; - } - - PANIC("could not retrieve information about file %s: %s", - path, strerror(errno)); - } - - return S_ISDIR(statbuf.st_mode); -#endif // _WIN32 -} - -void path_rename(const char *old_path, const char *new_path) -{ -#ifdef _WIN32 - if (!MoveFileEx(old_path, new_path, MOVEFILE_REPLACE_EXISTING)) { - PANIC("could not rename %s to %s: %s", old_path, new_path, - GetLastErrorAsString()); - } -#else - if (rename(old_path, new_path) < 0) { - PANIC("could not rename %s to %s: %s", old_path, new_path, - strerror(errno)); - } -#endif // _WIN32 -} - -void path_mkdirs(Cstr_Array path) -{ - if (path.count == 0) { - return; - } - - size_t len = 0; - for (size_t i = 0; i < path.count; ++i) { - len += strlen(path.elems[i]); - } - - size_t seps_count = path.count - 1; - const size_t sep_len = strlen(PATH_SEP); - - char *result = malloc(len + seps_count * sep_len + 1); - - len = 0; - for (size_t i = 0; i < path.count; ++i) { - size_t n = strlen(path.elems[i]); - memcpy(result + len, path.elems[i], n); - len += n; - - if (seps_count > 0) { - memcpy(result + len, PATH_SEP, sep_len); - len += sep_len; - seps_count -= 1; - } - - result[len] = '\0'; - - if (mkdir(result, 0755) < 0) { - if (errno == EEXIST) { - errno = 0; - WARN("directory %s already exists", result); - } else { - PANIC("could not create directory %s: %s", result, strerror(errno)); - } - } - } -} - -void path_rm(Cstr path) -{ - if (IS_DIR(path)) { - FOREACH_FILE_IN_DIR(file, path, { - if (strcmp(file, ".") != 0 && strcmp(file, "..") != 0) - { - path_rm(PATH(path, file)); - } - }); - - if (rmdir(path) < 0) { - if (errno == ENOENT) { - errno = 0; - WARN("directory %s does not exist", path); - } else { - PANIC("could not remove directory %s: %s", path, strerror(errno)); - } - } - } else { - if (unlink(path) < 0) { - if (errno == ENOENT) { - errno = 0; - WARN("file %s does not exist", path); - } else { - PANIC("could not remove file %s: %s", path, strerror(errno)); - } - } - } -} - -int is_path1_modified_after_path2(const char *path1, const char *path2) -{ -#ifdef _WIN32 - FILETIME path1_time, path2_time; - - Fd path1_fd = fd_open_for_read(path1); - if (!GetFileTime(path1_fd, NULL, NULL, &path1_time)) { - PANIC("could not get time of %s: %s", path1, GetLastErrorAsString()); - } - fd_close(path1_fd); - - Fd path2_fd = fd_open_for_read(path2); - if (!GetFileTime(path2_fd, NULL, NULL, &path2_time)) { - PANIC("could not get time of %s: %s", path2, GetLastErrorAsString()); - } - fd_close(path2_fd); - - return CompareFileTime(&path1_time, &path2_time) == 1; -#else - struct stat statbuf = {0}; - - if (stat(path1, &statbuf) < 0) { - PANIC("could not stat %s: %s\n", path1, strerror(errno)); - } - int path1_time = statbuf.st_mtime; - - if (stat(path2, &statbuf) < 0) { - PANIC("could not stat %s: %s\n", path2, strerror(errno)); - } - int path2_time = statbuf.st_mtime; - - return path1_time > path2_time; -#endif -} - -void VLOG(FILE *stream, Cstr tag, Cstr fmt, va_list args) -{ - fprintf(stream, "[%s] ", tag); - vfprintf(stream, fmt, args); - fprintf(stream, "\n"); -} - -void INFO(Cstr fmt, ...) -{ - va_list args; - va_start(args, fmt); - VLOG(stderr, "INFO", fmt, args); - va_end(args); -} - -void WARN(Cstr fmt, ...) -{ - va_list args; - va_start(args, fmt); - VLOG(stderr, "WARN", fmt, args); - va_end(args); -} - -void ERRO(Cstr fmt, ...) -{ - va_list args; - va_start(args, fmt); - VLOG(stderr, "ERRO", fmt, args); - va_end(args); -} - -void PANIC(Cstr fmt, ...) -{ - va_list args; - va_start(args, fmt); - VLOG(stderr, "ERRO", fmt, args); - va_end(args); - exit(1); -} - -char *shift_args(int *argc, char ***argv) -{ - assert(*argc > 0); - char *result = **argv; - *argc -= 1; - *argv += 1; - return result; -} - -#endif // NOBUILD_IMPLEMENTATION diff --git a/implementations/C/src/remcc.c b/implementations/C/src/remcc.c @@ -1,477 +0,0 @@ -#include <assert.h> -#include <ctype.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "remcc.h" - - -/* Function prototypes */ -int usage(char *arg0); -void write_bytecode(FILE *stream, codevec_t bytecode); - -/* Implementation: */ -int main(int argc, char **argv) { - char *input_fname = NULL, *output_fname = NULL; - FILE *input_f = NULL, *output_f = NULL; - tokenvec_t prog_tokens; - instvec_t prog_insts; - codevec_t prog_bytecode; - - if (argc <= 2) { - return usage(argv[0]); - } else if (argc == 3) { - // output and input specified - input_fname = argv[1]; - output_fname = argv[2]; - } else { - return usage(argv[0]); - } - - const char *open_err = "Unable to open file"; - if ((input_f = fopen(input_fname, "r")) == NULL) { - perror(open_err); - return 1; - } - if ((output_f = fopen(output_fname, "w")) == NULL) { - perror(open_err); - return 1; - } - - prog_tokens = lexer(input_f); - prog_insts = parse(prog_tokens); - prog_bytecode = byte_compile_prog(prog_insts); - - write_bytecode(output_f, prog_bytecode); - - return 0; -} - -int usage(char *arg0) { - fprintf(stderr, "Usage: %s input.rasm output.rin\n", arg0); - return 1; -} - -tokenvec_t lexer(FILE *stream) { - assert(NULL == "lexer not yet implemented"); - - char buf[MAX_TOK] = {0}; - size_t i, j = 0, tok_arr_size = 100; - token_t *tokens = calloc(tok_arr_size, sizeof(token_t)); - - while (!feof(stream)) { - for (i = 0; i < MAX_TOK && (buf[i] = getc(stream)) != ' '; i++); - buf[i] = '\0'; - - tokens[j] = lex(buf); - - j++; - if (j >= tok_arr_size) { - tok_arr_size *= 2; - tokens = realloc(tokens, tok_arr_size); - } - } - - return (tokenvec_t) { - .tokens = NULL, - .num = 0 - }; -} - - -instvec_t parse(tokenvec_t tokens) { - assert(NULL == "parse not yet implemented"); - - return (instvec_t) { - .insts = NULL, - .num = 0 - }; -} - -uint64_t byte_compile_inst(inst_t instruction) { - assert(NULL == "byte_compile_inst not yet implemented"); - // Opcodes are the first 5 bits of an instruction - uint8_t opcode = (uint8_t) instruction.opcode; - // The last three bits should be zero, else our opcode is too big! - assert(opcode >> 5 == 0); - - uint8_t cond = (uint8_t) instruction.cond; - // The last 5 bits of the conditional should be zero - assert(cond >> 3 == 0); - - uint8_t first_byte = (opcode << 3) + cond; - - // The dest is a register with address up to 22 bits - // Plus 2 bits for the register type - uint32_t dest = instruction.dest.num; - // The last 10 bits of this should be zero - assert(dest >> 22 == 0); - - uint8_t reg_type = (uint8_t) instruction.dest.type; - // the last 6 bits should be 0, we only want a 2 bit register identifier - assert(reg_type >> 2 == 0); - - uint32_t next_24_bits = (reg_type << 23) + dest; - - // What comes next depends on the type of instruction that we're dealing with, - // some instructions only take 2 other operand, some take 3. (including the dest) - // TODO: Implement inst_num_operands - uint32_t next_32_bits; - switch (inst_num_operands(instruction.opcode)) { - case 0: - case 1: - next_32_bits = 0; - break; - case 2: { - // The temp is some 31 bit number - // Plus 1 bit to differentiate between an immediate vs a register - // Where 2 bits are dedicated for the register type if it's a register - oper_t temp = instruction.temp_1; - - if (temp.type == IMM) { - uint32_t imm = temp.val.imm; - assert(imm >> 31 == 0); // final bit must not be used - next_32_bits = (1 << 31) + imm; - } else if (temp.type == REG) { - uint32_t reg_num = temp.val.reg.num; - uint8_t reg_type = temp.val.reg.type; - - assert(reg_num >> 29 == 0); // final 3 bits must not be used in the register number - assert(reg_type >> 2 == 0); // only the first 2 bits can be used of the reg type - next_32_bits = (0 << 31) + (reg_type << 30) + reg_num; - } else { - // We should never be here - assert(NULL == "We should never be in a situation where the temporary is neither an immediate, nor a register."); - next_32_bits = 0; - } - break; - } - case 3: - assert(NULL == "Instructions with 3 operands not yet implemented"); - // temp_1 is some 15 bit register addr or 15 bit immediate - // Plus 1 bit allocated to differentiate an immediate vs a register - oper_t temp_1 = instruction.temp_1; - - - // temp_2 is some 15 bit register addr or 15 bit immediate - // plus 1 bit allocated to differentiate an immediate vs register - break; - default: - // We should never be here - assert(NULL == "Invalid number of operands associated with an instruction"); - break; - } - - - // Different instructions will have different operand sizes - return 0; -} - -int inst_num_operands(opcode_t op) { - int ret = 0; - switch (op) { - // 0 operand instructions - case NOP: - case RETURN: - ret = 0; - break; - // 1 operand instructions - case PUSH: - case POP: - case PEEK: - case JUMP: - case CALL: - ret = 1; - break; - // 2 operand instructions - case NOT: - case MOVE: - case SWAP: - case LOAD: - case STORE: - ret = 2; - break; - // 3 operand instructions - case ADD: - case SUB: - case MUL: - case DIV: - case AND: - case OR: - case XOR: - case SHIFTL: - case SHIFTR_L: - case SHIFTR_A: - ret = 3; - break; - default: - ret = -1; - } - return ret; -} - -codevec_t byte_compile_prog(instvec_t instructions) { - uint64_t *program = calloc(instructions.num, sizeof(uint64_t)); - for (size_t i = 0; i < instructions.num; i++) { - program[i] = byte_compile_inst(instructions.insts[i]); - } - return (codevec_t) { - .data = program, - .num = instructions.num - }; -} - -void write_bytecode(FILE *stream, codevec_t bytecode) { - assert(NULL == "write_bytecode not yet implemented"); - return; -} - -inline token_t opcode_token(opcode_t opcode) { - return (token_t) { - .type = OPCODE, - .val.opcode = opcode - }; -} - -inline token_t cond_token(conditional_t cond) { - return (token_t) { - .type = COND, - .val.cond = cond - }; -} - -inline token_t oper_token(oper_t oper) { - return (token_t) { - .type = OPER, - .val.operand = oper - }; -} - -inline token_t error_token(error_t error) { - return (token_t) { - .type = ERR, - .val.error = error - }; -} - -inline oper_t reg_operand(REG_TYPE type, uint64_t num) { - return (oper_t) { - .type = REG, - .val.reg = { - .type = type, - .num = num - } - }; -} - -inline oper_t imm_operand(uint64_t immediate) { - return (oper_t) { - .type = IMM, - .val.imm = immediate - }; -} - -// My homemade lexer, it's a bit filthy but it'll do for now -token_t lex(char *tok_str) { - // TODO: Operands - // TODO: Case-Insensitive - token_t err = error_token(LEX_ERROR); - - switch (tok_str[0]) { - case 'a': - if (isdigit(tok_str[1])) { - unsigned long n = strtoul(tok_str + 1, NULL, 10); - if (errno) { - break; - } - return oper_token(reg_operand(ARG, n)); - } else if (strcmp(tok_str + 1, "dd") == 0) { - return opcode_token(ADD); - } else if (strcmp(tok_str + 1, "nd") == 0) { - return opcode_token(AND); - } - break; - - case 'c': - // The only C instruction is call - if (strcmp(tok_str + 1, "all") == 0) { - return opcode_token(CALL); - } - break; - - case 'd': - // The only D instruction is div - if (strcmp(tok_str + 1, "iv") == 0) { - return opcode_token(DIV); - } - break; - - case 'e': - if (strcmp(tok_str + 1, "q") == 0) { - return cond_token(EQ); - } - break; - - case 'g': - if (strcmp(tok_str + 1, "t") == 0) { - return cond_token(GT); - } else if (strcmp(tok_str + 1, "eq") == 0) { - return cond_token(GEQ); - } - break; - - case 'j': - // The only J instruction is jump - if (strcmp(tok_str + 1, "ump") == 0) { - return opcode_token(JUMP); - } - break; - - case 'l': - // The only L instruction is load - if (strcmp(tok_str + 1, "t") == 0) { - return cond_token(LT); - } else if (strcmp(tok_str + 1, "eq") == 0) { - return cond_token(LEQ); - } else if (strcmp(tok_str + 1, "oad") == 0) { - return opcode_token(LOAD); - } - break; - - case 'm': - if (strcmp(tok_str + 1, "ul") == 0) { - return opcode_token(MUL); - } else if (strcmp(tok_str + 1, "ove") == 0) { - return opcode_token(MOVE); - } - break; - - case 'n': - if (strcmp(tok_str + 1, "op") == 0) { - return opcode_token(NOP); - } else if (strcmp(tok_str + 1, "ot") == 0) { - return opcode_token(NOT); - } else if (strcmp(tok_str + 1, "eq") == 0) { - return cond_token(NEQ); - } else if (strcmp(tok_str + 1, "eg") == 0) { - return cond_token(NEG); - } - break; - - case 'o': - if (strcmp(tok_str + 1, "r") == 0) { - return opcode_token(OR); - } - break; - - case 'p': - switch (tok_str[1]) { - case 'u': - if (strcmp(tok_str + 2, "sh") == 0) { - return opcode_token(PUSH); - } - break; - - case 'o': - if (strcmp(tok_str + 2, "p") == 0) { - return opcode_token(POP); - } else if (strcmp(tok_str + 2, "s") == 0) { - return cond_token(POS); - } - break; - - case 'e': - if (strcmp(tok_str + 2, "ek") == 0) { - return opcode_token(PEEK); - } - break; - - default: - break; - } - break; - - case 'r': - if (strcmp(tok_str + 1, "eturn") == 0) { - return opcode_token(RETURN); - } else if (isdigit(tok_str[1])) { - unsigned long n = strtoul(tok_str + 1, NULL, 10); - if (errno) { - break; - } - return oper_token(reg_operand(RET, n)); - } - break; - - case 's': - // FIXME: Filthy - if (strcmp(tok_str + 1, "ub") == 0) { - return (token_t) { - .type = OPCODE, - .val.opcode = SUB - }; - } else if (strcmp(tok_str + 1, "wap") == 0) { - return (token_t) { - .type = OPCODE, - .val.opcode = SWAP - }; - } else if (strcmp(tok_str + 1, "tore") == 0) { - return (token_t) { - .type = OPCODE, - .val.opcode = STORE - }; - } else if (strcmp(tok_str + 1, "hiftl") == 0) { - return (token_t) { - .type = OPCODE, - .val.opcode = SHIFTL - }; - } else if (strcmp(tok_str + 1, "hiftr(l)") == 0) { - return (token_t) { - .type = OPCODE, - .val.opcode = SHIFTR_L - }; - } else if (strcmp(tok_str + 1, "hiftr(a)") == 0) { - return (token_t) { - .type = OPCODE, - .val.opcode = SHIFTR_A - }; - } - break; - - case 't': - if (isdigit(tok_str[1])) { - unsigned long n = strtoul(tok_str + 1, NULL, 10); - if (errno) { - break; - } - return (token_t) { - .type = OPER, - .val.operand = { - .type = REG, - .val.reg = { - .type = TEMP, - .num = n - } - } - }; - } - break; - - case 'x': - if (strcmp(tok_str + 1, "or") == 0) { - return (token_t) { - .type = OPCODE, - .val.opcode = XOR - }; - } - break; - - default: - break; - } - - fprintf(stderr, "Unknown token: %s\n", tok_str); - return err; -} diff --git a/implementations/C/src/remcc.h b/implementations/C/src/remcc.h @@ -1,131 +0,0 @@ -#include <stdint.h> - -/* Data types */ -typedef enum { - NOP = 0, - // Arithmetic - ADD, - SUB, - MUL, - DIV, - // Logical & bit - AND, - OR, - XOR, - NOT, - SHIFTL, - SHIFTR_L, - SHIFTR_A, - // Memory & registers - MOVE, - SWAP, - PUSH, - POP, - PEEK, - LOAD, - STORE, - // Control flow - JUMP, - CALL, - RETURN -} opcode_t; - -typedef enum { - GT = 0, - GEQ, - LT, - LEQ, - EQ, - NEQ, - POS, - NEG -} conditional_t; - -typedef enum { - ARG, - TEMP, - RET -} REG_TYPE; - -typedef struct { - REG_TYPE type; - uint32_t num; -} reg_t; - -typedef enum { - REG, - IMM -} OPER_TYPE; - -typedef struct { - OPER_TYPE type; - union { - reg_t reg; - uint32_t imm; - } val; -} oper_t; - -typedef struct { - opcode_t opcode; - conditional_t cond; - reg_t dest; - oper_t temp_1; - oper_t temp_2; -} inst_t; - -// The maximum number of characters representing a token -#define MAX_TOK 256 - -typedef enum { - OPCODE, - COND, - OPER, - ERR -} TOKEN_TYPE; - -typedef enum { - LEX_ERROR -} error_t; - -typedef struct { - TOKEN_TYPE type; - union { - opcode_t opcode; - conditional_t cond; - oper_t operand; - error_t error; - } val; -} token_t; - -typedef struct { - token_t *tokens; - size_t num; -} tokenvec_t; - -typedef struct { - inst_t *insts; - size_t num; -} instvec_t; - -typedef struct { - uint64_t *data; - size_t num; -} codevec_t; - -/* Function definitions */ -token_t opcode_token(opcode_t opcode); -token_t cond_token(conditional_t cond); -token_t oper_token(oper_t oper); -token_t error_token(error_t error); - -oper_t reg_operand(REG_TYPE type, uint64_t num); -oper_t imm_operand(uint64_t immediate); - -tokenvec_t lexer(FILE *stream); -token_t lex(char *tok); -instvec_t parse(tokenvec_t tokens); -codevec_t byte_compile_prog(instvec_t instructions); -uint64_t byte_compile_inst(inst_t instruction); - -int inst_num_operands(opcode_t op); - diff --git a/implementations/C/src/remvm_interp.c b/implementations/C/src/remvm_interp.c @@ -1,17 +0,0 @@ -#include <stdio.h> -#include <stdint.h> - -int usage(char *arg0); - -int main(int argc, char **argv) { - if (argc < 2) { - return usage(argv[0]); - } - - return 0; -} - -int usage(char *arg0) { - fprintf(stderr, "USAGE: %s <FILE>\n", arg0); - return 1; -} diff --git a/implementations/C/tests/remcc_tests.c b/implementations/C/tests/remcc_tests.c @@ -1,106 +0,0 @@ -// Test if the assembler lexer is functioning as expected -#import "../src/remcc.h" - -typedef enum { - PASS, - FAIL -} RESULT; - -typedef struct { - RESULT state; - union { - char *result; - char *error; - } val; -} result_t; - -result_t test_lex(char *tok, token_t expect); -result_t test_lexer(FILE *stream, token_t *expect); - -int main(int argc, char **argv) { - result_t result; - - char *lex_tests[] = { - // Instructions tests - "nop", - "add", - "sub", - "mul", - "div", - "and", - "or", - "xor", - "not", - "shiftl", - "shiftr(l)", - "shiftr(a)", - "move", - "swap", - "push", - "pop", - "peek", - "load", - "store", - "jump", - "call", - "return", - NULL - }; - - opcode_t lex_expects[] = { - NOP, - ADD, - SUB, - MUL, - DIV, - AND, - OR, - XOR, - NOT, - SHIFTL, - SHIFTR_L, - SHIFTR_A, - MOVE, - SWAP, - PUSH, - POP, - PEEK, - LOAD, - STORE, - JUMP, - CALL, - RETURN - }; - - //FILE *stream_tests[] = {0}; - - for (int i = 0; lex_tests[i] != NULL; i++) { - switch ((result = test_lex(lex_tests[i], lex_expects[i])).state) { - case PASS: - printf("We have a success!\n"); - printf("Result: %s\n", result.val.result); - break; - case FAIL: - fprintf(stderr, "Dumbledore dies\n"); - fprintf(stderr, "Error: %s\n", result.val.error); - break; - } - } - //test_lexer(); - return 0; -} - -result_t test_lex(char *tok, token_t expect) { - token_t res = lex(tok); - if (res.type == OPCODE && res.val.opcode == expect) { - return (result_t) { - .state = PASS, - .val.result = "Success!" - }; - } else { - return (result_t) { - .state = FAIL, - .val.error = "Didn't get what we expected!" - }; - } -} diff --git a/implementations/rust/src/lib/parse.rs b/src/lib/parse.rs diff --git a/implementations/rust/src/lib/remedy.rs b/src/lib/remedy.rs diff --git a/implementations/rust/src/main.rs b/src/main.rs