├── .gitignore ├── deps.edn ├── README.md ├── pom.xml └── src └── practicalli └── simple_repl.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | *.jar 5 | *.class 6 | /.cpcache 7 | /.lein-* 8 | /.nrepl-history 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths 2 | ["src" "resources"] 3 | 4 | :deps 5 | {org.clojure/clojure {:mvn/version "1.10.1"}} 6 | 7 | :aliases 8 | {:uberjar 9 | {:extra-deps {seancorfield/depstar {:mvn/version "1.0.94"}} 10 | :main-opts ["-m" "hf.depstar.uberjar" "simple-repl.jar" 11 | "-C" "-m" "practicalli.simple-repl"]}}} 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # practicalli/simple-repl 2 | 3 | A simple implementation of a Read, Evaluate, Print Loop (REPL) for Clojure, written in Clojure. The aim is to gain insight into how the main part of the REPL works. The project is not trying to implement a development tool. 4 | 5 | Download from https://github.com/practicalli/simple-repl. 6 | 7 | ## Developent 8 | Run the project via the command line 9 | 10 | ```shell 11 | clojure -m practicalli.simple-repl 12 | ``` 13 | 14 | Run all the project tests using the Cognitect Labs test runner (or add your own test runner) 15 | 16 | ```shell 17 | clojure -A:test:runner 18 | ``` 19 | 20 | ## Deployment 21 | Build an uberjar using depstar 22 | 23 | ```shell 24 | clojure -A:uberjar 25 | ``` 26 | 27 | Run the application from the uberjar 28 | 29 | ```shell 30 | java -jar simple-repl.jar 31 | ``` 32 | 33 | ## License 34 | 35 | Copyright © 2020 Practicalli 36 | 37 | Distributed under the Creative Commons Attribution Share-Alike 4.0 International 38 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | practicalli 5 | simple-repl 6 | 0.1.0-SNAPSHOT 7 | practicalli/simple-repl 8 | FIXME: my new application. 9 | https://github.com/practicalli/simple-repl 10 | 11 | 12 | Eclipse Public License 13 | http://www.eclipse.org/legal/epl-v10.html 14 | 15 | 16 | 17 | 18 | Jr0cket 19 | 20 | 21 | 22 | https://github.com/practicalli/simple-repl 23 | scm:git:git://github.com/practicalli/simple-repl.git 24 | scm:git:ssh://git@github.com/practicalli/simple-repl.git 25 | HEAD 26 | 27 | 28 | 29 | org.clojure 30 | clojure 31 | 1.10.1 32 | 33 | 34 | 35 | src 36 | 37 | 38 | 39 | clojars 40 | https://repo.clojars.org/ 41 | 42 | 43 | sonatype 44 | https://oss.sonatype.org/content/repositories/snapshots/ 45 | 46 | 47 | 48 | 49 | clojars 50 | Clojars repository 51 | https://clojars.org/repo 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/practicalli/simple_repl.clj: -------------------------------------------------------------------------------- 1 | (ns practicalli.simple-repl 2 | (:gen-class)) 3 | 4 | 5 | (defn -main 6 | "A naive Clojure REPL implementation. Enter `:quit` 7 | to exit." 8 | [] 9 | (print (str (ns-name *ns*) " λ ")) 10 | (flush) 11 | (let [result (eval (read))] 12 | (when (not= :quit result) 13 | (println result) 14 | (recur)))) 15 | 16 | 17 | ;; Usage 18 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 19 | 20 | ;; Call the `-main` function to start the REPL in a REPL 21 | 22 | (comment 23 | 24 | (-main) 25 | 26 | ) 27 | 28 | ;; Or run the project on the comand line, see README.md 29 | 30 | 31 | ;; Design Journal 32 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 33 | 34 | (comment 35 | 36 | ;; Use the -main function as the repl 37 | ;; - app template used to create the project 38 | 39 | 40 | ;; Create a prompt for the repl 41 | 42 | ;; `print` sends a value to the standard out 43 | 44 | ;; The REPL prompt shows the current namespace 45 | 46 | (ns-name 'practicalli.simple-repl) 47 | ;; => practicalli.simple-repl 48 | 49 | ;; The *ns* is dynamically assigned to the current namespace in the REPL 50 | ;; so `ns-name` with the argument `*ns*` will return the current namespace 51 | 52 | (ns-name *ns*) 53 | ;; => practicalli.simple-repl 54 | 55 | ;; When running this application on the command line, `user` will be the default namespace 56 | ;; as its the Clojure REPL default namespace 57 | 58 | ;; `flush` Flushes the output stream that is the current value of *out* 59 | ;; this ensures the REPL prompt is displayed each time around the loop. 60 | 61 | ;; The prompt is completed, now we need to get the clojure expressions and evaluate them 62 | 63 | 64 | ;; (read) will get the value from the standard in, *in* 65 | 66 | ;; As we are entering in Clojure expressions, then we can simply pass the value of standard in to eval 67 | 68 | (eval '(map inc [1 2 3])) 69 | ;; => (2 3 4) 70 | 71 | ;; println will sent the result of the eval function to the standard out. 72 | 73 | (println (eval (read))) 74 | 75 | ;; Put this code into a -main function definition 76 | 77 | (defn -main 78 | [] 79 | (print (str (ns-name *ns*) " λ ")) 80 | (flush) 81 | (println (eval (read)))) 82 | 83 | ;; So far there is a Read Evaluate and Print part of the REPL. 84 | 85 | ;; Using `recur` provides a simple way to have a loop, as it will continually call -main 86 | ;; The loop is stateless, so no need to pass any data back via the recur 87 | 88 | (defn -main 89 | [] 90 | (print (str (ns-name *ns*) " λ ")) 91 | (flush) 92 | (println (eval (read))) 93 | (recur)) 94 | 95 | ;; The REPL works, although improvements can be made 96 | 97 | ;; There is no way to stop the loop so the REPL is eternal. 98 | ;; A `when` expression is a simple way to add a conditional break. 99 | ;; The condition checks if something other than `:quit` keyword has been entered at the prompt 100 | 101 | (defn -main 102 | [] 103 | (print (str (ns-name *ns*) " λ ")) 104 | (flush) 105 | (when (not= :quit (read)) 106 | (println (eval (read))) 107 | (recur))) 108 | 109 | ;; This is not quite right as we need to read the expression typed at the prompt twice. 110 | 111 | ;; Use a let expression to capture the value read from the standard in 112 | 113 | (let [expression (read) 114 | result (eval expression)]) 115 | 116 | ;; Or even simpler 117 | (let [result (eval (read))]) 118 | 119 | ;; Then the value referred to by result can be used in several places 120 | 121 | (defn -main 122 | [] 123 | (print (str (ns-name *ns*) " λ ")) 124 | (flush) 125 | (let [result (eval (read))] 126 | (when (not= :quit result) 127 | (println result) 128 | (recur)))) 129 | 130 | ;; Now we have a working REPL that will end when it receives the keyword :quit 131 | 132 | ) 133 | --------------------------------------------------------------------------------