Compare commits
18 commits
751dd61da9
...
391a7689e5
| Author | SHA1 | Date | |
|---|---|---|---|
| 391a7689e5 | |||
| 1b90493a7a | |||
| 752f0efdd1 | |||
| 8bb99d3154 | |||
| f37b8538b2 | |||
| 42e702f9e8 | |||
| 9b8825bae5 | |||
| b058892cbb | |||
| 774517edaf | |||
| 509b032a53 | |||
| aeab05c9c0 | |||
| cbb81c0b93 | |||
| 06072576fc | |||
| 1eb3e39a9c | |||
| 93f5f1f548 | |||
| 2cd6628a71 | |||
| c61329124b | |||
| 6a39862787 |
74 changed files with 1978 additions and 327 deletions
2
.github/workflows/cmake-ubuntu-latest.yml
vendored
2
.github/workflows/cmake-ubuntu-latest.yml
vendored
|
|
@ -6,6 +6,7 @@ on:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_TYPE: Release
|
BUILD_TYPE: Release
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
@ -13,7 +14,6 @@ jobs:
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ${{github.workspace}}
|
working-directory: ${{github.workspace}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -7,9 +7,9 @@ Doxygen.in
|
||||||
!.gitignore
|
!.gitignore
|
||||||
!.github
|
!.github
|
||||||
!.gitkeep
|
!.gitkeep
|
||||||
!api/.gitkeep
|
|
||||||
.venv
|
.venv
|
||||||
api/
|
api/
|
||||||
|
!api/.gitkeep
|
||||||
build/*
|
build/*
|
||||||
build_debug/*
|
build_debug/*
|
||||||
build_release/*
|
build_release/*
|
||||||
|
|
@ -23,6 +23,8 @@ downloaded.png
|
||||||
json.xml
|
json.xml
|
||||||
lib/*.so
|
lib/*.so
|
||||||
out/
|
out/
|
||||||
|
playground/*
|
||||||
|
!playground/.gitkeep
|
||||||
src/*.o
|
src/*.o
|
||||||
src/rust/Cargo.lock
|
src/rust/Cargo.lock
|
||||||
src/rust/target
|
src/rust/target
|
||||||
|
|
|
||||||
24
CHANGELOG.md
24
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/),
|
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).
|
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
|
## [0.41.15] - 2026-05-26
|
||||||
### Changed
|
### Changed
|
||||||
- More internal optimizations.
|
- More internal optimizations.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.10)
|
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 99)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
|
||||||
2
Doxyfile
2
Doxyfile
|
|
@ -48,7 +48,7 @@ PROJECT_NAME = "Fun API Documentation"
|
||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# 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
|
# 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
|
# for a project that appears at the top of each page and should give viewers a
|
||||||
|
|
|
||||||
207
README.md
207
README.md
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
## What is Fun?
|
## 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).
|
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
|
## Characteristics
|
||||||
|
|
||||||
- Dynamic and optionally statically typed
|
- **Dynamic typing** with optional **static type annotations** (`number`, `string`, `boolean`, `float`, `byte`, `uint8`–`uint64`, `int8`–`int64`)
|
||||||
- Type safety
|
- **Stack-based bytecode VM** written in C99 with ~220 opcodes
|
||||||
- Written in C (C99) and Fun
|
- **First-class functions**, anonymous functions (`fn`), and higher-order operations (`map`, `filter`, `reduce`)
|
||||||
- Internal libs are written with no_camel_case even when written in Fun, except class names
|
- **Classes** with constructors, methods, and single inheritance
|
||||||
- Only a minimal function set is written in C, and most other core functions and libraries are implemented in Fun
|
- **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
|
## The Fun Manifesto
|
||||||
|
|
||||||
|
|
@ -45,13 +53,13 @@ Coding should be enjoyable, elegant, and consistent.
|
||||||
- **One Way to Do It**<br>
|
- **One Way to Do It**<br>
|
||||||
No clutter, no 15 ways of writing the same thing. Simplicity means clarity.
|
No clutter, no 15 ways of writing the same thing. Simplicity means clarity.
|
||||||
- **Hackable by Nature**<br>
|
- **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 — true to the hacker spirit.
|
||||||
- **Beautiful Defaults**<br>
|
- **Beautiful Defaults**<br>
|
||||||
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
|
## 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
|
- Respectful
|
||||||
- Curious
|
- Curious
|
||||||
|
|
@ -70,63 +78,162 @@ A language that feels like home for developers who:
|
||||||
- Believe consistency is freedom
|
- Believe consistency is freedom
|
||||||
- Want to write code that looks good and feels good
|
- 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
|
- Indentation-based blocks (2-space), line and block comments
|
||||||
- if/else if/else
|
- Static type annotations with automatic runtime range clamping for fixed-width integers
|
||||||
- try/catch/finally
|
- `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) ☑
|
### Functions
|
||||||
- [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) ☑
|
|
||||||
|
|
||||||
☑ = 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
|
||||||
|
|
||||||
|
<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`** — 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
|
## 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)
|
- Handbook: [./web/documentation/handbook/handbook.md](./web/documentation/handbook/handbook.md)
|
||||||
- Types overview: [./web/documentation/types/types.md](./web/documentation/types/types.md)
|
- Types: [./web/documentation/types/types.md](./web/documentation/types/types.md)
|
||||||
- REPL guide: [./web/documentation/repl/repl.md](./web/documentation/repl/repl.md)
|
- REPL: [./web/documentation/repl/repl.md](./web/documentation/repl/repl.md)
|
||||||
- Testing: [./web/documentation/testing/testing.md](./web/documentation/testing/testing.md)
|
- Testing: [./web/documentation/testing/testing.md](./web/documentation/testing/testing.md)
|
||||||
- Troubleshooting: [./web/documentation/troubleshooting/troubleshooting.md](./web/documentation/troubleshooting/troubleshooting.md)
|
- Spec: [./web/documentation/spec/v0.4.md](./web/documentation/spec/v0.4.md)
|
||||||
|
|
||||||
Additional references:
|
|
||||||
|
|
||||||
- Specification: [./web/documentation/spec/v0.4.md](./web/documentation/spec/v0.4.md) (work in progress)
|
|
||||||
- Changelog: [CHANGELOG.md](./CHANGELOG.md)
|
- Changelog: [CHANGELOG.md](./CHANGELOG.md)
|
||||||
- Examples demonstrating most features: [./examples/](./examples/)
|
- Examples: [./examples/features.fun](./examples/features.fun)
|
||||||
- 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.
|
|
||||||
|
|
||||||
## Author
|
## Author
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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/REPL.cmake)
|
||||||
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/SQLITE.cmake)
|
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/SQLITE.cmake)
|
||||||
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/XML2.cmake)
|
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/XML2.cmake)
|
||||||
|
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/REDIS.cmake)
|
||||||
|
|
||||||
# Summary of extension toggles
|
# Summary of extension toggles
|
||||||
message(STATUS "---- Fun extension summary ----")
|
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("PCSC-Lite (FUN_WITH_PCSC)" FUN_WITH_PCSC)
|
||||||
_fun_print_feature("REPL (FUN_WITH_REPL)" FUN_WITH_REPL)
|
_fun_print_feature("REPL (FUN_WITH_REPL)" FUN_WITH_REPL)
|
||||||
_fun_print_feature("SQLite (FUN_WITH_SQLITE)" FUN_WITH_SQLITE)
|
_fun_print_feature("SQLite (FUN_WITH_SQLITE)" FUN_WITH_SQLITE)
|
||||||
|
_fun_print_feature("Redis (FUN_WITH_REDIS)" FUN_WITH_REDIS)
|
||||||
message(STATUS "--------------------------------")
|
message(STATUS "--------------------------------")
|
||||||
|
|
|
||||||
22
cmake/Extensions/REDIS.cmake
Normal file
22
cmake/Extensions/REDIS.cmake
Normal 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()
|
||||||
|
|
@ -48,7 +48,8 @@ foreach(var_pair
|
||||||
LIBSQL
|
LIBSQL
|
||||||
LIBXML2
|
LIBXML2
|
||||||
OPENSSL
|
OPENSSL
|
||||||
KCGI)
|
KCGI
|
||||||
|
HIREDIS)
|
||||||
if(${var_pair}_INCLUDE_DIRS)
|
if(${var_pair}_INCLUDE_DIRS)
|
||||||
target_include_directories(fun_core PRIVATE ${${var_pair}_INCLUDE_DIRS})
|
target_include_directories(fun_core PRIVATE ${${var_pair}_INCLUDE_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -94,6 +95,9 @@ if(FUN_WITH_SQLITE)
|
||||||
target_compile_definitions(fun_core PUBLIC FUN_WITH_SQLITE=1)
|
target_compile_definitions(fun_core PUBLIC FUN_WITH_SQLITE=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(FUN_WITH_REDIS)
|
||||||
|
target_compile_definitions(fun_core PUBLIC FUN_WITH_REDIS=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(FUN_WITH_OPENSSL)
|
if(FUN_WITH_OPENSSL)
|
||||||
target_compile_definitions(fun_core PUBLIC FUN_WITH_OPENSSL=1)
|
target_compile_definitions(fun_core PUBLIC FUN_WITH_OPENSSL=1)
|
||||||
|
|
|
||||||
29
examples/extensions/redis/README.md
Normal file
29
examples/extensions/redis/README.md
Normal 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.
|
||||||
26
examples/extensions/redis/basic_ping.fun
Executable file
26
examples/extensions/redis/basic_ping.fun
Executable 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)
|
||||||
38
examples/extensions/redis/hash_ops.fun
Executable file
38
examples/extensions/redis/hash_ops.fun
Executable file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://fun-lang.xyz/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the Apache-2.0 license.
|
||||||
|
* https://opensource.org/license/apache-2-0
|
||||||
|
*
|
||||||
|
* Added: 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)
|
||||||
37
examples/extensions/redis/kv_set_get.fun
Executable file
37
examples/extensions/redis/kv_set_get.fun
Executable 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)
|
||||||
36
examples/extensions/redis/list_ops.fun
Executable file
36
examples/extensions/redis/list_ops.fun
Executable 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)
|
||||||
28
examples/extensions/redis/redis_test.fun
Executable file
28
examples/extensions/redis/redis_test.fun
Executable file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://fun-lang.xyz/
|
||||||
|
*
|
||||||
|
* Copyright 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)
|
||||||
|
|
@ -10,48 +10,120 @@ print("")
|
||||||
// ============================================
|
// ============================================
|
||||||
print("1. Strong Type System:")
|
print("1. Strong Type System:")
|
||||||
string name = "Fun Language"
|
string name = "Fun Language"
|
||||||
float version = 0.3
|
float version = 0.41
|
||||||
boolean is_awesome = true
|
boolean is_awesome = true
|
||||||
|
number meaning = 42
|
||||||
items = [1, 2, 3, 4, 5]
|
items = [1, 2, 3, 4, 5]
|
||||||
config = {"debug": true, "port": 8080}
|
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(" Awesome: " + to_string(is_awesome))
|
||||||
|
print(" Nil: " + to_string(nil_val))
|
||||||
print("")
|
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]
|
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
print(" Original: " + to_string(numbers))
|
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
|
// Slice syntax arr[start:end]
|
||||||
joined = join([10, 20, 30], ", ")
|
sliced = numbers[2:7]
|
||||||
print(" Joined: " + joined)
|
print(" Slice [2:7]: " + to_string(sliced))
|
||||||
|
|
||||||
// Iterate arrays
|
// Mutating array ops
|
||||||
print(" Iteration:")
|
arr = [10, 20, 30]
|
||||||
for item in ["apple", "banana", "cherry"]
|
push(arr, 40)
|
||||||
print(" " + item)
|
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("")
|
print("")
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 3. Maps (Dictionaries)
|
// 6. Maps (Dictionaries)
|
||||||
// ============================================
|
// ============================================
|
||||||
print("3. Map Operations:")
|
print("6. Map Operations:")
|
||||||
person = {"name": "Alice", "age": 30, "role": "Developer"}
|
person = {"name": "Alice", "age": 30, "role": "Developer"}
|
||||||
|
|
||||||
print(" Person: " + to_string(person))
|
print(" Person: " + to_string(person))
|
||||||
print(" Has 'age' key: " + to_string(has(person, "age")))
|
print(" Has 'age' key: " + to_string(has(person, "age")))
|
||||||
print(" Keys: " + to_string(keys(person)))
|
print(" Keys: " + to_string(keys(person)))
|
||||||
print(" Values: " + to_string(values(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("")
|
print("")
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 4. Object-Oriented Programming
|
// 7. Object-Oriented Programming
|
||||||
// ============================================
|
// ============================================
|
||||||
print("4. Classes & Objects:")
|
print("7. Classes & Objects:")
|
||||||
|
|
||||||
class Counter(number initial, string label)
|
class Counter(number initial, string label)
|
||||||
count = 0
|
count = 0
|
||||||
|
|
@ -78,9 +150,9 @@ counter.display()
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 5. Inheritance
|
// 8. Inheritance
|
||||||
// ============================================
|
// ============================================
|
||||||
print("5. Inheritance:")
|
print("8. Inheritance:")
|
||||||
|
|
||||||
class Animal(string type)
|
class Animal(string type)
|
||||||
species = ""
|
species = ""
|
||||||
|
|
@ -107,9 +179,9 @@ print(" Species: " + dog.species)
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 6. Error Handling
|
// 9. Error Handling & Exceptions
|
||||||
// ============================================
|
// ============================================
|
||||||
print("6. Exception Handling:")
|
print("9. Exception Handling:")
|
||||||
|
|
||||||
try
|
try
|
||||||
print(" Attempting risky operation...")
|
print(" Attempting risky operation...")
|
||||||
|
|
@ -122,51 +194,63 @@ finally
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 7. String Manipulation
|
// 10. Comparison & Logical Operators
|
||||||
// ============================================
|
// ============================================
|
||||||
print("7. String Features:")
|
print("10. Comparison & Logical Operators:")
|
||||||
string text = "Hello, Fun Language!"
|
number a = 10
|
||||||
print(" Original: " + text)
|
number b = 20
|
||||||
// Note: Using stdlib functions (assumed to exist in utils modules)
|
print(" a=10, b=20")
|
||||||
// len, substr, find, split would come from stdlib
|
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("")
|
print("")
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 8. Mathematical Operations
|
// 11. Control Flow: if/else if/else
|
||||||
// ============================================
|
// ============================================
|
||||||
print("8. Math Functions:")
|
print("11. If/Else If/Else:")
|
||||||
float x = 16.7
|
number score = 85
|
||||||
print(" x = " + to_string(x))
|
|
||||||
// Note: Math functions like sqrt, floor, ceil, abs, gcd, lcm
|
if score >= 90
|
||||||
// would come from stdlib <utils/math.fun> or similar
|
print(" Grade: A")
|
||||||
|
else if score >= 80
|
||||||
|
print(" Grade: B")
|
||||||
|
else if score >= 70
|
||||||
|
print(" Grade: C")
|
||||||
|
else
|
||||||
|
print(" Grade: F")
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 9. Bitwise Operations
|
// 12. Control Flow: Loops
|
||||||
// ============================================
|
// ============================================
|
||||||
print("9. Bitwise Operations:")
|
print("12. Loop Variants:")
|
||||||
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("")
|
|
||||||
|
|
||||||
// ============================================
|
// For-each with array
|
||||||
// 10. Control Flow
|
print(" For-each array:")
|
||||||
// ============================================
|
for item in ["apple", "banana", "cherry"]
|
||||||
print("10. Control Flow:")
|
print(" " + item)
|
||||||
|
|
||||||
// For loop with array
|
// For-range loop
|
||||||
print(" Countdown:")
|
print(" For-range 0..4:")
|
||||||
for i in [5, 4, 3, 2, 1]
|
for i in range(0, 5)
|
||||||
print(" " + to_string(i) + "...")
|
print(" " + to_string(i))
|
||||||
print(" Liftoff!")
|
|
||||||
|
// 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
|
// While with break/continue
|
||||||
print(" Skip evens:")
|
print(" While with break/continue:")
|
||||||
number n = 0
|
number n = 0
|
||||||
while n < 10
|
while n < 10
|
||||||
n = n + 1
|
n = n + 1
|
||||||
|
|
@ -178,24 +262,55 @@ while n < 10
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 11. Type Introspection
|
// 13. Mathematical Operations
|
||||||
// ============================================
|
// ============================================
|
||||||
print("11. Type Introspection:")
|
print("13. Math Functions:")
|
||||||
number check_int = 42
|
number mx = -16
|
||||||
string check_str = "hello"
|
print(" abs(-16) = " + to_string(abs(mx)))
|
||||||
check_arr = [1, 2, 3]
|
print(" min(10, 20) = " + to_string(min(10, 20)))
|
||||||
check_map = {"key": "value"}
|
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))
|
// Random (deterministic for demo)
|
||||||
print(" typeof(\"hello\") = " + typeof(check_str))
|
random_seed(42)
|
||||||
print(" typeof([1,2,3]) = " + typeof(check_arr))
|
print(" random_int(1, 100) = " + to_string(random_int(1, 100)))
|
||||||
print(" typeof(map) = " + typeof(check_map))
|
print(" random_number(8) = " + random_number(8))
|
||||||
print("")
|
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)
|
fun double(n)
|
||||||
return n * 2
|
return n * 2
|
||||||
|
|
@ -208,28 +323,112 @@ print(" apply_twice(5, double) = " + to_string(result))
|
||||||
print("")
|
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]
|
nums = [1, 2, 3, 4, 5]
|
||||||
|
|
||||||
fun square(x)
|
fun square(x)
|
||||||
return x * x
|
return x * x
|
||||||
|
|
||||||
squared = map(nums, square)
|
squared = map(nums, square)
|
||||||
print(" Squared: " + to_string(squared))
|
print(" map(nums, square): " + to_string(squared))
|
||||||
|
|
||||||
fun is_even(x)
|
fun is_even(x)
|
||||||
return x % 2 == 0
|
return x % 2 == 0
|
||||||
|
|
||||||
evens = filter(nums, is_even)
|
evens = filter(nums, is_even)
|
||||||
print(" Evens: " + to_string(evens))
|
print(" filter(nums, is_even): " + to_string(evens))
|
||||||
|
|
||||||
fun sum(acc, x)
|
fun sum(acc, x)
|
||||||
return acc + x
|
return acc + x
|
||||||
|
|
||||||
total = reduce(nums, 0, sum)
|
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("")
|
print("")
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|
|
||||||
2
make
2
make
|
|
@ -31,6 +31,7 @@ if [ "$target" = "all" ]; then
|
||||||
-DFUN_WITH_CPP=ON \
|
-DFUN_WITH_CPP=ON \
|
||||||
-DFUN_WITH_KCGI=ON \
|
-DFUN_WITH_KCGI=ON \
|
||||||
-DFUN_WITH_OPENSSL=ON \
|
-DFUN_WITH_OPENSSL=ON \
|
||||||
|
-DFUN_WITH_REDIS=ON \
|
||||||
&& cmake --build build --target fun
|
&& cmake --build build --target fun
|
||||||
elif [ "$target" = "all_debug" ]; then
|
elif [ "$target" = "all_debug" ]; then
|
||||||
rm -rf build \
|
rm -rf build \
|
||||||
|
|
@ -45,6 +46,7 @@ elif [ "$target" = "all_debug" ]; then
|
||||||
-DFUN_WITH_INI=ON \
|
-DFUN_WITH_INI=ON \
|
||||||
-DFUN_WITH_CPP=ON \
|
-DFUN_WITH_CPP=ON \
|
||||||
-DFUN_WITH_OPENSSL=ON \
|
-DFUN_WITH_OPENSSL=ON \
|
||||||
|
-DFUN_WITH_REDIS=ON \
|
||||||
-DFUN_DEBUG=ON \
|
-DFUN_DEBUG=ON \
|
||||||
&& cmake --build build --target fun
|
&& cmake --build build --target fun
|
||||||
elif [ "$target" = "alpine" ]; then
|
elif [ "$target" = "alpine" ]; then
|
||||||
|
|
|
||||||
0
playground/.gitkeep
Normal file
0
playground/.gitkeep
Normal file
|
|
@ -134,6 +134,9 @@ def parse_includes_from_vm(text: str) -> set[str]:
|
||||||
if d == "sqlite":
|
if d == "sqlite":
|
||||||
if n in {"open", "close", "exec", "query"}:
|
if n in {"open", "close", "exec", "query"}:
|
||||||
return f"SQLITE_{n.upper()}"
|
return f"SQLITE_{n.upper()}"
|
||||||
|
if d == "redis":
|
||||||
|
if n in {"connect", "cmd", "close"}:
|
||||||
|
return f"REDIS_{n.upper()}"
|
||||||
if d == "pcsc":
|
if d == "pcsc":
|
||||||
if n in {"establish", "release", "list_readers", "connect", "disconnect", "transmit"}:
|
if n in {"establish", "release", "list_readers", "connect", "disconnect", "transmit"}:
|
||||||
return f"PCSC_{n.upper()}"
|
return f"PCSC_{n.upper()}"
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,11 @@ typedef enum {
|
||||||
OP_SQLITE_EXEC, // pops sql, handle; pushes sqlite rc (0=OK)
|
OP_SQLITE_EXEC, // pops sql, handle; pushes sqlite rc (0=OK)
|
||||||
OP_SQLITE_QUERY, // pops sql, handle; pushes array<map>
|
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
|
// PCSC (smart card) opcodes
|
||||||
OP_PCSC_ESTABLISH, // returns context id (>0) or 0
|
OP_PCSC_ESTABLISH, // returns context id (>0) or 0
|
||||||
OP_PCSC_RELEASE, // pops ctx id; returns 1/0
|
OP_PCSC_RELEASE, // pops ctx id; returns 1/0
|
||||||
|
|
|
||||||
221
src/extensions/redis.c
Normal file
221
src/extensions/redis.c
Normal 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 */
|
||||||
21
src/fun.c
21
src/fun.c
|
|
@ -142,11 +142,26 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
char *joined = (char *)malloc(total);
|
char *joined = (char *)malloc(total);
|
||||||
if (joined) {
|
if (joined) {
|
||||||
joined[0] = '\0';
|
size_t off = 0;
|
||||||
for (int i = 0; i < sargc; ++i) {
|
for (int i = 0; i < sargc; ++i) {
|
||||||
strcat(joined, argv[sargi + i]);
|
int written = snprintf(joined + off, (off < total ? total - off : 0),
|
||||||
if (i + 1 < sargc) strcat(joined, " ");
|
"%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);
|
setenv("FUN_ARGS", joined, 1);
|
||||||
free(joined);
|
free(joined);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
69
src/parser.c
69
src/parser.c
|
|
@ -2070,6 +2070,75 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
free(name);
|
free(name);
|
||||||
return 1;
|
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) {
|
if (strcmp(name, "sqlite_close") == 0) {
|
||||||
(*pos)++; /* '(' */
|
(*pos)++; /* '(' */
|
||||||
if (!emit_expression(bc, src, len, pos)) {
|
if (!emit_expression(bc, src, len, pos)) {
|
||||||
|
|
|
||||||
|
|
@ -259,14 +259,16 @@ static int complete_load_path(char *buf, size_t *len_io) {
|
||||||
if (slash) {
|
if (slash) {
|
||||||
size_t dlen = (size_t)(slash - expanded);
|
size_t dlen = (size_t)(slash - expanded);
|
||||||
if (dlen == 0) {
|
if (dlen == 0) {
|
||||||
strcpy(dirpart, "/");
|
/* use bounded copy to avoid potential overflow (even though "/" fits) */
|
||||||
|
snprintf(dirpart, sizeof(dirpart), "%s", "/");
|
||||||
} else {
|
} else {
|
||||||
memcpy(dirpart, expanded, dlen);
|
memcpy(dirpart, expanded, dlen);
|
||||||
dirpart[dlen] = '\0';
|
dirpart[dlen] = '\0';
|
||||||
}
|
}
|
||||||
snprintf(base, sizeof(base), "%s", slash + 1);
|
snprintf(base, sizeof(base), "%s", slash + 1);
|
||||||
} else {
|
} else {
|
||||||
strcpy(dirpart, ".");
|
/* use bounded copy to avoid potential overflow (even though "." fits) */
|
||||||
|
snprintf(dirpart, sizeof(dirpart), "%s", ".");
|
||||||
snprintf(base, sizeof(base), "%s", expanded);
|
snprintf(base, sizeof(base), "%s", expanded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
8
src/vm.c
8
src/vm.c
|
|
@ -73,6 +73,7 @@
|
||||||
#include "extensions/sqlite.c"
|
#include "extensions/sqlite.c"
|
||||||
#include "extensions/xml2.c"
|
#include "extensions/xml2.c"
|
||||||
#include "extensions/kcgi.c"
|
#include "extensions/kcgi.c"
|
||||||
|
#include "extensions/redis.c"
|
||||||
|
|
||||||
/* forward declarations for include mapping used in error reporting */
|
/* forward declarations for include mapping used in error reporting */
|
||||||
extern char *preprocess_includes(const char *src);
|
extern char *preprocess_includes(const char *src);
|
||||||
|
|
@ -1230,6 +1231,13 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
#include "vm/sqlite/query.c"
|
#include "vm/sqlite/query.c"
|
||||||
#endif
|
#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) */
|
/* C++ demo opcodes (guarded) */
|
||||||
#ifdef FUN_WITH_CPP
|
#ifdef FUN_WITH_CPP
|
||||||
case OP_CPP_ADD: {
|
case OP_CPP_ADD: {
|
||||||
|
|
|
||||||
2
src/vm.h
2
src/vm.h
|
|
@ -66,6 +66,8 @@ static const char *opcode_names[] = {
|
||||||
"JSON_PARSE", "JSON_STRINGIFY", "JSON_FROM_FILE", "JSON_TO_FILE",
|
"JSON_PARSE", "JSON_STRINGIFY", "JSON_FROM_FILE", "JSON_TO_FILE",
|
||||||
"CURL_GET", "CURL_POST", "CURL_DOWNLOAD",
|
"CURL_GET", "CURL_POST", "CURL_DOWNLOAD",
|
||||||
"SQLITE_OPEN", "SQLITE_CLOSE", "SQLITE_EXEC", "SQLITE_QUERY",
|
"SQLITE_OPEN", "SQLITE_CLOSE", "SQLITE_EXEC", "SQLITE_QUERY",
|
||||||
|
/* Redis (hiredis) */
|
||||||
|
"REDIS_CONNECT", "REDIS_CMD", "REDIS_CLOSE",
|
||||||
"LIBSQL_OPEN", "LIBSQL_CLOSE", "LIBSQL_EXEC", "LIBSQL_QUERY",
|
"LIBSQL_OPEN", "LIBSQL_CLOSE", "LIBSQL_EXEC", "LIBSQL_QUERY",
|
||||||
"PCSC_ESTABLISH", "PCSC_RELEASE", "PCSC_LIST_READERS", "PCSC_CONNECT", "PCSC_DISCONNECT", "PCSC_TRANSMIT",
|
"PCSC_ESTABLISH", "PCSC_RELEASE", "PCSC_LIST_READERS", "PCSC_CONNECT", "PCSC_DISCONNECT", "PCSC_TRANSMIT",
|
||||||
"PCRE2_TEST", "PCRE2_MATCH", "PCRE2_FINDALL",
|
"PCRE2_TEST", "PCRE2_MATCH", "PCRE2_FINDALL",
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,24 @@ case OP_PROC_SYSTEM: {
|
||||||
push_value(vm, make_int(-1));
|
push_value(vm, make_int(-1));
|
||||||
break;
|
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 status = system(cmd);
|
||||||
int code = -1;
|
int code = -1;
|
||||||
#ifdef __unix__
|
#ifdef __unix__
|
||||||
|
|
|
||||||
45
src/vm/redis/close.c
Normal file
45
src/vm/redis/close.c
Normal 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
58
src/vm/redis/cmd.c
Normal 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
65
src/vm/redis/connect.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -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>-->
|
<!--<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>
|
<h2 style="display:none;">Navigation</h2>
|
||||||
<ul style="list-style:none;margin-left:0px;text-align:center;">
|
<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> -</li>
|
<li style="display:inline;"><a href="/" style="text-decoration:none;" title="Home" class="home">Home</a> -</li>
|
||||||
<!--<li style="display:inline;"> <a href="/about/" style="text-decoration:none;" title="About" class="about">About</a> -</li>-->
|
<!--<li style="display:inline;"> <a href="/about/" style="text-decoration:none;" title="About" class="about">About</a> -</li>-->
|
||||||
<li style="display:inline;"> <a href="/contact/" style="text-decoration:none;" title="Contact" class="mail">Contact</a> -</li>
|
<li style="display:inline;"> <a href="/contact/" style="text-decoration:none;" title="Contact" class="mail">Contact</a> -</li>
|
||||||
<li style="display:inline;"> <a href="/community/code-of-conduct/" style="text-decoration:none;" title="Code of Conduct" class="love">Code of Conduct</a> -</li>
|
<li style="display:inline;"> <a href="/community/code-of-conduct/" style="text-decoration:none;" title="Code of Conduct" class="love">Code of Conduct</a> -</li>
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,14 @@
|
||||||
<div class="trigger">
|
<div class="trigger">
|
||||||
<h2 style="display:none;">Navigation</h2>
|
<h2 style="display:none;">Navigation</h2>
|
||||||
<ul style="list-style: none;">
|
<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="/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="/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="/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="/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://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
|
https://dev.to/warish/how-to-create-a-dark-mode-toggle-with-html-css-and-javascript-378m
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ layout: default
|
||||||
|
|
||||||
<div class="post">
|
<div class="post">
|
||||||
<header class="post-header">
|
<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 %}
|
{% 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>
|
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 %}
|
{% if page.subtitle %}<p style="padding: 0px 0px 0px 0px;">{{ page.subtitle }}</p>{% endif %}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ layout: default
|
||||||
|
|
||||||
<div class="post">
|
<div class="post">
|
||||||
<header class="post-header">
|
<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>
|
<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>
|
</header>
|
||||||
<article class="post-content">
|
<article class="post-content">
|
||||||
|
|
|
||||||
58
web/_posts/2026-06-09-announcing-fun-0.42.1.md
Normal file
58
web/_posts/2026-06-09-announcing-fun-0.42.1.md
Normal 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.
|
||||||
|
|
||||||
|
### What’s 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 you’re 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 project’s [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
|
||||||
|
|
@ -5,7 +5,7 @@ subtitle: More information about having fun... :)
|
||||||
description: About
|
description: About
|
||||||
_date: 2023-09-20
|
_date: 2023-09-20
|
||||||
metasub: news
|
metasub: news
|
||||||
noToc: true
|
noToc: false
|
||||||
noDate: false
|
noDate: false
|
||||||
tags:
|
tags:
|
||||||
- fun
|
- fun
|
||||||
|
|
@ -19,14 +19,18 @@ permalink: /about/
|
||||||
|
|
||||||
## What is Fun?
|
## 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"}.
|
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
|
## Idea
|
||||||
|
|
||||||
- Simplicity
|
- Simplicity
|
||||||
|
|
@ -38,11 +42,19 @@ Fun is and will ever be 100% free under the terms of the [Apache-2.0 License](ht
|
||||||
|
|
||||||
## Characteristics
|
## Characteristics
|
||||||
|
|
||||||
- Dynamic and optionally statically typed
|
- **Dynamic typing** with optional **static type annotations** (`number`, `string`, `boolean`, `float`, `byte`, `uint8`–`uint64`, `int8`–`int64`)
|
||||||
- Type safety
|
- **Stack-based bytecode VM** written in C99 with ~220 opcodes
|
||||||
- Written in C (C99) and Fun
|
- **First-class functions**, anonymous functions (`fn`), and higher-order operations (`map`, `filter`, `reduce`)
|
||||||
- Internal libs are written with no_camel_case even when written in Fun, except class names
|
- **Classes** with constructors, methods, and single inheritance
|
||||||
- Only a minimal function set is written in C, and most other core functions and libraries are implemented in Fun
|
- **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
|
## The Fun Manifesto
|
||||||
|
|
||||||
|
|
@ -60,13 +72,13 @@ Coding should be enjoyable, elegant, and consistent.
|
||||||
- **One Way to Do It**<br>
|
- **One Way to Do It**<br>
|
||||||
No clutter, no 15 ways of writing the same thing. Simplicity means clarity.
|
No clutter, no 15 ways of writing the same thing. Simplicity means clarity.
|
||||||
- **Hackable by Nature**<br>
|
- **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 — true to the hacker spirit.
|
||||||
- **Beautiful Defaults**<br>
|
- **Beautiful Defaults**<br>
|
||||||
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
|
## 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
|
- Respectful
|
||||||
- Curious
|
- Curious
|
||||||
|
|
@ -84,41 +96,153 @@ A language that feels like home for developers who:
|
||||||
- Believe consistency is freedom
|
- Believe consistency is freedom
|
||||||
- Want to write code that looks good and feels good
|
- 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
|
||||||
|
- 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
|
||||||
|
- Source file includes: `#include "path"` and `#include <path>`
|
||||||
|
|
||||||
- functions/classes/objects
|
### Operators
|
||||||
- if/else if/else
|
- Arithmetic: `+`, `-`, `*`, `/`, `%`
|
||||||
- try/catch/finally
|
- 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` — iteration
|
||||||
|
- `for var in range(start, end)` — numeric range
|
||||||
|
- `for (key, value) in map` — 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` — named functions
|
||||||
|
- `fn(params) body` — 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` — class definition
|
||||||
|
- `_construct(this, ...)` — auto-invoked constructor
|
||||||
|
- `this` keyword for instance access
|
||||||
|
- `obj.method(args)` — method call sugar (auto-binds this)
|
||||||
|
- `obj.field` — dot property access
|
||||||
|
- `class Child(...) extends Parent` — 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;">☐</span>
|
### Mathematics
|
||||||
- [cURL](https://curl.se/){:class="ext"} support builtin using [libcurl](https://curl.se/libcurl/){:class="ext"} (optional) <span style="color:green;font-weight:bold;">☑</span>
|
- Built-in: `abs`, `min`, `max`, `fmin`, `fmax`, `clamp`, `pow`, `sqrt`, `floor`, `ceil`, `trunc`, `round`, `sin`, `cos`, `tan`, `exp`, `log`, `log10`, `gcd`, `lcm`, `isqrt`, `sign`
|
||||||
- [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;">☑</span>
|
- Random: `random_seed()`, `random_int()`, `random_number()` (cryptographic)
|
||||||
- [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;">☑</span>
|
- Integer clamping: `sclamp()`, `uclamp()`
|
||||||
- [PCRE2](https://pcre2project.github.io/pcre2/){:class="ext"} support builtin for Perl-Compatible Regular Expressions (optional) <span style="color:green;font-weight:bold;">☑</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;">☑</span>
|
|
||||||
- [SQLite](https://sqlite.org/){:class="ext"} support builtin (optional) <span style="color:green;font-weight:bold;">☑</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;">☑</span>
|
|
||||||
|
|
||||||
<span style="color:green;font-weight:bold;">☑</span> = Done / <span style="color:red;font-weight:bold;">☐</span> = Planned or in progress.
|
### 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
|
||||||
|
|
||||||
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)` — spawn thread, returns ID
|
||||||
|
- `thread_join(id)` — 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"}) — parse, stringify, file I/O <span style="color:green;font-weight:bold;">☑</span>
|
||||||
|
- **[cURL](/documentation/extensions/curl/)** (`FUN_WITH_CURL` / [libcurl](https://curl.se/libcurl/){:class="ext"}) — HTTP GET, POST, download <span style="color:green;font-weight:bold;">☑</span>
|
||||||
|
- **[SQLite](/documentation/extensions/sqlite/)** (`FUN_WITH_SQLITE` / [libsqlite3](https://www.sqlite.org/){:class="ext"}) — open, query, exec, close <span style="color:green;font-weight:bold;">☑</span>
|
||||||
|
- **[PCRE2](/documentation/extensions/pcre2/)** (`FUN_WITH_PCRE2` / [libpcre2](https://www.pcre.org/){:class="ext"}) — Perl-compatible regex with flags <span style="color:green;font-weight:bold;">☑</span>
|
||||||
|
- **[OpenSSL](/documentation/extensions/openssl/)** (`FUN_WITH_OPENSSL` / [libcrypto](https://www.openssl.org/){:class="ext"}) — MD5, SHA-256, SHA-512, RIPEMD-160 hashing <span style="color:green;font-weight:bold;">☑</span>
|
||||||
|
- **[INI](/documentation/extensions/ini/)** (`FUN_WITH_INI` / [iniparser](https://github.com/ndevilla/iniparser){:class="ext"}) — load, get/set, save config files <span style="color:green;font-weight:bold;">☑</span>
|
||||||
|
- **[XML](/documentation/extensions/xml2/)** (`FUN_WITH_XML2` / [libxml2](http://xmlsoft.org/){:class="ext"}) — parse, navigate, query XML documents <span style="color:green;font-weight:bold;">☑</span>
|
||||||
|
- **[PC/SC](/documentation/extensions/pcsc/)** (`FUN_WITH_PCSC` / [libpcsclite](https://pcsclite.apdu.fr/){:class="ext"}) — smart card communication <span style="color:green;font-weight:bold;">☑</span>
|
||||||
|
- **[KCGI](/documentation/extensions/kcgi/)** (`FUN_WITH_KCGI` / [libkcgi](https://kristaps.bsd.lv/kcgi/){:class="ext"}) — CGI web applications <span style="color:green;font-weight:bold;">☑</span>
|
||||||
|
- **[Redis/Valkey](/documentation/extensions/redis/)** (`FUN_WITH_REDIS` / [hiredis](https://github.com/redis/hiredis){:class="ext"}) — Redis and Valkey support <span style="color:green;font-weight:bold;">☑</span>
|
||||||
|
|
||||||
|
<span style="color:green;font-weight:bold;">☑</span> = Done
|
||||||
|
|
||||||
|
Each extension also has a corresponding stdlib wrapper class (e.g., `JSON`, `INI`, `XML`, `PCSC`, `PCRE2`, `KCGI`).
|
||||||
|
|
||||||
|
## Tooling
|
||||||
|
|
||||||
|
- **`fun`** — Interpreter/REPL: runs `.fun` scripts or starts interactive REPL
|
||||||
|
- **`funstx`** — Syntax checker with optional `--fix` mode
|
||||||
|
- **REPL** — 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** — breakpoints, step/next/finish, stack traces
|
||||||
|
- **Formatting** — `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 — example scripts run as automated tests
|
||||||
|
- Doxygen API reference generation
|
||||||
|
|
||||||
## Code
|
## 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"}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - Arrays in Fun
|
title: Arrays in Fun
|
||||||
subtitle: Working with arrays, creation, indexing/slicing, iteration patterns, helpers, and idioms.
|
subtitle: Working with arrays, creation, indexing/slicing, iteration patterns, helpers, and idioms.
|
||||||
description: 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/
|
permalink: /documentation/arrays/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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.
|
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.
|
description: Async I/O primitives and patterns, non-blocking sockets, fd polling, examples, and best practices.
|
||||||
permalink: /documentation/asyncio/
|
permalink: /documentation/asyncio/
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Building Fun
|
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).
|
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).
|
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/
|
permalink: /documentation/build/
|
||||||
lang: en
|
lang: en
|
||||||
tags:
|
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_CPP` (ON/OFF) - Enable C++-based opcode/examples support
|
||||||
- `FUN_WITH_RUST` (ON/OFF) - Build and link Rust staticlib from `src/rust/`
|
- `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_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
|
### VM configuration constants
|
||||||
|
|
||||||
|
|
@ -72,6 +73,8 @@ See [VM](../vm/) for more information.
|
||||||
FUN_USE_MUSL: ENABLED|DISABLED
|
FUN_USE_MUSL: ENABLED|DISABLED
|
||||||
FUN_WITH_CPP: ENABLED|DISABLED
|
FUN_WITH_CPP: ENABLED|DISABLED
|
||||||
FUN_WITH_RUST: ENABLED|DISABLED
|
FUN_WITH_RUST: ENABLED|DISABLED
|
||||||
|
FUN_WITH_OPENSSL: ENABLED|DISABLED
|
||||||
|
FUN_WITH_REDIS: ENABLED|DISABLED
|
||||||
===========================</pre>
|
===========================</pre>
|
||||||
|
|
||||||
## Example commands
|
## Example commands
|
||||||
|
|
@ -92,7 +95,7 @@ cmake --build build_release --target build</pre>
|
||||||
### Enabling optional extensions
|
### Enabling optional extensions
|
||||||
|
|
||||||
<pre>cmake -S . -B build_release -DCMAKE_BUILD_TYPE=Release \
|
<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>
|
cmake --build build_release --target build</pre>
|
||||||
|
|
||||||
### Customizing VM limits
|
### 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_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
|
## Running
|
||||||
- CLI: run the `fun` executable from your build directory.
|
- 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/)).
|
- REPL: `fun -i` or just run `fun` without a script, depending on your CLI version (see [CLI](../cli/)).
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - Bytecode Format (Overview)
|
title: Bytecode Format (Overview)
|
||||||
subtitle: Reference for the bytecode format (split out from internals for convenience).
|
subtitle: Reference for the bytecode format (split out from internals for convenience).
|
||||||
description: 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/
|
permalink: /documentation/bytecode-format/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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.
|
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.
|
description: Command-line usage of the `fun` executable, synopsis, options, exit codes, includes and library paths.
|
||||||
permalink: /documentation/cli/
|
permalink: /documentation/cli/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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.
|
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.
|
description: How to contribute, project structure, coding style, running tests, and PR guidelines.
|
||||||
permalink: /documentation/contributing/
|
permalink: /documentation/contributing/
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,21 @@
|
||||||
---
|
---
|
||||||
layout: page
|
layout: page
|
||||||
published: true
|
|
||||||
noToc: false
|
|
||||||
noComments: false
|
|
||||||
noDate: false
|
|
||||||
title: Documentation
|
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
|
description: The Fun Documentation Index
|
||||||
permalink: /documentation/
|
permalink: /documentation/
|
||||||
lang: en
|
lang: en
|
||||||
tags:
|
tags:
|
||||||
|
- documentation
|
||||||
- index
|
- index
|
||||||
|
- fun
|
||||||
- language
|
- language
|
||||||
- programming
|
- 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.
|
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
|
## 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/):
|
Documentation for optional, build-time selectable integrations lives in [./extensions/](./extensions/):
|
||||||
|
|
||||||
- [Index of 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
|
## Tips
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - Embedding Fun
|
title: Embedding Fun
|
||||||
subtitle: Embedding the VM from C/Rust, lifecycle, and host integration tips.
|
subtitle: Embedding the VM from C/Rust, lifecycle, and host integration tips.
|
||||||
description: 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/
|
permalink: /documentation/embedding/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - Errors and Diagnostics
|
title: Errors and Diagnostics
|
||||||
subtitle: Understanding parser/runtime errors and enabling diagnostics.
|
subtitle: Understanding parser/runtime errors and enabling diagnostics.
|
||||||
description: Understanding parser/runtime errors and enabling diagnostics.
|
description: Understanding parser/runtime errors and enabling diagnostics.
|
||||||
permalink: /documentation/errors-and-diagnostics/
|
permalink: /documentation/errors-and-diagnostics/
|
||||||
|
|
|
||||||
|
|
@ -27,21 +27,21 @@ All commands assume you are in the repository root.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- Build the interpreter (see handbook/). You’ll have `build/fun` (paths may vary by your setup/IDE).
|
- Build the interpreter (see [handbook](/documentation/handbook/). You’ll have `build/fun` (paths may vary by your setup/IDE).
|
||||||
- Set FUN_LIB_DIR to the repo’s lib directory when running without installation so `#include <...>` can find the standard library.
|
- Set FUN_LIB_DIR to the repo’s 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"
|
<pre>$env:FUN_LIB_DIR = "$PWD/lib"
|
||||||
./build/fun.exe .\examples\include_lib.fun</pre>
|
./build/fun.exe .\examples\include_lib.fun</pre>
|
||||||
|
|
||||||
## Interactive showcase: play.fun
|
## 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>
|
<pre>./scripts/play.fun</pre>
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ Notes:
|
||||||
|
|
||||||
Tip: you can run specific examples directly too:
|
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
|
## Example categories
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - cURL (libcurl) extension (optional)
|
title: cURL (libcurl) extension (optional)
|
||||||
subtitle: Documentation for cURL (libcurl) extension (optional)
|
subtitle: Documentation for cURL (libcurl) extension (optional)
|
||||||
description: Documentation for cURL (libcurl) extension (optional)
|
description: Documentation for cURL (libcurl) extension (optional)
|
||||||
permalink: /documentation/extensions/curl/
|
permalink: /documentation/extensions/curl/
|
||||||
|
|
@ -20,13 +20,14 @@ tags:
|
||||||
- Purpose: HTTP helpers using libcurl.
|
- Purpose: HTTP helpers using libcurl.
|
||||||
- Homepage: [https://curl.se/libcurl/](https://curl.se/libcurl/){:class="ext"}
|
- 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_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_POST: POST; pops body:string, url:string; pushes response:string
|
||||||
- OP_CURL_DOWNLOAD: Download to file; pops path:string, url:string; pushes 1/0
|
- OP_CURL_DOWNLOAD: Download to file; pops path:string, url:string; pushes 1/0
|
||||||
|
|
||||||
## Notes:
|
## Notes
|
||||||
|
|
||||||
- Requires libcurl development headers/libs.
|
- Requires libcurl development headers/libs.
|
||||||
- When disabled, helpers return empty strings/0 to match optional behavior.
|
- When disabled, helpers return empty strings/0 to match optional behavior.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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).
|
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).
|
description: Catalog of optional, build-time selectable extensions for the Fun VM (e.g., cURL, SQLite, JSON).
|
||||||
permalink: /documentation/extensions/
|
permalink: /documentation/extensions/
|
||||||
|
|
@ -48,6 +48,7 @@ This section documents Fun's optional, build-time selectable extensions. Each pa
|
||||||
- [PC/SC (Smart cards)](./pcsc/)
|
- [PC/SC (Smart cards)](./pcsc/)
|
||||||
- [OpenSSL](./openssl/)
|
- [OpenSSL](./openssl/)
|
||||||
- [kcgi](./kcgi/)
|
- [kcgi](./kcgi/)
|
||||||
|
- [Redis (hiredis)](./redis/)
|
||||||
|
|
||||||
## Notes:
|
## Notes:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - INI (iniparser) extension (optional)
|
title: INI (iniparser) extension (optional)
|
||||||
subtitle: Documentation for INI (iniparser) extension (optional)
|
subtitle: Documentation for INI (iniparser) extension (optional)
|
||||||
description: Documentation for INI (iniparser) extension (optional)
|
description: Documentation for INI (iniparser) extension (optional)
|
||||||
permalink: /documentation/extensions/ini/
|
permalink: /documentation/extensions/ini/
|
||||||
|
|
@ -20,7 +20,7 @@ tags:
|
||||||
- Purpose: Read/write simple INI configuration files.
|
- Purpose: Read/write simple INI configuration files.
|
||||||
- Homepage: [https://github.com/ndevilla/iniparser](https://github.com/ndevilla/iniparser){:class="ext"}
|
- 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_LOAD: pops path; pushes handle (>0) or 0
|
||||||
- OP_INI_FREE: pops handle; pushes 1/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_UNSET: pops key, section, handle; pushes 1/0
|
||||||
- OP_INI_SAVE: pops path, handle; pushes 1/0
|
- OP_INI_SAVE: pops path, handle; pushes 1/0
|
||||||
|
|
||||||
## Notes:
|
## Notes
|
||||||
|
|
||||||
- Requires iniparser development headers/libs.
|
- Requires iniparser development headers/libs.
|
||||||
- When disabled, helpers return neutral values (0/empty strings) like other optional extensions.
|
- When disabled, helpers return neutral values (0/empty strings) like other optional extensions.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - JSON (json-c) extension (optional)
|
title: JSON (json-c) extension (optional)
|
||||||
subtitle: Documentation for JSON (json-c) extension (optional)
|
subtitle: Documentation for JSON (json-c) extension (optional)
|
||||||
description: Documentation for JSON (json-c) extension (optional)
|
description: Documentation for JSON (json-c) extension (optional)
|
||||||
permalink: /documentation/extensions/json/
|
permalink: /documentation/extensions/json/
|
||||||
|
|
@ -19,14 +19,15 @@ tags:
|
||||||
- Purpose: JSON parse/stringify and file helpers via json-c.
|
- 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"}
|
- 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_PARSE: pops text; pushes value or Nil on error
|
||||||
- OP_JSON_STRINGIFY: pops pretty:int(0/1), value; pushes string
|
- OP_JSON_STRINGIFY: pops pretty:int(0/1), value; pushes string
|
||||||
- OP_JSON_FROM_FILE: pops path; pushes value or Nil
|
- OP_JSON_FROM_FILE: pops path; pushes value or Nil
|
||||||
- OP_JSON_TO_FILE: pops pretty:int(0/1), value, path; pushes 1/0
|
- OP_JSON_TO_FILE: pops pretty:int(0/1), value, path; pushes 1/0
|
||||||
|
|
||||||
## Notes:
|
## Notes
|
||||||
|
|
||||||
- Requires json-c development headers/libs.
|
- Requires json-c development headers/libs.
|
||||||
- When disabled, functions push empty/neutral values similar to other optional modules.
|
- When disabled, functions push empty/neutral values similar to other optional modules.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - KCGI extension (optional)
|
title: KCGI extension (optional)
|
||||||
subtitle: Documentation for KCGI (kcgi) extension (optional)
|
subtitle: Documentation for KCGI (kcgi) extension (optional)
|
||||||
description: Documentation for KCGI (kcgi) extension (optional)
|
description: Documentation for KCGI (kcgi) extension (optional)
|
||||||
permalink: /documentation/extensions/kcgi/
|
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.
|
- 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"}
|
- 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).
|
- 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).
|
- 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_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).
|
- 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.
|
- 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).
|
- 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
|
- Configure: cmake -S . -B build -DFUN_WITH_KCGI=ON
|
||||||
- Build: cmake --build build --target fun
|
- Build: cmake --build build --target fun
|
||||||
- Run example:
|
- Run example:
|
||||||
- FUN_LIB_DIR="$(pwd)/lib" ./build/fun examples/cgi/hello_kcgi.fun
|
- FUN_LIB_DIR="$(pwd)/lib" ./build/fun examples/cgi/hello_kcgi.fun
|
||||||
|
|
||||||
## Example output:
|
## Example output
|
||||||
|
|
||||||
- Content-Type: text/html; charset=utf-8
|
- Content-Type: text/html; charset=utf-8
|
||||||
- <h1>Hello, Fun!</h1>
|
- <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.
|
- 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.
|
- When FUN_WITH_KCGI is OFF, the helpers behave as no-ops (returning Nil/0) to keep scripts portable.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - OpenSSL extension (optional)
|
title: OpenSSL extension (optional)
|
||||||
subtitle: Documentation for OpenSSL extension (optional)
|
subtitle: Documentation for OpenSSL extension (optional)
|
||||||
description: Documentation for OpenSSL extension (optional)
|
description: Documentation for OpenSSL extension (optional)
|
||||||
permalink: /documentation/extensions/openssl/
|
permalink: /documentation/extensions/openssl/
|
||||||
|
|
@ -19,12 +19,12 @@ tags:
|
||||||
- Purpose: provide small crypto helpers backed by OpenSSL. Includes md5, sha256, sha512, ripemd160 helpers.
|
- 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"}
|
- Homepage: [https://www.openssl.org/](https://www.openssl.org/){:class="ext"}
|
||||||
|
|
||||||
## Build notes:
|
## Build notes
|
||||||
|
|
||||||
- Requires system OpenSSL development headers and libraries.
|
- Requires system OpenSSL development headers and libraries.
|
||||||
- On OpenSSL 3.x, legacy MD5_* APIs are deprecated; you may see warnings during build.
|
- 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_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).
|
- 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.
|
- 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).
|
- 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
|
- Configure: cmake -S . -B build -DFUN_WITH_OPENSSL=ON
|
||||||
- Build: cmake --build build --target fun
|
- Build: cmake --build build --target fun
|
||||||
|
|
@ -42,7 +42,7 @@ tags:
|
||||||
- ./build/fun examples/crypto/openssl_sha512.fun
|
- ./build/fun examples/crypto/openssl_sha512.fun
|
||||||
- ./build/fun examples/crypto/openssl_ripemd160.fun
|
- ./build/fun examples/crypto/openssl_ripemd160.fun
|
||||||
|
|
||||||
## Example output:
|
## Example output
|
||||||
|
|
||||||
- md5(abc) = 900150983cd24fb0d6963f7d28e17f72
|
- md5(abc) = 900150983cd24fb0d6963f7d28e17f72
|
||||||
- md5("") = d41d8cd98f00b204e9800998ecf8427e
|
- md5("") = d41d8cd98f00b204e9800998ecf8427e
|
||||||
|
|
@ -53,6 +53,7 @@ tags:
|
||||||
- ripemd160(abc) = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc
|
- ripemd160(abc) = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc
|
||||||
- ripemd160("") = 9c1185a5c5e9fc54612808977ee8f548b2258d31
|
- 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.
|
- 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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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)
|
subtitle: Documentation for PCRE2 (Perl-Compatible Regex) extension (optional)
|
||||||
description: Documentation for PCRE2 (Perl-Compatible Regex) extension (optional)
|
description: Documentation for PCRE2 (Perl-Compatible Regex) extension (optional)
|
||||||
permalink: /documentation/extensions/pcre2/
|
permalink: /documentation/extensions/pcre2/
|
||||||
|
|
@ -22,13 +22,14 @@ tags:
|
||||||
- Purpose: Advanced regular expressions via PCRE2.
|
- Purpose: Advanced regular expressions via PCRE2.
|
||||||
- Homepage: [https://www.pcre.org/](https://www.pcre.org/){:class="ext"}
|
- 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_TEST: pops flags, text, pattern; pushes 1/0
|
||||||
- OP_PCRE2_MATCH: pops flags, text, pattern; pushes match map or Nil
|
- OP_PCRE2_MATCH: pops flags, text, pattern; pushes match map or Nil
|
||||||
- OP_PCRE2_FINDALL: pops flags, text, pattern; pushes array of match maps
|
- OP_PCRE2_FINDALL: pops flags, text, pattern; pushes array of match maps
|
||||||
|
|
||||||
## Notes:
|
## Notes
|
||||||
|
|
||||||
- Requires PCRE2 development headers/libs.
|
- Requires PCRE2 development headers/libs.
|
||||||
- Flags are backend-specific; see implementation for supported bits.
|
- Flags are backend-specific; see implementation for supported bits.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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)
|
subtitle: Documentation for PC/SC (smart cards) extension (optional)
|
||||||
description: Documentation for PC/SC (smart cards) extension (optional)
|
description: Documentation for PC/SC (smart cards) extension (optional)
|
||||||
permalink: /documentation/extensions/pcsc/
|
permalink: /documentation/extensions/pcsc/
|
||||||
|
|
@ -20,7 +20,7 @@ tags:
|
||||||
- Purpose: Access smart card readers/cards via PC/SC (pcsclite).
|
- Purpose: Access smart card readers/cards via PC/SC (pcsclite).
|
||||||
- Homepage: [https://pcsclite.apdu.fr/](https://pcsclite.apdu.fr/){:class="ext"}
|
- Homepage: [https://pcsclite.apdu.fr/](https://pcsclite.apdu.fr/){:class="ext"}
|
||||||
|
|
||||||
## Opcodes:
|
## Opcodes
|
||||||
|
|
||||||
- OP_PCSC_ESTABLISH: returns context id (>0) or 0
|
- OP_PCSC_ESTABLISH: returns context id (>0) or 0
|
||||||
- OP_PCSC_RELEASE: pops ctx id; returns 1/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_DISCONNECT: pops handle id; returns 1/0
|
||||||
- OP_PCSC_TRANSMIT: pops apdu, handle id; returns map with data/SW/rc
|
- OP_PCSC_TRANSMIT: pops apdu, handle id; returns map with data/SW/rc
|
||||||
|
|
||||||
## Notes:
|
## Notes
|
||||||
|
|
||||||
- Requires PC/SC lite development headers/libs.
|
- Requires PC/SC lite development headers/libs.
|
||||||
- Behavior and availability depend on platform and reader drivers.
|
- Behavior and availability depend on platform and reader drivers.
|
||||||
|
|
||||||
|
|
|
||||||
84
web/documentation/extensions/redis/redis.md
Normal file
84
web/documentation/extensions/redis/redis.md
Normal 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`.
|
||||||
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - SQLite extension (optional)
|
title: SQLite extension (optional)
|
||||||
subtitle: Documentation for SQLite extension (optional)
|
subtitle: Documentation for SQLite extension (optional)
|
||||||
description: Documentation for SQLite extension (optional)
|
description: Documentation for SQLite extension (optional)
|
||||||
permalink: /documentation/extensions/sqlite/
|
permalink: /documentation/extensions/sqlite/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - XML (libxml2) extension (optional)
|
title: XML (libxml2) extension (optional)
|
||||||
subtitle: Documentation for XML (libxml2) extension (optional)
|
subtitle: Documentation for XML (libxml2) extension (optional)
|
||||||
description: Documentation for XML (libxml2) extension (optional)
|
description: Documentation for XML (libxml2) extension (optional)
|
||||||
permalink: /documentation/extensions/xml2/
|
permalink: /documentation/extensions/xml2/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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.
|
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.
|
description: Full usage guide for the `fun` executable, invocation patterns, REPL, env vars, include paths, examples, and install locations.
|
||||||
permalink: /documentation/fun/
|
permalink: /documentation/fun/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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.
|
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.
|
description: Syntax checker for .fun files with optional --fix auto-corrections; usage, exit codes, and limitations.
|
||||||
permalink: /documentation/funstx/
|
permalink: /documentation/funstx/
|
||||||
|
|
|
||||||
|
|
@ -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.).
|
- 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.
|
- 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:
|
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.
|
- Maps: OP_MAKE_MAP/KEYS/VALUES/HAS_KEY.
|
||||||
- Conversions/reflection: OP_TO_NUMBER/TO_STRING/CAST/TYPEOF, OP_UCLAMP/SCLAMP.
|
- 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.
|
- 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.
|
Each handler enforces argument types and returns clear error messages via vm_raise_error on misuse.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - Maps in Fun
|
title: Maps in Fun
|
||||||
subtitle: Working with maps, construction, lookup/update, merging, iteration, and common patterns.
|
subtitle: Working with maps, construction, lookup/update, merging, iteration, and common patterns.
|
||||||
description: 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/
|
permalink: /documentation/maps/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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.
|
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.
|
description: Working with integers and floats, arithmetic, conversions, clamping, bitwise ops, and patterns.
|
||||||
permalink: /documentation/numbers/
|
permalink: /documentation/numbers/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - Performance Guide
|
title: Performance Guide
|
||||||
subtitle: Build/runtime tuning tips and patterns for better performance.
|
subtitle: Build/runtime tuning tips and patterns for better performance.
|
||||||
description: Build/runtime tuning tips and patterns for better performance.
|
description: Build/runtime tuning tips and patterns for better performance.
|
||||||
permalink: /documentation/performance/
|
permalink: /documentation/performance/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - Roadmap
|
title: Roadmap
|
||||||
subtitle: High-level direction, planned features, and pointers to issues.
|
subtitle: High-level direction, planned features, and pointers to issues.
|
||||||
description: High-level direction, planned features, and pointers to issues.
|
description: High-level direction, planned features, and pointers to issues.
|
||||||
permalink: /documentation/roadmap/
|
permalink: /documentation/roadmap/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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.
|
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.
|
description: Writing Rust-backed opcodes and wiring them into the C VM; build/setup notes.
|
||||||
permalink: /documentation/rust/
|
permalink: /documentation/rust/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - Security and Sandboxing
|
title: Security and Sandboxing
|
||||||
subtitle: Trust boundaries, I/O expectations, and capability restrictions.
|
subtitle: Trust boundaries, I/O expectations, and capability restrictions.
|
||||||
description: Trust boundaries, I/O expectations, and capability restrictions.
|
description: Trust boundaries, I/O expectations, and capability restrictions.
|
||||||
permalink: /documentation/security-and-sandboxing/
|
permalink: /documentation/security-and-sandboxing/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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.
|
subtitle: Working with strings, literals/escaping, concatenation, substr/find, split, and conversions.
|
||||||
description: 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/
|
permalink: /documentation/strings/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - Fun Style Guide
|
title: Fun Style Guide
|
||||||
subtitle: Coding conventions for C and Fun (indentation, naming, idioms).
|
subtitle: Coding conventions for C and Fun (indentation, naming, idioms).
|
||||||
description: Coding conventions for C and Fun (indentation, naming, idioms).
|
description: Coding conventions for C and Fun (indentation, naming, idioms).
|
||||||
permalink: /documentation/style-guide/
|
permalink: /documentation/style-guide/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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.
|
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.
|
description: How to build and run tests/targets with CMake/CTest, and where to add new tests.
|
||||||
permalink: /documentation/testing/
|
permalink: /documentation/testing/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - Troubleshooting Guide
|
title: Troubleshooting Guide
|
||||||
subtitle: Common issues and quick fixes for build, includes, and REPL usage.
|
subtitle: Common issues and quick fixes for build, includes, and REPL usage.
|
||||||
description: 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/
|
permalink: /documentation/troubleshooting/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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.
|
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.
|
description: Core types (numbers, strings, arrays, maps, nil/bool), common operations, patterns, and interop notes.
|
||||||
permalink: /documentation/types/
|
permalink: /documentation/types/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: false
|
noDate: false
|
||||||
title: Fun - Writing Tests
|
title: Writing Tests
|
||||||
subtitle: How to author new tests for Fun and opcode components.
|
subtitle: How to author new tests for Fun and opcode components.
|
||||||
description: 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/
|
permalink: /documentation/writing-tests/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ published: true
|
||||||
noToc: false
|
noToc: false
|
||||||
noComments: false
|
noComments: false
|
||||||
noDate: 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.
|
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.
|
description: Comprehensive feature list of the Fun programming language, its VM/runtime, tooling, build options, and optional extensions.
|
||||||
permalink: /features/
|
permalink: /features/
|
||||||
|
|
@ -24,103 +24,406 @@ tags:
|
||||||
- cgi
|
- cgi
|
||||||
---
|
---
|
||||||
|
|
||||||
This page summarizes the core capabilities of Fun: the language, its virtual machine/runtime, tooling, build options, and the surrounding ecosystem. For in‑depth 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
|
## Language
|
||||||
|
|
||||||
- Simple, expressive syntax designed for scripting and embedding
|
### Syntax & Structure
|
||||||
- First‑class functions and function calls
|
|
||||||
- Variables, locals and globals with stack‑based 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)
|
|
||||||
|
|
||||||
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
|
## Virtual Machine & Runtime
|
||||||
|
|
||||||
- Compact stack‑based bytecode VM implemented in C (C99)
|
### Architecture
|
||||||
- Deterministic execution model with explicit opcodes for core language features
|
|
||||||
- Efficient array and map primitives with indexing and mutation opcodes
|
|
||||||
- Built‑in string operations and regex integration (with extension)
|
|
||||||
- Minimal error model integrated with VM (throw/try handlers)
|
|
||||||
|
|
||||||
## 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
|
- Deterministic execution model
|
||||||
- Maps: create, has_key, keys, values
|
- Reference-counted arrays and maps
|
||||||
- Strings: substr, find, split, replace (with regex), join
|
- Function/data sectioning with linker GC for small binaries
|
||||||
- Math and bitwise ops over fixed‑width integers used by the VM
|
- 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
|
## Tooling
|
||||||
|
|
||||||
- Fun interpreter/runtime executable (target: fun)
|
- **`fun`** — Interpreter/REPL. Runs `.fun` scripts or starts interactive REPL
|
||||||
- REPL and small tools (targets: repl, funstx, examples under examples/)
|
- **`funstx`** — Syntax checker with optional `--fix` mode
|
||||||
- Test binaries (target: fun_test, test_opcodes)
|
- **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`
|
||||||
- Formatting helper target (target: format) using clang‑format
|
- **Test harnesses** — `fun_test` (bytecode-level tests) and `test_opcodes` (opcode exercisers)
|
||||||
- Make‑like aggregate build target (target: build)
|
- **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 project’s optionality at build time:
|
## Build System
|
||||||
|
|
||||||
- FUN_DEBUG — enable extra diagnostics in builds
|
- **CMake** 3.10+ with C99 standard
|
||||||
- FUN_USE_MUSL — build against musl when available
|
- Build toggles: `FUN_DEBUG`, `FUN_USE_MUSL`, `FUN_WITH_REPL`, `FUN_WITH_CPP`, `FUN_WITH_RUST`, `FUN_BUILD_DOCS`
|
||||||
- FUN_WITH_CPP — enable C++ interop/components where applicable
|
- Per-extension toggles for all optional libraries
|
||||||
- FUN_WITH_RUST — build and link an optional Rust static library with opcode examples
|
- Release builds with LTO, function/data sectioning, and `--gc-sections`
|
||||||
- FUN_BUILD_DOCS — generate documentation (Doxygen/website pipelines)
|
- Doxygen documentation generation
|
||||||
- FUN_WITH_* — per‑extension toggles (see Extensions section)
|
- 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
|
## Portability
|
||||||
|
|
||||||
- Written in portable C99
|
- Written in **C99** with POSIX extensions
|
||||||
- Designed to build on common Linux environments; musl support available
|
- Primary target: **Linux** (glibc and musl)
|
||||||
|
- Partial **Windows** support
|
||||||
|
- Platform abstraction for threading, sockets, serial I/O
|
||||||
|
- Builds with GCC and Clang
|
||||||
|
|
||||||
## Extensions (Optional, Build‑time)
|
---
|
||||||
|
|
||||||
Fun ships with optional integrations that can be enabled per environment. Highlights include:
|
|
||||||
|
|
||||||
- cURL (HTTP client)
|
|
||||||
- INI (iniparser)
|
|
||||||
- JSON (json‑c)
|
|
||||||
- libxml2 (XML)
|
|
||||||
- SQLite
|
|
||||||
- PCRE2 (Perl‑compatible 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 CGI‑style programs and simple web endpoints (see Extensions)
|
|
||||||
- Website sources under web/ with Jekyll layouts; generated site in web/_site/
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
- Unit and VM‑level opcode tests (targets: fun_test, test_opcodes)
|
- Unit tests for VM opcodes and bytecode execution
|
||||||
- Example scripts under examples/ exercised by CI and manual runs
|
- 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
|
## Documentation
|
||||||
|
|
||||||
- Doxygen configuration for API references (Doxyfile)
|
- Jekyll-based website under `web/` with full documentation
|
||||||
- Human‑readable docs under web/documentation/ (opcodes, extensions, etc.)
|
- 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
|
## Licensing
|
||||||
|
|
||||||
- Licensed under an OSI‑approved license; see LICENSE at the repository root
|
- **Apache 2.0** — fully open source, freely usable and modifiable
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,13 @@ permalink: /
|
||||||
|
|
||||||
<h2>What is Fun?</h2>
|
<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>
|
<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 %}
|
{% 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 %}
|
{% endfor %}
|
||||||
</div><br>
|
</div><br>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue