This commit is contained in:
retoor 2026-01-25 22:09:29 +01:00
parent 1ee28a1644
commit a59dbe1d55
27 changed files with 3435 additions and 2 deletions

71
example/pexpect_demo.wren vendored Normal file
View File

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

105
example/yaml_demo.wren vendored Normal file
View File

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

View File

@ -5,6 +5,7 @@
#include "argparse.wren.inc"
#include "base64.wren.inc"
#include "pexpect.wren.inc"
#include "strutil.wren.inc"
#include "bytes.wren.inc"
#include "crypto.wren.inc"
@ -39,6 +40,7 @@
#include "wdantic.wren.inc"
#include "web.wren.inc"
#include "websocket.wren.inc"
#include "yaml.wren.inc"
extern void base64Encode(WrenVM* vm);
extern void base64Decode(WrenVM* vm);
@ -242,6 +244,47 @@ extern void pathlibSamefile(WrenVM* vm);
extern void pathlibGlob(WrenVM* vm);
extern void pathlibOwner(WrenVM* vm);
extern void pathlibGroup(WrenVM* vm);
extern void spawnAllocate(WrenVM* vm);
extern void spawnFinalize(void* data);
extern void spawnSend(WrenVM* vm);
extern void spawnSendcontrol(WrenVM* vm);
extern void spawnExpect(WrenVM* vm);
extern void spawnExpectExact(WrenVM* vm);
extern void spawnReadNonblocking(WrenVM* vm);
extern void spawnInteract(WrenVM* vm);
extern void spawnTerminate(WrenVM* vm);
extern void spawnKill(WrenVM* vm);
extern void spawnWait(WrenVM* vm);
extern void spawnIsalive(WrenVM* vm);
extern void spawnClose(WrenVM* vm);
extern void spawnSetecho(WrenVM* vm);
extern void spawnWaitnoecho(WrenVM* vm);
extern void spawnGetBuffer(WrenVM* vm);
extern void spawnSetBuffer(WrenVM* vm);
extern void spawnGetBefore(WrenVM* vm);
extern void spawnGetAfter(WrenVM* vm);
extern void spawnGetMatch(WrenVM* vm);
extern void spawnGetMatchIndex(WrenVM* vm);
extern void spawnGetPid(WrenVM* vm);
extern void spawnGetExitstatus(WrenVM* vm);
extern void spawnGetSignalstatus(WrenVM* vm);
extern void spawnGetTerminated(WrenVM* vm);
extern void spawnGetTimeout(WrenVM* vm);
extern void spawnSetTimeout(WrenVM* vm);
extern void spawnGetMaxread(WrenVM* vm);
extern void spawnSetMaxread(WrenVM* vm);
extern void spawnGetSearchwindowsize(WrenVM* vm);
extern void spawnSetSearchwindowsize(WrenVM* vm);
extern void spawnGetLogfile(WrenVM* vm);
extern void spawnSetLogfile(WrenVM* vm);
extern void spawnGetLogfileRead(WrenVM* vm);
extern void spawnSetLogfileRead(WrenVM* vm);
extern void spawnGetLogfileSend(WrenVM* vm);
extern void spawnSetLogfileSend(WrenVM* vm);
extern void spawnGetDelaybeforesend(WrenVM* vm);
extern void spawnSetDelaybeforesend(WrenVM* vm);
extern void spawnGetDelayafterclose(WrenVM* vm);
extern void spawnSetDelayafterclose(WrenVM* vm);
// The maximum number of foreign methods a single class defines. Ideally, we
// would use variable-length arrays for each class in the table below, but
@ -251,7 +294,7 @@ extern void pathlibGroup(WrenVM* vm);
// If you add a new method to the longest class below, make sure to bump this.
// Note that it also includes an extra slot for the sentinel value indicating
// the end of the list.
#define MAX_METHODS_PER_CLASS 24
#define MAX_METHODS_PER_CLASS 48
// The maximum number of foreign classes a single built-in module defines.
//
@ -514,6 +557,51 @@ static ModuleRegistry modules[] =
STATIC_METHOD("group_(_,_)", pathlibGroup)
END_CLASS
END_MODULE
MODULE(pexpect)
CLASS(Spawn)
ALLOCATE(spawnAllocate)
FINALIZE(spawnFinalize)
METHOD("send_(_)", spawnSend)
METHOD("sendcontrol(_)", spawnSendcontrol)
METHOD("expect_(_,_,_,_)", spawnExpect)
METHOD("expectExact_(_,_,_,_)", spawnExpectExact)
METHOD("readNonblocking_(_,_,_)", spawnReadNonblocking)
METHOD("interact_(_,_,_)", spawnInteract)
METHOD("terminate_(_)", spawnTerminate)
METHOD("kill_(_)", spawnKill)
METHOD("wait_(_)", spawnWait)
METHOD("isalive", spawnIsalive)
METHOD("close_(_)", spawnClose)
METHOD("setecho(_)", spawnSetecho)
METHOD("waitnoecho_(_,_)", spawnWaitnoecho)
METHOD("buffer", spawnGetBuffer)
METHOD("buffer=(_)", spawnSetBuffer)
METHOD("before", spawnGetBefore)
METHOD("after", spawnGetAfter)
METHOD("match", spawnGetMatch)
METHOD("matchIndex", spawnGetMatchIndex)
METHOD("pid", spawnGetPid)
METHOD("exitstatus", spawnGetExitstatus)
METHOD("signalstatus", spawnGetSignalstatus)
METHOD("terminated", spawnGetTerminated)
METHOD("timeout", spawnGetTimeout)
METHOD("timeout=(_)", spawnSetTimeout)
METHOD("maxread", spawnGetMaxread)
METHOD("maxread=(_)", spawnSetMaxread)
METHOD("searchwindowsize", spawnGetSearchwindowsize)
METHOD("searchwindowsize=(_)", spawnSetSearchwindowsize)
METHOD("logfile", spawnGetLogfile)
METHOD("logfile=(_)", spawnSetLogfile)
METHOD("logfileRead", spawnGetLogfileRead)
METHOD("logfileRead=(_)", spawnSetLogfileRead)
METHOD("logfileSend", spawnGetLogfileSend)
METHOD("logfileSend=(_)", spawnSetLogfileSend)
METHOD("delaybeforesend", spawnGetDelaybeforesend)
METHOD("delaybeforesend=(_)", spawnSetDelaybeforesend)
METHOD("delayafterclose", spawnGetDelayafterclose)
METHOD("delayafterclose=(_)", spawnSetDelayafterclose)
END_CLASS
END_MODULE
MODULE(regex)
CLASS(Regex)
ALLOCATE(regexAllocate)
@ -648,6 +736,8 @@ static ModuleRegistry modules[] =
END_MODULE
MODULE(websocket)
END_MODULE
MODULE(yaml)
END_MODULE
SENTINEL_MODULE
};

110
src/module/jinja.wren vendored
View File

