fixed comment before else in if;
dynamic now support indexing access
This commit is contained in:
parent
e8b630715a
commit
c854b06683
53
docs/OOP.md
53
docs/OOP.md
@ -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()
|
||||
}
|
||||
@ -275,8 +275,8 @@ The same we can provide writable dynamic fields (var-type), adding set method:
|
||||
// only 'bar' is mutable:
|
||||
if( name == "bar" )
|
||||
storedValueForBar = value
|
||||
// the rest is immotable. consider throw also
|
||||
// SymbolNotFoundException when needed.
|
||||
// the rest is immotable. consider throw also
|
||||
// SymbolNotFoundException when needed.
|
||||
else throw IllegalAssignmentException("Can't assign "+name)
|
||||
}
|
||||
}
|
||||
@ -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:
|
||||
|
@ -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"
|
||||
|
@ -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") {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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("""
|
||||
|
Loading…
x
Reference in New Issue
Block a user