fix #53 chained calls and multiline expressions
This commit is contained in:
		
							parent
							
								
									cb333ab6bd
								
							
						
					
					
						commit
						cad6ba936d
					
				@ -138,6 +138,7 @@ publishing {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//mavenPublishing {
 | 
					//mavenPublishing {
 | 
				
			||||||
//    publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
 | 
					//    publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
 | 
				
			|||||||
@ -211,11 +211,32 @@ class Compiler(
 | 
				
			|||||||
    private suspend fun parseTerm(): Accessor? {
 | 
					    private suspend fun parseTerm(): Accessor? {
 | 
				
			||||||
        var operand: Accessor? = null
 | 
					        var operand: Accessor? = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // newlines _before_
 | 
				
			||||||
 | 
					        cc.skipWsTokens()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while (true) {
 | 
					        while (true) {
 | 
				
			||||||
            val t = cc.next()
 | 
					            val t = cc.next()
 | 
				
			||||||
            val startPos = t.pos
 | 
					            val startPos = t.pos
 | 
				
			||||||
            when (t.type) {
 | 
					            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()
 | 
					                    cc.previous()
 | 
				
			||||||
                    return operand
 | 
					                    return operand
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -468,7 +489,13 @@ class Compiler(
 | 
				
			|||||||
                    // range operator
 | 
					                    // range operator
 | 
				
			||||||
                    val isEndInclusive = t.type == Token.Type.DOTDOT
 | 
					                    val isEndInclusive = t.type == Token.Type.DOTDOT
 | 
				
			||||||
                    val left = operand
 | 
					                    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 {
 | 
					                    operand = Accessor {
 | 
				
			||||||
                        ObjRange(
 | 
					                        ObjRange(
 | 
				
			||||||
                            left?.getter?.invoke(it)?.value ?: ObjNull,
 | 
					                            left?.getter?.invoke(it)?.value ?: ObjNull,
 | 
				
			||||||
@ -955,7 +982,7 @@ class Compiler(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private suspend fun parseWhenStatement(): Statement {
 | 
					    private suspend fun parseWhenStatement(): Statement {
 | 
				
			||||||
        // has a value, when(value) ?
 | 
					        // has a value, when(value) ?
 | 
				
			||||||
        var t = cc.skipWsTokens()
 | 
					        var t = cc.nextNonWhitespace()
 | 
				
			||||||
        return if (t.type == Token.Type.LPAREN) {
 | 
					        return if (t.type == Token.Type.LPAREN) {
 | 
				
			||||||
            // when(value)
 | 
					            // when(value)
 | 
				
			||||||
            val value = parseStatement() ?: throw ScriptError(cc.currentPos(), "when(value) expected")
 | 
					            val value = parseStatement() ?: throw ScriptError(cc.currentPos(), "when(value) expected")
 | 
				
			||||||
@ -978,7 +1005,7 @@ class Compiler(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                // loop conditions
 | 
					                // loop conditions
 | 
				
			||||||
                while (true) {
 | 
					                while (true) {
 | 
				
			||||||
                    t = cc.skipWsTokens()
 | 
					                    t = cc.nextNonWhitespace()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    when (t.type) {
 | 
					                    when (t.type) {
 | 
				
			||||||
                        Token.Type.IN,
 | 
					                        Token.Type.IN,
 | 
				
			||||||
@ -1191,11 +1218,11 @@ class Compiler(
 | 
				
			|||||||
        cc.skipTokenOfType(Token.Type.LBRACE)
 | 
					        cc.skipTokenOfType(Token.Type.LBRACE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        do {
 | 
					        do {
 | 
				
			||||||
            val t = cc.skipWsTokens()
 | 
					            val t = cc.nextNonWhitespace()
 | 
				
			||||||
            when (t.type) {
 | 
					            when (t.type) {
 | 
				
			||||||
                Token.Type.ID -> {
 | 
					                Token.Type.ID -> {
 | 
				
			||||||
                    names += t.value
 | 
					                    names += t.value
 | 
				
			||||||
                    val t1 = cc.skipWsTokens()
 | 
					                    val t1 = cc.nextNonWhitespace()
 | 
				
			||||||
                    when (t1.type) {
 | 
					                    when (t1.type) {
 | 
				
			||||||
                        Token.Type.COMMA ->
 | 
					                        Token.Type.COMMA ->
 | 
				
			||||||
                            continue
 | 
					                            continue
 | 
				
			||||||
 | 
				
			|||||||
@ -39,7 +39,6 @@ class CompilerContext(val tokens: List<Token>) {
 | 
				
			|||||||
    fun next() =
 | 
					    fun next() =
 | 
				
			||||||
        if (currentIndex < tokens.size) tokens[currentIndex++]
 | 
					        if (currentIndex < tokens.size) tokens[currentIndex++]
 | 
				
			||||||
        else Token("", tokens.last().pos, Token.Type.EOF)
 | 
					        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]
 | 
					    fun previous() = if (!hasPrevious()) throw IllegalStateException("No previous token") else tokens[--currentIndex]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -131,6 +130,28 @@ class CompilerContext(val tokens: List<Token>) {
 | 
				
			|||||||
        previous()
 | 
					        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 {
 | 
					    inline fun ifNextIs(typeId: Token.Type, f: (Token) -> Unit): Boolean {
 | 
				
			||||||
        val t = next()
 | 
					        val t = next()
 | 
				
			||||||
@ -207,7 +228,7 @@ class CompilerContext(val tokens: List<Token>) {
 | 
				
			|||||||
        while (current().type in wstokens) {
 | 
					        while (current().type in wstokens) {
 | 
				
			||||||
            next()
 | 
					            next()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return next()
 | 
					        return current()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
 | 
				
			|||||||
@ -436,6 +436,9 @@ object ObjNull : Obj() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * TODO: get rid of it. Maybe we ise some Lyng inheritance instead
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
interface Numeric {
 | 
					interface Numeric {
 | 
				
			||||||
    val longValue: Long
 | 
					    val longValue: Long
 | 
				
			||||||
    val doubleValue: Double
 | 
					    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
 | 
					    @Test
 | 
				
			||||||
    fun testCharacterRange() = runTest {
 | 
					    fun testCharacterRange() = runTest {
 | 
				
			||||||
        eval(
 | 
					        eval(
 | 
				
			||||||
@ -2991,5 +3020,20 @@ class ScriptTest {
 | 
				
			|||||||
        """.trimIndent())
 | 
					        """.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