sudo mkdir -p "$CORE_DIR"
sudo cp -r ./core/ "$CORE_DIR"
-# This is a development feature to allow for quickly reinstalling core libraries
-# without have to recompile the entire compiler
-[ "$1" = "core" ] && exit 0
sudo cp ./bin/onyx-pkg "$BIN_DIR/onyx-pkg"
sudo mkdir -p "$CORE_DIR/tools"
sudo cp ./scripts/onyx-pkg.onyx "$CORE_DIR/tools"
+# This is a development feature to allow for quickly reinstalling core libraries
+# without have to recompile the entire compiler
+[ "$1" = "core" ] && exit 0
+
if [ "$RUNTIME_LIBRARY" = "ovmwasm" ]; then
cd interpreter
./build.sh $1
+++ /dev/null
-The following things are defined in the `builtin` package, which is implicitly
-used in every file. Therefore, these things are effectively like "globals". Also,
-you can add your own things to the `builtin` package to define your own "globals"
-for your project.
-
-- `str` -
-This is the builtin string type, which is just a slice of bytes.
-
-- `cstr` -
-This is the builtin cstring type, used in every program as the type of the command line arguments given to main.
-
-- `range` -
-This is the type of a range literal, i.e. 1 .. 5. You can also use this type in a for-loop to iterate over the values.
-
-- `vararg` -
-This is a deprecated type used when you have an untyped variadic procedure, i.e. x :: (va: ...). This is deprecated because of the 'any' type.
-
-- `vararg_get` -
-This is also deprecated but it allowed you get values out of a `vararg` type.
-
-- `null_proc` -
-This is a special procedure that always matches any procedure type, regardless of whether the types actually match. This allows you to use it as a placeholder or "null" value when a procedure is expected.
-
-- `null` -
-Much like every other systems-ish programming language, Onyx has a "null" type. In Onyx, it is nothing more than a 0 value casted to a `rawptr`.
-
-- `null_str` -
-A helpful constant that is a string with the data being null and the count being 0. Useful if you want to have a blank string value. It is different from "", because the latter will still be assigned a data location, even through there will not be any data there.
-
-- `OnyxContext` -
-The builtin type of the `context`, described below. In theory, this type could exist at file scope in builtin.onyx, but I decided to leave it exposed.
-
-- `context` -
-This is a thread-local variable that stores the active allocators, loggers, and assert_handler for the current thread.
-
-- `assert` -
-A function that asserts a certain condition is true; if not, it calls the assert handler on the `context`.
-
-- `Logger` -
-The type used for the logger on the `context`.
-
-- `log` -
-Logs a message to the `context.logger`. Currently, this does not support formatted arguments, but that is planned in the near future.
-
-- `AllocationAction` -
-A simple enum that represent what action is to be taken when calling an allocator_proc.
-
-- `Allocator` -
-This type represents something that can allocate memory. It has two members, `data` and `func`. The `func` is an `allocator_proc`, which takes the data, the allocation action, size, alignment and old pointer. Not all of these values are used for every action, but the allocator procedure must consume all of them.
-
-- `raw_alloc` `raw_resize` `raw_free` -
-These functions are simply wrappers provided to invoke an `Allocator` with a specific action.
-
-- `calloc` `cresize` `cfree` -
-These functions simply call `raw_alloc`, `raw_resize` and `raw_free` using `context.allocator` as the allocator. These are effectively the equivalent of `malloc`, `realloc`, and `free` in C.
-
-- `new` -
-This function allocates enough space for the given type from the given allocator, which by default is the context's allocator. It also initializes the resulting memory, which uses the `__initialize` intrinsic that initializes all types, but most importantly it evalutes the default values for struct members.
-
-- `make` -
-This function is like new, except it does not initialize the memory.
-
-- `Iterator` -
-This type represents a custom iterator that can be iterated over using a `for` loop. It has three members. `data`, a simple pointer passed to the other functions. `next` which provides the next value for the iterator, and a continuation boolean. If the boolean is false, the iterator is done and the value given is garbage. And finally `close`, which is optional and allows you to run code when the iterator is done being used. When using a `for` loop, `close` is called automatically for you when the loop exits in any way.
-
-- `CallSite` -
-This type represents the value given by the `#callsite` expression. In practice, you will never use this type because you will just do something like: `f :: (site := #callsite)`
-
-- `any` -
-This type represents any value. It does this by using a data pointer and a type expression. Using the `runtime.info` package, you can introspect that type expression to retrieve data about the type, and from there reconstruct how to use the data pointer. See `conv.onyx` for how this works with `printf`.
-
-- `Code` -
-This is a dummy type that represents the type of a `#unquote {}` block. It is used when passing code around to macros or procedures, i.e. `f :: macro (body: Code)`
\ No newline at end of file
+++ /dev/null
-Ditching WASI
--------------
-
-After programming with the dedicated Onyx Runtime and extensible C interop,
-I am beginning to hate WASI, as I see it as unnecessarily restrictive and all
-implementations of WASI lack 50% of the features that it claim. If they fully
-supported all features proposed by WASI, I may be more for it. But as it stands
-WASI is an incomplete mess that has seen no active development in years and I
-would like to ditch it for the Onyx Runtime. It will still remain available for
-the WASI runtime, obviously, but I want to completely ditch it elsewhere.
-
-In addition to reimplementing the file, directory and clock operations that were
-available in WASI, Onyx could support a proper TCP/UDP networking protocol,
-probabily similar to the socket library in Python.
-
-Good article about differences between Winsock and Berkeley sockets:
-https://handsonnetworkprogramming.com/articles/differences-windows-winsock-linux-unix-bsd-sockets-compatibility/
-
-One small technicality that has to be resolved is command line arguments without
-WASI. I think just setting global data and then the Onyx `_startup` function will
-envoke specific function like is done in WASI.
-
--- /dev/null
+Ditching WASI
+-------------
+
+After programming with the dedicated Onyx Runtime and extensible C interop,
+I am beginning to hate WASI, as I see it as unnecessarily restrictive and all
+implementations of WASI lack 50% of the features that it claim. If they fully
+supported all features proposed by WASI, I may be more for it. But as it stands
+WASI is an incomplete mess that has seen no active development in years and I
+would like to ditch it for the Onyx Runtime. It will still remain available for
+the WASI runtime, obviously, but I want to completely ditch it elsewhere.
+
+In addition to reimplementing the file, directory and clock operations that were
+available in WASI, Onyx could support a proper TCP/UDP networking protocol,
+probabily similar to the socket library in Python.
+
+Good article about differences between Winsock and Berkeley sockets:
+https://handsonnetworkprogramming.com/articles/differences-windows-winsock-linux-unix-bsd-sockets-compatibility/
+
+One small technicality that has to be resolved is command line arguments without
+WASI. I think just setting global data and then the Onyx `_startup` function will
+envoke specific function like is done in WASI.
+
--- /dev/null
+// Currently the only way for something to be generic and type checked
+// in Onyx is to use polymorphic procedures and overloaded procedures
+// to allow a procedure to be statically duck-typed after it has been
+// determined that this is the procedure you are calling (think Map
+// and hash.to_u32). This has been okay, but it leads to some grossness
+// in the language semantics and the kinds of error messages that are
+// producable.
+//
+// For example, it is not immediately clear that in order to make a Map
+// or a Set out of a type, you need to declare a match option for hash.to_u32
+// and define '==' for the type. This is kind of probablimatic and doesn't
+// make for great self explanatory or maintainable code. Also I don't like
+// #match at all, as it feels like a complete and utter hack.
+//
+
+// Ignore everything that is said below. It all sounded like a good idea
+// at one point, until I started implementing it when I realized that not
+// only is it WAY too complicated and adds too much complexity in the compiler
+// but it doesn't feel like a natural extention to Onyx.
+//
+// Instead, 'interfaces' are going to be much much simpler, but to write
+// and for the compiler to deal with. Effectively, they are compile time checks
+// that assert that a particular set of expressions is valid, and optionally
+// return the correct type. They are offered as an extension to overloaded functions
+// and polymorphic functions, instead of being a replacement for them. To remedy
+// the "#add_match" syntax, some work has gone into the parser to make the following
+// legal syntax:
+
+#match hash.to_u32 (x: Vec2) -> u32 { /* .. */ }
+
+// This feels a lot nicer to write and doesn't have the weird line noise of the
+// _ and the ,.
+
+
+
+ValidKey :: interface (T: type_expr) {
+ hash.to_u32(T);
+ T == T;
+}
+
+
+// I think that interfaces should be a little bit more powerful than they are now.
+
+Hashable :: interface (T: type_expr) {
+
+ // This syntax seems pretty clean, but it introduces some complications when
+ // you actually think about using the type T in the expected type expression.
+ { hash.to_u32(T) } -> u32;
+}
+
+Ring :: interface (T: type_expr) {
+ // This syntax is very clean and easy to read; however, when you think about
+ // it for a second, the meaning of T becomes ambiguous...
+ // In the {} T is a value of type T, while in the type expression T is a type.
+ // Is this okay? Or should you have to say `typeof T` in the type expression?
+ // Or should the syntax for interfaces change?
+
+ { T + T } -> T;
+ { T - T } -> T;
+ { T * T } -> T;
+}
+
+Ring :: interface (t: $T) {
+ // This syntax is slightly better, as it makes clear where the value is being
+ // used and where the type is being used.
+
+ // They polymorphic variable "$T" is just for syntactic consistency. The "$" will
+ // always be expected before types in interfaces. Also, all types should just be
+ // a single symbol.
+
+ { t + t } -> T;
+ { t - t } -> T;
+ { t * t } -> T;
+
+ //
+ // The only problem I have with this syntax is that it is a little confusing when
+ // using the constraint syntax:
+ //
+ // foo :: (x, y: $T) -> T where Ring(T) {
+ // return x + y - x * y;
+ // }
+ //
+ // Because you are "calling" Ring with a type, not a value of type T. This might
+ // be worth giving up the syntactic consistency tho, just to avoid confusion about
+ // if T is a type or a value.
+ //
+}
--- /dev/null
+Link Options for Onyx
+===
+
+## Preface
+
+As Onyx compiles to WebAssembly, a sophisticated link-phase is not
+necessary. That is why, up until a week ago, Onyx did not have a way
+to specify any linking options, as Onyx could determine what it wants
+to do with the linear memory space. However, as I am trying to use
+Onyx with more things, I'm realizing that other WebAssembly runtimes
+are a bit stricter, and expect things to be in a particular way.
+
+Specifically, I was looking at the WASM4 "game engine" as something
+interesting Onyx could target. As WASM4 is trying to be as restrictive
+as possible to increase creativity, the memory layout for the program
+is defined. This means that Onyx's default layout will not suffice.
+Instead, you need to be able to control where the stack and data section
+elements go in the program. I have recently renovated the code that
+determines where a piece of data will be placed, as well as added a
+"link-phase" to update all references in the program to the data section
+element. Now I need to determine what options you will be allowed to
+control and what the syntax / semantics / method of communication will
+be. A good reference for which options should be supported is
+[wasm-ld](https://lld.llvm.org/WebAssembly.html)
+
+Because I am trying to stay away from a ton of command line options,
+especially options that *required* for your program to compile and work
+correctly, I would like specifying link options to be contained in the
+syntax of the program. Command-line options should be reserved for
+changing meta-level parameters about the program, such as the runtime
+and whether or not to disable features based on where the program is
+being compiled. *Fundamental* options, such as how to lay out the data
+section, should be within the program.
+
+## Proposal
+
+That being said, I think the syntax should like so:
+
+```onyx
+
+#link_options .{
+ .stack_size = 4096, // 1MiB
+ .stack_alignment = 16, // Align the start of the stack to 16 bytes
+ .stack_first = true, // Stack-before data section
+
+ .null_reserve_size = 16, // Reserve 16 bytes for null
+
+ .import_memory = true,
+ .import_memory_module_name = "onyx",
+ .import_memory_import_name = "memory",
+
+ .memory_min_size = 16, // 16 * 65536 Bytes
+ .memory_max_size = 24, // 24 * 65536 Bytes
+}
+
+```
+
+There will only be one `#link_options` directive in the entire set
+of included files. After it, it takes an expression that is of type
+`runtime.Link_Options`, which is type infered, as seen above. All
+members of this structure will have default values given by the settings
+that the program is compiling under. If no `#link_options` is provided,
+these default values are used.
+
+Alternatively, there could just be a optional variable in
+`package runtime.vars` that would define the link options. And if
+one is not specified than a definition in the standard library
+would define it like so
+
+```onyx
+package runtime.vars
+
+#if !#defined(link_options) {
+ link_options :: runtime.Link_Options.{}
+}
+
+```
+
+This would simplify a lot, as there would not have to be any other logic
+to deduplicate multiple `#link_options`. The only inconvience is that
+it will have to be part of a separate package, which currently means
+a separate file. That is a separate issue that will hopefully be tackled
+later.
--- /dev/null
+Mapped Directories
+==================
\ No newline at end of file
--- /dev/null
+Modules in Onyx
+---------------
+
+Modules are going to be the form of reusable code in Onyx. They should be a
+self-contained (maybe dependent on other modules), collection of Onyx files
+with a module.onyx in the root folder that loads all relevant files to the
+module. Modules can then easily be included by loading the module.onyx file
+of any module.
+
+Things that need to be decided about modules:
+ - How should modules be searched for in the filesystem?
+ - How should javascript code in modules be easily maintained?
+ Current solutions just copy the relevant javascript on build.
+
+Things that need to be done in the compiler:
+ - Rename 'package' to 'namespace'
+ I think 'namespace' better reflects the way I think about 'packages'.
+ I don't want it to feel too 'C++-y' but I think the nomenclature change is worthwhile.
+
+ 'package' was orginally inspired by Java, but there the folder structure must match
+ the package structure. In Onyx, that is not the case because it allows for packages
+ to be extended on without needing to modify the source of the original package.
+
+ - Add relative file loads and relative #file_contents.
+ If the string starts with './', it should be considered relative.
+ This will make it easier for modules to include their files, regardless of
+ how the project is set up.
+
+ ✔ This has been done
--- /dev/null
+Pluggable Modules
+-----------------
+
+
+After using the 'onyx run' functionality for a while, I've come to the realization
+that maybe pushing that feature farther may be the future of Onyx. Onyx can still
+target WebAssembly and be used for performance based web-applications. But using
+libwasmer and providing a way to do "pluggable modules" gives me the ability to use
+Onyx more natively. I'm basing this functionality on Lua's approach to C interop,
+which does seem a little cumbersome, but also is easy enough to understand and
+program for.
+
+
+The canonical name for this with be a "library", where "module" is reserved for a
+collection of "packages". A library is included in Onyx using the following syntax:
+
+ // Includes "some_useful_code.so / .dll" in this execution.
+ // Currently, this will only be allowed with the "onyx" runtime.
+ #library "some_useful_code"
+
+ // #library_path can be used to add to the places that libraries are
+ // searched for.
+ #if runtime.OS == runtime.OS_Linux {
+ #library_path "./libs/linux"
+ } else {
+ #library_path ".\libs\windows"
+ }
+
+Libraries will be shared objects files or dlls, depending on the OS. Hopefully, that
+is the only change that has to be added to the Onyx language itself. Everything else
+will be handled behind the scenes from the programmer.
+
+The "behinds the scenes" stuff is as follows:
+ - Locate and load the library using dlopen or LoadLibrary.
+ - Lookup the predefined function that will return a pointer to the table describing
+ the provided functions.
+ - During the module linking phase (aka building the import table), these functions
+ will be considered as options.
+
+
+
+
+A typical Onyx file for a library will look like:
+
+ package some_useful_code
+
+ #library "some_useful_code"
+
+ the_code :: (a, b: i32) -> i32 #foreign "some_useful_code" "the_code" ---
+
+
+The corresponding C file that will be compiled to a so/dll looks like:
+
+ #include "onyx_module.h"
+
+ #define ONYX_LIBRARY_NAME some_useful_code
+
+ ONYX_DEF(the_code, (I32, I32), (I32)) {
+ i32 a = params->data[0].of.i32;
+ i32 b = params->data[1].of.i32;
+ results->data[0] = WASM_I32_VAL(a + b);
+ return NULL;
+ }
+
+ ONYX_LIBRARY {
+ ONYX_FUNC(the_code)
+ }
+
+Compiling the C file with:
+
+ gcc -o some_useful_code.so -shared -fPIC some_useful_code.c -I ...
+
+
+A couple of questions that need to be answered:
+ - How is the WASM memory object going to be given to the shared object
+ code? Can it just be a public symbol that gets linked against? or
+ does it need to be passed to an initialization function?
\ No newline at end of file
--- /dev/null
+This file discusses what Onyx will look like in its shipped form, on Windows, MacOS and Linux.
+
+Windows
+---------------------
+Things that are needed:
+ * onyx.exe exists in the path
+ * onyx.exe knows where the core modules are installed (%APPDATA%\Local ?)
+
+MacOS
+---------------------
+I know nothing about shipping portable things on MacOS...
+
+Linux
+---------------------
+The way that build.sh installs Onyx on Linux is pretty close to the long term solution. That being,
+copying the core library to /usr/share/onyx/core and the necessary dependencies (libwasmer at the
+moment) to /usr/share/onyx/lib, and the executable to /usr/bin/onyx. This feels pretty intact with
+how most things are shipped on Linux. The only change I would have would be to copy things to
+/usr/local/... instead of /usr/... just to avoid possible conflicts.
+
--- /dev/null
+Rewriting Structure Assembly Code
+=================================
+
+Current in the Onyx code generator, structures r-values are represented
+in the execution stack as each of their individual primitive memebers.
+This solution has proved relatively easy to implement, but takes A LOT
+of extra instructions compared to the alternative.
+
+I would like to rewrite the way structure r-values are handled in the
+assembly to the following: all structure r-values are stored in the stack
+(or somewhere in memory, but probably the stack), and the execution stack
+simply contains a pointer to that address. This would allow for a structure
+load/store operation to simply be replaced by a COPY, instead of the potentially
+hundreds of instructions that it takes now.
+
+
+The largest issue that I have thought of so far with making this change
+is a flaw in how fixed-size arrays are being handled, which in turn would
+be a flaw with new structure r-values as well. This code example show it:
+
+ main :: () {
+ r := array_2(array_1(.[6, 7, 8, 9, 10]));
+ println(r);
+ }
+
+ array_1 :: (x: [5] u32) -> [5] u32 {
+ return .[ 1, 2, 3, 4, 5 ];
+ }
+
+ array_2 :: (x: [5] u32) -> [5] u32 {
+ y: [5] u32;
+ for^ y do *it = 1337;
+
+ printf("{} == {}\n", ^y, cast(^u32) x);
+
+ return x;
+ }
+
+Here, you would expect the print in `main` to be "[1, 2, 3, 4, 5]". However,
+because of a bug with where arrays are stored, you actually see "[1337, 1337, ...]".
+This bug is that because array literals are living in the stack space of the
+current function, returning then is the same thing as returning a pointer to
+a local variable. This works in the easiest case as show below, because the
+array is copied immediately into the result variable, which lives in `main`s
+stack, but not when anything more complicated is going on:
+
+ main :: () {
+ result := array_1(.[]);
+ println(result);
+ }
+
+This bug is easy to get around when working with fixed-size arrays, which is
+something I do rarely anyway. However, structures are different story. Chaining
+calls that return structures is done every where in Onyx programs. Just take the
+`core.iter` package as an example.
+
+ main :: () {
+ for iter.as_iter(1 .. 20)
+ |> iter.map(x => x * 2)
+ |> iter.filter(x => x > 4)
+ |> iter.take(5) {
+
+ println(it);
+ }
+ }
+
+In this example, each of the return values of those functions is an `Iterator(i32)`,
+which is obviously a structure. In the new system, passing each result as a paramter
+to the next function would not work as shown with the array example above. A solution
+to this problem must be found before this refactor can be completed.
+
+
+
+
+
+SOLUTION
+========
+
+I was very dumb when I wrote the above text.
+
+This is actually very simple, and already something that the compiler is doing. I am
+just doing it wrong in the fixed-size array case. The rule has to be: ALL structure
+arguments need to be copied into a buffer for ARGUMENTS to the call. This already
+happens with non-simple structure. Currently, it does not happen for array, so that
+is why I thought this was a larger issue. The optimization that l-value structures
+passed by value can simply be their pointer and not-copied can be done in the future,
+but to get this optimization working, that does not have to be done.
+++ /dev/null
-// Currently the only way for something to be generic and type checked
-// in Onyx is to use polymorphic procedures and overloaded procedures
-// to allow a procedure to be statically duck-typed after it has been
-// determined that this is the procedure you are calling (think Map
-// and hash.to_u32). This has been okay, but it leads to some grossness
-// in the language semantics and the kinds of error messages that are
-// producable.
-//
-// For example, it is not immediately clear that in order to make a Map
-// or a Set out of a type, you need to declare a match option for hash.to_u32
-// and define '==' for the type. This is kind of probablimatic and doesn't
-// make for great self explanatory or maintainable code. Also I don't like
-// #match at all, as it feels like a complete and utter hack.
-//
-
-// Ignore everything that is said below. It all sounded like a good idea
-// at one point, until I started implementing it when I realized that not
-// only is it WAY too complicated and adds too much complexity in the compiler
-// but it doesn't feel like a natural extention to Onyx.
-//
-// Instead, 'interfaces' are going to be much much simpler, but to write
-// and for the compiler to deal with. Effectively, they are compile time checks
-// that assert that a particular set of expressions is valid, and optionally
-// return the correct type. They are offered as an extension to overloaded functions
-// and polymorphic functions, instead of being a replacement for them. To remedy
-// the "#add_match" syntax, some work has gone into the parser to make the following
-// legal syntax:
-
-#match hash.to_u32 (x: Vec2) -> u32 { /* .. */ }
-
-// This feels a lot nicer to write and doesn't have the weird line noise of the
-// _ and the ,.
-
-
-
-ValidKey :: interface (T: type_expr) {
- hash.to_u32(T);
- T == T;
-}
-
-
-// I think that interfaces should be a little bit more powerful than they are now.
-
-Hashable :: interface (T: type_expr) {
-
- // This syntax seems pretty clean, but it introduces some complications when
- // you actually think about using the type T in the expected type expression.
- { hash.to_u32(T) } -> u32;
-}
-
-Ring :: interface (T: type_expr) {
- // This syntax is very clean and easy to read; however, when you think about
- // it for a second, the meaning of T becomes ambiguous...
- // In the {} T is a value of type T, while in the type expression T is a type.
- // Is this okay? Or should you have to say `typeof T` in the type expression?
- // Or should the syntax for interfaces change?
-
- { T + T } -> T;
- { T - T } -> T;
- { T * T } -> T;
-}
-
-Ring :: interface (t: $T) {
- // This syntax is slightly better, as it makes clear where the value is being
- // used and where the type is being used.
-
- // They polymorphic variable "$T" is just for syntactic consistency. The "$" will
- // always be expected before types in interfaces. Also, all types should just be
- // a single symbol.
-
- { t + t } -> T;
- { t - t } -> T;
- { t * t } -> T;
-
- //
- // The only problem I have with this syntax is that it is a little confusing when
- // using the constraint syntax:
- //
- // foo :: (x, y: $T) -> T where Ring(T) {
- // return x + y - x * y;
- // }
- //
- // Because you are "calling" Ring with a type, not a value of type T. This might
- // be worth giving up the syntactic consistency tho, just to avoid confusion about
- // if T is a type or a value.
- //
-}
+++ /dev/null
-Link Options for Onyx
-===
-
-## Preface
-
-As Onyx compiles to WebAssembly, a sophisticated link-phase is not
-necessary. That is why, up until a week ago, Onyx did not have a way
-to specify any linking options, as Onyx could determine what it wants
-to do with the linear memory space. However, as I am trying to use
-Onyx with more things, I'm realizing that other WebAssembly runtimes
-are a bit stricter, and expect things to be in a particular way.
-
-Specifically, I was looking at the WASM4 "game engine" as something
-interesting Onyx could target. As WASM4 is trying to be as restrictive
-as possible to increase creativity, the memory layout for the program
-is defined. This means that Onyx's default layout will not suffice.
-Instead, you need to be able to control where the stack and data section
-elements go in the program. I have recently renovated the code that
-determines where a piece of data will be placed, as well as added a
-"link-phase" to update all references in the program to the data section
-element. Now I need to determine what options you will be allowed to
-control and what the syntax / semantics / method of communication will
-be. A good reference for which options should be supported is
-[wasm-ld](https://lld.llvm.org/WebAssembly.html)
-
-Because I am trying to stay away from a ton of command line options,
-especially options that *required* for your program to compile and work
-correctly, I would like specifying link options to be contained in the
-syntax of the program. Command-line options should be reserved for
-changing meta-level parameters about the program, such as the runtime
-and whether or not to disable features based on where the program is
-being compiled. *Fundamental* options, such as how to lay out the data
-section, should be within the program.
-
-## Proposal
-
-That being said, I think the syntax should like so:
-
-```onyx
-
-#link_options .{
- .stack_size = 4096, // 1MiB
- .stack_alignment = 16, // Align the start of the stack to 16 bytes
- .stack_first = true, // Stack-before data section
-
- .null_reserve_size = 16, // Reserve 16 bytes for null
-
- .import_memory = true,
- .import_memory_module_name = "onyx",
- .import_memory_import_name = "memory",
-
- .memory_min_size = 16, // 16 * 65536 Bytes
- .memory_max_size = 24, // 24 * 65536 Bytes
-}
-
-```
-
-There will only be one `#link_options` directive in the entire set
-of included files. After it, it takes an expression that is of type
-`runtime.Link_Options`, which is type infered, as seen above. All
-members of this structure will have default values given by the settings
-that the program is compiling under. If no `#link_options` is provided,
-these default values are used.
-
-Alternatively, there could just be a optional variable in
-`package runtime.vars` that would define the link options. And if,
-one is not specified than a definition in the standard library
-would define it like so
-
-```onyx
-package runtime.vars
-
-#if !#defined(link_options) {
- link_options :: runtime.Link_Options.{}
-}
-
-```
-
-This would simplify a lot, as there would not have to be any other logic
-to deduplicate multiple `#link_options`. The only inconvience is that
-it will have to be part of a separate package, which currently means
-a separate file. That is a separate issue that will hopefully be tackled
-later.
+++ /dev/null
-Modules in Onyx
----------------
-
-Modules are going to be the form of reusable code in Onyx. They should be a
-self-contained (maybe dependent on other modules), collection of Onyx files
-with a module.onyx in the root folder that loads all relevant files to the
-module. Modules can then easily be included by loading the module.onyx file
-of any module.
-
-Things that need to be decided about modules:
- - How should modules be searched for in the filesystem?
- - How should javascript code in modules be easily maintained?
- Current solutions just copy the relevant javascript on build.
-
-Things that need to be done in the compiler:
- - Rename 'package' to 'namespace'
- I think 'namespace' better reflects the way I think about 'packages'.
- I don't want it to feel too 'C++-y' but I think the nomenclature change is worthwhile.
-
- 'package' was orginally inspired by Java, but there the folder structure must match
- the package structure. In Onyx, that is not the case because it allows for packages
- to be extended on without needing to modify the source of the original package.
-
- - Add relative file loads and relative #file_contents.
- If the string starts with './', it should be considered relative.
- This will make it easier for modules to include their files, regardless of
- how the project is set up.
-
- ✔ This has been done
--- /dev/null
+The following things are defined in the `builtin` package, which is implicitly
+used in every file. Therefore, these things are effectively like "globals". Also,
+you can add your own things to the `builtin` package to define your own "globals"
+for your project.
+
+- `str` -
+This is the builtin string type, which is just a slice of bytes.
+
+- `cstr` -
+This is the builtin cstring type, used in every program as the type of the command line arguments given to main.
+
+- `range` -
+This is the type of a range literal, i.e. 1 .. 5. You can also use this type in a for-loop to iterate over the values.
+
+- `vararg` -
+This is a deprecated type used when you have an untyped variadic procedure, i.e. x :: (va: ...). This is deprecated because of the 'any' type.
+
+- `vararg_get` -
+This is also deprecated but it allowed you get values out of a `vararg` type.
+
+- `null_proc` -
+This is a special procedure that always matches any procedure type, regardless of whether the types actually match. This allows you to use it as a placeholder or "null" value when a procedure is expected.
+
+- `null` -
+Much like every other systems-ish programming language, Onyx has a "null" type. In Onyx, it is nothing more than a 0 value casted to a `rawptr`.
+
+- `null_str` -
+A helpful constant that is a string with the data being null and the count being 0. Useful if you want to have a blank string value. It is different from "", because the latter will still be assigned a data location, even through there will not be any data there.
+
+- `OnyxContext` -
+The builtin type of the `context`, described below. In theory, this type could exist at file scope in builtin.onyx, but I decided to leave it exposed.
+
+- `context` -
+This is a thread-local variable that stores the active allocators, loggers, and assert_handler for the current thread.
+
+- `assert` -
+A function that asserts a certain condition is true; if not, it calls the assert handler on the `context`.
+
+- `Logger` -
+The type used for the logger on the `context`.
+
+- `log` -
+Logs a message to the `context.logger`. Currently, this does not support formatted arguments, but that is planned in the near future.
+
+- `AllocationAction` -
+A simple enum that represent what action is to be taken when calling an allocator_proc.
+
+- `Allocator` -
+This type represents something that can allocate memory. It has two members, `data` and `func`. The `func` is an `allocator_proc`, which takes the data, the allocation action, size, alignment and old pointer. Not all of these values are used for every action, but the allocator procedure must consume all of them.
+
+- `raw_alloc` `raw_resize` `raw_free` -
+These functions are simply wrappers provided to invoke an `Allocator` with a specific action.
+
+- `calloc` `cresize` `cfree` -
+These functions simply call `raw_alloc`, `raw_resize` and `raw_free` using `context.allocator` as the allocator. These are effectively the equivalent of `malloc`, `realloc`, and `free` in C.
+
+- `new` -
+This function allocates enough space for the given type from the given allocator, which by default is the context's allocator. It also initializes the resulting memory, which uses the `__initialize` intrinsic that initializes all types, but most importantly it evalutes the default values for struct members.
+
+- `make` -
+This function is like new, except it does not initialize the memory.
+
+- `Iterator` -
+This type represents a custom iterator that can be iterated over using a `for` loop. It has three members. `data`, a simple pointer passed to the other functions. `next` which provides the next value for the iterator, and a continuation boolean. If the boolean is false, the iterator is done and the value given is garbage. And finally `close`, which is optional and allows you to run code when the iterator is done being used. When using a `for` loop, `close` is called automatically for you when the loop exits in any way.
+
+- `CallSite` -
+This type represents the value given by the `#callsite` expression. In practice, you will never use this type because you will just do something like: `f :: (site := #callsite)`
+
+- `any` -
+This type represents any value. It does this by using a data pointer and a type expression. Using the `runtime.info` package, you can introspect that type expression to retrieve data about the type, and from there reconstruct how to use the data pointer. See `conv.onyx` for how this works with `printf`.
+
+- `Code` -
+This is a dummy type that represents the type of a `#unquote {}` block. It is used when passing code around to macros or procedures, i.e. `f :: macro (body: Code)`
\ No newline at end of file
--- /dev/null
+The ONYX Programming Language
+-----------------------------
+
+WHAT:
+ ONYX is a low-ish level programming language designed for use with
+ Web-Assembly 32-bit (WASM). It features some advanced features such
+ as comptime code execution and JS literals for external functions.
+
+WHY:
+ ONYX was made to help me learn about compiler design.
+
+END GOAL:
+ ONYX will be used to make a simple-ish game for the browser that leverages
+ WASM and WebGL for a performant experience. Language design will reflect the
+ needs of the game programming.
+
+FEATURES:
+ - Strong type system
+ - functions (no anonymous functions)
+ - Structs and enums
+ - Control structures
+ if, for, switch
+ - pointers
+ - inferred typing
+ - Smart package loading
+ - defer
+ - polymorphic functions
+
+HOW:
+ Currently there is a multi-phase development process since implementing everything
+ at once would be overwhelming and unsatisfying. The current progress of each stage:
+
+ Stage 3:
+ I have a working compiler with many features, but here are some additional features
+ I would like to consider adding in the future.
+
+ [X] Put type info in data section so it is runtime accessible
+ - type name
+ - size
+ - alignment
+ - struct member names
+ - array length
+
+ [X] baked parameters
+ - Compile time known parameters
+ - Removes the argument from the list and replaces the function with the
+ baked function
+
+ [X] Add threading intrinsics
+ - This will actually be fairly easy since I think all that is needed is
+ to implement the intrinsics.
+ - ^^^ This is a false statement. I also need to have 'thread local variables' for
+ stack pointers and separate stack allocation in the linear memory for each
+ of the threads.
+
+ [X] Array literals
+
+ [ ] transmute
+
+ [X] look into creating a source map
+ - first-look looks really gross
+ - whoever came up with the source map spec should be fired... why are people so afraid of binary files??
+ - DWARF looks like it might be easier, but it still doesn't look fun.
+
+ [ ] convert to using an 'atom' like table
+ - All identifier tokens are given a unique atom ptr, up to string equality.
+ - This means identifiers can be compared using ptr comparison, instead of string comparison
+ - This mean no more token_toggle_end!! Woo!!
+
+ [X] 'when' statements
+ - Compile time conditions
+ - Only evalutate code blocks that evaluate to be true
+
+ [X] multiple lvals and compound assignment
+ a := 2
+ b := 5
+ a, b = b, a;
+
+ [ ] All code paths return correct value
+
+ [X] Better checking for casts
+ - Checking which things are allowed to cast to/from should be checked in the checker,
+ not in the wasm generatation
+
+ [X] Interop with C
+ - Difficult feature
+ - Would be nice to use existing C code (such as stb headers)
+ - Some way to make this happen without needing a full C compiler would be nice
+
+ Stage 2:
+ [X] Order of symbol declaration is irrelevant
+ Either:
+ make a graph of symbol dependencies and produce a schedule on the graph
+ that would allow for all symbols to be resolved
+
+ OR
+
+ Do as many passes on the parse tree as needed to resolve all symbols.
+ This could be slow but it would be easier than creating a graph
+ scheduling algorithm.
+
+ [X] Consequence of the above, recursion works
+
+ [X] Better compiler interface
+ - Proper command line options
+ - Compiling multiple files at once
+ - Changing output location
+ - Viewing help screen
+
+ [X] 'use' statements work
+ - Adds '.onyx' to the end of the file name list
+ - Only searches in current directory for now
+
+ [X] Completely overhaul the type system
+ - Keep the builtins
+ - Add pointers
+ - Add structs
+
+ [X] Output 'drop' instruction for functions whose return value isn't used
+
+ [X] Strings should work as pointers to data.
+ - Literals should be placed in data section with pointers to the start.
+ - Should strings be null-terminated or a length at the start of the string?
+
+ [X] Struct splatting in arguments and parameters
+
+ [X] UFC syntax for structs
+
+ [X] Logical boolean operators
+
+ [X] Bitwise operators
+
+ [X] Dead code elimination
+ - Start with uncalled functions being removed
+ - Foreign functions will rename in the code because it is turning out
+ to be a nightmare to remove them. Lot's of refactoring... ugh
+
+ [X] Package system
+
+ [X] Enum types
+
+ [X] Static pointers to sized data
+
+ [X] 'using' parameters
+ - The following example will bring the members of the struct into the scope as field accesses
+ and allow for a more OO style programming, without diving into the crap that is OO
+
+ foo :: proc (use data: ^Data, other_arg: i32) {
+ member1_of_data = other_arg;
+ bar(member2_of_data);
+ }
+
+ [X] Procedures as arguments
+
+ [X] Deferred statements
+
+ [X] Pointer math
+ - Addition and subtraction
+
+ [X] #package
+ - symbol is scoped to package and not brought in from a 'use package' statement
+
+ [X] Hex literals
+
+ [X] #file_contents
+
+ [X] Convert to using a proper stack based system
+
+ [X] Be smart about when to use the stack versus use the wasm locals
+
+ [X] Better numeric literals
+ - suffix 'f' for float32
+ - no suffix 'f', but has decimal for float64
+ - suffix 'l' for int64
+ - nothing special for i32 or below
+
+ [X] Char literals
+
+ [X] Properly checking binary operators
+ - Shouldn't be able to add two structs/arrays together
+
+ [X] Include other directories to search for files
+
+ [X] remove struct splatting at parameters
+ - structs can still be passed by value however
+ - removed the implicit splatting feature
+
+ [X] package builtin
+ - Place to store builtin types and values
+ __heap_start
+ __stack_top
+ etc
+
+ [X] local variable allocator
+
+ [X] Struct literals
+ X All members specified means names not required
+ X Named member initialization
+ X Default values on structs so they don't have to be named
+
+ [X] #union on structs
+
+ [X] #align on structs
+
+ [X] #size on structs
+
+ [X] multiple return value
+ - Returning on the stack
+
+ [X] returning structs
+ - This will put forward a lot of the work that will be done for multiple return values
+
+ [X] intializers in if / while
+ if err := some_function(...); err != 0 {
+ print(err);
+ }
+
+ [X] else on while
+ while ... {
+
+ } else {
+ // Loop never run
+ }
+
+ [X] Add slices
+ - Arrays without a size
+ - Converted to a struct that looks like:
+ []T :: struct {
+ data : ^T;
+ count : u32;
+ }
+
+ [X] Switch statements
+
+ [X] fallthrough on cases in switch statements
+
+ [X] initializers on switch statements
+
+ [X] default parameters
+ - Must be the last parameters
+
+ [X] Basic documentation outputter
+
+ [X] Rewrite error reporting system
+ - don't sort errors
+ - add infos to warnings
+ - no more preformatted strings, just write them inline ffs
+
+ [X] #file and #line directives
+ - string and u32 respectively that represent the current file and line number where the directive is
+
+ [X] Generate a tags file
+ - This looks very simple
+ - I think all the infrastructure is there for this
+
+ [X] data structure based iteration
+ - Currently, I do not plan on having custom iterators. This may very well change in the near future.
+ - For now, a for loop will be reimagined to look like:
+ for val: iterable ...
+
+ - 'iterable' will be something of a type that the compiler knows how to iterate over:
+ * range
+ * array
+ * slice
+ * dynamic array
+
+ [X] Don't include foreign functions unless they're used
+ - Do multiple passes if needed
+ - Some APIs like WebGL have a ton of foreigns, and most of them aren't even used
+
+ [X] Variadic arguments
+
+ [X] Type parameterized structs
+
+ [X] Add SIMD intrinsics
+ - This also requires adding the v128 SIMD type
+
+ [X] 'use' enums and packages at an arbitrary scope
+
+ [X] Make the lexer much faster
+ - Technically it isn't slow right now
+ - But, profiling says we are spending 50% of the program execution time in the lexer
+ - That is awful
+
+ [X] Top level variable initialization
+ - Works for numeric literals
+
+
+ Stage 1 (MVP):
+ [X] Can declare procedures
+ [X] Procedures have params and returns of the following types:
+ - i32, u32
+ - i64, u64
+ - f32, f64
+ [X] Procedures have locals of the same set of types
+ [X] Locals are declared in the following way
+ local : (type) ((= or :) initial value);
+
+ if : is used, the value is unmodifiable
+ if type is specified, that is assumed to be the correct type
+ if type is not specified, the type of initial value is used as the type
+
+ [X] Five basic math operations are legal:
+ + - * / %
+ [X] Math operations are sign aware and only operate on operands of the same type
+ [X] All casts are explicit using this syntax:
+ X as T
+
+ casts X to type T
+
+ [X] Curly braces are required for all bodies of blocks
+ [X] Numeric literals are parsed
+ [X] Numeric literals have the minimum type detected
+ [X] Foreign imports (functions only)
+ [X] Comparison operators
+ [X] Proper boolean type
+ [X] Conditional branching works as expected
+ [X] Simple while loop is functioning as expected
+ [X] break and continue semantics
+ [X] Function calling works for the builtin types
+ [X] Function return values are type checked
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+
+ Onyx Language Specification
+
+ Brendan Hansen
+
+ This is the most formal specification of the Onyx programming language. At the time of writing, most
+ of the design and functionality of Onyx is entirely in my head and I am already beginning to forget some
+ of the features of the language.
+
+ How to read this document
+
+ To easily navigate this document, tags have been placed for your editor to easily find. If you see
+ #foo somewhere, you can search for [foo] which will be the place where foo is explained.
+
+ Some common abbrevations used in this paper
+ LHS for "Left Hand Side"
+ RHS for "Right Hand Side"
+
+ Language Features
+ Grammar.................................................................Line xxx
+ Operator Precedence.....................................................#op-pred
+ Operators...............................................................Line xxx
+ Arithmetic Operators................................................Line xxx
+ Logical Operators...................................................#log-ops
+ Bitwise Operators...................................................Line xxx
+ Miscellaneous Operators.............................................Line xxx
+ Packages................................................................Line xxx
+
+
+
+ Operator Precendence
+ [op-pred]
+
+ The following are all the binary operators in Onyx in their precedence from highest to lowest:
+
+ +---+-------------------------------------------+
+ | 9 | % |
+ | 8 | * / |
+ | 7 | + - |
+ | 6 | & | ^ << >> >>> |
+ | 5 | <= < >= > |
+ | 4 | == != |
+ | 3 | && || |
+ | 2 | |> .. |
+ | 1 | = += -= *= /= %= &= |= ^= <<= >>= >>>= |
+ +---+-------------------------------------------+
+
+
+ The following are all the unary operators. Prefix unary operators are right associative and all
+ of equal precedence. Postfix unary operators are left associative and also all of equal precedence.
+
+ Prefix unary operators:
+ ! Boolean NOT
+ - Arithmetic negative
+ ~ Bitwise NOT
+ ^ Address of
+ * Dereference
+ cast(X) Cast to X
+ ~~ Auto cast
+
+ Postfix unary operators:
+ [N] Array subscript
+ .foo Field access
+ (..) Function call
+
+
+
+
+
+ Logical Operators
+ [log-ops]
+
+ There is only one unary logical operator, !. It negates the boolean value given to it.
+
+ Binary logical operators expect both the LHS and RHS to be of boolean type. The following are the logical
+ operators in Onyx:
+
+ && Logical AND
+ || Logical OR
+
+ All logical operators are of equal precedence. && and || are standard operators in many C-style languages.
+
+++ /dev/null
-The ONYX Programming Language
------------------------------
-
-WHAT:
- ONYX is a low-ish level programming language designed for use with
- Web-Assembly 32-bit (WASM). It features some advanced features such
- as comptime code execution and JS literals for external functions.
-
-WHY:
- ONYX was made to help me learn about compiler design.
-
-END GOAL:
- ONYX will be used to make a simple-ish game for the browser that leverages
- WASM and WebGL for a performant experience. Language design will reflect the
- needs of the game programming.
-
-FEATURES:
- - Strong type system
- - functions (no anonymous functions)
- - Structs and enums
- - Control structures
- if, for, switch
- - pointers
- - inferred typing
- - Smart package loading
- - defer
- - polymorphic functions
-
-HOW:
- Currently there is a multi-phase development process since implementing everything
- at once would be overwhelming and unsatisfying. The current progress of each stage:
-
- Stage 3:
- I have a working compiler with many features, but here are some additional features
- I would like to consider adding in the future.
-
- [X] Put type info in data section so it is runtime accessible
- - type name
- - size
- - alignment
- - struct member names
- - array length
-
- [X] baked parameters
- - Compile time known parameters
- - Removes the argument from the list and replaces the function with the
- baked function
-
- [X] Add threading intrinsics
- - This will actually be fairly easy since I think all that is needed is
- to implement the intrinsics.
- - ^^^ This is a false statement. I also need to have 'thread local variables' for
- stack pointers and separate stack allocation in the linear memory for each
- of the threads.
-
- [X] Array literals
-
- [ ] transmute
-
- [X] look into creating a source map
- - first-look looks really gross
- - whoever came up with the source map spec should be fired... why are people so afraid of binary files??
- - DWARF looks like it might be easier, but it still doesn't look fun.
-
- [ ] convert to using an 'atom' like table
- - All identifier tokens are given a unique atom ptr, up to string equality.
- - This means identifiers can be compared using ptr comparison, instead of string comparison
- - This mean no more token_toggle_end!! Woo!!
-
- [X] 'when' statements
- - Compile time conditions
- - Only evalutate code blocks that evaluate to be true
-
- [X] multiple lvals and compound assignment
- a := 2
- b := 5
- a, b = b, a;
-
- [ ] All code paths return correct value
-
- [X] Better checking for casts
- - Checking which things are allowed to cast to/from should be checked in the checker,
- not in the wasm generatation
-
- [X] Interop with C
- - Difficult feature
- - Would be nice to use existing C code (such as stb headers)
- - Some way to make this happen without needing a full C compiler would be nice
-
- Stage 2:
- [X] Order of symbol declaration is irrelevant
- Either:
- make a graph of symbol dependencies and produce a schedule on the graph
- that would allow for all symbols to be resolved
-
- OR
-
- Do as many passes on the parse tree as needed to resolve all symbols.
- This could be slow but it would be easier than creating a graph
- scheduling algorithm.
-
- [X] Consequence of the above, recursion works
-
- [X] Better compiler interface
- - Proper command line options
- - Compiling multiple files at once
- - Changing output location
- - Viewing help screen
-
- [X] 'use' statements work
- - Adds '.onyx' to the end of the file name list
- - Only searches in current directory for now
-
- [X] Completely overhaul the type system
- - Keep the builtins
- - Add pointers
- - Add structs
-
- [X] Output 'drop' instruction for functions whose return value isn't used
-
- [X] Strings should work as pointers to data.
- - Literals should be placed in data section with pointers to the start.
- - Should strings be null-terminated or a length at the start of the string?
-
- [X] Struct splatting in arguments and parameters
-
- [X] UFC syntax for structs
-
- [X] Logical boolean operators
-
- [X] Bitwise operators
-
- [X] Dead code elimination
- - Start with uncalled functions being removed
- - Foreign functions will rename in the code because it is turning out
- to be a nightmare to remove them. Lot's of refactoring... ugh
-
- [X] Package system
-
- [X] Enum types
-
- [X] Static pointers to sized data
-
- [X] 'using' parameters
- - The following example will bring the members of the struct into the scope as field accesses
- and allow for a more OO style programming, without diving into the crap that is OO
-
- foo :: proc (use data: ^Data, other_arg: i32) {
- member1_of_data = other_arg;
- bar(member2_of_data);
- }
-
- [X] Procedures as arguments
-
- [X] Deferred statements
-
- [X] Pointer math
- - Addition and subtraction
-
- [X] #package
- - symbol is scoped to package and not brought in from a 'use package' statement
-
- [X] Hex literals
-
- [X] #file_contents
-
- [X] Convert to using a proper stack based system
-
- [X] Be smart about when to use the stack versus use the wasm locals
-
- [X] Better numeric literals
- - suffix 'f' for float32
- - no suffix 'f', but has decimal for float64
- - suffix 'l' for int64
- - nothing special for i32 or below
-
- [X] Char literals
-
- [X] Properly checking binary operators
- - Shouldn't be able to add two structs/arrays together
-
- [X] Include other directories to search for files
-
- [X] remove struct splatting at parameters
- - structs can still be passed by value however
- - removed the implicit splatting feature
-
- [X] package builtin
- - Place to store builtin types and values
- __heap_start
- __stack_top
- etc
-
- [X] local variable allocator
-
- [X] Struct literals
- X All members specified means names not required
- X Named member initialization
- X Default values on structs so they don't have to be named
-
- [X] #union on structs
-
- [X] #align on structs
-
- [X] #size on structs
-
- [X] multiple return value
- - Returning on the stack
-
- [X] returning structs
- - This will put forward a lot of the work that will be done for multiple return values
-
- [X] intializers in if / while
- if err := some_function(...); err != 0 {
- print(err);
- }
-
- [X] else on while
- while ... {
-
- } else {
- // Loop never run
- }
-
- [X] Add slices
- - Arrays without a size
- - Converted to a struct that looks like:
- []T :: struct {
- data : ^T;
- count : u32;
- }
-
- [X] Switch statements
-
- [X] fallthrough on cases in switch statements
-
- [X] initializers on switch statements
-
- [X] default parameters
- - Must be the last parameters
-
- [X] Basic documentation outputter
-
- [X] Rewrite error reporting system
- - don't sort errors
- - add infos to warnings
- - no more preformatted strings, just write them inline ffs
-
- [X] #file and #line directives
- - string and u32 respectively that represent the current file and line number where the directive is
-
- [X] Generate a tags file
- - This looks very simple
- - I think all the infrastructure is there for this
-
- [X] data structure based iteration
- - Currently, I do not plan on having custom iterators. This may very well change in the near future.
- - For now, a for loop will be reimagined to look like:
- for val: iterable ...
-
- - 'iterable' will be something of a type that the compiler knows how to iterate over:
- * range
- * array
- * slice
- * dynamic array
-
- [X] Don't include foreign functions unless they're used
- - Do multiple passes if needed
- - Some APIs like WebGL have a ton of foreigns, and most of them aren't even used
-
- [X] Variadic arguments
-
- [X] Type parameterized structs
-
- [X] Add SIMD intrinsics
- - This also requires adding the v128 SIMD type
-
- [X] 'use' enums and packages at an arbitrary scope
-
- [X] Make the lexer much faster
- - Technically it isn't slow right now
- - But, profiling says we are spending 50% of the program execution time in the lexer
- - That is awful
-
- [X] Top level variable initialization
- - Works for numeric literals
-
-
- Stage 1 (MVP):
- [X] Can declare procedures
- [X] Procedures have params and returns of the following types:
- - i32, u32
- - i64, u64
- - f32, f64
- [X] Procedures have locals of the same set of types
- [X] Locals are declared in the following way
- local : (type) ((= or :) initial value);
-
- if : is used, the value is unmodifiable
- if type is specified, that is assumed to be the correct type
- if type is not specified, the type of initial value is used as the type
-
- [X] Five basic math operations are legal:
- + - * / %
- [X] Math operations are sign aware and only operate on operands of the same type
- [X] All casts are explicit using this syntax:
- X as T
-
- casts X to type T
-
- [X] Curly braces are required for all bodies of blocks
- [X] Numeric literals are parsed
- [X] Numeric literals have the minimum type detected
- [X] Foreign imports (functions only)
- [X] Comparison operators
- [X] Proper boolean type
- [X] Conditional branching works as expected
- [X] Simple while loop is functioning as expected
- [X] break and continue semantics
- [X] Function calling works for the builtin types
- [X] Function return values are type checked
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+++ /dev/null
-Pluggable Modules
------------------
-
-
-After using the 'onyx run' functionality for a while, I've come to the realization
-that maybe pushing that feature farther may be the future of Onyx. Onyx can still
-target WebAssembly and be used for performance based web-applications. But using
-libwasmer and providing a way to do "pluggable modules" gives me the ability to use
-Onyx more natively. I'm basing this functionality on Lua's approach to C interop,
-which does seem a little cumbersome, but also is easy enough to understand and
-program for.
-
-
-The canonical name for this with be a "library", where "module" is reserved for a
-collection of "packages". A library is included in Onyx using the following syntax:
-
- // Includes "some_useful_code.so / .dll" in this execution.
- // Currently, this will only be allowed with the "onyx" runtime.
- #library "some_useful_code"
-
- // #library_path can be used to add to the places that libraries are
- // searched for.
- #if runtime.OS == runtime.OS_Linux {
- #library_path "./libs/linux"
- } else {
- #library_path ".\libs\windows"
- }
-
-Libraries will be shared objects files or dlls, depending on the OS. Hopefully, that
-is the only change that has to be added to the Onyx language itself. Everything else
-will be handled behind the scenes from the programmer.
-
-The "behinds the scenes" stuff is as follows:
- - Locate and load the library using dlopen or LoadLibrary.
- - Lookup the predefined function that will return a pointer to the table describing
- the provided functions.
- - During the module linking phase (aka building the import table), these functions
- will be considered as options.
-
-
-
-
-A typical Onyx file for a library will look like:
-
- package some_useful_code
-
- #library "some_useful_code"
-
- the_code :: (a, b: i32) -> i32 #foreign "some_useful_code" "the_code" ---
-
-
-The corresponding C file that will be compiled to a so/dll looks like:
-
- #include "onyx_module.h"
-
- #define ONYX_LIBRARY_NAME some_useful_code
-
- ONYX_DEF(the_code, (I32, I32), (I32)) {
- i32 a = params->data[0].of.i32;
- i32 b = params->data[1].of.i32;
- results->data[0] = WASM_I32_VAL(a + b);
- return NULL;
- }
-
- ONYX_LIBRARY {
- ONYX_FUNC(the_code)
- }
-
-Compiling the C file with:
-
- gcc -o some_useful_code.so -shared -fPIC some_useful_code.c -I ...
-
-
-A couple of questions that need to be answered:
- - How is the WASM memory object going to be given to the shared object
- code? Can it just be a public symbol that gets linked against? or
- does it need to be passed to an initialization function?
\ No newline at end of file
+++ /dev/null
-This file discusses what Onyx will look like in its shipped form, on Windows, MacOS and Linux.
-
-Windows ---------------------------------------------------------
-Things that are needed:
- * onyx.exe exists in the path
- * onyx.exe knows where the core modules are installed (%APPDATA%\Local ?)
-
-MacOS ---------------------------------------------------------
-I know nothing about shipping portable things on MacOS...
-
-Linux ---------------------------------------------------------
-The way that build.sh installs Onyx on Linux is pretty close to the long term solution. That being,
-copying the core library to /usr/share/onyx/core and the necessary dependencies (libwasmer at the
-moment) to /usr/share/onyx/lib, and the executable to /usr/bin/onyx. This feels pretty intact with
-how most things are shipped on Linux. The only change I would have would be to copy things to
-/usr/local/... instead of /usr/... just to avoid possible conflicts.
-
+++ /dev/null
-
- Onyx Language Specification
-
- Brendan Hansen
-
- This is the most formal specification of the Onyx programming language. At the time of writing, most
- of the design and functionality of Onyx is entirely in my head and I am already beginning to forget some
- of the features of the language.
-
- How to read this document
-
- To easily navigate this document, tags have been placed for your editor to easily find. If you see
- #foo somewhere, you can search for [foo] which will be the place where foo is explained.
-
- Some common abbrevations used in this paper
- LHS for "Left Hand Side"
- RHS for "Right Hand Side"
-
- Language Features
- Grammar.................................................................Line xxx
- Operator Precedence.....................................................#op-pred
- Operators...............................................................Line xxx
- Arithmetic Operators................................................Line xxx
- Logical Operators...................................................#log-ops
- Bitwise Operators...................................................Line xxx
- Miscellaneous Operators.............................................Line xxx
- Packages................................................................Line xxx
-
-
-
- Operator Precendence
- [op-pred]
-
- The following are all the binary operators in Onyx in their precedence from highest to lowest:
-
- +---+-------------------------------------------+
- | 9 | % |
- | 8 | * / |
- | 7 | + - |
- | 6 | & | ^ << >> >>> |
- | 5 | <= < >= > |
- | 4 | == != |
- | 3 | && || |
- | 2 | |> .. |
- | 1 | = += -= *= /= %= &= |= ^= <<= >>= >>>= |
- +---+-------------------------------------------+
-
-
- The following are all the unary operators. Prefix unary operators are right associative and all
- of equal precedence. Postfix unary operators are left associative and also all of equal precedence.
-
- Prefix unary operators:
- ! Boolean NOT
- - Arithmetic negative
- ~ Bitwise NOT
- ^ Address of
- * Dereference
- cast(X) Cast to X
- ~~ Auto cast
-
- Postfix unary operators:
- [N] Array subscript
- .foo Field access
- (..) Function call
-
-
-
-
-
- Logical Operators
- [log-ops]
-
- There is only one unary logical operator, !. It negates the boolean value given to it.
-
- Binary logical operators expect both the LHS and RHS to be of boolean type. The following are the logical
- operators in Onyx:
-
- && Logical AND
- || Logical OR
-
- All logical operators are of equal precedence. && and || are standard operators in many C-style languages.
-
+++ /dev/null
-Rewriting Structure Assembly Code
-=================================
-
-Current in the Onyx code generator, structures r-values are represented
-in the execution stack as each of their individual primitive memebers.
-This solution has proved relatively easy to implement, but takes A LOT
-of extra instructions compared to the alternative.
-
-I would like to rewrite the way structure r-values are handled in the
-assembly to the following: all structure r-values are stored in the stack
-(or somewhere in memory, but probably the stack), and the execution stack
-simply contains a pointer to that address. This would allow for a structure
-load/store operation to simply be replaced by a COPY, instead of the potentially
-hundreds of instructions that it takes now.
-
-
-The largest issue that I have thought of so far with making this change
-is a flaw in how fixed-size arrays are being handled, which in turn would
-be a flaw with new structure r-values as well. This code example show it:
-
- main :: () {
- r := array_2(array_1(.[6, 7, 8, 9, 10]));
- println(r);
- }
-
- array_1 :: (x: [5] u32) -> [5] u32 {
- return .[ 1, 2, 3, 4, 5 ];
- }
-
- array_2 :: (x: [5] u32) -> [5] u32 {
- y: [5] u32;
- for^ y do *it = 1337;
-
- printf("{} == {}\n", ^y, cast(^u32) x);
-
- return x;
- }
-
-Here, you would expect the print in `main` to be "[1, 2, 3, 4, 5]". However,
-because of a bug with where arrays are stored, you actually see "[1337, 1337, ...]".
-This bug is that because array literals are living in the stack space of the
-current function, returning then is the same thing as returning a pointer to
-a local variable. This works in the easiest case as show below, because the
-array is copied immediately into the result variable, which lives in `main`s
-stack, but not when anything more complicated is going on:
-
- main :: () {
- result := array_1(.[]);
- println(result);
- }
-
-This bug is easy to get around when working with fixed-size arrays, which is
-something I do rarely anyway. However, structures are different story. Chaining
-calls that return structures is done every where in Onyx programs. Just take the
-`core.iter` package as an example.
-
- main :: () {
- for iter.as_iter(1 .. 20)
- |> iter.map(x => x * 2)
- |> iter.filter(x => x > 4)
- |> iter.take(5) {
-
- println(it);
- }
- }
-
-In this example, each of the return values of those functions is an `Iterator(i32)`,
-which is obviously a structure. In the new system, passing each result as a paramter
-to the next function would not work as shown with the array example above. A solution
-to this problem must be found before this refactor can be completed.
-
-
-
-
-
-SOLUTION
-========
-
-I was very dumb when I wrote the above text.
-
-This is actually very simple, and already something that the compiler is doing. I am
-just doing it wrong in the fixed-size array case. The rule has to be: ALL structure
-arguments need to be copied into a buffer for ARGUMENTS to the call. This already
-happens with non-simple structure. Currently, it does not happen for array, so that
-is why I thought this was a larger issue. The optimization that l-value structures
-passed by value can simply be their pointer and not-copied can be done in the future,
-but to get this optimization working, that does not have to be done.
+++ /dev/null
-Some tags to watch out for in the code with features that may be removed in the future:
-
- :ValueDirectiveHack
- In the same way #type can be used to express a type expression
- in a value context, #value can be used to express a value expression
- in a type context, such as a #solidify directive.
-
- :NullProcHack
- There are many times where being able to store a procedure that
- is 'null' is useful, such as a vtable-like structure that doesn't
- need all members defined. For this reason, `null_proc` was added.
- It type matches against all procedure types without an error, allowing
- it to be used wherever a procedure was expected. Also, before calling
- a dynamic procedure, it should be checked against `null_proc`.
-
- The implementation of `null_proc` is rather simple, but feels a
- little verbose an targential compared to everything else being done
- in the compiler.
\ No newline at end of file
ovm_program_t *ovm_program_new(ovm_store_t *store);
void ovm_program_delete(ovm_program_t *program);
void ovm_program_add_instructions(ovm_program_t *program, i32 instr_count, ovm_instr_t *instrs);
-void ovm_program_print_instructions(ovm_program_t *program, i32 start_instr, i32 instr_count);
-void ovm_raw_print_instructions(i32 instr_count, ovm_instr_t *instrs);
int ovm_program_register_static_ints(ovm_program_t *program, int len, int *data);
int ovm_program_register_func(ovm_program_t *program, char *name, i32 instr, i32 param_count, i32 value_number_count);
ovm_state_t *state = ovm_state_new(engine, prog);
ovm_program_load_from_file(prog, engine, argv[1]);
- ovm_program_print_instructions(prog, 0, bh_arr_length(prog->code));
static int func_table[] = { 0, 1, 6 };
ovm_program_register_static_ints(prog, 3, func_table);
}
-static char *ovm_instr_name(i32 full_instr) {
- return "";
-}
-
-#if 0
-static char *ovm_instr_name(i32 full_instr) {
-#define C(...) \
- case __VA_ARGS__: return #__VA_ARGS__; \
- case __VA_ARGS__ | OVMI_ATOMIC: return "ATOMIC_" #__VA_ARGS__;
-
- static char buf[64];
-
- switch (full_instr) {
- C(OVMI_NOP)
-
- C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_DIV_S, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_DIV_S, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_DIV_S, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_DIV_S, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_DIV_S, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_DIV_S, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I64))
-
- C(OVM_TYPED_INSTR(OVMI_REM_S, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_REM_S, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_REM_S, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_REM_S, OVM_TYPE_I64))
-
- C(OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I64))
-
- C(OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I64))
-
- C(OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I64))
-
- C(OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I64))
-
- C(OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I64))
-
- C(OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I64))
-
- C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_F64))
-
- C(OVMI_MOV)
- C(OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_F64))
- C(OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_F64))
- C(OVMI_COPY)
- C(OVMI_FILL)
-
- C(OVMI_REG_GET)
- C(OVMI_REG_SET)
-
- C(OVMI_IDX_ARR)
-
- C(OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_F64))
-
- C(OVMI_PARAM)
- C(OVMI_RETURN)
- C(OVMI_CALL)
- C(OVMI_CALLI)
-
- C(OVMI_BR)
- C(OVMI_BR_Z)
- C(OVMI_BR_NZ)
- C(OVMI_BRI)
- C(OVMI_BRI_Z)
- C(OVMI_BRI_NZ)
-
- C(OVM_TYPED_INSTR(OVMI_CLZ, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_CLZ, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_CLZ, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_CLZ, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_CTZ, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_CTZ, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_CTZ, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_CTZ, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_POPCNT, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_POPCNT, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_POPCNT, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_POPCNT, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_ROTL, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_ROTL, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_ROTL, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_ROTL, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_ROTR, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_ROTR, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_ROTR, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_ROTR, OVM_TYPE_I64))
-
- C(OVM_TYPED_INSTR(OVMI_ABS, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_NEG, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_CEIL, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_FLOOR, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_TRUNC, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_NEAREST, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_SQRT, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_MIN, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_MAX, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_COPYSIGN, OVM_TYPE_F32))
-
- C(OVM_TYPED_INSTR(OVMI_ABS, OVM_TYPE_F64))
- C(OVM_TYPED_INSTR(OVMI_NEG, OVM_TYPE_F64))
- C(OVM_TYPED_INSTR(OVMI_CEIL, OVM_TYPE_F64))
- C(OVM_TYPED_INSTR(OVMI_FLOOR, OVM_TYPE_F64))
- C(OVM_TYPED_INSTR(OVMI_TRUNC, OVM_TYPE_F64))
- C(OVM_TYPED_INSTR(OVMI_NEAREST, OVM_TYPE_F64))
- C(OVM_TYPED_INSTR(OVMI_SQRT, OVM_TYPE_F64))
- C(OVM_TYPED_INSTR(OVMI_MIN, OVM_TYPE_F64))
- C(OVM_TYPED_INSTR(OVMI_MAX, OVM_TYPE_F64))
- C(OVM_TYPED_INSTR(OVMI_COPYSIGN, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_CVT_I8, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_CVT_I8, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_CVT_I8, OVM_TYPE_I64))
-
- C(OVM_TYPED_INSTR(OVMI_CVT_I16, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_CVT_I16, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_CVT_I16, OVM_TYPE_I64))
-
- C(OVM_TYPED_INSTR(OVMI_CVT_I32, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_CVT_I32, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_CVT_I32, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_CVT_I32, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_CVT_I32, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_CVT_I64, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_CVT_I64, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_CVT_I64, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_CVT_I64, OVM_TYPE_F32))
- C(OVM_TYPED_INSTR(OVMI_CVT_I64, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_CVT_F32, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_CVT_F32, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_CVT_F32, OVM_TYPE_F64))
-
- C(OVM_TYPED_INSTR(OVMI_CVT_F64, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_CVT_F64, OVM_TYPE_I64))
- C(OVM_TYPED_INSTR(OVMI_CVT_F64, OVM_TYPE_F32))
-
- C(OVM_TYPED_INSTR(OVMI_CMPXCHG, OVM_TYPE_I8))
- C(OVM_TYPED_INSTR(OVMI_CMPXCHG, OVM_TYPE_I16))
- C(OVM_TYPED_INSTR(OVMI_CMPXCHG, OVM_TYPE_I32))
- C(OVM_TYPED_INSTR(OVMI_CMPXCHG, OVM_TYPE_I64))
-
- default:
- snprintf(buf, 64, "unknown (%d)", full_instr);
- return buf;
- }
-}
-#endif
-
-void ovm_program_print_instructions(ovm_program_t *program, i32 start_instr, i32 instr_count) {
- fori (i, start_instr, start_instr + instr_count) {
- //
- // Horribly inefficient way of checking to see if this instruction
- // is the start of a function, but for now, it'll do. -brendanfh 06/13/2022
- bh_arr_each(ovm_func_t, func, program->funcs) {
- if (i == func->start_instr && func->kind == OVM_FUNC_INTERNAL) {
- printf("\n[%d] %s values=%d:\n", func->id, func->name, func->value_number_count);
- }
- }
-
- ovm_instr_t instr = program->code[i];
- printf("%6lx %50s | r=%02d a=%02d b=%02d i=%d f=%f l=%ld d=%lf\n", i, ovm_instr_name(instr.full_instr), instr.r, instr.a, instr.b, instr.i, instr.f, instr.l, instr.d);
- }
-}
-
-void ovm_raw_print_instructions(i32 instr_count, ovm_instr_t *instrs) {
- fori (i, 0, instr_count) {
- ovm_instr_t instr = instrs[i];
- printf("%6lx %50s | r=%02d a=%02d b=%02d i=%d f=%f l=%ld d=%lf\n", i, ovm_instr_name(instr.full_instr), instr.r, instr.a, instr.b, instr.i, instr.f, instr.l, instr.d);
- }
-}
-
//
// Engine
ovm_engine_t *ovm_engine_new(ovm_store_t *store) {
state->debug = debug_host_lookup_thread(engine->debug, thread_id);
}
-#ifdef OVM_VERBOSE
- ovm_program_print_instructions(program, 0, bh_arr_length(program->code));
-#endif
-
return state;
}
const wasm_name_t *export_name = wasm_exporttype_name(exports.data[i]);
bh_printf("exports: %b %d\n", export_name->data, export_name->size, wasm_externtype_kind(wasm_exporttype_type(exports.data[i])));
}
-
- ovm_program_print_instructions(module->program, 0, bh_arr_length(module->program->code));
}
printf("Creating new project manifest in {}.\n\n", global_arguments.config_file);
- read_field :: macro (f: str, dest: ^$T) {
+ read_field :: macro (f: str, dest: ^$T, default: T) {
while true {
print(f);
line := r->read_line(consume_newline=true, allocator=context.temp_allocator)
|> string.strip_whitespace();
+ if !line {
+ *dest = default;
+ break;
+ }
+
if conv.parse_any(dest, T, line, context.allocator) do break;
+
if T == str {
*cast(^str) dest = string.alloc_copy(line);
break;
// @TODO // Validation for these fields.
r := io.reader_make(^stdio_stream);
- read_field("Package name: ", ^config.metadata.name);
- read_field("Package description: ", ^config.metadata.description);
- read_field("Package url: ", ^config.metadata.url);
- read_field("Package author: ", ^config.metadata.author);
- read_field("Package version (X.Y.Z): ", ^config.metadata.version);
+ read_field("Package name: ", ^config.metadata.name, "");
+ read_field("Package description: ", ^config.metadata.description, "");
+ read_field("Package url: ", ^config.metadata.url, "");
+ read_field("Package author: ", ^config.metadata.author, "");
+ read_field("Package version (0.0.1): ", ^config.metadata.version, .{0, 0, 1});
}
#tag Command.{ "add", "Add a new dependency to the project.", "package-url [version]",
pack := string.as_str(args[0]);
+ pack = Git.get_full_repo_uri(pack);
versions := Git.get_available_versions(pack);
defer delete(^versions);
+ array.sort(versions, SemVer.compare);
+
for versions {
- printf("- {}\n", it);
+ printf("{}\n", it);
}
}
defer delete(^versions);
array.sort(versions, SemVer.compare);
- return versions[0];
+ latest := versions[0];
+ return latest;
}
get_latest_compatible_version :: (repo: str, current_version: SemVer) -> SemVer {
}
if !os.dir_rename(full_dest, actual_dest) {
+ eprintf("Failed to move temporary package to final destination when fetching {}.\n", repo);
os.remove_directory(full_dest);
return false;
}
// Remove the .git folder, as it is unneeded.
unnecessary_git_dir := tprintf("{}/.git", actual_dest);
- return os.remove_directory(unnecessary_git_dir);
+ if !os.remove_directory(unnecessary_git_dir) {
+ eprintf("Failed to delete .git folder of {}.\n", repo);
+ return false;
+ }
+
+ return true;
}
return false;
run_command(git_path, .["commit", "-m", tprintf("version {}", config.metadata.version)]);
run_command(git_path, .["tag", tprintf("v{}", config.metadata.version)]);
run_command(git_path, .["push", "--tags"]);
+ run_command(git_path, .["push"]);
return true;
}
}