Skip to content

Commit

Permalink
Merge pull request #31 from orangain/fix-string-escapes
Browse files Browse the repository at this point in the history
Fix string escapes
  • Loading branch information
orangain authored Apr 30, 2023
2 parents f2ba50c + f63de46 commit 96d36e9
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 26 deletions.
2 changes: 1 addition & 1 deletion ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class WriterTest {

@Test
fun testSimpleCharacterEscaping() {
assertParseAndWriteExact("""val x = "input\b\n\t\r\'\"\\\${'$'}"""")
assertParseAndWriteExact("""val x = "input\b\nTest\t\r\u3000\'\"\\\${'$'}"""")
}

@Test
Expand Down
14 changes: 14 additions & 0 deletions ast/src/commonMain/kotlin/ktast/ast/Dumper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ class Dumper(
is Node.Expression.Name -> mapOf("name" to name)
is Node.Expression.Constant -> mapOf("value" to value, "form" to form)
is Node.Extra.Comment -> mapOf("text" to text)
is Node.Expression.StringTemplate.Entry.Regular -> mapOf("str" to str)
is Node.Expression.StringTemplate.Entry.ShortTemplate -> mapOf("str" to str)
is Node.Expression.StringTemplate.Entry.UnicodeEscape -> mapOf("digits" to digits)
is Node.Expression.StringTemplate.Entry.RegularEscape -> mapOf("char" to char.toEscapedString())
else -> null
}?.let {
app.append(it.toString())
Expand All @@ -84,3 +88,13 @@ class Dumper(
app.appendLine()
}
}

private fun Char.toEscapedString(): String {
return when (this) {
'\b' -> "\\b"
'\n' -> "\\n"
'\r' -> "\\r"
'\t' -> "\\t"
else -> this.toString()
}
}
70 changes: 45 additions & 25 deletions ast/src/commonMain/kotlin/ktast/ast/Writer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,26 @@ open class Writer(

protected fun append(ch: Char) = append(ch.toString())
protected fun append(str: String) = also {
if (str == "") return@also
if (lastAppendedToken.endsWith(">") && str.startsWith("=")) {
doAppend(" ") // Insert heuristic space between '>' and '=' not to be confused with '>='
}
if (lastAppendedToken != "" && isNonSymbol(lastAppendedToken.last()) && isNonSymbol(str.first())) {
doAppend(" ") // Insert heuristic space between two non-symbols
if (heuristicSpaceInsertionCriteria.any { it(lastAppendedToken.lastOrNull(), str.firstOrNull()) }) {
doAppend(" ")
}
doAppend(str)
lastAppendedToken = str
}

protected fun isNonSymbol(ch: Char) = ch == '_' || nonSymbolCategories.contains(ch.category)
protected val heuristicSpaceInsertionCriteria: List<(Char?, Char?) -> Boolean> = listOf(
// Insert heuristic space between '>' and '=' not to be confused with '>='
{ last, next -> last == '>' && next == '=' },
// Insert heuristic space between two non-symbols
{ last, next -> isNonSymbol(last) && isNonSymbol(next) },
)

protected fun isNonSymbol(ch: Char?) =
ch != null && (ch == '_' || nonSymbolCategories.contains(ch.category))

protected fun doAppend(str: String) {
if (str == "") return
app.append(str)
lastAppendedToken = str
}

fun write(v: Node) {
Expand Down Expand Up @@ -371,25 +376,40 @@ open class Writer(
}
is Node.Expression.Parenthesized ->
append('(').also { children(expression) }.append(')')
is Node.Expression.StringTemplate ->
if (raw) append("\"\"\"").also { children(entries) }.append("\"\"\"")
else append('"').also { children(entries) }.append('"')
is Node.Expression.StringTemplate -> {
if (raw) {
append("\"\"\"")
children(entries)
append("\"\"\"")
} else {
append('"')
children(entries)
append('"')
}
}
is Node.Expression.StringTemplate.Entry.Regular ->
append(str)
is Node.Expression.StringTemplate.Entry.ShortTemplate ->
append('$').append(str)
is Node.Expression.StringTemplate.Entry.UnicodeEscape ->
append("\\u").append(digits)
is Node.Expression.StringTemplate.Entry.RegularEscape ->
append('\\').append(
when (char) {
'\b' -> 'b'
'\n' -> 'n'
'\t' -> 't'
'\r' -> 'r'
else -> char
}
doAppend(str)
is Node.Expression.StringTemplate.Entry.ShortTemplate -> {
doAppend("$")
doAppend(str)
}
is Node.Expression.StringTemplate.Entry.UnicodeEscape -> {
doAppend("\\u")
doAppend(digits)
}
is Node.Expression.StringTemplate.Entry.RegularEscape -> {
doAppend(
"\\${
when (char) {
'\b' -> 'b'
'\n' -> 'n'
'\t' -> 't'
'\r' -> 'r'
else -> char
}
}"
)
}
is Node.Expression.StringTemplate.Entry.LongTemplate ->
append("\${").also { children(expression) }.append('}')
is Node.Expression.Constant ->
Expand Down

0 comments on commit 96d36e9

Please sign in to comment.