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:
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