; From: Dat Thuc Nguyen
; Date: Wed, 7 Apr 1999 15:33:30 +0000
; URL: http://www.smalltickle.com
;
; CONTAINER IS A KEY CONCEPT IN OBJECT-ORIENTED PROGRAMMING.
; SMALLTALK, C++, etc. HAVE STANDARD LIBRARIES OF CONTAINERS.
; THE FOLLOWING SCRIPT DEFINES THE CLASS BAG IN C-KERMIT/KERMIT 95,
; BAG OFFERS A RICH USAGE INTERFACE:
;
; bag bag_name
;   create a new object identified as bag_name.
;
; bag_name.put key value
;   deposit into the object bag_name the element
;   identified by 'key' that has the content 'value'.
;   Both key and value can be alpha, numeric, alpha-numeric.
;
; bag_name.get key
;   query the element identified by 'key'.
;
; bag_name.size
;   query the number of the elements in the bag_name.
;
; bag_name.pairs
;   query all the pairs key => value in the bag_name.
;
; bag_name.values
;   query all the values in the bag_name.
;
; bag_name.keys
;   query all the keys in the bag_name.
;
; bag_name.reset
;   remove all the elements in the bag_name.
;
; bag_name.every_value dothis
;   apply dothis to every value of bag_name.
;
; bag_name.every_key dothis
;   apply dothis to every key of bag_name.
;
; bag_name.drop key
;   remove element identified by key from bag_name.
;
; bag_name.destroy
;   remove the bag_name itself.

;************************************************************************
;*      SERVICE IMPLEMENTATION TO ALL OBJECTS OF CLASS BAG              *
;*      TO REDUCE THE CODE DUPLICATION FOR EACH OBJECT                  *
;************************************************************************

define bag_service.reset {
    _assign \%1_size 0
    _assign \%1_dictionary \x02
}

define bag_service.put {
    local \%f \%i \%l \%m \%n \%v

    assign \%m \x02        ; STX
    assign \%n \x03        ; ETX

    assign \%f \findex(\%m\%2\%n, \m(\%1_dictionary))

    xif = \%f 0 {                ; key not yet used?
        _assign \%1_dictionary \m(\%1_dictionary)\%2\%n\%3\%m
        assign \%z \m(\%1_size)  ; get previous size
        increment \%z            ; update size
        _assign \%1_size \%z     ; save size
    } else {
        assign \%i \feval(\%f + \flength(\%m\%2\%n))
        assign \%f \findex(\%m, \m(\%1_dictionary), \%i)
        assign \%l \feval(\%f - \%i)
        assign \%v \fsubstring(\m(\%1_dictionary),\%i,\%l)
        _assign \%1_dictionary -
          \freplace(\m(\%1_dictionary),\%n\%v\%m,\%n\%3\%m)
    }
}

define bag_service.get {
    local \%f \%i \%v \%l \%m \%n
    assign \%m \x02              ; STX
    assign \%n \x03              ; ETX
    assign \%f \findex(\%m\%2\%n,\m(\%1_dictionary))
    xif = \%f 0 {
        echo \%2 IS UNKNOWN KEY IN THIS OBJECT
    } else {
        assign \%i \feval(\%f + \flength(\%m\%2\%n))
        assign \%f \findex(\%m, \m(\%1_dictionary), \%i)
        assign \%l \feval(\%f - \%i)
        assign \%v \fsubstring(\m(\%1_dictionary),\%i,\%l)
        echo \%2 => \%v
    }
}

define bag_service.pairs {
    local \%f \%i \%k \%l \%m \%n \%v
    assign \%m \x02        ; STX
    assign \%n \x03        ; ETX
    assign \%i \feval(1 + \flength(\%m))                     ; start dictionary
    while < \%i \flength(\m(\%1_dictionary)) {
        assign \%f \findex(\%n, \m(\%1_dictionary), \%i)     ; key pos
        assign \%l \feval(\%f - \%i)                         ; key length
        assign \%k \fsubstring(\m(\%1_dictionary), \%i, \%l) ; key
        assign \%i \feval(\%f + \flength(\%n))
        assign \%f \findex(\%m, \m(\%1_dictionary), \%i)     ; value pos
        assign \%l \feval(\%f - \%i)                         ; value length
        assign \%v \fsubstring(\m(\%1_dictionary), \%i, \%l) ; value
        echo \%k => \%v
        assign \%i \feval(\%f + \flength(\%m))               ; next key pos
    }
}

