Refactored examples directory structure. No code changes. (0.39.15)
This commit is contained in:
parent
463e6b691e
commit
bc75e7b681
39 changed files with 0 additions and 0 deletions
29
examples/extra/curl/curl_download.fun
Executable file
29
examples/extra/curl/curl_download.fun
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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-11-25
|
||||
*/
|
||||
|
||||
/*
|
||||
* Demonstrates curl_download saving a file to disk.
|
||||
*/
|
||||
|
||||
url = "https://httpbin.org/image/png"
|
||||
path = "./downloaded.png"
|
||||
ok = curl_download(url, path)
|
||||
if ok == 1
|
||||
print("Downloaded to " + path)
|
||||
else
|
||||
print("Download failed")
|
||||
|
||||
/* Expected output:
|
||||
Downloaded to ./downloaded.png
|
||||
*/
|
||||
|
||||
31
examples/extra/curl/curl_get_json.fun
Executable file
31
examples/extra/curl/curl_get_json.fun
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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-11-25
|
||||
*/
|
||||
|
||||
/*
|
||||
* Demonstrates curl_get and JSON.parse working together.
|
||||
*/
|
||||
|
||||
url = "https://httpbin.org/json"
|
||||
resp = curl_get(url)
|
||||
print("Raw length: " + to_string(len(resp)))
|
||||
|
||||
// If JSON support is enabled, parse it
|
||||
obj = json_parse(resp)
|
||||
if obj != nil
|
||||
print("Title: " + obj["slideshow"]["title"])
|
||||
|
||||
/* Expected output:
|
||||
Raw length: 429
|
||||
Title: Sample Slide Show
|
||||
*/
|
||||
|
||||
57
examples/extra/curl/curl_post.fun
Executable file
57
examples/extra/curl/curl_post.fun
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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-11-25
|
||||
*/
|
||||
|
||||
/*
|
||||
* Demonstrates curl_post sending form data and printing response.
|
||||
*/
|
||||
|
||||
url = "https://httpbin.org/post"
|
||||
data = "name=Fun&lang=fun"
|
||||
resp = curl_post(url, data)
|
||||
print("Response: " + resp)
|
||||
|
||||
// If JSON support is enabled, parse it
|
||||
obj = json_parse(resp)
|
||||
if obj != nil
|
||||
print("Content-Type: " + to_string(obj["headers"]["Content-Type"]))
|
||||
|
||||
if obj != nil
|
||||
print("Host: " + to_string(obj["headers"]["Host"]))
|
||||
|
||||
if obj != nil
|
||||
print("Origin: " + to_string(obj["origin"]))
|
||||
|
||||
// Possible output:
|
||||
// Response: {
|
||||
// "args": {},
|
||||
// "data": "",
|
||||
// "files": {},
|
||||
// "form": {
|
||||
// "lang": "fun",
|
||||
// "name": "Fun"
|
||||
// },
|
||||
// "headers": {
|
||||
// "Accept": "*/*",
|
||||
// "Content-Length": "17",
|
||||
// "Content-Type": "application/x-www-form-urlencoded",
|
||||
// "Host": "httpbin.org",
|
||||
// "X-Amzn-Trace-Id": "Root=1-6944834b-74f499e251c322713e7dd9a8"
|
||||
// },
|
||||
// "json": nil,
|
||||
// "origin": "5.252.226.107",
|
||||
// "url": "https://httpbin.org/post"
|
||||
// }
|
||||
//
|
||||
// Content-Type: application/x-www-form-urlencoded
|
||||
// Host: httpbin.org
|
||||
// Origin: 5.252.226.107
|
||||
58
examples/extra/ini/ini_class_demo.fun
Executable file
58
examples/extra/ini/ini_class_demo.fun
Executable file
|
|
@ -0,0 +1,58 @@
|
|||
#!/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: 2025-11-30
|
||||
*/
|
||||
|
||||
// Demonstration of the Ini stdlib class from lib/io/ini.fun
|
||||
include <io/ini.fun>
|
||||
|
||||
ini = INI()
|
||||
path = "./examples/data/complex.ini"
|
||||
|
||||
if (ini.load(path) == 0)
|
||||
print("Failed to load " + path)
|
||||
exit(1)
|
||||
|
||||
// Read a few values
|
||||
app_name = ini.get_string("app", "name", "FunApp")
|
||||
app_version = ini.get_string("app", "version", "0.0.0")
|
||||
app_debug = ini.get_bool("app", "debug", 0)
|
||||
|
||||
db_host = ini.get_string("database", "host", "localhost")
|
||||
db_port = ini.get_int("database", "port", 5432)
|
||||
|
||||
print("[app]")
|
||||
print(" name=" + app_name)
|
||||
print(" version=" + app_version)
|
||||
print(" debug=" + to_string(app_debug))
|
||||
|
||||
print("[database]")
|
||||
print(" host=" + db_host)
|
||||
print(" port=" + to_string(db_port))
|
||||
|
||||
// Update a value and save back to the same file
|
||||
ini.set("app", "debug", 1)
|
||||
ok = ini.save(nil)
|
||||
print("saved=" + to_string(ok))
|
||||
|
||||
ini.close()
|
||||
|
||||
/* Expected output:
|
||||
[app]
|
||||
name=FunApp
|
||||
version=1.2.3
|
||||
debug=1
|
||||
[database]
|
||||
host=localhost
|
||||
port=5432
|
||||
saved=1
|
||||
*/
|
||||
|
||||
99
examples/extra/ini/ini_complex.fun
Executable file
99
examples/extra/ini/ini_complex.fun
Executable file
|
|
@ -0,0 +1,99 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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-11-30
|
||||
*/
|
||||
|
||||
// Complex INI parsing example using iniparser 4.2.6 opcodes.
|
||||
|
||||
path = "./examples/data/complex.ini"
|
||||
h = ini_load(path)
|
||||
if h == 0
|
||||
print("Failed to load "+path)
|
||||
else
|
||||
// app
|
||||
app_name = ini_get_string(h, "app", "name", "FunApp")
|
||||
app_version = ini_get_string(h, "app", "version", "0.0.0")
|
||||
app_debug = ini_get_bool(h, "app", "debug", 0)
|
||||
|
||||
// database
|
||||
db_host = ini_get_string(h, "database", "host", "localhost")
|
||||
db_port = ini_get_int(h, "database", "port", 5432)
|
||||
db_user = ini_get_string(h, "database", "user", "user")
|
||||
db_pass = ini_get_string(h, "database", "pass", "")
|
||||
db_pool = ini_get_int(h, "database", "pool_size", 4)
|
||||
db_timeout = ini_get_double(h, "database", "timeout", 2.0)
|
||||
|
||||
// network
|
||||
net_ssl = ini_get_bool(h, "network", "ssl", 0)
|
||||
net_retries = ini_get_int(h, "network", "retries", 3)
|
||||
base_url = ini_get_string(h, "network", "base_url", "")
|
||||
|
||||
// features
|
||||
feature_x = ini_get_bool(h, "features", "feature_x", 0)
|
||||
feature_y = ini_get_bool(h, "features", "feature_y", 0)
|
||||
|
||||
// paths
|
||||
data_dir = ini_get_string(h, "paths", "data_dir", "./data")
|
||||
log_file = ini_get_string(h, "paths", "log_file", "./logs/app.log")
|
||||
|
||||
// Print a structured summary
|
||||
print("[app]")
|
||||
print(" name=" + app_name)
|
||||
print(" version=" + app_version)
|
||||
print(" debug=" + to_string(app_debug))
|
||||
|
||||
print("[database]")
|
||||
print(" host=" + db_host)
|
||||
print(" port=" + to_string(db_port))
|
||||
print(" user=" + db_user)
|
||||
print(" pass=" + db_pass)
|
||||
print(" pool_size=" + to_string(db_pool))
|
||||
print(" timeout=" + to_string(db_timeout))
|
||||
|
||||
print("[network]")
|
||||
print(" ssl=" + to_string(net_ssl))
|
||||
print(" retries=" + to_string(net_retries))
|
||||
print(" base_url=" + base_url)
|
||||
|
||||
print("[features]")
|
||||
print(" feature_x=" + to_string(feature_x))
|
||||
print(" feature_y=" + to_string(feature_y))
|
||||
|
||||
print("[paths]")
|
||||
print(" data_dir=" + data_dir)
|
||||
print(" log_file=" + log_file)
|
||||
|
||||
// Clean up
|
||||
ini_free(h)
|
||||
|
||||
/* Expected output:
|
||||
[app]
|
||||
name=FunApp
|
||||
version=1.2.3
|
||||
debug=1
|
||||
[database]
|
||||
host=localhost
|
||||
port=5432
|
||||
user=fun
|
||||
pass=secret
|
||||
pool_size=8
|
||||
timeout=2.5
|
||||
[network]
|
||||
ssl=1
|
||||
retries=3
|
||||
base_url=https://api.example.com
|
||||
[features]
|
||||
feature_x=1
|
||||
feature_y=0
|
||||
[paths]
|
||||
data_dir=./data
|
||||
log_file=./logs/app.log
|
||||
*/
|
||||
43
examples/extra/ini/ini_demo.fun
Executable file
43
examples/extra/ini/ini_demo.fun
Executable file
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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-11-30
|
||||
*/
|
||||
|
||||
// Minimal demo for INI opcodes using iniparser 4.2.6
|
||||
|
||||
path = "./examples/data/complex.ini"
|
||||
h = ini_load(path)
|
||||
if h == 0
|
||||
print("Failed to load " + path)
|
||||
else
|
||||
u = ini_get_string(h, "auth", "user", "guest")
|
||||
r = ini_get_int(h, "network", "retries", 5)
|
||||
s = ini_get_bool(h, "network", "ssl", 0)
|
||||
print("user=" + u)
|
||||
print("retries=" + to_string(r))
|
||||
print("ssl=" + to_string(s))
|
||||
ok = ini_set(h, "auth", "token", "abcd1234")
|
||||
if ok
|
||||
ini_save(h, path)
|
||||
ini_free(h)
|
||||
|
||||
/* Expected output:
|
||||
user=<EFBFBD><EFBFBD><EFBFBD><EFBFBD>U
|
||||
retries=3
|
||||
ssl=1
|
||||
|
||||
I wonder about the user= value when the default value is used!
|
||||
|
||||
It should look like:
|
||||
user=guest
|
||||
retries=3
|
||||
ssl=1
|
||||
*/
|
||||
50
examples/extra/ini/ini_diag.fun
Executable file
50
examples/extra/ini/ini_diag.fun
Executable file
|
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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: 2026-01-02
|
||||
*/
|
||||
|
||||
// Minimal diagnostic for INI lookups
|
||||
|
||||
path = "./examples/data/complex.ini"
|
||||
h = ini_load(path)
|
||||
print("h=" + to_string(h))
|
||||
if h == 0
|
||||
print("Failed to load: " + path)
|
||||
else
|
||||
print("[try app:name]")
|
||||
v1 = ini_get_string(h, "app", "name", "<def>")
|
||||
print("app:name => " + v1)
|
||||
|
||||
print("[try app:version]")
|
||||
v2 = ini_get_string(h, "app", "version", "<def>")
|
||||
print("app:version => " + v2)
|
||||
|
||||
print("[try database:port]")
|
||||
v3 = ini_get_int(h, "database", "port", -1)
|
||||
print("database:port => " + to_string(v3))
|
||||
|
||||
print("[try network:ssl]")
|
||||
v4 = ini_get_bool(h, "network", "ssl", -9)
|
||||
print("network:ssl => " + to_string(v4))
|
||||
|
||||
ini_free(h)
|
||||
|
||||
/* Expected output:
|
||||
h=1
|
||||
[try app:name]
|
||||
app:name => FunApp
|
||||
[try app:version]
|
||||
app:version => 1.2.3
|
||||
[try database:port]
|
||||
database:port => 5432
|
||||
[try network:ssl]
|
||||
network:ssl => 1
|
||||
*/
|
||||
93
examples/extra/ini/ini_subsections.fun
Executable file
93
examples/extra/ini/ini_subsections.fun
Executable file
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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-11-30
|
||||
*/
|
||||
|
||||
// Demonstration of INI subsections like [section.subsection]
|
||||
// Uses iniparser 4.2.6 via Fun's ini_* opcodes
|
||||
|
||||
path = "./examples/data/subsections.ini"
|
||||
h = ini_load(path)
|
||||
if h == 0
|
||||
print("Failed to load "+path)
|
||||
else
|
||||
// Top-level server
|
||||
srv_host = ini_get_string(h, "server", "host", "localhost")
|
||||
srv_port = ini_get_int(h, "server", "port", 80)
|
||||
|
||||
// Subsection: server.tls
|
||||
tls_enabled = ini_get_bool(h, "server.tls", "enabled", 0)
|
||||
tls_version = ini_get_double(h, "server.tls", "version", 1.2)
|
||||
tls_ciphers = ini_get_string(h, "server.tls", "ciphers", "")
|
||||
|
||||
// Subsections: users.*
|
||||
admin_name = ini_get_string(h, "users.admin", "name", "admin")
|
||||
admin_active = ini_get_bool(h, "users.admin", "active", 1)
|
||||
admin_quota = ini_get_int(h, "users.admin", "quota_gb", 10)
|
||||
|
||||
guest_name = ini_get_string(h, "users.guest", "name", "guest")
|
||||
guest_active = ini_get_bool(h, "users.guest", "active", 0)
|
||||
guest_quota = ini_get_int(h, "users.guest", "quota_gb", 1)
|
||||
|
||||
// Subsection: paths.logs
|
||||
logs_dir = ini_get_string(h, "paths.logs", "dir", "./logs")
|
||||
logs_rotate = ini_get_bool(h, "paths.logs", "rotate", 0)
|
||||
logs_max_files = ini_get_int(h, "paths.logs", "max_files", 5)
|
||||
|
||||
// Print
|
||||
print("[server]")
|
||||
print(" host=" + srv_host)
|
||||
print(" port=" + to_string(srv_port))
|
||||
|
||||
print("[server.tls]")
|
||||
print(" enabled=" + to_string(tls_enabled))
|
||||
print(" version=" + to_string(tls_version))
|
||||
print(" ciphers=" + tls_ciphers)
|
||||
|
||||
print("[users.admin]")
|
||||
print(" name=" + admin_name)
|
||||
print(" active=" + to_string(admin_active))
|
||||
print(" quota_gb=" + to_string(admin_quota))
|
||||
|
||||
print("[users.guest]")
|
||||
print(" name=" + guest_name)
|
||||
print(" active=" + to_string(guest_active))
|
||||
print(" quota_gb=" + to_string(guest_quota))
|
||||
|
||||
print("[paths.logs]")
|
||||
print(" dir=" + logs_dir)
|
||||
print(" rotate=" + to_string(logs_rotate))
|
||||
print(" max_files=" + to_string(logs_max_files))
|
||||
|
||||
ini_free(h)
|
||||
|
||||
/* Expected output:
|
||||
[server]
|
||||
host=example.org
|
||||
port=8080
|
||||
[server.tls]
|
||||
enabled=1
|
||||
version=1.3
|
||||
ciphers=TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256
|
||||
[users.admin]
|
||||
name=alice
|
||||
active=1
|
||||
quota_gb=100
|
||||
[users.guest]
|
||||
name=bob
|
||||
active=0
|
||||
quota_gb=5
|
||||
[paths.logs]
|
||||
dir=./var/log/fun
|
||||
rotate=1
|
||||
max_files=7
|
||||
*/
|
||||
|
||||
28
examples/extra/libressl/libressl_md5.fun
Executable file
28
examples/extra/libressl/libressl_md5.fun
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Added: 2026-02-19
|
||||
*/
|
||||
|
||||
// LibreSSL MD5 example
|
||||
// Enable with -DFUN_WITH_LIBRESSL=ON during build for real hashing.
|
||||
|
||||
s = "abc"
|
||||
d = libressl_md5(s)
|
||||
print("md5(abc) = " + d)
|
||||
|
||||
// Another quick check (empty string)
|
||||
e = ""
|
||||
print("md5(\"\") = " + libressl_md5(e))
|
||||
|
||||
/* Expected output:
|
||||
md5(abc) = 900150983cd24fb0d6963f7d28e17f72
|
||||
md5("") = d41d8cd98f00b204e9800998ecf8427e
|
||||
*/
|
||||
28
examples/extra/libressl/libressl_ripemd160.fun
Executable file
28
examples/extra/libressl/libressl_ripemd160.fun
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Added: 2026-02-19
|
||||
*/
|
||||
|
||||
// LibreSSL RIPEMD-160 example
|
||||
// Enable with -DFUN_WITH_LIBRESSL=ON during build for real hashing.
|
||||
|
||||
s = "abc"
|
||||
d = libressl_ripemd160(s)
|
||||
print("ripemd160(abc) = " + d)
|
||||
|
||||
// Another quick check (empty string)
|
||||
e = ""
|
||||
print("ripemd160(\"\") = " + libressl_ripemd160(e))
|
||||
|
||||
/* Expected output:
|
||||
ripemd160(abc) = 8eb208f7e05d987a9b049a9a5c0c2b74e07e6a5d
|
||||
ripemd160("") = 9c1185a5c5e9fc54612808977ee8f548b2258d31
|
||||
*/
|
||||
28
examples/extra/libressl/libressl_sha256.fun
Executable file
28
examples/extra/libressl/libressl_sha256.fun
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Added: 2026-02-19
|
||||
*/
|
||||
|
||||
// LibreSSL SHA-256 example
|
||||
// Enable with -DFUN_WITH_LIBRESSL=ON during build for real hashing.
|
||||
|
||||
s = "abc"
|
||||
d = libressl_sha256(s)
|
||||
print("sha256(abc) = " + d)
|
||||
|
||||
// Another quick check (empty string)
|
||||
e = ""
|
||||
print("sha256(\"\") = " + libressl_sha256(e))
|
||||
|
||||
/* Expected output:
|
||||
sha256(abc) = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
|
||||
sha256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||
*/
|
||||
28
examples/extra/libressl/libressl_sha512.fun
Executable file
28
examples/extra/libressl/libressl_sha512.fun
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Added: 2026-02-19
|
||||
*/
|
||||
|
||||
// LibreSSL SHA-512 example
|
||||
// Enable with -DFUN_WITH_LIBRESSL=ON during build for real hashing.
|
||||
|
||||
s = "abc"
|
||||
d = libressl_sha512(s)
|
||||
print("sha512(abc) = " + d)
|
||||
|
||||
// Another quick check (empty string)
|
||||
e = ""
|
||||
print("sha512(\"\") = " + libressl_sha512(e))
|
||||
|
||||
/* Expected output:
|
||||
sha512(abc) = ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
|
||||
sha512("") = cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
|
||||
*/
|
||||
46
examples/extra/notcurses/notcurses_hello.fun
Executable file
46
examples/extra/notcurses/notcurses_hello.fun
Executable file
|
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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: 2026-01-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Minimal Notcurses hello example.
|
||||
*/
|
||||
|
||||
include <ui/notcurses.fun>
|
||||
|
||||
n = Notcurses()
|
||||
|
||||
if n.init() == 0
|
||||
print("Notcurses not available. Rebuild with -DFUN_WITH_NOTCURSES=ON.")
|
||||
exit(0)
|
||||
|
||||
n.clear()
|
||||
n.draw_text(2, 0, "Fun + Notcurses")
|
||||
n.draw_text(4, 0, "Press any key to exit...")
|
||||
|
||||
// blocking until key
|
||||
_ = n.getch(0)
|
||||
n.shutdown()
|
||||
|
||||
/* Possible output:
|
||||
A TUI.
|
||||
|
||||
After exit:
|
||||
3 renders, 991,14µs (223,57µs min, 330,38µs avg, 531,91µs max)
|
||||
3 rasters, 320,76µs (106,45µs min, 106,92µs avg, 107,72µs max)
|
||||
3 writes, 198,26µs (62,54µs min, 66,09µs avg, 69,94µs max)
|
||||
59B (0B min, 19B avg, 30B max) 1 input Ghpa: 0
|
||||
0 failed renders, 0 failed rasters, 0 refreshes, 0 input errors
|
||||
RGB emits:elides: def 1:38 fg 0:0 bg 0:0
|
||||
Cell emits:elides: 39:74805 (99,95%) 97,44% 0,00% 0,00%
|
||||
Bmap emits:elides: 0:0 (0,00%) 0B (0,00%) SuM: 0 (0,00%)
|
||||
*/
|
||||
33
examples/extra/notcurses/notcurses_menu.fun
Executable file
33
examples/extra/notcurses/notcurses_menu.fun
Executable 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/notcurses_progress.fun
Executable file
38
examples/extra/notcurses/notcurses_progress.fun
Executable 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()
|
||||
28
examples/extra/openssl/openssl_md5.fun
Executable file
28
examples/extra/openssl/openssl_md5.fun
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Added: 2026-02-19
|
||||
*/
|
||||
|
||||
// OpenSSL MD5 example
|
||||
// Enable with -DFUN_WITH_OPENSSL=ON during build for real hashing.
|
||||
|
||||
s = "abc"
|
||||
d = openssl_md5(s)
|
||||
print("md5(abc) = " + d)
|
||||
|
||||
// Another quick check (empty string)
|
||||
e = ""
|
||||
print("md5(\"\") = " + openssl_md5(e))
|
||||
|
||||
/* Expected output:
|
||||
md5(abc) = 900150983cd24fb0d6963f7d28e17f72
|
||||
md5("") = d41d8cd98f00b204e9800998ecf8427e
|
||||
*/
|
||||
28
examples/extra/openssl/openssl_ripemd160.fun
Executable file
28
examples/extra/openssl/openssl_ripemd160.fun
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Added: 2026-02-19
|
||||
*/
|
||||
|
||||
// OpenSSL RIPEMD-160 example
|
||||
// Enable with -DFUN_WITH_OPENSSL=ON during build for real hashing.
|
||||
|
||||
s = "abc"
|
||||
d = openssl_ripemd160(s)
|
||||
print("ripemd160(abc) = " + d)
|
||||
|
||||
// Another quick check (empty string)
|
||||
e = ""
|
||||
print("ripemd160(\"\") = " + openssl_ripemd160(e))
|
||||
|
||||
/* Expected output (if RIPEMD-160 is available in your OpenSSL build):
|
||||
ripemd160(abc) = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc
|
||||
ripemd160("") = 9c1185a5c5e9fc54612808977ee8f548b2258d31
|
||||
*/
|
||||
28
examples/extra/openssl/openssl_sha256.fun
Executable file
28
examples/extra/openssl/openssl_sha256.fun
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Added: 2026-02-19
|
||||
*/
|
||||
|
||||
// OpenSSL SHA-256 example
|
||||
// Enable with -DFUN_WITH_OPENSSL=ON during build for real hashing.
|
||||
|
||||
s = "abc"
|
||||
d = openssl_sha256(s)
|
||||
print("sha256(abc) = " + d)
|
||||
|
||||
// Another quick check (empty string)
|
||||
e = ""
|
||||
print("sha256(\"\") = " + openssl_sha256(e))
|
||||
|
||||
/* Expected output:
|
||||
sha256(abc) = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
|
||||
sha256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||
*/
|
||||
28
examples/extra/openssl/openssl_sha512.fun
Executable file
28
examples/extra/openssl/openssl_sha512.fun
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Added: 2026-02-19
|
||||
*/
|
||||
|
||||
// OpenSSL SHA-512 example
|
||||
// Enable with -DFUN_WITH_OPENSSL=ON during build for real hashing.
|
||||
|
||||
s = "abc"
|
||||
d = openssl_sha512(s)
|
||||
print("sha512(abc) = " + d)
|
||||
|
||||
// Another quick check (empty string)
|
||||
e = ""
|
||||
print("sha512(\"\") = " + openssl_sha512(e))
|
||||
|
||||
/* Expected output:
|
||||
sha512(abc) = ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
|
||||
sha512("") = cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
|
||||
*/
|
||||
42
examples/extra/sqlite/sqlited/README.md
Normal file
42
examples/extra/sqlite/sqlited/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
Fun SQL TCP Demo (sqlited)
|
||||
|
||||
This example provides a minimal TCP server that executes SQL against a local SQLite database and a matching client.
|
||||
|
||||
Files
|
||||
- server.fun — TCP server daemon
|
||||
- client.fun — simple CLI client
|
||||
- protocol.md — wire protocol specification (line-based TSV)
|
||||
|
||||
Prerequisites
|
||||
- Build Fun with SQLite support enabled: configure with -DFUN_WITH_SQLITE=ON
|
||||
- Ensure the sqlite3 development headers and runtime are installed
|
||||
|
||||
Create a sample database
|
||||
- A schema is provided at examples/data/database.sql
|
||||
- Create ./database.sqlite at the repository root using the sqlite3 CLI:
|
||||
sqlite3 ./database.sqlite < ./examples/data/database.sql
|
||||
|
||||
Run the server
|
||||
- Set FUN_LIB_DIR to the repo’s lib directory or install Fun libs system-wide
|
||||
- Example (Debug profile path may differ):
|
||||
FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/sqlited/server.fun 127.0.0.1 5555
|
||||
|
||||
Run the client
|
||||
- Query:
|
||||
FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/sqlited/client.fun 127.0.0.1 5555 "SELECT id, title FROM tasks;"
|
||||
- Exec/DDL:
|
||||
FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/sqlited/client.fun 127.0.0.1 5555 "UPDATE tasks SET done=1 WHERE id=1;"
|
||||
|
||||
Protocol summary
|
||||
- Client sends one line with SQL ended by a newline (\n)
|
||||
- Server responds with either:
|
||||
- RESULT block (header + rows as TSV) ending with END
|
||||
- OK rc (for exec/DDL)
|
||||
- ERROR message (on error)
|
||||
See protocol.md for details.
|
||||
|
||||
Notes and limitations
|
||||
- Demo only; do not expose to untrusted networks (no auth/TLS; arbitrary SQL)
|
||||
- BLOBs and binary data are not specially handled in this v1
|
||||
- Very long SQL lines are capped at 64 KiB
|
||||
- The server handles one client at a time (simple model); extend with threads if desired
|
||||
142
examples/extra/sqlite/sqlited/client.fun
Executable file
142
examples/extra/sqlite/sqlited/client.fun
Executable file
|
|
@ -0,0 +1,142 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Added: 2026-01-19
|
||||
*/
|
||||
|
||||
// Simple TCP SQL client for Fun
|
||||
// Connects to host:port, sends a single-line SQL (from CLI args or default),
|
||||
// prints the server response, and exits.
|
||||
|
||||
// Run the server:
|
||||
// FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/sqlited/server.fun 127.0.0.1 5555
|
||||
|
||||
// Run the client:
|
||||
// FUN_LIB_DIR="$(pwd)/lib" ./build/fun --repl-on-error ./examples/sqlited/client.fun 127.0.0.1 5555 "SELECT * FROM tasks"
|
||||
|
||||
#include <cli.fun>
|
||||
|
||||
fun arg_or_default(args, i, d)
|
||||
if (len(args) > i)
|
||||
return args[i]
|
||||
else
|
||||
return d
|
||||
|
||||
fun read_all(fd)
|
||||
buf = ""
|
||||
while (true)
|
||||
chunk = sock_recv(fd, 1024)
|
||||
if (chunk == nil || len(chunk) == 0)
|
||||
break
|
||||
buf = buf + chunk
|
||||
return buf
|
||||
|
||||
fun main()
|
||||
args = argv()
|
||||
host = arg_or_default(args, 0, "127.0.0.1")
|
||||
port = to_number(arg_or_default(args, 1, 5555))
|
||||
sql = arg_or_default(args, 2, "SELECT 1 AS one;")
|
||||
|
||||
fd = tcp_connect(host, port)
|
||||
if (fd == 0)
|
||||
print("Connect failed to " + host + " " + to_string(port))
|
||||
return 1
|
||||
|
||||
// Ensure a single line terminated by \n
|
||||
if (len(sql) == 0 || substr(sql, len(sql)-1, 1) != "\n")
|
||||
sql = sql + "\n"
|
||||
|
||||
sent = sock_send(fd, sql)
|
||||
if (sent < 0)
|
||||
print("Send failed")
|
||||
sock_close(fd)
|
||||
return 1
|
||||
|
||||
resp = read_all(fd)
|
||||
sock_close(fd)
|
||||
if (resp == nil)
|
||||
resp = ""
|
||||
print(resp)
|
||||
|
||||
// Explicitly invoke main when the script is run
|
||||
main()
|
||||
|
||||
/* Possible result with 67 entries in the tasks table:
|
||||
RESULT
|
||||
value
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
{map n=4}
|
||||
END
|
||||
*/
|
||||
35
examples/extra/sqlite/sqlited/protocol.md
Normal file
35
examples/extra/sqlite/sqlited/protocol.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
Fun SQL TCP Demo Protocol (TSV, line-based)
|
||||
|
||||
- Client sends exactly one line with the SQL text terminated by a newline ("\n"). The server reads up to 64 KiB.
|
||||
|
||||
Responses
|
||||
|
||||
1) Query returning rows (e.g., SELECT):
|
||||
RESULT
|
||||
col1\tcol2\t...\n
|
||||
v11\tv12\t...\n
|
||||
...
|
||||
END
|
||||
|
||||
Notes:
|
||||
- First line is the literal word RESULT followed by a newline.
|
||||
- Second line is a header with column names separated by a single tab ("\t").
|
||||
- Each subsequent line is one row; fields are tab-separated. Nil/NULL are encoded as empty strings.
|
||||
- The block terminates with a line containing the literal END.
|
||||
|
||||
2) Exec/DDL (e.g., INSERT/UPDATE/CREATE):
|
||||
OK rc
|
||||
|
||||
Notes:
|
||||
- rc is the sqlite3 result code (0 indicates success).
|
||||
|
||||
3) Error:
|
||||
ERROR message
|
||||
|
||||
Notes:
|
||||
- The error message is human-readable and not machine-stable.
|
||||
|
||||
General
|
||||
- Newlines are Unix style ("\n").
|
||||
- Tabs and newlines in data are replaced with spaces for TSV safety.
|
||||
- The server closes the connection after sending the response.
|
||||
282
examples/extra/sqlite/sqlited/server.fun
Executable file
282
examples/extra/sqlite/sqlited/server.fun
Executable file
|
|
@ -0,0 +1,282 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Added: 2026-01-19
|
||||
*/
|
||||
|
||||
// Simple TCP SQL server for Fun
|
||||
// Listens on a TCP port, opens ./database.sqlite, executes one-line SQL per connection,
|
||||
// and returns results over the socket in a simple TSV protocol.
|
||||
|
||||
// Run the server:
|
||||
// FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/sqlited/server.fun 127.0.0.1 5555
|
||||
|
||||
// Run the client:
|
||||
// FUN_LIB_DIR="$(pwd)/lib" ./build/fun --repl-on-error ./examples/sqlited/client.fun 127.0.0.1 5555 "SELECT * FROM tasks"
|
||||
|
||||
// Protocol (per protocol.md):
|
||||
// - Client sends a single line of SQL ending with \n
|
||||
// - If query returns rows: respond with
|
||||
// RESULT\n
|
||||
// <col1>\t<col2>\t...\n
|
||||
// <v11>\t<v12>\t...\n
|
||||
// ...
|
||||
// END\n
|
||||
// - If exec/DDL: respond with
|
||||
// OK <rc>\n
|
||||
// - On error: respond with
|
||||
// ERROR <message>\n
|
||||
|
||||
// Helper: CLI args via stdlib
|
||||
#include <cli.fun>
|
||||
#include <strings.fun>
|
||||
|
||||
fun arg_or_default(args, i, d)
|
||||
if (len(args) > i)
|
||||
return args[i]
|
||||
else
|
||||
return d
|
||||
|
||||
// Helper: send a string (no newline added)
|
||||
fun send(fd, s)
|
||||
// sock_send returns bytes or -1
|
||||
return sock_send(fd, s)
|
||||
|
||||
// Helper: read a single line (up to max_len) ending with \n; returns string without trailing \r?\n or nil on EOF
|
||||
fun read_line(fd)
|
||||
max_len = 65536
|
||||
buf = ""
|
||||
while (len(buf) < max_len)
|
||||
chunk = sock_recv(fd, 256)
|
||||
if (chunk == nil || len(chunk) == 0)
|
||||
break
|
||||
buf = buf + chunk
|
||||
pos = find(buf, "\n")
|
||||
if (pos >= 0)
|
||||
line = substr(buf, 0, pos)
|
||||
// trim trailing \r if present
|
||||
if (len(line) > 0 && substr(line, len(line)-1, 1) == "\r")
|
||||
line = substr(line, 0, len(line)-1)
|
||||
return line
|
||||
if (len(buf) == 0)
|
||||
return nil
|
||||
// no newline; return whole buffer (trim any trailing CR)
|
||||
if (len(buf) > 0 && substr(buf, len(buf)-1, 1) == "\r")
|
||||
buf = substr(buf, 0, len(buf)-1)
|
||||
return buf
|
||||
|
||||
// Replace tab/newline with spaces for TSV safety
|
||||
fun sanitize_tsv(s)
|
||||
if (s == nil)
|
||||
return ""
|
||||
out = ""
|
||||
i = 0
|
||||
while (i < len(s))
|
||||
ch = substr(s, i, 1)
|
||||
if (ch == "\t" || ch == "\n" || ch == "\r")
|
||||
out = out + " "
|
||||
else
|
||||
out = out + ch
|
||||
i = i + 1
|
||||
return out
|
||||
|
||||
fun trim(s)
|
||||
// trim spaces and tabs
|
||||
i = 0
|
||||
j = len(s)
|
||||
while (i < j && (substr(s, i, 1) == " " || substr(s, i, 1) == "\t"))
|
||||
i = i + 1
|
||||
while (j > i && (substr(s, j-1, 1) == " " || substr(s, j-1, 1) == "\t" || substr(s, j-1, 1) == ";"))
|
||||
j = j - 1
|
||||
return substr(s, i, j - i)
|
||||
|
||||
fun split_on_comma(s)
|
||||
parts = []
|
||||
cur = ""
|
||||
i = 0
|
||||
while (i < len(s))
|
||||
ch = substr(s, i, 1)
|
||||
if (ch == ",")
|
||||
push(parts, trim(cur))
|
||||
cur = ""
|
||||
else
|
||||
cur = cur + ch
|
||||
i = i + 1
|
||||
push(parts, trim(cur))
|
||||
return parts
|
||||
|
||||
// Parse header from SQL SELECT list; for SELECT * tries PRAGMA table_info(table)
|
||||
fun parse_header_from_sql(sql, dbh)
|
||||
// Use stdlib helper for lowercase
|
||||
lower_sql = str_to_lower(sql)
|
||||
psel = find(lower_sql, "select ")
|
||||
pfrom = find(lower_sql, " from ")
|
||||
if (psel < 0 || pfrom < 0 || pfrom <= psel)
|
||||
return nil
|
||||
cols_str = substr(sql, psel + 7, pfrom - (psel + 7))
|
||||
cols_str = trim(cols_str)
|
||||
if (find(cols_str, "*") >= 0)
|
||||
// Attempt to detect table name after FROM
|
||||
rest = substr(sql, pfrom + 6, len(sql) - (pfrom + 6))
|
||||
rest = trim(rest)
|
||||
// table name is up to next space or semicolon
|
||||
sp = find(rest, " ")
|
||||
tname = rest
|
||||
if (sp > 0)
|
||||
tname = substr(rest, 0, sp)
|
||||
// remove trailing semicolon if any
|
||||
tname = trim(tname)
|
||||
if (len(tname) > 0)
|
||||
pragma_sql = "PRAGMA table_info(" + tname + ");"
|
||||
ti = sqlite_query(dbh, pragma_sql)
|
||||
if (ti != nil && len(ti) > 0)
|
||||
cols = []
|
||||
i = 0
|
||||
while (i < len(ti))
|
||||
nm = ti[i]["name"]
|
||||
if (nm != nil)
|
||||
push(cols, to_string(nm))
|
||||
i = i + 1
|
||||
if (len(cols) > 0)
|
||||
return cols
|
||||
// Parse explicit column list
|
||||
parts = split_on_comma(cols_str)
|
||||
cols = []
|
||||
i = 0
|
||||
while (i < len(parts))
|
||||
p = parts[i]
|
||||
pl = lower(p)
|
||||
// handle AS alias
|
||||
aspos = find(pl, " as ")
|
||||
if (aspos >= 0)
|
||||
alias = trim(substr(p, aspos + 4, len(p) - (aspos + 4)))
|
||||
push(cols, alias)
|
||||
else
|
||||
// take last token after dot
|
||||
dot = find(p, ".")
|
||||
if (dot >= 0)
|
||||
push(cols, trim(substr(p, dot + 1, len(p) - (dot + 1))))
|
||||
else
|
||||
push(cols, trim(p))
|
||||
i = i + 1
|
||||
if (len(cols) > 0)
|
||||
return cols
|
||||
return nil
|
||||
|
||||
// Attempt to build a deterministic header and row order using enumerate(row).
|
||||
// Falls back to attempting common column names if enumerate is unavailable.
|
||||
fun extract_header(row)
|
||||
// Build a header by probing a set of common keys present in many queries.
|
||||
// If none are present, fall back to a single synthetic column "value" and
|
||||
// the caller will print the entire row using to_string(row).
|
||||
hdr_candidates = [
|
||||
"id", "name", "title", "value", "count", "cnt",
|
||||
"done", "created_at", "updated_at", "rowid"
|
||||
]
|
||||
cols = []
|
||||
found = 0
|
||||
i = 0
|
||||
while (i < len(hdr_candidates))
|
||||
k = hdr_candidates[i]
|
||||
v = row[k]
|
||||
if (v != nil)
|
||||
push(cols, k)
|
||||
found = 1
|
||||
i = i + 1
|
||||
if (found == 1)
|
||||
return [cols, 0] // is_synthetic = 0
|
||||
else
|
||||
return [["value"], 1] // is_synthetic = 1
|
||||
|
||||
// Try to obtain map keys via enumerate(row). Returns [keys, is_synthetic]
|
||||
fun header_from_enumerate(row)
|
||||
keys = []
|
||||
pairs = enumerate(row)
|
||||
if (pairs == nil)
|
||||
return [["value"], 1]
|
||||
i = 0
|
||||
while (i < len(pairs))
|
||||
p = pairs[i]
|
||||
// Expect pair to be [key, value]
|
||||
if (p != nil && len(p) >= 1)
|
||||
push(keys, p[0])
|
||||
i = i + 1
|
||||
if (len(keys) == 0)
|
||||
return [["value"], 1]
|
||||
return [keys, 0]
|
||||
|
||||
fun handle_client(fd, dbh)
|
||||
print("[sqlited] client connected: fd=" + to_string(fd))
|
||||
sql = read_line(fd)
|
||||
print("[sqlited] received SQL: '" + (sql == nil ? "" : sql) + "'")
|
||||
if (sql == nil || len(sql) == 0)
|
||||
send(fd, "ERROR empty\n")
|
||||
sock_close(fd)
|
||||
return 0
|
||||
|
||||
// Try query first
|
||||
rows = sqlite_query(dbh, sql)
|
||||
if (rows != nil)
|
||||
print("[sqlited] query path; rows array obtained")
|
||||
// Build response in the stable synthetic format used in the 5558 build:
|
||||
// RESULT\n
|
||||
// value\n
|
||||
// {map n=...}\n (per row)
|
||||
resp = "RESULT\n"
|
||||
// Always emit single-column header 'value' for compatibility
|
||||
resp = resp + "value\n"
|
||||
// Emit rows
|
||||
r = 0
|
||||
while (r < len(rows))
|
||||
row = rows[r]
|
||||
print("[sqlited] sending row #" + to_string(r))
|
||||
resp = resp + sanitize_tsv(to_string(row)) + "\n"
|
||||
print("[sqlited] row #" + to_string(r) + " appended (synth)")
|
||||
r = r + 1
|
||||
// Terminate block
|
||||
print("[sqlited] finished building response; sending END and closing")
|
||||
resp = resp + "END\n"
|
||||
sb = send(fd, resp)
|
||||
print("[sqlited] total bytes sent=" + to_string(sb))
|
||||
sock_close(fd)
|
||||
return 1
|
||||
else
|
||||
// Exec path
|
||||
print("[sqlited] exec/DDL path")
|
||||
rc = sqlite_exec(dbh, sql)
|
||||
print("[sqlited] exec rc=" + to_string(rc))
|
||||
send(fd, "OK " + to_string(rc) + "\n")
|
||||
sock_close(fd)
|
||||
return 1
|
||||
|
||||
fun main()
|
||||
args = argv()
|
||||
host = arg_or_default(args, 0, "127.0.0.1")
|
||||
port = to_number(arg_or_default(args, 1, 5555))
|
||||
|
||||
dbh = sqlite_open("./database.sqlite")
|
||||
if (dbh == 0)
|
||||
print("Failed to open ./database.sqlite; create it first (sqlite3 ./database.sqlite < ./examples/data/database.sql)")
|
||||
return 1
|
||||
|
||||
lfd = tcp_listen(port, 16)
|
||||
if (lfd == 0)
|
||||
print("Failed to listen on port " + to_string(port))
|
||||
return 1
|
||||
|
||||
print("sqlited: listening on " + host + " " + to_string(port))
|
||||
while (true)
|
||||
cfd = tcp_accept(lfd)
|
||||
if (cfd > 0)
|
||||
// Handle sequentially to keep it simple for a demo
|
||||
handle_client(cfd, dbh)
|
||||
|
||||
// Explicitly invoke main when the script is run
|
||||
main()
|
||||
59
examples/extra/tcltk/tk_file_manager.fun
Executable file
59
examples/extra/tcltk/tk_file_manager.fun
Executable file
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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-12-23
|
||||
*/
|
||||
|
||||
#include <ui/tk.fun>
|
||||
|
||||
print("Initializing Fun File Manager...")
|
||||
tk = TK()
|
||||
tk.title("Fun File Manager")
|
||||
|
||||
// Current directory
|
||||
dir = env("PWD")
|
||||
if (dir == "")
|
||||
dir = "."
|
||||
print("Current directory: " + dir)
|
||||
|
||||
tk.label("path", "Current Dir: " + dir)
|
||||
tk.pack("path")
|
||||
|
||||
// Files listbox
|
||||
tk.listbox("files")
|
||||
tk.pack("files")
|
||||
|
||||
// Populate listbox
|
||||
fun refresh(tk, dir)
|
||||
print("Refreshing file list for: " + dir)
|
||||
tk.clear("files")
|
||||
files = os_list_dir(dir)
|
||||
print("Found " + to_string(len(files)) + " entries.")
|
||||
for f in files
|
||||
tk.insert("files", "end", f)
|
||||
|
||||
refresh(tk, dir)
|
||||
|
||||
// Refresh button
|
||||
// Using tk.eval for button because tk.button currently exits the app.
|
||||
tk.eval("button .refresh -text {Refresh} -command {puts {Refresh requested}}")
|
||||
tk.pack("refresh")
|
||||
|
||||
// Exit button
|
||||
tk.button("exit", "Exit")
|
||||
tk.pack("exit")
|
||||
|
||||
print("Entering Tk loop...")
|
||||
tk.loop()
|
||||
print("Tk loop exited.")
|
||||
|
||||
/* Expected output:
|
||||
A GUI... ;)
|
||||
*/
|
||||
33
examples/extra/tcltk/tk_hello.fun
Executable file
33
examples/extra/tcltk/tk_hello.fun
Executable file
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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-12-09
|
||||
*/
|
||||
|
||||
// Demonstrates the Tk stdlib wrapper class using the new Tk opcodes.
|
||||
|
||||
include <ui/tk.fun>
|
||||
|
||||
tk = TK()
|
||||
|
||||
tk.title("Fun + Tk GUI")
|
||||
|
||||
tk.label("hello", "Hello, world!")
|
||||
tk.pack("hello")
|
||||
|
||||
tk.button("ok", "OK")
|
||||
tk.pack("ok")
|
||||
|
||||
// Enter GUI loop (no-op if built without FUN_WITH_TCLTK)
|
||||
tk.loop()
|
||||
|
||||
/* Expected output:
|
||||
A GUI... ;)
|
||||
*/
|
||||
38
examples/extra/tcltk/tk_testing.fun
Executable file
38
examples/extra/tcltk/tk_testing.fun
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* 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-12-23
|
||||
*/
|
||||
|
||||
#include <ui/tk.fun>
|
||||
|
||||
print("Testing os_list_dir...")
|
||||
files = os_list_dir(".")
|
||||
print("Found " + to_string(len(files)) + " files.")
|
||||
if len(files) > 0
|
||||
print("First file: " + files[0])
|
||||
|
||||
print("Testing tk_bind parsing...")
|
||||
// We can't easily test Tk without an X server, but we can see if it crashes.
|
||||
// If built with FUN_WITH_TCLTK, it should at least initialize.
|
||||
// We use tk_eval to avoid full loop.
|
||||
rc = tk_eval("set x 1")
|
||||
print("tk_eval rc: " + to_string(rc))
|
||||
if rc == 0
|
||||
print("Tcl Result: " + tk_result())
|
||||
|
||||
/* Expected output:
|
||||
Testing os_list_dir...
|
||||
Found 23 files.
|
||||
First file: build
|
||||
Testing tk_bind parsing...
|
||||
tk_eval rc: 0
|
||||
Tcl Result: 1
|
||||
*/
|
||||
Loading…
Add table
Add a link
Reference in a new issue