Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to traverse parse tree from instaparse

I'm experimenting with Clojure and Instaparse. I have created a small toy language, and I'm getting stuck at how to properly treat the resulting tree. This is what i get:

[:ClassDescription 
 [:ClassName "Test"]
 [:Properties 
  [:Property 
   [:PropertyName "ID"] 
   [:PropertyType "Int"]] 
  [:Property 
   [:PropertyName "Name"] 
   [:PropertyType "string"]]]]

Now, as an example I would like to extract all PropertyTypes. I have two main ways and I'd like a solution for both.

  1. By specifying a path; something like [:ClassDescription :Properties :Property :PropertyType]
  2. By extracting all :PropertyType elements, regardless of depth.

For A., my first idea was to convert some parts of it to maps via insta/transform and then use get-in, but then I get a really clunky solution involving nested loops and get-in.

I can also use nth, and drill my self into the structure, but that seems cumbersome, and will break easily if I add another layer.

My other idea was a recursive solution where I treat every element the same way and loop through it and check for all matches.

For B. my only solution so far is a recursive function that just drills through everything and tries to match the first element.

I believe that these "handwritten" functions could be avoided by some clever combination of insta/transform, map, filter, reduce, etc. Can it?

like image 743
NiklasJ Avatar asked Nov 23 '25 01:11

NiklasJ


1 Answers

For getting elements out of the parsed data, you could use tree-seq to iterate all and pick what you need. E.g.:

(defn parsed-tree-seq [parsed]
  (tree-seq #(vector? (second %)) rest parsed))

(map second
     (filter
       #(= (first %) :PropertyType)
       (parsed-tree-seq parsed)))
; => ("Int" "string")

Yet you might be better off to shape your data already in the first place by using <...> in your parser and/or by turning them into something more mapish, what would be easier to access via transformation. E.g. turn the :Properties into a list of maps and turn the whole thing into a map:

(defn transform [parsed]
  (insta/transform
    {:Property #(into {} %&)
     :Properties #(vec (concat [:Properties] [%&]))
     :ClassDescription #(into {} %&)
     } parsed))

(map :PropertyType (:Properties (transform parsed)))
; => ("Int" "string")
like image 161
cfrick Avatar answered Nov 24 '25 21:11

cfrick



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!