Compare commits
No commits in common. "9a08da0dfde8db1df81f3b0c19bf3afe806ac29d" and "4dc73b91c2b7ee954cb2b6732a5977910c7570dd" have entirely different histories.
9a08da0dfd
...
4dc73b91c2
@ -1,98 +0,0 @@
|
|||||||
package net.sergeych.lyng
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of argument declarations in the __definition__ of the lambda, class constructor,
|
|
||||||
* function, etc. It is created by [Compiler.parseArgsDeclaration]
|
|
||||||
*/
|
|
||||||
data class ArgsDeclaration(val args: List<Item>, val endTokenType: Token.Type) {
|
|
||||||
init {
|
|
||||||
val i = args.count { it.isEllipsis }
|
|
||||||
if (i > 1) throw ScriptError(args[i].pos, "there can be only one argument")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* parse args and create local vars in a given context
|
|
||||||
*/
|
|
||||||
suspend fun assignToContext(
|
|
||||||
context: Context,
|
|
||||||
fromArgs: Arguments = context.args,
|
|
||||||
defaultAccessType: Compiler.AccessType = Compiler.AccessType.Var
|
|
||||||
) {
|
|
||||||
fun assign(a: Item, value: Obj) {
|
|
||||||
context.addItem(a.name, (a.accessType ?: defaultAccessType).isMutable, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun processHead(index: Int): Int {
|
|
||||||
var i = index
|
|
||||||
while (i != args.size) {
|
|
||||||
val a = args[i]
|
|
||||||
if (a.isEllipsis) break
|
|
||||||
val value = when {
|
|
||||||
i < fromArgs.size -> fromArgs[i]
|
|
||||||
a.defaultValue != null -> a.defaultValue.execute(context)
|
|
||||||
else -> context.raiseArgumentError("too few arguments for the call")
|
|
||||||
}
|
|
||||||
assign(a, value)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun processTail(index: Int): Int {
|
|
||||||
var i = args.size - 1
|
|
||||||
var j = fromArgs.size - 1
|
|
||||||
while (i > index) {
|
|
||||||
val a = args[i]
|
|
||||||
if (a.isEllipsis) break
|
|
||||||
val value = when {
|
|
||||||
j >= index -> {
|
|
||||||
fromArgs[j--]
|
|
||||||
}
|
|
||||||
|
|
||||||
a.defaultValue != null -> a.defaultValue.execute(context)
|
|
||||||
else -> context.raiseArgumentError("too few arguments for the call")
|
|
||||||
}
|
|
||||||
assign(a, value)
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
fun processEllipsis(index: Int, toFromIndex: Int) {
|
|
||||||
val a = args[index]
|
|
||||||
val l = if (index > toFromIndex) ObjList()
|
|
||||||
else ObjList( fromArgs.values.subList(index, toFromIndex+1).toMutableList())
|
|
||||||
assign(a, l)
|
|
||||||
}
|
|
||||||
|
|
||||||
val leftIndex = processHead(0)
|
|
||||||
if (leftIndex < args.size) {
|
|
||||||
val end = processTail(leftIndex)
|
|
||||||
processEllipsis(leftIndex, end)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if( leftIndex < fromArgs.size)
|
|
||||||
context.raiseArgumentError("too many arguments for the call")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Single argument declaration descriptor.
|
|
||||||
*
|
|
||||||
* @param defaultValue default value, if set, can't be an [Obj] as it can depend on the call site, call args, etc.
|
|
||||||
* If not null, could be executed on __caller context__ only.
|
|
||||||
*/
|
|
||||||
data class Item(
|
|
||||||
val name: String,
|
|
||||||
val type: TypeDecl = TypeDecl.Obj,
|
|
||||||
val pos: Pos = Pos.builtIn,
|
|
||||||
val isEllipsis: Boolean = false,
|
|
||||||
/**
|
|
||||||
* Default value, if set, can't be an [Obj] as it can depend on the call site, call args, etc.
|
|
||||||
* So it is a [Statement] that must be executed on __caller context__.
|
|
||||||
*/
|
|
||||||
val defaultValue: Statement? = null,
|
|
||||||
val accessType: Compiler.AccessType? = null,
|
|
||||||
val visibility: Compiler.Visibility? = null,
|
|
||||||
)
|
|
||||||
}
|
|
@ -33,7 +33,6 @@ data class Arguments(val list: List<Info>) : Iterable<Obj> {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY = Arguments(emptyList())
|
val EMPTY = Arguments(emptyList())
|
||||||
fun from(values: Collection<Obj>) = Arguments(values.map { Info(it, Pos.UNKNOWN) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun iterator(): Iterator<Obj> {
|
override fun iterator(): Iterator<Obj> {
|
||||||
|
@ -347,8 +347,22 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
context.addItem("it", false, itValue)
|
context.addItem("it", false, itValue)
|
||||||
} else {
|
} else {
|
||||||
// assign vars as declared the standard way
|
// assign vars as declared
|
||||||
argsDeclaration.assignToContext(context, defaultAccessType = AccessType.Val)
|
if (args.size != argsDeclaration.args.size && !argsDeclaration.args.last().isEllipsis)
|
||||||
|
raiseArgumentError("Too many arguments : called with ${args.size}, lambda accepts only ${argsDeclaration.args.size}")
|
||||||
|
for ((n, a) in argsDeclaration.args.withIndex()) {
|
||||||
|
if (n >= args.size) {
|
||||||
|
if (a.initialValue != null)
|
||||||
|
context.addItem(a.name, false, a.initialValue.execute(context))
|
||||||
|
else throw ScriptError(a.pos, "argument $n is out of scope")
|
||||||
|
} else {
|
||||||
|
val value = if (a.isEllipsis) {
|
||||||
|
ObjList(args.values.subList(n, args.values.size).toMutableList())
|
||||||
|
} else
|
||||||
|
args[n]
|
||||||
|
context.addItem(a.name, false, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
body.execute(context)
|
body.execute(context)
|
||||||
}
|
}
|
||||||
@ -397,12 +411,29 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class AccessType(val isMutable: Boolean) {
|
enum class AccessType {
|
||||||
Val(false), Var(true), Initialization(false)
|
Val, Var, Default
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Visibility {
|
enum class Visibility {
|
||||||
Public, Private, Protected, Internal
|
Default, Public, Private, Protected, Internal
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ArgVar(
|
||||||
|
val name: String,
|
||||||
|
val type: TypeDecl = TypeDecl.Obj,
|
||||||
|
val pos: Pos,
|
||||||
|
val isEllipsis: Boolean,
|
||||||
|
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) {
|
||||||
|
init {
|
||||||
|
val i = args.indexOfFirst { it.isEllipsis }
|
||||||
|
if (i >= 0 && i != args.lastIndex) throw ScriptError(args[i].pos, "ellipsis argument must be last")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -410,18 +441,13 @@ class Compiler(
|
|||||||
* @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, isClassDeclaration: Boolean = false): ArgsDeclaration? {
|
private fun parseArgsDeclaration(cc: CompilerContext, isClassDeclaration: Boolean = false): ArgsDeclaration? {
|
||||||
val result = mutableListOf<ArgsDeclaration.Item>()
|
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) {
|
||||||
var t = cc.next()
|
var t = cc.next()
|
||||||
when (t.type) {
|
when (t.type) {
|
||||||
Token.Type.RPAREN, Token.Type.ARROW -> {
|
|
||||||
// empty list?
|
|
||||||
endTokenType = t.type
|
|
||||||
}
|
|
||||||
|
|
||||||
Token.Type.NEWLINE -> {}
|
Token.Type.NEWLINE -> {}
|
||||||
Token.Type.ID -> {
|
Token.Type.ID -> {
|
||||||
// visibility
|
// visibility
|
||||||
@ -458,7 +484,7 @@ class Compiler(
|
|||||||
Visibility.Public
|
Visibility.Public
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> null
|
else -> Visibility.Default
|
||||||
}
|
}
|
||||||
// val/var?
|
// val/var?
|
||||||
val access = when (t.value) {
|
val access = when (t.value) {
|
||||||
@ -478,7 +504,7 @@ class Compiler(
|
|||||||
AccessType.Var
|
AccessType.Var
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> null
|
else -> AccessType.Default
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultValue: Statement? = null
|
var defaultValue: Statement? = null
|
||||||
@ -488,15 +514,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 += ArgsDeclaration.Item(
|
result += ArgVar(t.value, typeInfo, t.pos, isEllipsis, defaultValue, access, visibility)
|
||||||
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:
|
||||||
@ -696,45 +714,24 @@ class Compiler(
|
|||||||
private fun parseClassDeclaration(cc: CompilerContext, isStruct: Boolean): Statement {
|
private fun parseClassDeclaration(cc: CompilerContext, isStruct: Boolean): Statement {
|
||||||
val nameToken = cc.requireToken(Token.Type.ID)
|
val nameToken = cc.requireToken(Token.Type.ID)
|
||||||
val parsedArgs = parseArgsDeclaration(cc)
|
val parsedArgs = parseArgsDeclaration(cc)
|
||||||
|
|
||||||
if( parsedArgs != null && parsedArgs.endTokenType != Token.Type.RPAREN)
|
|
||||||
throw ScriptError(nameToken.pos, "Bad class declaration: expected ')' at the end of the primary constructor")
|
|
||||||
|
|
||||||
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
|
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
|
||||||
val t = cc.next()
|
val t = cc.next()
|
||||||
|
if (t.type == Token.Type.LBRACE) {
|
||||||
var extraInit: Statement? = null
|
|
||||||
val bodyInit: Statement? = if (t.type == Token.Type.LBRACE) {
|
|
||||||
// parse body
|
// parse body
|
||||||
TODO("parse body")
|
}
|
||||||
} else null
|
|
||||||
|
|
||||||
// create class
|
// create class
|
||||||
val className = nameToken.value
|
val className = nameToken.value
|
||||||
lateinit var classContext: Context
|
|
||||||
|
|
||||||
val defaultAccess = if (isStruct) AccessType.Var else AccessType.Initialization
|
// val constructorCode = statement {
|
||||||
val defaultVisibility = Visibility.Public
|
// val classContext = copy()
|
||||||
|
// }
|
||||||
|
|
||||||
// create instance constructor
|
|
||||||
// create custom objClass with all fields and instance constructor
|
|
||||||
|
|
||||||
val constructorCode = statement {
|
val newClass = ObjClass(className, parsedArgs?.args ?: emptyList())
|
||||||
// constructor code is registered with class instance and is called over
|
// statement {
|
||||||
// new `thisObj` already set by class to ObjInstance
|
// addConst(nameToken.value, )
|
||||||
thisObj as ObjInstance
|
// }
|
||||||
// the context now is a "class creation context", we must use its args to initialize
|
// }
|
||||||
// fields. Note that 'this' is already set by class
|
|
||||||
// parsedArgs?.let { pa ->
|
|
||||||
// pa.extractArgs { (def, value) ->
|
|
||||||
// val access = def.accessType ?: defaultAccess
|
|
||||||
// val visibility = def.visibility ?: defaultVisibility
|
|
||||||
// addItem(def.name, access.isMutable, value)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
thisObj
|
|
||||||
|
|
||||||
}
|
|
||||||
TODO()
|
TODO()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1051,6 +1048,12 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class FnParamDef(
|
||||||
|
val name: String,
|
||||||
|
val pos: Pos,
|
||||||
|
val defaultValue: Statement? = null
|
||||||
|
)
|
||||||
|
|
||||||
private fun parseFunctionDeclaration(tokens: CompilerContext): Statement {
|
private fun parseFunctionDeclaration(tokens: CompilerContext): Statement {
|
||||||
var t = tokens.next()
|
var t = tokens.next()
|
||||||
val start = t.pos
|
val start = t.pos
|
||||||
@ -1061,10 +1064,27 @@ class Compiler(
|
|||||||
t = tokens.next()
|
t = tokens.next()
|
||||||
if (t.type != Token.Type.LPAREN)
|
if (t.type != Token.Type.LPAREN)
|
||||||
throw ScriptError(t.pos, "Bad function definition: expected '(' after 'fn ${name}'")
|
throw ScriptError(t.pos, "Bad function definition: expected '(' after 'fn ${name}'")
|
||||||
|
val params = mutableListOf<FnParamDef>()
|
||||||
val argsDeclaration = parseArgsDeclaration(tokens)
|
var defaultListStarted = false
|
||||||
if( argsDeclaration == null || argsDeclaration.endTokenType != Token.Type.RPAREN)
|
do {
|
||||||
throw ScriptError(t.pos, "Bad function definition: expected valid argument declaration or () after 'fn ${name}'")
|
t = tokens.next()
|
||||||
|
if (t.type == Token.Type.RPAREN)
|
||||||
|
break
|
||||||
|
if (t.type != Token.Type.ID)
|
||||||
|
throw ScriptError(t.pos, "Expected identifier after '('")
|
||||||
|
val n = tokens.next()
|
||||||
|
val defaultValue = if (n.type == Token.Type.ASSIGN) {
|
||||||
|
parseExpression(tokens)?.also { defaultListStarted = true }
|
||||||
|
?: throw ScriptError(n.pos, "Expected initialization expression")
|
||||||
|
} else {
|
||||||
|
if (defaultListStarted)
|
||||||
|
throw ScriptError(n.pos, "requires default value too")
|
||||||
|
if (n.type != Token.Type.COMMA)
|
||||||
|
tokens.previous()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
params.add(FnParamDef(t.value, t.pos, defaultValue))
|
||||||
|
} while (true)
|
||||||
|
|
||||||
// Here we should be at open body
|
// Here we should be at open body
|
||||||
val fnStatements = parseBlock(tokens)
|
val fnStatements = parseBlock(tokens)
|
||||||
@ -1078,7 +1098,21 @@ class Compiler(
|
|||||||
val context = closure?.copy() ?: callerContext.raiseError("bug: closure not set")
|
val context = closure?.copy() ?: callerContext.raiseError("bug: closure not set")
|
||||||
|
|
||||||
// load params from caller context
|
// load params from caller context
|
||||||
argsDeclaration.assignToContext(context, callerContext.args, defaultAccessType = AccessType.Val)
|
for ((i, d) in params.withIndex()) {
|
||||||
|
if (i < callerContext.args.size)
|
||||||
|
context.addItem(d.name, false, callerContext.args.list[i].value)
|
||||||
|
else
|
||||||
|
context.addItem(
|
||||||
|
d.name,
|
||||||
|
false,
|
||||||
|
d.defaultValue?.execute(context)
|
||||||
|
?: throw ScriptError(
|
||||||
|
context.pos,
|
||||||
|
"missing required argument #${1 + i}: ${d.name}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// save closure
|
||||||
fnStatements.execute(context)
|
fnStatements.execute(context)
|
||||||
}
|
}
|
||||||
return statement(start) { context ->
|
return statement(start) { context ->
|
||||||
|
@ -65,9 +65,6 @@ class Context(
|
|||||||
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY,newThisObj: Obj? = null): Context =
|
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY,newThisObj: Obj? = null): Context =
|
||||||
Context(this, args, pos, newThisObj ?: thisObj)
|
Context(this, args, pos, newThisObj ?: thisObj)
|
||||||
|
|
||||||
fun copy(args: Arguments = Arguments.EMPTY,newThisObj: Obj? = null): Context =
|
|
||||||
Context(this, args, pos, newThisObj ?: thisObj)
|
|
||||||
|
|
||||||
fun copy() = Context(this, args, pos, thisObj)
|
fun copy() = Context(this, args, pos, thisObj)
|
||||||
|
|
||||||
fun addItem(name: String, isMutable: Boolean, value: Obj?): StoredObj {
|
fun addItem(name: String, isMutable: Boolean, value: Obj?): StoredObj {
|
||||||
|
@ -4,7 +4,7 @@ val ObjClassType by lazy { ObjClass("Class") }
|
|||||||
|
|
||||||
class ObjClass(
|
class ObjClass(
|
||||||
val className: String,
|
val className: String,
|
||||||
val constructorArgs: List<ArgsDeclaration.Item> = emptyList(),
|
val constructorArgs: List<Compiler.ArgVar> = emptyList(),
|
||||||
vararg val parents: ObjClass,
|
vararg val parents: ObjClass,
|
||||||
) : Obj() {
|
) : Obj() {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
class ObjList(val list: MutableList<Obj>) : Obj() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
for (p in objClass.parents)
|
for (p in objClass.parents)
|
||||||
|
@ -16,7 +16,6 @@ data class Pos(val source: Source, val line: Int, val column: Int) {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val builtIn = Pos(Source.builtIn, 0, 0)
|
val builtIn = Pos(Source.builtIn, 0, 0)
|
||||||
val UNKNOWN = Pos(Source.UNKNOWN, -1, -1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -119,13 +119,6 @@ class Script(
|
|||||||
raiseError(ObjAssertionError(this,"Assertion failed"))
|
raiseError(ObjAssertionError(this,"Assertion failed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
addVoidFn("assertEquals") {
|
|
||||||
val a = requiredArg<Obj>(0)
|
|
||||||
val b = requiredArg<Obj>(1)
|
|
||||||
if( a.compareTo(this, b) != 0 )
|
|
||||||
raiseError(ObjAssertionError(this,"Assertion failed: ${a.inspect()} == ${b.inspect()}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
addVoidFn("delay") {
|
addVoidFn("delay") {
|
||||||
delay((this.args.firstAndOnly().toDouble()/1000.0).roundToLong())
|
delay((this.args.firstAndOnly().toDouble()/1000.0).roundToLong())
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ class Source(val fileName: String, text: String) {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val builtIn: Source by lazy { Source("built-in", "") }
|
val builtIn: Source by lazy { Source("built-in", "") }
|
||||||
val UNKNOWN: Source by lazy { Source("UNKNOWN", "") }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val startPos: Pos = Pos(this, 0, 0)
|
val startPos: Pos = Pos(this, 0, 0)
|
||||||
|
@ -486,136 +486,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAssignArgumentsNoEllipsis() = runTest {
|
|
||||||
// equal args, no ellipsis, no defaults, ok
|
|
||||||
val ttEnd = Token.Type.RBRACE
|
|
||||||
var pa = ArgsDeclaration(listOf(
|
|
||||||
ArgsDeclaration.Item("a"),
|
|
||||||
ArgsDeclaration.Item("b"),
|
|
||||||
ArgsDeclaration.Item("c"),
|
|
||||||
), ttEnd)
|
|
||||||
var c = Context(pos = Pos.builtIn, args = Arguments.from(listOf(1,2,3).map { it.toObj() }))
|
|
||||||
pa.assignToContext(c)
|
|
||||||
assertEquals( ObjInt(1), c["a"]?.value)
|
|
||||||
assertEquals( ObjInt(2), c["b"]?.value)
|
|
||||||
assertEquals( ObjInt(3), c["c"]?.value)
|
|
||||||
// less args: error
|
|
||||||
c = Context(pos = Pos.builtIn, args = Arguments.from(listOf(1,2).map { it.toObj() }))
|
|
||||||
assertFailsWith<ScriptError> {
|
|
||||||
pa.assignToContext(c)
|
|
||||||
}
|
|
||||||
// less args, no ellipsis, defaults, ok
|
|
||||||
pa = ArgsDeclaration(listOf(
|
|
||||||
ArgsDeclaration.Item("a"),
|
|
||||||
ArgsDeclaration.Item("b"),
|
|
||||||
ArgsDeclaration.Item("c", defaultValue = statement { ObjInt(100) }),
|
|
||||||
), ttEnd)
|
|
||||||
pa.assignToContext(c)
|
|
||||||
assertEquals( ObjInt(1), c["a"]?.value)
|
|
||||||
assertEquals( ObjInt(2), c["b"]?.value)
|
|
||||||
assertEquals( ObjInt(100), c["c"]?.value)
|
|
||||||
// enough args. default value is ignored:
|
|
||||||
c = Context(pos = Pos.builtIn, args = Arguments.from(listOf(10, 2, 5).map { it.toObj() }))
|
|
||||||
pa.assignToContext(c)
|
|
||||||
assertEquals( ObjInt(10), c["a"]?.value)
|
|
||||||
assertEquals( ObjInt(2), c["b"]?.value)
|
|
||||||
assertEquals( ObjInt(5), c["c"]?.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAssignArgumentsEndEllipsis() = runTest {
|
|
||||||
// equal args,
|
|
||||||
// less args, no ellipsis, defaults, ok
|
|
||||||
val ttEnd = Token.Type.RBRACE
|
|
||||||
val pa = ArgsDeclaration(listOf(
|
|
||||||
ArgsDeclaration.Item("a"),
|
|
||||||
ArgsDeclaration.Item("b", isEllipsis = true),
|
|
||||||
), ttEnd)
|
|
||||||
var c = Context(args = Arguments.from(listOf(1,2,3).map { it.toObj() }))
|
|
||||||
pa.assignToContext(c)
|
|
||||||
c.eval("assert( a == 1 ); println(b)")
|
|
||||||
c.eval("assert( b == [2,3] )")
|
|
||||||
|
|
||||||
c = Context(args = Arguments.from(listOf(1,2).map { it.toObj() }))
|
|
||||||
pa.assignToContext(c)
|
|
||||||
c.eval("assertEquals( a, 1 ); println(b)")
|
|
||||||
c.eval("assertEquals( b, [2] )")
|
|
||||||
|
|
||||||
c = Context(args = Arguments.from(listOf(1).map { it.toObj() }))
|
|
||||||
pa.assignToContext(c)
|
|
||||||
c.eval("assert( a == 1 ); println(b)")
|
|
||||||
c.eval("assert( b == [] )")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAssignArgumentsStartEllipsis() = runTest {
|
|
||||||
val ttEnd = Token.Type.RBRACE
|
|
||||||
val pa = ArgsDeclaration(listOf(
|
|
||||||
ArgsDeclaration.Item("a", isEllipsis = true),
|
|
||||||
ArgsDeclaration.Item("b"),
|
|
||||||
ArgsDeclaration.Item("c"),
|
|
||||||
), ttEnd)
|
|
||||||
var c = Context(args = Arguments.from(listOf(0,1,2,3).map { it.toObj() }))
|
|
||||||
pa.assignToContext(c)
|
|
||||||
c.eval("assertEquals( a,[0,1] )")
|
|
||||||
c.eval("assertEquals( b, 2 )")
|
|
||||||
c.eval("assertEquals( c, 3 )")
|
|
||||||
|
|
||||||
c = Context(args = Arguments.from(listOf(1,2,3).map { it.toObj() }))
|
|
||||||
pa.assignToContext(c)
|
|
||||||
c.eval("assertEquals( a,[1] )")
|
|
||||||
c.eval("assertEquals( b, 2 )")
|
|
||||||
c.eval("assertEquals( c, 3 )")
|
|
||||||
|
|
||||||
c = Context(args = Arguments.from(listOf(2,3).map { it.toObj() }))
|
|
||||||
pa.assignToContext(c)
|
|
||||||
c.eval("assertEquals( a,[] )")
|
|
||||||
c.eval("assertEquals( b, 2 )")
|
|
||||||
c.eval("assertEquals( c, 3 )")
|
|
||||||
|
|
||||||
c = Context(args = Arguments.from(listOf(3).map { it.toObj() }))
|
|
||||||
assertFailsWith<ExecutionError> {
|
|
||||||
pa.assignToContext(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAssignArgumentsmiddleEllipsis() = runTest {
|
|
||||||
val ttEnd = Token.Type.RBRACE
|
|
||||||
val pa = ArgsDeclaration(listOf(
|
|
||||||
ArgsDeclaration.Item("i"),
|
|
||||||
ArgsDeclaration.Item("a", isEllipsis = true),
|
|
||||||
ArgsDeclaration.Item("b"),
|
|
||||||
ArgsDeclaration.Item("c"),
|
|
||||||
), ttEnd)
|
|
||||||
var c = Context(args = Arguments.from(listOf(-1,0,1,2,3).map { it.toObj() }))
|
|
||||||
pa.assignToContext(c)
|
|
||||||
c.eval("assertEquals( i, -1 )")
|
|
||||||
c.eval("assertEquals( a,[0,1] )")
|
|
||||||
c.eval("assertEquals( b, 2 )")
|
|
||||||
c.eval("assertEquals( c, 3 )")
|
|
||||||
|
|
||||||
c = Context(args = Arguments.from(listOf(0, 1,2,3).map { it.toObj() }))
|
|
||||||
pa.assignToContext(c)
|
|
||||||
c.eval("assertEquals( i, 0 )")
|
|
||||||
c.eval("assertEquals( a,[1] )")
|
|
||||||
c.eval("assertEquals( b, 2 )")
|
|
||||||
c.eval("assertEquals( c, 3 )")
|
|
||||||
|
|
||||||
c = Context(args = Arguments.from(listOf(1,2,3).map { it.toObj() }))
|
|
||||||
pa.assignToContext(c)
|
|
||||||
c.eval("assertEquals( i, 1)")
|
|
||||||
c.eval("assertEquals( a,[] )")
|
|
||||||
c.eval("assertEquals( b, 2 )")
|
|
||||||
c.eval("assertEquals( c, 3 )")
|
|
||||||
|
|
||||||
c = Context(args = Arguments.from(listOf(2,3).map { it.toObj() }))
|
|
||||||
assertFailsWith<ExecutionError> {
|
|
||||||
pa.assignToContext(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testWhileBlockIsolation1() = runTest {
|
fun testWhileBlockIsolation1() = runTest {
|
||||||
eval(
|
eval(
|
||||||
@ -1306,17 +1176,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testNewFnParser() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
fun f1(a,b) { a + b }
|
|
||||||
println(f1(1,2))
|
|
||||||
assertEquals( 7, f1(3,4) )
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSpoilArgsBug() = runTest {
|
fun testSpoilArgsBug() = runTest {
|
||||||
eval(
|
eval(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user