Daniel Janus
Daniel Janus

Reputation: 657

How to match against a hierarchy with clojure.core.match?

Assume I have an ad-hoc hierarchy of entities represented as Clojure keywords, like this:

(def h 
  (-> (make-hierarchy)
      (derive :animal :feline)
      (derive :feline :cat) 
      (derive :feline :lion) 
      (derive :feline :tiger)))

What would be the best way (preferably using clojure.core.match) to write a match-hierarchy function so that these contrived examples return expected results:

(defn example [x y]
  (match-hierarchy h [x y]
    [:cat :cat] 0
    [:cat :feline] 1
    [:cat :animal] 2
    [:animal :animal] 3))

(example :cat :cat)
;=> 0

(example :cat :tiger)
;=> 1

(example :tiger :tiger) 
;=> 3

i.e., match-hierarchy should return the value corresponding to the first clause whose elements are all either equal to the corresponding element of the value being matched, or its (direct or indirect) ancestors?

I'm happy with using something custom instead of make-hierarchy to create the hierarchies. I'm also happy with not using core.match if another option is available. However, I need it to work with objects of pre-existing classes, like numbers (e.g., I want to be able to say 3 is a :positive-integer is an :integer is a :number).


Background: I am writing a toy x86 assembler in Clojure, where I need to assemble my instruction based on its name and operands. Currently my code includes something like:

(match [(-> instr name keyword) (operand-type op1) (operand-type op2)]
  ;; other options
  [:int :imm nil]  (if (= op1 3)
                     {:opcode 0xcc}
                     {:opcode 0xcd, :immediate (imm 8 op1)})

(i.e., an INT instruction assembles to two bytes, 0xcd followed by the interrupt number, unless it's an INT 3 in which case it's only one byte, 0xcc). I find this somewhat ugly, however, and am looking for a more elegant approach. So instead I'd like to say something along the lines of

(asm-match [(-> instr name keyword) op1 op2]
  ;; other options
  [:int 3 nil]     {:opcode 0xcc}
  [:int :imm nil]  {:opcode 0xcd, :immediate (imm 8 op1)})

Upvotes: 2

Views: 357

Answers (1)

dnolen
dnolen

Reputation: 18556

(match [(-> instr name keyword) op1 (operand-type op1) (operand-type op2)]
  [:int 3 :imm nil] {:opcode 0xcc}
  [:int _ :imm nil] {:opcode 0xcd, :immediate (imm 8 op1)})

Does this not work for you?

Upvotes: 4

Related Questions