More notcurses fun. Not stable! (0.39.4)
This commit is contained in:
parent
69b9b87dd4
commit
18ac230c21
19 changed files with 715 additions and 15 deletions
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
project(fun VERSION 0.39.3 LANGUAGES C)
|
||||
project(fun VERSION 0.39.4 LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
|
|
|||
9
docs/external/notcurses.md
vendored
9
docs/external/notcurses.md
vendored
|
|
@ -11,8 +11,17 @@
|
|||
- OP_NC_CLEAR: clear screen/plane; returns 0
|
||||
- OP_NC_DRAW_TEXT: pops text, x, y; draws; returns 0
|
||||
- OP_NC_GETCH: pops timeout_ms; returns codepoint or -1 on timeout/error
|
||||
- OP_NC_GET_SIZE: push [rows, cols] of the stdplane, or -1 if unavailable
|
||||
- OP_NC_SET_STYLE: pops style, bg_rgb, fg_rgb; sets colors/styles for stdplane; returns 0/-1
|
||||
- OP_NC_DRAW_CHAR: pops ch, x, y; draws a single Unicode codepoint; returns 0/-1
|
||||
- OP_NC_DRAW_HLINE: pops len, x, y, ch; draws a horizontal line; returns 0/-1
|
||||
- OP_NC_DRAW_VLINE: pops len, x, y, ch; draws a vertical line; returns 0/-1
|
||||
- OP_NC_BOX: pops x, y, w, h, style; draws a rectangle using ASCII/Unicode box glyphs; returns 0/-1
|
||||
- OP_NC_FILL: pops x, y, w, h, ch; fills an area with a codepoint; returns 0/-1
|
||||
- OP_NC_RENDER: forces a render; returns 0/-1
|
||||
|
||||
## Notes:
|
||||
|
||||
- Requires Notcurses development headers/libs.
|
||||
- Behavior may vary across terminals; see the implementation for details.
|
||||
- When Fun is built without Notcurses (FUN_WITH_NOTCURSES=OFF), these opcodes still exist but perform no-ops and return -1/0 as documented, allowing scripts to degrade gracefully.
|
||||
|
|
|
|||
|
|
@ -232,6 +232,14 @@ This document provides an overview of the available VM opcodes implemented under
|
|||
- OP_NC_CLEAR: Clear screen; pushes 1/0.
|
||||
- OP_NC_DRAW_TEXT: Draw text at (x,y); pops text, x, y; pushes 1/0.
|
||||
- OP_NC_GETCH: Get key with timeout; pops timeout_ms:int; pushes int key or -1.
|
||||
- OP_NC_GET_SIZE: Get stdplane size; pushes [rows:int, cols:int] or -1 if unavailable.
|
||||
- OP_NC_SET_STYLE: Set stdplane style/colors; pops style:int, bg_rgb:int, fg_rgb:int; pushes 0/-1.
|
||||
- OP_NC_DRAW_CHAR: Draw a single codepoint at y,x; pops ch:int, x:int, y:int; pushes 0/-1.
|
||||
- OP_NC_DRAW_HLINE: Draw horizontal line; pops len:int, x:int, y:int, ch:int; pushes 0/-1.
|
||||
- OP_NC_DRAW_VLINE: Draw vertical line; pops len:int, x:int, y:int, ch:int; pushes 0/-1.
|
||||
- OP_NC_BOX: Draw a rectangular box; pops x:int, y:int, w:int, h:int, style:int; pushes 0/-1.
|
||||
- OP_NC_FILL: Fill rectangle with codepoint; pops x:int, y:int, w:int, h:int, ch:int; pushes 0/-1.
|
||||
- OP_NC_RENDER: Force a render; pushes 0/-1.
|
||||
|
||||
## SQLite-compatible lib (libSQL)
|
||||
|
||||
|
|
|
|||
33
examples/extra/notcurses_menu.fun
Normal file
33
examples/extra/notcurses_menu.fun
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
include <ui/notcurses.fun>
|
||||
|
||||
n = Notcurses()
|
||||
if n.init() == 0
|
||||
print("Notcurses not available. Build with -DFUN_WITH_NOTCURSES=ON.")
|
||||
exit(1)
|
||||
|
||||
items = ["First", "Second", "Third", "Fourth"]
|
||||
sel = 0
|
||||
while true
|
||||
n.clear()
|
||||
// Title
|
||||
n.set_style(0xFFFF00, 0x000000, 1) // bold yellow
|
||||
n.draw_text(1, 2, "Demo Menu (j/k to move, Enter to select, ESC to quit)")
|
||||
|
||||
// Render menu list
|
||||
n.menu(3, 4, items, sel, 30, 0, 32)
|
||||
|
||||
key = n.getch(0)
|
||||
if key == 27 // ESC
|
||||
break
|
||||
if key == 106 // 'j'
|
||||
sel = (sel + 1) % len(items)
|
||||
if key == 107 // 'k'
|
||||
sel = (sel - 1 + len(items)) % len(items)
|
||||
if key == 10 // Enter
|
||||
n.status_bar("Selected: " + items[sel], 32)
|
||||
// brief wait
|
||||
sleep_ms(300)
|
||||
|
||||
n.shutdown()
|
||||
38
examples/extra/notcurses_progress.fun
Normal file
38
examples/extra/notcurses_progress.fun
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
include <ui/notcurses.fun>
|
||||
|
||||
n = Notcurses()
|
||||
if n.init() == 0
|
||||
print("Notcurses not available. Build with -DFUN_WITH_NOTCURSES=ON.")
|
||||
exit(1)
|
||||
|
||||
n.clear()
|
||||
sz = n.size()
|
||||
rows = 24
|
||||
cols = 80
|
||||
if typeof(sz) == "array"
|
||||
if len(sz) >= 2
|
||||
rows = sz[0]
|
||||
cols = sz[1]
|
||||
|
||||
y = to_number(rows) / 2
|
||||
w = cols - 10
|
||||
if w < 20
|
||||
w = 20
|
||||
|
||||
i = 0
|
||||
while i <= 100
|
||||
// styles handled inside progress(); keep plane sane
|
||||
rc = n.set_style(0x00FF00, 0x000000, 1)
|
||||
frac = to_number(i) / 100
|
||||
rc = n.progress(y, 5, w, frac, 0)
|
||||
k = n.getch(10)
|
||||
i = i + 1
|
||||
|
||||
// final message
|
||||
rc = n.set_style(0xFFFFFF, 0x000000, 1)
|
||||
rc = n.draw_text(y + 2, 5, "Done!")
|
||||
rc = n.render()
|
||||
|
||||
rc = n.shutdown()
|
||||
|
|
@ -40,3 +40,133 @@ class Notcurses()
|
|||
return nc_shutdown()
|
||||
|
||||
// (banner helper removed temporarily due to parser limitations)
|
||||
|
||||
// --- Extended primitives ---
|
||||
fun size(this)
|
||||
return nc_get_size()
|
||||
|
||||
fun render(this)
|
||||
return nc_render()
|
||||
|
||||
// fg/bg are 0xRRGGBB, style bitmask: bold(1), dim(2), italic(4), underline(8), strike(16), reverse(32)
|
||||
fun set_style(this, fg, bg, style)
|
||||
// parser order for builtin is (style,bg,fg)
|
||||
return nc_set_style(to_number(style), to_number(bg), to_number(fg))
|
||||
|
||||
fun draw_char(this, y, x, ch)
|
||||
return nc_draw_char(to_number(y), to_number(x), to_number(ch))
|
||||
|
||||
fun hline(this, y, x, len, ch)
|
||||
return nc_draw_hline(to_number(y), to_number(x), to_number(len), to_number(ch))
|
||||
|
||||
fun vline(this, y, x, len, ch)
|
||||
return nc_draw_vline(to_number(y), to_number(x), to_number(len), to_number(ch))
|
||||
|
||||
fun box(this, y, x, h, w, style)
|
||||
// builtin expects (x,y,w,h,style)
|
||||
return nc_box(to_number(x), to_number(y), to_number(w), to_number(h), to_number(style))
|
||||
|
||||
fun fill(this, y, x, h, w, ch)
|
||||
// builtin expects (x,y,w,h,ch)
|
||||
return nc_fill(to_number(x), to_number(y), to_number(w), to_number(h), to_number(ch))
|
||||
|
||||
// --- Widgets as methods ---
|
||||
// Status bar on the last row
|
||||
fun status_bar(this, text, style)
|
||||
sz = this.size()
|
||||
if style == nil
|
||||
style = 32
|
||||
rc = this.set_style(0x000000, 0xFFFFFF, style)
|
||||
rc = this.fill(sz[0] - 1, 0, 1, sz[1], 32)
|
||||
rc = this.draw_text(sz[0] - 1, 1, text)
|
||||
rc = this.render()
|
||||
return 0
|
||||
|
||||
// Draw styled label
|
||||
fun label(this, y, x, text, style)
|
||||
if style != nil
|
||||
rc = this.set_style(0xFFFFFF, 0x000000, style)
|
||||
rc = this.draw_text(y, x, text)
|
||||
rc = this.render()
|
||||
return 0
|
||||
|
||||
// Panel with optional title
|
||||
fun box_panel(this, y, x, h, w, title, style)
|
||||
if style == nil
|
||||
style = 0
|
||||
rc = this.box(y, x, h, w, style)
|
||||
if title != nil
|
||||
rc = this.draw_text(y, x + 2, to_string(title))
|
||||
rc = this.render()
|
||||
return 0
|
||||
|
||||
// Progress bar (frac in 0..1)
|
||||
fun progress(this, y, x, w, frac, style)
|
||||
if style == nil
|
||||
style = 0
|
||||
if frac < 0
|
||||
frac = 0
|
||||
if frac > 1
|
||||
frac = 1
|
||||
// frame and title
|
||||
rc = nc_box(to_number(x - 2), to_number(y - 1), to_number(w + 4), 3, to_number(style))
|
||||
// background in dim gray
|
||||
rc = this.set_style(0x606060, 0x000000, 2)
|
||||
rc = nc_fill(to_number(y), to_number(x), to_number(w), 1, 46) // '.'
|
||||
// filled portion in bold green
|
||||
rc = this.set_style(0x00FF00, 0x000000, 1)
|
||||
fw = to_number(frac) * w
|
||||
if fw < 0
|
||||
fw = 0
|
||||
if fw > w
|
||||
fw = w
|
||||
if fw > 0
|
||||
rc = nc_fill(to_number(y), to_number(x), to_number(fw), 1, 35) // '#'
|
||||
rc = nc_draw_text(to_number(y - 1), to_number(x), "Progress")
|
||||
rc = nc_render()
|
||||
return 0
|
||||
|
||||
// Simple vertical menu, highlight selected index
|
||||
fun menu(this, y, x, items, selected_idx, w, style, sel_style)
|
||||
if style == nil
|
||||
style = 0
|
||||
if sel_style == nil
|
||||
sel_style = 32
|
||||
i = 0
|
||||
while i < len(items)
|
||||
if i == selected_idx
|
||||
rc = this.set_style(0x000000, 0xFFFFFF, sel_style)
|
||||
else
|
||||
rc = this.set_style(0xFFFFFF, 0x000000, style)
|
||||
if w != nil
|
||||
if len(to_string(items[i])) > w
|
||||
rc = this.draw_text(y + i, x, substr(to_string(items[i]), 0, w))
|
||||
else
|
||||
rc = this.draw_text(y + i, x, to_string(items[i]))
|
||||
else
|
||||
rc = this.draw_text(y + i, x, to_string(items[i]))
|
||||
i = i + 1
|
||||
rc = this.render()
|
||||
return 0
|
||||
|
||||
// Input line echo with cursor
|
||||
fun input_line(this, y, x, prompt, buffer, cursor_idx, w, style)
|
||||
if style == nil
|
||||
style = 0
|
||||
if w == nil
|
||||
w = len(buffer) + len(prompt) + 2
|
||||
rc = this.set_style(0xFFFFFF, 0x000000, style)
|
||||
rc = this.draw_text(y, x, to_string(prompt))
|
||||
rc = this.fill(y, x + len(prompt), 1, w, 32)
|
||||
rc = this.draw_text(y, x + len(prompt), to_string(buffer))
|
||||
this.set_style(0x000000, 0xFFFFFF, 32)
|
||||
ci = cursor_idx
|
||||
if ci < 0
|
||||
ci = 0
|
||||
if ci > len(buffer)
|
||||
ci = len(buffer)
|
||||
rc = this.draw_char(y, x + len(prompt) + ci, 32)
|
||||
rc = this.render()
|
||||
return 0
|
||||
|
||||
// --- End of class Notcurses ---
|
||||
|
|
|
|||
|
|
@ -290,7 +290,15 @@ typedef enum {
|
|||
OP_NC_SHUTDOWN, // shuts down Notcurses; returns 0
|
||||
OP_NC_CLEAR, // clears screen/plane; returns 0
|
||||
OP_NC_DRAW_TEXT, // pops text, x, y; draws; returns 0
|
||||
OP_NC_GETCH // pops timeout_ms; returns codepoint or -1 on timeout/error
|
||||
OP_NC_GETCH, // pops timeout_ms; returns codepoint or -1 on timeout/error
|
||||
OP_NC_GET_SIZE, // pushes [rows:int, cols:int] or -1 when unavailable
|
||||
OP_NC_SET_STYLE, // pops style:int, bg_rgb:int, fg_rgb:int; returns 0/-1
|
||||
OP_NC_DRAW_CHAR, // pops ch:int, x:int, y:int; returns 0/-1
|
||||
OP_NC_DRAW_HLINE,// pops len:int, x:int, y:int, ch:int; returns 0/-1
|
||||
OP_NC_DRAW_VLINE,// pops len:int, x:int, y:int, ch:int; returns 0/-1
|
||||
OP_NC_BOX, // pops x:int, y:int, w:int, h:int, style:int; returns 0/-1
|
||||
OP_NC_FILL, // pops x:int, y:int, w:int, h:int, ch:int; returns 0/-1
|
||||
OP_NC_RENDER // forces a render; returns 0/-1
|
||||
} OpCode;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/**
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
|
|
|||
130
src/parser.c
130
src/parser.c
|
|
@ -2437,6 +2437,118 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "nc_get_size") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!consume_char(src, len, pos, ')')) {
|
||||
parser_fail(*pos, "nc_get_size expects ()");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
bytecode_add_instruction(bc, OP_NC_GET_SIZE, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "nc_set_style") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
/* (style, bg_rgb, fg_rgb) */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_set_style expects (style, bg_rgb, fg_rgb)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_set_style expects (style, bg_rgb, fg_rgb)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_set_style expects (style, bg_rgb, fg_rgb)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_set_style expects (style, bg_rgb, fg_rgb)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_set_style expects (style, bg_rgb, fg_rgb)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after nc_set_style args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_NC_SET_STYLE, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "nc_draw_char") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
/* (y, x, ch) */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_char expects (y, x, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_draw_char expects (y, x, ch)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_char expects (y, x, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_draw_char expects (y, x, ch)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_char expects (y, x, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after nc_draw_char args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_NC_DRAW_CHAR, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "nc_draw_hline") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
/* (y, x, len, ch) */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_hline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_draw_hline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_hline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_draw_hline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_hline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_draw_hline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_hline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after nc_draw_hline args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_NC_DRAW_HLINE, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "nc_draw_vline") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
/* (y, x, len, ch) */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_vline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_draw_vline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_vline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_draw_vline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_vline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_draw_vline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_draw_vline expects (y, x, len, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after nc_draw_vline args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_NC_DRAW_VLINE, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "nc_box") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
/* (x, y, w, h, style) -- wrapper may reorder */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_box expects (x, y, w, h, style)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_box expects (x, y, w, h, style)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_box expects (x, y, w, h, style)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_box expects (x, y, w, h, style)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_box expects (x, y, w, h, style)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_box expects (x, y, w, h, style)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_box expects (x, y, w, h, style)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_box expects (x, y, w, h, style)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_box expects (x, y, w, h, style)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after nc_box args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_NC_BOX, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "nc_fill") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
/* (x, y, w, h, ch) */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_fill expects (x, y, w, h, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_fill expects (x, y, w, h, ch)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_fill expects (x, y, w, h, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_fill expects (x, y, w, h, ch)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_fill expects (x, y, w, h, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_fill expects (x, y, w, h, ch)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_fill expects (x, y, w, h, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "nc_fill expects (x, y, w, h, ch)"); free(name); return 0; }
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "nc_fill expects (x, y, w, h, ch)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after nc_fill args"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_NC_FILL, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "nc_render") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!consume_char(src, len, pos, ')')) {
|
||||
parser_fail(*pos, "nc_render expects ()");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
bytecode_add_instruction(bc, OP_NC_RENDER, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "pcsc_release") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) {
|
||||
|
|
@ -5686,18 +5798,18 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
*pos = local_pos;
|
||||
skip_to_eol(src, len, pos);
|
||||
return;
|
||||
} else if (local_pos < len && src[local_pos] == '(') {
|
||||
/* call as a statement: compile full call expression and drop result */
|
||||
/* rewind to statement start and emit as expression */
|
||||
} else {
|
||||
/* Treat as a generic expression-statement (e.g., method calls like obj.method(...),
|
||||
property access chains, or builtin calls behind wrappers). We rewind to the
|
||||
start of the statement, emit the full expression, and drop its result. */
|
||||
local_pos = *pos;
|
||||
if (emit_expression(bc, src, len, &local_pos)) {
|
||||
bytecode_add_instruction(bc, OP_POP, 0); /* dApache-2.0ard return value */
|
||||
bytecode_add_instruction(bc, OP_POP, 0); /* discard return value of standalone expr */
|
||||
*pos = local_pos;
|
||||
skip_to_eol(src, len, pos);
|
||||
return;
|
||||
}
|
||||
*pos = local_pos;
|
||||
skip_to_eol(src, len, pos);
|
||||
return;
|
||||
} else {
|
||||
/* invalid statement: identifier not followed by assignment or call */
|
||||
/* if it wasn't a valid expression either, report a meaningful error */
|
||||
parser_fail(local_pos, "Expected assignment '=' or call '(...)' after identifier");
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
8
src/vm.c
8
src/vm.c
|
|
@ -939,6 +939,14 @@ void vm_run(VM *vm, Bytecode *entry) {
|
|||
#include "vm/notcurses/getch.c"
|
||||
#include "vm/notcurses/init.c"
|
||||
#include "vm/notcurses/shutdown.c"
|
||||
#include "vm/notcurses/get_size.c"
|
||||
#include "vm/notcurses/set_style.c"
|
||||
#include "vm/notcurses/draw_char.c"
|
||||
#include "vm/notcurses/hline.c"
|
||||
#include "vm/notcurses/vline.c"
|
||||
#include "vm/notcurses/box.c"
|
||||
#include "vm/notcurses/fill.c"
|
||||
#include "vm/notcurses/render.c"
|
||||
#endif
|
||||
|
||||
/* SQLite ops */
|
||||
|
|
|
|||
6
src/vm.h
6
src/vm.h
|
|
@ -61,7 +61,9 @@ static const char *opcode_names[] = {
|
|||
/* C++ demo */
|
||||
"CPP_ADD",
|
||||
/* Notcurses TUI (optional) */
|
||||
"NC_INIT", "NC_SHUTDOWN", "NC_CLEAR", "NC_DRAW_TEXT", "NC_GETCH"};
|
||||
"NC_INIT", "NC_SHUTDOWN", "NC_CLEAR", "NC_DRAW_TEXT", "NC_GETCH",
|
||||
"NC_GET_SIZE", "NC_SET_STYLE", "NC_DRAW_CHAR", "NC_DRAW_HLINE", "NC_DRAW_VLINE",
|
||||
"NC_BOX", "NC_FILL", "NC_RENDER"};
|
||||
|
||||
typedef struct {
|
||||
Bytecode *fn;
|
||||
|
|
@ -145,7 +147,7 @@ void vm_debug_request_finish(VM *vm);
|
|||
void vm_debug_request_continue(VM *vm);
|
||||
|
||||
static inline int opcode_is_valid(int op) {
|
||||
return op >= OP_NOP && op <= OP_NC_GETCH; // all current opcodes (including optional NC_*)
|
||||
return op >= OP_NOP && op <= OP_NC_RENDER; // all current opcodes (including optional NC_*)
|
||||
}
|
||||
|
||||
/* --- Minimal C ABI helpers for FFI (Rust opcode experiments) --- */
|
||||
|
|
|
|||
66
src/vm/notcurses/box.c
Normal file
66
src/vm/notcurses/box.c
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
||||
/* NC_BOX */
|
||||
case OP_NC_BOX: {
|
||||
Value stylev = pop_value(vm);
|
||||
Value hv = pop_value(vm);
|
||||
Value wv = pop_value(vm);
|
||||
Value yv = pop_value(vm);
|
||||
Value xv = pop_value(vm);
|
||||
int style = (stylev.type == VAL_INT ? (int)stylev.i : (stylev.type == VAL_FLOAT ? (int)stylev.d : 0));
|
||||
int h = (hv.type == VAL_INT ? (int)hv.i : (hv.type == VAL_FLOAT ? (int)hv.d : 0));
|
||||
int w = (wv.type == VAL_INT ? (int)wv.i : (wv.type == VAL_FLOAT ? (int)wv.d : 0));
|
||||
int y = (yv.type == VAL_INT ? (int)yv.i : (yv.type == VAL_FLOAT ? (int)yv.d : 0));
|
||||
int x = (xv.type == VAL_INT ? (int)xv.i : (xv.type == VAL_FLOAT ? (int)xv.d : 0));
|
||||
free_value(stylev); free_value(hv); free_value(wv); free_value(yv); free_value(xv);
|
||||
#ifdef FUN_WITH_NOTCURSES
|
||||
if (_fun_nc && _fun_nc_std && h >= 2 && w >= 2) {
|
||||
/* Pick characters: style 0 light (ASCII), 1 heavy (Unicode), fallback to ASCII */
|
||||
uint32_t hch = (style == 1 ? 0x2501 : '-'); // heavy box drawings horizontal
|
||||
uint32_t vch = (style == 1 ? 0x2503 : '|'); // heavy vertical
|
||||
uint32_t tl = (style == 1 ? 0x250F : '+');
|
||||
uint32_t tr = (style == 1 ? 0x2513 : '+');
|
||||
uint32_t bl = (style == 1 ? 0x2517 : '+');
|
||||
uint32_t br = (style == 1 ? 0x251B : '+');
|
||||
|
||||
/* Draw edges */
|
||||
// top and bottom
|
||||
{
|
||||
char topc[5]; int tlen=0; uint32_t cp=hch;
|
||||
if (cp<=0x7F){topc[0]=(char)cp;tlen=1;} else if (cp<=0x7FF){topc[0]=(char)(0xC0|(cp>>6)); topc[1]=(char)(0x80|(cp&0x3F)); tlen=2;} else if (cp<=0xFFFF){topc[0]=(char)(0xE0|(cp>>12)); topc[1]=(char)(0x80|((cp>>6)&0x3F)); topc[2]=(char)(0x80|(cp&0x3F)); tlen=3;} else {topc[0]=(char)(0xF0|(cp>>18)); topc[1]=(char)(0x80|((cp>>12)&0x3F)); topc[2]=(char)(0x80|((cp>>6)&0x3F)); topc[3]=(char)(0x80|(cp&0x3F)); tlen=4;}
|
||||
topc[tlen]='\0';
|
||||
for (int i=1;i<w-1;i++){ ncplane_putstr_yx(_fun_nc_std, y, x+i, topc); ncplane_putstr_yx(_fun_nc_std, y+h-1, x+i, topc);}
|
||||
}
|
||||
// left and right
|
||||
{
|
||||
char vertc[5]; int vlen=0; uint32_t cp=vch;
|
||||
if (cp<=0x7F){vertc[0]=(char)cp;vlen=1;} else if (cp<=0x7FF){vertc[0]=(char)(0xC0|(cp>>6)); vertc[1]=(char)(0x80|(cp&0x3F)); vlen=2;} else if (cp<=0xFFFF){vertc[0]=(char)(0xE0|(cp>>12)); vertc[1]=(char)(0x80|((cp>>6)&0x3F)); vertc[2]=(char)(0x80|(cp&0x3F)); vlen=3;} else {vertc[0]=(char)(0xF0|(cp>>18)); vertc[1]=(char)(0x80|((cp>>12)&0x3F)); vertc[2]=(char)(0x80|((cp>>6)&0x3F)); vertc[3]=(char)(0x80|(cp&0x3F)); vlen=4;}
|
||||
vertc[vlen]='\0';
|
||||
for (int i=1;i<h-1;i++){ ncplane_putstr_yx(_fun_nc_std, y+i, x, vertc); ncplane_putstr_yx(_fun_nc_std, y+i, x+w-1, vertc);}
|
||||
}
|
||||
// corners
|
||||
{
|
||||
char cbuf[5]; int clen=0; uint32_t cp;
|
||||
cp=tl; if (cp<=0x7F){cbuf[0]=(char)cp;clen=1;} else if (cp<=0x7FF){cbuf[0]=(char)(0xC0|(cp>>6)); cbuf[1]=(char)(0x80|(cp&0x3F)); clen=2;} else if (cp<=0xFFFF){cbuf[0]=(char)(0xE0|(cp>>12)); cbuf[1]=(char)(0x80|((cp>>6)&0x3F)); cbuf[2]=(char)(0x80|(cp&0x3F)); clen=3;} else {cbuf[0]=(char)(0xF0|(cp>>18)); cbuf[1]=(char)(0x80|((cp>>12)&0x3F)); cbuf[2]=(char)(0x80|((cp>>6)&0x3F)); cbuf[3]=(char)(0x80|(cp&0x3F)); clen=4;} cbuf[clen]='\0'; ncplane_putstr_yx(_fun_nc_std, y, x, cbuf);
|
||||
cp=tr; if (cp<=0x7F){cbuf[0]=(char)cp;clen=1;} else if (cp<=0x7FF){cbuf[0]=(char)(0xC0|(cp>>6)); cbuf[1]=(char)(0x80|(cp&0x3F)); clen=2;} else if (cp<=0xFFFF){cbuf[0]=(char)(0xE0|(cp>>12)); cbuf[1]=(char)(0x80|((cp>>6)&0x3F)); cbuf[2]=(char)(0x80|(cp&0x3F)); clen=3;} else {cbuf[0]=(char)(0xF0|(cp>>18)); cbuf[1]=(char)(0x80|((cp>>12)&0x3F)); cbuf[2]=(char)(0x80|((cp>>6)&0x3F)); cbuf[3]=(char)(0x80|(cp&0x3F)); clen=4;} cbuf[clen]='\0'; ncplane_putstr_yx(_fun_nc_std, y, x+w-1, cbuf);
|
||||
cp=bl; if (cp<=0x7F){cbuf[0]=(char)cp;clen=1;} else if (cp<=0x7FF){cbuf[0]=(char)(0xC0|(cp>>6)); cbuf[1]=(char)(0x80|(cp&0x3F)); clen=2;} else if (cp<=0xFFFF){cbuf[0]=(char)(0xE0|(cp>>12)); cbuf[1]=(char)(0x80|((cp>>6)&0x3F)); cbuf[2]=(char)(0x80|(cp&0x3F)); clen=3;} else {cbuf[0]=(char)(0xF0|(cp>>18)); cbuf[1]=(char)(0x80|((cp>>12)&0x3F)); cbuf[2]=(char)(0x80|((cp>>6)&0x3F)); cbuf[3]=(char)(0x80|(cp&0x3F)); clen=4;} cbuf[clen]='\0'; ncplane_putstr_yx(_fun_nc_std, y+h-1, x, cbuf);
|
||||
cp=br; if (cp<=0x7F){cbuf[0]=(char)cp;clen=1;} else if (cp<=0x7FF){cbuf[0]=(char)(0xC0|(cp>>6)); cbuf[1]=(char)(0x80|(cp&0x3F)); clen=2;} else if (cp<=0xFFFF){cbuf[0]=(char)(0xE0|(cp>>12)); cbuf[1]=(char)(0x80|((cp>>6)&0x3F)); cbuf[2]=(char)(0x80|(cp&0x3F)); clen=3;} else {cbuf[0]=(char)(0xF0|(cp>>18)); cbuf[1]=(char)(0x80|((cp>>12)&0x3F)); cbuf[2]=(char)(0x80|((cp>>6)&0x3F)); cbuf[3]=(char)(0x80|(cp&0x3F)); clen=4;} cbuf[clen]='\0'; ncplane_putstr_yx(_fun_nc_std, y+h-1, x+w-1, cbuf);
|
||||
}
|
||||
|
||||
push_value(vm, make_int(0));
|
||||
} else {
|
||||
push_value(vm, make_int(-1));
|
||||
}
|
||||
#else
|
||||
(void)style; (void)h; (void)w; (void)y; (void)x;
|
||||
push_value(vm, make_int(-1));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
40
src/vm/notcurses/draw_char.c
Normal file
40
src/vm/notcurses/draw_char.c
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
||||
/* NC_DRAW_CHAR */
|
||||
case OP_NC_DRAW_CHAR: {
|
||||
Value yv = pop_value(vm);
|
||||
Value xv = pop_value(vm);
|
||||
Value chv = pop_value(vm);
|
||||
int y = (yv.type == VAL_INT ? (int)yv.i : (yv.type == VAL_FLOAT ? (int)yv.d : 0));
|
||||
int x = (xv.type == VAL_INT ? (int)xv.i : (xv.type == VAL_FLOAT ? (int)xv.d : 0));
|
||||
uint32_t cp = (chv.type == VAL_INT ? (uint32_t)chv.i : (chv.type == VAL_FLOAT ? (uint32_t)chv.d : (uint32_t)'?'));
|
||||
free_value(yv);
|
||||
free_value(xv);
|
||||
free_value(chv);
|
||||
#ifdef FUN_WITH_NOTCURSES
|
||||
if (_fun_nc && _fun_nc_std) {
|
||||
char utf8[5];
|
||||
int len = 0;
|
||||
if (cp <= 0x7F) { utf8[0] = (char)cp; len = 1; }
|
||||
else if (cp <= 0x7FF) { utf8[0] = (char)(0xC0 | (cp >> 6)); utf8[1] = (char)(0x80 | (cp & 0x3F)); len = 2; }
|
||||
else if (cp <= 0xFFFF) { utf8[0] = (char)(0xE0 | (cp >> 12)); utf8[1] = (char)(0x80 | ((cp >> 6) & 0x3F)); utf8[2] = (char)(0x80 | (cp & 0x3F)); len = 3; }
|
||||
else { utf8[0] = (char)(0xF0 | (cp >> 18)); utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F)); utf8[2] = (char)(0x80 | ((cp >> 6) & 0x3F)); utf8[3] = (char)(0x80 | (cp & 0x3F)); len = 4; }
|
||||
utf8[len] = '\0';
|
||||
ncplane_putstr_yx(_fun_nc_std, y, x, utf8);
|
||||
push_value(vm, make_int(0));
|
||||
} else {
|
||||
push_value(vm, make_int(-1));
|
||||
}
|
||||
#else
|
||||
(void)cp; (void)x; (void)y;
|
||||
push_value(vm, make_int(-1));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
45
src/vm/notcurses/fill.c
Normal file
45
src/vm/notcurses/fill.c
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
||||
/* NC_FILL */
|
||||
case OP_NC_FILL: {
|
||||
Value chv = pop_value(vm);
|
||||
Value hv = pop_value(vm);
|
||||
Value wv = pop_value(vm);
|
||||
Value yv = pop_value(vm);
|
||||
Value xv = pop_value(vm);
|
||||
int h = (hv.type == VAL_INT ? (int)hv.i : (hv.type == VAL_FLOAT ? (int)hv.d : 0));
|
||||
int w = (wv.type == VAL_INT ? (int)wv.i : (wv.type == VAL_FLOAT ? (int)wv.d : 0));
|
||||
int y = (yv.type == VAL_INT ? (int)yv.i : (yv.type == VAL_FLOAT ? (int)yv.d : 0));
|
||||
int x = (xv.type == VAL_INT ? (int)xv.i : (xv.type == VAL_FLOAT ? (int)xv.d : 0));
|
||||
uint32_t cp = (chv.type == VAL_INT ? (uint32_t)chv.i : (chv.type == VAL_FLOAT ? (uint32_t)chv.d : (uint32_t)' '));
|
||||
free_value(chv); free_value(hv); free_value(wv); free_value(yv); free_value(xv);
|
||||
#ifdef FUN_WITH_NOTCURSES
|
||||
if (_fun_nc && _fun_nc_std && h > 0 && w > 0) {
|
||||
char utf8[5]; int ulen = 0;
|
||||
if (cp <= 0x7F) { utf8[0] = (char)cp; ulen = 1; }
|
||||
else if (cp <= 0x7FF) { utf8[0]=(char)(0xC0|(cp>>6)); utf8[1]=(char)(0x80|(cp&0x3F)); ulen=2; }
|
||||
else if (cp <= 0xFFFF) { utf8[0]=(char)(0xE0|(cp>>12)); utf8[1]=(char)(0x80|((cp>>6)&0x3F)); utf8[2]=(char)(0x80|(cp&0x3F)); ulen=3; }
|
||||
else { utf8[0]=(char)(0xF0|(cp>>18)); utf8[1]=(char)(0x80|((cp>>12)&0x3F)); utf8[2]=(char)(0x80|((cp>>6)&0x3F)); utf8[3]=(char)(0x80|(cp&0x3F)); ulen=4; }
|
||||
utf8[ulen] = '\0';
|
||||
for (int yy = 0; yy < h; yy++) {
|
||||
for (int xx = 0; xx < w; xx++) {
|
||||
ncplane_putstr_yx(_fun_nc_std, y + yy, x + xx, utf8);
|
||||
}
|
||||
}
|
||||
push_value(vm, make_int(0));
|
||||
} else {
|
||||
push_value(vm, make_int(-1));
|
||||
}
|
||||
#else
|
||||
(void)cp; (void)x; (void)y; (void)w; (void)h;
|
||||
push_value(vm, make_int(-1));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
29
src/vm/notcurses/get_size.c
Normal file
29
src/vm/notcurses/get_size.c
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
||||
/* NC_GET_SIZE */
|
||||
case OP_NC_GET_SIZE: {
|
||||
#ifdef FUN_WITH_NOTCURSES
|
||||
if (_fun_nc_std) {
|
||||
int rows = 0, cols = 0;
|
||||
ncplane_dim_yx(_fun_nc_std, &rows, &cols);
|
||||
Value tmp[2];
|
||||
tmp[0] = make_int(rows);
|
||||
tmp[1] = make_int(cols);
|
||||
Value arr = make_array_from_values(tmp, 2);
|
||||
push_value(vm, arr);
|
||||
} else {
|
||||
push_value(vm, make_int(-1));
|
||||
}
|
||||
#else
|
||||
(void)_fun_nc_std;
|
||||
push_value(vm, make_int(-1));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
39
src/vm/notcurses/hline.c
Normal file
39
src/vm/notcurses/hline.c
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
||||
/* NC_DRAW_HLINE */
|
||||
case OP_NC_DRAW_HLINE: {
|
||||
Value chv = pop_value(vm);
|
||||
Value lenv = pop_value(vm);
|
||||
Value xv = pop_value(vm);
|
||||
Value yv = pop_value(vm);
|
||||
int len = (lenv.type == VAL_INT ? (int)lenv.i : (lenv.type == VAL_FLOAT ? (int)lenv.d : 0));
|
||||
int x = (xv.type == VAL_INT ? (int)xv.i : (xv.type == VAL_FLOAT ? (int)xv.d : 0));
|
||||
int y = (yv.type == VAL_INT ? (int)yv.i : (yv.type == VAL_FLOAT ? (int)yv.d : 0));
|
||||
uint32_t cp = (chv.type == VAL_INT ? (uint32_t)chv.i : (chv.type == VAL_FLOAT ? (uint32_t)chv.d : (uint32_t)'-'));
|
||||
free_value(lenv); free_value(xv); free_value(yv); free_value(chv);
|
||||
#ifdef FUN_WITH_NOTCURSES
|
||||
if (_fun_nc && _fun_nc_std && len > 0) {
|
||||
char utf8[5]; int ulen = 0;
|
||||
if (cp <= 0x7F) { utf8[0] = (char)cp; ulen = 1; }
|
||||
else if (cp <= 0x7FF) { utf8[0]=(char)(0xC0|(cp>>6)); utf8[1]=(char)(0x80|(cp&0x3F)); ulen=2; }
|
||||
else if (cp <= 0xFFFF) { utf8[0]=(char)(0xE0|(cp>>12)); utf8[1]=(char)(0x80|((cp>>6)&0x3F)); utf8[2]=(char)(0x80|(cp&0x3F)); ulen=3; }
|
||||
else { utf8[0]=(char)(0xF0|(cp>>18)); utf8[1]=(char)(0x80|((cp>>12)&0x3F)); utf8[2]=(char)(0x80|((cp>>6)&0x3F)); utf8[3]=(char)(0x80|(cp&0x3F)); ulen=4; }
|
||||
utf8[ulen] = '\0';
|
||||
for (int i = 0; i < len; i++) ncplane_putstr_yx(_fun_nc_std, y, x + i, utf8);
|
||||
push_value(vm, make_int(0));
|
||||
} else {
|
||||
push_value(vm, make_int(-1));
|
||||
}
|
||||
#else
|
||||
(void)cp; (void)x; (void)y; (void)len;
|
||||
push_value(vm, make_int(-1));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
23
src/vm/notcurses/render.c
Normal file
23
src/vm/notcurses/render.c
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
||||
/* NC_RENDER */
|
||||
case OP_NC_RENDER: {
|
||||
#ifdef FUN_WITH_NOTCURSES
|
||||
if (_fun_nc) {
|
||||
int rc = notcurses_render(_fun_nc);
|
||||
push_value(vm, make_int(rc == 0 ? 0 : -1));
|
||||
} else {
|
||||
push_value(vm, make_int(-1));
|
||||
}
|
||||
#else
|
||||
push_value(vm, make_int(-1));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
71
src/vm/notcurses/set_style.c
Normal file
71
src/vm/notcurses/set_style.c
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
||||
/* NC_SET_STYLE */
|
||||
case OP_NC_SET_STYLE: {
|
||||
Value fgv = pop_value(vm);
|
||||
Value bgv = pop_value(vm);
|
||||
Value stylev = pop_value(vm);
|
||||
int style = (stylev.type == VAL_INT ? (int)stylev.i : (stylev.type == VAL_FLOAT ? (int)stylev.d : 0));
|
||||
int bg = (bgv.type == VAL_INT ? (int)bgv.i : (bgv.type == VAL_FLOAT ? (int)bgv.d : 0));
|
||||
int fg = (fgv.type == VAL_INT ? (int)fgv.i : (fgv.type == VAL_FLOAT ? (int)fgv.d : 0));
|
||||
free_value(stylev);
|
||||
free_value(bgv);
|
||||
free_value(fgv);
|
||||
#ifdef FUN_WITH_NOTCURSES
|
||||
if (_fun_nc && _fun_nc_std) {
|
||||
/* Extract RGB components */
|
||||
unsigned fg_r = (unsigned)((fg >> 16) & 0xFF);
|
||||
unsigned fg_g = (unsigned)((fg >> 8) & 0xFF);
|
||||
unsigned fg_b = (unsigned)(fg & 0xFF);
|
||||
unsigned bg_r = (unsigned)((bg >> 16) & 0xFF);
|
||||
unsigned bg_g = (unsigned)((bg >> 8) & 0xFF);
|
||||
unsigned bg_b = (unsigned)(bg & 0xFF);
|
||||
|
||||
/* Set colors */
|
||||
ncplane_set_fg_rgb8(_fun_nc_std, fg_r, fg_g, fg_b);
|
||||
ncplane_set_bg_rgb8(_fun_nc_std, bg_r, bg_g, bg_b);
|
||||
|
||||
/* Map simple bitmask to notcurses styles */
|
||||
/* some notcurses versions spell these differently; provide fallbacks */
|
||||
#ifndef NCSTYLE_DIM
|
||||
# ifdef NCSTYLE_FAINT
|
||||
# define NCSTYLE_DIM NCSTYLE_FAINT
|
||||
# else
|
||||
# define NCSTYLE_DIM 0
|
||||
# endif
|
||||
#endif
|
||||
#ifndef NCSTYLE_REVERSE
|
||||
# ifdef NCSTYLE_REVERSED
|
||||
# define NCSTYLE_REVERSE NCSTYLE_REVERSED
|
||||
# else
|
||||
# define NCSTYLE_REVERSE 0
|
||||
# endif
|
||||
#endif
|
||||
unsigned styles = 0;
|
||||
if (style & 1) styles |= NCSTYLE_BOLD;
|
||||
if (style & 2) styles |= NCSTYLE_DIM;
|
||||
if (style & 4) styles |= NCSTYLE_ITALIC;
|
||||
if (style & 8) styles |= NCSTYLE_UNDERLINE;
|
||||
if (style & 16) styles |= NCSTYLE_STRUCK;
|
||||
if (style & 32) styles |= NCSTYLE_REVERSE;
|
||||
ncplane_set_styles(_fun_nc_std, styles);
|
||||
|
||||
push_value(vm, make_int(0));
|
||||
} else {
|
||||
push_value(vm, make_int(-1));
|
||||
}
|
||||
#else
|
||||
(void)style;
|
||||
(void)bg;
|
||||
(void)fg;
|
||||
push_value(vm, make_int(-1));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
39
src/vm/notcurses/vline.c
Normal file
39
src/vm/notcurses/vline.c
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
||||
/* NC_DRAW_VLINE */
|
||||
case OP_NC_DRAW_VLINE: {
|
||||
Value chv = pop_value(vm);
|
||||
Value lenv = pop_value(vm);
|
||||
Value xv = pop_value(vm);
|
||||
Value yv = pop_value(vm);
|
||||
int len = (lenv.type == VAL_INT ? (int)lenv.i : (lenv.type == VAL_FLOAT ? (int)lenv.d : 0));
|
||||
int x = (xv.type == VAL_INT ? (int)xv.i : (xv.type == VAL_FLOAT ? (int)xv.d : 0));
|
||||
int y = (yv.type == VAL_INT ? (int)yv.i : (yv.type == VAL_FLOAT ? (int)yv.d : 0));
|
||||
uint32_t cp = (chv.type == VAL_INT ? (uint32_t)chv.i : (chv.type == VAL_FLOAT ? (uint32_t)chv.d : (uint32_t)'|'));
|
||||
free_value(lenv); free_value(xv); free_value(yv); free_value(chv);
|
||||
#ifdef FUN_WITH_NOTCURSES
|
||||
if (_fun_nc && _fun_nc_std && len > 0) {
|
||||
char utf8[5]; int ulen = 0;
|
||||
if (cp <= 0x7F) { utf8[0] = (char)cp; ulen = 1; }
|
||||
else if (cp <= 0x7FF) { utf8[0]=(char)(0xC0|(cp>>6)); utf8[1]=(char)(0x80|(cp&0x3F)); ulen=2; }
|
||||
else if (cp <= 0xFFFF) { utf8[0]=(char)(0xE0|(cp>>12)); utf8[1]=(char)(0x80|((cp>>6)&0x3F)); utf8[2]=(char)(0x80|(cp&0x3F)); ulen=3; }
|
||||
else { utf8[0]=(char)(0xF0|(cp>>18)); utf8[1]=(char)(0x80|((cp>>12)&0x3F)); utf8[2]=(char)(0x80|((cp>>6)&0x3F)); utf8[3]=(char)(0x80|(cp&0x3F)); ulen=4; }
|
||||
utf8[ulen] = '\0';
|
||||
for (int i = 0; i < len; i++) ncplane_putstr_yx(_fun_nc_std, y + i, x, utf8);
|
||||
push_value(vm, make_int(0));
|
||||
} else {
|
||||
push_value(vm, make_int(-1));
|
||||
}
|
||||
#else
|
||||
(void)cp; (void)x; (void)y; (void)len;
|
||||
push_value(vm, make_int(-1));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue