diff options
| author | 2026-10-14 08:29:46 +0330 | |
|---|---|---|
| committer | 2026-10-14 08:29:46 +0330 | |
| commit | 1c53304d03414890af0da7432d1aea87e754a14a (patch) | |
| tree | 97190d86cf548c9bc63f68c86a4d2a4f09809713 /src/micro/port_linux.c | |
| parent | a (diff) | |
feat: grow memory limits, add icon/actor libraries, overhaul LSP diagnostics
Compiler and VM
- make every memory map limit dynamic and adjustable at both build time and
runtime, including compiler tables, stacks, and every execution region
- reserve and grow tables, operand and return stacks, frame segments, and
image-building output without relocating live code or globals
- rename i/o and string helpers consistently (puts -> put_s, putn -> put_n,
scat -> cat_s) across compiler, runtime, examples, tests, and docs
- recast and consume values assigned to globals from inside functions
- allow fixed and const together, add typed closure returns (fn:t(...)),
multi-include syntax (include rt, run, core;), and recurse type inference
- never infer string literals as word, anywhere, as argument, variable, or
return type
- draft docs/BORROW.md: a borrow checker less intrusive than Rust's but more
intelligent
LSP and editor integration
- require explicit includes and give every toolchain area its own namespace
(rt, lsp, run, core)
- hover for built-in types, inferred types, values known at hover time, array
and pointer lifetime/size, library names in include statements, and inferred
lifetimes with explanations
- honor /* */ comments as documentation for variables and arguments, and stop
misattributing section headers or trailing comments as variable hints
- stream diagnostics and cancel stale requests on change
- jump to definition and references, including cross-file
- quick fixes with automatic library import, and namespace completion
Hints and diagnostics
- render hints as blue squigglies by default, with hints on/small/off per file
- suppress magic-number hints via a preceding /* */ comment, and say so
- warn on unused arguments; allow multiple _ discards in parameters, pattern
matching, and tagged enums, warning if a _ is actually used
- rewrite hint and comment prose in professional compiler-doc register
Libraries
- add icon.zc: run-time self-modification, find/replace syntax, type and block
edits, and step-through inspection of locals and globals
- add actor.zc: async actors with Actor<I,O>, implicit this, spawn/recv/send,
injected into the compiler machinery at include time rather than built in
Tooling and docs
- reorganize sources, tests, and designs into subdirectories
- unify windows entry points in tinyvm.bat and add a matching tinyvm.sh
- add --help across the toolchain
- polish the learning guide, stdlib docs, and design notes
Tests
- add accept, reject, and regression coverage across the compiler, LSP,
- runtime, icon, actors, threads, formatting, and the toolchain contracts,
plus fuzzers and property tests
Diffstat (limited to 'src/micro/port_linux.c')
| -rw-r--r-- | src/micro/port_linux.c | 167 |
1 files changed, 148 insertions, 19 deletions
diff --git a/src/micro/port_linux.c b/src/micro/port_linux.c index e6c8847..9331a0b 100644 --- a/src/micro/port_linux.c +++ b/src/micro/port_linux.c @@ -19,6 +19,10 @@ #endif #else #include <unistd.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <poll.h> +#include <signal.h> #ifndef O_BINARY #define O_BINARY 0 #endif @@ -28,11 +32,16 @@ #include <time.h> #include "micro_port.h" #include "bops.h" +#ifdef ZY_THREADS +#define VM_TLS _Thread_local +#else +#define VM_TLS +#endif /* the machine's memory: a block that grows by realloc, zero in the new part. the base can move on a grow, which micro.c takes into account (it re-reads m). */ -static Cell *lmem; -static UCell lcap; +static VM_TLS Cell *lmem; +static VM_TLS UCell lcap; Cell *micro_mem_alloc(UCell words) { @@ -47,19 +56,24 @@ Cell *micro_mem_alloc(UCell words) return n; } -int micro_getc(void) -{ - int c = getchar(); - return c == EOF ? -1 : c; -} - -int micro_eof(void) -{ - int c = getchar(); - if (c == EOF) return 1; - ungetc(c, stdin); - return 0; +#if defined(ZY_THREADS) +static VM_TLS unsigned char input_bytes[65536]; +static VM_TLS int input_position, input_count, input_ended; +static int input_fill(int wait) { + if (input_position < input_count) return 1; + if (input_ended) return 0; + if (!wait) { struct pollfd descriptor = {0, POLLIN, 0}; if (poll(&descriptor, 1, 0) <= 0) return 0; } + else fflush(stdout); + int n = (int)read(0, input_bytes, sizeof input_bytes); + if (n <= 0) { input_ended = 1; return 0; } + input_position = 0; input_count = n; return 1; } +int micro_getc(void) { return input_fill(1) ? input_bytes[input_position++] : -1; } +int micro_eof(void) { input_fill(0); return input_ended; } +#else +int micro_getc(void) { int c = getchar(); return c == EOF ? -1 : c; } +int micro_eof(void) { int c = getchar(); if (c == EOF) return 1; ungetc(c, stdin); return 0; } +#endif void micro_putc(int c) { putchar(c & 255); } @@ -87,10 +101,10 @@ static int inmem(Cell a) { return (UCell)a < (UCell)micro_mem_words(); } #ifndef _WIN32 extern char **environ; #endif -static int zy_argc; -static char **zy_argv; -static int zy_envc; -static char **zy_envp; +static VM_TLS int zy_argc; +static VM_TLS char **zy_argv; +static VM_TLS int zy_envc; +static VM_TLS char **zy_envp; static void zy_args(int argc, char **argv) { @@ -126,7 +140,7 @@ static Cell vecstr(Cell *mem, char **v, int count, Cell a, Cell b, Cell c, const static Cell host_rand(void) { - static uint64_t s; + static VM_TLS uint64_t s; if (!s) s = (uint64_t)time(0) ^ 0x9E3779B97F4A7C15ull; if (!s) s = 0x9E3779B97F4A7C15ull; s ^= s << 13; s ^= s >> 7; s ^= s << 17; @@ -152,6 +166,84 @@ Cell micro_syscall(Cell *mem, Cell op, Cell a, Cell b, Cell c) char path[1024], tmp[512]; int n, i, got; switch (op) { + case 43: { /* clone an isolated execution context with a bidirectional channel */ +#if !defined(_WIN32) && !defined(__wasm__) + int fd[2]; pid_t child; + if (!inmem(a) || socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) return -1; + fflush(NULL); child = fork(); + if (child < 0) { close(fd[0]); close(fd[1]); return -1; } + if (!child) { close(fd[0]); mem[a] = fd[1]; return 0; } + close(fd[1]); mem[a] = fd[0]; return child; +#else + return -1; +#endif + } + case 44: case 45: { /* write/read complete word buffers on a context channel */ +#if !defined(_WIN32) && !defined(__wasm__) + size_t done = 0, length; ssize_t got; unsigned char *buffer; + if (b < 0 || c < 0 || c > (Cell)micro_mem_words() - b) return -1; + buffer = (unsigned char *)(mem + b); length = (size_t)c * sizeof(Cell); + while (done < length) { + if (op == 44) { +#ifdef __APPLE__ + int enabled = 1; setsockopt((int)a, SOL_SOCKET, SO_NOSIGPIPE, &enabled, sizeof enabled); + #ifdef MSG_NOSIGNAL + got = send((int)a, buffer + done, length - done, MSG_NOSIGNAL); +#else + got = send((int)a, buffer + done, length - done, 0); +#endif +#else + got = send((int)a, buffer + done, length - done, MSG_NOSIGNAL); +#endif + } else got = read((int)a, buffer + done, length - done); + if (got < 0 && errno == EINTR) continue; + if (got <= 0) return -1; + done += (size_t)got; + } + return c; +#else + return -1; +#endif + } + case 46: { /* channel readiness, with a millisecond timeout */ +#if !defined(_WIN32) && !defined(__wasm__) + struct pollfd fd; int ready; fd.fd = (int)a; fd.events = POLLIN; fd.revents = 0; + do { ready = poll(&fd, 1, (int)b); } while (ready < 0 && errno == EINTR); + return ready > 0 ? !!(fd.revents & (POLLIN | POLLHUP)) : ready; +#else + return -1; +#endif + } + case 47: case 49: { /* join a child context, optionally terminating it first */ +#if !defined(_WIN32) && !defined(__wasm__) + int status; pid_t joined; + if (a <= 0) return -1; + if (op == 49) kill((pid_t)a, SIGTERM); + do { joined = waitpid((pid_t)a, &status, 0); } while (joined < 0 && errno == EINTR); + return joined < 0 ? -1 : WIFEXITED(status) ? WEXITSTATUS(status) : -1; +#else + return -1; +#endif + } + case 50: { /* monotonic milliseconds for elapsed-time measurements */ +#if !defined(_WIN32) && !defined(__wasm__) + struct timespec now; if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) return -1; + return (Cell)now.tv_sec * 1000 + now.tv_nsec / 1000000; +#else + return -1; +#endif + } + case 48: { /* sleep for a bounded millisecond duration */ +#if !defined(_WIN32) && !defined(__wasm__) + struct timespec duration; + if (a < 0 || a > 86400000) return -1; + duration.tv_sec = (time_t)(a / 1000); duration.tv_nsec = (long)(a % 1000) * 1000000; + while (nanosleep(&duration, &duration) < 0) if (errno != EINTR) return -1; + return 0; +#else + return -1; +#endif + } case 1: if (zcstr(mem, a, path, 1024) < 0) return -1; if (b == 1) return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); @@ -269,6 +361,21 @@ Cell micro_syscall(Cell *mem, Cell op, Cell a, Cell b, Cell c) int main(int argc, char **argv) { UCell initial, limit; + if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) { + fputs("usage: zym [program arguments] < input\n" + "runs the embedded image with the portable vm; compiler images read source from stdin.\n" + "input after a backtick is program input; run-only images read program input directly.\n" + "program arguments are available through std.argv; stdin has the name <eval>.\n" + "options:\n --help, -h show this help and exit\n" + "environment:\n" + " TINYVM_MEMORY_WORDS initial vm memory in words\n" + " TINYVM_MAX_MEMORY_WORDS maximum vm memory in words\n", stdout); + return 0; + } + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '-') { + fprintf(stderr, "micro: unknown option %s; use --help for usage\n", argv[1]); + return 2; + } #ifdef _WIN32 _setmode(_fileno(stdin), _O_BINARY); _setmode(_fileno(stdout), _O_BINARY); @@ -282,3 +389,25 @@ int main(int argc, char **argv) { } return micro_run(); } + +#ifdef ZY_THREADS +struct PortSnapshot { + unsigned char bytes[65536]; + int position, count, ended, argc, envc; + char **argv, **envp; +}; +size_t micro_context_port_size(void) { return sizeof(struct PortSnapshot); } +void micro_context_port_save(void *destination) { + struct PortSnapshot *snapshot = destination; + memcpy(snapshot->bytes, input_bytes, sizeof input_bytes); + snapshot->position = input_position; snapshot->count = input_count; snapshot->ended = input_ended; + snapshot->argc = zy_argc; snapshot->argv = zy_argv; snapshot->envc = zy_envc; snapshot->envp = zy_envp; +} +void micro_context_port_restore(const void *source) { + const struct PortSnapshot *snapshot = source; + memcpy(input_bytes, snapshot->bytes, sizeof input_bytes); + input_position = snapshot->position; input_count = snapshot->count; input_ended = snapshot->ended; + zy_argc = snapshot->argc; zy_argv = snapshot->argv; zy_envc = snapshot->envc; zy_envp = snapshot->envp; +} +void micro_context_port_free(void) { free(lmem); lmem = 0; lcap = 0; } +#endif |
