Compare commits
7 Commits
e4a94d576b
...
fe2f087d9f
| Author | SHA1 | Date | |
|---|---|---|---|
| fe2f087d9f | |||
| a59dbe1d55 | |||
| 1ee28a1644 | |||
| a830238d11 | |||
| 90618f2336 | |||
| 4de99d477f | |||
| 735596fdf6 |
30
Makefile
30
Makefile
@ -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
|
||||
|
||||
39
README.md
39
README.md
@ -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
146
apps/generator.wren
vendored
Executable 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
144
apps/generator2.wren
vendored
Executable 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
2
apps/merge_all_wren.wren
vendored
Normal file → Executable 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
2
apps/merge_docs.wren
vendored
Normal file → Executable file
@ -1,3 +1,5 @@
|
||||
#!/usr/local/bin/wren
|
||||
|
||||
import "pathlib" for Path
|
||||
|
||||
var startTime = System.clock
|
||||
|
||||
107
apps/minitut.md
Normal file
107
apps/minitut.md
Normal 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
98
apps/phonebook.wren
vendored
Executable 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()
|
||||
90
deps/wren/src/vm/wren_compiler.c
vendored
90
deps/wren/src/vm/wren_compiler.c
vendored
@ -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]`.
|
||||
|
||||
1
deps/wren/src/vm/wren_vm.c
vendored
1
deps/wren/src/vm/wren_vm.c
vendored
@ -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.
|
||||
|
||||
17
deps/wren/src/vm/wren_vm.h
vendored
17
deps/wren/src/vm/wren_vm.h
vendored
@ -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.
|
||||
|
||||
243
example/await_demo.wren
vendored
243
example/await_demo.wren
vendored
@ -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
117
example/faker_demo.wren
vendored
Normal 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
54
example/fswatch_demo.wren
vendored
Normal 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)")
|
||||
10
example/openrouter_demo.wren
vendored
10
example/openrouter_demo.wren
vendored
@ -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
36
example/os_demo.wren
vendored
Normal 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
71
example/pexpect_demo.wren
vendored
Normal 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 ===")
|
||||
34
example/regex_demo.wren
vendored
34
example/regex_demo.wren
vendored
@ -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
58
example/subprocess_async_demo.wren
vendored
Normal 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
86
example/subprocess_concurrent_demo.wren
vendored
Normal 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
74
example/subprocess_fiber_demo.wren
vendored
Normal 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
65
example/sysinfo_demo.wren
vendored
Normal 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
60
example/udp_demo.wren
vendored
Normal 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
105
example/yaml_demo.wren
vendored
Normal 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])")
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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("<h1>Hello World</h1>")
|
||||
})
|
||||
|
||||
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") && !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>
|
||||
@ -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;
|
||||
}
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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();
|
||||
});
|
||||
})();
|
||||
@ -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>
|
||||
172
manual_src/data/navigation.yaml
Normal file
172
manual_src/data/navigation.yaml
Normal 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"
|
||||
7
manual_src/data/site.yaml
Normal file
7
manual_src/data/site.yaml
Normal 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"
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
814
manual_src/pages/api/faker.html
Normal file
814
manual_src/pages/api/faker.html
Normal 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 & Random</a></li>
|
||||
<li><a href="#format-helpers">Format Helpers</a></li>
|
||||
<li><a href="#person">Person & Names</a></li>
|
||||
<li><a href="#address">Address & Location</a></li>
|
||||
<li><a href="#internet">Internet & Network</a></li>
|
||||
<li><a href="#datetime">Date & Time</a></li>
|
||||
<li><a href="#text">Text & Lorem</a></li>
|
||||
<li><a href="#colors">Colors</a></li>
|
||||
<li><a href="#company">Company & Job</a></li>
|
||||
<li><a href="#commerce">Commerce & Products</a></li>
|
||||
<li><a href="#banking">Banking & Finance</a></li>
|
||||
<li><a href="#files">Files & 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 & 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>) → <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>) → <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>) → <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>) → <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>) → <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>() → <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>) → <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>) → <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>) → <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 & Names</h2>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Faker.firstName</span>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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 & Location</h2>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Faker.address</span>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>) → <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>() → <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>) → <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 & Network</h2>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Faker.email</span>() → <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>() → <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>() → <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>() → <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>) → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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 & Time</h2>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Faker.date</span>() → <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>() → <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>() → <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>() → <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>) → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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 & Lorem</h2>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Faker.word</span>() → <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>) → <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>() → <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>) → <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>() → <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>) → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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 & Job</h2>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Faker.company</span>() → <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>() → <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>() → <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>() → <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 & Products</h2>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Faker.product</span>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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 & Finance</h2>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Faker.iban</span>() → <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>() → <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>() → <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 & Hashes</h2>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Faker.fileName</span>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>() → <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>) → <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>() → <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>) → <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>) → <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>() → <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>() → <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>() → <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 %}
|
||||
255
manual_src/pages/api/fswatch.html
Normal file
255
manual_src/pages/api/fswatch.html
Normal 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>) → <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> → <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> → <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> → <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> → <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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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>) → <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> → <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> → <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> → <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> → <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> → <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> → <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> → <span class="type">Num</span>
|
||||
</div>
|
||||
<p>The file permission mode.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">inode</span> → <span class="type">Num</span>
|
||||
</div>
|
||||
<p>The inode number.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">device</span> → <span class="type">Num</span>
|
||||
</div>
|
||||
<p>The device ID containing the file.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">linkCount</span> → <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> → <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> → <span class="type">Num</span>
|
||||
</div>
|
||||
<p>The group ID of the file.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">blockSize</span> → <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> → <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 %}
|
||||
@ -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>) → <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>) → <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>) → <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>{% raw %}
|
||||
This {{ will not }} be processed.
|
||||
{% Neither will this %}
|
||||
{% endraw %}</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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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>() → <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>() → <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>() → <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>() → <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 %}
|
||||
@ -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 %}
|
||||
255
manual_src/pages/api/scheduler.html
Normal file
255
manual_src/pages/api/scheduler.html
Normal 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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
90
manual_src/pages/api/subprocess.html
Normal file
90
manual_src/pages/api/subprocess.html
Normal 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 %}
|
||||
244
manual_src/pages/api/sysinfo.html
Normal file
244
manual_src/pages/api/sysinfo.html
Normal 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> → <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> → <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> → <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> → <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> → <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> → <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> → <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> → <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> → <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> → <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> → <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 %}
|
||||
@ -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 %}
|
||||
227
manual_src/pages/api/timer.html
Normal file
227
manual_src/pages/api/timer.html
Normal 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>) → <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> → <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 %}
|
||||
@ -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 %}
|
||||
318
manual_src/pages/api/udp.html
Normal file
318
manual_src/pages/api/udp.html
Normal 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>() → <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>() → <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> → <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> → <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> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>The data contained in the message.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">address</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>The IP address of the sender.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">port</span> → <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 %}
|
||||
127
manual_src/pages/api/uuid.html
Normal file
127
manual_src/pages/api/uuid.html
Normal 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 %}
|
||||
@ -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 %}
|
||||
999
manual_src/pages/api/web.html
Normal file
999
manual_src/pages/api/web.html
Normal 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("<h1>Hello World</h1>")
|
||||
})
|
||||
|
||||
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") && !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("<script>var ws=new WebSocket('ws://localhost:8080/ws');ws.onmessage=e=>console.log(e.data);ws.onopen=()=>ws.send('hello');</script>")
|
||||
})
|
||||
|
||||
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 && data["type"] == "ping") {
|
||||
ws.sendJson({"type": "pong", "timestamp": data["timestamp"]})
|
||||
} else if (data is Map && 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("<h1>Chat Room</h1><p>Connect via WebSocket</p>")
|
||||
}
|
||||
|
||||
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 %}
|
||||
@ -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 %}
|
||||
218
manual_src/pages/api/yaml.html
Normal file
218
manual_src/pages/api/yaml.html
Normal 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>) → <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>) → <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>) → <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>></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 %}
|
||||
420
manual_src/pages/contributing/async-patterns.html
Normal file
420
manual_src/pages/contributing/async-patterns.html
Normal 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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uv.h>
|
||||
#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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uv.h>
|
||||
#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 <retoor@molodetz.nl>
|
||||
|
||||
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 %}
|
||||
412
manual_src/pages/contributing/c-backed-module.html
Normal file
412
manual_src/pages/contributing/c-backed-module.html
Normal 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/<name>.wren</code> with foreign method declarations:</p>
|
||||
|
||||
<pre><code>// retoor <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
#include <stdlib.h>
|
||||
#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 <retoor@molodetz.nl>
|
||||
|
||||
#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 $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"</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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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 <retoor@molodetz.nl>
|
||||
|
||||
#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/<name>.wren</code> with foreign declarations</li>
|
||||
<li>Generated <code>.wren.inc</code></li>
|
||||
<li>Created <code>src/module/<name>.c</code></li>
|
||||
<li>Created <code>src/module/<name>.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 %}
|
||||
370
manual_src/pages/contributing/documentation.html
Normal file
370
manual_src/pages/contributing/documentation.html
Normal 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><!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>[Page 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">
|
||||
<!-- Auto-generated by sync_sidebar.py -->
|
||||
</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>[Current Page]</span>
|
||||
</nav>
|
||||
<article>
|
||||
<h1>[Page Title]</h1>
|
||||
<!-- Content -->
|
||||
</article>
|
||||
<footer class="page-footer">
|
||||
<a href="[prev].html" class="prev">[Previous]</a>
|
||||
<a href="[next].html" class="next">[Next]</a>
|
||||
</footer>
|
||||
</main>
|
||||
</div>
|
||||
<script src="../js/main.js"></script>
|
||||
</body>
|
||||
</html></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><h1>modulename</h1>
|
||||
<p>Description paragraph.</p>
|
||||
<pre><code>import "modulename" for ClassName</code></pre>
|
||||
|
||||
<div class="class-header"><h2>Class: ClassName</h2></div>
|
||||
|
||||
<h3>Static Methods</h3>
|
||||
<div class="method-signature">
|
||||
<span class="method-name">ClassName.methodName</span>(<span class="param">arg</span>)
|
||||
&rarr; <span class="type">ReturnType</span>
|
||||
</div>
|
||||
<p>Method description.</p>
|
||||
|
||||
<h3>Examples</h3>
|
||||
<pre><code>import "modulename" for ClassName
|
||||
var result = ClassName.methodName("hello")
|
||||
System.print(result)</code></pre></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><div class="method-signature">
|
||||
<span class="method-name">ClassName.methodName</span>(<span class="param">arg1</span>, <span class="param">arg2</span>)
|
||||
&rarr; <span class="type">String</span>
|
||||
</div></code></pre>
|
||||
|
||||
<p>Instance method:</p>
|
||||
<pre><code><div class="method-signature">
|
||||
<span class="method-name">instance.methodName</span>(<span class="param">arg</span>)
|
||||
&rarr; <span class="type">Bool</span>
|
||||
</div></code></pre>
|
||||
|
||||
<p>Property (getter):</p>
|
||||
<pre><code><div class="method-signature">
|
||||
<span class="method-name">instance.propertyName</span>
|
||||
&rarr; <span class="type">Num</span>
|
||||
</div></code></pre>
|
||||
|
||||
<h2>Admonition Blocks</h2>
|
||||
|
||||
<p>Use admonitions for notes, warnings, and tips:</p>
|
||||
|
||||
<h3>Note</h3>
|
||||
<pre><code><div class="admonition note">
|
||||
<div class="admonition-title">Note</div>
|
||||
<p>Additional information.</p>
|
||||
</div></code></pre>
|
||||
|
||||
<div class="admonition note">
|
||||
<div class="admonition-title">Note</div>
|
||||
<p>Additional information.</p>
|
||||
</div>
|
||||
|
||||
<h3>Warning</h3>
|
||||
<pre><code><div class="admonition warning">
|
||||
<div class="admonition-title">Warning</div>
|
||||
<p>Important caution.</p>
|
||||
</div></code></pre>
|
||||
|
||||
<div class="admonition warning">
|
||||
<div class="admonition-title">Warning</div>
|
||||
<p>Important caution.</p>
|
||||
</div>
|
||||
|
||||
<h3>Tip</h3>
|
||||
<pre><code><div class="admonition tip">
|
||||
<div class="admonition-title">Tip</div>
|
||||
<p>Helpful suggestion.</p>
|
||||
</div></code></pre>
|
||||
|
||||
<h2>Parameter Lists</h2>
|
||||
|
||||
<p>For methods with complex parameters:</p>
|
||||
|
||||
<pre><code><div class="param-list">
|
||||
<div class="param-item">
|
||||
<span class="param-name">path</span>
|
||||
<span class="param-type">String</span>
|
||||
<p>The file path to read.</p>
|
||||
</div>
|
||||
<div class="param-item">
|
||||
<span class="param-name">encoding</span>
|
||||
<span class="param-type">String</span>
|
||||
<p>The character encoding (default: "utf-8").</p>
|
||||
</div>
|
||||
</div></code></pre>
|
||||
|
||||
<h2>Tables</h2>
|
||||
|
||||
<p>Use tables for options, type mappings, or comparisons:</p>
|
||||
|
||||
<pre><code><table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>"r"</code></td>
|
||||
<td>Read mode</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>"w"</code></td>
|
||||
<td>Write mode</td>
|
||||
</tr>
|
||||
</table></code></pre>
|
||||
|
||||
<h2>Footer Navigation</h2>
|
||||
|
||||
<p>Every page has previous/next navigation:</p>
|
||||
|
||||
<pre><code><footer class="page-footer">
|
||||
<a href="previous.html" class="prev">Previous Page</a>
|
||||
<a href="next.html" class="next">Next Page</a>
|
||||
</footer></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><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></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><nav class="sidebar-nav"></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/<name>.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><!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>mymodule - 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">
|
||||
</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>mymodule</span>
|
||||
</nav>
|
||||
|
||||
<article>
|
||||
<h1>mymodule</h1>
|
||||
|
||||
<p>The <code>mymodule</code> module provides...</p>
|
||||
|
||||
<pre><code>import "mymodule" for MyClass</code></pre>
|
||||
|
||||
<h2>MyClass</h2>
|
||||
|
||||
<h3>Static Methods</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">MyClass.process</span>(<span class="param">input</span>)
|
||||
&rarr; <span class="type">String</span>
|
||||
</div>
|
||||
<p>Processes the input and returns a result.</p>
|
||||
|
||||
<h3>Examples</h3>
|
||||
<pre><code>import "mymodule" for MyClass
|
||||
|
||||
var result = MyClass.process("hello")
|
||||
System.print(result)</code></pre>
|
||||
</article>
|
||||
|
||||
<footer class="page-footer">
|
||||
<a href="math.html" class="prev">math</a>
|
||||
<a href="net.html" class="next">net</a>
|
||||
</footer>
|
||||
</main>
|
||||
</div>
|
||||
<script src="../js/main.js"></script>
|
||||
</body>
|
||||
</html></code></pre>
|
||||
|
||||
<h2>Checklist for New Documentation</h2>
|
||||
|
||||
<ul>
|
||||
<li>Author comment on line 2</li>
|
||||
<li>Correct title in <code><title></code> and <code><h1></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 %}
|
||||
383
manual_src/pages/contributing/foreign-classes.html
Normal file
383
manual_src/pages/contributing/foreign-classes.html
Normal 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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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 %}
|
||||
128
manual_src/pages/contributing/index.html
Normal file
128
manual_src/pages/contributing/index.html
Normal 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/<name>.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/<name>/</code></li>
|
||||
<li>Create example in <code>example/<name>_demo.wren</code></li>
|
||||
<li>Create <code>manual/api/<name>.html</code></li>
|
||||
<li>Run <code>make sync-manual</code></li>
|
||||
<li>Verify with <code>python3 util/test.py <name></code></li>
|
||||
</ol>
|
||||
|
||||
<h3>C-Backed Module</h3>
|
||||
<ol>
|
||||
<li>All pure-Wren steps, plus:</li>
|
||||
<li>Create <code>src/module/<name>.c</code> with foreign method implementations</li>
|
||||
<li>Create <code>src/module/<name>.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/
|
||||
<name>.wren # Wren interface source
|
||||
<name>.wren.inc # Generated C string literal
|
||||
<name>.c # C implementation (if foreign methods)
|
||||
<name>.h # C header (if foreign methods)
|
||||
|
||||
test/
|
||||
<name>/
|
||||
<feature>.wren # Test files with annotations
|
||||
|
||||
example/
|
||||
<name>_demo.wren # Usage demonstrations
|
||||
|
||||
manual/
|
||||
api/<name>.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 %}
|
||||
221
manual_src/pages/contributing/module-overview.html
Normal file
221
manual_src/pages/contributing/module-overview.html
Normal 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/
|
||||
<name>.wren # Wren interface source
|
||||
<name>.wren.inc # Generated C string literal (do not edit)
|
||||
<name>.c # C implementation (only for foreign methods)
|
||||
<name>.h # C header (only for foreign methods)
|
||||
|
||||
test/
|
||||
<modulename>/
|
||||
<testname>.wren # Test files with inline annotations
|
||||
|
||||
example/
|
||||
<modulename>_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/<name>.wren</code></td>
|
||||
<td>Always</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Generated C string</td>
|
||||
<td><code>src/module/<name>.wren.inc</code></td>
|
||||
<td>Always</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>C implementation</td>
|
||||
<td><code>src/module/<name>.c</code></td>
|
||||
<td>Only if foreign methods</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>C header</td>
|
||||
<td><code>src/module/<name>.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 %}
|
||||
247
manual_src/pages/contributing/pure-wren-module.html
Normal file
247
manual_src/pages/contributing/pure-wren-module.html
Normal 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/<name>.wren</code> with your module implementation:</p>
|
||||
|
||||
<pre><code>// retoor <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>\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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
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/<name>.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/<name>/</code></li>
|
||||
<li>Created example in <code>example/<name>_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 %}
|
||||
314
manual_src/pages/contributing/testing.html
Normal file
314
manual_src/pages/contributing/testing.html
Normal 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/
|
||||
<modulename>/
|
||||
<feature>.wren # Functional tests
|
||||
error_<scenario>.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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
class Test {
|
||||
foo( // expect error
|
||||
}</code></pre>
|
||||
|
||||
<p>Or specify a different line number:</p>
|
||||
|
||||
<pre><code>// retoor <retoor@molodetz.nl>
|
||||
|
||||
// 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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
// 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 <retoor@molodetz.nl>
|
||||
// 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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
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 <retoor@molodetz.nl>
|
||||
|
||||
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 %}
|
||||
203
manual_src/pages/getting-started/first-script.html
Normal file
203
manual_src/pages/getting-started/first-script.html
Normal 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 %}
|
||||
104
manual_src/pages/getting-started/index.html
Normal file
104
manual_src/pages/getting-started/index.html
Normal 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 %}
|
||||
@ -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 %}
|
||||
151
manual_src/pages/getting-started/repl.html
Normal file
151
manual_src/pages/getting-started/repl.html
Normal 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 %}
|
||||
312
manual_src/pages/howto/async-operations.html
Normal file
312
manual_src/pages/howto/async-operations.html
Normal 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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
110
manual_src/pages/howto/index.html
Normal file
110
manual_src/pages/howto/index.html
Normal 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 %}
|
||||
243
manual_src/pages/howto/json-parsing.html
Normal file
243
manual_src/pages/howto/json-parsing.html
Normal 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 %}
|
||||
@ -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 %}
|
||||
93
manual_src/pages/index.html
Normal file
93
manual_src/pages/index.html
Normal 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 %}
|
||||
@ -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 %}
|
||||
@ -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
Loading…
Reference in New Issue
Block a user