As a follow up to my previous question, I am trying to implement a simple pattern matching in Clojure.
I would like something like the following:
(match target
[ub] expr1 ; ub should be bound to actual value in expr1
['< ub] expr2 ; match the literal less-than symbol
; and ub should be bound to actual value in expr2
[lb ub] expr3 ; lb and ub should be bound to actual values in expr3
:else expr4 ; default case if none match
)
Usage:
(match [< 5.0] ...)
should arrange to execute expr2 at runtime.
I would like to write a macro, but I am not sure of the expansion.
I am considering having each case-and-clause expand to a let with bindings to internal variables and checks that the literal symbols ('<) actually matches the pattern. Maybe for the second pattern (['< ub]):
(let [[sym1 ub] pattern]
(if (= '< sym1)
expr1)
Do I need to use (gensym) for the bindings? How?
Bigger picture:
(range-case target
[0.0 < 1.0] :greatly-disagree
[< 2.0] :disagree
[< 3.0] :neutral
[< 4.0] :agree
[5.0] :strongly-agree
42 :the-answer
:else :do-not-care)
I am trying to match the [...] patterns and convert them to the following:
[ub] (if previous-ub `(and (<= ~previous-ub ~target) (<= ~target ~ub))
`(< ~target ~ub))
['< ub] (if previous-ub `(and (<= ~previous-ub ~target) (< ~target ~ub))
`(< ~target ~ub))
[lb ub] `(and (<= ~lb ~target) (<= ~target ~ub))
['< lb ub] `(and (< ~lb ~target) (<= ~target ~ub))
[lb '< ub] `(and (<= ~lb ~target) (< ~target ~ub))
['< lb '< ub] `(and (< ~lb ~target) (< ~target ~ub))
I have a cond that checks that the case part is a vector. This pattern match should occur inside that case.
My first idea was basically the same: Bind stuff to internal locals and test on their contents in a big
and. For literals the value is bound to a generated local; symbols are used directly in the binding.I also added a check that the spec vector matches the length of the target vector. Otherwise you can’t have
[ub]as well as[lb ub]since neither contains a check which could fail. So always the first would be selected.Here is the code:
And an example expansion. I didn’t use syntax-quote in the example to reduce the noise in the expansion, but you should get the idea.
The invokation was:
Hope that helps you get started.