Compare commits

...

7 Commits

Author SHA1 Message Date
fe2f087d9f UPdate.
Some checks failed
WrenCI / mac (push) Waiting to run
WrenCI / windows (push) Waiting to run
WrenCI / linux (push) Failing after 55s
2026-01-26 05:12:14 +01:00
a59dbe1d55 Update. 2026-01-25 22:09:29 +01:00
1ee28a1644 Update. 2026-01-25 19:02:02 +01:00
a830238d11 Update. 2026-01-25 13:09:28 +01:00
90618f2336 Update. 2026-01-25 11:42:49 +01:00
4de99d477f Update. 2026-01-25 10:54:09 +01:00
735596fdf6 Update. 2026-01-25 10:50:20 +01:00
270 changed files with 28440 additions and 10617 deletions

View File

@ -1,6 +1,6 @@
# retoor <retoor@molodetz.nl>
.PHONY: build tests clean debug
.PHONY: build tests clean debug sync-sidebar buildmanual wasm wasm-clean install-emscripten
build:
cd projects/make && $(MAKE) -f wren_cli.make -j $$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
@ -11,6 +11,9 @@ debug:
tests: build
python3 util/test.py
create_merge:
wren apps/merge_all_wren.wren
create_docs:
wren apps/merge_docs.wren
@ -22,3 +25,28 @@ install:
clean:
cd projects/make && $(MAKE) -f wren_cli.make clean
wasm-clean:
cd projects/make.wasm && $(MAKE) -f wren_wasm.make clean
install-emscripten:
@if [ ! -d "$(HOME)/emsdk" ]; then \
echo "Cloning Emscripten SDK..."; \
git clone https://github.com/emscripten-core/emsdk.git $(HOME)/emsdk; \
cd $(HOME)/emsdk && ./emsdk install latest && ./emsdk activate latest; \
echo ""; \
echo "Emscripten installed. Run: source ~/emsdk/emsdk_env.sh"; \
else \
echo "Emscripten SDK already exists at ~/emsdk"; \
echo "Run: source ~/emsdk/emsdk_env.sh"; \
fi
wasm:
@if [ -z "$$EMSDK" ]; then \
echo "Error: EMSDK is not set. Run 'source ~/emsdk/emsdk_env.sh' first"; \
exit 1; \
fi
cd projects/make.wasm && $(MAKE) -f wren_wasm.make -j $$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
buildmanual:
python3 util/build_manual.py

View File

@ -41,6 +41,16 @@ Run a specific test suite:
python3 util/test.py json
```
## Build Targets
| Target | Description |
|--------|-------------|
| `make build` | Release build |
| `make debug` | Debug build |
| `make clean` | Clean build artifacts |
| `make tests` | Build and run all tests |
| `make buildmanual` | Build documentation to `bin/manual/` |
## Usage
```bash
@ -56,7 +66,34 @@ bin/wren_cli # Start the REPL
## Documentation
See the `manual/` directory for the full reference including API docs, tutorials, and how-to guides.
### Building the Manual
```bash
make buildmanual
```
Output: `bin/manual/` (open `index.html` in a browser)
The manual works offline when opened directly via `file://` protocol.
### Manual Source Structure
```
manual_src/
data/
site.yaml # Site metadata (name, version)
navigation.yaml # Navigation structure and page list
templates/ # Jinja2 base templates
pages/ # Content pages (api/, tutorials/, howto/, etc.)
static/ # CSS, JS assets
```
### Adding Documentation for a New Module
1. Create `manual_src/pages/api/<name>.html` using the template from an existing API page
2. Add entry to `manual_src/data/navigation.yaml` under the API Reference section
3. Update `prev_page`/`next_page` in adjacent API pages
4. Build: `make buildmanual`
## License

146
apps/generator.wren vendored Executable file
View File

@ -0,0 +1,146 @@
#!/usr/local/bin/wren
import "pathlib" for Path
import "http" for Http
import "json" for Json
import "env" for Environment
import "argparse" for ArgumentParser
import "os" for Process
class SourceCodeGenerator {
static VERSION { "1.0" }
static DEFAULT_MODEL { "anthropic/claude-haiku-4.5" }
static API_ENDPOINT { "https://openrouter.ai/api/v1/chat/completions" }
static TUTORIAL_PATH { "apps/minitut.md" }
static DEFAULT_PROMPT {
"A small but useful application that combines several modules / libraries that is plug and play to use! Small description on top commenten what the application is and what it does and where it can be used for."
}
static run() {
System.print("// wren source code generator v%(this.VERSION)")
var startTime = System.clock
var args = parseArguments()
var apiKey = getApiKey()
var tutorial = loadTutorial()
var messages = buildMessages(tutorial, args["prompt"])
var sourceCode = generateCode(apiKey, messages)
printResults(startTime, sourceCode)
saveOutput(args["output"], sourceCode)
}
static parseArguments() {
var parser = ArgumentParser.new()
parser.addArgument("prompt", {"default": this.DEFAULT_PROMPT})
parser.addArgument("-o", {"long": "--output", "default": ""})
return parser.parseArgs()
}
static getApiKey() {
var apiKey = Environment.get("OPENROUTER_API_KEY")
if (apiKey == null || apiKey.count == 0) {
System.print("Error: OPENROUTER_API_KEY environment variable not set")
Fiber.abort("Missing API key")
}
return apiKey
}
static loadTutorial() {
return Path.new(this.TUTORIAL_PATH).readText()
}
static buildMessages(tutorial, userPrompt) {
var messages = []
messages.add({
"role": "system",
"content": "You are an application generator that will produce wren source code exclusively as described below:\n" + tutorial
})
messages.add({
"role": "user",
"content": "Please generate source code based on everything what i say from now on."
})
messages.add({
"role": "assistant",
"content": "I will respond in literally valid JSON only in this format: {\"source_code\":\"<the source code>\"} without any markup or markdown formatting."
})
messages.add({
"role": "user",
"content": userPrompt
})
return messages
}
static generateCode(apiKey, messages) {
var requestBody = {
"model": "%(this.DEFAULT_MODEL)",
"messages": messages
}
var headers = {
"Authorization": "Bearer " + apiKey,
"Content-Type": "application/json"
}
var response = Http.post(this.API_ENDPOINT, requestBody, headers)
if (!response.ok) {
System.print("Error: %(response.body)")
return ""
}
return extractSourceCode(response.body)
}
static extractSourceCode(responseBody) {
var data = Json.parse(responseBody)
if (data == null || data["choices"] == null || data["choices"].count == 0) {
return ""
}
var message = data["choices"][0]["message"]
if (message == null || message["content"] == null) {
return ""
}
var text = message["content"]
text = stripMarkdownWrapper(text)
text = extractFromJson(text)
return text
}
static stripMarkdownWrapper(text) {
if (text.startsWith("```")) {
return text[7..-4].trim()
}
return text
}
static extractFromJson(text) {
if (text.startsWith("{")) {
return Json.parse(text)["source_code"]
}
return text
}
static printResults(startTime, output) {
var elapsedTime = System.clock - startTime
System.print("// Generation time: %(elapsedTime)")
System.print("")
System.print(output)
}
static saveOutput(outputPath, content) {
if (!outputPath.isEmpty) {
Path.new(outputPath).writeText(content)
}
}
}
System.print(SourceCodeGenerator.DEFAULT_MODEL)
SourceCodeGenerator.run()

144
apps/generator2.wren vendored Executable file
View File

@ -0,0 +1,144 @@
#!/usr/local/bin/wren
import "pathlib" for Path
import "http" for Http
import "json" for Json
import "env" for Environment
import "argparse" for ArgumentParser
import "os" for Process
class SourceCodeGenerator {
static VERSION { "1.0" }
static DEFAULT_MODEL { "anthropic/claude-haiku-4.5" }
static API_ENDPOINT { "https://openrouter.ai/api/v1/chat/completions" }
static TUTORIAL_PATH { "apps/minitut.md" }
static DEFAULT_PROMPT {
"A small but useful application that combines several modules / libraries that is plug and play to use! Small description on top commenten what the application is and what it does and where it can be used for."
}
static run() {
System.print("// wren source code generator v%(this.VERSION)")
var startTime = System.clock
var args = parseArguments()
var apiKey = getApiKey()
var tutorial = loadTutorial()
var messages = buildMessages(tutorial, args["prompt"])
var sourceCode = generateCode(apiKey, messages)
printResults(startTime, sourceCode)
saveOutput(args["output"], sourceCode)
}
static parseArguments() {
var parser = ArgumentParser.new()
parser.addArgument("prompt", {"default": this.DEFAULT_PROMPT})
parser.addArgument("-o", {"long": "--output", "default": ""})
return parser.parseArgs()
}
static getApiKey() {
var apiKey = Environment.get("OPENROUTER_API_KEY")
if (apiKey == null || apiKey.count == 0) {
System.print("Error: OPENROUTER_API_KEY environment variable not set")
Fiber.abort("Missing API key")
}
return apiKey
}
static loadTutorial() {
return Path.new(this.TUTORIAL_PATH).readText()
}
static buildMessages(tutorial, userPrompt) {
var messages = []
messages.add({
"role": "system",
"content": "You are an application generator that will produce wren source code exclusively as described below:\n" + tutorial
})
messages.add({
"role": "user",
"content": "Please generate source code based on everything what i say from now on."
})
messages.add({
"role": "assistant",
"content": "I will respond in literally valid JSON only in this format: {\"source_code\":\"<the source code>\"} without any markup or markdown formatting."
})
messages.add({
"role": "user",
"content": userPrompt
})
return messages
}
static generateCode(apiKey, messages) {
var requestBody = {
"model": "%(this.DEFAULT_MODEL)",
"messages": messages
}
var headers = {
"Authorization": "Bearer " + apiKey,
"Content-Type": "application/json"
}
var response = Http.post(this.API_ENDPOINT, requestBody, headers)
if (!response.ok) {
System.print("Error: %(response.body)")
return ""
}
return extractSourceCode(response.body)
}
static extractSourceCode(responseBody) {
var data = Json.parse(responseBody)
if (data == null || data["choices"] == null || data["choices"].count == 0) {
return ""
}
var message = data["choices"][0]["message"]
if (message == null || message["content"] == null) {
return ""
}
var text = message["content"]
text = stripMarkdownWrapper(text)
text = extractFromJson(text)
return text
}
static stripMarkdownWrapper(text) {
if (text.startsWith("```")) {
return text[7..-4].trim()
}
return text
}
static extractFromJson(text) {
if (text.startsWith("{")) {
return Json.parse(text)["source_code"]
}
return text
}
static printResults(startTime, output) {
var elapsedTime = System.clock - startTime
System.print("// Generation time: %(elapsedTime)")
System.print("")
System.print(output)
}
static saveOutput(outputPath, content) {
if (!outputPath.isEmpty) {
Path.new(outputPath).writeText(content)
}
}
}
SourceCodeGenerator.run()

2
apps/merge_all_wren.wren vendored Normal file → Executable file
View File

@ -1,3 +1,5 @@
#!/usr/local/bin/wren
import "io" for File, Directory
class WrenFileReader {

2
apps/merge_docs.wren vendored Normal file → Executable file
View File

@ -1,3 +1,5 @@
#!/usr/local/bin/wren
import "pathlib" for Path
var startTime = System.clock

107
apps/minitut.md Normal file
View File

@ -0,0 +1,107 @@
# Wren Tutorial: Core to Advanced
## Basics
Wren: OOP scripting. Classes: `class Name { construct new(args) { _vars } methods }`. Inherit: `is Super`. Fn: `Fn.new { |args| body }`. Import: `import "mod" for Items`. Vars: `var x = val`. Loops: `for (i in seq) { }`, `while (cond) { }`. Conditionals: `if/else`. Errors: `Fiber.abort("msg")`.
Example:
```
class Point {
construct new(x, y) { _x = x; _y = y }
x { _x }
}
var p = Point.new(1, 2)
System.print(p.x) // 1
```
## Core Classes
- **Bool/Fiber/Fn/Null/Num**: Base.
- **Sequence**: `all/any(f)`, `contains(el)`, `count/f`, `each(f)`, `isEmpty`, `map(f)`, `skip/take(n)`, `where(f)`, `reduce/acc+f`, `join/sep`, `toList`.
- **String**: `bytes/codePoints` (seqs), `split(delim)`, `replace(from/to)`, `trim/chars/start/end`, `*(n)`.
- **List**: `addAll(other)`, `sort/comparer` (quicksort), `toString`, `+/ *(other/n)`.
- **Map**: `keys/values` (seqs), `toString`, `iteratorValue``MapEntry(key/val)`.
- **Range**: Seq empty.
- **System**: `print/obj/All`, `write/obj/All`, `clock`.
## Meta/Random
- **Meta**: `getModuleVariables(mod)`, `eval(src)`, `compileExpression/src`.
- **Random**: `new/seed`, `float/int/range`, `sample(list/n)`, `shuffle(list)`.
## IO/OS
- **File**: `read/write(path)`, `exists/delete`.
- **Directory**: `list/exists/mkdir/rmdir/delete(path)`.
- **Stdin/Stdout**: `readLine`, `flush`.
- **Process**: `args`, timed exec.
## Fibers/Scheduler/Timer
- Fiber: `new { body }`, `call/try`.
- Scheduler: `add { body }` (runs on IO).
- Timer: `sleep(ms)` (suspends).
## Modules/Examples
- **Argparse**: `new(desc)`, `addArgument(opt, {help/default/action/type})`, `parseArgs(args) → map`.
- **Base64**: `encode/decode(str)`, `encodeUrl/decodeUrl`.
- **Bytes**: `fromList/toList`, `length/concat/slice`, `xorMask(payload/mask)`.
- **Crypto**: `randomBytes(n)/Int(min,max)`; Hash: `md5/sha1/sha256(str)`, `toHex`.
- **Dataset**: `memory()["table"]`: `insert(map) → uid`, `all/find(query)/findOne`, `update/delete(uid)`, `columns/tables`.
- **Datetime**: `now/fromTimestamp`, components, `format(fmt)`, `+/- Duration` (fromHours/etc, seconds/+/-/*).
- **Dns**: `lookup(host,4/6) → [ips]`.
- **Pathlib**: `new(path)`, `exists/isDir/File`, `expanduser/glob/rglob`, `iterdir/joinpath/match/mkdir`, `name/stem/suffix/es`, `parent/parents/parts`, `readText/writeText/unlink`, `relativeTo/rename/walk`, `withName/Stem/Suffix`.
- **Regex**: `new(pat/flags)`, `test/replace/All/split`.
- **Signal**: Constants (SIGINT=2, etc.).
- **Sqlite**: `memory()`: `execute(sql,params)`, `lastInsertId/changes`, `query → rows`.
- **String Utils**: `toLower/Upper/hexEncode/Decode/repeat/padLeft/Right/escapeHtml/Json/urlEncode/Decode`.
- **Strutil**: As above.
- **Subprocess**: `run(cmd/args) → ProcessResult(success/exitCode/stdout)`.
- **Tempfile**: `gettempdir/mkdtemp/stemp/temp(suffix/prefix/dir)`, NamedTemporaryFile/TemporaryDirectory (name/write/read/close/delete/use/cleanup).
- **Uuid**: `v4() → str`, `isValid/V4`.
- **Wdantic**: Schema: `new({field: Field.type(opts)})`, `validate(data) → ValidationResult(isValid/errors/data)`; Validators: email/domain/etc.
- **Web**: Client: `parseUrl_(url) → {scheme/host/port/path}`; Request: `new_(method/path/query/headers/body/params/files)`, `header/json/cookies/form`; Response: `text/html/json/redirect/new(status/body/header/cookie/build)`; Router: `new()`, `get/post/etc(path,handler)`, `match(method/path) → {handler/params}`; SessionStore: `create/get/save/destroy`.
- **Websocket**: `computeAcceptKey(base64)`, Message: `new_(opcode/payload/fin)`, `isText/etc`; Server: `bind/accept/receive/sendBinary/close`.
## Tests/Benchmarks
- Fib: Recursive fib(28).
- Nbody: Solar system sim (bodies, energy, advance).
- Demos: Animals game (tree nodes), chat server (TCP fibers), browser auto (WS commands), etc.
## Usage
Import modules, use classes/fns. Run: Compile/eval via Meta. Async: Fibers + Scheduler/Timer.
## Feature-Packed Example
```
import "io" for File, Directory, Stdin
import "timer" for Timer
import "scheduler" for Scheduler
import "random" for Random
import "meta" for Meta
import "os" for Process
class Demo is Sequence {
construct new() { _list = [1,2,3] }
iterate(i) { _list.iterate(i) }
iteratorValue(i) { _list.iteratorValue(i) * 2 }
}
var rand = Random.new()
System.print(rand.int(10)) // Random num
var seq = Demo.new()
System.print(seq.map { |x| x + 1 }.toList) // [3,5,7]
Scheduler.add {
Timer.sleep(100)
System.print("Async fiber")
}
var modVars = Meta.getModuleVariables("io")
System.print(modVars) // Module vars
var file = "temp.txt"
File.create(file) { |f| f.writeBytes("data") }
System.print(File.read(file)) // data
File.delete(file)
var sub = Subprocess.run("echo hello")
System.print(sub.stdout.trim()) // hello
System.print(Process.args) // Args
```

98
apps/phonebook.wren vendored Executable file
View File

@ -0,0 +1,98 @@
#!/usr/local/bin/wren
// wren source code generator v1.0
// Generation time: 0.101419
import "io" for File, Directory, Stdin, Stdout
import "sqlite" for Database
class Phonebook {
construct new() {
_db = Database.memory()
init_()
}
init_() {
_db.execute("CREATE TABLE IF NOT EXISTS contacts (id INTEGER PRIMARY KEY, name TEXT, phone TEXT)", [])
}
add(name, phone) {
_db.execute("INSERT INTO contacts (name, phone) VALUES (?, ?)", [name, phone])
System.print("Contact added: %(name)")
}
list() {
var rows = _db.query("SELECT * FROM contacts", [])
if (rows.isEmpty) {
System.print("No contacts found.")
return
}
System.print("\n--- Phonebook ---")
for (row in rows) {
System.print("%(row["name"]) : %(row["phone"])")
}
System.print("")
}
search(name) {
var rows = _db.query("SELECT * FROM contacts WHERE name LIKE ?", ["\%" + name + "\%"])
if (rows.isEmpty) {
System.print("No contacts found for '%(name)'.")
return
}
System.print("\n--- Search Results ---")
for (row in rows) {
System.print("%(row[1]) : %(row[2])")
}
System.print("")
}
delete(name) {
_db.execute("DELETE FROM contacts WHERE name = ?", [name])
System.print("Contact deleted: %(name)")
}
run() {
System.print("\n=== CLI Phonebook ===")
var running = true
while (running) {
System.print("\n1. Add contact")
System.print("2. List contacts")
System.print("3. Search contact")
System.print("4. Delete contact")
System.print("5. Exit")
System.write("Choose option: ")
Stdout.flush()
var choice = Stdin.readLine().trim()
if (choice == "1") {
System.write("Name: ")
Stdout.flush()
var name = Stdin.readLine().trim()
System.write("Phone: ")
Stdout.flush()
var phone = Stdin.readLine().trim()
add(name, phone)
} else if (choice == "2") {
list()
} else if (choice == "3") {
System.write("Search name: ")
Stdout.flush()
var name = Stdin.readLine().trim()
search(name)
} else if (choice == "4") {
System.write("Contact name to delete: ")
Stdout.flush()
var name = Stdin.readLine().trim()
delete(name)
} else if (choice == "5") {
System.print("Goodbye!")
running = false
} else {
System.print("Invalid option.")
}
}
}
}
var pb = Phonebook.new()
pb.run()

View File

@ -2534,7 +2534,52 @@ static void await_(Compiler* compiler, bool canAssign)
emitShortArg(compiler, CODE_LOAD_MODULE_VAR, schedulerSymbol);
ignoreNewlines(compiler);
expression(compiler);
if (peek(compiler) == TOKEN_NAME)
{
Token nameToken = compiler->parser->current;
Variable variable = resolveNonmodule(compiler, nameToken.start, nameToken.length);
if (variable.index == -1)
{
variable.scope = SCOPE_MODULE;
variable.index = wrenSymbolTableFind(&compiler->parser->module->variableNames,
nameToken.start, nameToken.length);
}
if (variable.index != -1)
{
TokenType following = peekNext(compiler);
if (following == TOKEN_LEFT_PAREN)
{
nextToken(compiler->parser);
loadVariable(compiler, variable);
Signature signature = { "call", 4, SIG_GETTER, 0 };
methodCall(compiler, CODE_CALL_0, &signature);
allowLineBeforeDot(compiler);
while (match(compiler, TOKEN_DOT))
{
namedCall(compiler, false, CODE_CALL_0);
allowLineBeforeDot(compiler);
}
}
else
{
expression(compiler);
}
}
else
{
expression(compiler);
}
}
else
{
expression(compiler);
}
callMethod(compiler, 1, "await_(_)", 9);
}
@ -2544,18 +2589,45 @@ static void async_(Compiler* compiler, bool canAssign)
int schedulerSymbol = resolveScheduler(compiler);
if (schedulerSymbol == -1) return;
emitShortArg(compiler, CODE_LOAD_MODULE_VAR, schedulerSymbol);
consume(compiler, TOKEN_LEFT_BRACE, "Expect '{' after 'async'.");
Compiler fnCompiler;
initCompiler(&fnCompiler, compiler->parser, compiler, false);
fnCompiler.fn->arity = 0;
if (match(compiler, TOKEN_PIPE))
{
Compiler outerCompiler;
initCompiler(&outerCompiler, compiler->parser, compiler, false);
finishBody(&fnCompiler);
endCompiler(&fnCompiler, "[async]", 7);
Signature outerSignature = { "", 0, SIG_METHOD, 0 };
finishParameterList(&outerCompiler, &outerSignature);
consume(compiler, TOKEN_PIPE, "Expect '|' after parameters.");
outerCompiler.fn->arity = outerSignature.arity;
callMethod(compiler, 1, "async_(_)", 9);
emitShortArg(&outerCompiler, CODE_LOAD_MODULE_VAR, schedulerSymbol);
Compiler innerCompiler;
initCompiler(&innerCompiler, outerCompiler.parser, &outerCompiler, false);
innerCompiler.fn->arity = 0;
finishBody(&innerCompiler);
endCompiler(&innerCompiler, "[async]", 7);
callMethod(&outerCompiler, 1, "async_(_)", 9);
emitOp(&outerCompiler, CODE_RETURN);
endCompiler(&outerCompiler, "[async fn]", 10);
}
else
{
emitShortArg(compiler, CODE_LOAD_MODULE_VAR, schedulerSymbol);
Compiler fnCompiler;
initCompiler(&fnCompiler, compiler->parser, compiler, false);
fnCompiler.fn->arity = 0;
finishBody(&fnCompiler);
endCompiler(&fnCompiler, "[async]", 7);
callMethod(compiler, 1, "async_(_)", 9);
}
}
// Subscript or "array indexing" operator like `foo[bar]`.

View File

@ -1215,6 +1215,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
CASE_CODE(RETURN):
{
Value result = POP();
WREN_PROFILE_EXIT(fiber);
fiber->numFrames--;
// Close any upvalues still in scope.

View File

@ -6,6 +6,22 @@
#include "wren_value.h"
#include "wren_utils.h"
#ifdef WREN_PROFILE_ENABLED
struct ProfileEntry;
struct ProfileFrame;
struct ProfileState;
extern struct ProfileState* g_profile;
void wrenProfileEnter(WrenVM* vm, ObjFiber* fiber, ObjClosure* closure);
void wrenProfileExit(ObjFiber* fiber);
#define WREN_PROFILE_ENTER(vm, fiber, closure) \
do { if (g_profile) wrenProfileEnter(vm, fiber, closure); } while(0)
#define WREN_PROFILE_EXIT(fiber) \
do { if (g_profile) wrenProfileExit(fiber); } while(0)
#else
#define WREN_PROFILE_ENTER(vm, fiber, closure) ((void)0)
#define WREN_PROFILE_EXIT(fiber) ((void)0)
#endif
// The maximum number of temporary objects that can be made visible to the GC
// at one time.
#define WREN_MAX_TEMP_ROOTS 8
@ -193,6 +209,7 @@ static inline void wrenCallFunction(WrenVM* vm, ObjFiber* fiber,
wrenEnsureStack(vm, fiber, needed);
wrenAppendCallFrame(vm, fiber, closure, fiber->stackTop - numArgs);
WREN_PROFILE_ENTER(vm, fiber, closure);
}
// Marks [obj] as a GC root so that it doesn't get collected.

View File

@ -3,20 +3,239 @@
import "scheduler" for Scheduler, Future
import "timer" for Timer
System.print("=== Await Demo ===\n")
System.print("=== Async/Await Demo ===\n")
System.print("--- Basic Await ---")
await Timer.sleep(1)
System.print("Timer completed")
System.print("Timer completed after 1ms")
System.print("\n--- Concurrent Async ---")
var task1 = async { Timer.sleep(1) }
var task2 = async { Timer.sleep(1) }
await task1
await task2
System.print("Both tasks completed concurrently")
var getValue = async { 42 }
var result = await getValue()
System.print("Async block returned: %(result)")
System.print("\n--- Async With Result ---")
var task = async { "computed value" }
var result = await task
System.print("Result: %(result)")
System.print("\n--- Parameterized Async Functions ---")
var double = async { |x| x * 2 }
var add = async { |a, b| a + b }
var multiply = async { |a, b| a * b }
System.print("double(21) = %(await double(21))")
System.print("add(3, 4) = %(await add(3, 4))")
System.print("multiply(6, 7) = %(await multiply(6, 7))")
System.print("\n--- Nested Awaits ---")
var addTen = async { |x| x + 10 }
System.print("addTen(double(5)) = %(await addTen(await double(5)))")
System.print("double(double(double(2))) = %(await double(await double(await double(2))))")
System.print("\n--- Await in Expressions ---")
System.print("1 + double(5) = %(1 + await double(5))")
System.print("double(3) + double(4) = %(await double(3) + await double(4))")
System.print("\n--- Await in Conditions ---")
var isPositive = async { |x| x > 0 }
if (await isPositive(5)) System.print("5 is positive: true")
var ternaryResult = await isPositive(10) ? "yes" : "no"
System.print("10 is positive? %(ternaryResult)")
System.print("\n--- Await in Loops ---")
var squares = []
for (i in 1..5) {
var sq = async { |x| x * x }
squares.add(await sq(i))
}
System.print("Squares of 1-5: %(squares)")
System.print("\n=== Class-Based Async Patterns ===\n")
System.print("--- Static Async Getters ---")
class Calculator {
static add { async { |a, b| a + b } }
static multiply { async { |a, b| a * b } }
static square { async { |x| x * x } }
static pi { async { 3.14159 } }
static compute(a, b) {
var addFn = Calculator.add
var multiplyFn = Calculator.multiply
var sum = await addFn(a, b)
var product = await multiplyFn(a, b)
return {"sum": sum, "product": product}
}
}
var addFn = Calculator.add
var multiplyFn = Calculator.multiply
var squareFn = Calculator.square
var piFn = Calculator.pi
System.print("add(3, 4) = %(await addFn(3, 4))")
System.print("multiply(5, 6) = %(await multiplyFn(5, 6))")
System.print("square(8) = %(await squareFn(8))")
System.print("pi() = %(await piFn())")
var results = Calculator.compute(5, 3)
System.print("Calculator.compute(5, 3) = %(results)")
System.print("\n--- Instance Methods with Async ---")
class Counter {
construct new(start) {
_value = start
}
value { _value }
add(n) {
var op = async { |x|
_value = _value + x
return _value
}
return await op(n)
}
subtract(n) {
var op = async { |x|
_value = _value - x
return _value
}
return await op(n)
}
reset() {
var op = async {
_value = 0
return _value
}
return await op()
}
}
var counter = Counter.new(100)
System.print("Initial value: %(counter.value)")
System.print("After add(25): %(counter.add(25))")
System.print("After subtract(10): %(counter.subtract(10))")
System.print("After reset(): %(counter.reset())")
System.print("Final value: %(counter.value)")
System.print("\n--- Mock API Service ---")
class UserApi {
static fetchUser { async { |id|
Timer.sleep(1)
return {"id": id, "name": "User%(id)", "email": "user%(id)@example.com"}
}}
static fetchPosts { async { |userId|
Timer.sleep(1)
return [
{"id": 1, "userId": userId, "title": "First Post"},
{"id": 2, "userId": userId, "title": "Second Post"}
]
}}
static getUserWithPosts(userId) {
var fetchUser = UserApi.fetchUser
var fetchPosts = UserApi.fetchPosts
var user = await fetchUser(userId)
var posts = await fetchPosts(userId)
user["posts"] = posts
return user
}
}
var fetchUser = UserApi.fetchUser
var user = await fetchUser(42)
System.print("User: %(user["name"]) <%(user["email"])>")
var fetchPosts = UserApi.fetchPosts
var posts = await fetchPosts(42)
System.print("Posts count: %(posts.count)")
var fullUser = UserApi.getUserWithPosts(123)
System.print("Full user: %(fullUser["name"]) with %(fullUser["posts"].count) posts")
System.print("\n--- Data Pipeline ---")
class DataPipeline {
static parse { async { |csv| csv.split(",") } }
static filter { async { |list, predicate|
var result = []
for (item in list) {
if (predicate.call(item)) result.add(item)
}
return result
}}
static transform { async { |list, mapper|
var result = []
for (item in list) {
result.add(mapper.call(item))
}
return result
}}
static reduce { async { |list, initial, reducer|
var acc = initial
for (item in list) {
acc = reducer.call(acc, item)
}
return acc
}}
static process(csv) {
var parse = DataPipeline.parse
var filter = DataPipeline.filter
var transform = DataPipeline.transform
var reduce = DataPipeline.reduce
var items = await parse(csv)
var filtered = await filter(items) { |s| s.count > 3 }
var lengths = await transform(filtered) { |s| s.count }
var total = await reduce(lengths, 0) { |acc, n| acc + n }
return total
}
}
var data = "one,two,three,four,five,six,seven"
var parse = DataPipeline.parse
var parsed = await parse(data)
System.print("Parsed: %(parsed)")
var filter = DataPipeline.filter
var filtered = await filter(parsed) { |s| s.count > 3 }
System.print("Filtered (length > 3): %(filtered)")
var transform = DataPipeline.transform
var lengths = await transform(filtered) { |s| s.count }
System.print("Lengths: %(lengths)")
var reduce = DataPipeline.reduce
var sum = await reduce(lengths, 0) { |acc, n| acc + n }
System.print("Sum of lengths: %(sum)")
var total = DataPipeline.process("apple,banana,cherry,date,elderberry")
System.print("Pipeline result: %(total)")
System.print("\n--- Parallel Task Execution ---")
var fetchA = async { |x|
Timer.sleep(10)
return "A:%(x)"
}
var fetchB = async { |x|
Timer.sleep(10)
return "B:%(x)"
}
var fetchC = async { |x|
Timer.sleep(10)
return "C:%(x)"
}
var futureA = fetchA.call(1)
var futureB = fetchB.call(2)
var futureC = fetchC.call(3)
System.print("Started 3 parallel tasks")
System.print("Results: %(await futureA), %(await futureB), %(await futureC)")
System.print("\n=== Demo Complete ===")

117
example/faker_demo.wren vendored Normal file
View File

@ -0,0 +1,117 @@
// retoor <retoor@molodetz.nl>
import "faker" for Faker
System.print("=== Faker Module Demo ===\n")
System.print("--- Person Information ---")
System.print("Name: %(Faker.name())")
System.print("First Name (Male): %(Faker.firstNameMale())")
System.print("First Name (Female): %(Faker.firstNameFemale())")
System.print("Username: %(Faker.username())")
System.print("Email: %(Faker.email())")
System.print("Phone: %(Faker.phoneNumber())")
System.print("Gender: %(Faker.gender())")
System.print("\n--- Address Information ---")
System.print("Street Address: %(Faker.streetAddress())")
System.print("City: %(Faker.city())")
System.print("State: %(Faker.state()) (%(Faker.stateAbbr()))")
System.print("ZIP Code: %(Faker.zipCode())")
System.print("Country: %(Faker.country()) (%(Faker.countryCode()))")
System.print("Full Address: %(Faker.address())")
System.print("Latitude: %(Faker.latitude())")
System.print("Longitude: %(Faker.longitude())")
System.print("\n--- Internet Data ---")
System.print("IPv4: %(Faker.ipv4())")
System.print("IPv6: %(Faker.ipv6())")
System.print("MAC Address: %(Faker.macAddress())")
System.print("Domain: %(Faker.domainName())")
System.print("URL: %(Faker.url())")
System.print("Password: %(Faker.password())")
System.print("UUID: %(Faker.uuid())")
System.print("User Agent: %(Faker.userAgent())")
System.print("\n--- Company and Job ---")
System.print("Company: %(Faker.company())")
System.print("Job Title: %(Faker.jobTitle())")
System.print("Job Descriptor: %(Faker.jobDescriptor())")
System.print("\n--- Product and Commerce ---")
System.print("Product: %(Faker.product())")
System.print("Category: %(Faker.productCategory())")
System.print("Price: $%(Faker.price())")
System.print("Currency: %(Faker.currency()) (%(Faker.currencySymbol()))")
System.print("Credit Card: %(Faker.creditCardType()) %(Faker.creditCardNumber())")
System.print("CVV: %(Faker.creditCardCVV())")
System.print("Expiry: %(Faker.creditCardExpiryDate())")
System.print("\n--- Date and Time ---")
System.print("Date: %(Faker.date())")
System.print("Past Date: %(Faker.pastDate())")
System.print("Future Date: %(Faker.futureDate())")
System.print("Date of Birth: %(Faker.dateOfBirth())")
System.print("Month: %(Faker.monthName())")
System.print("Day of Week: %(Faker.dayOfWeek())")
System.print("Time: %(Faker.time())")
System.print("\n--- Text Generation ---")
System.print("Word: %(Faker.word())")
System.print("Words: %(Faker.words(5).join(" "))")
System.print("Sentence: %(Faker.sentence())")
System.print("Paragraph: %(Faker.paragraph())")
System.print("Slug: %(Faker.slug())")
System.print("\n--- Colors ---")
System.print("Color Name: %(Faker.colorName())")
System.print("Hex Color: %(Faker.hexColor())")
System.print("RGB Color: %(Faker.rgbColor())")
System.print("\n--- File and Tech ---")
System.print("Filename: %(Faker.fileName())")
System.print("Extension: %(Faker.fileExtension())")
System.print("MIME Type: %(Faker.mimeType())")
System.print("Semver: %(Faker.semver())")
System.print("\n--- Banking ---")
System.print("IBAN: %(Faker.iban())")
System.print("Account Number: %(Faker.accountNumber())")
System.print("Routing Number: %(Faker.routingNumber())")
System.print("\n--- Cryptographic ---")
System.print("MD5: %(Faker.md5())")
System.print("SHA1: %(Faker.sha1())")
System.print("SHA256: %(Faker.sha256())")
System.print("\n--- Format Helpers ---")
System.print("Numerify ###-###-####: %(Faker.numerify("###-###-####"))")
System.print("Letterify ???-???: %(Faker.letterify("???-???"))")
System.print("Bothify ??-###: %(Faker.bothify("??-###"))")
System.print("\n--- Seeding for Reproducibility ---")
Faker.seed(42)
System.print("Seeded name 1: %(Faker.name())")
Faker.seed(42)
System.print("Seeded name 2: %(Faker.name())")
Faker.reset()
System.print("\n--- Profile Generation ---")
var profile = Faker.profile()
System.print("Profile:")
System.print(" Username: %(profile["username"])")
System.print(" Name: %(profile["name"])")
System.print(" Email: %(profile["email"])")
System.print(" Address: %(profile["address"])")
System.print(" Phone: %(profile["phone"])")
System.print(" Job: %(profile["job"])")
System.print(" Company: %(profile["company"])")
System.print(" Birthdate: %(profile["birthdate"])")
System.print("\n--- Bulk Generation Example ---")
System.print("Generating 5 users:")
for (i in 1..5) {
System.print(" %(i). %(Faker.name()) <%(Faker.email())>")
}
System.print("\n=== Demo Complete ===")

54
example/fswatch_demo.wren vendored Normal file
View File

@ -0,0 +1,54 @@
// retoor <retoor@molodetz.nl>
import "fswatch" for FileWatcher, FsEvent
import "timer" for Timer
import "io" for File, FileFlags
System.print("=== FSWatch Module Demo ===\n")
var testFile = "/tmp/fswatch_demo.txt"
System.print("--- Setting up test file ---")
var f = File.openWithFlags(testFile, FileFlags.writeOnly | FileFlags.create | FileFlags.truncate)
f.writeBytes("initial content", 0)
f.close()
System.print("Created test file: %(testFile)")
System.print("\n--- Starting file watcher ---")
var watcher = FileWatcher.new(testFile)
var eventCount = 0
watcher.start { |event|
eventCount = eventCount + 1
System.print("Event %(eventCount):")
System.print(" Filename: %(event.filename)")
System.print(" Is rename: %(event.isRename)")
System.print(" Is change: %(event.isChange)")
}
System.print("Watcher active: %(watcher.isActive)")
System.print("\n--- Modifying file ---")
Timer.immediate {
var f2 = File.openWithFlags(testFile, FileFlags.writeOnly | FileFlags.truncate)
f2.writeBytes("first modification", 0)
f2.close()
System.print("Made first modification")
}
Timer.sleep(150)
Timer.immediate {
var f3 = File.openWithFlags(testFile, FileFlags.writeOnly | FileFlags.truncate)
f3.writeBytes("second modification", 0)
f3.close()
System.print("Made second modification")
}
Timer.sleep(150)
System.print("\n--- Stopping watcher ---")
watcher.stop()
System.print("Watcher active: %(watcher.isActive)")
System.print("Total events received: %(eventCount)")

View File

@ -39,7 +39,15 @@ System.print("Status: %(response.statusCode)")
System.print("")
if (response.ok) {
var data = Json.parse(response.body)
let text = response.body
if(text.startsWith("```")){
text = text.trim("```")
text = text.trim("json")
text = text.trim("\n")
}
System.print(text)
var data = Json.parse(text)
if (data != null && data["choices"] != null && data["choices"].count > 0) {
var message = data["choices"][0]["message"]
if (message != null && message["content"] != null) {

36
example/os_demo.wren vendored Normal file
View File

@ -0,0 +1,36 @@
// retoor <retoor@molodetz.nl>
import "os" for Platform, Process
System.print("=== OS Module Demo ===\n")
System.print("--- Platform Information ---")
System.print("Name: %(Platform.name)")
System.print("Is POSIX: %(Platform.isPosix)")
System.print("Is Windows: %(Platform.isWindows)")
System.print("Home Path: %(Platform.homePath)")
System.print("\n--- Process Information ---")
System.print("PID: %(Process.pid)")
System.print("Parent PID: %(Process.ppid)")
System.print("Working Directory: %(Process.cwd)")
System.print("Wren Version: %(Process.version)")
System.print("\n--- Command Line Arguments ---")
System.print("Arguments: %(Process.arguments)")
System.print("All Arguments: %(Process.allArguments)")
System.print("\n--- Platform-Specific Paths ---")
var separator = Platform.isWindows ? "\\" : "/"
var configPath = Platform.homePath + separator + ".config"
System.print("Config directory: %(configPath)")
System.print("\n--- Conditional Exit ---")
var args = Process.arguments
if (args.count > 0 && args[0] == "--fail") {
System.print("Exiting with error code 1")
Process.exit(1)
}
System.print("Exiting normally")
Process.exit(0)

71
example/pexpect_demo.wren vendored Normal file
View File

@ -0,0 +1,71 @@
// retoor <retoor@molodetz.nl>
import "pexpect" for Spawn, Pexpect
System.print("=== Pexpect Module Demo ===\n")
System.print("--- Basic Spawn and Expect ---")
var child = Spawn.new("echo 'Hello from pexpect!'")
var idx = child.expect(["Hello"])
System.print("Matched pattern index: %(idx)")
System.print("After match: %(child.after)")
child.close()
System.print("\n--- Interactive Process ---")
child = Spawn.new("cat")
child.sendline("First line")
child.expect(["First line"])
System.print("Echo received: %(child.after)")
child.sendline("Second line")
child.expect(["Second line"])
System.print("Echo received: %(child.after)")
child.sendeof()
child.close()
System.print("\n--- Multiple Patterns ---")
child = Spawn.new("printf 'username: '")
idx = child.expect(["password:", "username:", "login:"])
System.print("Matched: %(idx == 1 ? "username" : "other")")
child.close()
System.print("\n--- Exact String Matching ---")
child = Spawn.new("echo 'The answer is 42'")
idx = child.expectExact(["42"])
System.print("Found exact match: %(child.after)")
System.print("Text before: %(child.before)")
child.close()
System.print("\n--- Timeout Handling ---")
child = Spawn.new("sleep 10")
child.timeout = 0.5
idx = child.expect(["never_match"])
if (idx == Pexpect.TIMEOUT) {
System.print("Got timeout as expected")
}
child.terminate(true)
child.close()
System.print("\n--- Process Properties ---")
child = Spawn.new("echo test")
System.print("PID: %(child.pid)")
System.print("Is alive: %(child.isalive)")
child.expect(["test"])
child.wait()
System.print("Exit status: %(child.exitstatus)")
child.close()
System.print("\n--- Read Non-blocking ---")
child = Spawn.new("printf 'quick output'")
var data = child.readNonblocking(100, 1)
System.print("Read: %(data.trim())")
child.close()
System.print("\n--- Constants ---")
System.print("EOF constant: %(Pexpect.EOF)")
System.print("TIMEOUT constant: %(Pexpect.TIMEOUT)")
System.print("\n--- Pexpect.run() ---")
var output = Pexpect.run("echo 'Simple run'")
System.print("Output: %(output.trim())")
System.print("\n=== Demo Complete ===")

View File

@ -1,6 +1,6 @@
// retoor <retoor@molodetz.nl>
import "regex" for Regex
import "regex" for Regex, Match
System.print("=== Regex Module Demo ===\n")
@ -128,3 +128,35 @@ var sentence = "hello, world; foo bar"
var tokens = tokenSeparator.split(sentence)
System.print("Input: %(sentence)")
System.print("Tokens: %(tokens)")
System.print("\n--- Match Object ---")
var emailRe = Regex.new("(\\w+)@(\\w+)\\.(\\w+)")
var match = emailRe.match("Contact: alice@example.com for info")
if (match != null) {
System.print("Full match: %(match.text)")
System.print("Start index: %(match.start)")
System.print("End index: %(match.end)")
System.print("group(0): %(match.group(0))")
System.print("group(1): %(match.group(1))")
System.print("group(2): %(match.group(2))")
System.print("group(3): %(match.group(3))")
}
System.print("\n--- Groups Property ---")
var dateRe = Regex.new("(\\d{4})-(\\d{2})-(\\d{2})")
var dateMatch = dateRe.match("Date: 2024-01-15")
if (dateMatch != null) {
System.print("groups[0] (full match): %(dateMatch.groups[0])")
System.print("groups[1] (year): %(dateMatch.groups[1])")
System.print("groups[2] (month): %(dateMatch.groups[2])")
System.print("groups[3] (day): %(dateMatch.groups[3])")
System.print("Total groups: %(dateMatch.groups.count)")
}
System.print("\n--- Find All Matches ---")
var numRe = Regex.new("\\d+")
var allMatches = numRe.matchAll("Order 123 has 5 items at $99 each")
System.print("Found %(allMatches.count) numbers:")
for (m in allMatches) {
System.print(" '%(m.text)' at position %(m.start)-%(m.end)")
}

58
example/subprocess_async_demo.wren vendored Normal file
View File

@ -0,0 +1,58 @@
// retoor <retoor@molodetz.nl>
import "subprocess" for Popen
System.print("=== Concurrent Ping Demo (Parallel Streaming) ===\n")
class PingTask {
construct new(host, label) {
_host = host
_label = label
_done = false
_proc = Popen.new(["ping", "-c", "3", host])
}
tick() {
if (_done) return
var chunk = _proc.stdout.read()
if (chunk == "") {
_done = true
} else {
for (line in chunk.split("\n")) {
if (line.trim() != "") {
System.print("[%(_label)] %(line)")
}
}
}
}
isDone { _done }
label { _label }
host { _host }
}
var tasks = [
PingTask.new("127.0.0.1", "LOCAL"),
PingTask.new("8.8.8.8", "GOOGLE"),
PingTask.new("1.1.1.1", "CLOUDFLARE"),
PingTask.new("9.9.9.9", "QUAD9"),
PingTask.new("208.67.222.222", "OPENDNS")
]
System.print("Starting 5 concurrent pings...")
for (t in tasks) System.print(" %(t.label) -> %(t.host)")
System.print("")
while (true) {
var allDone = true
for (task in tasks) {
if (!task.isDone) {
allDone = false
task.tick()
}
}
if (allDone) break
}
System.print("")
System.print("All 5 pings completed concurrently.")

86
example/subprocess_concurrent_demo.wren vendored Normal file
View File

@ -0,0 +1,86 @@
// retoor <retoor@molodetz.nl>
import "subprocess" for Popen
System.print("=== Concurrent Ping Demo - 5 Processes ===")
System.print("")
var p1 = Popen.new(["ping", "-c", "3", "127.0.0.1"])
var p2 = Popen.new(["ping", "-c", "3", "8.8.8.8"])
var p3 = Popen.new(["ping", "-c", "3", "1.1.1.1"])
var p4 = Popen.new(["ping", "-c", "3", "9.9.9.9"])
var p5 = Popen.new(["ping", "-c", "3", "208.67.222.222"])
System.print("Started 5 ping processes simultaneously:")
System.print(" PID %(p1.pid) -> 127.0.0.1 (localhost)")
System.print(" PID %(p2.pid) -> 8.8.8.8 (Google DNS)")
System.print(" PID %(p3.pid) -> 1.1.1.1 (Cloudflare)")
System.print(" PID %(p4.pid) -> 9.9.9.9 (Quad9)")
System.print(" PID %(p5.pid) -> 208.67.222.222 (OpenDNS)")
System.print("")
var done1 = false
var done2 = false
var done3 = false
var done4 = false
var done5 = false
while (!done1 || !done2 || !done3 || !done4 || !done5) {
if (!done1) {
var chunk = p1.stdout.read()
if (chunk == "") {
done1 = true
} else {
for (line in chunk.split("\n")) {
if (line.trim() != "") System.print("[LOCAL ] %(line)")
}
}
}
if (!done2) {
var chunk = p2.stdout.read()
if (chunk == "") {
done2 = true
} else {
for (line in chunk.split("\n")) {
if (line.trim() != "") System.print("[GOOGLE ] %(line)")
}
}
}
if (!done3) {
var chunk = p3.stdout.read()
if (chunk == "") {
done3 = true
} else {
for (line in chunk.split("\n")) {
if (line.trim() != "") System.print("[CLOUDFLR] %(line)")
}
}
}
if (!done4) {
var chunk = p4.stdout.read()
if (chunk == "") {
done4 = true
} else {
for (line in chunk.split("\n")) {
if (line.trim() != "") System.print("[QUAD9 ] %(line)")
}
}
}
if (!done5) {
var chunk = p5.stdout.read()
if (chunk == "") {
done5 = true
} else {
for (line in chunk.split("\n")) {
if (line.trim() != "") System.print("[OPENDNS ] %(line)")
}
}
}
}
System.print("")
System.print("All 5 ping processes completed concurrently!")

74
example/subprocess_fiber_demo.wren vendored Normal file
View File

@ -0,0 +1,74 @@
// retoor <retoor@molodetz.nl>
import "subprocess" for Popen
import "scheduler" for Scheduler
class AsyncPing {
construct new(host, label) {
_host = host
_label = label
_output = []
_done = false
}
start() {
_proc = Popen.new(["ping", "-c", "3", _host])
_fiber = Fiber.new {
while (true) {
var chunk = _proc.stdout.read()
if (chunk == "") {
_done = true
Fiber.yield()
return
}
for (line in chunk.split("\n")) {
if (line.trim() != "") _output.add(line)
}
Fiber.yield()
}
}
return this
}
tick() {
if (!_done && !_fiber.isDone) _fiber.call()
}
isDone { _done || _fiber.isDone }
label { _label }
output { _output }
host { _host }
}
System.print("=== Async Fiber Demo - 5 Concurrent Pings ===")
System.print("")
var tasks = [
AsyncPing.new("127.0.0.1", "LOCAL ").start(),
AsyncPing.new("8.8.8.8", "GOOGLE ").start(),
AsyncPing.new("1.1.1.1", "CLOUDFLR").start(),
AsyncPing.new("9.9.9.9", "QUAD9 ").start(),
AsyncPing.new("208.67.222.222", "OPENDNS ").start()
]
System.print("Started 5 async ping tasks:")
for (t in tasks) System.print(" [%(t.label)] -> %(t.host)")
System.print("")
while (true) {
var allDone = true
for (task in tasks) {
if (!task.isDone) {
allDone = false
var before = task.output.count
task.tick()
for (i in before...task.output.count) {
System.print("[%(task.label)] %(task.output[i])")
}
}
}
if (allDone) break
}
System.print("")
System.print("All 5 async tasks completed!")

65
example/sysinfo_demo.wren vendored Normal file
View File

@ -0,0 +1,65 @@
// retoor <retoor@molodetz.nl>
import "sysinfo" for SysInfo
var formatBytes = Fn.new { |bytes|
if (bytes < 1024) return "%(bytes) B"
if (bytes < 1024 * 1024) return "%(((bytes / 1024) * 10).floor / 10) KB"
if (bytes < 1024 * 1024 * 1024) return "%(((bytes / 1024 / 1024) * 10).floor / 10) MB"
return "%(((bytes / 1024 / 1024 / 1024) * 10).floor / 10) GB"
}
System.print("=== SysInfo Module Demo ===\n")
System.print("--- System Information ---")
System.print("Hostname: %(SysInfo.hostname)")
System.print("Uptime: %(SysInfo.uptime) seconds")
System.print("\n--- Memory Information ---")
var total = SysInfo.totalMemory
var free = SysInfo.freeMemory
var used = total - free
var resident = SysInfo.residentMemory
var constrained = SysInfo.constrainedMemory
System.print("Total: %(formatBytes.call(total))")
System.print("Free: %(formatBytes.call(free))")
System.print("Used: %(formatBytes.call(used))")
System.print("Process RSS: %(formatBytes.call(resident))")
if (constrained > 0) {
System.print("Constrained: %(formatBytes.call(constrained))")
}
System.print("\n--- CPU Information ---")
var cpus = SysInfo.cpuInfo
System.print("CPU Count: %(cpus.count)")
for (i in 0...cpus.count) {
var cpu = cpus[i]
System.print(" CPU %(i): %(cpu["model"]) @ %(cpu["speed"]) MHz")
}
System.print("\n--- Load Average ---")
var load = SysInfo.loadAverage
System.print("1 min: %(load[0])")
System.print("5 min: %(load[1])")
System.print("15 min: %(load[2])")
System.print("\n--- Network Interfaces ---")
var interfaces = SysInfo.networkInterfaces
for (name in interfaces.keys) {
System.print("%(name):")
for (addr in interfaces[name]) {
System.print(" %(addr["family"]): %(addr["address"])")
System.print(" Internal: %(addr["internal"])")
System.print(" Netmask: %(addr["netmask"])")
System.print(" MAC: %(addr["mac"])")
}
}
System.print("\n--- High Resolution Timer ---")
var t1 = SysInfo.hrtime
var t2 = SysInfo.hrtime
System.print("Time 1: %(t1) ns")
System.print("Time 2: %(t2) ns")
System.print("Delta: %(t2 - t1) ns")

60
example/udp_demo.wren vendored Normal file
View File

@ -0,0 +1,60 @@
// retoor <retoor@molodetz.nl>
import "udp" for UdpSocket, UdpMessage
import "timer" for Timer
import "scheduler" for Scheduler
System.print("=== UDP Module Demo ===\n")
System.print("--- Basic UDP Echo Server ---")
var server = UdpSocket.new()
server.bind("127.0.0.1", 8888)
System.print("Server listening on %(server.localAddress):%(server.localPort)")
var client = UdpSocket.new()
client.bind("127.0.0.1", 0)
System.print("Client bound to %(client.localAddress):%(client.localPort)")
var messageCount = 0
var maxMessages = 3
Scheduler.add {
while (messageCount < maxMessages) {
var msg = server.receive()
if (msg == null) break
System.print("Server received: '%(msg[0])' from %(msg[1]):%(msg[2])")
server.send("Echo: %(msg[0])", msg[1], msg[2])
messageCount = messageCount + 1
}
server.close()
System.print("Server closed")
}
Scheduler.add {
var messages = ["Hello", "UDP", "World"]
for (m in messages) {
client.send(m, "127.0.0.1", 8888)
System.print("Client sent: '%(m)'")
var response = client.receive()
if (response != null) {
System.print("Client received: '%(response[0])'")
}
}
client.close()
System.print("Client closed")
}
Timer.sleep(500)
System.print("\n--- Broadcast Configuration ---")
var bcast = UdpSocket.new()
bcast.bind("0.0.0.0", 0)
bcast.setBroadcast(true)
System.print("Broadcast enabled on port %(bcast.localPort)")
bcast.close()
System.print("\nDemo complete!")

105
example/yaml_demo.wren vendored Normal file
View File

@ -0,0 +1,105 @@
// retoor <retoor@molodetz.nl>
import "yaml" for Yaml
System.print("=== YAML Module Demo ===\n")
System.print("--- Basic Parsing ---")
var simple = Yaml.parse("name: Wren-CLI\nversion: 0.4.0")
System.print("Name: %(simple["name"])")
System.print("Version: %(simple["version"])")
System.print("\n--- Nested Structures ---")
var nested = "
database:
host: localhost
port: 5432
credentials:
user: admin
password: secret
"
var config = Yaml.parse(nested)
System.print("Host: %(config["database"]["host"])")
System.print("Port: %(config["database"]["port"])")
System.print("User: %(config["database"]["credentials"]["user"])")
System.print("\n--- Lists ---")
var listYaml = "
languages:
- Wren
- C
- Python
"
var langs = Yaml.parse(listYaml)
System.print("Languages:")
for (lang in langs["languages"]) {
System.print(" - %(lang)")
}
System.print("\n--- Complex Structure (Navigation) ---")
var navYaml = "
sections:
- title: Getting Started
pages:
- file: index
title: Overview
- file: installation
title: Installation
- title: API Reference
pages:
- file: yaml
title: yaml
"
var nav = Yaml.parse(navYaml)
for (section in nav["sections"]) {
System.print("Section: %(section["title"])")
for (page in section["pages"]) {
System.print(" - %(page["title"]) (%(page["file"]))")
}
}
System.print("\n--- Data Types ---")
var types = "
string: hello world
number: 42
float: 3.14159
bool_true: true
bool_false: false
null_value: null
quoted: \"with spaces\"
"
var data = Yaml.parse(types)
System.print("String: %(data["string"]) (%(data["string"].type))")
System.print("Number: %(data["number"]) (%(data["number"].type))")
System.print("Float: %(data["float"]) (%(data["float"].type))")
System.print("Bool true: %(data["bool_true"]) (%(data["bool_true"].type))")
System.print("Bool false: %(data["bool_false"]) (%(data["bool_false"].type))")
System.print("Null: %(data["null_value"])")
System.print("Quoted: %(data["quoted"])")
System.print("\n--- Stringify ---")
var obj = {
"name": "Example",
"count": 42,
"active": true,
"items": ["one", "two", "three"],
"nested": {
"key": "value"
}
}
System.print("Serialized:")
System.print(Yaml.stringify(obj))
System.print("\n--- Round-trip ---")
var original = "
title: Test
items:
- first
- second
"
var parsed = Yaml.parse(original)
var reserialized = Yaml.stringify(parsed)
var reparsed = Yaml.parse(reserialized)
System.print("Original title: %(parsed["title"])")
System.print("Reparsed title: %(reparsed["title"])")
System.print("Items match: %(parsed["items"][0] == reparsed["items"][0])")

View File

@ -1,196 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>scheduler - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html" class="active">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>scheduler</span>
</nav>
<article>
<h1>scheduler</h1>
<p>The <code>scheduler</code> module manages the async event loop and fiber scheduling. It is the foundation for all async operations in Wren-CLI.</p>
<pre><code>import "scheduler" for Scheduler</code></pre>
<h2>Scheduler Class</h2>
<div class="class-header">
<h3>Scheduler</h3>
<p>Async fiber scheduler</p>
</div>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Scheduler.await_</span>(<span class="param">block</span>) → <span class="type">any</span>
</div>
<p>Suspends the current fiber until an async operation completes. Used internally by modules to implement async operations.</p>
<ul class="param-list">
<li><span class="param-name">block</span> <span class="param-type">(Fn)</span> - Block that initiates the async operation</li>
<li><span class="returns">Returns:</span> Result of the async operation</li>
</ul>
<pre><code>var result = Scheduler.await_ {
Timer.sleep_(1000, Fiber.current)
}</code></pre>
<div class="method-signature">
<span class="method-name">Scheduler.resume_</span>(<span class="param">fiber</span>)
</div>
<p>Resumes a suspended fiber. Used by native code to wake up fibers when async operations complete.</p>
<div class="method-signature">
<span class="method-name">Scheduler.resume_</span>(<span class="param">fiber</span>, <span class="param">value</span>)
</div>
<p>Resumes a fiber with a result value.</p>
<h2>How Async Works</h2>
<p>Wren-CLI uses an event loop (libuv) for non-blocking I/O. Here is how async operations work:</p>
<ol>
<li>A Wren fiber calls an async method (e.g., <code>Http.get</code>)</li>
<li>The method starts a native async operation and suspends the fiber</li>
<li>The event loop continues processing other events</li>
<li>When the operation completes, the fiber is resumed with the result</li>
</ol>
<h3>Under the Hood</h3>
<pre><code>// How Timer.sleep works internally
class Timer {
static sleep(ms) {
return Scheduler.await_ {
Timer.sleep_(ms, Fiber.current)
}
}
foreign static sleep_(ms, fiber)
}</code></pre>
<h2>Examples</h2>
<h3>Sequential vs Parallel</h3>
<p>Operations are sequential by default:</p>
<pre><code>import "http" for Http
// These run one after another (sequential)
var r1 = Http.get("https://api.example.com/1")
var r2 = Http.get("https://api.example.com/2")
var r3 = Http.get("https://api.example.com/3")</code></pre>
<p>For parallel operations, use multiple fibers:</p>
<pre><code>import "http" for Http
var fibers = [
Fiber.new { Http.get("https://api.example.com/1") },
Fiber.new { Http.get("https://api.example.com/2") },
Fiber.new { Http.get("https://api.example.com/3") }
]
// Start all fibers
for (f in fibers) f.call()
// Results are already available when fibers complete</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>The scheduler is used internally by modules. Most user code does not need to interact with it directly. Use higher-level modules like <code>timer</code>, <code>http</code>, and <code>io</code> instead.</p>
</div>
<div class="admonition tip">
<div class="admonition-title">Tip</div>
<p>Async operations in Wren-CLI are cooperative, not preemptive. A fiber runs until it explicitly yields or calls an async operation.</p>
</div>
</article>
<footer class="page-footer">
<a href="pathlib.html" class="prev">pathlib</a>
<a href="math.html" class="next">math</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>

View File

@ -1,182 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>subprocess - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html" class="active">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>subprocess</span>
</nav>
<article>
<h1>subprocess</h1>
<p>The <code>subprocess</code> module allows running external commands and capturing their output.</p>
<pre><code>import "subprocess" for Subprocess</code></pre>
<h2>Subprocess Class</h2>
<div class="class-header">
<h3>Subprocess</h3>
<p>External process execution</p>
</div>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Subprocess.run</span>(<span class="param">command</span>) → <span class="type">Map</span>
</div>
<p>Runs a command and waits for it to complete.</p>
<ul class="param-list">
<li><span class="param-name">command</span> <span class="param-type">(List)</span> - Command and arguments as a list</li>
<li><span class="returns">Returns:</span> Map with "stdout", "stderr", and "exitCode"</li>
</ul>
<pre><code>var result = Subprocess.run(["ls", "-la"])
System.print("Exit code: %(result["exitCode"])")
System.print("Output: %(result["stdout"])")</code></pre>
<div class="method-signature">
<span class="method-name">Subprocess.run</span>(<span class="param">command</span>, <span class="param">input</span>) → <span class="type">Map</span>
</div>
<p>Runs a command with input data provided to stdin.</p>
<ul class="param-list">
<li><span class="param-name">command</span> <span class="param-type">(List)</span> - Command and arguments</li>
<li><span class="param-name">input</span> <span class="param-type">(String)</span> - Input to send to stdin</li>
</ul>
<pre><code>var result = Subprocess.run(["cat"], "Hello, World!")
System.print(result["stdout"]) // Hello, World!</code></pre>
<h2>Examples</h2>
<h3>Running Commands</h3>
<pre><code>import "subprocess" for Subprocess
var result = Subprocess.run(["echo", "Hello"])
System.print(result["stdout"]) // Hello
var files = Subprocess.run(["ls", "-la", "/tmp"])
System.print(files["stdout"])</code></pre>
<h3>Checking Exit Codes</h3>
<pre><code>import "subprocess" for Subprocess
var result = Subprocess.run(["grep", "pattern", "file.txt"])
if (result["exitCode"] == 0) {
System.print("Found: %(result["stdout"])")
} else {
System.print("Not found or error")
}</code></pre>
<h3>Processing Data</h3>
<pre><code>import "subprocess" for Subprocess
import "json" for Json
var result = Subprocess.run(["curl", "-s", "https://api.example.com/data"])
if (result["exitCode"] == 0) {
var data = Json.parse(result["stdout"])
System.print(data)
}</code></pre>
<h3>Piping Data</h3>
<pre><code>import "subprocess" for Subprocess
var input = "line1\nline2\nline3"
var result = Subprocess.run(["wc", "-l"], input)
System.print("Lines: %(result["stdout"].trim())")</code></pre>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Commands are not executed through a shell. Use explicit command and argument lists, not shell command strings.</p>
</div>
</article>
<footer class="page-footer">
<a href="signal.html" class="prev">signal</a>
<a href="sqlite.html" class="next">sqlite</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>

View File

@ -1,204 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>timer - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html" class="active">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>timer</span>
</nav>
<article>
<h1>timer</h1>
<p>The <code>timer</code> module provides timing functionality for delays and intervals.</p>
<pre><code>import "timer" for Timer</code></pre>
<h2>Timer Class</h2>
<div class="class-header">
<h3>Timer</h3>
<p>Timer and delay functionality</p>
</div>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Timer.sleep</span>(<span class="param">milliseconds</span>)
</div>
<p>Pauses execution for the specified duration. The fiber suspends and other operations can proceed.</p>
<ul class="param-list">
<li><span class="param-name">milliseconds</span> <span class="param-type">(Num)</span> - Duration to sleep in milliseconds</li>
</ul>
<pre><code>System.print("Starting...")
Timer.sleep(1000) // Wait 1 second
System.print("Done!")</code></pre>
<h2>Examples</h2>
<h3>Basic Delay</h3>
<pre><code>import "timer" for Timer
System.print("Starting...")
Timer.sleep(2000)
System.print("2 seconds later...")</code></pre>
<h3>Polling Loop</h3>
<pre><code>import "timer" for Timer
import "http" for Http
var checkStatus = Fn.new {
var response = Http.get("https://api.example.com/status")
return response.json["ready"]
}
while (!checkStatus.call()) {
System.print("Waiting...")
Timer.sleep(5000) // Check every 5 seconds
}
System.print("Ready!")</code></pre>
<h3>Rate Limiting</h3>
<pre><code>import "timer" for Timer
import "http" for Http
var urls = [
"https://api.example.com/1",
"https://api.example.com/2",
"https://api.example.com/3"
]
for (url in urls) {
var response = Http.get(url)
System.print("%(url): %(response.statusCode)")
Timer.sleep(1000) // 1 second between requests
}</code></pre>
<h3>Timeout Pattern</h3>
<pre><code>import "timer" for Timer
import "datetime" for DateTime, Duration
var start = DateTime.now()
var timeout = Duration.fromSeconds(30)
while (true) {
var elapsed = DateTime.now() - start
if (elapsed.seconds >= timeout.seconds) {
System.print("Timeout!")
break
}
// Do work...
Timer.sleep(100)
}</code></pre>
<h3>Animation/Progress</h3>
<pre><code>import "timer" for Timer
var frames = ["-", "\\", "|", "/"]
var i = 0
for (step in 1..20) {
System.write("\rProcessing %(frames[i]) ")
i = (i + 1) % 4
Timer.sleep(100)
}
System.print("\rDone! ")</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Timer.sleep is non-blocking at the event loop level. The current fiber suspends, but other scheduled operations can continue.</p>
</div>
</article>
<footer class="page-footer">
<a href="datetime.html" class="prev">datetime</a>
<a href="io.html" class="next">io</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>

View File

@ -1,226 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>uuid - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
<li><a href="uuid.html" class="active">uuid</a></li>
<li><a href="html.html">html</a></li>
<li><a href="argparse.html">argparse</a></li>
<li><a href="wdantic.html">wdantic</a></li>
<li><a href="dataset.html">dataset</a></li>
<li><a href="markdown.html">markdown</a></li>
<li><a href="web.html">web</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>uuid</span>
</nav>
<article>
<h1>uuid</h1>
<p>The <code>uuid</code> module provides UUID (Universally Unique Identifier) generation and validation. It supports version 4 UUIDs which are randomly generated.</p>
<pre><code>import "uuid" for Uuid</code></pre>
<h2>Uuid Class</h2>
<div class="class-header">
<h3>Uuid</h3>
<p>UUID generation and validation</p>
</div>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Uuid.v4</span>() → <span class="type">String</span>
</div>
<p>Generates a new random version 4 UUID.</p>
<ul class="param-list">
<li><span class="returns">Returns:</span> A 36-character UUID string in the format <code>xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx</code></li>
</ul>
<pre><code>var id = Uuid.v4()
System.print(id) // e.g., "550e8400-e29b-41d4-a716-446655440000"</code></pre>
<div class="method-signature">
<span class="method-name">Uuid.isValid</span>(<span class="param">string</span>) → <span class="type">Bool</span>
</div>
<p>Checks if a string is a valid UUID format (any version).</p>
<ul class="param-list">
<li><span class="param-name">string</span> <span class="param-type">(String)</span> - The string to validate</li>
<li><span class="returns">Returns:</span> <code>true</code> if the string is a valid UUID format, <code>false</code> otherwise</li>
</ul>
<pre><code>System.print(Uuid.isValid("550e8400-e29b-41d4-a716-446655440000")) // true
System.print(Uuid.isValid("not-a-uuid")) // false
System.print(Uuid.isValid("550e8400e29b41d4a716446655440000")) // false (missing hyphens)</code></pre>
<div class="method-signature">
<span class="method-name">Uuid.isV4</span>(<span class="param">string</span>) → <span class="type">Bool</span>
</div>
<p>Checks if a string is a valid version 4 UUID.</p>
<ul class="param-list">
<li><span class="param-name">string</span> <span class="param-type">(String)</span> - The string to validate</li>
<li><span class="returns">Returns:</span> <code>true</code> if the string is a valid v4 UUID, <code>false</code> otherwise</li>
</ul>
<pre><code>var id = Uuid.v4()
System.print(Uuid.isV4(id)) // true
System.print(Uuid.isV4("550e8400-e29b-11d4-a716-446655440000")) // false (version 1)</code></pre>
<h2>UUID Format</h2>
<p>UUIDs are 128-bit identifiers represented as 32 hexadecimal characters separated by hyphens into five groups:</p>
<pre><code>xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
8 4 4 4 12 = 32 hex chars + 4 hyphens = 36 chars</code></pre>
<table>
<tr>
<th>Position</th>
<th>Description</th>
</tr>
<tr>
<td>M</td>
<td>Version number (4 for v4 UUIDs)</td>
</tr>
<tr>
<td>N</td>
<td>Variant (8, 9, a, or b for RFC 4122 UUIDs)</td>
</tr>
</table>
<h2>Examples</h2>
<h3>Generating Unique Identifiers</h3>
<pre><code>import "uuid" for Uuid
var userId = Uuid.v4()
var sessionId = Uuid.v4()
System.print("User ID: %(userId)")
System.print("Session ID: %(sessionId)")</code></pre>
<h3>Validating User Input</h3>
<pre><code>import "uuid" for Uuid
var input = "550e8400-e29b-41d4-a716-446655440000"
if (Uuid.isValid(input)) {
System.print("Valid UUID")
if (Uuid.isV4(input)) {
System.print("This is a v4 UUID")
}
} else {
System.print("Invalid UUID format")
}</code></pre>
<h3>Using with Database Records</h3>
<pre><code>import "uuid" for Uuid
class User {
construct new(name) {
_id = Uuid.v4()
_name = name
}
id { _id }
name { _name }
}
var user = User.new("Alice")
System.print("Created user %(user.name) with ID %(user.id)")</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Version 4 UUIDs are generated using cryptographically secure random bytes from the <code>crypto</code> module. The probability of generating duplicate UUIDs is astronomically low.</p>
</div>
</article>
<footer class="page-footer">
<a href="math.html" class="prev">math</a>
<a href="html.html" class="next">html</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>

View File

@ -1,553 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>web - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
<li><a href="uuid.html">uuid</a></li>
<li><a href="html.html">html</a></li>
<li><a href="argparse.html">argparse</a></li>
<li><a href="wdantic.html">wdantic</a></li>
<li><a href="dataset.html">dataset</a></li>
<li><a href="markdown.html">markdown</a></li>
<li><a href="web.html" class="active">web</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>web</span>
</nav>
<article>
<h1>web</h1>
<p>The <code>web</code> module provides a web framework for building HTTP servers and a client for making HTTP requests. It includes routing, middleware, sessions, static file serving, and class-based views.</p>
<pre><code>import "web" for Application, Router, Request, Response, View, Session, Client</code></pre>
<h2>Application Class</h2>
<div class="class-header">
<h3>Application</h3>
<p>HTTP server with routing and middleware</p>
</div>
<h3>Constructor</h3>
<div class="method-signature">
<span class="method-name">Application.new</span>() → <span class="type">Application</span>
</div>
<p>Creates a new web application.</p>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">router</span><span class="type">Router</span>
</div>
<p>Access to the underlying router.</p>
<div class="method-signature">
<span class="method-name">sessionStore</span><span class="type">SessionStore</span>
</div>
<p>Access to the session storage.</p>
<h3>Routing Methods</h3>
<div class="method-signature">
<span class="method-name">get</span>(<span class="param">path</span>, <span class="param">handler</span>) → <span class="type">Application</span>
</div>
<div class="method-signature">
<span class="method-name">post</span>(<span class="param">path</span>, <span class="param">handler</span>) → <span class="type">Application</span>
</div>
<div class="method-signature">
<span class="method-name">put</span>(<span class="param">path</span>, <span class="param">handler</span>) → <span class="type">Application</span>
</div>
<div class="method-signature">
<span class="method-name">delete</span>(<span class="param">path</span>, <span class="param">handler</span>) → <span class="type">Application</span>
</div>
<div class="method-signature">
<span class="method-name">patch</span>(<span class="param">path</span>, <span class="param">handler</span>) → <span class="type">Application</span>
</div>
<p>Register route handlers. Handler receives <code>Request</code> and returns <code>Response</code>.</p>
<ul class="param-list">
<li><span class="param-name">path</span> <span class="param-type">(String)</span> - URL path with optional parameters (<code>:param</code>) or wildcards (<code>*</code>)</li>
<li><span class="param-name">handler</span> <span class="param-type">(Fn)</span> - Function receiving Request, returning Response</li>
</ul>
<div class="method-signature">
<span class="method-name">addView</span>(<span class="param">path</span>, <span class="param">viewClass</span>) → <span class="type">Application</span>
</div>
<p>Registers a class-based view for all HTTP methods.</p>
<div class="method-signature">
<span class="method-name">static_</span>(<span class="param">prefix</span>, <span class="param">directory</span>) → <span class="type">Application</span>
</div>
<p>Serves static files from a directory.</p>
<pre><code>app.static_("/assets", "./public")</code></pre>
<div class="method-signature">
<span class="method-name">use</span>(<span class="param">middleware</span>) → <span class="type">Application</span>
</div>
<p>Adds middleware function. Middleware receives Request, returns Response or null to continue.</p>
<div class="method-signature">
<span class="method-name">run</span>(<span class="param">host</span>, <span class="param">port</span>)
</div>
<p>Starts the server (blocking).</p>
<h2>Request Class</h2>
<div class="class-header">
<h3>Request</h3>
<p>Incoming HTTP request</p>
</div>
<h3>Properties</h3>
<table>
<tr>
<th>Property</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td><code>method</code></td>
<td>String</td>
<td>HTTP method (GET, POST, etc.)</td>
</tr>
<tr>
<td><code>path</code></td>
<td>String</td>
<td>Request path without query string</td>
</tr>
<tr>
<td><code>query</code></td>
<td>Map</td>
<td>Parsed query parameters</td>
</tr>
<tr>
<td><code>headers</code></td>
<td>Map</td>
<td>Request headers</td>
</tr>
<tr>
<td><code>body</code></td>
<td>String</td>
<td>Raw request body</td>
</tr>
<tr>
<td><code>params</code></td>
<td>Map</td>
<td>Route parameters from URL</td>
</tr>
<tr>
<td><code>cookies</code></td>
<td>Map</td>
<td>Parsed cookies</td>
</tr>
<tr>
<td><code>session</code></td>
<td>Session</td>
<td>Session data</td>
</tr>
</table>
<h3>Methods</h3>
<div class="method-signature">
<span class="method-name">header</span>(<span class="param">name</span>) → <span class="type">String|null</span>
</div>
<p>Gets header value (case-insensitive).</p>
<div class="method-signature">
<span class="method-name">json</span><span class="type">Map|List</span>
</div>
<p>Parses body as JSON (cached).</p>
<div class="method-signature">
<span class="method-name">form</span><span class="type">Map</span>
</div>
<p>Parses body as form data (cached).</p>
<h2>Response Class</h2>
<div class="class-header">
<h3>Response</h3>
<p>HTTP response builder</p>
</div>
<h3>Static Factory Methods</h3>
<div class="method-signature">
<span class="method-name">Response.text</span>(<span class="param">content</span>) → <span class="type">Response</span>
</div>
<p>Creates plain text response.</p>
<div class="method-signature">
<span class="method-name">Response.html</span>(<span class="param">content</span>) → <span class="type">Response</span>
</div>
<p>Creates HTML response.</p>
<div class="method-signature">
<span class="method-name">Response.json</span>(<span class="param">data</span>) → <span class="type">Response</span>
</div>
<p>Creates JSON response (automatically stringifies).</p>
<div class="method-signature">
<span class="method-name">Response.redirect</span>(<span class="param">url</span>) → <span class="type">Response</span>
</div>
<div class="method-signature">
<span class="method-name">Response.redirect</span>(<span class="param">url</span>, <span class="param">status</span>) → <span class="type">Response</span>
</div>
<p>Creates redirect response (default 302).</p>
<div class="method-signature">
<span class="method-name">Response.file</span>(<span class="param">path</span>) → <span class="type">Response</span>
</div>
<div class="method-signature">
<span class="method-name">Response.file</span>(<span class="param">path</span>, <span class="param">contentType</span>) → <span class="type">Response</span>
</div>
<p>Serves a file. Auto-detects content type if not specified.</p>
<h3>Instance Methods</h3>
<div class="method-signature">
<span class="method-name">header</span>(<span class="param">name</span>, <span class="param">value</span>) → <span class="type">Response</span>
</div>
<p>Sets a response header.</p>
<div class="method-signature">
<span class="method-name">cookie</span>(<span class="param">name</span>, <span class="param">value</span>) → <span class="type">Response</span>
</div>
<div class="method-signature">
<span class="method-name">cookie</span>(<span class="param">name</span>, <span class="param">value</span>, <span class="param">options</span>) → <span class="type">Response</span>
</div>
<p>Sets a cookie. Options: <code>path</code>, <code>maxAge</code>, <code>httpOnly</code>, <code>secure</code>.</p>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">status</span><span class="type">Num</span>
</div>
<p>Gets or sets HTTP status code.</p>
<div class="method-signature">
<span class="method-name">body</span><span class="type">String</span>
</div>
<p>Gets or sets response body.</p>
<h2>View Class</h2>
<div class="class-header">
<h3>View</h3>
<p>Base class for class-based views</p>
</div>
<p>Subclass and override methods for each HTTP method:</p>
<pre><code>class UserView is View {
get(request) {
return Response.json({"users": []})
}
post(request) {
var data = request.json
return Response.json({"created": data})
}
}</code></pre>
<h2>Session Class</h2>
<div class="class-header">
<h3>Session</h3>
<p>Session data storage</p>
</div>
<h3>Properties and Methods</h3>
<div class="method-signature">
<span class="method-name">id</span><span class="type">String</span>
</div>
<p>The session ID.</p>
<div class="method-signature">
<span class="method-name">[key]</span><span class="type">any</span>
</div>
<p>Gets session value.</p>
<div class="method-signature">
<span class="method-name">[key]=</span>(<span class="param">value</span>)
</div>
<p>Sets session value.</p>
<div class="method-signature">
<span class="method-name">remove</span>(<span class="param">key</span>)
</div>
<p>Removes a session key.</p>
<h2>Client Class</h2>
<div class="class-header">
<h3>Client</h3>
<p>HTTP client for making requests</p>
</div>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Client.get</span>(<span class="param">url</span>) → <span class="type">Map</span>
</div>
<div class="method-signature">
<span class="method-name">Client.get</span>(<span class="param">url</span>, <span class="param">options</span>) → <span class="type">Map</span>
</div>
<p>Makes GET request.</p>
<div class="method-signature">
<span class="method-name">Client.post</span>(<span class="param">url</span>) → <span class="type">Map</span>
</div>
<div class="method-signature">
<span class="method-name">Client.post</span>(<span class="param">url</span>, <span class="param">options</span>) → <span class="type">Map</span>
</div>
<p>Makes POST request.</p>
<div class="method-signature">
<span class="method-name">Client.put</span>(<span class="param">url</span>, <span class="param">options</span>) → <span class="type">Map</span>
</div>
<p>Makes PUT request.</p>
<div class="method-signature">
<span class="method-name">Client.delete</span>(<span class="param">url</span>, <span class="param">options</span>) → <span class="type">Map</span>
</div>
<p>Makes DELETE request.</p>
<h3>Client Options</h3>
<table>
<tr>
<th>Option</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td><code>headers</code></td>
<td>Map</td>
<td>Request headers</td>
</tr>
<tr>
<td><code>body</code></td>
<td>String</td>
<td>Request body</td>
</tr>
<tr>
<td><code>json</code></td>
<td>Map/List</td>
<td>JSON body (auto-stringifies and sets Content-Type)</td>
</tr>
</table>
<h3>Response Format</h3>
<pre><code>{
"status": 200,
"headers": {"Content-Type": "application/json"},
"body": "{...}"
}</code></pre>
<h2>Examples</h2>
<h3>Basic Server</h3>
<pre><code>import "web" for Application, Response
var app = Application.new()
app.get("/", Fn.new { |req|
return Response.html("&lt;h1&gt;Hello World&lt;/h1&gt;")
})
app.get("/api/users", Fn.new { |req|
return Response.json({"users": ["Alice", "Bob"]})
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>Route Parameters</h3>
<pre><code>import "web" for Application, Response
var app = Application.new()
app.get("/users/:id", Fn.new { |req|
var userId = req.params["id"]
return Response.json({"id": userId})
})
app.get("/files/*", Fn.new { |req|
return Response.text("Wildcard route")
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>Class-Based Views</h3>
<pre><code>import "web" for Application, Response, View
class ArticleView is View {
get(request) {
return Response.json({"articles": []})
}
post(request) {
var data = request.json
return Response.json({"created": data})
}
}
var app = Application.new()
app.addView("/articles", ArticleView)
app.run("0.0.0.0", 8080)</code></pre>
<h3>Sessions</h3>
<pre><code>import "web" for Application, Response
var app = Application.new()
app.get("/login", Fn.new { |req|
req.session["user"] = "Alice"
return Response.text("Logged in")
})
app.get("/profile", Fn.new { |req|
var user = req.session["user"]
if (user == null) {
return Response.redirect("/login")
}
return Response.text("Hello, %(user)")
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>Middleware</h3>
<pre><code>import "web" for Application, Response
var app = Application.new()
app.use(Fn.new { |req|
System.print("%(req.method) %(req.path)")
return null
})
app.use(Fn.new { |req|
if (req.path.startsWith("/admin") &amp;&amp; !req.session["isAdmin"]) {
return Response.redirect("/login")
}
return null
})
app.get("/", Fn.new { |req|
return Response.text("Home")
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>HTTP Client</h3>
<pre><code>import "web" for Client
import "json" for Json
var response = Client.get("https://api.example.com/data")
System.print("Status: %(response["status"])")
System.print("Body: %(response["body"])")
var postResponse = Client.post("https://api.example.com/users", {
"json": {"name": "Alice", "email": "alice@example.com"}
})
var data = Json.parse(postResponse["body"])</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Sessions are stored in memory by default. Data is lost when the server restarts. For production, consider persisting session data to a database.</p>
</div>
</article>
<footer class="page-footer">
<a href="markdown.html" class="prev">markdown</a>
<a href="index.html" class="next">Overview</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>

View File

@ -1,727 +0,0 @@
/* retoor <retoor@molodetz.nl> */
:root {
--sidebar-bg: #343131;
--sidebar-text: #d9d9d9;
--sidebar-link: #b3b3b3;
--sidebar-link-hover: #ffffff;
--sidebar-active: #2980b9;
--primary: #2980b9;
--primary-dark: #1a5276;
--text: #404040;
--heading: #2c3e50;
--code-bg: #f4f4f4;
--code-border: #e1e4e5;
--border: #e1e4e5;
--sidebar-width: 280px;
--content-max-width: 900px;
--link: #2980b9;
--link-hover: #1a5276;
--warning-bg: #fff3cd;
--warning-border: #ffc107;
--note-bg: #e7f2fa;
--note-border: #6ab0de;
--tip-bg: #dff0d8;
--tip-border: #3c763d;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 16px;
scroll-behavior: smooth;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.6;
color: var(--text);
background: #ffffff;
}
.container {
display: flex;
min-height: 100vh;
}
.sidebar {
position: fixed;
top: 0;
left: 0;
width: var(--sidebar-width);
height: 100vh;
background: var(--sidebar-bg);
color: var(--sidebar-text);
overflow-y: auto;
z-index: 100;
}
.sidebar-header {
padding: 20px;
background: rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.sidebar-header h1 {
font-size: 1.5rem;
font-weight: 700;
margin: 0;
}
.sidebar-header h1 a {
color: #ffffff;
text-decoration: none;
}
.sidebar-header .version {
font-size: 0.85rem;
color: var(--sidebar-link);
margin-top: 5px;
}
.sidebar-nav {
padding: 15px 0;
}
.sidebar-nav .section {
margin-bottom: 10px;
}
.sidebar-nav .section-title {
display: block;
padding: 8px 20px;
font-size: 0.85rem;
font-weight: 700;
text-transform: uppercase;
color: var(--sidebar-text);
letter-spacing: 0.05em;
}
.sidebar-nav ul {
list-style: none;
}
.sidebar-nav li a {
display: block;
padding: 6px 20px 6px 30px;
color: var(--sidebar-link);
text-decoration: none;
font-size: 0.9rem;
transition: color 0.15s, background 0.15s;
}
.sidebar-nav li a:hover {
color: var(--sidebar-link-hover);
background: rgba(255, 255, 255, 0.05);
}
.sidebar-nav li a.active {
color: var(--sidebar-active);
background: rgba(255, 255, 255, 0.05);
border-left: 3px solid var(--sidebar-active);
padding-left: 27px;
}
.sidebar-nav li.nested a {
padding-left: 45px;
font-size: 0.85rem;
}
.content {
flex: 1;
margin-left: var(--sidebar-width);
padding: 40px 50px;
max-width: calc(var(--content-max-width) + var(--sidebar-width) + 100px);
}
article {
max-width: var(--content-max-width);
}
h1, h2, h3, h4, h5, h6 {
color: var(--heading);
font-weight: 700;
line-height: 1.3;
margin-top: 1.5em;
margin-bottom: 0.5em;
}
h1 {
font-size: 2rem;
margin-top: 0;
padding-bottom: 0.3em;
border-bottom: 1px solid var(--border);
}
h2 {
font-size: 1.5rem;
margin-top: 2em;
}
h3 {
font-size: 1.25rem;
}
h4 {
font-size: 1.1rem;
}
p {
margin-bottom: 1em;
}
a {
color: var(--link);
text-decoration: none;
}
a:hover {
color: var(--link-hover);
text-decoration: underline;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Monaco, monospace;
font-size: 0.9em;
background: var(--code-bg);
padding: 2px 6px;
border-radius: 3px;
border: 1px solid var(--code-border);
}
pre {
background: var(--code-bg);
border: 1px solid var(--code-border);
border-radius: 4px;
padding: 15px 20px;
overflow-x: auto;
margin: 1em 0;
position: relative;
}
pre code {
background: none;
border: none;
padding: 0;
font-size: 0.875rem;
line-height: 1.5;
}
.code-block {
position: relative;
}
.code-block .copy-btn {
position: absolute;
top: 8px;
right: 8px;
padding: 4px 10px;
font-size: 0.75rem;
background: var(--primary);
color: #ffffff;
border: none;
border-radius: 3px;
cursor: pointer;
opacity: 0;
transition: opacity 0.2s;
}
.code-block:hover .copy-btn {
opacity: 1;
}
.copy-btn:hover {
background: var(--primary-dark);
}
.copy-btn.copied {
background: var(--tip-border);
}
.method-signature {
background: #f8f8f8;
border-left: 4px solid var(--primary);
padding: 12px 16px;
margin: 1em 0;
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 0.9rem;
overflow-x: auto;
}
.method-signature .method-name {
color: var(--primary-dark);
font-weight: 600;
}
.method-signature .param {
color: #c0392b;
}
.method-signature .type {
color: #27ae60;
}
ul, ol {
margin: 1em 0;
padding-left: 2em;
}
li {
margin-bottom: 0.5em;
}
ul.param-list {
list-style: none;
padding-left: 0;
margin: 0.5em 0 1em;
}
ul.param-list li {
padding: 4px 0;
padding-left: 1.5em;
position: relative;
}
ul.param-list li::before {
content: "•";
position: absolute;
left: 0.5em;
color: var(--primary);
}
.param-name {
font-family: monospace;
background: var(--code-bg);
padding: 1px 5px;
border-radius: 2px;
}
.param-type {
color: #27ae60;
font-size: 0.9em;
}
.returns {
color: #8e44ad;
font-weight: 500;
}
table {
width: 100%;
border-collapse: collapse;
margin: 1em 0;
}
th, td {
padding: 10px 12px;
text-align: left;
border: 1px solid var(--border);
}
th {
background: var(--code-bg);
font-weight: 600;
}
tr:nth-child(even) {
background: #fafafa;
}
.admonition {
padding: 15px 20px;
margin: 1.5em 0;
border-radius: 4px;
border-left: 4px solid;
}
.admonition-title {
font-weight: 700;
margin-bottom: 0.5em;
}
.admonition.note {
background: var(--note-bg);
border-color: var(--note-border);
}
.admonition.warning {
background: var(--warning-bg);
border-color: var(--warning-border);
}
.admonition.tip {
background: var(--tip-bg);
border-color: var(--tip-border);
}
.page-footer {
display: flex;
justify-content: space-between;
margin-top: 3em;
padding-top: 1.5em;
border-top: 1px solid var(--border);
}
.page-footer a {
display: inline-flex;
align-items: center;
padding: 8px 16px;
background: var(--code-bg);
border-radius: 4px;
font-weight: 500;
}
.page-footer a:hover {
background: var(--border);
text-decoration: none;
}
.page-footer .prev::before {
content: "← ";
}
.page-footer .next::after {
content: " →";
}
.breadcrumb {
margin-bottom: 1.5em;
font-size: 0.9rem;
color: #666;
}
.breadcrumb a {
color: #666;
}
.breadcrumb a:hover {
color: var(--primary);
}
.breadcrumb .separator {
margin: 0 0.5em;
}
.mobile-menu-toggle {
display: none;
position: fixed;
top: 15px;
left: 15px;
z-index: 200;
background: var(--sidebar-bg);
color: #ffffff;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
}
.hero {
text-align: center;
padding: 60px 20px;
background: linear-gradient(135deg, var(--heading) 0%, var(--primary) 100%);
color: #ffffff;
margin: -40px -50px 40px;
border-radius: 0 0 8px 8px;
}
.hero h1 {
color: #ffffff;
border: none;
font-size: 2.5rem;
margin-bottom: 0.5em;
}
.hero p {
font-size: 1.2rem;
opacity: 0.9;
max-width: 600px;
margin: 0 auto 1.5em;
}
.hero-buttons {
display: flex;
gap: 15px;
justify-content: center;
flex-wrap: wrap;
}
.hero-buttons a {
padding: 12px 24px;
border-radius: 4px;
font-weight: 600;
text-decoration: none;
transition: transform 0.15s, box-shadow 0.15s;
}
.hero-buttons .primary-btn {
background: #ffffff;
color: var(--primary);
}
.hero-buttons .secondary-btn {
background: rgba(255, 255, 255, 0.15);
color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.3);
}
.hero-buttons a:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin: 2em 0;
}
.card {
background: #ffffff;
border: 1px solid var(--border);
border-radius: 8px;
padding: 20px;
transition: box-shadow 0.2s, transform 0.2s;
}
.card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.card h3 {
margin-top: 0;
margin-bottom: 0.5em;
}
.card h3 a {
color: var(--heading);
}
.card p {
margin: 0;
color: #666;
font-size: 0.95rem;
}
.module-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 15px;
margin: 1.5em 0;
}
.module-card {
display: block;
padding: 15px;
background: var(--code-bg);
border: 1px solid var(--border);
border-radius: 6px;
text-align: center;
font-weight: 500;
color: var(--heading);
transition: all 0.2s;
}
.module-card:hover {
background: var(--primary);
color: #ffffff;
text-decoration: none;
border-color: var(--primary);
}
.toc {
background: var(--code-bg);
border: 1px solid var(--border);
border-radius: 4px;
padding: 20px;
margin: 1.5em 0;
}
.toc h4 {
margin: 0 0 10px;
font-size: 1rem;
}
.toc ul {
margin: 0;
padding-left: 1.5em;
}
.toc li {
margin-bottom: 0.3em;
}
.api-section {
margin: 2em 0;
padding: 1.5em;
background: #fafafa;
border-radius: 8px;
border: 1px solid var(--border);
}
.api-section h3 {
margin-top: 0;
color: var(--primary);
}
.class-header {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
padding: 20px;
border-radius: 8px;
margin: 1.5em 0 1em;
border-left: 4px solid var(--primary);
}
.class-header h3 {
margin: 0;
color: var(--heading);
}
.class-header p {
margin: 0.5em 0 0;
color: #666;
}
@media (max-width: 900px) {
.sidebar {
transform: translateX(-100%);
transition: transform 0.3s;
}
.sidebar.open {
transform: translateX(0);
}
.mobile-menu-toggle {
display: block;
}
.content {
margin-left: 0;
padding: 70px 20px 40px;
}
.hero {
margin: -70px -20px 30px;
padding: 80px 20px 40px;
}
.hero h1 {
font-size: 1.8rem;
}
h1 {
font-size: 1.6rem;
}
h2 {
font-size: 1.3rem;
}
pre {
padding: 12px;
}
.page-footer {
flex-direction: column;
gap: 10px;
}
.page-footer a {
text-align: center;
}
}
.sidebar-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 50;
}
.sidebar-overlay.visible {
display: block;
}
.search-box {
padding: 15px 20px;
}
.search-box input {
width: 100%;
padding: 8px 12px;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 4px;
background: rgba(255, 255, 255, 0.1);
color: #ffffff;
font-size: 0.9rem;
}
.search-box input::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.search-box input:focus {
outline: none;
border-color: var(--primary);
background: rgba(255, 255, 255, 0.15);
}
.example-output {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px 20px;
border-radius: 4px;
margin: 1em 0;
font-family: monospace;
font-size: 0.875rem;
}
.example-output::before {
content: "Output:";
display: block;
color: #888;
margin-bottom: 8px;
font-size: 0.8rem;
}
.deprecated {
opacity: 0.7;
text-decoration: line-through;
}
.badge {
display: inline-block;
padding: 2px 8px;
font-size: 0.75rem;
font-weight: 600;
border-radius: 3px;
margin-left: 8px;
vertical-align: middle;
}
.badge-new {
background: #27ae60;
color: #ffffff;
}
.badge-experimental {
background: #f39c12;
color: #ffffff;
}
.badge-async {
background: #9b59b6;
color: #ffffff;
}

View File

@ -1,306 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>First Script - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="installation.html">Installation</a></li>
<li><a href="first-script.html" class="active">First Script</a></li>
<li><a href="repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">Getting Started</a>
<span class="separator">/</span>
<span>First Script</span>
</nav>
<article>
<h1>First Script</h1>
<p>This guide walks you through creating and running your first Wren script. You will learn the basics of printing output, using variables, and importing modules.</p>
<h2>Hello World</h2>
<p>Create a file named <code>hello.wren</code> with the following content:</p>
<pre><code>System.print("Hello, World!")</code></pre>
<p>Run it:</p>
<pre><code>bin/wren_cli hello.wren</code></pre>
<div class="example-output">Hello, World!</div>
<p>The <code>System.print</code> method outputs text to the console followed by a newline.</p>
<h2>Variables and Types</h2>
<p>Wren is dynamically typed. Use <code>var</code> to declare variables:</p>
<pre><code>var name = "Wren"
var version = 0.4
var features = ["fast", "small", "class-based"]
var active = true
System.print("Language: %(name)")
System.print("Version: %(version)")
System.print("Features: %(features)")
System.print("Active: %(active)")</code></pre>
<div class="example-output">Language: Wren
Version: 0.4
Features: [fast, small, class-based]
Active: true</div>
<p>The <code>%(expression)</code> syntax is string interpolation. It evaluates the expression and inserts the result into the string.</p>
<h2>Working with Lists and Maps</h2>
<pre><code>var numbers = [1, 2, 3, 4, 5]
System.print("Count: %(numbers.count)")
System.print("First: %(numbers[0])")
System.print("Last: %(numbers[-1])")
var person = {
"name": "Alice",
"age": 30,
"city": "Amsterdam"
}
System.print("Name: %(person["name"])")</code></pre>
<div class="example-output">Count: 5
First: 1
Last: 5
Name: Alice</div>
<h2>Control Flow</h2>
<pre><code>var score = 85
if (score >= 90) {
System.print("Grade: A")
} else if (score >= 80) {
System.print("Grade: B")
} else {
System.print("Grade: C")
}
for (i in 1..5) {
System.print("Count: %(i)")
}</code></pre>
<div class="example-output">Grade: B
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5</div>
<h2>Functions</h2>
<p>Functions in Wren are created using block syntax:</p>
<pre><code>var greet = Fn.new { |name|
return "Hello, %(name)!"
}
System.print(greet.call("World"))
var add = Fn.new { |a, b| a + b }
System.print(add.call(3, 4))</code></pre>
<div class="example-output">Hello, World!
7</div>
<h2>Classes</h2>
<pre><code>class Person {
construct new(name, age) {
_name = name
_age = age
}
name { _name }
age { _age }
greet() {
System.print("Hello, I'm %(_name)")
}
}
var alice = Person.new("Alice", 30)
alice.greet()
System.print("Age: %(alice.age)")</code></pre>
<div class="example-output">Hello, I'm Alice
Age: 30</div>
<h2>Using Modules</h2>
<p>Wren-CLI provides many built-in modules. Import them to use their functionality:</p>
<pre><code>import "io" for File
var content = File.read("hello.wren")
System.print("File contents:")
System.print(content)</code></pre>
<h3>Making HTTP Requests</h3>
<pre><code>import "http" for Http
import "json" for Json
var response = Http.get("https://httpbin.org/get")
System.print("Status: %(response.status)")
var data = Json.parse(response.body)
System.print("Origin: %(data["origin"])")</code></pre>
<h3>Working with JSON</h3>
<pre><code>import "json" for Json
var data = {
"name": "Wren",
"version": 0.4,
"features": ["fast", "small"]
}
var jsonString = Json.stringify(data)
System.print(jsonString)
var parsed = Json.parse(jsonString)
System.print("Name: %(parsed["name"])")</code></pre>
<h2>Error Handling</h2>
<p>Use <code>Fiber.try</code> to catch runtime errors:</p>
<pre><code>var fiber = Fiber.new {
var x = 1 / 0
}
var error = fiber.try()
if (error) {
System.print("Error: %(error)")
}</code></pre>
<h2>Script Arguments</h2>
<p>Access command-line arguments via <code>Process.arguments</code>:</p>
<pre><code>import "os" for Process
System.print("Script arguments:")
for (arg in Process.arguments) {
System.print(" %(arg)")
}</code></pre>
<p>Run with arguments:</p>
<pre><code>bin/wren_cli script.wren arg1 arg2 arg3</code></pre>
<h2>Exit Codes</h2>
<p>Wren-CLI uses these exit codes:</p>
<table>
<tr>
<th>Code</th>
<th>Meaning</th>
</tr>
<tr>
<td>0</td>
<td>Success</td>
</tr>
<tr>
<td>65</td>
<td>Compile error (syntax error)</td>
</tr>
<tr>
<td>70</td>
<td>Runtime error</td>
</tr>
</table>
<h2>Next Steps</h2>
<ul>
<li><a href="repl.html">Using the REPL</a> - Interactive experimentation</li>
<li><a href="../language/index.html">Language Reference</a> - Deep dive into Wren syntax</li>
<li><a href="../tutorials/index.html">Tutorials</a> - Build real applications</li>
</ul>
</article>
<footer class="page-footer">
<a href="installation.html" class="prev">Installation</a>
<a href="repl.html" class="next">Using the REPL</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>

View File

@ -1,205 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Getting Started - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="index.html" class="active">Overview</a></li>
<li><a href="installation.html">Installation</a></li>
<li><a href="first-script.html">First Script</a></li>
<li><a href="repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<span>Getting Started</span>
</nav>
<article>
<h1>Getting Started</h1>
<p>Welcome to Wren-CLI, a command-line interface for the Wren programming language extended with powerful modules for networking, file I/O, databases, and more.</p>
<div class="toc">
<h4>In This Section</h4>
<ul>
<li><a href="installation.html">Installation</a> - Build from source on Linux, macOS, or FreeBSD</li>
<li><a href="first-script.html">First Script</a> - Write and run your first Wren program</li>
<li><a href="repl.html">Using the REPL</a> - Interactive experimentation</li>
</ul>
</div>
<h2>What is Wren?</h2>
<p>Wren is a small, fast, class-based scripting language. It has a familiar syntax, first-class functions, and a fiber-based concurrency model. Wren-CLI extends the core language with modules for:</p>
<ul>
<li>HTTP and WebSocket networking</li>
<li>File and directory operations</li>
<li>SQLite database access</li>
<li>JSON and regex processing</li>
<li>Template rendering (Jinja2-compatible)</li>
<li>Cryptographic operations</li>
<li>Process and signal handling</li>
</ul>
<h2>Quick Start</h2>
<p>If you want to dive right in, here is the fastest path to running your first script:</p>
<h3>1. Build</h3>
<pre><code>git clone https://github.com/wren-lang/wren-cli.git
cd wren-cli
cd projects/make && make</code></pre>
<h3>2. Create a Script</h3>
<p>Create a file named <code>hello.wren</code>:</p>
<pre><code>System.print("Hello, Wren!")</code></pre>
<h3>3. Run</h3>
<pre><code>bin/wren_cli hello.wren</code></pre>
<div class="example-output">Hello, Wren!</div>
<h2>Key Concepts</h2>
<p>Before diving deeper, understand these fundamental Wren concepts:</p>
<h3>Everything is an Object</h3>
<p>In Wren, everything is an object, including numbers, strings, and functions. Every object is an instance of a class.</p>
<pre><code>var name = "Wren"
System.print(name.count) // 4
System.print(name.bytes[0]) // 87 (ASCII for 'W')</code></pre>
<h3>Fibers for Concurrency</h3>
<p>Wren uses fibers (cooperative threads) for concurrency. The scheduler module manages async operations.</p>
<pre><code>import "timer" for Timer
Timer.sleep(1000) // Pauses for 1 second
System.print("Done waiting")</code></pre>
<h3>Module System</h3>
<p>Functionality is organized into modules that you import as needed:</p>
<pre><code>import "http" for Http
import "json" for Json
var response = Http.get("https://api.example.com/data")
var data = Json.parse(response.body)</code></pre>
<h2>System Requirements</h2>
<table>
<tr>
<th>Platform</th>
<th>Requirements</th>
</tr>
<tr>
<td>Linux</td>
<td>GCC, Make, OpenSSL development libraries</td>
</tr>
<tr>
<td>macOS</td>
<td>Xcode Command Line Tools, OpenSSL (via Homebrew)</td>
</tr>
<tr>
<td>FreeBSD</td>
<td>GCC or Clang, gmake, OpenSSL</td>
</tr>
</table>
<h2>Next Steps</h2>
<p>Continue with the following sections to learn more:</p>
<ul>
<li><a href="installation.html">Installation</a> - Detailed build instructions for all platforms</li>
<li><a href="first-script.html">First Script</a> - A more detailed walkthrough of your first program</li>
<li><a href="../language/index.html">Language Reference</a> - Learn the Wren language syntax</li>
<li><a href="../api/index.html">API Reference</a> - Explore all available modules</li>
</ul>
</article>
<footer class="page-footer">
<a href="../index.html" class="prev">Home</a>
<a href="installation.html" class="next">Installation</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>

View File

@ -1,254 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Using the REPL - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="installation.html">Installation</a></li>
<li><a href="first-script.html">First Script</a></li>
<li><a href="repl.html" class="active">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">Getting Started</a>
<span class="separator">/</span>
<span>Using the REPL</span>
</nav>
<article>
<h1>Using the REPL</h1>
<p>The REPL (Read-Eval-Print Loop) is an interactive environment for experimenting with Wren code. It is useful for testing ideas, exploring APIs, and learning the language.</p>
<h2>Starting the REPL</h2>
<p>Run <code>wren_cli</code> without arguments to start the REPL:</p>
<pre><code>bin/wren_cli</code></pre>
<p>You will see a prompt:</p>
<div class="example-output">> </div>
<h2>Basic Usage</h2>
<p>Type expressions and press Enter to evaluate them:</p>
<pre><code>> 1 + 2
3
> "hello".count
5
> [1, 2, 3].map { |x| x * 2 }
[2, 4, 6]</code></pre>
<h2>Multi-line Input</h2>
<p>The REPL automatically detects incomplete expressions and waits for more input:</p>
<pre><code>> class Person {
| construct new(name) {
| _name = name
| }
| name { _name }
| }
null
> var p = Person.new("Alice")
null
> p.name
Alice</code></pre>
<p>The <code>|</code> prompt indicates the REPL is waiting for more input.</p>
<h2>Importing Modules</h2>
<p>Modules can be imported in the REPL just like in scripts:</p>
<pre><code>> import "json" for Json
null
> Json.stringify({"name": "Wren"})
{"name":"Wren"}
> import "math" for Math
null
> Math.sqrt(16)
4</code></pre>
<h2>Variables Persist</h2>
<p>Variables defined in the REPL persist across lines:</p>
<pre><code>> var x = 10
null
> var y = 20
null
> x + y
30</code></pre>
<h2>Examining Values</h2>
<p>Use <code>System.print</code> for formatted output:</p>
<pre><code>> var data = {"name": "Wren", "version": 0.4}
null
> System.print(data)
{name: Wren, version: 0.4}</code></pre>
<p>Check the type of a value:</p>
<pre><code>> 42.type
Num
> "hello".type
String
> [1, 2, 3].type
List</code></pre>
<h2>Exploring Classes</h2>
<p>Examine what methods a class provides:</p>
<pre><code>> String
String
> "test".bytes
[116, 101, 115, 116]
> "test".codePoints.toList
[116, 101, 115, 116]</code></pre>
<h2>Error Handling</h2>
<p>Errors are displayed but do not crash the REPL:</p>
<pre><code>> 1 / 0
infinity
> "test"[10]
Subscript out of bounds.
> undefined_variable
[repl line 1] Error at 'undefined_variable': Variable is used but not defined.</code></pre>
<p>You can continue using the REPL after errors.</p>
<h2>REPL Tips</h2>
<h3>Quick Testing</h3>
<p>Use the REPL to quickly test regular expressions:</p>
<pre><code>> import "regex" for Regex
null
> Regex.new("\\d+").test("abc123")
true
> Regex.new("\\d+").match("abc123def").text
123</code></pre>
<h3>Exploring APIs</h3>
<p>Test module functionality before using it in scripts:</p>
<pre><code>> import "base64" for Base64
null
> Base64.encode("Hello, World!")
SGVsbG8sIFdvcmxkIQ==
> Base64.decode("SGVsbG8sIFdvcmxkIQ==")
Hello, World!</code></pre>
<h3>Date and Time</h3>
<pre><code>> import "datetime" for DateTime
null
> DateTime.now
2024-01-15T10:30:45
> DateTime.now.year
2024</code></pre>
<h2>Exiting the REPL</h2>
<p>Exit the REPL by pressing <code>Ctrl+D</code> (Unix) or <code>Ctrl+Z</code> followed by Enter (Windows).</p>
<h2>Limitations</h2>
<ul>
<li>No command history navigation (arrow keys)</li>
<li>No tab completion</li>
<li>No line editing beyond basic backspace</li>
<li>Cannot redefine classes once defined</li>
</ul>
<div class="admonition tip">
<div class="admonition-title">Tip</div>
<p>For complex experimentation, write code in a file and run it with <code>wren_cli script.wren</code>. The REPL is best for quick tests and exploration.</p>
</div>
<h2>Next Steps</h2>
<ul>
<li><a href="../language/index.html">Language Reference</a> - Learn Wren syntax in detail</li>
<li><a href="../api/index.html">API Reference</a> - Explore available modules</li>
<li><a href="../tutorials/index.html">Tutorials</a> - Build real applications</li>
</ul>
</article>
<footer class="page-footer">
<a href="first-script.html" class="prev">First Script</a>
<a href="../language/index.html" class="next">Language Reference</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>

View File

@ -1,424 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Async Programming - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="index.html">How-To List</a></li>
<li><a href="http-requests.html">HTTP Requests</a></li>
<li><a href="json-parsing.html">JSON Parsing</a></li>
<li><a href="regex-patterns.html">Regex Patterns</a></li>
<li><a href="file-operations.html">File Operations</a></li>
<li><a href="async-operations.html" class="active">Async Operations</a></li>
<li><a href="error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">How-To Guides</a>
<span class="separator">/</span>
<span>Async Operations</span>
</nav>
<article>
<h1>Async Programming</h1>
<p>Wren-CLI uses fibers for concurrent operations. Understanding fibers is key to writing efficient async code.</p>
<h2>Create a Fiber</h2>
<pre><code>var fiber = Fiber.new {
System.print("Hello from fiber!")
}
fiber.call()</code></pre>
<h2>Fibers with Return Values</h2>
<pre><code>var fiber = Fiber.new {
return 42
}
var result = fiber.call()
System.print("Result: %(result)") // 42</code></pre>
<h2>Sleep/Delay</h2>
<pre><code>import "timer" for Timer
System.print("Starting...")
Timer.sleep(1000) // Wait 1 second
System.print("Done!")</code></pre>
<h2>Sequential HTTP Requests</h2>
<pre><code>import "http" for Http
var r1 = Http.get("https://api.example.com/1")
var r2 = Http.get("https://api.example.com/2")
var r3 = Http.get("https://api.example.com/3")
System.print(r1.json)
System.print(r2.json)
System.print(r3.json)</code></pre>
<h2>Parallel HTTP Requests</h2>
<pre><code>import "http" for Http
var urls = [
"https://api.example.com/1",
"https://api.example.com/2",
"https://api.example.com/3"
]
var fibers = []
for (url in urls) {
fibers.add(Fiber.new { Http.get(url) })
}
for (fiber in fibers) {
fiber.call()
}
for (fiber in fibers) {
System.print(fiber.value)
}</code></pre>
<h2>Run Task in Background</h2>
<pre><code>import "timer" for Timer
var backgroundTask = Fiber.new {
for (i in 1..5) {
System.print("Background: %(i)")
Timer.sleep(500)
}
}
backgroundTask.call()
System.print("Main thread continues...")
Timer.sleep(3000)</code></pre>
<h2>Wait for Multiple Operations</h2>
<pre><code>import "http" for Http
var fetchAll = Fn.new { |urls|
var results = []
var fibers = []
for (url in urls) {
fibers.add(Fiber.new { Http.get(url) })
}
for (fiber in fibers) {
fiber.call()
}
for (fiber in fibers) {
results.add(fiber.value)
}
return results
}
var responses = fetchAll.call([
"https://api.example.com/a",
"https://api.example.com/b"
])
for (r in responses) {
System.print(r.statusCode)
}</code></pre>
<h2>Timeout Pattern</h2>
<pre><code>import "timer" for Timer
import "datetime" for DateTime
var withTimeout = Fn.new { |operation, timeoutMs|
var start = DateTime.now()
var result = null
var done = false
var workFiber = Fiber.new {
result = operation.call()
done = true
}
workFiber.call()
while (!done) {
var elapsed = (DateTime.now() - start).milliseconds
if (elapsed >= timeoutMs) {
Fiber.abort("Operation timed out")
}
Timer.sleep(10)
}
return result
}
var result = withTimeout.call(Fn.new {
Timer.sleep(500)
return "completed"
}, 1000)
System.print(result)</code></pre>
<h2>Polling Loop</h2>
<pre><code>import "timer" for Timer
import "http" for Http
var pollUntilReady = Fn.new { |url, maxAttempts|
var attempts = 0
while (attempts < maxAttempts) {
attempts = attempts + 1
System.print("Attempt %(attempts)...")
var response = Http.get(url)
if (response.json["ready"]) {
return response.json
}
Timer.sleep(2000)
}
return null
}
var result = pollUntilReady.call("https://api.example.com/status", 10)
if (result) {
System.print("Ready: %(result)")
} else {
System.print("Timed out waiting for ready state")
}</code></pre>
<h2>Rate Limiting</h2>
<pre><code>import "timer" for Timer
import "http" for Http
var rateLimitedFetch = Fn.new { |urls, delayMs|
var results = []
for (url in urls) {
var response = Http.get(url)
results.add(response)
Timer.sleep(delayMs)
}
return results
}
var responses = rateLimitedFetch.call([
"https://api.example.com/1",
"https://api.example.com/2",
"https://api.example.com/3"
], 1000)
for (r in responses) {
System.print(r.statusCode)
}</code></pre>
<h2>Producer/Consumer Pattern</h2>
<pre><code>import "timer" for Timer
var queue = []
var running = true
var producer = Fiber.new {
for (i in 1..10) {
queue.add(i)
System.print("Produced: %(i)")
Timer.sleep(200)
}
running = false
}
var consumer = Fiber.new {
while (running || queue.count > 0) {
if (queue.count > 0) {
var item = queue.removeAt(0)
System.print("Consumed: %(item)")
}
Timer.sleep(100)
}
}
producer.call()
consumer.call()
System.print("Done!")</code></pre>
<h2>Retry with Exponential Backoff</h2>
<pre><code>import "timer" for Timer
import "http" for Http
var retryWithBackoff = Fn.new { |operation, maxRetries|
var attempt = 0
var delay = 1000
while (attempt < maxRetries) {
var fiber = Fiber.new { operation.call() }
var result = fiber.try()
if (!fiber.error) {
return result
}
attempt = attempt + 1
System.print("Attempt %(attempt) failed, retrying in %(delay)ms...")
Timer.sleep(delay)
delay = delay * 2
}
Fiber.abort("All %(maxRetries) attempts failed")
}
var response = retryWithBackoff.call(Fn.new {
return Http.get("https://api.example.com/data")
}, 3)
System.print(response.json)</code></pre>
<h2>Concurrent WebSocket Handling</h2>
<pre><code>import "websocket" for WebSocket, WebSocketMessage
import "timer" for Timer
var ws = WebSocket.connect("ws://localhost:8080")
var receiver = Fiber.new {
while (true) {
var message = ws.receive()
if (message == null) break
if (message.opcode == WebSocketMessage.TEXT) {
System.print("Received: %(message.payload)")
}
}
}
var sender = Fiber.new {
for (i in 1..5) {
ws.send("Message %(i)")
Timer.sleep(1000)
}
ws.close()
}
receiver.call()
sender.call()</code></pre>
<h2>Graceful Shutdown</h2>
<pre><code>import "signal" for Signal
import "timer" for Timer
var running = true
Signal.handle("SIGINT", Fn.new {
System.print("\nShutting down gracefully...")
running = false
})
System.print("Press Ctrl+C to stop")
while (running) {
System.print("Working...")
Timer.sleep(1000)
}
System.print("Cleanup complete, exiting.")</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Wren fibers are cooperative, not preemptive. A fiber runs until it explicitly yields, calls an async operation, or completes. Long-running computations should periodically yield to allow other fibers to run.</p>
</div>
<div class="admonition tip">
<div class="admonition-title">See Also</div>
<p>For more on fibers, see the <a href="../language/fibers.html">Fibers language guide</a> and the <a href="../api/scheduler.html">Scheduler module reference</a>.</p>
</div>
</article>
<footer class="page-footer">
<a href="file-operations.html" class="prev">File Operations</a>
<a href="error-handling.html" class="next">Error Handling</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>

View File

@ -1,211 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>How-To Guides - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="index.html" class="active">How-To List</a></li>
<li><a href="http-requests.html">HTTP Requests</a></li>
<li><a href="json-parsing.html">JSON Parsing</a></li>
<li><a href="regex-patterns.html">Regex Patterns</a></li>
<li><a href="file-operations.html">File Operations</a></li>
<li><a href="async-operations.html">Async Operations</a></li>
<li><a href="error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<span>How-To Guides</span>
</nav>
<article>
<h1>How-To Guides</h1>
<p>Quick, focused guides that show you how to accomplish specific tasks. Each guide provides working code examples you can copy and adapt for your projects.</p>
<div class="card-grid">
<div class="card">
<h3><a href="http-requests.html">Making HTTP Requests</a></h3>
<p>GET, POST, PUT, DELETE requests with headers, authentication, and error handling.</p>
<div class="card-meta">
<span class="tag">http</span>
</div>
</div>
<div class="card">
<h3><a href="json-parsing.html">Working with JSON</a></h3>
<p>Parse JSON strings, access nested data, create JSON output, and handle errors.</p>
<div class="card-meta">
<span class="tag">json</span>
</div>
</div>
<div class="card">
<h3><a href="regex-patterns.html">Using Regular Expressions</a></h3>
<p>Match, search, replace, and split text with regex patterns.</p>
<div class="card-meta">
<span class="tag">regex</span>
</div>
</div>
<div class="card">
<h3><a href="file-operations.html">File Operations</a></h3>
<p>Read, write, copy, and delete files. Work with directories and paths.</p>
<div class="card-meta">
<span class="tag">io</span>
</div>
</div>
<div class="card">
<h3><a href="async-operations.html">Async Programming</a></h3>
<p>Use fibers for concurrent operations, parallel requests, and timeouts.</p>
<div class="card-meta">
<span class="tag">fibers</span>
<span class="tag">scheduler</span>
</div>
</div>
<div class="card">
<h3><a href="error-handling.html">Error Handling</a></h3>
<p>Catch errors with fibers, validate input, and handle edge cases gracefully.</p>
<div class="card-meta">
<span class="tag">fibers</span>
</div>
</div>
</div>
<h2>How-To vs Tutorials</h2>
<p><strong>Tutorials</strong> are learning-oriented. They walk you through building complete applications step by step, introducing concepts gradually.</p>
<p><strong>How-To Guides</strong> are goal-oriented. They assume you know the basics and need to accomplish a specific task quickly. Each guide focuses on one topic with copy-paste examples.</p>
<h2>Quick Reference</h2>
<table>
<tr>
<th>Task</th>
<th>Guide</th>
<th>Key Functions</th>
</tr>
<tr>
<td>Fetch data from API</td>
<td><a href="http-requests.html">HTTP Requests</a></td>
<td><code>Http.get()</code>, <code>response.json</code></td>
</tr>
<tr>
<td>Parse JSON string</td>
<td><a href="json-parsing.html">JSON Parsing</a></td>
<td><code>Json.parse()</code></td>
</tr>
<tr>
<td>Validate email format</td>
<td><a href="regex-patterns.html">Regex Patterns</a></td>
<td><code>Regex.new().test()</code></td>
</tr>
<tr>
<td>Read file contents</td>
<td><a href="file-operations.html">File Operations</a></td>
<td><code>File.read()</code></td>
</tr>
<tr>
<td>Run tasks in parallel</td>
<td><a href="async-operations.html">Async Operations</a></td>
<td><code>Fiber.new { }</code></td>
</tr>
<tr>
<td>Handle runtime errors</td>
<td><a href="error-handling.html">Error Handling</a></td>
<td><code>fiber.try()</code></td>
</tr>
</table>
</article>
<footer class="page-footer">
<a href="../tutorials/cli-tool.html" class="prev">CLI Tool</a>
<a href="http-requests.html" class="next">HTTP Requests</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>

View File

@ -1,346 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with JSON - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="index.html">How-To List</a></li>
<li><a href="http-requests.html">HTTP Requests</a></li>
<li><a href="json-parsing.html" class="active">JSON Parsing</a></li>
<li><a href="regex-patterns.html">Regex Patterns</a></li>
<li><a href="file-operations.html">File Operations</a></li>
<li><a href="async-operations.html">Async Operations</a></li>
<li><a href="error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">How-To Guides</a>
<span class="separator">/</span>
<span>JSON Parsing</span>
</nav>
<article>
<h1>Working with JSON</h1>
<h2>Parse JSON String</h2>
<pre><code>import "json" for Json
var jsonStr = '{"name": "Alice", "age": 30}'
var data = Json.parse(jsonStr)
System.print(data["name"]) // Alice
System.print(data["age"]) // 30</code></pre>
<h2>Parse JSON Array</h2>
<pre><code>import "json" for Json
var jsonStr = '[1, 2, 3, "four", true, null]'
var items = Json.parse(jsonStr)
for (item in items) {
System.print(item)
}</code></pre>
<h2>Access Nested Objects</h2>
<pre><code>import "json" for Json
var jsonStr = '{"user": {"name": "Bob", "address": {"city": "NYC"}}}'
var data = Json.parse(jsonStr)
System.print(data["user"]["name"]) // Bob
System.print(data["user"]["address"]["city"]) // NYC</code></pre>
<h2>Convert Wren Object to JSON</h2>
<pre><code>import "json" for Json
var data = {
"name": "Charlie",
"age": 25,
"active": true
}
var jsonStr = Json.stringify(data)
System.print(jsonStr) // {"name":"Charlie","age":25,"active":true}</code></pre>
<h2>Pretty Print JSON</h2>
<pre><code>import "json" for Json
var data = {
"users": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25}
]
}
var pretty = Json.stringify(data, 2)
System.print(pretty)</code></pre>
<p>Output:</p>
<pre><code>{
"users": [
{
"name": "Alice",
"age": 30
},
{
"name": "Bob",
"age": 25
}
]
}</code></pre>
<h2>Check if Key Exists</h2>
<pre><code>import "json" for Json
var data = Json.parse('{"name": "Alice"}')
if (data.containsKey("name")) {
System.print("Name: %(data["name"])")
}
if (!data.containsKey("age")) {
System.print("Age not specified")
}</code></pre>
<h2>Provide Default Values</h2>
<pre><code>import "json" for Json
var data = Json.parse('{"name": "Alice"}')
var name = data["name"]
var age = data.containsKey("age") ? data["age"] : 0
var city = data.containsKey("city") ? data["city"] : "Unknown"
System.print("%(name), %(age), %(city)")</code></pre>
<h2>Iterate Over Object Keys</h2>
<pre><code>import "json" for Json
var data = Json.parse('{"a": 1, "b": 2, "c": 3}')
for (key in data.keys) {
System.print("%(key): %(data[key])")
}</code></pre>
<h2>Iterate Over Array</h2>
<pre><code>import "json" for Json
var users = Json.parse('[{"name": "Alice"}, {"name": "Bob"}]')
for (i in 0...users.count) {
System.print("%(i + 1). %(users[i]["name"])")
}</code></pre>
<h2>Modify JSON Data</h2>
<pre><code>import "json" for Json
var data = Json.parse('{"name": "Alice", "age": 30}')
data["age"] = 31
data["email"] = "alice@example.com"
data.remove("name")
System.print(Json.stringify(data))</code></pre>
<h2>Parse JSON from File</h2>
<pre><code>import "json" for Json
import "io" for File
var content = File.read("config.json")
var config = Json.parse(content)
System.print(config["setting"])</code></pre>
<h2>Write JSON to File</h2>
<pre><code>import "json" for Json
import "io" for File
var data = {
"database": "myapp.db",
"port": 8080,
"debug": true
}
File.write("config.json", Json.stringify(data, 2))</code></pre>
<h2>Handle Parse Errors</h2>
<pre><code>import "json" for Json
var jsonStr = "invalid json {"
var fiber = Fiber.new { Json.parse(jsonStr) }
var result = fiber.try()
if (fiber.error) {
System.print("Parse error: %(fiber.error)")
} else {
System.print(result)
}</code></pre>
<h2>Work with Null Values</h2>
<pre><code>import "json" for Json
var data = Json.parse('{"name": "Alice", "address": null}')
if (data["address"] == null) {
System.print("No address provided")
}
var output = {"value": null}
System.print(Json.stringify(output)) // {"value":null}</code></pre>
<h2>Build JSON Array Dynamically</h2>
<pre><code>import "json" for Json
var users = []
users.add({"name": "Alice", "role": "admin"})
users.add({"name": "Bob", "role": "user"})
users.add({"name": "Charlie", "role": "user"})
System.print(Json.stringify(users, 2))</code></pre>
<h2>Filter JSON Array</h2>
<pre><code>import "json" for Json
var users = Json.parse('[
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 17},
{"name": "Charlie", "age": 25}
]')
var adults = []
for (user in users) {
if (user["age"] >= 18) {
adults.add(user)
}
}
System.print("Adults: %(Json.stringify(adults))")</code></pre>
<h2>Transform JSON Data</h2>
<pre><code>import "json" for Json
var users = Json.parse('[
{"firstName": "Alice", "lastName": "Smith"},
{"firstName": "Bob", "lastName": "Jones"}
]')
var names = []
for (user in users) {
names.add("%(user["firstName"]) %(user["lastName"])")
}
System.print(names.join(", "))</code></pre>
<h2>Merge JSON Objects</h2>
<pre><code>import "json" for Json
var defaults = {"theme": "light", "language": "en", "timeout": 30}
var userPrefs = {"theme": "dark"}
var config = {}
for (key in defaults.keys) {
config[key] = defaults[key]
}
for (key in userPrefs.keys) {
config[key] = userPrefs[key]
}
System.print(Json.stringify(config, 2))</code></pre>
<div class="admonition tip">
<div class="admonition-title">See Also</div>
<p>For full API documentation, see the <a href="../api/json.html">JSON module reference</a>.</p>
</div>
</article>
<footer class="page-footer">
<a href="http-requests.html" class="prev">HTTP Requests</a>
<a href="regex-patterns.html" class="next">Regex Patterns</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>

View File

@ -1,194 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wren-CLI Manual</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="getting-started/index.html">Overview</a></li>
<li><a href="getting-started/installation.html">Installation</a></li>
<li><a href="getting-started/first-script.html">First Script</a></li>
<li><a href="getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="language/index.html">Syntax Overview</a></li>
<li><a href="language/classes.html">Classes</a></li>
<li><a href="language/methods.html">Methods</a></li>
<li><a href="language/control-flow.html">Control Flow</a></li>
<li><a href="language/fibers.html">Fibers</a></li>
<li><a href="language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="api/index.html">Overview</a></li>
<li><a href="api/http.html">http</a></li>
<li><a href="api/websocket.html">websocket</a></li>
<li><a href="api/tls.html">tls</a></li>
<li><a href="api/net.html">net</a></li>
<li><a href="api/dns.html">dns</a></li>
<li><a href="api/json.html">json</a></li>
<li><a href="api/base64.html">base64</a></li>
<li><a href="api/regex.html">regex</a></li>
<li><a href="api/jinja.html">jinja</a></li>
<li><a href="api/crypto.html">crypto</a></li>
<li><a href="api/os.html">os</a></li>
<li><a href="api/env.html">env</a></li>
<li><a href="api/signal.html">signal</a></li>
<li><a href="api/subprocess.html">subprocess</a></li>
<li><a href="api/sqlite.html">sqlite</a></li>
<li><a href="api/datetime.html">datetime</a></li>
<li><a href="api/timer.html">timer</a></li>
<li><a href="api/io.html">io</a></li>
<li><a href="api/pathlib.html">pathlib</a></li>
<li><a href="api/scheduler.html">scheduler</a></li>
<li><a href="api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="tutorials/index.html">Tutorial List</a></li>
<li><a href="tutorials/http-client.html">HTTP Client</a></li>
<li><a href="tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="tutorials/database-app.html">Database App</a></li>
<li><a href="tutorials/template-rendering.html">Templates</a></li>
<li><a href="tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="howto/index.html">How-To List</a></li>
<li><a href="howto/http-requests.html">HTTP Requests</a></li>
<li><a href="howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="howto/file-operations.html">File Operations</a></li>
<li><a href="howto/async-operations.html">Async Operations</a></li>
<li><a href="howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<div class="hero">
<h1>Wren-CLI Manual</h1>
<p>A complete reference for the Wren scripting language command-line interface with networking, async I/O, and extended module support.</p>
<div class="hero-buttons">
<a href="getting-started/index.html" class="primary-btn">Get Started</a>
<a href="api/index.html" class="secondary-btn">API Reference</a>
</div>
</div>
<article>
<h2>What is Wren-CLI?</h2>
<p>Wren-CLI is a command-line interface for the <a href="https://wren.io">Wren programming language</a>, extended with powerful modules for networking, file I/O, databases, and more. It provides an async event loop powered by libuv, making it suitable for building servers, automation scripts, and command-line tools.</p>
<div class="card-grid">
<div class="card">
<h3><a href="getting-started/index.html">Getting Started</a></h3>
<p>Install Wren-CLI, write your first script, and learn the basics of the language.</p>
</div>
<div class="card">
<h3><a href="language/index.html">Language Reference</a></h3>
<p>Learn about classes, methods, control flow, fibers, and the module system.</p>
</div>
<div class="card">
<h3><a href="api/index.html">API Reference</a></h3>
<p>Complete documentation for all 21 built-in modules including HTTP, WebSocket, and SQLite.</p>
</div>
<div class="card">
<h3><a href="tutorials/index.html">Tutorials</a></h3>
<p>Step-by-step guides for building real applications with Wren-CLI.</p>
</div>
</div>
<h2>Available Modules</h2>
<p>Wren-CLI provides a rich set of modules for common programming tasks:</p>
<h3>Networking</h3>
<div class="module-grid">
<a href="api/http.html" class="module-card">http</a>
<a href="api/websocket.html" class="module-card">websocket</a>
<a href="api/tls.html" class="module-card">tls</a>
<a href="api/net.html" class="module-card">net</a>
<a href="api/dns.html" class="module-card">dns</a>
</div>
<h3>Data Processing</h3>
<div class="module-grid">
<a href="api/json.html" class="module-card">json</a>
<a href="api/base64.html" class="module-card">base64</a>
<a href="api/regex.html" class="module-card">regex</a>
<a href="api/jinja.html" class="module-card">jinja</a>
<a href="api/crypto.html" class="module-card">crypto</a>
</div>
<h3>System</h3>
<div class="module-grid">
<a href="api/os.html" class="module-card">os</a>
<a href="api/env.html" class="module-card">env</a>
<a href="api/signal.html" class="module-card">signal</a>
<a href="api/subprocess.html" class="module-card">subprocess</a>
<a href="api/io.html" class="module-card">io</a>
<a href="api/pathlib.html" class="module-card">pathlib</a>
</div>
<h3>Data & Time</h3>
<div class="module-grid">
<a href="api/sqlite.html" class="module-card">sqlite</a>
<a href="api/datetime.html" class="module-card">datetime</a>
<a href="api/timer.html" class="module-card">timer</a>
<a href="api/math.html" class="module-card">math</a>
<a href="api/scheduler.html" class="module-card">scheduler</a>
</div>
<h2>Quick Example</h2>
<pre><code>import "http" for Http
import "json" for Json
var response = Http.get("https://api.github.com/users/wren-lang")
var data = Json.parse(response.body)
System.print("User: %(data["login"])")
System.print("Repos: %(data["public_repos"])")</code></pre>
<h2>Features</h2>
<ul>
<li><strong>Async I/O</strong> - Non-blocking operations powered by libuv</li>
<li><strong>HTTP/HTTPS</strong> - Full HTTP client with TLS support</li>
<li><strong>WebSocket</strong> - Client and server WebSocket support</li>
<li><strong>SQLite</strong> - Embedded database for persistent storage</li>
<li><strong>Templates</strong> - Jinja2-compatible template engine</li>
<li><strong>Regex</strong> - Full regular expression support</li>
<li><strong>Cross-platform</strong> - Runs on Linux, macOS, and FreeBSD</li>
</ul>
</article>
<footer class="page-footer">
<span></span>
<a href="getting-started/index.html" class="next">Getting Started</a>
</footer>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>

View File

@ -1,112 +0,0 @@
// retoor <retoor@molodetz.nl>
(function() {
'use strict';
function initSidebar() {
var toggle = document.querySelector('.mobile-menu-toggle');
var sidebar = document.querySelector('.sidebar');
var overlay = document.createElement('div');
overlay.className = 'sidebar-overlay';
document.body.appendChild(overlay);
if (toggle) {
toggle.addEventListener('click', function() {
sidebar.classList.toggle('open');
overlay.classList.toggle('visible');
});
}
overlay.addEventListener('click', function() {
sidebar.classList.remove('open');
overlay.classList.remove('visible');
});
var currentPath = window.location.pathname;
var links = document.querySelectorAll('.sidebar-nav a');
links.forEach(function(link) {
var href = link.getAttribute('href');
if (href && currentPath.endsWith(href.replace(/^\.\.\//, '').replace(/^\.\//, ''))) {
link.classList.add('active');
}
});
}
function initCopyButtons() {
var codeBlocks = document.querySelectorAll('pre');
codeBlocks.forEach(function(pre) {
var wrapper = document.createElement('div');
wrapper.className = 'code-block';
pre.parentNode.insertBefore(wrapper, pre);
wrapper.appendChild(pre);
var btn = document.createElement('button');
btn.className = 'copy-btn';
btn.textContent = 'Copy';
wrapper.appendChild(btn);
btn.addEventListener('click', function() {
var code = pre.querySelector('code');
var text = code ? code.textContent : pre.textContent;
navigator.clipboard.writeText(text).then(function() {
btn.textContent = 'Copied!';
btn.classList.add('copied');
setTimeout(function() {
btn.textContent = 'Copy';
btn.classList.remove('copied');
}, 2000);
}).catch(function() {
btn.textContent = 'Failed';
setTimeout(function() {
btn.textContent = 'Copy';
}, 2000);
});
});
});
}
function initSmoothScroll() {
document.querySelectorAll('a[href^="#"]').forEach(function(anchor) {
anchor.addEventListener('click', function(e) {
var targetId = this.getAttribute('href').slice(1);
var target = document.getElementById(targetId);
if (target) {
e.preventDefault();
target.scrollIntoView({ behavior: 'smooth' });
history.pushState(null, null, '#' + targetId);
}
});
});
}
function highlightCode() {
var keywords = ['import', 'for', 'class', 'static', 'var', 'if', 'else', 'while',
'return', 'true', 'false', 'null', 'this', 'super', 'is', 'new',
'foreign', 'construct', 'break', 'continue', 'in'];
document.querySelectorAll('pre code').forEach(function(block) {
if (block.classList.contains('highlighted')) return;
block.classList.add('highlighted');
var html = block.innerHTML;
html = html.replace(/(\/\/[^\n]*)/g, '<span style="color:#6a9955">$1</span>');
html = html.replace(/("(?:[^"\\]|\\.)*")/g, '<span style="color:#ce9178">$1</span>');
keywords.forEach(function(kw) {
var regex = new RegExp('\\b(' + kw + ')\\b', 'g');
html = html.replace(regex, '<span style="color:#569cd6">$1</span>');
});
block.innerHTML = html;
});
}
document.addEventListener('DOMContentLoaded', function() {
initSidebar();
initCopyButtons();
initSmoothScroll();
highlightCode();
});
})();

View File

@ -1,177 +0,0 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tutorials - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="index.html" class="active">Tutorial List</a></li>
<li><a href="http-client.html">HTTP Client</a></li>
<li><a href="websocket-chat.html">WebSocket Chat</a></li>
<li><a href="database-app.html">Database App</a></li>
<li><a href="template-rendering.html">Template Rendering</a></li>
<li><a href="cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<span>Tutorials</span>
</nav>
<article>
<h1>Tutorials</h1>
<p>Step-by-step tutorials that guide you through building complete applications with Wren-CLI. Each tutorial introduces new concepts and builds upon previous knowledge.</p>
<div class="card-grid">
<div class="card">
<h3><a href="http-client.html">Building an HTTP Client</a></h3>
<p>Learn to make HTTP requests, parse JSON responses, and handle errors while building a REST API client.</p>
<div class="card-meta">
<span class="tag">http</span>
<span class="tag">json</span>
</div>
</div>
<div class="card">
<h3><a href="websocket-chat.html">WebSocket Chat Application</a></h3>
<p>Build a real-time chat application using WebSockets with both client and server components.</p>
<div class="card-meta">
<span class="tag">websocket</span>
<span class="tag">fibers</span>
</div>
</div>
<div class="card">
<h3><a href="database-app.html">Database Application</a></h3>
<p>Create a complete CRUD application using SQLite for persistent data storage.</p>
<div class="card-meta">
<span class="tag">sqlite</span>
<span class="tag">io</span>
</div>
</div>
<div class="card">
<h3><a href="template-rendering.html">Template Rendering</a></h3>
<p>Use Jinja templates to generate HTML pages, reports, and configuration files.</p>
<div class="card-meta">
<span class="tag">jinja</span>
<span class="tag">io</span>
</div>
</div>
<div class="card">
<h3><a href="cli-tool.html">Building a CLI Tool</a></h3>
<p>Create a command-line application with argument parsing, user input, and subprocess management.</p>
<div class="card-meta">
<span class="tag">os</span>
<span class="tag">subprocess</span>
</div>
</div>
</div>
<h2>Learning Path</h2>
<p>If you are new to Wren-CLI, we recommend following the tutorials in this order:</p>
<ol>
<li><strong>HTTP Client</strong> - Introduces async operations and JSON handling</li>
<li><strong>Database Application</strong> - Covers data persistence and file I/O</li>
<li><strong>Template Rendering</strong> - Learn the Jinja template system</li>
<li><strong>WebSocket Chat</strong> - Advanced async patterns with fibers</li>
<li><strong>CLI Tool</strong> - Bringing it all together in a real application</li>
</ol>
<h2>Prerequisites</h2>
<p>Before starting the tutorials, you should:</p>
<ul>
<li>Have Wren-CLI <a href="../getting-started/installation.html">installed</a></li>
<li>Understand the <a href="../language/index.html">basic syntax</a></li>
<li>Be familiar with <a href="../language/classes.html">classes</a> and <a href="../language/methods.html">methods</a></li>
</ul>
</article>
<footer class="page-footer">
<a href="../api/math.html" class="prev">math</a>
<a href="http-client.html" class="next">HTTP Client</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>

View File

@ -0,0 +1,172 @@
# retoor <retoor@molodetz.nl>
sections:
- title: "Getting Started"
directory: "getting-started"
pages:
- file: "index"
title: "Overview"
- file: "installation"
title: "Installation"
- file: "first-script"
title: "First Script"
- file: "repl"
title: "Using the REPL"
- title: "Playground"
directory: "."
pages:
- file: "playground"
title: "Try Wren"
- title: "Language"
directory: "language"
pages:
- file: "index"
title: "Syntax Overview"
- file: "classes"
title: "Classes"
- file: "methods"
title: "Methods"
- file: "control-flow"
title: "Control Flow"
- file: "fibers"
title: "Fibers"
- file: "modules"
title: "Modules"
- title: "API Reference"
directory: "api"
pages:
- file: "index"
title: "Overview"
- file: "string"
title: "String"
- file: "number"
title: "Num"
- file: "argparse"
title: "argparse"
- file: "base64"
title: "base64"
- file: "crypto"
title: "crypto"
- file: "dataset"
title: "dataset"
- file: "datetime"
title: "datetime"
- file: "dns"
title: "dns"
- file: "env"
title: "env"
- file: "faker"
title: "faker"
- file: "fswatch"
title: "fswatch"
- file: "html"
title: "html"
- file: "http"
title: "http"
- file: "io"
title: "io"
- file: "jinja"
title: "jinja"
- file: "json"
title: "json"
- file: "markdown"
title: "markdown"
- file: "math"
title: "math"
- file: "net"
title: "net"
- file: "os"
title: "os"
- file: "pathlib"
title: "pathlib"
- file: "regex"
title: "regex"
- file: "scheduler"
title: "scheduler"
- file: "signal"
title: "signal"
- file: "sqlite"
title: "sqlite"
- file: "subprocess"
title: "subprocess"
- file: "sysinfo"
title: "sysinfo"
- file: "tempfile"
title: "tempfile"
- file: "timer"
title: "timer"
- file: "tls"
title: "tls"
- file: "udp"
title: "udp"
- file: "uuid"
title: "uuid"
- file: "wdantic"
title: "wdantic"
- file: "web"
title: "web"
- file: "websocket"
title: "websocket"
- file: "yaml"
title: "yaml"
- title: "Tutorials"
directory: "tutorials"
pages:
- file: "index"
title: "Tutorial List"
- file: "http-client"
title: "HTTP Client"
- file: "websocket-chat"
title: "WebSocket Chat"
- file: "database-app"
title: "Database App"
- file: "template-rendering"
title: "Templates"
- file: "cli-tool"
title: "CLI Tool"
- file: "pexpect"
title: "Process Automation"
- file: "web-server"
title: "Web Server"
- title: "How-To Guides"
directory: "howto"
pages:
- file: "index"
title: "How-To List"
- file: "http-requests"
title: "HTTP Requests"
- file: "json-parsing"
title: "JSON Parsing"
- file: "regex-patterns"
title: "Regex Patterns"
- file: "file-operations"
title: "File Operations"
- file: "async-operations"
title: "Async Operations"
- file: "error-handling"
title: "Error Handling"
- title: "Contributing"
directory: "contributing"
pages:
- file: "index"
title: "Overview"
- file: "module-overview"
title: "Module Architecture"
- file: "pure-wren-module"
title: "Pure-Wren Modules"
- file: "c-backed-module"
title: "C-Backed Modules"
- file: "foreign-classes"
title: "Foreign Classes"
- file: "async-patterns"
title: "Async Patterns"
- file: "testing"
title: "Writing Tests"
- file: "documentation"
title: "Documentation"

View File

@ -0,0 +1,7 @@
# retoor <retoor@molodetz.nl>
name: "Wren-CLI"
version: "0.4.0"
description: "A complete reference for the Wren scripting language CLI"
author: "retoor <retoor@molodetz.nl>"
repo_url: "https://github.com/wren-lang/wren-cli"

View File

@ -1,101 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>argparse - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
<li><a href="uuid.html">uuid</a></li>
<li><a href="html.html">html</a></li>
<li><a href="argparse.html" class="active">argparse</a></li>
<li><a href="wdantic.html">wdantic</a></li>
<li><a href="dataset.html">dataset</a></li>
<li><a href="markdown.html">markdown</a></li>
<li><a href="web.html">web</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>argparse</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "argparse" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "argparse"}] %}
{% set prev_page = {"url": "api/html.html", "title": "html"} %}
{% set next_page = {"url": "api/wdantic.html", "title": "wdantic"} %}
{% block article %}
<h1>argparse</h1>
<p>The <code>argparse</code> module provides command-line argument parsing with support for positional arguments, optional flags, type conversion, and help generation.</p>
@ -386,14 +297,4 @@ System.print(args["v"]) // 3</code></pre>
<div class="admonition-title">Note</div>
<p>Argument names starting with <code>-</code> are treated as optional flags. Names without a leading dash are positional arguments. Hyphens in argument names are converted to underscores in the result map.</p>
</div>
</article>
<footer class="page-footer">
<a href="html.html" class="prev">html</a>
<a href="wdantic.html" class="next">wdantic</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>base64 - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html" class="active">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>base64</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "base64" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "base64"}] %}
{% set prev_page = {"url": "api/json.html", "title": "json"} %}
{% set next_page = {"url": "api/regex.html", "title": "regex"} %}
{% block article %}
<h1>base64</h1>
<p>The <code>base64</code> module provides Base64 encoding and decoding functionality, including URL-safe variants.</p>
@ -257,14 +175,4 @@ System.print("Payload: %(payloadB64)")</code></pre>
<div class="admonition-title">Tip</div>
<p>Use <code>encodeUrl</code> and <code>decodeUrl</code> when the encoded string will be used in URLs, query parameters, or filenames where <code>+</code>, <code>/</code>, and <code>=</code> characters may cause issues.</p>
</div>
</article>
<footer class="page-footer">
<a href="json.html" class="prev">json</a>
<a href="regex.html" class="next">regex</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>crypto - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html" class="active">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>crypto</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "crypto" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "crypto"}] %}
{% set prev_page = {"url": "api/jinja.html", "title": "jinja"} %}
{% set next_page = {"url": "api/os.html", "title": "os"} %}
{% block article %}
<h1>crypto</h1>
<p>The <code>crypto</code> module provides cryptographic functions including secure random number generation and hash algorithms.</p>
@ -305,14 +223,4 @@ System.print("Different input, different hash: %(Hash.toHex(hash1) != Hash.toHex
<div class="admonition-title">Tip</div>
<p>For password storage, always use a salt (random bytes prepended to the password) before hashing. Store the salt alongside the hash for verification.</p>
</div>
</article>
<footer class="page-footer">
<a href="jinja.html" class="prev">jinja</a>
<a href="os.html" class="next">os</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,101 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>dataset - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
<li><a href="uuid.html">uuid</a></li>
<li><a href="html.html">html</a></li>
<li><a href="argparse.html">argparse</a></li>
<li><a href="wdantic.html">wdantic</a></li>
<li><a href="dataset.html" class="active">dataset</a></li>
<li><a href="markdown.html">markdown</a></li>
<li><a href="web.html">web</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>dataset</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "dataset" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "dataset"}] %}
{% set prev_page = {"url": "api/wdantic.html", "title": "wdantic"} %}
{% set next_page = {"url": "api/markdown.html", "title": "markdown"} %}
{% block article %}
<h1>dataset</h1>
<p>The <code>dataset</code> module provides a simple ORM-like interface for SQLite databases with automatic schema management. Tables and columns are created automatically as you insert data.</p>
@ -379,14 +290,4 @@ System.print(post["metadata"]) // {"author": "Alice", ...}</code></pre>
<div class="admonition-title">Warning</div>
<p>The <code>uid</code> field is the primary key. Do not use <code>id</code> as a field name. If you provide a <code>uid</code> during insert, it will be used instead of auto-generating one.</p>
</div>
</article>
<footer class="page-footer">
<a href="wdantic.html" class="prev">wdantic</a>
<a href="markdown.html" class="next">markdown</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>datetime - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html" class="active">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>datetime</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "datetime" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "datetime"}] %}
{% set prev_page = {"url": "api/sqlite.html", "title": "sqlite"} %}
{% set next_page = {"url": "api/timer.html", "title": "timer"} %}
{% block article %}
<h1>datetime</h1>
<p>The <code>datetime</code> module provides date and time handling with formatting, arithmetic, and duration support.</p>
@ -454,14 +372,4 @@ var deadline = now + Duration.fromDays(7)
if (now < deadline) {
System.print("Still have time!")
}</code></pre>
</article>
<footer class="page-footer">
<a href="sqlite.html" class="prev">sqlite</a>
<a href="timer.html" class="next">timer</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>dns - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html" class="active">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>dns</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "dns" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "dns"}] %}
{% set prev_page = {"url": "api/net.html", "title": "net"} %}
{% set next_page = {"url": "api/json.html", "title": "json"} %}
{% block article %}
<h1>dns</h1>
<p>The <code>dns</code> module provides DNS resolution functionality for looking up hostnames and resolving them to IP addresses.</p>
@ -240,14 +158,4 @@ if (fiber.error != null) {
<div class="admonition-title">Warning</div>
<p>The hostname must be a string. Passing a non-string value will abort the fiber with an error. Similarly, the family parameter must be 0, 4, or 6.</p>
</div>
</article>
<footer class="page-footer">
<a href="net.html" class="prev">net</a>
<a href="json.html" class="next">json</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>env - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html" class="active">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>env</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "env" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "env"}] %}
{% set prev_page = {"url": "api/dns.html", "title": "dns"} %}
{% set next_page = {"url": "api/faker.html", "title": "faker"} %}
{% block article %}
<h1>env</h1>
<p>The <code>env</code> module provides access to environment variables for reading, writing, and deleting values.</p>
@ -287,14 +205,4 @@ if (dbUrl != null) {
<div class="admonition-title">Warning</div>
<p>Never log or print sensitive environment variables like API keys, passwords, or secrets. Use them directly in your application without exposing their values.</p>
</div>
</article>
<footer class="page-footer">
<a href="os.html" class="prev">os</a>
<a href="signal.html" class="next">signal</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -0,0 +1,814 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "faker" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "faker"}] %}
{% set prev_page = {"url": "api/env.html", "title": "env"} %}
{% set next_page = {"url": "api/fswatch.html", "title": "fswatch"} %}
{% block article %}
<h1>faker</h1>
<p>The <code>faker</code> module generates realistic fake data for testing, seeding databases, and prototyping. It provides deterministic output when seeded for reproducible test cases.</p>
<pre><code>import "faker" for Faker</code></pre>
<div class="toc">
<h4>On This Page</h4>
<ul>
<li><a href="#seeding">Seeding &amp; Random</a></li>
<li><a href="#format-helpers">Format Helpers</a></li>
<li><a href="#person">Person &amp; Names</a></li>
<li><a href="#address">Address &amp; Location</a></li>
<li><a href="#internet">Internet &amp; Network</a></li>
<li><a href="#datetime">Date &amp; Time</a></li>
<li><a href="#text">Text &amp; Lorem</a></li>
<li><a href="#colors">Colors</a></li>
<li><a href="#company">Company &amp; Job</a></li>
<li><a href="#commerce">Commerce &amp; Products</a></li>
<li><a href="#banking">Banking &amp; Finance</a></li>
<li><a href="#files">Files &amp; Hashes</a></li>
<li><a href="#utilities">Utilities</a></li>
</ul>
</div>
<h2>Faker Class</h2>
<div class="class-header">
<h3>Faker</h3>
<p>Generates realistic fake data across many categories</p>
</div>
<h2 id="seeding">Seeding &amp; Random</h2>
<div class="method-signature">
<span class="method-name">Faker.seed</span>(<span class="param">value</span>)
</div>
<p>Sets the random seed for deterministic output. Use the same seed to generate identical sequences.</p>
<ul class="param-list">
<li><span class="param-name">value</span> <span class="param-type">(Num)</span> - Integer seed value</li>
</ul>
<pre><code>Faker.seed(12345)
System.print(Faker.name()) // Always produces the same name with this seed</code></pre>
<div class="method-signature">
<span class="method-name">Faker.reset</span>()
</div>
<p>Resets to unseeded mode, using cryptographically random values.</p>
<pre><code>Faker.reset()
System.print(Faker.name()) // Now produces random names</code></pre>
<div class="method-signature">
<span class="method-name">Faker.randomElement</span>(<span class="param">list</span>) &rarr; <span class="type">Any</span>
</div>
<p>Returns a random element from a list.</p>
<pre><code>var colors = ["red", "green", "blue"]
System.print(Faker.randomElement(colors))</code></pre>
<div class="method-signature">
<span class="method-name">Faker.randomElements</span>(<span class="param">list</span>, <span class="param">count</span>) &rarr; <span class="type">List</span>
</div>
<p>Returns multiple random elements from a list (with possible duplicates).</p>
<pre><code>var picks = Faker.randomElements(["a", "b", "c", "d"], 3)
System.print(picks) // e.g., ["b", "a", "b"]</code></pre>
<div class="method-signature">
<span class="method-name">Faker.randomInt</span>(<span class="param">min</span>, <span class="param">max</span>) &rarr; <span class="type">Num</span>
</div>
<p>Returns a random integer between min and max (inclusive).</p>
<pre><code>var age = Faker.randomInt(18, 65)
System.print(age) // e.g., 42</code></pre>
<div class="method-signature">
<span class="method-name">Faker.randomFloat</span>(<span class="param">min</span>, <span class="param">max</span>) &rarr; <span class="type">Num</span>
</div>
<p>Returns a random float between min and max.</p>
<pre><code>var temp = Faker.randomFloat(20, 30)
System.print(temp) // e.g., 24.7831...</code></pre>
<div class="method-signature">
<span class="method-name">Faker.randomFloat</span>(<span class="param">min</span>, <span class="param">max</span>, <span class="param">precision</span>) &rarr; <span class="type">Num</span>
</div>
<p>Returns a random float with specified decimal precision.</p>
<pre><code>var price = Faker.randomFloat(10, 100, 2)
System.print(price) // e.g., 47.83</code></pre>
<div class="method-signature">
<span class="method-name">Faker.boolean</span>() &rarr; <span class="type">Bool</span>
</div>
<p>Returns a random boolean. Alias: <code>bool()</code>.</p>
<pre><code>var isActive = Faker.boolean()
System.print(isActive) // true or false</code></pre>
<h2 id="format-helpers">Format Helpers</h2>
<div class="method-signature">
<span class="method-name">Faker.numerify</span>(<span class="param">format</span>) &rarr; <span class="type">String</span>
</div>
<p>Replaces <code>#</code> characters with random digits.</p>
<pre><code>System.print(Faker.numerify("###-###-####")) // e.g., "415-555-2938"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.letterify</span>(<span class="param">format</span>) &rarr; <span class="type">String</span>
</div>
<p>Replaces <code>?</code> characters with random lowercase letters.</p>
<pre><code>System.print(Faker.letterify("???-????")) // e.g., "abc-defg"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.bothify</span>(<span class="param">format</span>) &rarr; <span class="type">String</span>
</div>
<p>Replaces both <code>#</code> with digits and <code>?</code> with letters.</p>
<pre><code>System.print(Faker.bothify("??-###")) // e.g., "ab-123"</code></pre>
<h2 id="person">Person &amp; Names</h2>
<div class="method-signature">
<span class="method-name">Faker.firstName</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random first name. Gender-specific variants: <code>firstNameMale()</code>, <code>firstNameFemale()</code>.</p>
<pre><code>System.print(Faker.firstName()) // e.g., "Emily"
System.print(Faker.firstNameMale()) // e.g., "James"
System.print(Faker.firstNameFemale()) // e.g., "Sarah"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.lastName</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random last name.</p>
<pre><code>System.print(Faker.lastName()) // e.g., "Johnson"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.name</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a full name (first + last). Variants: <code>nameMale()</code>, <code>nameFemale()</code>.</p>
<pre><code>System.print(Faker.name()) // e.g., "Emily Johnson"
System.print(Faker.nameMale()) // e.g., "James Smith"
System.print(Faker.nameFemale()) // e.g., "Sarah Williams"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.prefix</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a name prefix (Mr., Mrs., Ms., Miss, Dr.). Alias: <code>namePrefix()</code>.</p>
<pre><code>System.print(Faker.prefix()) // e.g., "Dr."</code></pre>
<div class="method-signature">
<span class="method-name">Faker.suffix</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a name suffix (Jr., Sr., MD, PhD, etc.). Alias: <code>nameSuffix()</code>.</p>
<pre><code>System.print(Faker.suffix()) // e.g., "Jr."</code></pre>
<div class="method-signature">
<span class="method-name">Faker.gender</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns "Male" or "Female". Alias: <code>sex()</code>.</p>
<pre><code>System.print(Faker.gender()) // "Male" or "Female"</code></pre>
<h2 id="address">Address &amp; Location</h2>
<div class="method-signature">
<span class="method-name">Faker.address</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a full US-style address.</p>
<pre><code>System.print(Faker.address()) // e.g., "1234 Oak Street, Denver, CO 80201"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.streetAddress</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a street address (number + street name).</p>
<pre><code>System.print(Faker.streetAddress()) // e.g., "1234 Oak Street"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.streetName</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a street name with suffix.</p>
<pre><code>System.print(Faker.streetName()) // e.g., "Oak Boulevard"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.buildingNumber</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random building number (1-9999).</p>
<pre><code>System.print(Faker.buildingNumber()) // e.g., "4521"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.city</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a US city name.</p>
<pre><code>System.print(Faker.city()) // e.g., "Portland"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.state</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a US state name. Aliases: <code>stateFull()</code>, <code>stateName()</code>.</p>
<pre><code>System.print(Faker.state()) // e.g., "California"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.stateAbbr</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a US state abbreviation.</p>
<pre><code>System.print(Faker.stateAbbr()) // e.g., "CA"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.country</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a country name. Alias: <code>countryName()</code>.</p>
<pre><code>System.print(Faker.country()) // e.g., "Germany"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.countryCode</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a two-letter country code.</p>
<pre><code>System.print(Faker.countryCode()) // e.g., "DE"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.zipCode</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a 5-digit US ZIP code. Alias: <code>postcode()</code>.</p>
<pre><code>System.print(Faker.zipCode()) // e.g., "90210"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.latitude</span>() &rarr; <span class="type">Num</span>
</div>
<p>Returns a random latitude (-90 to 90) with 6 decimal places.</p>
<pre><code>System.print(Faker.latitude()) // e.g., 40.712776</code></pre>
<div class="method-signature">
<span class="method-name">Faker.latitude</span>(<span class="param">min</span>, <span class="param">max</span>) &rarr; <span class="type">Num</span>
</div>
<p>Returns a latitude within a specific range.</p>
<pre><code>System.print(Faker.latitude(30, 50)) // e.g., 42.358429</code></pre>
<div class="method-signature">
<span class="method-name">Faker.longitude</span>() &rarr; <span class="type">Num</span>
</div>
<p>Returns a random longitude (-180 to 180) with 6 decimal places.</p>
<pre><code>System.print(Faker.longitude()) // e.g., -74.005974</code></pre>
<div class="method-signature">
<span class="method-name">Faker.longitude</span>(<span class="param">min</span>, <span class="param">max</span>) &rarr; <span class="type">Num</span>
</div>
<p>Returns a longitude within a specific range.</p>
<pre><code>System.print(Faker.longitude(-125, -65)) // e.g., -87.629799</code></pre>
<h2 id="internet">Internet &amp; Network</h2>
<div class="method-signature">
<span class="method-name">Faker.email</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random email address.</p>
<pre><code>System.print(Faker.email()) // e.g., "john.smith42@gmail.com"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.exampleEmail</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns an email using example.com/org/net domains (safe for testing).</p>
<pre><code>System.print(Faker.exampleEmail()) // e.g., "alice_jones@example.org"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.username</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random username in various formats.</p>
<pre><code>System.print(Faker.username()) // e.g., "john.smith", "emily_42"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.password</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a 12-character random password.</p>
<pre><code>System.print(Faker.password()) // e.g., "aB3$kLm9@pQr"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.password</span>(<span class="param">length</span>) &rarr; <span class="type">String</span>
</div>
<p>Returns a password of specified length.</p>
<pre><code>System.print(Faker.password(20)) // 20-character password</code></pre>
<div class="method-signature">
<span class="method-name">Faker.url</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random URL.</p>
<pre><code>System.print(Faker.url()) // e.g., "https://smith.io"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.domainName</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a domain name.</p>
<pre><code>System.print(Faker.domainName()) // e.g., "johnson.com"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.domainWord</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a domain word (without TLD).</p>
<pre><code>System.print(Faker.domainWord()) // e.g., "smith"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.tld</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a top-level domain. Alias: <code>topLevelDomain()</code>.</p>
<pre><code>System.print(Faker.tld()) // e.g., "com", "io", "org"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.protocol</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns "http" or "https".</p>
<pre><code>System.print(Faker.protocol()) // "http" or "https"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.ipv4</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random IPv4 address.</p>
<pre><code>System.print(Faker.ipv4()) // e.g., "192.168.45.123"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.ipv6</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random IPv6 address.</p>
<pre><code>System.print(Faker.ipv6()) // e.g., "2001:0db8:85a3:0000:0000:8a2e:0370:7334"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.macAddress</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random MAC address.</p>
<pre><code>System.print(Faker.macAddress()) // e.g., "00:1a:2b:3c:4d:5e"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.port</span>() &rarr; <span class="type">Num</span>
</div>
<p>Returns a random port number (1-65535).</p>
<pre><code>System.print(Faker.port()) // e.g., 8080</code></pre>
<div class="method-signature">
<span class="method-name">Faker.hostname</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a hostname with subdomain.</p>
<pre><code>System.print(Faker.hostname()) // e.g., "server-42.company.com"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.httpMethod</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random HTTP method.</p>
<pre><code>System.print(Faker.httpMethod()) // "GET", "POST", "PUT", etc.</code></pre>
<div class="method-signature">
<span class="method-name">Faker.httpStatusCode</span>() &rarr; <span class="type">Num</span>
</div>
<p>Returns a common HTTP status code.</p>
<pre><code>System.print(Faker.httpStatusCode()) // 200, 404, 500, etc.</code></pre>
<div class="method-signature">
<span class="method-name">Faker.userAgent</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a realistic browser user agent string.</p>
<pre><code>System.print(Faker.userAgent()) // e.g., "Mozilla/5.0 (Windows NT 10.0..."</code></pre>
<h2 id="datetime">Date &amp; Time</h2>
<div class="method-signature">
<span class="method-name">Faker.date</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random date within the past 10 years in YYYY-MM-DD format.</p>
<pre><code>System.print(Faker.date()) // e.g., "2021-07-15"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.dateTime</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random datetime within the past 10 years in ISO 8601 format.</p>
<pre><code>System.print(Faker.dateTime()) // e.g., "2022-03-14T09:26:53Z"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.pastDate</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a date within the past year. Accepts optional years parameter.</p>
<pre><code>System.print(Faker.pastDate()) // Within past year
System.print(Faker.pastDate(5)) // Within past 5 years</code></pre>
<div class="method-signature">
<span class="method-name">Faker.futureDate</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a date within the next year. Accepts optional years parameter.</p>
<pre><code>System.print(Faker.futureDate()) // Within next year
System.print(Faker.futureDate(2)) // Within next 2 years</code></pre>
<div class="method-signature">
<span class="method-name">Faker.dateBetween</span>(<span class="param">start</span>, <span class="param">end</span>) &rarr; <span class="type">String</span>
</div>
<p>Returns a random date between two DateTime objects.</p>
<pre><code>import "datetime" for DateTime, Duration
var start = DateTime.now() - Duration.fromDays(30)
var end = DateTime.now()
System.print(Faker.dateBetween(start, end))</code></pre>
<div class="method-signature">
<span class="method-name">Faker.dateOfBirth</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a birth date for someone aged 18-80. Accepts optional min/max age.</p>
<pre><code>System.print(Faker.dateOfBirth()) // Age 18-80
System.print(Faker.dateOfBirth(21, 35)) // Age 21-35</code></pre>
<div class="method-signature">
<span class="method-name">Faker.year</span>() &rarr; <span class="type">Num</span>
</div>
<p>Returns a random year (1950-2025).</p>
<pre><code>System.print(Faker.year()) // e.g., 1987</code></pre>
<div class="method-signature">
<span class="method-name">Faker.month</span>() &rarr; <span class="type">Num</span>
</div>
<p>Returns a random month number (1-12).</p>
<pre><code>System.print(Faker.month()) // e.g., 7</code></pre>
<div class="method-signature">
<span class="method-name">Faker.monthName</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a month name.</p>
<pre><code>System.print(Faker.monthName()) // e.g., "July"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.dayOfWeek</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a day of the week.</p>
<pre><code>System.print(Faker.dayOfWeek()) // e.g., "Wednesday"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.dayOfMonth</span>() &rarr; <span class="type">Num</span>
</div>
<p>Returns a random day (1-28).</p>
<pre><code>System.print(Faker.dayOfMonth()) // e.g., 15</code></pre>
<div class="method-signature">
<span class="method-name">Faker.time</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random time in HH:MM:SS format.</p>
<pre><code>System.print(Faker.time()) // e.g., "14:32:07"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.hour</span>() &rarr; <span class="type">Num</span>
</div>
<p>Returns a random hour (0-23). Related: <code>minute()</code>, <code>second()</code>, <code>amPm()</code>.</p>
<pre><code>System.print(Faker.hour()) // e.g., 14
System.print(Faker.minute()) // e.g., 32
System.print(Faker.second()) // e.g., 45
System.print(Faker.amPm()) // "AM" or "PM"</code></pre>
<h2 id="text">Text &amp; Lorem</h2>
<div class="method-signature">
<span class="method-name">Faker.word</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random lorem ipsum word.</p>
<pre><code>System.print(Faker.word()) // e.g., "consectetur"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.words</span>(<span class="param">count</span>) &rarr; <span class="type">List</span>
</div>
<p>Returns a list of random words.</p>
<pre><code>System.print(Faker.words(5)) // ["lorem", "ipsum", "dolor", "sit", "amet"]</code></pre>
<div class="method-signature">
<span class="method-name">Faker.sentence</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a sentence (5-12 words). Accepts optional word count.</p>
<pre><code>System.print(Faker.sentence()) // Random length
System.print(Faker.sentence(8)) // Exactly 8 words</code></pre>
<div class="method-signature">
<span class="method-name">Faker.sentences</span>(<span class="param">count</span>) &rarr; <span class="type">String</span>
</div>
<p>Returns multiple sentences joined with spaces.</p>
<pre><code>System.print(Faker.sentences(3)) // Three sentences</code></pre>
<div class="method-signature">
<span class="method-name">Faker.paragraph</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a paragraph (3-6 sentences). Accepts optional sentence count.</p>
<pre><code>System.print(Faker.paragraph()) // Random length
System.print(Faker.paragraph(5)) // Exactly 5 sentences</code></pre>
<div class="method-signature">
<span class="method-name">Faker.paragraphs</span>(<span class="param">count</span>) &rarr; <span class="type">String</span>
</div>
<p>Returns multiple paragraphs separated by double newlines.</p>
<pre><code>System.print(Faker.paragraphs(3)) // Three paragraphs</code></pre>
<div class="method-signature">
<span class="method-name">Faker.text</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns approximately 200 characters of text. Accepts optional max length.</p>
<pre><code>System.print(Faker.text()) // ~200 characters
System.print(Faker.text(500)) // ~500 characters</code></pre>
<div class="method-signature">
<span class="method-name">Faker.slug</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a URL-friendly slug (3 words). Accepts optional word count.</p>
<pre><code>System.print(Faker.slug()) // e.g., "lorem-ipsum-dolor"
System.print(Faker.slug(5)) // e.g., "lorem-ipsum-dolor-sit-amet"</code></pre>
<h2 id="colors">Colors</h2>
<div class="method-signature">
<span class="method-name">Faker.colorName</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a color name.</p>
<pre><code>System.print(Faker.colorName()) // e.g., "Crimson"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.hexColor</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a hex color code.</p>
<pre><code>System.print(Faker.hexColor()) // e.g., "#a3c2f0"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.rgbColor</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns an RGB color string. Alias: <code>rgb()</code>.</p>
<pre><code>System.print(Faker.rgbColor()) // e.g., "rgb(163, 194, 240)"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.rgbaCssColor</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns an RGBA color string with random alpha.</p>
<pre><code>System.print(Faker.rgbaCssColor()) // e.g., "rgba(163, 194, 240, 0.75)"</code></pre>
<h2 id="company">Company &amp; Job</h2>
<div class="method-signature">
<span class="method-name">Faker.company</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a company name. Alias: <code>companyName()</code>.</p>
<pre><code>System.print(Faker.company()) // e.g., "Smith Industries"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.companySuffix</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a company suffix.</p>
<pre><code>System.print(Faker.companySuffix()) // e.g., "LLC", "Inc.", "Corp."</code></pre>
<div class="method-signature">
<span class="method-name">Faker.job</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a job title. Alias: <code>jobTitle()</code>.</p>
<pre><code>System.print(Faker.job()) // e.g., "Software Engineer"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.jobDescriptor</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a job level descriptor.</p>
<pre><code>System.print(Faker.jobDescriptor()) // e.g., "Senior", "Lead", "Principal"</code></pre>
<h2 id="commerce">Commerce &amp; Products</h2>
<div class="method-signature">
<span class="method-name">Faker.product</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a product name. Alias: <code>productName()</code>.</p>
<pre><code>System.print(Faker.product()) // e.g., "Elegant Steel Chair"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.productCategory</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a product category.</p>
<pre><code>System.print(Faker.productCategory()) // e.g., "Electronics"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.price</span>() &rarr; <span class="type">Num</span>
</div>
<p>Returns a price (1-1000) with 2 decimal places. Accepts optional min/max.</p>
<pre><code>System.print(Faker.price()) // e.g., 49.99
System.print(Faker.price(10, 50)) // e.g., 24.95</code></pre>
<div class="method-signature">
<span class="method-name">Faker.currency</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a currency code.</p>
<pre><code>System.print(Faker.currency()) // e.g., "USD", "EUR", "GBP"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.currencyName</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a currency name.</p>
<pre><code>System.print(Faker.currencyName()) // e.g., "US Dollar"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.currencySymbol</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a currency symbol.</p>
<pre><code>System.print(Faker.currencySymbol()) // e.g., "$", "€", "£"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.creditCardType</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a credit card type.</p>
<pre><code>System.print(Faker.creditCardType()) // e.g., "Visa", "Mastercard"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.creditCardNumber</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a formatted credit card number.</p>
<pre><code>System.print(Faker.creditCardNumber()) // e.g., "4123-4567-8901-234"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.creditCardCVV</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a 3-digit CVV code.</p>
<pre><code>System.print(Faker.creditCardCVV()) // e.g., "123"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.creditCardExpiryDate</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns an expiry date in MM/YY format.</p>
<pre><code>System.print(Faker.creditCardExpiryDate()) // e.g., "09/27"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.phoneNumber</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a US-format phone number. Alias: <code>phone()</code>.</p>
<pre><code>System.print(Faker.phoneNumber()) // e.g., "(415) 555-1234"</code></pre>
<h2 id="banking">Banking &amp; Finance</h2>
<div class="method-signature">
<span class="method-name">Faker.iban</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns an IBAN (International Bank Account Number).</p>
<pre><code>System.print(Faker.iban()) // e.g., "DE89370400440532013000"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.accountNumber</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a 10-digit account number. Accepts optional length.</p>
<pre><code>System.print(Faker.accountNumber()) // 10 digits
System.print(Faker.accountNumber(12)) // 12 digits</code></pre>
<div class="method-signature">
<span class="method-name">Faker.routingNumber</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a 9-digit routing number.</p>
<pre><code>System.print(Faker.routingNumber()) // e.g., "123456789"</code></pre>
<h2 id="files">Files &amp; Hashes</h2>
<div class="method-signature">
<span class="method-name">Faker.fileName</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a file name with extension.</p>
<pre><code>System.print(Faker.fileName()) // e.g., "document.pdf"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.fileExtension</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a file extension.</p>
<pre><code>System.print(Faker.fileExtension()) // e.g., "pdf", "jpg", "txt"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.mimeType</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a MIME type.</p>
<pre><code>System.print(Faker.mimeType()) // e.g., "application/json"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.semver</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a semantic version string.</p>
<pre><code>System.print(Faker.semver()) // e.g., "2.14.3"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.uuid</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a UUID v4.</p>
<pre><code>System.print(Faker.uuid()) // e.g., "550e8400-e29b-41d4-a716-446655440000"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.md5</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns an MD5 hash string.</p>
<pre><code>System.print(Faker.md5()) // 32-character hex string</code></pre>
<div class="method-signature">
<span class="method-name">Faker.sha1</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a SHA-1 hash string.</p>
<pre><code>System.print(Faker.sha1()) // 40-character hex string</code></pre>
<div class="method-signature">
<span class="method-name">Faker.sha256</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a SHA-256 hash string.</p>
<pre><code>System.print(Faker.sha256()) // 64-character hex string</code></pre>
<h2 id="utilities">Utilities</h2>
<div class="method-signature">
<span class="method-name">Faker.digit</span>() &rarr; <span class="type">Num</span>
</div>
<p>Returns a random digit (0-9). Alias: <code>randomDigit()</code>.</p>
<pre><code>System.print(Faker.digit()) // 0-9</code></pre>
<div class="method-signature">
<span class="method-name">Faker.digits</span>(<span class="param">count</span>) &rarr; <span class="type">List</span>
</div>
<p>Returns a list of random digits.</p>
<pre><code>System.print(Faker.digits(4)) // e.g., [1, 4, 7, 2]</code></pre>
<div class="method-signature">
<span class="method-name">Faker.letter</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns a random lowercase letter.</p>
<pre><code>System.print(Faker.letter()) // e.g., "k"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.letters</span>(<span class="param">count</span>) &rarr; <span class="type">String</span>
</div>
<p>Returns a string of random lowercase letters.</p>
<pre><code>System.print(Faker.letters(6)) // e.g., "xkjmvq"</code></pre>
<div class="method-signature">
<span class="method-name">Faker.shuffle</span>(<span class="param">list</span>) &rarr; <span class="type">List</span>
</div>
<p>Returns a shuffled copy of the list.</p>
<pre><code>var nums = [1, 2, 3, 4, 5]
System.print(Faker.shuffle(nums)) // e.g., [3, 1, 5, 2, 4]</code></pre>
<div class="method-signature">
<span class="method-name">Faker.profile</span>() &rarr; <span class="type">Map</span>
</div>
<p>Returns a complete user profile with username, name, email, address, phone, job, company, and birthdate.</p>
<pre><code>var user = Faker.profile()
System.print(user["name"])
System.print(user["email"])
System.print(user["company"])</code></pre>
<div class="method-signature">
<span class="method-name">Faker.simpleProfile</span>() &rarr; <span class="type">Map</span>
</div>
<p>Returns a basic profile with username, name, email, and address.</p>
<pre><code>var user = Faker.simpleProfile()
System.print(user["name"])
System.print(user["email"])</code></pre>
<div class="method-signature">
<span class="method-name">Faker.locale</span>() &rarr; <span class="type">String</span>
</div>
<p>Returns the current locale (always "en_US").</p>
<pre><code>System.print(Faker.locale()) // "en_US"</code></pre>
<h2>Examples</h2>
<h3>Seeding for Reproducible Tests</h3>
<pre><code>import "faker" for Faker
Faker.seed(42)
var user1 = Faker.name()
var user2 = Faker.name()
Faker.seed(42)
System.print(Faker.name() == user1) // true
System.print(Faker.name() == user2) // true</code></pre>
<h3>Generating Test Users</h3>
<pre><code>import "faker" for Faker
import "json" for Json
var users = []
for (i in 0...5) {
users.add({
"id": Faker.uuid(),
"name": Faker.name(),
"email": Faker.email(),
"age": Faker.randomInt(18, 65),
"active": Faker.boolean()
})
}
System.print(Json.stringify(users, 2))</code></pre>
<h3>Creating Product Catalog</h3>
<pre><code>import "faker" for Faker
for (i in 0...3) {
System.print("Product: %(Faker.product())")
System.print("Category: %(Faker.productCategory())")
System.print("Price: %(Faker.currencySymbol())%(Faker.price())")
System.print("---")
}</code></pre>
<h3>Generating Addresses</h3>
<pre><code>import "faker" for Faker
for (i in 0...3) {
System.print(Faker.address())
System.print(" Lat: %(Faker.latitude())")
System.print(" Lng: %(Faker.longitude())")
System.print("")
}</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>When unseeded, Faker uses cryptographically secure random numbers from the <code>crypto</code> module. When seeded, it uses a deterministic linear congruential generator for reproducibility.</p>
</div>
<div class="admonition tip">
<div class="admonition-title">Tip</div>
<p>Use seeding in tests to ensure consistent, reproducible test data. Call <code>Faker.reset()</code> to return to random mode for production use.</p>
</div>
{% endblock %}

View File

@ -0,0 +1,255 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "fswatch" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "fswatch"}] %}
{% set prev_page = {"url": "api/faker.html", "title": "faker"} %}
{% set next_page = {"url": "api/html.html", "title": "html"} %}
{% block article %}
<h1>fswatch</h1>
<p>The <code>fswatch</code> module provides file system watching capabilities, allowing scripts to monitor files and directories for changes in real-time. Built on libuv's filesystem events.</p>
<pre><code>import "fswatch" for FileWatcher, FsEvent</code></pre>
<div class="toc">
<h4>On This Page</h4>
<ul>
<li><a href="#filewatcher-class">FileWatcher Class</a></li>
<li><a href="#fsevent-class">FsEvent Class</a></li>
<li><a href="#examples">Examples</a></li>
</ul>
</div>
<h2 id="filewatcher-class">FileWatcher Class</h2>
<div class="class-header">
<h3>FileWatcher</h3>
<p>Monitors a file or directory for changes</p>
</div>
<h3>Constructor</h3>
<div class="method-signature">
<span class="method-name">FileWatcher.new</span>(<span class="param">path</span>) &#8594; <span class="type">FileWatcher</span>
</div>
<p>Creates a new FileWatcher for the specified path. The path can be a file or directory.</p>
<ul class="param-list">
<li><span class="param-name">path</span> <span class="param-type">(String)</span> - Path to the file or directory to watch</li>
</ul>
<pre><code>var watcher = FileWatcher.new("./config.json")
var dirWatcher = FileWatcher.new("./src")</code></pre>
<h3>Methods</h3>
<div class="method-signature">
<span class="method-name">start</span>(<span class="param">callback</span>)
</div>
<p>Starts watching for changes. The callback function is invoked with an FsEvent whenever a change is detected.</p>
<ul class="param-list">
<li><span class="param-name">callback</span> <span class="param-type">(Fn)</span> - Function that receives an FsEvent parameter</li>
</ul>
<pre><code>watcher.start { |event|
System.print("Changed: %(event.filename)")
}</code></pre>
<div class="method-signature">
<span class="method-name">stop</span>()
</div>
<p>Stops watching for changes.</p>
<pre><code>watcher.stop()</code></pre>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">isActive</span> &#8594; <span class="type">Bool</span>
</div>
<p>Returns true if the watcher is currently active.</p>
<pre><code>if (watcher.isActive) {
System.print("Watcher is running")
}</code></pre>
<h2 id="fsevent-class">FsEvent Class</h2>
<div class="class-header">
<h3>FsEvent</h3>
<p>Represents a file system change event</p>
</div>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">filename</span> &#8594; <span class="type">String</span>
</div>
<p>The name of the file that changed. For directory watchers, this is the relative filename within the directory.</p>
<pre><code>System.print("File changed: %(event.filename)")</code></pre>
<div class="method-signature">
<span class="method-name">isRename</span> &#8594; <span class="type">Bool</span>
</div>
<p>True if the event is a rename/move operation. This includes file creation and deletion.</p>
<pre><code>if (event.isRename) {
System.print("File renamed/created/deleted")
}</code></pre>
<div class="method-signature">
<span class="method-name">isChange</span> &#8594; <span class="type">Bool</span>
</div>
<p>True if the event is a content change.</p>
<pre><code>if (event.isChange) {
System.print("File content modified")
}</code></pre>
<h2 id="examples">Examples</h2>
<h3>Watch a Single File</h3>
<pre><code>import "fswatch" for FileWatcher
var watcher = FileWatcher.new("./config.json")
watcher.start { |event|
System.print("Config changed!")
System.print(" Rename: %(event.isRename)")
System.print(" Change: %(event.isChange)")
}
System.print("Watching config.json for changes...")
System.print("Press Ctrl+C to stop")</code></pre>
<h3>Watch a Directory</h3>
<pre><code>import "fswatch" for FileWatcher
var watcher = FileWatcher.new("./src")
watcher.start { |event|
System.print("[%(event.filename)]")
if (event.isRename) {
System.print(" Created/Deleted/Renamed")
}
if (event.isChange) {
System.print(" Modified")
}
}
System.print("Watching ./src directory...")
System.print("Press Ctrl+C to stop")</code></pre>
<h3>Auto-Reload Configuration</h3>
<pre><code>import "fswatch" for FileWatcher
import "io" for File
import "json" for Json
var config = {}
var loadConfig = Fn.new {
var content = File.read("config.json")
config = Json.parse(content)
System.print("Config loaded: %(config)")
}
loadConfig.call()
var watcher = FileWatcher.new("./config.json")
watcher.start { |event|
System.print("Config file changed, reloading...")
loadConfig.call()
}
System.print("Running with auto-reload...")</code></pre>
<h3>Build on Change</h3>
<pre><code>import "fswatch" for FileWatcher
import "subprocess" for Subprocess
var watcher = FileWatcher.new("./src")
var build = Fn.new {
System.print("Building...")
var result = Subprocess.run("make", ["build"])
if (result.exitCode == 0) {
System.print("Build successful!")
} else {
System.print("Build failed!")
System.print(result.stderr)
}
}
watcher.start { |event|
if (event.filename.endsWith(".c") || event.filename.endsWith(".h")) {
System.print("%(event.filename) changed")
build.call()
}
}
System.print("Watching for source changes...")</code></pre>
<h3>Multiple Watchers</h3>
<pre><code>import "fswatch" for FileWatcher
var srcWatcher = FileWatcher.new("./src")
var testWatcher = FileWatcher.new("./test")
srcWatcher.start { |event|
System.print("[SRC] %(event.filename)")
}
testWatcher.start { |event|
System.print("[TEST] %(event.filename)")
}
System.print("Watching src/ and test/ directories...")</code></pre>
<h3>Debounced Watcher</h3>
<pre><code>import "fswatch" for FileWatcher
import "timer" for Timer
import "datetime" for DateTime
var lastChange = null
var debounceMs = 500
var watcher = FileWatcher.new("./src")
watcher.start { |event|
var now = DateTime.now()
if (lastChange == null || (now - lastChange).milliseconds > debounceMs) {
lastChange = now
System.print("Change detected: %(event.filename)")
}
}
System.print("Watching with %(debounceMs)ms debounce...")</code></pre>
<h3>Graceful Shutdown</h3>
<pre><code>import "fswatch" for FileWatcher
import "signal" for Signal
var watcher = FileWatcher.new("./data")
watcher.start { |event|
System.print("%(event.filename) changed")
}
Signal.trap(Signal.SIGINT) {
System.print("\nStopping watcher...")
watcher.stop()
System.print("Watcher stopped: %(watcher.isActive)")
}
System.print("Watching ./data (Ctrl+C to stop)")</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>File system event behavior varies by operating system. On some platforms, multiple events may be delivered for a single change, or event types may overlap (both <code>isRename</code> and <code>isChange</code> true).</p>
</div>
<div class="admonition tip">
<div class="admonition-title">Tip</div>
<p>When watching directories, the <code>filename</code> property contains the relative path of the changed file within the watched directory, not the full path.</p>
</div>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Watching a large directory tree may consume significant system resources. Consider watching specific subdirectories when possible.</p>
</div>
{% endblock %}

View File

@ -1,101 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>html - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
<li><a href="uuid.html">uuid</a></li>
<li><a href="html.html" class="active">html</a></li>
<li><a href="argparse.html">argparse</a></li>
<li><a href="wdantic.html">wdantic</a></li>
<li><a href="dataset.html">dataset</a></li>
<li><a href="markdown.html">markdown</a></li>
<li><a href="web.html">web</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>html</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "html" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "html"}] %}
{% set prev_page = {"url": "api/fswatch.html", "title": "fswatch"} %}
{% set next_page = {"url": "api/http.html", "title": "http"} %}
{% block article %}
<h1>html</h1>
<p>The <code>html</code> module provides utilities for HTML and URL encoding/decoding, slug generation, and query string handling.</p>
@ -271,14 +182,4 @@ for (key in params.keys) {
<div class="admonition-title">Warning</div>
<p>Always use <code>Html.quote()</code> when inserting user-provided content into HTML to prevent cross-site scripting (XSS) attacks.</p>
</div>
</article>
<footer class="page-footer">
<a href="uuid.html" class="prev">uuid</a>
<a href="argparse.html" class="next">argparse</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,105 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>http - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html" class="active">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>http</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "http" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "http"}] %}
{% set prev_page = {"url": "api/index.html", "title": "API Overview"} %}
{% set next_page = {"url": "api/websocket.html", "title": "websocket"} %}
{% block article %}
<h1>http</h1>
<p>The <code>http</code> module provides an HTTP client for making requests to web servers. It supports both HTTP and HTTPS, all common HTTP methods, custom headers, and JSON handling.</p>
@ -419,14 +326,4 @@ for (entry in response.headers) {
<div class="admonition-title">Tip</div>
<p>When posting a Map or List, the module automatically sets <code>Content-Type: application/json</code> and JSON-encodes the body. For other content types, pass a string body and set the Content-Type header explicitly.</p>
</div>
</article>
<footer class="page-footer">
<a href="index.html" class="prev">API Overview</a>
<a href="websocket.html" class="next">websocket</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,113 +1,15 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Reference - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html" class="active">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
<li><a href="uuid.html">uuid</a></li>
<li><a href="html.html">html</a></li>
<li><a href="argparse.html">argparse</a></li>
<li><a href="wdantic.html">wdantic</a></li>
<li><a href="dataset.html">dataset</a></li>
<li><a href="markdown.html">markdown</a></li>
<li><a href="web.html">web</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<span>API Reference</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "API Reference" %}
{% set breadcrumb = [{"title": "API Reference"}] %}
{% set prev_page = {"url": "language/modules.html", "title": "Modules"} %}
{% set next_page = {"url": "api/http.html", "title": "http"} %}
{% block article %}
<h1>API Reference</h1>
<p>Wren-CLI provides 29 built-in modules covering networking, file I/O, data processing, system operations, and more. All modules are imported using the <code>import</code> statement.</p>
<p>Wren-CLI provides 32 built-in modules covering networking, file I/O, data processing, system operations, and more. All modules are imported using the <code>import</code> statement.</p>
<pre><code>import "http" for Http
import "json" for Json
@ -147,6 +49,10 @@ import "io" for File</code></pre>
<h3><a href="net.html">net</a></h3>
<p>Low-level TCP sockets and servers for custom network protocols.</p>
</div>
<div class="card">
<h3><a href="udp.html">udp</a></h3>
<p>UDP datagram sockets for connectionless networking.</p>
</div>
<div class="card">
<h3><a href="dns.html">dns</a></h3>
<p>DNS resolution for hostname to IP address lookups.</p>
@ -219,6 +125,14 @@ import "io" for File</code></pre>
<h3><a href="pathlib.html">pathlib</a></h3>
<p>Object-oriented filesystem paths with glob, walk, and tree operations.</p>
</div>
<div class="card">
<h3><a href="sysinfo.html">sysinfo</a></h3>
<p>System information: CPU, memory, uptime, and network interfaces.</p>
</div>
<div class="card">
<h3><a href="fswatch.html">fswatch</a></h3>
<p>File system watching for monitoring file and directory changes.</p>
</div>
</div>
<h2>Data & Time</h2>
@ -296,6 +210,11 @@ import "io" for File</code></pre>
<td>Socket, Server</td>
<td>TCP networking</td>
</tr>
<tr>
<td><a href="udp.html">udp</a></td>
<td>UdpSocket, UdpMessage</td>
<td>UDP networking</td>
</tr>
<tr>
<td><a href="dns.html">dns</a></td>
<td>Dns</td>
@ -336,6 +255,11 @@ import "io" for File</code></pre>
<td>Env</td>
<td>Environment variables</td>
</tr>
<tr>
<td><a href="fswatch.html">fswatch</a></td>
<td>FileWatcher, FsEvent</td>
<td>File system watching</td>
</tr>
<tr>
<td><a href="signal.html">signal</a></td>
<td>Signal</td>
@ -346,6 +270,11 @@ import "io" for File</code></pre>
<td>Subprocess</td>
<td>External processes</td>
</tr>
<tr>
<td><a href="sysinfo.html">sysinfo</a></td>
<td>SysInfo</td>
<td>System information</td>
</tr>
<tr>
<td><a href="sqlite.html">sqlite</a></td>
<td>Sqlite</td>
@ -358,8 +287,8 @@ import "io" for File</code></pre>
</tr>
<tr>
<td><a href="timer.html">timer</a></td>
<td>Timer</td>
<td>Timers and delays</td>
<td>Timer, TimerHandle</td>
<td>Timers, delays, and intervals</td>
</tr>
<tr>
<td><a href="io.html">io</a></td>
@ -417,14 +346,4 @@ import "io" for File</code></pre>
<td>Web framework</td>
</tr>
</table>
</article>
<footer class="page-footer">
<a href="../language/modules.html" class="prev">Modules</a>
<a href="http.html" class="next">http</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,99 +1,17 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>io - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html" class="active">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>io</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "io" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "io"}] %}
{% set prev_page = {"url": "api/timer.html", "title": "timer"} %}
{% set next_page = {"url": "api/pathlib.html", "title": "pathlib"} %}
{% block article %}
<h1>io</h1>
<p>The <code>io</code> module provides file and directory operations, as well as stdin/stdout handling.</p>
<pre><code>import "io" for File, Directory, Stdin, Stdout</code></pre>
<pre><code>import "io" for File, Directory, Stdin, Stdout, Stat</code></pre>
<h2>File Class</h2>
@ -143,7 +61,13 @@ System.print(content)</code></pre>
<div class="method-signature">
<span class="method-name">File.rename</span>(<span class="param">oldPath</span>, <span class="param">newPath</span>)
</div>
<p>Renames or moves a file.</p>
<p>Renames a file within the same filesystem.</p>
<div class="method-signature">
<span class="method-name">File.move</span>(<span class="param">source</span>, <span class="param">dest</span>)
</div>
<p>Moves a file from source to destination. Uses libuv's async rename operation.</p>
<pre><code>File.move("old/location/file.txt", "new/location/file.txt")</code></pre>
<h2>Directory Class</h2>
@ -214,6 +138,96 @@ System.print("Hello, %(name)!")</code></pre>
</div>
<p>Flushes the stdout buffer.</p>
<h2>Stat Class</h2>
<div class="class-header">
<h3>Stat</h3>
<p>File metadata and statistics</p>
</div>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Stat.path</span>(<span class="param">path</span>) &#8594; <span class="type">Stat</span>
</div>
<p>Returns a Stat object for the specified path.</p>
<pre><code>var stat = Stat.path("/etc/hosts")
System.print("Size: %(stat.size) bytes")</code></pre>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">size</span> &#8594; <span class="type">Num</span>
</div>
<p>The size of the file in bytes.</p>
<div class="method-signature">
<span class="method-name">isFile</span> &#8594; <span class="type">Bool</span>
</div>
<p>True if the path is a regular file.</p>
<div class="method-signature">
<span class="method-name">isDirectory</span> &#8594; <span class="type">Bool</span>
</div>
<p>True if the path is a directory.</p>
<div class="method-signature">
<span class="method-name">mtime</span> &#8594; <span class="type">Num</span>
</div>
<p>The modification time as a Unix timestamp (seconds since epoch).</p>
<pre><code>var stat = Stat.path("file.txt")
System.print("Modified: %(stat.mtime)")</code></pre>
<div class="method-signature">
<span class="method-name">atime</span> &#8594; <span class="type">Num</span>
</div>
<p>The access time as a Unix timestamp.</p>
<div class="method-signature">
<span class="method-name">ctime</span> &#8594; <span class="type">Num</span>
</div>
<p>The change time (inode change) as a Unix timestamp.</p>
<div class="method-signature">
<span class="method-name">mode</span> &#8594; <span class="type">Num</span>
</div>
<p>The file permission mode.</p>
<div class="method-signature">
<span class="method-name">inode</span> &#8594; <span class="type">Num</span>
</div>
<p>The inode number.</p>
<div class="method-signature">
<span class="method-name">device</span> &#8594; <span class="type">Num</span>
</div>
<p>The device ID containing the file.</p>
<div class="method-signature">
<span class="method-name">linkCount</span> &#8594; <span class="type">Num</span>
</div>
<p>The number of hard links to the file.</p>
<div class="method-signature">
<span class="method-name">user</span> &#8594; <span class="type">Num</span>
</div>
<p>The user ID of the file owner.</p>
<div class="method-signature">
<span class="method-name">group</span> &#8594; <span class="type">Num</span>
</div>
<p>The group ID of the file.</p>
<div class="method-signature">
<span class="method-name">blockSize</span> &#8594; <span class="type">Num</span>
</div>
<p>The preferred block size for I/O operations.</p>
<div class="method-signature">
<span class="method-name">blockCount</span> &#8594; <span class="type">Num</span>
</div>
<p>The number of blocks allocated for the file.</p>
<h2>Examples</h2>
<h3>Reading and Writing Files</h3>
@ -273,14 +287,4 @@ backup.call("important.txt")</code></pre>
<div class="admonition-title">Note</div>
<p>File operations are synchronous but use libuv internally for async I/O. The fiber suspends during I/O operations.</p>
</div>
</article>
<footer class="page-footer">
<a href="timer.html" class="prev">timer</a>
<a href="pathlib.html" class="next">pathlib</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,110 +1,18 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>jinja - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html" class="active">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>jinja</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "jinja" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "jinja"}] %}
{% set prev_page = {"url": "api/regex.html", "title": "regex"} %}
{% set next_page = {"url": "api/crypto.html", "title": "crypto"} %}
{% block article %}
{% raw %}
<h1>jinja</h1>
<p>The <code>jinja</code> module provides a Jinja2-compatible template engine for Wren. It supports variable interpolation, filters, control structures, template inheritance, macros, and more.</p>
<pre><code>import "jinja" for Environment, DictLoader, FileSystemLoader, Template</code></pre>
<pre><code>import "jinja" for Environment, DictLoader, FileSystemLoader, ChoiceLoader, Template</code></pre>
<div class="toc">
<h4>On This Page</h4>
@ -112,6 +20,7 @@
<li><a href="#environment-class">Environment Class</a></li>
<li><a href="#dictloader-class">DictLoader Class</a></li>
<li><a href="#filesystemloader-class">FileSystemLoader Class</a></li>
<li><a href="#choiceloader-class">ChoiceLoader Class</a></li>
<li><a href="#template-syntax">Template Syntax</a>
<ul>
<li><a href="#variables">Variables</a></li>
@ -254,6 +163,41 @@ var template = env.getTemplate("page.html") // Loads ./templates/page.html</cod
</div>
<p>Checks if a template file exists.</p>
<h2 id="choiceloader-class">ChoiceLoader Class</h2>
<div class="class-header">
<h3>ChoiceLoader</h3>
<p>Chain multiple template loaders</p>
</div>
<p>The ChoiceLoader tries multiple loaders in order until one finds the requested template. This is useful for template inheritance scenarios where templates may be in different directories.</p>
<h3>Constructor</h3>
<div class="method-signature">
<span class="method-name">ChoiceLoader.new</span>(<span class="param">loaders</span>) &rarr; <span class="type">ChoiceLoader</span>
</div>
<p>Creates a loader that chains multiple loaders together.</p>
<ul class="param-list">
<li><span class="param-name">loaders</span> <span class="param-type">(List)</span> - List of loaders to try in order</li>
</ul>
<pre><code>var templatesLoader = FileSystemLoader.new("./templates")
var pagesLoader = FileSystemLoader.new("./pages")
var loader = ChoiceLoader.new([templatesLoader, pagesLoader])
var env = Environment.new(loader)</code></pre>
<h3>Methods</h3>
<div class="method-signature">
<span class="method-name">getSource</span>(<span class="param">name</span>) &rarr; <span class="type">String</span>
</div>
<p>Tries each loader in order and returns the source from the first one that has the template.</p>
<div class="method-signature">
<span class="method-name">exists</span>(<span class="param">name</span>) &rarr; <span class="type">Bool</span>
</div>
<p>Returns true if any loader has the template.</p>
<h2 id="template-syntax">Template Syntax</h2>
<p>The jinja module uses syntax compatible with Jinja2/Twig template engines.</p>
@ -346,10 +290,10 @@ var template = env.getTemplate("page.html") // Loads ./templates/page.html</cod
<h4>Raw Blocks</h4>
<p>Output template syntax literally without processing:</p>
<pre><code>{% raw %}
This {{ will not }} be processed.
{% Neither will this %}
{% endraw %}</code></pre>
<pre><code>&#123;% raw %&#125;
This &#123;&#123; will not &#125;&#125; be processed.
&#123;% Neither will this %&#125;
&#123;% endraw %&#125;</code></pre>
<h4>Filter Blocks</h4>
<p>Apply a filter to an entire block of content:</p>
@ -1081,14 +1025,5 @@ System.print(template.render({
<div class="admonition-title">Warning</div>
<p>When auto-escaping is disabled, be careful with user-provided content to prevent XSS vulnerabilities. Use the <code>|escape</code> filter explicitly when needed.</p>
</div>
</article>
<footer class="page-footer">
<a href="regex.html" class="prev">regex</a>
<a href="crypto.html" class="next">crypto</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endraw %}
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>json - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html" class="active">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>json</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "json" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "json"}] %}
{% set prev_page = {"url": "api/dns.html", "title": "dns"} %}
{% set next_page = {"url": "api/base64.html", "title": "base64"} %}
{% block article %}
<h1>json</h1>
<p>The <code>json</code> module provides JSON parsing and stringification. It uses the cJSON library for parsing and implements stringify in Wren.</p>
@ -245,14 +163,4 @@ System.print(Json.stringify(special))
<div class="admonition-title">Warning</div>
<p>Invalid JSON strings will cause a runtime error. Use <code>Fiber.try()</code> to catch parsing errors.</p>
</div>
</article>
<footer class="page-footer">
<a href="dns.html" class="prev">dns</a>
<a href="base64.html" class="next">base64</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,101 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>markdown - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
<li><a href="uuid.html">uuid</a></li>
<li><a href="html.html">html</a></li>
<li><a href="argparse.html">argparse</a></li>
<li><a href="wdantic.html">wdantic</a></li>
<li><a href="dataset.html">dataset</a></li>
<li><a href="markdown.html" class="active">markdown</a></li>
<li><a href="web.html">web</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>markdown</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "markdown" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "markdown"}] %}
{% set prev_page = {"url": "api/dataset.html", "title": "dataset"} %}
{% set next_page = {"url": "api/web.html", "title": "web"} %}
{% block article %}
<h1>markdown</h1>
<p>The <code>markdown</code> module provides bidirectional conversion between Markdown and HTML. It supports common Markdown syntax including headings, emphasis, code blocks, lists, links, and images.</p>
@ -420,14 +331,4 @@ System.print(clean) // Safe **content**</code></pre>
<div class="admonition-title">Note</div>
<p>Language identifiers after code fences (e.g., <code>```wren</code>) are currently ignored. The code is rendered without syntax highlighting. Consider using a client-side syntax highlighter like Prism.js or highlight.js for the resulting HTML.</p>
</div>
</article>
<footer class="page-footer">
<a href="dataset.html" class="prev">dataset</a>
<a href="web.html" class="next">web</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>math - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html" class="active">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>math</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "math" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "math"}] %}
{% set prev_page = {"url": "api/scheduler.html", "title": "scheduler"} %}
{% set next_page = {"url": "tutorials/index.html", "title": "Tutorials"} %}
{% block article %}
<h1>math</h1>
<p>The <code>math</code> module provides mathematical functions and constants.</p>
@ -279,14 +197,4 @@ var circleArea = Fn.new { |radius|
}
System.print(circleArea.call(5)) // 78.539...</code></pre>
</article>
<footer class="page-footer">
<a href="scheduler.html" class="prev">scheduler</a>
<a href="../tutorials/index.html" class="next">Tutorials</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>net - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html" class="active">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>net</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "net" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "net"}] %}
{% set prev_page = {"url": "api/tls.html", "title": "tls"} %}
{% set next_page = {"url": "api/dns.html", "title": "dns"} %}
{% block article %}
<h1>net</h1>
<p>The <code>net</code> module provides low-level TCP socket and server functionality for network programming. All operations are asynchronous using libuv.</p>
@ -319,14 +237,4 @@ socket.close()</code></pre>
<div class="admonition-title">Warning</div>
<p>Both host and port arguments are validated. Providing a non-string host or non-number port will abort the fiber with an error message.</p>
</div>
</article>
<footer class="page-footer">
<a href="tls.html" class="prev">tls</a>
<a href="dns.html" class="next">dns</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Num - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html" class="active">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>Num</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "Num" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "Num"}] %}
{% set prev_page = {"url": "api/string.html", "title": "String"} %}
{% set next_page = {"url": "api/http.html", "title": "http"} %}
{% block article %}
<h1>Num</h1>
<p>The <code>Num</code> class is the core numeric type in Wren. All numbers are double-precision floating point. The class provides arithmetic, trigonometric, bitwise, rounding, query, conversion, and formatting methods.</p>
@ -480,14 +398,4 @@ Num.fromString("nope") // null</code></pre>
<p>Creates an exclusive range from this number to <code>to</code> (excludes the end).</p>
<pre><code>for (i in 1..5) System.print(i) // 1 2 3 4 5
for (i in 1...5) System.print(i) // 1 2 3 4</code></pre>
</article>
<footer class="page-footer">
<a href="string.html" class="prev">String</a>
<a href="http.html" class="next">http</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>os - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html" class="active">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>os</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "os" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "os"}] %}
{% set prev_page = {"url": "api/crypto.html", "title": "crypto"} %}
{% set next_page = {"url": "api/env.html", "title": "env"} %}
{% block article %}
<h1>os</h1>
<p>The <code>os</code> module provides information about the operating system, platform, and current process.</p>
@ -188,6 +106,20 @@ System.print(Process.allArguments) // [wren_cli, script.wren, arg1, arg2]</code
<p>The version string of the Wren-CLI runtime.</p>
<pre><code>System.print(Process.version) // 0.4.0</code></pre>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Process.exit</span>(<span class="param">code</span>)
</div>
<p>Terminates the process with the specified exit code.</p>
<ul class="param-list">
<li><span class="param-name">code</span> <span class="param-type">(Num)</span> - Exit code (0 for success, non-zero for error)</li>
</ul>
<pre><code>if (hasError) {
Process.exit(1)
}
Process.exit(0)</code></pre>
<h2 id="examples">Examples</h2>
<h3>Platform-Specific Behavior</h3>
@ -291,14 +223,4 @@ System.print("Input files: %(inputFiles)")</code></pre>
<div class="admonition-title">Tip</div>
<p>Use <code>Platform.isPosix</code> to write cross-platform code that handles path separators, shell commands, and other platform-specific differences.</p>
</div>
</article>
<footer class="page-footer">
<a href="crypto.html" class="prev">crypto</a>
<a href="env.html" class="next">env</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,112 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pathlib - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html" class="active">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
<li><a href="uuid.html">uuid</a></li>
<li><a href="html.html">html</a></li>
<li><a href="argparse.html">argparse</a></li>
<li><a href="wdantic.html">wdantic</a></li>
<li><a href="dataset.html">dataset</a></li>
<li><a href="markdown.html">markdown</a></li>
<li><a href="web.html">web</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>pathlib</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "pathlib" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "pathlib"}] %}
{% set prev_page = {"url": "api/io.html", "title": "io"} %}
{% set next_page = {"url": "api/scheduler.html", "title": "scheduler"} %}
{% block article %}
<h1>pathlib</h1>
<p>The <code>pathlib</code> module provides object-oriented filesystem path operations. Inspired by Python's <code>pathlib</code>, it offers immutable <code>Path</code> objects that combine path manipulation with filesystem I/O in a single interface.</p>
@ -425,6 +325,30 @@ System.print(published) // article.txt</code></pre>
<p>Copies this file to <code>dest</code>.</p>
<pre><code>Path.new("original.txt").copyfile("backup.txt")</code></pre>
<div class="method-signature">
<span class="method-name">size</span>() &#8594; <span class="type">Num</span>
</div>
<p>Returns the size of the file in bytes.</p>
<pre><code>var bytes = Path.new("data.txt").size()
System.print("File size: %(bytes) bytes")</code></pre>
<div class="method-signature">
<span class="method-name">mtime</span>() &#8594; <span class="type">Num</span>
</div>
<p>Returns the modification time as a Unix timestamp (seconds since epoch).</p>
<pre><code>var modified = Path.new("file.txt").mtime()
System.print("Last modified: %(modified)")</code></pre>
<div class="method-signature">
<span class="method-name">atime</span>() &#8594; <span class="type">Num</span>
</div>
<p>Returns the access time as a Unix timestamp.</p>
<div class="method-signature">
<span class="method-name">ctime</span>() &#8594; <span class="type">Num</span>
</div>
<p>Returns the change time (inode change) as a Unix timestamp.</p>
<div class="method-signature">
<span class="method-name">chmod</span>(<span class="param">mode</span>)
</div>
@ -646,14 +570,4 @@ for (p in shortcuts) {
<div class="admonition-title">Tip</div>
<p>Use the <code>/</code> operator for readable path construction: <code>Path.home / ".config" / "myapp" / "settings.json"</code> reads naturally and handles separator insertion automatically.</p>
</div>
</article>
<footer class="page-footer">
<a href="io.html" class="prev">io</a>
<a href="scheduler.html" class="next">scheduler</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>regex - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html" class="active">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>regex</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "regex" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "regex"}] %}
{% set prev_page = {"url": "api/base64.html", "title": "base64"} %}
{% set next_page = {"url": "api/jinja.html", "title": "jinja"} %}
{% block article %}
<h1>regex</h1>
<p>The <code>regex</code> module provides regular expression matching, replacement, and splitting using PCRE-compatible patterns.</p>
@ -229,7 +147,7 @@ System.print(parts) // [a, b, c, d]</code></pre>
<tr>
<td><code>groups</code></td>
<td>List</td>
<td>List of captured groups</td>
<td>List where index 0 is the entire match, index 1+ are capture groups</td>
</tr>
</table>
@ -245,6 +163,11 @@ System.print(m.group(0)) // item-42
System.print(m.group(1)) // item
System.print(m.group(2)) // 42</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>The <code>groups</code> list uses 0-based indexing where <code>groups[0]</code> is the entire match (same as <code>text</code>), and <code>groups[1]</code>, <code>groups[2]</code>, etc. are the captured groups. This is consistent with <code>group(0)</code>, <code>group(1)</code>, etc.</p>
</div>
<h2 id="pattern-syntax">Pattern Syntax</h2>
<h3>Character Classes</h3>
@ -343,14 +266,4 @@ System.print("Path: %(m.group(3))") // /path/to/page</code></pre>
<div class="admonition-title">Note</div>
<p>Remember to escape backslashes in Wren strings. Use <code>\\d</code> instead of <code>\d</code>.</p>
</div>
</article>
<footer class="page-footer">
<a href="base64.html" class="prev">base64</a>
<a href="jinja.html" class="next">jinja</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -0,0 +1,255 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "scheduler" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "scheduler"}] %}
{% set prev_page = {"url": "api/pathlib.html", "title": "pathlib"} %}
{% set next_page = {"url": "api/math.html", "title": "math"} %}
{% block article %}
<h1>scheduler</h1>
<p>The <code>scheduler</code> module manages the async event loop and fiber scheduling. It is the foundation for all async operations in Wren-CLI.</p>
<pre><code>import "scheduler" for Scheduler, Future</code></pre>
<div class="admonition note">
<div class="admonition-title">Quick Reference</div>
<p>The scheduler module enables async programming with <code>async</code> and <code>await</code>:</p>
<ul>
<li><code>async { code }</code> — Create an async function</li>
<li><code>await fn()</code> — Call async function and wait for result</li>
<li><code>fn.call()</code> — Start async function, return Future (for concurrent execution)</li>
</ul>
</div>
<h2>Scheduler Class</h2>
<div class="class-header">
<h3>Scheduler</h3>
<p>Async fiber scheduler</p>
</div>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Scheduler.await_</span>(<span class="param">block</span>) → <span class="type">any</span>
</div>
<p>Suspends the current fiber until an async operation completes. Used internally by modules to implement async operations.</p>
<ul class="param-list">
<li><span class="param-name">block</span> <span class="param-type">(Fn)</span> - Block that initiates the async operation</li>
<li><span class="returns">Returns:</span> Result of the async operation</li>
</ul>
<pre><code>var result = Scheduler.await_ {
Timer.sleep_(1000, Fiber.current)
}</code></pre>
<div class="method-signature">
<span class="method-name">Scheduler.resume_</span>(<span class="param">fiber</span>)
</div>
<p>Resumes a suspended fiber. Used by native code to wake up fibers when async operations complete.</p>
<div class="method-signature">
<span class="method-name">Scheduler.resume_</span>(<span class="param">fiber</span>, <span class="param">value</span>)
</div>
<p>Resumes a fiber with a result value.</p>
<h2 id="await-syntax">Await Syntax</h2>
<p>Wren-CLI provides a convenient syntax for awaiting async functions. Instead of using <code>.call()</code>, you can call async functions directly after <code>await</code>:</p>
<h3>Before and After</h3>
<pre><code>// Old syntax (still works)
var result = await asyncFn.call(arg1, arg2)
// New syntax (recommended)
var result = await asyncFn(arg1, arg2)</code></pre>
<p>The compiler automatically translates <code>await fn(args)</code> to <code>await fn.call(args)</code>.</p>
<h3>Supported Patterns</h3>
<table class="params-table">
<thead>
<tr><th>Pattern</th><th>Example</th></tr>
</thead>
<tbody>
<tr><td>No arguments</td><td><code>await getValue()</code></td></tr>
<tr><td>Single argument</td><td><code>await double(21)</code></td></tr>
<tr><td>Multiple arguments</td><td><code>await add(3, 4, 5)</code></td></tr>
<tr><td>Nested awaits</td><td><code>await outer(await inner(x))</code></td></tr>
<tr><td>In expressions</td><td><code>var x = 1 + await fn(2)</code></td></tr>
<tr><td>In conditions</td><td><code>if (await check(x)) { }</code></td></tr>
<tr><td>Block argument</td><td><code>await map(list) { |x| x * 2 }</code></td></tr>
</tbody>
</table>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Method chaining after <code>await fn(args)</code> requires assigning the result first:</p>
<pre><code>var list = await getList(5)
System.print(list.count)</code></pre>
</div>
<h3>Examples</h3>
<pre><code>import "scheduler" for Scheduler, Future
var double = async { |x| x * 2 }
var add = async { |a, b| a + b }
// Basic usage
System.print(await double(21)) // 42
System.print(await add(3, 4)) // 7
// Nested awaits
var result = await double(await add(5, 5)) // 20
// Method chaining
var getList = async { |n| [1, 2, 3, n] }
System.print(await getList(4).count) // 4
// In expressions
var total = await add(10, 20) + await double(5) // 40</code></pre>
<h3>Class-Based Async Patterns</h3>
<p>Static getters that return async functions must first be assigned to a variable:</p>
<pre><code>import "scheduler" for Scheduler, Future
class Calculator {
static add { async { |a, b| a + b } }
static multiply { async { |a, b| a * b } }
static square { async { |x| x * x } }
static compute(a, b) {
var addFn = Calculator.add
var multiplyFn = Calculator.multiply
var sum = await addFn(a, b)
var product = await multiplyFn(a, b)
return [sum, product]
}
}
// Assign getter to variable first, then await
var add = Calculator.add
System.print(await add(3, 4)) // 7
var multiply = Calculator.multiply
System.print(await multiply(5, 6)) // 30
var square = Calculator.square
System.print(await square(8)) // 64</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>The <code>fn(args)</code> syntax only works directly after <code>await</code>. Outside of <code>await</code>, use <code>fn.call(args)</code> to invoke async functions.</p>
</div>
<h2>Future Class</h2>
<div class="class-header">
<h3>Future</h3>
<p>Represents a pending async result</p>
</div>
<p>A <code>Future</code> is returned when you call an async function with <code>.call()</code> instead of using <code>await</code> directly. This enables concurrent execution by starting multiple operations without waiting.</p>
<h3>Creating Futures</h3>
<pre><code>import "scheduler" for Scheduler, Future
var double = async { |x| x * 2 }
// Direct call with await - waits immediately
var result = await double(21)
// Using .call() returns a Future - does not wait
var future = double.call(21)
// Later, await the future to get the result
var result = await future</code></pre>
<h3>Concurrent Execution Pattern</h3>
<pre><code>import "scheduler" for Scheduler, Future
import "web" for Client
var fetch = async { |url| Client.get(url) }
// Start all requests at once (returns Futures)
var f1 = fetch.call("https://api.example.com/users")
var f2 = fetch.call("https://api.example.com/posts")
var f3 = fetch.call("https://api.example.com/comments")
// All three requests are now running concurrently
// Wait for each result
var users = await f1
var posts = await f2
var comments = await f3</code></pre>
<h2>How Async Works</h2>
<p>Wren-CLI uses an event loop (libuv) for non-blocking I/O. Here is how async operations work:</p>
<ol>
<li>A Wren fiber calls an async method (e.g., <code>Http.get</code>)</li>
<li>The method starts a native async operation and suspends the fiber</li>
<li>The event loop continues processing other events</li>
<li>When the operation completes, the fiber is resumed with the result</li>
</ol>
<h3>Under the Hood</h3>
<pre><code>// How Timer.sleep works internally
class Timer {
static sleep(ms) {
return Scheduler.await_ {
Timer.sleep_(ms, Fiber.current)
}
}
foreign static sleep_(ms, fiber)
}</code></pre>
<h2>Examples</h2>
<h3>Sequential vs Concurrent Execution</h3>
<p>Using <code>await fn(args)</code> executes operations sequentially:</p>
<pre><code>import "scheduler" for Scheduler, Future
import "web" for Client
var fetch = async { |url| Client.get(url) }
// Sequential: each request waits for the previous one
var r1 = await fetch("https://api.example.com/1")
var r2 = await fetch("https://api.example.com/2")
var r3 = await fetch("https://api.example.com/3")</code></pre>
<p>Using <code>.call()</code> enables concurrent execution:</p>
<pre><code>import "scheduler" for Scheduler, Future
import "web" for Client
var fetch = async { |url| Client.get(url) }
// Start all requests at once (concurrent)
var f1 = fetch.call("https://api.example.com/1")
var f2 = fetch.call("https://api.example.com/2")
var f3 = fetch.call("https://api.example.com/3")
// All three requests are running in parallel
// Now wait for results
var r1 = await f1
var r2 = await f2
var r3 = await f3</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>The scheduler is used internally by modules. Most user code does not need to interact with it directly. Use higher-level modules like <code>timer</code>, <code>http</code>, and <code>io</code> instead.</p>
</div>
<div class="admonition tip">
<div class="admonition-title">Tip</div>
<p>Async operations in Wren-CLI are cooperative, not preemptive. A fiber runs until it explicitly yields or calls an async operation.</p>
</div>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>signal - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html" class="active">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>signal</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "signal" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "signal"}] %}
{% set prev_page = {"url": "api/scheduler.html", "title": "scheduler"} %}
{% set next_page = {"url": "api/sqlite.html", "title": "sqlite"} %}
{% block article %}
<h1>signal</h1>
<p>The <code>signal</code> module provides Unix signal handling capabilities, allowing scripts to trap, ignore, or reset signal handlers.</p>
@ -339,14 +257,4 @@ while (true) {
<div class="admonition-title">Tip</div>
<p>Always implement graceful shutdown handlers for long-running services. Use SIGTERM for clean shutdowns and reserve SIGINT for interactive termination.</p>
</div>
</article>
<footer class="page-footer">
<a href="env.html" class="prev">env</a>
<a href="subprocess.html" class="next">subprocess</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>sqlite - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html" class="active">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>sqlite</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "sqlite" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "sqlite"}] %}
{% set prev_page = {"url": "api/signal.html", "title": "signal"} %}
{% set next_page = {"url": "api/subprocess.html", "title": "subprocess"} %}
{% block article %}
<h1>sqlite</h1>
<p>The <code>sqlite</code> module provides SQLite database functionality for persistent data storage.</p>
@ -246,14 +164,4 @@ db.close()</code></pre>
<div class="admonition-title">Note</div>
<p>Use <code>":memory:"</code> as the path for an in-memory database that does not persist to disk.</p>
</div>
</article>
<footer class="page-footer">
<a href="subprocess.html" class="prev">subprocess</a>
<a href="datetime.html" class="next">datetime</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,111 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>String - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html" class="active">String</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
<li><a href="uuid.html">uuid</a></li>
<li><a href="html.html">html</a></li>
<li><a href="argparse.html">argparse</a></li>
<li><a href="wdantic.html">wdantic</a></li>
<li><a href="dataset.html">dataset</a></li>
<li><a href="markdown.html">markdown</a></li>
<li><a href="web.html">web</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>String</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "String" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "String"}] %}
{% set prev_page = {"url": "api/index.html", "title": "Overview"} %}
{% set next_page = {"url": "api/http.html", "title": "http"} %}
{% block article %}
<h1>String</h1>
<p>The <code>String</code> class is a core type available globally without imports. Strings in Wren are immutable sequences of bytes, typically representing UTF-8 encoded text. All methods return new strings rather than modifying the original.</p>
@ -441,14 +342,4 @@ System.print(s.reverse) // dlroW olleH</code></pre>
<div class="admonition-title">Note</div>
<p>String is a core type available globally. No <code>import</code> statement is needed. All string methods operate on ASCII characters. Multi-byte UTF-8 characters are preserved but case conversion applies only to ASCII letters (a-z, A-Z).</p>
</div>
</article>
<footer class="page-footer">
<a href="index.html" class="prev">Overview</a>
<a href="http.html" class="next">http</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -0,0 +1,90 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "subprocess" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "subprocess"}] %}
{% set prev_page = {"url": "api/sqlite.html", "title": "sqlite"} %}
{% set next_page = {"url": "api/sysinfo.html", "title": "sysinfo"} %}
{% block article %}
<h1>subprocess</h1>
<p>The <code>subprocess</code> module allows running external commands and capturing their output.</p>
<pre><code>import "subprocess" for Subprocess</code></pre>
<h2>Subprocess Class</h2>
<div class="class-header">
<h3>Subprocess</h3>
<p>External process execution</p>
</div>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Subprocess.run</span>(<span class="param">command</span>) → <span class="type">Map</span>
</div>
<p>Runs a command and waits for it to complete.</p>
<ul class="param-list">
<li><span class="param-name">command</span> <span class="param-type">(List)</span> - Command and arguments as a list</li>
<li><span class="returns">Returns:</span> Map with "stdout", "stderr", and "exitCode"</li>
</ul>
<pre><code>var result = Subprocess.run(["ls", "-la"])
System.print("Exit code: %(result["exitCode"])")
System.print("Output: %(result["stdout"])")</code></pre>
<div class="method-signature">
<span class="method-name">Subprocess.run</span>(<span class="param">command</span>, <span class="param">input</span>) → <span class="type">Map</span>
</div>
<p>Runs a command with input data provided to stdin.</p>
<ul class="param-list">
<li><span class="param-name">command</span> <span class="param-type">(List)</span> - Command and arguments</li>
<li><span class="param-name">input</span> <span class="param-type">(String)</span> - Input to send to stdin</li>
</ul>
<pre><code>var result = Subprocess.run(["cat"], "Hello, World!")
System.print(result["stdout"]) // Hello, World!</code></pre>
<h2>Examples</h2>
<h3>Running Commands</h3>
<pre><code>import "subprocess" for Subprocess
var result = Subprocess.run(["echo", "Hello"])
System.print(result["stdout"]) // Hello
var files = Subprocess.run(["ls", "-la", "/tmp"])
System.print(files["stdout"])</code></pre>
<h3>Checking Exit Codes</h3>
<pre><code>import "subprocess" for Subprocess
var result = Subprocess.run(["grep", "pattern", "file.txt"])
if (result["exitCode"] == 0) {
System.print("Found: %(result["stdout"])")
} else {
System.print("Not found or error")
}</code></pre>
<h3>Processing Data</h3>
<pre><code>import "subprocess" for Subprocess
import "json" for Json
var result = Subprocess.run(["curl", "-s", "https://api.example.com/data"])
if (result["exitCode"] == 0) {
var data = Json.parse(result["stdout"])
System.print(data)
}</code></pre>
<h3>Piping Data</h3>
<pre><code>import "subprocess" for Subprocess
var input = "line1\nline2\nline3"
var result = Subprocess.run(["wc", "-l"], input)
System.print("Lines: %(result["stdout"].trim())")</code></pre>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Commands are not executed through a shell. Use explicit command and argument lists, not shell command strings.</p>
</div>
{% endblock %}

View File

@ -0,0 +1,244 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "sysinfo" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "sysinfo"}] %}
{% set prev_page = {"url": "api/subprocess.html", "title": "subprocess"} %}
{% set next_page = {"url": "api/tempfile.html", "title": "tempfile"} %}
{% block article %}
<h1>sysinfo</h1>
<p>The <code>sysinfo</code> module provides system information including CPU details, memory usage, uptime, and network interfaces. All values are retrieved from libuv.</p>
<pre><code>import "sysinfo" for SysInfo</code></pre>
<div class="toc">
<h4>On This Page</h4>
<ul>
<li><a href="#sysinfo-class">SysInfo Class</a></li>
<li><a href="#examples">Examples</a></li>
</ul>
</div>
<h2 id="sysinfo-class">SysInfo Class</h2>
<div class="class-header">
<h3>SysInfo</h3>
<p>System information properties</p>
</div>
<h3>Static Properties</h3>
<div class="method-signature">
<span class="method-name">SysInfo.cpuInfo</span> &#8594; <span class="type">List</span>
</div>
<p>Returns a list of maps containing CPU information. Each map includes <code>model</code>, <code>speed</code>, and <code>times</code> (with <code>user</code>, <code>nice</code>, <code>sys</code>, <code>idle</code>, <code>irq</code>).</p>
<pre><code>var cpus = SysInfo.cpuInfo
for (cpu in cpus) {
System.print("Model: %(cpu["model"])")
System.print("Speed: %(cpu["speed"]) MHz")
}</code></pre>
<div class="method-signature">
<span class="method-name">SysInfo.cpuCount</span> &#8594; <span class="type">Num</span>
</div>
<p>Returns the number of CPUs/cores available on the system.</p>
<pre><code>System.print("CPU count: %(SysInfo.cpuCount)")</code></pre>
<div class="method-signature">
<span class="method-name">SysInfo.loadAverage</span> &#8594; <span class="type">List</span>
</div>
<p>Returns the system load averages as a list of three numbers: 1-minute, 5-minute, and 15-minute averages. On Windows, returns <code>[0, 0, 0]</code>.</p>
<pre><code>var load = SysInfo.loadAverage
System.print("Load: %(load[0]) %(load[1]) %(load[2])")</code></pre>
<div class="method-signature">
<span class="method-name">SysInfo.totalMemory</span> &#8594; <span class="type">Num</span>
</div>
<p>Returns the total system memory in bytes.</p>
<pre><code>var totalMB = SysInfo.totalMemory / 1024 / 1024
System.print("Total memory: %(totalMB) MB")</code></pre>
<div class="method-signature">
<span class="method-name">SysInfo.freeMemory</span> &#8594; <span class="type">Num</span>
</div>
<p>Returns the free system memory in bytes.</p>
<pre><code>var freeMB = SysInfo.freeMemory / 1024 / 1024
System.print("Free memory: %(freeMB) MB")</code></pre>
<div class="method-signature">
<span class="method-name">SysInfo.uptime</span> &#8594; <span class="type">Num</span>
</div>
<p>Returns the system uptime in seconds.</p>
<pre><code>var hours = (SysInfo.uptime / 3600).floor
System.print("Uptime: %(hours) hours")</code></pre>
<div class="method-signature">
<span class="method-name">SysInfo.hostname</span> &#8594; <span class="type">String</span>
</div>
<p>Returns the system hostname.</p>
<pre><code>System.print("Hostname: %(SysInfo.hostname)")</code></pre>
<div class="method-signature">
<span class="method-name">SysInfo.networkInterfaces</span> &#8594; <span class="type">Map</span>
</div>
<p>Returns a map of network interfaces. Each key is an interface name, and each value is a list of address maps containing <code>address</code>, <code>netmask</code>, <code>family</code> (IPv4 or IPv6), <code>internal</code>, and <code>mac</code>.</p>
<pre><code>var interfaces = SysInfo.networkInterfaces
for (name in interfaces.keys) {
System.print("Interface: %(name)")
for (addr in interfaces[name]) {
System.print(" %(addr["family"]): %(addr["address"])")
}
}</code></pre>
<div class="method-signature">
<span class="method-name">SysInfo.hrtime</span> &#8594; <span class="type">Num</span>
</div>
<p>Returns a high-resolution time value in nanoseconds. Useful for precise timing measurements.</p>
<pre><code>var start = SysInfo.hrtime
// ... do some work ...
var elapsed = SysInfo.hrtime - start
System.print("Elapsed: %(elapsed / 1000000) ms")</code></pre>
<div class="method-signature">
<span class="method-name">SysInfo.residentMemory</span> &#8594; <span class="type">Num</span>
</div>
<p>Returns the resident set size (RSS) of the current process in bytes.</p>
<pre><code>var rssMB = SysInfo.residentMemory / 1024 / 1024
System.print("Process RSS: %(rssMB) MB")</code></pre>
<div class="method-signature">
<span class="method-name">SysInfo.constrainedMemory</span> &#8594; <span class="type">Num</span>
</div>
<p>Returns the memory limit for the process in bytes (e.g., cgroup limit). Returns 0 if no limit is set.</p>
<pre><code>var limit = SysInfo.constrainedMemory
if (limit > 0) {
System.print("Memory limit: %(limit / 1024 / 1024) MB")
} else {
System.print("No memory limit set")
}</code></pre>
<h2 id="examples">Examples</h2>
<h3>System Overview</h3>
<pre><code>import "sysinfo" for SysInfo
System.print("=== System Information ===")
System.print("Hostname: %(SysInfo.hostname)")
System.print("CPUs: %(SysInfo.cpuCount)")
System.print("")
System.print("=== Memory ===")
var totalGB = (SysInfo.totalMemory / 1024 / 1024 / 1024 * 100).floor / 100
var freeGB = (SysInfo.freeMemory / 1024 / 1024 / 1024 * 100).floor / 100
var usedPercent = ((1 - SysInfo.freeMemory / SysInfo.totalMemory) * 100).floor
System.print("Total: %(totalGB) GB")
System.print("Free: %(freeGB) GB")
System.print("Used: %(usedPercent)\%")
System.print("")
System.print("=== Load Average ===")
var load = SysInfo.loadAverage
System.print("1 min: %(load[0])")
System.print("5 min: %(load[1])")
System.print("15 min: %(load[2])")
System.print("")
System.print("=== Uptime ===")
var uptime = SysInfo.uptime
var days = (uptime / 86400).floor
var hours = ((uptime % 86400) / 3600).floor
var minutes = ((uptime % 3600) / 60).floor
System.print("%(days) days, %(hours) hours, %(minutes) minutes")</code></pre>
<h3>CPU Information</h3>
<pre><code>import "sysinfo" for SysInfo
var cpus = SysInfo.cpuInfo
System.print("CPU Information:")
System.print("================")
var i = 0
for (cpu in cpus) {
System.print("CPU %(i): %(cpu["model"])")
System.print(" Speed: %(cpu["speed"]) MHz")
var times = cpu["times"]
var total = times["user"] + times["nice"] + times["sys"] + times["idle"] + times["irq"]
var idle = (times["idle"] / total * 100).floor
System.print(" Idle: %(idle)\%")
i = i + 1
}</code></pre>
<h3>Network Interfaces</h3>
<pre><code>import "sysinfo" for SysInfo
var interfaces = SysInfo.networkInterfaces
System.print("Network Interfaces:")
System.print("===================")
for (name in interfaces.keys) {
var addrs = interfaces[name]
System.print("%(name):")
for (addr in addrs) {
if (!addr["internal"]) {
System.print(" %(addr["family"]): %(addr["address"])")
if (addr["mac"] != "00:00:00:00:00:00") {
System.print(" MAC: %(addr["mac"])")
}
}
}
}</code></pre>
<h3>Performance Timing</h3>
<pre><code>import "sysinfo" for SysInfo
var measure = Fn.new { |name, fn|
var start = SysInfo.hrtime
fn.call()
var elapsed = SysInfo.hrtime - start
System.print("%(name): %(elapsed / 1000000) ms")
}
measure.call("String concatenation") {
var s = ""
for (i in 0...1000) {
s = s + "x"
}
}
measure.call("List operations") {
var list = []
for (i in 0...10000) {
list.add(i)
}
}</code></pre>
<h3>Memory Monitoring</h3>
<pre><code>import "sysinfo" for SysInfo
import "timer" for Timer
System.print("Memory Monitor (press Ctrl+C to stop)")
System.print("=====================================")
while (true) {
var totalMB = (SysInfo.totalMemory / 1024 / 1024).floor
var freeMB = (SysInfo.freeMemory / 1024 / 1024).floor
var rssMB = (SysInfo.residentMemory / 1024 / 1024).floor
var usedPercent = ((1 - SysInfo.freeMemory / SysInfo.totalMemory) * 100).floor
System.print("System: %(freeMB)/%(totalMB) MB free (%(usedPercent)\% used) | Process RSS: %(rssMB) MB")
Timer.sleep(1000)
}</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Load average values are not available on Windows and will return <code>[0, 0, 0]</code>.</p>
</div>
<div class="admonition tip">
<div class="admonition-title">Tip</div>
<p>Use <code>SysInfo.hrtime</code> for precise performance measurements. It provides nanosecond resolution and is not affected by system clock adjustments.</p>
</div>
{% endblock %}

View File

@ -1,102 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>tempfile - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="tempfile.html" class="active">tempfile</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
<li><a href="uuid.html">uuid</a></li>
<li><a href="html.html">html</a></li>
<li><a href="argparse.html">argparse</a></li>
<li><a href="wdantic.html">wdantic</a></li>
<li><a href="dataset.html">dataset</a></li>
<li><a href="markdown.html">markdown</a></li>
<li><a href="web.html">web</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>tempfile</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "tempfile" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "tempfile"}] %}
{% set prev_page = {"url": "api/sysinfo.html", "title": "sysinfo"} %}
{% set next_page = {"url": "api/timer.html", "title": "timer"} %}
{% block article %}
<h1>tempfile</h1>
<p>The <code>tempfile</code> module provides utilities for creating temporary files and directories. It mirrors Python's <code>tempfile</code> API with three classes: <code>TempFile</code> for low-level operations, <code>NamedTemporaryFile</code> for temporary files with automatic cleanup, and <code>TemporaryDirectory</code> for temporary directories with recursive cleanup.</p>
@ -357,14 +267,4 @@ System.print("Log file: %(tmp.name)")</code></pre>
<div class="admonition-title">Note</div>
<p>File creation via <code>mkstemp</code> uses exclusive creation flags (<code>O_EXCL</code>) for atomic file creation, preventing race conditions between checking for existence and creating the file. Random suffixes are generated using <code>Crypto.randomBytes</code> for cryptographic quality randomness.</p>
</div>
</article>
<footer class="page-footer">
<a href="pathlib.html" class="prev">pathlib</a>
<a href="scheduler.html" class="next">scheduler</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -0,0 +1,227 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "timer" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "timer"}] %}
{% set prev_page = {"url": "api/tempfile.html", "title": "tempfile"} %}
{% set next_page = {"url": "api/tls.html", "title": "tls"} %}
{% block article %}
<h1>timer</h1>
<p>The <code>timer</code> module provides timing functionality for delays, intervals, and immediate execution.</p>
<pre><code>import "timer" for Timer, TimerHandle</code></pre>
<h2>Timer Class</h2>
<div class="class-header">
<h3>Timer</h3>
<p>Timer and delay functionality</p>
</div>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Timer.sleep</span>(<span class="param">milliseconds</span>)
</div>
<p>Pauses execution for the specified duration. The fiber suspends and other operations can proceed.</p>
<ul class="param-list">
<li><span class="param-name">milliseconds</span> <span class="param-type">(Num)</span> - Duration to sleep in milliseconds</li>
</ul>
<pre><code>System.print("Starting...")
Timer.sleep(1000) // Wait 1 second
System.print("Done!")</code></pre>
<div class="method-signature">
<span class="method-name">Timer.interval</span>(<span class="param">milliseconds</span>, <span class="param">fn</span>) &#8594; <span class="type">TimerHandle</span>
</div>
<p>Creates a repeating timer that calls the callback function at the specified interval. Returns a TimerHandle that can be used to stop the interval.</p>
<ul class="param-list">
<li><span class="param-name">milliseconds</span> <span class="param-type">(Num)</span> - Interval between calls in milliseconds</li>
<li><span class="param-name">fn</span> <span class="param-type">(Fn)</span> - Callback function to execute (no arguments)</li>
<li><span class="returns">Returns:</span> TimerHandle for controlling the interval</li>
</ul>
<pre><code>var count = 0
var handle = Timer.interval(1000) {
count = count + 1
System.print("Tick %(count)")
if (count >= 5) {
handle.stop()
}
}</code></pre>
<div class="method-signature">
<span class="method-name">Timer.immediate</span>(<span class="param">fn</span>)
</div>
<p>Schedules a function to run on the next iteration of the event loop. Useful for deferring execution without blocking.</p>
<ul class="param-list">
<li><span class="param-name">fn</span> <span class="param-type">(Fn)</span> - Callback function to execute (no arguments)</li>
</ul>
<pre><code>System.print("Before")
Timer.immediate {
System.print("Deferred execution")
}
System.print("After")
// Output: Before, After, Deferred execution</code></pre>
<h2>TimerHandle Class</h2>
<div class="class-header">
<h3>TimerHandle</h3>
<p>Handle for controlling interval timers</p>
</div>
<h3>Methods</h3>
<div class="method-signature">
<span class="method-name">stop</span>()
</div>
<p>Stops the interval timer. After calling stop, the callback will no longer be invoked.</p>
<pre><code>handle.stop()</code></pre>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">isActive</span> &#8594; <span class="type">Bool</span>
</div>
<p>Returns true if the interval timer is still active.</p>
<pre><code>if (handle.isActive) {
System.print("Timer is running")
}</code></pre>
<h2>Examples</h2>
<h3>Basic Delay</h3>
<pre><code>import "timer" for Timer
System.print("Starting...")
Timer.sleep(2000)
System.print("2 seconds later...")</code></pre>
<h3>Polling Loop</h3>
<pre><code>import "timer" for Timer
import "http" for Http
var checkStatus = Fn.new {
var response = Http.get("https://api.example.com/status")
return response.json["ready"]
}
while (!checkStatus.call()) {
System.print("Waiting...")
Timer.sleep(5000) // Check every 5 seconds
}
System.print("Ready!")</code></pre>
<h3>Rate Limiting</h3>
<pre><code>import "timer" for Timer
import "http" for Http
var urls = [
"https://api.example.com/1",
"https://api.example.com/2",
"https://api.example.com/3"
]
for (url in urls) {
var response = Http.get(url)
System.print("%(url): %(response.statusCode)")
Timer.sleep(1000) // 1 second between requests
}</code></pre>
<h3>Timeout Pattern</h3>
<pre><code>import "timer" for Timer
import "datetime" for DateTime, Duration
var start = DateTime.now()
var timeout = Duration.fromSeconds(30)
while (true) {
var elapsed = DateTime.now() - start
if (elapsed.seconds >= timeout.seconds) {
System.print("Timeout!")
break
}
// Do work...
Timer.sleep(100)
}</code></pre>
<h3>Animation/Progress</h3>
<pre><code>import "timer" for Timer
var frames = ["-", "\\", "|", "/"]
var i = 0
for (step in 1..20) {
System.write("\rProcessing %(frames[i]) ")
i = (i + 1) % 4
Timer.sleep(100)
}
System.print("\rDone! ")</code></pre>
<h3>Interval Timer</h3>
<pre><code>import "timer" for Timer, TimerHandle
var seconds = 0
var handle = Timer.interval(1000) {
seconds = seconds + 1
System.print("Elapsed: %(seconds) seconds")
}
Timer.sleep(5000)
handle.stop()
System.print("Timer stopped")</code></pre>
<h3>Periodic Status Updates</h3>
<pre><code>import "timer" for Timer, TimerHandle
import "sysinfo" for SysInfo
var handle = Timer.interval(2000) {
var freeMB = (SysInfo.freeMemory / 1024 / 1024).floor
var load = SysInfo.loadAverage[0]
System.print("Memory: %(freeMB) MB free | Load: %(load)")
}
System.print("Monitoring system (runs for 10 seconds)...")
Timer.sleep(10000)
handle.stop()</code></pre>
<h3>Immediate Execution</h3>
<pre><code>import "timer" for Timer
System.print("1. Synchronous")
Timer.immediate {
System.print("3. Immediate callback")
}
System.print("2. Still synchronous")
Timer.sleep(100)</code></pre>
<h3>Self-Stopping Interval</h3>
<pre><code>import "timer" for Timer, TimerHandle
var count = 0
var handle = Timer.interval(500) {
count = count + 1
System.print("Count: %(count)")
if (count >= 10) {
System.print("Stopping at %(count)")
handle.stop()
}
}</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Timer.sleep is non-blocking at the event loop level. The current fiber suspends, but other scheduled operations can continue.</p>
</div>
<div class="admonition tip">
<div class="admonition-title">Tip</div>
<p>Use <code>Timer.interval</code> for recurring tasks like heartbeats, polling, or animations. The returned handle allows you to stop the interval from within the callback itself.</p>
</div>
{% endblock %}

View File

@ -1,94 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>tls - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html" class="active">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>tls</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "tls" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "tls"}] %}
{% set prev_page = {"url": "api/timer.html", "title": "timer"} %}
{% set next_page = {"url": "api/udp.html", "title": "udp"} %}
{% block article %}
<h1>tls</h1>
<p>The <code>tls</code> module provides SSL/TLS socket support for secure network connections. It uses OpenSSL for encryption and is used internally by the <code>http</code> module for HTTPS connections.</p>
@ -232,14 +150,4 @@ socket.close()</code></pre>
<div class="admonition-title">Tip</div>
<p>For most use cases, consider using the higher-level <code>http</code> module which handles TLS connections, HTTP protocol details, and response parsing automatically.</p>
</div>
</article>
<footer class="page-footer">
<a href="websocket.html" class="prev">websocket</a>
<a href="net.html" class="next">net</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -0,0 +1,318 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "udp" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "udp"}] %}
{% set prev_page = {"url": "api/tls.html", "title": "tls"} %}
{% set next_page = {"url": "api/uuid.html", "title": "uuid"} %}
{% block article %}
<h1>udp</h1>
<p>The <code>udp</code> module provides UDP (User Datagram Protocol) socket functionality for connectionless networking. UDP is useful for applications requiring fast, lightweight communication without the overhead of TCP connection management.</p>
<pre><code>import "udp" for UdpSocket, UdpMessage</code></pre>
<div class="toc">
<h4>On This Page</h4>
<ul>
<li><a href="#udpsocket-class">UdpSocket Class</a></li>
<li><a href="#udpmessage-class">UdpMessage Class</a></li>
<li><a href="#examples">Examples</a></li>
</ul>
</div>
<h2 id="udpsocket-class">UdpSocket Class</h2>
<div class="class-header">
<h3>UdpSocket</h3>
<p>UDP datagram socket for sending and receiving messages</p>
</div>
<h3>Constructor</h3>
<div class="method-signature">
<span class="method-name">UdpSocket.new</span>() &#8594; <span class="type">UdpSocket</span>
</div>
<p>Creates a new UDP socket.</p>
<pre><code>var socket = UdpSocket.new()</code></pre>
<h3>Methods</h3>
<div class="method-signature">
<span class="method-name">bind</span>(<span class="param">host</span>, <span class="param">port</span>)
</div>
<p>Binds the socket to a local address and port. Required before receiving messages.</p>
<ul class="param-list">
<li><span class="param-name">host</span> <span class="param-type">(String)</span> - Local address to bind (e.g., "0.0.0.0" for all interfaces)</li>
<li><span class="param-name">port</span> <span class="param-type">(Num)</span> - Port number to bind</li>
</ul>
<pre><code>socket.bind("0.0.0.0", 8080)</code></pre>
<div class="method-signature">
<span class="method-name">send</span>(<span class="param">data</span>, <span class="param">host</span>, <span class="param">port</span>)
</div>
<p>Sends a datagram to the specified address. Async operation.</p>
<ul class="param-list">
<li><span class="param-name">data</span> <span class="param-type">(String)</span> - Data to send</li>
<li><span class="param-name">host</span> <span class="param-type">(String)</span> - Destination hostname or IP</li>
<li><span class="param-name">port</span> <span class="param-type">(Num)</span> - Destination port</li>
</ul>
<pre><code>socket.send("Hello!", "192.168.1.100", 8080)</code></pre>
<div class="method-signature">
<span class="method-name">receive</span>() &#8594; <span class="type">UdpMessage</span>
</div>
<p>Receives a datagram. Blocks until data arrives. Returns a UdpMessage containing the data and sender information.</p>
<pre><code>var msg = socket.receive()
System.print("From %(msg.address):%(msg.port): %(msg.data)")</code></pre>
<div class="method-signature">
<span class="method-name">close</span>()
</div>
<p>Closes the socket.</p>
<pre><code>socket.close()</code></pre>
<div class="method-signature">
<span class="method-name">setBroadcast</span>(<span class="param">enabled</span>)
</div>
<p>Enables or disables broadcast mode. When enabled, the socket can send to broadcast addresses.</p>
<ul class="param-list">
<li><span class="param-name">enabled</span> <span class="param-type">(Bool)</span> - True to enable broadcast</li>
</ul>
<pre><code>socket.setBroadcast(true)
socket.send("Broadcast message", "255.255.255.255", 8080)</code></pre>
<div class="method-signature">
<span class="method-name">setMulticastTTL</span>(<span class="param">ttl</span>)
</div>
<p>Sets the time-to-live for multicast packets.</p>
<ul class="param-list">
<li><span class="param-name">ttl</span> <span class="param-type">(Num)</span> - TTL value (1-255)</li>
</ul>
<pre><code>socket.setMulticastTTL(64)</code></pre>
<div class="method-signature">
<span class="method-name">setMulticastLoopback</span>(<span class="param">enabled</span>)
</div>
<p>Enables or disables multicast loopback. When enabled, the socket receives its own multicast messages.</p>
<ul class="param-list">
<li><span class="param-name">enabled</span> <span class="param-type">(Bool)</span> - True to enable loopback</li>
</ul>
<pre><code>socket.setMulticastLoopback(false)</code></pre>
<div class="method-signature">
<span class="method-name">joinMulticast</span>(<span class="param">group</span>)
</div>
<p>Joins a multicast group on the default interface.</p>
<ul class="param-list">
<li><span class="param-name">group</span> <span class="param-type">(String)</span> - Multicast group address (e.g., "239.255.0.1")</li>
</ul>
<pre><code>socket.joinMulticast("239.255.0.1")</code></pre>
<div class="method-signature">
<span class="method-name">joinMulticast</span>(<span class="param">group</span>, <span class="param">iface</span>)
</div>
<p>Joins a multicast group on a specific interface.</p>
<ul class="param-list">
<li><span class="param-name">group</span> <span class="param-type">(String)</span> - Multicast group address</li>
<li><span class="param-name">iface</span> <span class="param-type">(String)</span> - Local interface address to join from</li>
</ul>
<pre><code>socket.joinMulticast("239.255.0.1", "192.168.1.100")</code></pre>
<div class="method-signature">
<span class="method-name">leaveMulticast</span>(<span class="param">group</span>)
</div>
<p>Leaves a multicast group on the default interface.</p>
<ul class="param-list">
<li><span class="param-name">group</span> <span class="param-type">(String)</span> - Multicast group address</li>
</ul>
<pre><code>socket.leaveMulticast("239.255.0.1")</code></pre>
<div class="method-signature">
<span class="method-name">leaveMulticast</span>(<span class="param">group</span>, <span class="param">iface</span>)
</div>
<p>Leaves a multicast group on a specific interface.</p>
<ul class="param-list">
<li><span class="param-name">group</span> <span class="param-type">(String)</span> - Multicast group address</li>
<li><span class="param-name">iface</span> <span class="param-type">(String)</span> - Local interface address</li>
</ul>
<pre><code>socket.leaveMulticast("239.255.0.1", "192.168.1.100")</code></pre>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">localAddress</span> &#8594; <span class="type">String</span>
</div>
<p>Returns the local address the socket is bound to.</p>
<pre><code>System.print("Bound to: %(socket.localAddress)")</code></pre>
<div class="method-signature">
<span class="method-name">localPort</span> &#8594; <span class="type">Num</span>
</div>
<p>Returns the local port the socket is bound to.</p>
<pre><code>System.print("Port: %(socket.localPort)")</code></pre>
<h2 id="udpmessage-class">UdpMessage Class</h2>
<div class="class-header">
<h3>UdpMessage</h3>
<p>Represents a received UDP datagram</p>
</div>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">data</span> &#8594; <span class="type">String</span>
</div>
<p>The data contained in the message.</p>
<div class="method-signature">
<span class="method-name">address</span> &#8594; <span class="type">String</span>
</div>
<p>The IP address of the sender.</p>
<div class="method-signature">
<span class="method-name">port</span> &#8594; <span class="type">Num</span>
</div>
<p>The port number of the sender.</p>
<pre><code>var msg = socket.receive()
System.print("Data: %(msg.data)")
System.print("From: %(msg.address):%(msg.port)")</code></pre>
<h2 id="examples">Examples</h2>
<h3>Simple UDP Server</h3>
<pre><code>import "udp" for UdpSocket
var socket = UdpSocket.new()
socket.bind("0.0.0.0", 8080)
System.print("UDP server listening on port 8080")
while (true) {
var msg = socket.receive()
System.print("Received from %(msg.address):%(msg.port): %(msg.data)")
socket.send("Echo: %(msg.data)", msg.address, msg.port)
}</code></pre>
<h3>Simple UDP Client</h3>
<pre><code>import "udp" for UdpSocket
var socket = UdpSocket.new()
socket.bind("0.0.0.0", 0)
socket.send("Hello, server!", "127.0.0.1", 8080)
var response = socket.receive()
System.print("Server responded: %(response.data)")
socket.close()</code></pre>
<h3>Broadcast Discovery</h3>
<pre><code>import "udp" for UdpSocket
import "timer" for Timer
var socket = UdpSocket.new()
socket.bind("0.0.0.0", 0)
socket.setBroadcast(true)
System.print("Sending discovery broadcast...")
socket.send("DISCOVER", "255.255.255.255", 9999)
System.print("Waiting for responses...")
var timeout = false
var timeoutFiber = Fiber.new {
Timer.sleep(3000)
timeout = true
}
timeoutFiber.call()
while (!timeout) {
var msg = socket.receive()
System.print("Found device at %(msg.address): %(msg.data)")
}
socket.close()</code></pre>
<h3>Multicast Group</h3>
<pre><code>import "udp" for UdpSocket
var MULTICAST_GROUP = "239.255.0.1"
var MULTICAST_PORT = 5000
var socket = UdpSocket.new()
socket.bind("0.0.0.0", MULTICAST_PORT)
socket.joinMulticast(MULTICAST_GROUP)
socket.setMulticastLoopback(true)
System.print("Joined multicast group %(MULTICAST_GROUP)")
socket.send("Hello multicast!", MULTICAST_GROUP, MULTICAST_PORT)
var msg = socket.receive()
System.print("Received: %(msg.data) from %(msg.address)")
socket.leaveMulticast(MULTICAST_GROUP)
socket.close()</code></pre>
<h3>DNS-Style Query/Response</h3>
<pre><code>import "udp" for UdpSocket
import "json" for Json
var socket = UdpSocket.new()
socket.bind("0.0.0.0", 0)
var query = Json.stringify({
"type": "lookup",
"name": "example.local"
})
socket.send(query, "192.168.1.1", 5353)
var response = socket.receive()
var result = Json.parse(response.data)
System.print("Lookup result: %(result)")
socket.close()</code></pre>
<h3>UDP Ping</h3>
<pre><code>import "udp" for UdpSocket
import "sysinfo" for SysInfo
var socket = UdpSocket.new()
socket.bind("0.0.0.0", 0)
var target = "127.0.0.1"
var port = 8080
for (i in 1..5) {
var start = SysInfo.hrtime
socket.send("PING %(i)", target, port)
var msg = socket.receive()
var elapsed = (SysInfo.hrtime - start) / 1000000
System.print("Reply from %(msg.address): %(elapsed) ms")
}
socket.close()</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>UDP is connectionless and does not guarantee delivery. Messages may be lost, duplicated, or arrive out of order. Use TCP (<code>net</code> module) when reliability is required.</p>
</div>
<div class="admonition tip">
<div class="admonition-title">Tip</div>
<p>When binding to port 0, the system automatically assigns an available port. Use <code>localPort</code> to retrieve the assigned port number.</p>
</div>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Multicast group addresses must be in the range 224.0.0.0 to 239.255.255.255. Using addresses outside this range will cause errors.</p>
</div>
{% endblock %}

View File

@ -0,0 +1,127 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "uuid" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "uuid"}] %}
{% set prev_page = {"url": "api/udp.html", "title": "udp"} %}
{% set next_page = {"url": "api/wdantic.html", "title": "wdantic"} %}
{% block article %}
<h1>uuid</h1>
<p>The <code>uuid</code> module provides UUID (Universally Unique Identifier) generation and validation. It supports version 4 UUIDs which are randomly generated.</p>
<pre><code>import "uuid" for Uuid</code></pre>
<h2>Uuid Class</h2>
<div class="class-header">
<h3>Uuid</h3>
<p>UUID generation and validation</p>
</div>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Uuid.v4</span>() → <span class="type">String</span>
</div>
<p>Generates a new random version 4 UUID.</p>
<ul class="param-list">
<li><span class="returns">Returns:</span> A 36-character UUID string in the format <code>xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx</code></li>
</ul>
<pre><code>var id = Uuid.v4()
System.print(id) // e.g., "550e8400-e29b-41d4-a716-446655440000"</code></pre>
<div class="method-signature">
<span class="method-name">Uuid.isValid</span>(<span class="param">string</span>) → <span class="type">Bool</span>
</div>
<p>Checks if a string is a valid UUID format (any version).</p>
<ul class="param-list">
<li><span class="param-name">string</span> <span class="param-type">(String)</span> - The string to validate</li>
<li><span class="returns">Returns:</span> <code>true</code> if the string is a valid UUID format, <code>false</code> otherwise</li>
</ul>
<pre><code>System.print(Uuid.isValid("550e8400-e29b-41d4-a716-446655440000")) // true
System.print(Uuid.isValid("not-a-uuid")) // false
System.print(Uuid.isValid("550e8400e29b41d4a716446655440000")) // false (missing hyphens)</code></pre>
<div class="method-signature">
<span class="method-name">Uuid.isV4</span>(<span class="param">string</span>) → <span class="type">Bool</span>
</div>
<p>Checks if a string is a valid version 4 UUID.</p>
<ul class="param-list">
<li><span class="param-name">string</span> <span class="param-type">(String)</span> - The string to validate</li>
<li><span class="returns">Returns:</span> <code>true</code> if the string is a valid v4 UUID, <code>false</code> otherwise</li>
</ul>
<pre><code>var id = Uuid.v4()
System.print(Uuid.isV4(id)) // true
System.print(Uuid.isV4("550e8400-e29b-11d4-a716-446655440000")) // false (version 1)</code></pre>
<h2>UUID Format</h2>
<p>UUIDs are 128-bit identifiers represented as 32 hexadecimal characters separated by hyphens into five groups:</p>
<pre><code>xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
8 4 4 4 12 = 32 hex chars + 4 hyphens = 36 chars</code></pre>
<table>
<tr>
<th>Position</th>
<th>Description</th>
</tr>
<tr>
<td>M</td>
<td>Version number (4 for v4 UUIDs)</td>
</tr>
<tr>
<td>N</td>
<td>Variant (8, 9, a, or b for RFC 4122 UUIDs)</td>
</tr>
</table>
<h2>Examples</h2>
<h3>Generating Unique Identifiers</h3>
<pre><code>import "uuid" for Uuid
var userId = Uuid.v4()
var sessionId = Uuid.v4()
System.print("User ID: %(userId)")
System.print("Session ID: %(sessionId)")</code></pre>
<h3>Validating User Input</h3>
<pre><code>import "uuid" for Uuid
var input = "550e8400-e29b-41d4-a716-446655440000"
if (Uuid.isValid(input)) {
System.print("Valid UUID")
if (Uuid.isV4(input)) {
System.print("This is a v4 UUID")
}
} else {
System.print("Invalid UUID format")
}</code></pre>
<h3>Using with Database Records</h3>
<pre><code>import "uuid" for Uuid
class User {
construct new(name) {
_id = Uuid.v4()
_name = name
}
id { _id }
name { _name }
}
var user = User.new("Alice")
System.print("Created user %(user.name) with ID %(user.id)")</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Version 4 UUIDs are generated using cryptographically secure random bytes from the <code>crypto</code> module. The probability of generating duplicate UUIDs is astronomically low.</p>
</div>
{% endblock %}

View File

@ -1,101 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>wdantic - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
<li><a href="uuid.html">uuid</a></li>
<li><a href="html.html">html</a></li>
<li><a href="argparse.html">argparse</a></li>
<li><a href="wdantic.html" class="active">wdantic</a></li>
<li><a href="dataset.html">dataset</a></li>
<li><a href="markdown.html">markdown</a></li>
<li><a href="web.html">web</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>wdantic</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "wdantic" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "wdantic"}] %}
{% set prev_page = {"url": "api/argparse.html", "title": "argparse"} %}
{% set next_page = {"url": "api/dataset.html", "title": "dataset"} %}
{% block article %}
<h1>wdantic</h1>
<p>The <code>wdantic</code> module provides data validation similar to Python's Pydantic. It includes standalone validators and a schema-based validation system for structured data.</p>
@ -400,14 +311,4 @@ if (Validator.json(jsonStr)) {
<div class="admonition-title">Note</div>
<p>Field types automatically coerce values when possible. For example, <code>IntegerField</code> will convert the string <code>"42"</code> to the number <code>42</code>.</p>
</div>
</article>
<footer class="page-footer">
<a href="argparse.html" class="prev">argparse</a>
<a href="dataset.html" class="next">dataset</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -0,0 +1,999 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "web" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "web"}] %}
{% set prev_page = {"url": "api/wdantic.html", "title": "wdantic"} %}
{% set next_page = {"url": "api/websocket.html", "title": "websocket"} %}
{% block article %}
<h1>web</h1>
<p>The <code>web</code> module provides a web framework for building HTTP servers and a client for making HTTP requests. It includes routing, middleware, sessions, static file serving, and class-based views.</p>
<pre><code>import "web" for Application, Router, Request, Response, View, Session, Client, WebSocketResponse</code></pre>
<h2>Application Class</h2>
<div class="class-header">
<h3>Application</h3>
<p>HTTP server with routing and middleware</p>
</div>
<h3>Constructor</h3>
<div class="method-signature">
<span class="method-name">Application.new</span>() → <span class="type">Application</span>
</div>
<p>Creates a new web application.</p>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">router</span><span class="type">Router</span>
</div>
<p>Access to the underlying router.</p>
<div class="method-signature">
<span class="method-name">sessionStore</span><span class="type">SessionStore</span>
</div>
<p>Access to the session storage.</p>
<h3>Routing Methods</h3>
<div class="method-signature">
<span class="method-name">get</span>(<span class="param">path</span>, <span class="param">handler</span>) → <span class="type">Application</span>
</div>
<div class="method-signature">
<span class="method-name">post</span>(<span class="param">path</span>, <span class="param">handler</span>) → <span class="type">Application</span>
</div>
<div class="method-signature">
<span class="method-name">put</span>(<span class="param">path</span>, <span class="param">handler</span>) → <span class="type">Application</span>
</div>
<div class="method-signature">
<span class="method-name">delete</span>(<span class="param">path</span>, <span class="param">handler</span>) → <span class="type">Application</span>
</div>
<div class="method-signature">
<span class="method-name">patch</span>(<span class="param">path</span>, <span class="param">handler</span>) → <span class="type">Application</span>
</div>
<p>Register route handlers. Handler receives <code>Request</code> and returns <code>Response</code>.</p>
<ul class="param-list">
<li><span class="param-name">path</span> <span class="param-type">(String)</span> - URL path with optional parameters (<code>:param</code>) or wildcards (<code>*</code>)</li>
<li><span class="param-name">handler</span> <span class="param-type">(Fn)</span> - Function receiving Request, returning Response</li>
</ul>
<div class="method-signature">
<span class="method-name">addView</span>(<span class="param">path</span>, <span class="param">viewClass</span>) → <span class="type">Application</span>
</div>
<p>Registers a class-based view for all HTTP methods.</p>
<div class="method-signature">
<span class="method-name">static_</span>(<span class="param">prefix</span>, <span class="param">directory</span>) → <span class="type">Application</span>
</div>
<p>Serves static files from a directory.</p>
<pre><code>app.static_("/assets", "./public")</code></pre>
<div class="method-signature">
<span class="method-name">websocket</span>(<span class="param">path</span>, <span class="param">handler</span>) → <span class="type">Application</span>
</div>
<p>Registers a WebSocket handler for the given path. Handler receives Request and should use WebSocketResponse to handle the connection.</p>
<pre><code>app.websocket("/ws", Fn.new { |req|
var ws = WebSocketResponse.new()
ws.prepare(req)
while (ws.isOpen) {
var msg = ws.receive()
if (msg == null || msg.isClose) break
if (msg.isText) ws.send("Echo: " + msg.text)
}
return ws
})</code></pre>
<div class="method-signature">
<span class="method-name">use</span>(<span class="param">middleware</span>) → <span class="type">Application</span>
</div>
<p>Adds middleware function. Middleware receives Request, returns Response or null to continue.</p>
<div class="method-signature">
<span class="method-name">run</span>(<span class="param">host</span>, <span class="param">port</span>)
</div>
<p>Starts the server (blocking).</p>
<h2>Request Class</h2>
<div class="class-header">
<h3>Request</h3>
<p>Incoming HTTP request</p>
</div>
<h3>Properties</h3>
<table>
<tr>
<th>Property</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td><code>method</code></td>
<td>String</td>
<td>HTTP method (GET, POST, etc.)</td>
</tr>
<tr>
<td><code>path</code></td>
<td>String</td>
<td>Request path without query string</td>
</tr>
<tr>
<td><code>query</code></td>
<td>Map</td>
<td>Parsed query parameters</td>
</tr>
<tr>
<td><code>headers</code></td>
<td>Map</td>
<td>Request headers</td>
</tr>
<tr>
<td><code>body</code></td>
<td>String</td>
<td>Raw request body</td>
</tr>
<tr>
<td><code>params</code></td>
<td>Map</td>
<td>Route parameters from URL</td>
</tr>
<tr>
<td><code>cookies</code></td>
<td>Map</td>
<td>Parsed cookies</td>
</tr>
<tr>
<td><code>session</code></td>
<td>Session</td>
<td>Session data</td>
</tr>
</table>
<h3>Methods</h3>
<div class="method-signature">
<span class="method-name">header</span>(<span class="param">name</span>) → <span class="type">String|null</span>
</div>
<p>Gets header value (case-insensitive).</p>
<div class="method-signature">
<span class="method-name">json</span><span class="type">Map|List</span>
</div>
<p>Parses body as JSON (cached).</p>
<div class="method-signature">
<span class="method-name">form</span><span class="type">Map</span>
</div>
<p>Parses body as form data (cached).</p>
<h2>Response Class</h2>
<div class="class-header">
<h3>Response</h3>
<p>HTTP response builder</p>
</div>
<h3>Static Factory Methods</h3>
<div class="method-signature">
<span class="method-name">Response.text</span>(<span class="param">content</span>) → <span class="type">Response</span>
</div>
<p>Creates plain text response.</p>
<div class="method-signature">
<span class="method-name">Response.html</span>(<span class="param">content</span>) → <span class="type">Response</span>
</div>
<p>Creates HTML response.</p>
<div class="method-signature">
<span class="method-name">Response.json</span>(<span class="param">data</span>) → <span class="type">Response</span>
</div>
<p>Creates JSON response (automatically stringifies).</p>
<div class="method-signature">
<span class="method-name">Response.redirect</span>(<span class="param">url</span>) → <span class="type">Response</span>
</div>
<div class="method-signature">
<span class="method-name">Response.redirect</span>(<span class="param">url</span>, <span class="param">status</span>) → <span class="type">Response</span>
</div>
<p>Creates redirect response (default 302).</p>
<div class="method-signature">
<span class="method-name">Response.file</span>(<span class="param">path</span>) → <span class="type">Response</span>
</div>
<div class="method-signature">
<span class="method-name">Response.file</span>(<span class="param">path</span>, <span class="param">contentType</span>) → <span class="type">Response</span>
</div>
<p>Serves a file. Auto-detects content type if not specified.</p>
<h3>Instance Methods</h3>
<div class="method-signature">
<span class="method-name">header</span>(<span class="param">name</span>, <span class="param">value</span>) → <span class="type">Response</span>
</div>
<p>Sets a response header.</p>
<div class="method-signature">
<span class="method-name">cookie</span>(<span class="param">name</span>, <span class="param">value</span>) → <span class="type">Response</span>
</div>
<div class="method-signature">
<span class="method-name">cookie</span>(<span class="param">name</span>, <span class="param">value</span>, <span class="param">options</span>) → <span class="type">Response</span>
</div>
<p>Sets a cookie. Options: <code>path</code>, <code>maxAge</code>, <code>httpOnly</code>, <code>secure</code>.</p>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">status</span><span class="type">Num</span>
</div>
<p>Gets or sets HTTP status code.</p>
<div class="method-signature">
<span class="method-name">body</span><span class="type">String</span>
</div>
<p>Gets or sets response body.</p>
<h2>View Class</h2>
<div class="class-header">
<h3>View</h3>
<p>Base class for class-based views</p>
</div>
<p>Subclass and override methods for each HTTP method:</p>
<pre><code>class UserView is View {
get(request) {
return Response.json({"users": []})
}
post(request) {
var data = request.json
return Response.json({"created": data})
}
websocket(request) {
var ws = WebSocketResponse.new()
ws.prepare(request)
ws.iterate(Fn.new { |msg|
if (msg.isText) ws.send("Echo: " + msg.text)
})
return ws
}
}</code></pre>
<h2>WebSocketResponse Class</h2>
<div class="class-header">
<h3>WebSocketResponse</h3>
<p>Server-side WebSocket connection handler (aiohttp-style API)</p>
</div>
<p>The <code>WebSocketResponse</code> class provides a server-side WebSocket handler following the aiohttp-style API pattern. It handles the WebSocket handshake, frame encoding/decoding, and provides convenient methods for sending and receiving messages.</p>
<h3>Constructor</h3>
<div class="method-signature">
<span class="method-name">WebSocketResponse.new</span>() → <span class="type">WebSocketResponse</span>
</div>
<p>Creates a new WebSocket response handler. Call <code>prepare(request)</code> to complete the handshake before sending or receiving messages.</p>
<h3>Properties</h3>
<table>
<tr>
<th>Property</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td><code>isPrepared</code></td>
<td>Bool</td>
<td>True if the WebSocket handshake has been performed</td>
</tr>
<tr>
<td><code>isOpen</code></td>
<td>Bool</td>
<td>True if the connection is open and ready for messages</td>
</tr>
</table>
<h3>Connection Methods</h3>
<div class="method-signature">
<span class="method-name">prepare</span>(<span class="param">request</span>) → <span class="type">WebSocketResponse</span>
</div>
<p>Performs the WebSocket handshake using the request's socket. Must be called before sending or receiving messages. Returns <code>this</code> for method chaining.</p>
<ul class="param-list">
<li><span class="param-name">request</span> <span class="param-type">(Request)</span> - The HTTP request containing the WebSocket upgrade headers</li>
</ul>
<pre><code>var ws = WebSocketResponse.new()
ws.prepare(request)</code></pre>
<h3>Sending Methods</h3>
<div class="method-signature">
<span class="method-name">send</span>(<span class="param">text</span>)
</div>
<div class="method-signature">
<span class="method-name">sendText</span>(<span class="param">text</span>)
</div>
<p>Sends a text message to the client. Both methods are equivalent.</p>
<ul class="param-list">
<li><span class="param-name">text</span> <span class="param-type">(String)</span> - Text message to send</li>
</ul>
<pre><code>ws.send("Hello, client!")
ws.sendText("Another message")</code></pre>
<div class="method-signature">
<span class="method-name">sendJson</span>(<span class="param">data</span>)
</div>
<p>Serializes the data to JSON and sends it as a text message. Convenience method for JSON-based protocols.</p>
<ul class="param-list">
<li><span class="param-name">data</span> <span class="param-type">(Map|List)</span> - Data to serialize and send</li>
</ul>
<pre><code>ws.sendJson({"type": "update", "value": 42})
ws.sendJson(["item1", "item2", "item3"])</code></pre>
<div class="method-signature">
<span class="method-name">sendBinary</span>(<span class="param">bytes</span>)
</div>
<p>Sends a binary message to the client.</p>
<ul class="param-list">
<li><span class="param-name">bytes</span> <span class="param-type">(List)</span> - List of bytes to send</li>
</ul>
<pre><code>ws.sendBinary([0x01, 0x02, 0x03, 0x04])
var imageData = File.readBytes("image.png")
ws.sendBinary(imageData)</code></pre>
<h3>Receiving Methods</h3>
<div class="method-signature">
<span class="method-name">receive</span>() → <span class="type">WebSocketMessage|null</span>
</div>
<p>Receives the next message from the client. Blocks until a message arrives. Returns a <code>WebSocketMessage</code> object, or <code>null</code> if the connection closed unexpectedly. Ping frames are automatically answered with pong.</p>
<pre><code>var msg = ws.receive()
if (msg != null && msg.isText) {
System.print("Got: %(msg.text)")
}</code></pre>
<div class="method-signature">
<span class="method-name">receiveJson</span>() → <span class="type">Map|List|WebSocketMessage|null</span>
</div>
<p>Receives a text message and parses it as JSON. Returns the parsed JSON data, the close frame if the connection was closed, or <code>null</code> if the connection closed unexpectedly. Aborts if a binary frame is received.</p>
<pre><code>var data = ws.receiveJson()
if (data is Map && data.containsKey("type")) {
if (data["type"] == "message") {
System.print("Message: %(data["text"])")
}
}</code></pre>
<div class="method-signature">
<span class="method-name">receiveText</span>() → <span class="type">String|null</span>
</div>
<p>Receives a message and returns only the text content. Returns <code>null</code> if the message is not a text frame, if the connection was closed, or if the connection closed unexpectedly.</p>
<pre><code>var text = ws.receiveText()
if (text != null) {
System.print("Received: %(text)")
}</code></pre>
<div class="method-signature">
<span class="method-name">receiveBinary</span>() → <span class="type">List|null</span>
</div>
<p>Receives a message and returns only the binary content. Returns <code>null</code> if the message is not a binary frame, if the connection was closed, or if the connection closed unexpectedly.</p>
<pre><code>var bytes = ws.receiveBinary()
if (bytes != null) {
System.print("Received %(bytes.count) bytes")
}</code></pre>
<div class="method-signature">
<span class="method-name">iterate</span>(<span class="param">callback</span>)
</div>
<p>Helper method that loops while the connection is open, calling the callback for each received message (excluding close frames). Simplifies the common receive loop pattern.</p>
<ul class="param-list">
<li><span class="param-name">callback</span> <span class="param-type">(Fn)</span> - Function to call with each WebSocketMessage</li>
</ul>
<pre><code>ws.iterate(Fn.new { |msg|
if (msg.isText) {
ws.send("Echo: " + msg.text)
}
})</code></pre>
<h3>Control Methods</h3>
<div class="method-signature">
<span class="method-name">ping</span>()
</div>
<div class="method-signature">
<span class="method-name">ping</span>(<span class="param">data</span>)
</div>
<p>Sends a ping frame to check if the client is still connected. Payload must be 125 bytes or less.</p>
<ul class="param-list">
<li><span class="param-name">data</span> <span class="param-type">(String|List)</span> - Optional payload data</li>
</ul>
<div class="method-signature">
<span class="method-name">pong</span>(<span class="param">data</span>)
</div>
<p>Sends a pong frame. Usually automatic in response to ping, but can be sent manually.</p>
<div class="method-signature">
<span class="method-name">close</span>()
</div>
<div class="method-signature">
<span class="method-name">close</span>(<span class="param">code</span>, <span class="param">reason</span>)
</div>
<p>Closes the WebSocket connection. Default code is 1000 (normal closure).</p>
<ul class="param-list">
<li><span class="param-name">code</span> <span class="param-type">(Num)</span> - Close status code (1000-4999)</li>
<li><span class="param-name">reason</span> <span class="param-type">(String)</span> - Human-readable close reason</li>
</ul>
<pre><code>ws.close()
ws.close(1000, "Goodbye")</code></pre>
<h3>WebSocketMessage Reference</h3>
<p>The <code>receive()</code> method returns a <code>WebSocketMessage</code> object with the following properties:</p>
<table>
<tr>
<th>Property</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td><code>opcode</code></td>
<td>Num</td>
<td>Frame opcode (1=text, 2=binary, 8=close, 9=ping, 10=pong)</td>
</tr>
<tr>
<td><code>fin</code></td>
<td>Bool</td>
<td>True if this is the final fragment</td>
</tr>
<tr>
<td><code>isText</code></td>
<td>Bool</td>
<td>True if this is a text frame</td>
</tr>
<tr>
<td><code>isBinary</code></td>
<td>Bool</td>
<td>True if this is a binary frame</td>
</tr>
<tr>
<td><code>isClose</code></td>
<td>Bool</td>
<td>True if this is a close frame</td>
</tr>
<tr>
<td><code>isPing</code></td>
<td>Bool</td>
<td>True if this is a ping frame</td>
</tr>
<tr>
<td><code>isPong</code></td>
<td>Bool</td>
<td>True if this is a pong frame</td>
</tr>
<tr>
<td><code>text</code></td>
<td>String</td>
<td>Payload as string (for text frames)</td>
</tr>
<tr>
<td><code>bytes</code></td>
<td>List</td>
<td>Payload as byte list</td>
</tr>
<tr>
<td><code>closeCode</code></td>
<td>Num</td>
<td>Close status code (for close frames)</td>
</tr>
<tr>
<td><code>closeReason</code></td>
<td>String</td>
<td>Close reason text (for close frames)</td>
</tr>
</table>
<h3>Close Codes Reference</h3>
<table>
<tr>
<th>Code</th>
<th>Meaning</th>
</tr>
<tr>
<td>1000</td>
<td>Normal closure</td>
</tr>
<tr>
<td>1001</td>
<td>Going away (server shutdown)</td>
</tr>
<tr>
<td>1002</td>
<td>Protocol error</td>
</tr>
<tr>
<td>1003</td>
<td>Unsupported data type</td>
</tr>
<tr>
<td>1005</td>
<td>No status received</td>
</tr>
<tr>
<td>1006</td>
<td>Abnormal closure</td>
</tr>
<tr>
<td>1011</td>
<td>Server error</td>
</tr>
<tr>
<td>4000-4999</td>
<td>Application-specific codes</td>
</tr>
</table>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Ping frames received from clients are automatically answered with pong frames. You typically do not need to handle ping/pong manually unless implementing custom keepalive logic.</p>
</div>
<h2>Session Class</h2>
<div class="class-header">
<h3>Session</h3>
<p>Session data storage</p>
</div>
<h3>Properties and Methods</h3>
<div class="method-signature">
<span class="method-name">id</span><span class="type">String</span>
</div>
<p>The session ID.</p>
<div class="method-signature">
<span class="method-name">[key]</span><span class="type">any</span>
</div>
<p>Gets session value.</p>
<div class="method-signature">
<span class="method-name">[key]=</span>(<span class="param">value</span>)
</div>
<p>Sets session value.</p>
<div class="method-signature">
<span class="method-name">remove</span>(<span class="param">key</span>)
</div>
<p>Removes a session key.</p>
<h2>Client Class</h2>
<div class="class-header">
<h3>Client</h3>
<p>HTTP client for making requests</p>
</div>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Client.get</span>(<span class="param">url</span>) → <span class="type">Map</span>
</div>
<div class="method-signature">
<span class="method-name">Client.get</span>(<span class="param">url</span>, <span class="param">options</span>) → <span class="type">Map</span>
</div>
<p>Makes GET request.</p>
<div class="method-signature">
<span class="method-name">Client.post</span>(<span class="param">url</span>) → <span class="type">Map</span>
</div>
<div class="method-signature">
<span class="method-name">Client.post</span>(<span class="param">url</span>, <span class="param">options</span>) → <span class="type">Map</span>
</div>
<p>Makes POST request.</p>
<div class="method-signature">
<span class="method-name">Client.put</span>(<span class="param">url</span>, <span class="param">options</span>) → <span class="type">Map</span>
</div>
<p>Makes PUT request.</p>
<div class="method-signature">
<span class="method-name">Client.delete</span>(<span class="param">url</span>, <span class="param">options</span>) → <span class="type">Map</span>
</div>
<p>Makes DELETE request.</p>
<h3>Client Options</h3>
<table>
<tr>
<th>Option</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td><code>headers</code></td>
<td>Map</td>
<td>Request headers</td>
</tr>
<tr>
<td><code>body</code></td>
<td>String</td>
<td>Request body</td>
</tr>
<tr>
<td><code>json</code></td>
<td>Map/List</td>
<td>JSON body (auto-stringifies and sets Content-Type)</td>
</tr>
</table>
<h3>Response Format</h3>
<pre><code>{
"status": 200,
"headers": {"Content-Type": "application/json"},
"body": "{...}"
}</code></pre>
<h2>Examples</h2>
<h3>Basic Server</h3>
<pre><code>import "web" for Application, Response
var app = Application.new()
app.get("/", Fn.new { |req|
return Response.html("&lt;h1&gt;Hello World&lt;/h1&gt;")
})
app.get("/api/users", Fn.new { |req|
return Response.json({"users": ["Alice", "Bob"]})
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>Route Parameters</h3>
<pre><code>import "web" for Application, Response
var app = Application.new()
app.get("/users/:id", Fn.new { |req|
var userId = req.params["id"]
return Response.json({"id": userId})
})
app.get("/files/*", Fn.new { |req|
return Response.text("Wildcard route")
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>Class-Based Views</h3>
<pre><code>import "web" for Application, Response, View
class ArticleView is View {
get(request) {
return Response.json({"articles": []})
}
post(request) {
var data = request.json
return Response.json({"created": data})
}
}
var app = Application.new()
app.addView("/articles", ArticleView)
app.run("0.0.0.0", 8080)</code></pre>
<h3>Sessions</h3>
<pre><code>import "web" for Application, Response
var app = Application.new()
app.get("/login", Fn.new { |req|
req.session["user"] = "Alice"
return Response.text("Logged in")
})
app.get("/profile", Fn.new { |req|
var user = req.session["user"]
if (user == null) {
return Response.redirect("/login")
}
return Response.text("Hello, %(user)")
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>Middleware</h3>
<pre><code>import "web" for Application, Response
var app = Application.new()
app.use(Fn.new { |req|
System.print("%(req.method) %(req.path)")
return null
})
app.use(Fn.new { |req|
if (req.path.startsWith("/admin") &amp;&amp; !req.session["isAdmin"]) {
return Response.redirect("/login")
}
return null
})
app.get("/", Fn.new { |req|
return Response.text("Home")
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>HTTP Client</h3>
<pre><code>import "web" for Client
import "json" for Json
var response = Client.get("https://api.example.com/data")
System.print("Status: %(response["status"])")
System.print("Body: %(response["body"])")
var postResponse = Client.post("https://api.example.com/users", {
"json": {"name": "Alice", "email": "alice@example.com"}
})
var data = Json.parse(postResponse["body"])</code></pre>
<h3>Concurrent HTTP Requests</h3>
<p>Use the <code>async</code> keyword to create futures and <code>await</code> to wait for results. Each request runs in its own fiber and executes in parallel while waiting for I/O.</p>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
var urls = [
"https://api.example.com/users",
"https://api.example.com/posts",
"https://api.example.com/comments"
]
var futures = []
for (url in urls) {
futures.add(async { Client.get(url) })
}
var responses = []
for (future in futures) {
responses.add(await future)
}
for (i in 0...urls.count) {
System.print("%(urls[i]): %(responses[i]["status"])")
}</code></pre>
<h3>Parallel Batch Requests</h3>
<p>For batch operations, create all futures first, then await them. The requests execute concurrently.</p>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchUrl = async { |url| Client.get(url) }
class BatchClient {
static getAll(urls) {
var futures = []
for (url in urls) {
futures.add(async { Client.get(url) })
}
var results = []
for (f in futures) {
results.add(await f)
}
return results
}
static postAll(requests) {
var futures = []
for (req in requests) {
futures.add(async { Client.post(req["url"], req["options"]) })
}
var results = []
for (f in futures) {
results.add(await f)
}
return results
}
}
var urls = [
"https://api.example.com/endpoint1",
"https://api.example.com/endpoint2",
"https://api.example.com/endpoint3"
]
var results = BatchClient.getAll(urls)
for (result in results) {
System.print("Status: %(result["status"])")
}</code></pre>
<h3>Parameterized Async Functions</h3>
<p>Create reusable async functions with parameters using <code>async { |args| ... }</code>. There are two ways to invoke these functions:</p>
<ul>
<li><strong><code>await fn(args)</code></strong> — Direct call, waits immediately (sequential execution)</li>
<li><strong><code>fn.call(args)</code></strong> — Returns a Future, starts execution without waiting (concurrent execution)</li>
</ul>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchJson = async { |url|
var response = Client.get(url)
return Json.parse(response["body"])
}
var postJson = async { |url, data|
var response = Client.post(url, {"json": data})
return Json.parse(response["body"])
}
// DIRECT CALLING (preferred for sequential operations)
// Waits for each request to complete before continuing
var user = await fetchJson("https://api.example.com/user/1")
System.print(user["name"])
// CONCURRENT EXECUTION (use .call() to start without waiting)
// Both requests run at the same time
var f1 = fetchJson.call("https://api.example.com/posts")
var f2 = fetchJson.call("https://api.example.com/comments")
// Now wait for results
var posts = await f1
var comments = await f2</code></pre>
<div class="admonition tip">
<div class="admonition-title">Direct Calling vs .call()</div>
<p>Use <code>await fn(args)</code> when you want sequential execution. Use <code>fn.call(args)</code> when you want to start multiple operations concurrently, then <code>await</code> their results later.</p>
</div>
<h3>WebSocket Echo Server</h3>
<pre><code>import "web" for Application, Response, WebSocketResponse
var app = Application.new()
app.get("/", Fn.new { |req|
return Response.html("&lt;script&gt;var ws=new WebSocket('ws://localhost:8080/ws');ws.onmessage=e=&gt;console.log(e.data);ws.onopen=()=&gt;ws.send('hello');&lt;/script&gt;")
})
app.websocket("/ws", Fn.new { |req|
var ws = WebSocketResponse.new()
ws.prepare(req)
while (ws.isOpen) {
var msg = ws.receive()
if (msg == null || msg.isClose) break
if (msg.isText) {
ws.send("Echo: " + msg.text)
}
}
ws.close()
return ws
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>WebSocket JSON API</h3>
<pre><code>import "web" for Application, Response, WebSocketResponse
var app = Application.new()
app.websocket("/api", Fn.new { |req|
var ws = WebSocketResponse.new()
ws.prepare(req)
ws.sendJson({"type": "welcome", "message": "Connected to API"})
while (ws.isOpen) {
var data = ws.receiveJson()
if (data == null) break
if (data is Map &amp;&amp; data["type"] == "ping") {
ws.sendJson({"type": "pong", "timestamp": data["timestamp"]})
} else if (data is Map &amp;&amp; data["type"] == "echo") {
ws.sendJson({"type": "echo", "data": data["data"]})
}
}
return ws
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>WebSocket Binary Data Transfer</h3>
<pre><code>import "web" for Application, Response, WebSocketResponse
import "io" for File
var app = Application.new()
app.websocket("/binary", Fn.new { |req|
var ws = WebSocketResponse.new()
ws.prepare(req)
ws.send("Ready to receive binary data")
while (ws.isOpen) {
var msg = ws.receive()
if (msg == null || msg.isClose) break
if (msg.isBinary) {
System.print("Received %(msg.bytes.count) bytes")
ws.sendJson({"received": msg.bytes.count, "status": "ok"})
} else if (msg.isText) {
if (msg.text == "send-file") {
var data = File.readBytes("example.bin")
ws.sendBinary(data)
}
}
}
return ws
})
app.run("0.0.0.0", 8080)</code></pre>
<h3>WebSocket in Class-Based View</h3>
<pre><code>import "web" for Application, Response, View, WebSocketResponse
class ChatView is View {
get(request) {
return Response.html("&lt;h1&gt;Chat Room&lt;/h1&gt;&lt;p&gt;Connect via WebSocket&lt;/p&gt;")
}
websocket(request) {
var ws = WebSocketResponse.new()
ws.prepare(request)
ws.sendJson({"type": "connected", "room": "general"})
ws.iterate(Fn.new { |msg|
if (msg.isText) {
var data = Json.parse(msg.text)
if (data["type"] == "message") {
ws.sendJson({
"type": "broadcast",
"from": data["from"],
"text": data["text"]
})
}
}
})
return ws
}
}
var app = Application.new()
app.addView("/chat", ChatView)
app.run("0.0.0.0", 8080)</code></pre>
<div class="admonition warning">
<div class="admonition-title">WebSocket Error Handling</div>
<p>Always check for <code>null</code> or close frames when receiving messages. The connection can close at any time due to network issues or client disconnection. Wrap WebSocket handling in proper error handling to prevent server crashes.</p>
</div>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Sessions are stored in memory by default. Data is lost when the server restarts. For production, consider persisting session data to a database.</p>
</div>
{% endblock %}

View File

@ -1,105 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>websocket - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="string.html">String</a></li>
<li><a href="number.html">Num</a></li>
<li><a href="http.html">http</a></li>
<li><a href="websocket.html" class="active">websocket</a></li>
<li><a href="tls.html">tls</a></li>
<li><a href="net.html">net</a></li>
<li><a href="dns.html">dns</a></li>
<li><a href="json.html">json</a></li>
<li><a href="base64.html">base64</a></li>
<li><a href="regex.html">regex</a></li>
<li><a href="jinja.html">jinja</a></li>
<li><a href="crypto.html">crypto</a></li>
<li><a href="os.html">os</a></li>
<li><a href="env.html">env</a></li>
<li><a href="signal.html">signal</a></li>
<li><a href="subprocess.html">subprocess</a></li>
<li><a href="sqlite.html">sqlite</a></li>
<li><a href="datetime.html">datetime</a></li>
<li><a href="timer.html">timer</a></li>
<li><a href="io.html">io</a></li>
<li><a href="pathlib.html">pathlib</a></li>
<li><a href="scheduler.html">scheduler</a></li>
<li><a href="math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">API Reference</a>
<span class="separator">/</span>
<span>websocket</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "websocket" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "websocket"}] %}
{% set prev_page = {"url": "api/http.html", "title": "http"} %}
{% set next_page = {"url": "api/yaml.html", "title": "yaml"} %}
{% block article %}
<h1>websocket</h1>
<p>The <code>websocket</code> module provides WebSocket client and server functionality implementing the RFC 6455 protocol. It supports both <code>ws://</code> and <code>wss://</code> (secure) connections.</p>
@ -498,14 +405,4 @@ ws.close()</code></pre>
<div class="admonition-title">Note</div>
<p>Ping frames are automatically responded to with pong frames. You typically do not need to handle ping/pong manually unless implementing custom keepalive logic.</p>
</div>
</article>
<footer class="page-footer">
<a href="http.html" class="prev">http</a>
<a href="tls.html" class="next">tls</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -0,0 +1,218 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "yaml" %}
{% set breadcrumb = [{"url": "api/index.html", "title": "API Reference"}, {"title": "yaml"}] %}
{% set prev_page = {"url": "api/websocket.html", "title": "websocket"} %}
{% set next_page = {"url": "tutorials/index.html", "title": "Tutorial List"} %}
{% block article %}
<h1>yaml</h1>
<p>The <code>yaml</code> module provides YAML parsing and stringification. It implements a line-based parser in pure Wren that supports common YAML features including nested structures, lists, and scalar types.</p>
<pre><code>import "yaml" for Yaml</code></pre>
<h2>Yaml Class</h2>
<div class="class-header">
<h3>Yaml</h3>
<p>YAML parsing and stringification</p>
</div>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Yaml.parse</span>(<span class="param">text</span>) &rarr; <span class="type">Map|List|String|Num|Bool|null</span>
</div>
<p>Parses a YAML string and returns the corresponding Wren value.</p>
<ul class="param-list">
<li><span class="param-name">text</span> <span class="param-type">(String)</span> - YAML string to parse</li>
<li><span class="returns">Returns:</span> Parsed value (Map, List, String, Num, Bool, or null)</li>
</ul>
<pre><code>var data = Yaml.parse("name: Alice\nage: 30")
System.print(data["name"]) // Alice
System.print(data["age"]) // 30
var list = Yaml.parse("- one\n- two\n- three")
System.print(list[0]) // one</code></pre>
<div class="method-signature">
<span class="method-name">Yaml.stringify</span>(<span class="param">value</span>) &rarr; <span class="type">String</span>
</div>
<p>Converts a Wren value to a YAML string with default indentation (2 spaces).</p>
<ul class="param-list">
<li><span class="param-name">value</span> <span class="param-type">(any)</span> - Value to stringify</li>
<li><span class="returns">Returns:</span> YAML string</li>
</ul>
<pre><code>var yaml = Yaml.stringify({"name": "Alice", "age": 30})
System.print(yaml)
// name: Alice
// age: 30</code></pre>
<div class="method-signature">
<span class="method-name">Yaml.stringify</span>(<span class="param">value</span>, <span class="param">indent</span>) &rarr; <span class="type">String</span>
</div>
<p>Converts a Wren value to a YAML string with custom indentation.</p>
<ul class="param-list">
<li><span class="param-name">value</span> <span class="param-type">(any)</span> - Value to stringify</li>
<li><span class="param-name">indent</span> <span class="param-type">(Num)</span> - Number of spaces for indentation</li>
<li><span class="returns">Returns:</span> YAML string</li>
</ul>
<pre><code>var yaml = Yaml.stringify({"server": {"host": "localhost"}}, 4)
System.print(yaml)
// server:
// host: localhost</code></pre>
<h2>Supported Features</h2>
<h3>Key-Value Pairs</h3>
<pre><code>var data = Yaml.parse("
name: Alice
age: 30
email: alice@example.com
")
System.print(data["name"]) // Alice</code></pre>
<h3>Nested Maps</h3>
<pre><code>var config = Yaml.parse("
database:
host: localhost
port: 5432
credentials:
user: admin
password: secret
")
System.print(config["database"]["host"]) // localhost
System.print(config["database"]["credentials"]["user"]) // admin</code></pre>
<h3>Lists</h3>
<pre><code>var data = Yaml.parse("
languages:
- Wren
- C
- Python
")
for (lang in data["languages"]) {
System.print(lang)
}</code></pre>
<h3>Lists of Maps</h3>
<pre><code>var nav = Yaml.parse("
pages:
- file: index
title: Home
- file: about
title: About Us
")
for (page in nav["pages"]) {
System.print(page["title"])
}</code></pre>
<h3>Comments</h3>
<pre><code>var data = Yaml.parse("
# This is a comment
name: test
# Another comment
value: 123
")
System.print(data["name"]) // test</code></pre>
<h3>Data Types</h3>
<pre><code>var types = Yaml.parse("
string: hello world
number: 42
float: 3.14
bool_true: true
bool_false: false
null_value: null
tilde_null: ~
quoted: \"with:special chars\"
")
System.print(types["string"]) // hello world (String)
System.print(types["number"]) // 42 (Num)
System.print(types["bool_true"]) // true (Bool)
System.print(types["null_value"]) // null</code></pre>
<h2>Type Mapping</h2>
<table>
<tr>
<th>YAML Type</th>
<th>Wren Type</th>
</tr>
<tr>
<td>mapping</td>
<td>Map</td>
</tr>
<tr>
<td>sequence</td>
<td>List</td>
</tr>
<tr>
<td>string</td>
<td>String</td>
</tr>
<tr>
<td>integer/float</td>
<td>Num</td>
</tr>
<tr>
<td>true/false</td>
<td>Bool</td>
</tr>
<tr>
<td>null/~</td>
<td>null</td>
</tr>
</table>
<h2>Examples</h2>
<h3>Configuration File</h3>
<pre><code>import "yaml" for Yaml
import "io" for File
var config = Yaml.parse(File.read("config.yaml"))
System.print("Server: %(config["server"]["host"]):%(config["server"]["port"])")
System.print("Database: %(config["database"]["url"])")</code></pre>
<h3>Building and Stringifying</h3>
<pre><code>import "yaml" for Yaml
var data = {
"name": "Project",
"version": "1.0.0",
"dependencies": ["wren", "libuv"],
"settings": {
"debug": true,
"timeout": 30
}
}
System.print(Yaml.stringify(data))</code></pre>
<h3>Round-Trip</h3>
<pre><code>import "yaml" for Yaml
var original = "
title: Test
items:
- first
- second
"
var parsed = Yaml.parse(original)
var reserialized = Yaml.stringify(parsed)
var reparsed = Yaml.parse(reserialized)
System.print(parsed["title"] == reparsed["title"]) // true</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>This module implements a subset of YAML 1.2 suitable for configuration files. Advanced features like anchors, aliases, multi-line strings with <code>|</code> or <code>&gt;</code>, and tags are not supported.</p>
</div>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Strings containing special characters (<code>:</code>, <code>#</code>, quotes) should be quoted in YAML input. The stringify method handles this automatically.</p>
</div>
{% endblock %}

View File

@ -0,0 +1,420 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "Async Patterns" %}
{% set breadcrumb = [{"url": "contributing/index.html", "title": "Contributing"}, {"title": "Async Patterns"}] %}
{% set prev_page = {"url": "contributing/foreign-classes.html", "title": "Foreign Classes"} %}
{% set next_page = {"url": "contributing/testing.html", "title": "Writing Tests"} %}
{% block article %}
<h1>Async Patterns</h1>
<p>Wren-CLI uses libuv for non-blocking I/O. Wren fibers suspend during I/O operations and resume when complete. This section explains how to implement async operations in C-backed modules.</p>
<h2>The Scheduler/Fiber Pattern</h2>
<p>The standard pattern for async operations involves:</p>
<ol>
<li>A public Wren method that users call</li>
<li>An internal foreign method (ending with <code>_</code>) that takes a fiber handle</li>
<li>C code that stores the fiber handle and schedules async work</li>
<li>A libuv callback that resumes the fiber with the result</li>
</ol>
<h2>Basic Example</h2>
<h3>Wren Interface</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "scheduler" for Scheduler
class AsyncFile {
foreign static read_(path, fiber)
static read(path) {
return Scheduler.await_ { read_(path, Fiber.current) }
}
}</code></pre>
<p>The public <code>read</code> method wraps the internal <code>read_</code> method. <code>Scheduler.await_</code> suspends the current fiber until the async operation completes.</p>
<h3>Naming Convention</h3>
<ul>
<li>Internal methods end with <code>_</code> (e.g., <code>read_</code>, <code>write_</code>)</li>
<li>Public methods have clean names (e.g., <code>read</code>, <code>write</code>)</li>
<li>Internal methods take a fiber as the last argument</li>
</ul>
<h2>C Implementation Structure</h2>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;uv.h&gt;
#include "asyncfile.h"
#include "wren.h"
#include "vm.h"
typedef struct {
WrenVM* vm;
WrenHandle* fiber;
uv_fs_t req;
char* path;
uv_buf_t buffer;
} ReadRequest;
void asyncFileRead(WrenVM* vm) {
const char* path = wrenGetSlotString(vm, 1);
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
ReadRequest* request = (ReadRequest*)malloc(sizeof(ReadRequest));
request->vm = vm;
request->fiber = fiber;
request->path = strdup(path);
request->req.data = request;
uv_loop_t* loop = getLoop();
uv_fs_open(loop, &request->req, path, UV_FS_O_RDONLY, 0, onFileOpened);
}
void onFileOpened(uv_fs_t* req) {
ReadRequest* request = (ReadRequest*)req->data;
uv_fs_req_cleanup(req);
if (req->result < 0) {
resumeWithError(request, "Failed to open file.");
return;
}
int fd = (int)req->result;
uv_fs_fstat(getLoop(), &request->req, fd, onFileStat);
}
// ... additional callbacks for fstat, read, close ...</code></pre>
<h2>Fiber Handle Management</h2>
<h3>Capturing the Fiber</h3>
<pre><code>WrenHandle* fiber = wrenGetSlotHandle(vm, 2);</code></pre>
<p>The fiber handle is obtained from the slot where it was passed. This handle must be stored for later use.</p>
<h3>Resuming the Fiber</h3>
<p>Use the scheduler's resume mechanism:</p>
<pre><code>void resumeWithResult(ReadRequest* request, const char* result) {
WrenVM* vm = request->vm;
schedulerResume(request->fiber, true);
wrenReleaseHandle(vm, request->fiber);
wrenEnsureSlots(vm, 1);
wrenSetSlotString(vm, 0, result);
free(request->path);
free(request);
}
void resumeWithError(ReadRequest* request, const char* error) {
WrenVM* vm = request->vm;
schedulerResume(request->fiber, false);
wrenReleaseHandle(vm, request->fiber);
wrenEnsureSlots(vm, 1);
wrenSetSlotString(vm, 0, error);
free(request->path);
free(request);
}</code></pre>
<h3>schedulerResume</h3>
<pre><code>void schedulerResume(WrenHandle* fiber, bool success);</code></pre>
<ul>
<li><code>fiber</code>: The fiber handle to resume</li>
<li><code>success</code>: true if the operation succeeded, false for error</li>
</ul>
<p>After calling <code>schedulerResume</code>, the value in slot 0 becomes the return value (for success) or error message (for failure).</p>
<h2>libuv Integration</h2>
<h3>Getting the Event Loop</h3>
<pre><code>uv_loop_t* loop = getLoop();</code></pre>
<p>The <code>getLoop()</code> function returns the global libuv event loop used by Wren-CLI.</p>
<h3>Common libuv Operations</h3>
<table>
<tr>
<th>Operation</th>
<th>libuv Function</th>
</tr>
<tr>
<td>File read</td>
<td><code>uv_fs_read</code></td>
</tr>
<tr>
<td>File write</td>
<td><code>uv_fs_write</code></td>
</tr>
<tr>
<td>TCP connect</td>
<td><code>uv_tcp_connect</code></td>
</tr>
<tr>
<td>DNS lookup</td>
<td><code>uv_getaddrinfo</code></td>
</tr>
<tr>
<td>Timer</td>
<td><code>uv_timer_start</code></td>
</tr>
<tr>
<td>Process spawn</td>
<td><code>uv_spawn</code></td>
</tr>
</table>
<h3>Request Data Pattern</h3>
<p>Store context in the <code>data</code> field of libuv requests:</p>
<pre><code>typedef struct {
WrenVM* vm;
WrenHandle* fiber;
// ... operation-specific data ...
} MyRequest;
MyRequest* req = malloc(sizeof(MyRequest));
req->vm = vm;
req->fiber = fiber;
uvReq.data = req;</code></pre>
<p>In callbacks, retrieve the context:</p>
<pre><code>void onComplete(uv_xxx_t* uvReq) {
MyRequest* req = (MyRequest*)uvReq->data;
// ...
}</code></pre>
<h2>Complete Async File Read Example</h2>
<h3>asyncfile.wren</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "scheduler" for Scheduler
class AsyncFile {
foreign static read_(path, fiber)
foreign static write_(path, content, fiber)
static read(path) {
return Scheduler.await_ { read_(path, Fiber.current) }
}
static write(path, content) {
return Scheduler.await_ { write_(path, content, Fiber.current) }
}
}</code></pre>
<h3>asyncfile.c (simplified)</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;uv.h&gt;
#include "asyncfile.h"
#include "wren.h"
#include "vm.h"
#include "scheduler.h"
typedef struct {
WrenVM* vm;
WrenHandle* fiber;
uv_fs_t req;
uv_file fd;
char* buffer;
size_t size;
} FileRequest;
static void cleanupRequest(FileRequest* request) {
if (request->buffer) free(request->buffer);
wrenReleaseHandle(request->vm, request->fiber);
free(request);
}
static void onReadComplete(uv_fs_t* req) {
FileRequest* request = (FileRequest*)req->data;
uv_fs_req_cleanup(req);
if (req->result < 0) {
schedulerResume(request->fiber, false);
wrenEnsureSlots(request->vm, 1);
wrenSetSlotString(request->vm, 0, "Read failed.");
} else {
request->buffer[req->result] = '\0';
schedulerResume(request->fiber, true);
wrenEnsureSlots(request->vm, 1);
wrenSetSlotString(request->vm, 0, request->buffer);
}
uv_fs_close(getLoop(), req, request->fd, NULL);
cleanupRequest(request);
}
static void onFileStatComplete(uv_fs_t* req) {
FileRequest* request = (FileRequest*)req->data;
uv_fs_req_cleanup(req);
if (req->result < 0) {
schedulerResume(request->fiber, false);
wrenEnsureSlots(request->vm, 1);
wrenSetSlotString(request->vm, 0, "Stat failed.");
uv_fs_close(getLoop(), req, request->fd, NULL);
cleanupRequest(request);
return;
}
request->size = req->statbuf.st_size;
request->buffer = (char*)malloc(request->size + 1);
uv_buf_t buf = uv_buf_init(request->buffer, request->size);
uv_fs_read(getLoop(), &request->req, request->fd, &buf, 1, 0, onReadComplete);
}
static void onFileOpenComplete(uv_fs_t* req) {
FileRequest* request = (FileRequest*)req->data;
uv_fs_req_cleanup(req);
if (req->result < 0) {
schedulerResume(request->fiber, false);
wrenEnsureSlots(request->vm, 1);
wrenSetSlotString(request->vm, 0, "Open failed.");
cleanupRequest(request);
return;
}
request->fd = (uv_file)req->result;
uv_fs_fstat(getLoop(), &request->req, request->fd, onFileStatComplete);
}
void asyncFileRead(WrenVM* vm) {
const char* path = wrenGetSlotString(vm, 1);
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
FileRequest* request = (FileRequest*)malloc(sizeof(FileRequest));
request->vm = vm;
request->fiber = fiber;
request->buffer = NULL;
request->req.data = request;
uv_fs_open(getLoop(), &request->req, path, UV_FS_O_RDONLY, 0, onFileOpenComplete);
}</code></pre>
<h2>Error Handling in Async Operations</h2>
<h3>libuv Errors</h3>
<p>Check <code>req->result</code> for negative values:</p>
<pre><code>if (req->result < 0) {
const char* msg = uv_strerror((int)req->result);
schedulerResume(request->fiber, false);
wrenSetSlotString(request->vm, 0, msg);
return;
}</code></pre>
<h3>Propagating to Wren</h3>
<p>Use <code>schedulerResume(fiber, false)</code> for errors. The value in slot 0 becomes the error that <code>Scheduler.await_</code> throws as a runtime error.</p>
<h2>Timer Example</h2>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
typedef struct {
WrenVM* vm;
WrenHandle* fiber;
uv_timer_t timer;
} TimerRequest;
static void onTimerComplete(uv_timer_t* timer) {
TimerRequest* request = (TimerRequest*)timer->data;
schedulerResume(request->fiber, true);
wrenReleaseHandle(request->vm, request->fiber);
wrenEnsureSlots(request->vm, 1);
wrenSetSlotNull(request->vm, 0);
uv_close((uv_handle_t*)timer, NULL);
free(request);
}
void timerSleep(WrenVM* vm) {
double ms = wrenGetSlotDouble(vm, 1);
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
TimerRequest* request = (TimerRequest*)malloc(sizeof(TimerRequest));
request->vm = vm;
request->fiber = fiber;
request->timer.data = request;
uv_timer_init(getLoop(), &request->timer);
uv_timer_start(&request->timer, onTimerComplete, (uint64_t)ms, 0);
}</code></pre>
<h2>Multiple Concurrent Operations</h2>
<p>Each async operation gets its own request structure and fiber. Multiple operations can run concurrently:</p>
<pre><code>import "asyncfile" for AsyncFile
var fiber1 = Fiber.new {
var a = AsyncFile.read("a.txt")
System.print("A: %(a.count) bytes")
}
var fiber2 = Fiber.new {
var b = AsyncFile.read("b.txt")
System.print("B: %(b.count) bytes")
}
fiber1.call()
fiber2.call()</code></pre>
<h2>Best Practices</h2>
<ul>
<li><strong>Always release fiber handles</strong>: Call <code>wrenReleaseHandle</code> after resuming</li>
<li><strong>Cleanup on all paths</strong>: Free resources in both success and error cases</li>
<li><strong>Use uv_fs_req_cleanup</strong>: Required after filesystem operations</li>
<li><strong>Close handles properly</strong>: Use <code>uv_close</code> for handles like timers and sockets</li>
<li><strong>Check for VM validity</strong>: The VM pointer should remain valid during callbacks</li>
</ul>
<h2>Debugging Tips</h2>
<ul>
<li>Add logging in callbacks to trace execution flow</li>
<li>Verify libuv error codes with <code>uv_strerror</code></li>
<li>Use <code>make debug</code> build for symbols</li>
<li>Check for memory leaks with valgrind</li>
</ul>
<h2>Next Steps</h2>
<p>See <a href="testing.html">Writing Tests</a> for testing async operations, including the <code>// skip:</code> annotation for tests that require network access.</p>
{% endblock %}

View File

@ -0,0 +1,412 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "C-Backed Modules" %}
{% set breadcrumb = [{"url": "contributing/index.html", "title": "Contributing"}, {"title": "C-Backed Modules"}] %}
{% set prev_page = {"url": "contributing/pure-wren-module.html", "title": "Pure-Wren Modules"} %}
{% set next_page = {"url": "contributing/foreign-classes.html", "title": "Foreign Classes"} %}
{% block article %}
<h1>C-Backed Modules</h1>
<p>C-backed modules implement foreign methods in C, providing access to system libraries, native performance, or functionality not available in pure Wren. Examples: json, io, net, crypto, tls, sqlite, base64.</p>
<h2>Step 1: Create the Wren Interface</h2>
<p>Create <code>src/module/&lt;name&gt;.wren</code> with foreign method declarations:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
class Counter {
foreign static create()
foreign static increment(handle)
foreign static getValue(handle)
foreign static destroy(handle)
static use(fn) {
var handle = Counter.create()
var result = fn.call(handle)
Counter.destroy(handle)
return result
}
}</code></pre>
<p>The <code>foreign</code> keyword indicates the method is implemented in C. Non-foreign methods can provide higher-level Wren wrappers around the foreign primitives.</p>
<h2>Step 2: Generate the .wren.inc</h2>
<pre><code>python3 util/wren_to_c_string.py src/module/counter.wren.inc src/module/counter.wren</code></pre>
<h2>Step 3: Create the C Implementation</h2>
<p>Create <code>src/module/counter.c</code>:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#include &lt;stdlib.h&gt;
#include "counter.h"
#include "wren.h"
typedef struct {
int value;
} Counter;
void counterCreate(WrenVM* vm) {
Counter* counter = (Counter*)malloc(sizeof(Counter));
if (!counter) {
wrenSetSlotNull(vm, 0);
return;
}
counter->value = 0;
wrenSetSlotDouble(vm, 0, (double)(uintptr_t)counter);
}
void counterIncrement(WrenVM* vm) {
double handle = wrenGetSlotDouble(vm, 1);
Counter* counter = (Counter*)(uintptr_t)handle;
counter->value++;
}
void counterGetValue(WrenVM* vm) {
double handle = wrenGetSlotDouble(vm, 1);
Counter* counter = (Counter*)(uintptr_t)handle;
wrenSetSlotDouble(vm, 0, counter->value);
}
void counterDestroy(WrenVM* vm) {
double handle = wrenGetSlotDouble(vm, 1);
Counter* counter = (Counter*)(uintptr_t)handle;
free(counter);
}</code></pre>
<h2>Step 4: Create the C Header</h2>
<p>Create <code>src/module/counter.h</code>:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#ifndef counter_h
#define counter_h
#include "wren.h"
void counterCreate(WrenVM* vm);
void counterIncrement(WrenVM* vm);
void counterGetValue(WrenVM* vm);
void counterDestroy(WrenVM* vm);
#endif</code></pre>
<h2>Step 5: Register in modules.c</h2>
<p>Edit <code>src/cli/modules.c</code>:</p>
<h3>Add the Include</h3>
<pre><code>#include "counter.wren.inc"</code></pre>
<h3>Add Extern Declarations</h3>
<pre><code>extern void counterCreate(WrenVM* vm);
extern void counterIncrement(WrenVM* vm);
extern void counterGetValue(WrenVM* vm);
extern void counterDestroy(WrenVM* vm);</code></pre>
<h3>Add the Module Entry</h3>
<pre><code>MODULE(counter)
CLASS(Counter)
STATIC_METHOD("create()", counterCreate)
STATIC_METHOD("increment(_)", counterIncrement)
STATIC_METHOD("getValue(_)", counterGetValue)
STATIC_METHOD("destroy(_)", counterDestroy)
END_CLASS
END_MODULE</code></pre>
<h2>Step 6: Update the Makefile</h2>
<p>Edit <code>projects/make/wren_cli.make</code>:</p>
<h3>Add to OBJECTS</h3>
<pre><code>OBJECTS += $(OBJDIR)/counter.o</code></pre>
<h3>Add Compilation Rule</h3>
<pre><code>$(OBJDIR)/counter.o: ../../src/module/counter.c
@echo $(notdir $&lt;)
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$&lt;"</code></pre>
<h2>Step 7: Build and Test</h2>
<pre><code>make clean && make build
python3 util/test.py counter</code></pre>
<h2>Wren/C Data Exchange</h2>
<h3>Getting Values from Wren</h3>
<pre><code>const char* str = wrenGetSlotString(vm, 1);
double num = wrenGetSlotDouble(vm, 1);
bool b = wrenGetSlotBool(vm, 1);
void* foreign = wrenGetSlotForeign(vm, 0);
WrenHandle* handle = wrenGetSlotHandle(vm, 1);
int count = wrenGetSlotCount(vm);
WrenType type = wrenGetSlotType(vm, 1);</code></pre>
<h3>Setting Return Values</h3>
<pre><code>wrenSetSlotString(vm, 0, "result");
wrenSetSlotDouble(vm, 0, 42.0);
wrenSetSlotBool(vm, 0, true);
wrenSetSlotNull(vm, 0);
wrenSetSlotNewList(vm, 0);</code></pre>
<h3>Working with Lists</h3>
<pre><code>wrenSetSlotNewList(vm, 0);
wrenSetSlotString(vm, 1, "item");
wrenInsertInList(vm, 0, -1, 1);
int count = wrenGetListCount(vm, 0);
wrenGetListElement(vm, 0, index, 1);</code></pre>
<h3>Working with Maps</h3>
<pre><code>wrenSetSlotNewMap(vm, 0);
wrenSetSlotString(vm, 1, "key");
wrenSetSlotDouble(vm, 2, 123);
wrenSetMapValue(vm, 0, 1, 2);</code></pre>
<h3>Ensuring Slots</h3>
<pre><code>wrenEnsureSlots(vm, 5);</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Slot 0 is used for the return value and (for instance methods) the receiver. Arguments start at slot 1.</p>
</div>
<h2>Error Handling</h2>
<p>Use <code>wrenAbortFiber</code> to report errors:</p>
<pre><code>void myMethod(WrenVM* vm) {
const char* path = wrenGetSlotString(vm, 1);
FILE* file = fopen(path, "r");
if (!file) {
wrenSetSlotString(vm, 0, "Failed to open file.");
wrenAbortFiber(vm, 0);
return;
}
// ... process file ...
}</code></pre>
<p>The error message is set in slot 0, then <code>wrenAbortFiber</code> is called with the slot containing the message.</p>
<h2>Type Checking</h2>
<p>Verify argument types before using them:</p>
<pre><code>void myMethod(WrenVM* vm) {
if (wrenGetSlotType(vm, 1) != WREN_TYPE_STRING) {
wrenSetSlotString(vm, 0, "Argument must be a string.");
wrenAbortFiber(vm, 0);
return;
}
const char* str = wrenGetSlotString(vm, 1);
// ...
}</code></pre>
<p>WrenType values: <code>WREN_TYPE_BOOL</code>, <code>WREN_TYPE_NUM</code>, <code>WREN_TYPE_FOREIGN</code>, <code>WREN_TYPE_LIST</code>, <code>WREN_TYPE_MAP</code>, <code>WREN_TYPE_NULL</code>, <code>WREN_TYPE_STRING</code>, <code>WREN_TYPE_UNKNOWN</code>.</p>
<h2>Method Signature Rules</h2>
<table>
<tr>
<th>Wren Declaration</th>
<th>Signature String</th>
</tr>
<tr>
<td><code>foreign static foo()</code></td>
<td><code>"foo()"</code></td>
</tr>
<tr>
<td><code>foreign static foo(a)</code></td>
<td><code>"foo(_)"</code></td>
</tr>
<tr>
<td><code>foreign static foo(a, b)</code></td>
<td><code>"foo(_,_)"</code></td>
</tr>
<tr>
<td><code>foreign foo()</code></td>
<td><code>"foo()"</code> with <code>METHOD</code></td>
</tr>
<tr>
<td><code>foreign name</code> (getter)</td>
<td><code>"name"</code></td>
</tr>
<tr>
<td><code>foreign name=(v)</code> (setter)</td>
<td><code>"name=(_)"</code></td>
</tr>
</table>
<h2>Complete Example</h2>
<p>A more realistic example parsing hexadecimal strings:</p>
<h3>hex.wren</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
class Hex {
foreign static encode(bytes)
foreign static decode(str)
static isValid(str) {
for (c in str) {
var code = c.bytes[0]
var valid = (code >= 48 && code <= 57) ||
(code >= 65 && code <= 70) ||
(code >= 97 && code <= 102)
if (!valid) return false
}
return str.count \% 2 == 0
}
}</code></pre>
<h3>hex.c</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include "hex.h"
#include "wren.h"
static const char HEX_CHARS[] = "0123456789abcdef";
void hexEncode(WrenVM* vm) {
const char* input = wrenGetSlotString(vm, 1);
size_t len = strlen(input);
char* output = (char*)malloc(len * 2 + 1);
if (!output) {
wrenSetSlotString(vm, 0, "Memory allocation failed.");
wrenAbortFiber(vm, 0);
return;
}
for (size_t i = 0; i < len; i++) {
unsigned char c = (unsigned char)input[i];
output[i * 2] = HEX_CHARS[(c >> 4) & 0xF];
output[i * 2 + 1] = HEX_CHARS[c & 0xF];
}
output[len * 2] = '\0';
wrenSetSlotString(vm, 0, output);
free(output);
}
static int hexCharToInt(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
return -1;
}
void hexDecode(WrenVM* vm) {
const char* input = wrenGetSlotString(vm, 1);
size_t len = strlen(input);
if (len \% 2 != 0) {
wrenSetSlotString(vm, 0, "Hex string must have even length.");
wrenAbortFiber(vm, 0);
return;
}
char* output = (char*)malloc(len / 2 + 1);
if (!output) {
wrenSetSlotString(vm, 0, "Memory allocation failed.");
wrenAbortFiber(vm, 0);
return;
}
for (size_t i = 0; i < len; i += 2) {
int high = hexCharToInt(input[i]);
int low = hexCharToInt(input[i + 1]);
if (high < 0 || low < 0) {
free(output);
wrenSetSlotString(vm, 0, "Invalid hex character.");
wrenAbortFiber(vm, 0);
return;
}
output[i / 2] = (char)((high << 4) | low);
}
output[len / 2] = '\0';
wrenSetSlotString(vm, 0, output);
free(output);
}</code></pre>
<h3>hex.h</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#ifndef hex_h
#define hex_h
#include "wren.h"
void hexEncode(WrenVM* vm);
void hexDecode(WrenVM* vm);
#endif</code></pre>
<h3>modules.c entries</h3>
<pre><code>#include "hex.wren.inc"
extern void hexEncode(WrenVM* vm);
extern void hexDecode(WrenVM* vm);
MODULE(hex)
CLASS(Hex)
STATIC_METHOD("encode(_)", hexEncode)
STATIC_METHOD("decode(_)", hexDecode)
END_CLASS
END_MODULE</code></pre>
<h2>Common Pitfalls</h2>
<ul>
<li><strong>Stale .wren.inc</strong>: Always regenerate after editing the <code>.wren</code> file</li>
<li><strong>Signature mismatch</strong>: The string in <code>STATIC_METHOD("name(_)", fn)</code> must exactly match the Wren declaration's arity</li>
<li><strong>Slot management</strong>: Always call <code>wrenEnsureSlots(vm, n)</code> before using high-numbered slots</li>
<li><strong>Memory leaks</strong>: Free allocated memory before returning</li>
<li><strong>String lifetime</strong>: Strings from <code>wrenGetSlotString</code> are valid only until the next Wren API call</li>
<li><strong>make clean</strong>: Required after adding new object files</li>
</ul>
<h2>Checklist</h2>
<ul>
<li>Created <code>src/module/&lt;name&gt;.wren</code> with foreign declarations</li>
<li>Generated <code>.wren.inc</code></li>
<li>Created <code>src/module/&lt;name&gt;.c</code></li>
<li>Created <code>src/module/&lt;name&gt;.h</code></li>
<li>Added <code>#include</code> for <code>.wren.inc</code> in modules.c</li>
<li>Added extern declarations in modules.c</li>
<li>Added <code>MODULE</code>/<code>CLASS</code>/<code>METHOD</code> block in modules.c</li>
<li>Added <code>OBJECTS</code> entry in Makefile</li>
<li>Added compilation rule in Makefile</li>
<li>Built with <code>make clean && make build</code></li>
<li>Created tests and example</li>
<li>Created documentation page</li>
</ul>
<h2>Next Steps</h2>
<ul>
<li><a href="foreign-classes.html">Foreign Classes</a> - for native resource management</li>
<li><a href="async-patterns.html">Async Patterns</a> - for non-blocking I/O operations</li>
</ul>
{% endblock %}

View File

@ -0,0 +1,370 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "Documentation" %}
{% set breadcrumb = [{"url": "contributing/index.html", "title": "Contributing"}, {"title": "Documentation"}] %}
{% set prev_page = {"url": "contributing/testing.html", "title": "Writing Tests"} %}
{% set next_page = {"url": "api/index.html", "title": "API Reference"} %}
{% block article %}
<h1>Documentation</h1>
<p>The Wren-CLI manual is hand-written HTML in <code>manual/</code>. There is no generation step. Every page is a standalone HTML file sharing common CSS and JavaScript.</p>
<h2>HTML Template</h2>
<p>Every page follows this structure:</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;!-- retoor &lt;retoor@molodetz.nl&gt; --&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;[Page Title] - Wren-CLI Manual&lt;/title&gt;
&lt;link rel="stylesheet" href="../css/style.css"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;button class="mobile-menu-toggle"&gt;Menu&lt;/button&gt;
&lt;div class="container"&gt;
&lt;aside class="sidebar"&gt;
&lt;div class="sidebar-header"&gt;
&lt;h1&gt;&lt;a href="../index.html"&gt;Wren-CLI&lt;/a&gt;&lt;/h1&gt;
&lt;div class="version"&gt;v0.4.0&lt;/div&gt;
&lt;/div&gt;
&lt;nav class="sidebar-nav"&gt;
&lt;!-- Auto-generated by sync_sidebar.py --&gt;
&lt;/nav&gt;
&lt;/aside&gt;
&lt;main class="content"&gt;
&lt;nav class="breadcrumb"&gt;
&lt;a href="../index.html"&gt;Home&lt;/a&gt;
&lt;span class="separator"&gt;/&lt;/span&gt;
&lt;a href="index.html"&gt;API Reference&lt;/a&gt;
&lt;span class="separator"&gt;/&lt;/span&gt;
&lt;span&gt;[Current Page]&lt;/span&gt;
&lt;/nav&gt;
&lt;article&gt;
&lt;h1&gt;[Page Title]&lt;/h1&gt;
&lt;!-- Content --&gt;
&lt;/article&gt;
&lt;footer class="page-footer"&gt;
&lt;a href="[prev].html" class="prev"&gt;[Previous]&lt;/a&gt;
&lt;a href="[next].html" class="next"&gt;[Next]&lt;/a&gt;
&lt;/footer&gt;
&lt;/main&gt;
&lt;/div&gt;
&lt;script src="../js/main.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2>Sidebar Navigation</h2>
<p>The sidebar is automatically synchronized across all pages by <code>util/sync_sidebar.py</code>. Never edit the sidebar manually.</p>
<h3>Adding a New Module to the Sidebar</h3>
<ol>
<li>Create the module's HTML page in <code>manual/api/</code></li>
<li>Run <code>make sync-manual</code></li>
</ol>
<p>The sync script automatically discovers new API pages and adds them to the sidebar in alphabetical order.</p>
<h3>Adding a New Section</h3>
<p>To add a new top-level section (like "Contributing"), edit <code>util/sync_sidebar.py</code> and add an entry to the <code>SECTIONS</code> list:</p>
<pre><code>{
"title": "New Section",
"directory": "new-section",
"pages": [
("index.html", "Overview"),
("page1.html", "Page One"),
("page2.html", "Page Two"),
]
},</code></pre>
<h2>API Page Structure</h2>
<p>API documentation pages follow a consistent structure:</p>
<pre><code>&lt;h1&gt;modulename&lt;/h1&gt;
&lt;p&gt;Description paragraph.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import "modulename" for ClassName&lt;/code&gt;&lt;/pre&gt;
&lt;div class="class-header"&gt;&lt;h2&gt;Class: ClassName&lt;/h2&gt;&lt;/div&gt;
&lt;h3&gt;Static Methods&lt;/h3&gt;
&lt;div class="method-signature"&gt;
&lt;span class="method-name"&gt;ClassName.methodName&lt;/span&gt;(&lt;span class="param"&gt;arg&lt;/span&gt;)
&amp;rarr; &lt;span class="type"&gt;ReturnType&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Method description.&lt;/p&gt;
&lt;h3&gt;Examples&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import "modulename" for ClassName
var result = ClassName.methodName("hello")
System.print(result)&lt;/code&gt;&lt;/pre&gt;</code></pre>
<h2>CSS Classes</h2>
<table>
<tr>
<th>Class</th>
<th>Usage</th>
</tr>
<tr>
<td><code>.method-signature</code></td>
<td>Method signature box</td>
</tr>
<tr>
<td><code>.method-name</code></td>
<td>Method name within signature</td>
</tr>
<tr>
<td><code>.param</code></td>
<td>Parameter name</td>
</tr>
<tr>
<td><code>.type</code></td>
<td>Return type</td>
</tr>
<tr>
<td><code>.class-header</code></td>
<td>Class section header</td>
</tr>
<tr>
<td><code>.param-list</code></td>
<td>Parameter description list</td>
</tr>
<tr>
<td><code>.toc</code></td>
<td>Table of contents box</td>
</tr>
<tr>
<td><code>.admonition</code></td>
<td>Note/warning/tip box</td>
</tr>
<tr>
<td><code>.example-output</code></td>
<td>Expected output display</td>
</tr>
</table>
<h2>Method Signature Format</h2>
<p>Static method:</p>
<pre><code>&lt;div class="method-signature"&gt;
&lt;span class="method-name"&gt;ClassName.methodName&lt;/span&gt;(&lt;span class="param"&gt;arg1&lt;/span&gt;, &lt;span class="param"&gt;arg2&lt;/span&gt;)
&amp;rarr; &lt;span class="type"&gt;String&lt;/span&gt;
&lt;/div&gt;</code></pre>
<p>Instance method:</p>
<pre><code>&lt;div class="method-signature"&gt;
&lt;span class="method-name"&gt;instance.methodName&lt;/span&gt;(&lt;span class="param"&gt;arg&lt;/span&gt;)
&amp;rarr; &lt;span class="type"&gt;Bool&lt;/span&gt;
&lt;/div&gt;</code></pre>
<p>Property (getter):</p>
<pre><code>&lt;div class="method-signature"&gt;
&lt;span class="method-name"&gt;instance.propertyName&lt;/span&gt;
&amp;rarr; &lt;span class="type"&gt;Num&lt;/span&gt;
&lt;/div&gt;</code></pre>
<h2>Admonition Blocks</h2>
<p>Use admonitions for notes, warnings, and tips:</p>
<h3>Note</h3>
<pre><code>&lt;div class="admonition note"&gt;
&lt;div class="admonition-title"&gt;Note&lt;/div&gt;
&lt;p&gt;Additional information.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Additional information.</p>
</div>
<h3>Warning</h3>
<pre><code>&lt;div class="admonition warning"&gt;
&lt;div class="admonition-title"&gt;Warning&lt;/div&gt;
&lt;p&gt;Important caution.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Important caution.</p>
</div>
<h3>Tip</h3>
<pre><code>&lt;div class="admonition tip"&gt;
&lt;div class="admonition-title"&gt;Tip&lt;/div&gt;
&lt;p&gt;Helpful suggestion.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<h2>Parameter Lists</h2>
<p>For methods with complex parameters:</p>
<pre><code>&lt;div class="param-list"&gt;
&lt;div class="param-item"&gt;
&lt;span class="param-name"&gt;path&lt;/span&gt;
&lt;span class="param-type"&gt;String&lt;/span&gt;
&lt;p&gt;The file path to read.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="param-item"&gt;
&lt;span class="param-name"&gt;encoding&lt;/span&gt;
&lt;span class="param-type"&gt;String&lt;/span&gt;
&lt;p&gt;The character encoding (default: "utf-8").&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</code></pre>
<h2>Tables</h2>
<p>Use tables for options, type mappings, or comparisons:</p>
<pre><code>&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"r"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"w"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write mode&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;</code></pre>
<h2>Footer Navigation</h2>
<p>Every page has previous/next navigation:</p>
<pre><code>&lt;footer class="page-footer"&gt;
&lt;a href="previous.html" class="prev"&gt;Previous Page&lt;/a&gt;
&lt;a href="next.html" class="next"&gt;Next Page&lt;/a&gt;
&lt;/footer&gt;</code></pre>
<p>Update the footer on adjacent pages when adding new pages.</p>
<h2>Breadcrumb Navigation</h2>
<p>Shows the page hierarchy:</p>
<pre><code>&lt;nav class="breadcrumb"&gt;
&lt;a href="../index.html"&gt;Home&lt;/a&gt;
&lt;span class="separator"&gt;/&lt;/span&gt;
&lt;a href="index.html"&gt;API Reference&lt;/a&gt;
&lt;span class="separator"&gt;/&lt;/span&gt;
&lt;span&gt;io&lt;/span&gt;
&lt;/nav&gt;</code></pre>
<h2>Syncing the Sidebar</h2>
<pre><code>make sync-manual</code></pre>
<p>This runs <code>util/sync_sidebar.py</code> which:</p>
<ol>
<li>Discovers all API module pages</li>
<li>Builds the complete sidebar HTML</li>
<li>Updates the <code>&lt;nav class="sidebar-nav"&gt;</code> in every HTML file</li>
<li>Sets the <code>active</code> class on the current page's link</li>
</ol>
<p>The script is idempotent - running it twice produces the same result.</p>
<h2>Adding a New API Page</h2>
<ol>
<li>Create <code>manual/api/&lt;name&gt;.html</code></li>
<li>Use the template structure above</li>
<li>Run <code>make sync-manual</code></li>
<li>Update footer prev/next on adjacent pages</li>
<li>Add entry to <code>manual/api/index.html</code></li>
</ol>
<h2>Example: Minimal API Page</h2>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;!-- retoor &lt;retoor@molodetz.nl&gt; --&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;mymodule - Wren-CLI Manual&lt;/title&gt;
&lt;link rel="stylesheet" href="../css/style.css"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;button class="mobile-menu-toggle"&gt;Menu&lt;/button&gt;
&lt;div class="container"&gt;
&lt;aside class="sidebar"&gt;
&lt;div class="sidebar-header"&gt;
&lt;h1&gt;&lt;a href="../index.html"&gt;Wren-CLI&lt;/a&gt;&lt;/h1&gt;
&lt;div class="version"&gt;v0.4.0&lt;/div&gt;
&lt;/div&gt;
&lt;nav class="sidebar-nav"&gt;
&lt;/nav&gt;
&lt;/aside&gt;
&lt;main class="content"&gt;
&lt;nav class="breadcrumb"&gt;
&lt;a href="../index.html"&gt;Home&lt;/a&gt;
&lt;span class="separator"&gt;/&lt;/span&gt;
&lt;a href="index.html"&gt;API Reference&lt;/a&gt;
&lt;span class="separator"&gt;/&lt;/span&gt;
&lt;span&gt;mymodule&lt;/span&gt;
&lt;/nav&gt;
&lt;article&gt;
&lt;h1&gt;mymodule&lt;/h1&gt;
&lt;p&gt;The &lt;code&gt;mymodule&lt;/code&gt; module provides...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import "mymodule" for MyClass&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;MyClass&lt;/h2&gt;
&lt;h3&gt;Static Methods&lt;/h3&gt;
&lt;div class="method-signature"&gt;
&lt;span class="method-name"&gt;MyClass.process&lt;/span&gt;(&lt;span class="param"&gt;input&lt;/span&gt;)
&amp;rarr; &lt;span class="type"&gt;String&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Processes the input and returns a result.&lt;/p&gt;
&lt;h3&gt;Examples&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import "mymodule" for MyClass
var result = MyClass.process("hello")
System.print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;/article&gt;
&lt;footer class="page-footer"&gt;
&lt;a href="math.html" class="prev"&gt;math&lt;/a&gt;
&lt;a href="net.html" class="next"&gt;net&lt;/a&gt;
&lt;/footer&gt;
&lt;/main&gt;
&lt;/div&gt;
&lt;script src="../js/main.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2>Checklist for New Documentation</h2>
<ul>
<li>Author comment on line 2</li>
<li>Correct title in <code>&lt;title&gt;</code> and <code>&lt;h1&gt;</code></li>
<li>Proper breadcrumb navigation</li>
<li>Import statement example</li>
<li>All methods documented with signatures</li>
<li>Working code examples</li>
<li>Footer with prev/next links</li>
<li>Ran <code>make sync-manual</code></li>
<li>Updated adjacent page footers</li>
<li>Added to index page if applicable</li>
</ul>
{% endblock %}

View File

@ -0,0 +1,383 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "Foreign Classes" %}
{% set breadcrumb = [{"url": "contributing/index.html", "title": "Contributing"}, {"title": "Foreign Classes"}] %}
{% set prev_page = {"url": "contributing/c-backed-module.html", "title": "C-Backed Modules"} %}
{% set next_page = {"url": "contributing/async-patterns.html", "title": "Async Patterns"} %}
{% block article %}
<h1>Foreign Classes</h1>
<p>Foreign classes allow Wren objects to hold native C data. This is used when a Wren object needs to manage resources like file handles, network sockets, database connections, or any native data structure.</p>
<h2>When to Use Foreign Classes</h2>
<ul>
<li>Wrapping system resources (files, sockets, processes)</li>
<li>Managing native library objects</li>
<li>Storing data structures more complex than Wren's built-in types</li>
<li>Resources requiring explicit cleanup</li>
</ul>
<h2>Architecture</h2>
<p>A foreign class has three components:</p>
<ol>
<li><strong>Allocate function</strong>: Called when an instance is created via <code>construct new()</code></li>
<li><strong>Finalize function</strong>: Called when the garbage collector frees the instance</li>
<li><strong>Instance methods</strong>: Operate on the foreign data</li>
</ol>
<h2>Basic Pattern</h2>
<h3>Wren Interface</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
class Buffer {
foreign construct new(size)
foreign write(data)
foreign read()
foreign size
foreign clear()
}</code></pre>
<p>The constructor uses <code>foreign construct</code> to trigger allocation.</p>
<h3>C Implementation</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include "buffer.h"
#include "wren.h"
typedef struct {
char* data;
size_t size;
size_t capacity;
} Buffer;
void bufferAllocate(WrenVM* vm) {
Buffer* buffer = (Buffer*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(Buffer));
double capacity = wrenGetSlotDouble(vm, 1);
buffer->capacity = (size_t)capacity;
buffer->size = 0;
buffer->data = (char*)malloc(buffer->capacity);
if (!buffer->data) {
buffer->capacity = 0;
}
}
void bufferFinalize(void* data) {
Buffer* buffer = (Buffer*)data;
if (buffer->data) {
free(buffer->data);
buffer->data = NULL;
}
}
void bufferWrite(WrenVM* vm) {
Buffer* buffer = (Buffer*)wrenGetSlotForeign(vm, 0);
const char* str = wrenGetSlotString(vm, 1);
size_t len = strlen(str);
if (buffer->size + len > buffer->capacity) {
wrenSetSlotString(vm, 0, "Buffer overflow.");
wrenAbortFiber(vm, 0);
return;
}
memcpy(buffer->data + buffer->size, str, len);
buffer->size += len;
}
void bufferRead(WrenVM* vm) {
Buffer* buffer = (Buffer*)wrenGetSlotForeign(vm, 0);
char* copy = (char*)malloc(buffer->size + 1);
if (!copy) {
wrenSetSlotNull(vm, 0);
return;
}
memcpy(copy, buffer->data, buffer->size);
copy[buffer->size] = '\0';
wrenSetSlotString(vm, 0, copy);
free(copy);
}
void bufferSize(WrenVM* vm) {
Buffer* buffer = (Buffer*)wrenGetSlotForeign(vm, 0);
wrenSetSlotDouble(vm, 0, (double)buffer->size);
}
void bufferClear(WrenVM* vm) {
Buffer* buffer = (Buffer*)wrenGetSlotForeign(vm, 0);
buffer->size = 0;
}</code></pre>
<h3>Registration</h3>
<pre><code>MODULE(buffer)
CLASS(Buffer)
ALLOCATE(bufferAllocate)
FINALIZE(bufferFinalize)
METHOD("write(_)", bufferWrite)
METHOD("read()", bufferRead)
METHOD("size", bufferSize)
METHOD("clear()", bufferClear)
END_CLASS
END_MODULE</code></pre>
<h2>Memory Management</h2>
<h3>wrenSetSlotNewForeign</h3>
<pre><code>void* wrenSetSlotNewForeign(WrenVM* vm, int slot, int classSlot, size_t size);</code></pre>
<ul>
<li><code>slot</code>: Where to place the new instance (usually 0)</li>
<li><code>classSlot</code>: Slot containing the class (usually 0 for the current class)</li>
<li><code>size</code>: Size of the native data structure</li>
</ul>
<p>Returns a pointer to the allocated memory. This memory is managed by Wren's garbage collector.</p>
<h3>Finalize Function Signature</h3>
<pre><code>void myFinalize(void* data);</code></pre>
<p>The finalize function receives only a pointer to the foreign data, not the VM. This means:</p>
<ul>
<li>No Wren API calls in finalize</li>
<li>Cannot throw errors</li>
<li>Must be fast (GC is running)</li>
<li>Free any resources allocated in allocate or methods</li>
</ul>
<h3>Accessing Foreign Data</h3>
<p>In instance methods, use <code>wrenGetSlotForeign</code> on slot 0:</p>
<pre><code>void bufferMethod(WrenVM* vm) {
Buffer* buffer = (Buffer*)wrenGetSlotForeign(vm, 0);
// buffer points to the struct created in bufferAllocate
}</code></pre>
<h2>File Handle Example</h2>
<p>A practical example wrapping a file handle:</p>
<h3>filehandle.wren</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
class FileHandle {
foreign construct open(path, mode)
foreign read()
foreign write(data)
foreign close()
foreign isOpen
}</code></pre>
<h3>filehandle.c</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include "filehandle.h"
#include "wren.h"
typedef struct {
FILE* file;
char* path;
} FileHandle;
void fileHandleAllocate(WrenVM* vm) {
FileHandle* handle = (FileHandle*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(FileHandle));
const char* path = wrenGetSlotString(vm, 1);
const char* mode = wrenGetSlotString(vm, 2);
handle->path = strdup(path);
handle->file = fopen(path, mode);
if (!handle->file) {
wrenSetSlotString(vm, 0, "Failed to open file.");
wrenAbortFiber(vm, 0);
}
}
void fileHandleFinalize(void* data) {
FileHandle* handle = (FileHandle*)data;
if (handle->file) {
fclose(handle->file);
handle->file = NULL;
}
if (handle->path) {
free(handle->path);
handle->path = NULL;
}
}
void fileHandleRead(WrenVM* vm) {
FileHandle* handle = (FileHandle*)wrenGetSlotForeign(vm, 0);
if (!handle->file) {
wrenSetSlotString(vm, 0, "File not open.");
wrenAbortFiber(vm, 0);
return;
}
fseek(handle->file, 0, SEEK_END);
long size = ftell(handle->file);
fseek(handle->file, 0, SEEK_SET);
char* content = (char*)malloc(size + 1);
if (!content) {
wrenSetSlotString(vm, 0, "Memory allocation failed.");
wrenAbortFiber(vm, 0);
return;
}
fread(content, 1, size, handle->file);
content[size] = '\0';
wrenSetSlotString(vm, 0, content);
free(content);
}
void fileHandleWrite(WrenVM* vm) {
FileHandle* handle = (FileHandle*)wrenGetSlotForeign(vm, 0);
const char* data = wrenGetSlotString(vm, 1);
if (!handle->file) {
wrenSetSlotString(vm, 0, "File not open.");
wrenAbortFiber(vm, 0);
return;
}
size_t written = fwrite(data, 1, strlen(data), handle->file);
wrenSetSlotDouble(vm, 0, (double)written);
}
void fileHandleClose(WrenVM* vm) {
FileHandle* handle = (FileHandle*)wrenGetSlotForeign(vm, 0);
if (handle->file) {
fclose(handle->file);
handle->file = NULL;
}
}
void fileHandleIsOpen(WrenVM* vm) {
FileHandle* handle = (FileHandle*)wrenGetSlotForeign(vm, 0);
wrenSetSlotBool(vm, 0, handle->file != NULL);
}</code></pre>
<h3>Usage</h3>
<pre><code>import "filehandle" for FileHandle
var file = FileHandle.open("test.txt", "w")
file.write("Hello, World!")
file.close()
file = FileHandle.open("test.txt", "r")
System.print(file.read())
file.close()</code></pre>
<h2>Multiple Foreign Classes</h2>
<p>A module can have multiple foreign classes:</p>
<pre><code>MODULE(database)
CLASS(Connection)
ALLOCATE(connectionAllocate)
FINALIZE(connectionFinalize)
METHOD("query(_)", connectionQuery)
METHOD("close()", connectionClose)
END_CLASS
CLASS(Statement)
ALLOCATE(statementAllocate)
FINALIZE(statementFinalize)
METHOD("bind(_,_)", statementBind)
METHOD("execute()", statementExecute)
END_CLASS
END_MODULE</code></pre>
<h2>Resource Safety Patterns</h2>
<h3>Early Close</h3>
<p>Always check if resource is still valid:</p>
<pre><code>void handleMethod(WrenVM* vm) {
Handle* h = (Handle*)wrenGetSlotForeign(vm, 0);
if (!h->resource) {
wrenSetSlotString(vm, 0, "Handle already closed.");
wrenAbortFiber(vm, 0);
return;
}
// ...
}</code></pre>
<h3>Double-Free Prevention</h3>
<p>Set pointers to NULL after freeing:</p>
<pre><code>void handleClose(WrenVM* vm) {
Handle* h = (Handle*)wrenGetSlotForeign(vm, 0);
if (h->resource) {
resource_free(h->resource);
h->resource = NULL;
}
}</code></pre>
<h3>Defensive Finalize</h3>
<p>Always handle partially constructed objects:</p>
<pre><code>void handleFinalize(void* data) {
Handle* h = (Handle*)data;
if (h->resource) {
resource_free(h->resource);
}
if (h->name) {
free(h->name);
}
}</code></pre>
<h2>Common Pitfalls</h2>
<ul>
<li><strong>Forgetting FINALIZE</strong>: Memory leaks for any allocated resources</li>
<li><strong>Using VM in finalize</strong>: Causes undefined behavior</li>
<li><strong>Wrong slot for foreign data</strong>: Instance methods get <code>this</code> in slot 0</li>
<li><strong>Static methods on foreign class</strong>: Use <code>STATIC_METHOD</code> macro, but note that <code>wrenGetSlotForeign</code> is not available (no instance)</li>
</ul>
<h2>Checklist</h2>
<ul>
<li><code>foreign construct</code> in Wren class</li>
<li>Allocate function uses <code>wrenSetSlotNewForeign</code></li>
<li>Finalize function frees all resources</li>
<li><code>ALLOCATE</code> and <code>FINALIZE</code> in registration</li>
<li>Instance methods use <code>METHOD</code> (not <code>STATIC_METHOD</code>)</li>
<li>Methods access foreign data via slot 0</li>
<li>All methods check resource validity</li>
</ul>
<h2>Next Steps</h2>
<p>For I/O-bound foreign classes, see <a href="async-patterns.html">Async Patterns</a> to integrate with the libuv event loop.</p>
{% endblock %}

View File

@ -0,0 +1,128 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "Contributing" %}
{% set breadcrumb = [{"title": "Contributing"}] %}
{% set prev_page = {"url": "howto/error-handling.html", "title": "Error Handling"} %}
{% set next_page = {"url": "contributing/module-overview.html", "title": "Module Architecture"} %}
{% block article %}
<h1>Contributing</h1>
<p>This guide explains how to contribute new modules to Wren-CLI, write tests, and add documentation. Whether you are adding a pure-Wren module or implementing C-backed foreign methods, this section provides step-by-step instructions.</p>
<div class="toc">
<h4>In This Section</h4>
<ul>
<li><a href="module-overview.html">Module Architecture</a> - Project structure and artifact matrix</li>
<li><a href="pure-wren-module.html">Pure-Wren Modules</a> - Step-by-step for Wren-only modules</li>
<li><a href="c-backed-module.html">C-Backed Modules</a> - Implementing foreign methods in C</li>
<li><a href="foreign-classes.html">Foreign Classes</a> - Native resource management</li>
<li><a href="async-patterns.html">Async Patterns</a> - Scheduler/Fiber and libuv integration</li>
<li><a href="testing.html">Writing Tests</a> - Test structure and annotations</li>
<li><a href="documentation.html">Documentation</a> - Writing manual pages</li>
</ul>
</div>
<h2>Prerequisites</h2>
<p>Before contributing, ensure you have:</p>
<ul>
<li>A working build environment (see <a href="../getting-started/installation.html">Installation</a>)</li>
<li>Python 3 (for utility scripts)</li>
<li>Basic understanding of <a href="../language/index.html">Wren syntax</a></li>
<li>For C-backed modules: familiarity with C and the Wren embedding API</li>
</ul>
<h2>Module Types</h2>
<p>Wren-CLI supports two types of modules:</p>
<table>
<tr>
<th>Type</th>
<th>Description</th>
<th>Files Required</th>
</tr>
<tr>
<td>Pure-Wren</td>
<td>Modules written entirely in Wren. Examples: argparse, html, jinja, http, markdown, dataset, web, websocket, wdantic, uuid, tempfile</td>
<td><code>.wren</code>, <code>.wren.inc</code>, modules.c entry</td>
</tr>
<tr>
<td>C-Backed</td>
<td>Modules with foreign methods implemented in C. Examples: json, io, net, crypto, tls, sqlite, base64</td>
<td><code>.wren</code>, <code>.wren.inc</code>, <code>.c</code>, <code>.h</code>, modules.c entry, Makefile entry</td>
</tr>
</table>
<h2>Workflow Checklist</h2>
<h3>Pure-Wren Module</h3>
<ol>
<li>Create <code>src/module/&lt;name&gt;.wren</code></li>
<li>Generate <code>.wren.inc</code> with <code>python3 util/wren_to_c_string.py</code></li>
<li>Add <code>#include</code> and <code>MODULE</code> block in <code>src/cli/modules.c</code></li>
<li>Build with <code>make clean && make build</code></li>
<li>Create tests in <code>test/&lt;name&gt;/</code></li>
<li>Create example in <code>example/&lt;name&gt;_demo.wren</code></li>
<li>Create <code>manual/api/&lt;name&gt;.html</code></li>
<li>Run <code>make sync-manual</code></li>
<li>Verify with <code>python3 util/test.py &lt;name&gt;</code></li>
</ol>
<h3>C-Backed Module</h3>
<ol>
<li>All pure-Wren steps, plus:</li>
<li>Create <code>src/module/&lt;name&gt;.c</code> with foreign method implementations</li>
<li>Create <code>src/module/&lt;name&gt;.h</code> with function declarations</li>
<li>Add extern declarations in <code>modules.c</code></li>
<li>Add <code>CLASS</code>/<code>METHOD</code> registrations with correct signatures</li>
<li>Add <code>OBJECTS</code> and compilation rule to <code>projects/make/wren_cli.make</code></li>
</ol>
<h2>Build Commands</h2>
<pre><code>make build # Release build
make debug # Debug build
make clean # Clean artifacts
make tests # Build and run all tests
make sync-manual # Sync sidebar across manual pages</code></pre>
<h2>Directory Structure</h2>
<pre><code>src/
cli/
modules.c # Foreign function registry
vm.c # VM and module loading
module/
&lt;name&gt;.wren # Wren interface source
&lt;name&gt;.wren.inc # Generated C string literal
&lt;name&gt;.c # C implementation (if foreign methods)
&lt;name&gt;.h # C header (if foreign methods)
test/
&lt;name&gt;/
&lt;feature&gt;.wren # Test files with annotations
example/
&lt;name&gt;_demo.wren # Usage demonstrations
manual/
api/&lt;name&gt;.html # API documentation</code></pre>
<h2>Getting Help</h2>
<p>If you encounter issues while contributing:</p>
<ul>
<li>Review existing modules as reference implementations</li>
<li>Check the test files for expected behavior patterns</li>
<li>Consult the <a href="module-overview.html">Module Architecture</a> section for detailed artifact requirements</li>
</ul>
<h2>Next Steps</h2>
<p>Start with the <a href="module-overview.html">Module Architecture</a> section to understand the project structure, then proceed to the appropriate module guide based on your needs.</p>
{% endblock %}

View File

@ -0,0 +1,221 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "Module Architecture" %}
{% set breadcrumb = [{"url": "contributing/index.html", "title": "Contributing"}, {"title": "Module Architecture"}] %}
{% set prev_page = {"url": "contributing/index.html", "title": "Overview"} %}
{% set next_page = {"url": "contributing/pure-wren-module.html", "title": "Pure-Wren Modules"} %}
{% block article %}
<h1>Module Architecture</h1>
<p>This section explains how Wren-CLI modules are structured, what files each module type requires, and how the build system processes them.</p>
<h2>Project Structure</h2>
<pre><code>src/
cli/
main.c # Entry point
vm.c # VM initialization, libuv event loop, module loading
modules.c # Foreign function registry, module registration
modules.h # Public interface for module loading/binding
path.c # Cross-platform path manipulation
module/
&lt;name&gt;.wren # Wren interface source
&lt;name&gt;.wren.inc # Generated C string literal (do not edit)
&lt;name&gt;.c # C implementation (only for foreign methods)
&lt;name&gt;.h # C header (only for foreign methods)
test/
&lt;modulename&gt;/
&lt;testname&gt;.wren # Test files with inline annotations
example/
&lt;modulename&gt;_demo.wren # Comprehensive usage demonstrations
manual/
api/ # One HTML file per module
deps/
wren/ # Wren language VM
libuv/ # Async I/O library
cjson/ # JSON parsing library
sqlite/ # SQLite database library</code></pre>
<h2>Module Artifact Matrix</h2>
<p>Every built-in module has up to six artifacts:</p>
<table>
<tr>
<th>Artifact</th>
<th>Path</th>
<th>Required?</th>
</tr>
<tr>
<td>Wren source</td>
<td><code>src/module/&lt;name&gt;.wren</code></td>
<td>Always</td>
</tr>
<tr>
<td>Generated C string</td>
<td><code>src/module/&lt;name&gt;.wren.inc</code></td>
<td>Always</td>
</tr>
<tr>
<td>C implementation</td>
<td><code>src/module/&lt;name&gt;.c</code></td>
<td>Only if foreign methods</td>
</tr>
<tr>
<td>C header</td>
<td><code>src/module/&lt;name&gt;.h</code></td>
<td>Only if .c exists</td>
</tr>
<tr>
<td>Registration in modules.c</td>
<td><code>src/cli/modules.c</code></td>
<td>Always</td>
</tr>
<tr>
<td>Makefile object entry</td>
<td><code>projects/make/wren_cli.make</code></td>
<td>Only if .c exists</td>
</tr>
</table>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Pure-Wren modules (argparse, html, jinja, http, markdown, dataset, web, websocket, wdantic, uuid, tempfile, repl) skip the C files and Makefile entry. C-backed modules (json, io, net, crypto, tls, sqlite, base64, etc.) need all six.</p>
</div>
<h2>The .wren to .wren.inc Pipeline</h2>
<p>Module source code is embedded directly into the compiled binary as C string literals. The script <code>util/wren_to_c_string.py</code> performs this conversion.</p>
<h3>What It Does</h3>
<ol>
<li>Reads the <code>.wren</code> file line by line</li>
<li>Escapes <code>\</code> to <code>\\</code> and <code>"</code> to <code>\"</code></li>
<li>Wraps each line in C string literal quotes with <code>\n</code> appended</li>
<li>Outputs a <code>.wren.inc</code> file with a <code>static const char*</code> variable</li>
</ol>
<h3>Variable Naming</h3>
<p>The variable name is derived from the filename: <code>foo.wren</code> becomes <code>fooModuleSource</code>. Prefixes <code>opt_</code> and <code>wren_</code> are stripped automatically.</p>
<h3>Usage</h3>
<pre><code>python3 util/wren_to_c_string.py src/module/foo.wren.inc src/module/foo.wren</code></pre>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Every time a <code>.wren</code> file is edited, its <code>.wren.inc</code> must be regenerated. If forgotten, the binary will contain stale module source.</p>
</div>
<h2>Module Registration in modules.c</h2>
<p><code>src/cli/modules.c</code> has three sections to update:</p>
<h3>Section 1: Include the .wren.inc</h3>
<p>Add at the top of the file with other includes:</p>
<pre><code>#include "mymodule.wren.inc"</code></pre>
<h3>Section 2: Extern Declarations</h3>
<p>Only required for C-backed modules:</p>
<pre><code>extern void mymoduleDoSomething(WrenVM* vm);
extern void mymoduleComplexOp(WrenVM* vm);</code></pre>
<h3>Section 3: Module Array Entry</h3>
<p>For pure-Wren modules:</p>
<pre><code>MODULE(mymodule)
END_MODULE</code></pre>
<p>For C-backed modules with static methods:</p>
<pre><code>MODULE(mymodule)
CLASS(MyClass)
STATIC_METHOD("doSomething(_)", mymoduleDoSomething)
STATIC_METHOD("complexOp_(_,_,_)", mymoduleComplexOp)
END_CLASS
END_MODULE</code></pre>
<p>For foreign classes with allocation/finalization:</p>
<pre><code>MODULE(mymodule)
CLASS(MyForeignClass)
ALLOCATE(myClassAllocate)
FINALIZE(myClassFinalize)
METHOD("doThing(_)", myClassDoThing)
END_CLASS
END_MODULE</code></pre>
<h3>Registration Macros Reference</h3>
<table>
<tr>
<th>Macro</th>
<th>Usage</th>
</tr>
<tr>
<td><code>MODULE(name)</code> / <code>END_MODULE</code></td>
<td>Module boundary, name must match import string</td>
</tr>
<tr>
<td><code>CLASS(name)</code> / <code>END_CLASS</code></td>
<td>Class boundary, name must match Wren class name</td>
</tr>
<tr>
<td><code>STATIC_METHOD("sig", fn)</code></td>
<td>Bind static foreign method</td>
</tr>
<tr>
<td><code>METHOD("sig", fn)</code></td>
<td>Bind instance foreign method</td>
</tr>
<tr>
<td><code>ALLOCATE(fn)</code></td>
<td>Foreign class constructor</td>
</tr>
<tr>
<td><code>FINALIZE(fn)</code></td>
<td>Foreign class destructor</td>
</tr>
</table>
<h3>Method Signature Format</h3>
<p>The signature string must exactly match what Wren expects:</p>
<ul>
<li><code>"methodName(_)"</code> - one argument</li>
<li><code>"methodName(_,_)"</code> - two arguments</li>
<li><code>"propertyName"</code> - getter (no parentheses)</li>
<li><code>"propertyName=(_)"</code> - setter</li>
</ul>
<h2>Core Components</h2>
<h3>vm.c</h3>
<p>VM initialization, libuv event loop integration, module resolution. The <code>loadModule()</code> function first checks <code>wren_modules/</code> on disk, then falls back to <code>loadBuiltInModule()</code> which serves embedded <code>.wren.inc</code> strings. The <code>resolveModule()</code> function handles simple imports (bare names to built-in) and relative imports (<code>./</code>, <code>../</code> to file path resolution).</p>
<h3>modules.c</h3>
<p>Central registry of all built-in modules. Contains the <code>modules[]</code> array with module/class/method metadata, the <code>.wren.inc</code> includes, extern declarations for C functions, and lookup functions (<code>findModule</code>, <code>findClass</code>, <code>findMethod</code>).</p>
<h2>Event Loop</h2>
<p>All I/O is async via libuv. Wren fibers suspend during I/O operations and resume when complete. The event loop runs after script interpretation via <code>uv_run(loop, UV_RUN_DEFAULT)</code>.</p>
<h2>System Dependencies</h2>
<ul>
<li>OpenSSL (<code>libssl</code>, <code>libcrypto</code>) - required for TLS/HTTPS support</li>
<li>pthreads, dl, m - linked automatically</li>
</ul>
<h2>Next Steps</h2>
<p>Now that you understand the architecture, proceed to:</p>
<ul>
<li><a href="pure-wren-module.html">Pure-Wren Modules</a> - for modules without C code</li>
<li><a href="c-backed-module.html">C-Backed Modules</a> - for modules with foreign methods</li>
</ul>
{% endblock %}

View File

@ -0,0 +1,247 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "Pure-Wren Modules" %}
{% set breadcrumb = [{"url": "contributing/index.html", "title": "Contributing"}, {"title": "Pure-Wren Modules"}] %}
{% set prev_page = {"url": "contributing/module-overview.html", "title": "Module Architecture"} %}
{% set next_page = {"url": "contributing/c-backed-module.html", "title": "C-Backed Modules"} %}
{% block article %}
<h1>Pure-Wren Modules</h1>
<p>Pure-Wren modules are written entirely in Wren, with no C code required. They may depend on other modules (both pure-Wren and C-backed) but do not implement any foreign methods themselves.</p>
<p>Examples of pure-Wren modules: argparse, html, jinja, http, markdown, dataset, web, websocket, wdantic, uuid, tempfile.</p>
<h2>Step 1: Create the Wren Source</h2>
<p>Create <code>src/module/&lt;name&gt;.wren</code> with your module implementation:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
class Utils {
static capitalize(str) {
if (str.count == 0) return str
return str[0].upcase + str[1..-1]
}
static reverse(str) {
var result = ""
for (i in (str.count - 1)..0) {
result = result + str[i]
}
return result
}
static repeat(str, times) {
var result = ""
for (i in 0...times) {
result = result + str
}
return result
}
static words(str) {
return str.split(" ").where {|w| w.count > 0 }.toList
}
}</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Always include the author comment on the first line. Class names should be capitalized and descriptive.</p>
</div>
<h2>Step 2: Generate the .wren.inc</h2>
<p>Convert the Wren source to a C string literal:</p>
<pre><code>python3 util/wren_to_c_string.py src/module/utils.wren.inc src/module/utils.wren</code></pre>
<p>This creates <code>src/module/utils.wren.inc</code> containing:</p>
<pre><code>static const char* utilsModuleSource =
"// retoor &lt;retoor@molodetz.nl&gt;\n"
"\n"
"class Utils {\n"
" static capitalize(str) {\n"
// ... rest of the source
;</code></pre>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Never edit <code>.wren.inc</code> files directly. Always edit the <code>.wren</code> source and regenerate.</p>
</div>
<h2>Step 3: Register in modules.c</h2>
<p>Edit <code>src/cli/modules.c</code> to register the new module.</p>
<h3>Add the Include</h3>
<p>Near the top of the file with other includes:</p>
<pre><code>#include "utils.wren.inc"</code></pre>
<h3>Add the Module Entry</h3>
<p>In the <code>modules[]</code> array:</p>
<pre><code>MODULE(utils)
END_MODULE</code></pre>
<p>For pure-Wren modules, the block is empty. No class or method registrations are needed because there are no foreign bindings.</p>
<h2>Step 4: Build</h2>
<pre><code>make clean && make build</code></pre>
<p>The clean build ensures the new module is properly included.</p>
<h2>Step 5: Test</h2>
<p>Create the test directory:</p>
<pre><code>mkdir -p test/utils</code></pre>
<p>Create test files with inline annotations. For example, <code>test/utils/capitalize.wren</code>:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "utils" for Utils
System.print(Utils.capitalize("hello")) // expect: Hello
System.print(Utils.capitalize("WORLD")) // expect: WORLD
System.print(Utils.capitalize("")) // expect:</code></pre>
<p>Run the tests:</p>
<pre><code>python3 util/test.py utils</code></pre>
<h2>Step 6: Create Example</h2>
<p>Create <code>example/utils_demo.wren</code>:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "utils" for Utils
System.print("=== Utils Demo ===\n")
System.print("--- Capitalize ---")
System.print(Utils.capitalize("hello"))
System.print(Utils.capitalize("wren"))
System.print("\n--- Reverse ---")
System.print(Utils.reverse("hello"))
System.print(Utils.reverse("12345"))
System.print("\n--- Repeat ---")
System.print(Utils.repeat("ab", 3))
System.print(Utils.repeat("-", 10))
System.print("\n--- Words ---")
var sentence = "The quick brown fox"
var wordList = Utils.words(sentence)
for (word in wordList) {
System.print(" - %(word)")
}</code></pre>
<h2>Step 7: Add Documentation</h2>
<p>Create <code>manual/api/utils.html</code> following the API page template. See the <a href="documentation.html">Documentation</a> section for details.</p>
<h2>Step 8: Sync the Sidebar</h2>
<pre><code>make sync-manual</code></pre>
<p>This updates the sidebar navigation across all manual pages to include the new module.</p>
<h2>Step 9: Verify</h2>
<pre><code>python3 util/test.py utils
bin/wren_cli example/utils_demo.wren</code></pre>
<h2>Complete Example</h2>
<p>Here is a more realistic pure-Wren module that builds on existing modules:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "json" for Json
import "io" for File
class Config {
static load(path) {
var content = File.read(path)
return Json.parse(content)
}
static save(path, data) {
var content = Json.stringify(data, 2)
File.write(path, content)
}
static get(path, key) {
var config = Config.load(path)
return config[key]
}
static set(path, key, value) {
var config = {}
if (File.exists(path)) {
config = Config.load(path)
}
config[key] = value
Config.save(path, config)
}
}</code></pre>
<p>This module depends on <code>json</code> and <code>io</code>, demonstrating how pure-Wren modules compose functionality from other modules.</p>
<h2>Common Patterns</h2>
<h3>Static Utility Classes</h3>
<p>Most pure-Wren modules use static methods for stateless utilities:</p>
<pre><code>class StringUtils {
static trim(s) { ... }
static pad(s, width) { ... }
}</code></pre>
<h3>Factory Classes</h3>
<p>For stateful objects, use instance methods:</p>
<pre><code>class Builder {
construct new() {
_parts = []
}
add(part) {
_parts.add(part)
return this
}
build() { _parts.join("") }
}</code></pre>
<h3>Wrapping Async Operations</h3>
<p>Pure-Wren modules can wrap async operations from C-backed modules:</p>
<pre><code>import "http" for Http
class Api {
static get(endpoint) {
return Http.get("https://api.example.com" + endpoint)
}
}</code></pre>
<h2>Checklist</h2>
<ul>
<li>Created <code>src/module/&lt;name&gt;.wren</code> with author comment</li>
<li>Generated <code>.wren.inc</code> with util script</li>
<li>Added <code>#include</code> in modules.c</li>
<li>Added <code>MODULE</code>/<code>END_MODULE</code> block in modules.c</li>
<li>Built with <code>make clean && make build</code></li>
<li>Created tests in <code>test/&lt;name&gt;/</code></li>
<li>Created example in <code>example/&lt;name&gt;_demo.wren</code></li>
<li>Created documentation page</li>
<li>Ran <code>make sync-manual</code></li>
<li>All tests pass</li>
</ul>
<h2>Next Steps</h2>
<p>If your module requires native functionality not available through existing modules, see <a href="c-backed-module.html">C-Backed Modules</a>.</p>
{% endblock %}

View File

@ -0,0 +1,314 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "Writing Tests" %}
{% set breadcrumb = [{"url": "contributing/index.html", "title": "Contributing"}, {"title": "Writing Tests"}] %}
{% set prev_page = {"url": "contributing/async-patterns.html", "title": "Async Patterns"} %}
{% set next_page = {"url": "contributing/documentation.html", "title": "Documentation"} %}
{% block article %}
<h1>Writing Tests</h1>
<p>Wren-CLI uses inline annotations in test files to specify expected behavior. The test runner <code>util/test.py</code> parses these annotations and verifies the output.</p>
<h2>Test Directory Structure</h2>
<pre><code>test/
&lt;modulename&gt;/
&lt;feature&gt;.wren # Functional tests
error_&lt;scenario&gt;.wren # Error case tests (one runtime error each)</code></pre>
<p>Each module has its own directory under <code>test/</code>. Test files are named descriptively based on what they test.</p>
<h2>Running Tests</h2>
<pre><code># Build and run all tests
make tests
# Run tests for a specific module
python3 util/test.py json
# Run a specific test file (prefix match)
python3 util/test.py json/parse
# Run with debug binary
python3 util/test.py --suffix=_d</code></pre>
<h2>Test Annotations</h2>
<table>
<tr>
<th>Annotation</th>
<th>Purpose</th>
<th>Expected Exit Code</th>
</tr>
<tr>
<td><code>// expect: output</code></td>
<td>Assert stdout line (matched in order)</td>
<td>0</td>
</tr>
<tr>
<td><code>// expect error</code></td>
<td>Assert compile error on this line</td>
<td>65</td>
</tr>
<tr>
<td><code>// expect error line N</code></td>
<td>Assert compile error on line N</td>
<td>65</td>
</tr>
<tr>
<td><code>// expect runtime error: msg</code></td>
<td>Assert runtime error with exact message</td>
<td>70</td>
</tr>
<tr>
<td><code>// expect handled runtime error: msg</code></td>
<td>Assert caught runtime error</td>
<td>0</td>
</tr>
<tr>
<td><code>// stdin: text</code></td>
<td>Feed text to stdin (repeatable)</td>
<td>-</td>
</tr>
<tr>
<td><code>// skip: reason</code></td>
<td>Skip this test file</td>
<td>-</td>
</tr>
<tr>
<td><code>// nontest</code></td>
<td>Ignore this file</td>
<td>-</td>
</tr>
</table>
<h2>Basic Test Example</h2>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "json" for Json
var obj = {"name": "test", "value": 42}
var str = Json.stringify(obj)
var parsed = Json.parse(str)
System.print(parsed["name"]) // expect: test
System.print(parsed["value"]) // expect: 42</code></pre>
<p>Each <code>// expect:</code> annotation asserts that the corresponding line of output matches exactly.</p>
<h2>Multiple Expect Annotations</h2>
<p>Multiple expectations are matched in order:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "utils" for Utils
System.print(Utils.capitalize("hello")) // expect: Hello
System.print(Utils.capitalize("world")) // expect: World
System.print(Utils.capitalize("UPPER")) // expect: UPPER
System.print(Utils.capitalize("")) // expect:</code></pre>
<p>The empty <code>// expect:</code> matches an empty line.</p>
<h2>Runtime Error Tests</h2>
<p>For testing error conditions, create a separate file per error case:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "json" for Json
Json.parse("invalid json") // expect runtime error: Invalid JSON.</code></pre>
<div class="admonition warning">
<div class="admonition-title">Warning</div>
<p>Only one runtime error per test file. The test runner tracks a single expected error. Split multiple error cases into separate files.</p>
</div>
<h3>Error Line Matching</h3>
<p>The runtime error annotation must be on the line that calls the aborting code. The runner checks the stack trace for a <code>test/...</code> path and matches the line number.</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "io" for File
var content = File.read("/nonexistent/path") // expect runtime error: Cannot open file.</code></pre>
<h2>Compile Error Tests</h2>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
class Test {
foo( // expect error
}</code></pre>
<p>Or specify a different line number:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
// This is valid
var x = 1
class Broken {
// expect error line 7
foo(</code></pre>
<h2>Stdin Input</h2>
<p>Use <code>// stdin:</code> to provide input:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "io" for Stdin
// stdin: hello
// stdin: world
var line1 = Stdin.readLine()
var line2 = Stdin.readLine()
System.print(line1) // expect: hello
System.print(line2) // expect: world</code></pre>
<p>Multiple <code>// stdin:</code> lines are concatenated with newlines.</p>
<h2>Skipping Tests</h2>
<p>Use <code>// skip:</code> for tests that cannot run in all environments:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
// skip: Requires network access
import "http" for Http
var response = Http.get("https://example.com")
System.print(response.status) // expect: 200</code></pre>
<h2>Non-Test Files</h2>
<p>Use <code>// nontest</code> for helper files that should not be run as tests:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
// nontest
class TestHelper {
static setup() { ... }
}</code></pre>
<h2>Test File Organization</h2>
<h3>Feature Tests</h3>
<p>Test each feature in a dedicated file:</p>
<pre><code>test/json/
parse.wren # Basic parsing
parse_nested.wren # Nested objects/arrays
stringify.wren # JSON serialization
stringify_pretty.wren # Pretty printing
types.wren # Type handling</code></pre>
<h3>Error Tests</h3>
<p>One error per file, named with <code>error_</code> prefix:</p>
<pre><code>test/json/
error_invalid_syntax.wren
error_unexpected_eof.wren
error_invalid_escape.wren</code></pre>
<h2>Handled Runtime Error</h2>
<p>For testing error handling where errors are caught:</p>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "json" for Json
var result = Fiber.new {
Json.parse("bad")
}.try()
if (result.error) {
System.print("Caught error") // expect: Caught error
} // expect handled runtime error: Invalid JSON.</code></pre>
<h2>Test Timeout</h2>
<p>Each test file has a 15-second timeout. If a test hangs (e.g., waiting for input that never comes), it will be killed.</p>
<h2>Test Discovery</h2>
<p>The test runner discovers tests by:</p>
<ol>
<li>Walking the <code>test/</code> directory recursively</li>
<li>Filtering by <code>.wren</code> extension</li>
<li>Converting paths to relative paths from <code>test/</code></li>
<li>Checking if path starts with the filter argument</li>
</ol>
<p>This means:</p>
<ul>
<li><code>python3 util/test.py json</code> runs everything in <code>test/json/</code></li>
<li><code>python3 util/test.py io/file</code> runs everything in <code>test/io/file/</code></li>
<li>Subdirectories are supported for organizing large test suites</li>
</ul>
<h2>Example Test Suite</h2>
<pre><code>test/mymodule/
basic.wren
advanced.wren
edge_cases.wren
error_null_input.wren
error_invalid_type.wren
error_overflow.wren</code></pre>
<h3>basic.wren</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "mymodule" for MyClass
System.print(MyClass.process("hello")) // expect: HELLO
System.print(MyClass.process("world")) // expect: WORLD
System.print(MyClass.length("test")) // expect: 4</code></pre>
<h3>error_null_input.wren</h3>
<pre><code>// retoor &lt;retoor@molodetz.nl&gt;
import "mymodule" for MyClass
MyClass.process(null) // expect runtime error: Input cannot be null.</code></pre>
<h2>Debugging Failing Tests</h2>
<ol>
<li>Run the test manually: <code>bin/wren_cli test/mymodule/failing.wren</code></li>
<li>Check the actual output versus expected</li>
<li>Verify annotations are on correct lines</li>
<li>For runtime errors, ensure annotation is on the calling line</li>
</ol>
<h2>Best Practices</h2>
<ul>
<li>Test one concept per file when possible</li>
<li>Use descriptive file names</li>
<li>Always include the author comment</li>
<li>Test edge cases (empty strings, null, large values)</li>
<li>Test error conditions in separate files</li>
<li>Keep tests simple and focused</li>
</ul>
<h2>Next Steps</h2>
<p>After writing tests, add documentation in <a href="documentation.html">Documentation</a>.</p>
{% endblock %}

View File

@ -0,0 +1,203 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "First Script" %}
{% set breadcrumb = [{"url": "getting-started/index.html", "title": "Getting Started"}, {"title": "First Script"}] %}
{% set prev_page = {"url": "getting-started/installation.html", "title": "Installation"} %}
{% set next_page = {"url": "getting-started/repl.html", "title": "Using the REPL"} %}
{% block article %}
<h1>First Script</h1>
<p>This guide walks you through creating and running your first Wren script. You will learn the basics of printing output, using variables, and importing modules.</p>
<h2>Hello World</h2>
<p>Create a file named <code>hello.wren</code> with the following content:</p>
<pre><code>System.print("Hello, World!")</code></pre>
<p>Run it:</p>
<pre><code>bin/wren_cli hello.wren</code></pre>
<div class="example-output">Hello, World!</div>
<p>The <code>System.print</code> method outputs text to the console followed by a newline.</p>
<h2>Variables and Types</h2>
<p>Wren is dynamically typed. Use <code>var</code> to declare variables:</p>
<pre><code>var name = "Wren"
var version = 0.4
var features = ["fast", "small", "class-based"]
var active = true
System.print("Language: %(name)")
System.print("Version: %(version)")
System.print("Features: %(features)")
System.print("Active: %(active)")</code></pre>
<div class="example-output">Language: Wren
Version: 0.4
Features: [fast, small, class-based]
Active: true</div>
<p>The <code>%(expression)</code> syntax is string interpolation. It evaluates the expression and inserts the result into the string.</p>
<h2>Working with Lists and Maps</h2>
<pre><code>var numbers = [1, 2, 3, 4, 5]
System.print("Count: %(numbers.count)")
System.print("First: %(numbers[0])")
System.print("Last: %(numbers[-1])")
var person = {
"name": "Alice",
"age": 30,
"city": "Amsterdam"
}
System.print("Name: %(person["name"])")</code></pre>
<div class="example-output">Count: 5
First: 1
Last: 5
Name: Alice</div>
<h2>Control Flow</h2>
<pre><code>var score = 85
if (score >= 90) {
System.print("Grade: A")
} else if (score >= 80) {
System.print("Grade: B")
} else {
System.print("Grade: C")
}
for (i in 1..5) {
System.print("Count: %(i)")
}</code></pre>
<div class="example-output">Grade: B
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5</div>
<h2>Functions</h2>
<p>Functions in Wren are created using block syntax:</p>
<pre><code>var greet = Fn.new { |name|
return "Hello, %(name)!"
}
System.print(greet.call("World"))
var add = Fn.new { |a, b| a + b }
System.print(add.call(3, 4))</code></pre>
<div class="example-output">Hello, World!
7</div>
<h2>Classes</h2>
<pre><code>class Person {
construct new(name, age) {
_name = name
_age = age
}
name { _name }
age { _age }
greet() {
System.print("Hello, I'm %(_name)")
}
}
var alice = Person.new("Alice", 30)
alice.greet()
System.print("Age: %(alice.age)")</code></pre>
<div class="example-output">Hello, I'm Alice
Age: 30</div>
<h2>Using Modules</h2>
<p>Wren-CLI provides many built-in modules. Import them to use their functionality:</p>
<pre><code>import "io" for File
var content = File.read("hello.wren")
System.print("File contents:")
System.print(content)</code></pre>
<h3>Making HTTP Requests</h3>
<pre><code>import "http" for Http
import "json" for Json
var response = Http.get("https://httpbin.org/get")
System.print("Status: %(response.status)")
var data = Json.parse(response.body)
System.print("Origin: %(data["origin"])")</code></pre>
<h3>Working with JSON</h3>
<pre><code>import "json" for Json
var data = {
"name": "Wren",
"version": 0.4,
"features": ["fast", "small"]
}
var jsonString = Json.stringify(data)
System.print(jsonString)
var parsed = Json.parse(jsonString)
System.print("Name: %(parsed["name"])")</code></pre>
<h2>Error Handling</h2>
<p>Use <code>Fiber.try</code> to catch runtime errors:</p>
<pre><code>var fiber = Fiber.new {
var x = 1 / 0
}
var error = fiber.try()
if (error) {
System.print("Error: %(error)")
}</code></pre>
<h2>Script Arguments</h2>
<p>Access command-line arguments via <code>Process.arguments</code>:</p>
<pre><code>import "os" for Process
System.print("Script arguments:")
for (arg in Process.arguments) {
System.print(" %(arg)")
}</code></pre>
<p>Run with arguments:</p>
<pre><code>bin/wren_cli script.wren arg1 arg2 arg3</code></pre>
<h2>Exit Codes</h2>
<p>Wren-CLI uses these exit codes:</p>
<table>
<tr>
<th>Code</th>
<th>Meaning</th>
</tr>
<tr>
<td>0</td>
<td>Success</td>
</tr>
<tr>
<td>65</td>
<td>Compile error (syntax error)</td>
</tr>
<tr>
<td>70</td>
<td>Runtime error</td>
</tr>
</table>
<h2>Next Steps</h2>
<ul>
<li><a href="repl.html">Using the REPL</a> - Interactive experimentation</li>
<li><a href="../language/index.html">Language Reference</a> - Deep dive into Wren syntax</li>
<li><a href="../tutorials/index.html">Tutorials</a> - Build real applications</li>
</ul>
{% endblock %}

View File

@ -0,0 +1,104 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "Getting Started" %}
{% set breadcrumb = [{"title": "Getting Started"}] %}
{% set prev_page = {"url": "index.html", "title": "Home"} %}
{% set next_page = {"url": "getting-started/installation.html", "title": "Installation"} %}
{% block article %}
<h1>Getting Started</h1>
<p>Welcome to Wren-CLI, a command-line interface for the Wren programming language extended with powerful modules for networking, file I/O, databases, and more.</p>
<div class="toc">
<h4>In This Section</h4>
<ul>
<li><a href="installation.html">Installation</a> - Build from source on Linux, macOS, or FreeBSD</li>
<li><a href="first-script.html">First Script</a> - Write and run your first Wren program</li>
<li><a href="repl.html">Using the REPL</a> - Interactive experimentation</li>
</ul>
</div>
<h2>What is Wren?</h2>
<p>Wren is a small, fast, class-based scripting language. It has a familiar syntax, first-class functions, and a fiber-based concurrency model. Wren-CLI extends the core language with modules for:</p>
<ul>
<li>HTTP and WebSocket networking</li>
<li>File and directory operations</li>
<li>SQLite database access</li>
<li>JSON and regex processing</li>
<li>Template rendering (Jinja2-compatible)</li>
<li>Cryptographic operations</li>
<li>Process and signal handling</li>
</ul>
<h2>Quick Start</h2>
<p>If you want to dive right in, here is the fastest path to running your first script:</p>
<h3>1. Build</h3>
<pre><code>git clone https://github.com/wren-lang/wren-cli.git
cd wren-cli
cd projects/make && make</code></pre>
<h3>2. Create a Script</h3>
<p>Create a file named <code>hello.wren</code>:</p>
<pre><code>System.print("Hello, Wren!")</code></pre>
<h3>3. Run</h3>
<pre><code>bin/wren_cli hello.wren</code></pre>
<div class="example-output">Hello, Wren!</div>
<h2>Key Concepts</h2>
<p>Before diving deeper, understand these fundamental Wren concepts:</p>
<h3>Everything is an Object</h3>
<p>In Wren, everything is an object, including numbers, strings, and functions. Every object is an instance of a class.</p>
<pre><code>var name = "Wren"
System.print(name.count) // 4
System.print(name.bytes[0]) // 87 (ASCII for 'W')</code></pre>
<h3>Fibers for Concurrency</h3>
<p>Wren uses fibers (cooperative threads) for concurrency. The scheduler module manages async operations.</p>
<pre><code>import "timer" for Timer
Timer.sleep(1000) // Pauses for 1 second
System.print("Done waiting")</code></pre>
<h3>Module System</h3>
<p>Functionality is organized into modules that you import as needed:</p>
<pre><code>import "http" for Http
import "json" for Json
var response = Http.get("https://api.example.com/data")
var data = Json.parse(response.body)</code></pre>
<h2>System Requirements</h2>
<table>
<tr>
<th>Platform</th>
<th>Requirements</th>
</tr>
<tr>
<td>Linux</td>
<td>GCC, Make, OpenSSL development libraries</td>
</tr>
<tr>
<td>macOS</td>
<td>Xcode Command Line Tools, OpenSSL (via Homebrew)</td>
</tr>
<tr>
<td>FreeBSD</td>
<td>GCC or Clang, gmake, OpenSSL</td>
</tr>
</table>
<h2>Next Steps</h2>
<p>Continue with the following sections to learn more:</p>
<ul>
<li><a href="installation.html">Installation</a> - Detailed build instructions for all platforms</li>
<li><a href="first-script.html">First Script</a> - A more detailed walkthrough of your first program</li>
<li><a href="../language/index.html">Language Reference</a> - Learn the Wren language syntax</li>
<li><a href="../api/index.html">API Reference</a> - Explore all available modules</li>
</ul>
{% endblock %}

View File

@ -1,105 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Installation - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="index.html">Overview</a></li>
<li><a href="installation.html" class="active">Installation</a></li>
<li><a href="first-script.html">First Script</a></li>
<li><a href="repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">Getting Started</a>
<span class="separator">/</span>
<span>Installation</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "Installation" %}
{% set breadcrumb = [{"url": "getting-started/index.html", "title": "Getting Started"}, {"title": "Installation"}] %}
{% set prev_page = {"url": "getting-started/index.html", "title": "Overview"} %}
{% set next_page = {"url": "getting-started/first-script.html", "title": "First Script"} %}
{% block article %}
<h1>Installation</h1>
<p>Wren-CLI must be built from source. This page covers the build process for Linux, macOS, and FreeBSD.</p>
@ -250,14 +157,4 @@ make build</code></pre>
<h3>Test Failures</h3>
<p>Run a specific test module to isolate issues:</p>
<pre><code>python3 util/test.py json</code></pre>
</article>
<footer class="page-footer">
<a href="index.html" class="prev">Overview</a>
<a href="first-script.html" class="next">First Script</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -0,0 +1,151 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "Using the REPL" %}
{% set breadcrumb = [{"url": "getting-started/index.html", "title": "Getting Started"}, {"title": "Using the REPL"}] %}
{% set prev_page = {"url": "getting-started/first-script.html", "title": "First Script"} %}
{% set next_page = {"url": "language/index.html", "title": "Language Reference"} %}
{% block article %}
<h1>Using the REPL</h1>
<p>The REPL (Read-Eval-Print Loop) is an interactive environment for experimenting with Wren code. It is useful for testing ideas, exploring APIs, and learning the language.</p>
<h2>Starting the REPL</h2>
<p>Run <code>wren_cli</code> without arguments to start the REPL:</p>
<pre><code>bin/wren_cli</code></pre>
<p>You will see a prompt:</p>
<div class="example-output">> </div>
<h2>Basic Usage</h2>
<p>Type expressions and press Enter to evaluate them:</p>
<pre><code>> 1 + 2
3
> "hello".count
5
> [1, 2, 3].map { |x| x * 2 }
[2, 4, 6]</code></pre>
<h2>Multi-line Input</h2>
<p>The REPL automatically detects incomplete expressions and waits for more input:</p>
<pre><code>> class Person {
| construct new(name) {
| _name = name
| }
| name { _name }
| }
null
> var p = Person.new("Alice")
null
> p.name
Alice</code></pre>
<p>The <code>|</code> prompt indicates the REPL is waiting for more input.</p>
<h2>Importing Modules</h2>
<p>Modules can be imported in the REPL just like in scripts:</p>
<pre><code>> import "json" for Json
null
> Json.stringify({"name": "Wren"})
{"name":"Wren"}
> import "math" for Math
null
> Math.sqrt(16)
4</code></pre>
<h2>Variables Persist</h2>
<p>Variables defined in the REPL persist across lines:</p>
<pre><code>> var x = 10
null
> var y = 20
null
> x + y
30</code></pre>
<h2>Examining Values</h2>
<p>Use <code>System.print</code> for formatted output:</p>
<pre><code>> var data = {"name": "Wren", "version": 0.4}
null
> System.print(data)
{name: Wren, version: 0.4}</code></pre>
<p>Check the type of a value:</p>
<pre><code>> 42.type
Num
> "hello".type
String
> [1, 2, 3].type
List</code></pre>
<h2>Exploring Classes</h2>
<p>Examine what methods a class provides:</p>
<pre><code>> String
String
> "test".bytes
[116, 101, 115, 116]
> "test".codePoints.toList
[116, 101, 115, 116]</code></pre>
<h2>Error Handling</h2>
<p>Errors are displayed but do not crash the REPL:</p>
<pre><code>> 1 / 0
infinity
> "test"[10]
Subscript out of bounds.
> undefined_variable
[repl line 1] Error at 'undefined_variable': Variable is used but not defined.</code></pre>
<p>You can continue using the REPL after errors.</p>
<h2>REPL Tips</h2>
<h3>Quick Testing</h3>
<p>Use the REPL to quickly test regular expressions:</p>
<pre><code>> import "regex" for Regex
null
> Regex.new("\\d+").test("abc123")
true
> Regex.new("\\d+").match("abc123def").text
123</code></pre>
<h3>Exploring APIs</h3>
<p>Test module functionality before using it in scripts:</p>
<pre><code>> import "base64" for Base64
null
> Base64.encode("Hello, World!")
SGVsbG8sIFdvcmxkIQ==
> Base64.decode("SGVsbG8sIFdvcmxkIQ==")
Hello, World!</code></pre>
<h3>Date and Time</h3>
<pre><code>> import "datetime" for DateTime
null
> DateTime.now
2024-01-15T10:30:45
> DateTime.now.year
2024</code></pre>
<h2>Exiting the REPL</h2>
<p>Exit the REPL by pressing <code>Ctrl+D</code> (Unix) or <code>Ctrl+Z</code> followed by Enter (Windows).</p>
<h2>Limitations</h2>
<ul>
<li>No command history navigation (arrow keys)</li>
<li>No tab completion</li>
<li>No line editing beyond basic backspace</li>
<li>Cannot redefine classes once defined</li>
</ul>
<div class="admonition tip">
<div class="admonition-title">Tip</div>
<p>For complex experimentation, write code in a file and run it with <code>wren_cli script.wren</code>. The REPL is best for quick tests and exploration.</p>
</div>
<h2>Next Steps</h2>
<ul>
<li><a href="../language/index.html">Language Reference</a> - Learn Wren syntax in detail</li>
<li><a href="../api/index.html">API Reference</a> - Explore available modules</li>
<li><a href="../tutorials/index.html">Tutorials</a> - Build real applications</li>
</ul>
{% endblock %}

View File

@ -0,0 +1,312 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "Async Programming" %}
{% set breadcrumb = [{"url": "howto/index.html", "title": "How-To Guides"}, {"title": "Async Operations"}] %}
{% set prev_page = {"url": "howto/file-operations.html", "title": "File Operations"} %}
{% set next_page = {"url": "howto/error-handling.html", "title": "Error Handling"} %}
{% block article %}
<h1>Async Programming</h1>
<p>Wren-CLI provides <code>async</code> and <code>await</code> keywords for writing concurrent code. This guide covers the essential patterns for async programming.</p>
<h2>Create an Async Function</h2>
<pre><code>import "scheduler" for Scheduler, Future
var getValue = async { 42 }
var result = await getValue()
System.print(result) // 42</code></pre>
<h2>Async Functions with Parameters</h2>
<pre><code>import "scheduler" for Scheduler, Future
var double = async { |x| x * 2 }
var add = async { |a, b| a + b }
System.print(await double(21)) // 42
System.print(await add(3, 4)) // 7</code></pre>
<h2>Direct Calling vs .call()</h2>
<p>There are two ways to invoke async functions:</p>
<ul>
<li><code>await fn(args)</code> — Direct call, waits immediately (sequential)</li>
<li><code>fn.call(args)</code> — Returns Future, starts without waiting (concurrent)</li>
</ul>
<pre><code>import "scheduler" for Scheduler, Future
var slow = async { |n|
Timer.sleep(100)
return n
}
// SEQUENTIAL: Each call waits before the next starts
var a = await slow(1)
var b = await slow(2)
var c = await slow(3) // Total: ~300ms
// CONCURRENT: All calls start at once, then wait for results
var f1 = slow.call(1)
var f2 = slow.call(2)
var f3 = slow.call(3)
var r1 = await f1
var r2 = await f2
var r3 = await f3 // Total: ~100ms</code></pre>
<h2>Sequential HTTP Requests</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchJson = async { |url|
var response = Client.get(url)
return Json.parse(response["body"])
}
// Each request waits for the previous one
var user = await fetchJson("https://api.example.com/user/1")
var posts = await fetchJson("https://api.example.com/posts")
var comments = await fetchJson("https://api.example.com/comments")
System.print(user["name"])
System.print(posts.count)
System.print(comments.count)</code></pre>
<h2>Concurrent HTTP Requests</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchJson = async { |url|
var response = Client.get(url)
return Json.parse(response["body"])
}
// Start all requests at once
var f1 = fetchJson.call("https://api.example.com/user/1")
var f2 = fetchJson.call("https://api.example.com/posts")
var f3 = fetchJson.call("https://api.example.com/comments")
// Wait for results (requests run in parallel)
var user = await f1
var posts = await f2
var comments = await f3
System.print(user["name"])
System.print(posts.count)
System.print(comments.count)</code></pre>
<h2>Batch Processing</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
var urls = [
"https://api.example.com/1",
"https://api.example.com/2",
"https://api.example.com/3",
"https://api.example.com/4",
"https://api.example.com/5"
]
// Start all requests concurrently
var futures = []
for (url in urls) {
futures.add(async { Client.get(url) })
}
// Collect results
var responses = []
for (f in futures) {
responses.add(await f)
}
for (i in 0...urls.count) {
System.print("%(urls[i]): %(responses[i]["status"])")
}</code></pre>
<h2>Reusable Batch Fetcher</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "json" for Json
var fetchJson = async { |url|
var response = Client.get(url)
return Json.parse(response["body"])
}
class BatchFetcher {
static getAll(urls) {
var futures = []
for (url in urls) {
futures.add(fetchJson.call(url))
}
var results = []
for (f in futures) {
results.add(await f)
}
return results
}
}
var urls = [
"https://api.example.com/users",
"https://api.example.com/posts",
"https://api.example.com/comments"
]
var results = BatchFetcher.getAll(urls)
for (result in results) {
System.print(result)</code></pre>
<h2>Sleep/Delay</h2>
<pre><code>import "timer" for Timer
System.print("Starting...")
Timer.sleep(1000) // Wait 1 second
System.print("Done!")</code></pre>
<h2>Async with Error Handling</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "json" for Json
var safeFetch = async { |url|
var fiber = Fiber.new {
var response = Client.get(url)
return Json.parse(response["body"])
}
var result = fiber.try()
if (fiber.error) {
return {"error": fiber.error}
}
return {"data": result}
}
var result = await safeFetch("https://api.example.com/data")
if (result["error"]) {
System.print("Error: %(result["error"])")
} else {
System.print("Data: %(result["data"])")
}</code></pre>
<h2>Retry with Backoff</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "timer" for Timer
var fetchWithRetry = async { |url, maxRetries|
var attempt = 0
var delay = 1000
while (attempt < maxRetries) {
var fiber = Fiber.new { Client.get(url) }
var result = fiber.try()
if (!fiber.error && result["status"] == 200) {
return result
}
attempt = attempt + 1
System.print("Attempt %(attempt) failed, retrying...")
Timer.sleep(delay)
delay = delay * 2
}
Fiber.abort("All %(maxRetries) attempts failed")
}
var response = await fetchWithRetry("https://api.example.com/data", 3)
System.print("Success: %(response["status"])")</code></pre>
<h2>Polling Pattern</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "timer" for Timer
import "json" for Json
var pollUntilReady = async { |url, maxAttempts|
var attempts = 0
while (attempts < maxAttempts) {
attempts = attempts + 1
System.print("Checking status (attempt %(attempts))...")
var response = Client.get(url)
var data = Json.parse(response["body"])
if (data["status"] == "ready") {
return data
}
Timer.sleep(2000)
}
return null
}
var result = await pollUntilReady("https://api.example.com/job/123", 10)
if (result) {
System.print("Job completed: %(result)")
} else {
System.print("Timed out waiting for job")
}</code></pre>
<h2>Rate Limiting</h2>
<pre><code>import "web" for Client
import "scheduler" for Scheduler, Future
import "timer" for Timer
var rateLimitedFetch = async { |urls, delayMs|
var results = []
for (url in urls) {
var response = Client.get(url)
results.add(response)
Timer.sleep(delayMs)
}
return results
}
var urls = [
"https://api.example.com/1",
"https://api.example.com/2",
"https://api.example.com/3"
]
var responses = await rateLimitedFetch(urls, 500)
for (r in responses) {
System.print(r["status"])
}</code></pre>
<h2>Graceful Shutdown</h2>
<pre><code>import "signal" for Signal
import "timer" for Timer
var running = true
Signal.handle("SIGINT", Fn.new {
System.print("\nShutting down gracefully...")
running = false
})
System.print("Press Ctrl+C to stop")
while (running) {
System.print("Working...")
Timer.sleep(1000)
}
System.print("Cleanup complete, exiting.")</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Always import <code>Scheduler</code> and <code>Future</code> from the scheduler module when using <code>async</code> and <code>await</code>. The syntax requires these classes to be in scope.</p>
</div>
<div class="admonition tip">
<div class="admonition-title">See Also</div>
<p>For more details, see the <a href="../api/scheduler.html">Scheduler API reference</a> and the <a href="../api/web.html">Web module</a> for HTTP client examples.</p>
</div>
{% endblock %}

View File

@ -1,105 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Error Handling - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="index.html">How-To List</a></li>
<li><a href="http-requests.html">HTTP Requests</a></li>
<li><a href="json-parsing.html">JSON Parsing</a></li>
<li><a href="regex-patterns.html">Regex Patterns</a></li>
<li><a href="file-operations.html">File Operations</a></li>
<li><a href="async-operations.html">Async Operations</a></li>
<li><a href="error-handling.html" class="active">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">How-To Guides</a>
<span class="separator">/</span>
<span>Error Handling</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "Error Handling" %}
{% set breadcrumb = [{"url": "howto/index.html", "title": "How-To Guides"}, {"title": "Error Handling"}] %}
{% set prev_page = {"url": "howto/async-operations.html", "title": "Async Operations"} %}
{% set next_page = {"url": "contributing/index.html", "title": "Contributing"} %}
{% block article %}
<h1>Error Handling</h1>
<p>Wren uses fibers for error handling. The <code>fiber.try()</code> method catches errors without crashing your program.</p>
@ -468,14 +375,4 @@ var result = retry.call(Fn.new {
<div class="admonition-title">See Also</div>
<p>For more on fibers, see the <a href="../language/fibers.html">Fibers language guide</a>.</p>
</div>
</article>
<footer class="page-footer">
<a href="async-operations.html" class="prev">Async Operations</a>
<a href="../index.html" class="next">Home</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,105 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Operations - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="index.html">How-To List</a></li>
<li><a href="http-requests.html">HTTP Requests</a></li>
<li><a href="json-parsing.html">JSON Parsing</a></li>
<li><a href="regex-patterns.html">Regex Patterns</a></li>
<li><a href="file-operations.html" class="active">File Operations</a></li>
<li><a href="async-operations.html">Async Operations</a></li>
<li><a href="error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">How-To Guides</a>
<span class="separator">/</span>
<span>File Operations</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "File Operations" %}
{% set breadcrumb = [{"url": "howto/index.html", "title": "How-To Guides"}, {"title": "File Operations"}] %}
{% set prev_page = {"url": "howto/regex-patterns.html", "title": "Regex Patterns"} %}
{% set next_page = {"url": "howto/async-operations.html", "title": "Async Operations"} %}
{% block article %}
<h1>File Operations</h1>
<h2>Read Entire File</h2>
@ -396,14 +303,4 @@ System.print("Total size: %(size) bytes")</code></pre>
<div class="admonition-title">See Also</div>
<p>For full API documentation, see the <a href="../api/io.html">IO module reference</a>. For object-oriented path manipulation with glob, walk, and tree operations, see the <a href="../api/pathlib.html">pathlib module reference</a>.</p>
</div>
</article>
<footer class="page-footer">
<a href="regex-patterns.html" class="prev">Regex Patterns</a>
<a href="async-operations.html" class="next">Async Operations</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,105 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Making HTTP Requests - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="index.html">How-To List</a></li>
<li><a href="http-requests.html" class="active">HTTP Requests</a></li>
<li><a href="json-parsing.html">JSON Parsing</a></li>
<li><a href="regex-patterns.html">Regex Patterns</a></li>
<li><a href="file-operations.html">File Operations</a></li>
<li><a href="async-operations.html">Async Operations</a></li>
<li><a href="error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">How-To Guides</a>
<span class="separator">/</span>
<span>HTTP Requests</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "Making HTTP Requests" %}
{% set breadcrumb = [{"url": "howto/index.html", "title": "How-To Guides"}, {"title": "HTTP Requests"}] %}
{% set prev_page = {"url": "howto/index.html", "title": "How-To List"} %}
{% set next_page = {"url": "howto/json-parsing.html", "title": "JSON Parsing"} %}
{% block article %}
<h1>Making HTTP Requests</h1>
<h2>Basic GET Request</h2>
@ -324,14 +231,4 @@ if (response) {
<div class="admonition-title">See Also</div>
<p>For a complete API client example, see the <a href="../tutorials/http-client.html">HTTP Client Tutorial</a>. For full API documentation, see the <a href="../api/http.html">HTTP module reference</a>.</p>
</div>
</article>
<footer class="page-footer">
<a href="index.html" class="prev">How-To List</a>
<a href="json-parsing.html" class="next">JSON Parsing</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -0,0 +1,110 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "How-To Guides" %}
{% set breadcrumb = [{"title": "How-To Guides"}] %}
{% set prev_page = {"url": "tutorials/web-server.html", "title": "Web Server"} %}
{% set next_page = {"url": "howto/http-requests.html", "title": "HTTP Requests"} %}
{% block article %}
<h1>How-To Guides</h1>
<p>Quick, focused guides that show you how to accomplish specific tasks. Each guide provides working code examples you can copy and adapt for your projects.</p>
<div class="card-grid">
<div class="card">
<h3><a href="http-requests.html">Making HTTP Requests</a></h3>
<p>GET, POST, PUT, DELETE requests with headers, authentication, and error handling.</p>
<div class="card-meta">
<span class="tag">http</span>
</div>
</div>
<div class="card">
<h3><a href="json-parsing.html">Working with JSON</a></h3>
<p>Parse JSON strings, access nested data, create JSON output, and handle errors.</p>
<div class="card-meta">
<span class="tag">json</span>
</div>
</div>
<div class="card">
<h3><a href="regex-patterns.html">Using Regular Expressions</a></h3>
<p>Match, search, replace, and split text with regex patterns.</p>
<div class="card-meta">
<span class="tag">regex</span>
</div>
</div>
<div class="card">
<h3><a href="file-operations.html">File Operations</a></h3>
<p>Read, write, copy, and delete files. Work with directories and paths.</p>
<div class="card-meta">
<span class="tag">io</span>
</div>
</div>
<div class="card">
<h3><a href="async-operations.html">Async Programming</a></h3>
<p>Use fibers for concurrent operations, parallel requests, and timeouts.</p>
<div class="card-meta">
<span class="tag">fibers</span>
<span class="tag">scheduler</span>
</div>
</div>
<div class="card">
<h3><a href="error-handling.html">Error Handling</a></h3>
<p>Catch errors with fibers, validate input, and handle edge cases gracefully.</p>
<div class="card-meta">
<span class="tag">fibers</span>
</div>
</div>
</div>
<h2>How-To vs Tutorials</h2>
<p><strong>Tutorials</strong> are learning-oriented. They walk you through building complete applications step by step, introducing concepts gradually.</p>
<p><strong>How-To Guides</strong> are goal-oriented. They assume you know the basics and need to accomplish a specific task quickly. Each guide focuses on one topic with copy-paste examples.</p>
<h2>Quick Reference</h2>
<table>
<tr>
<th>Task</th>
<th>Guide</th>
<th>Key Functions</th>
</tr>
<tr>
<td>Fetch data from API</td>
<td><a href="http-requests.html">HTTP Requests</a></td>
<td><code>Http.get()</code>, <code>response.json</code></td>
</tr>
<tr>
<td>Parse JSON string</td>
<td><a href="json-parsing.html">JSON Parsing</a></td>
<td><code>Json.parse()</code></td>
</tr>
<tr>
<td>Validate email format</td>
<td><a href="regex-patterns.html">Regex Patterns</a></td>
<td><code>Regex.new().test()</code></td>
</tr>
<tr>
<td>Read file contents</td>
<td><a href="file-operations.html">File Operations</a></td>
<td><code>File.read()</code></td>
</tr>
<tr>
<td>Run tasks in parallel</td>
<td><a href="async-operations.html">Async Operations</a></td>
<td><code>Fiber.new { }</code></td>
</tr>
<tr>
<td>Handle runtime errors</td>
<td><a href="error-handling.html">Error Handling</a></td>
<td><code>fiber.try()</code></td>
</tr>
</table>
{% endblock %}

View File

@ -0,0 +1,243 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
{% set page_title = "Working with JSON" %}
{% set breadcrumb = [{"url": "howto/index.html", "title": "How-To Guides"}, {"title": "JSON Parsing"}] %}
{% set prev_page = {"url": "howto/http-requests.html", "title": "HTTP Requests"} %}
{% set next_page = {"url": "howto/regex-patterns.html", "title": "Regex Patterns"} %}
{% block article %}
<h1>Working with JSON</h1>
<h2>Parse JSON String</h2>
<pre><code>import "json" for Json
var jsonStr = '{"name": "Alice", "age": 30}'
var data = Json.parse(jsonStr)
System.print(data["name"]) // Alice
System.print(data["age"]) // 30</code></pre>
<h2>Parse JSON Array</h2>
<pre><code>import "json" for Json
var jsonStr = '[1, 2, 3, "four", true, null]'
var items = Json.parse(jsonStr)
for (item in items) {
System.print(item)
}</code></pre>
<h2>Access Nested Objects</h2>
<pre><code>import "json" for Json
var jsonStr = '{"user": {"name": "Bob", "address": {"city": "NYC"}}}'
var data = Json.parse(jsonStr)
System.print(data["user"]["name"]) // Bob
System.print(data["user"]["address"]["city"]) // NYC</code></pre>
<h2>Convert Wren Object to JSON</h2>
<pre><code>import "json" for Json
var data = {
"name": "Charlie",
"age": 25,
"active": true
}
var jsonStr = Json.stringify(data)
System.print(jsonStr) // {"name":"Charlie","age":25,"active":true}</code></pre>
<h2>Pretty Print JSON</h2>
<pre><code>import "json" for Json
var data = {
"users": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25}
]
}
var pretty = Json.stringify(data, 2)
System.print(pretty)</code></pre>
<p>Output:</p>
<pre><code>{
"users": [
{
"name": "Alice",
"age": 30
},
{
"name": "Bob",
"age": 25
}
]
}</code></pre>
<h2>Check if Key Exists</h2>
<pre><code>import "json" for Json
var data = Json.parse('{"name": "Alice"}')
if (data.containsKey("name")) {
System.print("Name: %(data["name"])")
}
if (!data.containsKey("age")) {
System.print("Age not specified")
}</code></pre>
<h2>Provide Default Values</h2>
<pre><code>import "json" for Json
var data = Json.parse('{"name": "Alice"}')
var name = data["name"]
var age = data.containsKey("age") ? data["age"] : 0
var city = data.containsKey("city") ? data["city"] : "Unknown"
System.print("%(name), %(age), %(city)")</code></pre>
<h2>Iterate Over Object Keys</h2>
<pre><code>import "json" for Json
var data = Json.parse('{"a": 1, "b": 2, "c": 3}')
for (key in data.keys) {
System.print("%(key): %(data[key])")
}</code></pre>
<h2>Iterate Over Array</h2>
<pre><code>import "json" for Json
var users = Json.parse('[{"name": "Alice"}, {"name": "Bob"}]')
for (i in 0...users.count) {
System.print("%(i + 1). %(users[i]["name"])")
}</code></pre>
<h2>Modify JSON Data</h2>
<pre><code>import "json" for Json
var data = Json.parse('{"name": "Alice", "age": 30}')
data["age"] = 31
data["email"] = "alice@example.com"
data.remove("name")
System.print(Json.stringify(data))</code></pre>
<h2>Parse JSON from File</h2>
<pre><code>import "json" for Json
import "io" for File
var content = File.read("config.json")
var config = Json.parse(content)
System.print(config["setting"])</code></pre>
<h2>Write JSON to File</h2>
<pre><code>import "json" for Json
import "io" for File
var data = {
"database": "myapp.db",
"port": 8080,
"debug": true
}
File.write("config.json", Json.stringify(data, 2))</code></pre>
<h2>Handle Parse Errors</h2>
<pre><code>import "json" for Json
var jsonStr = "invalid json {"
var fiber = Fiber.new { Json.parse(jsonStr) }
var result = fiber.try()
if (fiber.error) {
System.print("Parse error: %(fiber.error)")
} else {
System.print(result)
}</code></pre>
<h2>Work with Null Values</h2>
<pre><code>import "json" for Json
var data = Json.parse('{"name": "Alice", "address": null}')
if (data["address"] == null) {
System.print("No address provided")
}
var output = {"value": null}
System.print(Json.stringify(output)) // {"value":null}</code></pre>
<h2>Build JSON Array Dynamically</h2>
<pre><code>import "json" for Json
var users = []
users.add({"name": "Alice", "role": "admin"})
users.add({"name": "Bob", "role": "user"})
users.add({"name": "Charlie", "role": "user"})
System.print(Json.stringify(users, 2))</code></pre>
<h2>Filter JSON Array</h2>
<pre><code>import "json" for Json
var users = Json.parse('[
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 17},
{"name": "Charlie", "age": 25}
]')
var adults = []
for (user in users) {
if (user["age"] >= 18) {
adults.add(user)
}
}
System.print("Adults: %(Json.stringify(adults))")</code></pre>
<h2>Transform JSON Data</h2>
<pre><code>import "json" for Json
var users = Json.parse('[
{"firstName": "Alice", "lastName": "Smith"},
{"firstName": "Bob", "lastName": "Jones"}
]')
var names = []
for (user in users) {
names.add("%(user["firstName"]) %(user["lastName"])")
}
System.print(names.join(", "))</code></pre>
<h2>Merge JSON Objects</h2>
<pre><code>import "json" for Json
var defaults = {"theme": "light", "language": "en", "timeout": 30}
var userPrefs = {"theme": "dark"}
var config = {}
for (key in defaults.keys) {
config[key] = defaults[key]
}
for (key in userPrefs.keys) {
config[key] = userPrefs[key]
}
System.print(Json.stringify(config, 2))</code></pre>
<div class="admonition tip">
<div class="admonition-title">See Also</div>
<p>For full API documentation, see the <a href="../api/json.html">JSON module reference</a>.</p>
</div>
{% endblock %}

View File

@ -1,105 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Using Regular Expressions - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="../language/index.html">Syntax Overview</a></li>
<li><a href="../language/classes.html">Classes</a></li>
<li><a href="../language/methods.html">Methods</a></li>
<li><a href="../language/control-flow.html">Control Flow</a></li>
<li><a href="../language/fibers.html">Fibers</a></li>
<li><a href="../language/modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Template Rendering</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="index.html">How-To List</a></li>
<li><a href="http-requests.html">HTTP Requests</a></li>
<li><a href="json-parsing.html">JSON Parsing</a></li>
<li><a href="regex-patterns.html" class="active">Regex Patterns</a></li>
<li><a href="file-operations.html">File Operations</a></li>
<li><a href="async-operations.html">Async Operations</a></li>
<li><a href="error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">How-To Guides</a>
<span class="separator">/</span>
<span>Regex Patterns</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "Using Regular Expressions" %}
{% set breadcrumb = [{"url": "howto/index.html", "title": "How-To Guides"}, {"title": "Regex Patterns"}] %}
{% set prev_page = {"url": "howto/json-parsing.html", "title": "JSON Parsing"} %}
{% set next_page = {"url": "howto/file-operations.html", "title": "File Operations"} %}
{% block article %}
<h1>Using Regular Expressions</h1>
<h2>Test if String Matches Pattern</h2>
@ -327,14 +234,4 @@ System.print(ipPattern.test("10.0.0.255")) // true</code></pre>
<div class="admonition-title">See Also</div>
<p>For full API documentation, see the <a href="../api/regex.html">Regex module reference</a>.</p>
</div>
</article>
<footer class="page-footer">
<a href="json-parsing.html" class="prev">JSON Parsing</a>
<a href="file-operations.html" class="next">File Operations</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -0,0 +1,93 @@
{# retoor <retoor@molodetz.nl> #}
{% extends 'home.html' %}
{% set page_title = "Wren-CLI Manual" %}
{% set next_page = {"url": "getting-started/index.html", "title": "Getting Started"} %}
{% block article %}
<h2>What is Wren-CLI?</h2>
<p>Wren-CLI is a command-line interface for the <a href="https://wren.io">Wren programming language</a>, extended with powerful modules for networking, file I/O, databases, and more. It provides an async event loop powered by libuv, making it suitable for building servers, automation scripts, and command-line tools.</p>
<div class="card-grid">
<div class="card">
<h3><a href="getting-started/index.html">Getting Started</a></h3>
<p>Install Wren-CLI, write your first script, and learn the basics of the language.</p>
</div>
<div class="card">
<h3><a href="language/index.html">Language Reference</a></h3>
<p>Learn about classes, methods, control flow, fibers, and the module system.</p>
</div>
<div class="card">
<h3><a href="api/index.html">API Reference</a></h3>
<p>Complete documentation for all 32 built-in modules including HTTP, WebSocket, and SQLite.</p>
</div>
<div class="card">
<h3><a href="tutorials/index.html">Tutorials</a></h3>
<p>Step-by-step guides for building real applications with Wren-CLI.</p>
</div>
</div>
<h2>Available Modules</h2>
<p>Wren-CLI provides a rich set of modules for common programming tasks:</p>
<h3>Networking</h3>
<div class="module-grid">
<a href="api/http.html" class="module-card">http</a>
<a href="api/websocket.html" class="module-card">websocket</a>
<a href="api/tls.html" class="module-card">tls</a>
<a href="api/net.html" class="module-card">net</a>
<a href="api/udp.html" class="module-card">udp</a>
<a href="api/dns.html" class="module-card">dns</a>
</div>
<h3>Data Processing</h3>
<div class="module-grid">
<a href="api/json.html" class="module-card">json</a>
<a href="api/base64.html" class="module-card">base64</a>
<a href="api/regex.html" class="module-card">regex</a>
<a href="api/jinja.html" class="module-card">jinja</a>
<a href="api/crypto.html" class="module-card">crypto</a>
</div>
<h3>System</h3>
<div class="module-grid">
<a href="api/os.html" class="module-card">os</a>
<a href="api/env.html" class="module-card">env</a>
<a href="api/signal.html" class="module-card">signal</a>
<a href="api/subprocess.html" class="module-card">subprocess</a>
<a href="api/io.html" class="module-card">io</a>
<a href="api/pathlib.html" class="module-card">pathlib</a>
<a href="api/sysinfo.html" class="module-card">sysinfo</a>
<a href="api/fswatch.html" class="module-card">fswatch</a>
</div>
<h3>Data & Time</h3>
<div class="module-grid">
<a href="api/sqlite.html" class="module-card">sqlite</a>
<a href="api/datetime.html" class="module-card">datetime</a>
<a href="api/timer.html" class="module-card">timer</a>
<a href="api/math.html" class="module-card">math</a>
<a href="api/scheduler.html" class="module-card">scheduler</a>
</div>
<h2>Quick Example</h2>
<pre><code>import "http" for Http
import "json" for Json
var response = Http.get("https://api.github.com/users/wren-lang")
var data = Json.parse(response.body)
System.print("User: %(data["login"])")
System.print("Repos: %(data["public_repos"])")</code></pre>
<h2>Features</h2>
<ul>
<li><strong>Async I/O</strong> - Non-blocking operations powered by libuv</li>
<li><strong>HTTP/HTTPS</strong> - Full HTTP client with TLS support</li>
<li><strong>WebSocket</strong> - Client and server WebSocket support</li>
<li><strong>SQLite</strong> - Embedded database for persistent storage</li>
<li><strong>Templates</strong> - Jinja2-compatible template engine</li>
<li><strong>Regex</strong> - Full regular expression support</li>
<li><strong>Cross-platform</strong> - Runs on Linux, macOS, and FreeBSD</li>
</ul>
{% endblock %}

View File

@ -1,105 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Classes - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="index.html">Syntax Overview</a></li>
<li><a href="classes.html" class="active">Classes</a></li>
<li><a href="methods.html">Methods</a></li>
<li><a href="control-flow.html">Control Flow</a></li>
<li><a href="fibers.html">Fibers</a></li>
<li><a href="modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">Language</a>
<span class="separator">/</span>
<span>Classes</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "Classes" %}
{% set breadcrumb = [{"url": "language/index.html", "title": "Language"}, {"title": "Classes"}] %}
{% set prev_page = {"url": "language/index.html", "title": "Syntax Overview"} %}
{% set next_page = {"url": "language/methods.html", "title": "Methods"} %}
{% block article %}
<h1>Classes</h1>
<p>Wren is a class-based object-oriented language. Everything in Wren is an object, and every object is an instance of a class.</p>
@ -412,14 +319,4 @@ for (shape in shapes) {
shape.describe()
System.print("")
}</code></pre>
</article>
<footer class="page-footer">
<a href="index.html" class="prev">Syntax Overview</a>
<a href="methods.html" class="next">Methods</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,105 +1,12 @@
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Control Flow - Wren-CLI Manual</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<button class="mobile-menu-toggle">Menu</button>
<div class="container">
<aside class="sidebar">
<div class="sidebar-header">
<h1><a href="../index.html">Wren-CLI</a></h1>
<div class="version">v0.4.0</div>
</div>
<nav class="sidebar-nav">
<div class="section">
<span class="section-title">Getting Started</span>
<ul>
<li><a href="../getting-started/index.html">Overview</a></li>
<li><a href="../getting-started/installation.html">Installation</a></li>
<li><a href="../getting-started/first-script.html">First Script</a></li>
<li><a href="../getting-started/repl.html">Using the REPL</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Language</span>
<ul>
<li><a href="index.html">Syntax Overview</a></li>
<li><a href="classes.html">Classes</a></li>
<li><a href="methods.html">Methods</a></li>
<li><a href="control-flow.html" class="active">Control Flow</a></li>
<li><a href="fibers.html">Fibers</a></li>
<li><a href="modules.html">Modules</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">API Reference</span>
<ul>
<li><a href="../api/index.html">Overview</a></li>
<li><a href="../api/string.html">String</a></li>
<li><a href="../api/number.html">Num</a></li>
<li><a href="../api/http.html">http</a></li>
<li><a href="../api/websocket.html">websocket</a></li>
<li><a href="../api/tls.html">tls</a></li>
<li><a href="../api/net.html">net</a></li>
<li><a href="../api/dns.html">dns</a></li>
<li><a href="../api/json.html">json</a></li>
<li><a href="../api/base64.html">base64</a></li>
<li><a href="../api/regex.html">regex</a></li>
<li><a href="../api/jinja.html">jinja</a></li>
<li><a href="../api/crypto.html">crypto</a></li>
<li><a href="../api/os.html">os</a></li>
<li><a href="../api/env.html">env</a></li>
<li><a href="../api/signal.html">signal</a></li>
<li><a href="../api/subprocess.html">subprocess</a></li>
<li><a href="../api/sqlite.html">sqlite</a></li>
<li><a href="../api/datetime.html">datetime</a></li>
<li><a href="../api/timer.html">timer</a></li>
<li><a href="../api/io.html">io</a></li>
<li><a href="../api/pathlib.html">pathlib</a></li>
<li><a href="../api/scheduler.html">scheduler</a></li>
<li><a href="../api/math.html">math</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">Tutorials</span>
<ul>
<li><a href="../tutorials/index.html">Tutorial List</a></li>
<li><a href="../tutorials/http-client.html">HTTP Client</a></li>
<li><a href="../tutorials/websocket-chat.html">WebSocket Chat</a></li>
<li><a href="../tutorials/database-app.html">Database App</a></li>
<li><a href="../tutorials/template-rendering.html">Templates</a></li>
<li><a href="../tutorials/cli-tool.html">CLI Tool</a></li>
</ul>
</div>
<div class="section">
<span class="section-title">How-To Guides</span>
<ul>
<li><a href="../howto/index.html">How-To List</a></li>
<li><a href="../howto/http-requests.html">HTTP Requests</a></li>
<li><a href="../howto/json-parsing.html">JSON Parsing</a></li>
<li><a href="../howto/regex-patterns.html">Regex Patterns</a></li>
<li><a href="../howto/file-operations.html">File Operations</a></li>
<li><a href="../howto/async-operations.html">Async Operations</a></li>
<li><a href="../howto/error-handling.html">Error Handling</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<nav class="breadcrumb">
<a href="../index.html">Home</a>
<span class="separator">/</span>
<a href="index.html">Language</a>
<span class="separator">/</span>
<span>Control Flow</span>
</nav>
{# retoor <retoor@molodetz.nl> #}
{% extends 'page.html' %}
<article>
{% set page_title = "Control Flow" %}
{% set breadcrumb = [{"url": "language/index.html", "title": "Language"}, {"title": "Control Flow"}] %}
{% set prev_page = {"url": "language/methods.html", "title": "Methods"} %}
{% set next_page = {"url": "language/fibers.html", "title": "Fibers"} %}
{% block article %}
<h1>Control Flow</h1>
<p>Wren provides standard control flow constructs for conditionals, loops, and early exit.</p>
@ -322,14 +229,4 @@ System.print(sum) // 15</code></pre>
var evens = numbers.where { |n| n % 2 == 0 }.toList
var squared = numbers.map { |n| n * n }.toList
var sum = numbers.reduce(0) { |acc, n| acc + n }</code></pre>
</article>
<footer class="page-footer">
<a href="methods.html" class="prev">Methods</a>
<a href="fibers.html" class="next">Fibers</a>
</footer>
</main>
</div>
<script src="../js/main.js"></script>
</body>
</html>
{% endblock %}

Some files were not shown because too many files have changed in this diff Show More