1
0
Fork 0
forked from fun/fun

Renamed ./examples/external to ./examples/extensions. No code changes. (0.40.6)

This commit is contained in:
Johannes Findeisen 2026-04-27 01:20:38 +02:00
commit c68244d6f6
32 changed files with 40 additions and 41 deletions

View 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
*/

View 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
*/

View 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

View 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
*/

View 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
*/

View 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
*/

View 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
*/

View 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
*/

View file

@ -0,0 +1,107 @@
#!/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-24
*/
// Demonstrates JSON.parse/stringify/from_file/to_file via the stdlib JSON class.
include <io/json.fun>
json = JSON()
print("-- JSON: parse from string and pretty print --")
// Build a sample JSON text with most value types
sample = '{"name":"Ada","active":true,"score":99.5,"count":42,"tags":["C","Ada","Math"],"extra":null}'
obj = json.parse(sample)
print("Dump:")
print(obj)
print(obj["name"]) // Ada
print(obj["active"]) // 1
print(obj["count"]) // 42
print(len(obj["tags"])) // 3
pretty = json.stringify(obj, 1)
print(pretty)
print("-- JSON: load from file, inspect, and save pretty to /tmp --")
// Load non existent json file
path = "examples/data/nonexistent.json"
cfg = json.from_file(path)
print("Dump:")
print(cfg)
// Load a more complex example shipped with the repo
path = "examples/data/complex.json"
cfg = json.from_file(path)
print("Dump:")
print(cfg)
// Access nested fields
print(cfg["project"]["name"]) // project name
print(cfg["project"]["version"]) // version string
print(len(cfg["users"])) // number of users
// Derive a small summary map
summary = {}
summary["user_count"] = len(cfg["users"])
summary["first_user_name"] = cfg["users"][1]["name"]
summary["features_enabled"] = cfg["features"]["enabled"]
print(json.stringify(summary, 1))
// Write the loaded config back as pretty JSON
out_path = "/tmp/fun_complex_out.json"
ok = json.to_file(out_path, cfg, 1)
print(ok) // 1 on success
/* Expected output:
-- JSON: parse from string and pretty print --
Dump:
{"name": Ada, "active": true, "score": 99.5, "count": 42, "tags": [C, Ada, Math], "extra": nil}
Ada
true
42
3
{
"name":"Ada",
"active":true,
"score":99.5,
"count":42,
"tags":[
"C",
"Ada",
"Math"
],
"extra":null
}
-- JSON: load from file, inspect, and save pretty to /tmp --
Dump:
nil
Dump:
{"project": {"name": Fun, "version": 0.27.2, "website": https://fun-lang.xyz, "license": {"name": Apache-2.0, "url": https://opensource.org/license/apache-2-0}}, "features": {"enabled": [arrays, maps, json, pcsc], "experimental": {"repl": true, "sockets": true, "odbc": false, "notes": nil}}, "users": [{"id": 1, "name": Ada, "roles": [admin, math], "active": true, "score": 99.5, "prefs": {"theme": dark, "editor": {"tabWidth": 2, "font": Fira Code}}}, {"id": 2, "name": Linus, "roles": [user, kernel], "active": false, "score": 88, "prefs": {"theme": light, "editor": {"tabWidth": 8, "font": Monospace}}}], "metrics": {"counters": [0, 1, 1, 2, 3, 5, 8], "latency_ms": {"p50": 1.23, "p90": 3.21, "p99": 12.34}, "builds": 1234567890123456789, "last_release_ts": 1732406400000}, "matrix": [[1, 2, 3], [4, 5, 6], [7, 8, 9]], "notes": UTF-8 emojis: 🚀🔥, "null_field": nil}
Fun
0.27.2
2
{
"user_count":2,
"first_user_name":"Linus",
"features_enabled":[
"arrays",
"maps",
"json",
"pcsc"
]
}
1
*/

View 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
*/

View 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
*/

View 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
*/

View 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
*/

View 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-11-25
*/
/*
include <regex/pcre2.fun>
rx = Pcre2()
text = "E-mails: one@example.com, Two@Example.COM; invalid: x@y"
pattern = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
print("Has email? ", rx.test(pattern, text, rx.i()))
first = rx.match(pattern, text, rx.i())
if first != nil {
print("First: ", first["full"])
}
all = rx.find_all(pattern, text, rx.i())
for m in all {
print("Found: ", m["full"])
}
*/

View file

@ -0,0 +1,214 @@
#!/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
*/
// PCRE2 example using VM builtins directly (no class wrapper)
// Requires building Fun with -DFUN_WITH_PCRE2=ON
print("-- PCRE2 builtins (no class) --")
pattern = "(\\w+)" // capture a word
text = "Hello 123 world"
// Flags: 1=I, 2=M, 4=S, 8=U (UTF), 16=X; well use UTF by default
flags = 8
print("test:")
print(pcre2_test(pattern, text, flags))
m = pcre2_match(pattern, text, flags)
if (m != nil)
print("first full:")
print(m["full"])
print("span:")
print(m["start"])
print("..")
print(m["end"])
print("groups count:")
print(len(m["groups"]))
all = pcre2_findall("\\w+", text, flags)
for x in all
print("all:")
print(x["full"])
print("@")
print(x["start"])
print("..")
print(x["end"])
print("")
print("-- More regex demos --")
// Helper: OR flags (uses VM bor opcode)
fun OR(a, b)
return bor(a, b)
// Demo 1: E-mail extraction (case-insensitive)
email_text = "E-mails: one@example.com, Two@Example.COM; invalid: x@y"
email_pat = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
email_flags = OR(flags, 1) // UTF | I
print("Emails (findall):")
emails = pcre2_findall(email_pat, email_text, email_flags)
for e in emails
print(e["full"])
// Demo 2: URLs (very simple, for demo purposes)
url_text = "See http://example.com and https://fun-lang.xyz/docs?x=1#top"
url_pat = "https?://[A-Za-z0-9._~:/?#[@]!$&'()*+,;=%-]+"
print("URLs:")
for u in pcre2_findall(url_pat, url_text, flags)
print(u["full"])
// Demo 3: IPv4 addresses
ip_text = "ping 8.8.8.8 and 192.168.0.1; not 999.999.999.999"
ip_pat = "(?:(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)"
print("IPv4:")
for ip in pcre2_findall(ip_pat, ip_text, flags)
print(ip["full"])
// Demo 4: Dates (YYYY-MM-DD)
date_text = "Born 1999-12-31, updated 2025-11-25, bad 2025-13-40"
date_pat = "(\\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])"
print("Dates (with groups Y/M/D):")
for d in pcre2_findall(date_pat, date_text, flags)
print(d["full"])
print(join(d["groups"], "/"))
// Demo 5: Hex colors (#RRGGBB)
color_text = "Palette: #FF00FF, #1a2b3c, not #abcd or #12345g"
color_pat = "#[0-9A-Fa-f]{6}"
print("Hex colors:")
for c in pcre2_findall(color_pat, color_text, flags)
print(c["full"])
// Demo 6: Quoted strings with escapes
q_text = 'say "hi there" and "indented" plus "quote\\"inside"'
q_pat = '"([^"\\\\]|\\\\.)*"'
print("Quoted strings (with escapes):")
for q in pcre2_findall(q_pat, q_text, flags)
print(q["full"])
// Demo 7: Multiline anchors with /m (M flag)
ml_text = "first line\nSecond line\nthird"
ml_pat = "^(\\w+)"
ml_flags = OR(flags, 2) // UTF | M
print("Multiline ^ anchors (first token of each line):")
for ml in pcre2_findall(ml_pat, ml_text, ml_flags)
print(ml["full"])
// Demo 8: Dotall vs non-dotall
ds_text = "BEGIN\nline1\nline2\nEND"
pat_nd = "BEGIN.*END" // default: . does not match newlines
pat_ds = "BEGIN.*END" // with DOTALL, it does
print("Dotall OFF (should fail):")
print(pcre2_test(pat_nd, ds_text, flags))
print("Dotall ON (should match):")
print(pcre2_test(pat_ds, ds_text, OR(flags, 4)))
// Demo 9: Word boundaries and case-insensitive find
wb_text = "The theater and the THE can differ."
wb_pat = "\\bthe\\b"
print("Word boundary, case-insensitive:")
for w in pcre2_findall(wb_pat, wb_text, OR(flags, 1))
print(w["full"])
// Demo 10: Lookahead word followed by number
la_text = "foo 123, bar, baz 9"
la_pat = "\\w+(?=\\s+\\d+)"
print("Lookahead (word before number):")
for a in pcre2_findall(la_pat, la_text, flags)
print(a["full"])
// Demo 11: Non-greedy vs greedy
ng_text = "<a>one</a><a>two</a>"
greedy = "<a>.*</a>"
lazy = "<a>.*?</a>"
print("Greedy:")
for g in pcre2_findall(greedy, ng_text, OR(flags, 4)) // DOTALL ensures '.' covers any
print(g["full"])
print("Non-greedy:")
for l in pcre2_findall(lazy, ng_text, OR(flags, 4))
print(l["full"])
/* Expected output:
-- PCRE2 builtins (no class) --
test:
1
first full:
Hello
span:
0
..
5
groups count:
1
all:
Hello
@
0
..
5
all:
123
@
6
..
9
all:
world
@
10
..
15
-- More regex demos --
Emails (findall):
one@example.com
Two@Example.COM
URLs:
IPv4:
8.8.8.8
192.168.0.1
Dates (with groups Y/M/D):
1999-12-31
1999/12/31
2025-11-25
2025/11/25
Hex colors:
#FF00FF
#1a2b3c
Quoted strings (with escapes):
"hi there"
"indented"
"quote\"inside"
Multiline ^ anchors (first token of each line):
first
Second
third
Dotall OFF (should fail):
0
Dotall ON (should match):
1
Word boundary, case-insensitive:
The
the
THE
Lookahead (word before number):
foo
baz
Greedy:
<a>one</a><a>two</a>
Non-greedy:
<a>one</a>
<a>two</a>
*/

View file

@ -0,0 +1,32 @@
#!/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
*/
/*
include <regex/pcre2.fun>
re = PCRE2()
print("Testing PCRE2 showcase...")
print(re.test("\\d+", "Order #1234"))
m = re.match("(\\w+)", "hello WORLD", re.i())
if m != nil {
print(m["full"]) // hello
print(len(m["groups"]))
}
for x in re.find_all("[a-z]+", "One two THREE four", re.i()) {
print(x["full"]) // one two four
}
*/

View file

@ -0,0 +1,53 @@
#!/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-10-04
*/
// Example using stdlib PCSC2 class.
// Establish context, list readers, connect to first (if any),
// send a sample SELECT MF APDU, print results, and clean up.
#include <io/pcsc2.fun>
pc = PCSC2()
ctx = pc.establish()
print("ctx=" + to_string(ctx))
readers = pc.list_readers(ctx)
print("readers=" + to_string(readers))
number h = 0
if len(readers) > 0
h = pc.connect(ctx, readers[1])
print("handle=" + to_string(h))
if h != 0
// APDU 1: SELECT applet by AID (should return 251 data bytes, constant)
resp = pc.transmit_hex(h, "00a4040c0cD2760001354B414E4D30310000")
print(to_string(resp))
print("resp.data_hex=" + resp["data_hex"])
print("resp.sw1=" + to_string(resp["sw1"]) + " sw2=" + to_string(resp["sw2"]) + " code=" + to_string(resp["code"]))
number dlen1 = len(resp["data_hex"]) / 2
print("SELECT AID data length=" + to_string(dlen1) + " (expected 251)")
// APDU 2: GET CHALLENGE 8 (should return 8 random bytes)
resp = pc.transmit_hex(h, "0084000008")
print(to_string(resp))
print("resp.data_hex=" + resp["data_hex"])
print("resp.sw1=" + to_string(resp["sw1"]) + " sw2=" + to_string(resp["sw2"]) + " code=" + to_string(resp["code"]))
number dlen2 = len(resp["data_hex"]) / 2
print("GET CHALLENGE data length=" + to_string(dlen2) + " (expected 8)")
_ = pc.disconnect(h)
_ = pc.release(ctx)
print("done")

View file

@ -0,0 +1,44 @@
#!/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-10-02
*/
// PCSC demo using stdlib PCSC class.
// Establishes context, lists readers, optionally connects to the first,
// transmits a sample APDU (SELECT MF), and cleans up.
#include <io/pcsc.fun>
pc = PCSC()
ctx = pc.establish()
print("ctx=" + to_string(ctx))
readers = pc.list_readers(ctx)
print("readers=" + to_string(readers))
number h = 0
if len(readers) > 0
// Connect to first reader
h = pc.connect(ctx, readers[1])
print("handle=" + to_string(h))
if h != 0
// Sample APDU: SELECT MF (00 A4 00 00 02 3F 00)
// Use the safe convenience transmitter which returns a map {data, sw1, sw2, code}
resp = pc.transmit("00A40000023F00")
print("resp.data.len=" + to_string(len(resp["data"])) + " bytes")
print("resp.sw1=" + to_string(resp["sw1"]) + " sw2=" + to_string(resp["sw2"]) + " code=" + to_string(resp["code"]))
_ = pc.disconnect(h)
_ = pc.release(ctx)
print("done")

View file

@ -0,0 +1,83 @@
#!/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-10-02
*/
// Minimal PCSC example for Fun language
// Establish context and list readers (prints [] if none or unsupported)
// This example is very smartcard specific, but it works in my test environment.
// The card I am using is a german "eTicket" (local transport ticket).
//include <hex.fun>
include <io/pcsc.fun>
p = PCSC()
readers = p.get_readers()
number rc = len(readers)
print("Found " + to_string(rc) + " readers:")
for i in range(0, rc)
print(" " + to_string(i+1) + ". " + readers[i])
print("Dump:")
print(readers)
// Select Applet
string apdu = "00a4040c0cD2760001354B414E4D30310000"
print("Transmit APDU (Select Applet): " + apdu)
result = p.transmit(apdu)
print("Response:")
print(" Data: " + to_string(len(result["data"])) + " bytes")
print(" SW1: " + to_string(result["sw1"]))
print(" SW2: " + to_string(result["sw2"]))
print(" Code: " + to_string(result["code"]))
print("Dump:")
print(result)
// Call the random number generator (RNG)
apdu = "0084000008"
print("Transmit APDU (Call the random number generator (RNG): " + apdu)
result = p.transmit(apdu)
print("Response:")
print(" Data: " + to_string(len(result["data"])) + " bytes")
print(" SW1: " + to_string(result["sw1"]))
print(" SW2: " + to_string(result["sw2"]))
print(" Code: " + to_string(result["code"]))
print("Dump:")
print(result)
/* Possible output:
Found 2 readers:
1. OMNIKEY CardMan (076B:5321) 5321 00 00
2. OMNIKEY CardMan (076B:5321) 5321 00 01
Dump:
[OMNIKEY CardMan (076B:5321) 5321 00 00, OMNIKEY CardMan (076B:5321) 5321 00 01]
Transmit APDU (Select Applet): 00a4040c0cD2760001354B414E4D30310000
Response:
Data: 251 bytes
SW1: 144
SW2: 0
Code: 0
Dump:
{"data": [224, 129, 248, 226, 24, 192, 1, 1, 129, 15, 0, 0, 5, 250, 136, 243, 17, 36, 236, 105, 135, 46, 236, 105, 135, 128, 2, 7, 131, 228, 7, 192, 1, 2, 130, 2, 0, 162, 231, 3, 192, 1, 3, 236, 29, 192, 1, 4, 134, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 2, 0, 0, 195, 2, 1, 9, 144, 16, 9, 8, 7, 6, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 29, 192, 1, 9, 131, 20, 0, 0, 0, 1, 136, 243, 41, 106, 136, 142, 136, 42, 38, 33, 139, 156, 45, 30, 191, 125, 132, 2, 7, 1, 233, 29, 192, 1, 8, 131, 20, 0, 0, 0, 1, 136, 243, 41, 106, 136, 142, 136, 42, 38, 33, 139, 156, 45, 30, 191, 125, 132, 2, 7, 1, 233, 29, 192, 1, 7, 131, 20, 0, 0, 0, 1, 136, 243, 41, 106, 136, 142, 136, 42, 38, 33, 139, 156, 45, 30, 191, 125, 132, 2, 7, 1, 233, 29, 192, 1, 6, 131, 20, 0, 0, 15, 22, 136, 243, 31, 143, 136, 142, 136, 42, 37, 55, 0, 1, 47, 55, 191, 125, 132, 2, 7, 1, 233, 29, 192, 1, 5, 131, 20, 0, 0, 15, 21, 136, 243, 7, 216, 136, 143, 136, 42, 37, 55, 0, 1, 37, 58, 191, 125, 132, 2, 7, 1], "sw1": 144, "sw2": 0, "code": 0}
Transmit APDU (Call the random number generator (RNG): 0084000008
Response:
Data: 8 bytes
SW1: 144
SW2: 0
Code: 0
Dump:
{"data": [25, 179, 169, 196, 57, 36, 89, 139], "sw1": 144, "sw2": 0, "code": 0}
*/

View 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 repos lib directory or install Fun libs system-wide
- Example (Debug profile path may differ):
FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/extensions/sqlite/sqlited/server.fun 127.0.0.1 5555
Run the client
- Query:
FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/extensions/sqlite/sqlited/client.fun 127.0.0.1 5555 "SELECT id, title FROM tasks;"
- Exec/DDL:
FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/extensions/sqlite/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

View 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
*/

View 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.

View 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()

View file

@ -0,0 +1,97 @@
#!/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
*/
/*
* Demonstrate reading specific fields from an XML file using
* the minimal XML API (root name) and simple string parsing.
*/
include <io/xml.fun>
path = "./examples/data/catalog.xml"
xml = XML()
doc = xml.from_file(path)
if (doc == 0)
print("Failed to load: ")
print(path)
else
root = xml.root(doc)
print("Root element: ")
print(xml.name(root))
// Show how to access specific fields by quick-and-dirty parsing
content = read_file(path) // raw XML text
// First product name
name = xml.between(content, "<name>", "</name>")
// First price value and its currency attribute (extract value, then attribute)
price_val = xml.between(content, "<price", "</price>")
currency = ""
if (len(price_val) > 0)
currency = xml.between(price_val, "currency=\"", "\"")
// strip attribute tag part
price_text = xml.between(price_val, ">", "") // until end
if (len(price_text) == 0)
price_text = price_val
else
price_text = ""
print("First product name: ")
print(name)
print("First price: ")
if (len(currency) > 0)
print(currency)
print(" ")
print(price_text)
/* Expected output:
Root element:
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<product id="SKU-1001">
<name>Wireless Keyboard</name>
<category>Peripherals</category>
<price currency="USD">39.99</price>
<specs>
<layout>US</layout>
<connection>Bluetooth</connection>
<battery>AA</battery>
</specs>
</product>
<product id="SKU-2002">
<name>27" Monitor</name>
<category>Displays</category>
<price currency="USD">199.00</price>
<specs>
<resolution>2560x1440</resolution>
<panel>IPS</panel>
<refresh>75Hz</refresh>
</specs>
</product>
<product id="SKU-3003">
<name>USB-C Dock</name>
<category>Peripherals</category>
<price currency="USD">89.50</price>
<specs>
<ports>2xHDMI, 3xUSB-A, 1xUSB-C PD</ports>
<pd>65W</pd>
</specs>
</product>
</catalog>
First product name:
Wireless Keyboard
First price:
USD
39.99
*/

View file

@ -0,0 +1,81 @@
#!/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
*/
/*
* Access selected fields in employees.xml using minimal XML API.
*/
include <io/xml.fun>
path = "./examples/data/employees.xml"
xml = XML()
doc = xml.from_file(path)
if (doc == 0)
print("Failed to load: ")
print(path)
else
root = xml.root(doc)
print("Root element: ")
print(xml.name(root))
content = read_file(path)
// Find the first <employee> block and extract its fields
first_emp = xml.between(content, "<employee", "</employee>")
name = xml.between(first_emp, "<name>", "</name>")
role = xml.between(first_emp, "<role>", "</role>")
email = xml.between(first_emp, "<email>", "</email>")
emp_id = xml.between(first_emp, "id=\"", "\"")
print("First employee id: ")
print(emp_id)
print("Name: ")
print(name)
print("Role: ")
print(role)
print("Email: ")
print(email)
/* Expected output:
Root element:
<?xml version="1.0" encoding="UTF-8"?>
<company>
<department name="Engineering">
<employee id="E-100">
<name>Alice Doe</name>
<role>Senior Developer</role>
<email>alice@example.com</email>
</employee>
<employee id="E-101">
<name>Bob Roe</name>
<role>DevOps Engineer</role>
<email>bob@example.com</email>
</employee>
</department>
<department name="Sales">
<employee id="S-200">
<name>Carol Smith</name>
<role>Account Executive</role>
<email>carol@example.com</email>
</employee>
</department>
</company>
First employee id:
E-100
Name:
Alice Doe
Role:
Senior Developer
Email:
alice@example.com
*/

View file

@ -0,0 +1,60 @@
#!/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
*/
/*
* Access a namespaced XML file and show prefixed element names.
*/
include <io/xml.fun>
path = "./examples/data/ns_example.xml"
xml = XML()
doc = xml.from_file(path)
if (doc == 0)
print("Failed to load: ")
print(path)
else
root = xml.root(doc)
// With namespaces, the node name may include the prefix, e.g., "ns:library"
print("Root element: ")
print(xml.name(root))
content = read_file(path)
// Extract first book title and author (namespace prefix bk:)
b1 = xml.between(content, "<bk:book", "</bk:book>")
title = xml.between(b1, "<bk:title>", "</bk:title>")
author = xml.between(b1, "<bk:author>", "</bk:author>")
print("First book title: ")
print(title)
print("Author: ")
print(author)
/* Expected output:
Root element:
<?xml version="1.0" encoding="UTF-8"?>
<ns:library xmlns:ns="http://example.org/ns/library" xmlns:bk="http://example.org/ns/book">
<bk:book id="B-1">
<bk:title>The Art of Fun</bk:title>
<bk:author>J. Findeisen</bk:author>
</bk:book>
<bk:book id="B-2">
<bk:title>Minimal VM Design</bk:title>
<bk:author>A. Dev</bk:author>
</bk:book>
</ns:library>
First book title:
The Art of Fun
Author:
J. Findeisen
*/

View file

@ -0,0 +1,110 @@
#!/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
*/
// Example using the stdlib XML class wrapper
include <io/xml.fun>
xml = XML()
doc = xml.from_file("./examples/data/example.xml")
print("doc handle:")
print(doc)
if (doc == 0)
print("Failed to load XML file")
else
root = xml.root(doc)
print("root name:")
print(xml.name(root))
print("root text:")
print(xml.text(root))
/* Expected output:
doc handle:
<?xml version="1.0" encoding="UTF-8"?>
<company name="Acme Corp">
<departments>
<department id="eng" name="Engineering">
<team name="Platform">
<member id="u1">Alice</member>
<member id="u2">Bob</member>
</team>
<team name="Product">
<member id="u3">Carol</member>
</team>
</department>
<department id="ops" name="Operations">
<team name="SRE">
<member id="u4">Dave</member>
</team>
</department>
</departments>
<offices>
<office city="Berlin" country="DE"/>
<office city="Paris" country="FR"/>
</offices>
<note>Welcome to Acme!</note>
</company>
root name:
<?xml version="1.0" encoding="UTF-8"?>
<company name="Acme Corp">
<departments>
<department id="eng" name="Engineering">
<team name="Platform">
<member id="u1">Alice</member>
<member id="u2">Bob</member>
</team>
<team name="Product">
<member id="u3">Carol</member>
</team>
</department>
<department id="ops" name="Operations">
<team name="SRE">
<member id="u4">Dave</member>
</team>
</department>
</departments>
<offices>
<office city="Berlin" country="DE"/>
<office city="Paris" country="FR"/>
</offices>
<note>Welcome to Acme!</note>
</company>
root text:
<?xml version="1.0" encoding="UTF-8"?>
<company name="Acme Corp">
<departments>
<department id="eng" name="Engineering">
<team name="Platform">
<member id="u1">Alice</member>
<member id="u2">Bob</member>
</team>
<team name="Product">
<member id="u3">Carol</member>
</team>
</department>
<department id="ops" name="Operations">
<team name="SRE">
<member id="u4">Dave</member>
</team>
</department>
</departments>
<offices>
<office city="Berlin" country="DE"/>
<office city="Paris" country="FR"/>
</offices>
<note>Welcome to Acme!</note>
</company>
*/

View file

@ -0,0 +1,24 @@
#!/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
*/
// Minimal XML example using libxml2-backed builtins
doc = xml_parse("<root><item id=\"1\">a</item><item id=\"2\">b</item></root>")
print("doc handle=\(doc)")
root = xml_root(doc)
print("root name=\(xml_name(root)) text=\(xml_text(root))")
/* Expected output:
doc handle=(doc)
root name=(xml_name(root)) text=(xml_text(root))
*/