Add context receiver extensions for DSLs
This commit is contained in:
1 parent
e107296bca
commit
b2200e71ff
22 files changed
+860
-71
No files matched your search
+16
@@ -479,6 +479,18 @@ val block: context(Html, Head) Body.()->String = {
|
||||
}
|
||||
```
|
||||
|
||||
Context receivers can also constrain extension functions. The extension is visible only when the required receiver is
|
||||
already in the implicit receiver stack:
|
||||
|
||||
```lyng
|
||||
class Tag { fun addText(text: String) { /* ... */ } }
|
||||
|
||||
context(Tag)
|
||||
fun String.unaryPlus() {
|
||||
this@Tag.addText(this)
|
||||
}
|
||||
```
|
||||
|
||||
- Field inheritance (`val`/`var`) and collisions
|
||||
- Instance storage is kept per declaring class, internally disambiguated; unqualified read/write resolves to the first match in the resolution order (leftmost base).
|
||||
- Qualified read/write (via `this@Type` or casts) targets the chosen ancestor’s storage.
|
||||
@@ -650,10 +662,14 @@ Unary operators are overloaded by defining methods with no arguments:
|
||||
|
||||
| Operator | Method Name |
|
||||
| :--- | :--- |
|
||||
| `+a` | `unaryPlus()` |
|
||||
| `-a` | `negate()` |
|
||||
| `!a` | `logicalNot()` |
|
||||
| `~a` | `bitNot()` |
|
||||
|
||||
`unaryPlus()` is useful in DSL-style builders where `+"text"` should append text to
|
||||
the current receiver. See [samples/html_builder_dsl.lyng](samples/html_builder_dsl.lyng).
|
||||
|
||||
### Assignment Operators
|
||||
|
||||
Assignment operators like `+=` first attempt to call a specific assignment method. If that method is not defined, they fall back to a combination of the binary operator and a regular assignment (e.g., `a = a + b`).
|
||||
|
||||
@@ -83,6 +83,7 @@ Primary sources used: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/{Parser,T
|
||||
## 4. Operators (implemented)
|
||||
- Assignment: `=`, `+=`, `-=`, `*=`, `/=`, `%=`, `?=`.
|
||||
- Logical: `||`, `&&`, unary `!`.
|
||||
- Unary arithmetic/bitwise: unary `+`, unary `-`, `~`.
|
||||
- Bitwise: `|`, `^`, `&`, `~`, shifts `<<`, `>>`.
|
||||
- Equality/comparison: `==`, `!=`, `===`, `!==`, `<`, `<=`, `>`, `>=`, `<=>`, `=~`, `!~`.
|
||||
- Type/containment: `is`, `!is`, `in`, `!in`, `as`, `as?`.
|
||||
@@ -119,6 +120,7 @@ Primary sources used: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/{Parser,T
|
||||
- shorthand: `fun f(x) = expr`.
|
||||
- generics: `fun f<T>(x: T): T`.
|
||||
- extension functions: `fun Type.name(...) { ... }`.
|
||||
- context-aware extension functions: `context(Tag) fun String.unaryPlus() { this@Tag.addText(this) }`.
|
||||
- named singleton `object` declarations can be extension receivers too: `fun Config.describe(...) { ... }`, `val Config.tag get() = ...`.
|
||||
- static extension functions are callable on the type object: `static fun List<T>.fill(...)` -> `List.fill(...)`.
|
||||
- delegated callable: `fun f(...) by delegate`.
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
class Tag(name: String) {
|
||||
val name = name
|
||||
var inner = ""
|
||||
|
||||
fun child(tagName: String, block: Tag.()->void) {
|
||||
val child = Tag(tagName)
|
||||
with(child) { block(this) }
|
||||
inner += child.render()
|
||||
}
|
||||
|
||||
fun head(block: Tag.()->void) { child("head", block) }
|
||||
fun body(block: Tag.()->void) { child("body", block) }
|
||||
fun title(block: Tag.()->void) { child("title", block) }
|
||||
fun h1(block: Tag.()->void) { child("h1", block) }
|
||||
|
||||
fun addText(text: String) {
|
||||
inner += text
|
||||
}
|
||||
|
||||
fun render() {
|
||||
"<" + name + ">" + inner + "</" + name + ">"
|
||||
}
|
||||
}
|
||||
|
||||
context(Tag)
|
||||
fun String.unaryPlus() {
|
||||
this@Tag.addText(this)
|
||||
}
|
||||
|
||||
fun html(block: Tag.()->void) {
|
||||
val root = Tag("html")
|
||||
with(root) { block(this) }
|
||||
root.render()
|
||||
}
|
||||
|
||||
val page = html {
|
||||
head {
|
||||
title {
|
||||
+"Demo"
|
||||
}
|
||||
}
|
||||
body {
|
||||
h1 {
|
||||
+"Heading 1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println(page)
|
||||
assertEquals("<html><head><title>Demo</title></head><body><h1>Heading 1</h1></body></html>", page)
|
||||
@@ -1,6 +1,9 @@
|
||||
// Sample: Operator Overloading in Lyng
|
||||
|
||||
class Vector<T>(val x: T, val y: T) {
|
||||
// Overload unary +
|
||||
fun unaryPlus() = this
|
||||
|
||||
// Overload +
|
||||
fun plus(other: Vector<U>) = Vector(x + other.x, y + other.y)
|
||||
|
||||
@@ -28,6 +31,11 @@ val v2 = Vector(5, 5)
|
||||
println("v1: " + v1)
|
||||
println("v2: " + v2)
|
||||
|
||||
// Test unary +
|
||||
val v0 = +v1
|
||||
println("+v1 = " + v0)
|
||||
assertEquals(Vector(10, 20), v0)
|
||||
|
||||
// Test binary +
|
||||
val v3 = v1 + v2
|
||||
println("v1 + v2 = " + v3)
|
||||
|
||||
Reference in new issue
Block a user