define bag_service.keys {
    local \%f \%i \%k \%l \%m \%n
    assign \%m \x02        ; STX
    assign \%n \x03        ; ETX
    assign \%i \feval(1 + \flength(\%m))                     ; start dictionary
    while < \%i \flength(\m(\%1_dictionary)) {
        assign \%f \findex(\%n, \m(\%1_dictionary), \%i)     ; key pos
        assign \%l \feval(\%f - \%i)                         ; key length
        assign \%k \fsubstring(\m(\%1_dictionary), \%i, \%l) ; key
        assign \%i \feval(\%f + \flength(\%n))
        assign \%f \findex(\%m, \m(\%1_dictionary), \%i)     ; value pos
        assign \%l \feval(\%f - \%i)                         ; value length
        echo \%k
        assign \%i \feval(\%f + \flength(\%m))               ; next key pos
    }
}

define bag_service.values {
    local \%f \%i \%k \%l \%m \%n \%v
    assign \%m \x02        ; STX
    assign \%n \x03        ; ETX
    assign \%i \feval(1 + \flength(\%m))                     ; start dictionary
    while < \%i \flength(\m(\%1_dictionary)) {
        assign \%f \findex(\%n, \m(\%1_dictionary), \%i)     ; key pos
        assign \%l \feval(\%f - \%i) ; key length
        assign \%i \feval(\%f + \flength(\%n))
        assign \%f \findex(\%m, \m(\%1_dictionary), \%i)     ; value pos
        assign \%l \feval(\%f - \%i)                         ; value length
        assign \%v \fsubstring(\m(\%1_dictionary), \%i, \%l) ; value
        echo \%v
        assign \%i \feval(\%f + \flength(\%m))               ; next key pos
    }
}

define bag_service.every_value {
    local \%f \%i \%k \%l \%m \%n \%v
    assign \%m \x02        ; STX
    assign \%n \x03        ; ETX
    assign \%i \feval(1 + \flength(\%m))                     ; start dictionary
    while < \%i \flength(\m(\%1_dictionary)) {
        assign \%f \findex(\%n, \m(\%1_dictionary), \%i)     ; key pos
        assign \%l \feval(\%f - \%i)                         ; key length
        assign \%i \feval(\%f + \flength(\%n))
        assign \%f \findex(\%m, \m(\%1_dictionary), \%i)     ; value pos
        assign \%l \feval(\%f - \%i)                         ; value length
        assign \%v \fsubstring(\m(\%1_dictionary), \%i, \%l) ; value
        \%2 \%v                                              ; apply \%2 on val
        assign \%i \feval(\%f + \flength(\%m))               ; next key pos
    }
}
define bag_service.every_key {
    local \%f \%i \%k \%l \%m \%n \%v
    assign \%m \x02        ; STX
    assign \%n \x03        ; ETX
    assign \%i \feval(1 + \flength(\%m))                     ; start dictionary
    while < \%i \flength(\m(\%1_dictionary)) {
        assign \%f \findex(\%n, \m(\%1_dictionary), \%i)     ; key pos
        assign \%l \feval(\%f - \%i)                         ; key length
        assign \%k \fsubstring(\m(\%1_dictionary), \%i, \%l) ; key
        assign \%i \feval(\%f + \flength(\%n))
        assign \%f \findex(\%m, \m(\%1_dictionary), \%i)     ; value pos
        assign \%l \feval(\%f - \%i)                         ; value length
        \%2 \%k                                              ; apply \%2 on key
        assign \%i \feval(\%f + \flength(\%m))               ; next key pos
    }
}
define bag_service.drop {
    local \%f \%i \%k \%l \%m \%n \%v \%z
    assign \%m \x02        ; STX
    assign \%n \x03        ; ETX
    assign \%f \findex(\%m\%2\%n, \m(\%1_dictionary))         ; get key
    xif = \%f 0 {                                             ; not yet used?
        echo \%2 IS UNKNOWN KEY IN THIS OBJECT
    } else {
        assign \%i \feval(\%f + \flength(\%m\%2\%n))
        assign \%f \findex(\%m, \m(\%1_dictionary), \%i)
        assign \%l \feval(\%f - \%i)
        assign \%v \fsubstring(\m(\%1_dictionary),\%i,\%l)
        _assign \%1_dictionary -
          \freplace(\m(\%1_dictionary),\%m\%2\%n\%v)
        assign \%z \m(\%1_size)                               ; previous size
        increment \%z -1                                      ; update size
        _assign \%1_size \%z                                  ; save size
    }
}

