(defpackage #:argot.examples.calc (:use #:cl) (:import-from #:argot #:deflanguage)) (in-package #:argot.examples.calc) (deflanguage calc (:documentation "A calculator language") ( :match (:or (:seq (:eof)) (:seq (:eof)) (:seq (:eof)) (:seq (:eof))) :then car) ( :match (:or )) ( :match (:{} calc) :note "A subexpression, like (1 + 2 / cos(1.5))") ( :match (:item) :if numberp :note "A Number") ( :match (:seq (:@ lhs ) (:@ rhs (:+ (:seq (:or= + - / * ^ %) )))) :then (expand-binop lhs rhs)) ( :match (:seq (:or= sin cos tan -) ))) (defun lassoc? (op) (member op '(+ -))) (defparameter +op-table+ '((+ . +) (- . -) (^ . expt) (% . mod) (* . *) (/ . /))) (defun symb->op (op) (cdr (assoc op +op-table+))) (defun expand-binop (lhs rhs) (let ((op (symb->op (caar rhs)))) (if (lassoc? op) `(,(caar rhs) ,lhs ,(if (cdr rhs) (expand-binop (cadar rhs) (cdr rhs)) (cadar rhs))) (if (cdr rhs) (expand-binop `(,op ,lhs ,(cadar rhs)) (cdr rhs)) `(,op ,lhs ,(cadar rhs))))))