Representing Grammars in Lisp - common-lisp

Does anybody know of a project that uses Emacs-Lisp or Common-Lisp to formally encode grammars of programming languages? My special requirement is that I plan to use the representation to optionally encode translations between grammar rules.

you can use the Alexa and cl-yacc in Common Lisp.
Ill answer this by using some examples from a compiler I wrote.
suppose you want to write a grammar for python. you would do this:
define lexer:
(deftype token ()
`(cons type val))
(defun tok (type &optional val)
(cons type val))
(alexa:define-string-lexer python-lexer
"Make a lexical analyzer for Python expressions."
((:num "\\d+")
(:name "[A-Za-z][A-Za-z0-9_]*"))
("and" (return (tok :boolop (intern (string-upcase $#) 'keyword))))
("or" (return (tok :boolop (intern (string-upcase $#) 'keyword))))
("not" (return (tok :unaryop (intern (string-upcase $#) 'keyword))))
("\\(" (return (tok :left-paren)))
("\\)" (return (tok :right-paren)))
("\\+" (return (tok :plus (intern $# 'keyword))))
("\\-" (return (tok :minus (intern $# 'keyword))))
("\\==" (return (tok :cmp (intern (string-upcase $#) 'keyword))))
("\\!=" (return (tok :cmp (intern (string-upcase $#) 'keyword))))
("\\<" (return (tok :cmp (intern (string-upcase $#) 'keyword))))
("\\<=" (return (tok :cmp (intern (string-upcase $#) 'keyword))))
("\\>" (return (tok :cmp (intern (string-upcase $#) 'keyword))))
("\\>=" (return (tok :cmp (intern (string-upcase $#) 'keyword))))
("\\=" (return (tok :assignment)))
("True" (return (tok :bool (intern (string-upcase $#) 'keyword))))
("False" (return (tok :bool (intern (string-upcase $#) 'keyword))))
("if" (return (tok :if (intern (string-upcase $#) 'keyword))))
("else" (return (tok :else (intern (string-upcase $#) 'keyword))))
("while" (return (tok :while (intern (string-upcase $#) 'keyword))))
("print" (return (tok :print (intern (string-upcase $#) "ZETTA-VAR"))))
("fun" (return (tok :fun (intern (string-upcase $#) "ZETTA-VAR"))))
("\\:" (return (tok :colon)))
("{{NAME}}" (return (tok :name (intern (string-upcase $#) "ZETTA-VAR"))))
("{{NUM}}" (return (tok :constant (parse-integer $#))))
("\\s+" nil))
(defun token-generator (toks)
(lambda ()
(if (null toks)
(values nil nil)
(let ((tok (pop toks)))
(values (token-type tok)
(token-value tok))))))
(defun token-type (tok)
(car tok))
(defun token-value (tok)
(cdr tok))
(defun lex-line (string)
(loop :with lexer := (python-lexer string)
:for tok := (funcall lexer)
:while tok
:collect tok))
and the parser you would write like this:
(define-parser *python-grammar*
(:start-symbol module)
(:terminals (:boolop :unaryop :cmp :bool :assignment :if :else :while :colon :name :constant :right-paren :left-paren :print :plus :minus :fun))
(module
(statements #'build-module))
(statements
statement
exp
(statement statements)
(exp statements))
(statement
(:print :left-paren :right-paren)
(:print :left-paren exp :right-paren #'build-print)
function
exp
assignment
(statements :while exp :colon statements #'build-while)
(:if exp :colon statements :else :colon statements #'build-if))
(function
(:fun variable :left-paren args :right-paren :colon statements #'build-function))
(exp
variable
int
(:minus exp #'build-neg-num)
(exp :plus exp #'build-addition)
(exp :minus exp #'build-sub)
(:bool #'build-bool)
(exp :boolop exp #'build-bool-op)
(:unaryop exp #'build-unaryop)
(exp :cmp exp #'build-cmp))
(args
variable
(variable args))
(variable
(:name #'build-variable))
(assignment
(:name :assignment exp #'build-assignment))
(int
(:constant #'build-int))))
If you know how to write a grammar then writing the parser is straightforward. The only tricky part is the functions you see by the rules. these functions allow you to construct your ast; for example, consider the function build-function:
(defun build-function (fun-tok var lparen-tok args r-paren colon-tok statements)
(declare (ignore fun-tok lparen-tok r-paren colon-tok))
(make-py-function :name var
:args args
:statements statements))
where make-py-function is a structure ie:
(defstruct py-function name args statements)
so when the parser calls this functions it constructs a parser node using a the given structure.

Related

Common lisp macro not calling function

I am working on a complicated macro and have run into a roadblock.
(defmacro for-each-hashtable-band (body vars on &optional counter name)
`(block o
(with-hash-table-iterator (next-entry ,on)
(destructuring-bind
,(apply #'append vars)
(let ((current-band (list ,#(mapcar #'not (apply #'append vars)))))
(for (i 1 ,(length (apply #'append vars)) 2)
(multiple-value-bind
(succ k v) (next-entry)
(if succ
(progn
(setf (nth i current-band) k)
(setf (nth (+ 1 i) current-band) v))
(return-from o nil))))
current-band)
,#body))))
im getting "Evaluation aborted on #<UNDEFINED-FUNCTION NEXT-ENTRY {100229C693}>"
i dont understand why next-entry appears to be invisible to the macro i have created.
I've tried stripping down this to a small replicable example but i couldnt find a minimal scenario without the macro i created where next-entry would be invisible besides this scenario no matter what I tried, i've always managed to find a way to call next-entry in my other examples so im stumped as to why i cannot get it working here
I've tested the for macro ive created and it seems to generally work in most cases but for some reason it cannot see this next-entry variable. How do i make it visible?
In your code there are multiple places where the macro generates bindings in a way that is subject to variable capture (pdf).
(defmacro for-each-hashtable-band (body vars on &optional counter name)
`(block o ;; VARIABLE CAPTURE
(with-hash-table-iterator (next-entry ,on) ;; VARIABLE CAPTURE
(destructuring-bind ,(apply #'append vars)
(let ((current-band ;;; VARIABLE CAPTURE
(list ,#(mapcar #'not (apply #'append vars)))))
(for
(i ;;; VARIABLE CAPTURE
1 ,(length (apply #'append vars)) 2)
(multiple-value-bind (succ k v) ;;; VARIABLE CAPTURE
,(next-entry) ;;; WRONG EVALUATION TIME
(if succ
(progn
(setf (nth i current-band) k)
(setf (nth (+ 1 i) current-band) v))
(return-from o nil))))
current-band)
,#body))))
A simplified example of such a capture is:
`(let ((x 0)) ,#body)
Here above, the x variable is introduced, but if the code is expanded in a context where xis already bound, then body will not be able to reference that former x binding and will always see x bound to zero (you generally don't want this behavior).
Write a function instead
Instead of writing a big macro for this, let's first try understanding what you want to achieve and write instead a higher-order function, ie. a function that calls user-provided functions.
If I understand correctly, your function iterates over a hash-table by bands of entries. I assume vars holds a list of (key value) pairs of symbols, for example ((k1 v1) (k2 v2)). Then, body works on all the key/value pairs in the band.
In the following code, the function map-each-hashtable-band accepts a function, a hash-table, and instead of vars it accepts a size, the width of the band (the number of pairs).
Notice how in your code, you only have one loop, which builds a band using the hash-table iterator. But then, since the macro is named for-each-hashtable-band, I assume you also want to loop over all the bands. The macro with-hash-table-iterator provides an iterator but does not loop itself. That's why here I have two loops.
(defun map-each-hashtable-band (function hash-table band-size)
(with-hash-table-iterator (next-entry hash-table)
(loop :named outer-loop :do
(loop
:with key and value and next-p
:repeat band-size
:do (multiple-value-setq (next-p key value) (next-entry))
:while next-p
:collect key into current-band
:collect value into current-band
:finally (progn
(when current-band
(apply function current-band))
(unless next-p
(return-from outer-loop)))))))
For example:
(map-each-hashtable-band (lambda (&rest band) (print `(:band ,band)))
(alexandria:plist-hash-table
'(:a 0 :b 1 :c 2 :d 3 :e 4 :f 5 :g 6))
2)
NB. Iterating over a hash-table happens in an arbitrary order, there is no guarantee that you'll see the entries in any particular kind of order, this is implementation-dependant.
With my current version of SBCL this prints the following:
(:BAND (:A 0 :B 1))
(:BAND (:C 2 :D 3))
(:BAND (:E 4 :F 5))
(:BAND (:G 6))
Wrap the function in a macro
The previous function might not be exactly the behavior you want, so you need to adapt to your needs, but once it does what you want, you can wrap a macro around it.
(defmacro for-each-hashtable-band (vars hash-table &body body)
`(map-each-hashtable-band (lambda ,(apply #'append vars) ,#body)
,hash-table
,(length vars)))
For example:
(let ((test (alexandria:plist-hash-table '(:a 0 :b 1 :c 2 :d 3 :e 4 :f 5))))
(for-each-hashtable-band ((k1 v1) (k2 v2)) test
(format t "~a -> ~a && ~a -> ~a ~%" k1 v1 k2 v2)))
This prints:
A -> 0 && B -> 1
C -> 2 && D -> 3
E -> 4 && F -> 5
Macro-only solution, for completeness
If you want to have only one, single macro, you can start by inlining the body of the above function in the macro, you don't need to use apply anymore, but instead you need to establish bindings around the body, using destructuring-bind as you did. A first draft would be to simply as follows, but notice that this is not a proper solution:
(defmacro for-each-hashtable-band (vars hash-table &body body)
(let ((band-size (length vars)))
`(with-hash-table-iterator (next-entry ,hash-table)
(loop :named outer-loop :do
(loop
:with key and value and next-p
:repeat ,band-size
:do (multiple-value-setq (next-p key value) (next-entry))
:while next-p
:collect key into current-band
:collect value into current-band
:finally (progn
(when current-band
(destructuring-bind ,(apply #'append vars) current-band
,#body))
(unless next-p
(return-from outer-loop))))))))
In order to be free of variable capture problems with macros, each temporary variable you introduce must be named after a symbol that cannot exist in any context you expand your code. So instead we first unquote all the variables, making the macro definition fail to compile:
(defmacro for-each-hashtable-band (vars hash-table &body body)
(let ((band-size (length vars)))
`(with-hash-table-iterator (,next-entry ,hash-table)
(loop :named ,outer-loop :do
(loop
:with ,key and ,value and ,next-p
:repeat ,band-size
:do (multiple-value-setq (,next-p ,key ,value) (,next-entry))
:while ,next-p
:collect ,key into ,current-band
:collect ,value into ,current-band
:finally (progn
(when ,current-band
(destructuring-bind ,(apply #'append vars) ,current-band
,#body))
(unless ,next-p
(return-from ,outer-loop))))))))
When compiling the macro, the macro is supposed to inject symbols into the code, but here we have a compilation error that says undefined variables:
;; undefined variables: CURRENT-BAND KEY NEXT-ENTRY NEXT-P OUTER-LOOP VALUE
So now, those variables should be fresh symbols:
(defmacro for-each-hashtable-band (vars hash-table &body body)
(let ((band-size (length vars)))
(let ((current-band (gensym))
(key (gensym))
(next-entry (gensym))
(next-p (gensym))
(outer-loop (gensym))
(value (gensym)))
`(with-hash-table-iterator (,next-entry ,hash-table)
(loop :named ,outer-loop :do
(loop
:with ,key and ,value and ,next-p
:repeat ,band-size
:do (multiple-value-setq (,next-p ,key ,value) (,next-entry))
:while ,next-p
:collect ,key into ,current-band
:collect ,value into ,current-band
:finally (progn
(when ,current-band
(destructuring-bind ,(apply #'append vars) ,current-band
,#body))
(unless ,next-p
(return-from ,outer-loop)))))))))
This above is a bit verbose, but you could simplify that.
Here is what the previous for-each-hashtable-band example expands into with this new macro:
(with-hash-table-iterator (#:g1576 test)
(loop :named #:g1578
:do (loop :with #:g1575
and #:g1579
and #:g1577
:repeat 2
:do (multiple-value-setq (#:g1577 #:g1575 #:g1579) (#:g1576))
:while #:g1577
:collect #:g1575 into #:g1574
:collect #:g1579 into #:g1574
:finally (progn
(when #:g1574
(destructuring-bind
(k1 v1 k2 v2)
#:g1574
(format t "~a -> ~a && ~a -> ~a ~%" k1 v1 k2
v2)))
(unless #:g1577 (return-from #:g1578))))))
Each time you expand it, the #:gXXXX variables are different, and cannot possibly shadow existing bindings, so for example, the body can use variables named like current-band or value without breaking the expanded code.

Access to function arguments by their names in Common Lisp

I want to get a function argument value, using an argument name.
The following code don't works, because symbol-value working only with global variables:
(defun test1 (&key v1)
(format t "V1: ~A~%" (symbol-value (intern "V1"))))
Is there a portable way to do this in Common Lisp?
You can use a custom environment to map strings to functions:
(use-package :alexandria)
(defvar *env* nil)
(defun resolve (name &optional (env *env*))
(if-let (entry (assoc name env :test #'string=))
(cdr entry)
(error "~s not found in ~a" name env)))
(defmacro bind (bindings env &body body)
(assert (symbolp env))
(let ((env (or env '*env*)))
(loop
for (n v) in bindings
collect `(cons ,n ,v) into fresh-list
finally
(return
`(let ((,env (list* ,#fresh-list ,env)))
,#body)))))
(defmacro call (name &rest args)
`(funcall (resolve ,name) ,#args))
For example:
(bind (("a" (lambda (u) (+ 3 u)))
("b" (lambda (v) (* 5 v))))
nil
(call "a" (call "b" 10)))
Here is another version of an explicit named-binding hack. Note this isn't well (or at all) tested, and also note the performance is not going to be great.
(defun named-binding (n)
;; Get a binding by its name: this is an error outside
;; WITH-NAMED-BINDINGS
(declare (ignore n))
(error "out of scope"))
(defun (setf named-binding) (val n)
;; Set a binding by its name: this is an error outside
;; WITH-NAMED-BINDINGS
(declare (ignore val n))
(error "out of scope"))
(defmacro with-named-bindings ((&rest bindings) &body decls/forms)
;; establish a bunch of bindings (as LET) but allow access to them
;; by name
(let ((varnames (mapcar (lambda (b)
(cond
((symbolp b) b)
((and (consp b)
(= (length b) 2)
(symbolp (car b)))
(car b))
(t (error "bad binding ~S" b))))
bindings))
(decls (loop for df in decls/forms
while (and (consp df) (eql (car df) 'declare))
collect df))
(forms (loop for dft on decls/forms
for df = (first dft)
while (and (consp df) (eql (car df) 'declare))
finally (return dft)))
(btabn (make-symbol "BTAB")))
`(let (,#bindings)
,#decls
(let ((,btabn (list
,#(mapcar (lambda (v)
`(cons ',v (lambda (&optional (val nil valp))
(if valp
(setf ,v val)
,v))))
varnames))))
(flet ((named-binding (name)
(let ((found (assoc name ,btabn)))
(unless found
(error "no binding ~S" name))
(funcall (cdr found))))
((setf named-binding) (val name)
(let ((found (assoc name ,btabn)))
(unless found
(error "no binding ~S" name))
(funcall (cdr found) val))))
(declare (inline named-binding (setf named-binding)))
,#forms)))))
And now:
> (with-named-bindings ((x 1))
(setf (named-binding 'x) 2)
(named-binding 'x))
2
Even better:
(defun amusing (x y)
(with-named-bindings ((x x) (y y))
(values #'named-binding #'(setf named-binding))))
(multiple-value-bind (reader writer) (amusing 1 2)
(funcall writer 2 'x)
(funcall reader 'x))
will work.

Simple OO style programming with Common Lisp

I am trying to make a 'pseudo OO system':
(defun bank-account ()
(let ((balance))
(labels ((init (x)
(setf balance x))
(increment (x)
(setf balance (+ balance x)))
(get-balance ()
balance))
(lambda (func)
(case func (init #'init)
(increment #'increment)
(get-balance #'get-balance))))))
(defparameter bank-account-object (bank-account))
(funcall (funcall bank-account-object 'init) 42)
(funcall (funcall bank-account-object 'increment) 10)
(funcall (funcall bank-account-object 'get-balance))
Q: are there better ways to accomplish the same without using CLOS, defstruct, or defmacro?
The problem that I see with this is that it is closed for extension, and I see no simple way to add extensibility.
Minor nitpick: that's not a bank-system but a bank-account. When you think about that further, it seems to me that the interesting part about this example domain has not been touched: double accounting, i. e. ensuring the null-sum invariant.
There are two sayings: a closure is a poor man's object, and an object is a poor man's closure. I have the feeling that you are more in the realm of the former here. However, it might be a good learning experience to think about this—as long as you don't put it into production somewhere…
;; The "class"
(defun create-bank-account ()
(let ((balance))
(labels ((init (x)
(setf balance x))
(increment (x)
(setf balance (+ balance x)))
(get-balance ()
balance))
(lambda (func)
(case func (init #'init)
(increment #'increment)
(get-balance #'get-balance))))))
;; The "methods"
(defun init-balance (object amount)
(funcall (funcall object 'init) amount))
(defun increment-balance (object amount)
(funcall (funcall object 'increment) amount))
(defun get-balance (object)
(funcall (funcall object 'get-balance)))
;; Example usage
(defparameter bank-account (create-bank-account))
(init-balance bank-account 42) ; => 42
(increment-balance bank-account 10) ; => 52
(get-balance bank-account) ; => 52
As mentioned in other answers, the resulting object might be hard to extend. That could be a feature, but one possible way to improve on it is to let it be redefined dynamically. You can even switch from classes to protoypes.
(ql:quickload :optima)
(defpackage :obj (:use :cl :optima))
(in-package :obj)
(defun make-object (&optional prototype)
(let ((properties (make-hash-table :test #'eq))
(self))
(flet ((resolve (key)
(or (gethash key properties)
(and prototype (funcall prototype :get key)))))
(setf self
(lambda (&rest args)
(optima:ematch args
((list :get :prototype) prototype)
((list :get key) (resolve key))
((list :set :prototype p)
(cerror "Continue" "Changing prototype object, are you sure?")
(setf prototype p))
((list :set key value)
(if value
(setf (gethash key properties) value)
(remhash key properties)))
((list :invoke method args)
(let ((resolved (resolve method)))
(if resolved
(apply resolved self args)
(funcall (or (resolve :no-such-method)
(error "No such method: ~a in ~a"
method
self))
self
method))))))))))
Some helper symbols:
;; call built-in command
(defmacro $ (obj method &rest args)
`(funcall ,obj ,method ,#args))
;; access property
(declaim (inline # (setf #)))
(defun # (o k) ($ o :get k))
(defun (setf #) (v o k) ($ o :set k v))
;; invoke method
(defun % (o m &rest a)
($ o :invoke m a))
A simple test
(let ((a (make-object)))
;; set name property
(setf (# a :name) "a")
;; inherit
(let ((b (make-object a)))
(print (list (# b :name)
;; shadow name property
(setf (# b :name) "b")
(# a :name)))
;; define a method
(setf (# a :foo) (lambda (self) (print "FOO")))
;; invoke it
(% a :foo)))
Bank account
(defun create-bank-account (&optional parent)
(let ((account (make-object parent)))
(prog1 account
(setf (# account :init)
(lambda (self x)
(setf (# self :balance) x)))
(setf (# account :increment)
(lambda (self increment)
(incf (# self :balance) increment))))))
(let ((account (create-bank-account)))
(% account :init 0)
(% account :increment 100)
(# account :balance))
100

Common Lisp dispatch on first element of sequence

Is it possible to define methods that dispatch on particular elements (such as the first) in a sequence? Is it possible to do this without using "fully general" predicate dispatch machinery?
I have a program below that rewrites an expression in propositional calculus in negative normal form (i.e. all negation appears before a variable). It also removes all connectives besides and and or and replaces them with equivalent definitions.
It heavily uses methods, but I don't think I'm using the object system to full effect.
I want to be able to have a bunch of separate defmethod cases for normalize and normalize-not based on the symbol 'and, 'or, 'not, 'imp, 'iff currently heading the expression.
In particular, I'd like to be able to write something similar to the following
(defmethod normalize-all ((exprs ('and list)))
`(and ,#normalize-all (cdr exprs)))
as a standalone definition governing what happens when exprs is a list with 'and as its car.
What I have now performs "top-level" type checks only and uses cond heavily in the body of functions.
(defmethod normalize-all ((exprs list))
(loop for x in exprs collecting (normalize x)))
(defmethod prepend-not-to-all ((exprs list))
(loop for x in exprs collecting (list 'not x)))
(defmethod normalize ((expr symbol))
expr)
(defmethod normalize-not ((expr symbol))
`(not ,expr))
(defmethod normalize ((expr cons))
(let
((head (car expr))
(tail (cdr expr)))
(cond
((eq head 'and)
`(and ,#(normalize-all tail)))
((eq head 'or)
`(or ,#(normalize-all tail)))
((eq head 'imp)
`(or
,(normalize `(not ,(first tail)))
,(normalize (second tail))))
((eq head 'iff)
`(and ,(normalize `(imp ,(first tail) ,(second tail)))
,(normalize `(imp ,(second tail) ,(first tail)))))
((eq head 'not)
(normalize-not (first tail))))))
(defmethod normalize-not ((expr cons))
(let
((head (car expr))
(tail (cdr expr)))
(cond
((eq head 'and)
`(or ,#(normalize-all (prepend-not-to-all tail))))
((eq head 'or)
`(and ,#(normalize-all (prepend-not-to-all tail))))
((eq head 'imp)
`(and
,(normalize (first tail))
,(normalize-not (second tail))))
((eq head 'iff)
`(or
(normalize-not `(imp ,(first tail) ,(second tail)))
(normalize-not `(imp ,(second tail) ,(first tail)))))
((eq head 'not)
(normalize (first tail))))))
(print (normalize '(iff a b)))
More generally, I'm trying to mimic one style of writing functions in Haskell that looks like the following, because the cases are easy to inspect.
f :: Int -> String -> Int
f 0 _ = ...
f 1 (x:xs) = ...
f n _ | n `mod` 2 == 0 = ...
| otherwise = ...
The pattern appearing before the pipe is based on the structure of the types involved. Ints have no structure (n+k patterns don't count are gone anyway), so we can only match on particular integers, ignore the integer with _ or capture it with a variable.
Arbitrary boolean-valued expressions can appear after the | and control whether the definition on the rhs is in effect or not. (n `mod` 2 == 0 checks whether a number is even, otherwise is just True but reads better).
Cases that are textually earlier always have precedence over ones that are textually later. And f is closed in the sense that all components of its definition must appear at the definition site.
There's not built in way to do this. If you want to mimic the Haskell style, you could use a pattern matching library such as Trivia. It doesn't allow you to write the clauses in separate top-level forms though.
;; Dependencies: (ql:quickload '(:fare-quasiquote :trivia :trivia.quasiquote))
(defpackage #:normalize
(:use #:cl #:named-readtables #:trivia)
(:export #:normalize #:imp #:iff))
(in-package #:normalize)
(in-readtable :fare-quasiquote)
(defun normalize-all (exprs)
(mapcar #'normalize exprs))
(defun prepend-not-to-all (exprs)
(mapcar (lambda (x) `(not ,x)) exprs))
(defun-ematch normalize (expr)
;; The body of DEFUN-EMATCH is a list of clauses starting with the
;; pattern and followed by a body of forms to execute when the
;; pattern matches the sole argument (formatted here for the sake of
;; readability with the pattern on the left and the body on the
;; right). The EMATCH variant signals an error if no pattern matches
;; the input.
((type symbol) expr)
(`(and ,#tail) `(and ,#(normalize-all tail)))
(`(or ,#tail) `(or ,#(normalize-all tail)))
(`(imp ,a ,b) `(or ,(normalize `(not ,a))
,(normalize b)))
(`(iff ,a ,b) `(and ,(normalize `(imp ,a ,b))
,(normalize `(imp ,b ,a))))
(`(not ,expr) (normalize-not expr)))
(defun-ematch normalize-not (expr)
((type symbol) `(not ,expr))
(`(and ,#tail) `(or ,#(normalize-all (prepend-not-to-all tail))))
(`(or ,#tail) `(and ,#(normalize-all (prepend-not-to-all tail))))
(`(imp ,a ,b) `(and ,(normalize a)
,(normalize-not b)))
(`(iff ,a ,b) `(or ,(normalize-not `(imp ,a ,b))
,(normalize-not `(imp ,b ,a))))
(`(not ,expr) (normalize expr)))
(normalize '(iff foo bar))
;=> (AND (OR (NOT FOO) BAR) (OR (NOT BAR) FOO))
Alternatively, you could have another generic function to handle lists. Something like
(defmethod normalize ((expression list))
(normalize-list (first expression)
(rest expression)))
(defmethod normalize-list ((operator (eql 'and)) arguments)
`(and ,#(normalize-all arguments)))
(defmethod normalize-list ((operator (eql 'or)) arguments)
`(or ,#(normalize-all arguments)))
;; ...
But that's going to get way more verbose than pattern matching.

Joining a series of paths components in common lisp

How do I join a series of path components in common lisp?
In python, I can do,
`os.path.join("/home/", username, "dira", "dirb", "dirc");`
What would be the equivalent in common lisp?
Of course I can write my own function, but I suspect I should be able to use something built-in.
If you insist on using strings to represent pathnames, then there seems to be no built-in solution except rolling your own.
(defun join-strings (list &key (separator "/") (force-leading nil))
(let* ((length (length list))
(separator-size (length separator))
(text-size (reduce #'+ (mapcar #'length list) :initial-value 0))
(size (+ text-size (* separator-size (if force-leading length (1- length)))))
(buffer (make-string size)))
(flet ((copy-to (position string)
(loop
:with wp := position
:for char :across string
:do (setf (char buffer (prog1 wp (incf wp))) char)
:finally (return wp))))
(loop
:with wp := 0
:for string :in list
:do (when (or force-leading (plusp wp)) (setf wp (copy-to wp separator)))
(setf wp (copy-to wp string)))
buffer)))
(join-strings '("home" "kurt" "source" "file.txt") :force-leading t)
==> "/home/kurt/source/file.txt"
However, if you can use pathnames, then you could, for example, do:
(merge-pathnames #P"subdir1/subdir2/file.type" #P"/usr/share/my-app")
==> #P"/usr/share/my-app/subdir1/subdir2/file.type"
The pathname API also provides functions to manipulate pathnames symbolically, extract the components of a pathname, etc.:
(pathname-directory #P"subdir1/subdir2/file.type")
==> '(:relative "subdir1" "subdir2")
(pathname-name #P"subdir1/subdir2/file.type")
==> "file"
(pathname-type #P"subdir1/subdir2/file.type")
==> "type"
(make-pathname :name "file" :type "type" :directory '(:relative "subdir1" "subdir2"))
==> #P"subdir1/subdir2/file.type"
In particular, the directory component of a pathname is represented as a list, and thus, you can use the full set of list handling functions to derive directory values from others:
(make-pathname :directory (append '(:absolute "usr" "share") '("more" "stuff"))
:name "packages" :type "lisp")
A simpler join-strings
(defun join-strings (lst sep)
(if
(atom lst)
lst
(reduce
(lambda (a b)
(concatenate 'string a sep b))
(cdr lst)
:initial-value (car lst))))

Resources