X-Git-Url: https://git.distorted.org.uk/~mdw/sod/blobdiff_plain/5eeb83d2b620e77000ab77269faa974007c9ff28..6afec9101d5ea87e3df4bda2239ffd05f8154fa6:/src/pset-parse.lisp diff --git a/src/pset-parse.lisp b/src/pset-parse.lisp index e86be27..e3c0d4b 100644 --- a/src/pset-parse.lisp +++ b/src/pset-parse.lisp @@ -63,24 +63,39 @@ (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))))) + + (compareop (intop strop x y) + ;; Evaluate X and Y. If they're integers, then apply INTOP to + ;; them; if they're strings, apply STROP. The result is a + ;; boolean. + (oplambda + (multiple-value-bind (xty xval) (funcall x) + (case xty + (:int + (values :boolean + (funcall intop xval (want :int y)))) + ((:id :string :symbol) + (values :boolean + (funcall strop + (coerce-property-value xval xty :id) + (want :id y)))) + (t + (error "Can't compare objects of type ~(~A~)" xty))))))) (with-parser-context (token-scanner-context :scanner scanner) (when-parse () @@ -141,29 +156,50 @@ 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)) + (:op :shl binop "<<" (x y 50) (int-binop #'ash x y)) + (:op :shr binop ">>" (x y 50) + (int-binop (lambda (x y) (ash x (- y))) x y)) + (:op #\< binop "<" (x y 45) + (compareop #'< #'string< x y)) + (:op :le binop "<=" (x y 45) + (compareop #'<= #'string<= x y)) + (:op :ge binop ">=" (x y 45) + (compareop #'>= #'string>= x y)) + (:op #\> binop ">" (x y 45) + (compareop #'> #'string> x y)) + (:op :eq binop "==" (x y 40) + (compareop #'= #'string= x y)) + (:op :ne binop "!=" (x y 40) + (compareop #'/= #'string/= x y)) + (:op #\& binop "&" (x y 37) (int-binop #'logand x y)) + (:op #\^ binop "^" (x y 35) (int-binop #'logxor x y)) + (:op #\| binop "|" (x y 32) (int-binop #'logior x y)) + (:op :and binop "&&" (x y 27) + (oplambda (if (want :boolean x) (funcall y) + (values :boolean nil)))) + (:op :or binop "||" (x y 22) + (oplambda + (multiple-value-bind (xty xval) (funcall x) + (if (coerce-property-value xval xty :boolean) + (values xty xval) + (funcall 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 #'lognot x)) + (:op #\! preop "!" (x 90) + (oplambda + (values :boolean + (not (want :boolean (funcall x)))))) + (:op #\+ preop "+" (x 90) (int-unop #'identity x)) + (:op #\- preop "-" (x 90) (int-unop #'- x)) (:op #\( lparen #\))) ;; Postfix operators.