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