Fork me on GitHub
#rewrite-clj
<
2021-04-01
>
Dimitar Uzunov07:04:41

Hi! I’m interested in implementing a SLOC counter for clojure (if it is not too difficult)

Dimitar Uzunov07:04:01

it needs to count blank lines, comments (including (comment ) and #_

Dimitar Uzunov07:04:33

I will need the parser + node api right?

borkdude07:04:18

you can inspect the node/tag to see if the top level node is a comment or not

borkdude07:04:26

#_ is called :uneval I think

Dimitar Uzunov08:04:33

thanks for the pointers!

lread11:04:46

@dimitar.ouzounoff please let us know how it goes!

Dimitar Uzunov11:04:20

will do, I have to wrap one task first and I will get on this; my first impression is that most likely I will need to parse this line by line

borkdude11:04:26

@dimitar.ouzounoff No, you can just use parser/parse-string-all and this will give you a :forms node. Then you can go over its :children and skip all :comment and :uneval or empty lines, etc. And then just call str on the remaining nodes and count the number of lines from those.

Dimitar Uzunov11:04:49

well, I saw parse-string-all but it doesn’t return a seq; I guess I need to read on the nodes to understand them better

borkdude11:04:48

@dimitar.ouzounoff It returns one node, a :forms node

borkdude11:04:57

and that has :children which are all of the top level nodes

Dimitar Uzunov11:04:25

hmm this is what a comment looks like in cider-inspect: Class: rewrite_clj.node.seq.SeqNode Meta Information: :row = 87 :col = 1 :end-row = 91 :end-col = 5 Contents: :tag = :list :format-string = "(%s)" :wrap-length = 2 :seq-fn = rewrite_clj.node.seq$list_node$fn__7201@66935f35 :children = ( { :value comment, :string-value "comment", :map-qualifier nil } { :newlines "\n" } { :whitespace " " } { :tag :list, :format-string "(%s)", :wrap-length 2, :seq-fn rewrite_clj.node.seq$list_node$fn__7201@fab763f, :children ( { :value do,

Dimitar Uzunov11:04:41

I’m not sure how to access it, I can’t seem to use nth

Dimitar Uzunov11:04:59

in order to use a comment? predicate

borkdude11:04:37

so the first child of this form is a symbol node with :value 'token

lread11:04:47

@dimitar.ouzounoff you can achieve what your goal with the node API, but the zip API is a bit higher level.

borkdude11:04:32

something like:

(defn comment-node? [node]
  (and (= :list (node/tag node)) (some-> node :children first :value (= 'comment))))

borkdude11:04:13

Personally I don't see a need for the zipper API here, since it's pretty straightforward to only iterate over the top level nodes, there isn't a need to visit any deeper nodes and/or rewrite/remove them

lread11:04:58

No need, just an alternative that might offer easier nav through tree. Not sure exactly what @dimitar.ouzounoff wants to count here yet.

borkdude11:04:21

He want to count lines of code, but exclude comment forms

Dimitar Uzunov11:04:43

yes, I guess this is why something like rewrite-clj is necessary as it needs to exclude whole multiline comment forms from the line count

lread11:04:42

Oh I thought he also wanted to count comments and (comment and #_.

borkdude11:04:59

that doesn't change the problem very much, those are just different predicates

borkdude11:04:34

and you're also able to count whitespace lines, so rewrite-clj is a great tool for this problem to implement clocl (count line of clojure)

borkdude11:04:57

which will soon be available as a GraalVM binary? with proper command line interface please? :P

lread11:04:40

Ha! Let’s not get you started! 🙂

Dimitar Uzunov11:04:55

I’m still at a beginner level, but it sounds like fun 🙂

lread12:04:58

It is! Borkdude and I curate some tips over at https://github.com/lread/clj-graal-docs. I was more joking with borkdude about proper cmd lines, which is something he has been recently irked by.

lread11:04:12

Feel free to carry on with node API, just offering up an alternative.

Dimitar Uzunov11:04:58

well I’m not sure how to go over the nodes

Dimitar Uzunov11:04:08

rewrite-clj.zip/next might be something I can use

Dimitar Uzunov12:04:24

another thing that I’m not sure about is that if take this line: (set! *warn-on-reflection* true) ;; avoid reflexion so you can use graalvm

Dimitar Uzunov12:04:45

it is two children on the first depth

Dimitar Uzunov12:04:01

so it would count as a line of code + a line of column

borkdude12:04:09

@dimitar.ouzounoff

user=> (require '[clojure.string :as str] '[rewrite-clj.parser :as p])
nil
user=> (:children (p/parse-string-all "(+ 1 2 3)\n;;hello\n(comment 1 2 3)"))
(<list: (+ 1 2 3)> <newline: "\n"> <comment: ";;hello\n"> <list: (comment 1 2 3)>)
user=> (map str (:children (p/parse-string-all "(+ 1 2 3)\n;;hello\n(comment 1 2 3)")))
("(+ 1 2 3)" "\n" ";;hello\n" "(comment 1 2 3)")
user=> (map (comp count str/split-lines str) (:children (p/parse-string-all "(+ 1 2 3)\n;;hello\n(comment 1 2 3)")))
(1 0 1 1)
user=> (apply + (map (comp count str/split-lines str) (:children (p/parse-string-all "(+ 1 2 3)\n;;hello\n(comment 1 2 3)"))))
3

👍 3
borkdude12:04:31

user=> (require '[rewrite-clj.node :as node])
nil
user=> (defn comment-node? [node]
  (and (= :list (node/tag node)) (some-> node :children first :value (= 'comment))))
#'user/comment-node?
user=> (apply + (map (comp count str/split-lines str) (remove comment-node? (:children (p/parse-string-all "(+ 1 2 3)\n;;hello\n(comment 1 2 3)")))))
2

👍 3
borkdude12:04:13

@lee I'm pretty tempted to just add rewrite-clj to babashka so you can write little scripts like this ;)

borkdude12:04:08

I think when bb was only a few weeks old @sogaiu proposed it already, haha

lread12:04:38

hey, I’d use it!

Dimitar Uzunov12:04:56

ok, I think I get it now, thanks @borkdude!

ericdallo18:04:02

Any chance of rewrite-clj support babashka interpreter? 🧵

ericdallo18:04:18

(z/of-string "!/usr/bin/env bb\n\n(ns foo)")
=> Execution error (ExceptionInfo) at clojure.tools.reader.impl.errors/throw-ex (errors.clj:34).
Invalid symbol: !/usr/bin/env.

ericdallo18:04:53

Is just that if a user open a babashka file with that interpreter and is using clojure-lsp, it will throw a lot of those exceptions 😕

borkdude18:04:13

ouch! I think rewrite-clj should be able to handle this, but you need to start it with a #

borkdude18:04:35

so: #!/usr/bin/env bb

ericdallo18:04:44

oh yeah, my bad, but it happens with the # as well

borkdude18:04:10

that surprises me, since I'm supporting this in clj-kondo too

borkdude18:04:18

but it could be that it's only supported in my fork. I'll check

🤞 3
lread18:04:28

yeah I can confirm it throws for rewrite-clj @UKFSJSM38

😢 3
👍 3
lread18:04:56

cheer up buddy, we’ll deal with it.

ericdallo18:04:05

Thank you! 😄

ericdallo18:04:14

oh, it makes sense!

lread18:04:15

I shall take a peek @borkdude, thanks.

lread18:04:16

So kondo just skips, yeah?

lread18:04:51

What would we like rewrite-clj to do?

borkdude18:04:04

yes, #! is read exactly the same as ;

lread18:04:29

Oh, just something rewrite-clj does not understand yet.

borkdude18:04:49

Maybe a new :shebang type node?

borkdude18:04:57

Similar to ; I guess

ericdallo18:04:17

yes, a new node looks useful

borkdude18:04:38

user=> (p/parse-string ";; foo")
<comment: ";; foo">
user=> (p/parse-string "#! foo") ;;=> <shebang "#! foo">

lread18:04:51

Sure, sounds good.

borkdude18:04:18

Note:

user=> (+ 1 2 3 #! foo
4)
10
so yeah, it's exactly like ;

lread18:04:07

Huh, so where is #! documented?

borkdude18:04:50

it might not be documented, but this is how it works ;)

lread18:04:33

tis the way of things, I like shebang for a name, but I’ll see if it is called something specific in the reader

lread18:04:11

You are fast man! Tx! So technically the zipper should skip these guys too.

borkdude18:04:29

we could also just make it a :comment node perhaps

lread18:04:51

That would fit in more easily.

👍 3
lread18:04:50

Alrighty! I new rewrite-clj feature! Very exciting.

lread18:04:59

Or a bug fix.

lread18:04:05

Still exciting

borkdude18:04:24

Yeah, exciting that rewrite-clj is finally catching up with clj-kondo's fork after 2 years :P

borkdude18:04:52

hehe, sorry, just kidding

lread18:04:40

I think that windows command line argument might have set your tone for the day. simple_smile

lread18:04:47

cool, I’ll write up an issue for rewrite-clj and fix.

ericdallo18:04:53

yay a new feature!

lread18:04:55

now about that .bat file…

lread18:04:47

ya, thanks for raising @UKFSJSM38!

🚀 3
borkdude18:04:51

are you using argument in two senses here?

borkdude18:04:22

I didn't feel like I was arguing with someone, because nobody really replied, except the guy who was the "victim" of string quoting

lread18:04:32

It was a bad pun!

lread18:04:27

No, you were empathizing not arguing.

borkdude18:04:34

I'm strangely excited about this Windows shebang though. It's always nice to support some archaic niche use case

😆 9
borkdude18:04:06

Me having a Windows machine is really paying off here ;P

lread18:04:11

128gb at the ready… for any arcane issue…

ericdallo21:04:50

Nice, thank you very much! Just don't giving the exception will certainly work 😄 I'll make the change on clojure-lsp on next rewrite-clj release

lread21:04:25

Coolio, I’ll likely cut a release tomorrow then.

metal 3
borkdude21:04:27

@UKFSJSM38 btw, you mentioned that the integration tests were broken with the newest clj-kondo. could you follow up on this?

ericdallo21:04:00

yep, for some reason the order of the elements of the list were not in the expected order with the new version, it's not a issue and it seems now the order is "more" correct, following the asc pattern of the elements positions

ericdallo21:04:33

I didn't investigate that much, but I'll keep an eye if next clj-kondo releases impact that again

borkdude21:04:36

the version from earlier in march had a bug which reported unresolved symbols in the wrong order. maybe you captured this in an integration test

yes 3
nice 3
lread21:04:04

@borkdude, just reviewing https://github.com/clj-kondo/clj-kondo/commits/master/parser/clj_kondo/impl/rewrite_clj, I don’t think there is anything else immediately relevant that we are not already tracking for rewrite-clj v1, do you?

borkdude21:04:19

@lee I already had some patches around namespaced maps in the first commit, unfortunately I didn't specify that explicitly

borkdude21:04:10

but I think that was the major one before this inlined fork

lread21:04:15

I think we might be ok in that area

borkdude21:04:31

yeah. so I think it's pretty safe to drop the alpha suffix

lread21:04:41

yeah you were probably working around the half-finished namespaced map support in v0

borkdude21:04:19

yeah, also the *ns*thing bugged me

lread21:04:37

yeah, it was annoying

lread21:04:38

I guess we could drop alpha. I would have liked more feedback around sexpr work around namespaced elements from real usage, but if it does not come naturally, then… can’t force it.

borkdude21:04:08

can also wait some more

lread21:04:53

I might do that… doesn’t hurt. Spec set a precedent. simple_smile

borkdude21:04:54

not sure if that's a good example to follow though ;)

lread23:04:12

@sogaiu! Nice to hear from you! I finally took the time to introduce myself to tree-sitters the other day. Very interesting!

lread23:04:39

Yeah, I found the smart error detection pretty darn awesome. Not exactly sure how it works but the effect is nice.

sogaiu23:04:09

oh, where did you try that out? atm i don't think tree-sitter gives a grammar author the ability to tune how the correction works. consequently, i haven't found it to be so flexible for coping with broken code. the work that you all are doing on rewrite-clj, clj-kondo, clojure-lsp, etc. is much better for editor users from this perspective.

❤️ 3
lread14:04:11

Oh @sogaiu, I am a total tree-sitter noob and have only read/watched introductory stuff. I thought that https://github.com/tree-sitter/tree-sitter/issues/224, but again have not dug deep at all.

sogaiu22:04:28

yeah what you mentioned has a nice explanation (which i confess i do not understand that well 🙂 ) here is a more recent discussion that may be relevant for lisp-likes: https://github.com/tree-sitter/tree-sitter/issues/923 i've been working on trying to spell out specifically what one can do in the case of unbalanced delimiters. afaict, in general, when there is a missing closing delimiter, there are mutiple possible places it might go. if one can trust existing indentation i think this can be narrowed down a bit, but still hammocking / researching. anyway, sorry to have drifted off topic.

lread12:04:38

All interesting to me @sogaiu! Thanks for sharing.

🙂 3