Skip to content
This repository was archived by the owner on Jan 2, 2023. It is now read-only.

Commit 4bf5fb9

Browse files
committed
transpiler
1 parent 2bd322b commit 4bf5fb9

File tree

8 files changed

+102
-60
lines changed

8 files changed

+102
-60
lines changed

squint/README.markdown

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ Discussions](https://github.com/borkdude/cherry/discussions).
1010
Goals of cherry:
1111

1212
- Transpile `.cljs` files on the fly into ES6-compatible `.js` (or `.mjs`) files.
13-
- Compiler will be available on npm and can be used from JS tooling, but isn't
14-
part of the compiled output unless explicitly used.
15-
- Transpiled JS files are fairly readable and have source map support for debugging
13+
- Transpiler will be available on npm and can be used from JS tooling, but isn't
14+
part of the transpiled output unless explicitly used.
15+
- Transpiled JS files are fairly readable and have source map support for
16+
debugging
1617
- Transpiled JS files are linked to one shared NPM module which contains
1718
`"cherry/cljs.core"`, `"cherry/cljs.string"`, etc. such that libraries
18-
written in cherry can be compiled, tree-shaken with ES6 tooling and then
19+
written in cherry can be transpiled, tree-shaken with ES6 tooling and then
1920
hosted on NPM. Output linked to older versions of cherry will work with newer
2021
versions of cherry: i.e. 'binary' compatibility.
2122
- Light-weight and fast: heavy lifting such as optimizations are expected to be
@@ -30,7 +31,7 @@ Goals of cherry:
3031
Cherry may introduce new constructs such as `await` which won't be compatible
3132
with current CLJS. Also it might not support all features that CLJS offers. As
3233
such, using existing libraries from the CLJS ecosystem or compiling Cherry CLJS
33-
code with the normal CLJS compiler may become challenging. However, some results
34+
code with the CLJS compiler may become challenging. However, some results
3435
of this experiment may end up as improvements in the CLJS compiler if they turn
3536
out to be of value.
3637

squint/bb.edn

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
{:tasks {test {:doc "Run tests"
1+
{:paths ["src"]
2+
:tasks {test {:doc "Run tests"
23
:task (clojure "-M:cljs:test")}}}

squint/corpus/foo.cljs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(ns foo)
2+
3+
(js/console.log "hello")
4+
(js/console.log (+ 1 2 3))

squint/corpus/foo.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
console.log("hello")
2+
console.log((1 + 2 + 3))

squint/deps.edn

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
{:aliases {:cljs {:extra-deps {org.clojure/clojurescript {:mvn/version "1.11.60"}}}
2-
:test ;; added by neil
3-
{:extra-paths ["test"]
4-
:extra-deps {io.github.cognitect-labs/test-runner
5-
{:git/tag "v0.5.0" :git/sha "b3fd0d2"}
6-
babashka/fs {:mvn/version "0.1.6"}}
7-
:main-opts ["-m" "cognitect.test-runner"]
8-
:exec-fn cognitect.test-runner.api/test}}}
1+
{:deps {borkdude/edamame {:mvn/version "1.0.0"}}
2+
3+
:aliases
4+
{:cljs {:extra-deps {org.clojure/clojurescript {:mvn/version "1.11.60"}}}
5+
:test ;; added by neil
6+
{:extra-paths ["test"]
7+
:extra-deps {io.github.cognitect-labs/test-runner
8+
{:git/tag "v0.5.0" :git/sha "b3fd0d2"}
9+
babashka/fs {:mvn/version "0.1.6"}}
10+
:main-opts ["-m" "cognitect.test-runner"]
11+
:exec-fn cognitect.test-runner.api/test}}
12+
13+
}

squint/src/cherry/compiler.clj renamed to squint/src/cherry/transpiler.clj

Lines changed: 65 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
;;; scriptjure -- a library for generating javascript from Clojure s-exprs
22

33
;; by Allen Rohner, http://arohner.blogspot.com
4-
;; http://www.reasonr.com
4+
;; http://www.reasonr.com
55
;; October 7, 2009
66

77
;; Copyright (c) Allen Rohner, 2009. All rights reserved. The use
@@ -12,12 +12,12 @@
1212
;; agreeing to be bound by the terms of this license. You must not
1313
;; remove this notice, or any other, from this software.
1414

15-
;; This library generates javascript from Clojure s-exprs. To use it,
15+
;; This library generates javascript from Clojure s-exprs. To use it,
1616
;; (js (fn foo [x] (var x (+ 3 5)) (return x)))
1717
;; returns a string, "function foo (x) { var x = (3 + 5); return x; }"
1818
;;
1919
;; See the README and the tests for more information on what is supported.
20-
;;
20+
;;
2121
;; The library is intended to generate javascript glue code in Clojure
2222
;; webapps. One day it might become useful enough to write entirely
2323
;; JS libraries in clojure, but it's not there yet.
@@ -26,10 +26,11 @@
2626

2727
(ns #^{:author "Allen Rohner"
2828
:doc "A library for generating javascript from Clojure."}
29-
cherry.compiler
30-
(:require [clojure.string :as str])
31-
(:require [com.reasonr.string :as rstr])
32-
(:use clojure.walk))
29+
cherry.transpiler
30+
(:require [clojure.string :as str]
31+
[com.reasonr.string :as rstr]
32+
[edamame.core :as e])
33+
(:use clojure.walk))
3334

3435
(defn- throwf [& message]
3536
(throw (Exception. (apply format message))))
@@ -86,7 +87,7 @@
8687
'return 'delete 'new 'do 'aget 'while 'doseq
8788
'str 'inc! 'dec! 'dec 'inc 'defined? 'and 'or
8889
'? 'try 'break
89-
'await 'const 'defn 'let]))
90+
'await 'const 'defn 'let 'ns]))
9091

