Refactor using reflection support (#110)

This commit is contained in:
Michiel Borkent 2019-11-16 00:25:36 +01:00 committed by GitHub
parent a9347c7753
commit a0fa854969
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 130 additions and 262 deletions

View file

@ -36,7 +36,7 @@ Goals:
Non-goals: Non-goals:
* Performance * Performance
* Provide a mixed Clojure/bash DSL (see portability). * Provide a mixed Clojure/bash DSL (see portability).
* Replace existing shells. Babashka is a tool you can use inside existing shells like bash and it is designed to play well with them. It does not aim to replace them. * Replace existing shells. Babashka is a tool you can use inside existing shells like bash and it is designed to play well with them. It does not aim to replace them.
Reasons why babashka may not be the right fit for your use case: Reasons why babashka may not be the right fit for your use case:
@ -162,24 +162,26 @@ namespaces. If not all vars are available, they are enumerated explicitly.
- [`clojure.tools.cli`](https://github.com/clojure/tools.cli) aliased as `tools.cli` - [`clojure.tools.cli`](https://github.com/clojure/tools.cli) aliased as `tools.cli`
- [`clojure.data.csv`](https://github.com/clojure/data.csv) aliased as `csv` - [`clojure.data.csv`](https://github.com/clojure/data.csv) aliased as `csv`
From Java the following is available: The following Java classes are available:
- `Integer`: ```
- static methods: `parseInt` ArithmeticException
- `File`: AssertionError
- static methods: `createTempFile` Boolean
- instance methods: `.canRead`, `.canWrite`, `.delete`, Class
`.deleteOnExit`, `.exists`, `.getAbsoluteFile`, `.getCanonicalFile`, Double
`.getCanonicalPath`, `.getName`, `.getParent`, `.getParentFile`, Exception
`.getPath`, `.isAbsolute`, `.isDirectory`, `.isFile`, `.isHidden`, clojure.lang.ExceptionInfo
`.lastModified`, `.length`, `.list`, `.listFiles`, `.mkdir`, java.io.File
`.mkdirs`, `.renameTo`, `.setLastModified`, `.setReadOnly`, Integer
`.setReadable`, `.toPath`, `.toURI`. java.io.File
- `System`: java.util.regex.Pattern
- static methods: `exit`, `getProperty`, `setProperty`, `getProperties`, `getenv` String
- `Thread`: System
- static methods: `sleep` Thread
- `java.util.regex.Pattern` (all static and instance methods and constants) ```
More classes can be added by request.
Special vars: Special vars:
@ -442,8 +444,7 @@ Differences with Clojure:
- No first class vars. Note that you can define and redefine global values with - No first class vars. Note that you can define and redefine global values with
`def` / `defn`, but there is no `var` indirection. `def` / `defn`, but there is no `var` indirection.
- Java classes and interop are not available. For a selection of classes we mimic constructors and interop by having - A subset of Java classes are supported.
functions like `Exception.` and `.getCanonicalPath`.
- Only the `clojure.core`, `clojure.set` and `clojure.string` namespaces are - Only the `clojure.core`, `clojure.set` and `clojure.string` namespaces are
available from Clojure. available from Clojure.

View file

@ -1,4 +1,26 @@
[ [
{
"name":"java.lang.ArithmeticException",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.AssertionError",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.Boolean",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.io.BufferedReader",
"allPublicMethods":true
},
{ {
"name": "java.lang.Class", "name": "java.lang.Class",
"allDeclaredConstructors": true, "allDeclaredConstructors": true,
@ -7,9 +29,45 @@
"allPublicMethods": true "allPublicMethods": true
}, },
{ {
"name":"java.io.BufferedReader", "name":"java.lang.Double",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.Exception",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name": "clojure.lang.ExceptionInfo",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.Integer",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.io.File",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.util.concurrent.LinkedBlockingQueue",
"allPublicMethods":true "allPublicMethods":true
}, },
{
"name":"java.util.regex.Pattern",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{ {
"name":"java.lang.Process", "name":"java.lang.Process",
"allPublicMethods":true "allPublicMethods":true
@ -20,14 +78,25 @@
}, },
{ {
"name":"java.lang.String", "name":"java.lang.String",
"allPublicMethods":true "allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
}
,
{
"name":"java.lang.System",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.Thread",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
}, },
{ {
"name":"java.lang.UNIXProcess", "name":"java.lang.UNIXProcess",
"allPublicMethods":true "allPublicMethods":true
},
{
"name":"java.util.concurrent.LinkedBlockingQueue",
"allPublicMethods":true
} }
] ]

2
sci

@ -1 +1 @@
Subproject commit cb1dc3139a53c680b2bdc1038d2c6e26b975ee8e Subproject commit 419d39d24f9e3676d9922218e747fc277a921a72

View file

@ -1,87 +0,0 @@
(ns babashka.impl.File
{:no-doc true}
(:refer-clojure :exclude [list])
(:require [clojure.java.io :as io]
[clojure.string :as str]))
(set! *warn-on-reflection* true)
;; also see https://gist.github.com/plexus/68594ba9b5e3f0d63fe84dcae31c4a53
(defmacro gen-wrapper-fn [method-name]
`(defn ~method-name {:bb/export true}
[~(with-meta 'x {:tag 'java.io.File})]
(~(symbol (str "." method-name)) ~'x)))
(defmacro gen-wrapper-fn-2 [method-name]
`(defn ~method-name {:bb/export true}
[~(with-meta 'x {:tag 'java.io.File}) ~'y]
(~(symbol (str "." method-name)) ~'x ~'y)))
(gen-wrapper-fn canExecute)
(gen-wrapper-fn canRead)
(gen-wrapper-fn canWrite)
(defn createTempFile
([^String prefix ^String suffix]
(java.io.File/createTempFile prefix suffix))
([^String prefix ^String suffix ^java.io.File dir]
(java.io.File/createTempFile prefix suffix dir)))
(gen-wrapper-fn delete)
(gen-wrapper-fn deleteOnExit)
(gen-wrapper-fn exists)
(gen-wrapper-fn getAbsoluteFile)
(gen-wrapper-fn getCanonicalFile)
(gen-wrapper-fn getCanonicalPath)
(gen-wrapper-fn getName)
(gen-wrapper-fn getParent)
(gen-wrapper-fn getParentFile)
(gen-wrapper-fn getPath)
(gen-wrapper-fn isAbsolute)
(gen-wrapper-fn isDirectory)
(gen-wrapper-fn isFile)
(gen-wrapper-fn isHidden)
(gen-wrapper-fn lastModified)
(gen-wrapper-fn length)
(gen-wrapper-fn list)
(gen-wrapper-fn listFiles)
(gen-wrapper-fn mkdir)
(gen-wrapper-fn mkdirs)
(gen-wrapper-fn-2 renameTo)
(defn ^:bb/export setExecutable
([^java.io.File f b]
(.setExecutable f b))
([^java.io.File f b ownerOnly]
(.setExecutable f b ownerOnly)))
(gen-wrapper-fn-2 setLastModified)
(gen-wrapper-fn-2 setReadable)
(gen-wrapper-fn setReadOnly)
(defn ^:bb/export setWritable
([^java.io.File f b]
(.setWritable f b))
([^java.io.File f b ownerOnly]
(.setWritable f b ownerOnly)))
(gen-wrapper-fn toPath)
(gen-wrapper-fn toURI)
(def file-bindings
(-> (reduce (fn [acc [k v]]
(if (-> v meta :bb/export)
(assoc acc (symbol (str "." k))
@v)
acc))
{}
(ns-publics *ns*))
;; static methods
(assoc (symbol "File/createTempFile") createTempFile)
(assoc (symbol "java.io.File/createTempFile") createTempFile)))
(comment
(canRead (clojure.java.io/file "README.md"))
(canWrite (clojure.java.io/file "README.md"))
(exists (clojure.java.io/file "README.md"))
(renameTo (io/file "/tmp/script2.clj") (io/file "/tmp/script.clj"))
(.setWritable (io/file "/tmp/script.clj") true true)
(meta #'toURI)
(meta #'canWrite)
;; for README.md:
(str/join ", " (map #(format "`%s`" %) (sort (keys file-bindings))))
)

View file

@ -1,17 +0,0 @@
(ns babashka.impl.Integer
{:no-doc true}
(:refer-clojure :exclude [list]))
(set! *warn-on-reflection* true)
(defn parseInt
([^String x] (Integer/parseInt x))
([^String x ^long radix]
(Integer/parseInt x radix)))
(def integer-bindings
{'Integer/parseInt parseInt})
(comment
)

View file

@ -1,72 +0,0 @@
(ns babashka.impl.Pattern
{:no-doc true})
(set! *warn-on-reflection* true)
(defmacro gen-wrapper-fn [method-name]
`(defn ~method-name {:bb/export true}
[~(with-meta 'x {:tag 'java.util.regex.Pattern})]
(~(symbol (str "." method-name)) ~'x)))
(defmacro gen-wrapper-fn-2 [method-name]
`(defn ~method-name {:bb/export true}
[~(with-meta 'x {:tag 'java.util.regex.Pattern}) ~'y]
(~(symbol (str "." method-name)) ~'x ~'y)))
(defmacro gen-constants [& constant-names]
(let [defs (for [constant-name constant-names
:let [full-name (symbol (str "java.util.regex.Pattern/" constant-name))
meta {:bb/export-constant true
:bb/full-name (list 'quote full-name)}
constant-name (with-meta constant-name meta)]]
`(def ~constant-name
~full-name))]
`(do ~@defs)))
(gen-constants CANON_EQ CASE_INSENSITIVE COMMENTS DOTALL LITERAL MULTILINE
UNICODE_CASE UNICODE_CHARACTER_CLASS UNIX_LINES)
(gen-wrapper-fn asPredicate)
(defn compile*
([^String s]
(java.util.regex.Pattern/compile s))
([^String s ^long flags]
(java.util.regex.Pattern/compile s flags)))
(gen-wrapper-fn flags)
(gen-wrapper-fn-2 matcher)
(defn matches [^String regex ^CharSequence input]
(java.util.regex.Pattern/matches regex input))
(gen-wrapper-fn pattern)
(defn pattern-quote [s]
(java.util.regex.Pattern/quote s))
(defn ^:bb/export split
([^java.util.regex.Pattern p ^CharSequence input]
(.split p input))
([^java.util.regex.Pattern p ^CharSequence input ^long limit]
(.split p input limit)))
(gen-wrapper-fn-2 splitAsStream)
(def pattern-bindings
(-> (reduce (fn [acc [k v]]
(let [m (meta v)]
(cond (:bb/export m)
(assoc acc (symbol (str "." k))
@v),
(:bb/export-constant m)
(assoc acc (symbol (:bb/full-name m))
@v)
:else acc)))
{}
(ns-publics *ns*))
;; static method
(assoc (symbol "java.util.regex.Pattern/compile") compile*)
(assoc (symbol "java.util.regex.Pattern/quote") pattern-quote)
(assoc (symbol "java.util.regex.Pattern/matches") matches)))

View file

@ -1,28 +0,0 @@
(ns babashka.impl.System
{:no-doc true})
(defn get-env
([] (System/getenv))
([s] (System/getenv s)))
(defn get-property
([s]
(System/getProperty s))
([s d]
(System/getProperty s d)))
(defn set-property [k v]
(System/setProperty k v))
(defn get-properties []
(System/getProperties))
(defn exit [n]
(throw (ex-info "" {:bb/exit-code n})))
(def system-bindings
{'System/getenv get-env
'System/getProperty get-property
'System/setProperty set-property
'System/getProperties get-properties
'System/exit exit})

View file

@ -1,9 +0,0 @@
(ns babashka.impl.Thread
{:no-doc true})
(defn sleep
([millis] (Thread/sleep millis))
([millis nanos] (Thread/sleep millis nanos)))
(def thread-bindings
{'Thread/sleep sleep})

View file

@ -1,20 +1,12 @@
(ns babashka.main (ns babashka.main
{:no-doc true} {:no-doc true}
(:require (:require
[babashka.impl.File :refer [file-bindings]]
[babashka.impl.Integer :refer [integer-bindings]]
[babashka.impl.Double :refer [double-bindings]]
[babashka.impl.Boolean :refer [boolean-bindings]]
[babashka.impl.Pattern :refer [pattern-bindings]]
[babashka.impl.System :refer [system-bindings]]
[babashka.impl.Thread :refer [thread-bindings]]
[babashka.impl.async :refer [async-namespace]] [babashka.impl.async :refer [async-namespace]]
[babashka.impl.clojure.core :refer [core-extras]] [babashka.impl.clojure.core :refer [core-extras]]
[babashka.impl.clojure.java.io :refer [io-namespace]] [babashka.impl.clojure.java.io :refer [io-namespace]]
[babashka.impl.clojure.stacktrace :refer [print-stack-trace]] [babashka.impl.clojure.stacktrace :refer [print-stack-trace]]
[babashka.impl.conch :refer [conch-namespace]] [babashka.impl.conch :refer [conch-namespace]]
[babashka.impl.csv :as csv] [babashka.impl.csv :as csv]
[babashka.impl.exceptions :refer [exception-bindings]]
[babashka.impl.pipe-signal-handler :refer [handle-pipe! pipe-signal-received?]] [babashka.impl.pipe-signal-handler :refer [handle-pipe! pipe-signal-received?]]
[babashka.impl.socket-repl :as socket-repl] [babashka.impl.socket-repl :as socket-repl]
[babashka.impl.tools.cli :refer [tools-cli-namespace]] [babashka.impl.tools.cli :refer [tools-cli-namespace]]
@ -150,16 +142,6 @@ Everything after that is bound to *command-line-args*."))
(str/replace x #"^#!.*" "")) (str/replace x #"^#!.*" ""))
(throw (Exception. (str "File does not exist: " file)))))) (throw (Exception. (str "File does not exist: " file))))))
(def bindings
(merge system-bindings
file-bindings
thread-bindings
integer-bindings
double-bindings
boolean-bindings
exception-bindings
pattern-bindings))
(defn read-edn [] (defn read-edn []
(edn/read {;;:readers *data-readers* (edn/read {;;:readers *data-readers*
:eof ::EOF} *in*)) :eof ::EOF} *in*))
@ -167,7 +149,7 @@ Everything after that is bound to *command-line-args*."))
(defn load-file* [ctx file] (defn load-file* [ctx file]
(let [s (slurp file)] (let [s (slurp file)]
(sci/eval-string s ctx))) (sci/eval-string s ctx)))
(defn eval* [ctx form] (defn eval* [ctx form]
(sci/eval-string (pr-str form) ctx)) (sci/eval-string (pr-str form) ctx))
@ -180,6 +162,9 @@ Everything after that is bound to *command-line-args*."))
;; hang until SIGINT ;; hang until SIGINT
@(promise))) @(promise)))
(defn exit [n]
(throw (ex-info "" {:bb/exit-code n})))
(defn main (defn main
[& args] [& args]
(handle-pipe!) (handle-pipe!)
@ -223,10 +208,36 @@ Everything after that is bound to *command-line-args*."))
'me.raynes.conch.low-level conch-namespace 'me.raynes.conch.low-level conch-namespace
'clojure.core.async async-namespace 'clojure.core.async async-namespace
'clojure.data.csv csv/csv-namespace} 'clojure.data.csv csv/csv-namespace}
:bindings (assoc bindings '*command-line-args* command-line-args) :bindings {'*command-line-args* command-line-args
'java.lang.System/exit exit ;; override exit, so we have more control
'System/exit exit}
:env env :env env
:features #{:bb}} :features #{:bb}
ctx (update ctx :bindings assoc 'eval #(eval* ctx %) :classes {'java.lang.ArithmeticException ArithmeticException
'java.lang.AssertionError AssertionError
'java.lang.Boolean Boolean
'java.lang.Class Class
'java.lang.Double Double
'java.lang.Exception Exception
'clojure.lang.ExceptionInfo clojure.lang.ExceptionInfo
'java.lang.Integer Integer
'java.io.File java.io.File
'java.util.regex.Pattern java.util.regex.Pattern
'java.lang.String String
'java.lang.System System
'java.lang.Thread Thread}
:imports '{ArithmeticException java.lang.ArithmeticException
AssertionError java.lang.AssertionError
Boolean java.lang.Boolean
Class java.lang.Class
Double java.lang.Double
Exception java.lang.Exception
Integer java.lang.Integer
File java.io.File
String java.lang.String
System java.lang.System
Thread java.lang.Thread}}
ctx (update ctx :bindings assoc 'eval #(eval* ctx %)
'load-file #(load-file* ctx %)) 'load-file #(load-file* ctx %))
_preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim) (sci/eval-string ctx)) _preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim) (sci/eval-string ctx))
exit-code exit-code