<commit_before>(ns aurora.compiler
  (:require [aurora.jsth :as jsth]
            [aurora.ast :as ast]
            [aurora.util :refer [map!]])
  (:require-macros [aurora.macros :refer [for! check deftraced]]))

;; compiler

(let [next (atom 0)]
  (defn new-id []
    (if js/window.uuid
      (.replace (js/uuid) (js/RegExp. "-" "gi") "_")
      (swap! next inc))))

(deftraced id->value [id] [id]
  (check id)
  (symbol (str "value_" id)))

(deftraced id->temp [id] [id]
  (check id)
  (symbol (str "temp_" id)))

(deftraced ref->jsth [index x] [x]
  (case (:type x)
    :ref/id (id->value (:id x))
    :ref/js (symbol (:js x))
    (check false)))

(deftraced tag->jsth [index x] [x]
  `(cljs.core.keyword ~(:id x) ~(:name x)))

(deftraced data->jsth [index x] [x]
  (cond
   (= :tag (:type x)) (tag->jsth index x)
   (#{:ref/id :ref/js} (:type x)) (ref->jsth index x)
   (or (true? x) (false? x)) x
   (number? x) x
   (string? x) x
   ;;it's a table
   (get x "headers") `(aurora.runtime.table.table.call nil (cljs.core.PersistentVector.fromArray
                                              ~(vec (map! #(data->value-jsth index %) (x "headers"))))
                                             (cljs.core.PersistentVector.fromArray
                                              ~(vec (map! #(data->value-jsth index %) (x "columns"))))
                                             (cljs.core.PersistentVector.fromArray
                                              ~(vec (map! #(data->value-jsth index %) (x "rows"))))
                                             )
   (vector? x) `(cljs.core.PersistentVector.fromArray
                 ~(vec (map! #(data->jsth index %) x)))
   (map? x) `(cljs.core.PersistentHashMap.fromArrays
              ~(vec (map! #(data->jsth index %) (keys x)))
              ~(vec (map! #(data->jsth index %) (vals x))))
   :else (check false)))

(deftraced constant->jsth [index x id] [x id]
  (check (= :constant (:type x)))
  `(let! ~(id->value id) ~(data->jsth index (:data x))))

(deftraced js-data->jsth [index x] [x]
  (cond
   (nil? x) nil
   :else (data->jsth index x)))

(deftraced call->jsth [index x id] [x id]
  (let [arg->jsth (case (:type (:ref x))
                    :ref/id data->jsth
                    :ref/js js-data->jsth
                    (check false))] ;; allowed to use nil when calling cljs
    `(let! ~(id->value id) (~(data->jsth index (:ref x)) ~@(map! #(arg->jsth index %) (:args x))))))

(deftraced test->jsth [pred] [pred]
  `(if (not ~pred) (throw failure)))

(deftraced pattern->jsth [index x input] [x input]
  (cond
   (= :match/any (:type x)) `(do)
   (= :match/bind (:type x)) `(do
                                (let! ~(id->value (:id x)) ~(id->value input))
                                (set! (.. frame.vars ~(id->value (:id x))) ~(id->value (:id x)))
                                ~(pattern->jsth index (:pattern x) input))
   (= :tag (:type x)) (test->jsth `(= ~(tag->jsth index x) ~(id->value input)))
   (= :ref/id (:type x)) (test->jsth `(= ~(ref->jsth index x) ~(id->value input)))
   (or (true? x) (false? x)) (test->jsth `(= ~x ~(id->value input)))
   (number? x) (test->jsth `(= ~x ~(id->value input)))
   (string? x) (test->jsth `(= ~x ~(id->value input)))
   (vector? x) `(do
                  ~(test->jsth `(cljs.core.vector_QMARK_.call nil ~(id->value input)))
                  ~(test->jsth `(= ~(count x) (cljs.core.count.call nil ~(id->value input))))
                  ~@(for! [i (range (count x))]
                          (let [new-input (new-id)]
                            `(do
                               (let! ~(id->value new-input) (cljs.core.nth.call nil ~(id->value input) ~i))
                               ~(pattern->jsth index (nth x i) new-input)))))
   (map? x) `(do
               ~(test->jsth `(cljs.core.map_QMARK_.call nil ~(id->value input)))
               ~@(for! [k (keys x)]
                       (let [k-id (new-id)
                             new-input (new-id)]
                         `(do
                            (let! ~(id->temp k-id) ~(data->jsth index k))
                            ~(test->jsth `(cljs.core.contains_QMARK_.call nil ~(id->value input) ~(id->temp k-id)))
                            (let! ~(id->value new-input) (cljs.core.get.call nil ~(id->value input) ~(id->temp k-id)))
                            ~(pattern->jsth index (get x k) new-input)))))
   :else (check false)))

(deftraced action->jsth [index x id] [x id]
  (case (:type x)
    :call (call->jsth index x id)
    :constant (constant->jsth index x id)
    (check false)))

(deftraced guard->jsth [index x] [x]
  (check (= :call (:type x)))
  (let [temp (new-id)]
    `(do
       ~(call->jsth index x temp)
       ~(test->jsth (id->value temp)))))

(deftraced match->jsth [index x id] [x id]
  (check (= :match (:type x)))
  (let [input (new-id)]
    `(do
       ~(constant->jsth index {:type :constant :data (:arg x)} input)
       ~(reduce
         (fn [tail branch]
           (let [exception (new-id)]
             `(try
                (do
                  ~(pattern->jsth index (:pattern branch) input)
                  ~@(for! [guard (:guards branch)]
                          (guard->jsth index guard))
                  ~(action->jsth index (:action branch) id))
                (catch ~(id->temp exception)
                  (if (== ~(id->temp exception) failure)
                    ~tail
                    (throw ~(id->temp exception)))))))
         `(throw failure)
         (reverse (:branches x))))))

(deftraced step->jsth [index x id] [x id]<commit_msg>Note about bug in aurora pattern compiler<commit_after>(ns aurora.compiler
  (:require [aurora.jsth :as jsth]
            [aurora.ast :as ast]
            [aurora.util :refer [map!]])
  (:require-macros [aurora.macros :refer [for! check deftraced]]))

;; compiler

(let [next (atom 0)]
  (defn new-id []
    (if js/window.uuid
      (.replace (js/uuid) (js/RegExp. "-" "gi") "_")
      (swap! next inc))))

(deftraced id->value [id] [id]
  (check id)
  (symbol (str "value_" id)))

(deftraced id->temp [id] [id]
  (check id)
  (symbol (str "temp_" id)))

(deftraced ref->jsth [index x] [x]
  (case (:type x)
    :ref/id (id->value (:id x))
    :ref/js (symbol (:js x))
    (check false)))

(deftraced tag->jsth [index x] [x]
  `(cljs.core.keyword ~(:id x) ~(:name x)))

(deftraced data->jsth [index x] [x]
  (cond
   (= :tag (:type x)) (tag->jsth index x)
   (#{:ref/id :ref/js} (:type x)) (ref->jsth index x)
   (or (true? x) (false? x)) x
   (number? x) x
   (string? x) x
   ;;it's a table
   (get x "headers") `(aurora.runtime.table.table.call nil (cljs.core.PersistentVector.fromArray
                                              ~(vec (map! #(data->value-jsth index %) (x "headers"))))
                                             (cljs.core.PersistentVector.fromArray
                                              ~(vec (map! #(data->value-jsth index %) (x "columns"))))
                                             (cljs.core.PersistentVector.fromArray
                                              ~(vec (map! #(data->value-jsth index %) (x "rows"))))
                                             )
   (vector? x) `(cljs.core.PersistentVector.fromArray
                 ~(vec (map! #(data->jsth index %) x)))
   (map? x) `(cljs.core.PersistentHashMap.fromArrays
              ~(vec (map! #(data->jsth index %) (keys x)))
              ~(vec (map! #(data->jsth index %) (vals x))))
   :else (check false)))

(deftraced constant->jsth [index x id] [x id]
  (check (= :constant (:type x)))
  `(let! ~(id->value id) ~(data->jsth index (:data x))))

(deftraced js-data->jsth [index x] [x]
  (cond
   (nil? x) nil
   :else (data->jsth index x)))

(deftraced call->jsth [index x id] [x id]
  (let [arg->jsth (case (:type (:ref x))
                    :ref/id data->jsth
                    :ref/js js-data->jsth
                    (check false))] ;; allowed to use nil when calling cljs
    `(let! ~(id->value id) (~(data->jsth index (:ref x)) ~@(map! #(arg->jsth index %) (:args x))))))

(deftraced test->jsth [pred] [pred]
  `(if (not ~pred) (throw failure)))

(deftraced pattern->jsth [index x input] [x input]
  (cond
   (= :match/any (:type x)) `(do)
   (= :match/bind (:type x)) `(do
                                (let! ~(id->value (:id x)) ~(id->value input))
                                (set! (.. frame.vars ~(id->value (:id x))) ~(id->value (:id x)))
                                ~(pattern->jsth index (:pattern x) input))
   (= :tag (:type x)) (test->jsth `(= ~(tag->jsth index x) ~(id->value input)))
   (= :ref/id (:type x)) (test->jsth `(= ~(ref->jsth index x) ~(id->value input)))
   (or (true? x) (false? x)) (test->jsth `(= ~x ~(id->value input)))
   (number? x) (test->jsth `(= ~x ~(id->value input)))
   (string? x) (test->jsth `(= ~x ~(id->value input)))
   (vector? x) `(do
                  ~(test->jsth `(cljs.core.vector_QMARK_.call nil ~(id->value input)))
                  ~(test->jsth `(= ~(count x) (cljs.core.count.call nil ~(id->value input))))
                  ~@(for! [i (range (count x))]
                          (let [new-input (new-id)]
                            `(do
                               (let! ~(id->value new-input) (cljs.core.nth.call nil ~(id->value input) ~i))
                               ~(pattern->jsth index (nth x i) new-input)))))
   (map? x) `(do
               ~(test->jsth `(cljs.core.map_QMARK_.call nil ~(id->value input)))
               ~@(for! [k (keys x)]
                       (let [k-id (new-id)
                             new-input (new-id)]
                         `(do
                            (let! ~(id->temp k-id) ~(data->jsth index k))
                            ~(test->jsth `(cljs.core.contains_QMARK_.call nil ~(id->value input) ~(id->temp k-id)))
                            (let! ~(id->value new-input) (cljs.core.get.call nil ~(id->value input) ~(id->temp k-id)))
                            ~(pattern->jsth index (get x k) new-input)))))
   :else (check false)))

(deftraced action->jsth [index x id] [x id]
  (case (:type x)
    :call (call->jsth index x id)
    :constant (constant->jsth index x id)
    (check false)))

(deftraced guard->jsth [index x] [x]
  (check (= :call (:type x)))
  (let [temp (new-id)]
    `(do
       ~(call->jsth index x temp)
       ~(test->jsth (id->value temp)))))

(deftraced match->jsth [index x id] [x id]
  (check (= :match (:type x)))
  (let [input (new-id)]
    `(do
       ~(constant->jsth index {:type :constant :data (:arg x)} input)
       ~(reduce
         (fn [tail branch]
           (let [exception (new-id)]
             `(try
                (do
                  ~(pattern->jsth index (:pattern branch) input)
                  ~@(for! [guard (:guards branch)]
                          (guard->jsth index guard))
                  ~(action->jsth index (:action branch) id)) ;; TODO this could throw match failure too
                (catch ~(id->temp exception)
                  (if (== ~(id->temp exception) failure)
                    ~tail
                    (throw ~(id->temp exception)))))))
         `(throw failure)
         (reverse (:branches x))))))

(deftraced step->jsth [index x id] [x id]
{'fraction_non_alphanumeric': 0.1920582748512037, 'fraction_numerical': 0.00017766722927955937, 'mean_word_length': 1.7681337595279076, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 161, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5737187', 'n_tokens_mistral': 4030, 'n_tokens_neox': 3900, 'n_words': 1054}