9192
(def prefix-unary-operators (set ['!]))
9293

@@ -168,6 +169,10 @@
168169
(return (emit-do more)))
169170
{:async? *async*}))
170171

172+
(defmethod emit-special 'ns [_ & _]
173+
;; TODO
174+
)
175+
171176
(defmethod emit-special 'funcall [type [name & args]]
172177
(str (if (and (list? name) (= 'fn (first name))) ; function literal call
173178
(str "(" (emit name) ")")
@@ -316,27 +321,27 @@
316321
finally-clause (filter #(= 'finally (first %))
317322
body)]
318323
(cond
319-
(and (empty? catch-clause)
320-
(empty? finally-clause))
321-
(throw (new Exception (str "Must supply a catch or finally clause (or both) in a try statement! " expression)))
322-
323-
(> (count catch-clause) 1)
324-
(throw (new Exception (str "Multiple catch clauses in a try statement are not currently supported! " expression)))
325-
326-
(> (count finally-clause) 1)
327-
(throw (new Exception (str "Cannot supply more than one finally clause in a try statement! " expression)))
328-
329-
:true (str "try{\n"
330-
(emit-do try-body)
331-
"}\n"
332-
(if-let [[_ exception & catch-body] (first catch-clause)]
333-
(str "catch(" (emit exception) "){\n"
334-
(emit-do catch-body)
335-
"}\n"))
336-
(if-let [[_ & finally-body] (first finally-clause)]
337-
(str "finally{\n"
338-
(emit-do finally-body)
339-
"}\n"))))))
324+
(and (empty? catch-clause)
325+
(empty? finally-clause))
326+
(throw (new Exception (str "Must supply a catch or finally clause (or both) in a try statement! " expression)))
327+
328+
(> (count catch-clause) 1)
329+
(throw (new Exception (str "Multiple catch clauses in a try statement are not currently supported! " expression)))
330+
331+
(> (count finally-clause) 1)
332+
(throw (new Exception (str "Cannot supply more than one finally clause in a try statement! " expression)))
333+
334+
:true (str "try{\n"
335+
(emit-do try-body)
336+
"}\n"
337+
(if-let [[_ exception & catch-body] (first catch-clause)]
338+
(str "catch(" (emit exception) "){\n"
339+
(emit-do catch-body)
340+
"}\n"))
341+
(if-let [[_ & finally-body] (first finally-clause)]
342+
(str "finally{\n"
343+
(emit-do finally-body)
344+
"}\n"))))))
340345

