fixed comment before else in if;

dynamic now support indexing access
This commit is contained in:
Sergey Chernov 2025-10-04 11:25:19 +04:00
parent e8b630715a
commit c854b06683
7 changed files with 90 additions and 8 deletions

View File

@ -266,7 +266,7 @@ The same we can provide writable dynamic fields (var-type), adding set method:
// constant field
"foo" -> "bar"
// mutable field
"bar" -> setValueForBar
"bar" -> storedValueForBar
else -> throw SymbolNotFoundException()
}
@ -289,9 +289,56 @@ The same we can provide writable dynamic fields (var-type), adding set method:
assertThrows {
accessor.bad = "!23"
}
void
>>> void
Of course, you can return any object from dynamic fields; returning lambdas let create _dynamic methods_ - the callable method. It is very convenient to implement libraries with dynamic remote interfaces, etc.
### Dynamic indexers
Index access for dynamics is passed to the same getter and setter, so it is
generally the same:
var storedValue = "bar"
val x = dynamic {
get {
if( it == "foo" ) storedValue
else null
}
}
assertEquals("bar", x["foo"] )
assertEquals("bar", x.foo )
>>> void
And assigning them works the same. You can make it working
mimicking arrays, but remember, it is not Collection so
collection's sugar won't work with it:
var storedValue = "bar"
val x = dynamic {
get {
when(it) {
"size" -> 1
0 -> storedValue
else -> null
}
}
set { index, value ->
if( index == 0 ) storedValue = value
else throw "Illegal index: "+index
}
}
assertEquals("bar", x[0] )
assertEquals(1, x.size )
x[0] = "buzz"
assertThrows { x[1] = 1 }
assertEquals("buzz", storedValue)
assertEquals("buzz", x[0])
>>> void
If you want dynamic to function like an array, create a [feature
request](https://gitea.sergeych.net/SergeychWorks/lyng/issues).
# Theory
## Basic principles:

View File

@ -4,7 +4,7 @@ clikt = "5.0.3"
kotlin = "2.2.20"
android-minSdk = "24"
android-compileSdk = "34"
kotlinx-coroutines = "1.9.0"
kotlinx-coroutines = "1.10.2"
mp_bintools = "0.1.12"
firebaseCrashlyticsBuildtools = "3.0.3"
okioVersion = "3.10.2"

View File

@ -888,6 +888,7 @@ class Compiler(
}
}
@Suppress("SameParameterValue")
private fun parseNumber(isPlus: Boolean): Obj {
return parseNumberOrNull(isPlus) ?: throw ScriptError(cc.currentPos(), "Expecting number")
}
@ -1681,7 +1682,7 @@ class Compiler(
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
// could be else block:
val t2 = cc.next()
val t2 = cc.nextNonWhitespace()
// we generate different statements: optimization
return if (t2.type == Token.Type.ID && t2.value == "else") {

View File

@ -28,7 +28,9 @@ data class Pos(val source: Source, val line: Int, val column: Int) {
else if( line > 0) Pos(source, line-1, source.lines[line-1].length - 1)
else throw IllegalStateException("can't go back from line 0, column 0")
val currentLine: String get() = if( end ) "EOF" else source.lines[line]
val currentLine: String get() =
if( end ) "EOF"
else if( line >= 0 ) source.lines[line] else "<no line information>"
val end: Boolean get() = line >= source.lines.size

View File

@ -26,7 +26,7 @@ open class ScriptError(val pos: Pos, val errorMessage: String, cause: Throwable?
$pos: Error: $errorMessage
${pos.currentLine}
${"-".repeat(pos.column)}^
${if( pos.column >= 0 ) "-".repeat(pos.column) + "^" else ""}
""".trimIndent(),
cause
)

View File

@ -67,6 +67,16 @@ class ObjDynamic : Obj() {
?: super.writeField(scope, name, newValue)
}
override suspend fun getAt(scope: Scope, index: Obj): Obj {
return readCallback?.execute( scope.copy(Arguments(index)))
?: super.getAt(scope, index)
}
override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) {
writeCallback?.execute(scope.copy(Arguments(index, newValue)))
?: super.putAt(scope, index, newValue)
}
companion object {
suspend fun create(scope: Scope, builder: Statement): ObjDynamic {
@ -76,7 +86,9 @@ class ObjDynamic : Obj() {
return delegate
}
val type = object : ObjClass("Delegate") {}
val type = object : ObjClass("Delegate") {}.apply {
// addClassConst("IndexGetName", operatorGetName)
}
}
}

View File

@ -107,6 +107,26 @@ class OOTest {
""".trimIndent())
}
@Test
fun testDynamicIndexAccess() = runTest {
eval("""
val store = Map()
val accessor = dynamic {
get { name ->
store[name]
}
set { name, value ->
store[name] = value
}
}
assertEquals(null, accessor["foo"])
assertEquals(null, accessor.foo)
accessor["foo"] = "bar"
assertEquals("bar", accessor["foo"])
assertEquals("bar", accessor.foo)
""".trimIndent())
}
@Test
fun testMultilineConstructor() = runTest {
eval("""