fix: lambda now have correct closure
This commit is contained in:
parent
306a7f26ef
commit
c0cf190452
15
docs/OOP.md
15
docs/OOP.md
@ -70,3 +70,18 @@ Regular methods are called on instances as usual `instance.method()`. The method
|
|||||||
1. this instance methods;
|
1. this instance methods;
|
||||||
2. parents method: no guarantee but we enumerate parents in order of appearance;
|
2. parents method: no guarantee but we enumerate parents in order of appearance;
|
||||||
3. possible extension methods (scoped)
|
3. possible extension methods (scoped)
|
||||||
|
|
||||||
|
# Defining a new class
|
||||||
|
|
||||||
|
The class is a some data record with named fields and fixed order, in fact. To define a class,
|
||||||
|
just Provide a name and a record like this:
|
||||||
|
|
||||||
|
class Vec2(x,y)
|
||||||
|
|
||||||
|
This way, you have created a _constructor_, so calling `Vec2( 10, 20 )` would create an _instane_ of `Vec2` class:
|
||||||
|
|
||||||
|
class Vec2(x,y)
|
||||||
|
Vec2(10,20)
|
||||||
|
>> eee
|
||||||
|
|
||||||
|
TBD
|
@ -47,7 +47,7 @@ One interesting way of using closure isolation is to keep state of the functions
|
|||||||
>>> 0
|
>>> 0
|
||||||
>>> 1
|
>>> 1
|
||||||
>>> 2
|
>>> 2
|
||||||
>>> void
|
>> void
|
||||||
|
|
||||||
Inner `counter` is not accessible from outside, no way; still it is kept
|
Inner `counter` is not accessible from outside, no way; still it is kept
|
||||||
between calls in the closure, as inner function `doit`, returned from the
|
between calls in the closure, as inner function `doit`, returned from the
|
||||||
@ -75,3 +75,15 @@ The example above could be rewritten using inner lambda, too:
|
|||||||
>>> 1
|
>>> 1
|
||||||
>>> 2
|
>>> 2
|
||||||
>>> void
|
>>> void
|
||||||
|
|
||||||
|
Lambda functions remember their scopes, so it will work the same as previous:
|
||||||
|
|
||||||
|
var counter = 200
|
||||||
|
fun createLambda() {
|
||||||
|
var counter = 0
|
||||||
|
{ counter += 1 }
|
||||||
|
}
|
||||||
|
val c = createLambda()
|
||||||
|
println(c)
|
||||||
|
>> 1
|
||||||
|
>> void
|
24
docs/class_reference.md
Normal file
24
docs/class_reference.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Classes
|
||||||
|
|
||||||
|
## Declaring
|
||||||
|
|
||||||
|
class Foo1
|
||||||
|
class Foo2() // same, empty constructor
|
||||||
|
class Foo3() { // full
|
||||||
|
}
|
||||||
|
class Foo4 { // Only body
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
class_declaration = ["abstract",] "class" [, constructor] [, body]
|
||||||
|
constructor = "(", [field [, field]] ")
|
||||||
|
field = [visibility ,] [access ,] name [, typedecl]
|
||||||
|
body = [visibility] ("var", vardecl) | ("val", vardecl) | ("fun", fundecl)
|
||||||
|
visibility = "private" | "protected" | "internal"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Abstract classes
|
||||||
|
|
||||||
|
Contain one pr more abstract methods which must be implemented; though they
|
||||||
|
can have constructors, the instances of the abstract classes could not be
|
||||||
|
created independently
|
@ -326,41 +326,47 @@ class Compiler(
|
|||||||
throw ScriptError(startPos, "lambda must have either valid arguments declaration with '->' or no arguments")
|
throw ScriptError(startPos, "lambda must have either valid arguments declaration with '->' or no arguments")
|
||||||
val pos = cc.currentPos()
|
val pos = cc.currentPos()
|
||||||
val body = parseBlock(cc, skipLeadingBrace = true)
|
val body = parseBlock(cc, skipLeadingBrace = true)
|
||||||
return Accessor { _ ->
|
|
||||||
statement {
|
var closure: Context? = null
|
||||||
val context = this.copy(pos)
|
|
||||||
if (argsDeclaration == null) {
|
val callStatement = statement {
|
||||||
// no args: automatic var 'it'
|
val context = closure!!.copy(pos, args)
|
||||||
val l = args.values
|
if (argsDeclaration == null) {
|
||||||
val itValue: Obj = when (l.size) {
|
// no args: automatic var 'it'
|
||||||
// no args: it == void
|
val l = args.values
|
||||||
0 -> ObjVoid
|
val itValue: Obj = when (l.size) {
|
||||||
// one args: it is this arg
|
// no args: it == void
|
||||||
1 -> l[0]
|
0 -> ObjVoid
|
||||||
// more args: it is a list of args
|
// one args: it is this arg
|
||||||
else -> ObjList(l.toMutableList())
|
1 -> l[0]
|
||||||
}
|
// more args: it is a list of args
|
||||||
context.addItem("it", false, itValue)
|
else -> ObjList(l.toMutableList())
|
||||||
} else {
|
}
|
||||||
// assign vars as declared
|
context.addItem("it", false, itValue)
|
||||||
if (args.size != argsDeclaration.args.size && !argsDeclaration.args.last().isEllipsis)
|
} else {
|
||||||
raiseArgumentError("Too many arguments : called with ${args.size}, lambda accepts only ${argsDeclaration.args.size}")
|
// assign vars as declared
|
||||||
for ((n, a) in argsDeclaration.args.withIndex()) {
|
if (args.size != argsDeclaration.args.size && !argsDeclaration.args.last().isEllipsis)
|
||||||
if (n >= args.size) {
|
raiseArgumentError("Too many arguments : called with ${args.size}, lambda accepts only ${argsDeclaration.args.size}")
|
||||||
if (a.initialValue != null)
|
for ((n, a) in argsDeclaration.args.withIndex()) {
|
||||||
context.addItem(a.name, false, a.initialValue.execute(context))
|
if (n >= args.size) {
|
||||||
else throw ScriptError(a.pos, "argument $n is out of scope")
|
if (a.initialValue != null)
|
||||||
} else {
|
context.addItem(a.name, false, a.initialValue.execute(context))
|
||||||
val value = if (a.isEllipsis) {
|
else throw ScriptError(a.pos, "argument $n is out of scope")
|
||||||
ObjList(args.values.subList(n, args.values.size).toMutableList())
|
} else {
|
||||||
} else
|
val value = if (a.isEllipsis) {
|
||||||
args[n]
|
ObjList(args.values.subList(n, args.values.size).toMutableList())
|
||||||
context.addItem(a.name, false, value)
|
} else
|
||||||
}
|
args[n]
|
||||||
|
context.addItem(a.name, false, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
body.execute(context)
|
}
|
||||||
}.asReadonly
|
body.execute(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Accessor { x ->
|
||||||
|
if( closure == null ) closure = x
|
||||||
|
callStatement.asReadonly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,12 +408,22 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class AccessType {
|
||||||
|
Val, Var, Default
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Visibility {
|
||||||
|
Default, Public, Private, Protected, Internal
|
||||||
|
}
|
||||||
|
|
||||||
data class ArgVar(
|
data class ArgVar(
|
||||||
val name: String,
|
val name: String,
|
||||||
val type: TypeDecl = TypeDecl.Obj,
|
val type: TypeDecl = TypeDecl.Obj,
|
||||||
val pos: Pos,
|
val pos: Pos,
|
||||||
val isEllipsis: Boolean,
|
val isEllipsis: Boolean,
|
||||||
val initialValue: Statement? = null
|
val initialValue: Statement? = null,
|
||||||
|
val accessType: AccessType = AccessType.Default,
|
||||||
|
val visibility: Visibility = Visibility.Default
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ArgsDeclaration(val args: List<ArgVar>, val endTokenType: Token.Type) {
|
data class ArgsDeclaration(val args: List<ArgVar>, val endTokenType: Token.Type) {
|
||||||
@ -421,16 +437,73 @@ class Compiler(
|
|||||||
* Parse argument declaration, used in lambda (and later in fn too)
|
* Parse argument declaration, used in lambda (and later in fn too)
|
||||||
* @return declaration or null if there is no valid list of arguments
|
* @return declaration or null if there is no valid list of arguments
|
||||||
*/
|
*/
|
||||||
private fun parseArgsDeclaration(cc: CompilerContext): ArgsDeclaration? {
|
private fun parseArgsDeclaration(cc: CompilerContext, isClassDeclaration: Boolean = false): ArgsDeclaration? {
|
||||||
val result = mutableListOf<ArgVar>()
|
val result = mutableListOf<ArgVar>()
|
||||||
var endTokenType: Token.Type? = null
|
var endTokenType: Token.Type? = null
|
||||||
val startPos = cc.savePos()
|
val startPos = cc.savePos()
|
||||||
|
|
||||||
while (endTokenType == null) {
|
while (endTokenType == null) {
|
||||||
val t = cc.next()
|
var t = cc.next()
|
||||||
when (t.type) {
|
when (t.type) {
|
||||||
Token.Type.NEWLINE -> {}
|
Token.Type.NEWLINE -> {}
|
||||||
Token.Type.ID -> {
|
Token.Type.ID -> {
|
||||||
|
// visibility
|
||||||
|
val visibility = when (t.value) {
|
||||||
|
"private" -> {
|
||||||
|
if (!isClassDeclaration) {
|
||||||
|
cc.restorePos(startPos); return null
|
||||||
|
}
|
||||||
|
t = cc.next()
|
||||||
|
Visibility.Private
|
||||||
|
}
|
||||||
|
|
||||||
|
"protected" -> {
|
||||||
|
if (!isClassDeclaration) {
|
||||||
|
cc.restorePos(startPos); return null
|
||||||
|
}
|
||||||
|
t = cc.next()
|
||||||
|
Visibility.Protected
|
||||||
|
}
|
||||||
|
|
||||||
|
"internal" -> {
|
||||||
|
if (!isClassDeclaration) {
|
||||||
|
cc.restorePos(startPos); return null
|
||||||
|
}
|
||||||
|
t = cc.next()
|
||||||
|
Visibility.Internal
|
||||||
|
}
|
||||||
|
|
||||||
|
"public" -> {
|
||||||
|
if (!isClassDeclaration) {
|
||||||
|
cc.restorePos(startPos); return null
|
||||||
|
}
|
||||||
|
t = cc.next()
|
||||||
|
Visibility.Public
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> Visibility.Default
|
||||||
|
}
|
||||||
|
// val/var?
|
||||||
|
val access = when (t.value) {
|
||||||
|
"val" -> {
|
||||||
|
if (!isClassDeclaration) {
|
||||||
|
cc.restorePos(startPos); return null
|
||||||
|
}
|
||||||
|
t = cc.next()
|
||||||
|
AccessType.Val
|
||||||
|
}
|
||||||
|
|
||||||
|
"var" -> {
|
||||||
|
if (!isClassDeclaration) {
|
||||||
|
cc.restorePos(startPos); return null
|
||||||
|
}
|
||||||
|
t = cc.next()
|
||||||
|
AccessType.Var
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> AccessType.Default
|
||||||
|
}
|
||||||
|
|
||||||
var defaultValue: Statement? = null
|
var defaultValue: Statement? = null
|
||||||
cc.ifNextIs(Token.Type.ASSIGN) {
|
cc.ifNextIs(Token.Type.ASSIGN) {
|
||||||
defaultValue = parseExpression(cc)
|
defaultValue = parseExpression(cc)
|
||||||
@ -438,7 +511,7 @@ class Compiler(
|
|||||||
// type information
|
// type information
|
||||||
val typeInfo = parseTypeDeclaration(cc)
|
val typeInfo = parseTypeDeclaration(cc)
|
||||||
val isEllipsis = cc.skipTokenOfType(Token.Type.ELLIPSIS, isOptional = true)
|
val isEllipsis = cc.skipTokenOfType(Token.Type.ELLIPSIS, isOptional = true)
|
||||||
result += ArgVar(t.value, typeInfo, t.pos, isEllipsis, defaultValue)
|
result += ArgVar(t.value, typeInfo, t.pos, isEllipsis, defaultValue, access, visibility)
|
||||||
|
|
||||||
// important: valid argument list continues with ',' and ends with '->' or ')'
|
// important: valid argument list continues with ',' and ends with '->' or ')'
|
||||||
// otherwise it is not an argument list:
|
// otherwise it is not an argument list:
|
||||||
@ -630,9 +703,36 @@ class Compiler(
|
|||||||
"continue" -> parseContinueStatement(id.pos, cc)
|
"continue" -> parseContinueStatement(id.pos, cc)
|
||||||
"fn", "fun" -> parseFunctionDeclaration(cc)
|
"fn", "fun" -> parseFunctionDeclaration(cc)
|
||||||
"if" -> parseIfStatement(cc)
|
"if" -> parseIfStatement(cc)
|
||||||
|
"class" -> parseClassDeclaration(cc, false)
|
||||||
|
"struct" -> parseClassDeclaration(cc, true)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseClassDeclaration(cc: CompilerContext, isStruct: Boolean): Statement {
|
||||||
|
val nameToken = cc.requireToken(Token.Type.ID)
|
||||||
|
val parsedArgs = parseArgsDeclaration(cc)
|
||||||
|
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
|
||||||
|
val t = cc.next()
|
||||||
|
if (t.type == Token.Type.LBRACE) {
|
||||||
|
// parse body
|
||||||
|
}
|
||||||
|
// create class
|
||||||
|
val className = nameToken.value
|
||||||
|
|
||||||
|
// val constructorCode = statement {
|
||||||
|
// val classContext = copy()
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
val newClass = ObjClass(className, parsedArgs?.args ?: emptyList())
|
||||||
|
// statement {
|
||||||
|
// addConst(nameToken.value, )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun getLabel(cc: CompilerContext, maxDepth: Int = 2): String? {
|
private fun getLabel(cc: CompilerContext, maxDepth: Int = 2): String? {
|
||||||
var cnt = 0
|
var cnt = 0
|
||||||
var found: String? = null
|
var found: String? = null
|
||||||
@ -749,7 +849,7 @@ class Compiler(
|
|||||||
var result: Obj = ObjVoid
|
var result: Obj = ObjVoid
|
||||||
val iVar = ObjInt(0)
|
val iVar = ObjInt(0)
|
||||||
loopVar.value = iVar
|
loopVar.value = iVar
|
||||||
if( catchBreak) {
|
if (catchBreak) {
|
||||||
for (i in start..<end) {
|
for (i in start..<end) {
|
||||||
iVar.value = i.toLong()
|
iVar.value = i.toLong()
|
||||||
try {
|
try {
|
||||||
@ -1185,7 +1285,8 @@ class Compiler(
|
|||||||
Operator.simple(Token.Type.NOTIS, lastPrty) { c, a, b -> ObjBool(!a.isInstanceOf(b)) },
|
Operator.simple(Token.Type.NOTIS, lastPrty) { c, a, b -> ObjBool(!a.isInstanceOf(b)) },
|
||||||
// shuttle <=> 6
|
// shuttle <=> 6
|
||||||
Operator.simple(Token.Type.SHUTTLE, ++lastPrty) { c, a, b ->
|
Operator.simple(Token.Type.SHUTTLE, ++lastPrty) { c, a, b ->
|
||||||
ObjInt(a.compareTo(c, b).toLong()) },
|
ObjInt(a.compareTo(c, b).toLong())
|
||||||
|
},
|
||||||
// bit shifts 7
|
// bit shifts 7
|
||||||
Operator.simple(Token.Type.PLUS, ++lastPrty) { ctx, a, b -> a.plus(ctx, b) },
|
Operator.simple(Token.Type.PLUS, ++lastPrty) { ctx, a, b -> a.plus(ctx, b) },
|
||||||
Operator.simple(Token.Type.MINUS, lastPrty) { ctx, a, b -> a.minus(ctx, b) },
|
Operator.simple(Token.Type.MINUS, lastPrty) { ctx, a, b -> a.minus(ctx, b) },
|
||||||
@ -1213,7 +1314,7 @@ class Compiler(
|
|||||||
/**
|
/**
|
||||||
* The keywords that stop processing of expression term
|
* The keywords that stop processing of expression term
|
||||||
*/
|
*/
|
||||||
val stopKeywords = setOf("break", "continue", "return", "if", "when", "do", "while", "for")
|
val stopKeywords = setOf("break", "continue", "return", "if", "when", "do", "while", "for", "class", "struct")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,11 @@ import net.sergeych.synctools.ProtectedOp
|
|||||||
|
|
||||||
//typealias InstanceMethod = (Context, Obj) -> Obj
|
//typealias InstanceMethod = (Context, Obj) -> Obj
|
||||||
|
|
||||||
data class WithAccess<T>(var value: T, val isMutable: Boolean)
|
data class WithAccess<T>(
|
||||||
|
var value: T,
|
||||||
|
val isMutable: Boolean,
|
||||||
|
val visibility: Compiler.Visibility = Compiler.Visibility.Public
|
||||||
|
)
|
||||||
|
|
||||||
data class Accessor(
|
data class Accessor(
|
||||||
val getter: suspend (Context) -> WithAccess<Obj>,
|
val getter: suspend (Context) -> WithAccess<Obj>,
|
||||||
|
@ -4,8 +4,14 @@ val ObjClassType by lazy { ObjClass("Class") }
|
|||||||
|
|
||||||
class ObjClass(
|
class ObjClass(
|
||||||
val className: String,
|
val className: String,
|
||||||
|
val constructorArgs: List<Compiler.ArgVar> = emptyList(),
|
||||||
vararg val parents: ObjClass,
|
vararg val parents: ObjClass,
|
||||||
) : Obj() {
|
) : Obj() {
|
||||||
|
constructor(
|
||||||
|
className: String,
|
||||||
|
vararg parents: ObjClass,
|
||||||
|
) : this(className, emptyList(), *parents)
|
||||||
|
|
||||||
|
|
||||||
val allParentsSet: Set<ObjClass> = parents.flatMap {
|
val allParentsSet: Set<ObjClass> = parents.flatMap {
|
||||||
listOf(it) + it.allParentsSet
|
listOf(it) + it.allParentsSet
|
||||||
@ -22,7 +28,7 @@ class ObjClass(
|
|||||||
|
|
||||||
// private var initInstanceHandler: (suspend (Context, List<Obj>) -> Obj)? = null
|
// private var initInstanceHandler: (suspend (Context, List<Obj>) -> Obj)? = null
|
||||||
|
|
||||||
// suspend fun newInstance(context: Context, vararg args: Obj): Obj =
|
// suspend fun newInstance(context: Context, vararg args: Obj): Obj =
|
||||||
// initInstanceHandler?.invoke(context, args.toList())
|
// initInstanceHandler?.invoke(context, args.toList())
|
||||||
// ?: context.raiseError("No initInstance handler for $this")
|
// ?: context.raiseError("No initInstance handler for $this")
|
||||||
//
|
//
|
||||||
@ -70,24 +76,26 @@ class ObjClass(
|
|||||||
/**
|
/**
|
||||||
* Abstract class that must provide `iterator` method that returns [ObjIterator] instance.
|
* Abstract class that must provide `iterator` method that returns [ObjIterator] instance.
|
||||||
*/
|
*/
|
||||||
val ObjIterable by lazy { ObjClass("Iterable").apply {
|
val ObjIterable by lazy {
|
||||||
|
ObjClass("Iterable").apply {
|
||||||
|
|
||||||
addFn("toList") {
|
addFn("toList") {
|
||||||
val result = mutableListOf<Obj>()
|
val result = mutableListOf<Obj>()
|
||||||
val iterator = thisObj.invokeInstanceMethod(this, "iterator")
|
val iterator = thisObj.invokeInstanceMethod(this, "iterator")
|
||||||
|
|
||||||
while( iterator.invokeInstanceMethod(this, "hasNext").toBool() )
|
while (iterator.invokeInstanceMethod(this, "hasNext").toBool())
|
||||||
result += iterator.invokeInstanceMethod(this, "next")
|
result += iterator.invokeInstanceMethod(this, "next")
|
||||||
|
|
||||||
|
|
||||||
// val next = iterator.getMemberOrNull("next")!!
|
// val next = iterator.getMemberOrNull("next")!!
|
||||||
// val hasNext = iterator.getMemberOrNull("hasNext")!!
|
// val hasNext = iterator.getMemberOrNull("hasNext")!!
|
||||||
// while( hasNext.invoke(this, iterator).toBool() )
|
// while( hasNext.invoke(this, iterator).toBool() )
|
||||||
// result += next.invoke(this, iterator)
|
// result += next.invoke(this, iterator)
|
||||||
ObjList(result)
|
ObjList(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collection is an iterator with `size`]
|
* Collection is an iterator with `size`]
|
||||||
@ -123,7 +131,7 @@ class ObjArrayIterator(val array: Obj) : Obj() {
|
|||||||
}
|
}
|
||||||
addFn("hasNext") {
|
addFn("hasNext") {
|
||||||
val self = thisAs<ObjArrayIterator>()
|
val self = thisAs<ObjArrayIterator>()
|
||||||
if (self.nextIndex < self.lastIndex) ObjTrue else ObjFalse
|
if (self.nextIndex < self.lastIndex) ObjTrue else ObjFalse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,6 +150,10 @@ val ObjArray by lazy {
|
|||||||
addFn("iterator") {
|
addFn("iterator") {
|
||||||
ObjArrayIterator(thisObj).also { it.init(this) }
|
ObjArrayIterator(thisObj).also { it.init(this) }
|
||||||
}
|
}
|
||||||
addFn("isample") { "ok".toObj()}
|
addFn("isample") { "ok".toObj() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ObjInstance(override val objClass: ObjClass): Obj() {
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1167,6 +1167,7 @@ class ScriptTest {
|
|||||||
eval(
|
eval(
|
||||||
"""
|
"""
|
||||||
fun mapValues(iterable, transform) {
|
fun mapValues(iterable, transform) {
|
||||||
|
println("start: ", transform)
|
||||||
var result = []
|
var result = []
|
||||||
for( x in iterable ) result += transform(x)
|
for( x in iterable ) result += transform(x)
|
||||||
}
|
}
|
||||||
@ -1238,6 +1239,17 @@ class ScriptTest {
|
|||||||
assert( 5 <=> 7 < 0 )
|
assert( 5 <=> 7 < 0 )
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun simpleClassDelaration() = runTest {
|
||||||
|
eval( """
|
||||||
|
// class Vec2(x,y)
|
||||||
|
// println(Vec2(1,2)::class)
|
||||||
|
println("---------------------")
|
||||||
|
println(Int::class)
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,6 +8,7 @@ import net.sergeych.lyng.ObjVoid
|
|||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Files.readAllLines
|
import java.nio.file.Files.readAllLines
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
import kotlin.io.path.absolutePathString
|
||||||
import kotlin.io.path.extension
|
import kotlin.io.path.extension
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -41,7 +42,8 @@ data class DocTest(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "DocTest:$fileName:${line + 1}..${line + sourceLines.size}"
|
val absPath = Paths.get(fileName).absolutePathString()
|
||||||
|
return "DocTest: $absPath:${line + 1}"
|
||||||
}
|
}
|
||||||
|
|
||||||
val detailedString by lazy {
|
val detailedString by lazy {
|
||||||
@ -190,7 +192,7 @@ suspend fun DocTest.test(context: Context = Context()) {
|
|||||||
if (error != null || expectedOutput != collectedOutput.toString() ||
|
if (error != null || expectedOutput != collectedOutput.toString() ||
|
||||||
expectedResult != result
|
expectedResult != result
|
||||||
) {
|
) {
|
||||||
println("Test failed: ${this.detailedString}")
|
System.err.println("\nfailed: ${this.detailedString}")
|
||||||
}
|
}
|
||||||
error?.let {
|
error?.let {
|
||||||
fail("test failed", it)
|
fail("test failed", it)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user