341346
(defmethod emit-special 'break [type [break]]
342347
(statement "break"))
@@ -351,16 +356,16 @@
351356
(let [head (symbol (name (first expr))) ; remove any ns resolution
352357
expr (conj (rest expr) head)]
353358
(cond
354-
(and (= (rstr/get (str head) 0) \.)
355-
(> (count (str head)) 1)
359+
(and (= (rstr/get (str head) 0) \.)
360+
(> (count (str head)) 1)
356361

357-
(not (= (rstr/get (str head) 1) \.))) (emit-special 'dot-method expr)
362+
(not (= (rstr/get (str head) 1) \.))) (emit-special 'dot-method expr)
358363
(custom-form? head) (emit-custom head expr)
359-
(special-form? head) (emit-special head expr)
360-
(infix-operator? head) (emit-infix head expr)
364+
(special-form? head) (emit-special head expr)
365+
(infix-operator? head) (emit-infix head expr)
361366
(prefix-unary? head) (emit-prefix-unary head expr)
362367
(suffix-unary? head) (emit-suffix-unary head expr)
363-
:else (emit-special 'funcall expr)))
368+
:else (emit-special 'funcall expr)))
364369
(if (list? expr)
365370
(emit-special 'funcall expr)
366371
(throw (new Exception (str "invalid form: " expr))))))
@@ -377,11 +382,11 @@
377382

378383
(defn _js [forms]
379384
(with-var-declarations
380-
(let [code (if (> (count forms) 1)
381-
(emit-do forms {:top-level? true})
382-
(emit (first forms)))]
383-
;;(println "js " forms " => " code)
384-
(str (emit-var-declarations) code))))
385+
(let [code (if (> (count forms) 1)
386+
(emit-do forms {:top-level? true})
387+
(emit (first forms)))]
388+
;;(println "js " forms " => " code)
389+
(str (emit-var-declarations) code))))
385390

386391
(defn- unquote?
387392
"Tests whether the form is (unquote ...)."
@@ -394,9 +399,9 @@
394399
(declare inner-walk outer-walk)
395400

396401
(defn- inner-walk [form]
397-
(cond
398-
(unquote? form) (handle-unquote form)
399-
:else (walk inner-walk outer-walk form)))
402+
(cond
403+
(unquote? form) (handle-unquote form)
404+
:else (walk inner-walk outer-walk form)))
400405

401406
(defn- outer-walk [form]
402407
(cond
@@ -426,7 +431,7 @@
426431
[form]
427432
`(js (clj ~form)))
428433

429-
(defmacro js
434+
(defmacro js
430435
"takes one or more forms. Returns a string of the forms translated into javascript"
431436
[& forms]
432437
`(_js (quasiquote ~forms)))
@@ -442,7 +447,7 @@
442447
`(do
443448
(defn ~nme ~params
444449
(js*
445-
~@body))
450+
~@body))
446451
(add-custom-form '~nme ~nme)))
447452

448453
(defn add-custom-form [form func]
@@ -459,3 +464,18 @@
459464
(let [v (apply func (next expr))]
460465
(emit v))))
461466

467+
(defn transpile-string [s]
468+
(let [rdr (e/reader s)]
469+
(loop [transpiled ""]
470+
(let [next-form (e/parse-next rdr)]
471+
(if (= ::e/eof next-form)
472+
transpiled
473+
(let [next-js (js (clj next-form))]
474+
(recur (str transpiled next-js (when-not (str/blank? next-js)
475+
"\n")))))))))
476+
477+
(defn transpile-file [{:keys [in-file out-file]}]
478+
(let [out-file (or out-file
479+
(str/replace in-file #".cljs$" ".mjs"))]
480+
(spit out-file (transpile-string (slurp in-file)))
481+
{:out-file out-file}))

squint/test/cherry_test.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
(ns cherry-test
22
(:require
33
[babashka.fs :as fs]
4-
[cherry.compiler :refer [js]]
4+
[cherry.transpiler :refer [js]]
55
[clojure.java.shell :refer [sh]]
66
[clojure.string :as str]
77
[clojure.test :as t :refer [deftest is]]))

squint/transpile_and_run.clj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
(require '[babashka.process :refer [shell]]
2+
'[cherry.transpiler :as cherry]
3+
)
4+
5+
(def in-file (first *command-line-args*))
6+
(def out-file (:out-file (cherry/transpile-file {:in-file in-file})))
7+
8+
(shell "node --experimental-fetch" out-file)
9+
nil

0 commit comments

Comments
 (0)