Compare commits
5
Commits
0.8.0-SNAPSHOT
...
0.8.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de71c7c8ec | ||
|
|
be370dfdef | ||
|
|
53a6a88924 | ||
|
|
2c2dae4d85 | ||
|
|
63b2808109 |
No files matched your search
+42
-1
@@ -147,7 +147,7 @@ In many cases it is necessary to implement custom comparison and `toString`, sti
|
|||||||
each class is provided with default implementations:
|
each class is provided with default implementations:
|
||||||
|
|
||||||
- default toString outputs class name and its _public_ fields.
|
- default toString outputs class name and its _public_ fields.
|
||||||
- default comparison compares compares all public fields in order of appearance.
|
- default comparison compares all fields in order of appearance.
|
||||||
|
|
||||||
For example, for our class Point:
|
For example, for our class Point:
|
||||||
|
|
||||||
@@ -160,6 +160,47 @@ For example, for our class Point:
|
|||||||
Point(1,1+1)
|
Point(1,1+1)
|
||||||
>>> Point(x=1,y=2)
|
>>> Point(x=1,y=2)
|
||||||
|
|
||||||
|
## Statics: class fields and class methods
|
||||||
|
|
||||||
|
You can mark a field or a method as static. This is borrowed from Java as more plain version of a kotlin's companion object or Scala's object. Static field and functions is one for a class, not for an instance. From inside the class, e.g. from the class method, it is a regular var. From outside, it is accessible as `ClassName.field` or method:
|
||||||
|
|
||||||
|
|
||||||
|
class Value(x) {
|
||||||
|
static var foo = Value("foo")
|
||||||
|
|
||||||
|
static fun exclamation() {
|
||||||
|
// here foo is a regular var:
|
||||||
|
foo.x + "!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals( Value.foo.x, "foo" )
|
||||||
|
assertEquals( "foo!", Value.exclamation() )
|
||||||
|
|
||||||
|
// we can access foo from outside like this:
|
||||||
|
Value.foo = Value("bar")
|
||||||
|
assertEquals( "bar!", Value.exclamation() )
|
||||||
|
>>> void
|
||||||
|
|
||||||
|
As usual, private statics are not accessible from the outside:
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
// private, inacessible from outside protected data:
|
||||||
|
private static var data = null
|
||||||
|
|
||||||
|
// the interface to access and change it:
|
||||||
|
static fun getData() { data }
|
||||||
|
static fun setData(value) { data = value }
|
||||||
|
}
|
||||||
|
|
||||||
|
// no direct access:
|
||||||
|
assertThrows { Test.data }
|
||||||
|
|
||||||
|
// accessible with the interface:
|
||||||
|
assertEquals( null, Test.getData() )
|
||||||
|
Test.setData("fubar")
|
||||||
|
assertEquals("fubar", Test.getData() )
|
||||||
|
>>> void
|
||||||
|
|
||||||
# Extending classes
|
# Extending classes
|
||||||
|
|
||||||
It sometimes happen that the class is missing some particular functionality that can be _added to it_ without rewriting its inner logic and using its private state. In this case _extension methods_ could be used, for example. we want to create an extension method
|
It sometimes happen that the class is missing some particular functionality that can be _added to it_ without rewriting its inner logic and using its private state. In this case _extension methods_ could be used, for example. we want to create an extension method
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.8.0-SNAPSHOT"
|
version = "0.8.1-SNAPSHOT"
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
|
|||||||
@@ -17,6 +17,16 @@ class Compiler(
|
|||||||
|
|
||||||
class Settings
|
class Settings
|
||||||
|
|
||||||
|
private val initStack = mutableListOf<MutableList<Statement>>()
|
||||||
|
|
||||||
|
val currentInitScope: MutableList<Statement>
|
||||||
|
get() =
|
||||||
|
initStack.lastOrNull() ?: cc.syntaxError("no initialization scope exists here")
|
||||||
|
|
||||||
|
private fun pushInitScope(): MutableList<Statement> = mutableListOf<Statement>().also { initStack.add(it) }
|
||||||
|
|
||||||
|
private fun popInitScope(): MutableList<Statement> = initStack.removeLast()
|
||||||
|
|
||||||
private suspend fun parseScript(): Script {
|
private suspend fun parseScript(): Script {
|
||||||
val statements = mutableListOf<Statement>()
|
val statements = mutableListOf<Statement>()
|
||||||
val start = cc.currentPos()
|
val start = cc.currentPos()
|
||||||
@@ -823,6 +833,8 @@ class Compiler(
|
|||||||
val isExtern = cc.skipId("extern")
|
val isExtern = cc.skipId("extern")
|
||||||
when {
|
when {
|
||||||
cc.matchQualifiers("fun", "private") -> parseFunctionDeclaration(Visibility.Private, isExtern)
|
cc.matchQualifiers("fun", "private") -> parseFunctionDeclaration(Visibility.Private, isExtern)
|
||||||
|
cc.matchQualifiers("fun", "private", "static") -> parseFunctionDeclaration(Visibility.Private, isExtern, isStatic = true)
|
||||||
|
cc.matchQualifiers("fun", "static") -> parseFunctionDeclaration(Visibility.Public, isExtern, isStatic = true)
|
||||||
cc.matchQualifiers("fn", "private") -> parseFunctionDeclaration(Visibility.Private, isExtern)
|
cc.matchQualifiers("fn", "private") -> parseFunctionDeclaration(Visibility.Private, isExtern)
|
||||||
cc.matchQualifiers("fun", "open") -> parseFunctionDeclaration(isOpen = true, isExtern = isExtern)
|
cc.matchQualifiers("fun", "open") -> parseFunctionDeclaration(isOpen = true, isExtern = isExtern)
|
||||||
cc.matchQualifiers("fn", "open") -> parseFunctionDeclaration(isOpen = true, isExtern = isExtern)
|
cc.matchQualifiers("fn", "open") -> parseFunctionDeclaration(isOpen = true, isExtern = isExtern)
|
||||||
@@ -830,7 +842,21 @@ class Compiler(
|
|||||||
cc.matchQualifiers("fun") -> parseFunctionDeclaration(isOpen = false, isExtern = isExtern)
|
cc.matchQualifiers("fun") -> parseFunctionDeclaration(isOpen = false, isExtern = isExtern)
|
||||||
cc.matchQualifiers("fn") -> parseFunctionDeclaration(isOpen = false, isExtern = isExtern)
|
cc.matchQualifiers("fn") -> parseFunctionDeclaration(isOpen = false, isExtern = isExtern)
|
||||||
|
|
||||||
|
cc.matchQualifiers("val", "private", "static") -> parseVarDeclaration(
|
||||||
|
false,
|
||||||
|
Visibility.Private,
|
||||||
|
isStatic = true
|
||||||
|
)
|
||||||
|
|
||||||
|
cc.matchQualifiers("val", "static") -> parseVarDeclaration(false, Visibility.Public, isStatic = true)
|
||||||
cc.matchQualifiers("val", "private") -> parseVarDeclaration(false, Visibility.Private)
|
cc.matchQualifiers("val", "private") -> parseVarDeclaration(false, Visibility.Private)
|
||||||
|
cc.matchQualifiers("var", "static") -> parseVarDeclaration(true, Visibility.Public, isStatic = true)
|
||||||
|
cc.matchQualifiers("var", "static", "private") -> parseVarDeclaration(
|
||||||
|
true,
|
||||||
|
Visibility.Private,
|
||||||
|
isStatic = true
|
||||||
|
)
|
||||||
|
|
||||||
cc.matchQualifiers("var", "private") -> parseVarDeclaration(true, Visibility.Private)
|
cc.matchQualifiers("var", "private") -> parseVarDeclaration(true, Visibility.Private)
|
||||||
cc.matchQualifiers("val", "open") -> parseVarDeclaration(false, Visibility.Private, true)
|
cc.matchQualifiers("val", "open") -> parseVarDeclaration(false, Visibility.Private, true)
|
||||||
cc.matchQualifiers("var", "open") -> parseVarDeclaration(true, Visibility.Private, true)
|
cc.matchQualifiers("var", "open") -> parseVarDeclaration(true, Visibility.Private, true)
|
||||||
@@ -1090,6 +1116,8 @@ class Compiler(
|
|||||||
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
|
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
|
||||||
val t = cc.next()
|
val t = cc.next()
|
||||||
|
|
||||||
|
pushInitScope()
|
||||||
|
|
||||||
val bodyInit: Statement? = if (t.type == Token.Type.LBRACE) {
|
val bodyInit: Statement? = if (t.type == Token.Type.LBRACE) {
|
||||||
// parse body
|
// parse body
|
||||||
parseScript().also {
|
parseScript().also {
|
||||||
@@ -1100,6 +1128,8 @@ class Compiler(
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val initScope = popInitScope()
|
||||||
|
|
||||||
// create class
|
// create class
|
||||||
val className = nameToken.value
|
val className = nameToken.value
|
||||||
|
|
||||||
@@ -1131,6 +1161,13 @@ class Compiler(
|
|||||||
// the main statement should create custom ObjClass instance with field
|
// the main statement should create custom ObjClass instance with field
|
||||||
// accessors, constructor registration, etc.
|
// accessors, constructor registration, etc.
|
||||||
addItem(className, false, newClass)
|
addItem(className, false, newClass)
|
||||||
|
if (initScope.isNotEmpty()) {
|
||||||
|
val classScope = copy(newThisObj = newClass)
|
||||||
|
newClass.classScope = classScope
|
||||||
|
for (s in initScope)
|
||||||
|
s.execute(classScope)
|
||||||
|
.also { println("executed, ${classScope.objects}") }
|
||||||
|
}
|
||||||
newClass
|
newClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1506,7 +1543,8 @@ class Compiler(
|
|||||||
private suspend fun parseFunctionDeclaration(
|
private suspend fun parseFunctionDeclaration(
|
||||||
visibility: Visibility = Visibility.Public,
|
visibility: Visibility = Visibility.Public,
|
||||||
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false,
|
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false,
|
||||||
isExtern: Boolean = false
|
isExtern: Boolean = false,
|
||||||
|
isStatic: Boolean = false,
|
||||||
): Statement {
|
): Statement {
|
||||||
var t = cc.next()
|
var t = cc.next()
|
||||||
val start = t.pos
|
val start = t.pos
|
||||||
@@ -1559,7 +1597,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
fnStatements.execute(context)
|
fnStatements.execute(context)
|
||||||
}
|
}
|
||||||
return statement(start) { context ->
|
val fnCreatestatement = statement(start) { context ->
|
||||||
// we added fn in the context. now we must save closure
|
// we added fn in the context. now we must save closure
|
||||||
// for the function
|
// for the function
|
||||||
closure = context
|
closure = context
|
||||||
@@ -1577,6 +1615,11 @@ class Compiler(
|
|||||||
// saved the proper context in the closure
|
// saved the proper context in the closure
|
||||||
fnBody
|
fnBody
|
||||||
}
|
}
|
||||||
|
return if (isStatic) {
|
||||||
|
currentInitScope += fnCreatestatement
|
||||||
|
NopStatement
|
||||||
|
} else
|
||||||
|
fnCreatestatement
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun parseBlock(skipLeadingBrace: Boolean = false): Statement {
|
private suspend fun parseBlock(skipLeadingBrace: Boolean = false): Statement {
|
||||||
@@ -1600,7 +1643,8 @@ class Compiler(
|
|||||||
private suspend fun parseVarDeclaration(
|
private suspend fun parseVarDeclaration(
|
||||||
isMutable: Boolean,
|
isMutable: Boolean,
|
||||||
visibility: Visibility,
|
visibility: Visibility,
|
||||||
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false
|
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false,
|
||||||
|
isStatic: Boolean = false
|
||||||
): Statement {
|
): Statement {
|
||||||
val nameToken = cc.next()
|
val nameToken = cc.next()
|
||||||
val start = nameToken.pos
|
val start = nameToken.pos
|
||||||
@@ -1622,6 +1666,21 @@ class Compiler(
|
|||||||
val initialExpression = if (setNull) null else parseStatement(true)
|
val initialExpression = if (setNull) null else parseStatement(true)
|
||||||
?: throw ScriptError(eqToken.pos, "Expected initializer expression")
|
?: throw ScriptError(eqToken.pos, "Expected initializer expression")
|
||||||
|
|
||||||
|
if (isStatic) {
|
||||||
|
// find objclass instance: this is tricky: this code executes in object initializer,
|
||||||
|
// when creating instance, but we need to execute it in the class initializer which
|
||||||
|
// is missing as for now. Add it to the compiler context?
|
||||||
|
// add there
|
||||||
|
// return
|
||||||
|
currentInitScope += statement {
|
||||||
|
val initValue = initialExpression?.execute(this)?.byValueCopy() ?: ObjNull
|
||||||
|
(thisObj as ObjClass).createClassField(name, initValue, isMutable, visibility, pos)
|
||||||
|
addItem(name, isMutable, initValue, visibility, ObjRecord.Type.Field)
|
||||||
|
ObjVoid
|
||||||
|
}
|
||||||
|
return NopStatement
|
||||||
|
}
|
||||||
|
|
||||||
return statement(nameToken.pos) { context ->
|
return statement(nameToken.pos) { context ->
|
||||||
if (context.containsLocal(name))
|
if (context.containsLocal(name))
|
||||||
throw ScriptError(nameToken.pos, "Variable $name is already defined")
|
throw ScriptError(nameToken.pos, "Variable $name is already defined")
|
||||||
|
|||||||
@@ -49,6 +49,10 @@ class CompilerContext(val tokens: List<Token>) {
|
|||||||
throw ScriptError(at, message)
|
throw ScriptError(at, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun syntaxError(message: String = "Syntax error"): Nothing {
|
||||||
|
throw ScriptError(currentPos(), message)
|
||||||
|
}
|
||||||
|
|
||||||
fun currentPos(): Pos = tokens[currentIndex].pos
|
fun currentPos(): Pos = tokens[currentIndex].pos
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ open class Scope(
|
|||||||
fun raiseIllegalState(message: String = "Illegal argument error"): Nothing =
|
fun raiseIllegalState(message: String = "Illegal argument error"): Nothing =
|
||||||
raiseError(ObjIllegalStateException(this, message))
|
raiseError(ObjIllegalStateException(this, message))
|
||||||
|
|
||||||
|
fun raiseIllegalAssignment(message: String): Nothing =
|
||||||
|
raiseError(ObjIllegalAssignmentException(this, message))
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun raiseNoSuchElement(message: String = "No such element"): Nothing =
|
fun raiseNoSuchElement(message: String = "No such element"): Nothing =
|
||||||
raiseError(ObjIllegalArgumentException(this, message))
|
raiseError(ObjIllegalArgumentException(this, message))
|
||||||
|
|||||||
@@ -16,6 +16,16 @@ open class ObjClass(
|
|||||||
var constructorMeta: ArgsDeclaration? = null
|
var constructorMeta: ArgsDeclaration? = null
|
||||||
var instanceConstructor: Statement? = null
|
var instanceConstructor: Statement? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the scope for class methods, initialize class vars, etc.
|
||||||
|
*
|
||||||
|
* Important notice. When create a user class, e.g. from Lyng source, it should
|
||||||
|
* be set to a scope by compiler, so it could access local closure, etc. Otherwise,
|
||||||
|
* it will be initialized to default scope on first necessity, e.g. when used in
|
||||||
|
* external, kotlin classes with [addClassConst] and [addClassFn], etc.
|
||||||
|
*/
|
||||||
|
var classScope: Scope? = null
|
||||||
|
|
||||||
val allParentsSet: Set<ObjClass> =
|
val allParentsSet: Set<ObjClass> =
|
||||||
parents.flatMap {
|
parents.flatMap {
|
||||||
listOf(it) + it.allParentsSet
|
listOf(it) + it.allParentsSet
|
||||||
@@ -23,9 +33,10 @@ open class ObjClass(
|
|||||||
|
|
||||||
override val objClass: ObjClass by lazy { ObjClassType }
|
override val objClass: ObjClass by lazy { ObjClassType }
|
||||||
|
|
||||||
// members: fields most often
|
/**
|
||||||
|
* members: fields most often. These are called with [ObjInstance] withs ths [ObjInstance.objClass]
|
||||||
|
*/
|
||||||
private val members = mutableMapOf<String, ObjRecord>()
|
private val members = mutableMapOf<String, ObjRecord>()
|
||||||
private val classMembers = mutableMapOf<String, ObjRecord>()
|
|
||||||
|
|
||||||
override fun toString(): String = className
|
override fun toString(): String = className
|
||||||
|
|
||||||
@@ -40,6 +51,7 @@ open class ObjClass(
|
|||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun createField(
|
fun createField(
|
||||||
name: String,
|
name: String,
|
||||||
initialValue: Obj,
|
initialValue: Obj,
|
||||||
@@ -53,6 +65,11 @@ open class ObjClass(
|
|||||||
members[name] = ObjRecord(initialValue, isMutable, visibility)
|
members[name] = ObjRecord(initialValue, isMutable, visibility)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun initClassScope(): Scope {
|
||||||
|
if( classScope == null ) classScope = Scope()
|
||||||
|
return classScope!!
|
||||||
|
}
|
||||||
|
|
||||||
fun createClassField(
|
fun createClassField(
|
||||||
name: String,
|
name: String,
|
||||||
initialValue: Obj,
|
initialValue: Obj,
|
||||||
@@ -60,10 +77,11 @@ open class ObjClass(
|
|||||||
visibility: Visibility = Visibility.Public,
|
visibility: Visibility = Visibility.Public,
|
||||||
pos: Pos = Pos.builtIn
|
pos: Pos = Pos.builtIn
|
||||||
) {
|
) {
|
||||||
val existing = classMembers[name]
|
initClassScope()
|
||||||
|
val existing = classScope!!.objects[name]
|
||||||
if( existing != null)
|
if( existing != null)
|
||||||
throw ScriptError(pos, "$name is already defined in $objClass or one of its supertypes")
|
throw ScriptError(pos, "$name is already defined in $objClass or one of its supertypes")
|
||||||
classMembers[name] = ObjRecord(initialValue, isMutable, visibility)
|
classScope!!.addItem(name, isMutable, initialValue, visibility)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addFn(name: String, isOpen: Boolean = false, code: suspend Scope.() -> Obj) {
|
fun addFn(name: String, isOpen: Boolean = false, code: suspend Scope.() -> Obj) {
|
||||||
@@ -91,15 +109,23 @@ open class ObjClass(
|
|||||||
?: throw ScriptError(atPos, "symbol doesn't exist: $name")
|
?: throw ScriptError(atPos, "symbol doesn't exist: $name")
|
||||||
|
|
||||||
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
||||||
classMembers[name]?.let {
|
classScope?.objects?.get(name)?.let {
|
||||||
return it
|
if( it.visibility.isPublic ) return it
|
||||||
}
|
}
|
||||||
return super.readField(scope, name)
|
return super.readField(scope, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
|
||||||
|
initClassScope().objects[name]?.let {
|
||||||
|
if( it.isMutable) it.value = newValue
|
||||||
|
else scope.raiseIllegalAssignment("can't assign $name is not mutable")
|
||||||
|
}
|
||||||
|
?: super.writeField(scope, name, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun invokeInstanceMethod(scope: Scope, name: String, args: Arguments,
|
override suspend fun invokeInstanceMethod(scope: Scope, name: String, args: Arguments,
|
||||||
onNotFoundResult: Obj?): Obj {
|
onNotFoundResult: Obj?): Obj {
|
||||||
return classMembers[name]?.value?.invoke(scope, this, args)
|
return classScope?.objects?.get(name)?.value?.invoke(scope, this, args)
|
||||||
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
scope.raiseIllegalArgument("serialized vars has bigger size than instance vars")
|
scope.raiseIllegalArgument("serialized vars has bigger size than instance vars")
|
||||||
println("deser state vars $vars")
|
println("deser state vars $vars")
|
||||||
for ((i, v) in vars.withIndex()) {
|
for ((i, v) in vars.withIndex()) {
|
||||||
localVars[i].value = vars[i]
|
localVars[i].value = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ import net.sergeych.lynon.LynonType
|
|||||||
*/
|
*/
|
||||||
class ObjInstanceClass(val name: String) : ObjClass(name) {
|
class ObjInstanceClass(val name: String) : ObjClass(name) {
|
||||||
|
|
||||||
// val onDeserilaized =
|
|
||||||
|
|
||||||
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
|
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
|
||||||
val args = decoder.decodeAnyList(scope)
|
val args = decoder.decodeAnyList(scope)
|
||||||
println("deserializing constructor $name, $args params")
|
println("deserializing constructor $name, $args params")
|
||||||
|
|||||||
@@ -2,12 +2,16 @@ package net.sergeych.lyng
|
|||||||
|
|
||||||
import net.sergeych.lyng.obj.Obj
|
import net.sergeych.lyng.obj.Obj
|
||||||
import net.sergeych.lyng.obj.ObjClass
|
import net.sergeych.lyng.obj.ObjClass
|
||||||
|
import net.sergeych.lyng.obj.ObjVoid
|
||||||
|
|
||||||
fun String.toSource(name: String = "eval"): Source = Source(name, this)
|
fun String.toSource(name: String = "eval"): Source = Source(name, this)
|
||||||
|
|
||||||
sealed class ObjType {
|
sealed class ObjType {
|
||||||
object Any : ObjType()
|
object Any : ObjType()
|
||||||
object Int : ObjType()
|
object Void: ObjType()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -62,4 +66,8 @@ fun statement(isStaticConst: Boolean = false, isConst: Boolean = false, f: suspe
|
|||||||
override suspend fun execute(scope: Scope): Obj = f(scope)
|
override suspend fun execute(scope: Scope): Obj = f(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object NopStatement: Statement(true, true, ObjType.Void) {
|
||||||
|
override val pos: Pos = Pos.builtIn
|
||||||
|
|
||||||
|
override suspend fun execute(scope: Scope): Obj = ObjVoid
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import net.sergeych.lyng.eval
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
class OOTest {
|
||||||
|
@Test
|
||||||
|
fun testClassProps() = runTest {
|
||||||
|
eval("""
|
||||||
|
import lyng.time
|
||||||
|
|
||||||
|
class Point(x,y) {
|
||||||
|
static val origin = Point(0,0)
|
||||||
|
static var center = origin
|
||||||
|
}
|
||||||
|
assertEquals(Point(0,0), Point.origin)
|
||||||
|
assertEquals(Point(0,0), Point.center)
|
||||||
|
Point.center = Point(1,2)
|
||||||
|
assertEquals(Point(0,0), Point.origin)
|
||||||
|
assertEquals(Point(1,2), Point.center)
|
||||||
|
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun testClassMethods() = runTest {
|
||||||
|
eval("""
|
||||||
|
import lyng.time
|
||||||
|
|
||||||
|
class Point(x,y) {
|
||||||
|
private static var data = null
|
||||||
|
|
||||||
|
static fun getData() { data }
|
||||||
|
static fun setData(value) {
|
||||||
|
data = value
|
||||||
|
callFrom()
|
||||||
|
}
|
||||||
|
static fun callFrom() {
|
||||||
|
data = data + "!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(Point(0,0), Point(0,0) )
|
||||||
|
assertEquals(null, Point.getData() )
|
||||||
|
Point.setData("foo")
|
||||||
|
assertEquals( "foo!", Point.getData() )
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user