1
0
Fork 0
forked from fun/fun

Added EXIT opcode to make it possible to return exit codes to the shell. (0.21.4)

This commit is contained in:
Johannes Findeisen 2025-10-04 13:02:29 +02:00
commit 9ac42c6ed1
8 changed files with 96 additions and 5 deletions

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(fun VERSION 0.21.3 LANGUAGES C)
project(fun VERSION 0.21.4 LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

21
examples/exit_example.fun Executable file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env fun
/*
* This file is part of the Fun programming language.
* https://hanez.org/project/fun/
*
* 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-10-04
*/
/* Demonstrate exiting a Fun script with a specific exit code
* Run:
* fun examples/exit_example.fun
* Then in your shell, check the code with: echo $?
*/
/* exit with code 7 */
exit 7

View file

@ -157,7 +157,10 @@ typedef enum {
OP_SOCK_RECV, // pops maxlen, fd; returns data string ("" on EOF/error)
OP_SOCK_CLOSE, // pops fd; returns 1/0
OP_SOCK_UNIX_LISTEN, // pops backlog, path; returns listen fd (>0) or 0
OP_SOCK_UNIX_CONNECT // pops path; returns fd (>0) or 0
OP_SOCK_UNIX_CONNECT, // pops path; returns fd (>0) or 0
// process control
OP_EXIT // pops code (or uses operand) and terminates script with exit code
} OpCode;
typedef struct {

View file

@ -74,7 +74,7 @@ int main(int argc, char **argv) {
vm_print_output(&vm);
vm_clear_output(&vm);
bytecode_free(bc);
return 0;
return vm.exit_code;
}
#ifdef FUN_WITH_REPL

View file

@ -2070,6 +2070,25 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
return;
}
/* exit statement: exit [expr]? */
if (strcmp(name, "exit") == 0) {
free(name);
skip_spaces(src, len, &local_pos);
size_t save_pos = local_pos;
if (emit_expression(bc, src, len, &local_pos)) {
/* expression result already on stack */
} else {
/* default exit code 0 */
local_pos = save_pos;
int ci = bytecode_add_constant(bc, make_int(0));
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
}
bytecode_add_instruction(bc, OP_EXIT, 0);
*pos = local_pos;
skip_to_eol(src, len, pos);
return;
}
/* break / continue */
if (strcmp(name, "break") == 0) {
free(name);

View file

@ -139,6 +139,8 @@ void vm_reset(VM *vm) {
}
// Clear output buffer
vm_clear_output(vm);
// Reset exit code
vm->exit_code = 0;
}
void vm_dump_globals(VM *vm) {
@ -180,6 +182,7 @@ void vm_init(VM *vm) {
vm->fp = -1;
vm->output_count = 0;
vm->instr_count = 0;
vm->exit_code = 0;
for (int i = 0; i < MAX_GLOBALS; ++i)
vm->globals[i] = make_nil();
}
@ -280,6 +283,7 @@ void vm_run(VM *vm, Bytecode *entry) {
#include "vm/core/call.c"
#include "vm/core/dup.c"
#include "vm/core/halt.c"
#include "vm/core/exit.c"
#include "vm/core/jump.c"
#include "vm/core/jump_if_false.c"
#include "vm/core/load_const.c"

View file

@ -39,7 +39,8 @@ static const char *opcode_names[] = {
"THREAD_SPAWN","THREAD_JOIN","SLEEP_MS",
"BAND","BOR","BXOR","BNOT","SHL","SHR","ROTL","ROTR",
"PCSC_ESTABLISH","PCSC_RELEASE","PCSC_LIST_READERS","PCSC_CONNECT","PCSC_DISCONNECT","PCSC_TRANSMIT",
"SOCK_TCP_LISTEN","SOCK_TCP_ACCEPT","SOCK_TCP_CONNECT","SOCK_SEND","SOCK_RECV","SOCK_CLOSE","SOCK_UNIX_LISTEN","SOCK_UNIX_CONNECT"
"SOCK_TCP_LISTEN","SOCK_TCP_ACCEPT","SOCK_TCP_CONNECT","SOCK_SEND","SOCK_RECV","SOCK_CLOSE","SOCK_UNIX_LISTEN","SOCK_UNIX_CONNECT",
"EXIT"
};
typedef struct {
@ -63,6 +64,8 @@ typedef struct {
long long instr_count; // executed instructions in the last vm_run
int current_line; // last executed source line (debug)
int exit_code; // process exit code set by OP_EXIT
} VM;
// initialize VM (zero state)
@ -82,7 +85,7 @@ void vm_dump_globals(VM *vm);
void vm_run(VM *vm, Bytecode *entry);
static inline int opcode_is_valid(int op) {
return op >= OP_NOP && op <= OP_SOCK_UNIX_CONNECT; // all current opcodes
return op >= OP_NOP && op <= OP_EXIT; // all current opcodes
}
#endif

41
src/vm/core/exit.c Normal file
View file

@ -0,0 +1,41 @@
/**
* This file is part of the Fun programming language.
* https://hanez.org/project/fun/
*
* 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-10-04
*/
/**
* @file exit.c
* @brief Implements the OP_EXIT opcode to terminate the script with an exit code.
*
* Behavior:
* - Pops a value from the stack (if available) and converts it to an integer exit code.
* - Sets vm->exit_code.
* - Stops the VM execution immediately (returns from vm_run).
*/
case OP_EXIT: {
int code = 0;
if (vm->sp >= 0) {
Value v = pop_value(vm);
if (v.type == VAL_INT) {
code = (int)v.i;
} else if (v.type == VAL_STRING) {
/* best-effort parse number from string */
code = (int)strtoll(v.s, NULL, 10);
} else if (v.type == VAL_NIL) {
code = 0;
} else {
/* unsupported type for exit; default to 0 */
code = 0;
}
free_value(v);
}
vm->exit_code = code;
return;
}