fix #53 chained calls and multiline expressions
This commit is contained in:
		
							parent
							
								
									cb333ab6bd
								
							
						
					
					
						commit
						cad6ba936d
					
				@ -138,6 +138,7 @@ publishing {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//mavenPublishing {
 | 
			
		||||
//    publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
@ -211,11 +211,32 @@ class Compiler(
 | 
			
		||||
    private suspend fun parseTerm(): Accessor? {
 | 
			
		||||
        var operand: Accessor? = null
 | 
			
		||||
 | 
			
		||||
        // newlines _before_
 | 
			
		||||
        cc.skipWsTokens()
 | 
			
		||||
 | 
			
		||||
        while (true) {
 | 
			
		||||
            val t = cc.next()
 | 
			
		||||
            val startPos = t.pos
 | 
			
		||||
            when (t.type) {
 | 
			
		||||
                Token.Type.NEWLINE, Token.Type.SEMICOLON, Token.Type.EOF, Token.Type.RBRACE, Token.Type.COMMA -> {
 | 
			
		||||
//                Token.Type.NEWLINE, Token.Type.SINLGE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT-> {
 | 
			
		||||
//                    continue
 | 
			
		||||
//                }
 | 
			
		||||
 | 
			
		||||
                // very special case chained calls: call()<NL>.call2 {}.call3()
 | 
			
		||||
                Token.Type.NEWLINE -> {
 | 
			
		||||
                    val saved = cc.savePos()
 | 
			
		||||
                    if( cc.peekNextNonWhitespace().type == Token.Type.DOT) {
 | 
			
		||||
                        // chained call continue from it
 | 
			
		||||
                        continue
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // restore position and stop parsing as a term:
 | 
			
		||||
                        cc.restorePos(saved)
 | 
			
		||||
                        cc.previous()
 | 
			
		||||
                        return operand
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Token.Type.SEMICOLON, Token.Type.EOF, Token.Type.RBRACE, Token.Type.COMMA -> {
 | 
			
		||||
                    cc.previous()
 | 
			
		||||
                    return operand
 | 
			
		||||
                }
 | 
			
		||||
@ -468,7 +489,13 @@ class Compiler(
 | 
			
		||||
                    // range operator
 | 
			
		||||
                    val isEndInclusive = t.type == Token.Type.DOTDOT
 | 
			
		||||
                    val left = operand
 | 
			
		||||
                    val right = parseExpression()
 | 
			
		||||
                    // if it is an open end range, then the end of line could be here that we do not want
 | 
			
		||||
                    // to skip in parseExpression:
 | 
			
		||||
                    val current = cc.current()
 | 
			
		||||
                    val right = if( current.type == Token.Type.NEWLINE || current.type == Token.Type.SINLGE_LINE_COMMENT)
 | 
			
		||||
                        null
 | 
			
		||||
                    else
 | 
			
		||||
                        parseExpression()
 | 
			
		||||
                    operand = Accessor {
 | 
			
		||||
                        ObjRange(
 | 
			
		||||
                            left?.getter?.invoke(it)?.value ?: ObjNull,
 | 
			
		||||
@ -955,7 +982,7 @@ class Compiler(
 | 
			
		||||
 | 
			
		||||
    private suspend fun parseWhenStatement(): Statement {
 | 
			
		||||
        // has a value, when(value) ?
 | 
			
		||||
        var t = cc.skipWsTokens()
 | 
			
		||||
        var t = cc.nextNonWhitespace()
 | 
			
		||||
        return if (t.type == Token.Type.LPAREN) {
 | 
			
		||||
            // when(value)
 | 
			
		||||
            val value = parseStatement() ?: throw ScriptError(cc.currentPos(), "when(value) expected")
 | 
			
		||||
@ -978,7 +1005,7 @@ class Compiler(
 | 
			
		||||
 | 
			
		||||
                // loop conditions
 | 
			
		||||
                while (true) {
 | 
			
		||||
                    t = cc.skipWsTokens()
 | 
			
		||||
                    t = cc.nextNonWhitespace()
 | 
			
		||||
 | 
			
		||||
                    when (t.type) {
 | 
			
		||||
                        Token.Type.IN,
 | 
			
		||||
@ -1191,11 +1218,11 @@ class Compiler(
 | 
			
		||||
        cc.skipTokenOfType(Token.Type.LBRACE)
 | 
			
		||||
 | 
			
		||||
        do {
 | 
			
		||||
            val t = cc.skipWsTokens()
 | 
			
		||||
            val t = cc.nextNonWhitespace()
 | 
			
		||||
            when (t.type) {
 | 
			
		||||
                Token.Type.ID -> {
 | 
			
		||||
                    names += t.value
 | 
			
		||||
                    val t1 = cc.skipWsTokens()
 | 
			
		||||
                    val t1 = cc.nextNonWhitespace()
 | 
			
		||||
                    when (t1.type) {
 | 
			
		||||
                        Token.Type.COMMA ->
 | 
			
		||||
                            continue
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,6 @@ class CompilerContext(val tokens: List<Token>) {
 | 
			
		||||
    fun next() =
 | 
			
		||||
        if (currentIndex < tokens.size) tokens[currentIndex++]
 | 
			
		||||
        else Token("", tokens.last().pos, Token.Type.EOF)
 | 
			
		||||
//        throw IllegalStateException("No more tokens")
 | 
			
		||||
 | 
			
		||||
    fun previous() = if (!hasPrevious()) throw IllegalStateException("No previous token") else tokens[--currentIndex]
 | 
			
		||||
 | 
			
		||||
@ -131,6 +130,28 @@ class CompilerContext(val tokens: List<Token>) {
 | 
			
		||||
        previous()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun nextNonWhitespace(): Token {
 | 
			
		||||
        while (true) {
 | 
			
		||||
            val t = next()
 | 
			
		||||
            if (t.type !in wstokens) return t
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Find next non-whitespace token and return it. The token is not extracted,
 | 
			
		||||
     * is will be returned on [next] call.
 | 
			
		||||
     * @return next non-whitespace token without extracting it from tokens list
 | 
			
		||||
     */
 | 
			
		||||
    fun peekNextNonWhitespace(): Token {
 | 
			
		||||
        while (true) {
 | 
			
		||||
            val t = next()
 | 
			
		||||
            if (t.type !in wstokens) {
 | 
			
		||||
                previous()
 | 
			
		||||
                return t
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    inline fun ifNextIs(typeId: Token.Type, f: (Token) -> Unit): Boolean {
 | 
			
		||||
        val t = next()
 | 
			
		||||
@ -207,7 +228,7 @@ class CompilerContext(val tokens: List<Token>) {
 | 
			
		||||
        while (current().type in wstokens) {
 | 
			
		||||
            next()
 | 
			
		||||
        }
 | 
			
		||||
        return next()
 | 
			
		||||
        return current()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
@ -436,6 +436,9 @@ object ObjNull : Obj() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * TODO: get rid of it. Maybe we ise some Lyng inheritance instead
 | 
			
		||||
 */
 | 
			
		||||
interface Numeric {
 | 
			
		||||
    val longValue: Long
 | 
			
		||||
    val doubleValue: Double
 | 
			
		||||
 | 
			
		||||
@ -1184,6 +1184,35 @@ class ScriptTest {
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testOpenEndRanges2() = runTest {
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
            var r = 5..; var r2 = 6..
 | 
			
		||||
            val r3 = 7.. // open end
 | 
			
		||||
            assert( r::class == Range)
 | 
			
		||||
            assert( r.end == null)
 | 
			
		||||
            assert( r.start == 5)
 | 
			
		||||
            
 | 
			
		||||
            assert( r3::class == Range)
 | 
			
		||||
            assertEquals( r3.end, null)
 | 
			
		||||
            assert( r3.start == 7)
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testOpenEndRanges3() = runTest {
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
            val r3 = 7.. // open end with comment
 | 
			
		||||
            assert( r3::class == Range)
 | 
			
		||||
            assertEquals( r3.end, null)
 | 
			
		||||
            assert( r3.start == 7)
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testCharacterRange() = runTest {
 | 
			
		||||
        eval(
 | 
			
		||||
@ -2991,5 +3020,20 @@ class ScriptTest {
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testNewlinesAnsCommentsInExpressions() = runTest {
 | 
			
		||||
        assertEquals( 2, (Scope().eval("""
 | 
			
		||||
            val e = 1 + 4 -
 | 
			
		||||
                3
 | 
			
		||||
        """.trimIndent())).toInt())
 | 
			
		||||
 | 
			
		||||
        eval("""
 | 
			
		||||
                val x = [1,2,3]
 | 
			
		||||
                    .map { it * 10 }
 | 
			
		||||
                    .map { it + 1 }
 | 
			
		||||
                assertEquals( [11,21,31], x)
 | 
			
		||||
            """.trimIndent())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user