adventofcode

Happy Advent 2020! Please put answers in the pinned threads or create one if it does not exist yet. | https://github.com/adventofcode-clojurians/adventofcode-clojurians | Join the private leaderboard with code 217019-4a55b8eb
R.A. Porter 2020-12-19T03:45:52.407Z

I also went the grammar + instaparse route, anticipating that part 2 might be harder or, finally, tomorrow would build upon today. Made for almost no clojure code, but here are my crappy grammars https://github.com/coyotesqrl/advent2020/blob/master/resources/day18-no-precedence.grammar https://github.com/coyotesqrl/advent2020/blob/master/resources/day18-plus-precedence.grammar

1๐ŸŽ‰
2020-12-19T04:52:20.407800Z

Day 19 answers thread - Post your answers here

misha 2020-12-19T08:15:50.424500Z

https://github.com/rjray/advent-2020-clojure/blob/master/src/advent_of_code/day19.clj#L29 @rjray you could have just

(reduce-kv str/replace input
  {"8: 42"     "8: 42 | 42 8"
   "11: 42 31" "11: 42 31 | 42 11 31"})
or if you don't mind repetition
(-> input
  (str/replace "8: 42" "8: 42 | 42 8")
  (str/replace "11: 42 31" "11: 42 31 | 42 11 31")))

misha 2020-12-19T08:19:46.425Z

