From 4979de888ad699695849a884e0ab8faedd3544f1 Mon Sep 17 00:00:00 2001 Message-Id: <4979de888ad699695849a884e0ab8faedd3544f1.1716598044.git.mdw@distorted.org.uk> From: Mark Wooding Date: Tue, 20 Aug 2019 02:31:25 +0100 Subject: [PATCH] src/pset-parse.lisp: Replace `dispatch' by some more elementary functions. Organization: Straylight/Edgeware From: Mark Wooding This turns out to be an overall saving in terms of lines of code, as well as being more versatile. The price is that we've lost the specific per-operator type mismatch error, but I think that's worth paying. Somewhat sneakily, I've also fiddled with the operator precedence numbers, so as to leave more space for other operators, though the relative precedences are unchanged. --- src/pset-parse.lisp | 64 ++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/src/pset-parse.lisp b/src/pset-parse.lisp index e86be27..cfd2b4a 100644 --- a/src/pset-parse.lisp +++ b/src/pset-parse.lisp @@ -63,24 +63,21 @@ (defun parse-expression (scanner) (with-default-error-location (,floc) ,@body)))))) - (flet ((dispatch (name args &rest spec) - ;; Evaluate the ARGS to determine their types and values. Find - ;; the first SPEC, of the form (RETTY OP ARGTY*), where the - ;; ARGTYs match the argument types, in order, and apply OP to - ;; the argument values, return this as a result of type RETTY. - ;; If no SPEC matches, then report an error. - - (oplambda - (let ((args (mapcar (compose #'funcall #'cons) args))) - (aif (find-if (lambda (item) - (every (lambda (type arg) - (eql type (car arg))) - (cddr item) args)) - spec) - (values (car it) (apply (cadr it) (mapcar #'cdr args))) - (error "Type mismatch: operator `~A' applied to ~ - types ~{~(~A~)~#[~; and ~;, ~]~}" - name (mapcar #'car args))))))) + (labels ((want (type thunk) + ;; Evaluate THUNK and convert its result to the given TYPE. + (multiple-value-bind (ty val) (funcall thunk) + (coerce-property-value val ty type))) + + (int-unop (intop x) + ;; Evaluate X to an integer, and apply INTOP to the result, + ;; giving another integer. + (oplambda (values :int (funcall intop (want :int x))))) + + (int-binop (intop x y) + ;; Evaluate X and Y to integers, and apply INTOP to the + ;; results, giving another integer. + (oplambda + (values :int (funcall intop (want :int x) (want :int y)))))) (with-parser-context (token-scanner-context :scanner scanner) (when-parse () @@ -141,29 +138,18 @@ (defun parse-expression (scanner) nil nil)))) ;; Binary operators. - ((:op #\* binop "*" (x y 7) - (dispatch "*" (list x y) (list :int #'* :int :int))) - (:op #\/ binop "/" (x y 7) - (dispatch "/" (list x y) - (list :int - (lambda (x y) - (cond ((zerop y) - (cerror* - "Division by zero") - (cons :invalid nil)) - (t - (floor x y)))) - :int :int))) - (:op #\+ binop "+" (x y 5) - (dispatch "+" (list x y) (list :int #'+ :int :int))) - (:op #\- binop "-" (x y 5) - (dispatch "-" (list x y) (list :int #'- :int :int)))) + ((:op #\* binop "*" (x y 70) (int-binop #'* x y)) + (:op #\/ binop "/" (x y 70) + (oplambda + (let ((x (want :int x)) (y (want :int y))) + (when (zerop y) (error "Division by zero")) + (values :int (floor x y))))) + (:op #\+ binop "+" (x y 60) (int-binop #'+ x y)) + (:op #\- binop "-" (x y 60) (int-binop #'- x y))) ;; Prefix operators. - ((:op #\+ preop "+" (x 9) - (dispatch "+" (list x) (list :int #'+ :int))) - (:op #\- preop "-" (x 9) - (dispatch "-" (list x) (list :int #'- :int))) + ((:op #\+ preop "+" (x 90) (int-unop #'identity x)) + (:op #\- preop "-" (x 90) (int-unop #'- x)) (:op #\( lparen #\))) ;; Postfix operators. -- [mdw]