Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature enhancement: float options #9

Open
rpgoldman opened this issue Mar 2, 2023 · 2 comments
Open

Feature enhancement: float options #9

rpgoldman opened this issue Mar 2, 2023 · 2 comments

Comments

@rpgoldman
Copy link
Contributor

For scientific / numerical options, it would be very helpful to have options that accept floating point (decimal number) values.

For now, I hack around this by using :string and then parsing it myself. I wasn't confident I could add a new option type myself.

@dnaeon
Copy link
Owner

dnaeon commented Mar 2, 2023

Hey @rpgoldman ,

Here's how you can create a new option (feel free to submit a PR for it):

The code, which implements a new float option looks like this. The steps are pretty much these.

  1. Create a new class, which inherits from CLINGON:OPTION
  2. Implement CLINGON:INITIALIZE-OPTION, so that the option can be initialized from env vars, etc.
  3. Implement CLINGON:DERIVE-OPTION-VALUE
  4. Implement CLINGON:MAKE-OPTION

Here's what the code looks like.

;; Load systems
(ql:quickload :clingon)
(ql:quickload :parse-float)

;; Our sample package
(defpackage :clingon.extensions/option-float
  (:use :cl)
  (:import-from :parse-float)
  (:import-from
   :clingon
   :option
   :make-option
   :option-derive-error
   :initialize-option
   :option-value
   :derive-option-value)
  (:export
   :option-float
   :option-float-radix
   :parse-float-or-lose))
(in-package :clingon.extensions/option-float)

(defun parse-float-or-lose (value &key (radix 10))
  (when (floatp value)
    (return-from parse-float-or-lose value))
  (let ((f (parse-float:parse-float value :radix radix :junk-allowed t)))
    (unless f
      (error 'option-derive-error :reason (format nil "Cannot parse ~A as float" value)))
    f))

(defclass option-float (option)
  ((radix
    :initarg :radix
    :initform 10
    :reader option-float-radix))
   (:default-initargs
    :parameter "FLOAT")
   (:documentation "An option class to represent float numbers"))

(defmethod make-option ((kind (eql :float)) &rest rest)
  (apply #'make-instance 'option-float rest))

(defmethod initialize-option ((option option-float) &key)
  "Initializes our option"
  ;; Make sure to invoke our parent initialization method first, so
  ;; various things like setting up initial value from environment
  ;; variables can still be applied.
  (call-next-method)

  ;; If we don't have any value set, there's nothing else to
  ;; initialize further here.
  (unless (option-value option)
    (return-from initialize-option))

  ;; If we get to this point, that means we've got some initial value,
  ;; which is either set as a default, or via environment
  ;; variables. Next thing we need to do is make sure we've got a good
  ;; initial value, so let's derive a value from it.
  (let ((current (option-value option)))
    (setf (option-value option)
          (derive-option-value option current))))

(defmethod derive-option-value ((option option-float) value &key)
  (let ((radix (option-float-radix option)))
    (parse-float-or-lose value :radix radix)))

And testing it out on the REPL.

CL-USER> (defparameter *opt*
	   (clingon:make-option :float :short-name #\f :key :my-float :description "some float"))
*OPT*
CL-USER> (clingon:derive-option-value *opt* "10")
10.0
CL-USER> (clingon:derive-option-value *opt* "10e-2")
0.1 (10.0%)
CL-USER> (clingon:derive-option-value *opt* "1.2e-3")
0.0012 (0.120000005%)

You can also check #2 for additional examples about new options. Feel free to add anything else to this code and submit it as a PR! :)

@rpgoldman
Copy link
Contributor Author

Thanks for that. I will push it onto my todo list, and hope to get to it pretty soon...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants