Skip to content

Commit

Permalink
Fix #110: add parsing of quoted library arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
ileasile committed Nov 16, 2020
1 parent 8d67044 commit 8f8c257
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 7 deletions.
83 changes: 76 additions & 7 deletions src/main/kotlin/org/jetbrains/kotlin/jupyter/libraries/util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,88 @@ fun diagFailure(message: String): ResultWithDiagnostics.Failure {
return ResultWithDiagnostics.Failure(ScriptDiagnostic(ScriptDiagnostic.unspecifiedError, message))
}

fun parseLibraryArgument(str: String): Variable {
val eq = str.indexOf('=')
return if (eq == -1) Variable("", str.trim())
else Variable(str.substring(0, eq).trim(), str.substring(eq + 1).trim())
data class ArgParseResult(
val variable: Variable,
val end: Int
)

fun parseLibraryArgument(str: String, argEndChars: List<Char>, begin: Int): ArgParseResult? {
val eq = str.indexOf('=', begin)
val untrimmedName = if (eq < 0) "" else str.substring(begin, eq)
val name = untrimmedName.trim()

var argBegan = false
var argEnded = false
var quoteOpened = false
var escape = false

val builder = StringBuilder()

var i = if (eq < 0) begin - 1 else eq
while ((++i) < str.length) {
val c = str[i]

if (escape) {
builder.append(c)
escape = false
continue
}

when (c) {
'\\' -> {
if (quoteOpened) escape = true
else builder.append(c)
}
'"' -> {
if (argBegan) {
quoteOpened = false
argEnded = true
} else {
quoteOpened = true
argBegan = true
}
}
in argEndChars -> {
if (quoteOpened) builder.append(c)
else break
}
else -> {
if (!c.isWhitespace()) {
if (argEnded) {
throw ReplCompilerException(
"Cannot parse library arguments: unexpected char '$c' " +
"on position $i " +
"in arguments string '$str'"
)
}
argBegan = true
builder.append(c)
}
}
}
}

val value = builder.toString().trim()
if (eq == -1 && value.isEmpty()) return null

val nextIndex = if (i == str.length) i else i + 1
return ArgParseResult(Variable(name, value), nextIndex)
}

fun parseCall(str: String, brackets: Brackets): Pair<String, List<Variable>> {
val openBracketIndex = str.indexOf(brackets.open)
if (openBracketIndex == -1) return str.trim() to emptyList()
val name = str.substring(0, openBracketIndex).trim()
val args = str.substring(openBracketIndex + 1, str.indexOf(brackets.close, openBracketIndex))
.split(',')
.map(::parseLibraryArgument)
val argsString = str.substring(openBracketIndex + 1, str.indexOfLast { it == brackets.close })

val endChars = listOf(brackets.close, ',')
val firstArg = parseLibraryArgument(argsString, endChars, 0)
val args = generateSequence(firstArg) {
parseLibraryArgument(argsString, endChars, it.end)
}.map {
it.variable
}.toList()

return name to args
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,26 @@ class ParseArgumentsTests {
assertEquals("val2", args[1].value)
}

@Test
fun test4() {
val (ref, args) = libraryFactory.parseReferenceWithArgs("""lets-plot(api="[1.0,)")""")
assertEquals("lets-plot", ref.name)
assertEquals(1, args.count())
assertEquals("api", args[0].name)
assertEquals("[1.0,)", args[0].value)
}

@Test
fun test5() {
val (ref, args) = libraryFactory.parseReferenceWithArgs("""lets-plot(api = "[1.0,)" , lib=1.5.3 )""")
assertEquals("lets-plot", ref.name)
assertEquals(2, args.count())
assertEquals("api", args[0].name)
assertEquals("[1.0,)", args[0].value)
assertEquals("lib", args[1].name)
assertEquals("1.5.3", args[1].value)
}

@Test
fun testInfo1() {
val requestUrl = "https://raw.githubusercontent.com/Kotlin/kotlin-jupyter/master/libraries/default.json"
Expand Down

0 comments on commit 8f8c257

Please sign in to comment.