diff --git a/.github/workflows/cmake-ubuntu-latest.yml b/.github/workflows/cmake-ubuntu-latest.yml
index fc55f0f..6f62ae4 100644
--- a/.github/workflows/cmake-ubuntu-latest.yml
+++ b/.github/workflows/cmake-ubuntu-latest.yml
@@ -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
diff --git a/.gitignore b/.gitignore
index cf923d8..73e7471 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4b2424..e8def14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1c754fc..dc0523d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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"
)
diff --git a/Doxyfile b/Doxyfile
index 39f0078..95ef2de 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -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
diff --git a/README.md b/README.md
index 3690522..1511d85 100644
--- a/README.md
+++ b/README.md
@@ -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**
No clutter, no 15 ways of writing the same thing. Simplicity means clarity.
- **Hackable by Nature**
- 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 — true to the hacker spirit.
- **Beautiful Defaults**
- A language that doesn’t 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. It’s 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 — 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()` — 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` — iteration
+- `for var in range(start, end)` — numeric range
+- `for (key, value) in map` — 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) ☑
-- [cURL (libcurl)](./web/documentation/extensions/curl/curl.md) (optional) ☑
-- [INI (iniparser)](./web/documentation/extensions/ini/ini.md) (optional) ☑
-- [JSON (json-c)](./web/documentation/extensions/json/json.md) (optional) ☑
-- [PCRE2](./web/documentation/extensions/pcre2/pcre2.md) (optional) ☑
-- [PCSC (smart cards)](./web/documentation/extensions/pcsc/pcsc.md) (optional) ☑
-- [OpenSSL](./web/documentation/extensions/openssl/openssl.md) (optional) ☑
-- [SQLite](./web/documentation/extensions/sqlite/sqlite.md) (optional) ☑
-- [XML (libxml2)](./web/documentation/extensions/xml2/xml2.md) (optional) ☑
+### Functions
-☑ = Done (or basics implemented) / ☐ = Planned or in progress.
+- `fun name(params) body` — named functions
+- `fn(params) body` — 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()` — output
+- `read_file()`, `write_file()` — file I/O
+- `input_line()` — stdin with optional prompt
+- `env()`, `env_all()` — environment variables
+- `proc_run()`, `system()` — process execution
+- `os_list_dir()` — 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` — 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
+
+
$ 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
+
+## Build Options
+
+- **`-DCMAKE_BUILD_TYPE=Debug`** — debug build with asserts
+- **`-DCMAKE_BUILD_TYPE=Release`** — optimized build with LTO and stripping
+- **`-DFUN_BUILD_MUSL=ON`** — static musl build
+- Toggle each extension: `-DFUN_WITH_JSON=ON`, `-DFUN_WITH_CURL=ON`, etc.
+- **`-DFUN_BUILD_DOXYGEN=ON`** — 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 up‑to‑date entry point.
+- Examples: [./examples/features.fun](./examples/features.fun)
## Author
diff --git a/cmake/Extensions/Extensions.cmake b/cmake/Extensions/Extensions.cmake
index 3e21523..5f79041 100644
--- a/cmake/Extensions/Extensions.cmake
+++ b/cmake/Extensions/Extensions.cmake
@@ -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 "--------------------------------")
diff --git a/cmake/Extensions/REDIS.cmake b/cmake/Extensions/REDIS.cmake
new file mode 100644
index 0000000..eb48b9e
--- /dev/null
+++ b/cmake/Extensions/REDIS.cmake
@@ -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()
diff --git a/cmake/Targets.cmake b/cmake/Targets.cmake
index 534d279..b8939d8 100644
--- a/cmake/Targets.cmake
+++ b/cmake/Targets.cmake
@@ -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)
diff --git a/examples/extensions/redis/README.md b/examples/extensions/redis/README.md
new file mode 100644
index 0000000..88bace7
--- /dev/null
+++ b/examples/extensions/redis/README.md
@@ -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.
diff --git a/examples/extensions/redis/basic_ping.fun b/examples/extensions/redis/basic_ping.fun
new file mode 100755
index 0000000..773d8bb
--- /dev/null
+++ b/examples/extensions/redis/basic_ping.fun
@@ -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
+ * 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)
diff --git a/examples/extensions/redis/hash_ops.fun b/examples/extensions/redis/hash_ops.fun
new file mode 100755
index 0000000..e5a8be9
--- /dev/null
+++ b/examples/extensions/redis/hash_ops.fun
@@ -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
+ * 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)
diff --git a/examples/extensions/redis/kv_set_get.fun b/examples/extensions/redis/kv_set_get.fun
new file mode 100755
index 0000000..e68b7a6
--- /dev/null
+++ b/examples/extensions/redis/kv_set_get.fun
@@ -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
+ * 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)
diff --git a/examples/extensions/redis/list_ops.fun b/examples/extensions/redis/list_ops.fun
new file mode 100755
index 0000000..ba8d8ed
--- /dev/null
+++ b/examples/extensions/redis/list_ops.fun
@@ -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
+ * 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)
diff --git a/examples/extensions/redis/redis_test.fun b/examples/extensions/redis/redis_test.fun
new file mode 100755
index 0000000..b1b8989
--- /dev/null
+++ b/examples/extensions/redis/redis_test.fun
@@ -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
+ * 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)
diff --git a/examples/features.fun b/examples/features.fun
index 483219e..9ebce9d 100755
--- a/examples/features.fun
+++ b/examples/features.fun
@@ -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 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("")
// ============================================
diff --git a/make b/make
index c26a06a..8dcffd3 100755
--- a/make
+++ b/make
@@ -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
diff --git a/playground/.gitkeep b/playground/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/check_op_includes.py b/scripts/check_op_includes.py
index fbc58e9..be9b6e6 100755
--- a/scripts/check_op_includes.py
+++ b/scripts/check_op_includes.py
@@ -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()}"
diff --git a/src/bytecode.h b/src/bytecode.h
index 42c4aea..88765a7 100644
--- a/src/bytecode.h
+++ b/src/bytecode.h
@@ -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