define bag_service.destroy {
    \%1.reset
    _define \%1_size
    _define \%1_dictionary
    _define \%1.size
    _define \%1.put
    _define \%1.get
    _define \%1.pairs
    _define \%1.reset
    _define \%1.keys
    _define \%1.values
    _define \%1.every_value
    _define \%1.every_key
    _define \%1.drop
    _define \%1.destroy
}

define bag {
    _assign \%1_dictionary \x02    ; STX
    _assign \%1_size 0
    _define \%1.put {
        if NOT define \%1 end 1 ... missing 1.param
        if NOT define \%2 end 1 ... missing 2.param
        bag_service.put \fbreak(\v(macro),.) \%1 \%2
    }
    _define \%1.get {
        bag_service.get \fbreak(\v(macro),.) \%1
    }
    _define \%1.pairs {
        bag_service.pairs \fbreak(\v(macro),.)
    }
    _define \%1.size {
        local \%s
        assign \%s \fbreak(\v(macro),.)
        echo \m(\%s_size)
        return \m(\%s_size)
    }
    _define \%1.reset {
        bag_service.reset \fbreak(\v(macro),.)
    }
    _define \%1.keys {
        bag_service.keys \fbreak(\v(macro),.)
    }
    _define \%1.values {
        bag_service.values \fbreak(\v(macro),.)
    }
    _define \%1.every_value {
        bag_service.every_value \fbreak(\v(macro),.) \%1
    }
    _define \%1.every_key {
        bag_service.every_key \fbreak(\v(macro),.) \%1
    }
    _define \%1.drop {
        bag_service.drop \fbreak(\v(macro),.) \%1
    }
    _define \%1.destroy {
        bag_service.destroy \fbreak(\v(macro),.)
    }
}

;************************************************************************
;*    USAGE EXAMPLE                                                     *
;************************************************************************

bag abc                   ; Buy a new bag abc
abc.put a 1               ; Put the key a with the value 1 in the bag abc
abc.put b 2               ; Put the key b with the value 2 in the bag abc
abc.put c 3               ; Put the key c with the value 3 in the bag abc
abc.pairs                 ; Show me all the pairs key => value
abc.keys                  ; Show me all the keys
abc.values                ; Show me all the value

define sum 0
define sum_it {
    assign sum \feval(\%1 + \m(sum)) ; sum ::= \%1 + \m(sum) <-- C-Kermit 7.0
}
abc.every_value sum_it    ; Add all the value in the bag abc
echo \m(sum)              ; Show the sum

abc.put name Jordan       ; Put the key name with a value Jordan in the bag abc
abc.pairs                 ; Show me all the pairs key => value

abc.put 1999 Year         ; Put the key 1999 with the value Year in the bag abc
abc.pairs                 ; Show me all the pairs key => value

abc.put book Buch         ; English - German
abc.put Buch book         ; German - English

abc.drop name             ; Jordan retires
abc.pairs                 ; Show me all the pairs key => value

abc.reset                 ; empty the bag abc

for \%i 1 50 1 { abc.put a\%i b\%i }
abc.pairs                 ; Show me all the pairs key => value

abc.destroy               ; Throw the bag abc into the gabbage

end
