feat: publish Dever core with project scaffolding and versioned AI skills
This commit is contained in:
1 parent
70fa73cdc2
commit
ac458ffa7f
960 files changed
+221319
-1163
No files matched your search
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Task、Group 和 Channel 都有明确所有者;示例不启动服务或访问网络。
|
||||
|
||||
pause() () {
|
||||
dever.time.sleep(1)
|
||||
}
|
||||
|
||||
square(value: Int) (result: Int) pure {
|
||||
result = value * value
|
||||
}
|
||||
|
||||
send_after(channel: Channel<Int>, value: Int) () {
|
||||
dever.task.sleep(1)
|
||||
send(channel, value)
|
||||
}
|
||||
|
||||
main() (task_value: Int?, grouped_value: Int?, computed: Int) {
|
||||
messages = channel(Int, 1)
|
||||
sender = run(send_after(messages, 7))
|
||||
task_value = receive(messages)
|
||||
wait(sender)
|
||||
workers = group(1)
|
||||
run(workers, send_after(messages, 21))
|
||||
wait(workers)
|
||||
grouped_value = receive(messages)
|
||||
computed = parallel(square(12))
|
||||
blocking(pause())
|
||||
close(messages)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
connection(event: dever.net.ConnectResult.Connected(socket)) () recover("监听错误是流的最后一项,记录后结束服务") {
|
||||
dever.net.timeout(socket, 5000)
|
||||
dever.net.write(socket, dever.bytes.from_text("Hello from Dever\n"))
|
||||
dever.net.close(socket)
|
||||
}
|
||||
|
||||
connection(event: dever.net.ConnectResult.Failed(message)) () recover("监听错误是流的最后一项,记录后结束服务") {
|
||||
blocking(dever.io.println(message))
|
||||
}
|
||||
|
||||
serve() () {
|
||||
listener = dever.net.listen("127.0.0.1", 0)
|
||||
# 随机分配本机端口;最多同时处理 32 条连接。
|
||||
port = dever.net.port(listener)
|
||||
blocking(dever.io.println(int.to_text(port)))
|
||||
stream = dever.net.connections(listener)
|
||||
parallel_each(connection, stream, 32)
|
||||
dever.net.close_listener(listener)
|
||||
}
|
||||
|
||||
main() () {
|
||||
serve()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# 自包含后端示例:随机回环端口,只请求一次,然后排空并关闭。
|
||||
|
||||
type Context {
|
||||
prefix: Text
|
||||
requests: Channel<Int>
|
||||
}
|
||||
|
||||
route(request: dever.http.Request, context: Context) (
|
||||
response: dever.http.Response
|
||||
) {
|
||||
send(context.requests, 1)
|
||||
response = dever.http.Response {
|
||||
status = 200
|
||||
headers = [dever.http.header("Content-Type", "text/plain; charset=utf-8")]
|
||||
body = dever.bytes.from_text(context.prefix + request.target)
|
||||
}
|
||||
}
|
||||
|
||||
serve(listener: dever.system.Listener, context: Context) () {
|
||||
dever.system.http_serve(route, listener, dever.http.default_limits(), context)
|
||||
}
|
||||
|
||||
expired() () {
|
||||
blocking(dever.io.println("服务排空超时,剩余任务已停止"))
|
||||
}
|
||||
|
||||
count(value: Int) (text: Text) {
|
||||
text = "requests = " + int.to_text(value)
|
||||
}
|
||||
|
||||
count(value: null) (text: Text) {
|
||||
text = "request channel ended"
|
||||
}
|
||||
|
||||
main() () {
|
||||
listener = dever.net.listen("127.0.0.1", 0)
|
||||
port = dever.net.port(listener)
|
||||
requests = channel(Int, 1)
|
||||
server = run(
|
||||
serve(
|
||||
listener,
|
||||
Context {
|
||||
prefix = "Hello "
|
||||
requests = requests
|
||||
}
|
||||
)
|
||||
)
|
||||
client = dever.http.client(
|
||||
"http://127.0.0.1:" + int.to_text(port),
|
||||
dever.tls.system(),
|
||||
dever.http.default_limits(),
|
||||
dever.http.default_pool_limits()
|
||||
)
|
||||
response = dever.http.request(
|
||||
client,
|
||||
dever.http.Request {
|
||||
method = "GET"
|
||||
target = "/Dever"
|
||||
headers = []
|
||||
body = dever.bytes.from_text("")
|
||||
}
|
||||
)
|
||||
blocking(dever.io.println(dever.bytes.to_text(response.body)))
|
||||
dever.net.close_listener(listener)
|
||||
timeout(server, 2000, expired)
|
||||
dever.http.close_client(client)
|
||||
close(requests)
|
||||
blocking(dever.io.println(count(receive(requests))))
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
dever-api 4
|
||||
function domain.port(Int) (port: domain.Port []) pure=true recover=None effects=[] errors=[domain.PortResult.Failed] recoveries={}
|
||||
type domain.Port record construct=public {value: Int [1..65535]}
|
||||
type domain.PortResult Ready(port: domain.Port []) | error Failed(message: Text [])
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
public type Port {
|
||||
value: Int >= 1 and <= 65535
|
||||
}
|
||||
|
||||
public type PortResult {
|
||||
Ready(port: Port)
|
||||
error Failed(message: Text)
|
||||
}
|
||||
|
||||
public port(value: Int >= 1 and <= 65535) (port: Port) pure {
|
||||
port = Port {
|
||||
value = value
|
||||
}
|
||||
}
|
||||
|
||||
public port(value: other) (port: Port) pure {
|
||||
fail(PortResult.Failed("port must be between 1 and 65535"))
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
main() (configured: domain.Port, invalid: domain.PortResult, fallback: domain.Port) pure {
|
||||
configured = domain.Port {
|
||||
value = 8080
|
||||
}
|
||||
invalid = result(domain.port(0))
|
||||
fallback = configured_or_default(result(domain.port(-1)))
|
||||
}
|
||||
|
||||
configured_or_default(result: domain.PortResult.Ready(port)) (value: domain.Port) pure recover("optional local configuration uses port 8080") {
|
||||
value = port
|
||||
}
|
||||
|
||||
configured_or_default(result: domain.PortResult.Failed(message)) (value: domain.Port) pure recover("optional local configuration uses port 8080") {
|
||||
value = domain.Port {
|
||||
value = 8080
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
|
||||
type Pair {
|
||||
left: Int
|
||||
right: Int
|
||||
}
|
||||
|
||||
type Number {
|
||||
Present(value: Int)
|
||||
Missing
|
||||
}
|
||||
|
||||
type Key {
|
||||
First
|
||||
Second
|
||||
}
|
||||
|
||||
number(value: Number.Present(payload)) (answer: Int) {
|
||||
answer = payload
|
||||
}
|
||||
|
||||
number(value: other) (answer: Int) {
|
||||
answer = 0
|
||||
}
|
||||
|
||||
grade(value: Int < 0) (answer: Text) {
|
||||
answer = "negative"
|
||||
}
|
||||
|
||||
grade(value: Int >= 0 and < 10) (answer: Text) {
|
||||
answer = "small"
|
||||
}
|
||||
|
||||
grade(value: Int >= 10) (answer: Text) {
|
||||
answer = "large"
|
||||
}
|
||||
|
||||
scale(value: Int, factor: Int) (answer: Int) {
|
||||
answer = value * factor
|
||||
}
|
||||
|
||||
positive(value: Int) (answer: Bool) {
|
||||
answer = value > 0
|
||||
}
|
||||
|
||||
target(value: Int, expected: Int) (answer: Bool) {
|
||||
answer = value == expected
|
||||
}
|
||||
|
||||
parts(value: Text) (first: Text, last: Text) {
|
||||
first = text.trim(value)
|
||||
last = text.lower(first)
|
||||
}
|
||||
|
||||
change(pair: Pair) (answer: Pair) {
|
||||
pair.left = 99
|
||||
answer = pair
|
||||
}
|
||||
|
||||
map_key(entry: MapEntry<Text, Int>) (key: Text) {
|
||||
key = entry.key
|
||||
}
|
||||
|
||||
optional(value: Int?) (answer: Int?) {
|
||||
answer = value
|
||||
}
|
||||
|
||||
main() (
|
||||
original: Int,
|
||||
changed: Int,
|
||||
choice: Int,
|
||||
category: Text,
|
||||
exact: Bool,
|
||||
quotient: Decimal,
|
||||
third: Decimal,
|
||||
rounded: Decimal,
|
||||
integer_division: Int,
|
||||
remainder: Int,
|
||||
selected: List<Int>,
|
||||
found: Int?,
|
||||
total: Int,
|
||||
empty_total: Decimal,
|
||||
names: Text,
|
||||
ordered_keys: List<Text>,
|
||||
maps_equal: Bool,
|
||||
nan_equal: Bool,
|
||||
finite: Bool,
|
||||
optionals: List<Int?>,
|
||||
key_value: Int?,
|
||||
identity: Bool
|
||||
) {
|
||||
pair = Pair {
|
||||
right = 2
|
||||
left = 1
|
||||
}
|
||||
saved = pair
|
||||
pair = change(pair)
|
||||
original = saved.left
|
||||
changed = pair.left
|
||||
choice = number(Number.Present(7)) + number(Number.Missing)
|
||||
category = grade(12)
|
||||
exact = 0.1 + 0.2 == 0.3
|
||||
quotient = 5 / 2
|
||||
third = 1 / 3
|
||||
rounded = decimal.round(2.345, 2)
|
||||
integer_division = -5 // 2
|
||||
remainder = -5 % 2
|
||||
values = each(scale, [-1, 0, 1, 2], 3)
|
||||
selected = filter(positive, values)
|
||||
found = find(target, values, 3)
|
||||
total = sum(scale, selected, 2)
|
||||
empty_total = sum([])
|
||||
split = parts(" ABC ")
|
||||
names = split.first + split.last
|
||||
labels = {
|
||||
"first" = 1
|
||||
"second" = 2
|
||||
}
|
||||
original_labels = labels
|
||||
labels = put(labels, "first", 3)
|
||||
labels = remove(labels, "first")
|
||||
labels = put(labels, "first", 1)
|
||||
ordered_keys = each(map_key, entries(labels))
|
||||
maps_equal = labels == original_labels
|
||||
nan = 0.0e0 / 0.0e0
|
||||
nan_equal = [nan] == [nan]
|
||||
finite = math.is_finite(float.from_int(4))
|
||||
optionals = each(optional, [null, 1, null])
|
||||
keys = {
|
||||
Key.First = 1
|
||||
Key.Second = 2
|
||||
}
|
||||
key_value = get(keys, Key.Second)
|
||||
identity = dever.id.from_text("stable") == dever.id.from_text("stable")
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
public greet() () {
|
||||
dever.io.println("hello")
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
main() () {
|
||||
app.user.greet()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
route(request: dever.http.Request) (response: dever.http.Response) {
|
||||
response = dever.http.Response {
|
||||
status = 200
|
||||
headers = [dever.http.header("Content-Type", "text/plain; charset=utf-8")]
|
||||
body = dever.bytes.from_text("Hello Dever: " + request.target)
|
||||
}
|
||||
}
|
||||
|
||||
serve() () {
|
||||
listener = dever.net.listen("127.0.0.1", 0)
|
||||
port = dever.net.port(listener)
|
||||
blocking(dever.io.println("HTTP listening on 127.0.0.1:" + int.to_text(port)))
|
||||
dever.http.serve(route, listener, dever.http.default_limits())
|
||||
}
|
||||
|
||||
main() () {
|
||||
serve()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
main() (json: Text) {
|
||||
total = reduce(add, [1, 2, 3], 0)
|
||||
json = dever.json.stringify(
|
||||
dever.json.parse("{\"message\":\"你好 Dever\",\"total\":" + int.to_text(total) + "}")
|
||||
)
|
||||
}
|
||||
|
||||
add(value: Int, total: Int) (next: Int) {
|
||||
next = total + value
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
|
||||
route(request: dever.http.Request, reply: dever.system.HttpReply) () {
|
||||
path(request.target, reply)
|
||||
}
|
||||
|
||||
path(target: "/events", reply: dever.system.HttpReply) () {
|
||||
dever.sse.start(reply, [])
|
||||
tick(1, reply)
|
||||
tick(2, reply)
|
||||
tick(3, reply)
|
||||
}
|
||||
|
||||
path(target: "/stream", reply: dever.system.HttpReply) () {
|
||||
dever.http.start(reply, 200, [dever.http.header("Content-Type", "text/plain")])
|
||||
dever.http.write(reply, dever.bytes.from_text("first\n"))
|
||||
dever.task.sleep(100)
|
||||
dever.http.write(reply, dever.bytes.from_text("second\n"))
|
||||
dever.http.finish(reply)
|
||||
}
|
||||
|
||||
path(target: "/ws", reply: dever.system.HttpReply) () {
|
||||
upgraded(result(dever.websocket.accept(reply, dever.websocket.default_limits())), reply)
|
||||
}
|
||||
|
||||
path(target: other, reply: dever.system.HttpReply) () {
|
||||
dever.http.respond(
|
||||
reply,
|
||||
dever.http.Response {
|
||||
status = 200
|
||||
headers = [dever.http.header("Content-Type", "text/plain; charset=utf-8")]
|
||||
body = dever.bytes.from_text("Dever: /stream, /events, /ws")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
tick(index: Int, reply: dever.system.HttpReply) () {
|
||||
dever.task.sleep(100)
|
||||
dever.sse.send(
|
||||
reply,
|
||||
dever.sse.Event {
|
||||
event = "tick"
|
||||
data = int.to_text(index)
|
||||
id = int.to_text(index)
|
||||
retry_ms = 1000
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
upgraded(
|
||||
result: dever.websocket.ConnectResult.Connected(socket),
|
||||
reply: dever.system.HttpReply
|
||||
) () recover("WebSocket handshake errors are returned as HTTP 400") {
|
||||
each(message, dever.websocket.messages(socket), socket)
|
||||
}
|
||||
|
||||
upgraded(
|
||||
result: dever.websocket.ConnectResult.Failed(message),
|
||||
reply: dever.system.HttpReply
|
||||
) () recover("WebSocket handshake errors are returned as HTTP 400") {
|
||||
dever.http.respond(
|
||||
reply,
|
||||
dever.http.Response {
|
||||
status = 400
|
||||
headers = []
|
||||
body = dever.bytes.from_text(message)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
message(
|
||||
event: dever.websocket.MessageEvent.Read(value),
|
||||
socket: dever.system.WebSocket
|
||||
) () {
|
||||
echo(value, socket)
|
||||
}
|
||||
|
||||
message(
|
||||
event: dever.websocket.MessageEvent.Failed(message),
|
||||
socket: dever.system.WebSocket
|
||||
) () {
|
||||
fail(dever.websocket.MessageEvent.Failed(message))
|
||||
}
|
||||
|
||||
echo(value: dever.websocket.Message.Text(text), socket: dever.system.WebSocket) () {
|
||||
dever.websocket.send(socket, dever.websocket.Message.Text(text))
|
||||
}
|
||||
|
||||
echo(value: dever.websocket.Message.Binary(bytes), socket: dever.system.WebSocket) () {
|
||||
dever.websocket.send(socket, dever.websocket.Message.Binary(bytes))
|
||||
}
|
||||
|
||||
# Ping/Pong 已由协议引擎处理,应用只消费控制帧。
|
||||
echo(value: other, socket: dever.system.WebSocket) () {}
|
||||
|
||||
main() () {
|
||||
listener = dever.net.listen("127.0.0.1", 0)
|
||||
port = dever.net.port(listener)
|
||||
blocking(
|
||||
dever.io.println("HTTP / SSE / WebSocket on 127.0.0.1:" + int.to_text(port))
|
||||
)
|
||||
dever.http.serve_live(
|
||||
route,
|
||||
listener,
|
||||
dever.http.default_limits(),
|
||||
dever.http.default_live_limits()
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# 最多两个工作线程;输出顺序不保证,parallel_each 返回时任务都已完成。
|
||||
|
||||
show(name: Text) () {
|
||||
dever.io.println(name)
|
||||
}
|
||||
|
||||
main() () {
|
||||
parallel_each(show, ["alpha", "beta", "gamma"], 2)
|
||||
dever.io.println("done")
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# private 隐藏表示,调用方通过操作使用 Counter;普通数据无需强行套这层封装。
|
||||
|
||||
public type Counter {
|
||||
private value: Int
|
||||
}
|
||||
|
||||
public start() (counter: Counter) pure {
|
||||
counter = Counter {
|
||||
value = 0
|
||||
}
|
||||
}
|
||||
|
||||
public increment(counter: Counter) (next: Counter) pure {
|
||||
next = counter
|
||||
next.value = counter.value + 1
|
||||
}
|
||||
|
||||
public read(counter: Counter) (value: Int) pure {
|
||||
value = counter.value
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
main() (before: Int, after: Int) pure {
|
||||
original = counter.start()
|
||||
changed = counter.increment(original)
|
||||
before = counter.read(original)
|
||||
after = counter.read(changed)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# 预期 C009:缺少 false 分句。补齐后才能覆盖全部 Bool 输入。
|
||||
|
||||
label(enabled: true) (text: Text) {
|
||||
text = "enabled"
|
||||
}
|
||||
|
||||
main() (text: Text) {
|
||||
text = label(true)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# 预期 C012:赋值后丢弃失败。把失败作为命名输出返回可通过。
|
||||
|
||||
type Result {
|
||||
error Failed(message: Text)
|
||||
}
|
||||
|
||||
main(value: Result) () {
|
||||
discarded = value
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# 预期 C006:另一个 package 不能读取私有字段。
|
||||
|
||||
public read(box: secret.Box) (value: Int) {
|
||||
value = box.value
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
public type Box {
|
||||
private value: Int
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# 预期 C013:输出属于副作用。需要输出时应移除 pure。
|
||||
|
||||
main() () pure {
|
||||
dever.io.println("hello")
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# 预期 C011:0 不能构造合法端口。改为 8080 可通过。
|
||||
|
||||
type Port {
|
||||
value: Int >= 1 and <= 65535
|
||||
}
|
||||
|
||||
main() (port: Port) {
|
||||
port = Port {
|
||||
value = 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
# handler 是具名函数;reduce_until 处理当前元素后停止,不处理后面的 100。
|
||||
|
||||
type Summary {
|
||||
total: Int
|
||||
count: Int
|
||||
}
|
||||
|
||||
double(value: Int) (result: Int) pure {
|
||||
result = value * 2
|
||||
}
|
||||
|
||||
map_values(transform: handler(value: Int) (result: Int), values: List<Int>) (
|
||||
results: List<Int>
|
||||
) {
|
||||
results = each(transform, values)
|
||||
}
|
||||
|
||||
add(value: Int, total: Int) (next: Int) pure {
|
||||
next = total + value
|
||||
}
|
||||
|
||||
collect(value: Int, summary: Summary) (next: Summary, stop: Bool) pure {
|
||||
next = Summary {
|
||||
total = summary.total + value
|
||||
count = summary.count + 1
|
||||
}
|
||||
stop = next.total >= 6
|
||||
}
|
||||
|
||||
main() (
|
||||
mapped: List<Int>,
|
||||
total: Int,
|
||||
stopped: Summary,
|
||||
empty_total: Int,
|
||||
bytes_total: Int,
|
||||
first_value: Int?,
|
||||
missing: Int?
|
||||
) pure {
|
||||
mapped = map_values(double, [1, 2, 3])
|
||||
total = reduce(add, [1, 2, 3], 0)
|
||||
stopped = reduce_until(
|
||||
collect,
|
||||
[1, 2, 3, 100],
|
||||
Summary {
|
||||
total = 0
|
||||
count = 0
|
||||
}
|
||||
)
|
||||
empty_total = reduce(add, [], 0)
|
||||
bytes_total = reduce(add, dever.bytes.from_text("AB"), 0)
|
||||
first_value = first(mapped)
|
||||
missing = get(
|
||||
{
|
||||
"count" = 3
|
||||
},
|
||||
"unknown"
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ABC
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
sum_byte(value: Int, total: Int) (next: Int) {
|
||||
next = total + value
|
||||
}
|
||||
|
||||
consume(event: dever.io.ReadEvent.Chunk(bytes), total: Int) (next: Int) {
|
||||
next = total + reduce(sum_byte, bytes, 0)
|
||||
}
|
||||
|
||||
consume(event: dever.io.ReadEvent.Failed(message), total: Int) (
|
||||
next: Int
|
||||
) {
|
||||
fail(dever.io.ReadEvent.Failed(message))
|
||||
}
|
||||
|
||||
main() (total: Int) {
|
||||
file = dever.io.open("examples/old/dever/stream/input.txt")
|
||||
stream = dever.io.chunks(file, 2)
|
||||
total = reduce(consume, stream, 0)
|
||||
close(stream)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# 结果随启动参数及当前时间变化;失败以结果返回。
|
||||
|
||||
main() (
|
||||
arguments: List<Text>,
|
||||
clock: Int,
|
||||
invalid_sleep: dever.time.SleepResult
|
||||
) {
|
||||
arguments = dever.process.arguments()
|
||||
clock = dever.time.unix_millis()
|
||||
invalid_sleep = result(dever.time.sleep(-1))
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# 文本下标按 Unicode 标量;解析失败和越界保留 null,不自动改成零或空字符串。
|
||||
|
||||
main() (
|
||||
cleaned: Text,
|
||||
character: Text?,
|
||||
slice: Text?,
|
||||
pieces: List<Text>,
|
||||
joined: Text,
|
||||
parsed: Int?,
|
||||
invalid: Int?,
|
||||
exact: Decimal?,
|
||||
rounded: Decimal,
|
||||
utf8: Text,
|
||||
invalid_utf8: dever.bytes.DecodeResult,
|
||||
invalid_byte: dever.bytes.BufferResult
|
||||
) pure {
|
||||
cleaned = text.lower(text.trim(" Dever "))
|
||||
character = text.at("a中😀", 2)
|
||||
slice = text.slice("a中😀", 1, 3)
|
||||
pieces = text.split("a,,b", ",")
|
||||
joined = text.join(pieces, "|")
|
||||
parsed = int.parse("42")
|
||||
invalid = int.parse("forty-two")
|
||||
exact = decimal.parse("1.25")
|
||||
rounded = decimal.round(2.345, 2)
|
||||
utf8 = dever.bytes.to_text(dever.bytes.from_text("你好"))
|
||||
invalid_utf8 = result(dever.bytes.to_text(dever.bytes.from_ints([255])))
|
||||
invalid_byte = result(dever.bytes.from_ints([256]))
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"database": {
|
||||
"default": {
|
||||
"type": "sqlite",
|
||||
"path": "data/db/app.db",
|
||||
"max_connections": 1,
|
||||
"max_page_size": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
# 购物车结算
|
||||
|
||||
计算购物车中所有商品的合计金额、优惠金额和最终应付金额。
|
||||
|
||||
- 包:`main`
|
||||
- 公开类型:
|
||||
- `Product`
|
||||
- `OrderSummary`
|
||||
- 公开方法:无
|
||||
- 使用:无
|
||||
|
||||
## 商品
|
||||
|
||||
保存商品名称、单价和购买数量。
|
||||
|
||||
- 类型:`Product`
|
||||
- 字段:
|
||||
- `name: Text`:商品名称
|
||||
- `price: Decimal`:商品单价
|
||||
- `quantity: Int >= 1`:购买数量,最少为 1
|
||||
|
||||
```typescript dever
|
||||
public type Product {
|
||||
name: Text
|
||||
price: Decimal
|
||||
quantity: Int >= 1
|
||||
}
|
||||
```
|
||||
|
||||
## 订单结算结果
|
||||
|
||||
保存参与结算的商品以及各项金额。
|
||||
|
||||
- 类型:`OrderSummary`
|
||||
- 字段:
|
||||
- `items: List<Product>`:参与结算的商品
|
||||
- `subtotal: Decimal`:优惠前的商品合计
|
||||
- `discount: Decimal`:优惠金额
|
||||
- `payable: Decimal`:最终应付金额
|
||||
|
||||
```typescript dever
|
||||
public type OrderSummary {
|
||||
items: List<Product>
|
||||
subtotal: Decimal
|
||||
discount: Decimal
|
||||
payable: Decimal
|
||||
}
|
||||
```
|
||||
|
||||
## 计算单项商品金额
|
||||
|
||||
使用商品单价乘以购买数量。
|
||||
|
||||
- 函数:`line_total`
|
||||
- 输入:
|
||||
- `product: Product`:需要计算金额的商品
|
||||
- 输出:
|
||||
- `total: Decimal`:该商品的总金额
|
||||
|
||||
```typescript dever
|
||||
line_total(product: Product) (total: Decimal) {
|
||||
total = product.price * product.quantity
|
||||
}
|
||||
```
|
||||
|
||||
## 汇总商品金额
|
||||
|
||||
计算商品列表中所有商品的金额总和。
|
||||
|
||||
- 函数:`sum_products`
|
||||
- 输入:
|
||||
- `products: List<Product>`:需要汇总的商品列表
|
||||
- 输出:
|
||||
- `subtotal: Decimal`:所有商品的合计金额
|
||||
|
||||
```typescript dever
|
||||
sum_products(products: List<Product>) (subtotal: Decimal) {
|
||||
subtotal = sum(line_total, products)
|
||||
}
|
||||
```
|
||||
|
||||
## 结算订单
|
||||
|
||||
根据商品列表和会员应付比例计算订单结果。
|
||||
|
||||
- 函数:`checkout`
|
||||
- 输入:
|
||||
- `products: List<Product>`:需要结算的商品列表
|
||||
- `member_rate: Decimal`:会员实际应付比例,例如 0.90
|
||||
- 输出:
|
||||
- `summary: OrderSummary`:订单结算结果
|
||||
|
||||
```typescript dever
|
||||
checkout(products: List<Product>, member_rate: Decimal) (summary: OrderSummary) {
|
||||
subtotal = sum_products(products)
|
||||
payable = decimal.round(subtotal * member_rate, 2)
|
||||
discount = subtotal - payable
|
||||
summary = OrderSummary {
|
||||
items = products
|
||||
subtotal = subtotal
|
||||
discount = discount
|
||||
payable = payable
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 运行购物车示例
|
||||
|
||||
创建商品列表,并以九折会员价格完成结算。
|
||||
|
||||
- 函数:`main`
|
||||
- 输入:无
|
||||
- 输出:
|
||||
- `customer: Text`:客户名称
|
||||
- `summary: OrderSummary`:客户的订单结算结果
|
||||
|
||||
```typescript dever
|
||||
main() (customer: Text, summary: OrderSummary) {
|
||||
customer = "小明"
|
||||
products = [
|
||||
Product {
|
||||
name = "笔记本"
|
||||
price = 12.50
|
||||
quantity = 3
|
||||
},
|
||||
Product {
|
||||
name = "签字笔"
|
||||
price = 5.00
|
||||
quantity = 2
|
||||
}
|
||||
]
|
||||
summary = checkout(products, 0.90)
|
||||
}
|
||||
```
|
||||
|
||||
### 执行
|
||||
|
||||
```bash
|
||||
deverc check examples/old/markdown --entry app.main
|
||||
deverc fmt examples/old/markdown --check
|
||||
deverc run examples/old/markdown app.main
|
||||
```
|
||||
|
||||
结算结果:
|
||||
|
||||
| 项目 | 金额 |
|
||||
| --- | ---: |
|
||||
| 商品合计 | 47.50 |
|
||||
| 优惠金额 | 4.75 |
|
||||
| 应付金额 | 42.75 |
|
||||
Reference in new issue
Block a user