(defn to-grammar [rules]
  (str "S = 0\n" (str/replace rules #":" "=")))

(defn solve [input]
  (let [[rules messages] (str/split input #"\n\n")
        grammar (to-grammar rules)
        parser  (insta/parser grammar)]
     (->> messages
       (str/split-lines)
       (map #(insta/parse parser %))
       (remove insta/failure?)
       (count))))

2020-12-19T08:46:46.426300Z

๐Ÿ˜

1๐Ÿ‘
2020-12-19T09:58:37.427Z

I wonder anyone else solved it without instaparse?

1๐Ÿ‘
2020-12-19T10:10:37.427200Z

My solution with regexs https://github.com/zelark/AoC-2020/blob/master/src/zelark/aoc_2020/day_19.clj

4๐Ÿ’ช
misha 2020-12-19T10:13:55.427500Z

I did part 1 with recursion and re-pattern, bailed on it as soon as read p2

euccastro 2020-12-19T11:08:37.431300Z

today was brutal for me... satisfying too, in the end! https://github.com/euccastro/advent-of-code-2020/blob/master/src/advent/day19.clj

1๐Ÿ‘
euccastro 2020-12-19T11:15:06.431700Z

@zelark I eventually solved it without regexes or instaparse. I tried regexes, and even found a clever way to do recursive regexes in Java, but my first attempts didn't work and I decided that regexes were not the most fun thing to debug and moved on ๐Ÿ™‚

Joe 2020-12-19T11:25:39.432400Z

https://github.com/RedPenguin101/aoc2020/blob/main/day19.clj - Instaparse feels like cheating here, but the thought of building it myself filled me with horror.

3๐Ÿ‘
nbardiuk 2020-12-19T11:26:36.432900Z

without instaparse it is slow and hacky https://github.com/nbardiuk/adventofcode/blob/master/2020/src/day19.clj

1๐Ÿ’ก
euccastro 2020-12-19T11:32:25.433500Z

mine runs in ~300msecs

euccastro 2020-12-19T11:58:00.434600Z

btw by "found" I mean I looked it up, not that I discovered it myself: https://stackoverflow.com/a/3644267

1โœ”๏ธ
euccastro 2020-12-19T11:59:19.434900Z

a^n b^n is the pattern in 11. the pattern in 8 is just (42-pattern)+

2020-12-19T13:57:58.436400Z

my regex-ish version runs in 13.371895 msecs!

1๐Ÿ˜ฎ2๐ŸŽ๏ธ
erwinrooijakkers 2020-12-19T15:25:14.437600Z

instaparsed https://github.com/transducer/adventofcode/blob/master/src/adventofcode/2020/day19.clj

2๐Ÿ‘
2020-12-19T17:20:32.449500Z

I'm all for transforming inputs into some known problem and letting a lib do all the work (like creating a graph problem from the inputs and feeding it to loom for example) but this time I literally didn't have to change anything about the format. https://github.com/Dexterminator/advent-of-code-2020/tree/main/src/day19

2๐Ÿ‘
rjray 2020-12-19T17:42:31.452400Z

Wow. I hadnโ€™t realized that the format was usable as-is. I really canโ€™t wait until I can go back and clean up my code!

genmeblog 2020-12-20T00:29:25.465700Z

Manual glueing regex as my third attempt... it's fast and dirty and eventually works... https://github.com/genmeblog/advent-of-code/blob/master/src/advent_of_code_2020/day19.clj

1๐Ÿ‘
2020-12-19T05:14:17.408300Z

I am going to take the slow path and do something with instaparse.

erwinrooijakkers 2020-12-19T15:26:27.438300Z

> Tailwindsโ€™s grammar using instaparse tell me more!

2020-12-19T15:26:53.438500Z

(ns girouette.grammar.css-class
  (:require [instaparse.core :as insta]))

;; Use case 1: "css class name -> corresponding css"
;; - parse a class name,
;; - find its matching rule and its variable bindings,
;; - call the css generator with the bindings,
;; - get the css corresponding to the class name.

;; Use case 2: "generate css for given combinations of rules and bindings"
;; - given a rule, some bindings between variables and a collection of values,
;; - generate the corresponding css, aggregated in order.

;; Note: the generated css could be either a string or garden format.
;; Beware: the order of the css statements matter, it should be the same as Tailwind.

(def css-class-grammar "
  rule =
     (* Layout Section *)
     container | box-sizing | display | floats | clear |
     object-fit | object-position | overflow | overscroll |
     position | positioning | visibility | z-index |

     (* Flexbox Section *)
     flex-direction | flex-wrap | flex | order |

     (* Grid Section *)
     grid-template-columns | grid-column-start-end |
     grid-template-rows | grid-row-start-end |
     grid-auto-flow | grid-auto-columns | grid-auto-rows | gap |


     padding | margin


  container = <'container'>

  box-sizing = 'box-border' | 'box-content'

  display = 'block' | 'inline-block' | 'inline' | 'flex' | 'inline-flex' |
     'table' | 'table-caption' | 'table-cell' | 'table-column' | 'table-column-group' |
     'table-footer-group' | 'table-header-group' | 'table-row-group' | 'table-row' |
     'flow-root' | 'grid' |'inline-grid' | 'contents' | 'hidden'

  floats = <'float-'> ('left' | 'right' | 'none')

  clear = <'clear-'> ('left' | 'right' | 'both' | 'none')

  object-fit = <'object-'> ('contain' | 'cover' | 'fill' | 'none' | 'scale-down')

  object-position = <'object-'> object-position-side
  <object-position-side> = 'left' | 'left-bottom' | 'left-top' |
                           'right' | 'right-bottom' | 'right-top' |
                           'center' | 'bottom' | 'top'

  overflow = <'overflow-'> (axis <'-'>)? overflow-mode
  overflow-mode = 'auto' | 'hidden' | 'visible' | 'scroll'

  overscroll = <'overscroll-'> (axis <'-'>)? overscroll-mode
  overscroll-mode = 'auto' | 'contain' | 'none'

  position = 'static' | 'fixed' | 'absolute' | 'relative' | 'sticky'

  positioning = signus? positioning-mode <'-'> positioning-size
  positioning-mode = 'top' | 'right' | 'bottom' | 'left' | 'inset'
  positioning-size = size unit? | unit | fraction | 'auto'

  visibility = 'visible' | 'invisible'

  z-index = signus? <'z-'> (#'\\d+' | 'auto')

  flex-direction = <'flex-'> ('row' | 'row-reverse' | 'col' | 'col-reverse')

  flex-wrap = <'flex-'> ('wrap' | 'wrap-reverse' | 'nowrap')

  flex = <'flex-'> (flex-shorthand | (flex-grow-value (<'-'> flex-shrink-value)? <'-'> flex-basis-value))
  flex-grow = <'flex-grow'> (<'-'> flex-grow-value)?
  flex-shrink = <'flex-shrink'> (<'-'> flex-shrink-value)?

  flex-shorthand = '1' | 'auto' | 'initial' | 'none'
  flex-grow-value = int-number
  flex-shrink-value = int-number
  flex-basis-value = size unit? | unit | fraction | 'auto'

  order = signus? <'order-'> int-number | 'first' | 'last' | 'none'


  grid-template-columns = <'grid-cols-'> (int-number | 'none')
  grid-column-start-end =
     <'col-'> ('auto' |
               ('span-' (int-number | 'full')) |
               ('start' (int-number | 'auto')) |
               ('end' (int-number | 'auto')))

  grid-template-rows = <'grid-rows-'> (int-number | 'none')
  grid-row-start-end =
     <'row-'> ('auto' |
               ('span-' (int-number | 'full')) |
               ('start' (int-number | 'auto')) |
               ('end' (int-number | 'auto')))

  grid-auto-flow = <'grid-flow-'> ('row' | 'col') (<'-'> 'dense')?
  grid-auto-columns = <'auto-cols-'> ('auto' | 'min' | 'max' | (int-number? 'fr'))
  grid-auto-rows = <'auto-rows-'> ('auto' | 'min' | 'max' | (int-number? 'fr'))
  gap = <'gap-'> (axis <'-'>)? (size unit? | unit)


  padding = signus? <'p'> (direction | axis)? <'-'> (size unit? | unit)
  margin = signus? <'m'> (direction | axis)? <'-'> (size unit? | unit)

  signus = '-' | '+'
  direction = 't' | 'r' | 'b' | 'l'
  axis = 'x' | 'y'
  size = float-number
  <int-number> = #'\\d+'
  <float-number> = #'\\d+(_\\d+)?'
  fraction = #'\\d+' '/' #'\\d+' | 'full'
  unit = 'px' | 'em' | 'rem'
  ")

(def css-class-parser
  (insta/parser css-class-grammar))

(comment
  (css-class-parser "gap-x-37_5px")
  (css-class-parser "z-7")
  (css-class-parser "-order-37")
  (css-class-parser "flex-0-1-auto")
  (css-class-parser "overflow-x-auto")
  (css-class-parser "-inset-2/3")
  (css-class-parser "container")
  (css-class-parser "-mx-30_5px")
  (css-class-parser "pt-2px")
  (css-class-parser "pt-5")
  (css-class-parser "p-5"))

2020-12-19T15:27:09.438800Z

thatโ€™s my POC

erwinrooijakkers 2020-12-19T15:27:26.439Z

where do you use it for?

erwinrooijakkers 2020-12-19T15:27:29.439200Z

at work we use tailwind

erwinrooijakkers 2020-12-19T15:27:37.439400Z

makes css fun

erwinrooijakkers 2020-12-19T15:27:52.439600Z

i mean: where do you use that parser for?

2020-12-19T15:28:06.439900Z

Short answer: Where you can.

2020-12-19T15:28:13.440100Z

Long answer: Where you can.

erwinrooijakkers 2020-12-19T15:29:16.440300Z

note that you only needed to sort the numbers for instaparse in this question

erwinrooijakkers 2020-12-19T15:29:22.440500Z

it was already a valid grammar

2020-12-19T15:29:46.440700Z

I did without sorting, by adding a new root at the top.

2020-12-19T15:30:08.440900Z

I suspect that instaparse has an option to define which rule to use as the root.

erwinrooijakkers 2020-12-19T15:30:11.441100Z

you can use = : := and ::= to define rules

erwinrooijakkers 2020-12-19T15:30:14.441300Z

ah yes maybe

2020-12-19T15:30:30.441500Z

Yes, I know โ€ฆ I just wanted to play with the transformer.

2020-12-19T15:30:39.441700Z

I wanted to learn something.

erwinrooijakkers 2020-12-19T15:31:13.441900Z

๐Ÿ™‚

2020-12-19T15:46:10.442200Z

As "everyone", I used Instaparse. Discovered afterwards that you can just prepend "valid : 0\n" to the rules, instead of "sorting" rules so 0 is the first one.

(defn sort-rules [rules] (str "valid : 0\n" rules))

(defn solve-part-1 [input]
  (let [[rules messages] (str/split input #"\R\R")
        parse (insta/parser (sort-rules rules))
        valid? (comp not insta/failure? parse)]
    (count (filter valid? (str/split-lines messages)))))

1๐Ÿ’ฏ
erwinrooijakkers 2020-12-19T15:55:31.442500Z

aaaah

erwinrooijakkers 2020-12-19T15:55:31.442700Z

haha

benoit 2020-12-19T16:38:46.443900Z

This one was painful. A solution without instaparse: https://github.com/benfle/advent-of-code-2020/blob/main/day19.clj

1๐Ÿ‘1โค๏ธ1๐ŸŽ‰
2020-12-19T16:42:17.444400Z

How did you handle the infinite loops ?

benoit 2020-12-19T16:42:25.444600Z

It works for part2 with the recursive rules because concat is lazy ๐Ÿ™‚

2020-12-19T16:42:51.444800Z

ah, I see โ€ฆ breadth search?

2020-12-19T16:43:28.445Z

or just luck of being in the right order?

benoit 2020-12-19T16:43:48.445200Z

I basically generate all possible paths through the grammar until I find one that matches, yes.

1๐Ÿ‘
2020-12-19T16:44:48.445500Z

Todayโ€™s puzzle reminded me a lot of my library Minimallist where I did the exact same parsing.

2020-12-19T16:48:21.445800Z

@me1740 Does it still work if you reverse the order of the alt in the data?

(assoc-in [:rules 8] [:non-terminal '((42 8) (42))])

benoit 2020-12-19T16:49:23.446Z

(assoc-in [:rules 8] [:non-terminal '((42 8) (42))]) ?

benoit 2020-12-19T16:49:25.446200Z

yes

2020-12-19T16:49:40.446400Z

impressive

benoit 2020-12-19T16:59:14.446600Z

Actually it doesn't need to be lazy ๐Ÿ™‚ I'm not generating the strings, I'm matching against the messages so it will stop in the success case as well.

2020-12-19T17:01:01.446800Z

Start matching with this evil rule, does it still work? 666: 666 | 0

1๐Ÿ˜ˆ
benoit 2020-12-19T17:06:46.447100Z

That's where you need the laziness, yes ๐Ÿ™‚

benoit 2020-12-19T17:07:15.447300Z

It stack overflows when I doall the concat but works otherwise.

benoit 2020-12-19T17:08:10.447500Z

Nevermind I didn't test properly.

benoit 2020-12-19T17:08:18.447700Z

It overflows, yes.

1๐Ÿ˜ˆ
2020-12-19T17:09:24.448Z

Anyway, congratz on finishing it the hard way.

benoit 2020-12-19T17:13:40.448200Z

I would be curious to know the solution that takes advantage of the hint in part2. I still don't understand what they meant by this hint.

2020-12-19T17:15:11.448400Z

What hint?

benoit 2020-12-19T17:15:36.448600Z

This small change has a big impact: now, the rules do contain loops, and the list of messages they could hypothetically match is infinite. You'll need to determine how these changes affect which messages are valid.

Fortunately, many of the rules are unaffected by this change; it might help to start by looking at which rules always match the same set of values and how those rules (especially rules 42 and 31) are used by the new versions of rules 8 and 11.

1
benoit 2020-12-19T17:15:56.448800Z

Maybe it's for people who did Part1 by generating all possible strings in the language?

benoit 2020-12-19T17:17:11.449Z

For part1 I had a much simpler solution where I consumed the message while traversing the grammar.

benoit 2020-12-19T17:18:00.449200Z

Unfortunately this didn't work for Part2 because of the recursions. I was taking just one path in the grammar. That's when I had the idea to return a seq of matches instead of the first one,

2020-12-19T17:20:51.449700Z

You could have reuse your part 1 and push down the recursion a set of visited [rule matching-index] and fail a match if you are asked to match something you visited before (to detect a loop).

2020-12-19T17:22:33.450100Z

I donโ€™t understand the hint, it sounds more complicated to do something custom than doing it for the general case.

benoit 2020-12-19T17:23:12.450300Z

Why would you fail if you detect a cycle?

benoit 2020-12-19T17:23:32.450500Z

Some of the messages are only valid in part2 because those cycles are visited a few times.

2020-12-19T17:24:03.450700Z

the matching index is to be part of the state, not just the rule alone.

benoit 2020-12-19T17:26:21.450900Z

But my problem with part1 was not that it was looping but that it was stopping too early with unconsumed input

benoit 2020-12-19T17:27:38.451100Z

Because it was not greedy and repeating the cycles. That's where I got the idea to just generate all matches until I find one that return an empty string.

2020-12-19T17:28:18.451300Z

sleeping time here, see you tomorrow~

benoit 2020-12-19T17:29:26.451700Z

:thumbsup:

2020-12-20T00:06:48.461500Z

I solved part 1 by generating a regular expression, then rewrote my solution to solve both part 1 and 2 using a handwritten parser. It came out quite https://github.com/ocisly/advent2020/blob/674ec473fe599cc081a07a7d676502138cfbc672/day-19.clj#L26-L32!

1๐Ÿ‘
benoit 2020-12-20T04:43:50.466800Z

Looks great ๐Ÿ‘

2020-12-19T05:16:32.408400Z

.. because I am using it for another project, so I may learn something.

2020-12-19T05:31:02.408600Z

namely, I am parsing Tailwindsโ€™s grammar using instaparse.

2๐Ÿ‘
2020-12-19T05:36:42.408800Z

I hope to open source the project this weekend. Anyone who want to join the effort is welcome, I will make this project โ€œcommunity-builtโ€.

2๐Ÿ‘
rjray 2020-12-19T05:43:16.409Z

WTFโ€ฆ trying to checkout instaparse myself, and lein repl keeps erroring out on the :requireโ€ฆ

2020-12-19T05:44:20.409600Z

Push your code, I can take a look if you need.

rjray 2020-12-19T05:44:50.409800Z

(ns advent-of-code.day19
  (:require [clojure.string :as str]
            [instaparse.code :as insta]))

2020-12-19T05:44:59.410Z

.core

rjray 2020-12-19T05:45:09.410200Z

> _<

rjray 2020-12-19T05:45:13.410400Z

Dammit

rjray 2020-12-19T05:45:18.410600Z

Moving too fast.

2020-12-19T05:45:38.410800Z

(= 'too-fast 'slow)

rjray 2020-12-19T05:47:30.411Z

Yep ๐Ÿ™‚.

R.A. Porter 2020-12-19T05:50:22.411200Z

Well. Looks like I made the right choice using and learning Instaparse for yesterday. That is the first time Iโ€™ve ever solved both puzzles in under an hour.

(defn advent-19
  [input]
  (let [[grammar input] (input-&gt;groups input)
        grammar (-&gt;&gt; (str/split grammar #"\n")
                     (sort-by #(Integer/parseInt (first (str/split % #":"))))
                     (str/join "\n"))
        parser (insta/parser grammar)]
    (-&gt;&gt; (str/split input #"\n")
         (map #(insta/parses parser %))
         (remove insta/failure?)
         count)))

22๐Ÿป32๐Ÿ‘2๐Ÿ˜2โ˜๏ธ254๐ŸŽ‰
R.A. Porter 2020-12-19T05:51:10.411400Z

(`input->groups` is a utility function Iโ€™ve used a few times to split puzzle input on a blank line.)

1๐Ÿ‘
2020-12-19T05:51:36.411600Z

I am learning the transform function.

2020-12-19T06:07:55.411800Z

Which instaparse function to use for checking if a string matches a parser?

rjray 2020-12-19T06:08:30.412Z

I couldnโ€™t find one! If it matches, you get a vector, if it doesnโ€™t you get a map. So I test with sequential?.

rjray 2020-12-19T06:09:00.412200Z

Just made 793 on the second starโ€™s leaderboard. w00t!

2๐ŸŽ‰
2020-12-19T06:09:20.412400Z

wow

rjray 2020-12-19T06:09:57.412700Z

If you hadnโ€™t of mentioned Instaparse, Iโ€™d still be trying to hand-code a parser-generator!

1๐Ÿ˜„
R.A. Porter 2020-12-19T06:15:02.412900Z

Thereโ€™s no parsable? or similar, but if you call parses it should prove useful.

1โ˜๏ธ
2020-12-19T06:15:38.413100Z

I wrote a function to transform the input into an instaparse grammar, but I have a bug somewhere.

rjray 2020-12-19T06:16:13.413300Z

Hereโ€™s my code: https://github.com/rjray/advent-2020-clojure/blob/master/src/advent_of_code/day19.clj

2๐ŸŽ‰
rjray 2020-12-19T06:16:38.413600Z

Itโ€™s hacky. I plan to clean up the parts that transform the input into an Instaparse grammar tomorrow.

2020-12-19T06:17:25.413800Z

Here is mine. If you can spot the bug, that would be nice.

(def input-parser (insta/parser "
  &lt;input&gt; = rules &lt;#'\\R\\R'&gt; messages
  rules = rule+
  messages = message+
  rule = rule-ref &lt;':'&gt; disjunction
  disjunction = (conjunction (&lt;'|'&gt; conjunction)*)
  conjunction = rule-ref+ | text-literal
  rule-ref = #'\\d+'
  text-literal = &lt;'\"'&gt; #'[a-z]+' &lt;'\"'&gt;
  &lt;message&gt; = #'.+'
" :auto-whitespace :standard))

(let [[rules messages] (-&gt;&gt; (input-parser input-str)
                            (insta-tf/transform {:rules (fn [&amp; rules] (vec rules))
                                                 :messages (fn [&amp; messages] (vec messages))
                                                 :rule (fn [rule-id rule-definition]
                                                         (str rule-id " = " rule-definition "\n"))
                                                 :rule-ref (fn [rule-id]
                                                             (str "rule" rule-id))
                                                 :disjunction (fn [&amp; args]
                                                                (let [s (str/join " | " args)]
                                                                  (if (&gt; (count args) 1)
                                                                    (str "(" s ")")
                                                                    s)))
                                                 :conjunction (fn [&amp; args]
                                                                (str/join " " args))
                                                 :text-literal (fn [text-literal]
                                                                 (str "'" text-literal "'"))}))
      new-grammar (reduce str rules)
      new-parser (insta/parser new-grammar)]
  (-&gt;&gt; messages
       (map new-parser)
       (filter sequential?)
       count))

2020-12-19T06:17:48.414Z

I am not running for the leaderboard anymore.

rjray 2020-12-19T06:18:52.414200Z

Huh. My transformation of the rules was dead-simple.

rjray 2020-12-19T06:20:14.414400Z

For each line, change โ€œ:โ€ to โ€ = โ€œ. Change every number to โ€œR<num>โ€œ. Then preface it all with โ€œS = R0\nโ€.

2020-12-19T06:21:14.414600Z

I wanted to learn instaparse, so I chose what you see above ๐Ÿ˜›

rjray 2020-12-19T06:21:56.414800Z

Thatโ€™s a valid point. I come from a Perl background, so I tend to see regexp transformations as the solution to everything ๐Ÿ™‚.

2020-12-19T06:22:07.415Z

oh, I know โ€ฆ (a b | c d) should be (a b) | (c d)

rjray 2020-12-19T06:23:16.415200Z

Oh yeah, Thatโ€™s going to make a huge diff. I didnโ€™t see that when I looked at the code before.

rjray 2020-12-19T06:24:27.415400Z

Got my best finish-placement yet. And learned about Instaparse! https://github.com/rjray/advent-2020-clojure/blob/master/src/advent_of_code/day19.clj

2๐ŸŽ‰
2020-12-19T06:24:43.415600Z

I am not sure how instaparse does its grouping

2020-12-19T06:27:29.415800Z

It still does not work

2020-12-19T06:41:37.416300Z

TIL a few needed instaparse functions from @coyotesqrl

1๐Ÿ‘
2020-12-19T06:52:55.416500Z

@coyotesqrl Is that really necessary to sort the rules?

2020-12-19T06:53:53.416700Z

OOOOHHHH I found my bug !

2020-12-19T06:54:08.416900Z

I should parse from rule 0

2020-12-19T06:57:29.417100Z

Problem solved ๐Ÿ™‚

2020-12-19T07:06:39.417600Z

TIL that Instaparse places a precedence on the concatenation compared to the โ€˜|' operator.

2020-12-19T07:11:43.418500Z

> โ€œI am a good library, AoC had me tested!โ€ > > โ€” Instaparse

3๐Ÿ˜‚
2020-12-19T07:45:52.419500Z

@rjray: I feel like an impostor, having used instaparse for part 2.

joppe 2020-12-19T07:54:54.420200Z

Haha instaparse, really the tip of the day ๐Ÿ˜„

2020-12-19T07:58:26.420500Z

I solved it with regexs ๐Ÿ˜‚

2020-12-19T07:59:55.420700Z

the second part was hard, because Javaโ€™s regexs donโ€™t support recursion.

2020-12-19T08:02:38.422400Z

Should have gone with instaparse right away, I also got all the test data working but failed on the file. https://github.com/Dexterminator/advent-of-code-2020/blob/main/src/day18/core.clj

1๐Ÿ‘
misha 2020-12-19T08:07:08.424400Z

read part2 description and *insta*ntly replaced page of spaghetti embarrassment with instaparse :kappa:

misha 2020-12-19T08:15:50.424500Z

https://github.com/rjray/advent-2020-clojure/blob/master/src/advent_of_code/day19.clj#L29 @rjray you could have just

(reduce-kv str/replace input
  {"8: 42"     "8: 42 | 42 8"
   "11: 42 31" "11: 42 31 | 42 11 31"})
or if you don't mind repetition
(-&gt; input
  (str/replace "8: 42" "8: 42 | 42 8")
  (str/replace "11: 42 31" "11: 42 31 | 42 11 31")))

misha 2020-12-19T08:19:46.425Z

(defn to-grammar [rules]
  (str "S = 0\n" (str/replace rules #":" "=")))

(defn solve [input]
  (let [[rules messages] (str/split input #"\n\n")
        grammar (to-grammar rules)
        parser  (insta/parser grammar)]
     (-&gt;&gt; messages
       (str/split-lines)
       (map #(insta/parse parser %))
       (remove insta/failure?)
       (count))))

2020-12-19T08:46:46.426300Z

๐Ÿ˜

1๐Ÿ‘
2020-12-19T09:58:37.427Z

I wonder anyone else solved it without instaparse?

1๐Ÿ‘
2020-12-19T10:10:37.427200Z

My solution with regexs https://github.com/zelark/AoC-2020/blob/master/src/zelark/aoc_2020/day_19.clj

4๐Ÿ’ช
misha 2020-12-19T10:13:55.427500Z

I did part 1 with recursion and re-pattern, bailed on it as soon as read p2

2020-12-19T10:16:44.428700Z

me and my regular expression for part 1

ใ€œ(๏ผž๏ผœ)ใ€œ

"(?:(?:(?:b(?:b(?:(?:b(?:(?:(?:ab|aa)b|(?:ab|bb)a)b|(?:(?:ba|bb)b|(?:ab|bb)a)a)|a(?:(?:a(?:ab)|b(?:ba|bb))b|(?:b(?:ab|aa)|a(?:aa|b(?:b|a)))a))a|(?:b(?:a(?:(?:b|a)(?:(?:b|a)b|aa))|b(?:a(?:ab|bb)|b(?:aa|b(?:b|a))))|a(?:(?:(?:ab|aa)(?:b|a))a|(?:(?:aa)b|(?:ab|aa)a)b))b)|a(?:(?:b(?:(?:(?:ba|bb)b|(?:ab|bb)a)b|(?:a(?:(?:b|a)b|aa)|b(?:ab|ba))a)|a(?:a(?:(?:ab|bb)a|(?:(?:b|a)(?:b|a))b)|b(?:(?:ab|aa)(?:b|a))))b|(?:b(?:(?:(?:(?:b|a)(?:b|a))a|(?:bb)b)b|(?:(?:ab)a|(?:ab)b)a)|a(?:a(?:(?:ab|aa)a)|b(?:b(?:(?:b|a)(?:b|a))|a(?:bb))))a))|a(?:(?:a(?:(?:(?:a(?:ab|aa)|b(?:ab|(?:b|a)a))a|(?:b(?:ab)|a(?:ab|ba))b)b|(?:(?:(?:bb|aa)a)a|(?:(?:b|a)(?:ab|(?:b|a)a))b)a)|b(?:b(?:(?:a(?:ab))b|(?:b(?:(?:b|a)b|aa)|a(?:bb|aa))a)|a(?:b(?:b(?:bb|aa)|a(?:ba|bb))|a(?:(?:aa)a|(?:bb)b))))a|(?:(?:b(?:a(?:(?:ab)a|(?:aa|b(?:b|a))b)|b(?:b(?:(?:b|a)(?:b|a))|a(?:bb)))|a(?:(?:b(?:ba|bb)|a(?:bb))b|(?:(?:(?:b|a)b|aa)a)a))b|(?:(?:a(?:(?:ab|(?:b|a)a)b|(?:ab|ba)a)|b(?:(?:(?:b|a)(?:b|a))a|(?:ab|aa)b))a|(?:a(?:a(?:ab)|b(?:(?:b|a)b|aa))|b(?:b(?:ba|bb)|a(?:bb)))b)a)b)))(?:(?:b(?:b(?:(?:b(?:(?:(?:ab|aa)b|(?:ab|bb)a)b|(?:(?:ba|bb)b|(?:ab|bb)a)a)|a(?:(?:a(?:ab)|b(?:ba|bb))b|(?:b(?:ab|aa)|a(?:aa|b(?:b|a)))a))a|(?:b(?:a(?:(?:b|a)(?:(?:b|a)b|aa))|b(?:a(?:ab|bb)|b(?:aa|b(?:b|a))))|a(?:(?:(?:ab|aa)(?:b|a))a|(?:(?:aa)b|(?:ab|aa)a)b))b)|a(?:(?:b(?:(?:(?:ba|bb)b|(?:ab|bb)a)b|(?:a(?:(?:b|a)b|aa)|b(?:ab|ba))a)|a(?:a(?:(?:ab|bb)a|(?:(?:b|a)(?:b|a))b)|b(?:(?:ab|aa)(?:b|a))))b|(?:b(?:(?:(?:(?:b|a)(?:b|a))a|(?:bb)b)b|(?:(?:ab)a|(?:ab)b)a)|a(?:a(?:(?:ab|aa)a)|b(?:b(?:(?:b|a)(?:b|a))|a(?:bb))))a))|a(?:(?:a(?:(?:(?:a(?:ab|aa)|b(?:ab|(?:b|a)a))a|(?:b(?:ab)|a(?:ab|ba))b)b|(?:(?:(?:bb|aa)a)a|(?:(?:b|a)(?:ab|(?:b|a)a))b)a)|b(?:b(?:(?:a(?:ab))b|(?:b(?:(?:b|a)b|aa)|a(?:bb|aa))a)|a(?:b(?:b(?:bb|aa)|a(?:ba|bb))|a(?:(?:aa)a|(?:bb)b))))a|(?:(?:b(?:a(?:(?:ab)a|(?:aa|b(?:b|a))b)|b(?:b(?:(?:b|a)(?:b|a))|a(?:bb)))|a(?:(?:b(?:ba|bb)|a(?:bb))b|(?:(?:(?:b|a)b|aa)a)a))b|(?:(?:a(?:(?:ab|(?:b|a)a)b|(?:ab|ba)a)|b(?:(?:(?:b|a)(?:b|a))a|(?:ab|aa)b))a|(?:a(?:a(?:ab)|b(?:(?:b|a)b|aa))|b(?:b(?:ba|bb)|a(?:bb)))b)a)b))(?:a(?:a(?:(?:b(?:b(?:b(?:ab|aa)|a(?:ba|aa))|a(?:(?:b|a)(?:ab|bb)))|a(?:(?:b(?:ab|bb)|a(?:ab|aa))a|(?:(?:ab|bb)a|(?:ab|(?:b|a)a)b)b))b|(?:a(?:b(?:a(?:ab|aa)|b(?:(?:b|a)(?:b|a)))|a(?:a(?:bb)|b(?:aa)))|b(?:a(?:b(?:aa)|a(?:bb|aa))|b(?:b(?:(?:b|a)(?:b|a))|a(?:bb))))a)|b(?:a(?:a(?:(?:a(?:ba|(?:b|a)b)|b(?:ab|(?:b|a)a))a|(?:a(?:ab|ba)|b(?:ba|bb))b)|b(?:(?:b(?:ab)|a(?:ab|ba))b|(?:b(?:ab|ba)|a(?:ab|aa))a))|b(?:b(?:b(?:(?:(?:b|a)b|aa)a|(?:aa)b)|a(?:(?:ab|bb)a))|a(?:(?:(?:aa|b(?:b|a))b|(?:ab|ba)a)a|(?:a(?:ba|(?:b|a)b)|b(?:ab|(?:b|a)a))b))))|b(?:(?:(?:(?:(?:a(?:ab|aa)|b(?:(?:b|a)(?:b|a)))a|(?:a(?:aa)|b(?:aa))b)a|(?:b(?:a(?:ab|ba)|b(?:ba|bb))|a(?:a(?:ab|aa)|b(?:ab|(?:b|a)a)))b)b|(?:(?:b(?:(?:ab|ba)b|(?:ba|aa)a)|a(?:a(?:ab|ba)|b(?:aa|b(?:b|a))))b|(?:a(?:a(?:ab|ba)|b(?:aa|b(?:b|a)))|b(?:(?:ba|bb)a|(?:ab)b))a)a)a|(?:(?:(?:(?:(?:ba|bb)b|(?:bb)a)a|(?:b(?:ab|ba)|a(?:ab|bb))b)b|(?:a(?:a(?:(?:b|a)b|aa)|b(?:ab|ba))|b(?:(?:ab|bb)a|(?:ab)b))a)a|(?:(?:a(?:(?:ba|bb)a|(?:ab)b)|b(?:b(?:bb)|a(?:ba|bb)))a|(?:(?:b(?:ab)|a(?:ba))a|(?:(?:ab|ba)a|(?:ba|aa)b)b)b)b)b))))"

12๐Ÿ˜ฌ33๐Ÿ˜ฑ1๐Ÿ™ˆ1๐Ÿ˜ญ
erwinrooijakkers 2020-12-22T16:12:59.067100Z

@zelark did you generate that regex??

2020-12-22T17:36:36.073100Z

> how did you create that visualization? @euccastro https://regexper.com/

2020-12-22T17:39:04.073400Z

@erwinrooijakkers hahaha, did it by hands https://media.giphy.com/media/lCbSAbRrFEfkY/giphy.gif

2020-12-22T18:08:52.078300Z

@adam.thalhammer youโ€™re right, itโ€™s not possible at least in Clojure/Java. But there is a trick I used. You arenโ€™t asked to solve general problem, what they ask is to solve a given input. In the end, I generated a regex for the part 2, which searches only five levels down.

1โœ…
misha 2020-12-19T10:20:06.430800Z

you got to do what you got to doยฉ

misha 2020-12-19T10:27:31.430900Z

to get *second gold star

misha 2020-12-19T10:34:20.431100Z

(defn calc
  ;; outer
  ([priority form]
   (let [f (complement number?)
         x (reduce
             (fn [form ops]
               (if (f form)
                 (calc priority ops form)
                 (reduced form)))
             form
             priority)]
     (if (f x) (first x) x)))

  ;;inner
  ([priority OP form]
   (if (number? form)
     form
     (loop [todo  (rest form)
            done  [(calc priority (first form))]]
       (if (empty? todo)
         done
         (let [[op x &amp; todo] todo
               f (eval op)
               a (peek done)
               b (if (list? x) (calc priority x) x)]
           (if (OP op)
             (recur todo (conj (pop done) (f a b)))
             (recur todo (conj done op b)))))))))


(calc '[#{+} #{*}] '[(4 * (3 * 2 + 2) * (9 * 7 * 5 * 4 * 9) * (7 * 7 + 7 * 4 + 9)) + 6 * 4 + 8 + ((6 * 5) * 4 * (2 * 8 + 4 + 7 * 9 + 3) * 2 + 6) + 3])
(calc '[#{+} #{-}] '(10 + (6 * 5)))


(defn solve [input priority]
  (-&gt;&gt; input
    (str/split-lines)
    (map #(read-string (str "[" % "]")))
    (map #(calc priority %))
    (reduce +)))

(assert (= 15285807527593  (solve i '[#{+ *}])))
(assert (= 461295257566346 (solve i '[#{+} #{*}])))

1๐Ÿ‘
euccastro 2020-12-19T11:08:37.431300Z

today was brutal for me... satisfying too, in the end! https://github.com/euccastro/advent-of-code-2020/blob/master/src/advent/day19.clj

1๐Ÿ‘
euccastro 2020-12-19T11:15:06.431700Z

@zelark I eventually solved it without regexes or instaparse. I tried regexes, and even found a clever way to do recursive regexes in Java, but my first attempts didn't work and I decided that regexes were not the most fun thing to debug and moved on ๐Ÿ™‚

Joe 2020-12-19T11:25:39.432400Z

https://github.com/RedPenguin101/aoc2020/blob/main/day19.clj - Instaparse feels like cheating here, but the thought of building it myself filled me with horror.

3๐Ÿ‘
nbardiuk 2020-12-19T11:26:36.432900Z

without instaparse it is slow and hacky https://github.com/nbardiuk/adventofcode/blob/master/2020/src/day19.clj

1๐Ÿ’ก
nbardiuk 2020-12-19T11:32:25.433300Z

yes, the colors are not consistent, one receives two gold stars per day. But there are silver and gold stars on stats page https://adventofcode.com/2020/stats

euccastro 2020-12-19T11:32:25.433500Z

mine runs in ~300msecs

euccastro 2020-12-19T11:42:12.434Z

here's mine (I don't have one for part 2 though)

#"^(a((b(b(a(baa|(ba|a(b|a))b)|b(bba|abb))|a(b(aba|baa)|a((aa|(b|a)b)b|(ba|(b|a)b)a)))|a(a((b(b|a)|aa)(b|a)b|((aa|(b|a)b)b|(ba|(b|a)b)a)a)|b(((aa|(b|a)b)b|(ba|a(b|a))a)b|(baa|(b(b|a)|aa)b)a)))a|((a((b|a)(aa|bb)a|(ba|a(b|a))(b|a)b)|b(((ba|a(b|a))b|(ab|aa)a)b|a(aa|(b|a)b)a))b|(((b(ab|ba)|a(aa|bb))a|(baa|(ba|a(b|a))b)b)a|(((aa|ba)b|aaa)b|((ba|a(b|a))b|aaa)a)b)a)b)|b(b((b(b(baa|(ba|a(b|a))b)|a(bbb|bba))|a(a(a(aa|ba)|b(ba|a(b|a)))|ba(aa|(b|a)b)))a|(((b(ba|(b|a)b)|a(ba|a(b|a)))b|(a(aa|bb)|b(bb|ba))a)a|(babb|a(bbb|abb))b)b)|a(((b((ab|ba)b|aaa)|a(b|a)(ba|a(b|a)))a|((baa|(b(b|a)|aa)b)a|(bb|ba)bb)b)b|(((b|a)(ba|(b|a)b)a|bbab)a|(a((ab|ba)b|(ba|a(b|a))a)|b((ab|ba)b|(b(b|a)|aa)a))b)a)))(a((b(b(a(baa|(ba|a(b|a))b)|b(bba|abb))|a(b(aba|baa)|a((aa|(b|a)b)b|(ba|(b|a)b)a)))|a(a((b(b|a)|aa)(b|a)b|((aa|(b|a)b)b|(ba|(b|a)b)a)a)|b(((aa|(b|a)b)b|(ba|a(b|a))a)b|(baa|(b(b|a)|aa)b)a)))a|((a((b|a)(aa|bb)a|(ba|a(b|a))(b|a)b)|b(((ba|a(b|a))b|(ab|aa)a)b|a(aa|(b|a)b)a))b|(((b(ab|ba)|a(aa|bb))a|(baa|(ba|a(b|a))b)b)a|(((aa|ba)b|aaa)b|((ba|a(b|a))b|aaa)a)b)a)b)|b(b((b(b(baa|(ba|a(b|a))b)|a(bbb|bba))|a(a(a(aa|ba)|b(ba|a(b|a)))|ba(aa|(b|a)b)))a|(((b(ba|(b|a)b)|a(ba|a(b|a)))b|(a(aa|bb)|b(bb|ba))a)a|(babb|a(bbb|abb))b)b)|a(((b((ab|ba)b|aaa)|a(b|a)(ba|a(b|a)))a|((baa|(b(b|a)|aa)b)a|(bb|ba)bb)b)b|(((b|a)(ba|(b|a)b)a|bbab)a|(a((ab|ba)b|(ba|a(b|a))a)|b((ab|ba)b|(b(b|a)|aa)a))b)a)))(b(((b(b((b|a)(b|a)a|(b(b|a)|aa)b)|a(b|a)(ba|a(b|a)))|a((bba|a(b(b|a)|aa))a|((b|a)(b|a)a|(ab|aa)b)b))b|(b(b(aab|(ab|aa)a)|a((ab|ba)a|bbb))|a(a(bbb|abb)|b((aa|bb)b|(ba|(b|a)b)a)))a)b|((b(b((ab|aa)a|(aa|bb)b)|a(a(aa|(b|a)b)|b(bb|ba)))|a((b|a)(aa|bb)b|(abb|bab)a))b|(b(((ab|ba)b|(bb|ba)a)a|(a(bb|ba)|b(b(b|a)|aa))b)|a(a(aaa|(b|a)(b|a)b)|b((aa|(b|a)b)b|aaa)))a)a)|a(a(b((a(a(aa|ba)|b(ba|a(b|a)))|b(bba|(aa|(b|a)b)b))b|((bba|bab)b|(bba|a(b(b|a)|aa))a)a)|a((a(bb|ba)b|(aaa|bab)a)a|(aba|baa)(b|a)b))|b(((((bb|ba)a|bbb)b|(aba|b(b|a)(b|a))a)b|(b(b(b|a)(b|a)|a(ba|(b|a)b))|aa(aa|(b|a)b))a)b|((((ba|(b|a)b)b|aba)b|(a(ba|(b|a)b)|b(bb|ba))a)b|((b(b(b|a)|aa)|a(aa|ba))a|((ab|ba)b|(bb|ba)a)b)a)a)))$"

euccastro 2020-12-19T11:42:44.434200Z

so did yours work without anchoring?

euccastro 2020-12-19T11:45:17.434400Z

aaaand yes, I guess I should make my groups non-capturing ๐Ÿ™‚

euccastro 2020-12-19T11:58:00.434600Z

btw by "found" I mean I looked it up, not that I discovered it myself: https://stackoverflow.com/a/3644267

1โœ”๏ธ
euccastro 2020-12-19T11:59:19.434900Z

a^n b^n is the pattern in 11. the pattern in 8 is just (42-pattern)+

2020-12-19T13:57:58.436400Z

my regex-ish version runs in 13.371895 msecs!

1๐Ÿ˜ฎ2๐ŸŽ๏ธ
erwinrooijakkers 2020-12-19T15:25:14.437600Z

instaparsed https://github.com/transducer/adventofcode/blob/master/src/adventofcode/2020/day19.clj

2๐Ÿ‘
erwinrooijakkers 2020-12-19T15:26:27.438300Z

> Tailwindsโ€™s grammar using instaparse tell me more!

2020-12-19T15:26:53.438500Z

(ns girouette.grammar.css-class
  (:require [instaparse.core :as insta]))

;; Use case 1: "css class name -&gt; corresponding css"
;; - parse a class name,
;; - find its matching rule and its variable bindings,
;; - call the css generator with the bindings,
;; - get the css corresponding to the class name.

;; Use case 2: "generate css for given combinations of rules and bindings"
;; - given a rule, some bindings between variables and a collection of values,
;; - generate the corresponding css, aggregated in order.

;; Note: the generated css could be either a string or garden format.
;; Beware: the order of the css statements matter, it should be the same as Tailwind.

(def css-class-grammar "
  rule =
     (* Layout Section *)
     container | box-sizing | display | floats | clear |
     object-fit | object-position | overflow | overscroll |
     position | positioning | visibility | z-index |

     (* Flexbox Section *)
     flex-direction | flex-wrap | flex | order |

     (* Grid Section *)
     grid-template-columns | grid-column-start-end |
     grid-template-rows | grid-row-start-end |
     grid-auto-flow | grid-auto-columns | grid-auto-rows | gap |


     padding | margin


  container = &lt;'container'&gt;

  box-sizing = 'box-border' | 'box-content'

  display = 'block' | 'inline-block' | 'inline' | 'flex' | 'inline-flex' |
     'table' | 'table-caption' | 'table-cell' | 'table-column' | 'table-column-group' |
     'table-footer-group' | 'table-header-group' | 'table-row-group' | 'table-row' |
     'flow-root' | 'grid' |'inline-grid' | 'contents' | 'hidden'

  floats = &lt;'float-'&gt; ('left' | 'right' | 'none')

  clear = &lt;'clear-'&gt; ('left' | 'right' | 'both' | 'none')

  object-fit = &lt;'object-'&gt; ('contain' | 'cover' | 'fill' | 'none' | 'scale-down')

  object-position = &lt;'object-'&gt; object-position-side
  &lt;object-position-side&gt; = 'left' | 'left-bottom' | 'left-top' |
                           'right' | 'right-bottom' | 'right-top' |
                           'center' | 'bottom' | 'top'

  overflow = &lt;'overflow-'&gt; (axis &lt;'-'&gt;)? overflow-mode
  overflow-mode = 'auto' | 'hidden' | 'visible' | 'scroll'

  overscroll = &lt;'overscroll-'&gt; (axis &lt;'-'&gt;)? overscroll-mode
  overscroll-mode = 'auto' | 'contain' | 'none'

  position = 'static' | 'fixed' | 'absolute' | 'relative' | 'sticky'

  positioning = signus? positioning-mode &lt;'-'&gt; positioning-size
  positioning-mode = 'top' | 'right' | 'bottom' | 'left' | 'inset'
  positioning-size = size unit? | unit | fraction | 'auto'

  visibility = 'visible' | 'invisible'

  z-index = signus? &lt;'z-'&gt; (#'\\d+' | 'auto')

  flex-direction = &lt;'flex-'&gt; ('row' | 'row-reverse' | 'col' | 'col-reverse')

  flex-wrap = &lt;'flex-'&gt; ('wrap' | 'wrap-reverse' | 'nowrap')

  flex = &lt;'flex-'&gt; (flex-shorthand | (flex-grow-value (&lt;'-'&gt; flex-shrink-value)? &lt;'-'&gt; flex-basis-value))
  flex-grow = &lt;'flex-grow'&gt; (&lt;'-'&gt; flex-grow-value)?
  flex-shrink = &lt;'flex-shrink'&gt; (&lt;'-'&gt; flex-shrink-value)?

  flex-shorthand = '1' | 'auto' | 'initial' | 'none'
  flex-grow-value = int-number
  flex-shrink-value = int-number
  flex-basis-value = size unit? | unit | fraction | 'auto'

  order = signus? &lt;'order-'&gt; int-number | 'first' | 'last' | 'none'


  grid-template-columns = &lt;'grid-cols-'&gt; (int-number | 'none')
  grid-column-start-end =
     &lt;'col-'&gt; ('auto' |
               ('span-' (int-number | 'full')) |
               ('start' (int-number | 'auto')) |
               ('end' (int-number | 'auto')))

  grid-template-rows = &lt;'grid-rows-'&gt; (int-number | 'none')
  grid-row-start-end =
     &lt;'row-'&gt; ('auto' |
               ('span-' (int-number | 'full')) |
               ('start' (int-number | 'auto')) |
               ('end' (int-number | 'auto')))

  grid-auto-flow = &lt;'grid-flow-'&gt; ('row' | 'col') (&lt;'-'&gt; 'dense')?
  grid-auto-columns = &lt;'auto-cols-'&gt; ('auto' | 'min' | 'max' | (int-number? 'fr'))
  grid-auto-rows = &lt;'auto-rows-'&gt; ('auto' | 'min' | 'max' | (int-number? 'fr'))
  gap = &lt;'gap-'&gt; (axis &lt;'-'&gt;)? (size unit? | unit)


  padding = signus? &lt;'p'&gt; (direction | axis)? &lt;'-'&gt; (size unit? | unit)
  margin = signus? &lt;'m'&gt; (direction | axis)? &lt;'-'&gt; (size unit? | unit)

  signus = '-' | '+'
  direction = 't' | 'r' | 'b' | 'l'
  axis = 'x' | 'y'
  size = float-number
  &lt;int-number&gt; = #'\\d+'
  &lt;float-number&gt; = #'\\d+(_\\d+)?'
  fraction = #'\\d+' '/' #'\\d+' | 'full'
  unit = 'px' | 'em' | 'rem'
  ")

(def css-class-parser
  (insta/parser css-class-grammar))

(comment
  (css-class-parser "gap-x-37_5px")
  (css-class-parser "z-7")
  (css-class-parser "-order-37")
  (css-class-parser "flex-0-1-auto")
  (css-class-parser "overflow-x-auto")
  (css-class-parser "-inset-2/3")
  (css-class-parser "container")
  (css-class-parser "-mx-30_5px")
  (css-class-parser "pt-2px")
  (css-class-parser "pt-5")
  (css-class-parser "p-5"))

2020-12-19T15:27:09.438800Z

thatโ€™s my POC

erwinrooijakkers 2020-12-19T15:27:26.439Z

where do you use it for?

erwinrooijakkers 2020-12-19T15:27:29.439200Z

at work we use tailwind

erwinrooijakkers 2020-12-19T15:27:37.439400Z

makes css fun

erwinrooijakkers 2020-12-19T15:27:52.439600Z

i mean: where do you use that parser for?

2020-12-19T15:28:06.439900Z

Short answer: Where you can.

2020-12-19T15:28:13.440100Z

Long answer: Where you can.

erwinrooijakkers 2020-12-19T15:29:16.440300Z

note that you only needed to sort the numbers for instaparse in this question

erwinrooijakkers 2020-12-19T15:29:22.440500Z

it was already a valid grammar

2020-12-19T15:29:46.440700Z

I did without sorting, by adding a new root at the top.

2020-12-19T15:30:08.440900Z

I suspect that instaparse has an option to define which rule to use as the root.

erwinrooijakkers 2020-12-19T15:30:11.441100Z

you can use = : := and ::= to define rules

erwinrooijakkers 2020-12-19T15:30:14.441300Z

ah yes maybe

2020-12-19T15:30:30.441500Z

Yes, I know โ€ฆ I just wanted to play with the transformer.

2020-12-19T15:30:39.441700Z

I wanted to learn something.

erwinrooijakkers 2020-12-19T15:31:13.441900Z

๐Ÿ™‚

2020-12-19T15:46:10.442200Z

As "everyone", I used Instaparse. Discovered afterwards that you can just prepend "valid : 0\n" to the rules, instead of "sorting" rules so 0 is the first one.

(defn sort-rules [rules] (str "valid : 0\n" rules))

(defn solve-part-1 [input]
  (let [[rules messages] (str/split input #"\R\R")
        parse (insta/parser (sort-rules rules))
        valid? (comp not insta/failure? parse)]
    (count (filter valid? (str/split-lines messages)))))

1๐Ÿ’ฏ
erwinrooijakkers 2020-12-19T15:55:31.442500Z

aaaah

erwinrooijakkers 2020-12-19T15:55:31.442700Z

haha

2020-12-19T16:11:22.443500Z

Instaparse can start at any rule, there is an option for that.

(insta/parser new-grammar :start :rule0)

7๐Ÿ‘
benoit 2020-12-19T16:38:46.443900Z

This one was painful. A solution without instaparse: https://github.com/benfle/advent-of-code-2020/blob/main/day19.clj

1๐Ÿ‘1โค๏ธ1๐ŸŽ‰
2020-12-19T16:42:17.444400Z

How did you handle the infinite loops ?

benoit 2020-12-19T16:42:25.444600Z

It works for part2 with the recursive rules because concat is lazy ๐Ÿ™‚

2020-12-19T16:42:51.444800Z

ah, I see โ€ฆ breadth search?

2020-12-19T16:43:28.445Z

or just luck of being in the right order?

benoit 2020-12-19T16:43:48.445200Z

I basically generate all possible paths through the grammar until I find one that matches, yes.

1๐Ÿ‘
2020-12-19T16:44:48.445500Z

Todayโ€™s puzzle reminded me a lot of my library Minimallist where I did the exact same parsing.

2020-12-19T16:48:21.445800Z

@me1740 Does it still work if you reverse the order of the alt in the data?

(assoc-in [:rules 8] [:non-terminal '((42 8) (42))])

benoit 2020-12-19T16:49:23.446Z

(assoc-in [:rules 8] [:non-terminal '((42 8) (42))]) ?

benoit 2020-12-19T16:49:25.446200Z

yes

2020-12-19T16:49:40.446400Z

impressive

benoit 2020-12-19T16:59:14.446600Z

Actually it doesn't need to be lazy ๐Ÿ™‚ I'm not generating the strings, I'm matching against the messages so it will stop in the success case as well.

2020-12-19T17:01:01.446800Z

Start matching with this evil rule, does it still work? 666: 666 | 0

1๐Ÿ˜ˆ
benoit 2020-12-19T17:06:46.447100Z

That's where you need the laziness, yes ๐Ÿ™‚

benoit 2020-12-19T17:07:15.447300Z

It stack overflows when I doall the concat but works otherwise.

benoit 2020-12-19T17:08:10.447500Z

Nevermind I didn't test properly.

benoit 2020-12-19T17:08:18.447700Z

It overflows, yes.

1๐Ÿ˜ˆ
2020-12-19T17:09:24.448Z

Anyway, congratz on finishing it the hard way.

benoit 2020-12-19T17:13:40.448200Z

I would be curious to know the solution that takes advantage of the hint in part2. I still don't understand what they meant by this hint.

2020-12-19T17:15:11.448400Z

What hint?

benoit 2020-12-19T17:15:36.448600Z

This small change has a big impact: now, the rules do contain loops, and the list of messages they could hypothetically match is infinite. You'll need to determine how these changes affect which messages are valid.

Fortunately, many of the rules are unaffected by this change; it might help to start by looking at which rules always match the same set of values and how those rules (especially rules 42 and 31) are used by the new versions of rules 8 and 11.

1
benoit 2020-12-19T17:15:56.448800Z

Maybe it's for people who did Part1 by generating all possible strings in the language?

benoit 2020-12-19T17:17:11.449Z

For part1 I had a much simpler solution where I consumed the message while traversing the grammar.

benoit 2020-12-19T17:18:00.449200Z

Unfortunately this didn't work for Part2 because of the recursions. I was taking just one path in the grammar. That's when I had the idea to return a seq of matches instead of the first one,

2020-12-19T17:20:32.449500Z

I'm all for transforming inputs into some known problem and letting a lib do all the work (like creating a graph problem from the inputs and feeding it to loom for example) but this time I literally didn't have to change anything about the format. https://github.com/Dexterminator/advent-of-code-2020/tree/main/src/day19

2๐Ÿ‘
2020-12-19T17:20:51.449700Z

You could have reuse your part 1 and push down the recursion a set of visited [rule matching-index] and fail a match if you are asked to match something you visited before (to detect a loop).

2020-12-19T17:22:33.450100Z

I donโ€™t understand the hint, it sounds more complicated to do something custom than doing it for the general case.

benoit 2020-12-19T17:23:12.450300Z

Why would you fail if you detect a cycle?

benoit 2020-12-19T17:23:32.450500Z

Some of the messages are only valid in part2 because those cycles are visited a few times.

2020-12-19T17:24:03.450700Z

the matching index is to be part of the state, not just the rule alone.

benoit 2020-12-19T17:26:21.450900Z

But my problem with part1 was not that it was looping but that it was stopping too early with unconsumed input

benoit 2020-12-19T17:27:38.451100Z

Because it was not greedy and repeating the cycles. That's where I got the idea to just generate all matches until I find one that return an empty string.

2020-12-19T17:28:18.451300Z

sleeping time here, see you tomorrow~

benoit 2020-12-19T17:29:26.451700Z

:thumbsup:

rjray 2020-12-19T17:42:31.452400Z

Wow. I hadnโ€™t realized that the format was usable as-is. I really canโ€™t wait until I can go back and clean up my code!

rjray 2020-12-19T17:49:03.453800Z

Yeah, Iโ€™ve now learned that I didnโ€™t need to do any of the pre-processing to the rules. I wonder if Wastl knew about Clojure+Instaparse ๐Ÿ™‚.

2020-12-19T18:02:51.455100Z

Instaparse was so so helpful when I built this old thing (about four years ago ๐Ÿ˜ฎ) https://www.imperimetric.com/ I felt pretty dumb when I didn't use it for day 18 when I had used it so much before

2020-12-19T19:25:36.456Z

2๐Ÿ•ธ๏ธ
misha 2020-12-19T19:33:53.457Z

I did (str "S = 0\n" input)

1๐Ÿคฏ
misha 2020-12-19T19:36:27.457400Z

http://instaparse-live.matt.is/

2๐Ÿ‘
Joe 2020-12-19T22:41:03.458900Z

Has anyone else seen https://www.youtube.com/watch?v=Ren_QQHM3iI Livecoding Clojure AOC?

2๐Ÿ‘€
Mno 2020-12-19T22:44:03.460100Z

Oohh that's real nice

euccastro 2020-12-19T23:02:39.460400Z

how did you create that visualization?

Joe 2020-12-19T23:06:10.460600Z

I'd be interested in peoples thoughts. I found the results not as elegant as some of the solutions here.

2020-12-19T23:46:59.461200Z

I don't think it's possible to create a regular expression to solve part 2, because the rule:

11: 42 31 | 42 11 31
requires matching n times whatever rule 42 matches followed by n times whatever rule 31 matches. If n is not known, then the language described by this rule is not regular, as it would require a "memory" (i.e. a stack) to keep track of how many instances of rule 42 have already been observed. (Your input may differ, but if you have a rule of the same "shape" the same logic applies)