feat: add await syntax for direct async function calls and new generator/phonebook apps

Add compiler support for calling async functions directly with `await fn(args)` syntax, automatically translating to `await fn.call(args)`. Introduce `create_merge` Makefile target for merging Wren source files. Include new `apps/generator.wren` source code generator using OpenRouter API, `apps/phonebook.wren` CLI phonebook with SQLite backend, and `apps/minitut.md` Wren tutorial reference. Update `example/await_demo.wren` with parameterized async functions, nested awaits, and expression usage. Fix JSON parsing in `example/openrouter_demo.wren` to strip markdown code fences. Document new await syntax in `manual/api/scheduler.html`.
This commit is contained in:
2026-01-25 09:50:20 +00:00
parent e0cc7791e3
commit eadfc8e9cb
48 changed files with 1543 additions and 86 deletions
+8
View File
@@ -0,0 +1,8 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var delayed = async { |ms| ms }
System.print(await delayed(1)) // expect: 1
System.print(await delayed(2)) // expect: 2
+7
View File
@@ -0,0 +1,7 @@
import "scheduler" for Scheduler, Future
var base = 100
var addBase = async { |x| base + x }
var f = addBase.call(42)
System.print(await f) // expect: 142
+17
View File
@@ -0,0 +1,17 @@
import "scheduler" for Scheduler, Future
import "timer" for Timer
var order = []
var task = async { |name, ms|
await Timer.sleep(ms)
order.add(name)
name
}
var f1 = task.call("slow", 2)
var f2 = task.call("fast", 1)
await f1
await f2
System.print(order) // expect: [fast, slow]
+7
View File
@@ -0,0 +1,7 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var add = async { |a, b| a + b }
System.print(await add(3, 4)) // expect: 7
+17
View File
@@ -0,0 +1,17 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var transform = async { |list, fn|
var result = []
for (item in list) {
result.add(fn.call(item))
}
return result
}
var doubled = await transform([1, 2, 3]) { |x| x * 2 }
System.print(doubled) // expect: [2, 4, 6]
var squared = await transform([2, 3, 4]) { |x| x * x }
System.print(squared) // expect: [4, 9, 16]
+7
View File
@@ -0,0 +1,7 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var double = async { |x| x * 2 }
System.print(await double(21)) // expect: 42
+18
View File
@@ -0,0 +1,18 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var getList = async { |x| [x, x * 2, x * 3] }
var getString = async { |s| s }
var list = await getList(5)
System.print(list.count) // expect: 3
var list2 = await getList(10)
System.print(list2[1]) // expect: 20
var str = await getString("hello")
System.print(str.count) // expect: 5
var str2 = await getString("world")
System.print(str2.toList[0]) // expect: w
+50
View File
@@ -0,0 +1,50 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
import "timer" for Timer
class MockApi {
static fetchUser { async { |id|
Timer.sleep(1)
return {"id": id, "name": "User%(id)", "active": true}
}}
static fetchPosts { async { |userId|
Timer.sleep(1)
return [
{"id": 1, "userId": userId, "title": "Post 1"},
{"id": 2, "userId": userId, "title": "Post 2"}
]
}}
static createPost { async { |userId, title|
Timer.sleep(1)
return {"id": 3, "userId": userId, "title": title, "created": true}
}}
static getUserWithPosts(userId) {
var fetchUser = MockApi.fetchUser
var fetchPosts = MockApi.fetchPosts
var user = await fetchUser(userId)
var posts = await fetchPosts(userId)
user["posts"] = posts
return user
}
}
var fetchUser = MockApi.fetchUser
var user = await fetchUser(42)
System.print(user["name"]) // expect: User42
var fetchPosts = MockApi.fetchPosts
var posts = await fetchPosts(42)
System.print(posts.count) // expect: 2
var createPost = MockApi.createPost
var newPost = await createPost(42, "New Post")
System.print(newPost["created"]) // expect: true
var fullUser = MockApi.getUserWithPosts(99)
System.print(fullUser["name"]) // expect: User99
System.print(fullUser["posts"].count) // expect: 2
+54
View File
@@ -0,0 +1,54 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
class DataProcessor {
static parse { async { |text| text.split(",") } }
static filter { async { |list, fn|
var result = []
for (item in list) {
if (fn.call(item)) result.add(item)
}
return result
}}
static transform { async { |list, fn|
var result = []
for (item in list) {
result.add(fn.call(item))
}
return result
}}
static join { async { |list, sep| list.join(sep) } }
static pipeline(data) {
var parse = DataProcessor.parse
var filter = DataProcessor.filter
var transform = DataProcessor.transform
var join = DataProcessor.join
var parsed = await parse(data)
var filtered = await filter(parsed) { |s| s.count > 4 }
var transformed = await transform(filtered) { |s| s.toList[0] }
return await join(transformed, "-")
}
}
var data = "apple,banana,cherry,date"
var parse = DataProcessor.parse
var parsed = await parse(data)
System.print(parsed) // expect: [apple, banana, cherry, date]
var filter = DataProcessor.filter
var filtered = await filter(parsed) { |s| s.count > 5 }
System.print(filtered) // expect: [banana, cherry]
var transform = DataProcessor.transform
var transformed = await transform(filtered) { |s| s.toList[0] }
System.print(transformed) // expect: [b, c]
var join = DataProcessor.join
var joined = await join(transformed, "-")
System.print(joined) // expect: b-c
var result = DataProcessor.pipeline("one,two,three,four,five")
System.print(result) // expect: t
+50
View File
@@ -0,0 +1,50 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
class Counter {
construct new(start) {
_value = start
}
value { _value }
add(n) {
var op = async { |x|
_value = _value + x
return _value
}
return await op(n)
}
subtract(n) {
var op = async { |x|
_value = _value - x
return _value
}
return await op(n)
}
reset() {
var op = async {
_value = 0
return _value
}
return await op()
}
}
var counter = Counter.new(10)
System.print(counter.value) // expect: 10
System.print(counter.add(5)) // expect: 15
System.print(counter.value) // expect: 15
System.print(counter.subtract(3)) // expect: 12
System.print(counter.value) // expect: 12
System.print(counter.reset()) // expect: 0
System.print(counter.value) // expect: 0
System.print(counter.add(10)) // expect: 10
System.print(counter.subtract(3)) // expect: 7
+65
View File
@@ -0,0 +1,65 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
import "timer" for Timer
class CacheService {
construct new() {
_cache = {}
}
get(key) {
var op = async { |k|
Timer.sleep(1)
return _cache.containsKey(k) ? _cache[k] : null
}
return await op(key)
}
set(key, value) {
var op = async { |k, v|
Timer.sleep(1)
_cache[k] = v
return true
}
return await op(key, value)
}
has(key) {
var op = async { |k|
return _cache.containsKey(k)
}
return await op(key)
}
clear() {
var op = async {
_cache = {}
return true
}
return await op()
}
getOrSet(key, defaultValue) {
if (this.has(key)) {
return this.get(key)
}
this.set(key, defaultValue)
return defaultValue
}
}
var cache = CacheService.new()
System.print(cache.has("foo")) // expect: false
System.print(cache.get("foo")) // expect: null
System.print(cache.set("foo", "bar")) // expect: true
System.print(cache.has("foo")) // expect: true
System.print(cache.get("foo")) // expect: bar
System.print(cache.clear()) // expect: true
System.print(cache.has("foo")) // expect: false
System.print(cache.getOrSet("baz", "default")) // expect: default
System.print(cache.get("baz")) // expect: default
+33
View File
@@ -0,0 +1,33 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
class Calculator {
static add { async { |a, b| a + b } }
static multiply { async { |a, b| a * b } }
static square { async { |x| x * x } }
static constant { async { 42 } }
static compute(a, b) {
var addFn = Calculator.add
var multiplyFn = Calculator.multiply
var sum = await addFn(a, b)
var product = await multiplyFn(a, b)
return [sum, product]
}
}
var add = Calculator.add
System.print(await add(3, 4)) // expect: 7
var multiply = Calculator.multiply
System.print(await multiply(5, 6)) // expect: 30
var square = Calculator.square
System.print(await square(8)) // expect: 64
var constant = Calculator.constant
System.print(await constant()) // expect: 42
var results = Calculator.compute(2, 3)
System.print(results) // expect: [5, 6]
+23
View File
@@ -0,0 +1,23 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var multiplier = 10
var scale = async { |x| x * multiplier }
System.print(await scale(5)) // expect: 50
multiplier = 100
System.print(await scale(5)) // expect: 500
var prefix = "Result: "
var format = async { |x| prefix + x.toString }
System.print(await format(42)) // expect: Result: 42
var outer = "outer"
var fn = Fn.new {
var inner = "inner"
var capture = async { |x| outer + "-" + inner + "-" + x }
return await capture("arg")
}
System.print(fn.call()) // expect: outer-inner-arg
+24
View File
@@ -0,0 +1,24 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var isPositive = async { |x| x > 0 }
var getValue = async { |x| x }
if (await isPositive(5)) {
System.print("positive") // expect: positive
}
if (await isPositive(-5)) {
System.print("unreachable")
} else {
System.print("negative") // expect: negative
}
var result = await isPositive(10) ? "yes" : "no"
System.print(result) // expect: yes
while (await getValue(false)) {
System.print("unreachable")
}
System.print("after while") // expect: after while
+12
View File
@@ -0,0 +1,12 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var getValue = async { |x| x }
var double = async { |x| x * 2 }
System.print(1 + await getValue(2)) // expect: 3
System.print(await double(3) + await double(4)) // expect: 14
System.print(await getValue(10) * 5) // expect: 50
System.print(100 - await getValue(30)) // expect: 70
System.print((await getValue(4)) * (await getValue(5))) // expect: 20
+17
View File
@@ -0,0 +1,17 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var double = async { |x| x * 2 }
var sum = 0
for (i in [1, 2, 3, 4, 5]) {
sum = sum + await double(i)
}
System.print(sum) // expect: 30
var results = []
for (i in 1..3) {
results.add(await double(i))
}
System.print(results) // expect: [2, 4, 6]
+21
View File
@@ -0,0 +1,21 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
class Test {
static run() {
var localDouble = async { |x| x * 2 }
var localAdd = async { |a, b| a + b }
System.print(await localDouble(5)) // expect: 10
System.print(await localAdd(3, 4)) // expect: 7
var nested = Fn.new {
var innerFn = async { |x| x * 100 }
return await innerFn(3)
}
System.print(nested.call()) // expect: 300
}
}
Test.run()
+9
View File
@@ -0,0 +1,9 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var sum4 = async { |a, b, c, d| a + b + c + d }
var sum5 = async { |a, b, c, d, e| a + b + c + d + e }
System.print(await sum4(1, 2, 3, 4)) // expect: 10
System.print(await sum5(1, 2, 3, 4, 5)) // expect: 15
+17
View File
@@ -0,0 +1,17 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var double = async { |x| x * 2 }
var triple = async { |x| x * 3 }
System.print(await double(5)) // expect: 10
System.print(await double.call(5)) // expect: 10
var f1 = double.call(10)
var f2 = triple.call(10)
System.print(await f1) // expect: 20
System.print(await f2) // expect: 30
System.print(await double(await triple.call(2))) // expect: 12
System.print(await double.call(await triple(2))) // expect: 12
+20
View File
@@ -0,0 +1,20 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var moduleDouble = async { |x| x * 2 }
var moduleAdd = async { |a, b| a + b }
class Helper {
static useModuleVar(x) {
return await moduleDouble(x)
}
static combineModuleVars(a, b) {
return await moduleAdd(await moduleDouble(a), await moduleDouble(b))
}
}
System.print(await moduleDouble(10)) // expect: 20
System.print(Helper.useModuleVar(15)) // expect: 30
System.print(Helper.combineModuleVars(5, 10)) // expect: 30
+10
View File
@@ -0,0 +1,10 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var double = async { |x| x * 2 }
var addTen = async { |x| x + 10 }
System.print(await addTen(await double(5))) // expect: 20
System.print(await double(await addTen(3))) // expect: 26
System.print(await double(await double(await double(2)))) // expect: 16
+7
View File
@@ -0,0 +1,7 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var getValue = async { 42 }
System.print(await getValue()) // expect: 42
+30
View File
@@ -0,0 +1,30 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
import "timer" for Timer
var delayedValue = async { |ms, val|
Timer.sleep(ms)
return val
}
var f1 = delayedValue.call(10, "first")
var f2 = delayedValue.call(10, "second")
var f3 = delayedValue.call(10, "third")
System.print(await f1) // expect: first
System.print(await f2) // expect: second
System.print(await f3) // expect: third
var compute = async { |x| x * x }
var futures = []
for (i in 1..5) {
futures.add(compute.call(i))
}
var results = []
for (f in futures) {
results.add(await f)
}
System.print(results) // expect: [1, 4, 9, 16, 25]
+18
View File
@@ -0,0 +1,18 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var getString = async { |x| x.toString }
var getNum = async { |x| x * 2 }
var getList = async { |x| [x, x, x] }
var getMap = async { |k, v| {k: v} }
var getNull = async { null }
var getBool = async { |x| x > 0 }
System.print(await getString("hello")) // expect: hello
System.print(await getNum(21)) // expect: 42
System.print(await getList(7)) // expect: [7, 7, 7]
System.print(await getMap("key", 123)) // expect: {key: 123}
System.print(await getNull()) // expect: null
System.print(await getBool(5)) // expect: true
System.print(await getBool(-5)) // expect: false
+25
View File
@@ -0,0 +1,25 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var step1 = async { |x| x + 10 }
var step2 = async { |x| x * 2 }
var step3 = async { |x| x - 5 }
var result = 5
result = await step1(result)
System.print(result) // expect: 15
result = await step2(result)
System.print(result) // expect: 30
result = await step3(result)
System.print(result) // expect: 25
var pipeline = async { |x|
var r1 = await step1(x)
var r2 = await step2(r1)
var r3 = await step3(r2)
return r3
}
System.print(await pipeline(0)) // expect: 15
System.print(await pipeline(10)) // expect: 35
+28
View File
@@ -0,0 +1,28 @@
// retoor <retoor@molodetz.nl>
import "scheduler" for Scheduler, Future
var makeMultiplier = Fn.new { |factor|
var multiply = async { |x| x * factor }
return multiply
}
var times2 = makeMultiplier.call(2)
var times10 = makeMultiplier.call(10)
System.print(await times2(5)) // expect: 10
System.print(await times10(5)) // expect: 50
var counter = 0
var makeCounter = Fn.new {
var increment = async { |step|
counter = counter + step
return counter
}
return increment
}
var inc = makeCounter.call()
System.print(await inc(1)) // expect: 1
System.print(await inc(5)) // expect: 6
System.print(await inc(10)) // expect: 16