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:
|
||||
BUILD_TYPE: Release
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
@ -13,7 +14,6 @@ jobs:
|
|||
defaults:
|
||||
run:
|
||||
working-directory: ${{github.workspace}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
|
|
|||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -7,9 +7,9 @@ Doxygen.in
|
|||
!.gitignore
|
||||
!.github
|
||||
!.gitkeep
|
||||
!api/.gitkeep
|
||||
.venv
|
||||
api/
|
||||
!api/.gitkeep
|
||||
build/*
|
||||
build_debug/*
|
||||
build_release/*
|
||||
|
|
@ -23,6 +23,8 @@ downloaded.png
|
|||
json.xml
|
||||
lib/*.so
|
||||
out/
|
||||
playground/*
|
||||
!playground/.gitkeep
|
||||
src/*.o
|
||||
src/rust/Cargo.lock
|
||||
src/rust/target
|
||||
|
|
|
|||
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/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) (during 0.x.y development phase).
|
||||
|
||||
## [0.42.1] - 2026-06-08
|
||||
### Fixed
|
||||
- Small test script issue.
|
||||
### Changed
|
||||
- Documentation and website updates. No runtime code changes.
|
||||
- README, `make`, and `Doxyfile` updates. No runtime code changes.
|
||||
|
||||
## [0.42.0] - 2026-06-03
|
||||
### Added
|
||||
- Redis/Valkey extension named `redis` (see `src/extensions/redis.c` and `src/vm/redis/*`).
|
||||
- Redis example programs: `basic_ping.fun`, `hash_ops.fun`, `kv_set_get.fun`, `list_ops.fun`, and `redis_test.fun`.
|
||||
- Initial Redis documentation page under `web/documentation/extensions/redis/`.
|
||||
### Changed
|
||||
- Build system: Redis extension is not compiled by default; enable it explicitly if needed. No other code changes.
|
||||
- GitHub workflow tweaks. No code changes.
|
||||
|
||||
## [0.41.16] - 2026-05-29
|
||||
### Added
|
||||
- `playground/` directory for local-only experiments.
|
||||
### Changed
|
||||
- `examples/features.fun` and website features page updates.
|
||||
- README and API documentation updates. No code changes.
|
||||
- Documentation and web fixes. No code changes.
|
||||
|
||||
## [0.41.15] - 2026-05-26
|
||||
### Changed
|
||||
- More internal optimizations.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
project(fun VERSION 0.41.15 LANGUAGES C)
|
||||
project(fun VERSION 0.42.2 LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
|
@ -521,7 +521,7 @@ install(DIRECTORY lib/
|
|||
# Optionally install example scripts
|
||||
option(FUN_INSTALL_EXAMPLES "Install example .fun scripts" ON)
|
||||
if(FUN_INSTALL_EXAMPLES)
|
||||
install(DIRECTORY examples/
|
||||
install(DIRECTORY examples/
|
||||
DESTINATION /usr/share/fun/examples
|
||||
FILES_MATCHING PATTERN "*.fun"
|
||||
)
|
||||
|
|
|
|||
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
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 0.41.15
|
||||
PROJECT_NUMBER = 0.42.1
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewers a
|
||||
|
|
|
|||
211
README.md
211
README.md
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
## What is Fun?
|
||||
|
||||
Fun is a small, strict, and simple programming language that runs on a compact stack-based virtual machine. The C core is intentionally minimal; most functionality and standard libraries are implemented in Fun itself. The language emphasizes simplicity, consistency, and joy in coding.
|
||||
Fun is a small, strict, and simple programming language that runs on a compact stack-based virtual machine. The C core is intentionally minimal; most functionality and standard libraries are implemented in Fun itself.
|
||||
|
||||
Fun is an experiment, just for fun, but Fun works!
|
||||
Fun is dynamically typed with optional static type annotations, featuring first-class functions, classes with inheritance, pattern matching, and a rich standard library. It supports everything from basic scripting to TCP sockets, serial communication, threading, cryptography (MD5, SHA-1/256/384/512, CRC-32, AES-256), and a built-in debugger.
|
||||
|
||||
Fun is a highly strict programming language, but also highly simple. It looks like Python (My favorite language), but there are differences.
|
||||
Fun is an experiment — just for fun — but Fun works!
|
||||
|
||||
Influenced by **[Bash](https://www.gnu.org/software/bash/)**, **[C](https://en.wikipedia.org/wiki/The_C_Programming_Language)**, **[Lua](https://www.lua.org/)**, PHP, **[Python](https://www.python.org/)**, and Rust (Most influences came from linked languages).
|
||||
Influenced by **[Bash](https://www.gnu.org/software/bash/)**, **[C](https://en.wikipedia.org/wiki/The_C_Programming_Language)**, **[Lua](https://www.lua.org/)**, PHP, **[Python](https://www.python.org/)**, and **[Rust](https://www.rust-lang.org/)**.
|
||||
|
||||
Fun is and will ever be 100% free under the terms of the [Apache-2.0 License](https://opensource.org/license/apache-2-0).
|
||||
|
||||
|
|
@ -23,11 +23,19 @@ Fun is and will ever be 100% free under the terms of the [Apache-2.0 License](ht
|
|||
|
||||
## Characteristics
|
||||
|
||||
- Dynamic and optionally statically typed
|
||||
- Type safety
|
||||
- Written in C (C99) and Fun
|
||||
- Internal libs are written with no_camel_case even when written in Fun, except class names
|
||||
- Only a minimal function set is written in C, and most other core functions and libraries are implemented in Fun
|
||||
- **Dynamic typing** with optional **static type annotations** (`number`, `string`, `boolean`, `float`, `byte`, `uint8`–`uint64`, `int8`–`int64`)
|
||||
- **Stack-based bytecode VM** written in C99 with ~220 opcodes
|
||||
- **First-class functions**, anonymous functions (`fn`), and higher-order operations (`map`, `filter`, `reduce`)
|
||||
- **Classes** with constructors, methods, and single inheritance
|
||||
- **Exception handling** with `try`/`catch`/`finally`
|
||||
- **Built-in data structures**: arrays (with slicing), maps/objects, strings
|
||||
- **Concurrency**: threads (`thread_spawn`, `thread_join`) and cooperative async scheduler
|
||||
- **Networking**: TCP and Unix domain sockets with non-blocking I/O polling
|
||||
- **Serial communication**: full termios-based serial port control
|
||||
- **Cryptography**: pure-Fun implementations of MD5, SHA-1/256/384/512, CRC-32/CRC-32C, AES-256 ECB
|
||||
- **Built-in debugger** with breakpoints, step/next/finish, and stack inspection
|
||||
- **Minimal C core** — most standard libraries are implemented in Fun itself
|
||||
- **Internal style**: `snake_case` for functions and variables, `CamelCase` for class names
|
||||
|
||||
## The Fun Manifesto
|
||||
|
||||
|
|
@ -45,13 +53,13 @@ Coding should be enjoyable, elegant, and consistent.
|
|||
- **One Way to Do It**<br>
|
||||
No clutter, no 15 ways of writing the same thing. Simplicity means clarity.
|
||||
- **Hackable by Nature**<br>
|
||||
Fun should be small and embeddable, like Lua. Easy to understand, extend, and tinker with — true to the hacker spirit.
|
||||
Fun should be small and embeddable, like Lua. Easy to understand, extend, and tinker with — true to the hacker spirit.
|
||||
- **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
|
||||
|
||||
Fun is not about being the fastest or the most feature-rich. It’s about sharing joy in coding. The community should be:
|
||||
Fun is not about being the fastest or the most feature-rich. It's about sharing joy in coding. The community should be:
|
||||
|
||||
- Respectful
|
||||
- Curious
|
||||
|
|
@ -70,63 +78,162 @@ A language that feels like home for developers who:
|
|||
- Believe consistency is freedom
|
||||
- Want to write code that looks good and feels good
|
||||
|
||||
Fun may not change the world — but it will make programming a little more fun.
|
||||
Fun may not change the world — but it will make programming a little more fun.
|
||||
|
||||
## Features
|
||||
## Language Features
|
||||
|
||||
### Core
|
||||
### Core Syntax & Types
|
||||
|
||||
- functions/classes/objects
|
||||
- if/else if/else
|
||||
- try/catch/finally
|
||||
- Indentation-based blocks (2-space), line and block comments
|
||||
- Static type annotations with automatic runtime range clamping for fixed-width integers
|
||||
- `typeof()`, `to_string()`, `to_number()`, `cast()` — type introspection and conversion
|
||||
- `exit` statement with optional exit code, `#include` for source includes
|
||||
|
||||
And much more...! Look at the specs in [Spec](./spec/) for more detailed information.
|
||||
### Operators
|
||||
|
||||
### Lib (./lib/)
|
||||
- Arithmetic: `+`, `-`, `*`, `/`, `%`
|
||||
- Comparison: `<`, `<=`, `>`, `>=`, `==`, `!=`
|
||||
- Logical: `&&`, `||`, `!` (short-circuit)
|
||||
- Bitwise: `band()`, `bor()`, `bxor()`, `bnot()`, `shl()`, `shr()`, `rol()`, `ror()`
|
||||
- Ternary: `condition ? true_expr : false_expr`
|
||||
|
||||
See [./lib/](https://git.xw3.org/fun/fun/src/branch/main/lib) for what the standard library provides.
|
||||
### Control Flow
|
||||
|
||||
### Optional extensions (build-time selectable / only testing this on Linux actually):
|
||||
- `if` / `else if` / `else`
|
||||
- `while` with `break` and `continue`
|
||||
- `for var in array` — iteration
|
||||
- `for var in range(start, end)` — numeric range
|
||||
- `for (key, value) in map` — map destructuring
|
||||
- `match` expression (stdlib)
|
||||
- `try` / `catch` / `finally`
|
||||
|
||||
- [CGI](https://en.wikipedia.org/wiki/Common_Gateway_Interface) support builtin using [kcgi](https://kristaps.bsd.lv/kcgi/) (optional) — see [docs](./web/documentation/extensions/kcgi/kcgi.md) ☑
|
||||
- [cURL (libcurl)](./web/documentation/extensions/curl/curl.md) (optional) ☑
|
||||
- [INI (iniparser)](./web/documentation/extensions/ini/ini.md) (optional) ☑
|
||||
- [JSON (json-c)](./web/documentation/extensions/json/json.md) (optional) ☑
|
||||
- [PCRE2](./web/documentation/extensions/pcre2/pcre2.md) (optional) ☑
|
||||
- [PCSC (smart cards)](./web/documentation/extensions/pcsc/pcsc.md) (optional) ☑
|
||||
- [OpenSSL](./web/documentation/extensions/openssl/openssl.md) (optional) ☑
|
||||
- [SQLite](./web/documentation/extensions/sqlite/sqlite.md) (optional) ☑
|
||||
- [XML (libxml2)](./web/documentation/extensions/xml2/xml2.md) (optional) ☑
|
||||
### Functions
|
||||
|
||||
☑ = Done (or basics implemented) / ☐ = Planned or in progress.
|
||||
- `fun name(params) body` — named functions
|
||||
- `fn(params) body` — anonymous function literals
|
||||
- First-class: pass as arguments, store in variables, recursion
|
||||
- `return` with optional value (implicit nil)
|
||||
|
||||
Note: Not all of the above features will be implemented. Those who are marked "Done" will probaly remain in Fun, but I don't know actually... ;)
|
||||
### Object-Oriented Programming
|
||||
|
||||
There are some libs written in Fun available in the [./lib/](https://git.xw3.org/fun/fun/src/branch/main/lib) diretory. In the future most Fun enhancements should be written in Fun itself.
|
||||
- `class Name(typed params) body` with `_construct(this, ...)` auto-invoked constructor
|
||||
- `this` keyword, `obj.method(args)` method call sugar, `obj.field` dot property access
|
||||
- `class Child(...) extends Parent` with method overriding
|
||||
|
||||
### OpenSSL quickstart (MD5)
|
||||
### Data Structures
|
||||
|
||||
See the dedicated page: [./web/documentation/extensions/openssl/](./web/documentation/extensions/openssl/openssl.md)
|
||||
- **Arrays**: literal `[1, 2, 3]`, index `arr[0]`, slice `arr[1:3]`, negative indices; `len()`, `push()`, `pop()`, `insert()`, `remove()`, `contains()`, `indexOf()`, `clear()`, `enumerate()`, `zip()`, `join()`, `map()`, `filter()`, `reduce()`
|
||||
- **Maps**: literal `{key: value}`, bracket `map["key"]`, dot `map.key` access; `has()`, `keys()`, `values()`
|
||||
- **Strings**: concatenation with `+`, `len()`, `substr()`, `find()`, `split()`, `join()`
|
||||
|
||||
### Mathematics
|
||||
|
||||
- Built-in: `abs`, `min`, `max`, `fmin`, `fmax`, `clamp`, `pow`, `sqrt`, `floor`, `ceil`, `trunc`, `round`, `sin`, `cos`, `tan`, `exp`, `log`, `log10`, `gcd`, `lcm`, `isqrt`, `sign`
|
||||
- Random: `random_seed()`, `random_int()`, `random_number()` (cryptographic)
|
||||
- Integer clamping: `sclamp()`, `uclamp()`
|
||||
|
||||
### I/O & Platform
|
||||
|
||||
- `print()`, `echo()` — output
|
||||
- `read_file()`, `write_file()` — file I/O
|
||||
- `input_line()` — stdin with optional prompt
|
||||
- `env()`, `env_all()` — environment variables
|
||||
- `proc_run()`, `system()` — process execution
|
||||
- `os_list_dir()` — directory listing
|
||||
- `time_now_ms()`, `clock_mono_ms()`, `date_format()`, `sleep()`
|
||||
|
||||
### Networking (Built-in, Unix)
|
||||
|
||||
- TCP: listen, accept, connect, send, recv, close
|
||||
- Unix domain sockets: listen, connect
|
||||
- Non-blocking I/O: `fd_set_nonblock()`, `fd_poll_read()`, `fd_poll_write()`
|
||||
|
||||
### Serial Communication (Unix)
|
||||
|
||||
- `serial_open()`, `serial_config()`, `serial_send()`, `serial_recv()`, `serial_close()`
|
||||
|
||||
### Concurrency
|
||||
|
||||
- `thread_spawn(fn, args)` returns thread ID; `thread_join(id)` returns result
|
||||
- Cooperative async scheduler in stdlib
|
||||
|
||||
### Debugging & Tooling
|
||||
|
||||
- Built-in debugger with 64 breakpoints, step/next/finish/continue
|
||||
- `--trace` / `-t` for opcode-level execution tracing
|
||||
- `--repl-on-error`: enter REPL on runtime error with stack preserved
|
||||
- Full-featured REPL with history, tab completion, multi-line input, commands (`:help`, `:load`, `:edit`, `:save`, `:debug`, `:trace`, `:type`, and more)
|
||||
- `funstx` — syntax checker with optional `--fix` mode
|
||||
|
||||
## Standard Library (lib/)
|
||||
|
||||
Written primarily in Fun itself:
|
||||
|
||||
- **Strings**: trim, starts/ends-with, split, replace-all, case conversion, repeat
|
||||
- **Arrays**: slice, reverse, concat, unique, flatten
|
||||
- **Math**: `abs`, `clamp`, `gcd`, `lcm`, `powi`, min3, max3, array min/max
|
||||
- **Encoding**: hex encode/decode, base64 encode/decode
|
||||
- **Cryptography** (pure Fun): MD5, SHA-1, SHA-256, SHA-384, SHA-512, CRC-32, CRC-32C, AES-256 ECB
|
||||
- **Functional**: Option (Some/None), Result (Ok/Err), pattern matching
|
||||
- **Ranges**: `range(n)`, `range2(start, end)`, `range3(start, end, step)`
|
||||
- **Date/Time**: `DateTime` class with formatting, timers, sleep
|
||||
- **CLI**: `argv()`, `parse_args()` with flag support
|
||||
- **Console**: `Console` class with prompt, ask, hidden input, progress bar
|
||||
- **Thread**: `Thread` class, **Process**: `Process` class
|
||||
- **Socket classes**: `TcpClient`, `TcpServer`, `UnixClient`
|
||||
- **Serial**: `Serial` class
|
||||
- **Async**: Cooperative scheduler with I/O polling
|
||||
- **HTTP**: Static file server, CGI-capable server
|
||||
- **IRC**: `IRCClient` with full protocol support
|
||||
- **CGI**: `CGI` class for web applications
|
||||
|
||||
## Optional Extensions (Build-time)
|
||||
|
||||
Enabled via CMake flags, wrapping mature C libraries:
|
||||
|
||||
| Extension | Backend |
|
||||
|---------------------------------------------------------------|-------------|
|
||||
| [JSON](./web/documentation/extensions/json/json.md) | json-c |
|
||||
| [cURL](./web/documentation/extensions/curl/curl.md) | libcurl |
|
||||
| [SQLite](./web/documentation/extensions/sqlite/sqlite.md) | libsqlite3 |
|
||||
| [PCRE2](./web/documentation/extensions/pcre2/pcre2.md) | libpcre2 |
|
||||
| [OpenSSL](./web/documentation/extensions/openssl/openssl.md) | libcrypto |
|
||||
| [INI](./web/documentation/extensions/ini/ini.md) | iniparser |
|
||||
| [XML](./web/documentation/extensions/xml2/xml2.md) | libxml2 |
|
||||
| [PC/SC](./web/documentation/extensions/pcsc/pcsc.md) | libpcsclite |
|
||||
| [KCGI](./web/documentation/extensions/kcgi/kcgi.md) | libkcgi |
|
||||
| [Redis/Valkey](./web/documentation/extensions/redis/redis.md) | hiredis |
|
||||
|
||||
Some extensions also have a corresponding stdlib wrapper class (e.g., `JSON`, `INI`, `XML`, `PCSC`, `PCRE2`, `KCGI`).
|
||||
|
||||
## Quick start
|
||||
|
||||
<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
|
||||
|
||||
Looking for docs? Start here:
|
||||
|
||||
- Local documentation index: [./web/documentation/documenatation.md](./web/documentation/documentation.md)
|
||||
- Handbook: [./web/documentation/handbook/handbook.md](./web/documentation/handbook/handbook.md)
|
||||
- Types overview: [./web/documentation/types/types.md](./web/documentation/types/types.md)
|
||||
- REPL guide: [./web/documentation/repl/repl.md](./web/documentation/repl/repl.md)
|
||||
- Testing: [./web/documentation/testing/testing.md](./web/documentation/testing/testing.md)
|
||||
- Troubleshooting: [./web/documentation/troubleshooting/troubleshooting.md](./web/documentation/troubleshooting/troubleshooting.md)
|
||||
|
||||
Additional references:
|
||||
|
||||
- Specification: [./web/documentation/spec/v0.4.md](./web/documentation/spec/v0.4.md) (work in progress)
|
||||
- Handbook: [./web/documentation/handbook/handbook.md](./web/documentation/handbook/handbook.md)
|
||||
- Types: [./web/documentation/types/types.md](./web/documentation/types/types.md)
|
||||
- REPL: [./web/documentation/repl/repl.md](./web/documentation/repl/repl.md)
|
||||
- Testing: [./web/documentation/testing/testing.md](./web/documentation/testing/testing.md)
|
||||
- Spec: [./web/documentation/spec/v0.4.md](./web/documentation/spec/v0.4.md)
|
||||
- Changelog: [CHANGELOG.md](./CHANGELOG.md)
|
||||
- Examples demonstrating most features: [./examples/](./examples/)
|
||||
- Internals and VM opcodes live in [./src/](./src/) (see [./src/vm/](./src/vm) for opcode implementations)
|
||||
|
||||
Note: The project is evolving; some documents may lag behind. The docs index in [./web/documentation/documenatation.md](./web/documentation/documentation.md) is the most up‑to‑date entry point.
|
||||
- Examples: [./examples/features.fun](./examples/features.fun)
|
||||
|
||||
## Author
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ include(${CMAKE_SOURCE_DIR}/cmake/Extensions/PCSC.cmake)
|
|||
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/REPL.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/SQLITE.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/XML2.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/Extensions/REDIS.cmake)
|
||||
|
||||
# Summary of extension toggles
|
||||
message(STATUS "---- Fun extension summary ----")
|
||||
|
|
@ -34,4 +35,5 @@ _fun_print_feature("PCRE2 (FUN_WITH_PCRE2)" FUN_WITH_PCRE2)
|
|||
_fun_print_feature("PCSC-Lite (FUN_WITH_PCSC)" FUN_WITH_PCSC)
|
||||
_fun_print_feature("REPL (FUN_WITH_REPL)" FUN_WITH_REPL)
|
||||
_fun_print_feature("SQLite (FUN_WITH_SQLITE)" FUN_WITH_SQLITE)
|
||||
_fun_print_feature("Redis (FUN_WITH_REDIS)" FUN_WITH_REDIS)
|
||||
message(STATUS "--------------------------------")
|
||||
|
|
|
|||
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
|
||||
LIBXML2
|
||||
OPENSSL
|
||||
KCGI)
|
||||
KCGI
|
||||
HIREDIS)
|
||||
if(${var_pair}_INCLUDE_DIRS)
|
||||
target_include_directories(fun_core PRIVATE ${${var_pair}_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
|
@ -94,6 +95,9 @@ if(FUN_WITH_SQLITE)
|
|||
target_compile_definitions(fun_core PUBLIC FUN_WITH_SQLITE=1)
|
||||
endif()
|
||||
|
||||
if(FUN_WITH_REDIS)
|
||||
target_compile_definitions(fun_core PUBLIC FUN_WITH_REDIS=1)
|
||||
endif()
|
||||
|
||||
if(FUN_WITH_OPENSSL)
|
||||
target_compile_definitions(fun_core PUBLIC FUN_WITH_OPENSSL=1)
|
||||
|
|
|
|||
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:")
|
||||
string name = "Fun Language"
|
||||
float version = 0.3
|
||||
float version = 0.41
|
||||
boolean is_awesome = true
|
||||
number meaning = 42
|
||||
items = [1, 2, 3, 4, 5]
|
||||
config = {"debug": true, "port": 8080}
|
||||
nil_val = nil
|
||||
|
||||
print(" Language: " + name + " v" + to_string(version))
|
||||
print(" Language: " + name + " v" + fun_version())
|
||||
print(" Awesome: " + to_string(is_awesome))
|
||||
print(" Nil: " + to_string(nil_val))
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 2. Modern Array Operations
|
||||
// 2. Type Introspection
|
||||
// ============================================
|
||||
print("2. Array Operations:")
|
||||
print("2. Type Introspection:")
|
||||
number check_int = 42
|
||||
string check_str = "hello"
|
||||
check_arr = [1, 2, 3]
|
||||
check_map = {"key": "value"}
|
||||
check_float = 3.14
|
||||
|
||||
print(" typeof(42) = " + typeof(check_int))
|
||||
print(" typeof(\"hello\") = " + typeof(check_str))
|
||||
print(" typeof([1,2,3]) = " + typeof(check_arr))
|
||||
print(" typeof(map) = " + typeof(check_map))
|
||||
print(" typeof(3.14) = " + typeof(check_float))
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 3. Conversion & Casting
|
||||
// ============================================
|
||||
print("3. Conversion & Casting:")
|
||||
print(" to_string(42) = " + to_string(42))
|
||||
print(" to_number(\"99\") = " + to_string(to_number("99")))
|
||||
casted = cast(1, "boolean")
|
||||
print(" cast(1, \"boolean\") = " + to_string(casted))
|
||||
casted2 = cast("42", "number")
|
||||
print(" cast(\"42\", \"number\") = " + to_string(casted2))
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 4. String Manipulation
|
||||
// ============================================
|
||||
print("4. String Operations:")
|
||||
string text = "Hello, Fun Language!"
|
||||
print(" Original: " + text)
|
||||
print(" Length: " + to_string(len(text)))
|
||||
print(" Substr(0,5): " + substr(text, 0, 5))
|
||||
print(" Find(\"Fun\"): " + to_string(find(text, "Fun")))
|
||||
parts = split(text, " ")
|
||||
print(" Split by space: " + to_string(parts))
|
||||
joined = join(parts, "-")
|
||||
print(" Join with '-': " + joined)
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 5. Modern Array Operations
|
||||
// ============================================
|
||||
print("5. Array Operations:")
|
||||
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
print(" Original: " + to_string(numbers))
|
||||
print(" Length: " + to_string(len(numbers)))
|
||||
print(" Index 0: " + to_string(numbers[0]))
|
||||
print(" Index last: " + to_string(numbers[len(numbers) - 1]))
|
||||
|
||||
// Array helpers from spec: push, join, map, filter, reduce
|
||||
joined = join([10, 20, 30], ", ")
|
||||
print(" Joined: " + joined)
|
||||
// Slice syntax arr[start:end]
|
||||
sliced = numbers[2:7]
|
||||
print(" Slice [2:7]: " + to_string(sliced))
|
||||
|
||||
// Iterate arrays
|
||||
print(" Iteration:")
|
||||
for item in ["apple", "banana", "cherry"]
|
||||
print(" " + item)
|
||||
// Mutating array ops
|
||||
arr = [10, 20, 30]
|
||||
push(arr, 40)
|
||||
print(" After push(40): " + to_string(arr))
|
||||
removed = pop(arr)
|
||||
print(" Popped: " + to_string(removed) + ", arr: " + to_string(arr))
|
||||
insert(arr, 1, 15)
|
||||
print(" After insert(1, 15): " + to_string(arr))
|
||||
removed = remove(arr, 0)
|
||||
print(" Removed at 0: " + to_string(removed) + ", arr: " + to_string(arr))
|
||||
|
||||
// Search & utility
|
||||
print(" Contains 20? " + to_string(contains(arr, 20)))
|
||||
print(" Index of 20? " + to_string(indexOf(arr, 20)))
|
||||
print(" Enumerate: " + to_string(enumerate(["a", "b", "c"])))
|
||||
print(" Zip: " + to_string(zip([1, 2, 3], ["x", "y", "z"])))
|
||||
clear(arr)
|
||||
print(" After clear: " + to_string(arr))
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 3. Maps (Dictionaries)
|
||||
// 6. Maps (Dictionaries)
|
||||
// ============================================
|
||||
print("3. Map Operations:")
|
||||
print("6. Map Operations:")
|
||||
person = {"name": "Alice", "age": 30, "role": "Developer"}
|
||||
|
||||
print(" Person: " + to_string(person))
|
||||
print(" Has 'age' key: " + to_string(has(person, "age")))
|
||||
print(" Keys: " + to_string(keys(person)))
|
||||
print(" Values: " + to_string(values(person)))
|
||||
|
||||
// Bracket access and assignment
|
||||
print(" person[\"name\"]: " + person["name"])
|
||||
person["age"] = 31
|
||||
print(" Updated age: " + to_string(person["age"]))
|
||||
|
||||
// Object property access (dot notation)
|
||||
print(" person.name: " + person.name)
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 4. Object-Oriented Programming
|
||||
// 7. Object-Oriented Programming
|
||||
// ============================================
|
||||
print("4. Classes & Objects:")
|
||||
print("7. Classes & Objects:")
|
||||
|
||||
class Counter(number initial, string label)
|
||||
count = 0
|
||||
|
|
@ -78,9 +150,9 @@ counter.display()
|
|||
print("")
|
||||
|
||||
// ============================================
|
||||
// 5. Inheritance
|
||||
// 8. Inheritance
|
||||
// ============================================
|
||||
print("5. Inheritance:")
|
||||
print("8. Inheritance:")
|
||||
|
||||
class Animal(string type)
|
||||
species = ""
|
||||
|
|
@ -107,9 +179,9 @@ print(" Species: " + dog.species)
|
|||
print("")
|
||||
|
||||
// ============================================
|
||||
// 6. Error Handling
|
||||
// 9. Error Handling & Exceptions
|
||||
// ============================================
|
||||
print("6. Exception Handling:")
|
||||
print("9. Exception Handling:")
|
||||
|
||||
try
|
||||
print(" Attempting risky operation...")
|
||||
|
|
@ -122,51 +194,63 @@ finally
|
|||
print("")
|
||||
|
||||
// ============================================
|
||||
// 7. String Manipulation
|
||||
// 10. Comparison & Logical Operators
|
||||
// ============================================
|
||||
print("7. String Features:")
|
||||
string text = "Hello, Fun Language!"
|
||||
print(" Original: " + text)
|
||||
// Note: Using stdlib functions (assumed to exist in utils modules)
|
||||
// len, substr, find, split would come from stdlib
|
||||
print("10. Comparison & Logical Operators:")
|
||||
number a = 10
|
||||
number b = 20
|
||||
print(" a=10, b=20")
|
||||
print(" a < b: " + to_string(a < b))
|
||||
print(" a <= b: " + to_string(a <= b))
|
||||
print(" a > b: " + to_string(a > b))
|
||||
print(" a >= b: " + to_string(a >= b))
|
||||
print(" a == b: " + to_string(a == b))
|
||||
print(" a != b: " + to_string(a != b))
|
||||
print(" a < b && a > 0: " + to_string(a < b && a > 0))
|
||||
print(" a > b || a > 0: " + to_string(a > b || a > 0))
|
||||
print(" !true: " + to_string(!true))
|
||||
print(" Ternary (a < b ? \"yes\" : \"no\"): " + (a < b ? "yes" : "no"))
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 8. Mathematical Operations
|
||||
// 11. Control Flow: if/else if/else
|
||||
// ============================================
|
||||
print("8. Math Functions:")
|
||||
float x = 16.7
|
||||
print(" x = " + to_string(x))
|
||||
// Note: Math functions like sqrt, floor, ceil, abs, gcd, lcm
|
||||
// would come from stdlib <utils/math.fun> or similar
|
||||
print("11. If/Else If/Else:")
|
||||
number score = 85
|
||||
|
||||
if score >= 90
|
||||
print(" Grade: A")
|
||||
else if score >= 80
|
||||
print(" Grade: B")
|
||||
else if score >= 70
|
||||
print(" Grade: C")
|
||||
else
|
||||
print(" Grade: F")
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 9. Bitwise Operations
|
||||
// 12. Control Flow: Loops
|
||||
// ============================================
|
||||
print("9. Bitwise Operations:")
|
||||
number bits1 = 12
|
||||
number bits2 = 10
|
||||
print(" 12 & 10 = " + to_string(band(bits1, bits2)))
|
||||
print(" 12 | 10 = " + to_string(bor(bits1, bits2)))
|
||||
print(" 12 ^ 10 = " + to_string(bxor(bits1, bits2)))
|
||||
print(" 12 << 2 = " + to_string(shl(bits1, 2)))
|
||||
print(" ~12 = " + to_string(bnot(bits1)))
|
||||
print("")
|
||||
print("12. Loop Variants:")
|
||||
|
||||
// ============================================
|
||||
// 10. Control Flow
|
||||
// ============================================
|
||||
print("10. Control Flow:")
|
||||
// For-each with array
|
||||
print(" For-each array:")
|
||||
for item in ["apple", "banana", "cherry"]
|
||||
print(" " + item)
|
||||
|
||||
// For loop with array
|
||||
print(" Countdown:")
|
||||
for i in [5, 4, 3, 2, 1]
|
||||
print(" " + to_string(i) + "...")
|
||||
print(" Liftoff!")
|
||||
// For-range loop
|
||||
print(" For-range 0..4:")
|
||||
for i in range(0, 5)
|
||||
print(" " + to_string(i))
|
||||
|
||||
// For-map loop
|
||||
print(" For-map key, value:")
|
||||
config_map = {"a": 1, "b": 2, "c": 3}
|
||||
for (k, v) in config_map
|
||||
print(" " + k + " = " + to_string(v))
|
||||
|
||||
// While with break/continue
|
||||
print(" Skip evens:")
|
||||
print(" While with break/continue:")
|
||||
number n = 0
|
||||
while n < 10
|
||||
n = n + 1
|
||||
|
|
@ -178,24 +262,55 @@ while n < 10
|
|||
print("")
|
||||
|
||||
// ============================================
|
||||
// 11. Type Introspection
|
||||
// 13. Mathematical Operations
|
||||
// ============================================
|
||||
print("11. Type Introspection:")
|
||||
number check_int = 42
|
||||
string check_str = "hello"
|
||||
check_arr = [1, 2, 3]
|
||||
check_map = {"key": "value"}
|
||||
print("13. Math Functions:")
|
||||
number mx = -16
|
||||
print(" abs(-16) = " + to_string(abs(mx)))
|
||||
print(" min(10, 20) = " + to_string(min(10, 20)))
|
||||
print(" max(10, 20) = " + to_string(max(10, 20)))
|
||||
print(" clamp(50, 0, 10) = " + to_string(clamp(50, 0, 10)))
|
||||
print(" pow(2, 10) = " + to_string(pow(2, 10)))
|
||||
print(" sqrt(144) = " + to_string(sqrt(144)))
|
||||
print(" floor(3.7) = " + to_string(floor(3.7)))
|
||||
print(" ceil(3.2) = " + to_string(ceil(3.2)))
|
||||
print(" round(3.5) = " + to_string(round(3.5)))
|
||||
print(" trunc(3.9) = " + to_string(trunc(3.9)))
|
||||
print(" sin(0) = " + to_string(sin(0)))
|
||||
print(" cos(0) = " + to_string(cos(0)))
|
||||
print(" gcd(12, 8) = " + to_string(gcd(12, 8)))
|
||||
print(" lcm(12, 8) = " + to_string(lcm(12, 8)))
|
||||
print(" isqrt(50) = " + to_string(isqrt(50)))
|
||||
print(" sign(-42) = " + to_string(sign(-42)))
|
||||
print(" fmin(3.1, 2.9) = " + to_string(fmin(3.1, 2.9)))
|
||||
print(" fmax(3.1, 2.9) = " + to_string(fmax(3.1, 2.9)))
|
||||
|
||||
print(" typeof(42) = " + typeof(check_int))
|
||||
print(" typeof(\"hello\") = " + typeof(check_str))
|
||||
print(" typeof([1,2,3]) = " + typeof(check_arr))
|
||||
print(" typeof(map) = " + typeof(check_map))
|
||||
// Random (deterministic for demo)
|
||||
random_seed(42)
|
||||
print(" random_int(1, 100) = " + to_string(random_int(1, 100)))
|
||||
print(" random_number(8) = " + random_number(8))
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 12. Functional Programming
|
||||
// 14. Bitwise Operations
|
||||
// ============================================
|
||||
print("12. Higher-Order Functions:")
|
||||
print("14. Bitwise Operations:")
|
||||
number bits1 = 12 // 1100
|
||||
number bits2 = 10 // 1010
|
||||
print(" 12 & 10 = " + to_string(band(bits1, bits2)))
|
||||
print(" 12 | 10 = " + to_string(bor(bits1, bits2)))
|
||||
print(" 12 ^ 10 = " + to_string(bxor(bits1, bits2)))
|
||||
print(" ~12 = " + to_string(bnot(bits1)))
|
||||
print(" 12 << 2 = " + to_string(shl(bits1, 2)))
|
||||
print(" 12 >> 2 = " + to_string(shr(bits1, 2)))
|
||||
print(" rol(0x80000001, 1) = " + to_string(rol(0x80000001, 1)))
|
||||
print(" ror(0x80000001, 1) = " + to_string(ror(0x80000001, 1)))
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 15. Functional & Higher-Order Programming
|
||||
// ============================================
|
||||
print("15. Higher-Order Functions:")
|
||||
|
||||
fun double(n)
|
||||
return n * 2
|
||||
|
|
@ -208,28 +323,112 @@ print(" apply_twice(5, double) = " + to_string(result))
|
|||
print("")
|
||||
|
||||
// ============================================
|
||||
// 13. Array Operations with Spec Functions
|
||||
// 16. Array Higher-Order Functions
|
||||
// ============================================
|
||||
print("13. Array Higher-Order Functions:")
|
||||
print("16. Array Higher-Order Functions:")
|
||||
nums = [1, 2, 3, 4, 5]
|
||||
|
||||
fun square(x)
|
||||
return x * x
|
||||
|
||||
squared = map(nums, square)
|
||||
print(" Squared: " + to_string(squared))
|
||||
print(" map(nums, square): " + to_string(squared))
|
||||
|
||||
fun is_even(x)
|
||||
return x % 2 == 0
|
||||
|
||||
evens = filter(nums, is_even)
|
||||
print(" Evens: " + to_string(evens))
|
||||
print(" filter(nums, is_even): " + to_string(evens))
|
||||
|
||||
fun sum(acc, x)
|
||||
return acc + x
|
||||
|
||||
total = reduce(nums, 0, sum)
|
||||
print(" Sum: " + to_string(total))
|
||||
print(" reduce(nums, 0, sum): " + to_string(total))
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 17. File I/O
|
||||
// ============================================
|
||||
print("17. File I/O:")
|
||||
write_file("/tmp/fun_demo.txt", "Hello from Fun!")
|
||||
content = read_file("/tmp/fun_demo.txt")
|
||||
print(" Written and read back: " + content)
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 18. Environment & OS
|
||||
// ============================================
|
||||
print("18. Environment & OS:")
|
||||
print(" HOME: " + env("HOME"))
|
||||
print(" Version: " + fun_version())
|
||||
|
||||
// List directory (non-empty /tmp assumed)
|
||||
listing = os_list_dir("/tmp")
|
||||
print(" /tmp has " + to_string(len(listing)) + " entries")
|
||||
|
||||
// Run a command and capture output
|
||||
proc_result = proc_run("echo hello from fun")
|
||||
print(" proc_run output: " + proc_result["out"])
|
||||
|
||||
// System call exit code
|
||||
sys_code = system("true")
|
||||
print(" system(\"true\") exit: " + to_string(sys_code))
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 19. Date, Time & Sleep
|
||||
// ============================================
|
||||
print("19. Date, Time & Sleep:")
|
||||
now = time_now_ms()
|
||||
print(" Now (epoch ms): " + to_string(now))
|
||||
print(" Date formatted: " + date_format(now, "%Y-%m-%d %H:%M:%S"))
|
||||
|
||||
mono = clock_mono_ms()
|
||||
print(" Monotonic ms: " + to_string(mono))
|
||||
|
||||
// Short sleep to demonstrate
|
||||
sleep(10)
|
||||
after = clock_mono_ms()
|
||||
diff = after - mono
|
||||
print(" Slept 10 ms, elapsed: " + to_string(diff) + " ms")
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 20. Echo (print without newline)
|
||||
// ============================================
|
||||
print("20. Echo (no newline):")
|
||||
echo(" Hello, ")
|
||||
echo("world")
|
||||
print("!")
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 21. Threading
|
||||
// ============================================
|
||||
print("21. Threading:")
|
||||
|
||||
fun worker(id)
|
||||
print(" Thread " + to_string(id) + " says hi!")
|
||||
return id * 2
|
||||
|
||||
t1 = thread_spawn(worker, [1])
|
||||
t2 = thread_spawn(worker, [2])
|
||||
r1 = thread_join(t1)
|
||||
r2 = thread_join(t2)
|
||||
print(" Joined thread results: " + to_string(r1) + ", " + to_string(r2))
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
// 22. Type-Safe Integer Clamping
|
||||
// ============================================
|
||||
print("22. Integer Clamping:")
|
||||
byte val = 300
|
||||
print(" byte val = 300 -> clamped to " + to_string(val))
|
||||
int8 signed = 200
|
||||
print(" int8 val = 200 -> clamped to " + to_string(signed))
|
||||
uint16 big = 70000
|
||||
print(" uint16 val = 70000 -> clamped to " + to_string(big))
|
||||
print("")
|
||||
|
||||
// ============================================
|
||||
|
|
|
|||
2
make
2
make
|
|
@ -31,6 +31,7 @@ if [ "$target" = "all" ]; then
|
|||
-DFUN_WITH_CPP=ON \
|
||||
-DFUN_WITH_KCGI=ON \
|
||||
-DFUN_WITH_OPENSSL=ON \
|
||||
-DFUN_WITH_REDIS=ON \
|
||||
&& cmake --build build --target fun
|
||||
elif [ "$target" = "all_debug" ]; then
|
||||
rm -rf build \
|
||||
|
|
@ -45,6 +46,7 @@ elif [ "$target" = "all_debug" ]; then
|
|||
-DFUN_WITH_INI=ON \
|
||||
-DFUN_WITH_CPP=ON \
|
||||
-DFUN_WITH_OPENSSL=ON \
|
||||
-DFUN_WITH_REDIS=ON \
|
||||
-DFUN_DEBUG=ON \
|
||||
&& cmake --build build --target fun
|
||||
elif [ "$target" = "alpine" ]; then
|
||||
|
|
|
|||
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 n in {"open", "close", "exec", "query"}:
|
||||
return f"SQLITE_{n.upper()}"
|
||||
if d == "redis":
|
||||
if n in {"connect", "cmd", "close"}:
|
||||
return f"REDIS_{n.upper()}"
|
||||
if d == "pcsc":
|
||||
if n in {"establish", "release", "list_readers", "connect", "disconnect", "transmit"}:
|
||||
return f"PCSC_{n.upper()}"
|
||||
|
|
|
|||
|
|
@ -186,6 +186,11 @@ typedef enum {
|
|||
OP_SQLITE_EXEC, // pops sql, handle; pushes sqlite rc (0=OK)
|
||||
OP_SQLITE_QUERY, // pops sql, handle; pushes array<map>
|
||||
|
||||
// Redis (optional, hiredis)
|
||||
OP_REDIS_CONNECT, // pops port:int, host:string; pushes handle (>0) or 0
|
||||
OP_REDIS_CMD, // pops cmd:string, handle:int; pushes reply (string/int/array/nil/map)
|
||||
OP_REDIS_CLOSE, // pops handle:int; pushes Nil
|
||||
|
||||
// PCSC (smart card) opcodes
|
||||
OP_PCSC_ESTABLISH, // returns context id (>0) or 0
|
||||
OP_PCSC_RELEASE, // pops ctx id; returns 1/0
|
||||
|
|
|
|||
221
src/extensions/redis.c
Normal file
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);
|
||||
if (joined) {
|
||||
joined[0] = '\0';
|
||||
size_t off = 0;
|
||||
for (int i = 0; i < sargc; ++i) {
|
||||
strcat(joined, argv[sargi + i]);
|
||||
if (i + 1 < sargc) strcat(joined, " ");
|
||||
int written = snprintf(joined + off, (off < total ? total - off : 0),
|
||||
"%s%s",
|
||||
argv[sargi + i],
|
||||
(i + 1 < sargc) ? " " : "");
|
||||
if (written < 0) { /* encoding error */
|
||||
off = total; /* force stop */
|
||||
break;
|
||||
}
|
||||
size_t w = (size_t)written;
|
||||
if (off + w >= total) { /* ensure we don't advance beyond buffer */
|
||||
off = total ? total - 1 : 0;
|
||||
joined[off] = '\0';
|
||||
break;
|
||||
}
|
||||
off += w;
|
||||
}
|
||||
/* Ensure NUL termination even if loop didn't run */
|
||||
if (total > 0) joined[(off < total) ? off : (total - 1)] = '\0';
|
||||
setenv("FUN_ARGS", joined, 1);
|
||||
free(joined);
|
||||
}
|
||||
|
|
|
|||
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);
|
||||
return 1;
|
||||
}
|
||||
/* Redis builtins (hiredis) */
|
||||
if (strcmp(name, "redis_connect") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) {
|
||||
parser_fail(*pos, "redis_connect expects (host, port)");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
if (!consume_char(src, len, pos, ',')) {
|
||||
parser_fail(*pos, "redis_connect expects (host, port)");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
if (!emit_expression(bc, src, len, pos)) {
|
||||
parser_fail(*pos, "redis_connect expects (host, port)");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
if (!consume_char(src, len, pos, ')')) {
|
||||
parser_fail(*pos, "Expected ')' after redis_connect args");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
bytecode_add_instruction(bc, OP_REDIS_CONNECT, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "redis_cmd") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) {
|
||||
parser_fail(*pos, "redis_cmd expects (handle, cmd)");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
if (!consume_char(src, len, pos, ',')) {
|
||||
parser_fail(*pos, "redis_cmd expects (handle, cmd)");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
if (!emit_expression(bc, src, len, pos)) {
|
||||
parser_fail(*pos, "redis_cmd expects (handle, cmd)");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
if (!consume_char(src, len, pos, ')')) {
|
||||
parser_fail(*pos, "Expected ')' after redis_cmd args");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
bytecode_add_instruction(bc, OP_REDIS_CMD, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "redis_close") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) {
|
||||
parser_fail(*pos, "redis_close expects (handle)");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
if (!consume_char(src, len, pos, ')')) {
|
||||
parser_fail(*pos, "Expected ')' after redis_close arg");
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
bytecode_add_instruction(bc, OP_REDIS_CLOSE, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "sqlite_close") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) {
|
||||
|
|
|
|||
|
|
@ -259,14 +259,16 @@ static int complete_load_path(char *buf, size_t *len_io) {
|
|||
if (slash) {
|
||||
size_t dlen = (size_t)(slash - expanded);
|
||||
if (dlen == 0) {
|
||||
strcpy(dirpart, "/");
|
||||
/* use bounded copy to avoid potential overflow (even though "/" fits) */
|
||||
snprintf(dirpart, sizeof(dirpart), "%s", "/");
|
||||
} else {
|
||||
memcpy(dirpart, expanded, dlen);
|
||||
dirpart[dlen] = '\0';
|
||||
}
|
||||
snprintf(base, sizeof(base), "%s", slash + 1);
|
||||
} else {
|
||||
strcpy(dirpart, ".");
|
||||
/* use bounded copy to avoid potential overflow (even though "." fits) */
|
||||
snprintf(dirpart, sizeof(dirpart), "%s", ".");
|
||||
snprintf(base, sizeof(base), "%s", expanded);
|
||||
}
|
||||
|
||||
|
|
|
|||
8
src/vm.c
8
src/vm.c
|
|
@ -73,6 +73,7 @@
|
|||
#include "extensions/sqlite.c"
|
||||
#include "extensions/xml2.c"
|
||||
#include "extensions/kcgi.c"
|
||||
#include "extensions/redis.c"
|
||||
|
||||
/* forward declarations for include mapping used in error reporting */
|
||||
extern char *preprocess_includes(const char *src);
|
||||
|
|
@ -1230,6 +1231,13 @@ void vm_run(VM *vm, Bytecode *entry) {
|
|||
#include "vm/sqlite/query.c"
|
||||
#endif
|
||||
|
||||
/* Redis ops */
|
||||
#ifdef FUN_WITH_REDIS
|
||||
#include "vm/redis/connect.c"
|
||||
#include "vm/redis/cmd.c"
|
||||
#include "vm/redis/close.c"
|
||||
#endif
|
||||
|
||||
/* C++ demo opcodes (guarded) */
|
||||
#ifdef FUN_WITH_CPP
|
||||
case OP_CPP_ADD: {
|
||||
|
|
|
|||
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",
|
||||
"CURL_GET", "CURL_POST", "CURL_DOWNLOAD",
|
||||
"SQLITE_OPEN", "SQLITE_CLOSE", "SQLITE_EXEC", "SQLITE_QUERY",
|
||||
/* Redis (hiredis) */
|
||||
"REDIS_CONNECT", "REDIS_CMD", "REDIS_CLOSE",
|
||||
"LIBSQL_OPEN", "LIBSQL_CLOSE", "LIBSQL_EXEC", "LIBSQL_QUERY",
|
||||
"PCSC_ESTABLISH", "PCSC_RELEASE", "PCSC_LIST_READERS", "PCSC_CONNECT", "PCSC_DISCONNECT", "PCSC_TRANSMIT",
|
||||
"PCRE2_TEST", "PCRE2_MATCH", "PCRE2_FINDALL",
|
||||
|
|
|
|||
|
|
@ -27,6 +27,24 @@ case OP_PROC_SYSTEM: {
|
|||
push_value(vm, make_int(-1));
|
||||
break;
|
||||
}
|
||||
/* Security hardening: reject commands containing shell metacharacters or control chars
|
||||
to reduce risk of command injection when using system(3). This preserves simple
|
||||
command execution like "ls -l" but blocks dangerous constructs like pipes, redirects,
|
||||
command substitution, etc. */
|
||||
const char *bad = "&;|$<>`\\\"'()*?[]{}~";
|
||||
int unsafe = 0;
|
||||
for (const unsigned char *p = (const unsigned char *)cmd; *p; ++p) {
|
||||
if (*p < 0x20 || strchr(bad, (int)*p)) { /* control or meta */
|
||||
unsafe = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (unsafe) {
|
||||
/* refuse to execute potentially unsafe shell command */
|
||||
push_value(vm, make_int(-1));
|
||||
free(cmd);
|
||||
break;
|
||||
}
|
||||
int status = system(cmd);
|
||||
int code = -1;
|
||||
#ifdef __unix__
|
||||
|
|
|
|||
45
src/vm/redis/close.c
Normal file
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>-->
|
||||
<h2 style="display:none;">Navigation</h2>
|
||||
<ul style="list-style:none;margin-left:0px;text-align:center;">
|
||||
<li style="display:inline;"><a href="/" style="text-decoration:none;" title="Homepage" class="home">Homepage</a> -</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="/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>
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@
|
|||
<div class="trigger">
|
||||
<h2 style="display:none;">Navigation</h2>
|
||||
<ul style="list-style: none;">
|
||||
<li><a class="page-link" href="/" style="text-decoration:none;" title="Homepage">/</a></li>
|
||||
<li><a class="page-link" href="/" style="text-decoration:none;" title="Home">Home</a></li>
|
||||
<li><a class="page-link" href="/about/" style="text-decoration:none;" title="About">About</a></li>
|
||||
<li><a class="page-link" href="/features/" style="text-decoration:none;" title="Features">Features</a></li>
|
||||
<li><a class="page-link" href="/documentation/" style="text-decoration:none;" title="Documentation">Documentation</a></li>
|
||||
<li><a class="page-link" href="/download/" style="text-decoration:none;" title="Download">Download</a></li>
|
||||
<li><a class="page-link" href="/community/" style="text-decoration:none;" title="Community">Community</a></li>
|
||||
<li><a class="page-link" href="/contact/" style="text-decoration:none;" title="Contact">Contact</a></li>
|
||||
<li><a class="page-link" href="/documentation/" style="text-decoration:none;" title="Documentation">Documentation</a></li>
|
||||
<li><a class="page-link" href="/faq/" style="text-decoration:none;" title="FAQ">FAQ</a></li>
|
||||
<li><a class="page-link" href="/features/" style="text-decoration:none;" title="FAQ">Features</a></li>
|
||||
<li><a class="page-link" href="/download/" style="text-decoration:none;" title="Download">Download</a></li>
|
||||
<!--
|
||||
https://www.w3schools.com/howto/howto_js_toggle_dark_mode.asp
|
||||
https://dev.to/warish/how-to-create-a-dark-mode-toggle-with-html-css-and-javascript-378m
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ layout: default
|
|||
|
||||
<div class="post">
|
||||
<header class="post-header">
|
||||
<h1 class="post-title" id="terminal">{{ site.title }} {{ page.title }}</h1>
|
||||
<h1 class="post-title" id="NOterminal">{{ site.title }} {{ page.title }}</h1>
|
||||
{% if page.noDate != true %}<p class="post-meta" style="padding: 0;">{% if page.date %}{{ page.date | date: "%b %-d, %Y" }} - {% endif %}
|
||||
Edited: {{ page.path | file_date | date: "%b %-d, %Y" }}{% endif %}{% if page.author %}, <a href="{{ site.git_short }}/{{ page.author }}" target="_blank" class="user">{{ page.author }}</a>{% endif %}</p>
|
||||
{% if page.subtitle %}<p style="padding: 0px 0px 0px 0px;">{{ page.subtitle }}</p>{% endif %}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ layout: default
|
|||
|
||||
<div class="post">
|
||||
<header class="post-header">
|
||||
<h1 class="post-title" id="terminal">{{ site.title }} {{ page.title }}</h1>
|
||||
<h1 class="post-title" id="NOterminal">{{ site.title }} {{ page.title }}</h1>
|
||||
<p class="post-meta" style="padding: 0px 0px 0px 0px;">{% if page.date %}{{ page.date | date: "%b %-d, %Y" }}{% endif %}{% if page.date and page.date_updated %} - {% endif %}{% if page.date_updated %}Edited: {{ page.date_updated | date: "%b %-d, %Y" }}{% endif %}{% if page.author %}, <a href="{{ page.author_url }}" target="_blank" class="user">{{ page.author }}</a>{% endif %}</p>
|
||||
</header>
|
||||
<article class="post-content">
|
||||
|
|
|
|||
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
|
||||
_date: 2023-09-20
|
||||
metasub: news
|
||||
noToc: true
|
||||
noToc: false
|
||||
noDate: false
|
||||
tags:
|
||||
- fun
|
||||
|
|
@ -19,14 +19,18 @@ permalink: /about/
|
|||
|
||||
## What is Fun?
|
||||
|
||||
Fun is an experiment, just for fun, but Fun works!
|
||||
Fun is a small, strict, and simple programming language that runs on a compact stack-based virtual machine. The C core is intentionally minimal; most functionality and standard libraries are implemented in Fun itself.
|
||||
|
||||
Fun is a highly strict programming language, but also highly simple. It looks like Python (My favorite language), but there are differences.
|
||||
Fun is dynamically typed with optional static type annotations, <a href="/features/">featuring first-class functions</a>, classes with inheritance, pattern matching, and a rich standard library. It supports everything from basic scripting to TCP sockets, serial communication, threading, cryptography (MD5, SHA-1/256/384/512, CRC-32, AES-256), and a built-in debugger.
|
||||
|
||||
Influenced by **[Bash](https://www.gnu.org/software/bash/){:class="ext"}**, **[C](https://en.wikipedia.org/wiki/The_C_Programming_Language){:class="ext"}**, **[Lua](https://www.lua.org/){:class="ext"}**, PHP, **[Python](https://www.python.org/){:class="ext"}**, and a little Rust (Most influences came from linked languages).
|
||||
Influenced by: **[Bash](https://www.gnu.org/software/bash/){:class="ext"}**, **[C](https://en.wikipedia.org/wiki/The_C_Programming_Language){:class="ext"}**, **[Lua](https://www.lua.org/){:class="ext"}**, **[PHP](https://www.php.net/){:class="ext"}**, **[Python](https://www.python.org/){:class="ext"}**, and **[Rust](https://www.rust-lang.org/){:class="ext"}**.
|
||||
|
||||
Fun is an experiment - just for fun - but Fun works!
|
||||
|
||||
Fun is and will ever be 100% free under the terms of the [Apache-2.0 License](https://opensource.org/license/apache-2-0){:class="ext"}.
|
||||
|
||||
---
|
||||
|
||||
## Idea
|
||||
|
||||
- Simplicity
|
||||
|
|
@ -38,11 +42,19 @@ Fun is and will ever be 100% free under the terms of the [Apache-2.0 License](ht
|
|||
|
||||
## Characteristics
|
||||
|
||||
- Dynamic and optionally statically typed
|
||||
- Type safety
|
||||
- Written in C (C99) and Fun
|
||||
- Internal libs are written with no_camel_case even when written in Fun, except class names
|
||||
- Only a minimal function set is written in C, and most other core functions and libraries are implemented in Fun
|
||||
- **Dynamic typing** with optional **static type annotations** (`number`, `string`, `boolean`, `float`, `byte`, `uint8`–`uint64`, `int8`–`int64`)
|
||||
- **Stack-based bytecode VM** written in C99 with ~220 opcodes
|
||||
- **First-class functions**, anonymous functions (`fn`), and higher-order operations (`map`, `filter`, `reduce`)
|
||||
- **Classes** with constructors, methods, and single inheritance
|
||||
- **Exception handling** with `try`/`catch`/`finally`
|
||||
- **Built-in data structures**: arrays (with slicing), maps/objects, strings
|
||||
- **Concurrency**: threads (`thread_spawn`, `thread_join`) and cooperative async scheduler
|
||||
- **Networking**: TCP and Unix domain sockets with non-blocking I/O polling
|
||||
- **Serial communication**: full termios-based serial port control
|
||||
- **Cryptography**: pure-Fun implementations of MD5, SHA-1/256/384/512, CRC-32/CRC-32C, AES-256 ECB
|
||||
- **Built-in debugger** with breakpoints, step/next/finish, and stack inspection
|
||||
- **Minimal C core** — most standard libraries are implemented in Fun itself
|
||||
- **Internal style**: `snake_case` for functions and variables, `CamelCase` for class names
|
||||
|
||||
## The Fun Manifesto
|
||||
|
||||
|
|
@ -60,13 +72,13 @@ Coding should be enjoyable, elegant, and consistent.
|
|||
- **One Way to Do It**<br>
|
||||
No clutter, no 15 ways of writing the same thing. Simplicity means clarity.
|
||||
- **Hackable by Nature**<br>
|
||||
Fun should be small and embeddable, like Lua. Easy to understand, extend, and tinker with — true to the hacker spirit.
|
||||
Fun should be small and embeddable, like Lua. Easy to understand, extend, and tinker with — true to the hacker spirit.
|
||||
- **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
|
||||
|
||||
Fun is not about being the fastest or the most feature-rich. It’s about sharing joy in coding. The community should be:
|
||||
Fun is not about being the fastest or the most feature-rich. It's about sharing joy in coding. The community should be:
|
||||
|
||||
- Respectful
|
||||
- Curious
|
||||
|
|
@ -84,41 +96,153 @@ A language that feels like home for developers who:
|
|||
- Believe consistency is freedom
|
||||
- Want to write code that looks good and feels good
|
||||
|
||||
Fun may not change the world — but it will make programming a little more fun.
|
||||
Fun may not change the world — 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
|
||||
- if/else if/else
|
||||
- try/catch/finally
|
||||
### Operators
|
||||
- Arithmetic: `+`, `-`, `*`, `/`, `%`
|
||||
- Comparison: `<`, `<=`, `>`, `>=`, `==`, `!=`
|
||||
- Logical: `&&`, `||`, `!` (short-circuit)
|
||||
- Bitwise: `band()`, `bor()`, `bxor()`, `bnot()`, `shl()`, `shr()`, `rol()`, `ror()`
|
||||
- Ternary: `condition ? true_expr : false_expr`
|
||||
|
||||
and much more...
|
||||
### Control Flow
|
||||
- `if` / `else if` / `else`
|
||||
- `while` with `break` and `continue`
|
||||
- `for var in array` — 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>
|
||||
- [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>
|
||||
- [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>
|
||||
- [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>
|
||||
- [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>
|
||||
### Mathematics
|
||||
- Built-in: `abs`, `min`, `max`, `fmin`, `fmax`, `clamp`, `pow`, `sqrt`, `floor`, `ceil`, `trunc`, `round`, `sin`, `cos`, `tan`, `exp`, `log`, `log10`, `gcd`, `lcm`, `isqrt`, `sign`
|
||||
- Random: `random_seed()`, `random_int()`, `random_number()` (cryptographic)
|
||||
- Integer clamping: `sclamp()`, `uclamp()`
|
||||
|
||||
<span style="color:green;font-weight:bold;">☑</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
|
||||
|
||||
- [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
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Arrays in Fun
|
||||
title: Arrays in Fun
|
||||
subtitle: Working with arrays, creation, indexing/slicing, iteration patterns, helpers, and idioms.
|
||||
description: Working with arrays, creation, indexing/slicing, iteration patterns, helpers, and idioms.
|
||||
permalink: /documentation/arrays/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Async I/O ("asyncio") in Fun
|
||||
title: Async I/O ("asyncio") in Fun
|
||||
subtitle: Async I/O primitives and patterns, non-blocking sockets, fd polling, examples, and best practices.
|
||||
description: Async I/O primitives and patterns, non-blocking sockets, fd polling, examples, and best practices.
|
||||
permalink: /documentation/asyncio/
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ noToc: false
|
|||
noComments: false
|
||||
noDate: false
|
||||
title: Building Fun
|
||||
subtitle: How to build Fun with CMake, available targets, and build options (FUN_DEBUG, FUN_USE_MUSL, FUN_WITH_CPP, FUN_WITH_RUST, FUN_WITH_OPENSSL).
|
||||
description: How to build Fun with CMake, available targets, and build options (FUN_DEBUG, FUN_USE_MUSL, FUN_WITH_CPP, FUN_WITH_RUST, FUN_WITH_OPENSSL).
|
||||
subtitle: How to build Fun with CMake, available targets, and build options (FUN_DEBUG, FUN_USE_MUSL, FUN_WITH_CPP, FUN_WITH_RUST, FUN_WITH_OPENSSL, FUN_WITH_REDIS).
|
||||
description: How to build Fun with CMake, available targets, and build options (FUN_DEBUG, FUN_USE_MUSL, FUN_WITH_CPP, FUN_WITH_RUST, FUN_WITH_OPENSSL, FUN_WITH_REDIS).
|
||||
permalink: /documentation/build/
|
||||
lang: en
|
||||
tags:
|
||||
|
|
@ -50,6 +50,7 @@ Fun exposes several options you can toggle at configure time:
|
|||
- `FUN_WITH_CPP` (ON/OFF) - Enable C++-based opcode/examples support
|
||||
- `FUN_WITH_RUST` (ON/OFF) - Build and link Rust staticlib from `src/rust/`
|
||||
- `FUN_WITH_OPENSSL` (ON/OFF) - Enable OpenSSL-backed helpers (MD5/SHA-256/SHA-512/RIPEMD-160)
|
||||
- `FUN_WITH_REDIS` (ON/OFF) - Enable Redis extension powered by hiredis (sync API: connect/cmd/close)
|
||||
|
||||
### VM configuration constants
|
||||
|
||||
|
|
@ -72,6 +73,8 @@ See [VM](../vm/) for more information.
|
|||
FUN_USE_MUSL: ENABLED|DISABLED
|
||||
FUN_WITH_CPP: ENABLED|DISABLED
|
||||
FUN_WITH_RUST: ENABLED|DISABLED
|
||||
FUN_WITH_OPENSSL: ENABLED|DISABLED
|
||||
FUN_WITH_REDIS: ENABLED|DISABLED
|
||||
===========================</pre>
|
||||
|
||||
## Example commands
|
||||
|
|
@ -92,7 +95,7 @@ cmake --build build_release --target build</pre>
|
|||
### Enabling optional extensions
|
||||
|
||||
<pre>cmake -S . -B build_release -DCMAKE_BUILD_TYPE=Release \
|
||||
-DFUN_WITH_CPP=ON -DFUN_WITH_RUST=ON -DFUN_WITH_OPENSSL=ON
|
||||
-DFUN_WITH_CPP=ON -DFUN_WITH_RUST=ON -DFUN_WITH_OPENSSL=ON -DFUN_WITH_REDIS=ON
|
||||
cmake --build build_release --target build</pre>
|
||||
|
||||
### Customizing VM limits
|
||||
|
|
@ -104,6 +107,8 @@ If `FUN_WITH_RUST` is enabled, ensure `cargo` is available in PATH; the build wi
|
|||
|
||||
If `FUN_WITH_OPENSSL` is enabled, CMake must detect your system OpenSSL (libcrypto).
|
||||
|
||||
If `FUN_WITH_REDIS` is enabled, ensure `hiredis` headers and library are installed and discoverable (typically via pkg-config).
|
||||
|
||||
## Running
|
||||
- CLI: run the `fun` executable from your build directory.
|
||||
- REPL: `fun -i` or just run `fun` without a script, depending on your CLI version (see [CLI](../cli/)).
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Bytecode Format (Overview)
|
||||
title: Bytecode Format (Overview)
|
||||
subtitle: Reference for the bytecode format (split out from internals for convenience).
|
||||
description: Reference for the bytecode format (split out from internals for convenience).
|
||||
permalink: /documentation/bytecode-format/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Fun CLI
|
||||
title: Fun CLI
|
||||
subtitle: Command-line usage of the `fun` executable, synopsis, options, exit codes, includes and library paths.
|
||||
description: Command-line usage of the `fun` executable, synopsis, options, exit codes, includes and library paths.
|
||||
permalink: /documentation/cli/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Contributing to Fun
|
||||
title: Contributing to Fun
|
||||
subtitle: How to contribute, project structure, coding style, running tests, and PR guidelines.
|
||||
description: How to contribute, project structure, coding style, running tests, and PR guidelines.
|
||||
permalink: /documentation/contributing/
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
---
|
||||
layout: page
|
||||
published: true
|
||||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Documentation
|
||||
subtitle: Detailed documentation for the Fun programming language.<br><br><span style="color:red;">The documentation is always a work in progress! It will always be behind the development of the code. It will be 100% aligned with a 1.0 release... ;)</span>
|
||||
subtitle: Detailed documentation for the Fun programming language.
|
||||
description: The Fun Documentation Index
|
||||
permalink: /documentation/
|
||||
lang: en
|
||||
tags:
|
||||
- documentation
|
||||
- index
|
||||
- fun
|
||||
- language
|
||||
- programming
|
||||
- api
|
||||
---
|
||||
|
||||
> **Note:** The documentation is always a work in progress and may lag behind the code. It will be fully aligned with a 1.0 release.
|
||||
|
||||
This file serves as an index of the documents in this directory. Links are relative and can be opened directly on Git hosting or locally.
|
||||
|
||||
## Basics
|
||||
|
|
@ -77,7 +78,7 @@ The examples directory contains demonstrations of most Fun features, from basic
|
|||
Documentation for optional, build-time selectable integrations lives in [./extensions/](./extensions/):
|
||||
|
||||
- [Index of extensions](./extensions/)
|
||||
- Highlights: [cURL](./extensions/curl/), [INI](./extensions/ini/), [JSON](./extensions/json/), [XML (libxml2)](./extensions/xml2/), [SQLite](./extensions/sqlite/), [PCRE2](./extensions/pcre2/), [PC/SC](./extensions/pcsc/), [OpenSSL](./extensions/openssl/)
|
||||
- Highlights: [cURL](./extensions/curl/), [INI](./extensions/ini/), [JSON](./extensions/json/), [XML (libxml2)](./extensions/xml2/), [SQLite](./extensions/sqlite/), [PCRE2](./extensions/pcre2/), [PC/SC](./extensions/pcsc/), [OpenSSL](./extensions/openssl/), [Redis](./extensions/redis/)
|
||||
|
||||
## Tips
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Embedding Fun
|
||||
title: Embedding Fun
|
||||
subtitle: Embedding the VM from C/Rust, lifecycle, and host integration tips.
|
||||
description: Embedding the VM from C/Rust, lifecycle, and host integration tips.
|
||||
permalink: /documentation/embedding/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Errors and Diagnostics
|
||||
title: Errors and Diagnostics
|
||||
subtitle: Understanding parser/runtime errors and enabling diagnostics.
|
||||
description: Understanding parser/runtime errors and enabling diagnostics.
|
||||
permalink: /documentation/errors-and-diagnostics/
|
||||
|
|
|
|||
|
|
@ -27,21 +27,21 @@ All commands assume you are in the repository root.
|
|||
|
||||
## 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.
|
||||
|
||||
Example (Linux/macOS/BSD):
|
||||
### Linux/macOS/BSD:
|
||||
|
||||
<pre>FUN_LIB_DIR="$(pwd)/lib" ./build/fun examples/include_lib.fun</pre>
|
||||
<pre>FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/include_lib.fun</pre>
|
||||
|
||||
Windows (PowerShell):
|
||||
### Windows (PowerShell):
|
||||
|
||||
<pre>$env:FUN_LIB_DIR = "$PWD/lib"
|
||||
./build/fun.exe .\examples\include_lib.fun</pre>
|
||||
|
||||
## Interactive showcase: play.fun
|
||||
|
||||
The script `./scripts/play.fun` discovers all `.fun` files under `./examples` and offers to run them one by one:
|
||||
The script `./scripts/play.fun` discovers all `.fun` files in `./examples` and offers to run them one by one:
|
||||
|
||||
<pre>./scripts/play.fun</pre>
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ Notes:
|
|||
|
||||
Tip: you can run specific examples directly too:
|
||||
|
||||
<pre>FUN_LIB_DIR="$(pwd)/lib" fun examples/crypto/openssl_md5.fun</pre>
|
||||
<pre>FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/extensions/openssl/openssl_md5.fun</pre>
|
||||
|
||||
## Example categories
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - cURL (libcurl) extension (optional)
|
||||
title: cURL (libcurl) extension (optional)
|
||||
subtitle: Documentation for cURL (libcurl) extension (optional)
|
||||
description: Documentation for cURL (libcurl) extension (optional)
|
||||
permalink: /documentation/extensions/curl/
|
||||
|
|
@ -20,13 +20,14 @@ tags:
|
|||
- Purpose: HTTP helpers using libcurl.
|
||||
- Homepage: [https://curl.se/libcurl/](https://curl.se/libcurl/){:class="ext"}
|
||||
|
||||
## Opcodes:
|
||||
## Opcodes
|
||||
|
||||
- OP_CURL_GET: GET; pops url:string; pushes body:string (empty on error/disabled)
|
||||
- OP_CURL_POST: POST; pops body:string, url:string; pushes response:string
|
||||
- OP_CURL_DOWNLOAD: Download to file; pops path:string, url:string; pushes 1/0
|
||||
|
||||
## Notes:
|
||||
## Notes
|
||||
|
||||
- Requires libcurl development headers/libs.
|
||||
- When disabled, helpers return empty strings/0 to match optional behavior.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Optional extensions
|
||||
title: Optional extensions
|
||||
subtitle: Catalog of optional, build-time selectable extensions for the Fun VM (e.g., cURL, SQLite, JSON).
|
||||
description: Catalog of optional, build-time selectable extensions for the Fun VM (e.g., cURL, SQLite, JSON).
|
||||
permalink: /documentation/extensions/
|
||||
|
|
@ -48,6 +48,7 @@ This section documents Fun's optional, build-time selectable extensions. Each pa
|
|||
- [PC/SC (Smart cards)](./pcsc/)
|
||||
- [OpenSSL](./openssl/)
|
||||
- [kcgi](./kcgi/)
|
||||
- [Redis (hiredis)](./redis/)
|
||||
|
||||
## Notes:
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - INI (iniparser) extension (optional)
|
||||
title: INI (iniparser) extension (optional)
|
||||
subtitle: Documentation for INI (iniparser) extension (optional)
|
||||
description: Documentation for INI (iniparser) extension (optional)
|
||||
permalink: /documentation/extensions/ini/
|
||||
|
|
@ -20,7 +20,7 @@ tags:
|
|||
- Purpose: Read/write simple INI configuration files.
|
||||
- Homepage: [https://github.com/ndevilla/iniparser](https://github.com/ndevilla/iniparser){:class="ext"}
|
||||
|
||||
## Opcodes:
|
||||
## Opcodes
|
||||
|
||||
- OP_INI_LOAD: pops path; pushes handle (>0) or 0
|
||||
- OP_INI_FREE: pops handle; pushes 1/0
|
||||
|
|
@ -32,7 +32,8 @@ tags:
|
|||
- OP_INI_UNSET: pops key, section, handle; pushes 1/0
|
||||
- OP_INI_SAVE: pops path, handle; pushes 1/0
|
||||
|
||||
## Notes:
|
||||
## Notes
|
||||
|
||||
- Requires iniparser development headers/libs.
|
||||
- When disabled, helpers return neutral values (0/empty strings) like other optional extensions.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - JSON (json-c) extension (optional)
|
||||
title: JSON (json-c) extension (optional)
|
||||
subtitle: Documentation for JSON (json-c) extension (optional)
|
||||
description: Documentation for JSON (json-c) extension (optional)
|
||||
permalink: /documentation/extensions/json/
|
||||
|
|
@ -19,14 +19,15 @@ tags:
|
|||
- Purpose: JSON parse/stringify and file helpers via json-c.
|
||||
- Homepage: [https://json-c.github.io/json-c/](https://json-c.github.io/json-c/){:class="ext"}
|
||||
|
||||
## Opcodes:
|
||||
## Opcodes
|
||||
|
||||
- OP_JSON_PARSE: pops text; pushes value or Nil on error
|
||||
- OP_JSON_STRINGIFY: pops pretty:int(0/1), value; pushes string
|
||||
- OP_JSON_FROM_FILE: pops path; pushes value or Nil
|
||||
- OP_JSON_TO_FILE: pops pretty:int(0/1), value, path; pushes 1/0
|
||||
|
||||
## Notes:
|
||||
## Notes
|
||||
|
||||
- Requires json-c development headers/libs.
|
||||
- When disabled, functions push empty/neutral values similar to other optional modules.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - KCGI extension (optional)
|
||||
title: KCGI extension (optional)
|
||||
subtitle: Documentation for KCGI (kcgi) extension (optional)
|
||||
description: Documentation for KCGI (kcgi) extension (optional)
|
||||
permalink: /documentation/extensions/kcgi/
|
||||
|
|
@ -18,12 +18,12 @@ tags:
|
|||
- Purpose: integrate the kcgi (CGI/FastCGI) C library; provide request parsing and response helpers for building CGI apps in Fun.
|
||||
- Homepage: [https://kristaps.bsd.lv/kcgi/](https://kristaps.bsd.lv/kcgi/){:class="ext"}
|
||||
|
||||
## Build notes:
|
||||
## Build notes
|
||||
|
||||
- Requires system kcgi development headers and libraries (pkg-config name: kcgi).
|
||||
- If pkg-config is not available, build falls back to linking against libkcgi and zlib (as configured in cmake/Extensions/KCGI.cmake).
|
||||
|
||||
## Provided helper/opcodes:
|
||||
## Provided helper/opcodes
|
||||
|
||||
- Function: kcgi_parse(data: none) -> Map | Nil. Parses the current CGI/FastCGI request via kcgi and returns a Map with keys like method, scheme, host, port, path, suffix, query, and fields (GET/POST map). Returns Nil on failure or when the extension is disabled.
|
||||
- Function: kcgi_reply_start(code:int, content_type:string) -> 1/0. Starts the HTTP reply (sets Content-Type and opens the body).
|
||||
|
|
@ -31,19 +31,20 @@ tags:
|
|||
- Function: kcgi_end() -> 1/0. Finalizes the response and frees request resources.
|
||||
- Opcodes: OP_KCGI_PARSE, OP_KCGI_REPLY_START, OP_KCGI_WRITE, OP_KCGI_END (internal mappings for the functions above).
|
||||
|
||||
## Quickstart:
|
||||
## Quickstart
|
||||
|
||||
- Configure: cmake -S . -B build -DFUN_WITH_KCGI=ON
|
||||
- Build: cmake --build build --target fun
|
||||
- Run example:
|
||||
- FUN_LIB_DIR="$(pwd)/lib" ./build/fun examples/cgi/hello_kcgi.fun
|
||||
|
||||
## Example output:
|
||||
## Example output
|
||||
|
||||
- Content-Type: text/html; charset=utf-8
|
||||
- <h1>Hello, Fun!</h1>
|
||||
|
||||
## Notes:
|
||||
## Notes
|
||||
|
||||
- Running outside a real CGI/FastCGI environment may emit RFC warnings (e.g., missing REMOTE_ADDR); these are benign for local testing.
|
||||
- When FUN_WITH_KCGI is OFF, the helpers behave as no-ops (returning Nil/0) to keep scripts portable.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - OpenSSL extension (optional)
|
||||
title: OpenSSL extension (optional)
|
||||
subtitle: Documentation for OpenSSL extension (optional)
|
||||
description: Documentation for OpenSSL extension (optional)
|
||||
permalink: /documentation/extensions/openssl/
|
||||
|
|
@ -19,12 +19,12 @@ tags:
|
|||
- Purpose: provide small crypto helpers backed by OpenSSL. Includes md5, sha256, sha512, ripemd160 helpers.
|
||||
- Homepage: [https://www.openssl.org/](https://www.openssl.org/){:class="ext"}
|
||||
|
||||
## Build notes:
|
||||
## Build notes
|
||||
|
||||
- Requires system OpenSSL development headers and libraries.
|
||||
- On OpenSSL 3.x, legacy MD5_* APIs are deprecated; you may see warnings during build.
|
||||
|
||||
## Provided helper/opcodes:
|
||||
## Provided helper/opcodes
|
||||
|
||||
- Function: openssl_md5(data:string) -> string (lowercase hex). Falls back to empty string when the extension is disabled, mirroring other optional modules.
|
||||
- Function: openssl_sha256(data:string) -> string (lowercase hex).
|
||||
|
|
@ -32,7 +32,7 @@ tags:
|
|||
- Function: openssl_ripemd160(data:string) -> string (lowercase hex). Note: On OpenSSL 3.x this may require the legacy provider; if the digest is unavailable, the helper returns an empty string.
|
||||
- Opcodes: OP_OPENSSL_MD5, OP_OPENSSL_SHA256, OP_OPENSSL_SHA512, OP_OPENSSL_RIPEMD160 (internal mappings for the functions above).
|
||||
|
||||
## Quickstart:
|
||||
## Quickstart
|
||||
|
||||
- Configure: cmake -S . -B build -DFUN_WITH_OPENSSL=ON
|
||||
- Build: cmake --build build --target fun
|
||||
|
|
@ -42,7 +42,7 @@ tags:
|
|||
- ./build/fun examples/crypto/openssl_sha512.fun
|
||||
- ./build/fun examples/crypto/openssl_ripemd160.fun
|
||||
|
||||
## Example output:
|
||||
## Example output
|
||||
|
||||
- md5(abc) = 900150983cd24fb0d6963f7d28e17f72
|
||||
- md5("") = d41d8cd98f00b204e9800998ecf8427e
|
||||
|
|
@ -53,6 +53,7 @@ tags:
|
|||
- ripemd160(abc) = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc
|
||||
- ripemd160("") = 9c1185a5c5e9fc54612808977ee8f548b2258d31
|
||||
|
||||
## Notes:
|
||||
## Notes
|
||||
|
||||
- The OpenSSL 3.x provider configuration on your system determines availability of RIPEMD-160. If the legacy provider is not enabled, openssl_ripemd160() will return an empty string.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - PCRE2 (Perl-Compatible Regex) extension (optional)
|
||||
title: PCRE2 (Perl-Compatible Regex) extension (optional)
|
||||
subtitle: Documentation for PCRE2 (Perl-Compatible Regex) extension (optional)
|
||||
description: Documentation for PCRE2 (Perl-Compatible Regex) extension (optional)
|
||||
permalink: /documentation/extensions/pcre2/
|
||||
|
|
@ -22,13 +22,14 @@ tags:
|
|||
- Purpose: Advanced regular expressions via PCRE2.
|
||||
- Homepage: [https://www.pcre.org/](https://www.pcre.org/){:class="ext"}
|
||||
|
||||
## Opcodes:
|
||||
## Opcodes
|
||||
|
||||
- OP_PCRE2_TEST: pops flags, text, pattern; pushes 1/0
|
||||
- OP_PCRE2_MATCH: pops flags, text, pattern; pushes match map or Nil
|
||||
- OP_PCRE2_FINDALL: pops flags, text, pattern; pushes array of match maps
|
||||
|
||||
## Notes:
|
||||
## Notes
|
||||
|
||||
- Requires PCRE2 development headers/libs.
|
||||
- Flags are backend-specific; see implementation for supported bits.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - PC/SC (smart cards) extension (optional)
|
||||
title: PC/SC (smart cards) extension (optional)
|
||||
subtitle: Documentation for PC/SC (smart cards) extension (optional)
|
||||
description: Documentation for PC/SC (smart cards) extension (optional)
|
||||
permalink: /documentation/extensions/pcsc/
|
||||
|
|
@ -20,7 +20,7 @@ tags:
|
|||
- Purpose: Access smart card readers/cards via PC/SC (pcsclite).
|
||||
- Homepage: [https://pcsclite.apdu.fr/](https://pcsclite.apdu.fr/){:class="ext"}
|
||||
|
||||
## Opcodes:
|
||||
## Opcodes
|
||||
|
||||
- OP_PCSC_ESTABLISH: returns context id (>0) or 0
|
||||
- OP_PCSC_RELEASE: pops ctx id; returns 1/0
|
||||
|
|
@ -29,7 +29,8 @@ tags:
|
|||
- OP_PCSC_DISCONNECT: pops handle id; returns 1/0
|
||||
- OP_PCSC_TRANSMIT: pops apdu, handle id; returns map with data/SW/rc
|
||||
|
||||
## Notes:
|
||||
## Notes
|
||||
|
||||
- Requires PC/SC lite development headers/libs.
|
||||
- Behavior and availability depend on platform and reader drivers.
|
||||
|
||||
|
|
|
|||
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
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - SQLite extension (optional)
|
||||
title: SQLite extension (optional)
|
||||
subtitle: Documentation for SQLite extension (optional)
|
||||
description: Documentation for SQLite extension (optional)
|
||||
permalink: /documentation/extensions/sqlite/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - XML (libxml2) extension (optional)
|
||||
title: XML (libxml2) extension (optional)
|
||||
subtitle: Documentation for XML (libxml2) extension (optional)
|
||||
description: Documentation for XML (libxml2) extension (optional)
|
||||
permalink: /documentation/extensions/xml2/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Fun executable, full usage guide
|
||||
title: Fun executable, full usage guide
|
||||
subtitle: Full usage guide for the `fun` executable, invocation patterns, REPL, env vars, include paths, examples, and install locations.
|
||||
description: Full usage guide for the `fun` executable, invocation patterns, REPL, env vars, include paths, examples, and install locations.
|
||||
permalink: /documentation/fun/
|
||||
|
|
@ -28,7 +28,7 @@ This document explains how to use the `fun` binary after building or installing
|
|||
## Synopsis
|
||||
|
||||
<pre>fun [options] [script.fun] [-- args...]</pre>
|
||||
-
|
||||
-
|
||||
- If [script.fun] is provided, `fun` runs the script.
|
||||
- If omitted and the build enables the REPL, `fun` starts an interactive session.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - funstx — Fun syntax checker and fixer
|
||||
title: funstx — Fun syntax checker and fixer
|
||||
subtitle: Syntax checker for .fun files with optional --fix auto-corrections; usage, exit codes, and limitations.
|
||||
description: Syntax checker for .fun files with optional --fix auto-corrections; usage, exit codes, and limitations.
|
||||
permalink: /documentation/funstx/
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ Opcode handlers organization:
|
|||
|
||||
- To keep vm.c readable, most opcode implementations are factored into small .c files included directly into vm.c (e.g., vm/core/load_const.c, vm/logic/and.c, vm/arrays/push.c, vm/math/abs.c, vm/os/thread_spawn.c, etc.).
|
||||
- This is a deliberate “amalgamation” style: small single-purpose C units compiled as part of vm.c.
|
||||
- Optional subsystems (JSON, PCRE2, CURL, SQLite, libSQL, PC/SC, XML2, Tcl/Tk, Notcurses, INI, OpenSSL/LibreSSL crypto helpers, sockets, serial, OS helpers) are grouped under src/extensions and src/vm/<domain>/.
|
||||
- Optional subsystems (JSON, PCRE2, CURL, SQLite, libSQL, PC/SC, XML2, Redis, Tcl/Tk, Notcurses, INI, OpenSSL/LibreSSL crypto helpers, sockets, serial, OS helpers) are grouped under src/extensions and src/vm/<domain>/.
|
||||
|
||||
Dispatch naming and visibility:
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ The VM is dynamically typed. Values carry a tag; operations check types at runti
|
|||
- Maps: OP_MAKE_MAP/KEYS/VALUES/HAS_KEY.
|
||||
- Conversions/reflection: OP_TO_NUMBER/TO_STRING/CAST/TYPEOF, OP_UCLAMP/SCLAMP.
|
||||
- I/O and OS: OP_READ_FILE/WRITE_FILE/INPUT_LINE/ENV/PROC_RUN/PROC_SYSTEM/TIME_NOW_MS/CLOCK_MONO_MS/DATE_FORMAT/OS_LIST_DIR/RANDOM_NUMBER, sockets, serial.
|
||||
- Extensions (optional): JSON, CURL, SQLite, libSQL, PC/SC, XML2, Tcl/Tk, Notcurses, INI, OpenSSL/LibreSSL.
|
||||
- Extensions (optional): JSON, CURL, SQLite, libSQL, PC/SC, XML2, Redis, Tcl/Tk, Notcurses, INI, OpenSSL/LibreSSL.
|
||||
|
||||
Each handler enforces argument types and returns clear error messages via vm_raise_error on misuse.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Maps in Fun
|
||||
title: Maps in Fun
|
||||
subtitle: Working with maps, construction, lookup/update, merging, iteration, and common patterns.
|
||||
description: Working with maps, construction, lookup/update, merging, iteration, and common patterns.
|
||||
permalink: /documentation/maps/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Working with numbers and floats in Fun
|
||||
title: Working with numbers and floats in Fun
|
||||
subtitle: Working with integers and floats, arithmetic, conversions, clamping, bitwise ops, and patterns.
|
||||
description: Working with integers and floats, arithmetic, conversions, clamping, bitwise ops, and patterns.
|
||||
permalink: /documentation/numbers/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Performance Guide
|
||||
title: Performance Guide
|
||||
subtitle: Build/runtime tuning tips and patterns for better performance.
|
||||
description: Build/runtime tuning tips and patterns for better performance.
|
||||
permalink: /documentation/performance/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Roadmap
|
||||
title: Roadmap
|
||||
subtitle: High-level direction, planned features, and pointers to issues.
|
||||
description: High-level direction, planned features, and pointers to issues.
|
||||
permalink: /documentation/roadmap/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Writing Rust-backed opcodes for Fun VM
|
||||
title: Writing Rust-backed opcodes for Fun VM
|
||||
subtitle: Writing Rust-backed opcodes and wiring them into the C VM; build/setup notes.
|
||||
description: Writing Rust-backed opcodes and wiring them into the C VM; build/setup notes.
|
||||
permalink: /documentation/rust/
|
||||
|
|
@ -179,7 +179,7 @@ The minimal helpers shown cover 64-bit integers and simple strings. Extending th
|
|||
- Ensuring all pointers and lifetimes are well-defined: strings pushed to the VM should be copied or allocated using VM facilities so they remain valid after the call.
|
||||
- Keeping Rust no_std unless you add an allocator and link setup to support std.
|
||||
|
||||
## Troubleshooting
|
||||
## Troubleshooting
|
||||
|
||||
- Link errors: Make sure FUN_WITH_RUST=ON for your build directory and that the Rust library is compiled before linking the VM. Use the rust_ops_build target if provided by your profile.
|
||||
- Missing symbol at runtime: Confirm #[no_mangle] and extern "C" on the Rust function and that C sees the correct prototype.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Security and Sandboxing
|
||||
title: Security and Sandboxing
|
||||
subtitle: Trust boundaries, I/O expectations, and capability restrictions.
|
||||
description: Trust boundaries, I/O expectations, and capability restrictions.
|
||||
permalink: /documentation/security-and-sandboxing/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Working with strings in Fun
|
||||
title: Working with strings in Fun
|
||||
subtitle: Working with strings, literals/escaping, concatenation, substr/find, split, and conversions.
|
||||
description: Working with strings, literals/escaping, concatenation, substr/find, split, and conversions.
|
||||
permalink: /documentation/strings/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Fun Style Guide
|
||||
title: Fun Style Guide
|
||||
subtitle: Coding conventions for C and Fun (indentation, naming, idioms).
|
||||
description: Coding conventions for C and Fun (indentation, naming, idioms).
|
||||
permalink: /documentation/style-guide/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Building and Running Tests
|
||||
title: Building and Running Tests
|
||||
subtitle: How to build and run tests/targets with CMake/CTest, and where to add new tests.
|
||||
description: How to build and run tests/targets with CMake/CTest, and where to add new tests.
|
||||
permalink: /documentation/testing/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Troubleshooting Guide
|
||||
title: Troubleshooting Guide
|
||||
subtitle: Common issues and quick fixes for build, includes, and REPL usage.
|
||||
description: Common issues and quick fixes for build, includes, and REPL usage.
|
||||
permalink: /documentation/troubleshooting/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Core datatypes, arrays, maps, strings, numbers, booleans and nil
|
||||
title: Core datatypes, arrays, maps, strings, numbers, booleans and nil
|
||||
subtitle: Core types (numbers, strings, arrays, maps, nil/bool), common operations, patterns, and interop notes.
|
||||
description: Core types (numbers, strings, arrays, maps, nil/bool), common operations, patterns, and interop notes.
|
||||
permalink: /documentation/types/
|
||||
|
|
@ -177,7 +177,7 @@ print(typeof({})) // "map"</pre>
|
|||
## Common patterns
|
||||
|
||||
- Accumulate values:
|
||||
-
|
||||
-
|
||||
```
|
||||
acc = []
|
||||
for i = 0; i < 5; i = i + 1 { push(acc, i*i) }
|
||||
|
|
@ -185,14 +185,14 @@ print(typeof({})) // "map"</pre>
|
|||
```
|
||||
|
||||
- Group related data with a map:
|
||||
-
|
||||
-
|
||||
```
|
||||
person = { "name": "Lin", "skills": ["C", "Fun"] }
|
||||
print(person["skills"][1]) // Fun
|
||||
```
|
||||
|
||||
- Safe map read:
|
||||
-
|
||||
-
|
||||
```
|
||||
v = person["twitter"]
|
||||
handle = v != nil ? v : "(none)"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun - Writing Tests
|
||||
title: Writing Tests
|
||||
subtitle: How to author new tests for Fun and opcode components.
|
||||
description: How to author new tests for Fun and opcode components.
|
||||
permalink: /documentation/writing-tests/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ published: true
|
|||
noToc: false
|
||||
noComments: false
|
||||
noDate: false
|
||||
title: Fun — Feature Overview
|
||||
title: Feature Overview
|
||||
subtitle: A high-level list of language, VM/runtime, tooling, and ecosystem features in Fun.
|
||||
description: Comprehensive feature list of the Fun programming language, its VM/runtime, tooling, build options, and optional extensions.
|
||||
permalink: /features/
|
||||
|
|
@ -24,103 +24,406 @@ tags:
|
|||
- cgi
|
||||
---
|
||||
|
||||
This page summarizes the core capabilities of Fun: the language, its virtual machine/runtime, tooling, build options, and the surrounding ecosystem. For 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
|
||||
|
||||
- Simple, expressive syntax designed for scripting and embedding
|
||||
- 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)
|
||||
### Syntax & Structure
|
||||
|
||||
Notes:
|
||||
- Indentation-based block structure (strict 2-space indent)
|
||||
- Line comments (`//`) and block comments (`/* */`)
|
||||
- Optional shebang (`#!`) line for script execution
|
||||
- Unicode string support via UTF-8 throughout
|
||||
- `exit` statement with optional exit code
|
||||
|
||||
- See the opcode index for the precise stack behavior of each operation: /documentation/opcodes/
|
||||
### Type System
|
||||
|
||||
- **Dynamic typing** with optional **static type annotations**
|
||||
- **Value types:** Integer (signed 64-bit), Float (double), Boolean, String, Array, Map, Function, Nil
|
||||
- **Type annotations:** `number`, `string`, `boolean`, `float`, `nil`, `array`, `map`, `class`
|
||||
- **Fixed-width integer types:** `byte` / `uint8`, `uint16`, `uint32`, `uint64`, `int8`, `int16`, `int32`, `int64` — with automatic range clamping
|
||||
- **Type aliases:** `sint8`–`sint64` as synonyms for `int8`–`int64`
|
||||
- `typeof()` runtime type introspection
|
||||
- `to_string()` and `to_number()` conversion functions
|
||||
- `cast(value, typeName)` explicit type casting
|
||||
|
||||
### Variables & Scope
|
||||
|
||||
- Global and local (per-function) variable scoping
|
||||
- Auto-declaration on first assignment
|
||||
- Typed variable declarations: `string name = "Fun"`
|
||||
- Up to 128 globals, 64 locals per frame, 128 call frames
|
||||
|
||||
### Operators
|
||||
|
||||
- **Arithmetic:** `+`, `-`, `*`, `/`, `%` (addition also concatenates strings)
|
||||
- **Comparison:** `<`, `<=`, `>`, `>=`, `==`, `!=`
|
||||
- **Logical:** `&&` (and), `||` (or), `!` (not) — short-circuit evaluation
|
||||
- **Bitwise (32-bit):** `band()`, `bor()`, `bxor()`, `bnot()`, `shl()`, `shr()`, `rol()`, `ror()`
|
||||
- **Ternary:** `condition ? true_expr : false_expr`
|
||||
|
||||
### Data Structures
|
||||
|
||||
#### **Arrays**
|
||||
|
||||
- Literal syntax: `[1, 2, 3]`
|
||||
- Index get/set: `arr[0]`, `arr[0] = value`
|
||||
- Slice syntax: `arr[start:end]`
|
||||
- Negative indices for end-relative access
|
||||
- Built-in operations: `len()`, `push()`, `pop()`, `insert()`, `remove()`, `slice()`, `contains()`, `indexOf()`, `clear()`, `enumerate()`, `zip()`, `join()`, `map()`, `filter()`, `reduce()`
|
||||
|
||||
#### **Maps (dictionaries)**
|
||||
|
||||
- Literal syntax: `{"key": value, ...}`
|
||||
- Bracket access/assignment: `map["key"]`, `map["key"] = value`
|
||||
- Dot property access: `map.key`
|
||||
- Built-in operations: `has()`, `keys()`, `values()`
|
||||
|
||||
### Strings
|
||||
|
||||
- Double and single-quoted string literals
|
||||
- Concatenation with `+`
|
||||
- Built-in operations: `len()`, `substr()`, `find()`, `split()`, `join()`
|
||||
|
||||
### Control Flow
|
||||
|
||||
- `if` / `else if` / `else` conditional chains
|
||||
- `while` loops with `break` and `continue`
|
||||
- `for var in array` — array iteration
|
||||
- `for var in range(start, end)` — numeric range iteration (`[start, end)`)
|
||||
- `for (key, value) in map` — map key-value iteration
|
||||
- `match` expression (stdlib `lib/utils/match.fun`)
|
||||
|
||||
### Functions
|
||||
|
||||
- Named function definitions: `fun name(params) body`
|
||||
- Anonymous function literals: `fn(params) body`
|
||||
- First-class functions (pass as arguments, store in variables)
|
||||
- Recursion support
|
||||
- Return with `return expr` (or implicit nil)
|
||||
|
||||
### Object-Oriented Programming
|
||||
|
||||
- Class definitions: `class Name(typed params) body`
|
||||
- Constructor method: `_construct(this, ...)` — auto-invoked on instantiation
|
||||
- Methods: `fun method(this, ...)` inside class body
|
||||
- Field access and mutation via `this.field`
|
||||
- Property access via dot notation: `obj.field`
|
||||
- Method call sugar: `obj.method(args)` (auto-binds `this`)
|
||||
- Single inheritance: `class Child(...) extends Parent`
|
||||
- Method overriding in subclasses
|
||||
|
||||
### Error Handling
|
||||
|
||||
- `try` / `catch` / `finally` blocks
|
||||
- Error variable binding: `catch err`
|
||||
- `throw` opcode for raising exceptions
|
||||
- Per-frame try-stack for nested exception handlers
|
||||
|
||||
### Pattern Matching & Regex (Built-in POSIX)
|
||||
|
||||
- `regex_match(str, pattern)` — full match test (returns 1/0)
|
||||
- `regex_search(str, pattern)` — first match with groups (returns map)
|
||||
- `regex_replace(str, pattern, replacement)` — global search and replace
|
||||
|
||||
### Functional Programming
|
||||
|
||||
- First-class and anonymous functions
|
||||
- `map(array, fn)` — transform each element
|
||||
- `filter(array, fn)` — keep matching elements
|
||||
- `reduce(array, init, fn)` — accumulate values
|
||||
- Higher-order functions (functions that accept or return functions)
|
||||
- `enumerate()` and `zip()` iteration helpers
|
||||
|
||||
### Concurrency
|
||||
|
||||
- `thread_spawn(fn, args)` — spawn a thread, returns thread ID
|
||||
- `thread_join(id)` — join a thread, returns its result
|
||||
- Cooperative async scheduler (stdlib `lib/async/scheduler.fun`)
|
||||
|
||||
---
|
||||
|
||||
## Virtual Machine & Runtime
|
||||
|
||||
- Compact stack‑based bytecode VM implemented in C (C99)
|
||||
- Deterministic execution model with explicit opcodes for core language features
|
||||
- Efficient array and map primitives with indexing and mutation opcodes
|
||||
- Built‑in string operations and regex integration (with extension)
|
||||
- Minimal error model integrated with VM (throw/try handlers)
|
||||
### Architecture
|
||||
|
||||
## Standard Library (Core)
|
||||
- Compact stack-based bytecode VM written in **C99**
|
||||
- Tagged union value type supporting 8 runtime types
|
||||
- ~220 opcodes covering all language features
|
||||
- Separate operand stack (1024 entries), call frames (128 max), and globals (128)
|
||||
- Each frame has 64 local slots and a 16-entry try/catch stack
|
||||
|
||||
Core functionality available out of the box in the VM and library modules:
|
||||
### Memory & Performance
|
||||
|
||||
- Arrays: create, push/pop, insert/remove, slice, contains, join, enumerate
|
||||
- Maps: create, has_key, keys, values
|
||||
- Strings: substr, find, split, replace (with regex), join
|
||||
- Math and bitwise ops over fixed‑width integers used by the VM
|
||||
- Deterministic execution model
|
||||
- Reference-counted arrays and maps
|
||||
- Function/data sectioning with linker GC for small binaries
|
||||
- LTO (Link-Time Optimization) support for Release builds
|
||||
|
||||
See the opcode list for the canonical reference: /documentation/opcodes/
|
||||
### Debugging & Tracing
|
||||
|
||||
- Built-in debugger with breakpoints (up to 64)
|
||||
- Step, next, finish, and continue commands
|
||||
- `--trace` / `-t` flag for opcode-level execution tracing
|
||||
- Per-opcode execution counters (compile-time `FUN_TRACE`)
|
||||
- `--repl-on-error` flag: drops into interactive REPL on runtime error with stack preserved
|
||||
- Stack trace printing on errors
|
||||
- Source line mapping in error messages (includes include-file resolution)
|
||||
|
||||
### I/O & Platform
|
||||
|
||||
- `print()` — output with newline
|
||||
- `echo()` — output without newline (immediate flush)
|
||||
- `read_file(path)` — read entire file into string
|
||||
- `write_file(path, data)` — write string to file
|
||||
- `input_line()` — read a line from stdin (with optional prompt)
|
||||
- `env(name)` / `env_all()` — get environment variables
|
||||
- `proc_run(cmd)` — run command, capture stdout+exit code
|
||||
- `system(cmd)` — run command via shell, returns exit code
|
||||
- `os_list_dir(path)` — list directory entries
|
||||
|
||||
### Date, Time & Random
|
||||
|
||||
- `time_now_ms()` — wall clock in milliseconds since Unix epoch
|
||||
- `clock_mono_ms()` — monotonic clock for interval measurement
|
||||
- `date_format(ms, fmt)` — format timestamps via strftime
|
||||
- `sleep(ms)` — suspend execution
|
||||
- `random_seed(seed)` — seed the PRNG
|
||||
- `random_int(lo, hi)` — random integer in `[lo, hi)`
|
||||
- `random_number(len)` — cryptographically random hex string
|
||||
|
||||
### Networking (Built-in, Unix)
|
||||
|
||||
- `sock_tcp_listen(port, backlog)` — TCP server socket
|
||||
- `sock_tcp_accept(listen_fd)` — accept client connection
|
||||
- `sock_tcp_connect(host, port)` — TCP client connection
|
||||
- `sock_send(fd, data)` / `sock_recv(fd, maxlen)` — send/receive data
|
||||
- `sock_unix_listen(path, backlog)` / `sock_unix_connect(path)` — Unix domain sockets
|
||||
- `sock_close(fd)` — close socket
|
||||
- `fd_set_nonblock(fd, on)` — non-blocking mode
|
||||
- `fd_poll_read(fd, timeout_ms)` / `fd_poll_write(fd, timeout_ms)` — I/O readiness polling
|
||||
|
||||
### Serial Communication (Unix)
|
||||
|
||||
- `serial_open(path, baud_rate)` — open serial port
|
||||
- `serial_config(fd, data_bits, parity, stop_bits, flow_control)` — configure
|
||||
- `serial_send(fd, data)` / `serial_recv(fd, maxlen)` — send/receive
|
||||
- `serial_close(fd)` — close
|
||||
|
||||
### Integer Utilities
|
||||
|
||||
- `sclamp(value, bits)` / `uclamp(value, bits)` — signed/unsigned bit-width clamping
|
||||
- Integer type declarations (`byte`, `uint8`–`uint64`, `int8`–`int64`) with automatic range clamping
|
||||
- `gcd(a, b)`, `lcm(a, b)` — greatest common divisor, least common multiple
|
||||
- `isqrt(x)` — integer square root
|
||||
- `sign(x)` — signum (-1, 0, 1)
|
||||
|
||||
---
|
||||
|
||||
## Standard Library
|
||||
|
||||
The standard library is written primarily in **Fun itself** and lives in `lib/`:
|
||||
|
||||
### Strings (`lib/strings.fun`)
|
||||
|
||||
- `str_ltrim`, `str_rtrim`, `str_trim` — whitespace trimming
|
||||
- `str_starts_with`, `str_ends_with` — prefix/suffix checking
|
||||
- `str_split` — single-character delimiter splitting
|
||||
- `str_replace_all` — global substring replacement
|
||||
- `str_to_lower`, `str_to_upper` — ASCII case conversion
|
||||
- `str_repeat` — string repetition
|
||||
- `string_to_bytes_ascii` — ASCII string to byte array
|
||||
|
||||
### Arrays (`lib/arrays.fun`)
|
||||
|
||||
- `array_slice`, `array_reverse`, `array_concat` — slicing and combining
|
||||
- `array_index_of`, `array_contains` — searching
|
||||
- `array_unique` — deduplication
|
||||
- `array_flatten1` — flatten one level of nesting
|
||||
|
||||
### Math (`lib/math.fun`)
|
||||
|
||||
- `abs`, `clamp`, `gcd`, `lcm`, `powi` (integer exponentiation)
|
||||
- `min3`, `max3`, `array_min`, `array_max`
|
||||
|
||||
### Hex (`lib/hex.fun`)
|
||||
|
||||
- `hex_to_dec`, `dec_to_hex`, `hex_to_bytes`, `bytes_to_hex`
|
||||
|
||||
### Encoding (`lib/encoding/base64.fun`)
|
||||
|
||||
- `b64_encode_bytes`, `b64_decode_to_bytes` — Base64 encoding/decoding
|
||||
|
||||
### Cryptography (Pure Fun implementations in `lib/crypt/`)
|
||||
|
||||
- **MD5** — `MD5` class (`lib/crypt/md5.fun`)
|
||||
- **SHA-1** — `SHA1` class (`lib/crypt/sha1.fun`)
|
||||
- **SHA-256** — `SHA256` class (`lib/crypt/sha256.fun`)
|
||||
- **SHA-384** — `SHA384` class (`lib/crypt/sha384.fun`)
|
||||
- **SHA-512** — `SHA512` class (`lib/crypt/sha512.fun`)
|
||||
- **CRC-32** — `CRC32` class (IEEE 802.3)
|
||||
- **CRC-32C** — `CRC32C` class (Castagnoli)
|
||||
- **AES-256** — `AES256` class (ECB mode)
|
||||
|
||||
### Functional Utilities
|
||||
|
||||
- **Option type** (`lib/utils/option.fun`): `some()`, `none()`, `is_some`, `is_none`, `unwrap`, `unwrap_or`, `option_map`, `and_then`, `or_else`, `try_get`
|
||||
- **Result type** (`lib/utils/result.fun`): `ok()`, `err()`, `is_ok`, `is_err`, `unwrap`, `unwrap_or`, `result_map`, `map_err`, `and_then`, `or_else`, `to_option`
|
||||
- **Pattern matching** (`lib/utils/match.fun`): `match(value, cases)` with `is`, `when`, `else` patterns
|
||||
|
||||
### Range Utilities (`lib/utils/range.fun`)
|
||||
|
||||
- `range(n)` — `[0, n)`
|
||||
- `range2(start, end)` — `[start, end)`
|
||||
- `range3(start, end, step)` — stepped range
|
||||
|
||||
### Date/Time (`lib/utils/datetime.fun`)
|
||||
|
||||
- `DateTime` class with `now_ms`, `mono_ms`, `format`, `iso_now`, `iso_from`, `date_str`, `time_str`, `today_str`, `start_timer`, `elapsed_ms`, `sleep_ms`, `sleep_s`
|
||||
|
||||
### CLI (`lib/cli.fun`)
|
||||
|
||||
- `argv()` — retrieve command-line arguments
|
||||
- `parse_args(args)` — parse flags and positional arguments
|
||||
|
||||
### Console (`lib/io/console.fun`)
|
||||
|
||||
- `Console` class with `prompt`, `ask`, `ask_hidden`, `ask_yes_no`, `term_cols`, `progress` (progress bar)
|
||||
|
||||
### Process (`lib/io/process.fun`)
|
||||
|
||||
- `Process` class wrapping `proc_run` and `system`
|
||||
|
||||
### Thread (`lib/io/thread.fun`)
|
||||
|
||||
- `Thread` class wrapping `thread_spawn` / `thread_join`
|
||||
|
||||
### Socket Classes (`lib/io/socket.fun`)
|
||||
|
||||
- `TcpClient` — TCP client with connect/send/recv/close/recv_all
|
||||
- `TcpServer` — TCP server with listen/accept/close
|
||||
- `UnixClient` — Unix domain socket client
|
||||
|
||||
### Serial (`lib/io/serial.fun`)
|
||||
|
||||
- `Serial` class wrapping serial port operations
|
||||
|
||||
### Async Scheduler (`lib/async/scheduler.fun`)
|
||||
|
||||
- Cooperative multitasking with `task_spawn`, `co_yield`, `run_once`, `run_until_done`
|
||||
- I/O readiness polling: `await_read`, `await_write`
|
||||
|
||||
### Networking / Web
|
||||
|
||||
- **CGI** (`lib/net/cgi.fun`): full CGI request parsing, response generation, URL encoding/decoding
|
||||
- **HTTP Server** (`lib/net/http_server.fun`): static file serving with `.fun` script execution
|
||||
- **HTTP CGI Server** (`lib/net/http_cgi_server.fun`): CGI-based HTTP server
|
||||
- **IRC Client** (`lib/net/irc.fun`): IRC protocol client with message parsing
|
||||
|
||||
---
|
||||
|
||||
## Optional Extensions (Build-time)
|
||||
|
||||
Enabled via CMake flags; each wraps a mature C library:
|
||||
|
||||
| Extension | CMake Flag | Library | Features |
|
||||
|-----------|-----------|---------|----------|
|
||||
| **[JSON](/documentation/extensions/json/)** | `FUN_WITH_JSON` | [json-c](https://json-c.github.io/json-c/){:class="ext"} | `json_parse()`, `json_stringify()`, `json_from_file()`, `json_to_file()` |
|
||||
| **[cURL](/documentation/extensions/curl/)** | `FUN_WITH_CURL` | [libcurl](https://curl.se/libcurl/){:class="ext"} | `curl_get()`, `curl_post()`, `curl_download()` |
|
||||
| **[SQLite](/documentation/extensions/sqlite/)** | `FUN_WITH_SQLITE` | [libsqlite3](https://www.sqlite.org/){:class="ext"} | `sqlite_open()`, `sqlite_close()`, `sqlite_exec()`, `sqlite_query()` |
|
||||
| **[PCRE2](/documentation/extensions/pcre2/)** | `FUN_WITH_PCRE2` | [libpcre2](https://www.pcre.org/){:class="ext"} | `pcre2_test()`, `pcre2_match()`, `pcre2_find_all()` — with flags (i, m, s, u, x) |
|
||||
| **[OpenSSL](/documentation/extensions/openssl/)** | `FUN_WITH_OPENSSL` | [libcrypto](https://www.openssl.org/){:class="ext"} | `openssl_md5()`, `openssl_sha256()`, `openssl_sha512()`, `openssl_ripemd160()` |
|
||||
| **[INI](/documentation/extensions/ini/)** | `FUN_WITH_INI` | [iniparser](https://github.com/ndevilla/iniparser){:class="ext"} | `ini_load()`, `ini_get_string/int/double/bool()`, `ini_set()`, `ini_unset()`, `ini_save()` |
|
||||
| **[XML](/documentation/extensions/xml2/)** | `FUN_WITH_XML2` | [libxml2](http://xmlsoft.org/){:class="ext"} | `xml_parse()`, `xml_root()`, `xml_name()`, `xml_text()` |
|
||||
| **[PC/SC](/documentation/extensions/pcsc/)** | `FUN_WITH_PCSC` | [libpcsclite](https://pcsclite.apdu.fr/){:class="ext"} | `pcsc_establish()`, `pcsc_list_readers()`, `pcsc_connect()`, `pcsc_transmit()`, etc. |
|
||||
| **[KCGI](/documentation/extensions/kcgi/)** | `FUN_WITH_KCGI` | [libkcgi](https://kristaps.bsd.lv/kcgi/){:class="ext"} | `kcgi_parse()`, `kcgi_reply_start()`, `kcgi_write()`, `kcgi_end()` |
|
||||
| **[Redis/Valkey](/documentation/extensions/redis/)** | `FUN_WITH_REDIS` | [hiredis](https://github.com/redis/hiredis){:class="ext"} | `redis_connect()`, `redis_cmd()`, `redis_close()` |
|
||||
|
||||
Some extensions also have a corresponding **stdlib wrapper class** in `lib/io/` or `lib/net/`:
|
||||
|
||||
- `JSON` class (`lib/io/json.fun`)
|
||||
- `INI` class (`lib/io/ini.fun`)
|
||||
- `XML` class (`lib/io/xml.fun`)
|
||||
- `PCSC` / `PCSC2` classes (`lib/io/pcsc.fun`)
|
||||
- `PCRE2` class (`lib/regex/pcre2.fun`)
|
||||
- `KCGI` class (`lib/net/kcgi.fun`)
|
||||
|
||||
---
|
||||
|
||||
## FFI / Interop (Experimental)
|
||||
|
||||
### Rust FFI (`FUN_WITH_RUST`)
|
||||
|
||||
- Cargo-based Rust static library linked into the VM
|
||||
- Demo opcodes: `rust_hello()`, `rust_hello_args()`, `rust_hello_args_return()`, `rust_get_sp()`, `rust_set_exit()`
|
||||
- Rust has unsafe access to VM internals via raw pointer and struct offset APIs
|
||||
|
||||
### C++ FFI (`FUN_WITH_CPP`)
|
||||
|
||||
- C++ static library linked into the VM
|
||||
- Demo opcode: `cpp_add(a, b)`
|
||||
|
||||
---
|
||||
|
||||
## Tooling
|
||||
|
||||
- Fun interpreter/runtime executable (target: fun)
|
||||
- REPL and small tools (targets: repl, funstx, examples under examples/)
|
||||
- Test binaries (target: fun_test, test_opcodes)
|
||||
- Formatting helper target (target: format) using clang‑format
|
||||
- Make‑like aggregate build target (target: build)
|
||||
- **`fun`** — Interpreter/REPL. Runs `.fun` scripts or starts interactive REPL
|
||||
- **`funstx`** — Syntax checker with optional `--fix` mode
|
||||
- **REPL** — Interactive shell with history (1000 lines), multi-line input, tab completion, and commands: `:help`, `:env`, `:load`, `:run`, `:edit`, `:save`, `:clear`, `:exit`, `:quit`, `:debug`, `:import`, `:export`, `:type`, `:trace`, `:reload`
|
||||
- **Test harnesses** — `fun_test` (bytecode-level tests) and `test_opcodes` (opcode exercisers)
|
||||
- **CTest integration** — crypto example scripts run as automated tests
|
||||
- **clang-format** target for consistent C source formatting
|
||||
|
||||
## Build System & Options
|
||||
---
|
||||
|
||||
Fun uses CMake and exposes toggles that mirror the project’s optionality at build time:
|
||||
## Build System
|
||||
|
||||
- FUN_DEBUG — enable extra diagnostics in builds
|
||||
- FUN_USE_MUSL — build against musl when available
|
||||
- FUN_WITH_CPP — enable C++ interop/components where applicable
|
||||
- FUN_WITH_RUST — build and link an optional Rust static library with opcode examples
|
||||
- FUN_BUILD_DOCS — generate documentation (Doxygen/website pipelines)
|
||||
- FUN_WITH_* — per‑extension toggles (see Extensions section)
|
||||
- **CMake** 3.10+ with C99 standard
|
||||
- Build toggles: `FUN_DEBUG`, `FUN_USE_MUSL`, `FUN_WITH_REPL`, `FUN_WITH_CPP`, `FUN_WITH_RUST`, `FUN_BUILD_DOCS`
|
||||
- Per-extension toggles for all optional libraries
|
||||
- Release builds with LTO, function/data sectioning, and `--gc-sections`
|
||||
- Doxygen documentation generation
|
||||
- Install targets for binaries, libraries, examples, man pages
|
||||
- Uninstall target with safe directory cleanup
|
||||
|
||||
Targets available for CI/workflows include Experimental/Continuous/Nightly aggregates, plus unit tests and coverage helpers (see CMake targets).
|
||||
---
|
||||
|
||||
## Portability
|
||||
|
||||
- Written in portable C99
|
||||
- Designed to build on common Linux environments; musl support available
|
||||
- Written in **C99** with POSIX extensions
|
||||
- Primary target: **Linux** (glibc and musl)
|
||||
- Partial **Windows** support
|
||||
- Platform abstraction for threading, sockets, serial I/O
|
||||
- Builds with GCC and Clang
|
||||
|
||||
## Extensions (Optional, 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
|
||||
|
||||
- Unit and VM‑level opcode tests (targets: fun_test, test_opcodes)
|
||||
- Example scripts under examples/ exercised by CI and manual runs
|
||||
- Unit tests for VM opcodes and bytecode execution
|
||||
- CTest integration runs example scripts as automated tests
|
||||
- Example scripts in `examples/` (90+ files) cover all language features
|
||||
- Crypto self-tests for MD5, SHA-1/256/384/512, CRC-32/CRC-32C, AES-256
|
||||
- Include-line mapping regression test
|
||||
- KCGI smoke test (when enabled)
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
- Doxygen configuration for API references (Doxyfile)
|
||||
- Human‑readable docs under web/documentation/ (opcodes, extensions, etc.)
|
||||
- Jekyll-based website under `web/` with full documentation
|
||||
- Doxygen API reference
|
||||
- Language specification documents (`spec/`)
|
||||
- Over 90 annotated example scripts
|
||||
- Handbook, type system guide, REPL guide, testing guide
|
||||
- Opcode reference with stack effect documentation
|
||||
- Changelog and semantic versioning
|
||||
|
||||
---
|
||||
|
||||
## Licensing
|
||||
|
||||
- Licensed under an 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>
|
||||
|
||||
<p>Fun is an experiment, just for fun, but Fun works!</p>
|
||||
<p>Fun is a small, strict, and simple programming language that runs on a compact stack-based virtual machine. The C core is intentionally minimal; most functionality and standard libraries are implemented in Fun itself.</p>
|
||||
|
||||
<p>Fun is a highly strict programming language, but also highly simple. It looks like Python (My favorite language), but there are differences.</p>
|
||||
<p>Fun is dynamically typed with optional static type annotations, <a href="/features/">featuring first-class functions</a>, classes with inheritance, pattern matching, and a rich standard library. It supports everything from basic scripting to TCP sockets, serial communication, threading, cryptography (MD5, SHA-1/256/384/512, CRC-32, AES-256), and a built-in debugger.</p>
|
||||
|
||||
<p>Influenced by <a href="https://www.gnu.org/software/bash/" class="ext">Bash</a>, <a href="https://en.wikipedia.org/wiki/The_C_Programming_Language" class="ext">C</a>, <a href="https://www.lua.org/" class="ext">Lua</a>, PHP, <a href="https://www.python.org/" class="ext">Python</a>, and a little Rust (Most influences came from linked languages).</p>
|
||||
<p>Influenced by: <a href="https://www.gnu.org/software/bash/" class="ext">Bash</a>, <a href="https://en.wikipedia.org/wiki/The_C_Programming_Language" class="ext">C</a>, <a href="https://www.lua.org/" class="ext">Lua</a>, <a href="https://www.php.net/" class="ext">PHP</a>, <a href="https://www.python.org/" class="ext">Python</a>, and <a href="https://www.rust-lang.org/" class="ext">Rust</a>.</p>
|
||||
|
||||
<p>Fun is an experiment - just for fun - but Fun works!</p>
|
||||
|
||||
<p>Fun is and will ever be 100% free under the terms of the <a href="https://opensource.org/license/apache-2-0" class="ext">Apache-2.0 License</a>.</p>
|
||||
|
||||
|
|
@ -61,4 +63,3 @@ permalink: /
|
|||
{% if count == paginator.page %}<a href="/{{ count }}/" class="active" title="Page {{ count }}">{{ count }}</a>{% else %}<a href="/{{ count }}/" title="Page {{ count }}">{{ count }}</a>{% endif %}
|
||||
{% endfor %}
|
||||
</div><br>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue