]> git.lizzy.rs Git - uwu-lang.git/commitdiff
Initial commit
authorElias Fleckenstein <eliasfleckenstein@web.de>
Thu, 30 Dec 2021 13:18:15 +0000 (14:18 +0100)
committerElias Fleckenstein <eliasfleckenstein@web.de>
Thu, 30 Dec 2021 13:18:15 +0000 (14:18 +0100)
32 files changed:
Makefile [new file with mode: 0644]
README.md [new file with mode: 0644]
api/api.so [new file with mode: 0755]
api/bool.c [new file with mode: 0644]
api/bool.h [new file with mode: 0644]
api/int.c [new file with mode: 0644]
api/int.h [new file with mode: 0644]
api/nil.c [new file with mode: 0644]
api/nil.h [new file with mode: 0644]
api/str.c [new file with mode: 0644]
api/str.h [new file with mode: 0644]
api/vm.c [new file with mode: 0644]
api/vm.h [new file with mode: 0644]
example/fibo.uwu [new file with mode: 0644]
example/test.uwu [new file with mode: 0644]
src/collect.c [new file with mode: 0644]
src/collect.h [new file with mode: 0644]
src/err.h [new file with mode: 0644]
src/expression.h [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/parse.c [new file with mode: 0644]
src/parse.h [new file with mode: 0644]
src/util.h [new file with mode: 0644]
src/vm.c [new file with mode: 0644]
src/vm.h [new file with mode: 0644]
std/bool.c [new file with mode: 0644]
std/bool.so [new file with mode: 0755]
std/int.c [new file with mode: 0644]
std/int.so [new file with mode: 0755]
std/str.c [new file with mode: 0644]
std/str.so [new file with mode: 0755]
uwu [new file with mode: 0755]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..f218bdb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+all: uwu std api
+
+uwu: src/*.c src/*.h
+       gcc -g src/*.c -o uwu -ldl -D_GNU_SOURCE
+
+.PHONY: std api
+
+std: std/bool.so std/int.so std/str.so
+api: api/api.so
+
+std/%.so: std/%.c
+       gcc -g -shared -fpic $< -o $@ -D_GNU_SOURCE
+
+api/api.so: api/*.c api/*.h
+       gcc -g -shared -fpic api/*.c -o api/api.so -D_GNU_SOURCE
+
+.PHONY: clean
+
+clean:
+       rm -rf std/*.so api/api.so uwu
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..7300043
--- /dev/null
+++ b/README.md
@@ -0,0 +1,76 @@
+# uwulang
+UWU (**U**ltimate p**W**ogwamming lang**U**age) is a **functional**, **interpreted**, **weakly typed** programming language written in C.
+
+## What this language is
+
+- Demonstration of dlopen - 35%
+- Demonstration of lambda - 30%
+- Fun excercise for myself - 20%
+- A meme - 10%
+- Meant for actual use - 5%
+
+It's turing complete and somewhat useable.
+
+## Invocation
+
+To build:
+
+```
+make
+```
+
+To run:
+
+```
+./uwu <module>
+```
+
+`<module>` is a path relative to the current directory.
+Several file names are tried, the first one that exists is used:
+
+- `<module>`
+- `<module>.uwu`
+- `<module>.so`
+
+If the file name ends with `.so`, it is treated as a native module.
+
+## Features
+
+Makes heavy use of a module system with relative paths.
+
+There is support for native modules written in C (or a C ABI compatible language) that are loaded at runtime. The standard library relies on this feature.
+
+Strictly follows lambda principle. Functions without arguments are constants. Functions can (by design) not interact with anything globally, the input is passed to the main function and the only output is the return value of the main function. _However_, global interaction (e.g. print() or read()) could theoretically be added by native modules and since the VM does not cache any results and always calls functions, even .
+
+Arguments are always passed by value, there are no references (except references to functions). This way, all memory allocation is stack based and no garbage collector is needed.
+
+The syntax is very minimal. Almost everything is solved via functions (no control structures, no operators). The are only integer and string literals, but complex data structures are possible with native modules and available in the standard library.
+
+Currently there are no floating point values, but they might be added in the future (One could also add them using native modules).
+
+## Syntax
+
+A module is made out of functions. Each function is made out of a name and exactly one expression, seperated by arbitrary whitespace.
+
+```
+function_name expression
+
+other_function_name
+       other_expression
+
+
+               weird_indent_function_name
+
+       weird_indent_expression
+
+```
+
+## Platform
+
+Does not aim to run on a platform other than Linux. Only tested on x86_64 so far, but it should work on other architectures as well.
+
+Uses shared object files for native modules, opens them with `dlopen`, which is POSIX.1-2001.
+
+Requires a libc that implements asprintf (asprintf is not standard, glibc and musl implement it and there are drop-in replacements)
+
+The current implementation of the VM does not take advantage of lambda (no caching of results), but such a feature might be added in the future. I might also add a compiler for LLVM or x86_64 NASM or a JIT in the future.
diff --git a/api/api.so b/api/api.so
new file mode 100755 (executable)
index 0000000..d6eb8e5
Binary files /dev/null and b/api/api.so differ
diff --git a/api/bool.c b/api/bool.c
new file mode 100644 (file)
index 0000000..2857f0b
--- /dev/null
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bool.h"
+#include "nil.h"
+
+UwUVMValue uwubool_create(bool value)
+{
+       UwUVMValue vm_value = {
+               .type = VT_NAT,
+               .value = {
+                       .nat_value = {
+                               .type = &uwubool_type,
+                               .data = malloc(sizeof(bool))
+                       },
+               },
+       };
+
+       *(bool *) vm_value.value.nat_value.data = value;
+       return vm_value;
+}
+
+bool uwubool_get(UwUVMValue vm_value)
+{
+       if (vm_value.type != VT_NAT)
+               return true;
+       else if (vm_value.value.nat_value.type == &uwunil_type)
+               return false;
+       else if (vm_value.value.nat_value.type == &uwubool_type)
+               return *(bool *) vm_value.value.nat_value.data;
+       else
+               return true;
+}
+
+static void *uwubool_copy(void *data)
+{
+       bool *copy = malloc(sizeof(*copy));
+       *copy = *(bool *) data;
+       return  copy;
+}
+
+static char *uwubool_print(void *data)
+{
+       return strdup(((bool *) data) ? "true" : "false");
+}
+
+UwUVMNativeType uwubool_type = {
+       .copy = &uwubool_copy,
+       .delete = &free,
+       .print = &uwubool_print,
+};
diff --git a/api/bool.h b/api/bool.h
new file mode 100644 (file)
index 0000000..2299177
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _API_BOOL_H_
+#define _API_BOOL_H_
+
+#include <stdbool.h>
+#include "vm.h"
+
+extern UwUVMNativeType uwubool_type;
+
+UwUVMValue uwubool_create(bool value);
+bool uwubool_get(UwUVMValue vm_value);
+
+#endif
diff --git a/api/int.c b/api/int.c
new file mode 100644 (file)
index 0000000..aea8e26
--- /dev/null
+++ b/api/int.c
@@ -0,0 +1,11 @@
+#include "int.h"
+
+UwUVMValue uwuint_create(int value)
+{
+       return (UwUVMValue) {
+               .type = VT_INT,
+               .value = {
+                       .int_value = value,     
+               },
+       };
+}
diff --git a/api/int.h b/api/int.h
new file mode 100644 (file)
index 0000000..fd284c7
--- /dev/null
+++ b/api/int.h
@@ -0,0 +1,8 @@
+#ifndef _API_INT_H_
+#define _API_INT_H_
+
+#include "vm.h"
+
+UwUVMValue uwuint_create(int value);
+
+#endif
diff --git a/api/nil.c b/api/nil.c
new file mode 100644 (file)
index 0000000..a9c54cc
--- /dev/null
+++ b/api/nil.c
@@ -0,0 +1,37 @@
+#include <string.h>
+#include "nil.h"
+
+UwUVMValue uwunil_create()
+{
+       return (UwUVMValue) {
+               .type = VT_NAT,
+               .value = {
+                       .nat_value = {
+                               .type = &uwunil_type,
+                               .data = NULL,   
+                       }
+               }       
+       };
+}
+
+static void *uwunil_copy(void *data)
+{
+       return data;
+}
+
+static void uwunil_delete(void *data)
+{
+       (void) data;
+}
+
+static char *uwunil_print(void *data)
+{
+       (void) data;
+       return strdup("");
+}
+
+UwUVMNativeType uwunil_type = {
+       .copy = &uwunil_copy,
+       .delete = &uwunil_delete,
+       .print = &uwunil_print, 
+};
diff --git a/api/nil.h b/api/nil.h
new file mode 100644 (file)
index 0000000..22df640
--- /dev/null
+++ b/api/nil.h
@@ -0,0 +1,10 @@
+#ifndef _API_NIL_H
+#define _API_NIL_H
+
+#include "vm.h"
+
+extern UwUVMNativeType uwunil_type;
+
+UwUVMValue uwunil_create();
+
+#endif
diff --git a/api/str.c b/api/str.c
new file mode 100644 (file)
index 0000000..c634fc2
--- /dev/null
+++ b/api/str.c
@@ -0,0 +1,32 @@
+#include <string.h>
+#include "../src/util.h"
+#include "str.h"
+
+UwUVMValue uwustr_create(const char *value)
+{
+       return (UwUVMValue) {
+               .type = VT_STR,
+               .value = {
+                       .str_value = strdup(value),     
+               },
+       };
+}
+
+char *uwustr_get(UwUVMValue vm_value)
+{
+       switch (vm_value.type) {
+               case VT_INT:
+                       return asprintf_wrapper("%d", vm_value.value.int_value);
+
+               case VT_STR:
+                       return strdup(vm_value.value.str_value);
+
+               case VT_REF:
+                       return asprintf_wrapper("[Function reference: %p]", vm_value.value.ref_value);
+               
+               case VT_NAT:
+                       return vm_value.value.nat_value.type->print
+                               ? vm_value.value.nat_value.type->print(vm_value.value.nat_value.data)
+                               : asprintf_wrapper("[Native value: %p: %p]", vm_value.value.nat_value.data, vm_value.value.nat_value.type);
+       }
+}
diff --git a/api/str.h b/api/str.h
new file mode 100644 (file)
index 0000000..b035f2a
--- /dev/null
+++ b/api/str.h
@@ -0,0 +1,9 @@
+#ifndef _API_STR_H_
+#define _API_STR_H_
+
+#include "vm.h"
+
+UwUVMValue uwustr_create(const char *value);
+char *uwustr_get(UwUVMValue vm_value);
+
+#endif
diff --git a/api/vm.c b/api/vm.c
new file mode 100644 (file)
index 0000000..547805c
--- /dev/null
+++ b/api/vm.c
@@ -0,0 +1,104 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "../src/err.h"
+#include "vm.h"
+#include "str.h"
+#include "int.h"
+
+void uwuvm_free_value(UwUVMValue value)
+{
+       if (value.type == VT_STR)
+               free(value.value.str_value);
+       else if (value.type == VT_NAT)
+               value.value.nat_value.type->delete(value.value.nat_value.data);
+}
+
+void uwuvm_free_args(UwUVMArgs *args)
+{
+       if (args->evaluated) {
+               for (size_t i = 0; i < args->num; i++) {
+                       UwUVMValue *value = args->evaluated[i];
+
+                       if (value) {
+                               uwuvm_free_value(*value);
+                               free(value);
+                       }
+               }
+
+               free(args->evaluated);
+       }
+}
+
+UwUVMValue uwuvm_copy_value(UwUVMValue value)
+{
+       if (value.type == VT_STR)
+               return uwustr_create(value.value.str_value);
+       else if (value.type == VT_NAT)
+               return (UwUVMValue) {
+                       .type = value.type,
+                       .value = {
+                               .nat_value = {
+                                       .type = value.value.nat_value.type,
+                                       .data = value.value.nat_value.type->copy(value.value.nat_value.data),
+                               }
+                       }
+               };
+       else
+               return value;
+}
+
+UwUVMValue uwuvm_get_arg(UwUVMArgs *args, size_t i)
+{
+       if (! args->evaluated[i]) {
+               args->evaluated[i] = malloc(sizeof(UwUVMValue));
+               *(args->evaluated[i]) = uwuvm_evaluate_expression(&args->unevaluated[i], args->super);
+       }
+
+       return *(args->evaluated[i]);
+}
+
+UwUVMValue uwuvm_evaluate_expression(UwUVMExpression *expression, UwUVMArgs *args)
+{
+       switch (expression->type) {
+               case EX_INTLIT:
+                       return uwuint_create(expression->value.int_value);
+
+               case EX_STRLIT:
+                       return uwustr_create(expression->value.str_value);
+
+               case EX_ARGNUM:
+                       if ((size_t) expression->value.int_value >= args->num) 
+                               error("error: not enough arguments (accessed argument $%d, but only %lu arguments were passed)\n", expression->value.int_value, args->num);
+
+                       return uwuvm_copy_value(uwuvm_get_arg(args, expression->value.int_value));
+
+               case EX_FNNAME:
+                       return (UwUVMValue) {
+                               .type = VT_REF,
+                               .value = {
+                                       .ref_value = expression->value.ref_value,
+                               },
+                       };
+
+               case EX_FNCALL:
+                       return uwuvm_run_function(expression->value.cll_value.function, (UwUVMArgs) {
+                               .num = expression->value.cll_value.num_args,
+                               .evaluated = expression->value.cll_value.num_args == 0 ? NULL : calloc(expression->value.cll_value.num_args, sizeof(UwUVMValue *)),
+                               .unevaluated = expression->value.cll_value.args,
+                               .super = args,
+                       });
+
+               default:
+                       return (UwUVMValue) {};
+       }
+}
+
+UwUVMValue uwuvm_run_function(UwUVMFunction *function, UwUVMArgs args)
+{
+       UwUVMValue value = function->type == MODULE_PLAIN
+               ? uwuvm_evaluate_expression(function->value.plain, &args)
+               : function->value.native(&args);
+
+       uwuvm_free_args(&args);
+       return value;
+}
diff --git a/api/vm.h b/api/vm.h
new file mode 100644 (file)
index 0000000..94103d5
--- /dev/null
+++ b/api/vm.h
@@ -0,0 +1,99 @@
+#ifndef _API_VM_H_
+#define _API_VM_H_
+
+#include <stddef.h>
+#include <stdbool.h>
+#include "../src/expression.h"
+
+typedef enum
+{
+       MODULE_PLAIN,
+       MODULE_NATIVE,
+} UwUVMModuleType;
+
+typedef struct
+{
+       void *(*copy  )(void *data);
+       void  (*delete)(void *data);
+       char *(*print )(void *data);
+} UwUVMNativeType;
+
+typedef struct
+{
+       void *data;
+       UwUVMNativeType *type;
+} UwUVMNativeValue;
+
+typedef struct UwUVMArgs
+{
+       size_t num;
+       struct UwUVMValue       **evaluated;
+       struct UwUVMExpression *unevaluated;
+       struct UwUVMArgs *super;
+} UwUVMArgs;
+
+typedef struct UwUVMValue (*UwUVMNativeFunction)(UwUVMArgs *args);
+
+typedef struct
+{
+       UwUVMModuleType type;
+       union
+       {
+               struct UwUVMExpression *plain;
+               UwUVMNativeFunction native;
+       } value;
+} UwUVMFunction;
+
+typedef struct UwUVMValue
+{
+       enum
+       {
+               VT_INT,
+               VT_STR,
+               VT_REF,
+               VT_NAT,
+       } type;
+       union
+       {
+               int           int_value;
+               char         *str_value;
+               UwUVMFunction   *ref_value;
+               UwUVMNativeValue nat_value;
+       } value;
+} UwUVMValue;
+
+typedef struct UwUVMExpression
+{
+       ExpressionType type;
+       union
+       {
+               struct
+               {
+                       UwUVMFunction *function;
+                       struct UwUVMExpression *args;
+                       size_t           num_args;
+               }           cll_value;
+               int         int_value;
+               char       *str_value;
+               UwUVMFunction *ref_value;
+       } value;
+} UwUVMExpression;
+
+typedef struct
+{
+       void *api_library;
+       UwUVMFunction *main_function;
+       UwUVMFunction **functions;
+       size_t   num_functions;
+       void     **libraries;
+       size_t num_libraries;
+} UwUVMProgram;
+
+void uwuvm_free_value(UwUVMValue value);
+void uwuvm_free_args(UwUVMArgs *args);
+UwUVMValue uwuvm_copy_value(UwUVMValue value);
+UwUVMValue uwuvm_get_arg(UwUVMArgs *args, size_t i);
+UwUVMValue uwuvm_evaluate_expression(UwUVMExpression *expression, UwUVMArgs *args);
+UwUVMValue uwuvm_run_function(UwUVMFunction *function, UwUVMArgs args);
+
+#endif
diff --git a/example/fibo.uwu b/example/fibo.uwu
new file mode 100644 (file)
index 0000000..7ad77e4
--- /dev/null
@@ -0,0 +1,27 @@
+if :bool:if($0, $1, $2)
+smaller :int:smaller($0, $1)
+equal :int:equal($0, $1)
+add :int:add($0, $1)
+sub :int:sub($0, $1)
+
+newline "
+"
+
+unused poop:asd:this_does_definitely_not_exist
+
+fibo
+       if(smaller($0, 0), 0,
+       if(equal($0, 0), 1,
+       add(
+               fibo(sub($0, 1)),
+               fibo(sub($0, 2))
+       )))
+
+
+print
+       if(smaller($0, 0), "",
+       :str:cat(
+               print(sub($0, 1)),
+               fibo($0),
+               newline
+       ))
diff --git a/example/test.uwu b/example/test.uwu
new file mode 100644 (file)
index 0000000..375a03f
--- /dev/null
@@ -0,0 +1,7 @@
+hello_world "hello world
+"
+
+main :str:cat(
+       hello_world,
+       fibo:print(10)
+)
diff --git a/src/collect.c b/src/collect.c
new file mode 100644 (file)
index 0000000..fadb9d1
--- /dev/null
@@ -0,0 +1,406 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <libgen.h>
+#include <dlfcn.h>
+#include "err.h"
+#include "util.h"
+#include "collect.h"
+#include "parse.h"
+
+#define DEBUG 0
+
+// helper functions
+
+static char *wrap_name_func(const char *name, char *(*fn)(char *))
+{
+       char *copy = strdup(name);
+       char *result = fn(copy);
+       char *result_copy = strdup(result);
+
+       free(copy);
+       return result_copy;
+}
+
+static char *basename_wrapper(const char *name)
+{
+       return wrap_name_func(name, &basename);
+}
+
+static char *dirname_wrapper(const char *name)
+{
+       return wrap_name_func(name, &dirname);
+}
+
+static bool file_exists(const char *filename)
+{
+       FILE *f = fopen(filename, "r");
+
+       if (f) {
+               fclose(f);
+               return true;
+       }
+
+       return false;
+}
+
+// type definitions
+
+typedef struct
+{
+       char *name;
+       UwUVMFunction *ref;
+} FunctionLink;
+
+typedef struct
+{
+       char *path;                                     // path without file extension
+       char *filename;                         // path with file extension
+       char *environment;                      // directory path
+
+       UwUVMModuleType type;                   // native (.so) or plain (.uwu)
+
+       FunctionLink *functions;        // required functions
+       size_t    num_functions;        // number of required functions
+       size_t loaded_functions;        // number of loaded functions (<= num_functions)
+
+       union
+       {
+               AbstractSyntaxTree ast; // abstract syntax tree generated by parser (for plain modules)
+               void *lib;                              // dlopen() shared object handle (for native modules)
+       } handle;
+} Module;
+
+typedef struct 
+{
+       Module   **modules;                     // loaded modules
+       size_t num_modules;                     // count for modules
+       
+       char *std_path;                         // path to standard library
+
+       UwUVMProgram program;                   // the result program
+} CollectorState;
+
+// functions
+
+// returns mallocated string
+static inline char *get_filename(const char *module_path)
+{
+       const char *try_names[3] = {
+               "%s",
+               "%s.uwu",
+               "%s.so",
+       };
+
+       char *filename;
+
+       for (int i = 0; i < 3; i++) {
+               filename = asprintf_wrapper(try_names[i], module_path);
+
+               if (file_exists(filename))
+                       return filename;
+               else
+                       free(filename);
+       }
+
+       return NULL;
+}
+
+// module_path is a mallocated string
+static Module *require_module(CollectorState *state, char *module_path)
+{
+       for (size_t i = 0; i < state->num_modules; i++) {
+               Module *module = state->modules[i];
+
+               if (strcmp(module_path, module->path) == 0) {
+                       free(module_path);
+                       return module;
+               }
+       }
+
+       char *filename = get_filename(module_path);
+
+       if (! filename)
+               error("error: module %s not found\n", module_path);
+
+       size_t filename_len = strlen(filename);
+       UwUVMModuleType type = (filename_len >= 3 && strcmp(filename + filename_len - 3, ".so") == 0) ? MODULE_NATIVE : MODULE_PLAIN;
+
+       state->modules = realloc(state->modules, sizeof *state->modules * ++state->num_modules);
+       Module *module = state->modules[state->num_modules - 1] = malloc(sizeof *module);
+
+       *module = (Module) {
+               .path = module_path,
+               .filename = filename,
+               .environment = dirname_wrapper(module_path),
+
+               .type = type,
+
+               .functions = NULL,
+               .num_functions = 0,
+               .loaded_functions = 0,
+       };
+
+       if (type == MODULE_PLAIN) {
+               module->handle.ast = parse_file(filename);
+       } else {
+               state->program.libraries = realloc(state->program.libraries, sizeof(void *) * ++state->program.num_libraries);
+               state->program.libraries[state->program.num_libraries - 1] = module->handle.lib = dlopen(filename, RTLD_LAZY);
+
+               char *err = dlerror();
+               if (err)
+                       error("%s\n", err);
+       }
+
+       return module;
+}
+
+static UwUVMFunction *require_function(CollectorState *state, Module *module, const char *name)
+{
+       for (size_t i = 0; i < module->num_functions; i++) {
+               FunctionLink *link = &module->functions[i];
+
+               if (strcmp(link->name, name) == 0)
+                       return link->ref;
+       }
+
+       UwUVMFunction *ref = malloc(sizeof *ref);
+       ref->type = module->type;
+
+       state->program.functions = realloc(state->program.functions, sizeof *state->program.functions * ++state->program.num_functions);
+       state->program.functions[state->program.num_functions - 1] = ref;       
+
+       module->functions = realloc(module->functions, sizeof *module->functions * ++module->num_functions);
+       module->functions[module->num_functions - 1] = (FunctionLink) {
+               .name = strdup(name),
+               .ref = ref,
+       };
+
+       return ref;
+}
+
+static UwUVMFunction *resolve_function(CollectorState *state, Module *caller_module, const char *full_name)
+{
+       size_t len = strlen(full_name);
+
+       const char *fnname;
+       for (fnname = &full_name[len - 1]; *fnname != ':' && fnname > full_name; fnname--)
+               ;
+
+       if (*fnname == ':')
+               fnname++;
+
+       if (*fnname == '\0')
+               error("error: empty function name\n");
+
+       Module *callee_module;
+
+       if (fnname == full_name) {
+               callee_module = caller_module;
+       } else {
+               const char *caller_path = caller_module->environment;
+               const char *callee_name = full_name;
+
+               if (*callee_name == ':') {
+                       caller_path = state->std_path;
+                       callee_name++;
+               }
+
+               size_t path_len = fnname - callee_name; 
+               char callee_path[path_len];
+
+               for (size_t i = 0; i < path_len; i++)
+                       callee_path[i] = (i == path_len - 1) ? '\0'
+                               : (callee_name[i] == ':') ? '/'
+                               : callee_name[i];
+
+               callee_module = require_module(state, asprintf_wrapper("%s/%s", caller_path, callee_path));
+       }
+
+       return require_function(state, callee_module, fnname);
+}
+
+static void translate_expression(CollectorState *state, Module *module, UwUVMExpression *vm_expr, ParseExpression *parse_expr)
+{
+       UwUVMFunction *vm_function;
+
+       if (parse_expr->type == EX_FNNAME || parse_expr->type == EX_FNCALL) {
+               vm_function = resolve_function(state, module, parse_expr->value.str_value);
+               free(parse_expr->value.str_value);
+       }
+       
+       switch (vm_expr->type = parse_expr->type) {
+               case EX_INTLIT:
+               case EX_ARGNUM:
+                       vm_expr->value.int_value = parse_expr->value.int_value;
+                       break;
+
+               case EX_STRLIT:
+                       vm_expr->value.str_value = parse_expr->value.str_value;
+                       break;
+
+               case EX_FNNAME:
+                       vm_expr->value.ref_value = vm_function;
+                       break;
+
+               case EX_FNCALL:
+                       vm_expr->value.cll_value.function = vm_function;
+                       vm_expr->value.cll_value.args = malloc(sizeof(UwUVMExpression) * parse_expr->num_children);
+                       vm_expr->value.cll_value.num_args = parse_expr->num_children;
+
+                       for (size_t i = 0; i < parse_expr->num_children; i++)
+                               translate_expression(state, module, &vm_expr->value.cll_value.args[i], parse_expr->children[i]);
+
+                       if (parse_expr->children)
+                               free(parse_expr->children);
+                       break;
+
+               default:
+                       break;
+       }               
+
+       free(parse_expr);
+}
+
+static void load_functions(CollectorState *state, Module *module)
+{
+       for (; module->loaded_functions < module->num_functions; module->loaded_functions++) {
+               FunctionLink *linkptr = &module->functions[module->loaded_functions];
+               FunctionLink link = *linkptr;
+
+               bool found = false;
+
+               if (module->type == MODULE_PLAIN) {
+                       ParseFunction **function = NULL;
+
+                       for (size_t i = 0; i < module->handle.ast.num_functions; i++) {
+                               ParseFunction **fn = &module->handle.ast.functions[i];
+
+                               if (*fn && strcmp((*fn)->name, link.name) == 0) {
+                                       function = fn;
+                                       break;
+                               }
+                       }
+
+                       if (function) {
+                               found = true;
+                               linkptr = NULL;
+
+                               translate_expression(state, module, link.ref->value.plain = malloc(sizeof(UwUVMExpression)), (*function)->expression);
+                               free((*function)->name);
+                               free(*function);
+
+                               *function = NULL;
+                       }
+               } else {
+                       char *symbol = asprintf_wrapper("uwu_%s", link.name);
+                       linkptr->ref->value.native = dlsym(module->handle.lib, symbol);
+
+                       if (! dlerror())
+                               found = true;
+       
+                       free(symbol);
+               }
+
+               if (! found)
+                       error("error: no function %s in module %s\n", link.name, module->filename);
+       }
+}
+
+static void free_expression(ParseExpression *expr)
+{
+       if (expr->type == EX_FNCALL) {
+               for (size_t i = 0; i < expr->num_children; i++)
+                       free_expression(expr->children[i]);
+
+               if (expr->children)
+                       free(expr->children);
+       }
+
+       if (expr->type != EX_INTLIT && expr->type != EX_ARGNUM)
+               free(expr->value.str_value);
+
+       free(expr);     
+}
+
+UwUVMProgram create_program(const char *progname, const char *modname)
+{
+       char *prog_dirname = dirname_wrapper(progname);
+       char *api_path = asprintf_wrapper("%s/api/api.so", prog_dirname);
+
+       CollectorState state = {
+               .modules = NULL,
+               .num_modules = 0,
+               .std_path = asprintf_wrapper("%s/std", prog_dirname),
+               .program = {
+                       .api_library = dlopen(api_path, RTLD_NOW | RTLD_GLOBAL),
+                       .main_function = NULL,
+                       .functions = NULL,
+                       .num_functions = 0,
+                       .libraries = NULL,
+                       .num_libraries = 0,
+               },
+       };
+
+       free(prog_dirname);
+       free(api_path);
+       
+       state.program.main_function = require_function(&state, require_module(&state, strdup(modname)), "main");
+
+       while (true) {
+               bool fully_loaded = true;
+
+               for (size_t i = 0; i < state.num_modules; i++) {
+                       Module *module = state.modules[i];
+
+#if DEBUG
+                       printf("%s %lu/%lu\n", module->filename, module->loaded_functions, module->num_functions);
+#endif
+
+                       if (module->loaded_functions < module->num_functions) {
+                               fully_loaded = false;
+                               load_functions(&state, module);
+                       }
+               }
+
+               if (fully_loaded)
+                       break;
+       }
+
+       free(state.std_path);
+
+       for (size_t i = 0; i < state.num_modules; i++) {
+               Module *module = state.modules[i];
+
+               free(module->path);
+               free(module->filename);
+               free(module->environment);
+
+               for (size_t f = 0; f < module->num_functions; f++)
+                       free(module->functions[f].name);
+
+               free(module->functions);
+
+               if (module->type == MODULE_PLAIN) {
+                       for (size_t f = 0; f < module->handle.ast.num_functions; f++) {
+                               ParseFunction *function = module->handle.ast.functions[f];
+
+                               if (function) {
+                                       free_expression(function->expression);
+                                       free(function->name);
+                                       free(function);
+                               }
+                       }
+
+                       if (module->handle.ast.functions)
+                               free(module->handle.ast.functions);
+               }
+               
+               free(module);
+       }
+
+       free(state.modules);
+
+       return state.program;
+}
diff --git a/src/collect.h b/src/collect.h
new file mode 100644 (file)
index 0000000..da118a5
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _COLLECT_H_
+#define _COLLECT_H_
+
+#include "../api/vm.h"
+
+UwUVMProgram create_program(const char *progname, const char *modname);
+
+#endif
diff --git a/src/err.h b/src/err.h
new file mode 100644 (file)
index 0000000..1dccbf9
--- /dev/null
+++ b/src/err.h
@@ -0,0 +1,17 @@
+#ifndef _ERR_H_
+#define _ERR_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+static inline void error(const char *format, ...)
+{
+       va_list args;
+       va_start(args, format);
+       vfprintf(stderr, format, args);
+       va_end(args);
+       exit(1);
+}
+
+#endif
diff --git a/src/expression.h b/src/expression.h
new file mode 100644 (file)
index 0000000..da74620
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _EXPRESSION_H_
+#define _EXPRESSION_H_
+
+typedef enum 
+{
+       EX_UNINIT,
+       EX_INTLIT,
+       EX_STRLIT,
+       EX_ARGNUM,
+       EX_FNNAME,
+       EX_FNCALL,
+} ExpressionType;
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..60c22ba
--- /dev/null
@@ -0,0 +1,56 @@
+#include "err.h"
+#include "vm.h"
+
+int main(int argc, char *argv[])
+{
+       if (argc < 2)
+               error("usage: %s <module>\n", argv[0]);
+
+       vm_run_file(argv[0], argv[1]);
+       return 0;
+}
+
+/*
+
+0123
+"asd"
+$arg
+&fnname
+func(asd)
+
+:int:add
+:str:cat
+:boo:and
+:arr:arr
+:set:set
+
+integer::add()
+integer::sub()
+integer::mul()
+integer::div()
+integer::mod()
+integer::pow()
+
+string::concat()
+string::split()
+string::find()
+
+array::array()
+array::select()
+array::insert()
+array::length()
+array::reduce()
+array::map()
+
+set::set()
+set::pair()
+set::select()
+set::insert()
+set::remove()
+set::contains()
+
+boolean::and()
+boolean::or()
+boolean::xor()
+
+*/
diff --git a/src/parse.c b/src/parse.c
new file mode 100644 (file)
index 0000000..be0ee98
--- /dev/null
@@ -0,0 +1,325 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "err.h"
+#include "parse.h"
+
+#define DEBUG 0
+
+#if DEBUG
+#define DBG(FN) printf("%s %s %s\n", FN, expression_types[state->expression->type], (state->expression->type == EX_FNCALL && state->expression->value.str_value) ? state->expression->value.str_value : "");
+#else
+#define DBG(FN)
+#endif
+
+#if DEBUG
+static char *expression_types[EX_FNCALL + 1] = {
+       "uninitialized",
+       "integer-literal",
+       "string-literal",
+       "argument-number",
+       "function-name",
+       "function-call",
+};
+
+static void print_expression(ParseExpression *expr, int indent)
+{
+       for (int i = 0; i < indent; i++)
+               printf("\t");
+
+       printf("%s ", expression_types[expr->type]);
+
+       if (expr->type == EX_INTLIT || expr->type == EX_ARGNUM)
+               printf("%d\n", expr->value.int_value);
+       else
+               printf("\"%s\"\n", expr->value.str_value);
+
+       if (expr->type == EX_FNCALL)
+               for (size_t i = 0; i < expr->num_children; i++)
+                       print_expression(expr->children[i], indent + 1);
+}
+
+static void print_ast(AbstractSyntaxTree tree)
+{
+       printf("\n[Abstract Syntax Tree]\n\n");
+
+       for (size_t f = 0; f < tree.num_functions; f++) {
+               ParseFunction *function = tree.functions[f];
+       
+               printf("function %s\n", function->name);
+               print_expression(function->expression, 1);
+       }
+}
+#endif
+
+static void buffer_append(ParseState *state, char c)
+{
+       state->buffer = realloc(state->buffer, ++state->buffer_size);
+       state->buffer[state->buffer_size - 1] = c;
+}
+
+static char *buffer_terminate(ParseState *state)
+{
+       buffer_append(state, '\0');
+
+       char *buffer = state->buffer;
+       
+       state->buffer = NULL;
+       state->buffer_size = 0;
+
+       return buffer;
+}
+
+static void start_arg(ParseState *state)
+{
+       DBG(__FUNCTION__)
+       
+       ParseExpression *parent = state->expression;
+       parent->children = realloc(parent->children, sizeof *parent->children * ++parent->num_children);
+       ParseExpression *child = parent->children[parent->num_children - 1] = malloc(sizeof *child);
+
+       child->type = EX_UNINIT;
+       child->parent = parent;
+
+       state->expression = child;
+}
+
+static bool continue_arg(ParseState *state, char c)
+{
+       DBG(__FUNCTION__)
+                       
+       if (c == ',')
+               start_arg(state);
+       else if (c == ')')
+               state->expression = state->expression->parent;
+       else if (! isspace(c))
+               return false;
+
+       return true;
+}
+
+static bool finish_arg(ParseState *state, char c) 
+{      
+       state->expression = state->expression->parent;
+
+       if (state->expression)
+               continue_arg(state, c);
+       else if (! isspace(c))
+               return false;
+
+       return true;
+}
+
+static bool parse_expression_init(ParseState *state, char c)
+{
+       DBG(__FUNCTION__)
+
+       if (c == ',')
+               return false;
+
+       if (isspace(c))
+               return true;
+
+       switch (c) {
+               case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+                       state->expression->type = EX_INTLIT;
+                       buffer_append(state, c);
+                       return true;
+
+               case '"':
+                       state->expression->type = EX_STRLIT;
+                       return true;
+
+               case '$':
+                       state->expression->type = EX_ARGNUM;
+                       return true;
+
+               case '&':
+                       state->expression->type = EX_FNNAME;
+                       return true;
+
+               default:
+                       state->expression->type = EX_FNCALL;
+                       state->expression->value.str_value = NULL;
+                       buffer_append(state, c);
+                       return true;
+       }
+}
+
+static bool parse_expression_finish(ParseState *state, char c)
+{      
+       DBG(__FUNCTION__)
+
+       if (state->expression->type == EX_ARGNUM && state->buffer_size == 0)
+               return false;
+
+       char *buffer_read = buffer_terminate(state);
+
+       if (state->expression->type == EX_INTLIT || state->expression->type == EX_ARGNUM) {
+               state->expression->value.int_value = atoi(buffer_read);
+               free(buffer_read);
+       } else {
+               state->expression->value.str_value = buffer_read;
+       }
+
+       if (state->expression->type == EX_FNCALL) {
+               state->expression->num_children = 0;
+               state->expression->children = NULL;
+
+               if (c == '(')
+                       start_arg(state);
+               else
+                       return finish_arg(state, c);
+       } else {
+               if (c == ',' || c == ')')
+                       return finish_arg(state, c);
+               else
+                       state->expression = state->expression->parent;
+       }
+
+       return true;
+}
+
+static bool parse_expression_continue(ParseState *state, char c)
+{
+       DBG(__FUNCTION__)
+
+       if (state->expression->type == EX_FNCALL && state->expression->value.str_value)
+               return continue_arg(state, c);
+
+       if (
+               state->expression->type == EX_STRLIT
+               ? c == '"'
+               : (
+                       (state->expression->type == EX_FNCALL && c == '(')
+                       || isspace(c) || c == ',' || c == ')'
+               )
+       ) {
+               return parse_expression_finish(state, c);
+       } else {
+               if ((state->expression->type == EX_INTLIT || state->expression->type == EX_ARGNUM) && ! isdigit(c))
+                       return false;
+
+               if ((state->expression->type == EX_FNNAME || state->expression->type == EX_FNCALL) && (c == '&' || c == '$'))
+                       return false;
+
+               buffer_append(state, c);
+       }
+
+       return true;
+}
+
+static bool parse_expression(ParseState *state, char c)
+{      
+       DBG(__FUNCTION__)
+
+       return state->expression->type == EX_UNINIT
+               ? parse_expression_init(state, c)
+               : parse_expression_continue(state, c);
+}
+
+static bool parse_function(ParseState *state, char c)
+{
+#if DEBUG
+       printf("%s\n", __FUNCTION__);
+#endif
+
+       if (c == '\"' || c == '$' || c == ':' || c == ',' || c == '&' || c == '(' || c == ')' || isdigit(c))
+               return false;
+
+       if (! isspace(c)) {
+               buffer_append(state, c);
+               return true;
+       }
+
+       if (state->buffer_size == 0)
+               return true;
+
+       char *name = buffer_terminate(state);
+
+       state->expression = malloc(sizeof *state->expression);
+       state->expression->type = EX_UNINIT;
+       state->expression->parent = NULL;
+
+       state->tree.functions = realloc(state->tree.functions, sizeof *state->tree.functions * ++state->tree.num_functions);
+       *(state->tree.functions[state->tree.num_functions - 1] = malloc(sizeof(ParseFunction))) = (ParseFunction) {
+               .name = name,
+               .expression = state->expression,
+       };
+
+       return true;
+}
+
+static bool parse_character(ParseState *state, char c)
+{      
+#if DEBUG
+       printf("\nparse_character ");
+
+       if (isspace(c))
+               printf("<SPACE>");
+       else
+               printf("%c", c);
+
+        printf("\n");
+#endif
+       
+       return state->expression
+               ? parse_expression(state, c)
+               : parse_function(state, c);
+}
+
+AbstractSyntaxTree parse_file(const char *filename)
+{
+       ParseState state = {
+               .tree = {
+                       .num_functions = 0,
+                       .functions = NULL,
+               },
+               .buffer_size = 0,
+               .buffer = NULL,
+               .expression = NULL,
+       };
+       
+       int lines = 1;
+
+       FILE *f = fopen(filename, "r");
+
+       if (! f)
+               error("%s: unable to open\n", filename);
+
+#if DEBUG
+       printf("[File %s]\n[Line %d]\n", filename, lines);
+#endif
+
+       while (true) {
+               char c = getc(f);
+
+               if (feof(f))
+                       break;
+
+               if (ferror(f))
+                       error("%s: I/O error\n", filename);
+
+               if (c == '\n')
+                       ++lines;
+
+#if DEBUG
+               if (c == '\n')
+                       printf("\n[Line %d]\n", lines);
+#endif
+               
+               if (! parse_character(&state, c))
+                       error("%s: syntax error in line %d\n", filename, lines);
+       }
+
+       if (state.buffer || state.expression)
+               error("%s: syntax error at end of file\n", filename);
+
+       fclose(f);
+
+#if DEBUG
+       print_ast(state.tree);
+#endif
+
+       return state.tree;
+}
diff --git a/src/parse.h b/src/parse.h
new file mode 100644 (file)
index 0000000..861ad1c
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef _PARSE_H_
+#define _PARSE_H_
+
+#include <stddef.h>
+#include <stdbool.h>
+#include "expression.h"
+
+typedef struct ParseExpression
+{
+       ExpressionType type;
+       union
+       {
+               int   int_value;
+               char *str_value;
+       } value;
+       size_t num_children;
+       struct ParseExpression **children;
+       struct ParseExpression *parent;
+} ParseExpression;
+
+typedef struct
+{
+       char *name;
+       ParseExpression *expression;
+} ParseFunction;
+
+typedef struct
+{
+       size_t num_functions;
+       ParseFunction **functions;
+} AbstractSyntaxTree;
+
+typedef struct
+{
+       AbstractSyntaxTree tree;
+       
+       size_t buffer_size;
+       char *buffer;
+
+       ParseExpression *expression;
+
+       int lines;
+       bool success;
+} ParseState;
+
+AbstractSyntaxTree parse_file(const char *filename);
+
+#endif
diff --git a/src/util.h b/src/util.h
new file mode 100644 (file)
index 0000000..b0baa37
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+#include <stdio.h>
+#include <stdarg.h>
+
+static inline char *asprintf_wrapper(const char *format, ...)
+{
+       va_list args;
+       va_start(args, format);
+       char *ptr;
+       vasprintf(&ptr, format, args);
+       va_end(args);
+       return ptr;
+}
+
+#endif
diff --git a/src/vm.c b/src/vm.c
new file mode 100644 (file)
index 0000000..7f0db99
--- /dev/null
+++ b/src/vm.c
@@ -0,0 +1,52 @@
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "collect.h"
+#include "vm.h"
+
+static void free_expression(UwUVMExpression *expr)
+{
+       if (expr->type == EX_FNCALL) {
+               for (size_t i = 0; i < expr->value.cll_value.num_args; i++)
+                       free_expression(&expr->value.cll_value.args[i]);
+       
+               free(expr->value.cll_value.args);
+       }
+
+       if (expr->type == EX_STRLIT)
+               free(expr->value.str_value);
+}
+
+void vm_run_file(const char *progname, const char *modname)
+{
+       UwUVMProgram program = create_program(progname, modname);
+       UwUVMValue result = ((UwUVMValue (*)(UwUVMFunction *, UwUVMArgs args)) dlsym(program.api_library, "uwuvm_run_function"))(program.main_function, (UwUVMArgs) {.num = 0, .evaluated = NULL, .unevaluated = NULL, .super = NULL});
+
+       char *str = ((char *(*)(UwUVMValue)) dlsym(program.api_library, "uwustr_get"))(result);
+
+       printf("%s\n", str);
+       free(str);
+
+       ((void (*)(UwUVMValue)) dlsym(program.api_library, "uwuvm_free_value"))(result);
+
+       for (size_t i = 0; i < program.num_functions; i++) {
+               UwUVMFunction *function = program.functions[i];
+
+               if (function->type == MODULE_PLAIN) {
+                       free_expression(function->value.plain);
+                       free(function->value.plain);
+               }
+                       
+               free(function);
+       }
+
+       free(program.functions);
+
+       for (size_t i = 0; i < program.num_libraries; i++)
+               dlclose(program.libraries[i]);
+
+       if (program.libraries)
+               free(program.libraries);
+
+       dlclose(program.api_library);
+}
diff --git a/src/vm.h b/src/vm.h
new file mode 100644 (file)
index 0000000..5e23c8b
--- /dev/null
+++ b/src/vm.h
@@ -0,0 +1,6 @@
+#ifndef _VM_H_
+#define _VM_H_
+
+void vm_run_file(const char *progname, const char *modname);
+
+#endif
diff --git a/std/bool.c b/std/bool.c
new file mode 100644 (file)
index 0000000..cb4082b
--- /dev/null
@@ -0,0 +1,97 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "../src/err.h"
+#include "../api/vm.h"
+#include "../api/bool.h"
+
+static inline bool get_bool_arg(UwUVMArgs *args, size_t i)
+{
+       return uwubool_get(uwuvm_get_arg(args, i));
+}
+
+#include "../api/str.h"
+UwUVMValue uwu_if(UwUVMArgs *args)
+{
+       if (args->num != 3)
+               error("error: :bool:if requires exactly 3 arguments\n");
+
+       return uwuvm_copy_value(get_bool_arg(args, 0)
+               ? uwuvm_get_arg(args, 1)
+               : uwuvm_get_arg(args, 2)
+       );
+}
+
+UwUVMValue uwu_and(UwUVMArgs *args)
+{
+       if (args->num < 1)
+               error("error: :bool:and requires at least one argument\n");
+
+       for (size_t i = 0; i < args->num; i++)
+               if (! get_bool_arg(args, i))
+                       return uwubool_create(false);
+
+       return uwubool_create(true);
+}
+
+UwUVMValue uwu_or(UwUVMArgs *args)
+{
+       if (args->num < 1)
+               error("error: :bool:or requires at least one argument\n");
+
+       for (size_t i = 0; i < args->num; i++)
+               if (get_bool_arg(args, i))
+                       return uwubool_create(true);
+
+       return uwubool_create(false);
+}
+
+UwUVMValue uwu_equal(UwUVMArgs *args)
+{
+       if (args->num < 2)
+               error("error: :bool:equal requires at least 2 arguments\n");
+
+       bool value = get_bool_arg(args, 0);
+
+       for (size_t i = 1; i < args->num; i++)
+               if (get_bool_arg(args, i) != value)
+                       return uwubool_create(false);
+       
+       return uwubool_create(true);
+}
+
+UwUVMValue uwu_not(UwUVMArgs *args)
+{
+       if (args->num != 1)
+               error("error: :bool:not requires exactly 1 argument\n");
+
+       return uwubool_create(! get_bool_arg(args, 0));
+}
+
+UwUVMValue uwu_true(UwUVMArgs *args)
+{
+       if (args->num != 0)
+               error("error: :bool:true does not take any arguments\n");
+       
+       return uwubool_create(true);
+}
+
+UwUVMValue uwu_false(UwUVMArgs *args)
+{
+       if (args->num != 0)
+               error("error: :bool:false does not take any arguments\n");
+
+       return uwubool_create(false);
+}
+
+UwUVMValue uwu_is(UwUVMArgs *args)
+{
+       if (args->num < 1)
+               error("error: :bool:is requires at least 1 argument\n");
+
+       for (size_t i = 0; i < args->num; i++) {
+               UwUVMValue value = uwuvm_get_arg(args, i);
+               return uwubool_create(value.type != VT_NAT || value.value.nat_value.type != &uwubool_type);
+       }
+
+       return uwubool_create(true);
+}
diff --git a/std/bool.so b/std/bool.so
new file mode 100755 (executable)
index 0000000..5007f5f
Binary files /dev/null and b/std/bool.so differ
diff --git a/std/int.c b/std/int.c
new file mode 100644 (file)
index 0000000..a8d2c6d
--- /dev/null
+++ b/std/int.c
@@ -0,0 +1,134 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "../src/err.h"
+#include "../api/vm.h"
+#include "../api/int.h"
+#include "../api/bool.h"
+
+typedef enum
+{
+       BOP_SUB,
+       BOP_DIV,
+       BOP_MOD,
+       BOP_SML,
+       BOP_GRT,
+       BOP_EQU,
+} BinaryOP;
+
+static int binary(const char *fnname, UwUVMArgs *args, BinaryOP op)
+{
+       if (args->num != 2)
+               error("error: %s requires exactly 2 arguments\n", fnname);
+
+       UwUVMValue value0 = uwuvm_get_arg(args, 0);
+
+       if (value0.type != VT_INT)
+               error("error: %s requires an integer as $0\n", fnname);
+
+       UwUVMValue value1 = uwuvm_get_arg(args, 1);
+
+       if (value1.type != VT_INT)
+               error("error: %s requires an integer as $1\n", fnname);
+
+       int a = value0.value.int_value;
+       int b = value1.value.int_value;
+
+       switch (op) {
+               case BOP_SUB: return a - b;
+               case BOP_DIV: return a / b;
+               case BOP_MOD: return a % b;
+               case BOP_SML: return a < b;
+               case BOP_GRT: return a > b;
+               case BOP_EQU: return a == b;
+       }
+}
+
+typedef enum
+{
+       ROP_ADD,
+       ROP_MUL,
+       ROP_EQU,
+} ReduceOP;
+
+static int reduce(const char *fnname, UwUVMArgs *args, ReduceOP op, int result)
+{
+       int first;
+
+       for (size_t i = 0; i < args->num; i++) {
+               UwUVMValue value = uwuvm_get_arg(args, i);
+       
+               if (value.type != VT_INT)
+                       error("error: %s only accepts integers as arguments (invalid argument: $%lu)\n", fnname, i);
+
+               int this = value.value.int_value;
+
+               switch (op) {
+                       case ROP_ADD: result += this; break;
+                       case ROP_MUL: result *= this; break;
+                       case ROP_EQU:
+                               if (i == 0)
+                                       first = this;
+                               else if (this != first)
+                                       return 0;
+
+                               break;
+               }
+       }
+
+       return result;
+}
+
+UwUVMValue uwu_add(UwUVMArgs *args)
+{
+       return uwuint_create(reduce(":int:mul", args, ROP_ADD, 0));
+}
+
+UwUVMValue uwu_sub(UwUVMArgs *args)
+{
+       return uwuint_create(binary(":int:sub", args, BOP_SUB));
+}
+
+UwUVMValue uwu_mul(UwUVMArgs *args)
+{
+       return uwuint_create(reduce(":int:mul", args, ROP_MUL, 1));     
+}
+
+UwUVMValue uwu_div(UwUVMArgs *args)
+{
+       return uwuint_create(binary(":int:div", args, BOP_DIV));
+}
+
+UwUVMValue uwu_mod(UwUVMArgs *args)
+{
+       return uwuint_create(binary(":int:mod", args, BOP_MOD));
+}
+
+UwUVMValue uwu_smaller(UwUVMArgs *args)
+{
+       return uwubool_create(binary(":int:smaller", args, BOP_SML) == 1);
+}
+
+UwUVMValue uwu_greater(UwUVMArgs *args)
+{
+       return uwubool_create(binary(":int:greater", args, BOP_GRT) == 1);
+}
+
+UwUVMValue uwu_equal(UwUVMArgs *args)
+{      
+       if (args->num < 2)
+               error("error: :int:equal requires at least 2 arguments\n");
+
+       return uwubool_create(reduce(":int:equal", args, ROP_EQU, 1) == 1);
+}
+
+UwUVMValue uwu_is(UwUVMArgs *args)
+{
+       if (args->num < 1)
+               error("error: :int:is requires at least 1 argument\n");
+
+       for (size_t i = 0; i < args->num; i++)
+               if (uwuvm_get_arg(args, i).type != VT_INT)
+                       return uwubool_create(false);
+
+       return uwubool_create(true);
+}
diff --git a/std/int.so b/std/int.so
new file mode 100755 (executable)
index 0000000..e7ab065
Binary files /dev/null and b/std/int.so differ
diff --git a/std/str.c b/std/str.c
new file mode 100644 (file)
index 0000000..16f756b
--- /dev/null
+++ b/std/str.c
@@ -0,0 +1,30 @@
+#include <string.h>
+#include <stdlib.h>
+#include "../api/vm.h"
+#include "../api/str.h"
+
+UwUVMValue uwu_cat(UwUVMArgs *args)
+{
+       size_t total_len = 0;
+       size_t lengths[args->num];
+       char  *substrs[args->num];
+
+       for (size_t i = 0; i < args->num; i++) {
+               substrs[i] = uwustr_get(uwuvm_get_arg(args, i));
+               lengths[i] = strlen(substrs[i]);
+               total_len += lengths[i];
+       }
+
+       char result[total_len + 1];
+       char *result_ptr = result;
+
+       for (size_t i = 0; i < args->num; i++) {
+               strcpy(result_ptr, substrs[i]);
+               free(substrs[i]);
+               result_ptr += lengths[i];
+       }
+
+       *result_ptr = 0;
+
+       return uwustr_create(result);
+}
diff --git a/std/str.so b/std/str.so
new file mode 100755 (executable)
index 0000000..a7c6497
Binary files /dev/null and b/std/str.so differ
diff --git a/uwu b/uwu
new file mode 100755 (executable)
index 0000000..804f7b0
Binary files /dev/null and b/uwu differ