This commit is contained in:
retoor 2026-01-25 04:58:39 +01:00
parent 957e3a4762
commit 2e425571ca
139 changed files with 5078 additions and 73 deletions

15
Makefile Normal file
View File

@ -0,0 +1,15 @@
# retoor <retoor@molodetz.nl>
.PHONY: build tests clean debug
build:
cd projects/make && $(MAKE) -f wren_cli.make -j $$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
debug:
cd projects/make && $(MAKE) -f wren_cli.make config=debug_64bit -j $$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
tests: build
python3 util/test.py
clean:
cd projects/make && $(MAKE) -f wren_cli.make clean

View File

@ -1,59 +1,63 @@
# Wren CLI
# Wren-CLI
## The CLI project is a small and simple REPL and CLI tool for running Wren scripts.
retoor <retoor@molodetz.nl>
It is backed by [libuv](http://libuv.org/) to implement IO functionality, and is a work in progress.
A command-line interface and REPL for the [Wren](http://wren.io) programming language. Built on [libuv](http://libuv.org/) for asynchronous I/O with native support for HTTP/HTTPS, WebSockets, TLS, SQLite, JSON, regex, and more.
For documentation and usage, see http://wren.io/cli
For more information about Wren, the language used by the CLI, see http://wren.io
## Prerequisites
Like with Wren itself, we welcome [contributions][contribute].
- C compiler (GCC or Clang)
- Make
- OpenSSL development libraries
- Python 3 (for tests)
[contribute]: http://wren.io/contributing.html
## Build
[![Build Status](https://travis-ci.org/wren-lang/wren-cli.svg?branch=main)](https://travis-ci.org/wren-lang/wren-cli)
```bash
git clone https://github.com/wren-lang/wren-cli.git
cd wren-cli
make build
```
---
Output binary: `bin/wren_cli`
## To build the Wren CLI
### Platform-Specific Builds
### Windows
| Platform | Command |
|----------|---------|
| Linux | `make build` |
| macOS | `cd projects/make.mac && make` |
| FreeBSD | `cd projects/make.bsd && gmake` |
The `projects/vs20xx` folders contain Visual Studio projects.
## Test
### macOS
```bash
make tests
```
The `projects/xcode` folder contains an Xcode project.
Run a specific test suite:
The `projects/make.mac` folder also contains a `make` project.
From that folder, run `make`.
```bash
python3 util/test.py json
```
`cd projects/make.mac`
`make`
## Usage
### Linux
```bash
bin/wren_cli script.wren # Execute a script
bin/wren_cli # Start the REPL
```
The `projects/make` folder contains a `make` project.
From that folder, run `make`.
## Modules
`cd projects/make`
`make`
**Core**: io, os, scheduler, timer, net, repl
### FreeBSD
**Extended**: json, env, math, base64, datetime, dns, signal, subprocess, crypto, regex, http, tls, sqlite, websocket, jinja, pathlib, tempfile, uuid
The `projects/make.bsd` folder contains a `make` project.
From that folder, run `make`.
## Documentation
`cd projects/make.bsd`
`gmake`
## Alternative build options
The projects are generated by premake, found inside `projects/premake`.
You can use premake5 (alpha 14 was used) to generate other projects.
Generate other system's projects via the premake `--os` flag,
i.e if on linux, `premake vs2019 --os=windows` is valid.
---
See the `manual/` directory for the full reference including API docs, tutorials, and how-to guides.
## License
MIT

View File

@ -108,6 +108,8 @@ typedef enum
TOKEN_TRUE,
TOKEN_VAR,
TOKEN_WHILE,
TOKEN_ASYNC,
TOKEN_AWAIT,
TOKEN_FIELD,
TOKEN_STATIC_FIELD,
@ -619,6 +621,8 @@ static Keyword keywords[] =
{"true", 4, TOKEN_TRUE},
{"var", 3, TOKEN_VAR},
{"while", 5, TOKEN_WHILE},
{"async", 5, TOKEN_ASYNC},
{"await", 5, TOKEN_AWAIT},
{NULL, 0, TOKEN_EOF} // Sentinel to mark the end of the array.
};
@ -1746,6 +1750,8 @@ static void expression(Compiler* compiler);
static void statement(Compiler* compiler);
static void definition(Compiler* compiler);
static void parsePrecedence(Compiler* compiler, Precedence precedence);
static void async_(Compiler* compiler, bool canAssign);
static void await_(Compiler* compiler, bool canAssign);
// Replaces the placeholder argument for a previous CODE_JUMP or CODE_JUMP_IF
// instruction with an offset that jumps to the current end of bytecode.
@ -2499,6 +2505,59 @@ static void this_(Compiler* compiler, bool canAssign)
loadThis(compiler);
}
static int resolveScheduler(Compiler* compiler)
{
int symbol = wrenSymbolTableFind(
&compiler->parser->module->variableNames,
"Scheduler", 9);
if (symbol == -1)
{
symbol = wrenDeclareVariable(compiler->parser->vm,
compiler->parser->module,
"Scheduler", 9,
compiler->parser->previous.line);
if (symbol == -2)
{
error(compiler, "Too many module variables defined.");
return -1;
}
}
return symbol;
}
static void await_(Compiler* compiler, bool canAssign)
{
int schedulerSymbol = resolveScheduler(compiler);
if (schedulerSymbol == -1) return;
emitShortArg(compiler, CODE_LOAD_MODULE_VAR, schedulerSymbol);
ignoreNewlines(compiler);
expression(compiler);
callMethod(compiler, 1, "await_(_)", 9);
}
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;
finishBody(&fnCompiler);
endCompiler(&fnCompiler, "[async]", 7);
callMethod(compiler, 1, "async_(_)", 9);
}
// Subscript or "array indexing" operator like `foo[bar]`.
static void subscript(Compiler* compiler, bool canAssign)
{
@ -2799,6 +2858,8 @@ GrammarRule rules[] =
/* TOKEN_TRUE */ PREFIX(boolean),
/* TOKEN_VAR */ UNUSED,
/* TOKEN_WHILE */ UNUSED,
/* TOKEN_ASYNC */ PREFIX(async_),
/* TOKEN_AWAIT */ PREFIX(await_),
/* TOKEN_FIELD */ PREFIX(field),
/* TOKEN_STATIC_FIELD */ PREFIX(staticField),
/* TOKEN_NAME */ { name, NULL, namedSignature, PREC_NONE, NULL },

View File

@ -2,6 +2,7 @@
#include <errno.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
@ -647,6 +648,7 @@ DEF_NUM_CONSTANT(smallest, DBL_MIN)
DEF_NUM_CONSTANT(maxSafeInteger, 9007199254740991.0)
DEF_NUM_CONSTANT(minSafeInteger, -9007199254740991.0)
DEF_NUM_CONSTANT(e, 2.71828182845904523536)
// Defines a primitive on Num that calls infix [op] and returns [type].
#define DEF_NUM_INFIX(name, op, type) \
@ -704,6 +706,10 @@ DEF_NUM_FN(tan, tan)
DEF_NUM_FN(log, log)
DEF_NUM_FN(log2, log2)
DEF_NUM_FN(exp, exp)
DEF_NUM_FN(log10, log10)
DEF_NUM_FN(sinh, sinh)
DEF_NUM_FN(cosh, cosh)
DEF_NUM_FN(tanh, tanh)
DEF_PRIMITIVE(num_mod)
{
@ -843,6 +849,65 @@ DEF_PRIMITIVE(num_truncate)
RETURN_NUM(integer);
}
DEF_PRIMITIVE(num_toBase)
{
if (!validateInt(vm, args[1], "Radix")) return false;
int radix = (int)AS_NUM(args[1]);
if (radix < 2 || radix > 36)
{
RETURN_ERROR("Radix must be between 2 and 36.");
}
double raw = AS_NUM(args[0]);
if (isnan(raw) || isinf(raw))
{
RETURN_ERROR("Cannot convert NaN or Infinity to a base string.");
}
int64_t value = (int64_t)trunc(raw);
bool negative = value < 0;
if (negative) value = -value;
static const char lookup[] = "0123456789abcdefghijklmnopqrstuvwxyz";
char buf[66];
int len = 0;
if (value == 0)
{
buf[len++] = '0';
}
else
{
while (value > 0)
{
buf[len++] = lookup[value % radix];
value = value / radix;
}
}
if (negative) buf[len++] = '-';
for (int i = 0; i < len / 2; i++)
{
char tmp = buf[i];
buf[i] = buf[len - 1 - i];
buf[len - 1 - i] = tmp;
}
buf[len] = '\0';
RETURN_VAL(wrenNewStringLength(vm, buf, len));
}
DEF_PRIMITIVE(num_format)
{
if (!validateInt(vm, args[1], "Decimal places")) return false;
int decimals = (int)AS_NUM(args[1]);
if (decimals < 0 || decimals > 20)
{
RETURN_ERROR("Decimal places must be between 0 and 20.");
}
char buf[64];
int len = snprintf(buf, sizeof(buf), "%.*f", decimals, AS_NUM(args[0]));
if (len < 0 || len >= (int)sizeof(buf))
{
RETURN_ERROR("Formatting failed.");
}
RETURN_VAL(wrenNewStringLength(vm, buf, (size_t)len));
}
DEF_PRIMITIVE(object_same)
{
RETURN_BOOL(wrenValuesEqual(args[1], args[2]));
@ -1167,6 +1232,46 @@ DEF_PRIMITIVE(string_plus)
RETURN_VAL(wrenStringFormat(vm, "@@", args[0], args[1]));
}
DEF_PRIMITIVE(string_lt)
{
if (!validateString(vm, args[1], "Right operand")) return false;
ObjString* left = AS_STRING(args[0]);
ObjString* right = AS_STRING(args[1]);
uint32_t minLen = left->length < right->length ? left->length : right->length;
int cmp = memcmp(left->value, right->value, minLen);
RETURN_BOOL(cmp < 0 || (cmp == 0 && left->length < right->length));
}
DEF_PRIMITIVE(string_gt)
{
if (!validateString(vm, args[1], "Right operand")) return false;
ObjString* left = AS_STRING(args[0]);
ObjString* right = AS_STRING(args[1]);
uint32_t minLen = left->length < right->length ? left->length : right->length;
int cmp = memcmp(left->value, right->value, minLen);
RETURN_BOOL(cmp > 0 || (cmp == 0 && left->length > right->length));
}
DEF_PRIMITIVE(string_lte)
{
if (!validateString(vm, args[1], "Right operand")) return false;
ObjString* left = AS_STRING(args[0]);
ObjString* right = AS_STRING(args[1]);
uint32_t minLen = left->length < right->length ? left->length : right->length;
int cmp = memcmp(left->value, right->value, minLen);
RETURN_BOOL(cmp < 0 || (cmp == 0 && left->length <= right->length));
}
DEF_PRIMITIVE(string_gte)
{
if (!validateString(vm, args[1], "Right operand")) return false;
ObjString* left = AS_STRING(args[0]);
ObjString* right = AS_STRING(args[1]);
uint32_t minLen = left->length < right->length ? left->length : right->length;
int cmp = memcmp(left->value, right->value, minLen);
RETURN_BOOL(cmp > 0 || (cmp == 0 && left->length >= right->length));
}
DEF_PRIMITIVE(string_subscript)
{
ObjString* string = AS_STRING(args[0]);
@ -1197,6 +1302,36 @@ DEF_PRIMITIVE(string_toString)
RETURN_VAL(args[0]);
}
DEF_PRIMITIVE(string_lower)
{
ObjString* str = AS_STRING(args[0]);
char* buf = ALLOCATE_ARRAY(vm, char, str->length + 1);
for (uint32_t i = 0; i < str->length; i++)
{
uint8_t c = (uint8_t)str->value[i];
buf[i] = (c >= 'A' && c <= 'Z') ? (char)(c + 32) : (char)c;
}
buf[str->length] = '\0';
Value result = wrenNewStringLength(vm, buf, str->length);
DEALLOCATE(vm, buf);
RETURN_VAL(result);
}
DEF_PRIMITIVE(string_upper)
{
ObjString* str = AS_STRING(args[0]);
char* buf = ALLOCATE_ARRAY(vm, char, str->length + 1);
for (uint32_t i = 0; i < str->length; i++)
{
uint8_t c = (uint8_t)str->value[i];
buf[i] = (c >= 'a' && c <= 'z') ? (char)(c - 32) : (char)c;
}
buf[str->length] = '\0';
Value result = wrenNewStringLength(vm, buf, str->length);
DEALLOCATE(vm, buf);
RETURN_VAL(result);
}
DEF_PRIMITIVE(system_clock)
{
RETURN_NUM((double)clock() / CLOCKS_PER_SEC);
@ -1358,6 +1493,7 @@ void wrenInitializeCore(WrenVM* vm)
PRIMITIVE(vm->numClass->obj.classObj, "smallest", num_smallest);
PRIMITIVE(vm->numClass->obj.classObj, "maxSafeInteger", num_maxSafeInteger);
PRIMITIVE(vm->numClass->obj.classObj, "minSafeInteger", num_minSafeInteger);
PRIMITIVE(vm->numClass->obj.classObj, "e", num_e);
PRIMITIVE(vm->numClass, "-(_)", num_minus);
PRIMITIVE(vm->numClass, "+(_)", num_plus);
PRIMITIVE(vm->numClass, "*(_)", num_multiply);
@ -1403,6 +1539,12 @@ void wrenInitializeCore(WrenVM* vm)
PRIMITIVE(vm->numClass, "sign", num_sign);
PRIMITIVE(vm->numClass, "toString", num_toString);
PRIMITIVE(vm->numClass, "truncate", num_truncate);
PRIMITIVE(vm->numClass, "log10", num_log10);
PRIMITIVE(vm->numClass, "sinh", num_sinh);
PRIMITIVE(vm->numClass, "cosh", num_cosh);
PRIMITIVE(vm->numClass, "tanh", num_tanh);
PRIMITIVE(vm->numClass, "toBase(_)", num_toBase);
PRIMITIVE(vm->numClass, "format(_)", num_format);
// These are defined just so that 0 and -0 are equal, which is specified by
// IEEE 754 even though they have different bit representations.
@ -1413,6 +1555,10 @@ void wrenInitializeCore(WrenVM* vm)
PRIMITIVE(vm->stringClass->obj.classObj, "fromCodePoint(_)", string_fromCodePoint);
PRIMITIVE(vm->stringClass->obj.classObj, "fromByte(_)", string_fromByte);
PRIMITIVE(vm->stringClass, "+(_)", string_plus);
PRIMITIVE(vm->stringClass, "<(_)", string_lt);
PRIMITIVE(vm->stringClass, ">(_)", string_gt);
PRIMITIVE(vm->stringClass, "<=(_)", string_lte);
PRIMITIVE(vm->stringClass, ">=(_)", string_gte);
PRIMITIVE(vm->stringClass, "[_]", string_subscript);
PRIMITIVE(vm->stringClass, "byteAt_(_)", string_byteAt);
PRIMITIVE(vm->stringClass, "byteCount_", string_byteCount);
@ -1426,6 +1572,8 @@ void wrenInitializeCore(WrenVM* vm)
PRIMITIVE(vm->stringClass, "iteratorValue(_)", string_iteratorValue);
PRIMITIVE(vm->stringClass, "startsWith(_)", string_startsWith);
PRIMITIVE(vm->stringClass, "toString", string_toString);
PRIMITIVE(vm->stringClass, "lower_", string_lower);
PRIMITIVE(vm->stringClass, "upper_", string_upper);
vm->listClass = AS_CLASS(wrenFindVariable(vm, coreModule, "List"));
PRIMITIVE(vm->listClass->obj.classObj, "filled(_,_)", list_filled);

View File

@ -2,7 +2,62 @@ class Bool {}
class Fiber {}
class Fn {}
class Null {}
class Num {}
class Num {
isZero { this == 0 }
isPositive { this > 0 }
isNegative { this < 0 }
isFinite { !isInfinity && !isNan }
isEven { isInteger && this % 2 == 0 }
isOdd { isInteger && this % 2 != 0 }
isBetween(min, max) { this >= min && this <= max }
toDegrees { this * 180 / Num.pi }
toRadians { this * Num.pi / 180 }
toChar { String.fromCodePoint(this) }
toHex { toBase(16) }
toBinary { toBase(2) }
toOctal { toBase(8) }
gcd(other) {
var a = this.abs
var b = other.abs
while (b != 0) {
var t = b
b = a % b
a = t
}
return a
}
lcm(other) {
if (this == 0 && other == 0) return 0
return (this * other).abs / gcd(other)
}
digits {
if (!isInteger) Fiber.abort("Value must be an integer.")
var n = this.abs
if (n == 0) {
var z = List.new()
z.add(0)
return z
}
var result = List.new()
while (n > 0) {
result.add(n % 10)
n = (n / 10).floor
}
var reversed = List.new()
var i = result.count - 1
while (i >= 0) {
reversed.add(result[i])
i = i - 1
}
return reversed
}
}
class Sequence {
all(f) {
@ -289,6 +344,230 @@ class String is Sequence {
}
return result
}
lower { lower_ }
upper { upper_ }
capitalize {
if (isEmpty) return this
return this[0].upper + this[1..-1].lower
}
title {
if (isEmpty) return this
var result = ""
var capitalizeNext = true
for (c in this) {
if (c == " " || c == "\t" || c == "\n" || c == "\r") {
result = result + c
capitalizeNext = true
} else if (capitalizeNext) {
result = result + c.upper
capitalizeNext = false
} else {
result = result + c.lower
}
}
return result
}
swapCase {
var result = ""
for (c in this) {
var cp = c.codePoints.toList[0]
if (cp >= 65 && cp <= 90) {
result = result + c.lower
} else if (cp >= 97 && cp <= 122) {
result = result + c.upper
} else {
result = result + c
}
}
return result
}
lastIndexOf(search) {
var last = -1
var index = 0
var size = byteCount_
var searchSize = search.byteCount_
if (searchSize > size) return -1
while (index <= size - searchSize) {
var found = indexOf(search, index)
if (found == -1) break
last = found
index = found + 1
}
return last
}
lastIndexOf(search, start) {
var last = -1
var index = 0
var searchSize = search.byteCount_
if (searchSize > start + searchSize) return -1
while (index <= start) {
var found = indexOf(search, index)
if (found == -1 || found > start) break
last = found
index = found + 1
}
return last
}
isLower {
if (isEmpty) return false
var hasAlpha = false
for (b in bytes) {
if (b >= 65 && b <= 90) return false
if (b >= 97 && b <= 122) hasAlpha = true
}
return hasAlpha
}
isUpper {
if (isEmpty) return false
var hasAlpha = false
for (b in bytes) {
if (b >= 97 && b <= 122) return false
if (b >= 65 && b <= 90) hasAlpha = true
}
return hasAlpha
}
isDigit {
if (isEmpty) return false
for (b in bytes) {
if (b < 48 || b > 57) return false
}
return true
}
isAlpha {
if (isEmpty) return false
for (b in bytes) {
if (!((b >= 65 && b <= 90) || (b >= 97 && b <= 122))) return false
}
return true
}
isAlphaNumeric {
if (isEmpty) return false
for (b in bytes) {
if (!((b >= 65 && b <= 90) || (b >= 97 && b <= 122) || (b >= 48 && b <= 57))) return false
}
return true
}
isSpace {
if (isEmpty) return false
for (b in bytes) {
if (b != 32 && b != 9 && b != 10 && b != 13 && b != 12 && b != 11) return false
}
return true
}
isAscii {
for (b in bytes) {
if (b >= 128) return false
}
return true
}
reverse {
var list = []
for (c in this) {
list.insert(0, c)
}
return list.join("")
}
center(width) { center(width, " ") }
center(width, char) {
if (!(width is Num) || !width.isInteger) {
Fiber.abort("Width must be an integer.")
}
if (!(char is String) || char.isEmpty) {
Fiber.abort("Fill character must be a non-empty string.")
}
var len = count
if (len >= width) return this
var total = width - len
var left = (total / 2).floor
var right = total - left
return (char * (left + 1))[0...left] + this + (char * (right + 1))[0...right]
}
lpad(width, char) {
if (!(width is Num) || !width.isInteger) {
Fiber.abort("Width must be an integer.")
}
if (!(char is String) || char.isEmpty) {
Fiber.abort("Fill character must be a non-empty string.")
}
var len = count
if (len >= width) return this
var pad = width - len
return (char * (pad + 1))[0...pad] + this
}
rpad(width, char) {
if (!(width is Num) || !width.isInteger) {
Fiber.abort("Width must be an integer.")
}
if (!(char is String) || char.isEmpty) {
Fiber.abort("Fill character must be a non-empty string.")
}
var len = count
if (len >= width) return this
var pad = width - len
return this + (char * (pad + 1))[0...pad]
}
zfill(width) {
if (!(width is Num) || !width.isInteger) {
Fiber.abort("Width must be an integer.")
}
var len = count
if (len >= width) return this
if (startsWith("-") || startsWith("+")) {
return this[0] + ("0" * (width - len)) + this[1..-1]
}
return ("0" * (width - len)) + this
}
removePrefix(prefix) {
if (!(prefix is String)) {
Fiber.abort("Prefix must be a string.")
}
if (startsWith(prefix)) return this[prefix.byteCount_..-1]
return this
}
removeSuffix(suffix) {
if (!(suffix is String)) {
Fiber.abort("Suffix must be a string.")
}
if (endsWith(suffix)) {
var end = byteCount_ - suffix.byteCount_ - 1
if (end < 0) return ""
return this[0..end]
}
return this
}
splitLines { replace("\r\n", "\n").replace("\r", "\n").split("\n") }
chars {
var result = []
for (c in this) {
result.add(c)
}
return result
}
toNum { Num.fromString(this) }
}
class StringByteSequence is Sequence {
@ -480,4 +759,4 @@ class ClassAttributes {
_methods = methods
}
toString { "attributes:%(_attributes) methods:%(_methods)" }
}
}

View File

@ -1,10 +1,67 @@
// Generated automatically from src/vm/wren_core.wren. Do not edit.
// Please do not edit this file. It has been generated automatically
// from `deps/wren/src/vm/wren_core.wren` using `util/wren_to_c_string.py`
static const char* coreModuleSource =
"class Bool {}\n"
"class Fiber {}\n"
"class Fn {}\n"
"class Null {}\n"
"class Num {}\n"
"class Num {\n"
" isZero { this == 0 }\n"
" isPositive { this > 0 }\n"
" isNegative { this < 0 }\n"
" isFinite { !isInfinity && !isNan }\n"
" isEven { isInteger && this % 2 == 0 }\n"
" isOdd { isInteger && this % 2 != 0 }\n"
"\n"
" isBetween(min, max) { this >= min && this <= max }\n"
"\n"
" toDegrees { this * 180 / Num.pi }\n"
" toRadians { this * Num.pi / 180 }\n"
"\n"
" toChar { String.fromCodePoint(this) }\n"
" toHex { toBase(16) }\n"
" toBinary { toBase(2) }\n"
" toOctal { toBase(8) }\n"
"\n"
" gcd(other) {\n"
" var a = this.abs\n"
" var b = other.abs\n"
" while (b != 0) {\n"
" var t = b\n"
" b = a % b\n"
" a = t\n"
" }\n"
" return a\n"
" }\n"
"\n"
" lcm(other) {\n"
" if (this == 0 && other == 0) return 0\n"
" return (this * other).abs / gcd(other)\n"
" }\n"
"\n"
" digits {\n"
" if (!isInteger) Fiber.abort(\"Value must be an integer.\")\n"
" var n = this.abs\n"
" if (n == 0) {\n"
" var z = List.new()\n"
" z.add(0)\n"
" return z\n"
" }\n"
" var result = List.new()\n"
" while (n > 0) {\n"
" result.add(n % 10)\n"
" n = (n / 10).floor\n"
" }\n"
" var reversed = List.new()\n"
" var i = result.count - 1\n"
" while (i >= 0) {\n"
" reversed.add(result[i])\n"
" i = i - 1\n"
" }\n"
" return reversed\n"
" }\n"
"}\n"
"\n"
"class Sequence {\n"
" all(f) {\n"
@ -238,11 +295,11 @@ static const char* coreModuleSource =
" return result\n"
" }\n"
"\n"
" trim() { trim_(\"\t\r\n \", true, true) }\n"
" trim() { trim_(\"\\t\\r\\n \", true, true) }\n"
" trim(chars) { trim_(chars, true, true) }\n"
" trimEnd() { trim_(\"\t\r\n \", false, true) }\n"
" trimEnd() { trim_(\"\\t\\r\\n \", false, true) }\n"
" trimEnd(chars) { trim_(chars, false, true) }\n"
" trimStart() { trim_(\"\t\r\n \", true, false) }\n"
" trimStart() { trim_(\"\\t\\r\\n \", true, false) }\n"
" trimStart(chars) { trim_(chars, true, false) }\n"
"\n"
" trim_(chars, trimStart, trimEnd) {\n"
@ -291,6 +348,230 @@ static const char* coreModuleSource =
" }\n"
" return result\n"
" }\n"
"\n"
" lower { lower_ }\n"
" upper { upper_ }\n"
"\n"
" capitalize {\n"
" if (isEmpty) return this\n"
" return this[0].upper + this[1..-1].lower\n"
" }\n"
"\n"
" title {\n"
" if (isEmpty) return this\n"
" var result = \"\"\n"
" var capitalizeNext = true\n"
" for (c in this) {\n"
" if (c == \" \" || c == \"\\t\" || c == \"\\n\" || c == \"\\r\") {\n"
" result = result + c\n"
" capitalizeNext = true\n"
" } else if (capitalizeNext) {\n"
" result = result + c.upper\n"
" capitalizeNext = false\n"
" } else {\n"
" result = result + c.lower\n"
" }\n"
" }\n"
" return result\n"
" }\n"
"\n"
" swapCase {\n"
" var result = \"\"\n"
" for (c in this) {\n"
" var cp = c.codePoints.toList[0]\n"
" if (cp >= 65 && cp <= 90) {\n"
" result = result + c.lower\n"
" } else if (cp >= 97 && cp <= 122) {\n"
" result = result + c.upper\n"
" } else {\n"
" result = result + c\n"
" }\n"
" }\n"
" return result\n"
" }\n"
"\n"
" lastIndexOf(search) {\n"
" var last = -1\n"
" var index = 0\n"
" var size = byteCount_\n"
" var searchSize = search.byteCount_\n"
" if (searchSize > size) return -1\n"
" while (index <= size - searchSize) {\n"
" var found = indexOf(search, index)\n"
" if (found == -1) break\n"
" last = found\n"
" index = found + 1\n"
" }\n"
" return last\n"
" }\n"
"\n"
" lastIndexOf(search, start) {\n"
" var last = -1\n"
" var index = 0\n"
" var searchSize = search.byteCount_\n"
" if (searchSize > start + searchSize) return -1\n"
" while (index <= start) {\n"
" var found = indexOf(search, index)\n"
" if (found == -1 || found > start) break\n"
" last = found\n"
" index = found + 1\n"
" }\n"
" return last\n"
" }\n"
"\n"
" isLower {\n"
" if (isEmpty) return false\n"
" var hasAlpha = false\n"
" for (b in bytes) {\n"
" if (b >= 65 && b <= 90) return false\n"
" if (b >= 97 && b <= 122) hasAlpha = true\n"
" }\n"
" return hasAlpha\n"
" }\n"
"\n"
" isUpper {\n"
" if (isEmpty) return false\n"
" var hasAlpha = false\n"
" for (b in bytes) {\n"
" if (b >= 97 && b <= 122) return false\n"
" if (b >= 65 && b <= 90) hasAlpha = true\n"
" }\n"
" return hasAlpha\n"
" }\n"
"\n"
" isDigit {\n"
" if (isEmpty) return false\n"
" for (b in bytes) {\n"
" if (b < 48 || b > 57) return false\n"
" }\n"
" return true\n"
" }\n"
"\n"
" isAlpha {\n"
" if (isEmpty) return false\n"
" for (b in bytes) {\n"
" if (!((b >= 65 && b <= 90) || (b >= 97 && b <= 122))) return false\n"
" }\n"
" return true\n"
" }\n"
"\n"
" isAlphaNumeric {\n"
" if (isEmpty) return false\n"
" for (b in bytes) {\n"
" if (!((b >= 65 && b <= 90) || (b >= 97 && b <= 122) || (b >= 48 && b <= 57))) return false\n"
" }\n"
" return true\n"
" }\n"
"\n"
" isSpace {\n"
" if (isEmpty) return false\n"
" for (b in bytes) {\n"
" if (b != 32 && b != 9 && b != 10 && b != 13 && b != 12 && b != 11) return false\n"
" }\n"
" return true\n"
" }\n"
"\n"
" isAscii {\n"
" for (b in bytes) {\n"
" if (b >= 128) return false\n"
" }\n"
" return true\n"
" }\n"
"\n"
" reverse {\n"
" var list = []\n"
" for (c in this) {\n"
" list.insert(0, c)\n"
" }\n"
" return list.join(\"\")\n"
" }\n"
"\n"
" center(width) { center(width, \" \") }\n"
"\n"
" center(width, char) {\n"
" if (!(width is Num) || !width.isInteger) {\n"
" Fiber.abort(\"Width must be an integer.\")\n"
" }\n"
" if (!(char is String) || char.isEmpty) {\n"
" Fiber.abort(\"Fill character must be a non-empty string.\")\n"
" }\n"
" var len = count\n"
" if (len >= width) return this\n"
" var total = width - len\n"
" var left = (total / 2).floor\n"
" var right = total - left\n"
" return (char * (left + 1))[0...left] + this + (char * (right + 1))[0...right]\n"
" }\n"
"\n"
" lpad(width, char) {\n"
" if (!(width is Num) || !width.isInteger) {\n"
" Fiber.abort(\"Width must be an integer.\")\n"
" }\n"
" if (!(char is String) || char.isEmpty) {\n"
" Fiber.abort(\"Fill character must be a non-empty string.\")\n"
" }\n"
" var len = count\n"
" if (len >= width) return this\n"
" var pad = width - len\n"
" return (char * (pad + 1))[0...pad] + this\n"
" }\n"
"\n"
" rpad(width, char) {\n"
" if (!(width is Num) || !width.isInteger) {\n"
" Fiber.abort(\"Width must be an integer.\")\n"
" }\n"
" if (!(char is String) || char.isEmpty) {\n"
" Fiber.abort(\"Fill character must be a non-empty string.\")\n"
" }\n"
" var len = count\n"
" if (len >= width) return this\n"
" var pad = width - len\n"
" return this + (char * (pad + 1))[0...pad]\n"
" }\n"
"\n"
" zfill(width) {\n"
" if (!(width is Num) || !width.isInteger) {\n"
" Fiber.abort(\"Width must be an integer.\")\n"
" }\n"
" var len = count\n"
" if (len >= width) return this\n"
" if (startsWith(\"-\") || startsWith(\"+\")) {\n"
" return this[0] + (\"0\" * (width - len)) + this[1..-1]\n"
" }\n"
" return (\"0\" * (width - len)) + this\n"
" }\n"
"\n"
" removePrefix(prefix) {\n"
" if (!(prefix is String)) {\n"
" Fiber.abort(\"Prefix must be a string.\")\n"
" }\n"
" if (startsWith(prefix)) return this[prefix.byteCount_..-1]\n"
" return this\n"
" }\n"
"\n"
" removeSuffix(suffix) {\n"
" if (!(suffix is String)) {\n"
" Fiber.abort(\"Suffix must be a string.\")\n"
" }\n"
" if (endsWith(suffix)) {\n"
" var end = byteCount_ - suffix.byteCount_ - 1\n"
" if (end < 0) return \"\"\n"
" return this[0..end]\n"
" }\n"
" return this\n"
" }\n"
"\n"
" splitLines { replace(\"\\r\\n\", \"\\n\").replace(\"\\r\", \"\\n\").split(\"\\n\") }\n"
"\n"
" chars {\n"
" var result = []\n"
" for (c in this) {\n"
" result.add(c)\n"
" }\n"
" return result\n"
" }\n"
"\n"
" toNum { Num.fromString(this) }\n"
"}\n"
"\n"
"class StringByteSequence is Sequence {\n"
@ -441,18 +722,18 @@ static const char* coreModuleSource =
"\n"
"class System {\n"
" static print() {\n"
" writeString_(\"\n\")\n"
" writeString_(\"\\n\")\n"
" }\n"
"\n"
" static print(obj) {\n"
" writeObject_(obj)\n"
" writeString_(\"\n\")\n"
" writeString_(\"\\n\")\n"
" return obj\n"
" }\n"
"\n"
" static printAll(sequence) {\n"
" for (object in sequence) writeObject_(object)\n"
" writeString_(\"\n\")\n"
" writeString_(\"\\n\")\n"
" }\n"
"\n"
" static write(obj) {\n"
@ -483,4 +764,3 @@ static const char* coreModuleSource =
" }\n"
" toString { \"attributes:%(_attributes) methods:%(_methods)\" }\n"
"}\n";

22
example/await_demo.wren vendored Normal file
View File

@ -0,0 +1,22 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
import "timer" for Timer
System.print("=== Await Demo ===\n")
System.print("--- Basic Await ---")
await Timer.sleep(1)
System.print("Timer completed")
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")
System.print("\n--- Async With Result ---")
var task = async { "computed value" }
var result = await task
System.print("Result: %(result)")

67
example/jinja_markdown.wren vendored Normal file
View File

@ -0,0 +1,67 @@
// retoor <retoor@molodetz.nl>
import "jinja" for Environment, DictLoader
System.print("=" * 60)
System.print("JINJA MARKDOWN - Markdown/HTML Conversion in Templates")
System.print("=" * 60)
var templates = {
"md_to_html": "{\% markdowntohtml \%}# {{ title }}
{{ description }}
- Item **one**
- Item **two**
- Item **three**
{\% endmarkdowntohtml \%}",
"html_to_md": "{\% markdownfromhtml \%}<h1>{{ title }}</h1>
<p>This is a <strong>paragraph</strong> with <em>formatting</em>.</p>
<ul>
<li>First</li>
<li>Second</li>
</ul>{\% endmarkdownfromhtml \%}",
"filter_md": "{{ content|markdown }}",
"filter_fromhtml": "{{ content|markdownfromhtml }}",
"mixed": "<div class=\"article\">
{\% markdowntohtml \%}## {{ article.title }}
{{ article.body }}
{\% endmarkdowntohtml \%}
</div>"
}
var env = Environment.new(DictLoader.new(templates))
System.print("\n--- Markdown to HTML (block tag) ---")
System.print(env.getTemplate("md_to_html").render({
"title": "Welcome",
"description": "This is a *template* with markdown support."
}))
System.print("\n--- HTML to Markdown (block tag) ---")
System.print(env.getTemplate("html_to_md").render({
"title": "Documentation"
}))
System.print("\n--- Markdown filter ---")
System.print(env.getTemplate("filter_md").render({
"content": "**Bold** and *italic* text."
}))
System.print("\n--- Markdown from HTML filter ---")
System.print(env.getTemplate("filter_fromhtml").render({
"content": "<h2>Section</h2><p>A paragraph.</p>"
}))
System.print("\n--- Mixed HTML and Markdown ---")
System.print(env.getTemplate("mixed").render({
"article": {
"title": "Getting Started",
"body": "Read the **documentation** for details."
}
}))

51
example/number_demo.wren vendored Normal file
View File

@ -0,0 +1,51 @@
// retoor <retoor@molodetz.nl>
System.print("=== Num Extensions Demo ===\n")
System.print("--- Constants ---")
System.print("Num.pi: %(Num.pi)")
System.print("Num.tau: %(Num.tau)")
System.print("Num.e: %(Num.e)")
System.print("\n--- Query Methods ---")
System.print("0.isZero: %(0.isZero)")
System.print("5.isPositive: %(5.isPositive)")
System.print("(-3).isNegative: %((-3).isNegative)")
System.print("42.isFinite: %(42.isFinite)")
System.print("(1/0).isFinite: %((1/0).isFinite)")
System.print("4.isEven: %(4.isEven)")
System.print("3.isOdd: %(3.isOdd)")
System.print("5.isBetween(1, 10): %(5.isBetween(1, 10))")
System.print("\n--- Math Functions ---")
System.print("100.log10: %(100.log10)")
System.print("0.sinh: %(0.sinh)")
System.print("0.cosh: %(0.cosh)")
System.print("0.tanh: %(0.tanh)")
System.print("\n--- Angle Conversion ---")
var pi = Num.pi
System.print("pi.toDegrees: %(pi.toDegrees)")
System.print("180.toRadians: %(180.toRadians)")
System.print("\n--- Base Conversion ---")
System.print("255.toHex: %(255.toHex)")
System.print("10.toBinary: %(10.toBinary)")
System.print("8.toOctal: %(8.toOctal)")
System.print("255.toBase(16): %(255.toBase(16))")
System.print("100.toBase(36): %(100.toBase(36))")
System.print("\n--- Character Conversion ---")
System.print("65.toChar: %(65.toChar)")
System.print("97.toChar: %(97.toChar)")
System.print("\n--- Formatting ---")
System.print("3.14159.format(2): %(3.14159.format(2))")
System.print("42.format(3): %(42.format(3))")
System.print("(-1.5).format(1): %((-1.5).format(1))")
System.print("\n--- Integer Operations ---")
System.print("12.gcd(8): %(12.gcd(8))")
System.print("4.lcm(6): %(4.lcm(6))")
System.print("123.digits: %(123.digits)")
System.print("0.digits: %(0.digits)")

167
example/pathlib_demo.wren vendored Normal file
View File

@ -0,0 +1,167 @@
// retoor <retoor@molodetz.nl>
import "pathlib" for Path
System.print("=== Pathlib Module Demo ===\n")
System.print("--- Path Construction ---")
var p = Path.new("/home/user/documents/report.tar.gz")
System.print("Path: %(p)")
System.print("Name: %(p.name)")
System.print("Stem: %(p.stem)")
System.print("Suffix: %(p.suffix)")
System.print("Suffixes: %(p.suffixes)")
System.print("Parts: %(p.parts)")
System.print("\n--- Parent Navigation ---")
System.print("Parent: %(p.parent)")
System.print("Parents:")
for (ancestor in p.parents) {
System.print(" %(ancestor)")
}
System.print("\n--- Path Properties ---")
System.print("Root: %(p.root)")
System.print("Anchor: %(p.anchor)")
System.print("Is absolute: %(p.isAbsolute)")
var rel = Path.new("relative/path.txt")
System.print("%(rel) is absolute: %(rel.isAbsolute)")
System.print("\n--- Join Operator (/) ---")
var base = Path.new("/home/user")
var config = base / ".config" / "myapp" / "settings.json"
System.print("%(base) / .config / myapp / settings.json = %(config)")
System.print("\n--- joinpath ---")
var multi = Path.new("/opt").joinpath(["local", "bin", "wren_cli"])
System.print("Joined: %(multi)")
System.print("\n--- withName / withStem / withSuffix ---")
var orig = Path.new("/data/archive.tar.gz")
System.print("Original: %(orig)")
System.print("withName(\"backup.zip\"): %(orig.withName("backup.zip"))")
System.print("withStem(\"snapshot\"): %(orig.withStem("snapshot"))")
System.print("withSuffix(\".bak\"): %(orig.withSuffix(".bak"))")
System.print("\n--- relativeTo ---")
var full = Path.new("/home/user/projects/wren/src/main.c")
var project = Path.new("/home/user/projects/wren")
System.print("%(full) relative to %(project) = %(full.relativeTo(project))")
System.print("\n--- Glob Matching ---")
var file = Path.new("/tmp/notes.txt")
System.print("%(file.name) matches *.txt: %(file.match("*.txt"))")
System.print("%(file.name) matches *.md: %(file.match("*.md"))")
System.print("%(file.name) matches note?: %(file.match("note?"))")
System.print("%(file.name) matches n*s.txt: %(file.match("n*s.txt"))")
System.print("\n--- Home and CWD ---")
System.print("Home: %(Path.home)")
System.print("CWD: %(Path.cwd)")
System.print("\n--- Expand User ---")
var tilde = Path.new("~/.bashrc")
System.print("%(tilde) -> %(tilde.expanduser())")
System.print("\n--- Equality ---")
var a = Path.new("/tmp/test")
var b = Path.new("/tmp/test")
var c = Path.new("/tmp/other")
System.print("%(a) == %(b): %(a == b)")
System.print("%(a) == %(c): %(a == c)")
System.print("%(a) != %(c): %(a != c)")
System.print("\n--- Filesystem Operations ---")
var testDir = Path.new("/tmp/wren_pathlib_demo")
if (testDir.exists()) testDir.rmtree()
testDir.mkdir(true)
System.print("Created: %(testDir)")
System.print("Exists: %(testDir.exists())")
System.print("Is dir: %(testDir.isDir())")
var testFile = testDir / "hello.txt"
testFile.writeText("Hello from pathlib!")
System.print("Wrote: %(testFile)")
System.print("Content: %(testFile.readText())")
System.print("Is file: %(testFile.isFile())")
System.print("\n--- Stat ---")
var s = testFile.stat()
System.print("Size: %(s.size) bytes")
System.print("\n--- Touch ---")
var marker = testDir / "marker"
marker.touch()
System.print("Touched: %(marker) (exists: %(marker.exists()))")
System.print("\n--- Copy ---")
var copy = testDir / "hello_copy.txt"
testFile.copyfile(copy)
System.print("Copied to: %(copy)")
System.print("Copy content: %(copy.readText())")
System.print("\n--- Rename ---")
var renamed = testFile.rename(testDir / "greeting.txt")
System.print("Renamed to: %(renamed)")
System.print("Old exists: %(testFile.exists())")
System.print("New content: %(renamed.readText())")
System.print("\n--- Resolve ---")
var resolved = Path.new(".").resolve()
System.print("Resolved '.': %(resolved)")
System.print("\n--- Same File ---")
System.print("Same file: %(renamed.samefile(renamed))")
System.print("\n--- Owner and Group ---")
System.print("Owner: %(renamed.owner())")
System.print("Group: %(renamed.group())")
System.print("\n--- Mkdir with Parents ---")
var deep = testDir / "a" / "b" / "c"
deep.mkdir(true)
System.print("Created: %(deep)")
System.print("Exists: %(deep.exists())")
System.print("\n--- Iterdir ---")
(testDir / "file1.txt").writeText("one")
(testDir / "file2.txt").writeText("two")
var entries = testDir.iterdir()
System.print("Entries in %(testDir.name):")
for (entry in entries) {
var kind = entry.isDir() ? "dir" : "file"
System.print(" %(entry.name) (%(kind))")
}
System.print("\n--- Glob ---")
var txtFiles = testDir.glob("*.txt")
System.print("Text files in %(testDir.name):")
for (f in txtFiles) {
System.print(" %(f.name)")
}
System.print("\n--- Recursive Glob ---")
(testDir / "a" / "nested.txt").writeText("nested")
(testDir / "a" / "b" / "deep.txt").writeText("deep")
var allTxt = testDir.rglob("*.txt")
System.print("All text files (recursive):")
for (f in allTxt) {
System.print(" %(f.relativeTo(testDir))")
}
System.print("\n--- Walk ---")
System.print("Directory tree:")
for (entry in testDir.walk()) {
var dir = entry[0]
var dirs = entry[1]
var files = entry[2]
var indent = dir == testDir ? "" : " "
System.print("%(indent)%(dir.relativeTo(testDir))/ (%(dirs.count) dirs, %(files.count) files)")
}
System.print("\n--- Cleanup ---")
testDir.rmtree()
System.print("Cleaned up: %(testDir) (exists: %(testDir.exists()))")
System.print("\n=== Demo Complete ===")

63
example/string_demo.wren vendored Normal file
View File

@ -0,0 +1,63 @@
// retoor <retoor@molodetz.nl>
System.print("=== String Methods Demo ===\n")
System.print("--- Case Conversion ---")
var text = "Hello World"
System.print("Original: %(text)")
System.print("lower: %(text.lower)")
System.print("upper: %(text.upper)")
System.print("capitalize: %("hello world".capitalize)")
System.print("title: %("hello world".title)")
System.print("swapCase: %(text.swapCase)")
System.print("\n--- Character Testing ---")
System.print("\"hello\".isLower: %("hello".isLower)")
System.print("\"HELLO\".isUpper: %("HELLO".isUpper)")
System.print("\"12345\".isDigit: %("12345".isDigit)")
System.print("\"hello\".isAlpha: %("hello".isAlpha)")
System.print("\"hello1\".isAlphaNumeric: %("hello1".isAlphaNumeric)")
System.print("\" \".isSpace: %(" ".isSpace)")
System.print("\"hello\".isAscii: %("hello".isAscii)")
System.print("\n--- Search ---")
var sentence = "hello world hello"
System.print("Text: %(sentence)")
System.print("lastIndexOf(\"hello\"): %(sentence.lastIndexOf("hello"))")
System.print("lastIndexOf(\"hello\", 11): %(sentence.lastIndexOf("hello", 11))")
System.print("\n--- Transformation ---")
System.print("\"hello\".reverse: %("hello".reverse)")
System.print("\"hi\".center(10, \"-\"): %("hi".center(10, "-"))")
System.print("\"42\".lpad(5, \"0\"): %("42".lpad(5, "0"))")
System.print("\"hi\".rpad(5, \".\"): %("hi".rpad(5, "."))")
System.print("\"42\".zfill(5): %("42".zfill(5))")
System.print("\"-42\".zfill(6): %("-42".zfill(6))")
System.print("\n--- Prefix/Suffix ---")
System.print("removePrefix(\"Hello\"): %("HelloWorld".removePrefix("Hello"))")
System.print("removeSuffix(\"World\"): %("HelloWorld".removeSuffix("World"))")
System.print("\n--- Comparison ---")
System.print("\"apple\" < \"banana\": %("apple" < "banana")")
System.print("\"banana\" > \"apple\": %("banana" > "apple")")
System.print("\"abc\" <= \"abc\": %("abc" <= "abc")")
System.print("\"abc\" >= \"abc\": %("abc" >= "abc")")
System.print("\"abc\" < \"abcd\": %("abc" < "abcd")")
System.print("\"abcd\" > \"abc\": %("abcd" > "abc")")
System.print("\"Z\" < \"a\": %("Z" < "a")")
var fruits = ["cherry", "apple", "banana"]
System.print("Sorted: %(fruits.sort {|a, b| a < b})")
System.print("\n--- Splitting ---")
var lines = "line1\nline2\nline3".splitLines
System.print("splitLines: %(lines)")
var chars = "abc".chars
System.print("chars: %(chars)")
System.print("\n--- Conversion ---")
System.print("\"42\".toNum: %("42".toNum)")
System.print("\"3.14\".toNum: %("3.14".toNum)")
System.print("\"abc\".toNum: %("abc".toNum)")

60
example/tempfile_demo.wren vendored Normal file
View File

@ -0,0 +1,60 @@
// retoor <retoor@molodetz.nl>
import "tempfile" for TempFile, NamedTemporaryFile, TemporaryDirectory
import "pathlib" for Path
System.print("=== TempFile Module Demo ===\n")
System.print("--- Utility Functions ---")
System.print("Temp directory: %(TempFile.gettempdir())")
System.print("Temp prefix: %(TempFile.gettempprefix())")
System.print("\n--- mktemp (name only, no file created) ---")
var name = TempFile.mktemp(".txt", "demo_")
System.print("Generated name: %(name)")
System.print("File exists: %(Path.new(name).exists())")
System.print("\n--- mkstemp (creates file atomically) ---")
var path = TempFile.mkstemp(".dat", "demo_")
System.print("Created file: %(path)")
System.print("File exists: %(Path.new(path).exists())")
Path.new(path).unlink()
System.print("\n--- mkdtemp (creates directory) ---")
var dir = TempFile.mkdtemp("_work", "demo_")
System.print("Created directory: %(dir)")
System.print("Is directory: %(Path.new(dir).isDir())")
Path.new(dir).rmdir()
System.print("\n--- NamedTemporaryFile ---")
var tmp = NamedTemporaryFile.new(".txt", "demo_")
System.print("Temp file: %(tmp.name)")
tmp.write("Hello from tempfile module!")
System.print("Content: %(tmp.read())")
System.print("Auto-delete: %(tmp.delete)")
tmp.close()
System.print("Deleted after close: %(!(Path.new(tmp.name).exists()))")
System.print("\n--- NamedTemporaryFile.use (context manager) ---")
NamedTemporaryFile.new(".log").use {|f|
f.write("Log entry: operation completed")
System.print("Inside use block: %(f.read())")
}
System.print("Automatically cleaned up after use block")
System.print("\n--- TemporaryDirectory ---")
var tmpDir = TemporaryDirectory.new("_session", "demo_")
System.print("Temp dir: %(tmpDir.name)")
System.print("Is directory: %(tmpDir.path.isDir())")
tmpDir.cleanup()
System.print("Deleted after cleanup: %(!(Path.new(tmpDir.name).exists()))")
System.print("\n--- TemporaryDirectory.use (context manager) ---")
TemporaryDirectory.new().use {|d|
System.print("Working in: %(d.name)")
Path.new(d.name + "/data.txt").writeText("temporary data")
System.print("Created file inside temp dir")
}
System.print("Automatically cleaned up with all contents")
System.print("\n=== Done ===")

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>
@ -104,12 +107,26 @@
<article>
<h1>API Reference</h1>
<p>Wren-CLI provides 28 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 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>
<pre><code>import "http" for Http
import "json" for Json
import "io" for File</code></pre>
<h2>Core Types</h2>
<p>Extended methods on built-in types, available without imports.</p>
<div class="card-grid">
<div class="card">
<h3><a href="string.html">String</a></h3>
<p>Case conversion, character testing, padding, splitting, reversing, and more on all strings.</p>
</div>
<div class="card">
<h3><a href="number.html">Num</a></h3>
<p>Query predicates, hyperbolic functions, base conversion, formatting, GCD/LCM, and more on all numbers.</p>
</div>
</div>
<h2>Networking</h2>
<p>Modules for HTTP, WebSocket, and low-level network operations.</p>
@ -198,6 +215,10 @@ import "io" for File</code></pre>
<h3><a href="io.html">io</a></h3>
<p>File and directory operations, stdin/stdout handling.</p>
</div>
<div class="card">
<h3><a href="pathlib.html">pathlib</a></h3>
<p>Object-oriented filesystem paths with glob, walk, and tree operations.</p>
</div>
</div>
<h2>Data & Time</h2>
@ -345,6 +366,11 @@ import "io" for File</code></pre>
<td>File, Directory, Stdin, Stdout</td>
<td>File I/O</td>
</tr>
<tr>
<td><a href="pathlib.html">pathlib</a></td>
<td>Path, PurePath</td>
<td>Filesystem paths</td>
</tr>
<tr>
<td><a href="scheduler.html">scheduler</a></td>
<td>Scheduler</td>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>
@ -274,7 +277,7 @@ backup.call("important.txt")</code></pre>
<footer class="page-footer">
<a href="timer.html" class="prev">timer</a>
<a href="scheduler.html" class="next">scheduler</a>
<a href="pathlib.html" class="next">pathlib</a>
</footer>
</main>
</div>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>
@ -119,6 +122,7 @@
<li><a href="#tests">Tests</a></li>
<li><a href="#macros">Macros</a></li>
<li><a href="#template-inheritance">Template Inheritance</a></li>
<li><a href="#markdown-tags">Markdown Tags</a></li>
</ul>
</li>
<li><a href="#filters-reference">Filters Reference</a></li>
@ -357,6 +361,34 @@ var template = env.getTemplate("page.html") // Loads ./templates/page.html</cod
This long text will be truncated to 50 characters...
{% endfilter %}</code></pre>
<h3 id="markdown-tags">Markdown Tags</h3>
<p>Convert between Markdown and HTML within templates using block tags:</p>
<h4>markdowntohtml</h4>
<p>Converts Markdown content to HTML:</p>
<pre><code>{% markdowntohtml %}
# Heading
This is **bold** text.
{% endmarkdowntohtml %}</code></pre>
<p>Variables are resolved before conversion:</p>
<pre><code>{% markdowntohtml %}
# {{ title }}
{{ description }}
{% endmarkdowntohtml %}</code></pre>
<h4>markdownfromhtml</h4>
<p>Converts HTML content back to Markdown:</p>
<pre><code>{% markdownfromhtml %}
&lt;h1&gt;Heading&lt;/h1&gt;
&lt;p&gt;This is &lt;strong&gt;bold&lt;/strong&gt; text.&lt;/p&gt;
{% endmarkdownfromhtml %}</code></pre>
<p>Corresponding filters are also available for inline use:</p>
<pre><code>{{ markdown_text|markdown }}
{{ html_content|markdownfromhtml }}</code></pre>
<h3 id="comments">Comments</h3>
<p>Comments are not included in the output:</p>
@ -527,6 +559,16 @@ var template = env.getTemplate("page.html") // Loads ./templates/page.html</cod
<td>URL-encode string</td>
<td><code>{{ "hello world"|urlencode }}</code> &rarr; hello+world</td>
</tr>
<tr>
<td><code>markdown</code></td>
<td>Convert Markdown to HTML</td>
<td><code>{{ md_text|markdown }}</code></td>
</tr>
<tr>
<td><code>markdownfromhtml</code></td>
<td>Convert HTML to Markdown</td>
<td><code>{{ html_content|markdownfromhtml }}</code></td>
</tr>
</table>
<h3>List/Collection Filters</h3>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

493
manual/api/number.html Normal file
View File

@ -0,0 +1,493 @@
<!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>
<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>
<h2>Static Constants</h2>
<div class="method-signature">
<span class="method-name">Num.pi</span><span class="type">Num</span>
</div>
<p>The ratio of a circle's circumference to its diameter (3.14159...).</p>
<div class="method-signature">
<span class="method-name">Num.tau</span><span class="type">Num</span>
</div>
<p>Two times pi (6.28318...).</p>
<div class="method-signature">
<span class="method-name">Num.e</span><span class="type">Num</span>
</div>
<p>Euler's number (2.71828...).</p>
<div class="method-signature">
<span class="method-name">Num.infinity</span><span class="type">Num</span>
</div>
<p>Positive infinity.</p>
<div class="method-signature">
<span class="method-name">Num.nan</span><span class="type">Num</span>
</div>
<p>Not-a-number value.</p>
<div class="method-signature">
<span class="method-name">Num.largest</span><span class="type">Num</span>
</div>
<p>The largest representable double value.</p>
<div class="method-signature">
<span class="method-name">Num.smallest</span><span class="type">Num</span>
</div>
<p>The smallest positive representable double value.</p>
<div class="method-signature">
<span class="method-name">Num.maxSafeInteger</span><span class="type">Num</span>
</div>
<p>The largest integer representable without loss of precision (9007199254740991).</p>
<div class="method-signature">
<span class="method-name">Num.minSafeInteger</span><span class="type">Num</span>
</div>
<p>The smallest integer representable without loss of precision (-9007199254740991).</p>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">Num.fromString</span>(<span class="param">string</span>) → <span class="type">Num|null</span>
</div>
<p>Parses a string as a number. Returns <code>null</code> if the string is not a valid number.</p>
<pre><code>Num.fromString("42") // 42
Num.fromString("3.14") // 3.14
Num.fromString("nope") // null</code></pre>
<h2>Query Methods</h2>
<div class="method-signature">
<span class="method-name">isInteger</span><span class="type">Bool</span>
</div>
<p>Whether the number is an integer (has no fractional part).</p>
<div class="method-signature">
<span class="method-name">isNan</span><span class="type">Bool</span>
</div>
<p>Whether the number is NaN.</p>
<div class="method-signature">
<span class="method-name">isInfinity</span><span class="type">Bool</span>
</div>
<p>Whether the number is positive or negative infinity.</p>
<div class="method-signature">
<span class="method-name">isFinite</span><span class="type">Bool</span>
</div>
<p>Whether the number is finite (not infinity and not NaN).</p>
<pre><code>42.isFinite // true
(1/0).isFinite // false
(0/0).isFinite // false</code></pre>
<div class="method-signature">
<span class="method-name">isZero</span><span class="type">Bool</span>
</div>
<p>Whether the number equals zero.</p>
<div class="method-signature">
<span class="method-name">isPositive</span><span class="type">Bool</span>
</div>
<p>Whether the number is strictly greater than zero.</p>
<div class="method-signature">
<span class="method-name">isNegative</span><span class="type">Bool</span>
</div>
<p>Whether the number is strictly less than zero.</p>
<div class="method-signature">
<span class="method-name">isEven</span><span class="type">Bool</span>
</div>
<p>Whether the number is an even integer.</p>
<div class="method-signature">
<span class="method-name">isOdd</span><span class="type">Bool</span>
</div>
<p>Whether the number is an odd integer.</p>
<div class="method-signature">
<span class="method-name">isBetween</span>(<span class="param">min</span>, <span class="param">max</span>) → <span class="type">Bool</span>
</div>
<p>Whether the number is within the inclusive range [min, max].</p>
<pre><code>5.isBetween(1, 10) // true
0.isBetween(1, 10) // false
10.isBetween(1, 10) // true</code></pre>
<div class="method-signature">
<span class="method-name">sign</span><span class="type">Num</span>
</div>
<p>Returns 1 for positive, -1 for negative, or 0 for zero.</p>
<h2>Arithmetic</h2>
<div class="method-signature">
<span class="method-name">abs</span><span class="type">Num</span>
</div>
<p>Absolute value.</p>
<div class="method-signature">
<span class="method-name">min</span>(<span class="param">other</span>) → <span class="type">Num</span>
</div>
<p>Returns the smaller of this number and <code>other</code>.</p>
<div class="method-signature">
<span class="method-name">max</span>(<span class="param">other</span>) → <span class="type">Num</span>
</div>
<p>Returns the larger of this number and <code>other</code>.</p>
<div class="method-signature">
<span class="method-name">clamp</span>(<span class="param">min</span>, <span class="param">max</span>) → <span class="type">Num</span>
</div>
<p>Constrains this number to the range [min, max].</p>
<pre><code>15.clamp(0, 10) // 10
5.clamp(0, 10) // 5
(-3).clamp(0, 10) // 0</code></pre>
<div class="method-signature">
<span class="method-name">pow</span>(<span class="param">exponent</span>) → <span class="type">Num</span>
</div>
<p>Raises this number to the given power.</p>
<h2>Rounding</h2>
<div class="method-signature">
<span class="method-name">ceil</span><span class="type">Num</span>
</div>
<p>Rounds up to the nearest integer.</p>
<div class="method-signature">
<span class="method-name">floor</span><span class="type">Num</span>
</div>
<p>Rounds down to the nearest integer.</p>
<div class="method-signature">
<span class="method-name">round</span><span class="type">Num</span>
</div>
<p>Rounds to the nearest integer.</p>
<div class="method-signature">
<span class="method-name">truncate</span><span class="type">Num</span>
</div>
<p>Removes the fractional part, rounding toward zero.</p>
<div class="method-signature">
<span class="method-name">fraction</span><span class="type">Num</span>
</div>
<p>Returns the fractional part of the number.</p>
<h2>Roots &amp; Exponents</h2>
<div class="method-signature">
<span class="method-name">sqrt</span><span class="type">Num</span>
</div>
<p>Square root.</p>
<div class="method-signature">
<span class="method-name">cbrt</span><span class="type">Num</span>
</div>
<p>Cube root.</p>
<div class="method-signature">
<span class="method-name">exp</span><span class="type">Num</span>
</div>
<p>Returns e raised to this power.</p>
<h2>Logarithms</h2>
<div class="method-signature">
<span class="method-name">log</span><span class="type">Num</span>
</div>
<p>Natural logarithm (base e).</p>
<div class="method-signature">
<span class="method-name">log2</span><span class="type">Num</span>
</div>
<p>Base-2 logarithm.</p>
<div class="method-signature">
<span class="method-name">log10</span><span class="type">Num</span>
</div>
<p>Base-10 logarithm.</p>
<pre><code>100.log10 // 2
1000.log10 // 3
1.log10 // 0</code></pre>
<h2>Trigonometry</h2>
<div class="method-signature">
<span class="method-name">sin</span><span class="type">Num</span>
</div>
<p>Sine (argument in radians).</p>
<div class="method-signature">
<span class="method-name">cos</span><span class="type">Num</span>
</div>
<p>Cosine (argument in radians).</p>
<div class="method-signature">
<span class="method-name">tan</span><span class="type">Num</span>
</div>
<p>Tangent (argument in radians).</p>
<div class="method-signature">
<span class="method-name">asin</span><span class="type">Num</span>
</div>
<p>Arc sine, returns radians.</p>
<div class="method-signature">
<span class="method-name">acos</span><span class="type">Num</span>
</div>
<p>Arc cosine, returns radians.</p>
<div class="method-signature">
<span class="method-name">atan</span><span class="type">Num</span>
</div>
<p>Arc tangent, returns radians.</p>
<div class="method-signature">
<span class="method-name">atan</span>(<span class="param">x</span>) → <span class="type">Num</span>
</div>
<p>Two-argument arc tangent (atan2). Returns the angle between the positive x-axis and the point (x, this).</p>
<h3>Hyperbolic Functions</h3>
<div class="method-signature">
<span class="method-name">sinh</span><span class="type">Num</span>
</div>
<p>Hyperbolic sine.</p>
<div class="method-signature">
<span class="method-name">cosh</span><span class="type">Num</span>
</div>
<p>Hyperbolic cosine.</p>
<div class="method-signature">
<span class="method-name">tanh</span><span class="type">Num</span>
</div>
<p>Hyperbolic tangent.</p>
<pre><code>0.sinh // 0
0.cosh // 1
0.tanh // 0</code></pre>
<h2>Angle Conversion</h2>
<div class="method-signature">
<span class="method-name">toDegrees</span><span class="type">Num</span>
</div>
<p>Converts radians to degrees.</p>
<div class="method-signature">
<span class="method-name">toRadians</span><span class="type">Num</span>
</div>
<p>Converts degrees to radians.</p>
<pre><code>Num.pi.toDegrees // 180
180.toRadians // 3.14159...</code></pre>
<h2>Conversion</h2>
<div class="method-signature">
<span class="method-name">toString</span><span class="type">String</span>
</div>
<p>Converts the number to its string representation.</p>
<div class="method-signature">
<span class="method-name">toChar</span><span class="type">String</span>
</div>
<p>Returns the Unicode character for this code point.</p>
<pre><code>65.toChar // "A"
97.toChar // "a"</code></pre>
<div class="method-signature">
<span class="method-name">toBase</span>(<span class="param">radix</span>) → <span class="type">String</span>
</div>
<p>Converts the integer part to a string in the given base (2-36).</p>
<pre><code>255.toBase(16) // "ff"
255.toBase(2) // "11111111"
255.toBase(8) // "377"</code></pre>
<div class="method-signature">
<span class="method-name">toHex</span><span class="type">String</span>
</div>
<p>Shorthand for <code>toBase(16)</code>.</p>
<div class="method-signature">
<span class="method-name">toBinary</span><span class="type">String</span>
</div>
<p>Shorthand for <code>toBase(2)</code>.</p>
<div class="method-signature">
<span class="method-name">toOctal</span><span class="type">String</span>
</div>
<p>Shorthand for <code>toBase(8)</code>.</p>
<h2>Formatting</h2>
<div class="method-signature">
<span class="method-name">format</span>(<span class="param">decimals</span>) → <span class="type">String</span>
</div>
<p>Formats the number with a fixed number of decimal places (0-20).</p>
<pre><code>3.14159.format(2) // "3.14"
42.format(3) // "42.000"
(-1.5).format(1) // "-1.5"</code></pre>
<h2>Integer Operations</h2>
<div class="method-signature">
<span class="method-name">gcd</span>(<span class="param">other</span>) → <span class="type">Num</span>
</div>
<p>Greatest common divisor using the Euclidean algorithm. Operates on absolute values.</p>
<pre><code>12.gcd(8) // 4
54.gcd(24) // 6
7.gcd(13) // 1</code></pre>
<div class="method-signature">
<span class="method-name">lcm</span>(<span class="param">other</span>) → <span class="type">Num</span>
</div>
<p>Least common multiple.</p>
<pre><code>4.lcm(6) // 12
3.lcm(5) // 15</code></pre>
<div class="method-signature">
<span class="method-name">digits</span><span class="type">List</span>
</div>
<p>Returns a list of the individual digits of the integer's absolute value. Aborts if the number is not an integer.</p>
<pre><code>123.digits // [1, 2, 3]
0.digits // [0]
(-456).digits // [4, 5, 6]</code></pre>
<h2>Bitwise Operations</h2>
<p>Bitwise operators work on 32-bit unsigned integers.</p>
<table>
<tr>
<th>Operator</th>
<th>Description</th>
</tr>
<tr><td><code>&amp;(other)</code></td><td>Bitwise AND</td></tr>
<tr><td><code>|(other)</code></td><td>Bitwise OR</td></tr>
<tr><td><code>^(other)</code></td><td>Bitwise XOR</td></tr>
<tr><td><code>&lt;&lt;(other)</code></td><td>Left shift</td></tr>
<tr><td><code>&gt;&gt;(other)</code></td><td>Right shift</td></tr>
<tr><td><code>~</code></td><td>Bitwise NOT</td></tr>
</table>
<h2>Ranges</h2>
<div class="method-signature">
<span class="method-name">..</span>(<span class="param">to</span>) → <span class="type">Range</span>
</div>
<p>Creates an inclusive range from this number to <code>to</code>.</p>
<div class="method-signature">
<span class="method-name">...</span>(<span class="param">to</span>) → <span class="type">Range</span>
</div>
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

659
manual/api/pathlib.html Normal file
View File

@ -0,0 +1,659 @@
<!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>
<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>
<pre><code>import "pathlib" for Path</code></pre>
<div class="toc">
<h4>On This Page</h4>
<ul>
<li><a href="#purepath-class">PurePath Class</a></li>
<li><a href="#path-class">Path Class</a></li>
<li><a href="#path-properties">Path Properties</a></li>
<li><a href="#path-manipulation">Path Manipulation</a></li>
<li><a href="#filesystem-operations">Filesystem Operations</a></li>
<li><a href="#directory-operations">Directory Operations</a></li>
<li><a href="#symlink-operations">Symlink Operations</a></li>
<li><a href="#examples">Examples</a></li>
</ul>
</div>
<h2 id="purepath-class">PurePath Class</h2>
<div class="class-header">
<h3>PurePath</h3>
<p>String-only path operations without filesystem access. Base class for <code>Path</code>.</p>
</div>
<h3>Constructor</h3>
<div class="method-signature">
<span class="method-name">PurePath.new</span>(<span class="param">path</span>) &#8594; <span class="type">PurePath</span>
</div>
<p>Creates a new PurePath from a string or another PurePath.</p>
<pre><code>var p = PurePath.new("/home/user/file.txt")</code></pre>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">parts</span> &#8594; <span class="type">List</span>
</div>
<p>The individual components of the path.</p>
<pre><code>var p = Path.new("/home/user/file.txt")
System.print(p.parts) // [/, home, user, file.txt]</code></pre>
<div class="method-signature">
<span class="method-name">name</span> &#8594; <span class="type">String</span>
</div>
<p>The final component of the path (filename with extension).</p>
<pre><code>var p = Path.new("/home/user/file.tar.gz")
System.print(p.name) // file.tar.gz</code></pre>
<div class="method-signature">
<span class="method-name">stem</span> &#8594; <span class="type">String</span>
</div>
<p>The filename without the last extension.</p>
<pre><code>var p = Path.new("/home/user/file.tar.gz")
System.print(p.stem) // file.tar</code></pre>
<div class="method-signature">
<span class="method-name">suffix</span> &#8594; <span class="type">String</span>
</div>
<p>The last file extension, including the leading dot.</p>
<pre><code>var p = Path.new("/home/user/file.tar.gz")
System.print(p.suffix) // .gz</code></pre>
<div class="method-signature">
<span class="method-name">suffixes</span> &#8594; <span class="type">List</span>
</div>
<p>All file extensions as a list.</p>
<pre><code>var p = Path.new("/home/user/file.tar.gz")
System.print(p.suffixes) // [.tar, .gz]</code></pre>
<div class="method-signature">
<span class="method-name">parent</span> &#8594; <span class="type">Path</span>
</div>
<p>The logical parent of the path.</p>
<pre><code>var p = Path.new("/home/user/file.txt")
System.print(p.parent) // /home/user</code></pre>
<div class="method-signature">
<span class="method-name">parents</span> &#8594; <span class="type">List</span>
</div>
<p>A list of all ancestor paths, from immediate parent to root.</p>
<pre><code>var p = Path.new("/home/user/docs/file.txt")
for (ancestor in p.parents) {
System.print(ancestor)
}
// /home/user/docs
// /home/user
// /home
// /</code></pre>
<div class="method-signature">
<span class="method-name">drive</span> &#8594; <span class="type">String</span>
</div>
<p>The drive letter (Windows only). Returns empty string on POSIX.</p>
<div class="method-signature">
<span class="method-name">root</span> &#8594; <span class="type">String</span>
</div>
<p>The root of the path. Returns <code>"/"</code> for absolute paths, empty string for relative.</p>
<div class="method-signature">
<span class="method-name">anchor</span> &#8594; <span class="type">String</span>
</div>
<p>The concatenation of drive and root.</p>
<div class="method-signature">
<span class="method-name">isAbsolute</span> &#8594; <span class="type">Bool</span>
</div>
<p>True if the path is absolute (has a root).</p>
<pre><code>System.print(Path.new("/etc/hosts").isAbsolute) // true
System.print(Path.new("relative/path").isAbsolute) // false</code></pre>
<h2 id="path-manipulation">Path Manipulation</h2>
<div class="method-signature">
<span class="method-name">path</span> / <span class="param">other</span> &#8594; <span class="type">Path</span>
</div>
<p>Joins two paths using the <code>/</code> operator. If <code>other</code> is absolute, it replaces the current path.</p>
<pre><code>var base = Path.new("/home/user")
var full = base / "documents" / "file.txt"
System.print(full) // /home/user/documents/file.txt</code></pre>
<div class="method-signature">
<span class="method-name">joinpath</span>(<span class="param">other</span>) &#8594; <span class="type">Path</span>
</div>
<p>Joins paths. Accepts a string, Path, or List of components.</p>
<pre><code>var p = Path.new("/home").joinpath("user").joinpath("file.txt")
System.print(p) // /home/user/file.txt
var q = Path.new("/home").joinpath(["user", "docs", "file.txt"])
System.print(q) // /home/user/docs/file.txt</code></pre>
<div class="method-signature">
<span class="method-name">withName</span>(<span class="param">newName</span>) &#8594; <span class="type">Path</span>
</div>
<p>Returns a new path with the filename replaced.</p>
<pre><code>var p = Path.new("/home/user/old.txt")
System.print(p.withName("new.txt")) // /home/user/new.txt</code></pre>
<div class="method-signature">
<span class="method-name">withStem</span>(<span class="param">newStem</span>) &#8594; <span class="type">Path</span>
</div>
<p>Returns a new path with the stem replaced, keeping the extension.</p>
<pre><code>var p = Path.new("/data/archive.tar.gz")
System.print(p.withStem("backup")) // /data/backup.gz</code></pre>
<div class="method-signature">
<span class="method-name">withSuffix</span>(<span class="param">newSuffix</span>) &#8594; <span class="type">Path</span>
</div>
<p>Returns a new path with the extension replaced.</p>
<pre><code>var p = Path.new("/data/file.txt")
System.print(p.withSuffix(".md")) // /data/file.md</code></pre>
<div class="method-signature">
<span class="method-name">relativeTo</span>(<span class="param">base</span>) &#8594; <span class="type">Path</span>
</div>
<p>Returns a relative path from <code>base</code> to this path. Aborts if the path is not relative to <code>base</code>.</p>
<pre><code>var p = Path.new("/home/user/docs/file.txt")
System.print(p.relativeTo("/home/user")) // docs/file.txt</code></pre>
<div class="method-signature">
<span class="method-name">match</span>(<span class="param">pattern</span>) &#8594; <span class="type">Bool</span>
</div>
<p>Matches the filename against a glob pattern. Supports <code>*</code> and <code>?</code> wildcards.</p>
<pre><code>var p = Path.new("/home/user/notes.txt")
System.print(p.match("*.txt")) // true
System.print(p.match("*.md")) // false</code></pre>
<div class="method-signature">
<span class="method-name">asPosix</span> &#8594; <span class="type">String</span>
</div>
<p>Returns the path string with forward slashes (converts backslashes on Windows).</p>
<div class="method-signature">
<span class="method-name">expanduser</span>() &#8594; <span class="type">Path</span>
</div>
<p>Expands a leading <code>~</code> to the user's home directory.</p>
<pre><code>var p = Path.new("~/.config/app")
System.print(p.expanduser()) // /home/alice/.config/app</code></pre>
<h3>Operators</h3>
<div class="method-signature">
<span class="method-name">==</span>(<span class="param">other</span>) &#8594; <span class="type">Bool</span>
</div>
<p>Compares two paths for equality by their string representation.</p>
<div class="method-signature">
<span class="method-name">!=</span>(<span class="param">other</span>) &#8594; <span class="type">Bool</span>
</div>
<p>Compares two paths for inequality.</p>
<div class="method-signature">
<span class="method-name">toString</span> &#8594; <span class="type">String</span>
</div>
<p>Returns the path as a string.</p>
<h2 id="path-class">Path Class</h2>
<div class="class-header">
<h3>Path</h3>
<p>Extends PurePath with filesystem operations. This is the primary class for working with paths.</p>
</div>
<h3>Constructor</h3>
<div class="method-signature">
<span class="method-name">Path.new</span>(<span class="param">path</span>) &#8594; <span class="type">Path</span>
</div>
<p>Creates a new Path from a string or another Path.</p>
<pre><code>var p = Path.new("/home/user/file.txt")</code></pre>
<h3>Class Properties</h3>
<div class="method-signature">
<span class="method-name">Path.cwd</span> &#8594; <span class="type">Path</span>
</div>
<p>The current working directory as a Path.</p>
<pre><code>System.print(Path.cwd) // /home/user/projects</code></pre>
<div class="method-signature">
<span class="method-name">Path.home</span> &#8594; <span class="type">Path</span>
</div>
<p>The current user's home directory as a Path.</p>
<pre><code>System.print(Path.home) // /home/alice</code></pre>
<h2 id="filesystem-operations">Filesystem Operations</h2>
<div class="method-signature">
<span class="method-name">exists</span>() &#8594; <span class="type">Bool</span>
</div>
<p>Returns true if the path points to an existing file or directory.</p>
<pre><code>if (Path.new("/etc/hosts").exists()) {
System.print("File exists")
}</code></pre>
<div class="method-signature">
<span class="method-name">isFile</span>() &#8594; <span class="type">Bool</span>
</div>
<p>Returns true if the path points to a regular file.</p>
<div class="method-signature">
<span class="method-name">isDir</span>() &#8594; <span class="type">Bool</span>
</div>
<p>Returns true if the path points to a directory.</p>
<div class="method-signature">
<span class="method-name">isSymlink</span>() &#8594; <span class="type">Bool</span>
</div>
<p>Returns true if the path is a symbolic link.</p>
<div class="method-signature">
<span class="method-name">stat</span>() &#8594; <span class="type">Stat</span>
</div>
<p>Returns a Stat object with file metadata (size, mode, timestamps).</p>
<pre><code>var s = Path.new("/etc/hosts").stat()
System.print(s.size) // 234</code></pre>
<div class="method-signature">
<span class="method-name">lstat</span>() &#8594; <span class="type">Stat</span>
</div>
<p>Like <code>stat()</code> but does not follow symbolic links.</p>
<div class="method-signature">
<span class="method-name">readText</span>() &#8594; <span class="type">String</span>
</div>
<p>Reads the entire file contents as a string.</p>
<pre><code>var content = Path.new("config.json").readText()
System.print(content)</code></pre>
<div class="method-signature">
<span class="method-name">readBytes</span>() &#8594; <span class="type">String</span>
</div>
<p>Reads the entire file contents as bytes.</p>
<div class="method-signature">
<span class="method-name">writeText</span>(<span class="param">content</span>)
</div>
<p>Writes a string to the file, creating or overwriting it.</p>
<pre><code>Path.new("output.txt").writeText("Hello, World!")</code></pre>
<div class="method-signature">
<span class="method-name">writeBytes</span>(<span class="param">content</span>)
</div>
<p>Writes bytes to the file, creating or overwriting it.</p>
<div class="method-signature">
<span class="method-name">touch</span>()
</div>
<p>Creates an empty file if it does not exist, or updates its modification time if it does.</p>
<pre><code>Path.new("marker.txt").touch()</code></pre>
<div class="method-signature">
<span class="method-name">unlink</span>()
</div>
<p>Deletes the file.</p>
<div class="method-signature">
<span class="method-name">rename</span>(<span class="param">target</span>) &#8594; <span class="type">Path</span>
</div>
<p>Moves or renames the file to <code>target</code>. Returns the new Path.</p>
<pre><code>var old = Path.new("draft.txt")
var published = old.rename("article.txt")
System.print(published) // article.txt</code></pre>
<div class="method-signature">
<span class="method-name">replace</span>(<span class="param">target</span>) &#8594; <span class="type">Path</span>
</div>
<p>Alias for <code>rename()</code>. Replaces the target if it exists.</p>
<div class="method-signature">
<span class="method-name">copyfile</span>(<span class="param">dest</span>)
</div>
<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">chmod</span>(<span class="param">mode</span>)
</div>
<p>Changes the file permissions. Mode is an octal number.</p>
<pre><code>Path.new("script.sh").chmod(0x1ed) // 0755 in octal</code></pre>
<div class="method-signature">
<span class="method-name">resolve</span>() &#8594; <span class="type">Path</span>
</div>
<p>Returns the absolute real path, resolving any symlinks.</p>
<pre><code>var real = Path.new("./relative/../file.txt").resolve()
System.print(real) // /home/user/file.txt</code></pre>
<div class="method-signature">
<span class="method-name">samefile</span>(<span class="param">other</span>) &#8594; <span class="type">Bool</span>
</div>
<p>Returns true if this path and <code>other</code> refer to the same file (compares inodes).</p>
<div class="method-signature">
<span class="method-name">owner</span>() &#8594; <span class="type">String</span>
</div>
<p>Returns the username of the file owner (POSIX only).</p>
<div class="method-signature">
<span class="method-name">group</span>() &#8594; <span class="type">String</span>
</div>
<p>Returns the group name of the file (POSIX only).</p>
<h2 id="directory-operations">Directory Operations</h2>
<div class="method-signature">
<span class="method-name">mkdir</span>()
</div>
<p>Creates the directory.</p>
<div class="method-signature">
<span class="method-name">mkdir</span>(<span class="param">parents</span>)
</div>
<p>Creates the directory. If <code>parents</code> is true, creates all intermediate directories.</p>
<pre><code>Path.new("/tmp/a/b/c").mkdir(true)</code></pre>
<div class="method-signature">
<span class="method-name">rmdir</span>()
</div>
<p>Removes an empty directory.</p>
<div class="method-signature">
<span class="method-name">rmtree</span>()
</div>
<p>Recursively deletes the directory and all its contents.</p>
<pre><code>Path.new("/tmp/build_output").rmtree()</code></pre>
<div class="method-signature">
<span class="method-name">iterdir</span>() &#8594; <span class="type">List</span>
</div>
<p>Returns a list of Path objects for each entry in the directory.</p>
<pre><code>for (entry in Path.new(".").iterdir()) {
System.print(entry.name)
}</code></pre>
<div class="method-signature">
<span class="method-name">glob</span>(<span class="param">pattern</span>) &#8594; <span class="type">List</span>
</div>
<p>Returns paths matching the glob pattern in this directory. Supports <code>*</code> and <code>?</code> wildcards.</p>
<pre><code>var txtFiles = Path.new(".").glob("*.txt")
for (f in txtFiles) {
System.print(f.name)
}</code></pre>
<div class="method-signature">
<span class="method-name">rglob</span>(<span class="param">pattern</span>) &#8594; <span class="type">List</span>
</div>
<p>Recursively matches files in this directory and all subdirectories.</p>
<pre><code>var allWren = Path.cwd.rglob("*.wren")
System.print("Found %(allWren.count) Wren files")</code></pre>
<div class="method-signature">
<span class="method-name">walk</span>() &#8594; <span class="type">List</span>
</div>
<p>Recursively walks the directory tree top-down. Returns a list of <code>[dirPath, dirNames, fileNames]</code> tuples.</p>
<pre><code>for (entry in Path.new("src").walk()) {
var dir = entry[0]
var dirs = entry[1]
var files = entry[2]
for (f in files) {
System.print(dir / f)
}
}</code></pre>
<div class="method-signature">
<span class="method-name">walk</span>(<span class="param">topDown</span>) &#8594; <span class="type">List</span>
</div>
<p>Walks the directory tree. If <code>topDown</code> is false, visits child directories before parents.</p>
<h2 id="symlink-operations">Symlink Operations</h2>
<div class="method-signature">
<span class="method-name">symlinkTo</span>(<span class="param">target</span>)
</div>
<p>Creates a symbolic link at this path pointing to <code>target</code>.</p>
<pre><code>Path.new("/tmp/link").symlinkTo("/etc/hosts")</code></pre>
<div class="method-signature">
<span class="method-name">hardlinkTo</span>(<span class="param">target</span>)
</div>
<p>Creates a hard link at this path pointing to <code>target</code>.</p>
<div class="method-signature">
<span class="method-name">readlink</span>() &#8594; <span class="type">String</span>
</div>
<p>Returns the target of the symbolic link.</p>
<h2 id="examples">Examples</h2>
<h3>Configuration File Management</h3>
<pre><code>import "pathlib" for Path
import "json" for Json
var configDir = Path.home / ".config" / "myapp"
if (!configDir.exists()) {
configDir.mkdir(true)
}
var configFile = configDir / "settings.json"
if (!configFile.exists()) {
configFile.writeText(Json.stringify({
"theme": "dark",
"language": "en"
}, 2))
}
var settings = Json.parse(configFile.readText())
System.print("Theme: %(settings["theme"])")</code></pre>
<h3>Batch File Processing</h3>
<pre><code>import "pathlib" for Path
var dataDir = Path.new("./data")
for (file in dataDir.glob("*.csv")) {
var size = file.stat().size
System.print("%(file.name): %(size) bytes")
}
var total = 0
for (wren in Path.cwd.rglob("*.wren")) {
total = total + 1
}
System.print("Found %(total) Wren source files")</code></pre>
<h3>Directory Tree Traversal</h3>
<pre><code>import "pathlib" for Path
var src = Path.new("src")
for (entry in src.walk()) {
var dir = entry[0]
var files = entry[2]
if (files.count > 0) {
System.print("%(dir.relativeTo(src)):")
for (f in files) {
System.print(" %(f)")
}
}
}</code></pre>
<h3>Path Manipulation</h3>
<pre><code>import "pathlib" for Path
var p = Path.new("/home/user/documents/report.tar.gz")
System.print(p.name) // report.tar.gz
System.print(p.stem) // report.tar
System.print(p.suffix) // .gz
System.print(p.suffixes) // [.tar, .gz]
System.print(p.parent) // /home/user/documents
var backup = p.withSuffix(".bak")
System.print(backup) // /home/user/documents/report.tar.bak
var renamed = p.withName("summary.pdf")
System.print(renamed) // /home/user/documents/summary.pdf</code></pre>
<h3>Temporary File Cleanup</h3>
<pre><code>import "pathlib" for Path
var tmpDir = Path.new("/tmp/build_artifacts")
if (tmpDir.exists()) {
tmpDir.rmtree()
}
tmpDir.mkdir(true)
(tmpDir / "output.o").touch()
(tmpDir / "output.bin").touch()
var count = tmpDir.iterdir().count
System.print("%(count) files in build directory")
tmpDir.rmtree()</code></pre>
<h3>Home Directory Expansion</h3>
<pre><code>import "pathlib" for Path
var shortcuts = [
Path.new("~/.bashrc"),
Path.new("~/.config"),
Path.new("~/Documents")
]
for (p in shortcuts) {
var expanded = p.expanduser()
System.print("%(p) -> %(expanded) (exists: %(expanded.exists()))")
}</code></pre>
<div class="admonition note">
<div class="admonition-title">Note</div>
<p>Path objects are immutable. Methods like <code>withName()</code>, <code>withSuffix()</code>, and <code>joinpath()</code> return new Path objects rather than modifying the original.</p>
</div>
<div class="admonition tip">
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>
@ -183,7 +186,7 @@ for (f in fibers) f.call()
</article>
<footer class="page-footer">
<a href="io.html" class="prev">io</a>
<a href="pathlib.html" class="prev">pathlib</a>
<a href="math.html" class="next">math</a>
</footer>
</main>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

454
manual/api/string.html Normal file
View File

@ -0,0 +1,454 @@
<!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>
<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>
<pre><code>var s = "Hello World"
System.print(s.lower) // hello world
System.print(s.upper) // HELLO WORLD
System.print(s.title) // Hello World
System.print(s.reverse) // dlroW olleH</code></pre>
<h2>Case Conversion</h2>
<div class="method-signature">
<span class="method-name">lower</span> &#8594; <span class="type">String</span>
</div>
<p>Returns the string with all ASCII uppercase characters converted to lowercase.</p>
<pre><code>"Hello World".lower // "hello world"
"ABC 123".lower // "abc 123"</code></pre>
<div class="method-signature">
<span class="method-name">upper</span> &#8594; <span class="type">String</span>
</div>
<p>Returns the string with all ASCII lowercase characters converted to uppercase.</p>
<pre><code>"Hello World".upper // "HELLO WORLD"
"abc 123".upper // "ABC 123"</code></pre>
<div class="method-signature">
<span class="method-name">capitalize</span> &#8594; <span class="type">String</span>
</div>
<p>Returns the string with the first character uppercased and the rest lowercased.</p>
<pre><code>"hello world".capitalize // "Hello world"
"HELLO".capitalize // "Hello"</code></pre>
<div class="method-signature">
<span class="method-name">title</span> &#8594; <span class="type">String</span>
</div>
<p>Returns the string with the first character of each word capitalized and the rest lowercased. Words are separated by whitespace.</p>
<pre><code>"hello world".title // "Hello World"
"HELLO WORLD".title // "Hello World"</code></pre>
<div class="method-signature">
<span class="method-name">swapCase</span> &#8594; <span class="type">String</span>
</div>
<p>Returns the string with uppercase characters converted to lowercase and vice versa.</p>
<pre><code>"Hello World".swapCase // "hELLO wORLD"</code></pre>
<h2>Character Testing</h2>
<div class="method-signature">
<span class="method-name">isLower</span> &#8594; <span class="type">Bool</span>
</div>
<p>Returns <code>true</code> if the string contains at least one alphabetic character and all alphabetic characters are lowercase.</p>
<pre><code>"hello".isLower // true
"hello1".isLower // true
"Hello".isLower // false
"123".isLower // false</code></pre>
<div class="method-signature">
<span class="method-name">isUpper</span> &#8594; <span class="type">Bool</span>
</div>
<p>Returns <code>true</code> if the string contains at least one alphabetic character and all alphabetic characters are uppercase.</p>
<pre><code>"HELLO".isUpper // true
"HELLO1".isUpper // true
"Hello".isUpper // false</code></pre>
<div class="method-signature">
<span class="method-name">isDigit</span> &#8594; <span class="type">Bool</span>
</div>
<p>Returns <code>true</code> if the string is non-empty and all bytes are ASCII digits (0-9).</p>
<pre><code>"12345".isDigit // true
"123a5".isDigit // false
"".isDigit // false</code></pre>
<div class="method-signature">
<span class="method-name">isAlpha</span> &#8594; <span class="type">Bool</span>
</div>
<p>Returns <code>true</code> if the string is non-empty and all bytes are ASCII letters (a-z, A-Z).</p>
<pre><code>"hello".isAlpha // true
"hello1".isAlpha // false</code></pre>
<div class="method-signature">
<span class="method-name">isAlphaNumeric</span> &#8594; <span class="type">Bool</span>
</div>
<p>Returns <code>true</code> if the string is non-empty and all bytes are ASCII letters or digits.</p>
<pre><code>"hello123".isAlphaNumeric // true
"hello!".isAlphaNumeric // false</code></pre>
<div class="method-signature">
<span class="method-name">isSpace</span> &#8594; <span class="type">Bool</span>
</div>
<p>Returns <code>true</code> if the string is non-empty and all bytes are ASCII whitespace (space, tab, newline, carriage return, form feed, vertical tab).</p>
<pre><code>" ".isSpace // true
" \t\n".isSpace // true
"hello".isSpace // false</code></pre>
<div class="method-signature">
<span class="method-name">isAscii</span> &#8594; <span class="type">Bool</span>
</div>
<p>Returns <code>true</code> if all bytes in the string are less than 128. Returns <code>true</code> for empty strings.</p>
<pre><code>"hello".isAscii // true
"".isAscii // true</code></pre>
<h2>Search</h2>
<div class="method-signature">
<span class="method-name">lastIndexOf</span>(<span class="param">search</span>) &#8594; <span class="type">Num</span>
</div>
<p>Returns the byte index of the last occurrence of <code>search</code> in the string, or <code>-1</code> if not found.</p>
<ul class="param-list">
<li><span class="param-name">search</span> <span class="param-type">(String)</span> - The substring to search for</li>
<li><span class="returns">Returns:</span> Byte index or -1</li>
</ul>
<pre><code>"hello world hello".lastIndexOf("hello") // 12
"hello".lastIndexOf("x") // -1</code></pre>
<div class="method-signature">
<span class="method-name">lastIndexOf</span>(<span class="param">search</span>, <span class="param">start</span>) &#8594; <span class="type">Num</span>
</div>
<p>Returns the byte index of the last occurrence of <code>search</code> at or before <code>start</code>, or <code>-1</code> if not found.</p>
<ul class="param-list">
<li><span class="param-name">search</span> <span class="param-type">(String)</span> - The substring to search for</li>
<li><span class="param-name">start</span> <span class="param-type">(Num)</span> - Maximum byte index to search from</li>
<li><span class="returns">Returns:</span> Byte index or -1</li>
</ul>
<pre><code>"hello world hello".lastIndexOf("hello", 11) // 0
"hello world hello".lastIndexOf("hello", 12) // 12</code></pre>
<h2>Transformation</h2>
<div class="method-signature">
<span class="method-name">reverse</span> &#8594; <span class="type">String</span>
</div>
<p>Returns the string with characters in reverse order. Code point aware for UTF-8 strings.</p>
<pre><code>"hello".reverse // "olleh"
"abcde".reverse // "edcba"</code></pre>
<div class="method-signature">
<span class="method-name">center</span>(<span class="param">width</span>) &#8594; <span class="type">String</span>
</div>
<p>Centers the string in a field of <code>width</code> characters, padded with spaces.</p>
<pre><code>"hi".center(10) // " hi "</code></pre>
<div class="method-signature">
<span class="method-name">center</span>(<span class="param">width</span>, <span class="param">char</span>) &#8594; <span class="type">String</span>
</div>
<p>Centers the string in a field of <code>width</code> characters, padded with the given fill character.</p>
<pre><code>"hi".center(10, "-") // "----hi----"</code></pre>
<div class="method-signature">
<span class="method-name">lpad</span>(<span class="param">width</span>, <span class="param">char</span>) &#8594; <span class="type">String</span>
</div>
<p>Left-pads the string to <code>width</code> characters using the given fill character.</p>
<pre><code>"42".lpad(5, "0") // "00042"</code></pre>
<div class="method-signature">
<span class="method-name">rpad</span>(<span class="param">width</span>, <span class="param">char</span>) &#8594; <span class="type">String</span>
</div>
<p>Right-pads the string to <code>width</code> characters using the given fill character.</p>
<pre><code>"hi".rpad(5, ".") // "hi..."</code></pre>
<div class="method-signature">
<span class="method-name">zfill</span>(<span class="param">width</span>) &#8594; <span class="type">String</span>
</div>
<p>Pads the string with leading zeros to <code>width</code> characters. Preserves a leading <code>-</code> or <code>+</code> sign.</p>
<pre><code>"42".zfill(5) // "00042"
"-42".zfill(6) // "-00042"
"+42".zfill(6) // "+00042"</code></pre>
<div class="method-signature">
<span class="method-name">removePrefix</span>(<span class="param">prefix</span>) &#8594; <span class="type">String</span>
</div>
<p>If the string starts with <code>prefix</code>, returns the string with the prefix removed. Otherwise returns the original string.</p>
<pre><code>"HelloWorld".removePrefix("Hello") // "World"
"HelloWorld".removePrefix("World") // "HelloWorld"</code></pre>
<div class="method-signature">
<span class="method-name">removeSuffix</span>(<span class="param">suffix</span>) &#8594; <span class="type">String</span>
</div>
<p>If the string ends with <code>suffix</code>, returns the string with the suffix removed. Otherwise returns the original string.</p>
<pre><code>"HelloWorld".removeSuffix("World") // "Hello"
"HelloWorld".removeSuffix("Hello") // "HelloWorld"</code></pre>
<h2>Splitting &amp; Conversion</h2>
<div class="method-signature">
<span class="method-name">splitLines</span> &#8594; <span class="type">List</span>
</div>
<p>Splits the string by line endings (<code>\n</code>, <code>\r\n</code>, <code>\r</code>) and returns a list of strings.</p>
<pre><code>"line1\nline2\nline3".splitLines // ["line1", "line2", "line3"]
"a\r\nb\r\nc".splitLines // ["a", "b", "c"]</code></pre>
<div class="method-signature">
<span class="method-name">chars</span> &#8594; <span class="type">List</span>
</div>
<p>Returns a list of individual characters (code points) in the string.</p>
<pre><code>"abc".chars // ["a", "b", "c"]
"".chars // []</code></pre>
<div class="method-signature">
<span class="method-name">toNum</span> &#8594; <span class="type">Num</span> | <span class="type">Null</span>
</div>
<p>Parses the string as a number. Returns <code>null</code> if the string cannot be parsed.</p>
<pre><code>"42".toNum // 42
"3.14".toNum // 3.14
"-7".toNum // -7
"abc".toNum // null</code></pre>
<h2>Comparison</h2>
<p>Strings support lexicographic comparison using the standard comparison operators. Comparison is byte-by-byte using the raw byte values, which produces correct results for ASCII and UTF-8 encoded text.</p>
<div class="method-signature">
<span class="method-name">&lt;</span>(<span class="param">other</span>) &#8594; <span class="type">Bool</span>
</div>
<p>Returns <code>true</code> if this string is lexicographically less than <code>other</code>.</p>
<ul class="param-list">
<li><span class="param-name">other</span> <span class="param-type">(String)</span> - The string to compare against</li>
</ul>
<pre><code>"apple" < "banana" // true
"abc" < "abd" // true
"abc" < "abcd" // true (prefix is less than longer string)</code></pre>
<div class="method-signature">
<span class="method-name">&gt;</span>(<span class="param">other</span>) &#8594; <span class="type">Bool</span>
</div>
<p>Returns <code>true</code> if this string is lexicographically greater than <code>other</code>.</p>
<ul class="param-list">
<li><span class="param-name">other</span> <span class="param-type">(String)</span> - The string to compare against</li>
</ul>
<pre><code>"banana" > "apple" // true
"abd" > "abc" // true
"abcd" > "abc" // true</code></pre>
<div class="method-signature">
<span class="method-name">&lt;=</span>(<span class="param">other</span>) &#8594; <span class="type">Bool</span>
</div>
<p>Returns <code>true</code> if this string is lexicographically less than or equal to <code>other</code>.</p>
<ul class="param-list">
<li><span class="param-name">other</span> <span class="param-type">(String)</span> - The string to compare against</li>
</ul>
<pre><code>"abc" <= "abc" // true
"abc" <= "abd" // true
"abd" <= "abc" // false</code></pre>
<div class="method-signature">
<span class="method-name">&gt;=</span>(<span class="param">other</span>) &#8594; <span class="type">Bool</span>
</div>
<p>Returns <code>true</code> if this string is lexicographically greater than or equal to <code>other</code>.</p>
<ul class="param-list">
<li><span class="param-name">other</span> <span class="param-type">(String)</span> - The string to compare against</li>
</ul>
<pre><code>"abc" >= "abc" // true
"abd" >= "abc" // true
"abc" >= "abd" // false</code></pre>
<div class="admonition tip">
<div class="admonition-title">Tip</div>
<p>String comparison is byte-based, so uppercase letters sort before lowercase letters in ASCII order (e.g., <code>"Z" &lt; "a"</code> is <code>true</code>). If you need case-insensitive comparison, convert both strings to the same case first using <code>lower</code> or <code>upper</code>.</p>
</div>
<h2>Existing Methods</h2>
<p>The following methods are also available on all strings as part of the core VM.</p>
<table>
<tr>
<th>Method</th>
<th>Description</th>
</tr>
<tr>
<td><code>contains(s)</code></td>
<td>Returns true if the string contains <code>s</code></td>
</tr>
<tr>
<td><code>startsWith(s)</code></td>
<td>Returns true if the string starts with <code>s</code></td>
</tr>
<tr>
<td><code>endsWith(s)</code></td>
<td>Returns true if the string ends with <code>s</code></td>
</tr>
<tr>
<td><code>indexOf(s)</code></td>
<td>Returns the byte index of <code>s</code>, or -1</td>
</tr>
<tr>
<td><code>indexOf(s, start)</code></td>
<td>Returns the byte index of <code>s</code> starting from <code>start</code></td>
</tr>
<tr>
<td><code>split(delim)</code></td>
<td>Splits by delimiter, returns a list</td>
</tr>
<tr>
<td><code>replace(from, to)</code></td>
<td>Replaces all occurrences of <code>from</code> with <code>to</code></td>
</tr>
<tr>
<td><code>trim()</code></td>
<td>Removes leading and trailing whitespace</td>
</tr>
<tr>
<td><code>trimStart()</code></td>
<td>Removes leading whitespace</td>
</tr>
<tr>
<td><code>trimEnd()</code></td>
<td>Removes trailing whitespace</td>
</tr>
<tr>
<td><code>bytes</code></td>
<td>Returns a sequence of byte values</td>
</tr>
<tr>
<td><code>codePoints</code></td>
<td>Returns a sequence of code point values</td>
</tr>
<tr>
<td><code>count</code></td>
<td>Number of code points in the string</td>
</tr>
<tr>
<td><code>isEmpty</code></td>
<td>Returns true if the string has no characters</td>
</tr>
<tr>
<td><code>*(n)</code></td>
<td>Repeats the string <code>n</code> times</td>
</tr>
</table>
<div class="admonition note">
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

370
manual/api/tempfile.html Normal file
View File

@ -0,0 +1,370 @@
<!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>
<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>
<pre><code>import "tempfile" for TempFile, NamedTemporaryFile, TemporaryDirectory</code></pre>
<h2>TempFile Class</h2>
<div class="class-header">
<h3>TempFile</h3>
<p>Low-level temporary file and directory utilities</p>
</div>
<h3>Static Properties</h3>
<div class="method-signature">
<span class="method-name">TempFile.tempdir</span><span class="type">String|Null</span>
</div>
<p>Gets or sets a global override for the temporary directory. When set, <code>gettempdir()</code> returns this value instead of checking environment variables.</p>
<pre><code>TempFile.tempdir = "/my/custom/tmp"
System.print(TempFile.gettempdir()) // /my/custom/tmp
TempFile.tempdir = null // Reset to default</code></pre>
<h3>Static Methods</h3>
<div class="method-signature">
<span class="method-name">TempFile.gettempdir</span>() → <span class="type">String</span>
</div>
<p>Returns the resolved temporary directory path. Checks in order: the <code>tempdir</code> override, then <code>TMPDIR</code>, <code>TEMP</code>, <code>TMP</code> environment variables, and falls back to <code>/tmp</code> on POSIX or <code>C:\Temp</code> on Windows.</p>
<div class="method-signature">
<span class="method-name">TempFile.gettempprefix</span>() → <span class="type">String</span>
</div>
<p>Returns the default prefix for temporary file names: <code>"tmp"</code>.</p>
<div class="method-signature">
<span class="method-name">TempFile.mktemp</span>() → <span class="type">String</span><br>
<span class="method-name">TempFile.mktemp</span>(<span class="param">suffix</span>) → <span class="type">String</span><br>
<span class="method-name">TempFile.mktemp</span>(<span class="param">suffix</span>, <span class="param">prefix</span>) → <span class="type">String</span><br>
<span class="method-name">TempFile.mktemp</span>(<span class="param">suffix</span>, <span class="param">prefix</span>, <span class="param">dir</span>) → <span class="type">String</span>
</div>
<p>Generates a unique temporary file path without creating any file. The generated name consists of the prefix, 8 random hexadecimal characters, and the suffix.</p>
<ul class="param-list">
<li><span class="param-name">suffix</span> <span class="param-type">(String)</span> - Suffix for the filename (default: <code>""</code>)</li>
<li><span class="param-name">prefix</span> <span class="param-type">(String)</span> - Prefix for the filename (default: <code>"tmp"</code>)</li>
<li><span class="param-name">dir</span> <span class="param-type">(String)</span> - Directory for the path (default: <code>gettempdir()</code>)</li>
<li><span class="returns">Returns:</span> A file path string (file is not created)</li>
</ul>
<pre><code>var name = TempFile.mktemp(".txt", "data_")
System.print(name) // e.g., /tmp/data_a1b2c3d4.txt</code></pre>
<div class="method-signature">
<span class="method-name">TempFile.mkstemp</span>() → <span class="type">String</span><br>
<span class="method-name">TempFile.mkstemp</span>(<span class="param">suffix</span>) → <span class="type">String</span><br>
<span class="method-name">TempFile.mkstemp</span>(<span class="param">suffix</span>, <span class="param">prefix</span>) → <span class="type">String</span><br>
<span class="method-name">TempFile.mkstemp</span>(<span class="param">suffix</span>, <span class="param">prefix</span>, <span class="param">dir</span>) → <span class="type">String</span>
</div>
<p>Creates a temporary file atomically using exclusive creation flags (<code>O_EXCL</code>), preventing race conditions. Retries up to 100 times on name collisions.</p>
<ul class="param-list">
<li><span class="param-name">suffix</span> <span class="param-type">(String)</span> - Suffix for the filename (default: <code>""</code>)</li>
<li><span class="param-name">prefix</span> <span class="param-type">(String)</span> - Prefix for the filename (default: <code>"tmp"</code>)</li>
<li><span class="param-name">dir</span> <span class="param-type">(String)</span> - Directory for the file (default: <code>gettempdir()</code>)</li>
<li><span class="returns">Returns:</span> The path of the newly created file</li>
</ul>
<pre><code>import "io" for File
var path = TempFile.mkstemp(".dat", "work_")
System.print(path) // e.g., /tmp/work_e5f6a7b8.dat
File.delete(path) // Caller must clean up</code></pre>
<div class="method-signature">
<span class="method-name">TempFile.mkdtemp</span>() → <span class="type">String</span><br>
<span class="method-name">TempFile.mkdtemp</span>(<span class="param">suffix</span>) → <span class="type">String</span><br>
<span class="method-name">TempFile.mkdtemp</span>(<span class="param">suffix</span>, <span class="param">prefix</span>) → <span class="type">String</span><br>
<span class="method-name">TempFile.mkdtemp</span>(<span class="param">suffix</span>, <span class="param">prefix</span>, <span class="param">dir</span>) → <span class="type">String</span>
</div>
<p>Creates a temporary directory. Retries up to 100 times on name collisions.</p>
<ul class="param-list">
<li><span class="param-name">suffix</span> <span class="param-type">(String)</span> - Suffix for the directory name (default: <code>""</code>)</li>
<li><span class="param-name">prefix</span> <span class="param-type">(String)</span> - Prefix for the directory name (default: <code>"tmp"</code>)</li>
<li><span class="param-name">dir</span> <span class="param-type">(String)</span> - Parent directory (default: <code>gettempdir()</code>)</li>
<li><span class="returns">Returns:</span> The path of the newly created directory</li>
</ul>
<pre><code>import "io" for Directory
var dir = TempFile.mkdtemp("_work")
System.print(dir) // e.g., /tmp/tmp9c0d1e2f_work
Directory.delete(dir) // Caller must clean up</code></pre>
<h2>NamedTemporaryFile Class</h2>
<div class="class-header">
<h3>NamedTemporaryFile</h3>
<p>A temporary file with a visible name that is optionally deleted on close</p>
</div>
<h3>Constructors</h3>
<div class="method-signature">
<span class="method-name">NamedTemporaryFile.new</span>() → <span class="type">NamedTemporaryFile</span><br>
<span class="method-name">NamedTemporaryFile.new</span>(<span class="param">suffix</span>) → <span class="type">NamedTemporaryFile</span><br>
<span class="method-name">NamedTemporaryFile.new</span>(<span class="param">suffix</span>, <span class="param">prefix</span>) → <span class="type">NamedTemporaryFile</span><br>
<span class="method-name">NamedTemporaryFile.new</span>(<span class="param">suffix</span>, <span class="param">prefix</span>, <span class="param">dir</span>) → <span class="type">NamedTemporaryFile</span><br>
<span class="method-name">NamedTemporaryFile.new</span>(<span class="param">suffix</span>, <span class="param">prefix</span>, <span class="param">dir</span>, <span class="param">delete</span>) → <span class="type">NamedTemporaryFile</span>
</div>
<ul class="param-list">
<li><span class="param-name">suffix</span> <span class="param-type">(String)</span> - Suffix for the filename (default: <code>""</code>)</li>
<li><span class="param-name">prefix</span> <span class="param-type">(String)</span> - Prefix for the filename (default: <code>"tmp"</code>)</li>
<li><span class="param-name">dir</span> <span class="param-type">(String|Null)</span> - Directory for the file (default: <code>null</code>, uses <code>TempFile.gettempdir()</code>)</li>
<li><span class="param-name">delete</span> <span class="param-type">(Bool)</span> - Whether to delete the file on close (default: <code>true</code>)</li>
</ul>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">.name</span><span class="type">String</span>
</div>
<p>The full path of the temporary file.</p>
<div class="method-signature">
<span class="method-name">.path</span><span class="type">Path</span>
</div>
<p>A <code>Path</code> object for the temporary file.</p>
<div class="method-signature">
<span class="method-name">.delete</span><span class="type">Bool</span>
</div>
<p>Whether the file will be deleted on close. Can be set.</p>
<div class="method-signature">
<span class="method-name">.closed</span><span class="type">Bool</span>
</div>
<p>Whether the file has been closed.</p>
<h3>Methods</h3>
<div class="method-signature">
<span class="method-name">.write</span>(<span class="param">content</span>)
</div>
<p>Writes string content to the file, replacing any existing content.</p>
<div class="method-signature">
<span class="method-name">.read</span>() → <span class="type">String</span>
</div>
<p>Reads and returns the file content as a string.</p>
<div class="method-signature">
<span class="method-name">.close</span>()
</div>
<p>Closes the file. If <code>delete</code> is <code>true</code>, the file is deleted from disk.</p>
<div class="method-signature">
<span class="method-name">.use</span>(<span class="param">fn</span>) → <span class="type">*</span>
</div>
<p>Executes <code>fn</code> with the file as argument and guarantees cleanup afterwards, even if an error occurs. Equivalent to Python's <code>with</code> statement.</p>
<pre><code>NamedTemporaryFile.new(".txt").use {|f|
f.write("temporary data")
System.print(f.read())
}
// File is automatically deleted here</code></pre>
<h2>TemporaryDirectory Class</h2>
<div class="class-header">
<h3>TemporaryDirectory</h3>
<p>A temporary directory with recursive cleanup support</p>
</div>
<h3>Constructors</h3>
<div class="method-signature">
<span class="method-name">TemporaryDirectory.new</span>() → <span class="type">TemporaryDirectory</span><br>
<span class="method-name">TemporaryDirectory.new</span>(<span class="param">suffix</span>) → <span class="type">TemporaryDirectory</span><br>
<span class="method-name">TemporaryDirectory.new</span>(<span class="param">suffix</span>, <span class="param">prefix</span>) → <span class="type">TemporaryDirectory</span><br>
<span class="method-name">TemporaryDirectory.new</span>(<span class="param">suffix</span>, <span class="param">prefix</span>, <span class="param">dir</span>) → <span class="type">TemporaryDirectory</span><br>
<span class="method-name">TemporaryDirectory.new</span>(<span class="param">suffix</span>, <span class="param">prefix</span>, <span class="param">dir</span>, <span class="param">delete</span>) → <span class="type">TemporaryDirectory</span>
</div>
<ul class="param-list">
<li><span class="param-name">suffix</span> <span class="param-type">(String)</span> - Suffix for the directory name (default: <code>""</code>)</li>
<li><span class="param-name">prefix</span> <span class="param-type">(String)</span> - Prefix for the directory name (default: <code>"tmp"</code>)</li>
<li><span class="param-name">dir</span> <span class="param-type">(String|Null)</span> - Parent directory (default: <code>null</code>, uses <code>TempFile.gettempdir()</code>)</li>
<li><span class="param-name">delete</span> <span class="param-type">(Bool)</span> - Whether to delete the directory on cleanup (default: <code>true</code>)</li>
</ul>
<h3>Properties</h3>
<div class="method-signature">
<span class="method-name">.name</span><span class="type">String</span>
</div>
<p>The full path of the temporary directory.</p>
<div class="method-signature">
<span class="method-name">.path</span><span class="type">Path</span>
</div>
<p>A <code>Path</code> object for the temporary directory.</p>
<div class="method-signature">
<span class="method-name">.delete</span><span class="type">Bool</span>
</div>
<p>Whether the directory will be deleted on cleanup. Can be set.</p>
<div class="method-signature">
<span class="method-name">.closed</span><span class="type">Bool</span>
</div>
<p>Whether the directory has been cleaned up.</p>
<h3>Methods</h3>
<div class="method-signature">
<span class="method-name">.cleanup</span>()
</div>
<p>Removes the directory and all its contents recursively (using <code>Path.rmtree()</code>). If <code>delete</code> is <code>false</code>, the directory is not removed.</p>
<div class="method-signature">
<span class="method-name">.use</span>(<span class="param">fn</span>) → <span class="type">*</span>
</div>
<p>Executes <code>fn</code> with the directory as argument and guarantees cleanup afterwards, even if an error occurs.</p>
<pre><code>TemporaryDirectory.new().use {|d|
var path = d.name + "/data.txt"
// Create files, do work...
}
// Directory and all contents automatically removed</code></pre>
<h2>Examples</h2>
<h3>Processing Files Safely</h3>
<pre><code>import "tempfile" for NamedTemporaryFile
import "pathlib" for Path
NamedTemporaryFile.new(".csv", "report_").use {|tmp|
tmp.write("name,value\nalice,42\nbob,17")
var data = tmp.read()
System.print("Processing %(tmp.name)")
System.print(data)
}</code></pre>
<h3>Working Directory for Build Output</h3>
<pre><code>import "tempfile" for TemporaryDirectory
import "pathlib" for Path
TemporaryDirectory.new("_build").use {|build|
var src = Path.new(build.name) / "output.txt"
src.writeText("Build artifact")
System.print("Build dir: %(build.name)")
}
// Build directory cleaned up automatically</code></pre>
<h3>Persistent Temporary Files</h3>
<pre><code>import "tempfile" for NamedTemporaryFile
var tmp = NamedTemporaryFile.new(".log", "app_", null, false)
tmp.write("Application started")
tmp.close()
// File persists at tmp.name after close
System.print("Log file: %(tmp.name)")</code></pre>
<div class="admonition note">
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>
@ -139,7 +142,7 @@ sudo apt-get install build-essential libssl-dev git python3</code></pre>
<h3>Build</h3>
<pre><code>git clone https://github.com/wren-lang/wren-cli.git
cd wren-cli
cd projects/make && make</code></pre>
make build</code></pre>
<p>The binary will be created at <code>bin/wren_cli</code>.</p>
@ -221,7 +224,7 @@ cd projects/make.bsd && gmake</code></pre>
<pre><code>bin/wren_cli --version</code></pre>
<p>Run the test suite:</p>
<pre><code>python3 util/test.py</code></pre>
<pre><code>make tests</code></pre>
<p>Start the REPL:</p>
<pre><code>bin/wren_cli</code></pre>
@ -242,7 +245,7 @@ cd projects/make.bsd && gmake</code></pre>
<h3>Build Errors</h3>
<p>Try cleaning and rebuilding:</p>
<pre><code>make clean
make</code></pre>
make build</code></pre>
<h3>Test Failures</h3>
<p>Run a specific test module to isolate issues:</p>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>
@ -391,7 +394,7 @@ System.print("Total size: %(size) bytes")</code></pre>
<div class="admonition tip">
<div class="admonition-title">See Also</div>
<p>For full API documentation, see the <a href="../api/io.html">IO module reference</a>.</p>
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -58,6 +58,7 @@
<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>
@ -148,6 +149,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>
@ -215,6 +218,11 @@ System.print(data["name"])</code></pre>
<td>File I/O</td>
<td>File, Directory, Stdin</td>
</tr>
<tr>
<td><a href="../api/pathlib.html">pathlib</a></td>
<td>Filesystem paths</td>
<td>Path, PurePath</td>
</tr>
<tr>
<td><a href="../api/scheduler.html">scheduler</a></td>
<td>Async scheduling</td>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

View File

@ -40,6 +40,8 @@
<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>
@ -58,6 +60,7 @@
<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>

20
recursion_error.wren vendored Normal file
View File

@ -0,0 +1,20 @@
import "io" for File, Directory
var listWrenFiles
listWrenFiles = Fn.new { |path|
var items = Directory.list(path)
for (item in items) {
var fullPath = "%(path)/%(item)"
if (Directory.exists(fullPath)) {
listWrenFiles.call(fullPath)
} else if (item.endsWith(".wren")) {
System.print(fullPath)
}
}
}
listWrenFiles.call(".")

View File

@ -28,6 +28,7 @@
#include "signal.wren.inc"
#include "sqlite.wren.inc"
#include "subprocess.wren.inc"
#include "tempfile.wren.inc"
#include "timer.wren.inc"
#include "tls.wren.inc"
#include "uuid.wren.inc"
@ -504,6 +505,8 @@ static ModuleRegistry modules[] =
STATIC_METHOD("run_(_,_)", subprocessRun)
END_CLASS
END_MODULE
MODULE(tempfile)
END_MODULE
MODULE(timer)
CLASS(Timer)
STATIC_METHOD("startTimer_(_,_)", timerStartTimer)

View File

@ -156,7 +156,7 @@ void directoryCreate(WrenVM* vm)
{
const char* path = wrenGetSlotString(vm, 1);
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 2));
uv_fs_mkdir(getLoop(), request, path, 0, fileDirectoryCallback);
uv_fs_mkdir(getLoop(), request, path, 0777, fileDirectoryCallback);
}
void directoryDelete(WrenVM* vm)

