Page 1 of 1

[beginner] I need a macro (probably)

Posted: Thu Sep 05, 2013 8:59 am
by vega
Hello,
I am trying to parse some text and I am using the esrap library. Even if you are not familiar with it, please read on because you can perhaps still answer my question.

Code: Select all

(require :esrap)

(defpackage :my-grammar
  (:use :cl :esrap))

(in-package :my-grammar)

(defparameter *currency-names*
  '((usd . "USD")
    (eur . "EUR")
    (gbp . "GBP")))

(defun name-for-symbol (symbol table)
  (cdr (assoc symbol table)))

(defun symbol-for-name (name table)
  (car (rassoc name table :test 'equal)))

(defrule currency (or "USD" "EUR" "GBP") ;;esrap's defrule is a macro
  (:lambda (name)
    (symbol-for-name (text name) *currency-names*)))
The above works as intended, that is it recognizes the input if it is one of the three currency names and returns the corresponding symbol.

Code: Select all

MY-GRAMMAR> (parse 'currency "USD")
USD
NIL
But I want to change the definition of the rule like this, so that the names of the currencies are not hard-coded, because this would avoid code duplication and because I have to parse other things in a way similar to currencies.

Code: Select all

(defrule currency (or (all-names *currencies*)) ;; this is not working
  (:lambda (name)
    (symbol-for-name (text name) *currency-names*)))
where I have tried all-names to be

Code: Select all

(defun all-names (table)
  (mapcar #'cdr table))
and

Code: Select all

(defmacro all-names (table)
  `(mapcar #'cdr ,table))
among other things

I guess all-names must be a macro and not a function and I guess the problem is that my all-names returns ("USD" "EUR" "GBP"), while the "or" in defrule expects the list to be spliced. So if only I could create the list (or "USD" "EUR" "GBP") and give it to defrule. Also I read somewhere that I should use some instead of or in similar(?) cases, but some requires a predicate (in addition to a list) and I can't think of one.

I assume this might be simple, but I am a beginner in lisp. Any help would be appreciated. For anyone wishing to try, you should be able to run the above code if you load esrap using quicklisp first.

Re: [beginner] I need a macro (probably)

Posted: Thu Sep 05, 2013 12:28 pm
by Goheeca
You can write this macro:

Code: Select all

(defmacro all-names (table)
  `(or ,@(mapcar #'cdr table))
and use it this way:

Code: Select all

(defrule currency (all-names *currencies*)
  (:lambda (name)
    (symbol-for-name (text name) *currency-names*)))
According to the source code there is an option use a predicate so you can create one:

Code: Select all

(defun member-of-currencies (elem)
  (member elem (mapcar #'cdr *currencies*)))

(defrule currency (member-of-currencies)
  (:lambda (name)
    (symbol-for-name (text name) *currency-names*)))
I hard-coded the variable *currencies*, because I don't know how exactly is treated the predicate by the rule class and wether you can pass arguments to it.