2017-01-06 09:53:40 +00:00
|
|
|
|
|
|
|
class Collection(Object)
|
2015-10-08 14:26:04 +00:00
|
|
|
{
|
2017-03-08 13:53:41 +00:00
|
|
|
method isEmpty
|
|
|
|
{
|
|
|
|
^self size <= 0
|
|
|
|
}
|
|
|
|
|
|
|
|
method notEmpty
|
|
|
|
{
|
|
|
|
^self size > 0
|
|
|
|
}
|
2017-09-21 07:56:51 +00:00
|
|
|
|
2017-03-08 13:53:41 +00:00
|
|
|
method size
|
|
|
|
{
|
|
|
|
(* Each subclass must override this method because
|
|
|
|
* it interates over the all elements for counting *)
|
|
|
|
| count |
|
|
|
|
count := 0.
|
|
|
|
self do: [ :el | count := count + 1 ].
|
|
|
|
^count
|
|
|
|
}
|
2017-09-21 07:56:51 +00:00
|
|
|
|
2018-06-19 16:58:04 +00:00
|
|
|
method add: object
|
|
|
|
{
|
|
|
|
self subclassResponsibility: #add:.
|
|
|
|
}
|
|
|
|
|
|
|
|
## ===================================================================
|
2018-06-24 16:47:55 +00:00
|
|
|
## ENUMERATING
|
2018-06-19 16:58:04 +00:00
|
|
|
## ===================================================================
|
|
|
|
|
2017-03-08 13:53:41 +00:00
|
|
|
method do: block
|
|
|
|
{
|
|
|
|
^self subclassResponsibility: #do
|
|
|
|
}
|
2017-09-21 07:56:51 +00:00
|
|
|
|
2018-06-19 17:13:20 +00:00
|
|
|
method collect: block
|
|
|
|
{
|
|
|
|
| coll |
|
2018-06-24 16:47:55 +00:00
|
|
|
##coll := self class new: self basicSize.
|
|
|
|
coll := self class new.
|
2018-06-19 17:13:20 +00:00
|
|
|
self do: [ :el | coll add: (block value: el) ].
|
|
|
|
^coll
|
|
|
|
}
|
|
|
|
|
2017-03-08 13:53:41 +00:00
|
|
|
method detect: block
|
|
|
|
{
|
2018-06-19 17:13:20 +00:00
|
|
|
^self detect: block ifNone: [ ^self error: 'not found' ].
|
2017-03-08 13:53:41 +00:00
|
|
|
}
|
2017-09-21 07:56:51 +00:00
|
|
|
|
2017-03-08 13:53:41 +00:00
|
|
|
method detect: block ifNone: exception_block
|
|
|
|
{
|
2018-06-19 17:13:20 +00:00
|
|
|
## returns the first element for which the block evaluates to true.
|
2017-03-08 13:53:41 +00:00
|
|
|
self do: [ :el | if (block value: el) { ^el } ].
|
|
|
|
^exception_block value.
|
|
|
|
}
|
2018-05-09 16:43:58 +00:00
|
|
|
|
|
|
|
method select: condition_block
|
|
|
|
{
|
|
|
|
| coll |
|
2018-06-24 16:47:55 +00:00
|
|
|
##coll := self class new: self basicSize.
|
|
|
|
coll := self class new.
|
2018-05-09 16:43:58 +00:00
|
|
|
self do: [ :el | if (condition_block value: el) { coll add: el } ].
|
|
|
|
^coll
|
|
|
|
}
|
|
|
|
|
|
|
|
method reject: condition_block
|
|
|
|
{
|
|
|
|
| coll |
|
2018-06-24 16:47:55 +00:00
|
|
|
##coll := self class new: self basicSize.
|
|
|
|
coll := self class new.
|
2018-06-19 16:11:20 +00:00
|
|
|
self do: [ :el | ifnot (condition_block value: el) { coll add: el } ].
|
2018-05-09 16:43:58 +00:00
|
|
|
^coll
|
|
|
|
}
|
2018-05-24 10:10:52 +00:00
|
|
|
|
|
|
|
method emptyCheck
|
|
|
|
{
|
|
|
|
if (self size <= 0) { Exception signal: 'empty collection' }.
|
|
|
|
}
|
2015-10-08 14:26:04 +00:00
|
|
|
}
|
2016-05-13 15:10:34 +00:00
|
|
|
|
|
|
|
## -------------------------------------------------------------------------------
|
2018-05-24 10:10:52 +00:00
|
|
|
class SequenceableCollection(Collection)
|
|
|
|
{
|
|
|
|
method do: aBlock
|
|
|
|
{
|
|
|
|
0 to: (self size - 1) do: [:i | aBlock value: (self at: i)].
|
|
|
|
}
|
|
|
|
|
|
|
|
method reverseDo: aBlock
|
|
|
|
{
|
|
|
|
(self size - 1) to: 0 by: -1 do: [:i | aBlock value: (self at: i)].
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-30 03:46:45 +00:00
|
|
|
## -------------------------------------------------------------------------------
|
2018-05-24 10:10:52 +00:00
|
|
|
class(#pointer) Array(SequenceableCollection)
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
2017-01-06 09:53:40 +00:00
|
|
|
method size
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
2017-01-06 09:53:40 +00:00
|
|
|
^self basicSize
|
2016-05-13 15:10:34 +00:00
|
|
|
}
|
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
method at: anInteger
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
|
|
|
^self basicAt: anInteger.
|
|
|
|
}
|
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
method at: anInteger put: aValue
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
|
|
|
^self basicAt: anInteger put: aValue.
|
|
|
|
}
|
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
method first
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
|
|
|
^self at: 0.
|
|
|
|
}
|
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
method last
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
2017-10-08 15:40:32 +00:00
|
|
|
^self at: (self size - 1).
|
2016-05-13 15:10:34 +00:00
|
|
|
}
|
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
method copy: anArray
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
2017-10-08 15:40:32 +00:00
|
|
|
0 priorTo: (anArray size) do: [:i | self at: i put: (anArray at: i) ].
|
|
|
|
}
|
|
|
|
|
|
|
|
method copy: anArray from: start to: end
|
|
|
|
{
|
|
|
|
## copy elements from an array 'anArray' starting from
|
|
|
|
## the index 'start' to the index 'end'.
|
|
|
|
|
|
|
|
| s i ss |
|
|
|
|
|
|
|
|
(*
|
|
|
|
s := anArray size.
|
|
|
|
|
|
|
|
if (start < 0) { start := 0 }
|
|
|
|
elsif (start >= s) { start := s - 1 }.
|
|
|
|
|
|
|
|
if (end < 0) { end := 0 }
|
|
|
|
elsif (end >= s) { end := s - 1 }.
|
|
|
|
*)
|
|
|
|
i := 0.
|
|
|
|
ss := self size.
|
|
|
|
while (start <= end)
|
|
|
|
{
|
|
|
|
if (i >= ss) { break }.
|
|
|
|
self at: i put: (anArray at: start).
|
|
|
|
i := i + 1.
|
|
|
|
start := start + 1.
|
|
|
|
}.
|
|
|
|
}
|
|
|
|
|
2018-01-04 10:07:42 +00:00
|
|
|
method copyFrom: start
|
|
|
|
{
|
|
|
|
| newsz |
|
|
|
|
newsz := (self size) - start.
|
|
|
|
^(self class new: newsz) copy: self from: start to: ((self size) - 1).
|
|
|
|
}
|
|
|
|
|
2017-10-08 15:40:32 +00:00
|
|
|
method copyFrom: start to: end
|
|
|
|
{
|
|
|
|
## returns a copy of the receiver starting from the element
|
|
|
|
## at index 'start' to the element at index 'end'.
|
|
|
|
| newsz |
|
|
|
|
newsz := end - start + 1.
|
|
|
|
^(self class new: newsz) copy: self from: start to: end
|
2016-05-13 15:10:34 +00:00
|
|
|
}
|
2017-10-02 01:22:49 +00:00
|
|
|
|
2018-06-12 10:17:32 +00:00
|
|
|
method copyFrom: start count: count
|
|
|
|
{
|
|
|
|
^(self class new: count) copy: self from: start to: (start + count - 1)
|
|
|
|
}
|
|
|
|
|
2018-05-24 10:10:52 +00:00
|
|
|
method replaceFrom: start to: stop with: replacement
|
|
|
|
{
|
2018-06-09 16:48:18 +00:00
|
|
|
^self replaceFrom: start to: stop with: replacement startingAt: 0.
|
2018-05-24 10:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method replaceFrom: start to: stop with: replacement startingAt: rstart
|
|
|
|
{
|
|
|
|
| offset i |
|
|
|
|
offset := rstart - start.
|
|
|
|
i := start.
|
|
|
|
while (i <= stop)
|
|
|
|
{
|
|
|
|
self at: i put: (replacement at: i + offset).
|
|
|
|
i := i + 1.
|
|
|
|
}.
|
|
|
|
}
|
|
|
|
|
2018-06-09 16:48:18 +00:00
|
|
|
method replaceFrom: start count: count with: replacement
|
|
|
|
{
|
|
|
|
^self replaceFrom: start to: (start + count - 1) with: replacement startingAt: 0.
|
|
|
|
}
|
|
|
|
|
|
|
|
method replaceFrom: start count: count with: replacement startingAt: rstart
|
|
|
|
{
|
|
|
|
^self replaceFrom: start to: (start + count - 1) with: replacement startingAt: rstart
|
|
|
|
}
|
|
|
|
|
2017-10-02 01:22:49 +00:00
|
|
|
method = anArray
|
|
|
|
{
|
|
|
|
| size i |
|
|
|
|
if (self class ~~ anArray class) { ^false }.
|
|
|
|
|
|
|
|
size := self size.
|
|
|
|
if (size ~~ anArray size) { ^false }.
|
|
|
|
|
|
|
|
i := 0.
|
|
|
|
while (i < size)
|
|
|
|
{
|
|
|
|
if ((self at: i) ~= (anArray at: i)) { ^false }.
|
|
|
|
i := i + 1.
|
|
|
|
}.
|
|
|
|
|
|
|
|
^true.
|
|
|
|
}
|
|
|
|
|
|
|
|
method ~= anArray
|
|
|
|
{
|
|
|
|
^(self = anArray) not
|
|
|
|
}
|
2016-05-13 15:10:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
## -------------------------------------------------------------------------------
|
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
class(#character) String(Array)
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
2017-01-06 09:53:40 +00:00
|
|
|
method & string
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
2017-01-05 10:16:04 +00:00
|
|
|
(* TOOD: make this a primitive for performance. *)
|
|
|
|
|
|
|
|
(* concatenate two strings. *)
|
|
|
|
| newsize newstr cursize appsize |
|
2016-06-22 03:23:14 +00:00
|
|
|
|
2017-01-05 10:16:04 +00:00
|
|
|
cursize := self basicSize.
|
|
|
|
appsize := string basicSize.
|
|
|
|
newsize := cursize + appsize.
|
|
|
|
(*newstr := self class basicNew: newsize.*)
|
|
|
|
newstr := String basicNew: newsize.
|
2016-06-22 03:23:14 +00:00
|
|
|
|
2017-01-05 10:16:04 +00:00
|
|
|
0 priorTo: cursize do: [:i | newstr at: i put: (self at: i) ].
|
|
|
|
0 priorTo: appsize do: [:i | newstr at: (i + cursize) put: (string at: i) ].
|
2016-06-22 03:23:14 +00:00
|
|
|
|
2016-05-13 15:10:34 +00:00
|
|
|
^newstr
|
|
|
|
}
|
2017-09-21 07:56:51 +00:00
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
method asString
|
2017-01-05 10:16:04 +00:00
|
|
|
{
|
|
|
|
^self
|
|
|
|
}
|
2017-09-21 07:56:51 +00:00
|
|
|
|
2017-05-01 12:54:41 +00:00
|
|
|
(* TODO: Symbol is a #final class. Symbol new is not allowed. To create a symbol programatically, you should
|
|
|
|
* build a string and send asSymbol to the string............
|
|
|
|
method asSymbol
|
|
|
|
{
|
|
|
|
}
|
|
|
|
*)
|
2017-05-16 02:04:18 +00:00
|
|
|
|
|
|
|
(* The strlen method returns the number of characters before a terminating null.
|
|
|
|
* if no terminating null character exists, it returns the same value as the size method *)
|
|
|
|
method(#primitive,#lenient) _strlen.
|
|
|
|
method(#primitive) strlen.
|
2018-12-21 16:25:25 +00:00
|
|
|
|
|
|
|
method(#primitive,#variadic) strfmt().
|
2016-05-13 15:10:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
## -------------------------------------------------------------------------------
|
|
|
|
|
2017-05-07 05:18:21 +00:00
|
|
|
class(#character,#final,#limited,#immutable) Symbol(String)
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
2017-01-06 09:53:40 +00:00
|
|
|
method asString
|
2017-01-05 10:16:04 +00:00
|
|
|
{
|
|
|
|
(* TODO: make this a primitive for performance *)
|
|
|
|
|
|
|
|
(* convert a symbol to a string *)
|
2017-02-07 18:09:07 +00:00
|
|
|
| size str i |
|
2017-01-05 10:16:04 +00:00
|
|
|
size := self basicSize.
|
|
|
|
str := String basicNew: size.
|
2017-02-07 18:09:07 +00:00
|
|
|
|
|
|
|
##0 priorTo: size do: [:i | str at: i put: (self at: i) ].
|
|
|
|
i := 0.
|
|
|
|
while (i < size)
|
|
|
|
{
|
|
|
|
str at: i put: (self at: i).
|
|
|
|
i := i + 1
|
|
|
|
}.
|
2017-01-05 10:16:04 +00:00
|
|
|
^str.
|
|
|
|
}
|
2017-01-06 13:27:49 +00:00
|
|
|
|
|
|
|
method = anObject
|
|
|
|
{
|
|
|
|
(* for a symbol, equality check is the same as the identity check *)
|
2017-12-03 17:08:04 +00:00
|
|
|
<primitive: #'Apex_=='>
|
2017-01-06 13:27:49 +00:00
|
|
|
self primitiveFailed.
|
|
|
|
}
|
|
|
|
|
|
|
|
method ~= anObject
|
|
|
|
{
|
|
|
|
(* for a symbol, equality check is the same as the identity check *)
|
2017-12-03 17:08:04 +00:00
|
|
|
<primitive: #'Apex_~~'>
|
2017-01-06 13:27:49 +00:00
|
|
|
^(self == anObject) not.
|
|
|
|
}
|
2016-05-13 15:10:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
## -------------------------------------------------------------------------------
|
|
|
|
|
2018-01-28 16:03:03 +00:00
|
|
|
class(#byte) ByteArray(Array)
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
2018-01-28 16:03:03 +00:00
|
|
|
## TODO: is it ok for ByteArray to inherit from Array?
|
2018-06-17 17:41:04 +00:00
|
|
|
|
2018-06-18 14:32:31 +00:00
|
|
|
## method asByteString
|
|
|
|
## {
|
|
|
|
## }
|
|
|
|
|
2018-06-17 17:41:04 +00:00
|
|
|
method decodeToCharacter
|
|
|
|
{
|
|
|
|
## TODO: support other encodings. it only supports utf8 as of now.
|
|
|
|
<primitive: #_utf8_to_uc>
|
|
|
|
self primitiveFailed(thisContext method).
|
|
|
|
|
|
|
|
(*
|
|
|
|
### TODO: implement this in moo also..
|
|
|
|
| firstByte |
|
|
|
|
firstByte := self at: 0.
|
|
|
|
if ((firstByte bitAnd:2r10000000) == 0) { 1 }
|
|
|
|
elsif (firstByte bitAnd:2r11000000) == 2r10000000) { 2 }
|
|
|
|
elsif (firstByte bitAnd:2r11100000) == 2r11000000) { 3 }
|
|
|
|
elsif (firstByte bitAnd:2r11110000) == 2r11100000) { 4 }
|
|
|
|
elsif (firstByte bitAnd:2r11111000) == 2r11110000) { 5 }
|
|
|
|
elsif (firstByte bitAnd:2r11111100) == 2r11111000) { 6 }.
|
|
|
|
*)
|
|
|
|
}
|
2016-05-13 15:10:34 +00:00
|
|
|
}
|
|
|
|
|
2018-06-18 14:32:31 +00:00
|
|
|
class(#byte) ByteString(Array)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-05-22 16:22:32 +00:00
|
|
|
## -------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
class OrderedCollection(SequenceableCollection)
|
|
|
|
{
|
2018-05-25 17:56:08 +00:00
|
|
|
var buffer.
|
2018-05-22 16:22:32 +00:00
|
|
|
var firstIndex.
|
2018-05-24 10:10:52 +00:00
|
|
|
var lastIndex. ## this is the last index plus 1.
|
2018-05-22 16:22:32 +00:00
|
|
|
|
|
|
|
method(#class) new
|
|
|
|
{
|
|
|
|
^self new: 16.
|
|
|
|
}
|
|
|
|
|
2018-05-27 16:46:49 +00:00
|
|
|
method(#class) new: capacity
|
2018-05-22 16:22:32 +00:00
|
|
|
{
|
2018-06-20 18:01:04 +00:00
|
|
|
^super new __init_with_capacity: capacity.
|
2018-05-22 16:22:32 +00:00
|
|
|
}
|
|
|
|
|
2018-06-20 18:01:04 +00:00
|
|
|
method __init_with_capacity: capacity
|
2018-05-22 16:22:32 +00:00
|
|
|
{
|
2018-05-27 16:46:49 +00:00
|
|
|
self.buffer := Array new: capacity.
|
2018-05-30 11:31:26 +00:00
|
|
|
self.firstIndex := capacity bitShift: -1.
|
2018-05-24 10:10:52 +00:00
|
|
|
self.lastIndex := self.firstIndex.
|
2018-05-22 16:22:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method size
|
|
|
|
{
|
2018-05-24 10:10:52 +00:00
|
|
|
^self.lastIndex - self.firstIndex.
|
2018-05-22 16:22:32 +00:00
|
|
|
}
|
|
|
|
|
2018-05-25 17:56:08 +00:00
|
|
|
method capacity
|
|
|
|
{
|
2018-05-27 16:46:49 +00:00
|
|
|
^self.buffer size.
|
2018-05-25 17:56:08 +00:00
|
|
|
}
|
|
|
|
|
2018-05-22 16:22:32 +00:00
|
|
|
method at: index
|
|
|
|
{
|
|
|
|
| i |
|
2018-05-24 10:10:52 +00:00
|
|
|
i := index + self.firstIndex.
|
2018-05-30 15:32:09 +00:00
|
|
|
if ((i >= self.firstIndex) and (i < self.lastIndex)) { ^self.buffer at: index }.
|
2018-05-22 16:22:32 +00:00
|
|
|
Exception signal: ('index ' & index asString & ' out of range').
|
|
|
|
}
|
|
|
|
|
2018-05-25 17:56:08 +00:00
|
|
|
method at: index put: obj
|
2018-05-22 16:22:32 +00:00
|
|
|
{
|
2018-05-25 17:56:08 +00:00
|
|
|
## replace an existing element. it doesn't grow the buffer.
|
2018-05-22 16:22:32 +00:00
|
|
|
| i |
|
2018-05-24 10:10:52 +00:00
|
|
|
i := index + self.firstIndex.
|
2018-05-30 15:32:09 +00:00
|
|
|
if ((i >= self.firstIndex) and (i < self.lastIndex)) { ^self.buffer at: index put: obj }.
|
2018-05-22 16:22:32 +00:00
|
|
|
Exception signal: ('index ' & index asString & ' out of range').
|
|
|
|
}
|
|
|
|
|
2018-05-25 17:56:08 +00:00
|
|
|
method first
|
|
|
|
{
|
|
|
|
self emptyCheck.
|
|
|
|
^self.buffer at: self.firstIndex.
|
|
|
|
}
|
|
|
|
|
|
|
|
method last
|
|
|
|
{
|
|
|
|
self emptyCheck.
|
|
|
|
^self.buffer at: self.lastIndex.
|
|
|
|
}
|
|
|
|
|
2018-05-22 16:22:32 +00:00
|
|
|
method addFirst: obj
|
|
|
|
{
|
2018-05-30 11:31:26 +00:00
|
|
|
if (self.firstIndex == 0) { self __grow_and_shift(8, 8) }.
|
2018-05-24 10:10:52 +00:00
|
|
|
self.firstIndex := self.firstIndex - 1.
|
2018-05-27 16:46:49 +00:00
|
|
|
self.buffer at: self.firstIndex put: obj.
|
|
|
|
^obj.
|
2018-05-22 16:22:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method addLast: obj
|
|
|
|
{
|
2018-05-30 11:31:26 +00:00
|
|
|
if (self.lastIndex == self.buffer size) { self __grow_and_shift(8, 0) }.
|
2018-05-27 16:46:49 +00:00
|
|
|
self.buffer at: self.lastIndex put: obj.
|
2018-05-24 10:10:52 +00:00
|
|
|
self.lastIndex := self.lastIndex + 1.
|
2018-05-27 16:46:49 +00:00
|
|
|
^obj.
|
2018-05-22 16:22:32 +00:00
|
|
|
}
|
|
|
|
|
2018-05-27 16:46:49 +00:00
|
|
|
method addAllFirst: coll
|
2018-05-22 16:22:32 +00:00
|
|
|
{
|
2018-05-27 16:46:49 +00:00
|
|
|
| coll_to_add |
|
|
|
|
coll_to_add := if (self == coll) { coll shallowCopy } else { coll }.
|
2018-05-30 11:31:26 +00:00
|
|
|
self __ensure_head_room(coll_to_add size).
|
2018-05-27 16:46:49 +00:00
|
|
|
coll_to_add reverseDo: [:obj |
|
|
|
|
self.firstIndex := self.firstIndex - 1.
|
|
|
|
self.buffer at: self.firstIndex put: obj.
|
|
|
|
].
|
|
|
|
^coll_to_add.
|
2018-05-22 16:22:32 +00:00
|
|
|
}
|
|
|
|
|
2018-05-27 16:46:49 +00:00
|
|
|
method addAllLast: coll
|
2018-05-22 16:22:32 +00:00
|
|
|
{
|
2018-05-27 16:46:49 +00:00
|
|
|
| coll_to_add |
|
|
|
|
coll_to_add := if (self == coll) { coll shallowCopy } else { coll }.
|
2018-05-30 11:31:26 +00:00
|
|
|
self __ensure_tail_room(coll_to_add size).
|
2018-05-27 16:46:49 +00:00
|
|
|
coll_to_add do: [:obj |
|
|
|
|
self.buffer at: self.lastIndex put: obj.
|
|
|
|
self.lastIndex := self.lastIndex + 1.
|
|
|
|
].
|
|
|
|
^coll_to_add
|
|
|
|
}
|
|
|
|
|
|
|
|
method add: obj beforeIndex: index
|
|
|
|
{
|
2018-05-30 11:31:26 +00:00
|
|
|
self __insert_before_index(obj, index).
|
|
|
|
^obj.
|
|
|
|
}
|
|
|
|
|
|
|
|
method add: obj afterIndex: index
|
|
|
|
{
|
|
|
|
self __insert_before_index(obj, index + 1).
|
|
|
|
^obj.
|
2018-05-22 16:22:32 +00:00
|
|
|
}
|
|
|
|
|
2018-06-19 17:13:20 +00:00
|
|
|
method add: obj
|
|
|
|
{
|
|
|
|
^self addLast: obj.
|
|
|
|
}
|
|
|
|
|
|
|
|
method addAll: coll
|
|
|
|
{
|
|
|
|
coll do: [:el | self addLast: el ].
|
|
|
|
^coll.
|
|
|
|
}
|
|
|
|
|
2018-05-22 16:22:32 +00:00
|
|
|
method removeFirst
|
|
|
|
{
|
2018-05-24 10:10:52 +00:00
|
|
|
| obj |
|
|
|
|
self emptyCheck.
|
2018-05-25 17:56:08 +00:00
|
|
|
obj := self.buffer at: self.firstIndex.
|
|
|
|
self.buffer at: self.firstIndex put: nil.
|
2018-05-24 10:10:52 +00:00
|
|
|
self.firstIndex := self.firstIndex + 1.
|
|
|
|
^obj.
|
2018-05-22 16:22:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method removeLast
|
|
|
|
{
|
2018-05-24 10:10:52 +00:00
|
|
|
| obj li |
|
|
|
|
self emptyCheck.
|
|
|
|
li := self.lastIndex - 1.
|
2018-05-25 17:56:08 +00:00
|
|
|
obj := self.buffer at: li.
|
|
|
|
self.buffer at: li put: nil.
|
2018-05-24 10:10:52 +00:00
|
|
|
self.lastIndex := li.
|
|
|
|
^obj
|
|
|
|
}
|
|
|
|
|
2018-05-26 03:39:58 +00:00
|
|
|
method removeFirst: count
|
|
|
|
{
|
|
|
|
| r i |
|
2018-05-30 15:32:09 +00:00
|
|
|
if ((count > self size) or (count < 0)) { Exception signal: 'removal count too big/small' }.
|
2018-05-26 03:39:58 +00:00
|
|
|
r := Array new: count.
|
|
|
|
i := 0.
|
|
|
|
while (i < count)
|
|
|
|
{
|
|
|
|
r at: i put: (self.buffer at: self.firstIndex).
|
|
|
|
self.buffer at: self.firstIndex put: nil.
|
|
|
|
self.firstIndex := self.firstIndex + 1.
|
|
|
|
i := i + 1.
|
|
|
|
}.
|
|
|
|
^r.
|
|
|
|
}
|
|
|
|
|
|
|
|
method removeLast: count
|
|
|
|
{
|
|
|
|
| r i li |
|
2018-05-30 15:32:09 +00:00
|
|
|
if ((count > self size) or (count < 0)) { Exception signal: 'removal count too big/small' }.
|
2018-05-26 03:39:58 +00:00
|
|
|
r := Array new: count.
|
|
|
|
i := 0.
|
|
|
|
while (i < count)
|
|
|
|
{
|
|
|
|
li := self.lastIndex - 1.
|
|
|
|
r at: i put: (self.buffer at: li).
|
|
|
|
self.buffer at: li put: nil.
|
|
|
|
self.lastIndex := li.
|
|
|
|
i := i + 1.
|
|
|
|
}.
|
|
|
|
^r
|
|
|
|
}
|
|
|
|
|
2018-05-25 17:56:08 +00:00
|
|
|
method removeAtIndex: index
|
2018-05-24 10:10:52 +00:00
|
|
|
{
|
2018-05-25 17:56:08 +00:00
|
|
|
## remove the element at the given position.
|
2018-05-24 10:10:52 +00:00
|
|
|
| obj |
|
2018-05-26 03:39:58 +00:00
|
|
|
obj := self at: index. ## the range is checed by the at: method.
|
2018-05-30 15:32:09 +00:00
|
|
|
self __remove_index(index + self.firstIndex).
|
2018-05-24 10:10:52 +00:00
|
|
|
^obj
|
2018-05-22 16:22:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method remove: obj ifAbsent: error_block
|
|
|
|
{
|
2018-05-26 03:39:58 +00:00
|
|
|
## remove the first element equivalent to the given object.
|
|
|
|
## and returns the removed element.
|
|
|
|
## if no matching element is found, it evaluates error_block
|
|
|
|
## and return the evaluation result.
|
|
|
|
| i cand |
|
2018-05-25 17:56:08 +00:00
|
|
|
i := self.firstIndex.
|
|
|
|
while (i < self.lastIndex)
|
|
|
|
{
|
2018-05-26 03:39:58 +00:00
|
|
|
cand := self.buffer at: i.
|
2018-05-30 11:31:26 +00:00
|
|
|
if (obj = cand) { self __remove_index(i). ^cand }.
|
2018-05-25 17:56:08 +00:00
|
|
|
i := i + 1.
|
|
|
|
}.
|
|
|
|
^error_block value.
|
2018-05-22 16:22:32 +00:00
|
|
|
}
|
|
|
|
|
2018-05-24 10:10:52 +00:00
|
|
|
## ------------------------------------------------
|
|
|
|
## ENUMERATING
|
|
|
|
## ------------------------------------------------
|
2018-06-24 16:47:55 +00:00
|
|
|
method collect: aBlock
|
2018-05-22 16:22:32 +00:00
|
|
|
{
|
2018-06-24 16:47:55 +00:00
|
|
|
| coll |
|
|
|
|
coll := self class new: self capacity.
|
|
|
|
self do: [ :el | coll add: (aBlock value: el) ].
|
|
|
|
^coll
|
|
|
|
}
|
|
|
|
|
|
|
|
method do: aBlock
|
|
|
|
{
|
|
|
|
##^self.firstIndex to: (self.lastIndex - 1) do: [:i | aBlock value: (self.buffer at: i)].
|
2018-05-24 10:10:52 +00:00
|
|
|
|
|
|
|
| i |
|
|
|
|
i := self.firstIndex.
|
|
|
|
while (i < self.lastIndex)
|
|
|
|
{
|
2018-06-24 16:47:55 +00:00
|
|
|
aBlock value: (self.buffer at: i).
|
2018-05-24 10:10:52 +00:00
|
|
|
i := i + 1.
|
|
|
|
}.
|
|
|
|
}
|
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
method reverseDo: aBlock
|
2018-05-27 16:46:49 +00:00
|
|
|
{
|
|
|
|
| i |
|
|
|
|
i := self.lastIndex.
|
|
|
|
while (i > self.firstIndex)
|
|
|
|
{
|
|
|
|
i := i - 1.
|
2018-06-24 16:47:55 +00:00
|
|
|
aBlock value: (self.buffer at: i).
|
2018-05-27 16:46:49 +00:00
|
|
|
}.
|
|
|
|
}
|
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
method keysAndValuesDo: aBlock
|
2018-05-27 16:46:49 +00:00
|
|
|
{
|
|
|
|
| i |
|
|
|
|
i := self.firstIndex.
|
|
|
|
while (i < self.lastIndex)
|
|
|
|
{
|
2018-06-24 16:47:55 +00:00
|
|
|
aBlock value: (i - self.firstIndex) value: (self.buffer at: i).
|
2018-05-27 16:46:49 +00:00
|
|
|
i := i + 1.
|
|
|
|
}.
|
|
|
|
}
|
|
|
|
|
2018-05-24 10:10:52 +00:00
|
|
|
## ------------------------------------------------
|
|
|
|
## PRIVATE METHODS
|
|
|
|
## ------------------------------------------------
|
2018-05-30 11:31:26 +00:00
|
|
|
method __grow_and_shift(grow_size, shift_count)
|
2018-05-24 10:10:52 +00:00
|
|
|
{
|
|
|
|
| newcon |
|
2018-05-25 17:56:08 +00:00
|
|
|
newcon := (self.buffer class) new: (self.buffer size + grow_size).
|
2018-05-24 10:10:52 +00:00
|
|
|
newcon replaceFrom: (self.firstIndex + shift_count)
|
|
|
|
to: (self.lastIndex - 1 + shift_count)
|
2018-05-25 17:56:08 +00:00
|
|
|
with: self.buffer startingAt: (self.firstIndex).
|
|
|
|
self.buffer := newcon.
|
2018-05-24 10:10:52 +00:00
|
|
|
self.firstIndex := self.firstIndex + shift_count.
|
|
|
|
self.lastIndex := self.lastIndex + shift_count.
|
2018-05-22 16:22:32 +00:00
|
|
|
}
|
2018-05-25 10:19:25 +00:00
|
|
|
|
2018-05-30 11:31:26 +00:00
|
|
|
method __ensure_head_room(size)
|
2018-05-27 16:46:49 +00:00
|
|
|
{
|
|
|
|
| grow_size |
|
|
|
|
if (self.firstIndex < size)
|
|
|
|
{
|
|
|
|
grow_size := size - self.firstIndex + 8.
|
2018-05-30 11:31:26 +00:00
|
|
|
self __grow_and_shift(grow_size, grow_size).
|
2018-05-27 16:46:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-30 11:31:26 +00:00
|
|
|
method __ensure_tail_room(size)
|
2018-05-27 16:46:49 +00:00
|
|
|
{
|
|
|
|
| tmp |
|
|
|
|
tmp := (self.buffer size) - self.lastIndex. ## remaining capacity
|
|
|
|
if (tmp < size)
|
|
|
|
{
|
2018-05-30 11:31:26 +00:00
|
|
|
tmp := size - tmp + 8. ## grow by this amount
|
|
|
|
self __grow_and_shift(tmp, 0).
|
2018-05-27 16:46:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-30 11:31:26 +00:00
|
|
|
method __insert_before_index(obj, index)
|
2018-05-25 10:19:25 +00:00
|
|
|
{
|
2018-05-30 11:31:26 +00:00
|
|
|
| i start pos cursize reqsize |
|
2018-05-25 10:19:25 +00:00
|
|
|
|
2018-05-30 11:31:26 +00:00
|
|
|
if (index <= 0)
|
2018-05-25 10:19:25 +00:00
|
|
|
{
|
2018-05-30 11:31:26 +00:00
|
|
|
## treat a negative index as the indicator to
|
|
|
|
## grow space in the front side.
|
|
|
|
reqsize := index * -1 + 1.
|
|
|
|
if (reqsize >= self.firstIndex) { self __ensure_head_room(reqsize) }.
|
|
|
|
self.firstIndex := self.firstIndex + index - 1.
|
|
|
|
self.buffer at: self.firstIndex put: obj.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cursize := self size.
|
2018-05-30 15:32:09 +00:00
|
|
|
if ((self.firstIndex > 0) and (index <= (cursize bitShift: -1)))
|
2018-05-30 11:31:26 +00:00
|
|
|
{
|
|
|
|
start := self.firstIndex - 1.
|
|
|
|
self.buffer replaceFrom: start to: (start + index - 1) with: self.buffer startingAt: self.firstIndex.
|
|
|
|
self.buffer at: (start + index) put: obj.
|
|
|
|
self.firstIndex := start.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reqsize := if (index > cursize) { index - cursize } else { 1 }.
|
|
|
|
if (reqsize < 8) { reqsize := 8 }.
|
|
|
|
self __grow_and_shift(reqsize + 8, 0).
|
|
|
|
|
|
|
|
start := self.firstIndex + index.
|
|
|
|
i := self.lastIndex.
|
|
|
|
while (i > start)
|
|
|
|
{
|
|
|
|
self.buffer at: i put: (self.buffer at: i - 1).
|
|
|
|
i := i - 1.
|
|
|
|
}.
|
|
|
|
pos := index + self.firstIndex.
|
|
|
|
self.lastIndex := if (pos > self.lastIndex) { pos + 1 } else { self.lastIndex + 1 }.
|
|
|
|
self.buffer at: pos put: obj.
|
|
|
|
}.
|
2018-05-25 10:19:25 +00:00
|
|
|
}.
|
|
|
|
}
|
2018-05-25 17:56:08 +00:00
|
|
|
|
2018-05-30 11:31:26 +00:00
|
|
|
method __remove_index(index)
|
2018-05-25 17:56:08 +00:00
|
|
|
{
|
2018-05-26 03:39:58 +00:00
|
|
|
## remove an element at the given index from the internal buffer's
|
|
|
|
## angle. as it is for internal use only, no check is performed
|
|
|
|
## about the range of index. the given index must be equal to or
|
|
|
|
## grater than self.firstIndex and less than self.lastIndex.
|
2018-05-25 17:56:08 +00:00
|
|
|
self.buffer
|
|
|
|
replaceFrom: index
|
|
|
|
to: self.lastIndex - 2
|
|
|
|
with: self.buffer
|
|
|
|
startingAt: index + 1.
|
|
|
|
self.lastIndex := self.lastIndex - 1.
|
|
|
|
self.buffer at: self.lastIndex put: nil.
|
|
|
|
}
|
2018-05-26 03:39:58 +00:00
|
|
|
|
|
|
|
(*
|
|
|
|
method findIndex: obj
|
|
|
|
{
|
|
|
|
| i |
|
|
|
|
|
|
|
|
i := self.firstIndex.
|
|
|
|
while (i < self.lastIndex)
|
|
|
|
{
|
|
|
|
if (obj = (self.buffer at: i)) { ^i }.
|
|
|
|
i := i + 1.
|
|
|
|
}.
|
|
|
|
|
|
|
|
^Error.Code.ENOENT.
|
|
|
|
} *)
|
2018-05-22 16:22:32 +00:00
|
|
|
}
|
2018-05-24 10:10:52 +00:00
|
|
|
|
2016-05-13 15:10:34 +00:00
|
|
|
## -------------------------------------------------------------------------------
|
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
class Set(Collection)
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
2017-04-19 16:46:44 +00:00
|
|
|
var tally, bucket.
|
2017-01-09 13:20:46 +00:00
|
|
|
|
2018-06-22 15:27:38 +00:00
|
|
|
method(#class) new
|
2017-01-09 13:20:46 +00:00
|
|
|
{
|
2018-06-22 15:27:38 +00:00
|
|
|
^self new: 16. ### TODO: default size.
|
2018-06-20 18:01:04 +00:00
|
|
|
}
|
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
method(#class) new: capacity
|
2018-06-20 18:01:04 +00:00
|
|
|
{
|
2018-06-24 16:47:55 +00:00
|
|
|
^super new __init_with_capacity: capacity.
|
2018-06-20 18:01:04 +00:00
|
|
|
}
|
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
method __init_with_capacity: capacity
|
2018-06-20 18:01:04 +00:00
|
|
|
{
|
2018-06-24 16:47:55 +00:00
|
|
|
if (capacity <= 0) { capacity := 2 }.
|
2018-06-20 18:01:04 +00:00
|
|
|
self.tally := 0.
|
2018-06-24 16:47:55 +00:00
|
|
|
self.bucket := Array new: capacity.
|
2018-06-20 18:01:04 +00:00
|
|
|
}
|
|
|
|
|
2018-06-22 15:27:38 +00:00
|
|
|
method isEmpty
|
|
|
|
{
|
|
|
|
^self.tally == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
method size
|
|
|
|
{
|
|
|
|
^self.tally
|
|
|
|
}
|
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
method capacity
|
|
|
|
{
|
|
|
|
^self.bucket size.
|
|
|
|
}
|
|
|
|
|
2018-06-20 18:01:04 +00:00
|
|
|
method __make_expanded_bucket: bs
|
|
|
|
{
|
|
|
|
| newbuc newsz ass index i |
|
|
|
|
|
|
|
|
(* expand the bucket *)
|
2018-06-22 15:27:38 +00:00
|
|
|
newsz := bs + 16. ## TODO: make this sizing operation configurable.
|
2018-06-20 18:01:04 +00:00
|
|
|
newbuc := Array new: newsz.
|
|
|
|
i := 0.
|
|
|
|
while (i < bs)
|
|
|
|
{
|
|
|
|
ass := self.bucket at: i.
|
|
|
|
if (ass notNil)
|
|
|
|
{
|
|
|
|
index := (ass hash) rem: newsz.
|
|
|
|
while ((newbuc at: index) notNil) { index := (index + 1) rem: newsz }.
|
|
|
|
newbuc at: index put: ass
|
|
|
|
}.
|
|
|
|
i := i + 1.
|
|
|
|
}.
|
|
|
|
|
|
|
|
^newbuc.
|
|
|
|
}
|
|
|
|
|
2018-06-22 15:14:56 +00:00
|
|
|
method __find_index_for_add: anObject
|
2018-06-20 18:01:04 +00:00
|
|
|
{
|
|
|
|
| bs ass index |
|
|
|
|
|
|
|
|
bs := self.bucket size.
|
|
|
|
index := (anObject hash) rem: bs.
|
|
|
|
|
|
|
|
while ((ass := self.bucket at: index) notNil)
|
|
|
|
{
|
|
|
|
if (anObject = ass) { ^index }.
|
|
|
|
index := (index + 1) rem: bs.
|
|
|
|
}.
|
|
|
|
|
|
|
|
^index. ## the item at this index is nil.
|
|
|
|
}
|
|
|
|
|
2018-06-22 15:14:56 +00:00
|
|
|
method __find_index: anObject
|
|
|
|
{
|
|
|
|
| bs ass index |
|
|
|
|
|
|
|
|
bs := self.bucket size.
|
|
|
|
index := (anObject hash) rem: bs.
|
|
|
|
|
|
|
|
while ((ass := self.bucket at: index) notNil)
|
|
|
|
{
|
|
|
|
if (anObject = ass) { ^index }.
|
|
|
|
index := (index + 1) rem: bs.
|
|
|
|
}.
|
|
|
|
|
|
|
|
^Error.Code.ENOENT.
|
|
|
|
}
|
|
|
|
|
|
|
|
method __remove_at: index
|
|
|
|
{
|
|
|
|
| bs x y i v ass z |
|
|
|
|
|
|
|
|
bs := self.bucket size.
|
|
|
|
v := self.bucket at: index.
|
|
|
|
|
|
|
|
x := index.
|
|
|
|
y := index.
|
|
|
|
i := 0.
|
|
|
|
while (i < self.tally)
|
|
|
|
{
|
|
|
|
y := (y + 1) rem: bs.
|
|
|
|
|
|
|
|
ass := self.bucket at: y.
|
|
|
|
if (ass isNil) { (* done. the slot at the current index is nil *) break }.
|
|
|
|
|
|
|
|
(* get the natural hash index *)
|
|
|
|
z := (ass key hash) rem: bs.
|
|
|
|
|
|
|
|
(* move an element if necessary *)
|
|
|
|
if (((y > x) and ((z <= x) or (z > y))) or ((y < x) and ((z <= x) and (z > y))))
|
|
|
|
{
|
|
|
|
self.bucket at: x put: (self.bucket at: y).
|
|
|
|
x := y.
|
|
|
|
}.
|
|
|
|
|
|
|
|
i := i + 1.
|
|
|
|
}.
|
|
|
|
|
|
|
|
self.bucket at: x put: nil.
|
|
|
|
self.tally := self.tally - 1.
|
|
|
|
|
|
|
|
(* return the affected association *)
|
|
|
|
^v
|
|
|
|
}
|
|
|
|
|
2018-06-20 18:01:04 +00:00
|
|
|
method add: anObject
|
|
|
|
{
|
|
|
|
| index absent bs |
|
2018-06-24 16:47:55 +00:00
|
|
|
|
|
|
|
## you cannot add nil to a set. however, the add: method doesn't
|
|
|
|
## raise an exception for this. the includes: for nil returns
|
|
|
|
## false naturally for the way it's implemented.
|
2018-06-20 18:01:04 +00:00
|
|
|
if (anObject isNil) { ^anObject }.
|
|
|
|
|
2018-06-22 15:14:56 +00:00
|
|
|
index := self __find_index_for_add: anObject.
|
2018-06-20 18:01:04 +00:00
|
|
|
absent := (self.bucket at: index) isNil.
|
|
|
|
self.bucket at: index put: anObject.
|
|
|
|
|
|
|
|
if (absent)
|
|
|
|
{
|
|
|
|
self.tally := self.tally + 1.
|
|
|
|
bs := self.bucket size.
|
|
|
|
if (self.tally > (bs * 3 div: 4)) { self.bucket := self __make_expanded_bucket: bs }.
|
|
|
|
}.
|
|
|
|
^anObject.
|
|
|
|
}
|
|
|
|
|
2018-06-22 15:14:56 +00:00
|
|
|
method remove: oldObject
|
|
|
|
{
|
|
|
|
| index |
|
|
|
|
index := self __find_index: oldObject.
|
|
|
|
if (index isError) { ^NotFoundException signal. }.
|
|
|
|
^self __remove_at: index.
|
|
|
|
}
|
|
|
|
|
2018-06-20 18:01:04 +00:00
|
|
|
method remove: oldObject ifAbsent: anExceptionBlock
|
|
|
|
{
|
2018-06-22 15:14:56 +00:00
|
|
|
| index |
|
|
|
|
index := self __find_index: oldObject.
|
|
|
|
if (index isError) { ^anExceptionBlock value }.
|
|
|
|
^self __remove_at: index.
|
2018-06-20 18:01:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method includes: anObject
|
|
|
|
{
|
2018-06-22 15:14:56 +00:00
|
|
|
^(self __find_index: anObject) notError.
|
2018-06-20 18:01:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method = aSet
|
|
|
|
{
|
|
|
|
ifnot (self class == aSet class) { ^false }.
|
|
|
|
if (self == aSet){ ^true }.
|
|
|
|
ifnot (self.tally = aSet size) { ^false }.
|
|
|
|
self do: [ :el | ifnot (aSet includes: el) { ^false } ].
|
|
|
|
^true
|
|
|
|
}
|
|
|
|
|
|
|
|
method do: aBlock
|
|
|
|
{
|
|
|
|
| bs i obj |
|
|
|
|
bs := self.bucket size.
|
|
|
|
i := 0.
|
|
|
|
while (i < bs)
|
|
|
|
{
|
|
|
|
if ((obj := self.bucket at: i) notNil) { aBlock value: obj }.
|
|
|
|
i := i + 1.
|
|
|
|
}.
|
|
|
|
}
|
2018-06-24 16:47:55 +00:00
|
|
|
|
|
|
|
method collect: aBlock
|
|
|
|
{
|
|
|
|
## override the default implementation to avoid frequent growth
|
|
|
|
## of the new collection being constructed. the block tends to
|
|
|
|
## include expression that will produce a unique value for each
|
|
|
|
## element. so sizing the returning collection to the same size
|
|
|
|
## as the receiver is likely to help. however, this assumption
|
|
|
|
## isn't always true.
|
|
|
|
|
|
|
|
| coll |
|
|
|
|
coll := self class new: self capacity.
|
|
|
|
self do: [ :el | coll add: (aBlock value: el) ].
|
|
|
|
^coll
|
|
|
|
}
|
2018-06-20 18:01:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class AssociativeCollection(Collection)
|
|
|
|
{
|
|
|
|
var tally, bucket.
|
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
method(#class) new
|
2018-06-20 18:01:04 +00:00
|
|
|
{
|
2018-06-22 15:27:38 +00:00
|
|
|
^self new: 16.
|
2017-01-09 13:20:46 +00:00
|
|
|
}
|
2018-06-24 16:47:55 +00:00
|
|
|
|
|
|
|
method(#class) new: capacity
|
2017-01-05 10:16:04 +00:00
|
|
|
{
|
2018-06-24 16:47:55 +00:00
|
|
|
^super new __init_with_capacity: capacity.
|
2017-01-09 13:20:46 +00:00
|
|
|
}
|
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
method __init_with_capacity: capacity
|
2017-01-09 13:20:46 +00:00
|
|
|
{
|
2018-06-24 16:47:55 +00:00
|
|
|
if (capacity <= 0) { capacity := 2 }.
|
2017-01-05 10:16:04 +00:00
|
|
|
self.tally := 0.
|
2018-06-24 16:47:55 +00:00
|
|
|
self.bucket := Array new: capacity.
|
2017-01-05 10:16:04 +00:00
|
|
|
}
|
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
method size
|
2017-01-05 10:16:04 +00:00
|
|
|
{
|
|
|
|
^self.tally
|
|
|
|
}
|
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
method capacity
|
|
|
|
{
|
|
|
|
^self.bucket size.
|
|
|
|
}
|
|
|
|
|
2017-02-07 17:40:34 +00:00
|
|
|
method __make_expanded_bucket: bs
|
|
|
|
{
|
2018-06-20 18:01:04 +00:00
|
|
|
| newbuc newsz ass index i |
|
2017-02-07 17:40:34 +00:00
|
|
|
|
|
|
|
(* expand the bucket *)
|
|
|
|
newsz := bs + 128. (* TODO: keep this growth policy in sync with VM(dic.c) *)
|
|
|
|
newbuc := Array new: newsz.
|
2018-06-20 18:01:04 +00:00
|
|
|
i := 0.
|
|
|
|
while (i < bs)
|
|
|
|
{
|
2017-02-07 17:40:34 +00:00
|
|
|
ass := self.bucket at: i.
|
2017-02-07 18:09:07 +00:00
|
|
|
if (ass notNil)
|
|
|
|
{
|
2017-02-07 17:40:34 +00:00
|
|
|
index := (ass key hash) rem: newsz.
|
2018-06-20 18:01:04 +00:00
|
|
|
while ((newbuc at: index) notNil) { index := (index + 1) rem: newsz }.
|
2017-02-07 17:40:34 +00:00
|
|
|
newbuc at: index put: ass
|
2017-02-07 18:09:07 +00:00
|
|
|
}.
|
2018-06-20 18:01:04 +00:00
|
|
|
i := i + 1.
|
|
|
|
}.
|
2017-02-07 17:40:34 +00:00
|
|
|
|
|
|
|
^newbuc.
|
|
|
|
}
|
2018-06-20 18:01:04 +00:00
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
method __find: key or_upsert: upsert with: value
|
2017-01-05 10:16:04 +00:00
|
|
|
{
|
|
|
|
| hv ass bs index ntally |
|
|
|
|
|
|
|
|
bs := self.bucket size.
|
|
|
|
hv := key hash.
|
|
|
|
index := hv rem: bs.
|
|
|
|
|
2017-02-07 18:09:07 +00:00
|
|
|
while ((ass := self.bucket at: index) notNil)
|
|
|
|
{
|
|
|
|
if (key = ass key)
|
|
|
|
{
|
|
|
|
(* found *)
|
|
|
|
if (upsert) { ass value: value }.
|
|
|
|
^ass
|
|
|
|
}.
|
|
|
|
index := (index + 1) rem: bs.
|
|
|
|
}.
|
2017-01-05 10:16:04 +00:00
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
ifnot (upsert) { ^Error.Code.ENOENT }.
|
2017-01-05 10:16:04 +00:00
|
|
|
|
|
|
|
ntally := self.tally + 1.
|
2017-02-07 17:40:34 +00:00
|
|
|
if (ntally >= bs)
|
|
|
|
{
|
|
|
|
self.bucket := self __make_expanded_bucket: bs.
|
2017-01-06 14:49:42 +00:00
|
|
|
bs := self.bucket size.
|
|
|
|
index := hv rem: bs.
|
2017-02-07 18:09:07 +00:00
|
|
|
while ((self.bucket at: index) notNil) { index := (index + 1) rem: bs }.
|
2017-02-07 17:40:34 +00:00
|
|
|
}.
|
2017-09-21 07:56:51 +00:00
|
|
|
|
2017-01-05 10:16:04 +00:00
|
|
|
ass := Association key: key value: value.
|
|
|
|
self.tally := ntally.
|
|
|
|
self.bucket at: index put: ass.
|
2018-06-20 18:01:04 +00:00
|
|
|
|
2017-01-05 10:16:04 +00:00
|
|
|
^ass
|
|
|
|
}
|
|
|
|
|
2017-01-06 14:49:42 +00:00
|
|
|
method at: key
|
|
|
|
{
|
|
|
|
| ass |
|
|
|
|
ass := self __find: key or_upsert: false with: nil.
|
2018-06-26 16:22:16 +00:00
|
|
|
if (ass isError) { ^KeyNotFoundException signal: ('Unable to find ' & (key asString)) }.
|
2017-01-09 09:11:36 +00:00
|
|
|
^ass value
|
2017-01-06 14:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method at: key ifAbsent: error_block
|
|
|
|
{
|
|
|
|
| ass |
|
|
|
|
ass := self __find: key or_upsert: false with: nil.
|
2017-02-07 18:09:07 +00:00
|
|
|
if (ass isError) { ^error_block value }.
|
2017-01-09 09:11:36 +00:00
|
|
|
^ass value
|
|
|
|
}
|
|
|
|
|
2017-02-07 17:40:34 +00:00
|
|
|
method associationAt: assoc
|
2017-01-09 09:11:36 +00:00
|
|
|
{
|
2018-12-22 08:37:18 +00:00
|
|
|
| ass |
|
|
|
|
ass := self __find: (assoc key) or_upsert: false with: nil.
|
|
|
|
if (ass isError) { ^KeyNotFoundException signal: ('Unable to find ' & (assoc key asString)) }.
|
|
|
|
^ass.
|
2017-01-09 09:11:36 +00:00
|
|
|
}
|
|
|
|
|
2017-02-07 17:40:34 +00:00
|
|
|
method associationAt: assoc ifAbsent: error_block
|
2017-01-09 09:11:36 +00:00
|
|
|
{
|
|
|
|
| ass |
|
2017-02-07 17:40:34 +00:00
|
|
|
ass := self __find: (assoc key) or_upsert: false with: nil.
|
2017-02-07 18:09:07 +00:00
|
|
|
if (ass isError) { ^error_block value }.
|
2017-01-09 09:11:36 +00:00
|
|
|
^ass
|
2017-01-06 14:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method at: key put: value
|
|
|
|
{
|
2018-06-24 16:47:55 +00:00
|
|
|
## returns the affected/inserted association
|
2017-01-09 09:11:36 +00:00
|
|
|
^self __find: key or_upsert: true with: value.
|
2017-01-06 14:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method includesKey: key
|
|
|
|
{
|
|
|
|
| ass |
|
|
|
|
ass := self __find: key or_upsert: false with: nil.
|
2017-01-09 09:11:36 +00:00
|
|
|
^ass notError
|
2017-01-06 14:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method includesAssociation: assoc
|
|
|
|
{
|
|
|
|
| ass |
|
|
|
|
ass := self __find: (assoc key) or_upsert: false with: nil.
|
|
|
|
^ass = assoc.
|
|
|
|
}
|
2018-06-24 16:47:55 +00:00
|
|
|
|
2017-01-06 14:49:42 +00:00
|
|
|
method includesKey: key value: value
|
|
|
|
{
|
|
|
|
| ass |
|
|
|
|
ass := self __find: key or_upsert: false with: nil.
|
2018-05-30 15:32:09 +00:00
|
|
|
^(ass key = key) and (ass value = value)
|
2017-01-06 14:49:42 +00:00
|
|
|
}
|
2017-01-09 09:11:36 +00:00
|
|
|
|
|
|
|
method __find_index: key
|
|
|
|
{
|
|
|
|
| bs ass index |
|
2018-06-20 18:01:04 +00:00
|
|
|
|
2017-01-09 09:11:36 +00:00
|
|
|
bs := self.bucket size.
|
|
|
|
index := (key hash) rem: bs.
|
|
|
|
|
2017-02-07 18:09:07 +00:00
|
|
|
while ((ass := self.bucket at: index) notNil)
|
|
|
|
{
|
|
|
|
if (key = ass key) { ^index }.
|
|
|
|
index := (index + 1) rem: bs.
|
|
|
|
}.
|
2017-01-09 09:11:36 +00:00
|
|
|
|
2017-03-08 14:48:12 +00:00
|
|
|
^Error.Code.ENOENT.
|
2017-01-09 09:11:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method __remove_at: index
|
|
|
|
{
|
2017-03-19 14:18:37 +00:00
|
|
|
| bs x y i v ass z |
|
2017-01-09 09:11:36 +00:00
|
|
|
|
|
|
|
bs := self.bucket size.
|
2017-03-19 14:18:37 +00:00
|
|
|
v := self.bucket at: index.
|
2017-01-09 09:11:36 +00:00
|
|
|
|
|
|
|
x := index.
|
|
|
|
y := index.
|
|
|
|
i := 0.
|
2017-03-19 14:18:37 +00:00
|
|
|
while (i < self.tally)
|
|
|
|
{
|
2017-01-09 09:11:36 +00:00
|
|
|
y := (y + 1) rem: bs.
|
|
|
|
|
2017-03-19 14:18:37 +00:00
|
|
|
ass := self.bucket at: y.
|
|
|
|
if (ass isNil) { (* done. the slot at the current index is nil *) break }.
|
|
|
|
|
|
|
|
(* get the natural hash index *)
|
|
|
|
z := (ass key hash) rem: bs.
|
2017-02-07 18:09:07 +00:00
|
|
|
|
2017-03-19 14:18:37 +00:00
|
|
|
(* move an element if necessary *)
|
2018-05-30 15:32:09 +00:00
|
|
|
if (((y > x) and ((z <= x) or (z > y))) or ((y < x) and ((z <= x) and (z > y))))
|
2017-03-19 14:18:37 +00:00
|
|
|
{
|
|
|
|
self.bucket at: x put: (self.bucket at: y).
|
|
|
|
x := y.
|
2017-02-07 18:09:07 +00:00
|
|
|
}.
|
2017-03-19 14:18:37 +00:00
|
|
|
|
|
|
|
i := i + 1.
|
|
|
|
}.
|
2018-06-24 16:47:55 +00:00
|
|
|
|
2017-01-09 09:11:36 +00:00
|
|
|
self.bucket at: x put: nil.
|
|
|
|
self.tally := self.tally - 1.
|
2018-06-24 16:47:55 +00:00
|
|
|
|
|
|
|
## return the affected association
|
2017-01-09 09:11:36 +00:00
|
|
|
^v
|
|
|
|
}
|
|
|
|
|
2017-01-06 14:49:42 +00:00
|
|
|
method removeKey: key
|
|
|
|
{
|
2017-01-09 09:11:36 +00:00
|
|
|
| index |
|
|
|
|
index := self __find_index: key.
|
2018-06-26 16:22:16 +00:00
|
|
|
if (index isError) { ^KeyNotFoundException signal: ('Unable to find ' & (key asString)). }.
|
2017-01-09 09:11:36 +00:00
|
|
|
^self __remove_at: index.
|
2017-01-06 14:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method removeKey: key ifAbsent: error_block
|
|
|
|
{
|
2017-01-09 09:11:36 +00:00
|
|
|
| index |
|
|
|
|
index := self __find_index: key.
|
2017-02-07 18:09:07 +00:00
|
|
|
if (index isError) { ^error_block value }.
|
2017-01-09 09:11:36 +00:00
|
|
|
^self __remove_at: index.
|
2017-01-06 14:49:42 +00:00
|
|
|
}
|
|
|
|
|
2017-01-09 09:11:36 +00:00
|
|
|
method removeAllKeys
|
|
|
|
{
|
|
|
|
(* remove all items from a dictionary *)
|
|
|
|
| bs |
|
|
|
|
bs := self.bucket size.
|
|
|
|
0 priorTo: bs do: [:i | self.bucket at: i put: nil ].
|
|
|
|
self.tally := 0
|
|
|
|
}
|
|
|
|
|
|
|
|
(* TODO: ... keys is an array of keys.
|
|
|
|
method removeAllKeys: keys
|
|
|
|
{
|
|
|
|
self notImplemented: #removeAllKeys:
|
|
|
|
}
|
|
|
|
*)
|
|
|
|
|
2017-01-06 14:49:42 +00:00
|
|
|
method remove: assoc
|
|
|
|
{
|
|
|
|
^self removeKey: (assoc key)
|
|
|
|
}
|
|
|
|
|
|
|
|
method remove: assoc ifAbsent: error_block
|
|
|
|
{
|
|
|
|
^self removeKey: (assoc key) ifAbsent: error_block
|
|
|
|
}
|
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
|
2018-06-27 14:40:31 +00:00
|
|
|
method add: anAssociation
|
|
|
|
{
|
|
|
|
^self __find: (anAssociation key) or_upsert: true with: (anAssociation value)
|
|
|
|
}
|
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
## ===================================================================
|
|
|
|
## ENUMERATING
|
|
|
|
## ===================================================================
|
|
|
|
|
|
|
|
method collect: aBlock
|
|
|
|
{
|
|
|
|
| coll |
|
|
|
|
coll := OrderedCollection new: self capacity.
|
|
|
|
self do: [ :el | coll add: (aBlock value: el) ].
|
|
|
|
^coll
|
|
|
|
}
|
|
|
|
|
|
|
|
method select: aBlock
|
|
|
|
{
|
|
|
|
| coll |
|
|
|
|
coll := self class new.
|
|
|
|
## TODO: using at:put: here isn't really right. implement add: to be able to insert the assocication without
|
|
|
|
## creating another new association.
|
|
|
|
##self associationsDo: [ :ass | if (aBlock value: ass value) { coll add: ass } ].
|
|
|
|
self associationsDo: [ :ass | if (aBlock value: ass value) { coll at: (ass key) put: (ass value) } ].
|
|
|
|
^coll
|
|
|
|
}
|
|
|
|
|
|
|
|
method do: aBlock
|
2017-01-05 10:16:04 +00:00
|
|
|
{
|
2018-06-19 16:58:04 +00:00
|
|
|
| bs i ass |
|
2017-01-05 10:16:04 +00:00
|
|
|
bs := self.bucket size.
|
2018-06-19 16:58:04 +00:00
|
|
|
i := 0.
|
|
|
|
while (i < bs)
|
|
|
|
{
|
2018-06-24 16:47:55 +00:00
|
|
|
if ((ass := self.bucket at: i) notNil) { aBlock value: ass value }.
|
2018-06-19 16:58:04 +00:00
|
|
|
i := i + 1.
|
|
|
|
}.
|
2017-01-05 10:16:04 +00:00
|
|
|
}
|
2018-06-19 16:58:04 +00:00
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
method keysDo: aBlock
|
2017-01-05 10:16:04 +00:00
|
|
|
{
|
2018-06-19 16:58:04 +00:00
|
|
|
| bs i ass |
|
2017-01-06 13:27:49 +00:00
|
|
|
bs := self.bucket size.
|
2018-06-19 16:58:04 +00:00
|
|
|
i := 0.
|
|
|
|
while (i < bs)
|
|
|
|
{
|
2018-06-24 16:47:55 +00:00
|
|
|
if ((ass := self.bucket at: i) notNil) { aBlock value: ass key }.
|
2018-06-19 16:58:04 +00:00
|
|
|
i := i + 1.
|
|
|
|
}.
|
2017-01-06 13:27:49 +00:00
|
|
|
}
|
2017-01-05 10:16:04 +00:00
|
|
|
|
2018-06-24 16:47:55 +00:00
|
|
|
method keysAndValuesDo: aBlock
|
2017-01-06 13:27:49 +00:00
|
|
|
{
|
2018-06-19 16:58:04 +00:00
|
|
|
| bs i ass |
|
2017-01-05 10:16:04 +00:00
|
|
|
bs := self.bucket size.
|
2018-06-19 16:58:04 +00:00
|
|
|
i := 0.
|
|
|
|
while (i < bs)
|
|
|
|
{
|
2018-06-24 16:47:55 +00:00
|
|
|
if ((ass := self.bucket at: i) notNil) { aBlock value: ass key value: ass value }.
|
|
|
|
i := i + 1.
|
|
|
|
}.
|
|
|
|
}
|
|
|
|
|
|
|
|
method associationsDo: aBlock
|
|
|
|
{
|
|
|
|
| bs i ass |
|
|
|
|
bs := self.bucket size.
|
|
|
|
i := 0.
|
|
|
|
while (i < bs)
|
|
|
|
{
|
|
|
|
if ((ass := self.bucket at: i) notNil) { aBlock value: ass }.
|
2018-06-19 16:58:04 +00:00
|
|
|
i := i + 1.
|
|
|
|
}.
|
2017-01-05 10:16:04 +00:00
|
|
|
}
|
2016-05-13 15:10:34 +00:00
|
|
|
}
|
|
|
|
|
2018-06-20 18:01:04 +00:00
|
|
|
class SymbolTable(AssociativeCollection)
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-06-20 18:01:04 +00:00
|
|
|
class Dictionary(AssociativeCollection)
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
2017-02-10 07:38:29 +00:00
|
|
|
(* [NOTE]
|
|
|
|
* VM require Dictionary to implement new: and __put_assoc
|
2017-06-16 09:45:22 +00:00
|
|
|
* for the dictionary expression notation - %{ }
|
2017-02-10 07:38:29 +00:00
|
|
|
*)
|
|
|
|
|
|
|
|
## TODO: implement Dictionary as a Hashed List/Table or Red-Black Tree
|
|
|
|
## Do not inherit Set upon reimplementation
|
|
|
|
##
|
2018-06-24 16:47:55 +00:00
|
|
|
method(#class) new: capacity
|
2017-02-10 07:38:29 +00:00
|
|
|
{
|
2018-06-24 16:47:55 +00:00
|
|
|
^super new: (capacity + 10).
|
2017-02-10 07:38:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
(* put_assoc: is called internally by VM to add an association
|
|
|
|
* to a dictionary with the dictionary/association expression notation
|
|
|
|
* like this:
|
|
|
|
*
|
2017-06-16 09:45:22 +00:00
|
|
|
* %{ 1 -> 20, #moo -> 999 }
|
2017-02-10 07:38:29 +00:00
|
|
|
*
|
|
|
|
* it must return self for the way VM works.
|
|
|
|
*)
|
2018-06-20 18:01:04 +00:00
|
|
|
method __put_assoc: assoc
|
2017-02-10 07:38:29 +00:00
|
|
|
{
|
|
|
|
| hv ass bs index ntally key |
|
|
|
|
|
|
|
|
key := assoc key.
|
|
|
|
bs := self.bucket size.
|
|
|
|
hv := key hash.
|
|
|
|
index := hv rem: bs.
|
|
|
|
|
2017-03-09 07:26:43 +00:00
|
|
|
(* as long as 'assoc' supports the message 'key' and 'value'
|
|
|
|
* this dictionary should work. there is no explicit check
|
|
|
|
* on this protocol of key and value. *)
|
|
|
|
|
2017-02-10 07:38:29 +00:00
|
|
|
while ((ass := self.bucket at: index) notNil)
|
|
|
|
{
|
|
|
|
if (key = ass key)
|
|
|
|
{
|
|
|
|
(* found *)
|
|
|
|
self.bucket at: index put: assoc.
|
|
|
|
^self. ## it must return self for the instructions generated by the compiler.
|
|
|
|
}.
|
|
|
|
index := (index + 1) rem: bs.
|
|
|
|
}.
|
|
|
|
|
|
|
|
(* not found *)
|
|
|
|
ntally := self.tally + 1.
|
|
|
|
if (ntally >= bs)
|
|
|
|
{
|
|
|
|
self.bucket := self __make_expanded_bucket: bs.
|
|
|
|
bs := self.bucket size.
|
|
|
|
index := hv rem: bs.
|
|
|
|
while ((self.bucket at: index) notNil) { index := (index + 1) rem: bs }.
|
|
|
|
}.
|
|
|
|
|
|
|
|
self.tally := ntally.
|
|
|
|
self.bucket at: index put: assoc.
|
|
|
|
|
2017-03-09 07:26:43 +00:00
|
|
|
(* it must return self for the instructions generated by the compiler.
|
|
|
|
* otherwise, VM will break. *)
|
|
|
|
^self.
|
2017-02-10 07:38:29 +00:00
|
|
|
}
|
2016-05-13 15:10:34 +00:00
|
|
|
}
|
|
|
|
|
2017-05-20 02:27:48 +00:00
|
|
|
(* Namespace is marked with #limited. If a compiler is writeen in moo itself, it must
|
|
|
|
* call a primitive to instantiate a new namespace rather than sending the new message
|
|
|
|
* to Namespace *)
|
2018-06-20 18:01:04 +00:00
|
|
|
class(#limited) Namespace(AssociativeCollection)
|
2016-06-30 13:44:37 +00:00
|
|
|
{
|
2017-05-20 02:27:48 +00:00
|
|
|
var name, nsup.
|
|
|
|
|
|
|
|
method name { ^self.name }
|
|
|
|
## method name: name { self.name := name }
|
|
|
|
|
|
|
|
(* nsup points to either the class associated with this namespace or directly
|
|
|
|
* the upper namespace placed above this namespace. when it points to a class,
|
|
|
|
* you should inspect the nsup field of the class to reach the actual upper
|
|
|
|
* namespace *)
|
|
|
|
method nsup { ^self.nsup }
|
|
|
|
## method nsup: nsup { self.nsup := nsup }
|
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
method at: key
|
|
|
|
{
|
2017-02-07 18:09:07 +00:00
|
|
|
if (key class ~= Symbol) { InvalidArgumentException signal: 'key is not a symbol' }.
|
2017-01-06 09:53:40 +00:00
|
|
|
^super at: key.
|
|
|
|
}
|
2017-01-06 13:27:49 +00:00
|
|
|
|
|
|
|
method at: key put: value
|
|
|
|
{
|
2017-02-07 18:09:07 +00:00
|
|
|
if (key class ~= Symbol) { InvalidArgumentException signal: 'key is not a symbol' }.
|
2017-01-06 13:27:49 +00:00
|
|
|
^super at: key put: value
|
|
|
|
}
|
2016-06-30 13:44:37 +00:00
|
|
|
}
|
2016-05-13 15:10:34 +00:00
|
|
|
|
2018-06-20 18:01:04 +00:00
|
|
|
class PoolDictionary(AssociativeCollection)
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-06-20 18:01:04 +00:00
|
|
|
class MethodDictionary(AssociativeCollection)
|
2016-05-13 15:10:34 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
extend Apex
|
2016-07-01 16:31:47 +00:00
|
|
|
{
|
|
|
|
## -------------------------------------------------------
|
|
|
|
## Association has been defined now. let's add association
|
|
|
|
## creating methods
|
|
|
|
## -------------------------------------------------------
|
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
method(#class) -> object
|
2016-07-01 16:31:47 +00:00
|
|
|
{
|
|
|
|
^Association new key: self value: object
|
|
|
|
}
|
|
|
|
|
2017-01-06 09:53:40 +00:00
|
|
|
method -> object
|
2016-07-01 16:31:47 +00:00
|
|
|
{
|
|
|
|
^Association new key: self value: object
|
|
|
|
}
|
2017-01-18 17:17:05 +00:00
|
|
|
}
|
2017-03-04 05:48:23 +00:00
|
|
|
|
|
|
|
## -------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
class Link(Object)
|
|
|
|
{
|
2017-04-19 16:46:44 +00:00
|
|
|
var prev, next, value.
|
2017-03-04 05:48:23 +00:00
|
|
|
|
|
|
|
method(#class) new: value
|
|
|
|
{
|
|
|
|
| x |
|
|
|
|
x := self new.
|
|
|
|
x value: value.
|
|
|
|
^x
|
|
|
|
}
|
|
|
|
|
|
|
|
method prev { ^self.prev }
|
|
|
|
method next { ^self.next }
|
|
|
|
method value { ^self.value }
|
|
|
|
method prev: link { self.prev := link }
|
|
|
|
method next: link { self.next := link }
|
|
|
|
method value: value { self.value := value }
|
|
|
|
}
|
|
|
|
|
|
|
|
class LinkedList(Collection)
|
|
|
|
{
|
2017-04-19 16:46:44 +00:00
|
|
|
var first, last, tally.
|
2017-03-04 05:48:23 +00:00
|
|
|
|
|
|
|
method initialize
|
|
|
|
{
|
|
|
|
self.tally := 0.
|
|
|
|
}
|
|
|
|
|
|
|
|
method size
|
|
|
|
{
|
|
|
|
^self.tally
|
|
|
|
}
|
|
|
|
|
|
|
|
method first
|
|
|
|
{
|
|
|
|
^self.first
|
|
|
|
}
|
|
|
|
|
|
|
|
method last
|
|
|
|
{
|
|
|
|
^self.last
|
|
|
|
}
|
|
|
|
|
|
|
|
method insertLink: link at: pos
|
|
|
|
{
|
|
|
|
if (pos isNil)
|
|
|
|
{
|
|
|
|
(* add link at the back *)
|
|
|
|
if (self.tally == 0)
|
|
|
|
{
|
|
|
|
self.first := link.
|
|
|
|
self.last := link.
|
|
|
|
link prev: nil.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
link prev: self.last.
|
|
|
|
self.last next: link.
|
|
|
|
self.last := link.
|
|
|
|
}.
|
|
|
|
link next: nil.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
(* insert the link before pos *)
|
|
|
|
link next: pos.
|
|
|
|
link prev: pos prev.
|
|
|
|
if (pos prev notNil) { pos prev next: link }
|
|
|
|
else { self.first := link }.
|
|
|
|
pos prev: link
|
|
|
|
}.
|
|
|
|
|
|
|
|
self.tally := self.tally + 1.
|
|
|
|
^link
|
|
|
|
}
|
|
|
|
|
|
|
|
method insert: value at: pos
|
|
|
|
{
|
|
|
|
^self insertLink: (Link new: value) at: pos
|
|
|
|
}
|
|
|
|
|
|
|
|
method addFirst: value
|
|
|
|
{
|
|
|
|
^self insert: value at: self.first
|
|
|
|
}
|
|
|
|
|
|
|
|
method addLast: value
|
|
|
|
{
|
|
|
|
^self insert: value at: nil
|
|
|
|
}
|
|
|
|
|
|
|
|
method addFirstLink: link
|
|
|
|
{
|
|
|
|
^self insertLink: link at: self.first
|
|
|
|
}
|
|
|
|
|
|
|
|
method addLastLink: link
|
|
|
|
{
|
|
|
|
^self insertLink: link at: nil
|
|
|
|
}
|
|
|
|
|
|
|
|
method removeLink: link
|
|
|
|
{
|
|
|
|
if (link next notNil) { link next prev: link prev }
|
|
|
|
else { self.last := link prev }.
|
|
|
|
|
|
|
|
if (link prev notNil) { link prev next: link next }
|
|
|
|
else { self.first := link next }.
|
|
|
|
|
|
|
|
self.tally := self.tally - 1.
|
2017-03-19 14:18:37 +00:00
|
|
|
^link.
|
2017-03-04 05:48:23 +00:00
|
|
|
}
|
|
|
|
|
2017-03-19 14:18:37 +00:00
|
|
|
method removeFirstLink
|
2017-03-04 05:48:23 +00:00
|
|
|
{
|
|
|
|
^self removeLink: self.first
|
|
|
|
}
|
|
|
|
|
2017-03-19 14:18:37 +00:00
|
|
|
method removeLastLink
|
2017-03-04 05:48:23 +00:00
|
|
|
{
|
|
|
|
^self removeLink: self.last
|
|
|
|
}
|
|
|
|
|
|
|
|
method do: block
|
|
|
|
{
|
|
|
|
| link next |
|
|
|
|
link := self.first.
|
|
|
|
while (link notNil)
|
|
|
|
{
|
|
|
|
next := link next.
|
|
|
|
block value: link value.
|
|
|
|
link := next.
|
|
|
|
}
|
|
|
|
}
|
2017-03-19 14:18:37 +00:00
|
|
|
|
|
|
|
method doOverLink: block
|
|
|
|
{
|
|
|
|
| link next |
|
|
|
|
link := self.first.
|
|
|
|
while (link notNil)
|
|
|
|
{
|
|
|
|
next := link next.
|
|
|
|
block value: link.
|
|
|
|
link := next.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-27 16:03:29 +00:00
|
|
|
method findEqualLink: value
|
2017-03-19 14:18:37 +00:00
|
|
|
{
|
|
|
|
self doOverLink: [:el | if (el value = value) { ^el }].
|
|
|
|
^Error.Code.ENOENT
|
|
|
|
}
|
2017-06-27 16:03:29 +00:00
|
|
|
|
|
|
|
method findIdenticalLink: value
|
|
|
|
{
|
|
|
|
self doOverLink: [:el | if (el value == value) { ^el }].
|
|
|
|
^Error.Code.ENOENT
|
|
|
|
}
|
2017-03-04 05:48:23 +00:00
|
|
|
}
|