diff --git a/CMakeLists.txt b/CMakeLists.txt index ffd71d0..d88bac1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(fun VERSION 0.37.48 LANGUAGES C) +project(fun VERSION 0.37.45 LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) diff --git a/examples/class_without_object.fun b/examples/class_without_object.fun deleted file mode 100755 index d60ed75..0000000 --- a/examples/class_without_object.fun +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2025 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-12 - */ - -// Demonstrates the usage of functions in classes without initiating an object. - -include - -print(SHA256().sha256_hex("")) - -/* Expected output: -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -*/ diff --git a/examples/console_prompt.fun b/examples/console_prompt.fun deleted file mode 100755 index 938532e..0000000 --- a/examples/console_prompt.fun +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2025 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-12 - */ - -// Demonstrates the the console prompt. - -include - -input = Console().prompt("fun> ") -print("You entered: " + input) - -/* Possible output: -fun> Fun! -You entered: Fun! -*/ diff --git a/examples/byte_for_demo.fun b/examples/error/byte_for_demo.fun similarity index 100% rename from examples/byte_for_demo.fun rename to examples/error/byte_for_demo.fun diff --git a/examples/progress.fun b/examples/progress.fun deleted file mode 100755 index 82f754b..0000000 --- a/examples/progress.fun +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2025 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-12 - */ - -// Demonstrates system library includes after installation to /usr/lib/fun - -// includes can also be done with a leading # like in C, but some programmers maybe like more clean code without a -// leading #. -include -include // for sleep() - -c = Console() - -print("Progress demo: 0..100") - -total = 100 -for i in range(0, total + 1) - c.progress(i, total, "Downloading") - sleep(30) // milliseconds - -print("\nMultiple phases demo") - -// Phase 1 -phase_total = 40 -for i in range(0, phase_total + 1) - c.progress(i, phase_total, "Phase 1") - sleep(20) - -// Phase 2 -phase_total = 60 -for i in range(0, phase_total + 1) - c.progress(i, phase_total, "Phase 2") - sleep(15) diff --git a/examples/progress_inline.fun b/examples/progress_inline.fun deleted file mode 100644 index 42cb3b8..0000000 --- a/examples/progress_inline.fun +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-13 - */ - -// Realtime progress bar demo without printing extra newlines. -// It only updates a single console line in-place. The progress() -// helper emits one trailing newline automatically at 100%. - -include -include // for sleep() - -c = Console() - -total = 100 -for i in range(0, total + 1) - c.progress(i, total, "Downloading") - sleep(20) // milliseconds - -// Done. No additional prints/newlines here; progress() already -// finalized the line when it reached 100%. diff --git a/lib/io/console.fun b/lib/io/console.fun index c7f76ba..e2a0cd0 100644 --- a/lib/io/console.fun +++ b/lib/io/console.fun @@ -1,18 +1,8 @@ -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2025 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - */ - /* * Console utilities: prompt, ask, and yes/no helpers built on input(). */ #include -#include class Console() // Print a prompt and read a line (no trailing newline) @@ -48,66 +38,3 @@ class Console() else if (a == "n" || a == "no") return 0 // otherwise loop again - - // Internal: best-effort terminal columns via `tput cols` (fallback to 80) - fun term_cols(this) - // Use builtin proc_run directly to avoid any method dispatch quirks - r = proc_run("tput cols") - if (r["code"] == 0) - s = str_trim(r["out"]) // trim trailing newline - if (len(s) > 0) - n = to_number(s) - if (n > 0) - return n - // Fallback if tput not available or not a TTY - return 80 - - // Draw/update a full-width progress bar on a single console line. - // - current: Number (0..total) - // - total: Number (>0) - // - label: Optional text shown left of the bar - // Returns the integer percent [0..100]. Adds a newline automatically at 100%. - fun progress(this, current, total, label) - cur = to_number(current) - tot = to_number(total) - if (tot <= 0) - tot = 1 - pct = to_number((cur * 100) / tot) - if (pct < 0) - pct = 0 - if (pct > 100) - pct = 100 - - cols = this.term_cols() - - lbl = to_string(label) - if (len(lbl) > 0) - prefix = lbl + " " - else - prefix = "" - - perc_str = to_string(pct) + "%" - // We render: [====....] plus percent; keep the whole line within terminal width - // Compute bar width: terminal cols minus prefix, brackets, space, and percent. - fixed = len(prefix) + 2 + 1 + len(perc_str) // [] + space + percent - bar_w = cols - fixed - if (bar_w < 10) - bar_w = 10 // minimal bar width for visibility - - filled = to_number((bar_w * pct) / 100) - if (filled < 0) - filled = 0 - if (filled > bar_w) - filled = bar_w - - bar = "[" + str_repeat("=", filled) + str_repeat(" ", bar_w - filled) + "]" - line = prefix + bar + " " + perc_str - - // In-place update: CR + full-width content (we already fit to cols), no newline - echo("\r" + line) - - // When done, end the line cleanly - if (cur >= tot) - print("") - - return pct diff --git a/src/repl.c b/src/repl.c index 215a38f..9e3c481 100644 --- a/src/repl.c +++ b/src/repl.c @@ -860,8 +860,6 @@ static void show_repl_help(void) { printf(" :frame N Select frame N for :locals/:list/:disas (default: top)\n"); printf(" :list [±K] Show K lines of source around current frame line (default 5)\n"); printf(" :disas [±N] Disassemble around current frame ip (default 5)\n"); - printf(" :disasm WHAT [off [len]] [to ] Hexdump VM memory region to screen or file\n"); - printf(" WHAT = code | stack | globals | consts\n"); printf(" :stack [N] Show top N (default all) stack values\n"); printf(" :top Show the top of the VM stack\n"); printf(" :locals [FRAME] Show locals of frame (default: selected frame)\n"); @@ -900,33 +898,6 @@ static int write_entire_file(const char *path, const char *data, size_t len) { return n == len; } -/* ---------- Hexdump helper ---------- */ -static void hexdump_to(FILE *out, const unsigned char *data, size_t len, size_t base_off) { - if (!out || !data || len == 0) return; - const size_t width = 16; - char ascii[17]; - ascii[16] = '\0'; - for (size_t i = 0; i < len; i += width) { - size_t chunk = (len - i < width) ? (len - i) : width; - /* address */ - fprintf(out, "%08zx ", base_off + i); - /* hex bytes */ - for (size_t j = 0; j < width; ++j) { - if (j < chunk) { - fprintf(out, "%02x ", data[i + j]); - unsigned char c = data[i + j]; - ascii[j] = (c >= 32 && c <= 126) ? (char)c : '.'; - } else { - fputs(" ", out); - ascii[j] = ' '; - } - if (j == 7) fputc(' ', out); /* extra space between 8-byte halves */ - } - ascii[chunk < width ? chunk : width] = '\0'; - fprintf(out, " |%s|\n", ascii); - } -} - static void print_last_n_lines(const char *path, int n) { if (n <= 0) n = 50; size_t flen = 0; @@ -1417,145 +1388,16 @@ int fun_run_repl(VM *vm) { if (idx < 0) { printf("(no current frame)\n"); continue; } Frame *f = &vm->frames[idx]; if (!f->fn) { printf("(no function)\n"); continue; } - - /* Ensure we have a valid instruction buffer */ - if (f->fn->instr_count <= 0 || f->fn->instructions == NULL) { - printf("(no instructions)\n"); - continue; - } - - int count = f->fn->instr_count; - int curip = f->ip - 1; - if (curip < 0) curip = 0; - if (curip >= count) curip = count - 1; - + int curip = f->ip - 1; if (curip < 0) curip = 0; int from = curip - n; if (from < 0) from = 0; - int to = curip + n; if (to >= count) to = count - 1; - if (to < from) { /* nothing to show */ continue; } - + int to = curip + n; if (to >= f->fn->instr_count) to = f->fn->instr_count - 1; for (int i = from; i <= to; ++i) { Instruction ins = f->fn->instructions[i]; - const char *opname = opcode_is_valid(ins.op) ? opcode_names[ins.op] : "???"; + const char *opname = (ins.op >= 0 && ins.op < (int)(sizeof(opcode_names)/sizeof(opcode_names[0]))) + ? opcode_names[ins.op] : "???"; printf("%c %6d: %-14s %d\n", (i == curip ? '>' : ' '), i, opname, ins.operand); } continue; - } else if (strcmp(cmd, "disasm") == 0) { - /* Syntax: :disasm WHAT [off [len]] [to ] - WHAT: code | stack | globals | consts */ - const char *p = lstrip(arg); - if (!p || !*p) { - printf("Usage: :disasm WHAT [off [len]] [to ]\n"); - printf(" WHAT = code | stack | globals | consts\n"); - continue; - } - - char what[32]; - int consumed = 0; - if (sscanf(p, "%31s %n", what, &consumed) != 1) { - printf("Usage: :disasm WHAT [off [len]] [to ]\n"); - continue; - } - p += consumed; - - size_t off = 0; - size_t len = (size_t)-1; /* default later to clamp */ - - /* parse optional off */ - while (*p == ' ' || *p == '\t') p++; - if (*p && (isdigit((unsigned char)*p))) { - char *endp = NULL; - long long v = strtoll(p, &endp, 10); - if (endp && endp != p && v >= 0) { - off = (size_t)v; - p = endp; - } - } - /* parse optional len */ - while (*p == ' ' || *p == '\t') p++; - if (*p && (isdigit((unsigned char)*p))) { - char *endp = NULL; - long long v = strtoll(p, &endp, 10); - if (endp && endp != p && v >= 0) { - len = (size_t)v; - p = endp; - } - } - - /* optional: to */ - while (*p == ' ' || *p == '\t') p++; - int to_file = 0; - char path[PATH_MAX]; - path[0] = '\0'; - if (strncmp(p, "to", 2) == 0 && (p[2] == '\0' || isspace((unsigned char)p[2]))) { - p += 2; - while (*p == ' ' || *p == '\t') p++; - if (!*p) { printf("Missing after 'to'\n"); continue; } - /* read until whitespace end or end of string; simple paths without spaces */ - size_t i = 0; - while (*p && !isspace((unsigned char)*p) && i + 1 < sizeof(path)) path[i++] = *p++; - path[i] = '\0'; - if (path[0] == '\0') { printf("Invalid file path\n"); continue; } - to_file = 1; - } - - const unsigned char *base = NULL; - size_t total = 0; - int ok_region = 1; - - if (strcmp(what, "code") == 0) { - int idx = (selected_frame >= 0 && selected_frame <= vm->fp) ? selected_frame : vm->fp; - if (idx < 0) { printf("(no current frame)\n"); continue; } - Frame *fr = &vm->frames[idx]; - if (!fr->fn || fr->fn->instr_count <= 0 || fr->fn->instructions == NULL) { - printf("(no code to dump)\n"); continue; - } - base = (const unsigned char*)fr->fn->instructions; - total = (size_t)fr->fn->instr_count * sizeof(Instruction); - } else if (strcmp(what, "consts") == 0) { - int idx = (selected_frame >= 0 && selected_frame <= vm->fp) ? selected_frame : vm->fp; - if (idx < 0) { printf("(no current frame)\n"); continue; } - Frame *fr = &vm->frames[idx]; - if (!fr->fn || fr->fn->const_count <= 0 || fr->fn->constants == NULL) { - printf("(no constants to dump)\n"); continue; - } - base = (const unsigned char*)fr->fn->constants; - total = (size_t)fr->fn->const_count * sizeof(Value); - } else if (strcmp(what, "stack") == 0) { - int count = vm->sp + 1; - if (count <= 0) { printf("(stack empty)\n"); continue; } - base = (const unsigned char*)vm->stack; - total = (size_t)count * sizeof(Value); - } else if (strcmp(what, "globals") == 0) { - base = (const unsigned char*)vm->globals; - total = (size_t)MAX_GLOBALS * sizeof(Value); - } else { - ok_region = 0; - } - - if (!ok_region) { - printf("Unknown region '%s'. Use one of: code, stack, globals, consts\n", what); - continue; - } - if (!base || total == 0) { printf("(nothing to dump)\n"); continue; } - - if (off >= total) { printf("(empty range: offset beyond end)\n"); continue; } - size_t avail = total - off; - if (len == (size_t)-1 || len == 0 || len > avail) { - /* default to min(256, avail) */ - len = avail < 256 ? avail : 256; - } - - if (!to_file) { - printf("Hexdump %s: total=%zu, offset=%zu, len=%zu\n", what, total, off, len); - hexdump_to(stdout, base + off, len, off); - } else { - FILE *fout = fopen(path, "wb"); - if (!fout) { printf("Failed to open '%s' for writing\n", path); continue; } - hexdump_to(fout, base + off, len, off); - fclose(fout); - printf("Wrote hexdump (%zu bytes from %s) to %s\n", len, what, path); - } - continue; } else if (strcmp(cmd, "printv") == 0) { const char *spec = lstrip(arg); if (!spec || !*spec) { printf("Usage: :printv local[i] | stack[i] | global[i]\n"); continue; } diff --git a/src/vm.c b/src/vm.c index 61e347f..f21d110 100644 --- a/src/vm.c +++ b/src/vm.c @@ -503,6 +503,10 @@ void vm_print_output(VM *vm) { printf("\n"); } } + /* If the last item was partial (from echo), terminate the line for cleanliness */ + if (vm->output_count > 0 && vm->output_is_partial[vm->output_count - 1]) { + printf("\n"); + } } void vm_run(VM *vm, Bytecode *entry) { @@ -849,18 +853,6 @@ void vm_run(VM *vm, Bytecode *entry) { } break; } - - /* Stream console output in realtime for scripts: - * When PRINT/ECHO pushed items into the VM's output buffer, flush them - * immediately to stdout and clear the buffer to avoid end-of-run bursts. - * This keeps REPL compatibility (REPL still prints after each submit), - * while regular script execution shows progress bars live. - */ - if (inst.op == OP_PRINT || inst.op == OP_ECHO) { - vm_print_output(vm); - vm_clear_output(vm); - fflush(stdout); - } } g_active_vm = NULL; } diff --git a/src/vm/echo.c b/src/vm/echo.c index b29d8e6..0a18101 100644 --- a/src/vm/echo.c +++ b/src/vm/echo.c @@ -1,13 +1,4 @@ -/* -* This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2025 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - */ - - /** +/** * Implements OP_ECHO: print top-of-stack value without trailing newline. * Now stores the value into the VM's output buffer and marks it as partial, * so the CLI can render echo output together with following print output.