Merge remote-tracking branch 'origin/trunk' into name-functions-with-morphic

This commit is contained in:
Folkert 2021-06-13 17:21:47 +02:00
commit 8cbc8b0334
6 changed files with 147 additions and 0 deletions

View File

@ -0,0 +1,76 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
void* roc_alloc(size_t size, unsigned int alignment) {
return malloc(size);
}
void* roc_realloc(void* ptr, size_t old_size, size_t new_size, unsigned int alignment) {
return realloc(ptr, new_size);
}
void roc_dealloc(void* ptr, unsigned int alignment) {
free(ptr);
}
struct RocStr {
char* bytes;
size_t len;
};
struct RocCallResult {
size_t flag;
struct RocStr content;
};
extern void roc__mainForHost_1_exposed(struct RocCallResult *re);
const size_t MAX_STACK_STR_BYTES = 1024;
int main() {
// make space for the result
struct RocCallResult callresult;
// call roc to populate the callresult
roc__mainForHost_1_exposed(&callresult);
struct RocStr str = callresult.content;
// Convert from RocStr to C string (null-terminated char*)
size_t len = str.len;
char* c_str;
// Allocate on the stack unless the string is particularly big.
// (Don't want a stack overflow!)
if (len <= MAX_STACK_STR_BYTES) {
c_str = (char*)alloca(len + 1);
} else {
c_str = (char*)malloc(len + 1);
}
memcpy(c_str, str.bytes, len);
// null-terminate
c_str[len] = 0;
// Print the string to stdout
printf("%s\n", c_str);
// Pointer to the beginning of the RocStr's actual allocation, which is
// the size_t immediately preceding the first stored byte.
size_t* str_base_ptr = (size_t*)str.bytes - 1;
// If *str_base_ptr is equal to 0, then the string is in the
// read-only data section of the binary, and can't be freed!
if (*str_base_ptr != 0) {
roc_dealloc(str_base_ptr, 8);
}
// If we malloc'd c_str, free it.
if (len > MAX_STACK_STR_BYTES) {
free(c_str);
}
return 0;
}

1
examples/hello-zig/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
hello-world

View File

@ -0,0 +1,12 @@
app "hello-world"
packages { base: "platform" }
imports []
provides [ main ] to base
greeting =
hi = "Hello"
name = "World"
"\(hi), \(name)!!!!!!!!!!!!!"
main = greeting

View File

@ -0,0 +1,48 @@
# Hello, World!
To run, `cd` into this directory and run:
```bash
$ cargo run run Hello.roc
```
To run in release mode instead, do:
```bash
$ cargo run --release run Hello.roc
```
## Troubleshooting
If you encounter `cannot find -lc++`, run the following for ubuntu `sudo apt install libc++-dev`.
## Design Notes
This demonstrates the basic design of hosts: Roc code gets compiled into a pure
function (in this case, a thunk that always returns `"Hello, World!"`) and
then the host calls that function. Fundamentally, that's the whole idea! The host
might not even have a `main` - it could be a library, a plugin, anything.
Everything else is built on this basic "hosts calling linked pure functions" design.
For example, things get more interesting when the compiled Roc function returns
a `Task` - that is, a tagged union data structure containing function pointers
to callback closures. This lets the Roc pure function describe arbitrary
chainable effects, which the host can interpret to perform I/O as requested by
the Roc program. (The tagged union `Task` would have a variant for each supported
I/O operation.)
In this trivial example, it's very easy to line up the API between the host and
the Roc program. In a more involved host, this would be much trickier - especially
if the API were changing frequently during development.
The idea there is to have a first-class concept of "glue code" which host authors
can write (it would be plain Roc code, but with some extra keywords that aren't
available in normal modules - kinda like `port module` in Elm), and which
describe both the Roc-host/C boundary as well as the Roc-host/Roc-app boundary.
Roc application authors only care about the Roc-host/Roc-app portion, and the
host author only cares about the Roc-host/C bounary when implementing the host.
Using this glue code, the Roc compiler can generate C header files describing the
boundary. This not only gets us host compatibility with C compilers, but also
Rust FFI for free, because [`rust-bindgen`](https://github.com/rust-lang/rust-bindgen)
generates correct Rust FFI bindings from C headers.

View File

@ -0,0 +1,10 @@
platform examples/hello-world
requires {}{ main : Str }
exposes []
packages {}
imports []
provides [ mainForHost ]
effects fx.Effect {}
mainForHost : Str
mainForHost = main