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)
|
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 11)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
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_RECV, // pops maxlen, fd; returns data string ("" on EOF/error)
|
||||||
OP_SOCK_CLOSE, // pops fd; returns 1/0
|
OP_SOCK_CLOSE, // pops fd; returns 1/0
|
||||||
OP_SOCK_UNIX_LISTEN, // pops backlog, path; returns listen fd (>0) or 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;
|
} OpCode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ int main(int argc, char **argv) {
|
||||||
vm_print_output(&vm);
|
vm_print_output(&vm);
|
||||||
vm_clear_output(&vm);
|
vm_clear_output(&vm);
|
||||||
bytecode_free(bc);
|
bytecode_free(bc);
|
||||||
return 0;
|
return vm.exit_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FUN_WITH_REPL
|
#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;
|
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 */
|
/* break / continue */
|
||||||
if (strcmp(name, "break") == 0) {
|
if (strcmp(name, "break") == 0) {
|
||||||
free(name);
|
free(name);
|
||||||
|
|
|
||||||
4
src/vm.c
4
src/vm.c
|
|
@ -139,6 +139,8 @@ void vm_reset(VM *vm) {
|
||||||
}
|
}
|
||||||
// Clear output buffer
|
// Clear output buffer
|
||||||
vm_clear_output(vm);
|
vm_clear_output(vm);
|
||||||
|
// Reset exit code
|
||||||
|
vm->exit_code = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vm_dump_globals(VM *vm) {
|
void vm_dump_globals(VM *vm) {
|
||||||
|
|
@ -180,6 +182,7 @@ void vm_init(VM *vm) {
|
||||||
vm->fp = -1;
|
vm->fp = -1;
|
||||||
vm->output_count = 0;
|
vm->output_count = 0;
|
||||||
vm->instr_count = 0;
|
vm->instr_count = 0;
|
||||||
|
vm->exit_code = 0;
|
||||||
for (int i = 0; i < MAX_GLOBALS; ++i)
|
for (int i = 0; i < MAX_GLOBALS; ++i)
|
||||||
vm->globals[i] = make_nil();
|
vm->globals[i] = make_nil();
|
||||||
}
|
}
|
||||||
|
|
@ -280,6 +283,7 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
#include "vm/core/call.c"
|
#include "vm/core/call.c"
|
||||||
#include "vm/core/dup.c"
|
#include "vm/core/dup.c"
|
||||||
#include "vm/core/halt.c"
|
#include "vm/core/halt.c"
|
||||||
|
#include "vm/core/exit.c"
|
||||||
#include "vm/core/jump.c"
|
#include "vm/core/jump.c"
|
||||||
#include "vm/core/jump_if_false.c"
|
#include "vm/core/jump_if_false.c"
|
||||||
#include "vm/core/load_const.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",
|
"THREAD_SPAWN","THREAD_JOIN","SLEEP_MS",
|
||||||
"BAND","BOR","BXOR","BNOT","SHL","SHR","ROTL","ROTR",
|
"BAND","BOR","BXOR","BNOT","SHL","SHR","ROTL","ROTR",
|
||||||
"PCSC_ESTABLISH","PCSC_RELEASE","PCSC_LIST_READERS","PCSC_CONNECT","PCSC_DISCONNECT","PCSC_TRANSMIT",
|
"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 {
|
typedef struct {
|
||||||
|
|
@ -63,6 +64,8 @@ typedef struct {
|
||||||
long long instr_count; // executed instructions in the last vm_run
|
long long instr_count; // executed instructions in the last vm_run
|
||||||
|
|
||||||
int current_line; // last executed source line (debug)
|
int current_line; // last executed source line (debug)
|
||||||
|
|
||||||
|
int exit_code; // process exit code set by OP_EXIT
|
||||||
} VM;
|
} VM;
|
||||||
|
|
||||||
// initialize VM (zero state)
|
// initialize VM (zero state)
|
||||||
|
|
@ -82,7 +85,7 @@ void vm_dump_globals(VM *vm);
|
||||||
void vm_run(VM *vm, Bytecode *entry);
|
void vm_run(VM *vm, Bytecode *entry);
|
||||||
|
|
||||||
static inline int opcode_is_valid(int op) {
|
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
|
#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