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
|
// constant field
|
||||||
"foo" -> "bar"
|
"foo" -> "bar"
|
||||||
// mutable field
|
// mutable field
|
||||||
"bar" -> setValueForBar
|
"bar" -> storedValueForBar
|
||||||
|
|
||||||
else -> throw SymbolNotFoundException()
|
else -> throw SymbolNotFoundException()
|
||||||
}
|
}
|
||||||
@ -275,8 +275,8 @@ The same we can provide writable dynamic fields (var-type), adding set method:
|
|||||||
// only 'bar' is mutable:
|
// only 'bar' is mutable:
|
||||||
if( name == "bar" )
|
if( name == "bar" )
|
||||||
storedValueForBar = value
|
storedValueForBar = value
|
||||||
// the rest is immotable. consider throw also
|
// the rest is immotable. consider throw also
|
||||||
// SymbolNotFoundException when needed.
|
// SymbolNotFoundException when needed.
|
||||||
else throw IllegalAssignmentException("Can't assign "+name)
|
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 {
|
assertThrows {
|
||||||
accessor.bad = "!23"
|
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.
|
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
|
# Theory
|
||||||
|
|
||||||
## Basic principles:
|
## Basic principles:
|
||||||
|
@ -4,7 +4,7 @@ clikt = "5.0.3"
|
|||||||
kotlin = "2.2.20"
|
kotlin = "2.2.20"
|
||||||
android-minSdk = "24"
|
android-minSdk = "24"
|
||||||
android-compileSdk = "34"
|
android-compileSdk = "34"
|
||||||
kotlinx-coroutines = "1.9.0"
|
kotlinx-coroutines = "1.10.2"
|
||||||
mp_bintools = "0.1.12"
|
mp_bintools = "0.1.12"
|
||||||
firebaseCrashlyticsBuildtools = "3.0.3"
|
firebaseCrashlyticsBuildtools = "3.0.3"
|
||||||
okioVersion = "3.10.2"
|
okioVersion = "3.10.2"
|
||||||
|
@ -888,6 +888,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("SameParameterValue")
|
||||||
private fun parseNumber(isPlus: Boolean): Obj {
|
private fun parseNumber(isPlus: Boolean): Obj {
|
||||||
return parseNumberOrNull(isPlus) ?: throw ScriptError(cc.currentPos(), "Expecting number")
|
return parseNumberOrNull(isPlus) ?: throw ScriptError(cc.currentPos(), "Expecting number")
|
||||||
}
|
}
|
||||||
@ -1681,7 +1682,7 @@ class Compiler(
|
|||||||
|
|
||||||
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
|
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
|
||||||
// could be else block:
|
// could be else block:
|
||||||
val t2 = cc.next()
|
val t2 = cc.nextNonWhitespace()
|
||||||
|
|
||||||
// we generate different statements: optimization
|
// we generate different statements: optimization
|
||||||
return if (t2.type == Token.Type.ID && t2.value == "else") {
|
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 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")
|
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
|
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: Error: $errorMessage
|
||||||
|
|
||||||
${pos.currentLine}
|
${pos.currentLine}
|
||||||
${"-".repeat(pos.column)}^
|
${if( pos.column >= 0 ) "-".repeat(pos.column) + "^" else ""}
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
cause
|
cause
|
||||||
)
|
)
|
||||||
|
@ -67,6 +67,16 @@ class ObjDynamic : Obj() {
|
|||||||
?: super.writeField(scope, name, newValue)
|
?: 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 {
|
companion object {
|
||||||
|
|
||||||
suspend fun create(scope: Scope, builder: Statement): ObjDynamic {
|
suspend fun create(scope: Scope, builder: Statement): ObjDynamic {
|
||||||
@ -76,7 +86,9 @@ class ObjDynamic : Obj() {
|
|||||||
return delegate
|
return delegate
|
||||||
}
|
}
|
||||||
|
|
||||||
val type = object : ObjClass("Delegate") {}
|
val type = object : ObjClass("Delegate") {}.apply {
|
||||||
|
// addClassConst("IndexGetName", operatorGetName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -107,6 +107,26 @@ class OOTest {
|
|||||||
""".trimIndent())
|
""".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
|
@Test
|
||||||
fun testMultilineConstructor() = runTest {
|
fun testMultilineConstructor() = runTest {
|
||||||
eval("""
|
eval("""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user