Permalink
Browse files

Initial commit

  • Loading branch information...
wilkerlucio committed Nov 2, 2018
0 parents commit 1adefd0cb8fce78d6da093f94912acc18c5fbc04
Showing with 325 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +34 −0 README.adoc
  3. +9 −0 deps.edn
  4. +10 −0 project.clj
  5. +5 −0 script/release
  6. +1 −0 src/edn_query_language/core.cljc
  7. +47 −0 src/edn_query_language/spec/ast.cljc
  8. +217 −0 src/edn_query_language/spec/query.cljc
@@ -0,0 +1,2 @@
.idea

@@ -0,0 +1,34 @@
:source-highlighter: coderay
:source-language: clojure
:toc:
:toc-placement: preamble
:sectlinks:
:sectanchors:
:sectnums:

= EQL

This repository contains a specification for EQL and a support library to help the development
of tools using it.

== Rational

*EQL* is a query syntax to express information requirements using link:https://github.com/edn-format/edn[edn].

== Specification

== Library

=== Clojure Specs

=== AST Encode/Decode

== History

EQL is a later baptized language, it's initial syntax was defined by link:https://docs.datomic.com/pull.html[Datomic Pull Syntax], later extended
by link:https://github.com/omcljs/om[Om.next].

== Projects using EQL

