feat: add async/await keywords, Num/String extensions, and Jinja markdown example
Add `async` and `await` token support to the Wren compiler with scheduler resolution logic, extend `Num` with `e` constant, hyperbolic trig, base conversion, and new methods (`isZero`, `gcd`, `lcm`, `digits`, etc.), extend `String` with `lower`, `upper`, `capitalize`, `title`, and regenerate `wren_core.wren.inc`. Include `Makefile` for multi-platform builds, rewrite `README.md` with updated build instructions, and add `example/await_demo.wren` and `example/jinja_markdown.wren` demonstrating new features.
This commit is contained in:
@@ -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)
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
Vendored
+81
@@ -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_()
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
+5
-1
@@ -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;
|
||||
|
||||
|
||||
Vendored
+48
-5
@@ -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_()
|
||||
|
||||
@@ -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";
|
||||
|
||||
Vendored
+173
@@ -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))" }
|
||||
}
|
||||
@@ -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";
|
||||
Reference in New Issue
Block a user