Skip to content

Commit a75297f

Browse files
authored
prn (#765)
1 parent 463aa12 commit a75297f

File tree

5 files changed

+62
-33
lines changed

5 files changed

+62
-33
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
## Unreleased
66

77
- Fix [#758](https://github.com/squint-cljs/squint/issues/758): `volatile!`, `vswap!`, `vreset!`
8+
- `pr-str`, `prn` etc now print EDN (with the idea that you can paste it back into your program)
9+
- new `#js/map` reader that reads a JavaScript `Map` from a Clojure map (maps are printed like this with `pr-str` too)
810

911
## v0.9.178
1012

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ need the extra performance, startup time and/or small bundle size.
8383
- `assoc!`, `dissoc!`, `conj!`, etc. perform in place mutation on objects
8484
- `assoc`, `dissoc`, `conj`, etc. return a new shallow copy of objects
8585
- `println` is a synonym for `console.log`
86-
- `pr-str` and `prn` coerce values to a string using `JSON.stringify` (currently, this may change)
86+
- `pr-str` and `prn` print EDN with the idea that you can paste the output back into your programs
87+
- JavaScript `Map`s are printed like maps with a `#js/map` prefix
8788
- Since JavaScript only supports strings for keys in maps, any data structures used as keys will be stringified
8889

8990
If you are looking for closer ClojureScript semantics, take a look at [Cherry 🍒](https://github.com/squint-cljs/cherry).

src/squint/compiler.cljc

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
[squint.compiler-common :as cc :refer [#?(:cljs Exception)
1717
#?(:cljs format)
1818
*aliases* *cljs-ns* *excluded-core-vars* *imported-vars* *public-vars*
19-
comma-list emit emit-args emit-infix emit-return escape-jsx
19+
emit emit-args emit-infix emit-return escape-jsx
2020
expr-env infix-operator? prefix-unary? suffix-unary?]]
2121
[squint.defclass :as defclass]
2222
[squint.internal.deftype :as deftype]
@@ -44,9 +44,11 @@
4444
;; prefixed to avoid conflicts
4545
'squint-compiler-jsx
4646
'squint-compiler-html
47+
'squint-compiler-js-map
4748
'require 'squint.defclass/defclass* 'squint.defclass/super*
4849
'squint.impl/for-of
49-
'squint.impl/defonce]))
50+
'squint.impl/defonce
51+
]))
5052

5153
(def built-in-macros (merge {'-> macros/core->
5254
'->> macros/core->>
@@ -337,19 +339,26 @@
337339
(defn html [form]
338340
(list 'squint-compiler-html form))
339341

342+
(defn js-map [form]
343+
(list 'squint-compiler-js-map form))
344+
340345
(defmethod emit-special 'squint-compiler-jsx [_ env [_ form]]
341346
(set! *jsx* true)
342347
(let [env (assoc env :jsx true)]
343348
(emit form env)))
344349

350+
(defmethod emit-special 'squint-compiler-js-map [_ env [_ form]]
351+
(emit (list 'new 'js/Map (mapv (fn [kv] kv) form)) env))
352+
345353
(def squint-parse-opts
346354
(e/normalize-opts
347355
{:all true
348356
:end-location false
349357
:location? seq?
350358
:readers {'js #(vary-meta % assoc ::js true)
351359
'jsx jsx
352-
'html html}
360+
'html html
361+
'js/map js-map}
353362
:read-cond :allow
354363
:features #{:squint :cljs}}))
355364

src/squint/core.js

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -980,33 +980,6 @@ export function nil_QMARK_(v) {
980980

981981
export const PROTOCOL_SENTINEL = {};
982982

983-
function pr_str_1(x) {
984-
if (x === null) {
985-
return 'null';
986-
}
987-
return JSON.stringify(x, (_key, value) => {
988-
switch (typeConst(value)) {
989-
case SET_TYPE:
990-
case LAZY_ITERABLE_TYPE:
991-
return [...value];
992-
case MAP_TYPE:
993-
return Object.fromEntries(value);
994-
default: {
995-
// console.log(value);
996-
return value;
997-
}
998-
}
999-
});
1000-
}
1001-
1002-
export function pr_str(...xs) {
1003-
return xs.map(pr_str_1).join(' ');
1004-
}
1005-
1006-
export function prn(...xs) {
1007-
println(pr_str(...xs));
1008-
}
1009-
1010983
export class Atom {
1011984
constructor(init) {
1012985
this.val = init;
@@ -2995,3 +2968,45 @@ export function volatile_BANG_(x) {
29952968
export function vreset_BANG_(vol, v) {
29962969
vol.v = v;
29972970
}
2971+
2972+
function toEDN(value, seen = new WeakSet()) {
2973+
if (value === null) return 'nil';
2974+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
2975+
if (typeof value === 'string') return JSON.stringify(value);
2976+
if (typeof value === 'bigint') return `${value}N`;
2977+
2978+
if (typeof value === 'object') {
2979+
if (seen.has(value)) return '#object[circular]';
2980+
seen.add(value);
2981+
const T = typeConst(value);
2982+
let keys;
2983+
switch (T) {
2984+
case ARRAY_TYPE:
2985+
return `[${value.map((v) => toEDN(v, seen)).join(' ')}]`;
2986+
case SET_TYPE:
2987+
return `#{${Array.from(value)
2988+
.map((v) => toEDN(v, seen))
2989+
.join(' ')}}`;
2990+
case MAP_TYPE:
2991+
return `#js/map {${Array.from(value.entries())
2992+
.map(([k, v]) => `${toEDN(k, seen)} ${toEDN(v, seen)}`)
2993+
.join(', ')}}`;
2994+
case LAZY_ITERABLE_TYPE:
2995+
case LIST_TYPE:
2996+
return `(${mapv((v) => `${toEDN(v, seen)}`, value).join(', ')})`;
2997+
default:
2998+
keys = Object.keys(value);
2999+
return `{${keys.map((k) => `:${k} ${toEDN(value[k], seen)}`).join(', ')}}`;
3000+
}
3001+
}
3002+
3003+
return `#object[${value.constructor.name}]`;
3004+
}
3005+
3006+
export function pr_str(...xs) {
3007+
return xs.map((v, _) => toEDN(v)).join(' ');
3008+
}
3009+
3010+
export function prn(...xs) {
3011+
return console.log(pr_str(...xs));
3012+
}

test/squint/compiler_test.cljs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,9 @@
657657
(is (eq "hello/world" (jsv! "(ns hello) ::world"))))
658658

659659
(deftest pr-str-test
660-
(is (eq (js/Set. #js ["a" "b" "c"]) (js/Set. (js/JSON.parse (jsv! '(pr-str #{:a :b :c})))))))
660+
(doseq [coll [#{"a" "b" "c"} {:a 1 :b 2}]]
661+
(is (eq (pr-str coll) (jsv! `(pr-str ~coll)))))
662+
(is (eq "#js/map {\"a\" 1}" (jsv! "(pr-str #js/map {:a 1})"))))
661663

662664
(deftest str-test
663665
(is (eq "123" (jsv! '(str 1 2 3))))
@@ -2007,7 +2009,7 @@ globalThis.foo.fs = fs;")))))
20072009
(fs/mkdirSync "test-output"))
20082010
(fs/writeFileSync "test-output/foo.mjs" js)
20092011
(is (str/includes? (process/execSync "node test-output/foo.mjs")
2010-
"[[-1,-2,-3],true,-10]"))))
2012+
"[[-1 -2 -3] true -10]"))))
20112013

20122014
(deftest pre-post-test
20132015
(testing "pre"

0 commit comments

Comments
 (0)