- link:http://fulcro.fulcrologic.com/[Fulcro]
- link:https://github.com/wilkerlucio/pathom[Pathom]
@@ -0,0 +1,9 @@
{:paths
["src"]

:deps
{}

:aliases
{:provided
{:extra-deps {org.clojure/test.check {:mvn/version "0.10.0-alpha3"}}}}}
@@ -0,0 +1,10 @@
(defproject edn-query-language/edn "1.0.0"
:description "EDN Query Language support library"
:url "https://github.com/edn-query-language/eql"
:license {:name "MIT" :url "https://opensource.org/licenses/MIT"}

:plugins [[lein-tools-deps "0.4.1"]]
:middleware [lein-tools-deps.plugin/resolve-dependencies-with-deps-edn]
:lein-tools-deps/config {:config-files [:install :user :project]}

:jar-exclusions [#"public/.*" #"\.DS_Store"])
@@ -0,0 +1,5 @@
#!/bin/bash

lein vcs tag v
git push --follow-tags
lein deploy clojars
@@ -0,0 +1 @@
(ns edn-query-language.core)
@@ -0,0 +1,47 @@
(ns edn-query-language.spec.ast
(:require [com.wsscode.pathom.specs.query :as q]
[clojure.spec.alpha :as s]))

(s/def ::query ::q/join-query)
(s/def ::key (s/or :prop ::q/property :ident ::q/ident :sym symbol?))
(s/def ::dispatch-key (s/or :prop ::q/property :sym symbol?))
(s/def ::union-key ::q/property)

(s/def ::children
(s/coll-of ::node))

(s/def ::root
(s/and (s/keys :req-un [::type ::children])
#(= :root (:type %))
(fn [x] (every? (comp #(contains? #{:prop :join :call nil} %) :type) (:children x)))))

(defmulti node-type :type)

(defmethod node-type nil [_]
(s/keys :req-un [::key ::dispatch-key]))

(defmethod node-type :prop [_]
(s/keys :req-un [::type ::key ::dispatch-key]))

(defmethod node-type :join [_]
(s/and (s/keys :req-un [::type ::key ::dispatch-key ::query] :opt-un [::children])
#(if (-> % :query first (= :recursion)) % (if (contains? % :children) % false))
(fn [x] (every? (comp #(contains? #{:prop :join :union :call nil} %) :type) (:children x)))))

(defmethod node-type :union [_]
(s/and (s/keys :req-un [::type ::query ::children])
#(every? (comp #{:union-entry} :type) (:children %))))

(defmethod node-type :union-entry [_]
(s/and (s/keys :req-un [::type ::union-key ::query ::children])
(fn [x] (every? (comp #(contains? #{:prop :join :call nil} %) :type) (:children x)))))

(defmethod node-type :call [_]
(s/and (s/keys :req-un [::type ::key ::dispatch-key ::q/params] :opt-un [::query ::children])
(fn [x] (every? (comp #(contains? #{:prop :join :call nil} %) :type) (:children x)))))

(defmethod node-type :root [_]
(s/spec ::root))

(s/def ::type (set (keys (methods node-type))))
(s/def ::node (s/multi-spec node-type :type))
@@ -0,0 +1,217 @@
(ns edn-query-language.spec.query
(:require [clojure.spec.alpha :as s]
[clojure.test.check]
[clojure.test.check.generators :as gen #?@(:cljs [:include-macros true])]
[clojure.test.check.properties]))

(def generators
{::gen-max-depth
4

::gen-property
(fn gen-property [_] gen/keyword-ns)

::gen-special-property
(fn gen-special-property [_] (gen/return '*))

::gen-ident-key
(fn gen-ident-key [_] gen/keyword-ns)

::gen-ident-value
(fn gen-ident-value [_]
(gen/frequency [[15 gen/simple-type-printable]
[1 (gen/return '_)]]))

::gen-ident
(fn gen-ident [{::keys [gen-ident-key gen-ident-value] :as env}]
(gen/tuple
(gen-ident-key env)
(gen-ident-value env)))

::gen-params
(fn gen-params [_] (gen/map gen/any-printable gen/any-printable))

::gen-join-key
(fn gen-join-key [{::keys [gen-property gen-ident gen-join-key-param-expr] :as env}]
(gen/frequency [[10 (gen-property env)]
[3 (gen-ident env)]
[1 (gen-join-key-param-expr env)]]))

::gen-join-key-param-key
(fn gen-join-key-param-key [{::keys [gen-property gen-ident] :as env}]
(gen/one-of [(gen-property env) (gen-ident env)]))

::gen-join-key-param-expr
(fn gen-join-key-param-expr [{::keys [gen-join-key-param-key gen-params] :as env}]
(gen/let [q (gen-join-key-param-key env)
p (gen-params env)]
(list q p)))

::gen-join
(fn gen-join [{::keys [gen-join-key gen-join-query] :as env}]
(gen/map (gen-join-key env) (gen-join-query env) {:num-elements 1}))

::gen-join-query
(fn gen-join-query [{::keys [gen-query gen-union gen-recursion] :as env}]
(gen/frequency [[10 (gen-query env)]
[2 (gen-union env)]
[1 (gen-recursion env)]]))

::gen-union-key
(fn gen-union-key [_] gen/keyword-ns)

::gen-union
(fn gen-union [{::keys [gen-union-key gen-query] :as env}]
(gen/map (gen-union-key env) (gen-query env) {:min-elements 1}))

::gen-depth
(fn gen-depth [_] (gen/large-integer* {:min 1 :max 5}))

::gen-recursion
(fn gen-recursion [{::keys [gen-depth] :as env}]
(gen/one-of [(gen-depth env) (gen/return '...)]))

::gen-param-expr-key
(fn gen-param-expr-key [{::keys [gen-property gen-join gen-ident] :as env}]
(gen/frequency [[20 (gen-property env)]
[8 (gen-join env)]
[4 (gen-ident env)]]))

::gen-param-expr
(fn gen-param-expr [{::keys [gen-param-expr-key gen-params] :as env}]
(gen/let [q (gen-param-expr-key env)
p (gen-params env)]
(list q p)))

::gen-query-expr
(fn gen-query-expr [{::keys [gen-property gen-join gen-ident gen-param-expr gen-special-property gen-mutation]
:as env}]
(gen/frequency [[20 (gen-property env)]
[6 (gen-join env)]
[1 (gen-ident env)]
[2 (gen-param-expr env)]
[1 (gen-mutation env)]
[1 (gen-special-property env)]]))

::gen-query
(fn gen-query [{::keys [gen-property gen-query-expr gen-max-depth] :as env}]
(if (> gen-max-depth 0)
(gen/vector (gen-query-expr (update env ::gen-max-depth dec)))
(gen/vector-distinct (gen-property env))))

::gen-mutation-key
(fn gen-mutation-key [_] gen/symbol)

::gen-mutation-expr
(fn gen-mutation-expr [{::keys [gen-mutation-key gen-params] :as env}]
(gen/let [key (gen-mutation-key env)
val (gen-params env)]
(list key val)))

::gen-mutation-join
(fn mutation-join [{::keys [gen-mutation-expr gen-query] :as env}]
(gen/map (gen-mutation-expr env) (gen-query env) {:num-elements 1}))

::gen-mutation
(fn gen-mutation [{::keys [gen-mutation-expr gen-mutation-join] :as env}]
(gen/frequency [[5 (gen-mutation-expr env)]
[1 (gen-mutation-join env)]]))})

(defn default-gen [name]
#((get generators name) generators))

(defn make-gen [env name]
(let [env (merge generators env)
gen (get env name)]
(assert gen (str "No generator available for " name))
((get env name) env)))

(s/def ::property keyword?)
(s/def ::special-property #{'*})
(s/def ::ident-value (s/with-gen any? (default-gen ::gen-ident-value)))
(s/def ::ident (s/with-gen (s/tuple ::property ::ident-value) (default-gen ::gen-ident)))
(s/def ::key (s/or :prop ::property, :ident ::ident))
(s/def ::join-key (s/or :prop ::property, :ident ::ident, :param-exp ::join-key-param-expr))
(s/def ::join (s/map-of ::join-key ::join-query, :count 1, :conform-keys true))
(s/def ::union (s/map-of ::property ::query, :min-count 1, :conform-keys true))
(s/def ::recursion-depth (s/with-gen nat-int? (default-gen ::gen-depth)))
(s/def ::recursion (s/or :depth ::recursion-depth, :unbounded #{'...}))

(s/def ::join-query
(s/with-gen
(s/or :query ::query
:union ::union
:recursion ::recursion)
(default-gen ::gen-join-query)))

(s/def ::params
(s/with-gen map? (default-gen ::gen-params)))

(s/def ::param-expr-key
(s/with-gen
(s/or :prop ::property
:join ::join
:ident ::ident)
(default-gen ::gen-param-expr-key)))

(s/def ::param-expr
(s/with-gen
(s/and seq? (s/cat :expr ::param-expr-key :params (s/? ::params)))
(default-gen ::gen-param-expr)))

(s/def ::join-key-param-key (s/or :prop ::property :ident ::ident))

(s/def ::join-key-param-expr
(s/with-gen
(s/and seq? (s/cat :expr ::join-key-param-key :params (s/? ::params)))
(default-gen ::gen-join-key-param-expr)))

(s/def ::mutation-key (s/with-gen symbol? (default-gen ::gen-mutation-key)))

(s/def ::mutation-expr
(s/with-gen
(s/and seq? (s/cat :mutate-key ::mutation-key :params (s/? ::params)))
(default-gen ::gen-mutation-expr)))

(s/def ::mutation-join
(s/map-of ::mutation-expr ::query :count 1 :conform-keys true))

(s/def ::mutation
(s/or :mutation ::mutation-expr
:mutation-join ::mutation-join))

(s/def ::query-expr
(s/or :prop ::property
:join ::join
:ident ::ident
:mutation ::mutation
:param-exp ::param-expr
:special ::special-property))

(s/def ::query
(s/coll-of ::query-expr :kind vector? :gen (default-gen ::gen-query)))

(comment
(gen/sample (make-gen {::gen-params
(fn [_] (gen/return {:param "value"}))}
::gen-query)
10)

; example of customizing parts of the query generator
(let [system (assoc generators
::gen-params
(fn [_] (gen/map (gen/elements [:param :foo/param]) gen/string-ascii))

::gen-property
(fn [_] (gen/elements [:id :name :title :foo :bar :other :price :namespaced/value]))

::gen-ident-key
(fn [_] (gen/elements [:user/by-id :other/by-id]))

::gen-ident-value
(fn [_] gen/string-ascii)

::gen-mutation-key
(fn [_] (gen/elements '[do-something create/this-thing operation.on/space])))]
(gen/sample ((::gen-query system) system))))

0 comments on commit 1adefd0

Please sign in to comment.