This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

[beginner] I need a macro (probably)

2 posts · 1959 views

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.
(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.
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.
(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
(defun all-names (table)
  (mapcar #'cdr table))
and
(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)

You can write this macro:
(defmacro all-names (table)
  `(or ,@(mapcar #'cdr table))
and use it this way:
(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:
(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.
cl-2dsyntax is my attempt to create a Python-like reader. My mirror of CLHS (and the dark themed version). Temporary mirrors of aferomentioned: CLHS and a dark version.