Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor #14

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions .github/workflows/clojars_deploy.yml

This file was deleted.

20 changes: 20 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: release shape-shifter

on: push

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: 'get version'
run: |
echo "APP_VERSION=$(cat VERSION.md)-beta" >> $GITHUB_ENV

- uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: ${{ env.APP_VERSION }}
prerelease: true
title: "Development Build"
28 changes: 20 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Clojure CI
name: Clojure tests

on: push

Expand All @@ -8,10 +8,22 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: lein deps
- name: Run lint
run: lein lint
- name: Run tests
run: lein test

- uses: actions/checkout@v2

- name: Prepare java
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'

- name: Install clojure tools
uses: DeLaGuardo/[email protected]
with:
cli: 'latest'

- name: Run lint
run: clojure -M:clojure-lsp format --dry

- name: Run tests
run: clojure -M:test
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/target
/classes
/checkouts
profiles.clj
pom.xml
pom.xml.asc
*.jar
Expand All @@ -13,3 +12,6 @@ pom.xml.asc
.hg/
*.iml
.idea/
.clj-kondo/
.cpcache/
.lsp/
1 change: 1 addition & 0 deletions VERSION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v1.0.0
21 changes: 21 additions & 0 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{:deps {org.clojure/clojure {:mvn/version "1.11.1"}
org.babashka/sci {:mvn/version "0.5.36"}
carocad/parcera {:mvn/version "0.11.6"}
clj-antlr/clj-antlr {:mvn/version "0.2.12"}
;org.antlr/antlr4-runtime {:mvn/version "4.11.1"}
}

:aliases {:nREPL {:extra-deps {nrepl/nrepl {:mvn/version "0.9.0"}}}

:outdated {:replace-deps {olical/depot {:mvn/version "2.3.0"}}
:main-opts ["-m" "depot.outdated.main"]}

:clojure-lsp {:replace-deps {com.github.clojure-lsp/clojure-lsp-standalone {:mvn/version "2022.02.01-20.02.32"}}
:main-opts ["-m" "clojure-lsp.main"]}

:test {:extra-paths ["test"]
:extra-deps {org.clojure/test.check {:mvn/version "1.1.1"}
lambdaisland/kaocha {:mvn/version "1.67.1055"}
nubank/mockfn {:mvn/version "0.7.0"}
nubank/state-flow {:mvn/version "5.14.1"}}
:main-opts ["-m" "kaocha.runner"]}}}
42 changes: 0 additions & 42 deletions project.clj

This file was deleted.

23 changes: 23 additions & 0 deletions src/shape_shifter/config.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
(ns shape-shifter.config)

;; hash-map with global configs to shape-shifter
(def ^:dynamic *config* {:interpret-regex? false})

;; hash-map with all wildcards that can be used
;; and the respective function it will translate too.
(def ^:dynamic *wildcards*
{"$" `any?
"$fn" `(s/cat :function #(= 'fn %)
:args (s/coll-of symbol? :kind vector?)
:body any?)
"$macro-keyword" `qualified-keyword?
"$symbol" `symbol?
"$string" `string?
"$set" `set?
"$char" `char?
"$keyword" `keyword?
"$map" `map?
"$number" `number?
"$list" `list?
"$vector" `vector?
"$regex" `s/regex?})
139 changes: 4 additions & 135 deletions src/shape_shifter/core.clj
Original file line number Diff line number Diff line change
@@ -1,138 +1,7 @@
(ns shape-shifter.core
(:gen-class)
(:require [clojure.edn :as edn]
[clojure.spec.alpha :as s]
[clojure.string :as string]
[parcera.core :as parcera]
[sci.core :as sci]
[sci.impl.vars :as vars]
[shape-shifter.utils :as utils]))
(:require
[shape-shifter.transform :as transform]))

(def sns (vars/->SciNamespace 'clojure.spec.alpha nil))

(def spec-namespace
{'def (sci/copy-var s/def sns)
'valid? (sci/copy-var s/valid? sns)
'gen (sci/copy-var s/gen sns)
'cat (sci/copy-var s/cat sns)
'cat-impl (sci/copy-var s/cat-impl sns)
'and (sci/copy-var s/and sns)
'and-spec-impl (sci/copy-var s/and-spec-impl sns)
'* (sci/copy-var s/* sns)
'rep-impl (sci/copy-var s/rep-impl sns)
'or (sci/copy-var s/or sns)
'coll-of (sci/copy-var s/coll-of sns)
'regex? (sci/copy-var s/regex? sns)
'spec (sci/copy-var s/spec sns)
'spec-impl (sci/copy-var s/spec-impl sns)})

(def ^:dynamic *config* {:interpret-regex? false})

(def ^:dynamic *wildcards*
{"$" `any?
"$fn" `(s/cat :function #(= 'fn %)
:args (s/coll-of symbol? :kind vector?)
:body any?)
"$macro-keyword" `qualified-keyword?
"$symbol" `symbol?
"$string" `string?
"$set" `set?
"$char" `char?
"$keyword" `keyword?
"$map" `map?
"$number" `number?
"$list" `list?
"$vector" `vector?
"$regex" `s/regex?})

