Skip to content
This repository
Browse code

CLJS compiler and REPL working in Node.js

Compile and run the CLJS REPL:
    cd node
    ../bin/cljsc ../src/cljs/noderepl.cljs > noderepl.js
    cp ../src/cljs/goog.js out/
    ./run.js noderepl.js

Compile and run the the CLJS compiler wrapper:
    cd node
    ../bin/cljsc ../src/cljs/nodecljs.cljs > nodecljs.js
    cp ../src/cljs/goog.js out/
    ./run.js nodecljs.js hello.cljs

The :nodejs compilation target is currently broken. However, the
node/run.js bootstrap script enables compiled CLJS code to be
invoked that was not compiled with a :target.

Also, rename the jsrepl.cljs to webrepl.cljs to disambiguate with the
node.js equivalent (noderepl.js).
  • Loading branch information...
commit 78037a60e28159af54c3ec280d6263a9cf0e71a5 1 parent e58ebc2
Joel Martin authored November 29, 2012
2  .gitignore
@@ -8,6 +8,8 @@ closure
8 8
 /coreadvanced.js
9 9
 /coresimple.js
10 10
 /out
  11
+/web/out
  12
+/node/out
11 13
 .repl
12 14
 *.swp
13 15
 *.zip
42  README.md
Source Rendered
@@ -20,16 +20,18 @@ solved by [chouser](https://github.com/chouser).
20 20
 ### Current Caveats
21 21
 
22 22
 * No macro support yet. This means defining a function currently
23  
-  looks like this `(def dbl (fn* [x] (* x x)))`.
  23
+  looks like this `(def sqr (fn* [x] (* x x)))`.
24 24
 * The code is not yet compatible with the normal Clojure ClojureScript
25 25
   compiler. To make it compatible we really need [Feature Expressions in
26 26
   Clojure](http://dev.clojure.org/display/design/Feature+Expressions)
27 27
 * JavaScript output is not optimized by the Google Closure Compiler
28 28
   (which is a Java program).
29  
-* The :nodejs compilation target is currently broken for some reason.
  29
+* The :nodejs compilation target is currently broken. However, the
  30
+  `node/run.js` bootstrap script enables compiled CLJS code to be
  31
+  invoked that was not compiled with a :target.
30 32
 * The `analyzer/@namespaces` atom is needed by the compiler but it is
31 33
   not currently generated automatically so we generated a static
32  
-  version.
  34
+  bootstrap version in `src/cljs/bs.cljs`.
33 35
 * Other miscellaneous broken things that haven't been tracked down
34 36
   yet.
35 37
 
@@ -52,11 +54,41 @@ bootstrap pieces with a web REPL like this:
52 54
 
53 55
 ```
54 56
 cd web
55  
-../bin/cljsc ../src/cljs/jsrepl.cljs > jsrepl.js
  57
+../bin/cljsc ../src/cljs/webrepl.cljs > webrepl.js
56 58
 ```
57  
-
58 59
 Now load the `web/jsrepl.html` file in a browser.
59 60
 
  61
+For a REPL in nodejs, build the `src/cljs/noderepl.cljs` code:
  62
+
  63
+```
  64
+cd node
  65
+../bin/cljsc ../src/cljs/noderepl.cljs > noderepl.js
  66
+cp ../src/cljs/goog.js out/
  67
+```
  68
+
  69
+Now use the `run.js` bootstrap code to launch the repl:
  70
+
  71
+```
  72
+./run.js noderepl.js
  73
+```
  74
+
  75
+For direct *.cljs file compilation/evaluation, build the nodecljs.cljs compiler:
  76
+
  77
+```
  78
+cd node
  79
+../bin/cljsc ../src/cljs/nodecljs.cljs > nodecljs.js
  80
+cp ../src/cljs/goog.js out/
  81
+```
  82
+
  83
+You can now use a combination of the `run.js` bootstrap code and
  84
+`nodecljs.js` to compile/evaluate the `hello.cljs` file:
  85
+
  86
+```
  87
+./run.js nodecljs.cljs hello.cljs
  88
+```
  89
+
  90
+
  91
+
60 92
 --------
61 93
 
62 94
 ## What is ClojureScript? ##
3  node/hello.cljs
... ...
@@ -0,0 +1,3 @@
  1
+(def sqr (fn* [x] (* x x)))
  2
+
  3
+(println "hello world" (sqr 16))
25  node/run.js
... ...
@@ -0,0 +1,25 @@
  1
+#!/usr/bin/env node
  2
+
  3
+// Launch a CLJS compiled program using node
  4
+// - First argument is the path to the compiled *.js file
  5
+// - Rest of the arguments are passed to the *main-cli*
  6
+
  7
+(function() {
  8
+    var path = require("path"),
  9
+        targ = process.argv[2],
  10
+        ns = path.basename(targ).replace(/[.]js$/, ""),
  11
+        jsfile = path.resolve("./", targ);
  12
+
  13
+    require('./out/goog.js');
  14
+    require(jsfile);
  15
+    goog.require(ns);
  16
+
  17
+    cljs.core._STAR_print_fn_STAR_ = require("util").print;
  18
+    bs.reset();
  19
+    cljs.user = {};
  20
+    env = cljs.analyzer.empty_env();
  21
+})();
  22
+
  23
+// Call the users's main function
  24
+cljs.core.apply.call(null,cljs.core._STAR_main_cli_fn_STAR_,cljs.core.drop.call(null,3,process.argv));
  25
+
37  src/cljs/goog.js
... ...
@@ -0,0 +1,37 @@
  1
+// Simple module wrapper around Google Closure Library
  2
+// goog/ directory should be in the same directory as this file
  3
+
  4
+goog = exports;
  5
+path_ = require("path");
  6
+googDir_ = path_.join(path_.dirname(module.filename), "goog");
  7
+
  8
+rawLoaded_ = {};
  9
+rawLoad_ = function(file) {
  10
+  var path = path_.resolve(googDir_, file);
  11
+  //console.log("rawLoad_ file:", file, "path:", path);
  12
+
  13
+  if (rawLoaded_[path]) { return; }
  14
+  rawLoaded_[path] = true;
  15
+
  16
+  var contents = require('fs').readFileSync(path);
  17
+  // TODO: cljs.nodejscli needs require, but this is gross
  18
+  global.require = require;
  19
+  process.binding('evals').NodeScript.
  20
+      runInThisContext.call(global, contents, file);
  21
+};
  22
+
  23
+rawLoad_('base.js');
  24
+goog.global = goog.window = global.top = global;
  25
+rawLoad_('deps.js');
  26
+
  27
+// Override goog.require script loader/evaluator
  28
+goog.writeScriptTag_ = function(file) {
  29
+  try {
  30
+    rawLoad_(file);
  31
+  } catch (exc) {
  32
+    console.error('Could not goog.require("' + file + '")\n' + exc.stack);
  33
+    process.exit(1);
  34
+  }
  35
+  return false;
  36
+};
  37
+
27  src/cljs/nodecljs.cljs
... ...
@@ -0,0 +1,27 @@
  1
+(ns nodecljs
  2
+  (:require [cljs.core]
  3
+            [bs :as bs]
  4
+            [cljs.analyzer :as ana]
  5
+            [cljs.compiler :as comp]
  6
+            [cljs.reader :as reader]))
  7
+
  8
+(defn ep [text]
  9
+  (let [r (reader/push-back-reader text)]
  10
+    (loop [str (reader/read r false :eof false)]
  11
+      (when (not= str :eof)
  12
+        (try
  13
+          (let [res (comp/emit-str (ana/analyze js/env str))]
  14
+            (js/eval res))
  15
+          (catch js/Error e
  16
+           (println e)
  17
+           #_(set! *e e)))
  18
+        (recur (reader/read r false :eof false))))))
  19
+
  20
+(defn -main [file & args]
  21
+  ;(set! js/env (assoc js/env :context :expr))
  22
+  (let [fs (js/require "fs")
  23
+        text (.toString (.readFileSync fs file))]
  24
+    (ep text)))
  25
+
  26
+(set! *main-cli-fn* -main)
  27
+
50  src/cljs/noderepl.cljs
... ...
@@ -0,0 +1,50 @@
  1
+(ns noderepl
  2
+  (:require [cljs.core]
  3
+            [bs :as bs]
  4
+            [cljs.analyzer :as ana]
  5
+            [cljs.compiler :as comp]
  6
+            [cljs.reader :as reader]))
  7
+
  8
+(def prompt "cljs.user=> ")
  9
+
  10
+(defn repl-print [text cls]
  11
+  (doseq [line (.split (str text) #"\n")]
  12
+    (when (= "err" cls)
  13
+      (print "ERR: "))
  14
+    (println line)))
  15
+
  16
+(defn postexpr [text]
  17
+  (println prompt text))
  18
+
  19
+(defn ep [text]
  20
+  (try
  21
+   (let [res (comp/emit-str (ana/analyze js/env (reader/read-string text)))]
  22
+     (repl-print (pr-str (js/eval res)) "rtn"))
  23
+   (catch js/Error e
  24
+    (repl-print e "err")
  25
+    #_(set! *e e))))
  26
+
  27
+(defn pep [text]
  28
+  (postexpr text)
  29
+  (ep text))
  30
+
  31
+(defn -main [& args]
  32
+  (set! js/env (assoc js/env :context :expr))
  33
+  (println ";; ClojureScript")
  34
+  (println ";;   - http://github.com/kanaka/clojurescript")
  35
+  (println ";;   - A port of the ClojureScript to ClojureScript")
  36
+  (println ";;   - No macros (yet)")
  37
+  (pep "(+ 1 2)")
  38
+  (pep "(def sqr (fn* [x] (* x x)))")
  39
+  (pep "(sqr 8)")
  40
+  (let [readline (js/require "readline")
  41
+        rl (.createInterface readline js/process.stdin js/process.stdout)]
  42
+    (.setPrompt rl prompt)
  43
+    (.prompt rl)
  44
+    (.on rl "line" (fn [line]
  45
+                     (ep line)
  46
+                     (.prompt rl)))
  47
+    (.on rl "close" (fn [] (.exit js/process 0)))))
  48
+
  49
+(set! *main-cli-fn* -main)
  50
+
6  src/cljs/jsrepl.cljs → src/cljs/webrepl.cljs
... ...
@@ -1,4 +1,4 @@
1  
-(ns jsrepl
  1
+(ns webrepl
2 2
   (:require [cljs.core]
3 3
             [bs :as bs]
4 4
             [cljs.analyzer :as ana]
@@ -91,7 +91,7 @@
91 91
     (println ";;   - A port of the ClojureScript to ClojureScript")
92 92
     (println ";;   - No macros (yet)")
93 93
     (pep log "(+ 1 2)")
94  
-    (pep log "(def double (fn* [x] (* x x)))")
95  
-    (pep log "(double 8)")
  94
+    (pep log "(def sqr (fn* [x] (* x x)))")
  95
+    (pep log "(sqr 8)")
96 96
 
97 97
     (.focus input))))
4  web/build.sh
@@ -2,5 +2,5 @@
2 2
 
3 3
 cd $(dirname ${0})
4 4
 
5  
-echo "Building jsrepl.js"
6  
-time ../bin/cljsc ../src/cljs/jsrepl.cljs > jsrepl.js
  5
+echo "Building webrepl.js"
  6
+time ../bin/cljsc ../src/cljs/webrepl.cljs > webrepl.js
4  web/jsrepl.html
@@ -47,12 +47,12 @@
47 47
     </style>
48 48
     <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
49 49
     <script type="text/javascript" src="out/goog/base.js"></script>
50  
-    <script type="text/javascript" src="jsrepl.js"></script>
  50
+    <script type="text/javascript" src="webrepl.js"></script>
51 51
     <script type="text/javascript">goog.require('cljs.analyzer');</script>
52 52
     <script type="text/javascript">goog.require('cljs.compiler');</script>
53 53
     <script type="text/javascript">goog.require('cljs.reader');</script>
54 54
     <script type="text/javascript">goog.require('bs');</script>
55  
-    <script type="text/javascript">goog.require('jsrepl');</script>
  55
+    <script type="text/javascript">goog.require('webrepl');</script>
56 56
     <script>
57 57
         /*
58 58
       cljs.core._STAR_print_fn_STAR_ = function (x) {

0 notes on commit 78037a6

Please sign in to comment.
Something went wrong with that request. Please try again.