This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-09-13
Channels
- # announcements (15)
- # babashka (48)
- # beginners (5)
- # biff (4)
- # calva (3)
- # cider (10)
- # clerk (16)
- # clj-kondo (6)
- # cljdoc (20)
- # cljs-dev (13)
- # clojure (117)
- # clojure-argentina (1)
- # clojure-brasil (5)
- # clojure-europe (40)
- # clojure-nl (1)
- # clojure-norway (111)
- # clojure-uk (5)
- # clojurescript (16)
- # cursive (20)
- # datascript (2)
- # datomic (106)
- # etaoin (2)
- # events (3)
- # funcool (1)
- # graphql (1)
- # helix (8)
- # hyperfiddle (36)
- # leiningen (12)
- # matrix (1)
- # nrepl (1)
- # off-topic (61)
- # other-languages (10)
- # polylith (22)
- # practicalli (1)
- # reagent (28)
- # reitit (11)
- # remote-jobs (3)
- # ring (12)
- # shadow-cljs (109)
- # slack-help (6)
- # solo-full-stack (23)
- # squint (7)
- # xtdb (11)
How do I construct java field descriptor symbol or in general the type such as [Lorg.xbill.DNS.Record;
so I can use it in (instance? ... result)
?
Hi, I'm building a macro that takes a string and transforms it operating on the &env
:
(defmacro s* [some-string] ...)
and I would like to introduce a data-reader for it:
{s my-ns/s*}
It works fine if I'm using the macro explicitly:
(s* "abc")
but for data-reader #s "abc"
it raises Syntax error ...
telling me that it received wrong number of args (1). If I introduce the auxiliary function that tagged literal would point to, then I lose &env
. How to deal with that? Apparently defmacro
does not allow to use multiarity in a tandem of single argument [s]
and any number of arguments [& args]
Also, a data reader implementation has to be a function that transforms code, not a macro.
> yeah, but macro does
Macro receives them because the code that invokes macros as macros passes &form
and &env
to it. You can't plug a macro instead of a function to data-readers map and expect it to receive &env
.
a macro is basically a function that looks like:
(fn [form env your-arg1 your-arg2] ...)
@U03N5HN4K1N The problem is that reader tags expand at read time (cue the name), not at macroexpension time (which is know as "analyze time" in the compiler). At read time, locals are unknown.
There is a way to obtain locals not just in a macro as &env
argument, but by manually calling (clojure.lang.Compiler/LOCAL_ENV)
. But that won't help you here because the env will be empty at read time. You can verify that yourself.
Could your reader literal (#s) perhaps emit a macro or function call? (not completely sure if that works. Edit: doesn’t work. Edit2: it does work, apparently!)
The value emitted by the tagged literal is not further evaluated. It's a data-returning function, not a macro.
> The value emitted by the tagged literal is not further evaluated. It's a data-returning function, not a macro. Well, actually, it is further macroexpanded, so @U3X7174KS suggestion might be valid.
=> (defmacro example-macro [v] `(println "here" ~v))
=> (defn example-reader [v] `(example-macro ~v))
=> (binding [*data-readers* {'s #'user/example-reader}] (eval (read-string "#s[]")))
here []
nil
No problem! As I noted, I wasn’t quite sure myself, but I had a hunch. Thanks Olexandr for figuring it out!
Hello. I am wondering if there is some way to conditionalize (e.g. reader-macro magic) how library A behaves based on a dependency library B’s version. In this case, library B is a commonly used library that may have ABI breaking changes between versions, but different users of library A may be on different versions of B.
For context: https://clojurians.slack.com/archives/C056TDVQ5L1/p1694558869377739 Temporal SDK depends on a defprotocol in promesa, but the ABI of this protocol has evolved from release to release. Different users may be on different versions of promesa, and I would like to support them all, if possible.
If there's a way to get the version of B at run time, you can rely on that. If not, then you can try doing something that only a particular version of B support when the namespace in A is loaded and determine the version based on whether that something succeeds or fails.
Seems like you can simply check for presence of particular functions in the case with Promesa.
Thank you @U2FRKM4TW. I am a little fuzzy on what that probing might look like. Any pointers appreciated
You could use resolve
to check for the presence of a function. Something like this:
(def promesa-11?
(some? (resolve 'promesa/-fmap)))
I guess you could inspect the classpath at macroexpansion time Not very pretty... also won't be AOT-friendly
I’m trying to understand PEG (someone on Twitter was passionate about it in #janet, and I got curious). Trying to use @ericnormand’s https://github.com/ericnormand/squarepeg for this and am running into troubles. Partly because the docs seem to lie a bit, and partly because PEG blows my mind, and partly because I don’t get the Java interop well enough. Wondering if there is anyone here that’s given squarepeg a spin, or that has ideas about how I can use Clojure to figure PEG out. I’ll post an example trouble I have in a reply. ->🧵
Here’s a thing strange to me:
(def digit (peg/mkpr peg/digit))
(def minutes (peg/mkseq digit digit))
(tests
((peg/mkfn minutes) ["1" "1" "1"] {}) := []
((peg/mkfn minutes) ["1" "1"] {}) := ["1" "1"]
:rcf)
This doesn’t crash (even if it doesn’t do what I want it to either, but that’s a later matter). These two does not work:
(def minutes (peg/mkseq peg/digit peg/digit)) ; => No matching method isDigit found taking 1 args
This looks equivalent to me…
peg/digit
is implemented like so:
(defrule digit (mkpr #(Character/isDigit %)))
I have a BNF that I am trying to implement. I get the BNF to work with instaparse. Which also speaks about PEG… So maybe I can use instaparse to figure PEG out?
i know a fair amount about PEG parsers, but I don't know the library. seems like the issue is the library and not your understanding of PEG parsers?
My vague guess is you are passing single character strings where a single character is expected
Because you aren't use defrule for minutes, because defrule automatically applies mksub which seems horrendous
A PEG is a way of describing a grammar, it just happens that the way PEG describes grammars happens to map fairly directly to naive top down parser combinators
A PEG is the same kind of thing as a context free grammar, and they have a non-trivial amount of overlap in languages you can describe using them
So you can use PEG grammars as the basis of a parser generator, something that takes a text descript of the PEG and spits out code that parses the language that PEG describes, and there are libraries that do that (dunno if any exist in clojure)
But PEG and parser combinators are often treated as synonymous, when they definitely are not
@UEENNMX0T, it’s both. 😃 My goal is to understand what PEG, how it compares to regexes (that Janet does not have regexes and people instead use PEGs there peaks my interest), to BNF, to instaparse, and generally what they could bring to the table for me. I then hit a wall trying to use squarepeg (where not even some of the examples work, and on top of that, I just don’t understand how to use it, and that recurses back to me not knowing what PEGs are…).
I now understand PEGs a bit better, thanks to @U0NCTKEV8. 🙏 I think instaparse might do both those, but I’m not sure.
instaparse both supports a deterministic choice operator (the main difference between pegs and cfgs) and also has a parser combinator api
regexs (that match regular langauges) are actually easy to implement in a way that peforms poorly, but it is very educational to do so
Where I’m coming from: I’m reading The Pragmatic Programmer and it had these exercises:
1. Write a BNF for a time language that understands things like 1
, 2pm
, 4:30
, 11:30am
2. Implement a parser for the BNF using a PEG parser generator in the language of my choice, where the output should be number of minutes past midnight.
3. Implement the same parser using regexes.
As I didn’t quite understand what a PEG, or a PEG parser is, I fed my BNF to instaparse and wrote a transformer and could check of 2. that way. But then I also have this interest in PEGs so wanted to do it the PEG way.
https://crypto.stanford.edu/~blynn/haskell/re.html might be a good place to start for that, I haven't looked at that page specifically, but the technique "derivatives of regular expressions" is what makes implementing them easy, and that page even seems to suggest you can go from that to a dfa, which is how you get something high performance, which I was not aware of
> regexs (that match regular langauges) are actually easy to implement in a way that peforms poorly Yeah, the Clojure lexer in Calva is written with regexes, so I know all about that. 😃 Very educational indeed.
I’d love to implement it using DFA. I’m thinking that would speed things up quite considerably. But… I also need a lot of time to do that.
Thanks for that Haskell link, @U0NCTKEV8! I’ve found some other regex->dfa things before, but not quite understood what I’m reading about. Maybe this one can make it click…
the derivative stuff is often not how regexes are introduced, which is shame because for functional languages it is very elegant
If I could automatically translate my lexer regexes to DFA equivalents, then I could keep writing regexes, maybe?
and there has been some recentish working extending it from regular languages to context free languages "parsing with derivatives"
It’s pretty hairy stuff, tbh: https://github.com/BetterThanTomorrow/calva/blob/published/src/cursor-doc/clojure-lexer.ts
so in the context of parsing time strings, you really don't need the power of the recursion, so a regex works fine
with a language like say clojure, each part of the expression you are trying to parse is often potentially another clojure expression, so there is the recursion
lexing is actually what I first got into the regex derivative stuff for, because I was writing a lexer and wanted to be able to feed characters one at a time to a regex, but the regex library built into the language I was using (lua) didn't support that
so I ended up writing my own regex library, and the derivative stuff was the easiest way to do that
derivatives of regular languages, dfas, nfas, are all things for implementing regular expressions, writing a regex library, not techniques for using an existing regex library (which I think the code above is doing? or at least using the regex library built into the lexer library)
I’ve been hoping I could find a dfa library, and then also find a translator eat my regexes and spit out something I can feed to the dfa engine. But I haven’t investigated if that is possible. These regexes are so important that I need the safety regexes give me, because I can read regexes. I don’t know how fast I could learn something else up to the same reading ability.
if you are using a regex library (https://github.com/BetterThanTomorrow/calva/blob/published/src/cursor-doc/lexer.ts#L95 looks like you are, not sure where Regex is coming from there? built in js?) then under the covers it is almost certainly producing a dfa to make matching efficient
but it is considered such a crucial optimization technique that I don't think any regex engine would be taken seriously without it
Interesting. Maybe what I am after is to actually rewrite the lexer so that it does not require backtracking. It was a while since I analyzed this, but iirc there are some backtracking scenarios.
this was part of why I wanted to be able to feed a character at a time to a regex, I wanted to run a bunch of regexes "in parallel", so read from the input, hand the character to each regex, do the next etc
where what is likely happening here is building a buffer of input and then running each regex against the buffer
> Because you aren’t use defrule for minutes, because defrule automatically applies mksub which seems horrendous What did you mean with this, @U0NCTKEV8? It’s not like I know what I am doing. 😃 I was inspired by this example from the README:
;; match a seq of ones
(def ones (mkzom (mklit 1)))
;; match a seq of ones followed by 2s
(def onesthentwos (mkseq (mksub ones) (mkzom (mklit 2))))
(onesthentwos [[1 1 1] 2 2] {}) => SUCCESS
(onesthentwos [1 1 1 2 2] {}) => FAILURE
Which doesn’t actually work, but I could make it work by wrapping the whatever-it-is-called in mkfn
:
((peg/mkfn onesthentwos) [[1 1 1] 2 2] {})
I also wish that there was a straight PEG library for clojure (also was mainly exposed to it when I played around with Janet), I like the 'flow' of PEGs a lot more than the BNF-style
I've never played with squarepeg
, I'll give it a look though, I'm honestly surprised there weren't too many exploratory PEG efforts in the clojure ecosystem
I guess Instaparse passes the "good enough" threshold
Oh, thats right, theres also https://github.com/aroemers/crustimoney
crustimoney looks much better, even in terms of distinguishing PEG as a way to define a grammar vs. how a program is written to parse things that are written in that grammar, but just glancing at it, the screenshot at the top of the readme is impossible (parsing use the :root value of the map, but not passed the whole map, but the root value contains references to other keys in the map)
@U0NCTKEV8 it looks like that sample is valid, assuming that the call to ref
in the definition is proper magic that'll make things work
https://github.com/aroemers/crustimoney/blob/v2/test/crustimoney/combinators_test.clj#L169
I’m still a bit confused around how BNF and PEG parsers relate. Does this even make sense?
When I used instaparse to parse and transform the time strings using my BNF grammar. Did I “Implement a parser for the BNF grammar using a PEG parser generator”? Sorry for the dumb questions, but this is the level of confusion I am experiencing right now. 😃
My grammar looks like so:
time ::= suffixed-time | twenty-four-hour-time
twenty-four-hour-time ::= hours [<":"> minutes]
suffixed-time ::= hours [<":"> minutes] am-or-pm
am-or-pm ::= "am" | "pm"
hours ::= [digit] digit
minutes ::= digit digit
<digit> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
And my parser + transformer like so:
(def parser
(insta/parser "src/pez/pragmatic/time.bnf"))
(def transformer {:hours (comp edn/read-string str)
:minutes (comp edn/read-string str)
:suffixed-time (fn suffixed-time
([h am-or-pm]
(suffixed-time h 0 am-or-pm))
([h m am-or-pm]
(+ (* 60 h) (* 60 am-or-pm) m)))
:twenty-four-hour-time (fn twenty-four-hour-time
([h]
(twenty-four-hour-time h 0))
([h m]
(+ (* 60 h) m)))
:am-or-pm (fn [am-or-pm]
(case am-or-pm
"am" 0
"pm" 12))
:time identity})
(tests
(->> (parser "01:12")
(insta/transform transformer)) := 72
(->> (parser "01:12am")
(insta/transform transformer)) := 72
(->> (parser "4pm")
(insta/transform transformer)) := 960
(->> (parser "16:00")
(insta/transform transformer)) := 960
(->> (parser "01:00pm")
(insta/transform transformer)) := 780
:rcf)
The other ambiguity is they could be using BNF to refer to the textual description of the grammar, and the textual description of the grammar might not actually be BNF
Thanks. It was a long time ago I dealt with BNFs so I literally checked Wikipedia about it and that’s how I came up with that BNF. It confused the heck out of me that I was then asked to create a PEG parser generator that parsed the grammar. 😃
My next question is if PEG allows for writing the grammar for these time strings more succinctly? Or does that get into play for more complex languages?
I think the peg description language has some features that are outside the traditionally accepted features of a bnf description, but most libraries that use a notation like bnf actually accept some kind of extended version of it
I think peg is actually both a textual format for describing a grammar, and an algorithm for parsing the grammars the text format describes
so like instaparse is not a peg parser, I think it is gll based maybe? but it parsers the peg textual description and uses the gll bits to parse the grammar described
I have a fairly well used parsing framework called Pegex that I was planning to port to clojure. It already works in multiple other languages
Sounds great, @U05H8N9V0HZ ! I’m not having a ton of luck with crustimoney.
Hey @U0ETXRFEW. I'm happy to jump on a call. BTW, that library was very terse and concise, and I didn't document it well. I don't know if I'd recommend it anymore!
mksub is weird is because the parser can work equally well for a single item as it does a sequence.
Nowadays, when I want to parse something, I use instaparse. My thing is better if you want to parse clojure data.
Hello, I've been trying to authenticate to Snowflake using an unencrypted private key with JDBC and am not sure where I'm going wrong. I've tried to use buddy-core, bouncycastle, and some other java interop things but everything I've tried ends up in the same place. When I attempt to connect to the snowflake database I get
Error in warehouse, re-throwing as ExceptionInfo
net.snowflake.client.jdbc.SnowflakeSQLLoggedException, "Private key provided is invalid or not supported: Use java.security.interfaces.RSAPrivateCrtKey.class for the private key"
(SQLState 42601, error code 200045)
net.snowflake.client.jdbc.SnowflakeSQLLoggedException: Private key provided is invalid or not supported: Use java.security.interfaces.RSAPrivateCrtKey.class for the private key
For each method I've tried to generate the private key it comes out as a sun.security.rsa.RSAPrivateCrtKeyImpl
which to my understanding is a subclass of the desired java.security.interfaces.RSAPrivateCrtKey.class
and should work just the same but it does not (attempting to use Java interop in Clojure is the breadth of my Java experience).
The private key is used in a map of connection info as :private-key thekey
I am currently working off of [org.bouncycastle/bcprov-jdk15on "1.70"]
and
openjdk 11.0.20.1 2023-08-24
OpenJDK Runtime Environment (build 11.0.20.1+1-post-Ubuntu-0ubuntu120.04)
OpenJDK 64-Bit Server VM (build 11.0.20.1+1-post-Ubuntu-0ubuntu120.04, mixed mode, sharing)
I have confirmed that the private key is valid and can successfully authenticate with Python.jdbc is the name of a popular java interface to sql style databases, presumably you are not using the java interface directly, but some clojure wrapper, what clojure wrapper are you using?
clojure.java.jdbc
what you'll need to do is look at the docs the presumable snowflake has for using the private key with jdbc, and then look at how to replicate that behavior with clojure.java.jdbc
you may want to look at moving to next.jdbc (https://github.com/seancorfield/next-jdbc), since I know some people are using that with snowflake (not sure about clojure.java.jdbc) there is a #sql room and some people have asked questions related to snowflake there in the past
Awesome thank you! I'll look at next-jdbc