:url "http://github.com/noprompt/frak"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
+ :jar-exclusions [#"(?:\.(?:cljx|sw[onp])|cli\.cljs?)"]
:dependencies [[org.clojure/clojure "1.5.1"]]
- :plugins [[lein-cljsbuild "0.3.2"]]
- :source-paths ["src/clj"]
- :profiles {:dev {:dependencies [[criterium "0.4.1"]]}}
- :cljsbuild {:crossovers [frak]
- :crossover-path "crossovers"
- :crossover-jar true
- :builds [{:id "dev"
- :source-paths ["src/clj" "src/cljs"]
- :compiler {:output-to "bin/frak.dev.js"
- :optimizations :simple
- :pretty-print true
- :target :nodejs}}
-
- {:id "prod"
- :source-paths ["src/clj" "src/cljs"]
- :compiler {:output-to "bin/frak.prod.js"
+ :plugins [[lein-cljsbuild "0.3.2"]
+ [com.keminglabs/cljx "0.3.0"]]
+ :source-paths ["src/cljx"]
+ :profiles {:dev {:dependencies [[criterium "0.4.1"]
+ [com.keminglabs/cljx "0.3.0"]]
+ :repl-options {:nrepl-middleware [cljx.repl-middleware/wrap-cljx]}}}
+ :cljx {:builds [{:source-paths ["src/cljx"]
+ :output-path "target/classes"
+ :rules :clj}
+ {:source-paths ["src/cljx"]
+ :output-path "target/classes"
+ :rules :cljs}]}
+ :cljsbuild {:builds [{:id "browser"
+ :source-paths ["target/classes"]
+ :compiler {:output-to "resources/frak.min.js"
+ :optimizations :advanced
+ :pretty-print false}}
+ {:id "node"
+ :source-paths ["target/classes"]
+ :compiler {:output-to "bin/frak"
:externs ["resources/externs/process.js"]
:optimizations :advanced
:pretty-print false
- :target :nodejs}}]}
+ :target :nodejs}}]}
:main frak.cli
:repl-options {:init-ns frak})
+++ /dev/null
-(ns frak.cli
- (:require [frak]))
-
-(defn -main
- "Passes arguments to frak/pattern"
- [& args]
- (println (str (frak/pattern args)))
- (System/exit 0))
+++ /dev/null
-(ns frak.cli
- (:require [clojure.string :as s]
- [frak]))
-
-(def FLAGS
- [[["-e" "--exact"] "Generated pattern requires an exact match"]
- [["-c" "--capture"] "Generated pattern captures"]
- [["-h" "--help"] "Display this help message"]])
-
-(def flag-re (frak/pattern (mapcat first FLAGS)))
-
-(defn parse-args [args]
- (split-with #(re-matches flag-re %) args))
-
-(defn flag-val [s]
- (condp re-seq s
- #"-(?:e|-exact)" {:exact? true}
- #"-(?:c|-capture)" {:capture? true}
- #"-(?:h|-help)" {:help? true}
- {}))
-
-(defn flags->opts [flags]
- (reduce
- (fn [m flag]
- (merge m (flag-val flag)))
- {}
- flags))
-
-(def summary
- (reduce
- (fn [message [flags info]]
- (format "%s\t%s\t%s\n" message (s/join ", " flags) info))
- "Usage: frak <flags*> <strings+>\n\n"
- FLAGS))
-
-(defn exit [code]
- (.exit js/process code))
-
-(defn help []
- (.error js/console summary)
- (exit 0))
-
-(defn start
- [& args]
- (let [[flags words] (parse-args args)
- opts (flags->opts flags)]
- (if (or (empty? args) (:help? opts))
- (help)
- (let [pat (str (frak/pattern words opts))]
- (.log js/console (subs pat 1 (dec (count pat))))))))
-
-(set! *main-cli-fn* start)
([chars]
(re-char-set chars false))
([chars optional?]
- (when (seq chars)
+ (when-let [chars (and (seq chars) (map escape chars))]
(str
(if (= 1 (count chars))
(first chars)
(defmethod render-trie ::single-char
[{:keys [char]}]
- (str char))
+ (escape char))
(defmethod render-trie ::single-child-terminal
[{:keys [char children]}]
(let [child (first children)]
(str
- char
+ (escape char)
(if (and (:terminal? child)
(not (seq (:children child))))
(render-trie
(defmethod render-trie ::single-child-non-terminal
[{:keys [char children]}]
- (let [child (first children)]
- (str char (render-trie child))))
+ (str (escape char) (render-trie (first children))))
(defmethod render-trie :default
[{:keys [char children terminal?]}]
(-> (first v)
(dissoc :char)
(render-trie)
+ ;; Replace instances of "(?:[abc])" with "[abc]".
;; This is such an ugly hack.
(string/replace #"\(\?:?(\[[^\]]+\])\)" "$1"))))
groups)]
- (str char
+ (str (escape char)
(if (= (first grouped) (peek grouped))
(str (peek grouped) (when terminal? "?"))
(re-group grouped terminal?)))))
(str "^" pattern "$")
pattern)))
-(defn pattern
+(defn ^:export pattern
"Construct a regular expression from a collection of strings."
([strs]
(pattern strs {:capture? false, :exact? false}))
--- /dev/null
+(ns frak.cli
+ "Command line interface."
+ (:require [clojure.string :as string]
+ [frak]))
+
+;;;; Utilities
+
+#+clj
+(defn exit [code]
+ (System/exit code))
+
+#+cljs
+(defn exit [code]
+ (.exit js/process code))
+
+#+clj
+(defn printerr [message]
+ (.println *err* message))
+
+#+cljs
+(defn printerr [message]
+ (.error js/console message))
+
+#+clj
+(defn log [message]
+ (println message))
+
+#+cljs
+(defn log [message]
+ (.log js/console message))
+
+;;;; Main
+
+(def main-flags
+ [[["-e" "--exact"] "Generated pattern requires an exact match"]
+ [["-c" "--capture"] "Generated pattern captures"]
+ [["-h" "--help"] "Display this help message"]])
+
+(defn flag-val [s]
+ (condp re-seq s
+ #"-(?:e|-exact)" {:exact? true}
+ #"-(?:c|-capture)" {:capture? true}
+ #"-(?:h|-help)" {:help? true}
+ {}))
+
+(defn flags->opts [flags]
+ (reduce
+ (fn [m flag]
+ (merge m (flag-val flag)))
+ {}
+ flags))
+
+(def summary
+ (reduce
+ (fn [message [flags info]]
+ (format "%s\t%s\t%s\n" message (string/join ", " flags) info))
+ "Usage: frak <flags*> <strings+>\n\n"
+ main-flags))
+
+(defn parse-args [args]
+ (let [flag? (->> (mapcat first main-flags)
+ (frak/pattern)
+ (partial re-matches))]
+ (split-with flag? args)))
+
+(defn -main
+ "Passes arguments to frak/pattern"
+ [& args]
+ (let [[flags words] (parse-args args)
+ opts (flags->opts flags)]
+ (if (or (empty? args) (:help? opts))
+ (log summary)
+ (log (frak/string-pattern words opts)))
+ (exit 0)))
+
+#+cljs (set! *main-cli-fn* -main)