Skip to content
Draft
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
2 changes: 1 addition & 1 deletion deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
org.clojure/tools.cli {:mvn/version "1.1.230"}
nrepl/nrepl {:mvn/version "1.3.1"}
com.cnuernber/charred {:mvn/version "1.037"}
carocad/parcera {:mvn/version "0.11.6"}
read-kinds/read-kinds {:local/root "../read-kinds"}
org.antlr/antlr4-runtime {:mvn/version "4.7.1"}
http-kit/http-kit {:mvn/version "2.8.0"}
ring/ring-core {:mvn/version "1.14.1"}
Expand Down
31 changes: 31 additions & 0 deletions src/scicloj/clay/v2/notebook.clj
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,41 @@
(print s)
(flush))))

;; Babashka
;; - Make Clay runnable in Babashka
;; - The way Clay reads - dependency of `carocad/parcera`, maybe we just remove it.
;; - or make parcera optional... or replace the functionality.
;; - major motivation for `read-kinds`
;; - maybe we can use `read-kinds` or adapt it.
;;
;; - Vs Run Clay in Clojure, and run Babashka as separate process <<
;; - still valuable
;; -

;; Jank
;; - Could we ever run clay in Jank?
;; - If we can't, can we read Jank code in Clojure?
;; - If we can read it, and we can send it to an nREPL, that's interesting.

;; should we use nREPL for Clojure
;; - We already do in some way -- the integrations are via nREPL
;; - Where should **Clay** run? Same process or different process.
;; - Can we cancel evaluation?

;; Error messages
;; - Not integrated with the tooling
;; - Problem: we are sending things like `(clay/make! {:single-form "(+ 1 2)"})`
;; but we want our repl to treat it like `(+ 1 2)`...
;; - We want our nREPL to return the result of `(+ 1 2)` (or exception), and with that stack.
;; - Can we frame the stack around eval?
;; - Is output being done right?

(defn read-eval-capture
"Captures stdout and stderr while evaluating a note"
[{:as note
:keys [code form]}]
note
#_
(let [out (StringWriter.)
err (StringWriter.)
note (try
Expand Down
110 changes: 8 additions & 102 deletions src/scicloj/clay/v2/read.clj
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
(ns scicloj.clay.v2.read
(:require [clojure.tools.reader]
[clojure.tools.reader.reader-types]
[parcera.core :as parcera]
[clojure.string :as str]))
(:require [scicloj.read-kinds.notes :as notes]
[scicloj.read-kinds.read :as read]))

;; TODO: not sure if generation is necessary???

(def *generation (atom 0))

(defn generation []
(swap! *generation inc)
@*generation)

;; for finding the ns only
(defn read-forms [code]
(->> code
clojure.tools.reader.reader-types/source-logging-push-back-reader
repeat
(map #(clojure.tools.reader/read % false ::EOF))
(take-while (partial not= ::EOF))))


(defn read-ns-form [code]
(->> code
read-forms
Expand All @@ -26,105 +26,11 @@
(-> form first (= 'ns)))))
first))

(defn read-by-tools-reader [code]
(-> code
;; avoiding a tools.reader bug -- see:
;; https://github.com/scicloj/clay/issues/151#issuecomment-2373488031
(str/replace #"\r\n" "\n")
(->> read-forms
(map (fn [form]
(let [{:keys [line column
end-line end-column
code]}
(meta form)]
(when line ; skip forms with no location info
{:method :tools-reader
:region [line column
end-line end-column]
:code (-> form meta :source)
:form form}))))
(filter some?))))

(defn read-by-parcera [code]
(->> code
parcera/ast
rest
(map (fn [node]
(let [node-type (first node)
node-contents (rest node)]
;; We use parcera only for specific types of
;; code blocks, that tools.reader does not
;; provide location info for.
(some->> (when (#{:number :string :symbol :keyword :comment}
node-type)
{:code (first node-contents)})
(merge {:method :parcera
:region (->> node
meta
((juxt :parcera.core/start
:parcera.core/end))
(mapcat (juxt :row
(comp inc
:column)))
vec)}
(when (= :comment node-type)
{:comment? true}))))))
(filter some?)))

(defn unified-cleaned-comment-block [comment-blocks-sorted-by-region]
{:region (vec (concat (->> comment-blocks-sorted-by-region
first
:region
(take 2))
(->> comment-blocks-sorted-by-region
last
:region
(drop 2))))
:code (->> comment-blocks-sorted-by-region
(reduce (fn [{:keys [generated-string max-line]}
{:keys [region code]}]
{:generated-string (str generated-string
(apply str (-> region
first
(- max-line)
(repeat "\n")))
code)
:max-line (-> region
(nth 2)
(max max-line))})
{:generated-string ""
:max-line (->> comment-blocks-sorted-by-region
first
:region
first)})
:generated-string)
:comment? true})

(defn ->notes [code]
(->> code
((juxt read-by-tools-reader read-by-parcera))
(apply concat)
(group-by :region)
(map (fn [[region results]]
(if (-> results count (= 1))
(first results)
;; prefer tools.reader over parcera
(->> results
(filter #(-> % :method (= :tools-reader)))
first))))
(sort-by :region)
(map #(dissoc % :method))
(partition-by :comment?)
(mapcat (fn [part]
(if (-> part first :comment?)
[(unified-cleaned-comment-block part)]
part)))
(mapv (let [g (generation)]
(fn [note-data]
(-> note-data
(assoc :gen g)))))))

(->> (read/read-string-all code)
(into [] notes/notebook-xform)))

;; TODO: Not needed? read-kinds has a safe-notes wrapper already...
(defn ->safe-notes [code]
(try
(->notes code)
Expand Down