diff --git a/xenomorph/xenomorph/enum.rkt b/xenomorph/xenomorph/enum.rkt index 97817477..6c574dbf 100644 --- a/xenomorph/xenomorph/enum.rkt +++ b/xenomorph/xenomorph/enum.rkt @@ -1,5 +1,10 @@ #lang racket/base -(require racket/class racket/match "base.rkt" racket/list) +(require racket/class + racket/match + "base.rkt" + "int.rkt" + racket/contract + racket/list) (provide (all-defined-out)) #| @@ -12,14 +17,17 @@ https://github.com/mbutterick/restructure/blob/master/src/Enum.coffee (super-new) (init-field [(@type type)] [(@values values)]) - (unless (xenomorphic-type? @type) - (raise-argument-error 'x:enum "xenomorphic type" @type)) + (unless (x:int? @type) + (raise-argument-error 'x:enum "xenomorphic integer type" @type)) + (unless (list? @values) (raise-argument-error 'x:enum "list of values" @values)) (define/augment (x:decode port parent) (define index (send @type x:decode port parent)) - (or (list-ref @values index) index)) + (if (< index (length @values)) + (or (list-ref @values index) index) + index)) (define/augment (x:encode val port [parent #f]) (match (index-of @values val) @@ -29,12 +37,25 @@ https://github.com/mbutterick/restructure/blob/master/src/Enum.coffee (define/augment (x:size [val #f] [parent #f]) (send @type x:size val parent)))) -(define (x:enum [type-arg #f] [values-arg #f] - #:type [type-kwarg #f] - #:values [values-kwarg #f] - #:pre-encode [pre-proc #f] - #:post-decode [post-proc #f] - #:base-class [base-class x:enum%]) +(define (x:enum? x) (is-a? x x:enum%)) + +(define/contract (x:enum [type-arg #f] + [values-arg #f] + #:type [type-kwarg #f] + #:values [values-kwarg #f] + #:pre-encode [pre-proc #f] + #:post-decode [post-proc #f] + #:base-class [base-class x:enum%]) + (() + ((or/c x:int? #false) + (listof any/c) + #:type (or/c x:int? #false) + #:values (listof any/c) + #:pre-encode (or/c (any/c . -> . any/c) #false) + #:post-decode (or/c (any/c . -> . any/c) #false) + #:base-class (λ (c) (subclass? c x:enum%))) + . ->* . + x:enum?) (define type (or type-arg type-kwarg)) (define values (or values-arg values-kwarg)) (new (generate-subclass base-class pre-proc post-proc) [type type] [values values])) \ No newline at end of file diff --git a/xenomorph/xenomorph/scribblings/xenomorph.scrbl b/xenomorph/xenomorph/scribblings/xenomorph.scrbl index c10607c4..a490f17b 100644 --- a/xenomorph/xenomorph/scribblings/xenomorph.scrbl +++ b/xenomorph/xenomorph/scribblings/xenomorph.scrbl @@ -1155,6 +1155,68 @@ Generate an instance of @racket[x:bitfield%] (or a subclass of @racket[x:bitfiel @defmodule[xenomorph/enum] +An @deftech{enumeration} is a mapping of values to sequential integers. + + +@defclass[x:enum% x:base% ()]{ +Base class for list formats. Use @racket[x:enum] to conveniently instantiate new list formats. + +@defconstructor[ +([type x:int?] +[values (listof any/c)])]{ +Create class instance that represents an enumeration format of type @racket[type], sequentially mapped to @racket[values]. +} + +@defmethod[ +#:mode extend +(x:decode +[input-port input-port?] +[parent (or/c xenomorphic? #false)]) +any/c]{ +Returns either the value associated with a certain integer, or if the value is @racket[#false] or doesn't exist, then the integer itself. +} + +@defmethod[ +#:mode extend +(x:encode +[val any/c] +[input-port input-port?] +[parent (or/c xenomorphic? #false)]) +bytes?]{ +Take value listed in the @racket[_values] field and encode it as a @tech{byte string}. +} + +} + +@defproc[ +(x:enum? +[x any/c]) +boolean?]{ +Whether @racket[x] is an object of type @racket[x:enum%]. +} + +@defproc[ +(x:enum +[type-arg (or/c x:int? #false) #false] +[values-arg (listof any/c) #false] +[#:type type-kw (or/c x:int? #false) uint8] +[#:values values-kw (listof any/c) null] +[#:pre-encode pre-encode-proc (or/c (any/c . -> . any/c) #false) #false] +[#:post-decode post-decode-proc (or/c (any/c . -> . any/c) #false) #false] +[#:base-class base-class (λ (c) (subclass? c x:enum%)) x:enum%] +) +x:enum?]{ +Generate an instance of @racket[x:enum%] (or a subclass of @racket[x:enum%]) with certain optional attributes. + +@racket[type-arg] or @racket[type-kw] (whichever is provided, though @racket[type-arg] takes precedence) determines the integer type for the enumeration. Default is @racket[uint8]. + +@racket[values-arg] or @racket[values-kw] (whichever is provided, though @racket[values-arg] takes precedence) determines the mapping of values to integers, where each value corresponds to its index in the list. @racket[#false] indicates skipped values. Default is @racket[null]. + +@racket[pre-encode-proc] and @racket[post-decode-proc] control the pre-encoding and post-decoding procedures, respectively. Each takes as input the value to be processed and returns a new value. + +@racket[base-class] controls the class used for instantiation of the new object. +} + @subsection{Optional} diff --git a/xenomorph/xenomorph/test/enum-test.rkt b/xenomorph/xenomorph/test/enum-test.rkt index 686ee8cd..829e1592 100644 --- a/xenomorph/xenomorph/test/enum-test.rkt +++ b/xenomorph/xenomorph/test/enum-test.rkt @@ -11,7 +11,7 @@ https://github.com/mbutterick/restructure/blob/master/test/Enum.coffee |# (define e (x:enum #:type uint8 - #:values '("foo" "bar" "baz"))) + #:values '("foo" "bar" "baz" #f))) (test-case "enum: should error with invalid type" @@ -27,10 +27,12 @@ https://github.com/mbutterick/restructure/blob/master/test/Enum.coffee (test-case "enum: decode should decode" - (parameterize ([current-input-port (open-input-bytes (bytes 1 2 0))]) + (parameterize ([current-input-port (open-input-bytes (bytes 1 2 0 3 4))]) (check-equal? (decode e) "bar") (check-equal? (decode e) "baz") - (check-equal? (decode e) "foo"))) + (check-equal? (decode e) "foo") + (check-equal? (decode e) 3) + (check-equal? (decode e) 4))) (test-case "enum: decode should decode with post-decode"