Added EXIT opcode to make it possible to return exit codes to the shell. (0.21.4)
This commit is contained in:
parent
e2e2ac6a66
commit
9ac42c6ed1
8 changed files with 96 additions and 5 deletions
|
|
@ -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
21
examples/exit_example.fun
Executable 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
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
19
src/parser.c
19
src/parser.c
|
|
@ -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);
|
||||
|
|
|
|||
4
src/vm.c
4
src/vm.c
|
|
@ -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"
|
||||
|
|
|
|||
7
src/vm.h
7
src/vm.h
|
|
@ -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
41
src/vm/core/exit.c
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue