1
0
Fork 0
forked from fun/fun

Compare commits

...

18 commits

Author SHA1 Message Date
391a7689e5 Some documentation fixes. No code changes. (0.42.2) 2026-06-14 22:16:13 +02:00
1b90493a7a Some small injection fixes. (0.42.2) 2026-06-14 22:09:49 +02:00
752f0efdd1 Some web content fixes. No code changes. (0.42.1) 2026-06-12 18:45:22 +02:00
8bb99d3154 Preparing releasing 0.42.1. No code changes. (0.42.1) 2026-06-09 00:56:31 +02:00
f37b8538b2 Preparing releasing 0.42.1. No code changes. (0.42.1) 2026-06-09 00:03:20 +02:00
42e702f9e8 Documentation and Web update. No code changes. (0.42.1) 2026-06-06 18:52:16 +02:00
9b8825bae5 README update. No code changes. (0.42.1) 2026-06-04 02:46:02 +02:00
b058892cbb README update, ./make update and Doxyfile update. No code changes. (0.42.1) 2026-06-04 00:08:11 +02:00
774517edaf Small test script fix. (0.42.1) 2026-06-03 22:08:25 +02:00
509b032a53 Set Redis extension to not compile by default. No code changes. (0.42.0) 2026-06-03 22:02:48 +02:00
aeab05c9c0 Hopefully fixed the GitHub workflow file. No code changes. (0.42.0) 2026-06-03 21:59:49 +02:00
cbb81c0b93 Added a Redis/Valkey extension named redis. (0.42.0) 2026-06-03 21:48:45 +02:00
06072576fc README update. No code changes. (0.41.16) 2026-05-29 02:33:40 +02:00
1eb3e39a9c README update. No code changes. (0.41.16) 2026-05-29 02:29:42 +02:00
93f5f1f548 API documentation update. No code changes. (0.41.16) 2026-05-29 02:01:03 +02:00
2cd6628a71 Documentation update and some web fixes. No code changes. (0.41.16) 2026-05-28 23:44:45 +02:00
c61329124b Added ./playground/ for local only stuff. (0.41.16) 2026-05-28 01:59:13 +02:00
6a39862787 ./examples/features.fun and ./web/features/features.md update. (0.41.16) 2026-05-28 01:27:08 +02:00
74 changed files with 1978 additions and 327 deletions

View file

@ -6,6 +6,7 @@ on:
env:
BUILD_TYPE: Release
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
build:
@ -13,7 +14,6 @@ jobs:
defaults:
run:
working-directory: ${{github.workspace}}
steps:
- uses: actions/checkout@v4

4
.gitignore vendored
View file

@ -7,9 +7,9 @@ Doxygen.in
!.gitignore
!.github
!.gitkeep
!api/.gitkeep
.venv
api/
!api/.gitkeep
build/*
build_debug/*
build_release/*
@ -23,6 +23,8 @@ downloaded.png
json.xml
lib/*.so
out/
playground/*
!playground/.gitkeep
src/*.o
src/rust/Cargo.lock
src/rust/target

View file

@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) (during 0.x.y development phase).
## [0.42.1] - 2026-06-08
### Fixed
- Small test script issue.
### Changed
- Documentation and website updates. No runtime code changes.
- README, `make`, and `Doxyfile` updates. No runtime code changes.
## [0.42.0] - 2026-06-03
### Added
- Redis/Valkey extension named `redis` (see `src/extensions/redis.c` and `src/vm/redis/*`).
- Redis example programs: `basic_ping.fun`, `hash_ops.fun`, `kv_set_get.fun`, `list_ops.fun`, and `redis_test.fun`.
- Initial Redis documentation page under `web/documentation/extensions/redis/`.
### Changed
- Build system: Redis extension is not compiled by default; enable it explicitly if needed. No other code changes.
- GitHub workflow tweaks. No code changes.
## [0.41.16] - 2026-05-29
### Added
- `playground/` directory for local-only experiments.
### Changed
- `examples/features.fun` and website features page updates.
- README and API documentation updates. No code changes.
- Documentation and web fixes. No code changes.
## [0.41.15] - 2026-05-26
### Changed
- More internal optimizations.

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.10)
project(fun VERSION 0.41.15 LANGUAGES C)
project(fun VERSION 0.42.2 LANGUAGES C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
@ -521,7 +521,7 @@ install(DIRECTORY lib/
# Optionally install example scripts
option(FUN_INSTALL_EXAMPLES "Install example .fun scripts" ON)
if(FUN_INSTALL_EXAMPLES)
install(DIRECTORY examples/
install(DIRECTORY examples/
DESTINATION /usr/share/fun/examples
FILES_MATCHING PATTERN "*.fun"
)

View file

@ -48,7 +48,7 @@ PROJECT_NAME = "Fun API Documentation"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 0.41.15
PROJECT_NUMBER = 0.42.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewers a

211
README.md
View file

@ -2,13 +2,13 @@
## What is Fun?
Fun is a small, strict, and simple programming language that runs on a compact stack-based virtual machine. The C core is intentionally minimal; most functionality and standard libraries are implemented in Fun itself. The language emphasizes simplicity, consistency, and joy in coding.
Fun is a small, strict, and simple programming language that runs on a compact stack-based virtual machine. The C core is intentionally minimal; most functionality and standard libraries are implemented in Fun itself.
Fun is an experiment, just for fun, but Fun works!
Fun is dynamically typed with optional static type annotations, featuring first-class functions, classes with inheritance, pattern matching, and a rich standard library. It supports everything from basic scripting to TCP sockets, serial communication, threading, cryptography (MD5, SHA-1/256/384/512, CRC-32, AES-256), and a built-in debugger.
Fun is a highly strict programming language, but also highly simple. It looks like Python (My favorite language), but there are differences.
Fun is an experiment — just for fun — but Fun works!
Influenced by **[Bash](https://www.gnu.org/software/bash/)**, **[C](https://en.wikipedia.org/wiki/The_C_Programming_Language)**, **[Lua](https://www.lua.org/)**, PHP, **[Python](https://www.python.org/)**, and Rust (Most influences came from linked languages).
Influenced by **[Bash](https://www.gnu.org/software/bash/)**, **[C](https://en.wikipedia.org/wiki/The_C_Programming_Language)**, **[Lua](https://www.lua.org/)**, PHP, **[Python](https://www.python.org/)**, and **[Rust](https://www.rust-lang.org/)**.
Fun is and will ever be 100% free under the terms of the [Apache-2.0 License](https://opensource.org/license/apache-2-0).
@ -23,11 +23,19 @@ Fun is and will ever be 100% free under the terms of the [Apache-2.0 License](ht
## Characteristics
- Dynamic and optionally statically typed
- Type safety
- Written in C (C99) and Fun
- Internal libs are written with no_camel_case even when written in Fun, except class names
- Only a minimal function set is written in C, and most other core functions and libraries are implemented in Fun
- **Dynamic typing** with optional **static type annotations** (`number`, `string`, `boolean`, `float`, `byte`, `uint8`–`uint64`, `int8`–`int64`)
- **Stack-based bytecode VM** written in C99 with ~220 opcodes
- **First-class functions**, anonymous functions (`fn`), and higher-order operations (`map`, `filter`, `reduce`)
- **Classes** with constructors, methods, and single inheritance
- **Exception handling** with `try`/`catch`/`finally`
- **Built-in data structures**: arrays (with slicing), maps/objects, strings
- **Concurrency**: threads (`thread_spawn`, `thread_join`) and cooperative async scheduler
- **Networking**: TCP and Unix domain sockets with non-blocking I/O polling
- **Serial communication**: full termios-based serial port control
- **Cryptography**: pure-Fun implementations of MD5, SHA-1/256/384/512, CRC-32/CRC-32C, AES-256 ECB
- **Built-in debugger** with breakpoints, step/next/finish, and stack inspection
- **Minimal C core** — most standard libraries are implemented in Fun itself
- **Internal style**: `snake_case` for functions and variables, `CamelCase` for class names
## The Fun Manifesto
@ -45,13 +53,13 @@ Coding should be enjoyable, elegant, and consistent.
- **One Way to Do It**<br>
No clutter, no 15 ways of writing the same thing. Simplicity means clarity.
- **Hackable by Nature**<br>
Fun should be small and embeddable, like Lua. Easy to understand, extend, and tinker with true to the hacker spirit.
Fun should be small and embeddable, like Lua. Easy to understand, extend, and tinker with &mdash; true to the hacker spirit.
- **Beautiful Defaults**<br>
A language that doesnt need linters, formatters, or style guides. Beauty is built in.
A language that doesn't need linters, formatters, or style guides. Beauty is built in.
## The Community
Fun is not about being the fastest or the most feature-rich. Its about sharing joy in coding. The community should be:
Fun is not about being the fastest or the most feature-rich. It's about sharing joy in coding. The community should be:
- Respectful
- Curious
@ -70,63 +78,162 @@ A language that feels like home for developers who:
- Believe consistency is freedom
- Want to write code that looks good and feels good
Fun may not change the world but it will make programming a little more fun.
Fun may not change the world &mdash; but it will make programming a little more fun.
## Features
## Language Features
### Core
### Core Syntax & Types
- functions/classes/objects
- if/else if/else
- try/catch/finally
- Indentation-based blocks (2-space), line and block comments
- Static type annotations with automatic runtime range clamping for fixed-width integers
- `typeof()`, `to_string()`, `to_number()`, `cast()` &mdash; type introspection and conversion
- `exit` statement with optional exit code, `#include` for source includes
And much more...! Look at the specs in [Spec](./spec/) for more detailed information.
### Operators
### Lib (./lib/)
- Arithmetic: `+`, `-`, `*`, `/`, `%`
- Comparison: `<`, `<=`, `>`, `>=`, `==`, `!=`
- Logical: `&&`, `||`, `!` (short-circuit)
- Bitwise: `band()`, `bor()`, `bxor()`, `bnot()`, `shl()`, `shr()`, `rol()`, `ror()`
- Ternary: `condition ? true_expr : false_expr`
See [./lib/](https://git.xw3.org/fun/fun/src/branch/main/lib) for what the standard library provides.
### Control Flow
### Optional extensions (build-time selectable / only testing this on Linux actually):
- `if` / `else if` / `else`
- `while` with `break` and `continue`
- `for var in array` &mdash; iteration
- `for var in range(start, end)` &mdash; numeric range
- `for (key, value) in map` &mdash; map destructuring
- `match` expression (stdlib)
- `try` / `catch` / `finally`
- [CGI](https://en.wikipedia.org/wiki/Common_Gateway_Interface) support builtin using [kcgi](https://kristaps.bsd.lv/kcgi/) (optional) — see [docs](./web/documentation/extensions/kcgi/kcgi.md) &#9745;
- [cURL (libcurl)](./web/documentation/extensions/curl/curl.md) (optional) &#9745;
- [INI (iniparser)](./web/documentation/extensions/ini/ini.md) (optional) &#9745;
- [JSON (json-c)](./web/documentation/extensions/json/json.md) (optional) &#9745;
- [PCRE2](./web/documentation/extensions/pcre2/pcre2.md) (optional) &#9745;
- [PCSC (smart cards)](./web/documentation/extensions/pcsc/pcsc.md) (optional) &#9745;
- [OpenSSL](./web/documentation/extensions/openssl/openssl.md) (optional) &#9745;
- [SQLite](./web/documentation/extensions/sqlite/sqlite.md) (optional) &#9745;
- [XML (libxml2)](./web/documentation/extensions/xml2/xml2.md) (optional) &#9745;
### Functions
&#9745; = Done (or basics implemented) / &#9744; = Planned or in progress.
- `fun name(params) body` &mdash; named functions
- `fn(params) body` &mdash; anonymous function literals
- First-class: pass as arguments, store in variables, recursion
- `return` with optional value (implicit nil)
Note: Not all of the above features will be implemented. Those who are marked "Done" will probaly remain in Fun, but I don't know actually... ;)
### Object-Oriented Programming
There are some libs written in Fun available in the [./lib/](https://git.xw3.org/fun/fun/src/branch/main/lib) diretory. In the future most Fun enhancements should be written in Fun itself.
- `class Name(typed params) body` with `_construct(this, ...)` auto-invoked constructor
- `this` keyword, `obj.method(args)` method call sugar, `obj.field` dot property access
- `class Child(...) extends Parent` with method overriding
### OpenSSL quickstart (MD5)
### Data Structures
See the dedicated page: [./web/documentation/extensions/openssl/](./web/documentation/extensions/openssl/openssl.md)
- **Arrays**: literal `[1, 2, 3]`, index `arr[0]`, slice `arr[1:3]`, negative indices; `len()`, `push()`, `pop()`, `insert()`, `remove()`, `contains()`, `indexOf()`, `clear()`, `enumerate()`, `zip()`, `join()`, `map()`, `filter()`, `reduce()`
- **Maps**: literal `{key: value}`, bracket `map["key"]`, dot `map.key` access; `has()`, `keys()`, `values()`
- **Strings**: concatenation with `+`, `len()`, `substr()`, `find()`, `split()`, `join()`
### Mathematics
- Built-in: `abs`, `min`, `max`, `fmin`, `fmax`, `clamp`, `pow`, `sqrt`, `floor`, `ceil`, `trunc`, `round`, `sin`, `cos`, `tan`, `exp`, `log`, `log10`, `gcd`, `lcm`, `isqrt`, `sign`
- Random: `random_seed()`, `random_int()`, `random_number()` (cryptographic)
- Integer clamping: `sclamp()`, `uclamp()`
### I/O & Platform
- `print()`, `echo()` &mdash; output
- `read_file()`, `write_file()` &mdash; file I/O
- `input_line()` &mdash; stdin with optional prompt
- `env()`, `env_all()` &mdash; environment variables
- `proc_run()`, `system()` &mdash; process execution
- `os_list_dir()` &mdash; directory listing
- `time_now_ms()`, `clock_mono_ms()`, `date_format()`, `sleep()`
### Networking (Built-in, Unix)
- TCP: listen, accept, connect, send, recv, close
- Unix domain sockets: listen, connect
- Non-blocking I/O: `fd_set_nonblock()`, `fd_poll_read()`, `fd_poll_write()`
### Serial Communication (Unix)
- `serial_open()`, `serial_config()`, `serial_send()`, `serial_recv()`, `serial_close()`
### Concurrency
- `thread_spawn(fn, args)` returns thread ID; `thread_join(id)` returns result
- Cooperative async scheduler in stdlib
### Debugging & Tooling
- Built-in debugger with 64 breakpoints, step/next/finish/continue
- `--trace` / `-t` for opcode-level execution tracing
- `--repl-on-error`: enter REPL on runtime error with stack preserved
- Full-featured REPL with history, tab completion, multi-line input, commands (`:help`, `:load`, `:edit`, `:save`, `:debug`, `:trace`, `:type`, and more)
- `funstx` &mdash; syntax checker with optional `--fix` mode
## Standard Library (lib/)
Written primarily in Fun itself:
- **Strings**: trim, starts/ends-with, split, replace-all, case conversion, repeat
- **Arrays**: slice, reverse, concat, unique, flatten
- **Math**: `abs`, `clamp`, `gcd`, `lcm`, `powi`, min3, max3, array min/max
- **Encoding**: hex encode/decode, base64 encode/decode
- **Cryptography** (pure Fun): MD5, SHA-1, SHA-256, SHA-384, SHA-512, CRC-32, CRC-32C, AES-256 ECB
- **Functional**: Option (Some/None), Result (Ok/Err), pattern matching
- **Ranges**: `range(n)`, `range2(start, end)`, `range3(start, end, step)`
- **Date/Time**: `DateTime` class with formatting, timers, sleep
- **CLI**: `argv()`, `parse_args()` with flag support
- **Console**: `Console` class with prompt, ask, hidden input, progress bar
- **Thread**: `Thread` class, **Process**: `Process` class
- **Socket classes**: `TcpClient`, `TcpServer`, `UnixClient`
- **Serial**: `Serial` class
- **Async**: Cooperative scheduler with I/O polling
- **HTTP**: Static file server, CGI-capable server
- **IRC**: `IRCClient` with full protocol support
- **CGI**: `CGI` class for web applications
## Optional Extensions (Build-time)
Enabled via CMake flags, wrapping mature C libraries:
| Extension | Backend |
|---------------------------------------------------------------|-------------|
| [JSON](./web/documentation/extensions/json/json.md) | json-c |
| [cURL](./web/documentation/extensions/curl/curl.md) | libcurl |
| [SQLite](./web/documentation/extensions/sqlite/sqlite.md) | libsqlite3 |
| [PCRE2](./web/documentation/extensions/pcre2/pcre2.md) | libpcre2 |
| [OpenSSL](./web/documentation/extensions/openssl/openssl.md) | libcrypto |
| [INI](./web/documentation/extensions/ini/ini.md) | iniparser |
| [XML](./web/documentation/extensions/xml2/xml2.md) | libxml2 |
| [PC/SC](./web/documentation/extensions/pcsc/pcsc.md) | libpcsclite |
| [KCGI](./web/documentation/extensions/kcgi/kcgi.md) | libkcgi |
| [Redis/Valkey](./web/documentation/extensions/redis/redis.md) | hiredis |
Some extensions also have a corresponding stdlib wrapper class (e.g., `JSON`, `INI`, `XML`, `PCSC`, `PCRE2`, `KCGI`).
## Quick start
<pre>$ git clone https://git.xw3.org/fun/fun.git
$ cd fun
$ cmake -B build -DCMAKE_BUILD_TYPE=Release
$ cmake --build build --target fun
$ ./build/fun
fun> print("Hello, World!")
Hello, World!
fun> :quit</pre>
## Build Options
- **`-DCMAKE_BUILD_TYPE=Debug`** &mdash; debug build with asserts
- **`-DCMAKE_BUILD_TYPE=Release`** &mdash; optimized build with LTO and stripping
- **`-DFUN_BUILD_MUSL=ON`** &mdash; static musl build
- Toggle each extension: `-DFUN_WITH_JSON=ON`, `-DFUN_WITH_CURL=ON`, etc.
- **`-DFUN_BUILD_DOXYGEN=ON`** &mdash; API reference
## Documentation
Looking for docs? Start here:
- Local documentation index: [./web/documentation/documenatation.md](./web/documentation/documentation.md)
- Handbook: [./web/documentation/handbook/handbook.md](./web/documentation/handbook/handbook.md)
- Types overview: [./web/documentation/types/types.md](./web/documentation/types/types.md)
- REPL guide: [./web/documentation/repl/repl.md](./web/documentation/repl/repl.md)
- Testing: [./web/documentation/testing/testing.md](./web/documentation/testing/testing.md)
- Troubleshooting: [./web/documentation/troubleshooting/troubleshooting.md](./web/documentation/troubleshooting/troubleshooting.md)
Additional references:
- Specification: [./web/documentation/spec/v0.4.md](./web/documentation/spec/v0.4.md) (work in progress)
- Handbook: [./web/documentation/handbook/handbook.md](./web/documentation/handbook/handbook.md)
- Types: [./web/documentation/types/types.md](./web/documentation/types/types.md)
- REPL: [./web/documentation/repl/repl.md](./web/documentation/repl/repl.md)
- Testing: [./web/documentation/testing/testing.md](./web/documentation/testing/testing.md)
- Spec: [./web/documentation/spec/v0.4.md](./web/documentation/spec/v0.4.md)
- Changelog: [CHANGELOG.md](./CHANGELOG.md)
- Examples demonstrating most features: [./examples/](./examples/)
- Internals and VM opcodes live in [./src/](./src/) (see [./src/vm/](./src/vm) for opcode implementations)
Note: The project is evolving; some documents may lag behind. The docs index in [./web/documentation/documenatation.md](./web/documentation/documentation.md) is the most uptodate entry point.
- Examples: [./examples/features.fun](./examples/features.fun)
## Author

View file

@ -21,6 +21,7 @@ include(${CMAKE_SOURCE_DIR}/cmake/Extensions/PCSC.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/REPL.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/SQLITE.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/XML2.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/REDIS.cmake)
# Summary of extension toggles
message(STATUS "---- Fun extension summary ----")
@ -34,4 +35,5 @@ _fun_print_feature("PCRE2 (FUN_WITH_PCRE2)" FUN_WITH_PCRE2)
_fun_print_feature("PCSC-Lite (FUN_WITH_PCSC)" FUN_WITH_PCSC)
_fun_print_feature("REPL (FUN_WITH_REPL)" FUN_WITH_REPL)
_fun_print_feature("SQLite (FUN_WITH_SQLITE)" FUN_WITH_SQLITE)
_fun_print_feature("Redis (FUN_WITH_REDIS)" FUN_WITH_REDIS)
message(STATUS "--------------------------------")

View file

@ -0,0 +1,22 @@
# Redis (hiredis)
option(FUN_WITH_REDIS "Enable Redis (hiredis) support" OFF)
set(HIREDIS_INCLUDE_DIRS "")
set(HIREDIS_LINK_LIBS "")
if(FUN_WITH_REDIS)
add_definitions(-DFUN_WITH_REDIS)
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(HIREDIS QUIET hiredis)
endif()
if(HIREDIS_FOUND)
list(APPEND HIREDIS_INCLUDE_DIRS ${HIREDIS_INCLUDE_DIRS} ${HIREDIS_INCLUDE_DIRS})
list(APPEND HIREDIS_LINK_LIBS ${HIREDIS_LINK_LIBS} ${HIREDIS_LIBRARIES})
else()
find_library(HIREDIS_LIB hiredis)
if(HIREDIS_LIB)
list(APPEND HIREDIS_LINK_LIBS ${HIREDIS_LIB})
else()
message(FATAL_ERROR "hiredis not found. Install hiredis (dev headers) or disable FUN_WITH_REDIS.")
endif()
endif()
endif()

View file

@ -48,7 +48,8 @@ foreach(var_pair
LIBSQL
LIBXML2
OPENSSL
KCGI)
KCGI
HIREDIS)
if(${var_pair}_INCLUDE_DIRS)
target_include_directories(fun_core PRIVATE ${${var_pair}_INCLUDE_DIRS})
endif()
@ -94,6 +95,9 @@ if(FUN_WITH_SQLITE)
target_compile_definitions(fun_core PUBLIC FUN_WITH_SQLITE=1)
endif()
if(FUN_WITH_REDIS)
target_compile_definitions(fun_core PUBLIC FUN_WITH_REDIS=1)
endif()
if(FUN_WITH_OPENSSL)
target_compile_definitions(fun_core PUBLIC FUN_WITH_OPENSSL=1)

View file

@ -0,0 +1,29 @@
Redis extension examples (hiredis)
This folder contains small Fun scripts that demonstrate how to use the Redis extension.
Prerequisites
- Build Fun with Redis support enabled (FUN_WITH_REDIS=ON). This is ON by default in cmake/Extensions/REDIS.cmake.
- A Redis-compatible server reachable at 127.0.0.1:6379.
How to run
- Using the Fun CLI from the repository root:
- Debug profile path: build_debug/fun
- Release profile path: build_release/fun
Examples
1. basic_ping.fun
- Connects, PINGs, then closes.
2. kv_set_get.fun
- SET/GET, EXISTS and DEL for a demo key.
3. list_ops.fun
- Demonstrates LPUSH and LRANGE on a list.
4. hash_ops.fun
- Demonstrates HSET, HGET and HGETALL on a hash.
Note
- All examples use direct inline command strings with redis_cmd(handle, "COMMAND args...").
- Close the connection with redis_close(handle) when finished.

View file

@ -0,0 +1,26 @@
#!/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-06-03
*/
/*
* Simple Redis test using Fun builtins backed by hiredis.
*
* Basic PING
*/
h = redis_connect('127.0.0.1', 6379)
print('handle type: ' + typeof(h))
print(redis_cmd(h, 'PING'))
redis_close(h)

