<commit_before>;; trammel.clj -- Contracts programming library for Clojure

;; by <NAME> - <http://fogus.me/fun/trammel>

; Copyright (c) <NAME>, 2010-2012. All rights reserved.  The use
; and distribution terms for this software are covered by the Eclipse
; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file COPYING the root of this
; distribution.  By using this software in any fashion, you are
; agreeing to be bound by the terms of this license.  You must not
; remove this notice, or any other, from this software.

(ns trammel.core
  "The core contracts programming functions and macros for Trammel."
  (:use [trammel.funcify :only (funcify)]
        [trammel.factors]
        [trammel.utils])
  (:require [fogus.thneed.utils :as fogus]))

;; HOF support

(defrecord HOC [args argspec ctx])

(defmacro _ [args & argspec]
  `(HOC. '~args (vec '~argspec) nil))

(comment
  (_ [n] even? number? => number?)
)

;; # base functions and macros

(defn- build-pre-post-map
  "Takes a vector of the form `[pre ... => post ...]` and infers the expectations described
   therein.  The map that comes out will look like Clojure's default pre- and post-conditions
   map.  If the argument is already a map then it's assumed that the default pre/post map is used and
   as a result is used directly without manipulation.
  "
  [cnstr]
  (if (vector? cnstr)
    (let [[L M R] (partition-by #{'=>} cnstr)]
      {:pre  (when (not= L '(=>)) L)
       :post (if (= L '(=>)) M R)})
    cnstr))

(defn tag-hocs [cnstr]
    (map (fn [form]
           (if (and (seq? form) (= '_ (first form)))
             (list 'fn? (second form))
             form))
         cnstr))

(defn- build-constraints-map 
  "Takes the corresponding arglist and a vector of the contract expectations, the latter of which looks 
   like any of the following:

       [(= 0 _)] or [number?] ;; only the pre-
       [number? => number?]   ;; a pre- and post-
       [=> number?]           ;; only a post-
       [foo bar => baz]       ;; 2 pre- and 1 post-

   It then takes this form and builds a pre- and post-condition map of the form:

       {:pre  [(foo x) (bar x)]
        :post [(baz %)]}
  "
  [args cnstr]
  (let [cnstr (vec (tag-hocs cnstr))]
    #_(swank.core/break)
    [args 
     (->> (build-pre-post-map cnstr)
          (fogus/manip-map (partial funcify '[%]) [:post])
          (fogus/manip-map (partial funcify args) [:pre]))]))

(comment
  (let [hoc (_ [n] even? number? => number?)]
    (build-contract 'hof (build-constraints-map (:args hoc) (:argspec hoc))))

  (macroexpand '(contract my-map "mymap" [fun sq] [(_ [n] number? => number?) (seq sq) => seq]))

  (contract my-map "mymap" [fun sq] [(_ fun [n] number? => number?) (seq sq) => seq])

  (def cnstr '[(_ fun [n] number? => number?) (seq sq) => seq])

)

(defn- build-contract 
  "Expects a seq representing an arity-based expectation of the form:

        [[x] {:pre [(foo x)] :post [(bar %)]}]

   It then uses this data to build another list reprsenting a specific arity body
   for a higher-order function with attached pre- and post-conditions that directly 
   calls the function passed in:

        ([f x] {:pre [(foo x)] :post [(bar %)]} (f x))

   However, the picture is slightly more compilcated than that because Clojure does
   not have disparate pre-/post-conditions.  Therefore, it's on me to provide a
   slightly more crystaline picture of the condition failure when it occurs.  As a
   result the body of the contract is interwoven with `try`/`catch` blocks to catch
   and examine the contents of `AssertionErrors` and based on context rethrow them
   with more information.  At the moment this information only takes the form of a
   richer assertion message.
  "
  [message cnstr]
  (let [[args pre-post-map] cnstr]
    `(~(into '[f] args)
      (let [ret# (try
                   ((fn []
                      ~(select-keys pre-post-map [:pre])
                      ~(list* 'f (mapcat (fn [item]
                                           (cond (symbol? item) [item]
                                                 (map? item) [(:as item)]
                                                 :else [item]))
                                         args))))
                   (catch AssertionError pre#
                     (throw (AssertionError. (str "Pre-condition failure: " ~message \newline (.getMessage pre#))))))]
        (try
          ((fn []
             ~(select-keys pre-post-map [:post])
             ret#))<commit_msg>hardcoded f causes issue in cnstr'd records<commit_after>;; trammel.clj -- Contracts programming library for Clojure

;; by <NAME> - <http://fogus.me/fun/trammel>

; Copyright (c) <NAME>, 2010-2012. All rights reserved.  The use
; and distribution terms for this software are covered by the Eclipse
; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file COPYING the root of this
; distribution.  By using this software in any fashion, you are
; agreeing to be bound by the terms of this license.  You must not
; remove this notice, or any other, from this software.

(ns trammel.core
  "The core contracts programming functions and macros for Trammel."
  (:use [trammel.funcify :only (funcify)]
        [trammel.factors]
        [trammel.utils])
  (:require [fogus.thneed.utils :as fogus]))

;; HOF support

(defrecord HOC [args argspec ctx])

(defmacro _ [args & argspec]
  `(HOC. '~args (vec '~argspec) nil))

(comment
  (_ [n] even? number? => number?)
)

;; # base functions and macros

(defn- build-pre-post-map
  "Takes a vector of the form `[pre ... => post ...]` and infers the expectations described
   therein.  The map that comes out will look like Clojure's default pre- and post-conditions
   map.  If the argument is already a map then it's assumed that the default pre/post map is used and
   as a result is used directly without manipulation.
  "
  [cnstr]
  (if (vector? cnstr)
    (let [[L M R] (partition-by #{'=>} cnstr)]
      {:pre  (when (not= L '(=>)) L)
       :post (if (= L '(=>)) M R)})
    cnstr))

(defn tag-hocs [cnstr]
    (map (fn [form]
           (if (and (seq? form) (= '_ (first form)))
             (list 'fn? (second form))
             form))
         cnstr))

(defn- build-constraints-map 
  "Takes the corresponding arglist and a vector of the contract expectations, the latter of which looks 
   like any of the following:

       [(= 0 _)] or [number?] ;; only the pre-
       [number? => number?]   ;; a pre- and post-
       [=> number?]           ;; only a post-
       [foo bar => baz]       ;; 2 pre- and 1 post-

   It then takes this form and builds a pre- and post-condition map of the form:

       {:pre  [(foo x) (bar x)]
        :post [(baz %)]}
  "
  [args cnstr]
  (let [cnstr (vec (tag-hocs cnstr))]
    #_(swank.core/break)
    [args 
     (->> (build-pre-post-map cnstr)
          (fogus/manip-map (partial funcify '[%]) [:post])
          (fogus/manip-map (partial funcify args) [:pre]))]))

(comment
  (let [hoc (_ [n] even? number? => number?)]
    (build-contract 'hof (build-constraints-map (:args hoc) (:argspec hoc))))

  (macroexpand '(contract my-map "mymap" [fun sq] [(_ [n] number? => number?) (seq sq) => seq]))

  (contract my-map "mymap" [fun sq] [(_ fun [n] number? => number?) (seq sq) => seq])

  (def cnstr '[(_ fun [n] number? => number?) (seq sq) => seq])

)

(defn- build-contract 
  "Expects a seq representing an arity-based expectation of the form:

        [[x] {:pre [(foo x)] :post [(bar %)]}]

   It then uses this data to build another list reprsenting a specific arity body
   for a higher-order function with attached pre- and post-conditions that directly 
   calls the function passed in:

        ([f x] {:pre [(foo x)] :post [(bar %)]} (f x))

   However, the picture is slightly more compilcated than that because Clojure does
   not have disparate pre-/post-conditions.  Therefore, it's on me to provide a
   slightly more crystaline picture of the condition failure when it occurs.  As a
   result the body of the contract is interwoven with `try`/`catch` blocks to catch
   and examine the contents of `AssertionErrors` and based on context rethrow them
   with more information.  At the moment this information only takes the form of a
   richer assertion message.
  "
  [message cnstr]
  (let [[args pre-post-map] cnstr
        _f_ (gensym)]
    `(~(into (vector _f_) args)
      (let [ret# (try
                   ((fn []
                      ~(select-keys pre-post-map [:pre])
                      ~(list* _f_ (mapcat (fn [item]
                                           (cond (symbol? item) [item]
                                                 (map? item) [(:as item)]
                                                 :else [item]))
                                         args))))
                   (catch AssertionError pre#
                     (throw (AssertionError. (str "Pre-condition failure: " ~message \newline (.getMessage pre#))))))]
        (try
          ((fn []
             ~(select-keys pre-post-map [:post])
             ret#))
{'fraction_non_alphanumeric': 0.1301354895104895, 'fraction_numerical': 0.003277972027972028, 'mean_word_length': 2.3283636363636364, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6678290', 'n_tokens_mistral': 2950, 'n_tokens_neox': 2789, 'n_words': 1086}