(defmulti ^:private transform (fn [[key _]] key))

(defn ^:private recursively-transform [coll-of-specs value]
(let [random-key-name (utils/random-keyword 5)
transformed-value (transform value)]
(if transformed-value
(conj coll-of-specs random-key-name transformed-value)
coll-of-specs)))

(defn ^:private pattern->collection [patterns kind]
(->> patterns
(reduce recursively-transform [])
(cons `s/cat)
list
(concat `(s/and ~kind))
list
(cons `s/spec)))

(defn ^:private any-number-of-wildcard [symbol]
(if-let [wildcard (some-> #"\$\w*&"
(re-matches symbol)
(string/replace "&" ""))]
`(s/* ~(get *wildcards* wildcard))))

(defn ^:private ->boolean [value]
(cond
(= "true" value) `true?
(= "false" value) `false?))

(defmethod ^:private transform :list [[_ & values]]
(pattern->collection values `list?))

(defmethod ^:private transform :vector [[_ & values]]
(pattern->collection values `vector?))

(defmethod ^:private transform :set [[_ & values]]
(pattern->collection values `set?))

(defmethod ^:private transform :symbol [[_ value]]
(or (any-number-of-wildcard value)
(->boolean value)
(get *wildcards* value)
`#{(symbol ~value)}))

(defmethod ^:private transform :number [[_ value]]
`#{(edn/read-string ~value)})

(defmethod ^:private transform :string [[_ value]]
`#{~(string/replace value #"\"" "")})

(defmethod ^:private transform :keyword [[_ value]]
`#{~(keyword (string/replace value ":" ""))})

(defmethod ^:private transform :regex [[_ value]]
(if (:interpret-regex? *config*)
(let [regex (-> value
(subs 1)
(string/reverse)
(subs 1)
(string/reverse)
re-pattern)]
`(fn [x#]
(->> x# str (re-matches ~regex) nil? not)))
`#{~(format "#%s" value)}))

(defn build-hash-map-fn [[key value]]
(let [function-name (->> key first name (format "check-key-%s-value") symbol)]
`(fn ~function-name [x#]
(s/valid? ~value (get-in x# ~key)))))

(defmethod ^:private transform :map [[_ & values]]
(->> values
(filter (comp #(not= :whitespace %) first))
(map transform)
(partition 2)
(map build-hash-map-fn)
(cons `s/and)
list
(cons `s/spec)))

(defmethod ^:private transform :whitespace [[_ _]] nil)

(defn pattern->spec
[pattern]
(let [ast (parcera/ast pattern :unhide :literals)]
(-> ast rest first transform str (sci/eval-string {:namespaces {'clojure.spec.alpha spec-namespace}}))))

(comment
(-> "[$keyword& $string&]"
pattern->spec
(s/valid? [:jose :maria "banana" "uva"])))
(defn pattern->spec [pattern]
(transform/->spec pattern))
20 changes: 20 additions & 0 deletions src/shape_shifter/sci.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
(ns shape-shifter.sci
(:require
[sci.core :as sci]))

;; creates a namespace object named clojure.spec.alpha inside sci.
;; copy all public functions from clojure.spec.alpha to our sci-environment.
(def ^:private spec-ns-copy
(let [ns-object (sci/create-ns 'clojure.spec.alpha)]
(-> 'clojure.spec.alpha
ns-publics
(update-vals #(sci/copy-var* % ns-object)))))

;; receive a string and transform it into code
;; uses the spec-ns-copy to resolve functions from the original ns
(defn eval-string [string]
(let [opts {:namespaces {'clojure.spec.alpha spec-ns-copy}}]
(sci/eval-string string opts)))

(comment
(eval-string "(clojure.spec.alpha/valid? string? \"banana\")"))
Loading