View 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: 2026-06-03
*/
/*
* Simple Redis test using Fun builtins backed by hiredis.
*
* Hash operations (HSET/HGET/HGETALL)
*/
h = redis_connect('127.0.0.1', 6379)
key = 'fun:examples:redis:hash:user1'
// Start fresh
_ = redis_cmd(h, 'DEL ' + key)
// Set a couple of fields
print(redis_cmd(h, 'HSET ' + key + ' name Alice'))
print(redis_cmd(h, 'HSET ' + key + ' age 30'))
// Fetch a single field
print('HGET name -> ' + redis_cmd(h, 'HGET ' + key + ' name'))
// Fetch all fields (returns a flat array [field, value, field, value, ...])
all = redis_cmd(h, 'HGETALL ' + key)
print('HGETALL ->')
print(all)
redis_close(h)

View file

@ -0,0 +1,37 @@
#!/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-06-03
*/
/*
* Simple Redis test using Fun builtins backed by hiredis.
*
* Simple key/value set-get-delete
*/
h = redis_connect('127.0.0.1', 6379)
key = 'fun:examples:redis:key'
// Clean slate
_ = redis_cmd(h, 'DEL ' + key)
// Set and get
print(redis_cmd(h, 'SET ' + key + ' 42'))
print('GET -> ' + redis_cmd(h, 'GET ' + key))
// Check existence
print('EXISTS -> ' + to_string(redis_cmd(h, 'EXISTS ' + key)))
// Delete
print('DEL -> ' + to_string(redis_cmd(h, 'DEL ' + key)))
print('EXISTS(after DEL) -> ' + to_string(redis_cmd(h, 'EXISTS ' + key)))
redis_close(h)

View file

@ -0,0 +1,36 @@
#!/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-06-03
*/
/*
* Simple Redis test using Fun builtins backed by hiredis.
*
* List operations (LPUSH/LRANGE)
*/
h = redis_connect('127.0.0.1', 6379)
key = 'fun:examples:redis:list'
// Start fresh
_ = redis_cmd(h, 'DEL ' + key)
// Push some values to the left
print(redis_cmd(h, 'LPUSH ' + key + ' a'))
print(redis_cmd(h, 'LPUSH ' + key + ' b'))
print(redis_cmd(h, 'LPUSH ' + key + ' c'))
// Read entire list
vals = redis_cmd(h, 'LRANGE ' + key + ' 0 -1')
print('LRANGE 0 -1 -> ')
print(vals)
redis_close(h)

View file

@ -0,0 +1,28 @@
#!/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-06-03
*/
/*
* Simple Redis test using Fun builtins backed by hiredis.
*/
h = redis_connect('127.0.0.1', 6379)
print(typeof(h))
print(redis_cmd(h, 'PING'))
_ = redis_cmd(h, 'SET fun_demo_key 42')
print(redis_cmd(h, 'GET fun_demo_key'))
redis_close(h)

View file

@ -10,48 +10,120 @@ print("")
// ============================================
print("1. Strong Type System:")
string name = "Fun Language"
float version = 0.3
float version = 0.41
boolean is_awesome = true
number meaning = 42
items = [1, 2, 3, 4, 5]
config = {"debug": true, "port": 8080}
nil_val = nil
print(" Language: " + name + " v" + to_string(version))
print(" Language: " + name + " v" + fun_version())
print(" Awesome: " + to_string(is_awesome))
print(" Nil: " + to_string(nil_val))
print("")
// ============================================
// 2. Modern Array Operations
// 2. Type Introspection
// ============================================
print("2. Array Operations:")
print("2. Type Introspection:")
number check_int = 42
string check_str = "hello"
check_arr = [1, 2, 3]
check_map = {"key": "value"}
check_float = 3.14
print(" typeof(42) = " + typeof(check_int))
print(" typeof(\"hello\") = " + typeof(check_str))
print(" typeof([1,2,3]) = " + typeof(check_arr))
print(" typeof(map) = " + typeof(check_map))
print(" typeof(3.14) = " + typeof(check_float))
print("")
// ============================================
// 3. Conversion & Casting
// ============================================
print("3. Conversion & Casting:")
print(" to_string(42) = " + to_string(42))
print(" to_number(\"99\") = " + to_string(to_number("99")))
casted = cast(1, "boolean")
print(" cast(1, \"boolean\") = " + to_string(casted))
casted2 = cast("42", "number")
print(" cast(\"42\", \"number\") = " + to_string(casted2))
print("")
// ============================================
// 4. String Manipulation
// ============================================
print("4. String Operations:")
string text = "Hello, Fun Language!"
print(" Original: " + text)
print(" Length: " + to_string(len(text)))
print(" Substr(0,5): " + substr(text, 0, 5))
print(" Find(\"Fun\"): " + to_string(find(text, "Fun")))
parts = split(text, " ")
print(" Split by space: " + to_string(parts))
joined = join(parts, "-")
print(" Join with '-': " + joined)
print("")
// ============================================
// 5. Modern Array Operations
// ============================================
print("5. Array Operations:")
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(" Original: " + to_string(numbers))
print(" Length: " + to_string(len(numbers)))
print(" Index 0: " + to_string(numbers[0]))
print(" Index last: " + to_string(numbers[len(numbers) - 1]))
// Array helpers from spec: push, join, map, filter, reduce
joined = join([10, 20, 30], ", ")
print(" Joined: " + joined)
// Slice syntax arr[start:end]
sliced = numbers[2:7]
print(" Slice [2:7]: " + to_string(sliced))
// Iterate arrays
print(" Iteration:")
for item in ["apple", "banana", "cherry"]
print(" " + item)
// Mutating array ops
arr = [10, 20, 30]
push(arr, 40)
print(" After push(40): " + to_string(arr))
removed = pop(arr)
print(" Popped: " + to_string(removed) + ", arr: " + to_string(arr))
insert(arr, 1, 15)
print(" After insert(1, 15): " + to_string(arr))
removed = remove(arr, 0)
print(" Removed at 0: " + to_string(removed) + ", arr: " + to_string(arr))
// Search & utility
print(" Contains 20? " + to_string(contains(arr, 20)))
print(" Index of 20? " + to_string(indexOf(arr, 20)))
print(" Enumerate: " + to_string(enumerate(["a", "b", "c"])))
print(" Zip: " + to_string(zip([1, 2, 3], ["x", "y", "z"])))
clear(arr)
print(" After clear: " + to_string(arr))
print("")
// ============================================
// 3. Maps (Dictionaries)
// 6. Maps (Dictionaries)
// ============================================
print("3. Map Operations:")
print("6. Map Operations:")
person = {"name": "Alice", "age": 30, "role": "Developer"}
print(" Person: " + to_string(person))
print(" Has 'age' key: " + to_string(has(person, "age")))
print(" Keys: " + to_string(keys(person)))
print(" Values: " + to_string(values(person)))
// Bracket access and assignment
print(" person[\"name\"]: " + person["name"])
person["age"] = 31
print(" Updated age: " + to_string(person["age"]))
// Object property access (dot notation)
print(" person.name: " + person.name)
print("")
// ============================================
// 4. Object-Oriented Programming
// 7. Object-Oriented Programming
// ============================================
print("4. Classes & Objects:")
print("7. Classes & Objects:")
class Counter(number initial, string label)
count = 0
@ -78,9 +150,9 @@ counter.display()
print("")
// ============================================
// 5. Inheritance
// 8. Inheritance
// ============================================
print("5. Inheritance:")
print("8. Inheritance:")
class Animal(string type)
species = ""
@ -107,9 +179,9 @@ print(" Species: " + dog.species)
print("")
// ============================================
// 6. Error Handling
// 9. Error Handling & Exceptions
// ============================================
print("6. Exception Handling:")
print("9. Exception Handling:")
try
print(" Attempting risky operation...")
@ -122,51 +194,63 @@ finally
print("")
// ============================================
// 7. String Manipulation
// 10. Comparison & Logical Operators
// ============================================
print("7. String Features:")
string text = "Hello, Fun Language!"
print(" Original: " + text)
// Note: Using stdlib functions (assumed to exist in utils modules)
// len, substr, find, split would come from stdlib
print("10. Comparison & Logical Operators:")
number a = 10
number b = 20
print(" a=10, b=20")
print(" a < b: " + to_string(a < b))
print(" a <= b: " + to_string(a <= b))
print(" a > b: " + to_string(a > b))
print(" a >= b: " + to_string(a >= b))
print(" a == b: " + to_string(a == b))
print(" a != b: " + to_string(a != b))
print(" a < b && a > 0: " + to_string(a < b && a > 0))
print(" a > b || a > 0: " + to_string(a > b || a > 0))
print(" !true: " + to_string(!true))
print(" Ternary (a < b ? \"yes\" : \"no\"): " + (a < b ? "yes" : "no"))
print("")
// ============================================
// 8. Mathematical Operations
// 11. Control Flow: if/else if/else
// ============================================
print("8. Math Functions:")
float x = 16.7
print(" x = " + to_string(x))
// Note: Math functions like sqrt, floor, ceil, abs, gcd, lcm
// would come from stdlib <utils/math.fun> or similar
print("11. If/Else If/Else:")
number score = 85
if score >= 90
print(" Grade: A")
else if score >= 80
print(" Grade: B")
else if score >= 70
print(" Grade: C")
else
print(" Grade: F")
print("")
// ============================================
// 9. Bitwise Operations
// 12. Control Flow: Loops
// ============================================
print("9. Bitwise Operations:")
number bits1 = 12
number bits2 = 10
print(" 12 & 10 = " + to_string(band(bits1, bits2)))
print(" 12 | 10 = " + to_string(bor(bits1, bits2)))
print(" 12 ^ 10 = " + to_string(bxor(bits1, bits2)))
print(" 12 << 2 = " + to_string(shl(bits1, 2)))
print(" ~12 = " + to_string(bnot(bits1)))
print("")
print("12. Loop Variants:")
// ============================================
// 10. Control Flow
// ============================================
print("10. Control Flow:")
// For-each with array
print(" For-each array:")
for item in ["apple", "banana", "cherry"]
print(" " + item)
// For loop with array
print(" Countdown:")
for i in [5, 4, 3, 2, 1]
print(" " + to_string(i) + "...")
print(" Liftoff!")
// For-range loop
print(" For-range 0..4:")
for i in range(0, 5)
print(" " + to_string(i))
// For-map loop
print(" For-map key, value:")
config_map = {"a": 1, "b": 2, "c": 3}
for (k, v) in config_map
print(" " + k + " = " + to_string(v))
// While with break/continue
print(" Skip evens:")
print(" While with break/continue:")
number n = 0
while n < 10
n = n + 1
@ -178,24 +262,55 @@ while n < 10
print("")
// ============================================
// 11. Type Introspection
// 13. Mathematical Operations
// ============================================
print("11. Type Introspection:")
number check_int = 42
string check_str = "hello"
check_arr = [1, 2, 3]
check_map = {"key": "value"}
print("13. Math Functions:")
number mx = -16
print(" abs(-16) = " + to_string(abs(mx)))
print(" min(10, 20) = " + to_string(min(10, 20)))
print(" max(10, 20) = " + to_string(max(10, 20)))
print(" clamp(50, 0, 10) = " + to_string(clamp(50, 0, 10)))
print(" pow(2, 10) = " + to_string(pow(2, 10)))
print(" sqrt(144) = " + to_string(sqrt(144)))
print(" floor(3.7) = " + to_string(floor(3.7)))
print(" ceil(3.2) = " + to_string(ceil(3.2)))
print(" round(3.5) = " + to_string(round(3.5)))
print(" trunc(3.9) = " + to_string(trunc(3.9)))
print(" sin(0) = " + to_string(sin(0)))
print(" cos(0) = " + to_string(cos(0)))
print(" gcd(12, 8) = " + to_string(gcd(12, 8)))
print(" lcm(12, 8) = " + to_string(lcm(12, 8)))
print(" isqrt(50) = " + to_string(isqrt(50)))
print(" sign(-42) = " + to_string(sign(-42)))
print(" fmin(3.1, 2.9) = " + to_string(fmin(3.1, 2.9)))
print(" fmax(3.1, 2.9) = " + to_string(fmax(3.1, 2.9)))
print(" typeof(42) = " + typeof(check_int))
print(" typeof(\"hello\") = " + typeof(check_str))
print(" typeof([1,2,3]) = " + typeof(check_arr))
print(" typeof(map) = " + typeof(check_map))
// Random (deterministic for demo)
random_seed(42)
print(" random_int(1, 100) = " + to_string(random_int(1, 100)))
print(" random_number(8) = " + random_number(8))
print("")
// ============================================
// 12. Functional Programming
// 14. Bitwise Operations
// ============================================
print("12. Higher-Order Functions:")
print("14. Bitwise Operations:")
number bits1 = 12 // 1100
number bits2 = 10 // 1010
print(" 12 & 10 = " + to_string(band(bits1, bits2)))
print(" 12 | 10 = " + to_string(bor(bits1, bits2)))
print(" 12 ^ 10 = " + to_string(bxor(bits1, bits2)))
print(" ~12 = " + to_string(bnot(bits1)))
print(" 12 << 2 = " + to_string(shl(bits1, 2)))
print(" 12 >> 2 = " + to_string(shr(bits1, 2)))
print(" rol(0x80000001, 1) = " + to_string(rol(0x80000001, 1)))
print(" ror(0x80000001, 1) = " + to_string(ror(0x80000001, 1)))
print("")
// ============================================
// 15. Functional & Higher-Order Programming
// ============================================
print("15. Higher-Order Functions:")
fun double(n)
return n * 2
@ -208,28 +323,112 @@ print(" apply_twice(5, double) = " + to_string(result))
print("")
// ============================================
// 13. Array Operations with Spec Functions
// 16. Array Higher-Order Functions
// ============================================
print("13. Array Higher-Order Functions:")
print("16. Array Higher-Order Functions:")
nums = [1, 2, 3, 4, 5]
fun square(x)
return x * x
squared = map(nums, square)
print(" Squared: " + to_string(squared))
print(" map(nums, square): " + to_string(squared))
fun is_even(x)
return x % 2 == 0
evens = filter(nums, is_even)
print(" Evens: " + to_string(evens))
print(" filter(nums, is_even): " + to_string(evens))
fun sum(acc, x)
return acc + x
total = reduce(nums, 0, sum)
print(" Sum: " + to_string(total))
print(" reduce(nums, 0, sum): " + to_string(total))
print("")
// ============================================
// 17. File I/O
// ============================================
print("17. File I/O:")
write_file("/tmp/fun_demo.txt", "Hello from Fun!")
content = read_file("/tmp/fun_demo.txt")
print(" Written and read back: " + content)
print("")
// ============================================
// 18. Environment & OS
// ============================================
print("18. Environment & OS:")
print(" HOME: " + env("HOME"))
print(" Version: " + fun_version())
// List directory (non-empty /tmp assumed)
listing = os_list_dir("/tmp")
print(" /tmp has " + to_string(len(listing)) + " entries")
// Run a command and capture output
proc_result = proc_run("echo hello from fun")
print(" proc_run output: " + proc_result["out"])
// System call exit code
sys_code = system("true")
print(" system(\"true\") exit: " + to_string(sys_code))
print("")
// ============================================
// 19. Date, Time & Sleep
// ============================================
print("19. Date, Time & Sleep:")
now = time_now_ms()
print(" Now (epoch ms): " + to_string(now))
print(" Date formatted: " + date_format(now, "%Y-%m-%d %H:%M:%S"))
mono = clock_mono_ms()
print(" Monotonic ms: " + to_string(mono))
// Short sleep to demonstrate
sleep(10)
after = clock_mono_ms()
diff = after - mono
print(" Slept 10 ms, elapsed: " + to_string(diff) + " ms")
print("")
// ============================================
// 20. Echo (print without newline)
// ============================================
print("20. Echo (no newline):")
echo(" Hello, ")
echo("world")
print("!")
print("")
// ============================================
// 21. Threading
// ============================================
print("21. Threading:")
fun worker(id)
print(" Thread " + to_string(id) + " says hi!")
return id * 2
t1 = thread_spawn(worker, [1])
t2 = thread_spawn(worker, [2])
r1 = thread_join(t1)
r2 = thread_join(t2)
print(" Joined thread results: " + to_string(r1) + ", " + to_string(r2))
print("")
// ============================================
// 22. Type-Safe Integer Clamping
// ============================================
print("22. Integer Clamping:")
byte val = 300
print(" byte val = 300 -> clamped to " + to_string(val))
int8 signed = 200
print(" int8 val = 200 -> clamped to " + to_string(signed))
uint16 big = 70000
print(" uint16 val = 70000 -> clamped to " + to_string(big))
print("")
// ============================================

2
make
View file

@ -31,6 +31,7 @@ if [ "$target" = "all" ]; then
-DFUN_WITH_CPP=ON \
-DFUN_WITH_KCGI=ON \
-DFUN_WITH_OPENSSL=ON \
-DFUN_WITH_REDIS=ON \
&& cmake --build build --target fun
elif [ "$target" = "all_debug" ]; then
rm -rf build \
@ -45,6 +46,7 @@ elif [ "$target" = "all_debug" ]; then
-DFUN_WITH_INI=ON \
-DFUN_WITH_CPP=ON \
-DFUN_WITH_OPENSSL=ON \
-DFUN_WITH_REDIS=ON \
-DFUN_DEBUG=ON \
&& cmake --build build --target fun
elif [ "$target" = "alpine" ]; then

0
playground/.gitkeep Normal file
View file

View file

@ -134,6 +134,9 @@ def parse_includes_from_vm(text: str) -> set[str]:
if d == "sqlite":
if n in {"open", "close", "exec", "query"}:
return f"SQLITE_{n.upper()}"
if d == "redis":
if n in {"connect", "cmd", "close"}:
return f"REDIS_{n.upper()}"
if d == "pcsc":
if n in {"establish", "release", "list_readers", "connect", "disconnect", "transmit"}:
return f"PCSC_{n.upper()}"

View file

@ -186,6 +186,11 @@ typedef enum {
OP_SQLITE_EXEC, // pops sql, handle; pushes sqlite rc (0=OK)
OP_SQLITE_QUERY, // pops sql, handle; pushes array<map>
// Redis (optional, hiredis)
OP_REDIS_CONNECT, // pops port:int, host:string; pushes handle (>0) or 0
OP_REDIS_CMD, // pops cmd:string, handle:int; pushes reply (string/int/array/nil/map)
OP_REDIS_CLOSE, // pops handle:int; pushes Nil
// PCSC (smart card) opcodes
OP_PCSC_ESTABLISH, // returns context id (>0) or 0
OP_PCSC_RELEASE, // pops ctx id; returns 1/0

221
src/extensions/redis.c Normal file
View file

@ -0,0 +1,221 @@
/*
* 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
*/
/**
* @file redis.c
* @brief Hiredis handle registry and reply mapping helpers for the Fun VM.
*
* This translation unit provides two small building blocks used by the Redis
* opcodes (implemented under src/vm/redis/*.c) and included from src/vm.c:
*
* 1) A process-local registry that assigns monotonically increasing positive
* integer identifiers to hiredis connection pointers (redisContext*).
* VM opcodes pass integer ids on the stack instead of raw pointers, keeping
* the bytecode portable and preventing accidental misuse of pointers.
*
* 2) Utilities to convert hiredis reply objects (redisReply) into Fun VM
* Value instances, recursively mapping arrays and supporting basic numeric
* and string types.
*
* Build-time feature flag
* -----------------------
* The code is compiled only when the CMake option FUN_WITH_REDIS is enabled
* (i.e., the preprocessor symbol FUN_WITH_REDIS is defined). When disabled,
* this file contributes no symbols and the corresponding opcodes are compiled
* into stubs that return neutral values.
*
* Ownership and lifetime
* ----------------------
* - The registry does NOT open or close Redis connections by itself; it merely
* stores pointers created elsewhere (e.g., via redisConnectWithTimeout()).
* - Adding an entry does not transfer ownership of the redisContext. Callers
* remain responsible for invoking redisFree() at the appropriate time.
* - Removing an entry from the registry does NOT free the connection; it only
* forgets the mapping between id and pointer. The connect/close opcodes take
* care of proper ownership transitions.
* - Integer identifiers are monotonically increasing per process. Once a
* handle id is deleted, it will not be reused within the same process
* lifetime.
*
* Error handling
* --------------
* Functions here perform basic validation/allocations only. Allocation
* failures return NULL (for lookups/additions) or are silently ignored (for
* deletions of non-existent ids). No hiredis API calls are made here, so no
* hiredis error codes are produced by this module itself.
*
* Thread-safety
* -------------
* The registry is a simple singly-linked list with no synchronization. It is
* NOT thread-safe. If the VM uses Redis from multiple threads, the caller must
* provide external synchronization around calls to these helpers.
*
* Example
* -------
* @code{.c}
* // Open a hiredis connection elsewhere:
* struct timeval tv = { .tv_sec = 2, .tv_usec = 0 };
* redisContext *ctx = redisConnectWithTimeout("127.0.0.1", 6379, tv);
* if (ctx && !ctx->err) {
* // Register and get an id:
* RedisHandle *h = redis_reg_add(ctx);
* int id = h ? h->id : -1;
*
* // Later look it up:
* RedisHandle *same = redis_reg_get(id);
* if (same) {
* // use same->ctx with hiredis APIs
* }
*
* // When finished, drop the registry entry and free manually:
* redis_reg_del(id);
* redisFree(ctx);
* }
* @endcode
*/
#ifdef FUN_WITH_REDIS
#include <hiredis/hiredis.h>
#include <stdlib.h>
#include <string.h>
/* Forward declarations from the VM (available in the same TU via includes) */
static Value hiredis_reply_to_value(const redisReply *r);
/**
* @brief Node in a singly-linked list of registered Redis handles.
*
* Associates a monotonically increasing positive integer identifier with a raw
* hiredis connection pointer. The list head is stored in a file-static global
* (g_redis_handles).
*/
typedef struct RedisHandle {
int id; /**< Positive identifier assigned by the registry. */
redisContext *ctx; /**< Opaque pointer to a hiredis connection. */
struct RedisHandle *next;/**< Next entry in the singly-linked list. */
} RedisHandle;
/** @brief Global head of the Redis handle list (NULL denotes empty list). */
static RedisHandle *g_redis_handles = NULL;
/** @brief Next positive identifier to assign to a newly added handle. */
static int g_redis_next_id = 1;
/**
* @brief Add a hiredis connection handle to the registry.
*
* Allocates a new list node, assigns a fresh positive id, and prepends it to
* the internal registry list. Ownership of the redisContext remains with the
* caller; this registry does not free it during deletion.
*
* @param ctx Valid pointer to an opened hiredis connection.
* @return Pointer to the newly created RedisHandle on success; NULL on
* allocation failure. The returned pointer remains owned by the
* registry; do not free it directly.
*/
static RedisHandle *redis_reg_add(redisContext *ctx) {
RedisHandle *h = (RedisHandle *)calloc(1, sizeof(RedisHandle));
if (!h) return NULL;
h->id = g_redis_next_id++;
h->ctx = ctx;
h->next = g_redis_handles;
g_redis_handles = h;
return h;
}
/**
* @brief Look up a registered Redis handle by id.
*
* Performs a linear search over the internal list to find a matching id.
*
* @param id Positive identifier previously returned by redis_reg_add().
* @return Pointer to the RedisHandle entry if found; NULL otherwise.
*
* @note The returned pointer is owned by the registry and must not be freed by
* the caller.
*/
static RedisHandle *redis_reg_get(int id) {
for (RedisHandle *p = g_redis_handles; p; p = p->next)
if (p->id == id) return p;
return NULL;
}
/**
* @brief Remove a Redis handle entry from the registry.
*
* Deletes the list node associated with the given id.
*
* @param id Positive identifier of the entry to remove.
*
* @note This function does not free the underlying redisContext; the caller is
* responsible for calling redisFree() if appropriate.
* @note If the id does not exist, the function is a no-op.
*/
static void redis_reg_del(int id) {
RedisHandle **pp = &g_redis_handles;
while (*pp) {
if ((*pp)->id == id) {
RedisHandle *d = *pp;
*pp = d->next;
free(d);
return;
}
pp = &(*pp)->next;
}
}
/**
* @brief Convert a hiredis reply to a Fun Value.
*
* Recursively maps hiredis reply types to the closest Fun representation:
* - REDIS_REPLY_STRING / STATUS -> string
* - REDIS_REPLY_INTEGER -> int
* - REDIS_REPLY_NIL -> nil
* - REDIS_REPLY_ARRAY -> array of recursively converted values
* - REDIS_REPLY_DOUBLE (if available) -> float
* - REDIS_REPLY_ERROR / default -> string (error text or "ERR")
*
* @param r Non-owning pointer to a redisReply.
* @return Value converted from the reply. For NULL replies, returns nil.
*/
static Value hiredis_reply_to_value(const redisReply *r) {
if (!r) return make_nil();
switch (r->type) {
case REDIS_REPLY_STRING:
case REDIS_REPLY_STATUS:
return make_string(r->str ? r->str : "");
case REDIS_REPLY_INTEGER:
return make_int((int64_t)r->integer);
case REDIS_REPLY_NIL:
return make_nil();
case REDIS_REPLY_ARRAY: {
int n = (int)r->elements;
if (n <= 0) {
return make_array_from_values(NULL, 0);
}
Value *items = (Value *)calloc((size_t)n, sizeof(Value));
if (!items) return make_array_from_values(NULL, 0);
for (int i = 0; i < n; i++) {
items[i] = hiredis_reply_to_value(r->element[i]);
}
Value arr = make_array_from_values(items, n);
for (int i = 0; i < n; i++) free_value(items[i]);
free(items);
return arr;
}
#ifdef REDIS_REPLY_DOUBLE
case REDIS_REPLY_DOUBLE:
return make_float(r->dval);
#endif
case REDIS_REPLY_ERROR:
default:
return make_string(r->str ? r->str : "ERR");
}
}
#endif /* FUN_WITH_REDIS */

View file

@ -142,11 +142,26 @@ int main(int argc, char **argv) {
}
char *joined = (char *)malloc(total);
if (joined) {
joined[0] = '\0';
size_t off = 0;
for (int i = 0; i < sargc; ++i) {
strcat(joined, argv[sargi + i]);
if (i + 1 < sargc) strcat(joined, " ");
int written = snprintf(joined + off, (off < total ? total - off : 0),
"%s%s",
argv[sargi + i],
(i + 1 < sargc) ? " " : "");
if (written < 0) { /* encoding error */
off = total; /* force stop */
break;
}
size_t w = (size_t)written;
if (off + w >= total) { /* ensure we don't advance beyond buffer */
off = total ? total - 1 : 0;
joined[off] = '\0';
break;
}
off += w;
}
/* Ensure NUL termination even if loop didn't run */
if (total > 0) joined[(off < total) ? off : (total - 1)] = '\0';
setenv("FUN_ARGS", joined, 1);
free(joined);
}

View file

@ -2070,6 +2070,75 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
free(name);
return 1;
}
/* Redis builtins (hiredis) */
if (strcmp(name, "redis_connect") == 0) {
(*pos)++; /* '(' */
if (!emit_expression(bc, src, len, pos)) {
parser_fail(*pos, "redis_connect expects (host, port)");
free(name);
return 0;
}
if (!consume_char(src, len, pos, ',')) {
parser_fail(*pos, "redis_connect expects (host, port)");
free(name);
return 0;
}
if (!emit_expression(bc, src, len, pos)) {
parser_fail(*pos, "redis_connect expects (host, port)");
free(name);
return 0;
}
if (!consume_char(src, len, pos, ')')) {
parser_fail(*pos, "Expected ')' after redis_connect args");
free(name);
return 0;
}
bytecode_add_instruction(bc, OP_REDIS_CONNECT, 0);
free(name);
return 1;
}
if (strcmp(name, "redis_cmd") == 0) {
(*pos)++; /* '(' */
if (!emit_expression(bc, src, len, pos)) {
parser_fail(*pos, "redis_cmd expects (handle, cmd)");
free(name);
return 0;
}
if (!consume_char(src, len, pos, ',')) {
parser_fail(*pos, "redis_cmd expects (handle, cmd)");
free(name);
return 0;
}
if (!emit_expression(bc, src, len, pos)) {
parser_fail(*pos, "redis_cmd expects (handle, cmd)");
free(name);
return 0;
}
if (!consume_char(src, len, pos, ')')) {
parser_fail(*pos, "Expected ')' after redis_cmd args");
free(name);
return 0;
}
bytecode_add_instruction(bc, OP_REDIS_CMD, 0);
free(name);
return 1;
}
if (strcmp(name, "redis_close") == 0) {
(*pos)++; /* '(' */
if (!emit_expression(bc, src, len, pos)) {
parser_fail(*pos, "redis_close expects (handle)");
free(name);
return 0;
}
if (!consume_char(src, len, pos, ')')) {
parser_fail(*pos, "Expected ')' after redis_close arg");
free(name);
return 0;
}
bytecode_add_instruction(bc, OP_REDIS_CLOSE, 0);
free(name);
return 1;
}
if (strcmp(name, "sqlite_close") == 0) {
(*pos)++; /* '(' */
if (!emit_expression(bc, src, len, pos)) {

View file

@ -259,14 +259,16 @@ static int complete_load_path(char *buf, size_t *len_io) {
if (slash) {
size_t dlen = (size_t)(slash - expanded);
if (dlen == 0) {
strcpy(dirpart, "/");
/* use bounded copy to avoid potential overflow (even though "/" fits) */
snprintf(dirpart, sizeof(dirpart), "%s", "/");
} else {
memcpy(dirpart, expanded, dlen);
dirpart[dlen] = '\0';
}
snprintf(base, sizeof(base), "%s", slash + 1);
} else {
strcpy(dirpart, ".");
/* use bounded copy to avoid potential overflow (even though "." fits) */
snprintf(dirpart, sizeof(dirpart), "%s", ".");
snprintf(base, sizeof(base), "%s", expanded);
}

View file

@ -73,6 +73,7 @@
#include "extensions/sqlite.c"
#include "extensions/xml2.c"
#include "extensions/kcgi.c"
#include "extensions/redis.c"
/* forward declarations for include mapping used in error reporting */
extern char *preprocess_includes(const char *src);
@ -1230,6 +1231,13 @@ void vm_run(VM *vm, Bytecode *entry) {
#include "vm/sqlite/query.c"
#endif
/* Redis ops */
#ifdef FUN_WITH_REDIS
#include "vm/redis/connect.c"
#include "vm/redis/cmd.c"
#include "vm/redis/close.c"
#endif
/* C++ demo opcodes (guarded) */
#ifdef FUN_WITH_CPP
case OP_CPP_ADD: {

View file

@ -66,6 +66,8 @@ static const char *opcode_names[] = {
"JSON_PARSE", "JSON_STRINGIFY", "JSON_FROM_FILE", "JSON_TO_FILE",
"CURL_GET", "CURL_POST", "CURL_DOWNLOAD",
"SQLITE_OPEN", "SQLITE_CLOSE", "SQLITE_EXEC", "SQLITE_QUERY",
/* Redis (hiredis) */
"REDIS_CONNECT", "REDIS_CMD", "REDIS_CLOSE",
"LIBSQL_OPEN", "LIBSQL_CLOSE", "LIBSQL_EXEC", "LIBSQL_QUERY",
"PCSC_ESTABLISH", "PCSC_RELEASE", "PCSC_LIST_READERS", "PCSC_CONNECT", "PCSC_DISCONNECT", "PCSC_TRANSMIT",
"PCRE2_TEST", "PCRE2_MATCH", "PCRE2_FINDALL",

View file

@ -27,6 +27,24 @@ case OP_PROC_SYSTEM: {
push_value(vm, make_int(-1));
break;
}
/* Security hardening: reject commands containing shell metacharacters or control chars
to reduce risk of command injection when using system(3). This preserves simple
command execution like "ls -l" but blocks dangerous constructs like pipes, redirects,
command substitution, etc. */
const char *bad = "&;|$<>`\\\"'()*?[]{}~";
int unsafe = 0;
for (const unsigned char *p = (const unsigned char *)cmd; *p; ++p) {
if (*p < 0x20 || strchr(bad, (int)*p)) { /* control or meta */
unsafe = 1;
break;
}
}
if (unsafe) {
/* refuse to execute potentially unsafe shell command */
push_value(vm, make_int(-1));
free(cmd);
break;
}
int status = system(cmd);
int code = -1;
#ifdef __unix__

45
src/vm/redis/close.c Normal file
View file

@ -0,0 +1,45 @@
/*
* 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
*/
/**
* @file close.c
* @brief Implements the OP_REDIS_CLOSE opcode (conditional build).
*
* Closes a previously opened Redis connection and removes its registry entry.
*
* Stack effect
* ------------
* OP_REDIS_CLOSE: (handle:int) -> Nil
*
* Behavior
* --------
* - When FUN_WITH_REDIS is enabled, looks up the handle, calls redisFree() on
* the underlying connection if present, clears the pointer, and removes the
* registry entry. Always pushes Nil.
* - When FUN_WITH_REDIS is disabled, pops the argument and pushes Nil.
*/
case OP_REDIS_CLOSE: {
#ifdef FUN_WITH_REDIS
Value vh = pop_value(vm);
int hid = (int)vh.i;
free_value(vh);
RedisHandle *h = redis_reg_get(hid);
if (h && h->ctx) {
redisFree(h->ctx);
h->ctx = NULL;
}
redis_reg_del(hid);
push_value(vm, make_nil());
#else
Value v1 = pop_value(vm); free_value(v1);
push_value(vm, make_nil());
#endif
break;
}

58
src/vm/redis/cmd.c Normal file
View file

@ -0,0 +1,58 @@
/*
* 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
*/
/**
* @file cmd.c
* @brief Implements the OP_REDIS_CMD opcode (conditional build).
*
* Executes a Redis command using the synchronous hiredis API on a previously
* opened handle and converts the reply to a Fun Value.
*
* Stack effect
* ------------
* OP_REDIS_CMD: (handle:int, cmd:string) -> reply:Value
*
* Behavior
* --------
* - The command string uses Redis inline protocol formatting (e.g.,
* "PING", "SET key val", "LRANGE list 0 -1").
* - When FUN_WITH_REDIS is enabled, the opcode looks up the connection by
* handle id, issues redisCommand(), and converts the resulting reply to a
* Fun Value:
* - status/string -> string
* - integer -> int
* - nil -> nil
* - array -> array of converted elements
* On errors or lookup failures, pushes Nil.
* - When FUN_WITH_REDIS is disabled, pops arguments and pushes Nil.
*/
case OP_REDIS_CMD: {
#ifdef FUN_WITH_REDIS
Value vcmd = pop_value(vm);
Value vh = pop_value(vm);
int hid = (int)vh.i;
char *cmd = value_to_string_alloc(&vcmd);
free_value(vh);
free_value(vcmd);
RedisHandle *h = redis_reg_get(hid);
if (!h || !h->ctx || !cmd) { if (cmd) free(cmd); push_value(vm, make_nil()); break; }
redisReply *r = (redisReply *)redisCommand(h->ctx, cmd);
free(cmd);
if (!r) { push_value(vm, make_nil()); break; }
Value out = hiredis_reply_to_value(r);
freeReplyObject(r);
push_value(vm, out);
#else
Value v1 = pop_value(vm); free_value(v1);
Value v2 = pop_value(vm); free_value(v2);
push_value(vm, make_nil());
#endif
break;
}

65
src/vm/redis/connect.c Normal file
View file

@ -0,0 +1,65 @@
/*
* 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
*/
/**
* @file connect.c
* @brief Implements the OP_REDIS_CONNECT opcode (conditional build).
*
* Establishes a synchronous TCP connection to a Redis-compatible server using
* hiredis and registers the connection handle in the internal registry.
*
* Stack effect
* ------------
* OP_REDIS_CONNECT: (host:string, port:int) -> handle:int (>0) or 0 on error
*
* Behavior
* --------
* - When FUN_WITH_REDIS is enabled, attempts a blocking connect with a
* 2-second timeout via redisConnectWithTimeout(). On success, the created
* redisContext* is stored in the registry and the assigned positive handle
* id is pushed. On failure, 0 is pushed.
* - When FUN_WITH_REDIS is disabled, arguments are popped and 0 is pushed.
*
* Notes
* -----
* - The returned handle must be closed with OP_REDIS_CLOSE to release
* resources and delete the registry entry.
*/
case OP_REDIS_CONNECT: {
#ifdef FUN_WITH_REDIS
Value vport = pop_value(vm);
Value vhost = pop_value(vm);
int port = (int)vport.i;
char *host = value_to_string_alloc(&vhost);
free_value(vport);
free_value(vhost);
if (!host) { push_value(vm, make_int(0)); break; }
struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0;
redisContext *ctx = redisConnectWithTimeout(host, port, tv);
free(host);
if (!ctx || ctx->err) {
if (ctx) redisFree(ctx);
push_value(vm, make_int(0));
break;
}
RedisHandle *h = redis_reg_add(ctx);
if (!h) {
redisFree(ctx);
push_value(vm, make_int(0));
break;
}
push_value(vm, make_int(h->id));
#else
Value v1 = pop_value(vm); free_value(v1);
Value v2 = pop_value(vm); free_value(v2);
push_value(vm, make_int(0));
#endif
break;
}

View file

@ -4,7 +4,7 @@
<!--<p style="text-align:center;">{% if site.git %}Source: <a href="{{ site.git }}{{ site.branch }}{{ page.path }}" class="git" target="_blank">{{ site.git }}{{ site.branch }}{{ page.path }}</a>{% endif %}</p>-->
<h2 style="display:none;">Navigation</h2>
<ul style="list-style:none;margin-left:0px;text-align:center;">
<li style="display:inline;"><a href="/" style="text-decoration:none;" title="Homepage" class="home">Homepage</a>&nbsp;-</li>
<li style="display:inline;"><a href="/" style="text-decoration:none;" title="Home" class="home">Home</a>&nbsp;-</li>
<!--<li style="display:inline;">&nbsp;<a href="/about/" style="text-decoration:none;" title="About" class="about">About</a>&nbsp;-</li>-->
<li style="display:inline;">&nbsp;<a href="/contact/" style="text-decoration:none;" title="Contact" class="mail">Contact</a>&nbsp;-</li>
<li style="display:inline;">&nbsp;<a href="/community/code-of-conduct/" style="text-decoration:none;" title="Code of Conduct" class="love">Code of Conduct</a>&nbsp;-</li>

View file

@ -6,14 +6,14 @@
<div class="trigger">
<h2 style="display:none;">Navigation</h2>
<ul style="list-style: none;">
<li><a class="page-link" href="/" style="text-decoration:none;" title="Homepage">/</a></li>
<li><a class="page-link" href="/" style="text-decoration:none;" title="Home">Home</a></li>
<li><a class="page-link" href="/about/" style="text-decoration:none;" title="About">About</a></li>
<li><a class="page-link" href="/features/" style="text-decoration:none;" title="Features">Features</a></li>
<li><a class="page-link" href="/documentation/" style="text-decoration:none;" title="Documentation">Documentation</a></li>
<li><a class="page-link" href="/download/" style="text-decoration:none;" title="Download">Download</a></li>
<li><a class="page-link" href="/community/" style="text-decoration:none;" title="Community">Community</a></li>
<li><a class="page-link" href="/contact/" style="text-decoration:none;" title="Contact">Contact</a></li>
<li><a class="page-link" href="/documentation/" style="text-decoration:none;" title="Documentation">Documentation</a></li>
<li><a class="page-link" href="/faq/" style="text-decoration:none;" title="FAQ">FAQ</a></li>
<li><a class="page-link" href="/features/" style="text-decoration:none;" title="FAQ">Features</a></li>
<li><a class="page-link" href="/download/" style="text-decoration:none;" title="Download">Download</a></li>
<!--
https://www.w3schools.com/howto/howto_js_toggle_dark_mode.asp
https://dev.to/warish/how-to-create-a-dark-mode-toggle-with-html-css-and-javascript-378m

View file

@ -4,7 +4,7 @@ layout: default
<div class="post">
<header class="post-header">
<h1 class="post-title" id="terminal">{{ site.title }} {{ page.title }}</h1>
<h1 class="post-title" id="NOterminal">{{ site.title }} {{ page.title }}</h1>
{% if page.noDate != true %}<p class="post-meta" style="padding: 0;">{% if page.date %}{{ page.date | date: "%b %-d, %Y" }} - {% endif %}
Edited: {{ page.path | file_date | date: "%b %-d, %Y" }}{% endif %}{% if page.author %}, <a href="{{ site.git_short }}/{{ page.author }}" target="_blank" class="user">{{ page.author }}</a>{% endif %}</p>
{% if page.subtitle %}<p style="padding: 0px 0px 0px 0px;">{{ page.subtitle }}</p>{% endif %}

View file

@ -4,7 +4,7 @@ layout: default
<div class="post">
<header class="post-header">
<h1 class="post-title" id="terminal">{{ site.title }} {{ page.title }}</h1>
<h1 class="post-title" id="NOterminal">{{ site.title }} {{ page.title }}</h1>
<p class="post-meta" style="padding: 0px 0px 0px 0px;">{% if page.date %}{{ page.date | date: "%b %-d, %Y" }}{% endif %}{% if page.date and page.date_updated %} - {% endif %}{% if page.date_updated %}Edited: {{ page.date_updated | date: "%b %-d, %Y" }}{% endif %}{% if page.author %}, <a href="{{ page.author_url }}" target="_blank" class="user">{{ page.author }}</a>{% endif %}</p>
</header>
<article class="post-content">

View file

@ -0,0 +1,58 @@
---
layout: post
published: true
author: hanez
author_email: you@hanez.org
author_url: https://hanez.org
noToc: false
title: Announcing Fun 0.42.1
description: Maintenance release with documentation updates and a small test script fix
date: 2026-06-09
categories:
- news
- releases
tags:
- fun
- release
- docs
- redis
---
We just released Fun 0.42.1. This is a small, safe maintenance update you can adopt immediately.
### Whats in 0.42.1
- Fixed: Small test script issue.
- Changed: Documentation and website updates. No runtime code changes.
- Changed: README, `make`, and `Doxyfile` updates. No runtime code changes.
If youre already on 0.42.0, this update has no behavioral impact on your programs.
### Recap: Highlights from 0.42.0
In case you missed the previous release, 0.42.0 introduced the optional Redis/Valkey extension powered by `hiredis`, along with example programs and docs.
- New extension: `redis` (connect, cmd, close)
- Examples: `basic_ping.fun`, `hash_ops.fun`, `kv_set_get.fun`, `list_ops.fun`, `redis_test.fun`
- Docs: see Documentation → Extensions → Redis
To enable the Redis extension when building Fun, pass the CMake option `-DFUN_WITH_REDIS=ON` (ensure `hiredis` is installed and visible to the toolchain). See the build guide for details.
Example (Release build directory shown by your setup):
```
cmake -S . -B build -DFUN_WITH_REDIS=ON
cmake --build build --target fun
```
### Changelog
For the complete list of changes and dates, see the projects [CHANGELOG.md](https://git.xw3.org/fun/fun/src/branch/main/CHANGELOG.md){:class="git"} in the repository.
### Thank you
Thanks to everyone testing, filing issues, and contributing examples and docs. If you want to help, check out the repository, read the docs, and say hi in the community.
Happy hacking!
Johannes Findeisen (hanez) — hanez@fun-lang.xyz

View file

@ -5,7 +5,7 @@ subtitle: More information about having fun... :)
description: About
_date: 2023-09-20
metasub: news
noToc: true
noToc: false
noDate: false
tags:
- fun
@ -19,14 +19,18 @@ permalink: /about/
## What is Fun?
Fun is an experiment, just for fun, but Fun works!
Fun is a small, strict, and simple programming language that runs on a compact stack-based virtual machine. The C core is intentionally minimal; most functionality and standard libraries are implemented in Fun itself.
Fun is a highly strict programming language, but also highly simple. It looks like Python (My favorite language), but there are differences.
Fun is dynamically typed with optional static type annotations, <a href="/features/">featuring first-class functions</a>, classes with inheritance, pattern matching, and a rich standard library. It supports everything from basic scripting to TCP sockets, serial communication, threading, cryptography (MD5, SHA-1/256/384/512, CRC-32, AES-256), and a built-in debugger.
Influenced by **[Bash](https://www.gnu.org/software/bash/){:class="ext"}**, **[C](https://en.wikipedia.org/wiki/The_C_Programming_Language){:class="ext"}**, **[Lua](https://www.lua.org/){:class="ext"}**, PHP, **[Python](https://www.python.org/){:class="ext"}**, and a little Rust (Most influences came from linked languages).
Influenced by: **[Bash](https://www.gnu.org/software/bash/){:class="ext"}**, **[C](https://en.wikipedia.org/wiki/The_C_Programming_Language){:class="ext"}**, **[Lua](https://www.lua.org/){:class="ext"}**, **[PHP](https://www.php.net/){:class="ext"}**, **[Python](https://www.python.org/){:class="ext"}**, and **[Rust](https://www.rust-lang.org/){:class="ext"}**.
Fun is an experiment - just for fun - but Fun works!
Fun is and will ever be 100% free under the terms of the [Apache-2.0 License](https://opensource.org/license/apache-2-0){:class="ext"}.
---
## Idea
- Simplicity
@ -38,11 +42,19 @@ Fun is and will ever be 100% free under the terms of the [Apache-2.0 License](ht
## Characteristics
- Dynamic and optionally statically typed
- Type safety
- Written in C (C99) and Fun
- Internal libs are written with no_camel_case even when written in Fun, except class names
- Only a minimal function set is written in C, and most other core functions and libraries are implemented in Fun
- **Dynamic typing** with optional **static type annotations** (`number`, `string`, `boolean`, `float`, `byte`, `uint8`&ndash;`uint64`, `int8`&ndash;`int64`)
- **Stack-based bytecode VM** written in C99 with ~220 opcodes
- **First-class functions**, anonymous functions (`fn`), and higher-order operations (`map`, `filter`, `reduce`)
- **Classes** with constructors, methods, and single inheritance
- **Exception handling** with `try`/`catch`/`finally`
- **Built-in data structures**: arrays (with slicing), maps/objects, strings
- **Concurrency**: threads (`thread_spawn`, `thread_join`) and cooperative async scheduler
- **Networking**: TCP and Unix domain sockets with non-blocking I/O polling
- **Serial communication**: full termios-based serial port control
- **Cryptography**: pure-Fun implementations of MD5, SHA-1/256/384/512, CRC-32/CRC-32C, AES-256 ECB
- **Built-in debugger** with breakpoints, step/next/finish, and stack inspection
- **Minimal C core** &mdash; most standard libraries are implemented in Fun itself
- **Internal style**: `snake_case` for functions and variables, `CamelCase` for class names
## The Fun Manifesto
@ -60,13 +72,13 @@ Coding should be enjoyable, elegant, and consistent.
- **One Way to Do It**<br>
No clutter, no 15 ways of writing the same thing. Simplicity means clarity.
- **Hackable by Nature**<br>
Fun should be small and embeddable, like Lua. Easy to understand, extend, and tinker with true to the hacker spirit.
Fun should be small and embeddable, like Lua. Easy to understand, extend, and tinker with &mdash; true to the hacker spirit.
- **Beautiful Defaults**<br>
A language that doesnt need linters, formatters, or style guides. Beauty is built in.
A language that doesn't need linters, formatters, or style guides. Beauty is built in.
## The Community
Fun is not about being the fastest or the most feature-rich. Its about sharing joy in coding. The community should be:
Fun is not about being the fastest or the most feature-rich. It's about sharing joy in coding. The community should be:
- Respectful
- Curious
@ -84,41 +96,153 @@ A language that feels like home for developers who:
- Believe consistency is freedom
- Want to write code that looks good and feels good
Fun may not change the world but it will make programming a little more fun.
Fun may not change the world &mdash; but it will make programming a little more fun.
## Features
## Language Features
### Core
### Core Syntax & Types
- Indentation-based blocks (2-space)
- Line and block comments
- Static type annotations with automatic runtime range clamping for fixed-width integers
- `typeof()`, `to_string()`, `to_number()`, `cast()` &mdash; type introspection and conversion
- `exit` statement with optional exit code
- Source file includes: `#include "path"` and `#include <path>`
- functions/classes/objects
- if/else if/else
- try/catch/finally
### Operators
- Arithmetic: `+`, `-`, `*`, `/`, `%`
- Comparison: `<`, `<=`, `>`, `>=`, `==`, `!=`
- Logical: `&&`, `||`, `!` (short-circuit)
- Bitwise: `band()`, `bor()`, `bxor()`, `bnot()`, `shl()`, `shr()`, `rol()`, `ror()`
- Ternary: `condition ? true_expr : false_expr`
and much more...
### Control Flow
- `if` / `else if` / `else`
- `while` with `break` and `continue`
- `for var in array` &mdash; iteration
- `for var in range(start, end)` &mdash; numeric range
- `for (key, value) in map` &mdash; map destructuring
- `match` expression (stdlib)
- `try` / `catch` / `finally`
Look at the examples here: [https://git.xw3.org/fun/fun/src/branch/main/examples](https://git.xw3.org/fun/fun/src/branch/main/examples){:class="ext"}
### Functions
- `fun name(params) body` &mdash; named functions
- `fn(params) body` &mdash; anonymous function literals
- First-class: pass as arguments, store in variables
- Recursion support
- `return` with optional value (implicit nil)
### Lib
### Object-Oriented Programming
- `class Name(typed params) body` &mdash; class definition
- `_construct(this, ...)` &mdash; auto-invoked constructor
- `this` keyword for instance access
- `obj.method(args)` &mdash; method call sugar (auto-binds this)
- `obj.field` &mdash; dot property access
- `class Child(...) extends Parent` &mdash; inheritance with method overriding
See [https://git.xw3.org/fun/fun/src/branch/main/lib](https://git.xw3.org/fun/fun/src/branch/main/lib) for what the standard library provides.
### Data Structures
- **Arrays**: literal `[1, 2, 3]`, index access `arr[0]`, slice `arr[1:3]`, negative indices, `len()`, `push()`, `pop()`, `insert()`, `remove()`, `contains()`, `indexOf()`, `clear()`, `enumerate()`, `zip()`, `join()`, `map()`, `filter()`, `reduce()`
- **Maps**: literal `{"key": value}`, bracket access `map["key"]`, dot access `map.key`, `has()`, `keys()`, `values()`
- **Strings**: concatenation with `+`, `len()`, `substr()`, `find()`, `split()`, `join()`
### Extensions (only Linux actually)
### Pattern Matching & Regex
- POSIX regex: `regex_match()`, `regex_search()`, `regex_replace()`
- [CGI](https://en.wikipedia.org/wiki/Common_Gateway_Interface){:class="ext"} support builtin using [kcgi](https://kristaps.bsd.lv/kcgi/){:class="ext"} (optional) <span style="color:red;font-weight:bold;">&#9744;</span>
- [cURL](https://curl.se/){:class="ext"} support builtin using [libcurl](https://curl.se/libcurl/){:class="ext"} (optional) <span style="color:green;font-weight:bold;">&#9745;</span>
- [INI](https://en.wikipedia.org/wiki/INI_file){:class="ext"} support builtin using [iniparser](https://gitlab.com/iniparser/iniparser/){:class="ext"} (optional) <span style="color:green;font-weight:bold;">&#9745;</span>
- [JSON](https://www.json.org/){:class="ext"} support builtin using [json-c](https://github.com/json-c/json-c){:class="ext"} (optional) <span style="color:green;font-weight:bold;">&#9745;</span>
- [PCRE2](https://pcre2project.github.io/pcre2/){:class="ext"} support builtin for Perl-Compatible Regular Expressions (optional) <span style="color:green;font-weight:bold;">&#9745;</span>
- [PCSC](https://pcscworkgroup.com/){:class="ext"} smart card support builtin using [PCSC lite](https://pcsclite.apdu.fr/){:class="ext"} (optional) <span style="color:green;font-weight:bold;">&#9745;</span>
- [SQLite](https://sqlite.org/){:class="ext"} support builtin (optional) <span style="color:green;font-weight:bold;">&#9745;</span>
- [XML](https://www.w3.org/XML/){:class="ext"} support builtin using [libxml2](https://gitlab.gnome.org/GNOME/libxml2/-/wikis/home){:class="ext"} (optional) <span style="color:green;font-weight:bold;">&#9745;</span>
### Mathematics
- Built-in: `abs`, `min`, `max`, `fmin`, `fmax`, `clamp`, `pow`, `sqrt`, `floor`, `ceil`, `trunc`, `round`, `sin`, `cos`, `tan`, `exp`, `log`, `log10`, `gcd`, `lcm`, `isqrt`, `sign`
- Random: `random_seed()`, `random_int()`, `random_number()` (cryptographic)
- Integer clamping: `sclamp()`, `uclamp()`
<span style="color:green;font-weight:bold;">&#9745;</span> = Done / <span style="color:red;font-weight:bold;">&#9744;</span> = Planned or in progress.
### I/O & Platform
- `print()`, `echo()` &mdash; output
- `read_file()`, `write_file()` &mdash; file I/O
- `input_line()` &mdash; stdin with optional prompt
- `env()`, `env_all()` &mdash; environment variables
- `proc_run()`, `system()` &mdash; process execution
- `os_list_dir()` &mdash; directory listing
Note: Not all of the above features will be implemented. Those who are marked "Done" will probably remain in Fun, but I don't know actually... ;)
### Date, Time & Sleep
- `time_now_ms()`, `clock_mono_ms()`, `date_format()`, `sleep()`
There are some libs written in Fun available in the [https://git.xw3.org/fun/fun/src/branch/main/lib/](https://git.xw3.org/fun/fun/src/branch/main/lib){:class="git"} diretory. In the future most Fun enhancements should be written in Fun itself.
### Networking (Built-in, Unix)
- **TCP**: listen, accept, connect, send, recv, close
- **Unix domain sockets**: listen, connect
- **Non-blocking I/O**: `fd_set_nonblock()`, `fd_poll_read()`, `fd_poll_write()`
### Serial Communication (Unix)
- `serial_open()`, `serial_config()`, `serial_send()`, `serial_recv()`, `serial_close()`
### Concurrency
- `thread_spawn(fn, args)` &mdash; spawn thread, returns ID
- `thread_join(id)` &mdash; join, returns result
- Cooperative async scheduler in stdlib
### Debugging
- Built-in debugger with 64 breakpoints
- Step, next, finish, continue modes
- `--trace` / `-t` for opcode-level execution tracing
- `--repl-on-error`: enter REPL on runtime error with stack preserved
## Standard Library (stdlib)
Written primarily in Fun itself (lib/):
- **String utilities**: trim, starts/ends-with, split, replace-all, case conversion, repeat
- **Array utilities**: slice, reverse, concat, unique, flatten
- **Math helpers**: `abs`, `clamp`, `gcd`, `lcm`, `powi`, min3, max3, array min/max
- **Hex encoding**: hex-to-bytes, bytes-to-hex, dec-to-hex, hex-to-dec
- **Base64 encoding**: encode/decode
- **Cryptography** (pure Fun): MD5, SHA-1, SHA-256, SHA-384, SHA-512, CRC-32, CRC-32C, AES-256 ECB
- **Functional utilities**: Option type (Some/None), Result type (Ok/Err), pattern matching
- **Range utilities**: `range(n)`, `range2(start, end)`, `range3(start, end, step)`
- **Date/Time**: `DateTime` class with formatting, timers, sleep
- **CLI**: `argv()`, `parse_args()` with flag support
- **Console**: `Console` class with prompt, ask, hidden input, progress bar
- **Process**: `Process` class wrapping proc_run/system
- **Thread**: `Thread` class wrapping spawn/join
- **Socket classes**: `TcpClient`, `TcpServer`, `UnixClient`
- **Serial**: `Serial` class
- **Async scheduler**: cooperative multitasking with I/O polling
- **HTTP Servers**: static file server, CGI-capable server
- **IRC**: `IRCClient` with full protocol support
- **CGI**: `CGI` class for web applications
## Optional Extensions (Build-time)
Enabled via CMake flags, each wrapping a mature C library:
- **[JSON](/documentation/extensions/json/)** (`FUN_WITH_JSON` / [json-c](https://json-c.github.io/json-c/){:class="ext"}) &mdash; parse, stringify, file I/O <span style="color:green;font-weight:bold;">&#9745;</span>
- **[cURL](/documentation/extensions/curl/)** (`FUN_WITH_CURL` / [libcurl](https://curl.se/libcurl/){:class="ext"}) &mdash; HTTP GET, POST, download <span style="color:green;font-weight:bold;">&#9745;</span>
- **[SQLite](/documentation/extensions/sqlite/)** (`FUN_WITH_SQLITE` / [libsqlite3](https://www.sqlite.org/){:class="ext"}) &mdash; open, query, exec, close <span style="color:green;font-weight:bold;">&#9745;</span>
- **[PCRE2](/documentation/extensions/pcre2/)** (`FUN_WITH_PCRE2` / [libpcre2](https://www.pcre.org/){:class="ext"}) &mdash; Perl-compatible regex with flags <span style="color:green;font-weight:bold;">&#9745;</span>
- **[OpenSSL](/documentation/extensions/openssl/)** (`FUN_WITH_OPENSSL` / [libcrypto](https://www.openssl.org/){:class="ext"}) &mdash; MD5, SHA-256, SHA-512, RIPEMD-160 hashing <span style="color:green;font-weight:bold;">&#9745;</span>
- **[INI](/documentation/extensions/ini/)** (`FUN_WITH_INI` / [iniparser](https://github.com/ndevilla/iniparser){:class="ext"}) &mdash; load, get/set, save config files <span style="color:green;font-weight:bold;">&#9745;</span>
- **[XML](/documentation/extensions/xml2/)** (`FUN_WITH_XML2` / [libxml2](http://xmlsoft.org/){:class="ext"}) &mdash; parse, navigate, query XML documents <span style="color:green;font-weight:bold;">&#9745;</span>
- **[PC/SC](/documentation/extensions/pcsc/)** (`FUN_WITH_PCSC` / [libpcsclite](https://pcsclite.apdu.fr/){:class="ext"}) &mdash; smart card communication <span style="color:green;font-weight:bold;">&#9745;</span>
- **[KCGI](/documentation/extensions/kcgi/)** (`FUN_WITH_KCGI` / [libkcgi](https://kristaps.bsd.lv/kcgi/){:class="ext"}) &mdash; CGI web applications <span style="color:green;font-weight:bold;">&#9745;</span>
- **[Redis/Valkey](/documentation/extensions/redis/)** (`FUN_WITH_REDIS` / [hiredis](https://github.com/redis/hiredis){:class="ext"}) &mdash; Redis and Valkey support <span style="color:green;font-weight:bold;">&#9745;</span>
<span style="color:green;font-weight:bold;">&#9745;</span> = Done
Each extension also has a corresponding stdlib wrapper class (e.g., `JSON`, `INI`, `XML`, `PCSC`, `PCRE2`, `KCGI`).
## Tooling
- **`fun`** &mdash; Interpreter/REPL: runs `.fun` scripts or starts interactive REPL
- **`funstx`** &mdash; Syntax checker with optional `--fix` mode
- **REPL** &mdash; Full-featured interactive shell with history, tab completion, multi-line input, and commands (`:help`, `:load`, `:edit`, `:save`, `:debug`, `:trace`, `:type`, and more)
- **Built-in debugger** &mdash; breakpoints, step/next/finish, stack traces
- **Formatting** &mdash; `clang-format` target for consistent C source
## Build System
- **CMake** 3.10+ with C99 standard
- Build toggles for debug mode, musl, REPL, C++ FFI, Rust FFI, Doxygen docs
- Per-extension toggles for all optional libraries
- Release builds with LTO, section GC, and symbol stripping
- CTest integration &mdash; example scripts run as automated tests
- Doxygen API reference generation
## Code
- [https://git.xw3.org/fun/fun/](https://git.xw3.org/fun/fun/){:class="git"}
- [https://git.xw3.org/fun/fun/](https://git.xw3.org/fun/fun/){:class="git"}

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Arrays in Fun
title: Arrays in Fun
subtitle: Working with arrays, creation, indexing/slicing, iteration patterns, helpers, and idioms.
description: Working with arrays, creation, indexing/slicing, iteration patterns, helpers, and idioms.
permalink: /documentation/arrays/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Async I/O ("asyncio") in Fun
title: Async I/O ("asyncio") in Fun
subtitle: Async I/O primitives and patterns, non-blocking sockets, fd polling, examples, and best practices.
description: Async I/O primitives and patterns, non-blocking sockets, fd polling, examples, and best practices.
permalink: /documentation/asyncio/

View file

@ -5,8 +5,8 @@ noToc: false
noComments: false
noDate: false
title: Building Fun
subtitle: How to build Fun with CMake, available targets, and build options (FUN_DEBUG, FUN_USE_MUSL, FUN_WITH_CPP, FUN_WITH_RUST, FUN_WITH_OPENSSL).
description: How to build Fun with CMake, available targets, and build options (FUN_DEBUG, FUN_USE_MUSL, FUN_WITH_CPP, FUN_WITH_RUST, FUN_WITH_OPENSSL).
subtitle: How to build Fun with CMake, available targets, and build options (FUN_DEBUG, FUN_USE_MUSL, FUN_WITH_CPP, FUN_WITH_RUST, FUN_WITH_OPENSSL, FUN_WITH_REDIS).
description: How to build Fun with CMake, available targets, and build options (FUN_DEBUG, FUN_USE_MUSL, FUN_WITH_CPP, FUN_WITH_RUST, FUN_WITH_OPENSSL, FUN_WITH_REDIS).
permalink: /documentation/build/
lang: en
tags:
@ -50,6 +50,7 @@ Fun exposes several options you can toggle at configure time:
- `FUN_WITH_CPP` (ON/OFF) - Enable C++-based opcode/examples support
- `FUN_WITH_RUST` (ON/OFF) - Build and link Rust staticlib from `src/rust/`
- `FUN_WITH_OPENSSL` (ON/OFF) - Enable OpenSSL-backed helpers (MD5/SHA-256/SHA-512/RIPEMD-160)
- `FUN_WITH_REDIS` (ON/OFF) - Enable Redis extension powered by hiredis (sync API: connect/cmd/close)
### VM configuration constants
@ -72,6 +73,8 @@ See [VM](../vm/) for more information.
FUN_USE_MUSL: ENABLED|DISABLED
FUN_WITH_CPP: ENABLED|DISABLED
FUN_WITH_RUST: ENABLED|DISABLED
FUN_WITH_OPENSSL: ENABLED|DISABLED
FUN_WITH_REDIS: ENABLED|DISABLED
===========================</pre>
## Example commands
@ -92,7 +95,7 @@ cmake --build build_release --target build</pre>
### Enabling optional extensions
<pre>cmake -S . -B build_release -DCMAKE_BUILD_TYPE=Release \
-DFUN_WITH_CPP=ON -DFUN_WITH_RUST=ON -DFUN_WITH_OPENSSL=ON
-DFUN_WITH_CPP=ON -DFUN_WITH_RUST=ON -DFUN_WITH_OPENSSL=ON -DFUN_WITH_REDIS=ON
cmake --build build_release --target build</pre>
### Customizing VM limits
@ -104,6 +107,8 @@ If `FUN_WITH_RUST` is enabled, ensure `cargo` is available in PATH; the build wi
If `FUN_WITH_OPENSSL` is enabled, CMake must detect your system OpenSSL (libcrypto).
If `FUN_WITH_REDIS` is enabled, ensure `hiredis` headers and library are installed and discoverable (typically via pkg-config).
## Running
- CLI: run the `fun` executable from your build directory.
- REPL: `fun -i` or just run `fun` without a script, depending on your CLI version (see [CLI](../cli/)).

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Bytecode Format (Overview)
title: Bytecode Format (Overview)
subtitle: Reference for the bytecode format (split out from internals for convenience).
description: Reference for the bytecode format (split out from internals for convenience).
permalink: /documentation/bytecode-format/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Fun CLI
title: Fun CLI
subtitle: Command-line usage of the `fun` executable, synopsis, options, exit codes, includes and library paths.
description: Command-line usage of the `fun` executable, synopsis, options, exit codes, includes and library paths.
permalink: /documentation/cli/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Contributing to Fun
title: Contributing to Fun
subtitle: How to contribute, project structure, coding style, running tests, and PR guidelines.
description: How to contribute, project structure, coding style, running tests, and PR guidelines.
permalink: /documentation/contributing/

View file

@ -1,20 +1,21 @@
---
layout: page
published: true
noToc: false
noComments: false
noDate: false
title: Documentation
subtitle: Detailed documentation for the Fun programming language.<br><br><span style="color:red;">The documentation is always a work in progress! It will always be behind the development of the code. It will be 100% aligned with a 1.0 release... ;)</span>
subtitle: Detailed documentation for the Fun programming language.
description: The Fun Documentation Index
permalink: /documentation/
lang: en
tags:
- documentation
- index
- fun
- language
- programming
- api
---
> **Note:** The documentation is always a work in progress and may lag behind the code. It will be fully aligned with a 1.0 release.
This file serves as an index of the documents in this directory. Links are relative and can be opened directly on Git hosting or locally.
## Basics
@ -77,7 +78,7 @@ The examples directory contains demonstrations of most Fun features, from basic
Documentation for optional, build-time selectable integrations lives in [./extensions/](./extensions/):
- [Index of extensions](./extensions/)
- Highlights: [cURL](./extensions/curl/), [INI](./extensions/ini/), [JSON](./extensions/json/), [XML (libxml2)](./extensions/xml2/), [SQLite](./extensions/sqlite/), [PCRE2](./extensions/pcre2/), [PC/SC](./extensions/pcsc/), [OpenSSL](./extensions/openssl/)
- Highlights: [cURL](./extensions/curl/), [INI](./extensions/ini/), [JSON](./extensions/json/), [XML (libxml2)](./extensions/xml2/), [SQLite](./extensions/sqlite/), [PCRE2](./extensions/pcre2/), [PC/SC](./extensions/pcsc/), [OpenSSL](./extensions/openssl/), [Redis](./extensions/redis/)
## Tips

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Embedding Fun
title: Embedding Fun
subtitle: Embedding the VM from C/Rust, lifecycle, and host integration tips.
description: Embedding the VM from C/Rust, lifecycle, and host integration tips.
permalink: /documentation/embedding/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Errors and Diagnostics
title: Errors and Diagnostics
subtitle: Understanding parser/runtime errors and enabling diagnostics.
description: Understanding parser/runtime errors and enabling diagnostics.
permalink: /documentation/errors-and-diagnostics/

View file

@ -27,21 +27,21 @@ All commands assume you are in the repository root.
## Prerequisites
- Build the interpreter (see handbook/). Youll have `build/fun` (paths may vary by your setup/IDE).
- Build the interpreter (see [handbook](/documentation/handbook/). Youll have `build/fun` (paths may vary by your setup/IDE).
- Set FUN_LIB_DIR to the repos lib directory when running without installation so `#include <...>` can find the standard library.
Example (Linux/macOS/BSD):
### Linux/macOS/BSD:
<pre>FUN_LIB_DIR="$(pwd)/lib" ./build/fun examples/include_lib.fun</pre>
<pre>FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/include_lib.fun</pre>
Windows (PowerShell):
### Windows (PowerShell):
<pre>$env:FUN_LIB_DIR = "$PWD/lib"
./build/fun.exe .\examples\include_lib.fun</pre>
## Interactive showcase: play.fun
The script `./scripts/play.fun` discovers all `.fun` files under `./examples` and offers to run them one by one:
The script `./scripts/play.fun` discovers all `.fun` files in `./examples` and offers to run them one by one:
<pre>./scripts/play.fun</pre>
@ -52,7 +52,7 @@ Notes:
Tip: you can run specific examples directly too:
<pre>FUN_LIB_DIR="$(pwd)/lib" fun examples/crypto/openssl_md5.fun</pre>
<pre>FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/extensions/openssl/openssl_md5.fun</pre>
## Example categories

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - cURL (libcurl) extension (optional)
title: cURL (libcurl) extension (optional)
subtitle: Documentation for cURL (libcurl) extension (optional)
description: Documentation for cURL (libcurl) extension (optional)
permalink: /documentation/extensions/curl/
@ -20,13 +20,14 @@ tags:
- Purpose: HTTP helpers using libcurl.
- Homepage: [https://curl.se/libcurl/](https://curl.se/libcurl/){:class="ext"}
## Opcodes:
## Opcodes
- OP_CURL_GET: GET; pops url:string; pushes body:string (empty on error/disabled)
- OP_CURL_POST: POST; pops body:string, url:string; pushes response:string
- OP_CURL_DOWNLOAD: Download to file; pops path:string, url:string; pushes 1/0
## Notes:
## Notes
- Requires libcurl development headers/libs.
- When disabled, helpers return empty strings/0 to match optional behavior.

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Optional extensions
title: Optional extensions
subtitle: Catalog of optional, build-time selectable extensions for the Fun VM (e.g., cURL, SQLite, JSON).
description: Catalog of optional, build-time selectable extensions for the Fun VM (e.g., cURL, SQLite, JSON).
permalink: /documentation/extensions/
@ -48,6 +48,7 @@ This section documents Fun's optional, build-time selectable extensions. Each pa
- [PC/SC (Smart cards)](./pcsc/)
- [OpenSSL](./openssl/)
- [kcgi](./kcgi/)
- [Redis (hiredis)](./redis/)
## Notes:

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - INI (iniparser) extension (optional)
title: INI (iniparser) extension (optional)
subtitle: Documentation for INI (iniparser) extension (optional)
description: Documentation for INI (iniparser) extension (optional)
permalink: /documentation/extensions/ini/
@ -20,7 +20,7 @@ tags:
- Purpose: Read/write simple INI configuration files.
- Homepage: [https://github.com/ndevilla/iniparser](https://github.com/ndevilla/iniparser){:class="ext"}
## Opcodes:
## Opcodes
- OP_INI_LOAD: pops path; pushes handle (>0) or 0
- OP_INI_FREE: pops handle; pushes 1/0
@ -32,7 +32,8 @@ tags:
- OP_INI_UNSET: pops key, section, handle; pushes 1/0
- OP_INI_SAVE: pops path, handle; pushes 1/0
## Notes:
## Notes
- Requires iniparser development headers/libs.
- When disabled, helpers return neutral values (0/empty strings) like other optional extensions.

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - JSON (json-c) extension (optional)
title: JSON (json-c) extension (optional)
subtitle: Documentation for JSON (json-c) extension (optional)
description: Documentation for JSON (json-c) extension (optional)
permalink: /documentation/extensions/json/
@ -19,14 +19,15 @@ tags:
- Purpose: JSON parse/stringify and file helpers via json-c.
- Homepage: [https://json-c.github.io/json-c/](https://json-c.github.io/json-c/){:class="ext"}
## Opcodes:
## Opcodes
- OP_JSON_PARSE: pops text; pushes value or Nil on error
- OP_JSON_STRINGIFY: pops pretty:int(0/1), value; pushes string
- OP_JSON_FROM_FILE: pops path; pushes value or Nil
- OP_JSON_TO_FILE: pops pretty:int(0/1), value, path; pushes 1/0
## Notes:
## Notes
- Requires json-c development headers/libs.
- When disabled, functions push empty/neutral values similar to other optional modules.

View file

@ -3,7 +3,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - KCGI extension (optional)
title: KCGI extension (optional)
subtitle: Documentation for KCGI (kcgi) extension (optional)
description: Documentation for KCGI (kcgi) extension (optional)
permalink: /documentation/extensions/kcgi/
@ -18,12 +18,12 @@ tags:
- Purpose: integrate the kcgi (CGI/FastCGI) C library; provide request parsing and response helpers for building CGI apps in Fun.
- Homepage: [https://kristaps.bsd.lv/kcgi/](https://kristaps.bsd.lv/kcgi/){:class="ext"}
## Build notes:
## Build notes
- Requires system kcgi development headers and libraries (pkg-config name: kcgi).
- If pkg-config is not available, build falls back to linking against libkcgi and zlib (as configured in cmake/Extensions/KCGI.cmake).
## Provided helper/opcodes:
## Provided helper/opcodes
- Function: kcgi_parse(data: none) -> Map | Nil. Parses the current CGI/FastCGI request via kcgi and returns a Map with keys like method, scheme, host, port, path, suffix, query, and fields (GET/POST map). Returns Nil on failure or when the extension is disabled.
- Function: kcgi_reply_start(code:int, content_type:string) -> 1/0. Starts the HTTP reply (sets Content-Type and opens the body).
@ -31,19 +31,20 @@ tags:
- Function: kcgi_end() -> 1/0. Finalizes the response and frees request resources.
- Opcodes: OP_KCGI_PARSE, OP_KCGI_REPLY_START, OP_KCGI_WRITE, OP_KCGI_END (internal mappings for the functions above).
## Quickstart:
## Quickstart
- Configure: cmake -S . -B build -DFUN_WITH_KCGI=ON
- Build: cmake --build build --target fun
- Run example:
- FUN_LIB_DIR="$(pwd)/lib" ./build/fun examples/cgi/hello_kcgi.fun
## Example output:
## Example output
- Content-Type: text/html; charset=utf-8
- <h1>Hello, Fun!</h1>
## Notes:
## Notes
- Running outside a real CGI/FastCGI environment may emit RFC warnings (e.g., missing REMOTE_ADDR); these are benign for local testing.
- When FUN_WITH_KCGI is OFF, the helpers behave as no-ops (returning Nil/0) to keep scripts portable.

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - OpenSSL extension (optional)
title: OpenSSL extension (optional)
subtitle: Documentation for OpenSSL extension (optional)
description: Documentation for OpenSSL extension (optional)
permalink: /documentation/extensions/openssl/
@ -19,12 +19,12 @@ tags:
- Purpose: provide small crypto helpers backed by OpenSSL. Includes md5, sha256, sha512, ripemd160 helpers.
- Homepage: [https://www.openssl.org/](https://www.openssl.org/){:class="ext"}
## Build notes:
## Build notes
- Requires system OpenSSL development headers and libraries.
- On OpenSSL 3.x, legacy MD5_* APIs are deprecated; you may see warnings during build.
## Provided helper/opcodes:
## Provided helper/opcodes
- Function: openssl_md5(data:string) -> string (lowercase hex). Falls back to empty string when the extension is disabled, mirroring other optional modules.
- Function: openssl_sha256(data:string) -> string (lowercase hex).
@ -32,7 +32,7 @@ tags:
- Function: openssl_ripemd160(data:string) -> string (lowercase hex). Note: On OpenSSL 3.x this may require the legacy provider; if the digest is unavailable, the helper returns an empty string.
- Opcodes: OP_OPENSSL_MD5, OP_OPENSSL_SHA256, OP_OPENSSL_SHA512, OP_OPENSSL_RIPEMD160 (internal mappings for the functions above).
## Quickstart:
## Quickstart
- Configure: cmake -S . -B build -DFUN_WITH_OPENSSL=ON
- Build: cmake --build build --target fun
@ -42,7 +42,7 @@ tags:
- ./build/fun examples/crypto/openssl_sha512.fun
- ./build/fun examples/crypto/openssl_ripemd160.fun
## Example output:
## Example output
- md5(abc) = 900150983cd24fb0d6963f7d28e17f72
- md5("") = d41d8cd98f00b204e9800998ecf8427e
@ -53,6 +53,7 @@ tags:
- ripemd160(abc) = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc
- ripemd160("") = 9c1185a5c5e9fc54612808977ee8f548b2258d31
## Notes:
## Notes
- The OpenSSL 3.x provider configuration on your system determines availability of RIPEMD-160. If the legacy provider is not enabled, openssl_ripemd160() will return an empty string.

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - PCRE2 (Perl-Compatible Regex) extension (optional)
title: PCRE2 (Perl-Compatible Regex) extension (optional)
subtitle: Documentation for PCRE2 (Perl-Compatible Regex) extension (optional)
description: Documentation for PCRE2 (Perl-Compatible Regex) extension (optional)
permalink: /documentation/extensions/pcre2/
@ -22,13 +22,14 @@ tags:
- Purpose: Advanced regular expressions via PCRE2.
- Homepage: [https://www.pcre.org/](https://www.pcre.org/){:class="ext"}
## Opcodes:
## Opcodes
- OP_PCRE2_TEST: pops flags, text, pattern; pushes 1/0
- OP_PCRE2_MATCH: pops flags, text, pattern; pushes match map or Nil
- OP_PCRE2_FINDALL: pops flags, text, pattern; pushes array of match maps
## Notes:
## Notes
- Requires PCRE2 development headers/libs.
- Flags are backend-specific; see implementation for supported bits.

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - PC/SC (smart cards) extension (optional)
title: PC/SC (smart cards) extension (optional)
subtitle: Documentation for PC/SC (smart cards) extension (optional)
description: Documentation for PC/SC (smart cards) extension (optional)
permalink: /documentation/extensions/pcsc/
@ -20,7 +20,7 @@ tags:
- Purpose: Access smart card readers/cards via PC/SC (pcsclite).
- Homepage: [https://pcsclite.apdu.fr/](https://pcsclite.apdu.fr/){:class="ext"}
## Opcodes:
## Opcodes
- OP_PCSC_ESTABLISH: returns context id (>0) or 0
- OP_PCSC_RELEASE: pops ctx id; returns 1/0
@ -29,7 +29,8 @@ tags:
- OP_PCSC_DISCONNECT: pops handle id; returns 1/0
- OP_PCSC_TRANSMIT: pops apdu, handle id; returns map with data/SW/rc
## Notes:
## Notes
- Requires PC/SC lite development headers/libs.
- Behavior and availability depend on platform and reader drivers.

View file

@ -0,0 +1,84 @@
---
layout: page
published: true
noToc: false
noComments: false
noDate: false
title: Redis (hiredis) Extension
subtitle: Redis/Valkey client integration for Fun using the hiredis C library.
description: Documentation for the optional Redis extension in Fun. Provides redis_connect, redis_cmd, and redis_close builtins backed by hiredis.
permalink: /documentation/extensions/redis/
lang: en
tags:
- redis
- valkey
- hiredis
- database
- cache
- extension
- networking
- async
---
The Redis extension adds a minimal client for Redis-compatible servers using the [hiredis](https://github.com/redis/hiredis){:class="ext"} C library. It currently exposes a simple synchronous API; Async I/O through Fun's event loop will be added later.
## Requirements
- Build-time option: `-DFUN_WITH_REDIS=ON`
- System libraries: `hiredis` headers and library available (via pkg-config or default linker search paths)
- Runtime: a Redis-compatible server (e.g., on `127.0.0.1:6379`)
## Enabling the extension
Example CMake configure line enabling Redis along with other options:
<pre>cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \
-DFUN_WITH_REDIS=ON</pre>
When configured, the build summary will include a line:
<pre>Redis (FUN_WITH_REDIS): ENABLED</pre>
## Provided builtins/opcodes
- `redis_connect(host: string, port: int) -> int`
- Establishes a TCP connection and returns a positive handle id on success, or `0` on error.
- Example: `h = redis_connect('127.0.0.1', 6379)`
- `redis_cmd(handle: int, cmd: string) -> Value`
- Executes a Redis inline command string and returns the reply as a Fun value.
- Reply mapping:
- Status/String -> string
- Integer -> number
- Nil -> `nil`
- Array -> array of recursively converted values
- Example: `print(redis_cmd(h, 'PING'))``PONG`
- `redis_close(handle: int) -> Nil`
- Closes the connection associated with the handle and frees resources.
## Notes and limitations
- Error handling: invalid handles or failed commands yield `nil` or an empty/neutral value depending on context.
- Security: this initial version does not include TLS; TLS support may be added in a future iteration once hiredis SSL is wired in.
- Async: the current API is synchronous. Integration with Fun's asyncio is planned.
## Examples
Runnable examples are included in the source tree:
- `examples/extensions/redis/basic_ping.fun`
- `examples/extensions/redis/kv_set_get.fun`
- `examples/extensions/redis/list_ops.fun`
- `examples/extensions/redis/hash_ops.fun`
- `examples/extensions/redis/redis_test.fun`
Quick start (from repo root, using a built Fun executable):
<pre>build_debug/fun examples/extensions/redis/basic_ping.fun</pre>
## Troubleshooting
- Ensure a Redis server is reachable at the host/port you pass to `redis_connect`.
- If `redis_connect` returns `0`, verify the hiredis library and headers are installed and that Fun was configured with `-DFUN_WITH_REDIS=ON`.

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - SQLite extension (optional)
title: SQLite extension (optional)
subtitle: Documentation for SQLite extension (optional)
description: Documentation for SQLite extension (optional)
permalink: /documentation/extensions/sqlite/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - XML (libxml2) extension (optional)
title: XML (libxml2) extension (optional)
subtitle: Documentation for XML (libxml2) extension (optional)
description: Documentation for XML (libxml2) extension (optional)
permalink: /documentation/extensions/xml2/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Fun executable, full usage guide
title: Fun executable, full usage guide
subtitle: Full usage guide for the `fun` executable, invocation patterns, REPL, env vars, include paths, examples, and install locations.
description: Full usage guide for the `fun` executable, invocation patterns, REPL, env vars, include paths, examples, and install locations.
permalink: /documentation/fun/
@ -28,7 +28,7 @@ This document explains how to use the `fun` binary after building or installing
## Synopsis
<pre>fun [options] [script.fun] [-- args...]</pre>
-
-
- If [script.fun] is provided, `fun` runs the script.
- If omitted and the build enables the REPL, `fun` starts an interactive session.

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - funstx — Fun syntax checker and fixer
title: funstx — Fun syntax checker and fixer
subtitle: Syntax checker for .fun files with optional --fix auto-corrections; usage, exit codes, and limitations.
description: Syntax checker for .fun files with optional --fix auto-corrections; usage, exit codes, and limitations.
permalink: /documentation/funstx/

View file

@ -111,7 +111,7 @@ Opcode handlers organization:
- To keep vm.c readable, most opcode implementations are factored into small .c files included directly into vm.c (e.g., vm/core/load_const.c, vm/logic/and.c, vm/arrays/push.c, vm/math/abs.c, vm/os/thread_spawn.c, etc.).
- This is a deliberate “amalgamation” style: small single-purpose C units compiled as part of vm.c.
- Optional subsystems (JSON, PCRE2, CURL, SQLite, libSQL, PC/SC, XML2, Tcl/Tk, Notcurses, INI, OpenSSL/LibreSSL crypto helpers, sockets, serial, OS helpers) are grouped under src/extensions and src/vm/<domain>/.
- Optional subsystems (JSON, PCRE2, CURL, SQLite, libSQL, PC/SC, XML2, Redis, Tcl/Tk, Notcurses, INI, OpenSSL/LibreSSL crypto helpers, sockets, serial, OS helpers) are grouped under src/extensions and src/vm/<domain>/.
Dispatch naming and visibility:
@ -152,7 +152,7 @@ The VM is dynamically typed. Values carry a tag; operations check types at runti
- Maps: OP_MAKE_MAP/KEYS/VALUES/HAS_KEY.
- Conversions/reflection: OP_TO_NUMBER/TO_STRING/CAST/TYPEOF, OP_UCLAMP/SCLAMP.
- I/O and OS: OP_READ_FILE/WRITE_FILE/INPUT_LINE/ENV/PROC_RUN/PROC_SYSTEM/TIME_NOW_MS/CLOCK_MONO_MS/DATE_FORMAT/OS_LIST_DIR/RANDOM_NUMBER, sockets, serial.
- Extensions (optional): JSON, CURL, SQLite, libSQL, PC/SC, XML2, Tcl/Tk, Notcurses, INI, OpenSSL/LibreSSL.
- Extensions (optional): JSON, CURL, SQLite, libSQL, PC/SC, XML2, Redis, Tcl/Tk, Notcurses, INI, OpenSSL/LibreSSL.
Each handler enforces argument types and returns clear error messages via vm_raise_error on misuse.

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Maps in Fun
title: Maps in Fun
subtitle: Working with maps, construction, lookup/update, merging, iteration, and common patterns.
description: Working with maps, construction, lookup/update, merging, iteration, and common patterns.
permalink: /documentation/maps/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Working with numbers and floats in Fun
title: Working with numbers and floats in Fun
subtitle: Working with integers and floats, arithmetic, conversions, clamping, bitwise ops, and patterns.
description: Working with integers and floats, arithmetic, conversions, clamping, bitwise ops, and patterns.
permalink: /documentation/numbers/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Performance Guide
title: Performance Guide
subtitle: Build/runtime tuning tips and patterns for better performance.
description: Build/runtime tuning tips and patterns for better performance.
permalink: /documentation/performance/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Roadmap
title: Roadmap
subtitle: High-level direction, planned features, and pointers to issues.
description: High-level direction, planned features, and pointers to issues.
permalink: /documentation/roadmap/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Writing Rust-backed opcodes for Fun VM
title: Writing Rust-backed opcodes for Fun VM
subtitle: Writing Rust-backed opcodes and wiring them into the C VM; build/setup notes.
description: Writing Rust-backed opcodes and wiring them into the C VM; build/setup notes.
permalink: /documentation/rust/
@ -179,7 +179,7 @@ The minimal helpers shown cover 64-bit integers and simple strings. Extending th
- Ensuring all pointers and lifetimes are well-defined: strings pushed to the VM should be copied or allocated using VM facilities so they remain valid after the call.
- Keeping Rust no_std unless you add an allocator and link setup to support std.
## Troubleshooting
## Troubleshooting
- Link errors: Make sure FUN_WITH_RUST=ON for your build directory and that the Rust library is compiled before linking the VM. Use the rust_ops_build target if provided by your profile.
- Missing symbol at runtime: Confirm #[no_mangle] and extern "C" on the Rust function and that C sees the correct prototype.

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Security and Sandboxing
title: Security and Sandboxing
subtitle: Trust boundaries, I/O expectations, and capability restrictions.
description: Trust boundaries, I/O expectations, and capability restrictions.
permalink: /documentation/security-and-sandboxing/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Working with strings in Fun
title: Working with strings in Fun
subtitle: Working with strings, literals/escaping, concatenation, substr/find, split, and conversions.
description: Working with strings, literals/escaping, concatenation, substr/find, split, and conversions.
permalink: /documentation/strings/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Fun Style Guide
title: Fun Style Guide
subtitle: Coding conventions for C and Fun (indentation, naming, idioms).
description: Coding conventions for C and Fun (indentation, naming, idioms).
permalink: /documentation/style-guide/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Building and Running Tests
title: Building and Running Tests
subtitle: How to build and run tests/targets with CMake/CTest, and where to add new tests.
description: How to build and run tests/targets with CMake/CTest, and where to add new tests.
permalink: /documentation/testing/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Troubleshooting Guide
title: Troubleshooting Guide
subtitle: Common issues and quick fixes for build, includes, and REPL usage.
description: Common issues and quick fixes for build, includes, and REPL usage.
permalink: /documentation/troubleshooting/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Core datatypes, arrays, maps, strings, numbers, booleans and nil
title: Core datatypes, arrays, maps, strings, numbers, booleans and nil
subtitle: Core types (numbers, strings, arrays, maps, nil/bool), common operations, patterns, and interop notes.
description: Core types (numbers, strings, arrays, maps, nil/bool), common operations, patterns, and interop notes.
permalink: /documentation/types/
@ -177,7 +177,7 @@ print(typeof({})) // "map"</pre>
## Common patterns
- Accumulate values:
-
-
```
acc = []
for i = 0; i < 5; i = i + 1 { push(acc, i*i) }
@ -185,14 +185,14 @@ print(typeof({})) // "map"</pre>
```
- Group related data with a map:
-
-
```
person = { "name": "Lin", "skills": ["C", "Fun"] }
print(person["skills"][1]) // Fun
```
- Safe map read:
-
-
```
v = person["twitter"]
handle = v != nil ? v : "(none)"

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun - Writing Tests
title: Writing Tests
subtitle: How to author new tests for Fun and opcode components.
description: How to author new tests for Fun and opcode components.
permalink: /documentation/writing-tests/

View file

@ -4,7 +4,7 @@ published: true
noToc: false
noComments: false
noDate: false
title: Fun — Feature Overview
title: Feature Overview
subtitle: A high-level list of language, VM/runtime, tooling, and ecosystem features in Fun.
description: Comprehensive feature list of the Fun programming language, its VM/runtime, tooling, build options, and optional extensions.
permalink: /features/
@ -24,103 +24,406 @@ tags:
- cgi
---
This page summarizes the core capabilities of Fun: the language, its virtual machine/runtime, tooling, build options, and the surrounding ecosystem. For indepth pages, see also:
This page summarizes the core capabilities of Fun: the language, its virtual machine/runtime, tooling, build options, and the surrounding ecosystem.
- VM opcodes overview: /documentation/opcodes/
- Optional extensions catalog: /documentation/extensions/
---
## Language
- Simple, expressive syntax designed for scripting and embedding
- Firstclass functions and function calls
- Variables, locals and globals with stackbased execution model
- Control flow: conditional jumps, returns, and basic boolean operators
- Arrays and maps as primary collection types (literal construction, indexing, slicing, membership)
- Strings with common operations (substring, find, split, join)
- Arithmetic and bitwise integer operations (add, sub, mul, div, mod; band, bor, bxor, shifts, rotations)
- Exceptions and error handling primitives (throw/try semantics at VM level)
- Regular expressions support via optional PCRE2 extension (see Extensions)
### Syntax & Structure
Notes:
- Indentation-based block structure (strict 2-space indent)
- Line comments (`//`) and block comments (`/* */`)
- Optional shebang (`#!`) line for script execution
- Unicode string support via UTF-8 throughout
- `exit` statement with optional exit code
- See the opcode index for the precise stack behavior of each operation: /documentation/opcodes/
### Type System
- **Dynamic typing** with optional **static type annotations**
- **Value types:** Integer (signed 64-bit), Float (double), Boolean, String, Array, Map, Function, Nil
- **Type annotations:** `number`, `string`, `boolean`, `float`, `nil`, `array`, `map`, `class`
- **Fixed-width integer types:** `byte` / `uint8`, `uint16`, `uint32`, `uint64`, `int8`, `int16`, `int32`, `int64` — with automatic range clamping
- **Type aliases:** `sint8``sint64` as synonyms for `int8``int64`
- `typeof()` runtime type introspection
- `to_string()` and `to_number()` conversion functions
- `cast(value, typeName)` explicit type casting
### Variables & Scope
- Global and local (per-function) variable scoping
- Auto-declaration on first assignment
- Typed variable declarations: `string name = "Fun"`
- Up to 128 globals, 64 locals per frame, 128 call frames
### Operators
- **Arithmetic:** `+`, `-`, `*`, `/`, `%` (addition also concatenates strings)
- **Comparison:** `<`, `<=`, `>`, `>=`, `==`, `!=`
- **Logical:** `&&` (and), `||` (or), `!` (not) — short-circuit evaluation
- **Bitwise (32-bit):** `band()`, `bor()`, `bxor()`, `bnot()`, `shl()`, `shr()`, `rol()`, `ror()`
- **Ternary:** `condition ? true_expr : false_expr`
### Data Structures
#### **Arrays**
- Literal syntax: `[1, 2, 3]`
- Index get/set: `arr[0]`, `arr[0] = value`
- Slice syntax: `arr[start:end]`
- Negative indices for end-relative access
- Built-in operations: `len()`, `push()`, `pop()`, `insert()`, `remove()`, `slice()`, `contains()`, `indexOf()`, `clear()`, `enumerate()`, `zip()`, `join()`, `map()`, `filter()`, `reduce()`
#### **Maps (dictionaries)**
- Literal syntax: `{"key": value, ...}`
- Bracket access/assignment: `map["key"]`, `map["key"] = value`
- Dot property access: `map.key`
- Built-in operations: `has()`, `keys()`, `values()`
### Strings
- Double and single-quoted string literals
- Concatenation with `+`
- Built-in operations: `len()`, `substr()`, `find()`, `split()`, `join()`
### Control Flow
- `if` / `else if` / `else` conditional chains
- `while` loops with `break` and `continue`
- `for var in array` — array iteration
- `for var in range(start, end)` — numeric range iteration (`[start, end)`)
- `for (key, value) in map` — map key-value iteration
- `match` expression (stdlib `lib/utils/match.fun`)
### Functions
- Named function definitions: `fun name(params) body`
- Anonymous function literals: `fn(params) body`
- First-class functions (pass as arguments, store in variables)
- Recursion support
- Return with `return expr` (or implicit nil)
### Object-Oriented Programming
- Class definitions: `class Name(typed params) body`
- Constructor method: `_construct(this, ...)` — auto-invoked on instantiation
- Methods: `fun method(this, ...)` inside class body
- Field access and mutation via `this.field`
- Property access via dot notation: `obj.field`
- Method call sugar: `obj.method(args)` (auto-binds `this`)
- Single inheritance: `class Child(...) extends Parent`
- Method overriding in subclasses
### Error Handling
- `try` / `catch` / `finally` blocks
- Error variable binding: `catch err`
- `throw` opcode for raising exceptions
- Per-frame try-stack for nested exception handlers
### Pattern Matching & Regex (Built-in POSIX)
- `regex_match(str, pattern)` — full match test (returns 1/0)
- `regex_search(str, pattern)` — first match with groups (returns map)
- `regex_replace(str, pattern, replacement)` — global search and replace
### Functional Programming
- First-class and anonymous functions
- `map(array, fn)` — transform each element
- `filter(array, fn)` — keep matching elements
- `reduce(array, init, fn)` — accumulate values
- Higher-order functions (functions that accept or return functions)
- `enumerate()` and `zip()` iteration helpers
### Concurrency
- `thread_spawn(fn, args)` — spawn a thread, returns thread ID
- `thread_join(id)` — join a thread, returns its result
- Cooperative async scheduler (stdlib `lib/async/scheduler.fun`)
---
## Virtual Machine & Runtime
- Compact stackbased bytecode VM implemented in C (C99)
- Deterministic execution model with explicit opcodes for core language features
- Efficient array and map primitives with indexing and mutation opcodes
- Builtin string operations and regex integration (with extension)
- Minimal error model integrated with VM (throw/try handlers)
### Architecture
## Standard Library (Core)
- Compact stack-based bytecode VM written in **C99**
- Tagged union value type supporting 8 runtime types
- ~220 opcodes covering all language features
- Separate operand stack (1024 entries), call frames (128 max), and globals (128)
- Each frame has 64 local slots and a 16-entry try/catch stack
Core functionality available out of the box in the VM and library modules:
### Memory & Performance
- Arrays: create, push/pop, insert/remove, slice, contains, join, enumerate
- Maps: create, has_key, keys, values
- Strings: substr, find, split, replace (with regex), join
- Math and bitwise ops over fixedwidth integers used by the VM
- Deterministic execution model
- Reference-counted arrays and maps
- Function/data sectioning with linker GC for small binaries
- LTO (Link-Time Optimization) support for Release builds
See the opcode list for the canonical reference: /documentation/opcodes/
### Debugging & Tracing
- Built-in debugger with breakpoints (up to 64)
- Step, next, finish, and continue commands
- `--trace` / `-t` flag for opcode-level execution tracing
- Per-opcode execution counters (compile-time `FUN_TRACE`)
- `--repl-on-error` flag: drops into interactive REPL on runtime error with stack preserved
- Stack trace printing on errors
- Source line mapping in error messages (includes include-file resolution)
### I/O & Platform
- `print()` — output with newline
- `echo()` — output without newline (immediate flush)
- `read_file(path)` — read entire file into string
- `write_file(path, data)` — write string to file
- `input_line()` — read a line from stdin (with optional prompt)
- `env(name)` / `env_all()` — get environment variables
- `proc_run(cmd)` — run command, capture stdout+exit code
- `system(cmd)` — run command via shell, returns exit code
- `os_list_dir(path)` — list directory entries
### Date, Time & Random
- `time_now_ms()` — wall clock in milliseconds since Unix epoch
- `clock_mono_ms()` — monotonic clock for interval measurement
- `date_format(ms, fmt)` — format timestamps via strftime
- `sleep(ms)` — suspend execution
- `random_seed(seed)` — seed the PRNG
- `random_int(lo, hi)` — random integer in `[lo, hi)`
- `random_number(len)` — cryptographically random hex string
### Networking (Built-in, Unix)
- `sock_tcp_listen(port, backlog)` — TCP server socket
- `sock_tcp_accept(listen_fd)` — accept client connection
- `sock_tcp_connect(host, port)` — TCP client connection
- `sock_send(fd, data)` / `sock_recv(fd, maxlen)` — send/receive data
- `sock_unix_listen(path, backlog)` / `sock_unix_connect(path)` — Unix domain sockets
- `sock_close(fd)` — close socket
- `fd_set_nonblock(fd, on)` — non-blocking mode
- `fd_poll_read(fd, timeout_ms)` / `fd_poll_write(fd, timeout_ms)` — I/O readiness polling
### Serial Communication (Unix)
- `serial_open(path, baud_rate)` — open serial port
- `serial_config(fd, data_bits, parity, stop_bits, flow_control)` — configure
- `serial_send(fd, data)` / `serial_recv(fd, maxlen)` — send/receive
- `serial_close(fd)` — close
### Integer Utilities
- `sclamp(value, bits)` / `uclamp(value, bits)` — signed/unsigned bit-width clamping
- Integer type declarations (`byte`, `uint8``uint64`, `int8``int64`) with automatic range clamping
- `gcd(a, b)`, `lcm(a, b)` — greatest common divisor, least common multiple
- `isqrt(x)` — integer square root
- `sign(x)` — signum (-1, 0, 1)
---
## Standard Library
The standard library is written primarily in **Fun itself** and lives in `lib/`:
### Strings (`lib/strings.fun`)
- `str_ltrim`, `str_rtrim`, `str_trim` — whitespace trimming
- `str_starts_with`, `str_ends_with` — prefix/suffix checking
- `str_split` — single-character delimiter splitting
- `str_replace_all` — global substring replacement
- `str_to_lower`, `str_to_upper` — ASCII case conversion
- `str_repeat` — string repetition
- `string_to_bytes_ascii` — ASCII string to byte array
### Arrays (`lib/arrays.fun`)
- `array_slice`, `array_reverse`, `array_concat` — slicing and combining
- `array_index_of`, `array_contains` — searching
- `array_unique` — deduplication
- `array_flatten1` — flatten one level of nesting
### Math (`lib/math.fun`)
- `abs`, `clamp`, `gcd`, `lcm`, `powi` (integer exponentiation)
- `min3`, `max3`, `array_min`, `array_max`
### Hex (`lib/hex.fun`)
- `hex_to_dec`, `dec_to_hex`, `hex_to_bytes`, `bytes_to_hex`
### Encoding (`lib/encoding/base64.fun`)
- `b64_encode_bytes`, `b64_decode_to_bytes` — Base64 encoding/decoding
### Cryptography (Pure Fun implementations in `lib/crypt/`)
- **MD5**`MD5` class (`lib/crypt/md5.fun`)
- **SHA-1**`SHA1` class (`lib/crypt/sha1.fun`)
- **SHA-256**`SHA256` class (`lib/crypt/sha256.fun`)
- **SHA-384**`SHA384` class (`lib/crypt/sha384.fun`)
- **SHA-512**`SHA512` class (`lib/crypt/sha512.fun`)
- **CRC-32**`CRC32` class (IEEE 802.3)
- **CRC-32C**`CRC32C` class (Castagnoli)
- **AES-256**`AES256` class (ECB mode)
### Functional Utilities
- **Option type** (`lib/utils/option.fun`): `some()`, `none()`, `is_some`, `is_none`, `unwrap`, `unwrap_or`, `option_map`, `and_then`, `or_else`, `try_get`
- **Result type** (`lib/utils/result.fun`): `ok()`, `err()`, `is_ok`, `is_err`, `unwrap`, `unwrap_or`, `result_map`, `map_err`, `and_then`, `or_else`, `to_option`
- **Pattern matching** (`lib/utils/match.fun`): `match(value, cases)` with `is`, `when`, `else` patterns
### Range Utilities (`lib/utils/range.fun`)
- `range(n)``[0, n)`
- `range2(start, end)``[start, end)`
- `range3(start, end, step)` — stepped range
### Date/Time (`lib/utils/datetime.fun`)
- `DateTime` class with `now_ms`, `mono_ms`, `format`, `iso_now`, `iso_from`, `date_str`, `time_str`, `today_str`, `start_timer`, `elapsed_ms`, `sleep_ms`, `sleep_s`
### CLI (`lib/cli.fun`)
- `argv()` — retrieve command-line arguments
- `parse_args(args)` — parse flags and positional arguments
### Console (`lib/io/console.fun`)
- `Console` class with `prompt`, `ask`, `ask_hidden`, `ask_yes_no`, `term_cols`, `progress` (progress bar)
### Process (`lib/io/process.fun`)
- `Process` class wrapping `proc_run` and `system`
### Thread (`lib/io/thread.fun`)
- `Thread` class wrapping `thread_spawn` / `thread_join`
### Socket Classes (`lib/io/socket.fun`)
- `TcpClient` — TCP client with connect/send/recv/close/recv_all
- `TcpServer` — TCP server with listen/accept/close
- `UnixClient` — Unix domain socket client
### Serial (`lib/io/serial.fun`)
- `Serial` class wrapping serial port operations
### Async Scheduler (`lib/async/scheduler.fun`)
- Cooperative multitasking with `task_spawn`, `co_yield`, `run_once`, `run_until_done`
- I/O readiness polling: `await_read`, `await_write`
### Networking / Web
- **CGI** (`lib/net/cgi.fun`): full CGI request parsing, response generation, URL encoding/decoding
- **HTTP Server** (`lib/net/http_server.fun`): static file serving with `.fun` script execution
- **HTTP CGI Server** (`lib/net/http_cgi_server.fun`): CGI-based HTTP server
- **IRC Client** (`lib/net/irc.fun`): IRC protocol client with message parsing
---
## Optional Extensions (Build-time)
Enabled via CMake flags; each wraps a mature C library:
| Extension | CMake Flag | Library | Features |
|-----------|-----------|---------|----------|
| **[JSON](/documentation/extensions/json/)** | `FUN_WITH_JSON` | [json-c](https://json-c.github.io/json-c/){:class="ext"} | `json_parse()`, `json_stringify()`, `json_from_file()`, `json_to_file()` |
| **[cURL](/documentation/extensions/curl/)** | `FUN_WITH_CURL` | [libcurl](https://curl.se/libcurl/){:class="ext"} | `curl_get()`, `curl_post()`, `curl_download()` |
| **[SQLite](/documentation/extensions/sqlite/)** | `FUN_WITH_SQLITE` | [libsqlite3](https://www.sqlite.org/){:class="ext"} | `sqlite_open()`, `sqlite_close()`, `sqlite_exec()`, `sqlite_query()` |
| **[PCRE2](/documentation/extensions/pcre2/)** | `FUN_WITH_PCRE2` | [libpcre2](https://www.pcre.org/){:class="ext"} | `pcre2_test()`, `pcre2_match()`, `pcre2_find_all()` — with flags (i, m, s, u, x) |
| **[OpenSSL](/documentation/extensions/openssl/)** | `FUN_WITH_OPENSSL` | [libcrypto](https://www.openssl.org/){:class="ext"} | `openssl_md5()`, `openssl_sha256()`, `openssl_sha512()`, `openssl_ripemd160()` |
| **[INI](/documentation/extensions/ini/)** | `FUN_WITH_INI` | [iniparser](https://github.com/ndevilla/iniparser){:class="ext"} | `ini_load()`, `ini_get_string/int/double/bool()`, `ini_set()`, `ini_unset()`, `ini_save()` |
| **[XML](/documentation/extensions/xml2/)** | `FUN_WITH_XML2` | [libxml2](http://xmlsoft.org/){:class="ext"} | `xml_parse()`, `xml_root()`, `xml_name()`, `xml_text()` |
| **[PC/SC](/documentation/extensions/pcsc/)** | `FUN_WITH_PCSC` | [libpcsclite](https://pcsclite.apdu.fr/){:class="ext"} | `pcsc_establish()`, `pcsc_list_readers()`, `pcsc_connect()`, `pcsc_transmit()`, etc. |
| **[KCGI](/documentation/extensions/kcgi/)** | `FUN_WITH_KCGI` | [libkcgi](https://kristaps.bsd.lv/kcgi/){:class="ext"} | `kcgi_parse()`, `kcgi_reply_start()`, `kcgi_write()`, `kcgi_end()` |
| **[Redis/Valkey](/documentation/extensions/redis/)** | `FUN_WITH_REDIS` | [hiredis](https://github.com/redis/hiredis){:class="ext"} | `redis_connect()`, `redis_cmd()`, `redis_close()` |
Some extensions also have a corresponding **stdlib wrapper class** in `lib/io/` or `lib/net/`:
- `JSON` class (`lib/io/json.fun`)
- `INI` class (`lib/io/ini.fun`)
- `XML` class (`lib/io/xml.fun`)
- `PCSC` / `PCSC2` classes (`lib/io/pcsc.fun`)
- `PCRE2` class (`lib/regex/pcre2.fun`)
- `KCGI` class (`lib/net/kcgi.fun`)
---
## FFI / Interop (Experimental)
### Rust FFI (`FUN_WITH_RUST`)
- Cargo-based Rust static library linked into the VM
- Demo opcodes: `rust_hello()`, `rust_hello_args()`, `rust_hello_args_return()`, `rust_get_sp()`, `rust_set_exit()`
- Rust has unsafe access to VM internals via raw pointer and struct offset APIs
### C++ FFI (`FUN_WITH_CPP`)
- C++ static library linked into the VM
- Demo opcode: `cpp_add(a, b)`
---
## Tooling
- Fun interpreter/runtime executable (target: fun)
- REPL and small tools (targets: repl, funstx, examples under examples/)
- Test binaries (target: fun_test, test_opcodes)
- Formatting helper target (target: format) using clangformat
- Makelike aggregate build target (target: build)
- **`fun`** — Interpreter/REPL. Runs `.fun` scripts or starts interactive REPL
- **`funstx`** — Syntax checker with optional `--fix` mode
- **REPL** — Interactive shell with history (1000 lines), multi-line input, tab completion, and commands: `:help`, `:env`, `:load`, `:run`, `:edit`, `:save`, `:clear`, `:exit`, `:quit`, `:debug`, `:import`, `:export`, `:type`, `:trace`, `:reload`
- **Test harnesses**`fun_test` (bytecode-level tests) and `test_opcodes` (opcode exercisers)
- **CTest integration** — crypto example scripts run as automated tests
- **clang-format** target for consistent C source formatting
## Build System & Options
---
Fun uses CMake and exposes toggles that mirror the projects optionality at build time:
## Build System
- FUN_DEBUG — enable extra diagnostics in builds
- FUN_USE_MUSL — build against musl when available
- FUN_WITH_CPP — enable C++ interop/components where applicable
- FUN_WITH_RUST — build and link an optional Rust static library with opcode examples
- FUN_BUILD_DOCS — generate documentation (Doxygen/website pipelines)
- FUN_WITH_* — perextension toggles (see Extensions section)
- **CMake** 3.10+ with C99 standard
- Build toggles: `FUN_DEBUG`, `FUN_USE_MUSL`, `FUN_WITH_REPL`, `FUN_WITH_CPP`, `FUN_WITH_RUST`, `FUN_BUILD_DOCS`
- Per-extension toggles for all optional libraries
- Release builds with LTO, function/data sectioning, and `--gc-sections`
- Doxygen documentation generation
- Install targets for binaries, libraries, examples, man pages
- Uninstall target with safe directory cleanup
Targets available for CI/workflows include Experimental/Continuous/Nightly aggregates, plus unit tests and coverage helpers (see CMake targets).
---
## Portability
- Written in portable C99
- Designed to build on common Linux environments; musl support available
- Written in **C99** with POSIX extensions
- Primary target: **Linux** (glibc and musl)
- Partial **Windows** support
- Platform abstraction for threading, sockets, serial I/O
- Builds with GCC and Clang
## Extensions (Optional, Buildtime)
Fun ships with optional integrations that can be enabled per environment. Highlights include:
- cURL (HTTP client)
- INI (iniparser)
- JSON (jsonc)
- libxml2 (XML)
- SQLite
- PCRE2 (Perlcompatible regex)
- PC/SC (Smart cards)
- OpenSSL (crypto/TLS)
- kcgi (CGI/web helper)
See the full catalog with enable/requirement notes: /documentation/extensions/
## Web/CGI
- Optional kcgi integration for building CGIstyle programs and simple web endpoints (see Extensions)
- Website sources under web/ with Jekyll layouts; generated site in web/_site/
---
## Testing
- Unit and VMlevel opcode tests (targets: fun_test, test_opcodes)
- Example scripts under examples/ exercised by CI and manual runs
- Unit tests for VM opcodes and bytecode execution
- CTest integration runs example scripts as automated tests
- Example scripts in `examples/` (90+ files) cover all language features
- Crypto self-tests for MD5, SHA-1/256/384/512, CRC-32/CRC-32C, AES-256
- Include-line mapping regression test
- KCGI smoke test (when enabled)
---
## Documentation
- Doxygen configuration for API references (Doxyfile)
- Humanreadable docs under web/documentation/ (opcodes, extensions, etc.)
- Jekyll-based website under `web/` with full documentation
- Doxygen API reference
- Language specification documents (`spec/`)
- Over 90 annotated example scripts
- Handbook, type system guide, REPL guide, testing guide
- Opcode reference with stack effect documentation
- Changelog and semantic versioning
---
## Licensing
- Licensed under an OSIapproved license; see LICENSE at the repository root
- **Apache 2.0** — fully open source, freely usable and modifiable

View file

@ -19,11 +19,13 @@ permalink: /
<h2>What is Fun?</h2>
<p>Fun is an experiment, just for fun, but Fun works!</p>
<p>Fun is a small, strict, and simple programming language that runs on a compact stack-based virtual machine. The C core is intentionally minimal; most functionality and standard libraries are implemented in Fun itself.</p>
<p>Fun is a highly strict programming language, but also highly simple. It looks like Python (My favorite language), but there are differences.</p>
<p>Fun is dynamically typed with optional static type annotations, <a href="/features/">featuring first-class functions</a>, classes with inheritance, pattern matching, and a rich standard library. It supports everything from basic scripting to TCP sockets, serial communication, threading, cryptography (MD5, SHA-1/256/384/512, CRC-32, AES-256), and a built-in debugger.</p>
<p>Influenced by <a href="https://www.gnu.org/software/bash/" class="ext">Bash</a>, <a href="https://en.wikipedia.org/wiki/The_C_Programming_Language" class="ext">C</a>, <a href="https://www.lua.org/" class="ext">Lua</a>, PHP, <a href="https://www.python.org/" class="ext">Python</a>, and a little Rust (Most influences came from linked languages).</p>
<p>Influenced by: <a href="https://www.gnu.org/software/bash/" class="ext">Bash</a>, <a href="https://en.wikipedia.org/wiki/The_C_Programming_Language" class="ext">C</a>, <a href="https://www.lua.org/" class="ext">Lua</a>, <a href="https://www.php.net/" class="ext">PHP</a>, <a href="https://www.python.org/" class="ext">Python</a>, and <a href="https://www.rust-lang.org/" class="ext">Rust</a>.</p>
<p>Fun is an experiment - just for fun - but Fun works!</p>
<p>Fun is and will ever be 100% free under the terms of the <a href="https://opensource.org/license/apache-2-0" class="ext">Apache-2.0 License</a>.</p>
@ -61,4 +63,3 @@ permalink: /
{% if count == paginator.page %}<a href="/{{ count }}/" class="active" title="Page {{ count }}">{{ count }}</a>{% else %}<a href="/{{ count }}/" title="Page {{ count }}">{{ count }}</a>{% endif %}
{% endfor %}
</div><br>