From 89ceb4538e0893ebb23389dec2255ab35507cf2f Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Sun, 22 Sep 2024 21:08:27 -0400 Subject: [PATCH] Fix double quote encoding in `basilisp.edn/write-string` --- CHANGELOG.md | 1 + src/basilisp/edn.lpy | 19 +++++++++-- tests/basilisp/test_edn.lpy | 64 +++++++++++++++++++++++++------------ 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a486b3bd8..44c006b87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed * Fix a bug where the reader was double counting the CRLF newline seq in metadata (#1063) * Conform to the `cider-nrepl` `info` ops spec by ensuring result's `:file` is URI, also added missing :column number (#1066) + * Fix a bug with `basilisp.edn/write-string` where nested double quotes were not escaped properly (#1071) ## [v0.2.3] ### Added diff --git a/src/basilisp/edn.lpy b/src/basilisp/edn.lpy index 353cbc604..bd28d5fef 100644 --- a/src/basilisp/edn.lpy +++ b/src/basilisp/edn.lpy @@ -539,6 +539,18 @@ ;; Writers ;; ;;;;;;;;;;;;; +(def ^:private str-escape-chars-translation + (python.str/maketrans + #py {"\\" "\\\\" + "\"" "\\\"" + "\a" "\\a" + "\b" "\\b" + "\f" "\\f" + "\n" "\\n" + "\r" "\\r" + "\t" "\\t" + "\v" "\\v"})) + (defprotocol EDNEncodeable (write* [this writer] "Write the object ``this`` to the stream ``writer`` encoded as EDN. @@ -617,9 +629,10 @@ nil) python/str (write* [this writer] - (.write writer "\"") - (.write writer this) - (.write writer "\"") + (let [decoded (.translate this str-escape-chars-translation)] + (.write writer "\"") + (.write writer decoded) + (.write writer "\"")) nil) nil (write* [_this writer] diff --git a/tests/basilisp/test_edn.lpy b/tests/basilisp/test_edn.lpy index 279b371cf..368a3d8cf 100644 --- a/tests/basilisp/test_edn.lpy +++ b/tests/basilisp/test_edn.lpy @@ -365,28 +365,52 @@ \u03A9 "\"Ω\"" \space "\" \"" - \newline "\"\n\"" - \tab "\"\t\"" - \return "\"\r\"")) + \newline "\"\\n\"" + \tab "\"\\t\"" + \return "\"\\r\"")) (deftest write-string - (are [v s] (= s (edn/write-string v)) - "" "\"\"" - " " "\" \"" - "\"" "\"\"\"" - "\\" "\"\\\"" - "\a" "\"\a\"" - "\b" "\"\b\"" - "\f" "\"\f\"" - "\n" "\"\n\"" - "\r" "\"\r\"" - "\t" "\"\t\"" - "\v" "\"\v\"" - - "Hello,\nmy name is\tChris." "\"Hello,\nmy name is\tChris.\"" - "Regular string" "\"Regular string\"" - "String with 'inner string'" "\"String with 'inner string'\"" - "String with \"inner string\"" "\"String with \"inner string\"\"")) + (testing "expected outputs" + (are [v s] (= s (edn/write-string v)) + "" "\"\"" + " " "\" \"" + "\"" "\"\\\"\"" + "\\" "\"\\\\\"" + "\a" "\"\\a\"" + "\b" "\"\\b\"" + "\f" "\"\\f\"" + "\n" "\"\\n\"" + "\r" "\"\\r\"" + "\t" "\"\\t\"" + "\v" "\"\\v\"" + + (str ["a"]) "\"[\\\"a\\\"]\"" + + "Hello,\nmy name is\tChris." "\"Hello,\\nmy name is\\tChris.\"" + "Regular string" "\"Regular string\"" + "String with 'inner string'" "\"String with 'inner string'\"" + "String with \"inner string\"" "\"String with \\\"inner string\\\"\"")) + + (testing "round-trip" + (are [s] (= s (edn/read-string (edn/write-string s))) + "" + " " + "\"" + "\\" + "\a" + "\b" + "\f" + "\n" + "\r" + "\t" + "\v" + + (str ["a"]) + + "Hello,\nmy name is\tChris." + "Regular string" + "String with 'inner string'" + "String with \"inner string\""))) (deftest write-dispatch (are [v s] (= s (edn/write-string v))