Fixed the code style in *.c files to two spaces indentation and add a linter named funstx to Fun. (0.39.0)
This commit is contained in:
parent
33001a7ad2
commit
03b2532474
237 changed files with 17550 additions and 13911 deletions
|
|
@ -17,23 +17,23 @@
|
|||
* Stack after: [int ms]
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
case OP_CLOCK_MONO_MS: {
|
||||
int64_t ms;
|
||||
int64_t ms;
|
||||
#if defined(CLOCK_MONOTONIC) && !defined(_WIN32)
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
|
||||
ms = (int64_t)ts.tv_sec * 1000 + (int64_t)(ts.tv_nsec / 1000000);
|
||||
} else {
|
||||
time_t s = time(NULL);
|
||||
ms = (int64_t)s * 1000;
|
||||
}
|
||||
#else
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
|
||||
ms = (int64_t)ts.tv_sec * 1000 + (int64_t)(ts.tv_nsec / 1000000);
|
||||
} else {
|
||||
time_t s = time(NULL);
|
||||
ms = (int64_t)s * 1000;
|
||||
}
|
||||
#else
|
||||
time_t s = time(NULL);
|
||||
ms = (int64_t)s * 1000;
|
||||
#endif
|
||||
push_value(vm, make_int(ms));
|
||||
break;
|
||||
push_value(vm, make_int(ms));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
|
|
@ -21,52 +21,53 @@
|
|||
* - If types are wrong, prints error and pushes empty string to keep stack safe.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
case OP_DATE_FORMAT: {
|
||||
Value fmt = pop_value(vm);
|
||||
Value ms = pop_value(vm);
|
||||
if (ms.type != VAL_INT || fmt.type != VAL_STRING) {
|
||||
fprintf(stderr, "DATE_FORMAT expects (fmt:string, ms:int)\n");
|
||||
if (ms.type != VAL_NIL) free_value(ms);
|
||||
if (fmt.type != VAL_NIL) free_value(fmt);
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
time_t secs = (time_t)(ms.i / 1000);
|
||||
struct tm tmv;
|
||||
Value fmt = pop_value(vm);
|
||||
Value ms = pop_value(vm);
|
||||
if (ms.type != VAL_INT || fmt.type != VAL_STRING) {
|
||||
fprintf(stderr, "DATE_FORMAT expects (fmt:string, ms:int)\n");
|
||||
if (ms.type != VAL_NIL) free_value(ms);
|
||||
if (fmt.type != VAL_NIL) free_value(fmt);
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
time_t secs = (time_t)(ms.i / 1000);
|
||||
struct tm tmv;
|
||||
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1
|
||||
localtime_r(&secs, &tmv);
|
||||
localtime_r(&secs, &tmv);
|
||||
#else
|
||||
struct tm *ptm = localtime(&secs);
|
||||
if (!ptm) {
|
||||
free_value(ms); free_value(fmt);
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
tmv = *ptm;
|
||||
#endif
|
||||
/* Determine buffer size by attempting once with a reasonable size and retrying if needed */
|
||||
size_t cap = 128;
|
||||
char *buf = (char*)malloc(cap);
|
||||
size_t n = strftime(buf, cap, fmt.s, &tmv);
|
||||
if (n == 0) {
|
||||
/* try larger */
|
||||
cap = 512;
|
||||
buf = (char*)realloc(buf, cap);
|
||||
n = strftime(buf, cap, fmt.s, &tmv);
|
||||
}
|
||||
Value out;
|
||||
if (n == 0) {
|
||||
out = make_string("");
|
||||
} else {
|
||||
out = make_string(buf);
|
||||
}
|
||||
free(buf);
|
||||
struct tm *ptm = localtime(&secs);
|
||||
if (!ptm) {
|
||||
free_value(ms);
|
||||
free_value(fmt);
|
||||
push_value(vm, out);
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
tmv = *ptm;
|
||||
#endif
|
||||
/* Determine buffer size by attempting once with a reasonable size and retrying if needed */
|
||||
size_t cap = 128;
|
||||
char *buf = (char *)malloc(cap);
|
||||
size_t n = strftime(buf, cap, fmt.s, &tmv);
|
||||
if (n == 0) {
|
||||
/* try larger */
|
||||
cap = 512;
|
||||
buf = (char *)realloc(buf, cap);
|
||||
n = strftime(buf, cap, fmt.s, &tmv);
|
||||
}
|
||||
Value out;
|
||||
if (n == 0) {
|
||||
out = make_string("");
|
||||
} else {
|
||||
out = make_string(buf);
|
||||
}
|
||||
free(buf);
|
||||
free_value(ms);
|
||||
free_value(fmt);
|
||||
push_value(vm, out);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
|
|
@ -10,16 +10,16 @@
|
|||
// Get environment variables of the operation system.
|
||||
|
||||
case OP_ENV: {
|
||||
Value key = pop_value(vm);
|
||||
if (key.type != VAL_STRING) {
|
||||
fprintf(stderr, "Runtime type error: ENV expects string name\n");
|
||||
free_value(key);
|
||||
exit(1);
|
||||
}
|
||||
const char *name = key.s ? key.s : "";
|
||||
const char *val = getenv(name);
|
||||
/* Return empty string if not set (consistent with read_file fallback style) */
|
||||
push_value(vm, make_string(val ? val : ""));
|
||||
Value key = pop_value(vm);
|
||||
if (key.type != VAL_STRING) {
|
||||
fprintf(stderr, "Runtime type error: ENV expects string name\n");
|
||||
free_value(key);
|
||||
break;
|
||||
exit(1);
|
||||
}
|
||||
const char *name = key.s ? key.s : "";
|
||||
const char *val = getenv(name);
|
||||
/* Return empty string if not set (consistent with read_file fallback style) */
|
||||
push_value(vm, make_string(val ? val : ""));
|
||||
free_value(key);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,31 +5,31 @@
|
|||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
*
|
||||
* Added: 2025-12-28
|
||||
*/
|
||||
|
||||
// Get all environment variables of the operation system and push them as a map.
|
||||
|
||||
case OP_ENV_ALL: {
|
||||
extern char **environ;
|
||||
Value m = make_map_empty();
|
||||
if (environ) {
|
||||
for (char **env = environ; *env; ++env) {
|
||||
char *entry = *env;
|
||||
char *equals = strchr(entry, '=');
|
||||
if (equals) {
|
||||
size_t key_len = equals - entry;
|
||||
char *key = malloc(key_len + 1);
|
||||
if (key) {
|
||||
memcpy(key, entry, key_len);
|
||||
key[key_len] = '\0';
|
||||
map_set(&m, key, make_string(equals + 1));
|
||||
free(key);
|
||||
}
|
||||
}
|
||||
extern char **environ;
|
||||
Value m = make_map_empty();
|
||||
if (environ) {
|
||||
for (char **env = environ; *env; ++env) {
|
||||
char *entry = *env;
|
||||
char *equals = strchr(entry, '=');
|
||||
if (equals) {
|
||||
size_t key_len = equals - entry;
|
||||
char *key = malloc(key_len + 1);
|
||||
if (key) {
|
||||
memcpy(key, entry, key_len);
|
||||
key[key_len] = '\0';
|
||||
map_set(&m, key, make_string(equals + 1));
|
||||
free(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
push_value(vm, m);
|
||||
break;
|
||||
}
|
||||
push_value(vm, m);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
*
|
||||
* Added: 2025-12-28
|
||||
*/
|
||||
|
||||
|
|
@ -15,6 +15,6 @@ case OP_FUN_VERSION: {
|
|||
#ifndef FUN_VERSION
|
||||
#define FUN_VERSION "0.0.0-dev"
|
||||
#endif
|
||||
push_value(vm, make_string(FUN_VERSION));
|
||||
break;
|
||||
push_value(vm, make_string(FUN_VERSION));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,40 +10,40 @@
|
|||
*/
|
||||
|
||||
case OP_OS_LIST_DIR: {
|
||||
/* pops path string; pushes array of strings */
|
||||
Value pathv = pop_value(vm);
|
||||
char *path = value_to_string_alloc(&pathv);
|
||||
free_value(pathv);
|
||||
/* pops path string; pushes array of strings */
|
||||
Value pathv = pop_value(vm);
|
||||
char *path = value_to_string_alloc(&pathv);
|
||||
free_value(pathv);
|
||||
|
||||
Value arr = make_array_from_values(NULL, 0);
|
||||
if (path) {
|
||||
/*
|
||||
* Using 'ls -1' as a fallback to avoid dirent.h conflicts on some systems.
|
||||
* We escape the path minimally for the shell.
|
||||
*/
|
||||
size_t slen = strlen(path) + 16;
|
||||
char *cmd = (char*)malloc(slen);
|
||||
if (cmd) {
|
||||
snprintf(cmd, slen, "ls -1 \"%s\"", path);
|
||||
FILE *fp = popen(cmd, "r");
|
||||
if (fp) {
|
||||
char line[1024];
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
/* Strip trailing newline */
|
||||
size_t l = strlen(line);
|
||||
if (l > 0 && line[l-1] == '\n') line[l-1] = '\0';
|
||||
if (l > 1 && line[l-2] == '\r') line[l-2] = '\0';
|
||||
|
||||
if (line[0] != '\0') {
|
||||
array_push(&arr, make_string(line));
|
||||
}
|
||||
}
|
||||
pclose(fp);
|
||||
}
|
||||
free(cmd);
|
||||
Value arr = make_array_from_values(NULL, 0);
|
||||
if (path) {
|
||||
/*
|
||||
* Using 'ls -1' as a fallback to avoid dirent.h conflicts on some systems.
|
||||
* We escape the path minimally for the shell.
|
||||
*/
|
||||
size_t slen = strlen(path) + 16;
|
||||
char *cmd = (char *)malloc(slen);
|
||||
if (cmd) {
|
||||
snprintf(cmd, slen, "ls -1 \"%s\"", path);
|
||||
FILE *fp = popen(cmd, "r");
|
||||
if (fp) {
|
||||
char line[1024];
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
/* Strip trailing newline */
|
||||
size_t l = strlen(line);
|
||||
if (l > 0 && line[l - 1] == '\n') line[l - 1] = '\0';
|
||||
if (l > 1 && line[l - 2] == '\r') line[l - 2] = '\0';
|
||||
|
||||
if (line[0] != '\0') {
|
||||
array_push(&arr, make_string(line));
|
||||
}
|
||||
}
|
||||
free(path);
|
||||
pclose(fp);
|
||||
}
|
||||
free(cmd);
|
||||
}
|
||||
push_value(vm, arr);
|
||||
break;
|
||||
free(path);
|
||||
}
|
||||
push_value(vm, arr);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,79 +13,81 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
#define popen _popen
|
||||
#define pclose _pclose
|
||||
#define popen _popen
|
||||
#define pclose _pclose
|
||||
#endif
|
||||
|
||||
case OP_PROC_RUN: {
|
||||
/* Pops command string; pushes map {"out": string, "code": int} */
|
||||
Value cmdv = pop_value(vm);
|
||||
char *cmd = value_to_string_alloc(&cmdv);
|
||||
free_value(cmdv);
|
||||
if (!cmd) {
|
||||
Value m = make_map_empty();
|
||||
map_set(&m, "out", make_string(""));
|
||||
map_set(&m, "code", make_int(-1));
|
||||
push_value(vm, m);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use popen to run via shell and capture stdout */
|
||||
FILE *fp = popen(cmd, "r");
|
||||
int exit_code = -1;
|
||||
char *out = NULL;
|
||||
size_t cap = 0, len = 0;
|
||||
if (fp) {
|
||||
cap = 4096;
|
||||
out = (char*)malloc(cap);
|
||||
if (!out) {
|
||||
pclose(fp);
|
||||
free(cmd);
|
||||
Value m = make_map_empty();
|
||||
map_set(&m, "out", make_string(""));
|
||||
map_set(&m, "code", make_int(-1));
|
||||
push_value(vm, m);
|
||||
break;
|
||||
}
|
||||
int c;
|
||||
while ((c = fgetc(fp)) != EOF) {
|
||||
if (len + 1 >= cap) {
|
||||
cap *= 2;
|
||||
char *nb = (char*)realloc(out, cap);
|
||||
if (!nb) {
|
||||
free(out);
|
||||
pclose(fp);
|
||||
free(cmd);
|
||||
Value m = make_map_empty();
|
||||
map_set(&m, "out", make_string(""));
|
||||
map_set(&m, "code", make_int(-1));
|
||||
push_value(vm, m);
|
||||
goto done_push;
|
||||
}
|
||||
out = nb;
|
||||
}
|
||||
out[len++] = (char)c;
|
||||
}
|
||||
out[len] = '\0';
|
||||
int status = pclose(fp);
|
||||
#ifdef __unix__
|
||||
if (WIFEXITED(status)) exit_code = WEXITSTATUS(status);
|
||||
else exit_code = -1;
|
||||
#else
|
||||
exit_code = status;
|
||||
#endif
|
||||
} else {
|
||||
out = strdup("");
|
||||
exit_code = -1;
|
||||
}
|
||||
|
||||
/* Pops command string; pushes map {"out": string, "code": int} */
|
||||
Value cmdv = pop_value(vm);
|
||||
char *cmd = value_to_string_alloc(&cmdv);
|
||||
free_value(cmdv);
|
||||
if (!cmd) {
|
||||
Value m = make_map_empty();
|
||||
map_set(&m, "out", make_string(out ? out : ""));
|
||||
map_set(&m, "code", make_int(exit_code));
|
||||
map_set(&m, "out", make_string(""));
|
||||
map_set(&m, "code", make_int(-1));
|
||||
push_value(vm, m);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use popen to run via shell and capture stdout */
|
||||
FILE *fp = popen(cmd, "r");
|
||||
int exit_code = -1;
|
||||
char *out = NULL;
|
||||
size_t cap = 0, len = 0;
|
||||
if (fp) {
|
||||
cap = 4096;
|
||||
out = (char *)malloc(cap);
|
||||
if (!out) {
|
||||
pclose(fp);
|
||||
free(cmd);
|
||||
Value m = make_map_empty();
|
||||
map_set(&m, "out", make_string(""));
|
||||
map_set(&m, "code", make_int(-1));
|
||||
push_value(vm, m);
|
||||
break;
|
||||
}
|
||||
int c;
|
||||
while ((c = fgetc(fp)) != EOF) {
|
||||
if (len + 1 >= cap) {
|
||||
cap *= 2;
|
||||
char *nb = (char *)realloc(out, cap);
|
||||
if (!nb) {
|
||||
free(out);
|
||||
pclose(fp);
|
||||
free(cmd);
|
||||
Value m = make_map_empty();
|
||||
map_set(&m, "out", make_string(""));
|
||||
map_set(&m, "code", make_int(-1));
|
||||
push_value(vm, m);
|
||||
goto done_push;
|
||||
}
|
||||
out = nb;
|
||||
}
|
||||
out[len++] = (char)c;
|
||||
}
|
||||
out[len] = '\0';
|
||||
int status = pclose(fp);
|
||||
#ifdef __unix__
|
||||
if (WIFEXITED(status))
|
||||
exit_code = WEXITSTATUS(status);
|
||||
else
|
||||
exit_code = -1;
|
||||
#else
|
||||
exit_code = status;
|
||||
#endif
|
||||
} else {
|
||||
out = strdup("");
|
||||
exit_code = -1;
|
||||
}
|
||||
|
||||
Value m = make_map_empty();
|
||||
map_set(&m, "out", make_string(out ? out : ""));
|
||||
map_set(&m, "code", make_int(exit_code));
|
||||
push_value(vm, m);
|
||||
|
||||
done_push:
|
||||
if (out) free(out);
|
||||
free(cmd);
|
||||
break;
|
||||
if (out) free(out);
|
||||
free(cmd);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,24 +10,27 @@
|
|||
*/
|
||||
|
||||
case OP_PROC_SYSTEM: {
|
||||
/* Pops command string; pushes exit code number */
|
||||
Value cmdv = pop_value(vm);
|
||||
char *cmd = value_to_string_alloc(&cmdv);
|
||||
free_value(cmdv);
|
||||
if (!cmd) {
|
||||
push_value(vm, make_int(-1));
|
||||
break;
|
||||
}
|
||||
int status = system(cmd);
|
||||
int code = -1;
|
||||
#ifdef __unix__
|
||||
if (status == -1) code = -1;
|
||||
else if (WIFEXITED(status)) code = WEXITSTATUS(status);
|
||||
else code = -1;
|
||||
#else
|
||||
code = status;
|
||||
#endif
|
||||
push_value(vm, make_int(code));
|
||||
free(cmd);
|
||||
/* Pops command string; pushes exit code number */
|
||||
Value cmdv = pop_value(vm);
|
||||
char *cmd = value_to_string_alloc(&cmdv);
|
||||
free_value(cmdv);
|
||||
if (!cmd) {
|
||||
push_value(vm, make_int(-1));
|
||||
break;
|
||||
}
|
||||
int status = system(cmd);
|
||||
int code = -1;
|
||||
#ifdef __unix__
|
||||
if (status == -1)
|
||||
code = -1;
|
||||
else if (WIFEXITED(status))
|
||||
code = WEXITSTATUS(status);
|
||||
else
|
||||
code = -1;
|
||||
#else
|
||||
code = status;
|
||||
#endif
|
||||
push_value(vm, make_int(code));
|
||||
free(cmd);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
*
|
||||
* Added: 2025-12-12
|
||||
*/
|
||||
|
||||
|
|
@ -22,121 +22,123 @@
|
|||
|
||||
/* Platform-specific headers guarded per OS to avoid leaking problematic macros */
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <windows.h>
|
||||
#include <bcrypt.h>
|
||||
#include <bcrypt.h>
|
||||
#include <windows.h>
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
|
||||
#include <stdlib.h>
|
||||
#include <sys/random.h>
|
||||
/* On some BSDs, arc4random_buf might be hidden by _POSIX_C_SOURCE. */
|
||||
void arc4random_buf(void *, size_t);
|
||||
#include <stdlib.h>
|
||||
#include <sys/random.h>
|
||||
/* On some BSDs, arc4random_buf might be hidden by _POSIX_C_SOURCE. */
|
||||
void arc4random_buf(void *, size_t);
|
||||
#elif defined(__unix__)
|
||||
#if __has_include(<sys/random.h>)
|
||||
#include <sys/random.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#if __has_include(<sys/random.h>)
|
||||
#include <sys/random.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
case OP_RANDOM_NUMBER: {
|
||||
/* pop requested raw byte length */
|
||||
Value lv = pop_value(vm);
|
||||
if (lv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: random_number(len) expects integer length\n");
|
||||
free_value(lv);
|
||||
/* For safety, push empty string so callers expecting a value won't underflow */
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
int64_t len = lv.i;
|
||||
if (len < 0) {
|
||||
fprintf(stderr, "random_number error: negative length (%" PRId64 ")\n", len);
|
||||
free_value(lv);
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
if (len == 0) {
|
||||
free_value(lv);
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
/* pop requested raw byte length */
|
||||
Value lv = pop_value(vm);
|
||||
if (lv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: random_number(len) expects integer length\n");
|
||||
free_value(lv);
|
||||
/* For safety, push empty string so callers expecting a value won't underflow */
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
int64_t len = lv.i;
|
||||
if (len < 0) {
|
||||
fprintf(stderr, "random_number error: negative length (%" PRId64 ")\n", len);
|
||||
free_value(lv);
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
if (len == 0) {
|
||||
free_value(lv);
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Cap to prevent excessive allocations (max 1 MiB raw -> 2 MiB hex) */
|
||||
const int64_t MAX_RAW = (1LL << 20);
|
||||
if (len > MAX_RAW) {
|
||||
fprintf(stderr, "random_number error: requested length too large (%" PRId64 ", max %" PRId64 ")\n", len, MAX_RAW);
|
||||
free_value(lv);
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
/* Cap to prevent excessive allocations (max 1 MiB raw -> 2 MiB hex) */
|
||||
const int64_t MAX_RAW = (1LL << 20);
|
||||
if (len > MAX_RAW) {
|
||||
fprintf(stderr, "random_number error: requested length too large (%" PRId64 ", max %" PRId64 ")\n", len, MAX_RAW);
|
||||
free_value(lv);
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned char *raw = (unsigned char*)malloc((size_t)len);
|
||||
char *hex = (char*)malloc((size_t)len * 2 + 1);
|
||||
if (!raw || !hex) {
|
||||
if (raw) free(raw);
|
||||
if (hex) free(hex);
|
||||
free_value(lv);
|
||||
fprintf(stderr, "Out of memory in random_number\n");
|
||||
exit(1);
|
||||
}
|
||||
unsigned char *raw = (unsigned char *)malloc((size_t)len);
|
||||
char *hex = (char *)malloc((size_t)len * 2 + 1);
|
||||
if (!raw || !hex) {
|
||||
if (raw) free(raw);
|
||||
if (hex) free(hex);
|
||||
free_value(lv);
|
||||
fprintf(stderr, "Out of memory in random_number\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int ok = 0;
|
||||
int ok = 0;
|
||||
|
||||
/* --- Fill raw with cryptographically secure random bytes from the OS --- */
|
||||
/* --- Fill raw with cryptographically secure random bytes from the OS --- */
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
{
|
||||
/* Windows: use BCryptGenRandom */
|
||||
NTSTATUS st = BCryptGenRandom(NULL, raw, (ULONG)len, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
||||
ok = (st == 0);
|
||||
}
|
||||
{
|
||||
/* Windows: use BCryptGenRandom */
|
||||
NTSTATUS st = BCryptGenRandom(NULL, raw, (ULONG)len, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
||||
ok = (st == 0);
|
||||
}
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
|
||||
{
|
||||
arc4random_buf(raw, (size_t)len);
|
||||
ok = 1;
|
||||
}
|
||||
{
|
||||
arc4random_buf(raw, (size_t)len);
|
||||
ok = 1;
|
||||
}
|
||||
#elif defined(__unix__)
|
||||
{
|
||||
/* Prefer getrandom if available, otherwise /dev/urandom */
|
||||
#if __has_include(<sys/random.h>)
|
||||
ssize_t n = getrandom(raw, (size_t)len, 0);
|
||||
ok = (n == (ssize_t)len);
|
||||
#else
|
||||
ok = 0;
|
||||
#endif
|
||||
if (!ok) {
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
size_t off = 0; ssize_t n;
|
||||
while (off < (size_t)len && (n = read(fd, raw + off, (size_t)len - off)) > 0) off += (size_t)n;
|
||||
close(fd);
|
||||
ok = (off == (size_t)len);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
/* Prefer getrandom if available, otherwise /dev/urandom */
|
||||
#if __has_include(<sys/random.h>)
|
||||
ssize_t n = getrandom(raw, (size_t)len, 0);
|
||||
ok = (n == (ssize_t)len);
|
||||
#else
|
||||
ok = 0;
|
||||
#endif
|
||||
|
||||
if (!ok) {
|
||||
free(raw);
|
||||
free(hex);
|
||||
free_value(lv);
|
||||
fprintf(stderr, "random_number error: OS RNG unavailable or failed\n");
|
||||
exit(1);
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
size_t off = 0;
|
||||
ssize_t n;
|
||||
while (off < (size_t)len && (n = read(fd, raw + off, (size_t)len - off)) > 0)
|
||||
off += (size_t)n;
|
||||
close(fd);
|
||||
ok = (off == (size_t)len);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
ok = 0;
|
||||
#endif
|
||||
|
||||
/* hex encode */
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
for (int64_t i = 0; i < len; ++i) {
|
||||
unsigned char b = raw[i];
|
||||
hex[2*i] = hexdig[(b >> 4) & 0xF];
|
||||
hex[2*i+1] = hexdig[b & 0xF];
|
||||
}
|
||||
hex[len * 2] = '\0';
|
||||
|
||||
Value s = make_string(hex);
|
||||
if (!ok) {
|
||||
free(raw);
|
||||
free(hex);
|
||||
free_value(lv);
|
||||
push_value(vm, s);
|
||||
break;
|
||||
fprintf(stderr, "random_number error: OS RNG unavailable or failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* hex encode */
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
for (int64_t i = 0; i < len; ++i) {
|
||||
unsigned char b = raw[i];
|
||||
hex[2 * i] = hexdig[(b >> 4) & 0xF];
|
||||
hex[2 * i + 1] = hexdig[b & 0xF];
|
||||
}
|
||||
hex[len * 2] = '\0';
|
||||
|
||||
Value s = make_string(hex);
|
||||
free(raw);
|
||||
free(hex);
|
||||
free_value(lv);
|
||||
push_value(vm, s);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,18 +14,18 @@
|
|||
#endif
|
||||
|
||||
case OP_SERIAL_CLOSE: {
|
||||
/* Pops fd (int); returns 1/0 */
|
||||
Value fdv = pop_value(vm);
|
||||
int ok = 0;
|
||||
/* Pops fd (int); returns 1/0 */
|
||||
Value fdv = pop_value(vm);
|
||||
int ok = 0;
|
||||
#ifdef __unix__
|
||||
if (fdv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: serial_close expects (int fd)\n");
|
||||
} else {
|
||||
int fd = (int)fdv.i;
|
||||
if (close(fd) == 0) ok = 1;
|
||||
}
|
||||
if (fdv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: serial_close expects (int fd)\n");
|
||||
} else {
|
||||
int fd = (int)fdv.i;
|
||||
if (close(fd) == 0) ok = 1;
|
||||
}
|
||||
#endif
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(ok));
|
||||
break;
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(ok));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,77 +14,86 @@
|
|||
#endif
|
||||
|
||||
case OP_SERIAL_CONFIG: {
|
||||
/* Pops flow_control (int), stop_bits (int), parity (int), data_bits (int), fd (int); returns 1/0 */
|
||||
Value flowv = pop_value(vm);
|
||||
Value stopv = pop_value(vm);
|
||||
Value parityv = pop_value(vm);
|
||||
Value datav = pop_value(vm);
|
||||
Value fdv = pop_value(vm);
|
||||
int ok = 0;
|
||||
/* Pops flow_control (int), stop_bits (int), parity (int), data_bits (int), fd (int); returns 1/0 */
|
||||
Value flowv = pop_value(vm);
|
||||
Value stopv = pop_value(vm);
|
||||
Value parityv = pop_value(vm);
|
||||
Value datav = pop_value(vm);
|
||||
Value fdv = pop_value(vm);
|
||||
int ok = 0;
|
||||
#ifdef __unix__
|
||||
if (flowv.type != VAL_INT || stopv.type != VAL_INT || parityv.type != VAL_INT ||
|
||||
datav.type != VAL_INT || fdv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: serial_config expects (int fd, int data_bits, int parity, int stop_bits, int flow_control)\n");
|
||||
} else {
|
||||
int fd = (int)fdv.i;
|
||||
int data_bits = (int)datav.i;
|
||||
int parity = (int)parityv.i;
|
||||
int stop_bits = (int)stopv.i;
|
||||
int flow = (int)flowv.i;
|
||||
if (flowv.type != VAL_INT || stopv.type != VAL_INT || parityv.type != VAL_INT ||
|
||||
datav.type != VAL_INT || fdv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: serial_config expects (int fd, int data_bits, int parity, int stop_bits, int flow_control)\n");
|
||||
} else {
|
||||
int fd = (int)fdv.i;
|
||||
int data_bits = (int)datav.i;
|
||||
int parity = (int)parityv.i;
|
||||
int stop_bits = (int)stopv.i;
|
||||
int flow = (int)flowv.i;
|
||||
|
||||
struct termios options;
|
||||
if (tcgetattr(fd, &options) == 0) {
|
||||
// Data bits
|
||||
options.c_cflag &= ~CSIZE;
|
||||
switch (data_bits) {
|
||||
case 5: options.c_cflag |= CS5; break;
|
||||
case 6: options.c_cflag |= CS6; break;
|
||||
case 7: options.c_cflag |= CS7; break;
|
||||
case 8: default: options.c_cflag |= CS8; break;
|
||||
}
|
||||
struct termios options;
|
||||
if (tcgetattr(fd, &options) == 0) {
|
||||
// Data bits
|
||||
options.c_cflag &= ~CSIZE;
|
||||
switch (data_bits) {
|
||||
case 5:
|
||||
options.c_cflag |= CS5;
|
||||
break;
|
||||
case 6:
|
||||
options.c_cflag |= CS6;
|
||||
break;
|
||||
case 7:
|
||||
options.c_cflag |= CS7;
|
||||
break;
|
||||
case 8:
|
||||
default:
|
||||
options.c_cflag |= CS8;
|
||||
break;
|
||||
}
|
||||
|
||||
// Parity
|
||||
switch (parity) {
|
||||
case 0: // None
|
||||
options.c_cflag &= ~PARENB;
|
||||
break;
|
||||
case 1: // Odd
|
||||
options.c_cflag |= PARENB;
|
||||
options.c_cflag |= PARODD;
|
||||
break;
|
||||
case 2: // Even
|
||||
options.c_cflag |= PARENB;
|
||||
options.c_cflag &= ~PARODD;
|
||||
break;
|
||||
}
|
||||
// Parity
|
||||
switch (parity) {
|
||||
case 0: // None
|
||||
options.c_cflag &= ~PARENB;
|
||||
break;
|
||||
case 1: // Odd
|
||||
options.c_cflag |= PARENB;
|
||||
options.c_cflag |= PARODD;
|
||||
break;
|
||||
case 2: // Even
|
||||
options.c_cflag |= PARENB;
|
||||
options.c_cflag &= ~PARODD;
|
||||
break;
|
||||
}
|
||||
|
||||
// Stop bits
|
||||
if (stop_bits == 2) {
|
||||
options.c_cflag |= CSTOPB;
|
||||
} else {
|
||||
options.c_cflag &= ~CSTOPB;
|
||||
}
|
||||
// Stop bits
|
||||
if (stop_bits == 2) {
|
||||
options.c_cflag |= CSTOPB;
|
||||
} else {
|
||||
options.c_cflag &= ~CSTOPB;
|
||||
}
|
||||
|
||||
// Flow control
|
||||
// Flow control
|
||||
#ifdef CRTSCTS
|
||||
if (flow == 1) { // Hardware (RTS/CTS)
|
||||
options.c_cflag |= CRTSCTS;
|
||||
} else {
|
||||
options.c_cflag &= ~CRTSCTS;
|
||||
}
|
||||
if (flow == 1) { // Hardware (RTS/CTS)
|
||||
options.c_cflag |= CRTSCTS;
|
||||
} else {
|
||||
options.c_cflag &= ~CRTSCTS;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (tcsetattr(fd, TCSANOW, &options) == 0) {
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
if (tcsetattr(fd, TCSANOW, &options) == 0) {
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
free_value(flowv);
|
||||
free_value(stopv);
|
||||
free_value(parityv);
|
||||
free_value(datav);
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(ok));
|
||||
break;
|
||||
free_value(flowv);
|
||||
free_value(stopv);
|
||||
free_value(parityv);
|
||||
free_value(datav);
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(ok));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,67 +76,105 @@
|
|||
#endif
|
||||
|
||||
case OP_SERIAL_OPEN: {
|
||||
/* Pops baud_rate (int), path (string); returns fd (int) or 0 */
|
||||
Value baudv = pop_value(vm);
|
||||
Value pathv = pop_value(vm);
|
||||
int fd = 0;
|
||||
/* Pops baud_rate (int), path (string); returns fd (int) or 0 */
|
||||
Value baudv = pop_value(vm);
|
||||
Value pathv = pop_value(vm);
|
||||
int fd = 0;
|
||||
#ifdef __unix__
|
||||
if (baudv.type != VAL_INT || pathv.type != VAL_STRING) {
|
||||
fprintf(stderr, "Runtime type error: serial_open expects (string path, int baud_rate)\n");
|
||||
free_value(baudv);
|
||||
free_value(pathv);
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
|
||||
const char *path = pathv.s ? pathv.s : "";
|
||||
int baud = (int)baudv.i;
|
||||
speed_t speed;
|
||||
|
||||
switch (baud) {
|
||||
case 50: speed = B50; break;
|
||||
case 75: speed = B75; break;
|
||||
case 110: speed = B110; break;
|
||||
case 134: speed = B134; break;
|
||||
case 150: speed = B150; break;
|
||||
case 200: speed = B200; break;
|
||||
case 300: speed = B300; break;
|
||||
case 600: speed = B600; break;
|
||||
case 1200: speed = B1200; break;
|
||||
case 1800: speed = B1800; break;
|
||||
case 2400: speed = B2400; break;
|
||||
case 4800: speed = B4800; break;
|
||||
case 9600: speed = B9600; break;
|
||||
case 19200: speed = B19200; break;
|
||||
case 38400: speed = B38400; break;
|
||||
case 57600: speed = B57600; break;
|
||||
case 115200: speed = B115200; break;
|
||||
case 230400: speed = B230400; break;
|
||||
default: speed = B9600; break;
|
||||
}
|
||||
|
||||
fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY);
|
||||
if (fd != -1) {
|
||||
struct termios options;
|
||||
tcgetattr(fd, &options);
|
||||
cfsetispeed(&options, speed);
|
||||
cfsetospeed(&options, speed);
|
||||
options.c_cflag |= (CLOCAL | CREAD);
|
||||
options.c_cflag &= ~PARENB;
|
||||
options.c_cflag &= ~CSTOPB;
|
||||
options.c_cflag &= ~CSIZE;
|
||||
options.c_cflag |= CS8;
|
||||
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||
options.c_iflag &= ~(IXON | IXOFF | IXANY);
|
||||
options.c_oflag &= ~OPOST;
|
||||
tcsetattr(fd, TCSANOW, &options);
|
||||
fcntl(fd, F_SETFL, 0); // block on read
|
||||
} else {
|
||||
fd = 0;
|
||||
}
|
||||
#endif
|
||||
if (baudv.type != VAL_INT || pathv.type != VAL_STRING) {
|
||||
fprintf(stderr, "Runtime type error: serial_open expects (string path, int baud_rate)\n");
|
||||
free_value(baudv);
|
||||
free_value(pathv);
|
||||
push_value(vm, make_int(fd > 0 ? fd : 0));
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
|
||||
const char *path = pathv.s ? pathv.s : "";
|
||||
int baud = (int)baudv.i;
|
||||
speed_t speed;
|
||||
|
||||
switch (baud) {
|
||||
case 50:
|
||||
speed = B50;
|
||||
break;
|
||||
case 75:
|
||||
speed = B75;
|
||||
break;
|
||||
case 110:
|
||||
speed = B110;
|
||||
break;
|
||||
case 134:
|
||||
speed = B134;
|
||||
break;
|
||||
case 150:
|
||||
speed = B150;
|
||||
break;
|
||||
case 200:
|
||||
speed = B200;
|
||||
break;
|
||||
case 300:
|
||||
speed = B300;
|
||||
break;
|
||||
case 600:
|
||||
speed = B600;
|
||||
break;
|
||||
case 1200:
|
||||
speed = B1200;
|
||||
break;
|
||||
case 1800:
|
||||
speed = B1800;
|
||||
break;
|
||||
case 2400:
|
||||
speed = B2400;
|
||||
break;
|
||||
case 4800:
|
||||
speed = B4800;
|
||||
break;
|
||||
case 9600:
|
||||
speed = B9600;
|
||||
break;
|
||||
case 19200:
|
||||
speed = B19200;
|
||||
break;
|
||||
case 38400:
|
||||
speed = B38400;
|
||||
break;
|
||||
case 57600:
|
||||
speed = B57600;
|
||||
break;
|
||||
case 115200:
|
||||
speed = B115200;
|
||||
break;
|
||||
case 230400:
|
||||
speed = B230400;
|
||||
break;
|
||||
default:
|
||||
speed = B9600;
|
||||
break;
|
||||
}
|
||||
|
||||
fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY);
|
||||
if (fd != -1) {
|
||||
struct termios options;
|
||||
tcgetattr(fd, &options);
|
||||
cfsetispeed(&options, speed);
|
||||
cfsetospeed(&options, speed);
|
||||
options.c_cflag |= (CLOCAL | CREAD);
|
||||
options.c_cflag &= ~PARENB;
|
||||
options.c_cflag &= ~CSTOPB;
|
||||
options.c_cflag &= ~CSIZE;
|
||||
options.c_cflag |= CS8;
|
||||
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||
options.c_iflag &= ~(IXON | IXOFF | IXANY);
|
||||
options.c_oflag &= ~OPOST;
|
||||
tcsetattr(fd, TCSANOW, &options);
|
||||
fcntl(fd, F_SETFL, 0); // block on read
|
||||
} else {
|
||||
fd = 0;
|
||||
}
|
||||
#endif
|
||||
free_value(baudv);
|
||||
free_value(pathv);
|
||||
push_value(vm, make_int(fd > 0 ? fd : 0));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,34 +14,34 @@
|
|||
#endif
|
||||
|
||||
case OP_SERIAL_RECV: {
|
||||
/* Pops maxlen (int), fd (int); returns data (string) */
|
||||
Value maxv = pop_value(vm);
|
||||
Value fdv = pop_value(vm);
|
||||
char *out = NULL;
|
||||
/* Pops maxlen (int), fd (int); returns data (string) */
|
||||
Value maxv = pop_value(vm);
|
||||
Value fdv = pop_value(vm);
|
||||
char *out = NULL;
|
||||
#ifdef __unix__
|
||||
if (fdv.type != VAL_INT || maxv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: serial_recv expects (int fd, int maxlen)\n");
|
||||
} else {
|
||||
int fd = (int)fdv.i;
|
||||
int maxlen = (int)maxv.i;
|
||||
if (maxlen <= 0) maxlen = 4096;
|
||||
if (maxlen > 1<<20) maxlen = 1<<20; /* cap at 1MB */
|
||||
out = (char*)malloc((size_t)maxlen + 1);
|
||||
if (out) {
|
||||
ssize_t n = read(fd, out, (size_t)maxlen);
|
||||
if (n <= 0) {
|
||||
free(out);
|
||||
out = NULL;
|
||||
} else {
|
||||
out[n] = '\0';
|
||||
}
|
||||
}
|
||||
if (fdv.type != VAL_INT || maxv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: serial_recv expects (int fd, int maxlen)\n");
|
||||
} else {
|
||||
int fd = (int)fdv.i;
|
||||
int maxlen = (int)maxv.i;
|
||||
if (maxlen <= 0) maxlen = 4096;
|
||||
if (maxlen > 1 << 20) maxlen = 1 << 20; /* cap at 1MB */
|
||||
out = (char *)malloc((size_t)maxlen + 1);
|
||||
if (out) {
|
||||
ssize_t n = read(fd, out, (size_t)maxlen);
|
||||
if (n <= 0) {
|
||||
free(out);
|
||||
out = NULL;
|
||||
} else {
|
||||
out[n] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
free_value(maxv);
|
||||
free_value(fdv);
|
||||
Value s = make_string(out ? out : "");
|
||||
if (out) free(out);
|
||||
push_value(vm, s);
|
||||
break;
|
||||
free_value(maxv);
|
||||
free_value(fdv);
|
||||
Value s = make_string(out ? out : "");
|
||||
if (out) free(out);
|
||||
push_value(vm, s);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,23 +14,23 @@
|
|||
#endif
|
||||
|
||||
case OP_SERIAL_SEND: {
|
||||
/* Pops data (string), fd (int); returns bytes sent (int) */
|
||||
Value datav = pop_value(vm);
|
||||
Value fdv = pop_value(vm);
|
||||
int sent = -1;
|
||||
/* Pops data (string), fd (int); returns bytes sent (int) */
|
||||
Value datav = pop_value(vm);
|
||||
Value fdv = pop_value(vm);
|
||||
int sent = -1;
|
||||
#ifdef __unix__
|
||||
if (fdv.type != VAL_INT || datav.type != VAL_STRING) {
|
||||
fprintf(stderr, "Runtime type error: serial_send expects (int fd, string data)\n");
|
||||
} else {
|
||||
int fd = (int)fdv.i;
|
||||
const char *buf = datav.s ? datav.s : "";
|
||||
size_t len = strlen(buf);
|
||||
ssize_t n = write(fd, buf, len);
|
||||
if (n >= 0) sent = (int)n;
|
||||
}
|
||||
if (fdv.type != VAL_INT || datav.type != VAL_STRING) {
|
||||
fprintf(stderr, "Runtime type error: serial_send expects (int fd, string data)\n");
|
||||
} else {
|
||||
int fd = (int)fdv.i;
|
||||
const char *buf = datav.s ? datav.s : "";
|
||||
size_t len = strlen(buf);
|
||||
ssize_t n = write(fd, buf, len);
|
||||
if (n >= 0) sent = (int)n;
|
||||
}
|
||||
#endif
|
||||
free_value(datav);
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(sent));
|
||||
break;
|
||||
free_value(datav);
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(sent));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,18 +10,18 @@
|
|||
*/
|
||||
|
||||
case OP_SLEEP_MS: {
|
||||
Value ms = pop_value(vm);
|
||||
if (ms.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: sleep(ms) expects Number (milliseconds)\n");
|
||||
free_value(ms);
|
||||
/* push Nil so caller-side POP is safe */
|
||||
push_value(vm, make_nil());
|
||||
break;
|
||||
}
|
||||
long t = (long)ms.i;
|
||||
if (t > 0) fun_sleep_ms(t);
|
||||
Value ms = pop_value(vm);
|
||||
if (ms.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: sleep(ms) expects Number (milliseconds)\n");
|
||||
free_value(ms);
|
||||
/* push Nil so statement POP does not underflow */
|
||||
/* push Nil so caller-side POP is safe */
|
||||
push_value(vm, make_nil());
|
||||
break;
|
||||
}
|
||||
long t = (long)ms.i;
|
||||
if (t > 0) fun_sleep_ms(t);
|
||||
free_value(ms);
|
||||
/* push Nil so statement POP does not underflow */
|
||||
push_value(vm, make_nil());
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,20 +10,20 @@
|
|||
*/
|
||||
|
||||
case OP_SOCK_CLOSE: {
|
||||
/* Pops fd; returns 1/0 */
|
||||
Value fdv = pop_value(vm);
|
||||
int ok = 0;
|
||||
/* Pops fd; returns 1/0 */
|
||||
Value fdv = pop_value(vm);
|
||||
int ok = 0;
|
||||
#ifdef __unix__
|
||||
if (fdv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: sock_close expects (int fd)\n");
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
int fd = (int)fdv.i;
|
||||
ok = (close(fd) == 0) ? 1 : 0;
|
||||
#endif
|
||||
if (fdv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: sock_close expects (int fd)\n");
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(ok));
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
int fd = (int)fdv.i;
|
||||
ok = (close(fd) == 0) ? 1 : 0;
|
||||
#endif
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(ok));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,36 +10,37 @@
|
|||
*/
|
||||
|
||||
case OP_SOCK_RECV: {
|
||||
/* Pops maxlen, fd; pushes data string ("" on EOF/error) */
|
||||
Value maxv = pop_value(vm);
|
||||
Value fdv = pop_value(vm);
|
||||
char *out = NULL;
|
||||
/* Pops maxlen, fd; pushes data string ("" on EOF/error) */
|
||||
Value maxv = pop_value(vm);
|
||||
Value fdv = pop_value(vm);
|
||||
char *out = NULL;
|
||||
#ifdef __unix__
|
||||
if (fdv.type != VAL_INT || maxv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: sock_recv expects (int fd, int maxlen)\n");
|
||||
free_value(maxv);
|
||||
free_value(fdv);
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
int fd = (int)fdv.i;
|
||||
int maxlen = (int)maxv.i;
|
||||
if (maxlen <= 0) maxlen = 4096;
|
||||
if (maxlen > 1<<20) maxlen = 1<<20; /* cap at 1MB */
|
||||
out = (char*)malloc((size_t)maxlen + 1);
|
||||
if (out) {
|
||||
ssize_t n = recv(fd, out, (size_t)maxlen, 0);
|
||||
if (n <= 0) {
|
||||
free(out); out = NULL;
|
||||
} else {
|
||||
out[n] = '\0';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (fdv.type != VAL_INT || maxv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: sock_recv expects (int fd, int maxlen)\n");
|
||||
free_value(maxv);
|
||||
free_value(fdv);
|
||||
Value s = make_string(out ? out : "");
|
||||
if (out) free(out);
|
||||
push_value(vm, s);
|
||||
push_value(vm, make_string(""));
|
||||
break;
|
||||
}
|
||||
int fd = (int)fdv.i;
|
||||
int maxlen = (int)maxv.i;
|
||||
if (maxlen <= 0) maxlen = 4096;
|
||||
if (maxlen > 1 << 20) maxlen = 1 << 20; /* cap at 1MB */
|
||||
out = (char *)malloc((size_t)maxlen + 1);
|
||||
if (out) {
|
||||
ssize_t n = recv(fd, out, (size_t)maxlen, 0);
|
||||
if (n <= 0) {
|
||||
free(out);
|
||||
out = NULL;
|
||||
} else {
|
||||
out[n] = '\0';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
free_value(maxv);
|
||||
free_value(fdv);
|
||||
Value s = make_string(out ? out : "");
|
||||
if (out) free(out);
|
||||
push_value(vm, s);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,26 +10,29 @@
|
|||
*/
|
||||
|
||||
case OP_SOCK_SEND: {
|
||||
/* Pops data string, fd; pushes bytes sent (>=0) or -1 */
|
||||
Value datav = pop_value(vm);
|
||||
Value fdv = pop_value(vm);
|
||||
int sent = -1;
|
||||
/* Pops data string, fd; pushes bytes sent (>=0) or -1 */
|
||||
Value datav = pop_value(vm);
|
||||
Value fdv = pop_value(vm);
|
||||
int sent = -1;
|
||||
#ifdef __unix__
|
||||
if (fdv.type != VAL_INT || datav.type != VAL_STRING) {
|
||||
fprintf(stderr, "Runtime type error: sock_send expects (int fd, string data)\n");
|
||||
free_value(datav);
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(-1));
|
||||
break;
|
||||
}
|
||||
int fd = (int)fdv.i;
|
||||
const char *buf = datav.s ? datav.s : "";
|
||||
size_t len = strlen(buf);
|
||||
ssize_t n = send(fd, buf, len, 0);
|
||||
if (n >= 0) sent = (int)n; else sent = -1;
|
||||
#endif
|
||||
if (fdv.type != VAL_INT || datav.type != VAL_STRING) {
|
||||
fprintf(stderr, "Runtime type error: sock_send expects (int fd, string data)\n");
|
||||
free_value(datav);
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(sent));
|
||||
push_value(vm, make_int(-1));
|
||||
break;
|
||||
}
|
||||
int fd = (int)fdv.i;
|
||||
const char *buf = datav.s ? datav.s : "";
|
||||
size_t len = strlen(buf);
|
||||
ssize_t n = send(fd, buf, len, 0);
|
||||
if (n >= 0)
|
||||
sent = (int)n;
|
||||
else
|
||||
sent = -1;
|
||||
#endif
|
||||
free_value(datav);
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(sent));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,21 +10,21 @@
|
|||
*/
|
||||
|
||||
case OP_SOCK_TCP_ACCEPT: {
|
||||
/* Pops listen fd; pushes client fd (>0) or 0 */
|
||||
Value fdv = pop_value(vm);
|
||||
int client = 0;
|
||||
/* Pops listen fd; pushes client fd (>0) or 0 */
|
||||
Value fdv = pop_value(vm);
|
||||
int client = 0;
|
||||
#ifdef __unix__
|
||||
if (fdv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: tcp_accept expects (int listen_fd)\n");
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
int s = (int)fdv.i;
|
||||
int c = accept(s, NULL, NULL);
|
||||
if (c >= 0) client = c;
|
||||
#endif
|
||||
if (fdv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: tcp_accept expects (int listen_fd)\n");
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(client > 0 ? client : 0));
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
int s = (int)fdv.i;
|
||||
int c = accept(s, NULL, NULL);
|
||||
if (c >= 0) client = c;
|
||||
#endif
|
||||
free_value(fdv);
|
||||
push_value(vm, make_int(client > 0 ? client : 0));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,41 +10,44 @@
|
|||
*/
|
||||
|
||||
case OP_SOCK_TCP_CONNECT: {
|
||||
/* Pops port, host; pushes fd (>0) or 0 */
|
||||
Value portv = pop_value(vm);
|
||||
Value hostv = pop_value(vm);
|
||||
int fd = 0;
|
||||
/* Pops port, host; pushes fd (>0) or 0 */
|
||||
Value portv = pop_value(vm);
|
||||
Value hostv = pop_value(vm);
|
||||
int fd = 0;
|
||||
#ifdef __unix__
|
||||
if (hostv.type != VAL_STRING || portv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: tcp_connect expects (string host, int port)\n");
|
||||
free_value(portv);
|
||||
free_value(hostv);
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
char *host = value_to_string_alloc(&hostv);
|
||||
int port = (int)portv.i;
|
||||
if (host) {
|
||||
char portstr[16];
|
||||
snprintf(portstr, sizeof(portstr), "%d", port);
|
||||
struct addrinfo hints, *res = NULL, *rp;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
if (getaddrinfo(host, portstr, &hints, &res) == 0) {
|
||||
for (rp = res; rp != NULL; rp = rp->ai_next) {
|
||||
int s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (s < 0) continue;
|
||||
if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0) { fd = s; break; }
|
||||
close(s);
|
||||
}
|
||||
if (res) freeaddrinfo(res);
|
||||
}
|
||||
free(host);
|
||||
}
|
||||
#endif
|
||||
if (hostv.type != VAL_STRING || portv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: tcp_connect expects (string host, int port)\n");
|
||||
free_value(portv);
|
||||
free_value(hostv);
|
||||
push_value(vm, make_int(fd > 0 ? fd : 0));
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
char *host = value_to_string_alloc(&hostv);
|
||||
int port = (int)portv.i;
|
||||
if (host) {
|
||||
char portstr[16];
|
||||
snprintf(portstr, sizeof(portstr), "%d", port);
|
||||
struct addrinfo hints, *res = NULL, *rp;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
if (getaddrinfo(host, portstr, &hints, &res) == 0) {
|
||||
for (rp = res; rp != NULL; rp = rp->ai_next) {
|
||||
int s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (s < 0) continue;
|
||||
if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0) {
|
||||
fd = s;
|
||||
break;
|
||||
}
|
||||
close(s);
|
||||
}
|
||||
if (res) freeaddrinfo(res);
|
||||
}
|
||||
free(host);
|
||||
}
|
||||
#endif
|
||||
free_value(portv);
|
||||
free_value(hostv);
|
||||
push_value(vm, make_int(fd > 0 ? fd : 0));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,44 +10,44 @@
|
|||
*/
|
||||
|
||||
case OP_SOCK_TCP_LISTEN: {
|
||||
/* Pops backlog, port; pushes listen fd (>0) or 0 */
|
||||
Value backlogv = pop_value(vm);
|
||||
Value portv = pop_value(vm);
|
||||
int fd = 0;
|
||||
/* Pops backlog, port; pushes listen fd (>0) or 0 */
|
||||
Value backlogv = pop_value(vm);
|
||||
Value portv = pop_value(vm);
|
||||
int fd = 0;
|
||||
#ifdef __unix__
|
||||
if (portv.type != VAL_INT || backlogv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: tcp_listen expects (int port, int backlog)\n");
|
||||
free_value(backlogv);
|
||||
free_value(portv);
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
int port = (int)portv.i;
|
||||
int backlog = (int)backlogv.i;
|
||||
int s = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (s >= 0) {
|
||||
int yes = 1;
|
||||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons((uint16_t)port);
|
||||
if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
|
||||
if (listen(s, backlog > 0 ? backlog : 1) == 0) {
|
||||
fd = s;
|
||||
} else {
|
||||
close(s);
|
||||
}
|
||||
} else {
|
||||
close(s);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)fd;
|
||||
#endif
|
||||
if (portv.type != VAL_INT || backlogv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: tcp_listen expects (int port, int backlog)\n");
|
||||
free_value(backlogv);
|
||||
free_value(portv);
|
||||
push_value(vm, make_int(fd > 0 ? fd : 0));
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
int port = (int)portv.i;
|
||||
int backlog = (int)backlogv.i;
|
||||
int s = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (s >= 0) {
|
||||
int yes = 1;
|
||||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons((uint16_t)port);
|
||||
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
|
||||
if (listen(s, backlog > 0 ? backlog : 1) == 0) {
|
||||
fd = s;
|
||||
} else {
|
||||
close(s);
|
||||
}
|
||||
} else {
|
||||
close(s);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)fd;
|
||||
#endif
|
||||
free_value(backlogv);
|
||||
free_value(portv);
|
||||
push_value(vm, make_int(fd > 0 ? fd : 0));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
|
|
@ -11,43 +11,43 @@
|
|||
|
||||
#include <string.h>
|
||||
#ifdef __unix__
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
case OP_SOCK_UNIX_CONNECT: {
|
||||
/* Pops path; returns fd (>0) or 0 */
|
||||
Value pathv = pop_value(vm);
|
||||
int fd = 0;
|
||||
/* Pops path; returns fd (>0) or 0 */
|
||||
Value pathv = pop_value(vm);
|
||||
int fd = 0;
|
||||
#ifdef __unix__
|
||||
if (pathv.type != VAL_STRING) {
|
||||
fprintf(stderr, "Runtime type error: unix_connect expects (string path)\n");
|
||||
free_value(pathv);
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
char *path = value_to_string_alloc(&pathv);
|
||||
if (path) {
|
||||
int s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s >= 0) {
|
||||
struct sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
size_t maxlen = sizeof(addr.sun_path) - 1;
|
||||
strncpy(addr.sun_path, path, maxlen);
|
||||
addr.sun_path[maxlen] = '\0';
|
||||
if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
|
||||
fd = s;
|
||||
} else {
|
||||
close(s);
|
||||
}
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
#endif
|
||||
if (pathv.type != VAL_STRING) {
|
||||
fprintf(stderr, "Runtime type error: unix_connect expects (string path)\n");
|
||||
free_value(pathv);
|
||||
push_value(vm, make_int(fd > 0 ? fd : 0));
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
char *path = value_to_string_alloc(&pathv);
|
||||
if (path) {
|
||||
int s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s >= 0) {
|
||||
struct sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
size_t maxlen = sizeof(addr.sun_path) - 1;
|
||||
strncpy(addr.sun_path, path, maxlen);
|
||||
addr.sun_path[maxlen] = '\0';
|
||||
if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
|
||||
fd = s;
|
||||
} else {
|
||||
close(s);
|
||||
}
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
#endif
|
||||
free_value(pathv);
|
||||
push_value(vm, make_int(fd > 0 ? fd : 0));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,45 +10,45 @@
|
|||
*/
|
||||
|
||||
case OP_SOCK_UNIX_LISTEN: {
|
||||
/* Pops backlog, path; returns listen fd (>0) or 0 */
|
||||
Value backlogv = pop_value(vm);
|
||||
Value pathv = pop_value(vm);
|
||||
int fd = 0;
|
||||
/* Pops backlog, path; returns listen fd (>0) or 0 */
|
||||
Value backlogv = pop_value(vm);
|
||||
Value pathv = pop_value(vm);
|
||||
int fd = 0;
|
||||
#ifdef __unix__
|
||||
if (pathv.type != VAL_STRING || backlogv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: unix_listen expects (string path, int backlog)\n");
|
||||
free_value(backlogv);
|
||||
free_value(pathv);
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
char *path = value_to_string_alloc(&pathv);
|
||||
int backlog = (int)backlogv.i;
|
||||
if (path) {
|
||||
int s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s >= 0) {
|
||||
struct sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
size_t maxlen = sizeof(addr.sun_path) - 1;
|
||||
strncpy(addr.sun_path, path, maxlen);
|
||||
addr.sun_path[maxlen] = '\0';
|
||||
unlink(addr.sun_path); /* best effort */
|
||||
if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
|
||||
if (listen(s, backlog > 0 ? backlog : 1) == 0) {
|
||||
fd = s;
|
||||
} else {
|
||||
close(s);
|
||||
}
|
||||
} else {
|
||||
close(s);
|
||||
}
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
#endif
|
||||
if (pathv.type != VAL_STRING || backlogv.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: unix_listen expects (string path, int backlog)\n");
|
||||
free_value(backlogv);
|
||||
free_value(pathv);
|
||||
push_value(vm, make_int(fd > 0 ? fd : 0));
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
char *path = value_to_string_alloc(&pathv);
|
||||
int backlog = (int)backlogv.i;
|
||||
if (path) {
|
||||
int s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s >= 0) {
|
||||
struct sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
size_t maxlen = sizeof(addr.sun_path) - 1;
|
||||
strncpy(addr.sun_path, path, maxlen);
|
||||
addr.sun_path[maxlen] = '\0';
|
||||
unlink(addr.sun_path); /* best effort */
|
||||
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
|
||||
if (listen(s, backlog > 0 ? backlog : 1) == 0) {
|
||||
fd = s;
|
||||
} else {
|
||||
close(s);
|
||||
}
|
||||
} else {
|
||||
close(s);
|
||||
}
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
#endif
|
||||
free_value(backlogv);
|
||||
free_value(pathv);
|
||||
push_value(vm, make_int(fd > 0 ? fd : 0));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,24 +15,24 @@
|
|||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
typedef HANDLE fun_thread_handle_t;
|
||||
typedef DWORD fun_thread_ret_t;
|
||||
#define FUN_THREAD_CALL WINAPI
|
||||
#define fun_sleep_ms(ms) Sleep((DWORD)(ms))
|
||||
#include <windows.h>
|
||||
typedef HANDLE fun_thread_handle_t;
|
||||
typedef DWORD fun_thread_ret_t;
|
||||
#define FUN_THREAD_CALL WINAPI
|
||||
#define fun_sleep_ms(ms) Sleep((DWORD)(ms))
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
typedef pthread_t fun_thread_handle_t;
|
||||
typedef void* fun_thread_ret_t;
|
||||
#define FUN_THREAD_CALL
|
||||
static inline void fun_sleep_ms(long ms) {
|
||||
if (ms <= 0) return;
|
||||
struct timespec ts;
|
||||
ts.tv_sec = ms / 1000;
|
||||
ts.tv_nsec = (ms % 1000) * 1000000L;
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
typedef pthread_t fun_thread_handle_t;
|
||||
typedef void *fun_thread_ret_t;
|
||||
#define FUN_THREAD_CALL
|
||||
static inline void fun_sleep_ms(long ms) {
|
||||
if (ms <= 0) return;
|
||||
struct timespec ts;
|
||||
ts.tv_sec = ms / 1000;
|
||||
ts.tv_nsec = (ms % 1000) * 1000000L;
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef FUN_MAX_THREADS
|
||||
|
|
@ -40,12 +40,12 @@
|
|||
#endif
|
||||
|
||||
typedef struct {
|
||||
fun_thread_handle_t handle;
|
||||
int used;
|
||||
int done;
|
||||
Value result; /* owned */
|
||||
fun_thread_handle_t handle;
|
||||
int used;
|
||||
int done;
|
||||
Value result; /* owned */
|
||||
#ifdef _WIN32
|
||||
DWORD threadId;
|
||||
DWORD threadId;
|
||||
#endif
|
||||
} FunThreadEntry;
|
||||
|
||||
|
|
@ -55,24 +55,33 @@ static FunThreadEntry g_threads[FUN_MAX_THREADS];
|
|||
static CRITICAL_SECTION g_thr_lock;
|
||||
static int g_thr_lock_inited = 0;
|
||||
static void fun_thr_lock_init(void) {
|
||||
if (!g_thr_lock_inited) {
|
||||
InitializeCriticalSection(&g_thr_lock);
|
||||
g_thr_lock_inited = 1;
|
||||
}
|
||||
if (!g_thr_lock_inited) {
|
||||
InitializeCriticalSection(&g_thr_lock);
|
||||
g_thr_lock_inited = 1;
|
||||
}
|
||||
}
|
||||
static void fun_lock(void) {
|
||||
if (!g_thr_lock_inited) fun_thr_lock_init();
|
||||
EnterCriticalSection(&g_thr_lock);
|
||||
}
|
||||
static void fun_unlock(void) {
|
||||
LeaveCriticalSection(&g_thr_lock);
|
||||
}
|
||||
static void fun_lock(void) { if (!g_thr_lock_inited) fun_thr_lock_init(); EnterCriticalSection(&g_thr_lock); }
|
||||
static void fun_unlock(void) { LeaveCriticalSection(&g_thr_lock); }
|
||||
#else
|
||||
static pthread_mutex_t g_thr_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static void fun_lock(void) { pthread_mutex_lock(&g_thr_lock); }
|
||||
static void fun_unlock(void) { pthread_mutex_unlock(&g_thr_lock); }
|
||||
static void fun_lock(void) {
|
||||
pthread_mutex_lock(&g_thr_lock);
|
||||
}
|
||||
static void fun_unlock(void) {
|
||||
pthread_mutex_unlock(&g_thr_lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
Bytecode *fn; /* function to call */
|
||||
int argc;
|
||||
Value *args; /* array of argc Values (owned by task, will be freed) */
|
||||
int slot; /* registry slot */
|
||||
Bytecode *fn; /* function to call */
|
||||
int argc;
|
||||
Value *args; /* array of argc Values (owned by task, will be freed) */
|
||||
int slot; /* registry slot */
|
||||
} FunTask;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
@ -81,181 +90,201 @@ static fun_thread_ret_t FUN_THREAD_CALL fun_thread_main(LPVOID param)
|
|||
static fun_thread_ret_t fun_thread_main(void *param)
|
||||
#endif
|
||||
{
|
||||
FunTask *task = (FunTask*)param;
|
||||
VM tvm;
|
||||
vm_init(&tvm);
|
||||
FunTask *task = (FunTask *)param;
|
||||
VM tvm;
|
||||
vm_init(&tvm);
|
||||
|
||||
/* Build wrapper: LOAD_CONST <fn>; LOAD_CONST <arg0> ...; CALL argc; HALT */
|
||||
Bytecode *wrap = bytecode_new();
|
||||
int cFn = bytecode_add_constant(wrap, copy_value(&(Value){ .type = VAL_FUNCTION, .fn = task->fn }));
|
||||
bytecode_add_instruction(wrap, OP_LOAD_CONST, cFn);
|
||||
for (int i = 0; i < task->argc; ++i) {
|
||||
int cArg = bytecode_add_constant(wrap, deep_copy_value(&task->args[i]));
|
||||
bytecode_add_instruction(wrap, OP_LOAD_CONST, cArg);
|
||||
}
|
||||
bytecode_add_instruction(wrap, OP_CALL, task->argc);
|
||||
bytecode_add_instruction(wrap, OP_HALT, 0);
|
||||
/* Build wrapper: LOAD_CONST <fn>; LOAD_CONST <arg0> ...; CALL argc; HALT */
|
||||
Bytecode *wrap = bytecode_new();
|
||||
int cFn = bytecode_add_constant(wrap, copy_value(&(Value){.type = VAL_FUNCTION, .fn = task->fn}));
|
||||
bytecode_add_instruction(wrap, OP_LOAD_CONST, cFn);
|
||||
for (int i = 0; i < task->argc; ++i) {
|
||||
int cArg = bytecode_add_constant(wrap, deep_copy_value(&task->args[i]));
|
||||
bytecode_add_instruction(wrap, OP_LOAD_CONST, cArg);
|
||||
}
|
||||
bytecode_add_instruction(wrap, OP_CALL, task->argc);
|
||||
bytecode_add_instruction(wrap, OP_HALT, 0);
|
||||
|
||||
vm_run(&tvm, wrap);
|
||||
vm_run(&tvm, wrap);
|
||||
|
||||
/* Take the top of stack as result if present, else Nil */
|
||||
Value res = make_nil();
|
||||
if (tvm.sp >= 0) {
|
||||
res = deep_copy_value(&tvm.stack[tvm.sp]);
|
||||
}
|
||||
/* Take the top of stack as result if present, else Nil */
|
||||
Value res = make_nil();
|
||||
if (tvm.sp >= 0) {
|
||||
res = deep_copy_value(&tvm.stack[tvm.sp]);
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
for (int i = 0; i < task->argc; ++i) {
|
||||
free_value(task->args[i]);
|
||||
}
|
||||
free(task->args);
|
||||
bytecode_free(wrap);
|
||||
/* cleanup */
|
||||
for (int i = 0; i < task->argc; ++i) {
|
||||
free_value(task->args[i]);
|
||||
}
|
||||
free(task->args);
|
||||
bytecode_free(wrap);
|
||||
|
||||
/* store result */
|
||||
fun_lock();
|
||||
g_threads[task->slot].result = res;
|
||||
g_threads[task->slot].done = 1;
|
||||
fun_unlock();
|
||||
/* store result */
|
||||
fun_lock();
|
||||
g_threads[task->slot].result = res;
|
||||
g_threads[task->slot].done = 1;
|
||||
fun_unlock();
|
||||
|
||||
free(task);
|
||||
free(task);
|
||||
|
||||
#ifdef _WIN32
|
||||
return 0;
|
||||
return 0;
|
||||
#else
|
||||
return NULL;
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int fun_alloc_thread_slot(void) {
|
||||
fun_lock();
|
||||
int idx = -1;
|
||||
for (int i = 0; i < FUN_MAX_THREADS; ++i) {
|
||||
if (!g_threads[i].used) { g_threads[i].used = 1; g_threads[i].done = 0; g_threads[i].result = make_nil(); idx = i; break; }
|
||||
fun_lock();
|
||||
int idx = -1;
|
||||
for (int i = 0; i < FUN_MAX_THREADS; ++i) {
|
||||
if (!g_threads[i].used) {
|
||||
g_threads[i].used = 1;
|
||||
g_threads[i].done = 0;
|
||||
g_threads[i].result = make_nil();
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
fun_unlock();
|
||||
return idx;
|
||||
}
|
||||
fun_unlock();
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int fun_thread_spawn(Value fnVal, Value argsMaybe, int hasArgs) {
|
||||
if (fnVal.type != VAL_FUNCTION || !fnVal.fn) {
|
||||
fprintf(stderr, "Runtime error: thread_spawn expects Function as first argument\n");
|
||||
return 0;
|
||||
}
|
||||
if (fnVal.type != VAL_FUNCTION || !fnVal.fn) {
|
||||
fprintf(stderr, "Runtime error: thread_spawn expects Function as first argument\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Collect args */
|
||||
int argc = 0;
|
||||
Value *args = NULL;
|
||||
/* Collect args */
|
||||
int argc = 0;
|
||||
Value *args = NULL;
|
||||
|
||||
if (hasArgs) {
|
||||
if (argsMaybe.type == VAL_ARRAY && argsMaybe.arr) {
|
||||
int n = array_length(&argsMaybe);
|
||||
if (n > 0) {
|
||||
args = (Value*)calloc((size_t)n, sizeof(Value));
|
||||
if (!args) n = 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value vi;
|
||||
if (array_get_copy(&argsMaybe, i, &vi)) {
|
||||
args[i] = deep_copy_value(&vi);
|
||||
free_value(vi);
|
||||
} else {
|
||||
args[i] = make_nil();
|
||||
}
|
||||
}
|
||||
argc = n;
|
||||
}
|
||||
} else if (argsMaybe.type != VAL_NIL) {
|
||||
args = (Value*)calloc(1, sizeof(Value));
|
||||
if (args) {
|
||||
args[0] = deep_copy_value(&argsMaybe);
|
||||
argc = 1;
|
||||
}
|
||||
if (hasArgs) {
|
||||
if (argsMaybe.type == VAL_ARRAY && argsMaybe.arr) {
|
||||
int n = array_length(&argsMaybe);
|
||||
if (n > 0) {
|
||||
args = (Value *)calloc((size_t)n, sizeof(Value));
|
||||
if (!args) n = 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value vi;
|
||||
if (array_get_copy(&argsMaybe, i, &vi)) {
|
||||
args[i] = deep_copy_value(&vi);
|
||||
free_value(vi);
|
||||
} else {
|
||||
args[i] = make_nil();
|
||||
}
|
||||
}
|
||||
argc = n;
|
||||
}
|
||||
} else if (argsMaybe.type != VAL_NIL) {
|
||||
args = (Value *)calloc(1, sizeof(Value));
|
||||
if (args) {
|
||||
args[0] = deep_copy_value(&argsMaybe);
|
||||
argc = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int slot = fun_alloc_thread_slot();
|
||||
if (slot < 0) {
|
||||
fprintf(stderr, "Runtime error: too many threads\n");
|
||||
/* free args */
|
||||
for (int i = 0; i < argc; ++i) free_value(args[i]);
|
||||
free(args);
|
||||
return 0;
|
||||
}
|
||||
int slot = fun_alloc_thread_slot();
|
||||
if (slot < 0) {
|
||||
fprintf(stderr, "Runtime error: too many threads\n");
|
||||
/* free args */
|
||||
for (int i = 0; i < argc; ++i)
|
||||
free_value(args[i]);
|
||||
free(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FunTask *task = (FunTask*)calloc(1, sizeof(FunTask));
|
||||
if (!task) {
|
||||
for (int i = 0; i < argc; ++i) free_value(args[i]);
|
||||
free(args);
|
||||
fun_lock(); g_threads[slot].used = 0; fun_unlock();
|
||||
return 0;
|
||||
}
|
||||
task->fn = fnVal.fn;
|
||||
task->argc = argc;
|
||||
task->args = args;
|
||||
task->slot = slot;
|
||||
FunTask *task = (FunTask *)calloc(1, sizeof(FunTask));
|
||||
if (!task) {
|
||||
for (int i = 0; i < argc; ++i)
|
||||
free_value(args[i]);
|
||||
free(args);
|
||||
fun_lock();
|
||||
g_threads[slot].used = 0;
|
||||
fun_unlock();
|
||||
return 0;
|
||||
}
|
||||
task->fn = fnVal.fn;
|
||||
task->argc = argc;
|
||||
task->args = args;
|
||||
task->slot = slot;
|
||||
|
||||
#ifdef _WIN32
|
||||
HANDLE h = CreateThread(NULL, 0, fun_thread_main, (LPVOID)task, 0, &g_threads[slot].threadId);
|
||||
if (!h) {
|
||||
fprintf(stderr, "Runtime error: CreateThread failed\n");
|
||||
for (int i = 0; i < argc; ++i) free_value(args[i]);
|
||||
free(args);
|
||||
free(task);
|
||||
fun_lock(); g_threads[slot].used = 0; fun_unlock();
|
||||
return 0;
|
||||
}
|
||||
fun_lock(); g_threads[slot].handle = h; fun_unlock();
|
||||
HANDLE h = CreateThread(NULL, 0, fun_thread_main, (LPVOID)task, 0, &g_threads[slot].threadId);
|
||||
if (!h) {
|
||||
fprintf(stderr, "Runtime error: CreateThread failed\n");
|
||||
for (int i = 0; i < argc; ++i)
|
||||
free_value(args[i]);
|
||||
free(args);
|
||||
free(task);
|
||||
fun_lock();
|
||||
g_threads[slot].used = 0;
|
||||
fun_unlock();
|
||||
return 0;
|
||||
}
|
||||
fun_lock();
|
||||
g_threads[slot].handle = h;
|
||||
fun_unlock();
|
||||
#else
|
||||
pthread_t tid;
|
||||
int rc = pthread_create(&tid, NULL, fun_thread_main, (void*)task);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Runtime error: pthread_create failed\n");
|
||||
for (int i = 0; i < argc; ++i) free_value(args[i]);
|
||||
free(args);
|
||||
free(task);
|
||||
fun_lock(); g_threads[slot].used = 0; fun_unlock();
|
||||
return 0;
|
||||
}
|
||||
fun_lock(); g_threads[slot].handle = tid; fun_unlock();
|
||||
pthread_t tid;
|
||||
int rc = pthread_create(&tid, NULL, fun_thread_main, (void *)task);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Runtime error: pthread_create failed\n");
|
||||
for (int i = 0; i < argc; ++i)
|
||||
free_value(args[i]);
|
||||
free(args);
|
||||
free(task);
|
||||
fun_lock();
|
||||
g_threads[slot].used = 0;
|
||||
fun_unlock();
|
||||
return 0;
|
||||
}
|
||||
fun_lock();
|
||||
g_threads[slot].handle = tid;
|
||||
fun_unlock();
|
||||
#endif
|
||||
|
||||
return slot + 1; /* external thread id: 1..N */
|
||||
return slot + 1; /* external thread id: 1..N */
|
||||
}
|
||||
|
||||
static Value fun_thread_join(int tid) {
|
||||
if (tid <= 0 || tid > FUN_MAX_THREADS) {
|
||||
fprintf(stderr, "Runtime error: thread_join invalid id %d\n", tid);
|
||||
return make_nil();
|
||||
}
|
||||
int idx = tid - 1;
|
||||
if (tid <= 0 || tid > FUN_MAX_THREADS) {
|
||||
fprintf(stderr, "Runtime error: thread_join invalid id %d\n", tid);
|
||||
return make_nil();
|
||||
}
|
||||
int idx = tid - 1;
|
||||
|
||||
#ifdef _WIN32
|
||||
fun_lock();
|
||||
HANDLE h = g_threads[idx].handle;
|
||||
int used = g_threads[idx].used;
|
||||
fun_unlock();
|
||||
if (!used || !h) return make_nil();
|
||||
WaitForSingleObject(h, INFINITE);
|
||||
CloseHandle(h);
|
||||
fun_lock();
|
||||
HANDLE h = g_threads[idx].handle;
|
||||
int used = g_threads[idx].used;
|
||||
fun_unlock();
|
||||
if (!used || !h) return make_nil();
|
||||
WaitForSingleObject(h, INFINITE);
|
||||
CloseHandle(h);
|
||||
#else
|
||||
fun_lock();
|
||||
pthread_t h = g_threads[idx].handle;
|
||||
int used = g_threads[idx].used;
|
||||
fun_unlock();
|
||||
if (!used) return make_nil();
|
||||
pthread_join(h, NULL);
|
||||
fun_lock();
|
||||
pthread_t h = g_threads[idx].handle;
|
||||
int used = g_threads[idx].used;
|
||||
fun_unlock();
|
||||
if (!used) return make_nil();
|
||||
pthread_join(h, NULL);
|
||||
#endif
|
||||
|
||||
/* fetch result and free slot */
|
||||
fun_lock();
|
||||
Value res = deep_copy_value(&g_threads[idx].result);
|
||||
free_value(g_threads[idx].result);
|
||||
g_threads[idx].result = make_nil();
|
||||
g_threads[idx].used = 0;
|
||||
g_threads[idx].done = 0;
|
||||
/* fetch result and free slot */
|
||||
fun_lock();
|
||||
Value res = deep_copy_value(&g_threads[idx].result);
|
||||
free_value(g_threads[idx].result);
|
||||
g_threads[idx].result = make_nil();
|
||||
g_threads[idx].used = 0;
|
||||
g_threads[idx].done = 0;
|
||||
#ifdef _WIN32
|
||||
g_threads[idx].threadId = 0;
|
||||
g_threads[idx].threadId = 0;
|
||||
#endif
|
||||
fun_unlock();
|
||||
fun_unlock();
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@
|
|||
*/
|
||||
|
||||
case OP_THREAD_JOIN: {
|
||||
Value vtid = pop_value(vm);
|
||||
if (vtid.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: thread_join expects thread id (int)\n");
|
||||
push_value(vm, make_nil());
|
||||
free_value(vtid);
|
||||
break;
|
||||
}
|
||||
Value res = fun_thread_join((int)vtid.i);
|
||||
Value vtid = pop_value(vm);
|
||||
if (vtid.type != VAL_INT) {
|
||||
fprintf(stderr, "Runtime type error: thread_join expects thread id (int)\n");
|
||||
push_value(vm, make_nil());
|
||||
free_value(vtid);
|
||||
push_value(vm, res); /* takes ownership */
|
||||
break;
|
||||
}
|
||||
Value res = fun_thread_join((int)vtid.i);
|
||||
free_value(vtid);
|
||||
push_value(vm, res); /* takes ownership */
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@
|
|||
*/
|
||||
|
||||
case OP_THREAD_SPAWN: {
|
||||
/* operand: 0 -> no args; 1 -> has args array or single arg */
|
||||
Value argsMaybe = make_nil();
|
||||
if (inst.operand == 1) {
|
||||
argsMaybe = pop_value(vm); /* maybe array or scalar */
|
||||
}
|
||||
Value fnv = pop_value(vm);
|
||||
int tid = fun_thread_spawn(fnv, argsMaybe, inst.operand == 1);
|
||||
free_value(fnv);
|
||||
if (inst.operand == 1) free_value(argsMaybe);
|
||||
push_value(vm, make_int(tid));
|
||||
break;
|
||||
/* operand: 0 -> no args; 1 -> has args array or single arg */
|
||||
Value argsMaybe = make_nil();
|
||||
if (inst.operand == 1) {
|
||||
argsMaybe = pop_value(vm); /* maybe array or scalar */
|
||||
}
|
||||
Value fnv = pop_value(vm);
|
||||
int tid = fun_thread_spawn(fnv, argsMaybe, inst.operand == 1);
|
||||
free_value(fnv);
|
||||
if (inst.operand == 1) free_value(argsMaybe);
|
||||
push_value(vm, make_int(tid));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
|
|
@ -17,23 +17,23 @@
|
|||
* Stack after: [int ms]
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
case OP_TIME_NOW_MS: {
|
||||
int64_t ms;
|
||||
int64_t ms;
|
||||
#if defined(CLOCK_REALTIME) && !defined(_WIN32)
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
|
||||
ms = (int64_t)ts.tv_sec * 1000 + (int64_t)(ts.tv_nsec / 1000000);
|
||||
} else {
|
||||
time_t s = time(NULL);
|
||||
ms = (int64_t)s * 1000;
|
||||
}
|
||||
#else
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
|
||||
ms = (int64_t)ts.tv_sec * 1000 + (int64_t)(ts.tv_nsec / 1000000);
|
||||
} else {
|
||||
time_t s = time(NULL);
|
||||
ms = (int64_t)s * 1000;
|
||||
}
|
||||
#else
|
||||
time_t s = time(NULL);
|
||||
ms = (int64_t)s * 1000;
|
||||
#endif
|
||||
push_value(vm, make_int(ms));
|
||||
break;
|
||||
push_value(vm, make_int(ms));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue