Fun is a small imperative language executed by a register-less stack-based virtual machine (VM). Source files are compiled to bytecode; the VM executes opcodes that work on a value stack. Functions and methods push/pop their arguments and return values on that stack.
High-level architecture:
- Front-end: parses .fun files, handles includes and constant folding, emits bytecode with debug markers (OP_LINE) for tracing and REPL-on-error.
- VM core: runs a loop over opcodes (see src/bytecode.h). Values include numbers (integers), strings, arrays, maps, booleans (0/1), functions, and nil.
- Built-ins: I/O, strings, arrays, regex, date/time, OS, networking, threading, optional JSON and PC/SC. Many are exposed as opcodes with friendly global functions in the language.
Key VM concepts (non-exhaustive):
- Control flow: OP_JUMP, OP_JUMP_IF_FALSE, OP_RETURN.
- Optional features: JSON opcodes (OP_JSON_PARSE and friends in src/vm/json/*) are compiled in only if -DFUN_WITH_JSON=ON. CURL builtins (curl_get/curl_post/curl_download) are available if -DFUN_WITH_CURL=ON. PCSC opcodes are available if -DFUN_WITH_PCSC=ON.
- Run with --trace to print executed lines and opcodes.
- Run with --repl-on-error to drop into an interactive REPL when a runtime error occurs, allowing inspection of variables and stepping.
## Command line interface and REPL
Running a script:
- fun path/to/script.fun
- Options: --trace, --repl-on-error (can combine), see build section for REPL availability.
REPL:
- Launch with fun (no script) when built with FUN_WITH_REPL=ON.
- In trace/REPL-on-error mode, the VM annotates output with file:line and function names to aid debugging (see examples/debug_reporting.fun and examples/repl_on_error.fun).
## Core types and operations
Types:
- number: signed integer. Conversions: to_number(x). Bitwise ops exist via bnot, band, bor, bxor, shl, shr, rol, ror in stdlib/VM.
- string: immutable sequence of bytes; length via len(s); concatenate via join([a,b], ""). Substring: substr(s, start, len). Find: find(haystack, needle) returns index or -1.
- array: ordered list. Create with [a, b, c] or built-ins. len(a), push(a, v) appends, apop(a) removes last, insert(a, idx, v), remove(a, idx), slice(a, start, end).
- map: associative dictionary with string keys typically: m = {}; m["key"] = value; keys can be strings and sometimes numbers.
- boolean: represented as number 1 (true) or 0 (false). Logical operators: &&, ||, !.
- nil: absence of value. Many defensive stdlib wrappers return [] or {} or nil defaults on errors.
Control flow:
- if/else, while loops, for-like range utilities (see utils.range in stdlib), break/continue (see examples/loops_break_continue.fun).
Functions and classes:
- Define a function with fun name(args) ...
- Define a class with class Name(constructor params) and methods fun method(this, ...) ...; _construct is called as a constructor if present.
- Methods use explicit this.
Modules and includes:
- Use #include <path/to/module.fun> to include from FUN_LIB_DIR.
- Use #include "relative/path.fun" to include a file relative to your script.
- You can alias includes with "as" to create namespaces: #include <utils/math.fun> as m; then call m.add(...).
## Built-ins overview
Console and I/O:
- print(x): prints a value with a trailing newline. input(prompt): returns a line as string without trailing newline.
- json_from_file(path) -> value or nil; json_to_file(path, value, prettyFlag) -> 1/0
PC/SC (optional):
- pcsc_establish() -> context id (>0) or 0
- pcsc_list_readers(ctx) -> array of reader names (strings) or nil
- pcsc_connect(ctx, readerName) -> handle id (>0) or 0
- pcsc_disconnect(handle) -> 1/0
- pcsc_transmit(handle, bytesArray) -> map {"data": array of numbers, "sw1": n, "sw2": n, "code": n}
Note: Optional feature availability depends on your CMake flags at build time.
---
## Standard library APIs
The stdlib provides small, defensive wrappers around VM built-ins, typically with class-based APIs to avoid global name collisions and to offer sensible defaults.
### io.console
Class Console (lib/io/console.fun):
- prompt(text) -> string: print text and read a line.
- ask(question) -> string: prints "question: " and reads a line.
- ask_yes_no(question) -> 1/0: loops until user answers y/yes or n/no (case-insensitive).
MD5 (lib/crypt/md5.fun): Pure Fun implementation with class MD5 and helper md5_hex(hexStr). See examples/md5_demo.fun.
SHA family (lib/crypt/sha1.fun, sha256.fun, sha384.fun, sha512.fun): class wrappers SHA1/SHA256/SHA384/SHA512 with methods digest_hex_of_string(str) and helpers as documented in files. Examples: sha1_demo.fun, sha256_demo.fun, sha256_str_demo.fun, sha384_example.fun, sha512_demo.fun, sha512_str_demo.fun.
### encoding.base64
Module lib/encoding/base64.fun provides base64_encode(string) and base64_decode(string) helpers (see file for exact APIs). Used in some examples.
### arrays, strings, maps helpers
- lib/arrays.fun: helper functions for common array patterns.
- lib/strings.fun: string helpers like str_to_lower/upper and more; used by several stdlib modules.
- lib/hex.fun: bytes_to_hex(arrayOfNumbers) and hex_to_bytes(hexString) helpers as used by PCSC.
- lib/utils/range.fun: utilities for building numeric ranges; see for_range_test.fun.
- lib/utils/math.fun and lib/math.fun: higher-level math helpers.
---
## Extra libraries
### JSON (optional)
Build flag: -DFUN_WITH_JSON=ON. Requires json-c available on your system. Internals are in src/vm/json/ and wrap json-c to convert between json_object and Fun values.
VM functions:
- json_parse(text) -> value or nil on parse error.
- json_stringify(value, pretty) -> string; pretty is 0/1.
- json_from_file(path) -> value or nil if file missing/unreadable.
- json_to_file(path, value, pretty) -> 1 on success else 0.
Stdlib wrapper class JSON (lib/io/json.fun):
- parse(text)
- stringify(value, pretty=0)
- from_file(path)
- to_file(path, value, pretty=0)
Example walkthrough (examples/json_showcase.fun):
- Parses a JSON string into a map/array structure; demonstrates indexing (obj["name"]).
- Pretty prints the object with json.stringify(obj, 1).
- Attempts to read a non-existent file to show defensive behavior.
- Loads examples/data/complex.json, accesses nested fields, constructs a summary map, and writes pretty JSON to /tmp.
Build flag: -DFUN_WITH_CURL=ON. Requires libcurl (development headers) available on your system. If built without CURL, the functions below still exist but safely degrade: they return an empty string "" (for curl_get/curl_post) or 0 (for curl_download).
VM functions (minimal interface similar to JSON builtins):
- curl_get(url) -> string response body, or "" on error.
- curl_post(url, body) -> string response body, or "" on error. Body is sent as the raw POST body; for form-encoded data provide "key=value&..." yourself.
- curl_download(url, path) -> 1 on success, 0 on failure; saves response to the given file path.
Notes:
- Redirects are followed automatically (CURLOPT_FOLLOWLOCATION=1L).
- TLS/HTTPS handling, proxies, etc., are handled by libcurl defaults. This minimal interface does not expose custom headers or advanced options.
Examples:
- examples/curl_get_json.fun — GETs JSON from httpbin and parses it with json_parse when JSON is enabled.
- examples/curl_post.fun — POSTs simple form data to httpbin and prints the echoed response.
- examples/curl_download.fun — Downloads an image to ./downloaded.png and reports success.
Build flag: -DFUN_WITH_PCSC=ON. Requires PC/SC (e.g., pcsc-lite on Unix) and a reader. VM opcodes are wrapped by global functions as listed under Built-ins.
Stdlib wrapper class PCSC (lib/io/pcsc.fun):
- get_readers() -> array of reader names.
- transmit(hex_apdu) -> map result by establishing context, selecting a reader, connecting, transmitting, and disconnecting. It returns a map with keys data (array), sw1, sw2, code. The wrapper includes defensive defaults when no reader exists.
- There is also a commented-out full-featured variant exposing establish/release/connect/disconnect/transmit_bytes/transmit_hex for advanced use.
Example:
- examples/pcsc_example.fun: shows establishing and transmitting an APDU, or printing []/default map if no readers present.
---
## Examples reference
You can run examples without installing by pointing FUN_LIB_DIR to the repository lib directory:
- while_test.fun — While loops, counters, and loop termination conditions.
Notes:
- Some examples are platform-dependent (PCSC, UNIX sockets) or rely on optional features (JSON). They degrade gracefully when unavailable, printing empty arrays or default maps.
---
## Internals notes for JSON
Fun wraps json-c. See src/vm/json/parse.c, stringify.c, from_file.c, to_file.c. For parsing, OP_JSON_PARSE converts the input string into a json_object using a tokener and then converts to Fun values via json_to_fun. On error or when JSON is compiled out, the VM returns nil. The stdlib JSON class converts arguments defensively (to_string) and provides default pretty=0.
## Internals notes for PCSC
The PCSC functions in the VM interface with pcsc-lite/WinSCard. Transmit returns a map with raw data bytes and status words (sw1, sw2) and a code field. The stdlib wrapper in lib/io/pcsc.fun demonstrates defensive patterns: when no readers are found, it returns a default map so that indexing like res["sw1"] is always safe.
---
## Contributing and further reading
- Browse lib/ for up-to-date stdlib APIs; many files document their own public interfaces in comments at the top.
- src/bytecode.h lists all opcodes supported by the VM. The corresponding implementations live under src/vm/.
- examples/ are the best starting point to learn by doing.