81
src/module/jinja.wren vendored
View File

@ -1,6 +1,7 @@
// retoor <retoor@molodetz.nl>
import "io" for File
import "markdown" for Markdown
class Token {
static TEXT { "TEXT" }
@ -745,6 +746,34 @@ class FilterBlockNode is Node {
}
}
class MarkdownToHtmlNode is Node {
construct new(body) {
_body = body
}
render(ctx) {
var content = ""
for (node in _body) {
content = content + node.render(ctx)
}
return Markdown.toHtml(content)
}
}
class MarkdownFromHtmlNode is Node {
construct new(body) {
_body = body
}
render(ctx) {
var content = ""
for (node in _body) {
content = content + node.render(ctx)
}
return Markdown.fromHtml(content)
}
}
class Expression {
evaluate(ctx) { null }
}
@ -1172,6 +1201,8 @@ class Filters {
if (name == "indent") return indent(value, args.count > 0 ? args[0] : 4, args.count > 1 ? args[1] : false)
if (name == "tojson") return tojson(value)
if (name == "debug") return debug(value)
if (name == "markdown") return SafeString.new(Markdown.toHtml(value.toString))
if (name == "markdownfromhtml") return Markdown.fromHtml(value.toString)
Fiber.abort(TemplateError.new("Unknown filter: %(name)", 0, 0).toString)
}
@ -1801,6 +1832,8 @@ class Parser {
if (tagName == "macro") return parseMacro_()
if (tagName == "raw") return parseRaw_()
if (tagName == "filter") return parseFilterBlock_()
if (tagName == "markdowntohtml") return parseMarkdownToHtml_()
if (tagName == "markdownfromhtml") return parseMarkdownFromHtml_()
Fiber.abort(TemplateError.new("Unknown block tag: %(tagName)", nameToken.line, nameToken.col).toString)
}
@ -2180,6 +2213,54 @@ class Parser {
Fiber.abort(TemplateError.new("Unclosed filter tag", 0, 0).toString)
}
parseMarkdownToHtml_() {
consumeBlockEnd_()
var body = []
while (!isAtEnd_()) {
var token = peek_()
if (token.type == Token.BLOCK_START || token.type == Token.BLOCK_START_TRIM) {
advance_()
var nextName = peek_()
if (nextName.type == Token.NAME && nextName.value == "endmarkdowntohtml") {
advance_()
consumeBlockEnd_()
return MarkdownToHtmlNode.new(body)
}
_pos = _pos - 1
var innerNode = parseInnerNode_()
if (innerNode != null) body.add(innerNode)
} else {
var innerNode = parseInnerNode_()
if (innerNode != null) body.add(innerNode)
}
}
Fiber.abort(TemplateError.new("Unclosed markdowntohtml tag", 0, 0).toString)
}
parseMarkdownFromHtml_() {
consumeBlockEnd_()
var body = []
while (!isAtEnd_()) {
var token = peek_()
if (token.type == Token.BLOCK_START || token.type == Token.BLOCK_START_TRIM) {
advance_()
var nextName = peek_()
if (nextName.type == Token.NAME && nextName.value == "endmarkdownfromhtml") {
advance_()
consumeBlockEnd_()
return MarkdownFromHtmlNode.new(body)
}
_pos = _pos - 1
var innerNode = parseInnerNode_()
if (innerNode != null) body.add(innerNode)
} else {
var innerNode = parseInnerNode_()
if (innerNode != null) body.add(innerNode)
}
}
Fiber.abort(TemplateError.new("Unclosed markdownfromhtml tag", 0, 0).toString)
}
parseInnerNode_() {
var token = peek_()

View File

@ -5,6 +5,7 @@ static const char* jinjaModuleSource =
"// retoor <retoor@molodetz.nl>\n"
"\n"
"import \"io\" for File\n"
"import \"markdown\" for Markdown\n"
"\n"
"class Token {\n"
" static TEXT { \"TEXT\" }\n"
@ -749,6 +750,34 @@ static const char* jinjaModuleSource =
" }\n"
"}\n"
"\n"
"class MarkdownToHtmlNode is Node {\n"
" construct new(body) {\n"
" _body = body\n"
" }\n"
"\n"
" render(ctx) {\n"
" var content = \"\"\n"
" for (node in _body) {\n"
" content = content + node.render(ctx)\n"
" }\n"
" return Markdown.toHtml(content)\n"
" }\n"
"}\n"
"\n"
"class MarkdownFromHtmlNode is Node {\n"
" construct new(body) {\n"
" _body = body\n"
" }\n"
"\n"
" render(ctx) {\n"
" var content = \"\"\n"
" for (node in _body) {\n"
" content = content + node.render(ctx)\n"
" }\n"
" return Markdown.fromHtml(content)\n"
" }\n"
"}\n"
"\n"
"class Expression {\n"
" evaluate(ctx) { null }\n"
"}\n"
@ -1176,6 +1205,8 @@ static const char* jinjaModuleSource =
" if (name == \"indent\") return indent(value, args.count > 0 ? args[0] : 4, args.count > 1 ? args[1] : false)\n"
" if (name == \"tojson\") return tojson(value)\n"
" if (name == \"debug\") return debug(value)\n"
" if (name == \"markdown\") return SafeString.new(Markdown.toHtml(value.toString))\n"
" if (name == \"markdownfromhtml\") return Markdown.fromHtml(value.toString)\n"
" Fiber.abort(TemplateError.new(\"Unknown filter: %(name)\", 0, 0).toString)\n"
" }\n"
"\n"
@ -1805,6 +1836,8 @@ static const char* jinjaModuleSource =
" if (tagName == \"macro\") return parseMacro_()\n"
" if (tagName == \"raw\") return parseRaw_()\n"
" if (tagName == \"filter\") return parseFilterBlock_()\n"
" if (tagName == \"markdowntohtml\") return parseMarkdownToHtml_()\n"
" if (tagName == \"markdownfromhtml\") return parseMarkdownFromHtml_()\n"
"\n"
" Fiber.abort(TemplateError.new(\"Unknown block tag: %(tagName)\", nameToken.line, nameToken.col).toString)\n"
" }\n"
@ -2184,6 +2217,54 @@ static const char* jinjaModuleSource =
" Fiber.abort(TemplateError.new(\"Unclosed filter tag\", 0, 0).toString)\n"
" }\n"
"\n"
" parseMarkdownToHtml_() {\n"
" consumeBlockEnd_()\n"
" var body = []\n"
" while (!isAtEnd_()) {\n"
" var token = peek_()\n"
" if (token.type == Token.BLOCK_START || token.type == Token.BLOCK_START_TRIM) {\n"
" advance_()\n"
" var nextName = peek_()\n"
" if (nextName.type == Token.NAME && nextName.value == \"endmarkdowntohtml\") {\n"
" advance_()\n"
" consumeBlockEnd_()\n"
" return MarkdownToHtmlNode.new(body)\n"
" }\n"
" _pos = _pos - 1\n"
" var innerNode = parseInnerNode_()\n"
" if (innerNode != null) body.add(innerNode)\n"
" } else {\n"
" var innerNode = parseInnerNode_()\n"
" if (innerNode != null) body.add(innerNode)\n"
" }\n"
" }\n"
" Fiber.abort(TemplateError.new(\"Unclosed markdowntohtml tag\", 0, 0).toString)\n"
" }\n"
"\n"
" parseMarkdownFromHtml_() {\n"
" consumeBlockEnd_()\n"
" var body = []\n"
" while (!isAtEnd_()) {\n"
" var token = peek_()\n"
" if (token.type == Token.BLOCK_START || token.type == Token.BLOCK_START_TRIM) {\n"
" advance_()\n"
" var nextName = peek_()\n"
" if (nextName.type == Token.NAME && nextName.value == \"endmarkdownfromhtml\") {\n"
" advance_()\n"
" consumeBlockEnd_()\n"
" return MarkdownFromHtmlNode.new(body)\n"
" }\n"
" _pos = _pos - 1\n"
" var innerNode = parseInnerNode_()\n"
" if (innerNode != null) body.add(innerNode)\n"
" } else {\n"
" var innerNode = parseInnerNode_()\n"
" if (innerNode != null) body.add(innerNode)\n"
" }\n"
" }\n"
" Fiber.abort(TemplateError.new(\"Unclosed markdownfromhtml tag\", 0, 0).toString)\n"
" }\n"
"\n"
" parseInnerNode_() {\n"
" var token = peek_()\n"
"\n"

View File

@ -205,10 +205,14 @@ static void allocCallback(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* b
static void readCallback(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
{
if (nread == 0) {
free(buf->base);
return;
}
SocketData* data = (SocketData*)stream->data;
WrenHandle* fiber = data->fiber;
// Stop reading immediately. We only want one chunk.
uv_read_stop(stream);
data->fiber = NULL;

View File

@ -8,15 +8,28 @@ class Scheduler {
})
}
// Called by native code.
static resume_(fiber) { fiber.transfer() }
static resume_(fiber, arg) { fiber.transfer(arg) }
static resumeError_(fiber, error) { fiber.transferError(error) }
// wait for a method to finish that has a callback on the C side
static await_(fn) {
fn.call()
return Scheduler.runNextScheduled_()
static await_(arg) {
if (arg is Fn) {
arg.call()
return Scheduler.runNextScheduled_()
}
if (arg is Future) return arg.await_()
return arg
}
static async_(fn) {
return Future.new(fn)
}
static scheduleTransfer_(fiber, value) {
if (__scheduled == null) __scheduled = []
__scheduled.add(Fiber.new {
fiber.transfer(value)
})
}
static runNextScheduled_() {
@ -30,4 +43,34 @@ class Scheduler {
foreign static captureMethods_()
}
class Future {
construct new(fn) {
_resolved = false
_result = null
_waiters = []
var self = this
Scheduler.add {
self.resolve_(fn.call())
}
}
resolve_(value) {
_resolved = true
_result = value
for (w in _waiters) {
Scheduler.scheduleTransfer_(w, value)
}
_waiters = []
}
await_() {
if (_resolved) return _result
_waiters.add(Fiber.current)
return Scheduler.runNextScheduled_()
}
isDone { _resolved }
result { _result }
}
Scheduler.captureMethods_()

View File

@ -12,15 +12,28 @@ static const char* schedulerModuleSource =
" })\n"
" }\n"
"\n"
" // Called by native code.\n"
" static resume_(fiber) { fiber.transfer() }\n"
" static resume_(fiber, arg) { fiber.transfer(arg) }\n"
" static resumeError_(fiber, error) { fiber.transferError(error) }\n"
"\n"
" // wait for a method to finish that has a callback on the C side\n"
" static await_(fn) {\n"
" fn.call()\n"
" return Scheduler.runNextScheduled_()\n"
" static await_(arg) {\n"
" if (arg is Fn) {\n"
" arg.call()\n"
" return Scheduler.runNextScheduled_()\n"
" }\n"
" if (arg is Future) return arg.await_()\n"
" return arg\n"
" }\n"
"\n"
" static async_(fn) {\n"
" return Future.new(fn)\n"
" }\n"
"\n"
" static scheduleTransfer_(fiber, value) {\n"
" if (__scheduled == null) __scheduled = []\n"
" __scheduled.add(Fiber.new {\n"
" fiber.transfer(value)\n"
" })\n"
" }\n"
"\n"
" static runNextScheduled_() {\n"
@ -34,4 +47,34 @@ static const char* schedulerModuleSource =
" foreign static captureMethods_()\n"
"}\n"
"\n"
"class Future {\n"
" construct new(fn) {\n"
" _resolved = false\n"
" _result = null\n"
" _waiters = []\n"
" var self = this\n"
" Scheduler.add {\n"
" self.resolve_(fn.call())\n"
" }\n"
" }\n"
"\n"
" resolve_(value) {\n"
" _resolved = true\n"
" _result = value\n"
" for (w in _waiters) {\n"
" Scheduler.scheduleTransfer_(w, value)\n"
" }\n"
" _waiters = []\n"
" }\n"
"\n"
" await_() {\n"
" if (_resolved) return _result\n"
" _waiters.add(Fiber.current)\n"
" return Scheduler.runNextScheduled_()\n"
" }\n"
"\n"
" isDone { _resolved }\n"
" result { _result }\n"
"}\n"
"\n"
"Scheduler.captureMethods_()\n";

173
src/module/tempfile.wren vendored Normal file
View File

@ -0,0 +1,173 @@
// retoor <retoor@molodetz.nl>
import "io" for Directory, File, FileFlags
import "os" for Platform
import "env" for Environment
import "crypto" for Crypto
import "strutil" for Str
import "bytes" for Bytes
import "pathlib" for Path
class TempFile {
static tempdir { __tempdir }
static tempdir=(value) { __tempdir = value }
static gettempdir() {
if (__tempdir != null) return __tempdir
var candidates = ["TMPDIR", "TEMP", "TMP"]
for (name in candidates) {
var value = Environment.get(name)
if (value != null && value.count > 0) return value
}
if (Platform.isPosix) return "/tmp"
return "C:\\Temp"
}
static gettempprefix() { "tmp" }
static randomSuffix_() {
var bytes = Crypto.randomBytes(4)
return Str.hexEncode(Bytes.fromList(bytes))
}
static mktemp() { mktemp("", gettempprefix(), gettempdir()) }
static mktemp(suffix) { mktemp(suffix, gettempprefix(), gettempdir()) }
static mktemp(suffix, prefix) { mktemp(suffix, prefix, gettempdir()) }
static mktemp(suffix, prefix, dir) {
var name = prefix + randomSuffix_() + suffix
return dir + "/" + name
}
static mkstemp() { mkstemp("", gettempprefix(), gettempdir()) }
static mkstemp(suffix) { mkstemp(suffix, gettempprefix(), gettempdir()) }
static mkstemp(suffix, prefix) { mkstemp(suffix, prefix, gettempdir()) }
static mkstemp(suffix, prefix, dir) {
var flags = FileFlags.writeOnly | FileFlags.create | FileFlags.exclusive
for (i in 0...100) {
var name = prefix + randomSuffix_() + suffix
var path = dir + "/" + name
var file
var error
var fiber = Fiber.new {
file = File.openWithFlags(path, flags)
}
fiber.try()
if (fiber.error == null && file != null) {
file.close()
return path
}
}
Fiber.abort("Failed to create temporary file after 100 attempts.")
}
static mkdtemp() { mkdtemp("", gettempprefix(), gettempdir()) }
static mkdtemp(suffix) { mkdtemp(suffix, gettempprefix(), gettempdir()) }
static mkdtemp(suffix, prefix) { mkdtemp(suffix, prefix, gettempdir()) }
static mkdtemp(suffix, prefix, dir) {
for (i in 0...100) {
var name = prefix + randomSuffix_() + suffix
var path = dir + "/" + name
var p = Path.new(path)
if (!p.exists()) {
Directory.create(path)
return path
}
}
Fiber.abort("Failed to create temporary directory after 100 attempts.")
}
}
class NamedTemporaryFile {
construct new() { init_("", TempFile.gettempprefix(), TempFile.gettempdir(), true) }
construct new(suffix) { init_(suffix, TempFile.gettempprefix(), TempFile.gettempdir(), true) }
construct new(suffix, prefix) { init_(suffix, prefix, TempFile.gettempdir(), true) }
construct new(suffix, prefix, dir) { init_(suffix, prefix, dir, true) }
construct new(suffix, prefix, dir, delete) { init_(suffix, prefix, dir, delete) }
init_(suffix, prefix, dir, delete) {
var d = dir != null ? dir : TempFile.gettempdir()
_name = TempFile.mkstemp(suffix, prefix, d)
_delete = delete
_closed = false
}
name { _name }
path { Path.new(_name) }
delete { _delete }
delete=(value) { _delete = value }
closed { _closed }
write(content) {
if (_closed) Fiber.abort("File is closed.")
File.openWithFlags(_name, FileFlags.writeOnly | FileFlags.create) {|f|
f.writeBytes(content, 0)
}
}
read() {
if (_closed) Fiber.abort("File is closed.")
return File.read(_name)
}
close() {
if (_closed) return
_closed = true
if (_delete) {
var fiber = Fiber.new { File.delete(_name) }
fiber.try()
}
}
use(fn) {
var fiber = Fiber.new { fn.call(this) }
var result = fiber.try()
close()
if (fiber.error != null) Fiber.abort(fiber.error)
return result
}
toString { "NamedTemporaryFile(%(name))" }
}
class TemporaryDirectory {
construct new() { init_("", TempFile.gettempprefix(), TempFile.gettempdir(), true) }
construct new(suffix) { init_(suffix, TempFile.gettempprefix(), TempFile.gettempdir(), true) }
construct new(suffix, prefix) { init_(suffix, prefix, TempFile.gettempdir(), true) }
construct new(suffix, prefix, dir) { init_(suffix, prefix, dir, true) }
construct new(suffix, prefix, dir, delete) { init_(suffix, prefix, dir, delete) }
init_(suffix, prefix, dir, delete) {
var d = dir != null ? dir : TempFile.gettempdir()
_name = TempFile.mkdtemp(suffix, prefix, d)
_delete = delete
_closed = false
}
name { _name }
path { Path.new(_name) }
delete { _delete }
delete=(value) { _delete = value }
closed { _closed }
cleanup() {
if (_closed) return
_closed = true
if (_delete) {
var fiber = Fiber.new { Path.new(_name).rmtree() }
fiber.try()
}
}
use(fn) {
var fiber = Fiber.new { fn.call(this) }
var result = fiber.try()
cleanup()
if (fiber.error != null) Fiber.abort(fiber.error)
return result
}
toString { "TemporaryDirectory(%(name))" }
}

View File

@ -0,0 +1,177 @@
// Please do not edit this file. It has been generated automatically
// from `src/module/tempfile.wren` using `util/wren_to_c_string.py`
static const char* tempfileModuleSource =
"// retoor <retoor@molodetz.nl>\n"
"\n"
"import \"io\" for Directory, File, FileFlags\n"
"import \"os\" for Platform\n"
"import \"env\" for Environment\n"
"import \"crypto\" for Crypto\n"
"import \"strutil\" for Str\n"
"import \"bytes\" for Bytes\n"
"import \"pathlib\" for Path\n"
"\n"
"class TempFile {\n"
" static tempdir { __tempdir }\n"
" static tempdir=(value) { __tempdir = value }\n"
"\n"
" static gettempdir() {\n"
" if (__tempdir != null) return __tempdir\n"
" var candidates = [\"TMPDIR\", \"TEMP\", \"TMP\"]\n"
" for (name in candidates) {\n"
" var value = Environment.get(name)\n"
" if (value != null && value.count > 0) return value\n"
" }\n"
" if (Platform.isPosix) return \"/tmp\"\n"
" return \"C:\\\\Temp\"\n"
" }\n"
"\n"
" static gettempprefix() { \"tmp\" }\n"
"\n"
" static randomSuffix_() {\n"
" var bytes = Crypto.randomBytes(4)\n"
" return Str.hexEncode(Bytes.fromList(bytes))\n"
" }\n"
"\n"
" static mktemp() { mktemp(\"\", gettempprefix(), gettempdir()) }\n"
" static mktemp(suffix) { mktemp(suffix, gettempprefix(), gettempdir()) }\n"
" static mktemp(suffix, prefix) { mktemp(suffix, prefix, gettempdir()) }\n"
"\n"
" static mktemp(suffix, prefix, dir) {\n"
" var name = prefix + randomSuffix_() + suffix\n"
" return dir + \"/\" + name\n"
" }\n"
"\n"
" static mkstemp() { mkstemp(\"\", gettempprefix(), gettempdir()) }\n"
" static mkstemp(suffix) { mkstemp(suffix, gettempprefix(), gettempdir()) }\n"
" static mkstemp(suffix, prefix) { mkstemp(suffix, prefix, gettempdir()) }\n"
"\n"
" static mkstemp(suffix, prefix, dir) {\n"
" var flags = FileFlags.writeOnly | FileFlags.create | FileFlags.exclusive\n"
" for (i in 0...100) {\n"
" var name = prefix + randomSuffix_() + suffix\n"
" var path = dir + \"/\" + name\n"
" var file\n"
" var error\n"
" var fiber = Fiber.new {\n"
" file = File.openWithFlags(path, flags)\n"
" }\n"
" fiber.try()\n"
" if (fiber.error == null && file != null) {\n"
" file.close()\n"
" return path\n"
" }\n"
" }\n"
" Fiber.abort(\"Failed to create temporary file after 100 attempts.\")\n"
" }\n"
"\n"
" static mkdtemp() { mkdtemp(\"\", gettempprefix(), gettempdir()) }\n"
" static mkdtemp(suffix) { mkdtemp(suffix, gettempprefix(), gettempdir()) }\n"
" static mkdtemp(suffix, prefix) { mkdtemp(suffix, prefix, gettempdir()) }\n"
"\n"
" static mkdtemp(suffix, prefix, dir) {\n"
" for (i in 0...100) {\n"
" var name = prefix + randomSuffix_() + suffix\n"
" var path = dir + \"/\" + name\n"
" var p = Path.new(path)\n"
" if (!p.exists()) {\n"
" Directory.create(path)\n"
" return path\n"
" }\n"
" }\n"
" Fiber.abort(\"Failed to create temporary directory after 100 attempts.\")\n"
" }\n"
"}\n"
"\n"
"class NamedTemporaryFile {\n"
" construct new() { init_(\"\", TempFile.gettempprefix(), TempFile.gettempdir(), true) }\n"
" construct new(suffix) { init_(suffix, TempFile.gettempprefix(), TempFile.gettempdir(), true) }\n"
" construct new(suffix, prefix) { init_(suffix, prefix, TempFile.gettempdir(), true) }\n"
" construct new(suffix, prefix, dir) { init_(suffix, prefix, dir, true) }\n"
" construct new(suffix, prefix, dir, delete) { init_(suffix, prefix, dir, delete) }\n"
"\n"
" init_(suffix, prefix, dir, delete) {\n"
" var d = dir != null ? dir : TempFile.gettempdir()\n"
" _name = TempFile.mkstemp(suffix, prefix, d)\n"
" _delete = delete\n"
" _closed = false\n"
" }\n"
"\n"
" name { _name }\n"
" path { Path.new(_name) }\n"
" delete { _delete }\n"
" delete=(value) { _delete = value }\n"
" closed { _closed }\n"
"\n"
" write(content) {\n"
" if (_closed) Fiber.abort(\"File is closed.\")\n"
" File.openWithFlags(_name, FileFlags.writeOnly | FileFlags.create) {|f|\n"
" f.writeBytes(content, 0)\n"
" }\n"
" }\n"
"\n"
" read() {\n"
" if (_closed) Fiber.abort(\"File is closed.\")\n"
" return File.read(_name)\n"
" }\n"
"\n"
" close() {\n"
" if (_closed) return\n"
" _closed = true\n"
" if (_delete) {\n"
" var fiber = Fiber.new { File.delete(_name) }\n"
" fiber.try()\n"
" }\n"
" }\n"
"\n"
" use(fn) {\n"
" var fiber = Fiber.new { fn.call(this) }\n"
" var result = fiber.try()\n"
" close()\n"
" if (fiber.error != null) Fiber.abort(fiber.error)\n"
" return result\n"
" }\n"
"\n"
" toString { \"NamedTemporaryFile(%(name))\" }\n"
"}\n"
"\n"
"class TemporaryDirectory {\n"
" construct new() { init_(\"\", TempFile.gettempprefix(), TempFile.gettempdir(), true) }\n"
" construct new(suffix) { init_(suffix, TempFile.gettempprefix(), TempFile.gettempdir(), true) }\n"
" construct new(suffix, prefix) { init_(suffix, prefix, TempFile.gettempdir(), true) }\n"
" construct new(suffix, prefix, dir) { init_(suffix, prefix, dir, true) }\n"
" construct new(suffix, prefix, dir, delete) { init_(suffix, prefix, dir, delete) }\n"
"\n"
" init_(suffix, prefix, dir, delete) {\n"
" var d = dir != null ? dir : TempFile.gettempdir()\n"
" _name = TempFile.mkdtemp(suffix, prefix, d)\n"
" _delete = delete\n"
" _closed = false\n"
" }\n"
"\n"
" name { _name }\n"
" path { Path.new(_name) }\n"
" delete { _delete }\n"
" delete=(value) { _delete = value }\n"
" closed { _closed }\n"
"\n"
" cleanup() {\n"
" if (_closed) return\n"
" _closed = true\n"
" if (_delete) {\n"
" var fiber = Fiber.new { Path.new(_name).rmtree() }\n"
" fiber.try()\n"
" }\n"
" }\n"
"\n"
" use(fn) {\n"
" var fiber = Fiber.new { fn.call(this) }\n"
" var result = fiber.try()\n"
" cleanup()\n"
" if (fiber.error != null) Fiber.abort(fiber.error)\n"
" return result\n"
" }\n"
"\n"
" toString { \"TemporaryDirectory(%(name))\" }\n"
"}\n";

15
test/argparse/choices.wren vendored Normal file
View File

@ -0,0 +1,15 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new()
parser.addArgument("-l", {"long": "--level", "choices": ["debug", "info", "warn", "error"]})
var args = parser.parseArgs(["-l", "info"])
System.print(args["level"]) // expect: info
var args2 = parser.parseArgs(["--level", "error"])
System.print(args2["level"]) // expect: error
var args3 = parser.parseArgs(["--level", "debug"])
System.print(args3["level"]) // expect: debug

View File

@ -0,0 +1,8 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new()
parser.addArgument("-l", {"choices": ["debug", "info", "warn", "error"]})
var args = parser.parseArgs(["-l", "trace"]) // expect runtime error: Invalid choice 'trace' for -l

View File

@ -0,0 +1,8 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new()
parser.addArgument("-o", {"required": true})
var args = parser.parseArgs([]) // expect runtime error: Missing required option: -o

View File

@ -0,0 +1,8 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new()
parser.addArgument("filename")
var args = parser.parseArgs([]) // expect runtime error: Missing required argument: filename

8
test/argparse/error_nargs_plus.wren vendored Normal file
View File

@ -0,0 +1,8 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new()
parser.addArgument("-f", {"nargs": "+"})
var args = parser.parseArgs(["-f"]) // expect runtime error: Option -f requires at least one value

View File

@ -0,0 +1,7 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new()
var args = parser.parseArgs(["extra"]) // expect runtime error: Unexpected argument: extra

View File

@ -0,0 +1,7 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new()
var args = parser.parseArgs(["--unknown"]) // expect runtime error: Unknown option: --unknown

21
test/argparse/help.wren vendored Normal file
View File

@ -0,0 +1,21 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new("A test program")
parser.prog = "myapp"
parser.addArgument("input", {"help": "Input file path"})
parser.addArgument("-o", {"long": "--output", "help": "Output file path"})
parser.addArgument("-v", {"long": "--verbose", "action": "storeTrue", "help": "Enable verbose mode"})
parser.printHelp()
// expect: usage: myapp [options] input
// expect:
// expect: A test program
// expect:
// expect: positional arguments:
// expect: input Input file path
// expect:
// expect: optional arguments:
// expect: -o, --output Output file path
// expect: -v, --verbose Enable verbose mode

21
test/argparse/mixed.wren vendored Normal file
View File

@ -0,0 +1,21 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new()
parser.addArgument("input")
parser.addArgument("-o", {"long": "--output", "default": "out.txt"})
parser.addArgument("-v", {"action": "storeTrue"})
parser.addArgument("-n", {"type": "int", "default": 1})
var args = parser.parseArgs(["data.csv", "-o", "result.json", "-v", "-n", "10"])
System.print(args["input"]) // expect: data.csv
System.print(args["output"]) // expect: result.json
System.print(args["v"]) // expect: true
System.print(args["n"]) // expect: 10
var args2 = parser.parseArgs(["data.csv"])
System.print(args2["input"]) // expect: data.csv
System.print(args2["output"]) // expect: out.txt
System.print(args2["v"]) // expect: false
System.print(args2["n"]) // expect: 1

16
test/argparse/nargs_num.wren vendored Normal file
View File

@ -0,0 +1,16 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new()
parser.addArgument("-p", {"long": "--point", "nargs": 2, "type": "int"})
var args = parser.parseArgs(["-p", "10", "20"])
System.print(args["point"].count) // expect: 2
System.print(args["point"][0]) // expect: 10
System.print(args["point"][1]) // expect: 20
var args2 = parser.parseArgs(["--point", "5", "15"])
System.print(args2["point"].count) // expect: 2
System.print(args2["point"][0]) // expect: 5
System.print(args2["point"][1]) // expect: 15

15
test/argparse/nargs_plus.wren vendored Normal file
View File

@ -0,0 +1,15 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new()
parser.addArgument("-f", {"long": "--files", "nargs": "+"})
var args = parser.parseArgs(["-f", "a.txt", "b.txt"])
System.print(args["files"].count) // expect: 2
System.print(args["files"][0]) // expect: a.txt
System.print(args["files"][1]) // expect: b.txt
var args2 = parser.parseArgs(["--files", "single.txt"])
System.print(args2["files"].count) // expect: 1
System.print(args2["files"][0]) // expect: single.txt

20
test/argparse/nargs_positional.wren vendored Normal file
View File

@ -0,0 +1,20 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new()
parser.addArgument("files", {"nargs": "*", "required": false})
var args = parser.parseArgs(["a.txt", "b.txt", "c.txt"])
System.print(args["files"].count) // expect: 3
System.print(args["files"][0]) // expect: a.txt
System.print(args["files"][1]) // expect: b.txt
System.print(args["files"][2]) // expect: c.txt
var parser2 = ArgumentParser.new()
parser2.addArgument("files", {"nargs": "+"})
var args2 = parser2.parseArgs(["x.txt", "y.txt"])
System.print(args2["files"].count) // expect: 2
System.print(args2["files"][0]) // expect: x.txt
System.print(args2["files"][1]) // expect: y.txt

15
test/argparse/nargs_star.wren vendored Normal file
View File

@ -0,0 +1,15 @@
// retoor <retoor@molodetz.nl>
import "argparse" for ArgumentParser
var parser = ArgumentParser.new()
parser.addArgument("-f", {"long": "--files", "nargs": "*"})
var args = parser.parseArgs(["-f", "a.txt", "b.txt", "c.txt"])
System.print(args["files"].count) // expect: 3
System.print(args["files"][0]) // expect: a.txt
System.print(args["files"][1]) // expect: b.txt
System.print(args["files"][2]) // expect: c.txt
var args2 = parser.parseArgs(["--files"])
System.print(args2["files"].count) // expect: 0

View File

@ -0,0 +1,8 @@
import "scheduler" for Scheduler, Future
import "timer" for Timer
var task = async { "ready" }
await Timer.sleep(10)
System.print(task.isDone) // expect: true
System.print(await task) // expect: ready

7
test/await/async_basic.wren vendored Normal file
View File

@ -0,0 +1,7 @@
import "scheduler" for Scheduler, Future
import "timer" for Timer
var task = async { 42 }
var result = await task
System.print(result) // expect: 42

18
test/await/async_concurrent.wren vendored Normal file
View File

@ -0,0 +1,18 @@
import "scheduler" for Scheduler, Future
import "timer" for Timer
var order = []
var task1 = async {
await Timer.sleep(2)
order.add("task1")
}
var task2 = async {
await Timer.sleep(1)
order.add("task2")
}
await task1
await task2
System.print(order) // expect: [task2, task1]

5
test/await/basic.wren vendored Normal file
View File

@ -0,0 +1,5 @@
import "scheduler" for Scheduler
import "timer" for Timer
await Timer.sleep(1)
System.print("done") // expect: done

11
test/await/in_method.wren vendored Normal file
View File

@ -0,0 +1,11 @@
import "scheduler" for Scheduler
import "timer" for Timer
class Helper {
static run() {
await Timer.sleep(1)
return "completed"
}
}
System.print(Helper.run()) // expect: completed

6
test/await/return_value.wren vendored Normal file
View File

@ -0,0 +1,6 @@
import "scheduler" for Scheduler
import "timer" for Timer
var result = await Timer.sleep(1)
System.print(result == null) // expect: true
System.print("done") // expect: done

51
test/core/string/comparison.wren vendored Normal file
View File

@ -0,0 +1,51 @@
// retoor <retoor@molodetz.nl>
System.print("apple" < "banana") // expect: true
System.print("banana" > "apple") // expect: true
System.print("abc" <= "abc") // expect: true
System.print("abc" >= "abc") // expect: true
System.print("abc" < "abd") // expect: true
System.print("abd" > "abc") // expect: true
System.print("abd" < "abc") // expect: false
System.print("abc" > "abd") // expect: false
System.print("abc" < "abcd") // expect: true
System.print("abcd" > "abc") // expect: true
System.print("abcd" < "abc") // expect: false
System.print("abc" > "abcd") // expect: false
System.print("a" <= "b") // expect: true
System.print("b" >= "a") // expect: true
System.print("b" <= "a") // expect: false
System.print("a" >= "b") // expect: false
System.print("b" < "a") // expect: false
System.print("a" > "b") // expect: false
System.print("" < "a") // expect: true
System.print("a" > "") // expect: true
System.print("" <= "") // expect: true
System.print("" >= "") // expect: true
System.print("" < "") // expect: false
System.print("" > "") // expect: false
System.print("a" < "a") // expect: false
System.print("a" > "a") // expect: false
System.print("a" <= "a") // expect: true
System.print("a" >= "a") // expect: true
System.print("A" < "a") // expect: true
System.print("a" > "A") // expect: true
System.print("Z" < "a") // expect: true
System.print("a" > "Z") // expect: true
System.print("abc" < "abc") // expect: false
System.print("abc" > "abc") // expect: false
System.print("abc" <= "abc") // expect: true
System.print("abc" >= "abc") // expect: true
System.print("xyz" < "xya") // expect: false
System.print("xyz" > "xya") // expect: true
System.print("xyz" <= "xya") // expect: false
System.print("xyz" >= "xya") // expect: true

21
test/jinja/markdown_tags.wren vendored Normal file
View File

@ -0,0 +1,21 @@
// retoor <retoor@molodetz.nl>
import "jinja" for Environment, DictLoader
var env = Environment.new(DictLoader.new({
"md_heading": "{\% markdowntohtml \%}# Hello{\% endmarkdowntohtml \%}",
"md_bold": "{\% markdowntohtml \%}**bold**{\% endmarkdowntohtml \%}",
"md_var": "{\% markdowntohtml \%}# {{ title }}{\% endmarkdowntohtml \%}",
"html_to_md": "{\% markdownfromhtml \%}<h1>Hello</h1>{\% endmarkdownfromhtml \%}",
"html_to_md_bold": "{\% markdownfromhtml \%}<p><strong>bold</strong></p>{\% endmarkdownfromhtml \%}",
"filter_md": "{{ text|markdown }}",
"filter_fromhtml": "{{ html|markdownfromhtml }}"
}))
System.print(env.getTemplate("md_heading").render({})) // expect: <h1>Hello</h1>
System.print(env.getTemplate("md_bold").render({})) // expect: <p><strong>bold</strong></p>
System.print(env.getTemplate("md_var").render({"title": "World"})) // expect: <h1>World</h1>
System.print(env.getTemplate("html_to_md").render({})) // expect: # Hello
System.print(env.getTemplate("html_to_md_bold").render({})) // expect: **bold**
System.print(env.getTemplate("filter_md").render({"text": "**strong**"})) // expect: <p><strong>strong</strong></p>
System.print(env.getTemplate("filter_fromhtml").render({"html": "<h1>Title</h1>"})) // expect: # Title

6
test/number/angle.wren vendored Normal file
View File

@ -0,0 +1,6 @@
// retoor <retoor@molodetz.nl>
System.print(Num.pi.toDegrees) // expect: 180
System.print(180.toRadians == Num.pi) // expect: true
System.print(0.toDegrees) // expect: 0
System.print(0.toRadians) // expect: 0

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