@ -19,6 +19,8 @@ class Token {
static RPAREN { "RPAREN" }
static LBRACKET { "LBRACKET" }
static RBRACKET { "RBRACKET" }
static LBRACE { "LBRACE" }
static RBRACE { "RBRACE" }
static COLON { "COLON" }
static ASSIGN { "ASSIGN" }
static EQ { "EQ" }
@ -260,6 +262,18 @@ class Lexer {
return
}
if (c == "{") {
advance_()
_tokens.add(Token.new(Token.LBRACE, c, startLine, startCol))
return
}
if (c == "}") {
advance_()
_tokens.add(Token.new(Token.RBRACE, c, startLine, startCol))
return
}
if (c == ":") {
advance_()
_tokens.add(Token.new(Token.COLON, c, startLine, startCol))
@ -667,6 +681,37 @@ class IncludeNode is Node {
}
}
class FromImportNode is Node {
construct new(templateName, imports) {
_templateName = templateName
_imports = imports
}
render(ctx) {
var env = ctx.get("__env__")
if (env == null) {
Fiber.abort(TemplateError.new("Cannot import without environment", 0, 0).toString)
}
var template = env.getTemplate(_templateName)
collectMacros_(template.nodes, ctx)
return ""
}
collectMacros_(nodes, ctx) {
for (node in nodes) {
if (node is MacroNode) {
for (imp in _imports) {
var name = imp[0]
var alias = imp[1]
if (node.name == name) {
ctx.setMacro(alias, node)
}
}
}
}
}
}
class SetNode is Node {
construct new(name, expr) {
_name = name
@ -1834,6 +1879,7 @@ class Parser {
if (tagName == "filter") return parseFilterBlock_()
if (tagName == "markdowntohtml") return parseMarkdownToHtml_()
if (tagName == "markdownfromhtml") return parseMarkdownFromHtml_()
if (tagName == "from") return parseFrom_()
Fiber.abort(TemplateError.new("Unknown block tag: %(tagName)", nameToken.line, nameToken.col).toString)
}
@ -2045,6 +2091,36 @@ class Parser {
return IncludeNode.new(templateName, withContext)
}
parseFrom_() {
var templateName = ""
if (check_(Token.STRING)) {
templateName = advance_().value
} else {
Fiber.abort(TemplateError.new("Expected template name after 'from'", peek_().line, peek_().col).toString)
}
if (!checkName_("import")) {
Fiber.abort(TemplateError.new("Expected 'import' after template name", peek_().line, peek_().col).toString)
}
advance_()
var imports = []
while (true) {
var name = consume_(Token.NAME).value
var alias = name
if (checkName_("as")) {
advance_()
alias = consume_(Token.NAME).value
}
imports.add([name, alias])
if (!check_(Token.COMMA)) break
advance_()
}
consumeBlockEnd_()
return FromImportNode.new(templateName, imports)
}
parseSet_() {
var varName = consume_(Token.NAME).value
consume_(Token.ASSIGN)
@ -2532,6 +2608,20 @@ class Parser {
return ListExpr.new(items)
}
if (token.type == Token.LBRACE) {
advance_()
var pairs = []
while (!check_(Token.RBRACE)) {
var key = parseExpression_()
consume_(Token.COLON)
var value = parseExpression_()
pairs.add([key, value])
if (!check_(Token.RBRACE)) consume_(Token.COMMA)
}
consume_(Token.RBRACE)
return MapExpr.new(pairs)
}
if (token.type == Token.LPAREN) {
advance_()
var expr = parseExpression_()
@ -2693,6 +2783,26 @@ class FileSystemLoader {
}
}
class ChoiceLoader {
construct new(loaders) {
_loaders = loaders
}
getSource(name) {
for (loader in _loaders) {
if (loader.exists(name)) return loader.getSource(name)
}
Fiber.abort(TemplateError.new("Template not found: %(name)", 0, 0).toString)
}
exists(name) {
for (loader in _loaders) {
if (loader.exists(name)) return true
}
return false
}
}
class Environment {
construct new(loader) {
_loader = loader

View File

@ -1,5 +1,5 @@
// Please do not edit this file. It has been generated automatically
// from `src/module/jinja.wren` using `util/wren_to_c_string.py`
// from `/home/retoor/projects/wren-cli/src/module/jinja.wren` using `util/wren_to_c_string.py`
static const char* jinjaModuleSource =
"// retoor <retoor@molodetz.nl>\n"
@ -23,6 +23,8 @@ static const char* jinjaModuleSource =
" static RPAREN { \"RPAREN\" }\n"
" static LBRACKET { \"LBRACKET\" }\n"
" static RBRACKET { \"RBRACKET\" }\n"
" static LBRACE { \"LBRACE\" }\n"
" static RBRACE { \"RBRACE\" }\n"
" static COLON { \"COLON\" }\n"
" static ASSIGN { \"ASSIGN\" }\n"
" static EQ { \"EQ\" }\n"
@ -264,6 +266,18 @@ static const char* jinjaModuleSource =
" return\n"
" }\n"
"\n"
" if (c == \"{\") {\n"
" advance_()\n"
" _tokens.add(Token.new(Token.LBRACE, c, startLine, startCol))\n"
" return\n"
" }\n"
"\n"
" if (c == \"}\") {\n"
" advance_()\n"
" _tokens.add(Token.new(Token.RBRACE, c, startLine, startCol))\n"
" return\n"
" }\n"
"\n"
" if (c == \":\") {\n"
" advance_()\n"
" _tokens.add(Token.new(Token.COLON, c, startLine, startCol))\n"
@ -671,6 +685,37 @@ static const char* jinjaModuleSource =
" }\n"
"}\n"
"\n"
"class FromImportNode is Node {\n"
" construct new(templateName, imports) {\n"
" _templateName = templateName\n"
" _imports = imports\n"
" }\n"
"\n"
" render(ctx) {\n"
" var env = ctx.get(\"__env__\")\n"
" if (env == null) {\n"
" Fiber.abort(TemplateError.new(\"Cannot import without environment\", 0, 0).toString)\n"
" }\n"
" var template = env.getTemplate(_templateName)\n"
" collectMacros_(template.nodes, ctx)\n"
" return \"\"\n"
" }\n"
"\n"
" collectMacros_(nodes, ctx) {\n"
" for (node in nodes) {\n"
" if (node is MacroNode) {\n"
" for (imp in _imports) {\n"
" var name = imp[0]\n"
" var alias = imp[1]\n"
" if (node.name == name) {\n"
" ctx.setMacro(alias, node)\n"
" }\n"
" }\n"
" }\n"
" }\n"
" }\n"
"}\n"
"\n"
"class SetNode is Node {\n"
" construct new(name, expr) {\n"
" _name = name\n"
@ -1838,6 +1883,7 @@ static const char* jinjaModuleSource =
" if (tagName == \"filter\") return parseFilterBlock_()\n"
" if (tagName == \"markdowntohtml\") return parseMarkdownToHtml_()\n"
" if (tagName == \"markdownfromhtml\") return parseMarkdownFromHtml_()\n"
" if (tagName == \"from\") return parseFrom_()\n"
"\n"
" Fiber.abort(TemplateError.new(\"Unknown block tag: %(tagName)\", nameToken.line, nameToken.col).toString)\n"
" }\n"
@ -2049,6 +2095,36 @@ static const char* jinjaModuleSource =
" return IncludeNode.new(templateName, withContext)\n"
" }\n"
"\n"
" parseFrom_() {\n"
" var templateName = \"\"\n"
" if (check_(Token.STRING)) {\n"
" templateName = advance_().value\n"
" } else {\n"
" Fiber.abort(TemplateError.new(\"Expected template name after 'from'\", peek_().line, peek_().col).toString)\n"
" }\n"
"\n"
" if (!checkName_(\"import\")) {\n"
" Fiber.abort(TemplateError.new(\"Expected 'import' after template name\", peek_().line, peek_().col).toString)\n"
" }\n"
" advance_()\n"
"\n"
" var imports = []\n"
" while (true) {\n"
" var name = consume_(Token.NAME).value\n"
" var alias = name\n"
" if (checkName_(\"as\")) {\n"
" advance_()\n"
" alias = consume_(Token.NAME).value\n"
" }\n"
" imports.add([name, alias])\n"
" if (!check_(Token.COMMA)) break\n"
" advance_()\n"
" }\n"
"\n"
" consumeBlockEnd_()\n"
" return FromImportNode.new(templateName, imports)\n"
" }\n"
"\n"
" parseSet_() {\n"
" var varName = consume_(Token.NAME).value\n"
" consume_(Token.ASSIGN)\n"
@ -2536,6 +2612,20 @@ static const char* jinjaModuleSource =
" return ListExpr.new(items)\n"
" }\n"
"\n"
" if (token.type == Token.LBRACE) {\n"
" advance_()\n"
" var pairs = []\n"
" while (!check_(Token.RBRACE)) {\n"
" var key = parseExpression_()\n"
" consume_(Token.COLON)\n"
" var value = parseExpression_()\n"
" pairs.add([key, value])\n"
" if (!check_(Token.RBRACE)) consume_(Token.COMMA)\n"
" }\n"
" consume_(Token.RBRACE)\n"
" return MapExpr.new(pairs)\n"
" }\n"
"\n"
" if (token.type == Token.LPAREN) {\n"
" advance_()\n"
" var expr = parseExpression_()\n"
@ -2697,6 +2787,26 @@ static const char* jinjaModuleSource =
" }\n"
"}\n"
"\n"
"class ChoiceLoader {\n"
" construct new(loaders) {\n"
" _loaders = loaders\n"
" }\n"
"\n"
" getSource(name) {\n"
" for (loader in _loaders) {\n"
" if (loader.exists(name)) return loader.getSource(name)\n"
" }\n"
" Fiber.abort(TemplateError.new(\"Template not found: %(name)\", 0, 0).toString)\n"
" }\n"
"\n"
" exists(name) {\n"
" for (loader in _loaders) {\n"
" if (loader.exists(name)) return true\n"
" }\n"
" return false\n"
" }\n"
"}\n"
"\n"
"class Environment {\n"
" construct new(loader) {\n"
" _loader = loader\n"

1179
src/module/pexpect.c Normal file

File diff suppressed because it is too large Load Diff

52
src/module/pexpect.h Normal file
View File

@ -0,0 +1,52 @@
// retoor <retoor@molodetz.nl>
#ifndef pexpect_h
#define pexpect_h
#include "wren.h"
void spawnAllocate(WrenVM* vm);
void spawnFinalize(void* data);
void spawnSend(WrenVM* vm);
void spawnSendcontrol(WrenVM* vm);
void spawnExpect(WrenVM* vm);
void spawnExpectExact(WrenVM* vm);
void spawnReadNonblocking(WrenVM* vm);
void spawnInteract(WrenVM* vm);
void spawnTerminate(WrenVM* vm);
void spawnKill(WrenVM* vm);
void spawnWait(WrenVM* vm);
void spawnIsalive(WrenVM* vm);
void spawnClose(WrenVM* vm);
void spawnSetecho(WrenVM* vm);
void spawnWaitnoecho(WrenVM* vm);
void spawnGetBuffer(WrenVM* vm);
void spawnSetBuffer(WrenVM* vm);
void spawnGetBefore(WrenVM* vm);
void spawnGetAfter(WrenVM* vm);
void spawnGetMatch(WrenVM* vm);
void spawnGetMatchIndex(WrenVM* vm);
void spawnGetPid(WrenVM* vm);
void spawnGetExitstatus(WrenVM* vm);
void spawnGetSignalstatus(WrenVM* vm);
void spawnGetTerminated(WrenVM* vm);
void spawnGetTimeout(WrenVM* vm);
void spawnSetTimeout(WrenVM* vm);
void spawnGetMaxread(WrenVM* vm);
void spawnSetMaxread(WrenVM* vm);
void spawnGetSearchwindowsize(WrenVM* vm);
void spawnSetSearchwindowsize(WrenVM* vm);
void spawnGetLogfile(WrenVM* vm);
void spawnSetLogfile(WrenVM* vm);
void spawnGetLogfileRead(WrenVM* vm);
void spawnSetLogfileRead(WrenVM* vm);
void spawnGetLogfileSend(WrenVM* vm);
void spawnSetLogfileSend(WrenVM* vm);
void spawnGetDelaybeforesend(WrenVM* vm);
void spawnSetDelaybeforesend(WrenVM* vm);
void spawnGetDelayafterclose(WrenVM* vm);
void spawnSetDelayafterclose(WrenVM* vm);
#endif

227
src/module/pexpect.wren vendored Normal file
View File

@ -0,0 +1,227 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler
import "regex" for Regex, Match
class Pexpect {
static EOF { -1 }
static TIMEOUT { -2 }
static run(command) { run(command, 30, false, null, null, null, null) }
static run(command, timeout) { run(command, timeout, false, null, null, null, null) }
static run(command, timeout, withexitstatus) { run(command, timeout, withexitstatus, null, null, null, null) }
static run(command, timeout, withexitstatus, events) { run(command, timeout, withexitstatus, events, null, null, null) }
static run(command, timeout, withexitstatus, events, logfile) { run(command, timeout, withexitstatus, events, logfile, null, null) }
static run(command, timeout, withexitstatus, events, logfile, cwd) { run(command, timeout, withexitstatus, events, logfile, cwd, null) }
static run(command, timeout, withexitstatus, events, logfile, cwd, env) {
var child = Spawn.new(command)
if (timeout != null) child.timeout = timeout
var output = ""
var maxLoops = 1000
var loops = 0
while (loops < maxLoops) {
loops = loops + 1
if (!child.isalive && child.buffer.count == 0) break
var data = child.readNonblocking(4096, 0.05)
if (data != null && data.count > 0) {
output = output + data
}
}
child.wait()
var status = child.exitstatus
child.close()
if (withexitstatus) {
return [output, status]
}
return output
}
}
foreign class Spawn {
construct new(command) {}
construct new(command, args) {}
construct new(command, args, options) {}
foreign send_(s)
send(s) { send_(s) }
sendline(s) { send_(s + "\n") }
sendline() { send_("\n") }
foreign sendcontrol(char)
sendeof() { sendcontrol("d") }
write(s) { send_(s) }
writelines(lines) {
for (line in lines) send_(line)
}
foreign expect_(patterns, timeout, searchwindowsize, fiber)
expect(pattern) {
var patternList = pattern is List ? pattern : [pattern]
return expect(patternList, timeout, searchwindowsize)
}
expect(patterns, t) { expect(patterns, t, searchwindowsize) }
expect(patterns, t, sw) {
var patternList = patterns is List ? patterns : [patterns]
return Scheduler.await_ { expect_(patternList, t, sw, Fiber.current) }
}
foreign expectExact_(patterns, timeout, searchwindowsize, fiber)
expectExact(pattern) {
var patternList = pattern is List ? pattern : [pattern]
return expectExact(patternList, timeout, searchwindowsize)
}
expectExact(patterns, t) { expectExact(patterns, t, searchwindowsize) }
expectExact(patterns, t, sw) {
var patternList = patterns is List ? patterns : [patterns]
return Scheduler.await_ { expectExact_(patternList, t, sw, Fiber.current) }
}
expectList(patternList) { expectList(patternList, timeout, searchwindowsize) }
expectList(patternList, t) { expectList(patternList, t, searchwindowsize) }
expectList(patternList, t, sw) {
return Scheduler.await_ { expect_(patternList, t, sw, Fiber.current) }
}
foreign readNonblocking_(size, timeout, fiber)
readNonblocking(size, timeout) {
return Scheduler.await_ { readNonblocking_(size, timeout, Fiber.current) }
}
readNonblocking(size) { readNonblocking(size, timeout) }
readNonblocking() { readNonblocking(maxread, timeout) }
read() { readNonblocking(maxread, timeout) }
readline() {
var line = ""
while (true) {
var ch = readNonblocking(1, timeout)
if (ch == null || ch.count == 0) return line
if (ch == "\n") return line
if (ch == "\r") {
var next = readNonblocking(1, 0.01)
if (next != "\n" && next != null && next.count > 0) {
line = line + next
}
return line
}
line = line + ch
}
}
foreign interact_(escapeChar, outputFilter, inputFilter)
interact() { interact_(29, null, null) }
interact(escapeChar) {
var code = escapeChar is Num ? escapeChar : escapeChar.bytes[0]
interact_(code, null, null)
}
interact(escapeChar, outputFilter, inputFilter) {
var code = escapeChar is Num ? escapeChar : escapeChar.bytes[0]
interact_(code, outputFilter, inputFilter)
}
foreign terminate_(force)
terminate() { terminate_(false) }
terminate(force) { terminate_(force) }
foreign kill_(signal)
kill(signal) { kill_(signal) }
foreign wait_(fiber)
wait() { Scheduler.await_ { wait_(Fiber.current) } }
foreign isalive
foreign close_(force)
close() { close_(false) }
close(force) { close_(force) }
foreign setecho(state)
foreign waitnoecho_(timeout, fiber)
waitnoecho() { waitnoecho(timeout) }
waitnoecho(t) {
return Scheduler.await_ { waitnoecho_(t, Fiber.current) }
}
foreign buffer
foreign buffer=(value)
foreign before
foreign after
foreign match
foreign matchIndex
foreign pid
foreign exitstatus
foreign signalstatus
foreign terminated
foreign timeout
foreign timeout=(value)
foreign maxread
foreign maxread=(value)
foreign searchwindowsize
foreign searchwindowsize=(value)
foreign logfile
foreign logfile=(value)
foreign logfileRead
foreign logfileRead=(value)
foreign logfileSend
foreign logfileSend=(value)
foreign delaybeforesend
foreign delaybeforesend=(value)
foreign delayafterclose
foreign delayafterclose=(value)
}
class SpawnMatch {
construct new_(text, start, end, groups) {
_text = text
_start = start
_end = end
_groups = groups
}
text { _text }
start { _start }
end { _end }
groups { _groups }
group(index) {
if (index < 0 || index >= _groups.count) return null
return _groups[index]
}
toString { _text }
}

231
src/module/pexpect.wren.inc Normal file
View File

@ -0,0 +1,231 @@
// Please do not edit this file. It has been generated automatically
// from `src/module/pexpect.wren` using `util/wren_to_c_string.py`
static const char* pexpectModuleSource =
"// retoor <retoor@molodetz.nl>\n"
"\n"
"import \"scheduler\" for Scheduler\n"
"import \"regex\" for Regex, Match\n"
"\n"
"class Pexpect {\n"
" static EOF { -1 }\n"
" static TIMEOUT { -2 }\n"
"\n"
" static run(command) { run(command, 30, false, null, null, null, null) }\n"
" static run(command, timeout) { run(command, timeout, false, null, null, null, null) }\n"
" static run(command, timeout, withexitstatus) { run(command, timeout, withexitstatus, null, null, null, null) }\n"
" static run(command, timeout, withexitstatus, events) { run(command, timeout, withexitstatus, events, null, null, null) }\n"
" static run(command, timeout, withexitstatus, events, logfile) { run(command, timeout, withexitstatus, events, logfile, null, null) }\n"
" static run(command, timeout, withexitstatus, events, logfile, cwd) { run(command, timeout, withexitstatus, events, logfile, cwd, null) }\n"
"\n"
" static run(command, timeout, withexitstatus, events, logfile, cwd, env) {\n"
" var child = Spawn.new(command)\n"
" if (timeout != null) child.timeout = timeout\n"
"\n"
" var output = \"\"\n"
" var maxLoops = 1000\n"
" var loops = 0\n"
" while (loops < maxLoops) {\n"
" loops = loops + 1\n"
" if (!child.isalive && child.buffer.count == 0) break\n"
" var data = child.readNonblocking(4096, 0.05)\n"
" if (data != null && data.count > 0) {\n"
" output = output + data\n"
" }\n"
" }\n"
"\n"
" child.wait()\n"
" var status = child.exitstatus\n"
" child.close()\n"
"\n"
" if (withexitstatus) {\n"
" return [output, status]\n"
" }\n"
" return output\n"
" }\n"
"}\n"
"\n"
"foreign class Spawn {\n"
" construct new(command) {}\n"
" construct new(command, args) {}\n"
" construct new(command, args, options) {}\n"
"\n"
" foreign send_(s)\n"
"\n"
" send(s) { send_(s) }\n"
"\n"
" sendline(s) { send_(s + \"\\n\") }\n"
" sendline() { send_(\"\\n\") }\n"
"\n"
" foreign sendcontrol(char)\n"
"\n"
" sendeof() { sendcontrol(\"d\") }\n"
"\n"
" write(s) { send_(s) }\n"
"\n"
" writelines(lines) {\n"
" for (line in lines) send_(line)\n"
" }\n"
"\n"
" foreign expect_(patterns, timeout, searchwindowsize, fiber)\n"
"\n"
" expect(pattern) {\n"
" var patternList = pattern is List ? pattern : [pattern]\n"
" return expect(patternList, timeout, searchwindowsize)\n"
" }\n"
" expect(patterns, t) { expect(patterns, t, searchwindowsize) }\n"
" expect(patterns, t, sw) {\n"
" var patternList = patterns is List ? patterns : [patterns]\n"
" return Scheduler.await_ { expect_(patternList, t, sw, Fiber.current) }\n"
" }\n"
"\n"
" foreign expectExact_(patterns, timeout, searchwindowsize, fiber)\n"
"\n"
" expectExact(pattern) {\n"
" var patternList = pattern is List ? pattern : [pattern]\n"
" return expectExact(patternList, timeout, searchwindowsize)\n"
" }\n"
" expectExact(patterns, t) { expectExact(patterns, t, searchwindowsize) }\n"
" expectExact(patterns, t, sw) {\n"
" var patternList = patterns is List ? patterns : [patterns]\n"
" return Scheduler.await_ { expectExact_(patternList, t, sw, Fiber.current) }\n"
" }\n"
"\n"
" expectList(patternList) { expectList(patternList, timeout, searchwindowsize) }\n"
" expectList(patternList, t) { expectList(patternList, t, searchwindowsize) }\n"
" expectList(patternList, t, sw) {\n"
" return Scheduler.await_ { expect_(patternList, t, sw, Fiber.current) }\n"
" }\n"
"\n"
" foreign readNonblocking_(size, timeout, fiber)\n"
"\n"
" readNonblocking(size, timeout) {\n"
" return Scheduler.await_ { readNonblocking_(size, timeout, Fiber.current) }\n"
" }\n"
" readNonblocking(size) { readNonblocking(size, timeout) }\n"
" readNonblocking() { readNonblocking(maxread, timeout) }\n"
"\n"
" read() { readNonblocking(maxread, timeout) }\n"
"\n"
" readline() {\n"
" var line = \"\"\n"
" while (true) {\n"
" var ch = readNonblocking(1, timeout)\n"
" if (ch == null || ch.count == 0) return line\n"
" if (ch == \"\\n\") return line\n"
" if (ch == \"\\r\") {\n"
" var next = readNonblocking(1, 0.01)\n"
" if (next != \"\\n\" && next != null && next.count > 0) {\n"
" line = line + next\n"
" }\n"
" return line\n"
" }\n"
" line = line + ch\n"
" }\n"
" }\n"
"\n"
" foreign interact_(escapeChar, outputFilter, inputFilter)\n"
"\n"
" interact() { interact_(29, null, null) }\n"
" interact(escapeChar) {\n"
" var code = escapeChar is Num ? escapeChar : escapeChar.bytes[0]\n"
" interact_(code, null, null)\n"
" }\n"
" interact(escapeChar, outputFilter, inputFilter) {\n"
" var code = escapeChar is Num ? escapeChar : escapeChar.bytes[0]\n"
" interact_(code, outputFilter, inputFilter)\n"
" }\n"
"\n"
" foreign terminate_(force)\n"
"\n"
" terminate() { terminate_(false) }\n"
" terminate(force) { terminate_(force) }\n"
"\n"
" foreign kill_(signal)\n"
"\n"
" kill(signal) { kill_(signal) }\n"
"\n"
" foreign wait_(fiber)\n"
"\n"
" wait() { Scheduler.await_ { wait_(Fiber.current) } }\n"
"\n"
" foreign isalive\n"
"\n"
" foreign close_(force)\n"
"\n"
" close() { close_(false) }\n"
" close(force) { close_(force) }\n"
"\n"
" foreign setecho(state)\n"
"\n"
" foreign waitnoecho_(timeout, fiber)\n"
"\n"
" waitnoecho() { waitnoecho(timeout) }\n"
" waitnoecho(t) {\n"
" return Scheduler.await_ { waitnoecho_(t, Fiber.current) }\n"
" }\n"
"\n"
" foreign buffer\n"
" foreign buffer=(value)\n"
"\n"
" foreign before\n"
"\n"
" foreign after\n"
"\n"
" foreign match\n"
"\n"
" foreign matchIndex\n"
"\n"
" foreign pid\n"
"\n"
" foreign exitstatus\n"
"\n"
" foreign signalstatus\n"
"\n"
" foreign terminated\n"
"\n"
" foreign timeout\n"
" foreign timeout=(value)\n"
"\n"
" foreign maxread\n"
" foreign maxread=(value)\n"
"\n"
" foreign searchwindowsize\n"
" foreign searchwindowsize=(value)\n"
"\n"
" foreign logfile\n"
" foreign logfile=(value)\n"
"\n"
" foreign logfileRead\n"
" foreign logfileRead=(value)\n"
"\n"
" foreign logfileSend\n"
" foreign logfileSend=(value)\n"
"\n"
" foreign delaybeforesend\n"
" foreign delaybeforesend=(value)\n"
"\n"
" foreign delayafterclose\n"
" foreign delayafterclose=(value)\n"
"}\n"
"\n"
"class SpawnMatch {\n"
" construct new_(text, start, end, groups) {\n"
" _text = text\n"
" _start = start\n"
" _end = end\n"
" _groups = groups\n"
" }\n"
"\n"
" text { _text }\n"
" start { _start }\n"
" end { _end }\n"
" groups { _groups }\n"
"\n"
" group(index) {\n"
" if (index < 0 || index >= _groups.count) return null\n"
" return _groups[index]\n"
" }\n"
"\n"
" toString { _text }\n"
"}\n";

479
src/module/yaml.wren vendored Normal file
View File

@ -0,0 +1,479 @@
// retoor <retoor@molodetz.nl>
class YamlError {
construct new(message, line) {
_message = message
_line = line
}
message { _message }
line { _line }
toString { "YAML error at line %(_line): %(_message)" }
}
class YamlParser {
construct new(text) {
_text = text
_lines = []
_lineNum = 0
}
parse() {
_lines = splitLines_(_text)
_lineNum = 0
if (_lines.count == 0) return null
return parseValue_(0)
}
splitLines_(text) {
var result = []
var lines = text.split("\n")
for (i in 0...lines.count) {
var line = lines[i]
if (line.count > 0 && line[-1] == "\r") {
line = line[0...-1]
}
result.add({"text": line, "num": i + 1})
}
return result
}
parseValue_(minIndent) {
skipBlankAndComments_()
if (_lineNum >= _lines.count) return null
var line = _lines[_lineNum]
var indent = getIndent_(line["text"])
if (indent < minIndent) return null
var content = line["text"].trim()
if (content.count == 0) return null
if (content.startsWith("-")) {
return parseList_(indent)
}
return parseMap_(indent)
}
parseList_(listIndent) {
var result = []
while (_lineNum < _lines.count) {
skipBlankAndComments_()
if (_lineNum >= _lines.count) break
var line = _lines[_lineNum]
var indent = getIndent_(line["text"])
var content = line["text"].trim()
if (content.count == 0) {
_lineNum = _lineNum + 1
continue
}
if (indent < listIndent) break
if (indent > listIndent && !content.startsWith("-")) break
if (!content.startsWith("-")) break
var itemContent = content[1..-1].trim()
if (itemContent.count > 0 && itemContent.contains(":") && !isQuotedString_(itemContent)) {
var colonIdx = findUnquotedColon_(itemContent)
if (colonIdx > 0) {
_lineNum = _lineNum + 1
var inlineMap = parseInlineKeyValue_(itemContent)
var nextIndent = indent + 2
skipBlankAndComments_()
if (_lineNum < _lines.count) {
var nextLine = _lines[_lineNum]
var nextActualIndent = getIndent_(nextLine["text"])
var nextContent = nextLine["text"].trim()
if (nextActualIndent > indent && nextContent.count > 0 && !nextContent.startsWith("-")) {
var nestedMap = parseMap_(nextActualIndent)
if (nestedMap is Map) {
for (key in nestedMap.keys) {
inlineMap[key] = nestedMap[key]
}
}
}
}
result.add(inlineMap)
continue
}
}
if (itemContent.count == 0) {
_lineNum = _lineNum + 1
skipBlankAndComments_()
if (_lineNum < _lines.count) {
var nextLine = _lines[_lineNum]
var nextIndent = getIndent_(nextLine["text"])
if (nextIndent > indent) {
var nestedValue = parseValue_(nextIndent)
result.add(nestedValue)
} else {
result.add(null)
}
} else {
result.add(null)
}
} else {
_lineNum = _lineNum + 1
result.add(parseScalar_(itemContent))
}
}
return result
}
parseMap_(mapIndent) {
var result = {}
while (_lineNum < _lines.count) {
skipBlankAndComments_()
if (_lineNum >= _lines.count) break
var line = _lines[_lineNum]
var indent = getIndent_(line["text"])
var content = line["text"].trim()
if (content.count == 0) {
_lineNum = _lineNum + 1
continue
}
if (indent < mapIndent) break
if (indent > mapIndent) break
var colonIdx = findUnquotedColon_(content)
if (colonIdx < 0) break
var key = content[0...colonIdx].trim()
key = unquote_(key)
var valueStr = content[colonIdx + 1..-1].trim()
_lineNum = _lineNum + 1
if (valueStr.count == 0) {
skipBlankAndComments_()
if (_lineNum < _lines.count) {
var nextLine = _lines[_lineNum]
var nextIndent = getIndent_(nextLine["text"])
if (nextIndent > mapIndent) {
result[key] = parseValue_(nextIndent)
} else {
result[key] = null
}
} else {
result[key] = null
}
} else {
result[key] = parseScalar_(valueStr)
}
}
return result
}
parseInlineKeyValue_(content) {
var result = {}
var colonIdx = findUnquotedColon_(content)
if (colonIdx < 0) return result
var key = content[0...colonIdx].trim()
key = unquote_(key)
var valueStr = content[colonIdx + 1..-1].trim()
result[key] = parseScalar_(valueStr)
return result
}
parseScalar_(str) {
if (str.count == 0) return null
if (str == "null" || str == "~") return null
if (str == "true") return true
if (str == "false") return false
if (isQuotedString_(str)) return unquote_(str)
var num = parseNumber_(str)
if (num != null) return num
var commentIdx = str.indexOf(" #")
if (commentIdx >= 0) {
str = str[0...commentIdx].trim()
}
return str
}
parseNumber_(str) {
if (str.count == 0) return null
var hasDigit = false
var hasDot = false
var hasSign = false
var i = 0
if (str[0] == "-" || str[0] == "+") {
hasSign = true
i = 1
}
while (i < str.count) {
var c = str[i]
var code = c.codePoints.toList[0]
if (code >= 48 && code <= 57) {
hasDigit = true
} else if (c == "." && !hasDot) {
hasDot = true
} else if (c == " " || c == "#") {
break
} else {
return null
}
i = i + 1
}
if (!hasDigit) return null
var numStr = str[0...i]
return Num.fromString(numStr)
}
isQuotedString_(str) {
if (str.count < 2) return false
return (str[0] == "\"" && str[-1] == "\"") ||
(str[0] == "'" && str[-1] == "'")
}
unquote_(str) {
if (str.count < 2) return str
if ((str[0] == "\"" && str[-1] == "\"") ||
(str[0] == "'" && str[-1] == "'")) {
var inner = str[1...-1]
inner = inner.replace("\\n", "\n")
inner = inner.replace("\\t", "\t")
inner = inner.replace("\\\"", "\"")
inner = inner.replace("\\'", "'")
inner = inner.replace("\\\\", "\\")
return inner
}
return str
}
findUnquotedColon_(str) {
var inSingleQuote = false
var inDoubleQuote = false
for (i in 0...str.count) {
var c = str[i]
if (c == "'" && !inDoubleQuote) {
inSingleQuote = !inSingleQuote
} else if (c == "\"" && !inSingleQuote) {
inDoubleQuote = !inDoubleQuote
} else if (c == ":" && !inSingleQuote && !inDoubleQuote) {
return i
}
}
return -1
}
getIndent_(line) {
var indent = 0
for (c in line) {
if (c == " ") {
indent = indent + 1
} else if (c == "\t") {
indent = indent + 2
} else {
break
}
}
return indent
}
skipBlankAndComments_() {
while (_lineNum < _lines.count) {
var line = _lines[_lineNum]
var content = line["text"].trim()
if (content.count == 0 || content.startsWith("#")) {
_lineNum = _lineNum + 1
} else {
break
}
}
}
}
class Yaml {
static parse(text) {
if (text == null) return null
if (text.count == 0) return null
var parser = YamlParser.new(text)
return parser.parse()
}
static stringify(value) { stringify(value, 2) }
static stringify(value, indent) {
if (value == null) return "null"
return stringifyValue_(value, indent, 0)
}
static stringifyValue_(value, indent, level) {
if (value == null) return "null"
if (value is Bool) return value ? "true" : "false"
if (value is Num) {
if (value == value.truncate) return value.truncate.toString
return value.toString
}
if (value is String) return stringifyString_(value)
if (value is List) return stringifyList_(value, indent, level)
if (value is Map) return stringifyMap_(value, indent, level)
return "null"
}
static stringifyString_(str) {
if (str.count == 0) return "\"\""
var needsQuotes = false
var chars = str.toList
if (str == "true" || str == "false" || str == "null") {
needsQuotes = true
}
var specialChars = ":# \n\"\t'[]{}|>,&*!@`"
var percentChar = String.fromCodePoint(37)
specialChars = specialChars + percentChar
for (c in chars) {
if (specialChars.contains(c)) {
needsQuotes = true
break
}
}
if (chars.count > 0 && (chars[0] == " " || chars[-1] == " ")) {
needsQuotes = true
}
if (!needsQuotes) {
var num = Num.fromString(str)
if (num != null) needsQuotes = true
}
if (!needsQuotes) return str
var backslash = String.fromCodePoint(92)
var quote = String.fromCodePoint(34)
var newline = String.fromCodePoint(10)
var tab = String.fromCodePoint(9)
var escaped = str.replace(backslash, backslash + backslash)
escaped = escaped.replace(quote, backslash + quote)
escaped = escaped.replace(newline, backslash + "n")
escaped = escaped.replace(tab, backslash + "t")
return quote + escaped + quote
}
static stringifyList_(list, indent, level) {
if (list.count == 0) return "[]"
var result = []
var prefix = ""
for (i in 0...level) {
for (j in 0...indent) {
prefix = prefix + " "
}
}
for (item in list) {
if (item is Map && item.count > 0) {
var first = true
for (key in item.keys) {
var valueStr = stringifyValue_(item[key], indent, level + 1)
if (first) {
if (item[key] is Map || item[key] is List) {
result.add(prefix + "- " + key + ":")
var nestedLines = valueStr.split("\n")
for (nestedLine in nestedLines) {
result.add(nestedLine)
}
} else {
result.add(prefix + "- " + key + ": " + valueStr)
}
first = false
} else {
var keyPrefix = prefix
for (j in 0...indent) {
keyPrefix = keyPrefix + " "
}
if (item[key] is Map || item[key] is List) {
result.add(keyPrefix + key + ":")
var nestedLines = valueStr.split("\n")
for (nestedLine in nestedLines) {
result.add(nestedLine)
}
} else {
result.add(keyPrefix + key + ": " + valueStr)
}
}
}
} else if (item is List && item.count > 0) {
result.add(prefix + "-")
var nestedStr = stringifyList_(item, indent, level + 1)
var nestedLines = nestedStr.split("\n")
for (nestedLine in nestedLines) {
result.add(nestedLine)
}
} else {
var valueStr = stringifyValue_(item, indent, level)
result.add(prefix + "- " + valueStr)
}
}
return result.join("\n")
}
static stringifyMap_(map, indent, level) {
if (map.count == 0) return "{}"
var result = []
var prefix = ""
for (i in 0...level) {
for (j in 0...indent) {
prefix = prefix + " "
}
}
for (key in map.keys) {
var keyStr = stringifyString_(key.toString)
var value = map[key]
if (value is Map || value is List) {
result.add(prefix + keyStr + ":")
var nestedStr = stringifyValue_(value, indent, level + 1)
var nestedLines = nestedStr.split("\n")
for (nestedLine in nestedLines) {
result.add(nestedLine)
}
} else {
var valueStr = stringifyValue_(value, indent, level)
result.add(prefix + keyStr + ": " + valueStr)
}
}
return result.join("\n")
}
}

483
src/module/yaml.wren.inc Normal file
View File

@ -0,0 +1,483 @@
// Please do not edit this file. It has been generated automatically
// from `src/module/yaml.wren` using `util/wren_to_c_string.py`
static const char* yamlModuleSource =
"// retoor <retoor@molodetz.nl>\n"
"\n"
"class YamlError {\n"
" construct new(message, line) {\n"
" _message = message\n"
" _line = line\n"
" }\n"
"\n"
" message { _message }\n"
" line { _line }\n"
"\n"
" toString { \"YAML error at line %(_line): %(_message)\" }\n"
"}\n"
"\n"
"class YamlParser {\n"
" construct new(text) {\n"
" _text = text\n"
" _lines = []\n"
" _lineNum = 0\n"
" }\n"
"\n"
" parse() {\n"
" _lines = splitLines_(_text)\n"
" _lineNum = 0\n"
" if (_lines.count == 0) return null\n"
" return parseValue_(0)\n"
" }\n"
"\n"
" splitLines_(text) {\n"
" var result = []\n"
" var lines = text.split(\"\\n\")\n"
" for (i in 0...lines.count) {\n"
" var line = lines[i]\n"
" if (line.count > 0 && line[-1] == \"\\r\") {\n"
" line = line[0...-1]\n"
" }\n"
" result.add({\"text\": line, \"num\": i + 1})\n"
" }\n"
" return result\n"
" }\n"
"\n"
" parseValue_(minIndent) {\n"
" skipBlankAndComments_()\n"
" if (_lineNum >= _lines.count) return null\n"
"\n"
" var line = _lines[_lineNum]\n"
" var indent = getIndent_(line[\"text\"])\n"
"\n"
" if (indent < minIndent) return null\n"
"\n"
" var content = line[\"text\"].trim()\n"
" if (content.count == 0) return null\n"
"\n"
" if (content.startsWith(\"-\")) {\n"
" return parseList_(indent)\n"
" }\n"
"\n"
" return parseMap_(indent)\n"
" }\n"
"\n"
" parseList_(listIndent) {\n"
" var result = []\n"
"\n"
" while (_lineNum < _lines.count) {\n"
" skipBlankAndComments_()\n"
" if (_lineNum >= _lines.count) break\n"
"\n"
" var line = _lines[_lineNum]\n"
" var indent = getIndent_(line[\"text\"])\n"
" var content = line[\"text\"].trim()\n"
"\n"
" if (content.count == 0) {\n"
" _lineNum = _lineNum + 1\n"
" continue\n"
" }\n"
"\n"
" if (indent < listIndent) break\n"
" if (indent > listIndent && !content.startsWith(\"-\")) break\n"
"\n"
" if (!content.startsWith(\"-\")) break\n"
"\n"
" var itemContent = content[1..-1].trim()\n"
"\n"
" if (itemContent.count > 0 && itemContent.contains(\":\") && !isQuotedString_(itemContent)) {\n"
" var colonIdx = findUnquotedColon_(itemContent)\n"
" if (colonIdx > 0) {\n"
" _lineNum = _lineNum + 1\n"
" var inlineMap = parseInlineKeyValue_(itemContent)\n"
" var nextIndent = indent + 2\n"
" skipBlankAndComments_()\n"
" if (_lineNum < _lines.count) {\n"
" var nextLine = _lines[_lineNum]\n"
" var nextActualIndent = getIndent_(nextLine[\"text\"])\n"
" var nextContent = nextLine[\"text\"].trim()\n"
" if (nextActualIndent > indent && nextContent.count > 0 && !nextContent.startsWith(\"-\")) {\n"
" var nestedMap = parseMap_(nextActualIndent)\n"
" if (nestedMap is Map) {\n"
" for (key in nestedMap.keys) {\n"
" inlineMap[key] = nestedMap[key]\n"
" }\n"
" }\n"
" }\n"
" }\n"
" result.add(inlineMap)\n"
" continue\n"
" }\n"
" }\n"
"\n"
" if (itemContent.count == 0) {\n"
" _lineNum = _lineNum + 1\n"
" skipBlankAndComments_()\n"
" if (_lineNum < _lines.count) {\n"
" var nextLine = _lines[_lineNum]\n"
" var nextIndent = getIndent_(nextLine[\"text\"])\n"
" if (nextIndent > indent) {\n"
" var nestedValue = parseValue_(nextIndent)\n"
" result.add(nestedValue)\n"
" } else {\n"
" result.add(null)\n"
" }\n"
" } else {\n"
" result.add(null)\n"
" }\n"
" } else {\n"
" _lineNum = _lineNum + 1\n"
" result.add(parseScalar_(itemContent))\n"
" }\n"
" }\n"
"\n"
" return result\n"
" }\n"
"\n"
" parseMap_(mapIndent) {\n"
" var result = {}\n"
"\n"
" while (_lineNum < _lines.count) {\n"
" skipBlankAndComments_()\n"
" if (_lineNum >= _lines.count) break\n"
"\n"
" var line = _lines[_lineNum]\n"
" var indent = getIndent_(line[\"text\"])\n"
" var content = line[\"text\"].trim()\n"
"\n"
" if (content.count == 0) {\n"
" _lineNum = _lineNum + 1\n"
" continue\n"
" }\n"
"\n"
" if (indent < mapIndent) break\n"
" if (indent > mapIndent) break\n"
"\n"
" var colonIdx = findUnquotedColon_(content)\n"
" if (colonIdx < 0) break\n"
"\n"
" var key = content[0...colonIdx].trim()\n"
" key = unquote_(key)\n"
" var valueStr = content[colonIdx + 1..-1].trim()\n"
"\n"
" _lineNum = _lineNum + 1\n"
"\n"
" if (valueStr.count == 0) {\n"
" skipBlankAndComments_()\n"
" if (_lineNum < _lines.count) {\n"
" var nextLine = _lines[_lineNum]\n"
" var nextIndent = getIndent_(nextLine[\"text\"])\n"
" if (nextIndent > mapIndent) {\n"
" result[key] = parseValue_(nextIndent)\n"
" } else {\n"
" result[key] = null\n"
" }\n"
" } else {\n"
" result[key] = null\n"
" }\n"
" } else {\n"
" result[key] = parseScalar_(valueStr)\n"
" }\n"
" }\n"
"\n"
" return result\n"
" }\n"
"\n"
" parseInlineKeyValue_(content) {\n"
" var result = {}\n"
" var colonIdx = findUnquotedColon_(content)\n"
" if (colonIdx < 0) return result\n"
"\n"
" var key = content[0...colonIdx].trim()\n"
" key = unquote_(key)\n"
" var valueStr = content[colonIdx + 1..-1].trim()\n"
"\n"
" result[key] = parseScalar_(valueStr)\n"
" return result\n"
" }\n"
"\n"
" parseScalar_(str) {\n"
" if (str.count == 0) return null\n"
"\n"
" if (str == \"null\" || str == \"~\") return null\n"
" if (str == \"true\") return true\n"
" if (str == \"false\") return false\n"
"\n"
" if (isQuotedString_(str)) return unquote_(str)\n"
"\n"
" var num = parseNumber_(str)\n"
" if (num != null) return num\n"
"\n"
" var commentIdx = str.indexOf(\" #\")\n"
" if (commentIdx >= 0) {\n"
" str = str[0...commentIdx].trim()\n"
" }\n"
"\n"
" return str\n"
" }\n"
"\n"
" parseNumber_(str) {\n"
" if (str.count == 0) return null\n"
"\n"
" var hasDigit = false\n"
" var hasDot = false\n"
" var hasSign = false\n"
" var i = 0\n"
"\n"
" if (str[0] == \"-\" || str[0] == \"+\") {\n"
" hasSign = true\n"
" i = 1\n"
" }\n"
"\n"
" while (i < str.count) {\n"
" var c = str[i]\n"
" var code = c.codePoints.toList[0]\n"
"\n"
" if (code >= 48 && code <= 57) {\n"
" hasDigit = true\n"
" } else if (c == \".\" && !hasDot) {\n"
" hasDot = true\n"
" } else if (c == \" \" || c == \"#\") {\n"
" break\n"
" } else {\n"
" return null\n"
" }\n"
" i = i + 1\n"
" }\n"
"\n"
" if (!hasDigit) return null\n"
"\n"
" var numStr = str[0...i]\n"
" return Num.fromString(numStr)\n"
" }\n"
"\n"
" isQuotedString_(str) {\n"
" if (str.count < 2) return false\n"
" return (str[0] == \"\\\"\" && str[-1] == \"\\\"\") ||\n"
" (str[0] == \"'\" && str[-1] == \"'\")\n"
" }\n"
"\n"
" unquote_(str) {\n"
" if (str.count < 2) return str\n"
" if ((str[0] == \"\\\"\" && str[-1] == \"\\\"\") ||\n"
" (str[0] == \"'\" && str[-1] == \"'\")) {\n"
" var inner = str[1...-1]\n"
" inner = inner.replace(\"\\\\n\", \"\\n\")\n"
" inner = inner.replace(\"\\\\t\", \"\\t\")\n"
" inner = inner.replace(\"\\\\\\\"\", \"\\\"\")\n"
" inner = inner.replace(\"\\\\'\", \"'\")\n"
" inner = inner.replace(\"\\\\\\\\\", \"\\\\\")\n"
" return inner\n"
" }\n"
" return str\n"
" }\n"
"\n"
" findUnquotedColon_(str) {\n"
" var inSingleQuote = false\n"
" var inDoubleQuote = false\n"
"\n"
" for (i in 0...str.count) {\n"
" var c = str[i]\n"
"\n"
" if (c == \"'\" && !inDoubleQuote) {\n"
" inSingleQuote = !inSingleQuote\n"
" } else if (c == \"\\\"\" && !inSingleQuote) {\n"
" inDoubleQuote = !inDoubleQuote\n"
" } else if (c == \":\" && !inSingleQuote && !inDoubleQuote) {\n"
" return i\n"
" }\n"
" }\n"
"\n"
" return -1\n"
" }\n"
"\n"
" getIndent_(line) {\n"
" var indent = 0\n"
" for (c in line) {\n"
" if (c == \" \") {\n"
" indent = indent + 1\n"
" } else if (c == \"\\t\") {\n"
" indent = indent + 2\n"
" } else {\n"
" break\n"
" }\n"
" }\n"
" return indent\n"
" }\n"
"\n"
" skipBlankAndComments_() {\n"
" while (_lineNum < _lines.count) {\n"
" var line = _lines[_lineNum]\n"
" var content = line[\"text\"].trim()\n"
" if (content.count == 0 || content.startsWith(\"#\")) {\n"
" _lineNum = _lineNum + 1\n"
" } else {\n"
" break\n"
" }\n"
" }\n"
" }\n"
"}\n"
"\n"
"class Yaml {\n"
" static parse(text) {\n"
" if (text == null) return null\n"
" if (text.count == 0) return null\n"
"\n"
" var parser = YamlParser.new(text)\n"
" return parser.parse()\n"
" }\n"
"\n"
" static stringify(value) { stringify(value, 2) }\n"
"\n"
" static stringify(value, indent) {\n"
" if (value == null) return \"null\"\n"
" return stringifyValue_(value, indent, 0)\n"
" }\n"
"\n"
" static stringifyValue_(value, indent, level) {\n"
" if (value == null) return \"null\"\n"
" if (value is Bool) return value ? \"true\" : \"false\"\n"
" if (value is Num) {\n"
" if (value == value.truncate) return value.truncate.toString\n"
" return value.toString\n"
" }\n"
" if (value is String) return stringifyString_(value)\n"
" if (value is List) return stringifyList_(value, indent, level)\n"
" if (value is Map) return stringifyMap_(value, indent, level)\n"
" return \"null\"\n"
" }\n"
"\n"
" static stringifyString_(str) {\n"
" if (str.count == 0) return \"\\\"\\\"\"\n"
"\n"
" var needsQuotes = false\n"
" var chars = str.toList\n"
"\n"
" if (str == \"true\" || str == \"false\" || str == \"null\") {\n"
" needsQuotes = true\n"
" }\n"
"\n"
" var specialChars = \":# \\n\\\"\\t'[]{}|>,&*!@`\"\n"
" var percentChar = String.fromCodePoint(37)\n"
" specialChars = specialChars + percentChar\n"
"\n"
" for (c in chars) {\n"
" if (specialChars.contains(c)) {\n"
" needsQuotes = true\n"
" break\n"
" }\n"
" }\n"
"\n"
" if (chars.count > 0 && (chars[0] == \" \" || chars[-1] == \" \")) {\n"
" needsQuotes = true\n"
" }\n"
"\n"
" if (!needsQuotes) {\n"
" var num = Num.fromString(str)\n"
" if (num != null) needsQuotes = true\n"
" }\n"
"\n"
" if (!needsQuotes) return str\n"
"\n"
" var backslash = String.fromCodePoint(92)\n"
" var quote = String.fromCodePoint(34)\n"
" var newline = String.fromCodePoint(10)\n"
" var tab = String.fromCodePoint(9)\n"
"\n"
" var escaped = str.replace(backslash, backslash + backslash)\n"
" escaped = escaped.replace(quote, backslash + quote)\n"
" escaped = escaped.replace(newline, backslash + \"n\")\n"
" escaped = escaped.replace(tab, backslash + \"t\")\n"
" return quote + escaped + quote\n"
" }\n"
"\n"
" static stringifyList_(list, indent, level) {\n"
" if (list.count == 0) return \"[]\"\n"
"\n"
" var result = []\n"
" var prefix = \"\"\n"
" for (i in 0...level) {\n"
" for (j in 0...indent) {\n"
" prefix = prefix + \" \"\n"
" }\n"
" }\n"
"\n"
" for (item in list) {\n"
" if (item is Map && item.count > 0) {\n"
" var first = true\n"
" for (key in item.keys) {\n"
" var valueStr = stringifyValue_(item[key], indent, level + 1)\n"
" if (first) {\n"
" if (item[key] is Map || item[key] is List) {\n"
" result.add(prefix + \"- \" + key + \":\")\n"
" var nestedLines = valueStr.split(\"\\n\")\n"
" for (nestedLine in nestedLines) {\n"
" result.add(nestedLine)\n"
" }\n"
" } else {\n"
" result.add(prefix + \"- \" + key + \": \" + valueStr)\n"
" }\n"
" first = false\n"
" } else {\n"
" var keyPrefix = prefix\n"
" for (j in 0...indent) {\n"
" keyPrefix = keyPrefix + \" \"\n"
" }\n"
" if (item[key] is Map || item[key] is List) {\n"
" result.add(keyPrefix + key + \":\")\n"
" var nestedLines = valueStr.split(\"\\n\")\n"
" for (nestedLine in nestedLines) {\n"
" result.add(nestedLine)\n"
" }\n"
" } else {\n"
" result.add(keyPrefix + key + \": \" + valueStr)\n"
" }\n"
" }\n"
" }\n"
" } else if (item is List && item.count > 0) {\n"
" result.add(prefix + \"-\")\n"
" var nestedStr = stringifyList_(item, indent, level + 1)\n"
" var nestedLines = nestedStr.split(\"\\n\")\n"
" for (nestedLine in nestedLines) {\n"
" result.add(nestedLine)\n"
" }\n"
" } else {\n"
" var valueStr = stringifyValue_(item, indent, level)\n"
" result.add(prefix + \"- \" + valueStr)\n"
" }\n"
" }\n"
"\n"
" return result.join(\"\\n\")\n"
" }\n"
"\n"
" static stringifyMap_(map, indent, level) {\n"
" if (map.count == 0) return \"{}\"\n"
"\n"
" var result = []\n"
" var prefix = \"\"\n"
" for (i in 0...level) {\n"
" for (j in 0...indent) {\n"
" prefix = prefix + \" \"\n"
" }\n"
" }\n"
"\n"
" for (key in map.keys) {\n"
" var keyStr = stringifyString_(key.toString)\n"
" var value = map[key]\n"
"\n"
" if (value is Map || value is List) {\n"
" result.add(prefix + keyStr + \":\")\n"
" var nestedStr = stringifyValue_(value, indent, level + 1)\n"
" var nestedLines = nestedStr.split(\"\\n\")\n"
" for (nestedLine in nestedLines) {\n"
" result.add(nestedLine)\n"
" }\n"
" } else {\n"
" var valueStr = stringifyValue_(value, indent, level)\n"
" result.add(prefix + keyStr + \": \" + valueStr)\n"
" }\n"
" }\n"
"\n"
" return result.join(\"\\n\")\n"
" }\n"
"}\n";

19
test/jinja/choice_loader.wren vendored Normal file
View File

@ -0,0 +1,19 @@
// retoor <retoor@molodetz.nl>
import "jinja" for Environment, DictLoader, ChoiceLoader
var loader1 = DictLoader.new({"a.html": "Template A"})
var loader2 = DictLoader.new({"b.html": "Template B"})
var choice = ChoiceLoader.new([loader1, loader2])
System.print(choice.exists("a.html")) // expect: true
System.print(choice.exists("b.html")) // expect: true
System.print(choice.exists("c.html")) // expect: false
System.print(choice.getSource("a.html")) // expect: Template A
System.print(choice.getSource("b.html")) // expect: Template B
var env = Environment.new(choice)
var tplA = env.getTemplate("a.html")
var tplB = env.getTemplate("b.html")
System.print(tplA.render({})) // expect: Template A
System.print(tplB.render({})) // expect: Template B

24
test/jinja/map_literal.wren vendored Normal file
View File

@ -0,0 +1,24 @@
// retoor <retoor@molodetz.nl>
import "jinja" for Environment, DictLoader
var tpl1 = "{\x25 set data = {\"name\": \"Alice\", \"age\": 30} \x25}Name: {{ data.name }}, Age: {{ data.age }}"
var tpl2 = "{\x25 set empty = {} \x25}Done"
var tpl3 = "{\x25 set single = {\"key\": \"value\"} \x25}Key: {{ single.key }}"
var loader = DictLoader.new({
"tpl1.html": tpl1,
"tpl2.html": tpl2,
"tpl3.html": tpl3
})
var env = Environment.new(loader)
var result1 = env.getTemplate("tpl1.html").render({})
System.print(result1) // expect: Name: Alice, Age: 30
var result2 = env.getTemplate("tpl2.html").render({})
System.print(result2) // expect: Done
var result3 = env.getTemplate("tpl3.html").render({})
System.print(result3) // expect: Key: value

15
test/pexpect/isalive_test.wren vendored Normal file
View File

@ -0,0 +1,15 @@
// retoor <retoor@molodetz.nl>
// nontest
import "pexpect" for Spawn
import "timer" for Timer
System.print("Creating child")
var child = Spawn.new("echo test")
System.print("Child created, pid: %(child.pid)")
System.print("isalive: %(child.isalive)")
Timer.sleep(500)
System.print("After sleep, isalive: %(child.isalive)")
System.print("buffer: [%(child.buffer)]")
child.close()
System.print("Done")

21
test/pexpect/patterns.wren vendored Normal file
View File

@ -0,0 +1,21 @@
// retoor <retoor@molodetz.nl>
import "pexpect" for Spawn, Pexpect
var child = Spawn.new("echo 'hello world'")
var result = child.expect(["world", "hello"])
System.print(result) // expect: 1
System.print(child.after) // expect: hello
child.close()
child = Spawn.new("printf 'abc123def'")
result = child.expectExact(["123"])
System.print(result) // expect: 0
System.print(child.before) // expect: abc
System.print(child.after) // expect: 123
child.close()
child = Spawn.new("echo 'test multiple patterns'")
result = child.expect(["foo", "bar", "multiple"])
System.print(result) // expect: 2
child.close()

16
test/pexpect/properties.wren vendored Normal file
View File

@ -0,0 +1,16 @@
// retoor <retoor@molodetz.nl>
import "pexpect" for Spawn, Pexpect
var child = Spawn.new("echo test")
System.print(child.pid > 0) // expect: true
System.print(child.timeout) // expect: 30
child.timeout = 5
System.print(child.timeout) // expect: 5
System.print(child.maxread) // expect: 2000
System.print(child.searchwindowsize) // expect: 0
child.expect(["test"])
child.wait()
System.print(child.exitstatus == 0) // expect: true
child.close()

23
test/pexpect/read_test.wren vendored Normal file
View File

@ -0,0 +1,23 @@
// retoor <retoor@molodetz.nl>
// nontest
import "pexpect" for Spawn
import "timer" for Timer
System.print("1. Creating child")
var child = Spawn.new("echo hello")
System.print("2. Sleeping 200ms")
Timer.sleep(200)
System.print("3. Buffer: [%(child.buffer)]")
System.print("4. isalive: %(child.isalive)")
System.print("5. Calling readNonblocking")
var data = child.readNonblocking(100, 1)
System.print("6. Data: [%(data)]")
System.print("7. Buffer after read: [%(child.buffer)]")
System.print("8. Closing")
child.close()
System.print("Done")

9
test/pexpect/run_simple.wren vendored Normal file
View File

@ -0,0 +1,9 @@
// retoor <retoor@molodetz.nl>
// nontest
import "pexpect" for Spawn, Pexpect
System.print("Testing Pexpect.run")
var output = Pexpect.run("echo test")
System.print("Output: [%(output)]")
System.print("Done")

14
test/pexpect/spawn.wren vendored Normal file
View File

@ -0,0 +1,14 @@
// retoor <retoor@molodetz.nl>
import "pexpect" for Spawn, Pexpect
var child = Spawn.new("echo hello")
var result = child.expect(["hello"])
System.print(result) // expect: 0
child = Spawn.new("cat")
child.sendline("test input")
var idx = child.expect(["test input"])
System.print(idx) // expect: 0
child.sendeof()
child.close()

10
test/pexpect/timeout.wren vendored Normal file
View File

@ -0,0 +1,10 @@
// retoor <retoor@molodetz.nl>
import "pexpect" for Spawn, Pexpect
var child = Spawn.new("sleep 10")
child.timeout = 0.5
var result = child.expect(["never_match"])
System.print(result) // expect: -2
child.terminate(true)
child.close()

18
test/yaml/comments.wren vendored Normal file
View File

@ -0,0 +1,18 @@
// retoor <retoor@molodetz.nl>
import "yaml" for Yaml
var yaml = "
# This is a comment
name: test
# Another comment
value: 123
"
var data = Yaml.parse(yaml)
System.print(data["name"]) // expect: test
System.print(data["value"]) // expect: 123
var inlineComment = "name: test # inline comment"
var data2 = Yaml.parse(inlineComment)
System.print(data2["name"]) // expect: test

25
test/yaml/complex.wren vendored Normal file
View File

@ -0,0 +1,25 @@
// retoor <retoor@molodetz.nl>
import "yaml" for Yaml
var yaml = "
sections:
- title: Getting Started
pages:
- file: index
title: Overview
- file: installation
title: Installation
- title: API Reference
pages:
- file: json
title: json
"
var nav = Yaml.parse(yaml)
System.print(nav["sections"][0]["title"]) // expect: Getting Started
System.print(nav["sections"][0]["pages"][0]["file"]) // expect: index
System.print(nav["sections"][0]["pages"][0]["title"]) // expect: Overview
System.print(nav["sections"][0]["pages"][1]["file"]) // expect: installation
System.print(nav["sections"][1]["title"]) // expect: API Reference
System.print(nav["sections"][1]["pages"][0]["file"]) // expect: json

12
test/yaml/error_invalid.wren vendored Normal file
View File

@ -0,0 +1,12 @@
// retoor <retoor@molodetz.nl>
import "yaml" for Yaml
var empty = Yaml.parse("")
System.print(empty) // expect: null
var nullInput = Yaml.parse(null)
System.print(nullInput) // expect: null
var whitespace = Yaml.parse(" \n \n ")
System.print(whitespace) // expect: null

21
test/yaml/lists.wren vendored Normal file
View File

@ -0,0 +1,21 @@
// retoor <retoor@molodetz.nl>
import "yaml" for Yaml
var yaml = "items:\n - first\n - second\n - third"
var data = Yaml.parse(yaml)
System.print(data["items"][0]) // expect: first
System.print(data["items"][1]) // expect: second
System.print(data["items"][2]) // expect: third
System.print(data["items"].count) // expect: 3
var simple = "- one\n- two\n- three"
var list = Yaml.parse(simple)
System.print(list[0]) // expect: one
System.print(list[1]) // expect: two
System.print(list.count) // expect: 3
var numbers = "- 1\n- 2\n- 3"
var numList = Yaml.parse(numbers)
System.print(numList[0]) // expect: 1
System.print(numList[1]) // expect: 2

17
test/yaml/nested.wren vendored Normal file
View File

@ -0,0 +1,17 @@
// retoor <retoor@molodetz.nl>
import "yaml" for Yaml
var yaml = "server:\n host: localhost\n port: 8080"
var config = Yaml.parse(yaml)
System.print(config["server"]["host"]) // expect: localhost
System.print(config["server"]["port"]) // expect: 8080
var deep = "
level1:
level2:
level3:
value: deep
"
var deepConfig = Yaml.parse(deep)
System.print(deepConfig["level1"]["level2"]["level3"]["value"]) // expect: deep

27
test/yaml/parse.wren vendored Normal file
View File

@ -0,0 +1,27 @@
// retoor <retoor@molodetz.nl>
import "yaml" for Yaml
var simple = Yaml.parse("name: test")
System.print(simple["name"]) // expect: test
var num = Yaml.parse("count: 42")
System.print(num["count"]) // expect: 42
var float = Yaml.parse("value: 3.14")
System.print(float["value"]) // expect: 3.14
var bool = Yaml.parse("active: true")
System.print(bool["active"]) // expect: true
var boolFalse = Yaml.parse("active: false")
System.print(boolFalse["active"]) // expect: false
var nullVal = Yaml.parse("value: null")
System.print(nullVal["value"]) // expect: null
var quoted = Yaml.parse("name: \"hello world\"")
System.print(quoted["name"]) // expect: hello world
var singleQuoted = Yaml.parse("name: 'hello world'")
System.print(singleQuoted["name"]) // expect: hello world

25
test/yaml/stringify.wren vendored Normal file
View File

@ -0,0 +1,25 @@
// retoor <retoor@molodetz.nl>
import "yaml" for Yaml
var simple = {"name": "test", "value": 42}
var str = Yaml.stringify(simple)
System.print(str.contains("name: test")) // expect: true
System.print(str.contains("value: 42")) // expect: true
var withList = {"items": ["one", "two", "three"]}
var listStr = Yaml.stringify(withList)
System.print(listStr.contains("items:")) // expect: true
System.print(listStr.contains("- one")) // expect: true
System.print(listStr.contains("- two")) // expect: true
var nested = {"server": {"host": "localhost", "port": 8080}}
var nestedStr = Yaml.stringify(nested)
System.print(nestedStr.contains("server:")) // expect: true
System.print(nestedStr.contains("host: localhost")) // expect: true
System.print(Yaml.stringify(null)) // expect: null
System.print(Yaml.stringify(true)) // expect: true
System.print(Yaml.stringify(false)) // expect: false
System.print(Yaml.stringify(42)) // expect: 42
System.print(Yaml.stringify("hello")) // expect: hello