Update.
This commit is contained in:
parent
4de99d477f
commit
90618f2336
144
apps/generator2.wren
vendored
Executable file
144
apps/generator2.wren
vendored
Executable file
@ -0,0 +1,144 @@
|
||||
#!/usr/local/bin/wren
|
||||
|
||||
import "pathlib" for Path
|
||||
import "http" for Http
|
||||
import "json" for Json
|
||||
import "env" for Environment
|
||||
import "argparse" for ArgumentParser
|
||||
import "os" for Process
|
||||
|
||||
class SourceCodeGenerator {
|
||||
static VERSION { "1.0" }
|
||||
static DEFAULT_MODEL { "anthropic/claude-haiku-4.5" }
|
||||
static API_ENDPOINT { "https://openrouter.ai/api/v1/chat/completions" }
|
||||
static TUTORIAL_PATH { "apps/minitut.md" }
|
||||
|
||||
static DEFAULT_PROMPT {
|
||||
"A small but useful application that combines several modules / libraries that is plug and play to use! Small description on top commenten what the application is and what it does and where it can be used for."
|
||||
}
|
||||
|
||||
static run() {
|
||||
System.print("// wren source code generator v%(this.VERSION)")
|
||||
|
||||
var startTime = System.clock
|
||||
var args = parseArguments()
|
||||
var apiKey = getApiKey()
|
||||
var tutorial = loadTutorial()
|
||||
var messages = buildMessages(tutorial, args["prompt"])
|
||||
var sourceCode = generateCode(apiKey, messages)
|
||||
|
||||
printResults(startTime, sourceCode)
|
||||
saveOutput(args["output"], sourceCode)
|
||||
}
|
||||
|
||||
static parseArguments() {
|
||||
var parser = ArgumentParser.new()
|
||||
parser.addArgument("prompt", {"default": this.DEFAULT_PROMPT})
|
||||
parser.addArgument("-o", {"long": "--output", "default": ""})
|
||||
return parser.parseArgs()
|
||||
}
|
||||
|
||||
static getApiKey() {
|
||||
var apiKey = Environment.get("OPENROUTER_API_KEY")
|
||||
if (apiKey == null || apiKey.count == 0) {
|
||||
System.print("Error: OPENROUTER_API_KEY environment variable not set")
|
||||
Fiber.abort("Missing API key")
|
||||
}
|
||||
return apiKey
|
||||
}
|
||||
|
||||
static loadTutorial() {
|
||||
return Path.new(this.TUTORIAL_PATH).readText()
|
||||
}
|
||||
|
||||
static buildMessages(tutorial, userPrompt) {
|
||||
var messages = []
|
||||
messages.add({
|
||||
"role": "system",
|
||||
"content": "You are an application generator that will produce wren source code exclusively as described below:\n" + tutorial
|
||||
})
|
||||
messages.add({
|
||||
"role": "user",
|
||||
"content": "Please generate source code based on everything what i say from now on."
|
||||
})
|
||||
messages.add({
|
||||
"role": "assistant",
|
||||
"content": "I will respond in literally valid JSON only in this format: {\"source_code\":\"<the source code>\"} without any markup or markdown formatting."
|
||||
})
|
||||
messages.add({
|
||||
"role": "user",
|
||||
"content": userPrompt
|
||||
})
|
||||
return messages
|
||||
}
|
||||
|
||||
static generateCode(apiKey, messages) {
|
||||
var requestBody = {
|
||||
"model": "%(this.DEFAULT_MODEL)",
|
||||
"messages": messages
|
||||
}
|
||||
|
||||
var headers = {
|
||||
"Authorization": "Bearer " + apiKey,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
var response = Http.post(this.API_ENDPOINT, requestBody, headers)
|
||||
|
||||
if (!response.ok) {
|
||||
System.print("Error: %(response.body)")
|
||||
return ""
|
||||
}
|
||||
|
||||
return extractSourceCode(response.body)
|
||||
}
|
||||
|
||||
static extractSourceCode(responseBody) {
|
||||
var data = Json.parse(responseBody)
|
||||
|
||||
if (data == null || data["choices"] == null || data["choices"].count == 0) {
|
||||
return ""
|
||||
}
|
||||
|
||||
var message = data["choices"][0]["message"]
|
||||
if (message == null || message["content"] == null) {
|
||||
return ""
|
||||
}
|
||||
|
||||
var text = message["content"]
|
||||
text = stripMarkdownWrapper(text)
|
||||
text = extractFromJson(text)
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
static stripMarkdownWrapper(text) {
|
||||
if (text.startsWith("```")) {
|
||||
return text[7..-4].trim()
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
static extractFromJson(text) {
|
||||
if (text.startsWith("{")) {
|
||||
return Json.parse(text)["source_code"]
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
static printResults(startTime, output) {
|
||||
var elapsedTime = System.clock - startTime
|
||||
System.print("// Generation time: %(elapsedTime)")
|
||||
System.print("")
|
||||
System.print(output)
|
||||
}
|
||||
|
||||
static saveOutput(outputPath, content) {
|
||||
if (!outputPath.isEmpty) {
|
||||
Path.new(outputPath).writeText(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SourceCodeGenerator.run()
|
||||
|
||||
2
apps/merge_all_wren.wren
vendored
Normal file → Executable file
2
apps/merge_all_wren.wren
vendored
Normal file → Executable file
@ -1,3 +1,5 @@
|
||||
#!/usr/local/bin/wren
|
||||
|
||||
import "io" for File, Directory
|
||||
|
||||
class WrenFileReader {
|
||||
|
||||
2
apps/merge_docs.wren
vendored
Normal file → Executable file
2
apps/merge_docs.wren
vendored
Normal file → Executable file
@ -1,3 +1,5 @@
|
||||
#!/usr/local/bin/wren
|
||||
|
||||
import "pathlib" for Path
|
||||
|
||||
var startTime = System.clock
|
||||
|
||||
1
apps/phonebook.wren
vendored
Normal file → Executable file
1
apps/phonebook.wren
vendored
Normal file → Executable file
@ -1,3 +1,4 @@
|
||||
#!/usr/local/bin/wren
|
||||
// wren source code generator v1.0
|
||||
// Generation time: 0.101419
|
||||
|
||||
|
||||
@ -116,6 +116,7 @@ OBJECTS += $(OBJDIR)/crypto.o
|
||||
OBJECTS += $(OBJDIR)/datetime.o
|
||||
OBJECTS += $(OBJDIR)/dns.o
|
||||
OBJECTS += $(OBJDIR)/env.o
|
||||
OBJECTS += $(OBJDIR)/fswatch.o
|
||||
OBJECTS += $(OBJDIR)/io.o
|
||||
OBJECTS += $(OBJDIR)/json.o
|
||||
OBJECTS += $(OBJDIR)/math_module.o
|
||||
@ -143,12 +144,14 @@ OBJECTS += $(OBJDIR)/repl.o
|
||||
OBJECTS += $(OBJDIR)/scheduler.o
|
||||
OBJECTS += $(OBJDIR)/signal.o
|
||||
OBJECTS += $(OBJDIR)/signal_module.o
|
||||
OBJECTS += $(OBJDIR)/sysinfo.o
|
||||
OBJECTS += $(OBJDIR)/sqlite3.o
|
||||
OBJECTS += $(OBJDIR)/sqlite_module.o
|
||||
OBJECTS += $(OBJDIR)/subprocess.o
|
||||
OBJECTS += $(OBJDIR)/strutil.o
|
||||
OBJECTS += $(OBJDIR)/pathlib.o
|
||||
OBJECTS += $(OBJDIR)/tls.o
|
||||
OBJECTS += $(OBJDIR)/udp_module.o
|
||||
OBJECTS += $(OBJDIR)/stream.o
|
||||
OBJECTS += $(OBJDIR)/strscpy.o
|
||||
OBJECTS += $(OBJDIR)/sysinfo-loadavg.o
|
||||
@ -401,6 +404,9 @@ $(OBJDIR)/dns.o: ../../src/module/dns.c
|
||||
$(OBJDIR)/env.o: ../../src/module/env.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/fswatch.o: ../../src/module/fswatch.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/io.o: ../../src/module/io.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
@ -428,6 +434,9 @@ $(OBJDIR)/timer1.o: ../../src/module/timer.c
|
||||
$(OBJDIR)/signal_module.o: ../../src/module/signal_module.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/sysinfo.o: ../../src/module/sysinfo.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/subprocess.o: ../../src/module/subprocess.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
@ -449,6 +458,9 @@ $(OBJDIR)/pathlib.o: ../../src/module/pathlib.c
|
||||
$(OBJDIR)/tls.o: ../../src/module/tls.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/udp_module.o: ../../src/module/udp.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
|
||||
-include $(OBJECTS:%.o=%.d)
|
||||
ifneq (,$(PCH))
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include "datetime.wren.inc"
|
||||
#include "dns.wren.inc"
|
||||
#include "env.wren.inc"
|
||||
#include "fswatch.wren.inc"
|
||||
#include "html.wren.inc"
|
||||
#include "http.wren.inc"
|
||||
#include "io.wren.inc"
|
||||
@ -26,11 +27,13 @@
|
||||
#include "repl.wren.inc"
|
||||
#include "scheduler.wren.inc"
|
||||
#include "signal.wren.inc"
|
||||
#include "sysinfo.wren.inc"
|
||||
#include "sqlite.wren.inc"
|
||||
#include "subprocess.wren.inc"
|
||||
#include "tempfile.wren.inc"
|
||||
#include "timer.wren.inc"
|
||||
#include "tls.wren.inc"
|
||||
#include "udp.wren.inc"
|
||||
#include "uuid.wren.inc"
|
||||
#include "wdantic.wren.inc"
|
||||
#include "web.wren.inc"
|
||||
@ -60,6 +63,11 @@ extern void envGet(WrenVM* vm);
|
||||
extern void envSet(WrenVM* vm);
|
||||
extern void envDelete(WrenVM* vm);
|
||||
extern void envAll(WrenVM* vm);
|
||||
extern void fswatchAllocate(WrenVM* vm);
|
||||
extern void fswatchFinalize(void* data);
|
||||
extern void fswatchStart(WrenVM* vm);
|
||||
extern void fswatchStop(WrenVM* vm);
|
||||
extern void fswatchIsActive(WrenVM* vm);
|
||||
extern void jsonParse(WrenVM* vm);
|
||||
extern void mathSin(WrenVM* vm);
|
||||
extern void mathCos(WrenVM* vm);
|
||||
@ -84,7 +92,9 @@ extern void mathRound(WrenVM* vm);
|
||||
extern void mathAbs(WrenVM* vm);
|
||||
extern void fileAllocate(WrenVM* vm);
|
||||
extern void fileFinalize(void* data);
|
||||
extern void fileCopy(WrenVM* vm);
|
||||
extern void fileDelete(WrenVM* vm);
|
||||
extern void fileMove(WrenVM* vm);
|
||||
extern void fileOpen(WrenVM* vm);
|
||||
extern void fileSizePath(WrenVM* vm);
|
||||
extern void fileClose(WrenVM* vm);
|
||||
@ -115,15 +125,23 @@ extern void statSpecialDevice(WrenVM* vm);
|
||||
extern void statUser(WrenVM* vm);
|
||||
extern void statIsDirectory(WrenVM* vm);
|
||||
extern void statIsFile(WrenVM* vm);
|
||||
extern void statMtime(WrenVM* vm);
|
||||
extern void statAtime(WrenVM* vm);
|
||||
extern void statCtime(WrenVM* vm);
|
||||
extern void stdinIsRaw(WrenVM* vm);
|
||||
extern void stdinIsRawSet(WrenVM* vm);
|
||||
extern void stdinIsTerminal(WrenVM* vm);
|
||||
extern void stdinWindowSize(WrenVM* vm);
|
||||
extern void stdinReadStart(WrenVM* vm);
|
||||
extern void stdinReadStop(WrenVM* vm);
|
||||
extern void stdoutFlush(WrenVM* vm);
|
||||
extern void stderrWrite(WrenVM* vm);
|
||||
extern void schedulerCaptureMethods(WrenVM* vm);
|
||||
extern void timerStartTimer(WrenVM* vm);
|
||||
extern void timerStartInterval(WrenVM* vm);
|
||||
extern void timerStopInterval(WrenVM* vm);
|
||||
extern void timerIsIntervalActive(WrenVM* vm);
|
||||
extern void timerStartImmediate(WrenVM* vm);
|
||||
extern void socketAllocate(WrenVM* vm);
|
||||
extern void socketFinalize(void* data);
|
||||
extern void socketConnect(WrenVM* vm);
|
||||
@ -140,6 +158,17 @@ extern void serverClose(WrenVM* vm);
|
||||
extern void signalTrap(WrenVM* vm);
|
||||
extern void signalIgnore(WrenVM* vm);
|
||||
extern void signalReset(WrenVM* vm);
|
||||
extern void sysinfoGetCpuInfo(WrenVM* vm);
|
||||
extern void sysinfoGetCpuCount(WrenVM* vm);
|
||||
extern void sysinfoGetLoadAverage(WrenVM* vm);
|
||||
extern void sysinfoGetTotalMemory(WrenVM* vm);
|
||||
extern void sysinfoGetFreeMemory(WrenVM* vm);
|
||||
extern void sysinfoGetUptime(WrenVM* vm);
|
||||
extern void sysinfoGetHostname(WrenVM* vm);
|
||||
extern void sysinfoGetNetworkInterfaces(WrenVM* vm);
|
||||
extern void sysinfoGetHrtime(WrenVM* vm);
|
||||
extern void sysinfoGetResidentMemory(WrenVM* vm);
|
||||
extern void sysinfoGetConstrainedMemory(WrenVM* vm);
|
||||
extern void sqliteAllocate(WrenVM* vm);
|
||||
extern void sqliteFinalize(void* data);
|
||||
extern void sqliteExecute(WrenVM* vm);
|
||||
@ -166,6 +195,19 @@ extern void tlsSocketConnect(WrenVM* vm);
|
||||
extern void tlsSocketWrite(WrenVM* vm);
|
||||
extern void tlsSocketRead(WrenVM* vm);
|
||||
extern void tlsSocketClose(WrenVM* vm);
|
||||
extern void udpSocketAllocate(WrenVM* vm);
|
||||
extern void udpSocketFinalize(void* data);
|
||||
extern void udpSocketBind(WrenVM* vm);
|
||||
extern void udpSocketSend(WrenVM* vm);
|
||||
extern void udpSocketReceive(WrenVM* vm);
|
||||
extern void udpSocketClose(WrenVM* vm);
|
||||
extern void udpSocketSetBroadcast(WrenVM* vm);
|
||||
extern void udpSocketSetMulticastTTL(WrenVM* vm);
|
||||
extern void udpSocketSetMulticastLoopback(WrenVM* vm);
|
||||
extern void udpSocketJoinMulticast(WrenVM* vm);
|
||||
extern void udpSocketLeaveMulticast(WrenVM* vm);
|
||||
extern void udpSocketLocalAddress(WrenVM* vm);
|
||||
extern void udpSocketLocalPort(WrenVM* vm);
|
||||
extern void strutilToLower(WrenVM* vm);
|
||||
extern void strutilToUpper(WrenVM* vm);
|
||||
extern void strutilHexEncode(WrenVM* vm);
|
||||
@ -309,6 +351,15 @@ static ModuleRegistry modules[] =
|
||||
STATIC_METHOD("all", envAll)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
MODULE(fswatch)
|
||||
CLASS(FileWatcher)
|
||||
ALLOCATE(fswatchAllocate)
|
||||
FINALIZE(fswatchFinalize)
|
||||
METHOD("start_(_)", fswatchStart)
|
||||
METHOD("stop_()", fswatchStop)
|
||||
METHOD("isActive", fswatchIsActive)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
MODULE(html)
|
||||
END_MODULE
|
||||
MODULE(http)
|
||||
@ -322,7 +373,9 @@ static ModuleRegistry modules[] =
|
||||
CLASS(File)
|
||||
ALLOCATE(fileAllocate)
|
||||
FINALIZE(fileFinalize)
|
||||
STATIC_METHOD("copy_(_,_,_)", fileCopy)
|
||||
STATIC_METHOD("delete_(_,_)", fileDelete)
|
||||
STATIC_METHOD("move_(_,_,_)", fileMove)
|
||||
STATIC_METHOD("open_(_,_,_)", fileOpen)
|
||||
STATIC_METHOD("realPath_(_,_)", fileRealPath)
|
||||
STATIC_METHOD("sizePath_(_,_)", fileSizePath)
|
||||
@ -347,11 +400,15 @@ static ModuleRegistry modules[] =
|
||||
METHOD("user", statUser)
|
||||
METHOD("isDirectory", statIsDirectory)
|
||||
METHOD("isFile", statIsFile)
|
||||
METHOD("mtime", statMtime)
|
||||
METHOD("atime", statAtime)
|
||||
METHOD("ctime", statCtime)
|
||||
END_CLASS
|
||||
CLASS(Stdin)
|
||||
STATIC_METHOD("isRaw", stdinIsRaw)
|
||||
STATIC_METHOD("isRaw=(_)", stdinIsRawSet)
|
||||
STATIC_METHOD("isTerminal", stdinIsTerminal)
|
||||
STATIC_METHOD("windowSize", stdinWindowSize)
|
||||
STATIC_METHOD("readStart_()", stdinReadStart)
|
||||
STATIC_METHOD("readStop_()", stdinReadStop)
|
||||
END_CLASS
|
||||
@ -472,6 +529,21 @@ static ModuleRegistry modules[] =
|
||||
STATIC_METHOD("reset_(_)", signalReset)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
MODULE(sysinfo)
|
||||
CLASS(SysInfo)
|
||||
STATIC_METHOD("cpuInfo", sysinfoGetCpuInfo)
|
||||
STATIC_METHOD("cpuCount", sysinfoGetCpuCount)
|
||||
STATIC_METHOD("loadAverage", sysinfoGetLoadAverage)
|
||||
STATIC_METHOD("totalMemory", sysinfoGetTotalMemory)
|
||||
STATIC_METHOD("freeMemory", sysinfoGetFreeMemory)
|
||||
STATIC_METHOD("uptime", sysinfoGetUptime)
|
||||
STATIC_METHOD("hostname", sysinfoGetHostname)
|
||||
STATIC_METHOD("networkInterfaces", sysinfoGetNetworkInterfaces)
|
||||
STATIC_METHOD("hrtime", sysinfoGetHrtime)
|
||||
STATIC_METHOD("residentMemory", sysinfoGetResidentMemory)
|
||||
STATIC_METHOD("constrainedMemory", sysinfoGetConstrainedMemory)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
MODULE(strutil)
|
||||
CLASS(Str)
|
||||
STATIC_METHOD("toLower(_)", strutilToLower)
|
||||
@ -510,6 +582,10 @@ static ModuleRegistry modules[] =
|
||||
MODULE(timer)
|
||||
CLASS(Timer)
|
||||
STATIC_METHOD("startTimer_(_,_)", timerStartTimer)
|
||||
STATIC_METHOD("startInterval_(_,_)", timerStartInterval)
|
||||
STATIC_METHOD("stopInterval_(_)", timerStopInterval)
|
||||
STATIC_METHOD("isIntervalActive_(_)", timerIsIntervalActive)
|
||||
STATIC_METHOD("startImmediate_(_)", timerStartImmediate)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
MODULE(tls)
|
||||
@ -522,6 +598,23 @@ static ModuleRegistry modules[] =
|
||||
METHOD("close_()", tlsSocketClose)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
MODULE(udp)
|
||||
CLASS(UdpSocket)
|
||||
ALLOCATE(udpSocketAllocate)
|
||||
FINALIZE(udpSocketFinalize)
|
||||
METHOD("bind_(_,_)", udpSocketBind)
|
||||
METHOD("send_(_,_,_,_)", udpSocketSend)
|
||||
METHOD("receive_(_)", udpSocketReceive)
|
||||
METHOD("close_()", udpSocketClose)
|
||||
METHOD("setBroadcast_(_)", udpSocketSetBroadcast)
|
||||
METHOD("setMulticastTTL_(_)", udpSocketSetMulticastTTL)
|
||||
METHOD("setMulticastLoopback_(_)", udpSocketSetMulticastLoopback)
|
||||
METHOD("joinMulticast_(_,_)", udpSocketJoinMulticast)
|
||||
METHOD("leaveMulticast_(_,_)", udpSocketLeaveMulticast)
|
||||
METHOD("localAddress", udpSocketLocalAddress)
|
||||
METHOD("localPort", udpSocketLocalPort)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
MODULE(uuid)
|
||||
END_MODULE
|
||||
MODULE(wdantic)
|
||||
|
||||
@ -189,6 +189,23 @@ void fileFinalize(void* data)
|
||||
uv_fs_req_cleanup(&request);
|
||||
}
|
||||
|
||||
static void fileCopyCallback(uv_fs_t* request)
|
||||
{
|
||||
if (handleRequestError(request)) return;
|
||||
schedulerResume(freeRequest(request), false);
|
||||
}
|
||||
|
||||
void fileCopy(WrenVM* vm)
|
||||
{
|
||||
const char* src = wrenGetSlotString(vm, 1);
|
||||
const char* dst = wrenGetSlotString(vm, 2);
|
||||
WrenHandle* fiber = wrenGetSlotHandle(vm, 3);
|
||||
uv_fs_t* request = createRequest(fiber);
|
||||
|
||||
int error = uv_fs_copyfile(getLoop(), request, src, dst, 0, fileCopyCallback);
|
||||
if (error != 0) schedulerResumeError(fiber, uv_strerror(error));
|
||||
}
|
||||
|
||||
static void fileDeleteCallback(uv_fs_t* request)
|
||||
{
|
||||
if (handleRequestError(request)) return;
|
||||
@ -200,11 +217,28 @@ void fileDelete(WrenVM* vm)
|
||||
const char* path = wrenGetSlotString(vm, 1);
|
||||
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
|
||||
uv_fs_t* request = createRequest(fiber);
|
||||
|
||||
|
||||
int error = uv_fs_unlink(getLoop(), request, path, fileDeleteCallback);
|
||||
if (error != 0 ) schedulerResumeError(fiber, uv_strerror(error));
|
||||
}
|
||||
|
||||
static void fileMoveCallback(uv_fs_t* request)
|
||||
{
|
||||
if (handleRequestError(request)) return;
|
||||
schedulerResume(freeRequest(request), false);
|
||||
}
|
||||
|
||||
void fileMove(WrenVM* vm)
|
||||
{
|
||||
const char* src = wrenGetSlotString(vm, 1);
|
||||
const char* dst = wrenGetSlotString(vm, 2);
|
||||
WrenHandle* fiber = wrenGetSlotHandle(vm, 3);
|
||||
uv_fs_t* request = createRequest(fiber);
|
||||
|
||||
int error = uv_fs_rename(getLoop(), request, src, dst, fileMoveCallback);
|
||||
if (error != 0) schedulerResumeError(fiber, uv_strerror(error));
|
||||
}
|
||||
|
||||
static void fileOpenCallback(uv_fs_t* request)
|
||||
{
|
||||
if (handleRequestError(request)) return;
|
||||
@ -502,6 +536,24 @@ void statIsFile(WrenVM* vm)
|
||||
wrenSetSlotBool(vm, 0, S_ISREG(stat->st_mode));
|
||||
}
|
||||
|
||||
void statMtime(WrenVM* vm)
|
||||
{
|
||||
uv_stat_t* stat = (uv_stat_t*)wrenGetSlotForeign(vm, 0);
|
||||
wrenSetSlotDouble(vm, 0, (double)stat->st_mtim.tv_sec + (double)stat->st_mtim.tv_nsec / 1e9);
|
||||
}
|
||||
|
||||
void statAtime(WrenVM* vm)
|
||||
{
|
||||
uv_stat_t* stat = (uv_stat_t*)wrenGetSlotForeign(vm, 0);
|
||||
wrenSetSlotDouble(vm, 0, (double)stat->st_atim.tv_sec + (double)stat->st_atim.tv_nsec / 1e9);
|
||||
}
|
||||
|
||||
void statCtime(WrenVM* vm)
|
||||
{
|
||||
uv_stat_t* stat = (uv_stat_t*)wrenGetSlotForeign(vm, 0);
|
||||
wrenSetSlotDouble(vm, 0, (double)stat->st_ctim.tv_sec + (double)stat->st_ctim.tv_nsec / 1e9);
|
||||
}
|
||||
|
||||
// Sets up the stdin stream if not already initialized.
|
||||
static void initStdin()
|
||||
{
|
||||
@ -563,6 +615,34 @@ void stdinIsTerminal(WrenVM* vm)
|
||||
wrenSetSlotBool(vm, 0, uv_guess_handle(stdinDescriptor) == UV_TTY);
|
||||
}
|
||||
|
||||
void stdinWindowSize(WrenVM* vm)
|
||||
{
|
||||
initStdin();
|
||||
wrenEnsureSlots(vm, 2);
|
||||
|
||||
if (uv_guess_handle(stdinDescriptor) != UV_TTY)
|
||||
{
|
||||
wrenSetSlotNull(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
uv_tty_t* handle = (uv_tty_t*)stdinStream;
|
||||
int width, height;
|
||||
int result = uv_tty_get_winsize(handle, &width, &height);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
wrenSetSlotNull(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
wrenSetSlotNewList(vm, 0);
|
||||
wrenSetSlotDouble(vm, 1, (double)width);
|
||||
wrenInsertInList(vm, 0, -1, 1);
|
||||
wrenSetSlotDouble(vm, 1, (double)height);
|
||||
wrenInsertInList(vm, 0, -1, 1);
|
||||
}
|
||||
|
||||
void stdoutFlush(WrenVM* vm)
|
||||
{
|
||||
fflush(stdout);
|
||||
|
||||
23
src/module/io.wren
vendored
23
src/module/io.wren
vendored
@ -100,6 +100,22 @@ foreign class File {
|
||||
return File.open(path) {|file| file.readBytes(file.size) }
|
||||
}
|
||||
|
||||
static write(path, data) {
|
||||
return File.create(path) {|file| file.writeBytes(data, 0) }
|
||||
}
|
||||
|
||||
static copy(src, dst) {
|
||||
ensureString_(src)
|
||||
ensureString_(dst)
|
||||
return Scheduler.await_ { copy_(src, dst, Fiber.current) }
|
||||
}
|
||||
|
||||
static move(src, dst) {
|
||||
ensureString_(src)
|
||||
ensureString_(dst)
|
||||
return Scheduler.await_ { move_(src, dst, Fiber.current) }
|
||||
}
|
||||
|
||||
// TODO: This works for directories too, so putting it on File is kind of
|
||||
// lame. Consider reorganizing these classes some.
|
||||
static realPath(path) {
|
||||
@ -167,7 +183,9 @@ foreign class File {
|
||||
if (value < 0) Fiber.abort("%(name) cannot be negative.")
|
||||
}
|
||||
|
||||
foreign static copy_(src, dst, fiber)
|
||||
foreign static delete_(path, fiber)
|
||||
foreign static move_(src, dst, fiber)
|
||||
foreign static open_(path, flags, fiber)
|
||||
foreign static realPath_(path, fiber)
|
||||
foreign static sizePath_(path, fiber)
|
||||
@ -213,13 +231,16 @@ foreign class Stat {
|
||||
|
||||
foreign isFile
|
||||
foreign isDirectory
|
||||
// TODO: Other mode checks.
|
||||
foreign mtime
|
||||
foreign atime
|
||||
foreign ctime
|
||||
}
|
||||
|
||||
class Stdin {
|
||||
foreign static isRaw
|
||||
foreign static isRaw=(value)
|
||||
foreign static isTerminal
|
||||
foreign static windowSize
|
||||
|
||||
static readByte() {
|
||||
return read_ {
|
||||
|
||||
@ -104,6 +104,22 @@ static const char* ioModuleSource =
|
||||
" return File.open(path) {|file| file.readBytes(file.size) }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static write(path, data) {\n"
|
||||
" return File.create(path) {|file| file.writeBytes(data, 0) }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static copy(src, dst) {\n"
|
||||
" ensureString_(src)\n"
|
||||
" ensureString_(dst)\n"
|
||||
" return Scheduler.await_ { copy_(src, dst, Fiber.current) }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static move(src, dst) {\n"
|
||||
" ensureString_(src)\n"
|
||||
" ensureString_(dst)\n"
|
||||
" return Scheduler.await_ { move_(src, dst, Fiber.current) }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" // TODO: This works for directories too, so putting it on File is kind of\n"
|
||||
" // lame. Consider reorganizing these classes some.\n"
|
||||
" static realPath(path) {\n"
|
||||
@ -171,7 +187,9 @@ static const char* ioModuleSource =
|
||||
" if (value < 0) Fiber.abort(\"%(name) cannot be negative.\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static copy_(src, dst, fiber)\n"
|
||||
" foreign static delete_(path, fiber)\n"
|
||||
" foreign static move_(src, dst, fiber)\n"
|
||||
" foreign static open_(path, flags, fiber)\n"
|
||||
" foreign static realPath_(path, fiber)\n"
|
||||
" foreign static sizePath_(path, fiber)\n"
|
||||
@ -217,13 +235,16 @@ static const char* ioModuleSource =
|
||||
"\n"
|
||||
" foreign isFile\n"
|
||||
" foreign isDirectory\n"
|
||||
" // TODO: Other mode checks.\n"
|
||||
" foreign mtime\n"
|
||||
" foreign atime\n"
|
||||
" foreign ctime\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class Stdin {\n"
|
||||
" foreign static isRaw\n"
|
||||
" foreign static isRaw=(value)\n"
|
||||
" foreign static isTerminal\n"
|
||||
" foreign static windowSize\n"
|
||||
"\n"
|
||||
" static readByte() {\n"
|
||||
" return read_ {\n"
|
||||
|
||||
25
src/module/pathlib.wren
vendored
25
src/module/pathlib.wren
vendored
@ -509,8 +509,27 @@ class Path is PurePath {
|
||||
}
|
||||
|
||||
copyfile(dest) {
|
||||
var content = readBytes()
|
||||
var destPath = dest is Path ? dest : Path.new(dest)
|
||||
destPath.writeBytes(content)
|
||||
var destPath = dest is Path ? dest.toString : dest.toString
|
||||
File.copy(toString, destPath)
|
||||
}
|
||||
|
||||
size() {
|
||||
var s = stat()
|
||||
return s.size
|
||||
}
|
||||
|
||||
mtime() {
|
||||
var s = lstat()
|
||||
return s.mtime
|
||||
}
|
||||
|
||||
atime() {
|
||||
var s = lstat()
|
||||
return s.atime
|
||||
}
|
||||
|
||||
ctime() {
|
||||
var s = lstat()
|
||||
return s.ctime
|
||||
}
|
||||
}
|
||||
|
||||
@ -513,8 +513,27 @@ static const char* pathlibModuleSource =
|
||||
" }\n"
|
||||
"\n"
|
||||
" copyfile(dest) {\n"
|
||||
" var content = readBytes()\n"
|
||||
" var destPath = dest is Path ? dest : Path.new(dest)\n"
|
||||
" destPath.writeBytes(content)\n"
|
||||
" var destPath = dest is Path ? dest.toString : dest.toString\n"
|
||||
" File.copy(toString, destPath)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" size() {\n"
|
||||
" var s = stat()\n"
|
||||
" return s.size\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" mtime() {\n"
|
||||
" var s = lstat()\n"
|
||||
" return s.mtime\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" atime() {\n"
|
||||
" var s = lstat()\n"
|
||||
" return s.atime\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" ctime() {\n"
|
||||
" var s = lstat()\n"
|
||||
" return s.ctime\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -7,33 +9,212 @@
|
||||
#include "vm.h"
|
||||
#include "wren.h"
|
||||
|
||||
// Called by libuv when the timer finished closing.
|
||||
static void timerCloseCallback(uv_handle_t* handle)
|
||||
#define MAX_INTERVAL_TIMERS 64
|
||||
|
||||
static WrenHandle* schedulerClass = NULL;
|
||||
static WrenHandle* resumeMethod = NULL;
|
||||
|
||||
typedef struct {
|
||||
uv_timer_t* handle;
|
||||
WrenHandle* fiber;
|
||||
int isActive;
|
||||
int id;
|
||||
} IntervalTimerData;
|
||||
|
||||
static IntervalTimerData intervalTimers[MAX_INTERVAL_TIMERS];
|
||||
static int nextTimerId = 1;
|
||||
static int initialized = 0;
|
||||
|
||||
static void ensureSchedulerMethods()
|
||||
{
|
||||
free(handle);
|
||||
if (schedulerClass != NULL) return;
|
||||
|
||||
WrenVM* vm = getVM();
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "scheduler", "Scheduler", 0);
|
||||
schedulerClass = wrenGetSlotHandle(vm, 0);
|
||||
resumeMethod = wrenMakeCallHandle(vm, "resume_(_)");
|
||||
}
|
||||
|
||||
static void resumeFiberWithoutRelease(WrenHandle* fiber)
|
||||
{
|
||||
WrenVM* vm = getVM();
|
||||
ensureSchedulerMethods();
|
||||
|
||||
wrenEnsureSlots(vm, 2);
|
||||
wrenSetSlotHandle(vm, 0, schedulerClass);
|
||||
wrenSetSlotHandle(vm, 1, fiber);
|
||||
|
||||
WrenInterpretResult result = wrenCall(vm, resumeMethod);
|
||||
if (result == WREN_RESULT_RUNTIME_ERROR)
|
||||
{
|
||||
uv_stop(getLoop());
|
||||
setExitCode(70);
|
||||
}
|
||||
}
|
||||
|
||||
static void initTimerSystem()
|
||||
{
|
||||
if (initialized) return;
|
||||
for (int i = 0; i < MAX_INTERVAL_TIMERS; i++) {
|
||||
intervalTimers[i].handle = NULL;
|
||||
intervalTimers[i].fiber = NULL;
|
||||
intervalTimers[i].isActive = 0;
|
||||
intervalTimers[i].id = 0;
|
||||
}
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
static IntervalTimerData* findTimerSlot()
|
||||
{
|
||||
initTimerSystem();
|
||||
for (int i = 0; i < MAX_INTERVAL_TIMERS; i++) {
|
||||
if (!intervalTimers[i].isActive) {
|
||||
return &intervalTimers[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static IntervalTimerData* findTimerById(int id)
|
||||
{
|
||||
initTimerSystem();
|
||||
for (int i = 0; i < MAX_INTERVAL_TIMERS; i++) {
|
||||
if (intervalTimers[i].isActive && intervalTimers[i].id == id) {
|
||||
return &intervalTimers[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void timerCloseCallback(uv_handle_t* handle)
|
||||
{
|
||||
free(handle);
|
||||
}
|
||||
|
||||
// Called by libuv when the timer has completed.
|
||||
static void timerCallback(uv_timer_t* handle)
|
||||
{
|
||||
WrenHandle* fiber = (WrenHandle*)handle->data;
|
||||
WrenHandle* fiber = (WrenHandle*)handle->data;
|
||||
|
||||
// Tell libuv that we don't need the timer anymore.
|
||||
uv_close((uv_handle_t*)handle, timerCloseCallback);
|
||||
uv_close((uv_handle_t*)handle, timerCloseCallback);
|
||||
|
||||
// Run the fiber that was sleeping.
|
||||
schedulerResume(fiber, false);
|
||||
schedulerResume(fiber, false);
|
||||
}
|
||||
|
||||
void timerStartTimer(WrenVM* vm)
|
||||
{
|
||||
int milliseconds = (int)wrenGetSlotDouble(vm, 1);
|
||||
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
|
||||
int milliseconds = (int)wrenGetSlotDouble(vm, 1);
|
||||
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
|
||||
|
||||
// Store the fiber to resume when the timer completes.
|
||||
uv_timer_t* handle = (uv_timer_t*)malloc(sizeof(uv_timer_t));
|
||||
handle->data = fiber;
|
||||
uv_timer_t* handle = (uv_timer_t*)malloc(sizeof(uv_timer_t));
|
||||
handle->data = fiber;
|
||||
|
||||
uv_timer_init(getLoop(), handle);
|
||||
uv_timer_start(handle, timerCallback, milliseconds, 0);
|
||||
uv_timer_init(getLoop(), handle);
|
||||
uv_timer_start(handle, timerCallback, milliseconds, 0);
|
||||
}
|
||||
|
||||
static void intervalCallback(uv_timer_t* handle)
|
||||
{
|
||||
IntervalTimerData* data = (IntervalTimerData*)handle->data;
|
||||
|
||||
if (!data->isActive) return;
|
||||
|
||||
resumeFiberWithoutRelease(data->fiber);
|
||||
}
|
||||
|
||||
static void intervalCloseCallback(uv_handle_t* handle)
|
||||
{
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void timerStartInterval(WrenVM* vm)
|
||||
{
|
||||
int milliseconds = (int)wrenGetSlotDouble(vm, 1);
|
||||
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
|
||||
|
||||
IntervalTimerData* timerData = findTimerSlot();
|
||||
if (timerData == NULL) {
|
||||
wrenSetSlotString(vm, 0, "Too many active interval timers.");
|
||||
wrenAbortFiber(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
uv_timer_t* handle = (uv_timer_t*)malloc(sizeof(uv_timer_t));
|
||||
if (handle == NULL) {
|
||||
wrenSetSlotString(vm, 0, "Memory allocation failed.");
|
||||
wrenAbortFiber(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
uv_timer_init(getLoop(), handle);
|
||||
|
||||
int id = nextTimerId++;
|
||||
timerData->handle = handle;
|
||||
timerData->fiber = fiber;
|
||||
timerData->isActive = 1;
|
||||
timerData->id = id;
|
||||
handle->data = timerData;
|
||||
|
||||
uv_timer_start(handle, intervalCallback, milliseconds, milliseconds);
|
||||
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotDouble(vm, 0, (double)id);
|
||||
}
|
||||
|
||||
void timerStopInterval(WrenVM* vm)
|
||||
{
|
||||
int id = (int)wrenGetSlotDouble(vm, 1);
|
||||
|
||||
IntervalTimerData* timerData = findTimerById(id);
|
||||
if (timerData == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (timerData->handle != NULL && timerData->isActive) {
|
||||
uv_timer_stop(timerData->handle);
|
||||
uv_close((uv_handle_t*)timerData->handle, intervalCloseCallback);
|
||||
timerData->handle = NULL;
|
||||
}
|
||||
|
||||
if (timerData->fiber != NULL) {
|
||||
wrenReleaseHandle(getVM(), timerData->fiber);
|
||||
timerData->fiber = NULL;
|
||||
}
|
||||
|
||||
timerData->isActive = 0;
|
||||
}
|
||||
|
||||
void timerIsIntervalActive(WrenVM* vm)
|
||||
{
|
||||
int id = (int)wrenGetSlotDouble(vm, 1);
|
||||
|
||||
IntervalTimerData* timerData = findTimerById(id);
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotBool(vm, 0, timerData != NULL && timerData->isActive);
|
||||
}
|
||||
|
||||
static void immediateCallback(uv_timer_t* handle)
|
||||
{
|
||||
WrenHandle* fiber = (WrenHandle*)handle->data;
|
||||
|
||||
uv_close((uv_handle_t*)handle, timerCloseCallback);
|
||||
|
||||
schedulerResume(fiber, false);
|
||||
}
|
||||
|
||||
void timerStartImmediate(WrenVM* vm)
|
||||
{
|
||||
WrenHandle* fiber = wrenGetSlotHandle(vm, 1);
|
||||
|
||||
uv_timer_t* handle = (uv_timer_t*)malloc(sizeof(uv_timer_t));
|
||||
if (handle == NULL) {
|
||||
wrenSetSlotString(vm, 0, "Memory allocation failed.");
|
||||
wrenAbortFiber(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
handle->data = fiber;
|
||||
|
||||
uv_timer_init(getLoop(), handle);
|
||||
uv_timer_start(handle, immediateCallback, 0, 0);
|
||||
}
|
||||
|
||||
37
src/module/timer.wren
vendored
37
src/module/timer.wren
vendored
@ -1,5 +1,16 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "scheduler" for Scheduler
|
||||
|
||||
class TimerHandle {
|
||||
construct new_(id) {
|
||||
_id = id
|
||||
}
|
||||
|
||||
stop() { Timer.stopInterval_(_id) }
|
||||
isActive { Timer.isIntervalActive_(_id) }
|
||||
}
|
||||
|
||||
class Timer {
|
||||
static sleep(milliseconds) {
|
||||
if (!(milliseconds is Num)) Fiber.abort("Milliseconds must be a number.")
|
||||
@ -8,5 +19,31 @@ class Timer {
|
||||
return Scheduler.await_ { startTimer_(milliseconds, Fiber.current) }
|
||||
}
|
||||
|
||||
static interval(milliseconds, fn) {
|
||||
if (!(milliseconds is Num)) Fiber.abort("Milliseconds must be a number.")
|
||||
if (milliseconds < 0) Fiber.abort("Milliseconds cannot be negative.")
|
||||
if (!(fn is Fn)) Fiber.abort("Callback must be a function.")
|
||||
|
||||
var fiber = Fiber.new {
|
||||
while (true) {
|
||||
fn.call()
|
||||
Fiber.yield()
|
||||
}
|
||||
}
|
||||
var id = startInterval_(milliseconds, fiber)
|
||||
return TimerHandle.new_(id)
|
||||
}
|
||||
|
||||
static immediate(fn) {
|
||||
if (!(fn is Fn)) Fiber.abort("Callback must be a function.")
|
||||
|
||||
var fiber = Fiber.new { fn.call() }
|
||||
startImmediate_(fiber)
|
||||
}
|
||||
|
||||
foreign static startTimer_(milliseconds, fiber)
|
||||
foreign static startInterval_(milliseconds, fiber)
|
||||
foreign static stopInterval_(id)
|
||||
foreign static isIntervalActive_(id)
|
||||
foreign static startImmediate_(fiber)
|
||||
}
|
||||
|
||||
@ -2,8 +2,19 @@
|
||||
// from `src/module/timer.wren` using `util/wren_to_c_string.py`
|
||||
|
||||
static const char* timerModuleSource =
|
||||
"// retoor <retoor@molodetz.nl>\n"
|
||||
"\n"
|
||||
"import \"scheduler\" for Scheduler\n"
|
||||
"\n"
|
||||
"class TimerHandle {\n"
|
||||
" construct new_(id) {\n"
|
||||
" _id = id\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" stop() { Timer.stopInterval_(_id) }\n"
|
||||
" isActive { Timer.isIntervalActive_(_id) }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class Timer {\n"
|
||||
" static sleep(milliseconds) {\n"
|
||||
" if (!(milliseconds is Num)) Fiber.abort(\"Milliseconds must be a number.\")\n"
|
||||
@ -12,5 +23,31 @@ static const char* timerModuleSource =
|
||||
" return Scheduler.await_ { startTimer_(milliseconds, Fiber.current) }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static interval(milliseconds, fn) {\n"
|
||||
" if (!(milliseconds is Num)) Fiber.abort(\"Milliseconds must be a number.\")\n"
|
||||
" if (milliseconds < 0) Fiber.abort(\"Milliseconds cannot be negative.\")\n"
|
||||
" if (!(fn is Fn)) Fiber.abort(\"Callback must be a function.\")\n"
|
||||
"\n"
|
||||
" var fiber = Fiber.new {\n"
|
||||
" while (true) {\n"
|
||||
" fn.call()\n"
|
||||
" Fiber.yield()\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" var id = startInterval_(milliseconds, fiber)\n"
|
||||
" return TimerHandle.new_(id)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static immediate(fn) {\n"
|
||||
" if (!(fn is Fn)) Fiber.abort(\"Callback must be a function.\")\n"
|
||||
"\n"
|
||||
" var fiber = Fiber.new { fn.call() }\n"
|
||||
" startImmediate_(fiber)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static startTimer_(milliseconds, fiber)\n"
|
||||
" foreign static startInterval_(milliseconds, fiber)\n"
|
||||
" foreign static stopInterval_(id)\n"
|
||||
" foreign static isIntervalActive_(id)\n"
|
||||
" foreign static startImmediate_(fiber)\n"
|
||||
"}\n";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user