USING: parser kernel math sequences namespaces assocs inspector
words splitting math.parser arrays sequences.next mirrors
shuffle compiler.units ;
IN: bitfields

! Example:
! BITFIELD: blah short:16 char:8 nothing:5 ;
! defines <blah> blah-short blah-char blah-nothing.

! An efficient bitfield has a sum of 29 bits or less
! so it can fit in a fixnum.
! No class is defined and there is no overflow checking.
! The first field is the most significant.

: >ranges ( slots/sizes -- slots/ranges )
    ! range is { start length }
    reverse 0 swap [
        swap >r tuck >r [ + ] keep r> 2array r> swap
    ] assoc-map nip reverse ;

SYMBOL: safe-bitfields? ! default f; set at parsetime

TUPLE: check< number bound ;
M: check< summary drop "Number exceeds upper bound" ;

: check< ( num cmp -- num )
    2dup < [ drop ] [ \ check< construct-boa throw ] if ;

: ?check ( length -- )
    safe-bitfields? get [ 2^ , \ check< , ] [ drop ] if ;

: put-together ( lengths -- )
    ! messy because of bounds checking
    dup length 1- [ \ >r , ] times [ 0 swap ] % [
        ?check [ \ bitor , , [ shift r> ] % ] when*
    ] each-next \ bitor , ;

: padding-name? ( string -- ? )
    [ "10" member? ] all? ;

: pad ( i name -- )
    bin> , , \ -nrot , ;

: add-padding ( names -- ) 
    <enum>
    [ dup padding-name? [ pad ] [ 2drop ] if ] assoc-each ;

: [constructor] ( names lengths -- quot )
    [ swap add-padding put-together ] [ ] make ;

: define-constructor ( classname slots -- )
    [ keys ] keep values [constructor]
    >r in get constructor-word dup save-location r>
    define ;

: range>accessor ( range -- quot )
    [
        dup first neg , \ shift ,
        second 2^ 1- , \ bitand ,
    ] [ ] make ;

: [accessors] ( lengths -- accessors )
    [ range>accessor ] map ;

: clear-range ( range -- num )
    first2 dupd + [ 2^ 1- ] 2apply bitnot bitor ;

: range>setter ( range -- quot )
    [
        \ >r , dup second ?check \ r> ,
        dup clear-range ,
        [ bitand >r ] %
        first , [ shift r> bitor ] %
    ] [ ] make ;

: [setters] ( lengths -- setters )
    [ range>setter ] map ;

: parse-slots ( slotspecs -- slots )
    [ ":" split1 string>number [ dup length ] unless* ] { } map>assoc ;

: define-slots ( prefix names quots -- )
    >r [ "-" swap 3append create-in ] with map r>
    [ define ] 2each ;

: define-accessors ( classname slots -- )
    dup values [accessors]
    >r keys r> define-slots ;

: define-setters ( classname slots -- )
    >r "with-" swap append r>
    dup values [setters]
    >r keys r> define-slots ;

: filter-pad ( slots -- slots )
    [ drop padding-name? not ] assoc-subset ;

: define-bitfield ( classname slots -- ) 
    [
        [ define-constructor ] 2keep
        >ranges filter-pad [ define-setters ] 2keep define-accessors
    ] with-compilation-unit ;

: parse-bitfield 
    scan ";" parse-tokens parse-slots define-bitfield ;

: BITFIELD:
    parse-bitfield ; parsing

: SAFE-BITFIELD:
    [ safe-bitfields? on parse-bitfield ] with-scope ; parsing