added object expression support to the site, tnbundle, etc
This commit is contained in:
parent
eec732d11a
commit
5fc0969491
@ -105,6 +105,22 @@ object Config {
|
||||
Config.show()
|
||||
```
|
||||
|
||||
### Object Expressions
|
||||
You can now create anonymous objects that inherit from classes or interfaces using the `object : Base { ... }` syntax. These expressions capture their lexical scope and support multiple inheritance.
|
||||
|
||||
```lyng
|
||||
val worker = object : Runnable {
|
||||
override fun run() = println("Working...")
|
||||
}
|
||||
|
||||
val x = object : Base(arg1), Interface1 {
|
||||
val property = 42
|
||||
override fun method() = this@object.property * 2
|
||||
}
|
||||
```
|
||||
|
||||
Use `this@object` to refer to the innermost anonymous object instance when `this` is rebound.
|
||||
|
||||
### Unified Delegation Model
|
||||
A powerful new delegation system allows `val`, `var`, and `fun` members to delegate their logic to other objects using the `by` keyword.
|
||||
|
||||
|
||||
@ -76,9 +76,9 @@
|
||||
},
|
||||
"labels": { "patterns": [ { "name": "entity.name.label.lyng", "match": "[\\p{L}_][\\p{L}\\p{N}_]*:" } ] },
|
||||
"directives": { "patterns": [ { "name": "meta.directive.lyng", "match": "^\\s*#[_A-Za-z][_A-Za-z0-9]*" } ] },
|
||||
"declarations": { "patterns": [ { "name": "meta.function.declaration.lyng", "match": "\\b(fun|fn)\\s+(?:([\\p{L}_][\\p{L}\\p{N}_]*)\\.)?([\\p{L}_][\\p{L}\\p{N}_]*)", "captures": { "1": { "name": "keyword.declaration.lyng" }, "2": { "name": "entity.name.type.lyng" }, "3": { "name": "entity.name.function.lyng" } } }, { "name": "meta.type.declaration.lyng", "match": "\\b(?:class|enum|interface)\\s+([\\p{L}_][\\p{L}\\p{N}_]*)", "captures": { "1": { "name": "entity.name.type.lyng" } } }, { "name": "meta.variable.declaration.lyng", "match": "\\b(val|var)\\s+(?:([\\p{L}_][\\p{L}\\p{N}_]*)\\.)?([\\p{L}_][\\p{L}\\p{N}_]*)", "captures": { "1": { "name": "keyword.declaration.lyng" }, "2": { "name": "entity.name.type.lyng" }, "3": { "name": "variable.other.declaration.lyng" } } } ] },
|
||||
"keywords": { "patterns": [ { "name": "keyword.control.lyng", "match": "\\b(?:if|else|when|while|do|for|try|catch|finally|throw|return|break|continue)\\b" }, { "name": "keyword.declaration.lyng", "match": "\\b(?:fun|fn|class|enum|interface|val|var|import|package|constructor|property|abstract|override|open|closed|extern|private|protected|static|get|set)\\b" }, { "name": "keyword.operator.word.lyng", "match": "\\bnot\\s+(?:in|is)\\b" }, { "name": "keyword.operator.word.lyng", "match": "\\b(?:and|or|not|in|is|as|as\\?)\\b" } ] },
|
||||
"constants": { "patterns": [ { "name": "constant.language.lyng", "match": "(?:\\b(?:true|false|null|this)\\b|π)" } ] },
|
||||
"declarations": { "patterns": [ { "name": "meta.function.declaration.lyng", "match": "\\b(fun|fn)\\s+(?:([\\p{L}_][\\p{L}\\p{N}_]*)\\.)?([\\p{L}_][\\p{L}\\p{N}_]*)", "captures": { "1": { "name": "keyword.declaration.lyng" }, "2": { "name": "entity.name.type.lyng" }, "3": { "name": "entity.name.function.lyng" } } }, { "name": "meta.type.declaration.lyng", "match": "\\b(?:class|enum|interface|object)(?:\\s+([\\p{L}_][\\p{L}\\p{N}_]*))?", "captures": { "1": { "name": "entity.name.type.lyng" } } }, { "name": "meta.variable.declaration.lyng", "match": "\\b(val|var)\\s+(?:([\\p{L}_][\\p{L}\\p{N}_]*)\\.)?([\\p{L}_][\\p{L}\\p{N}_]*)", "captures": { "1": { "name": "keyword.declaration.lyng" }, "2": { "name": "entity.name.type.lyng" }, "3": { "name": "variable.other.declaration.lyng" } } } ] },
|
||||
"keywords": { "patterns": [ { "name": "keyword.control.lyng", "match": "\\b(?:if|else|when|while|do|for|try|catch|finally|throw|return|break|continue)\\b" }, { "name": "keyword.declaration.lyng", "match": "\\b(?:fun|fn|class|enum|interface|val|var|import|package|constructor|property|abstract|override|open|closed|extern|private|protected|static|get|set|object|init|by)\\b" }, { "name": "keyword.operator.word.lyng", "match": "\\bnot\\s+(?:in|is)\\b" }, { "name": "keyword.operator.word.lyng", "match": "\\b(?:and|or|not|in|is|as|as\\?)\\b" } ] },
|
||||
"constants": { "patterns": [ { "name": "constant.language.lyng", "match": "(?:\\b(?:true|false|null|this(?:@[\\p{L}_][\\p{L}\\p{N}_]*)?)\\b|π)" } ] },
|
||||
"types": { "patterns": [ { "name": "storage.type.lyng", "match": "\\b(?:Int|Real|String|Bool|Char|Regex)\\b" }, { "name": "entity.name.type.lyng", "match": "\\b[A-Z][A-Za-z0-9_]*\\b(?!\\s*\\()" } ] },
|
||||
"operators": { "patterns": [ { "name": "keyword.operator.comparison.lyng", "match": "===|!==|==|!=|<=|>=|<|>" }, { "name": "keyword.operator.shuttle.lyng", "match": "<=>" }, { "name": "keyword.operator.arrow.lyng", "match": "=>|->|::" }, { "name": "keyword.operator.range.lyng", "match": "\\.\\.\\.|\\.\\.<|\\.\\." }, { "name": "keyword.operator.nullsafe.lyng", "match": "\\?\\.|\\?\\[|\\?\\(|\\?\\{|\\?:|\\?\\?" }, { "name": "keyword.operator.assignment.lyng", "match": "(?:\\+=|-=|\\*=|/=|%=|=)" }, { "name": "keyword.operator.logical.lyng", "match": "&&|\\|\\|" }, { "name": "keyword.operator.bitwise.lyng", "match": "<<|>>|&|\\||\\^|~" }, { "name": "keyword.operator.match.lyng", "match": "=~|!~" }, { "name": "keyword.operator.arithmetic.lyng", "match": "\\+\\+|--|[+\\-*/%]" }, { "name": "keyword.operator.other.lyng", "match": "[!?]" } ] },
|
||||
"punctuation": { "patterns": [ { "name": "punctuation.separator.comma.lyng", "match": "," }, { "name": "punctuation.terminator.statement.lyng", "match": ";" }, { "name": "punctuation.section.block.begin.lyng", "match": "[(]{1}|[{]{1}|\\[" }, { "name": "punctuation.section.block.end.lyng", "match": "[)]{1}|[}]{1}|\\]" }, { "name": "punctuation.accessor.dot.lyng", "match": "\\." }, { "name": "punctuation.separator.colon.lyng", "match": ":" } ] }
|
||||
|
||||
@ -33,10 +33,10 @@ class LyngLexer : LexerBase() {
|
||||
|
||||
private val keywords = setOf(
|
||||
"fun", "val", "var", "class", "interface", "type", "import", "as",
|
||||
"abstract", "closed", "override",
|
||||
"abstract", "closed", "override", "static", "extern", "open", "private", "protected",
|
||||
"if", "else", "for", "while", "return", "true", "false", "null",
|
||||
"when", "in", "is", "break", "continue", "try", "catch", "finally",
|
||||
"get", "set"
|
||||
"get", "set", "object", "enum", "init", "by", "property", "constructor"
|
||||
)
|
||||
|
||||
override fun start(buffer: CharSequence, startOffset: Int, endOffset: Int, initialState: Int) {
|
||||
@ -160,5 +160,5 @@ class LyngLexer : LexerBase() {
|
||||
private fun Char.isDigit(): Boolean = this in '0'..'9'
|
||||
private fun Char.isIdentifierStart(): Boolean = this == '_' || this.isLetter()
|
||||
private fun Char.isIdentifierPart(): Boolean = this.isIdentifierStart() || this.isDigit()
|
||||
private fun isPunct(c: Char): Boolean = c in setOf('(', ')', '{', '}', '[', ']', '.', ',', ';', ':', '+', '-', '*', '/', '%', '=', '<', '>', '!', '?', '&', '|', '^', '~')
|
||||
private fun isPunct(c: Char): Boolean = c in setOf('(', ')', '{', '}', '[', ']', '.', ',', ';', ':', '+', '-', '*', '/', '%', '=', '<', '>', '!', '?', '&', '|', '^', '~', '@')
|
||||
}
|
||||
|
||||
@ -530,7 +530,7 @@ private fun detectDeclarationAndParamOverrides(text: String): Map<Pair<Int, Int>
|
||||
fun isIdentPart(ch: Char) = ch == '_' || ch == '$' || ch == '~' || ch.isLetterOrDigit()
|
||||
// A conservative list of language keywords to avoid misclassifying as function calls
|
||||
val kw = setOf(
|
||||
"package", "import", "fun", "fn", "class", "interface", "enum", "val", "var",
|
||||
"package", "import", "fun", "fn", "class", "interface", "enum", "object", "val", "var",
|
||||
"if", "else", "while", "do", "for", "when", "try", "catch", "finally",
|
||||
"throw", "return", "break", "continue", "in", "is", "as", "as?", "not",
|
||||
"true", "false", "null", "private", "protected", "abstract", "closed", "override", "open", "extern", "static",
|
||||
@ -640,8 +640,12 @@ private fun detectDeclarationAndParamOverrides(text: String): Map<Pair<Int, Int>
|
||||
i = p
|
||||
continue
|
||||
}
|
||||
if (text.startsWith("class", i) && (i == 0 || !isIdentPart(text[i-1])) && (i+5 >= n || !isIdentPart(text.getOrNull(i+5) ?: '\u0000'))) {
|
||||
var p = skipWs(i + 5)
|
||||
if ((text.startsWith("class", i) && (i == 0 || !isIdentPart(text[i-1])) && (i+5 >= n || !isIdentPart(text.getOrNull(i+5) ?: '\u0000'))) ||
|
||||
(text.startsWith("object", i) && (i == 0 || !isIdentPart(text[i-1])) && (i+6 >= n || !isIdentPart(text.getOrNull(i+6) ?: '\u0000'))) ||
|
||||
(text.startsWith("enum", i) && (i == 0 || !isIdentPart(text[i-1])) && (i+4 >= n || !isIdentPart(text.getOrNull(i+4) ?: '\u0000'))) ||
|
||||
(text.startsWith("interface", i) && (i == 0 || !isIdentPart(text[i-1])) && (i+9 >= n || !isIdentPart(text.getOrNull(i+9) ?: '\u0000')))) {
|
||||
val kwLen = if (text.startsWith("class", i)) 5 else if (text.startsWith("object", i)) 6 else if (text.startsWith("enum", i)) 4 else 9
|
||||
var p = skipWs(i + kwLen)
|
||||
val name = readIdent(p)
|
||||
if (name != null) result[p to name.second] = "hl-class"
|
||||
i = p
|
||||
|
||||
@ -86,8 +86,12 @@ val base = { a: 1, b: 2 }
|
||||
val patch = { b: 3, c: }
|
||||
val m = { "a": 0, ...base, ...patch, d: 4 }
|
||||
assertEquals(1, m["a"]) // base overwrites 0
|
||||
assertEquals(3, m["b"]) // patch overwrites base
|
||||
assertEquals(4, m["d"]) // literal key
|
||||
|
||||
// Object expressions: anonymous classes on the fly
|
||||
val worker = object : Runnable {
|
||||
override fun run() = println("Working...")
|
||||
}
|
||||
worker.run()
|
||||
>>> void
|
||||
""".trimIndent()
|
||||
val mapHtml = "<pre><code>" + htmlEscape(code) + "</code></pre>"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user