--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ArithVal.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,613 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Magnitude subclass:#ArithmeticValue
+ instanceVariableNames:''
+ classVariableNames:'DivisionByZeroSignal DomainErrorSignal
+ OverflowSignal UnderflowSignal
+ AnyArithmeticSignal'
+ poolDictionaries:''
+ category:'Magnitude-Numbers'
+!
+
+ArithmeticValue comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ArithmeticValue is an abstract superclass for all things responding to
+arithmetic messages. It was inserted into the hierarchy, to allow things
+like matrices, functions etc. share the arithmetic methods defined here.
+
+(In the old hierarchy these had to be Numbers to do that
+ - which is not quite correct)
+
+%W% %E%
+'!
+
+!ArithmeticValue class methodsFor:'initialization' !
+
+initialize
+ "setup the signals"
+
+ DomainErrorSignal := (Signal new) mayProceed:false.
+ DomainErrorSignal notifierString:'domain error'.
+
+ DivisionByZeroSignal := (Signal new) mayProceed:false.
+ DivisionByZeroSignal notifierString:'division by zero'.
+
+ OverflowSignal := (Signal new) mayProceed:false.
+ OverflowSignal notifierString:'overflow'.
+
+ UnderflowSignal := (Signal new) mayProceed:false.
+ UnderflowSignal notifierString:'underflow'.
+
+ AnyArithmeticSignal := SignalSet with:DomainErrorSignal
+ with:DivisionByZeroSignal
+ with:OverflowSignal
+ with:UnderflowSignal.
+! !
+
+!ArithmeticValue class methodsFor:'signal access' !
+
+domainErrorSignal
+ "return the signal which is raised on math errors
+ (such as log of 0 etc.)"
+
+ ^ DomainErrorSignal
+!
+
+divisionByZeroSignal
+ "return the signal which is raised on division by zero"
+
+ ^ DivisionByZeroSignal
+!
+
+overflowSignal
+ "return the signal which is raised on overflow conditions (in floats)"
+
+ ^ OverflowSignal
+!
+
+underflowSignal
+ "return the signal which is raised on underflow conditions (in floats)"
+
+ ^ UnderflowSignal
+!
+
+anyArithmeticSignal
+ "return a signalSet with all possible arithmetic signals"
+
+ ^ AnyArithmeticSignal
+! !
+
+!ArithmeticValue methodsFor:'converting' !
+
+degreesToRadians
+ "interpreting the receiver as radians, return the degrees"
+
+ ^ self asFloat degreesToRadians
+!
+
+radiansToDegrees
+ "interpreting the receiver as degrees, return the radians"
+
+ ^ self asFloat radiansToDegrees
+!
+
+asInteger
+ "return an integer with same value - might truncate"
+
+ ^ self truncated
+!
+
+asFloat
+ "return a float with same value"
+
+ ^ self subclassResponsibility
+!
+
+asFraction
+ "return a fraction with same value"
+
+ ^ self subclassResponsibility
+!
+
+coerce:aNumber
+ "convert aNumber into an instance of the receivers class and return it."
+
+ ^ self subclassResponsibility
+!
+
+generality
+ "return a number giving the receivers generality, that number is
+ used to convert one of the arguments in a mixed expression.
+ The generality has to be defined in subclasses,
+ such that gen(a) > gen(b) iff, conversion of b into a's class
+ does not cut precision. For example, Integer has 40, Float has 80,
+ meaning that if we convert a Float to an Integer, some precision may
+ be lost. The generality is used by ArithmeticValue>>retry:cuercing:"
+
+ ^ self subclassResponsibility
+!
+
+retry:aSymbol coercing:aNumber
+ "arithmetic represented by the binary operator, aSymbol,
+ could not be performed with the receiver and the argument, aNumber,
+ because of the differences in representation.
+ Coerce either the receiver or the argument, depending on which has higher
+ generality, and try again.
+ If the operation is compare for same value (=), return false if
+ the argument is not a Number.
+ If the generalities are the same, create an error message, since this
+ means that a subclass has not been fully implemented."
+
+ |myGenerality otherGenerality|
+
+ (aSymbol == #=) ifTrue:[
+ (aNumber respondsTo:#generality) ifFalse:[^ false]
+ ] ifFalse:[
+ (aNumber respondsTo:#generality) ifFalse:[
+ self error:'retry:coercing: argument is not a number'.
+ ^ self
+ ]
+ ].
+ myGenerality := self generality.
+ otherGenerality := aNumber generality.
+ (myGenerality > otherGenerality) ifTrue:[
+ ^ self perform:aSymbol with:(self coerce:aNumber)
+ ].
+ (myGenerality < otherGenerality) ifTrue:[
+ ^ (aNumber coerce:self) perform:aSymbol with:aNumber
+ ].
+ self error:'retry:coercing: oops - same generality'
+! !
+
+!ArithmeticValue methodsFor:'queries' !
+
+respondsToArithmetic
+ "return true, if the receiver responds to arithmetic messages"
+
+ ^ true
+! !
+
+!ArithmeticValue methodsFor:'arithmetic' !
+
++ something
+ "return the sum of the receiver and the argument"
+
+ ^ self subclassResponsibility
+!
+
+- something
+ "return the difference of the receiver and the argument"
+
+ ^ self subclassResponsibility
+!
+
+* something
+ "return the product of the receiver and the argument"
+
+ ^ self subclassResponsibility
+!
+
+/ something
+ "return the quotient of the receiver and the argument"
+
+ ^ self subclassResponsibility
+!
+
+// something
+ "return the integer quotient of the receiver and the argument"
+
+ ^ (self / something) floor
+!
+
+\\ something
+ "return the integer modulu of the receiver and the argument"
+
+ ^ self - ((self // something) * something)
+!
+
+quo:something
+ "Return the integer quotient of dividing the receiver by the argument
+ with truncation towards zero."
+
+ ^ (self / something) truncated
+!
+
+rem:something
+ "Return the integer remainder of dividing the receiver by the argument
+ with truncation towards zero.
+ The remainder has the same sign as the receiver."
+
+ ^ self - ((self quo:something) * something)
+!
+
+abs
+ "return the absolute value of the receiver"
+
+ (self negative) ifTrue:[^ self negated].
+ ^ self
+!
+
+negated
+ "return the receiver negated"
+
+ ^ self class zero - self
+!
+
+reciprocal
+ "return the receivers reciprocal"
+
+ ^ self class unity / self
+! !
+
+!ArithmeticValue methodsFor:'comparing'!
+
+>= something
+ "return true, if the argument is less or equal than the receiver"
+
+ ^ (self < something) not
+!
+
+> something
+ "return true, if the argument is less than the receiver"
+
+ ^ something < self
+!
+
+<= something
+ "return true, if the argument is greater or equal than the receiver"
+
+ ^ (something < self) not
+!
+
+< something
+ "return true, if the argument is greater than the receiver"
+
+ ^ self subclassResponsibility
+!
+
+compare:arg ifLess:lessBlock ifEqual:equalBlock ifGreater:greaterBlock
+ "three-way compare - thanks to Self for this idea.
+ Can be redefined in subclasses to do it with a single comparison if
+ comparison is expensive."
+
+ self < arg ifTrue:[
+ ^ lessBlock value
+ ].
+ self = arg ifTrue:[
+ ^ equalBlock value
+ ].
+ ^ greaterBlock value
+! !
+
+!ArithmeticValue methodsFor:'truncation and rounding'!
+
+ceiling
+ "return the integer nearest the receiver towards positive infinity."
+
+ |anInteger|
+
+ anInteger := self // 1. "truncates towards negative infinity"
+ anInteger = self ifTrue:[^ anInteger].
+ ^ anInteger + 1
+!
+
+floor
+ "return the receiver truncated towards negative infinity"
+
+ ^ self // 1
+!
+
+truncated
+ "return the receiver truncated towards zero"
+
+ ^ self floor asInteger
+!
+
+truncateTo:aNumber
+ "return the receiver truncated to multiples of aNumber"
+
+ ^ ((self / aNumber) floor * aNumber) asInteger
+!
+
+rounded
+ "return the integer nearest the receiver"
+
+ ^ (self + 0.5) floor asInteger
+!
+
+roundTo:aNumber
+ "return the receiver rounded to multiples of aNumber"
+
+ ^ (self / aNumber) rounded * aNumber
+! !
+
+!ArithmeticValue methodsFor:'double dispatching'!
+
+sumFromInteger:anInteger
+ "the receiver does not know how to add an integer -
+ retry the operation by coercing to higher generality"
+
+ ^ anInteger retry:#+ coercing:self
+!
+
+sumFromFloat:aFloat
+ "the receiver does not know how to add a float -
+ retry the operation by coercing to higher generality"
+
+ ^ aFloat retry:#+ coercing:self
+!
+
+sumFromFraction:aFraction
+ "the receiver does not know how to add a fraction -
+ retry the operation by coercing to higher generality"
+
+ ^ aFraction retry:#+ coercing:self
+!
+
+differenceFromInteger:anInteger
+ "the receiver does not know how to subtract from an integer -
+ retry the operation by coercing to higher generality"
+
+ ^ anInteger retry:#- coercing:self
+!
+
+differenceFromFloat:aFloat
+ "the receiver does not know how to subtract from a float -
+ retry the operation by coercing to higher generality"
+
+ ^ aFloat retry:#- coercing:self
+!
+
+differenceFromFraction:aFraction
+ "the receiver does not know how to subtract from a fraction -
+ retry the operation by coercing to higher generality"
+
+ ^ aFraction retry:#- coercing:self
+!
+
+productFromInteger:anInteger
+ "the receiver does not know how to multiply an integer -
+ retry the operation by coercing to higher generality"
+
+ ^ anInteger retry:#* coercing:self
+!
+
+productFromFloat:aFloat
+ "the receiver does not know how to multiply a float -
+ retry the operation by coercing to higher generality"
+
+ ^ aFloat retry:#* coercing:self
+!
+
+productFromFraction:aFraction
+ "the receiver does not know how to multiply a fraction -
+ retry the operation by coercing to higher generality"
+
+ ^ aFraction retry:#* coercing:self
+!
+
+quotientFromInteger:anInteger
+ "the receiver does not know how to divide an integer -
+ retry the operation by coercing to higher generality"
+
+ ^ anInteger retry:#/ coercing:self
+!
+
+quotientFromFloat:aFloat
+ "the receiver does not know how to divide a float -
+ retry the operation by coercing to higher generality"
+
+ ^ aFloat retry:#/ coercing:self
+!
+
+quotientFromFraction:aFraction
+ "the receiver does not know how to divide a fraction -
+ retry the operation by coercing to higher generality"
+
+ ^ aFraction retry:#/ coercing:self
+!
+
+lessFromInteger:anInteger
+ "the receiver does not know how to compare to an integer -
+ retry the operation by coercing to higher generality"
+
+ ^ anInteger retry:#< coercing:self
+!
+
+lessFromFloat:aFloat
+ "the receiver does not know how to compare to a float -
+ retry the operation by coercing to higher generality"
+
+ ^ aFloat retry:#< coercing:self
+!
+
+lessFromFraction:aFraction
+ "the receiver does not know how to compare to a fraction -
+ retry the operation by coercing to higher generality"
+
+ ^ aFraction retry:#< coercing:self
+! !
+
+!ArithmeticValue methodsFor:'misc math'!
+
+squared
+ "return receiver * receiver"
+
+ ^ self * self
+!
+
+exp
+ "return e ^ receiver"
+
+ ^ self asFloat exp
+!
+
+ln
+ "return the natural logarithm of the receiver"
+
+ ^ self asFloat ln
+!
+
+log
+ "return log base 10 of the receiver"
+
+ ^ self log:10
+!
+
+log:aNumber
+ "return log base aNumber of the receiver"
+
+ ^ self ln / aNumber ln
+!
+
+sqrt
+ "return the square root of the receiver"
+
+ ^ self asFloat sqrt
+!
+
+floorLog:radix
+ "return the logarithm truncated as an integer"
+
+ ^ (self log:radix) floor
+!
+
+raisedTo:aNumber
+ "return the receiver raised to aNumber"
+
+ aNumber = 0 ifTrue:[^ 1].
+ aNumber = 1 ifTrue:[^ self].
+ aNumber isInteger ifTrue:[
+ ^ self raisedToInteger:aNumber
+ ].
+ ^ self asFloat raisedTo:aNumber
+!
+
+raisedToInteger:anInteger
+ "return the receiver raised to anInteger"
+
+ |count result|
+
+ result := self coerce:1.
+ count := anInteger abs.
+ count timesRepeat:[result := result * self].
+ (anInteger < 0) ifTrue:[
+ ^ 1 / result
+ ].
+ ^ result
+! !
+
+!ArithmeticValue methodsFor:'trigonometric'!
+
+sin
+ "return the sine of the receiver (interpreted as radians)"
+
+ ^ self asFloat sin
+!
+
+cos
+ "return the cosine of the receiver (interpreted as radians)"
+
+ ^ self asFloat cos
+!
+
+tan
+ "return the tangens of the receiver (interpreted as radians)"
+
+ ^ self asFloat tan
+!
+
+arcCos
+ "return the arccosine of the receiver (in radians)"
+
+ ^ self asFloat arcCos
+!
+
+arcSin
+ "return the arcsine of the receiver (in radians)"
+
+ ^ self asFloat arcSin
+!
+
+arcTan
+ "return the arctangens of the receiver (in radians)"
+
+ ^ self asFloat arcTan
+! !
+
+!ArithmeticValue methodsFor:'error handling'!
+
+divideByZeroError
+ "report a division by zero error"
+
+ DivisionByZeroSignal raise
+ "self error:'division by zero'"
+! !
+
+!ArithmeticValue methodsFor:'testing'!
+
+negative
+ "return true, if the receiver is < 0"
+
+ " this would lead to infinite recursion ...
+ ^ (self < 0)
+ "
+ ^ self subclassResponsibility
+!
+
+positive
+ "return true, if the receiver is >= 0"
+
+ ^ self negative not
+!
+
+strictlyPositive
+ "return true, if the receiver is > 0"
+
+ ^ (self > 0)
+!
+
+sign
+ "return the sign of the receiver"
+
+ (self < 0) ifTrue:[^ -1].
+ (self > 0) ifTrue:[^ 1].
+ ^ 0
+!
+
+even
+ "return true if the receiver is divisible by 2"
+
+ ^ self truncated asInteger even
+!
+
+odd
+ "return true if the receiver is not divisible by 2"
+
+ ^ self even not
+!
+
+denominator
+ "return the denominator of the receiver"
+
+ ^ 1
+!
+
+numerator
+ "return the numerator of the receiver."
+
+ ^ self
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ArithmeticValue.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,613 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Magnitude subclass:#ArithmeticValue
+ instanceVariableNames:''
+ classVariableNames:'DivisionByZeroSignal DomainErrorSignal
+ OverflowSignal UnderflowSignal
+ AnyArithmeticSignal'
+ poolDictionaries:''
+ category:'Magnitude-Numbers'
+!
+
+ArithmeticValue comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ArithmeticValue is an abstract superclass for all things responding to
+arithmetic messages. It was inserted into the hierarchy, to allow things
+like matrices, functions etc. share the arithmetic methods defined here.
+
+(In the old hierarchy these had to be Numbers to do that
+ - which is not quite correct)
+
+%W% %E%
+'!
+
+!ArithmeticValue class methodsFor:'initialization' !
+
+initialize
+ "setup the signals"
+
+ DomainErrorSignal := (Signal new) mayProceed:false.
+ DomainErrorSignal notifierString:'domain error'.
+
+ DivisionByZeroSignal := (Signal new) mayProceed:false.
+ DivisionByZeroSignal notifierString:'division by zero'.
+
+ OverflowSignal := (Signal new) mayProceed:false.
+ OverflowSignal notifierString:'overflow'.
+
+ UnderflowSignal := (Signal new) mayProceed:false.
+ UnderflowSignal notifierString:'underflow'.
+
+ AnyArithmeticSignal := SignalSet with:DomainErrorSignal
+ with:DivisionByZeroSignal
+ with:OverflowSignal
+ with:UnderflowSignal.
+! !
+
+!ArithmeticValue class methodsFor:'signal access' !
+
+domainErrorSignal
+ "return the signal which is raised on math errors
+ (such as log of 0 etc.)"
+
+ ^ DomainErrorSignal
+!
+
+divisionByZeroSignal
+ "return the signal which is raised on division by zero"
+
+ ^ DivisionByZeroSignal
+!
+
+overflowSignal
+ "return the signal which is raised on overflow conditions (in floats)"
+
+ ^ OverflowSignal
+!
+
+underflowSignal
+ "return the signal which is raised on underflow conditions (in floats)"
+
+ ^ UnderflowSignal
+!
+
+anyArithmeticSignal
+ "return a signalSet with all possible arithmetic signals"
+
+ ^ AnyArithmeticSignal
+! !
+
+!ArithmeticValue methodsFor:'converting' !
+
+degreesToRadians
+ "interpreting the receiver as radians, return the degrees"
+
+ ^ self asFloat degreesToRadians
+!
+
+radiansToDegrees
+ "interpreting the receiver as degrees, return the radians"
+
+ ^ self asFloat radiansToDegrees
+!
+
+asInteger
+ "return an integer with same value - might truncate"
+
+ ^ self truncated
+!
+
+asFloat
+ "return a float with same value"
+
+ ^ self subclassResponsibility
+!
+
+asFraction
+ "return a fraction with same value"
+
+ ^ self subclassResponsibility
+!
+
+coerce:aNumber
+ "convert aNumber into an instance of the receivers class and return it."
+
+ ^ self subclassResponsibility
+!
+
+generality
+ "return a number giving the receivers generality, that number is
+ used to convert one of the arguments in a mixed expression.
+ The generality has to be defined in subclasses,
+ such that gen(a) > gen(b) iff, conversion of b into a's class
+ does not cut precision. For example, Integer has 40, Float has 80,
+ meaning that if we convert a Float to an Integer, some precision may
+ be lost. The generality is used by ArithmeticValue>>retry:cuercing:"
+
+ ^ self subclassResponsibility
+!
+
+retry:aSymbol coercing:aNumber
+ "arithmetic represented by the binary operator, aSymbol,
+ could not be performed with the receiver and the argument, aNumber,
+ because of the differences in representation.
+ Coerce either the receiver or the argument, depending on which has higher
+ generality, and try again.
+ If the operation is compare for same value (=), return false if
+ the argument is not a Number.
+ If the generalities are the same, create an error message, since this
+ means that a subclass has not been fully implemented."
+
+ |myGenerality otherGenerality|
+
+ (aSymbol == #=) ifTrue:[
+ (aNumber respondsTo:#generality) ifFalse:[^ false]
+ ] ifFalse:[
+ (aNumber respondsTo:#generality) ifFalse:[
+ self error:'retry:coercing: argument is not a number'.
+ ^ self
+ ]
+ ].
+ myGenerality := self generality.
+ otherGenerality := aNumber generality.
+ (myGenerality > otherGenerality) ifTrue:[
+ ^ self perform:aSymbol with:(self coerce:aNumber)
+ ].
+ (myGenerality < otherGenerality) ifTrue:[
+ ^ (aNumber coerce:self) perform:aSymbol with:aNumber
+ ].
+ self error:'retry:coercing: oops - same generality'
+! !
+
+!ArithmeticValue methodsFor:'queries' !
+
+respondsToArithmetic
+ "return true, if the receiver responds to arithmetic messages"
+
+ ^ true
+! !
+
+!ArithmeticValue methodsFor:'arithmetic' !
+
++ something
+ "return the sum of the receiver and the argument"
+
+ ^ self subclassResponsibility
+!
+
+- something
+ "return the difference of the receiver and the argument"
+
+ ^ self subclassResponsibility
+!
+
+* something
+ "return the product of the receiver and the argument"
+
+ ^ self subclassResponsibility
+!
+
+/ something
+ "return the quotient of the receiver and the argument"
+
+ ^ self subclassResponsibility
+!
+
+// something
+ "return the integer quotient of the receiver and the argument"
+
+ ^ (self / something) floor
+!
+
+\\ something
+ "return the integer modulu of the receiver and the argument"
+
+ ^ self - ((self // something) * something)
+!
+
+quo:something
+ "Return the integer quotient of dividing the receiver by the argument
+ with truncation towards zero."
+
+ ^ (self / something) truncated
+!
+
+rem:something
+ "Return the integer remainder of dividing the receiver by the argument
+ with truncation towards zero.
+ The remainder has the same sign as the receiver."
+
+ ^ self - ((self quo:something) * something)
+!
+
+abs
+ "return the absolute value of the receiver"
+
+ (self negative) ifTrue:[^ self negated].
+ ^ self
+!
+
+negated
+ "return the receiver negated"
+
+ ^ self class zero - self
+!
+
+reciprocal
+ "return the receivers reciprocal"
+
+ ^ self class unity / self
+! !
+
+!ArithmeticValue methodsFor:'comparing'!
+
+>= something
+ "return true, if the argument is less or equal than the receiver"
+
+ ^ (self < something) not
+!
+
+> something
+ "return true, if the argument is less than the receiver"
+
+ ^ something < self
+!
+
+<= something
+ "return true, if the argument is greater or equal than the receiver"
+
+ ^ (something < self) not
+!
+
+< something
+ "return true, if the argument is greater than the receiver"
+
+ ^ self subclassResponsibility
+!
+
+compare:arg ifLess:lessBlock ifEqual:equalBlock ifGreater:greaterBlock
+ "three-way compare - thanks to Self for this idea.
+ Can be redefined in subclasses to do it with a single comparison if
+ comparison is expensive."
+
+ self < arg ifTrue:[
+ ^ lessBlock value
+ ].
+ self = arg ifTrue:[
+ ^ equalBlock value
+ ].
+ ^ greaterBlock value
+! !
+
+!ArithmeticValue methodsFor:'truncation and rounding'!
+
+ceiling
+ "return the integer nearest the receiver towards positive infinity."
+
+ |anInteger|
+
+ anInteger := self // 1. "truncates towards negative infinity"
+ anInteger = self ifTrue:[^ anInteger].
+ ^ anInteger + 1
+!
+
+floor
+ "return the receiver truncated towards negative infinity"
+
+ ^ self // 1
+!
+
+truncated
+ "return the receiver truncated towards zero"
+
+ ^ self floor asInteger
+!
+
+truncateTo:aNumber
+ "return the receiver truncated to multiples of aNumber"
+
+ ^ ((self / aNumber) floor * aNumber) asInteger
+!
+
+rounded
+ "return the integer nearest the receiver"
+
+ ^ (self + 0.5) floor asInteger
+!
+
+roundTo:aNumber
+ "return the receiver rounded to multiples of aNumber"
+
+ ^ (self / aNumber) rounded * aNumber
+! !
+
+!ArithmeticValue methodsFor:'double dispatching'!
+
+sumFromInteger:anInteger
+ "the receiver does not know how to add an integer -
+ retry the operation by coercing to higher generality"
+
+ ^ anInteger retry:#+ coercing:self
+!
+
+sumFromFloat:aFloat
+ "the receiver does not know how to add a float -
+ retry the operation by coercing to higher generality"
+
+ ^ aFloat retry:#+ coercing:self
+!
+
+sumFromFraction:aFraction
+ "the receiver does not know how to add a fraction -
+ retry the operation by coercing to higher generality"
+
+ ^ aFraction retry:#+ coercing:self
+!
+
+differenceFromInteger:anInteger
+ "the receiver does not know how to subtract from an integer -
+ retry the operation by coercing to higher generality"
+
+ ^ anInteger retry:#- coercing:self
+!
+
+differenceFromFloat:aFloat
+ "the receiver does not know how to subtract from a float -
+ retry the operation by coercing to higher generality"
+
+ ^ aFloat retry:#- coercing:self
+!
+
+differenceFromFraction:aFraction
+ "the receiver does not know how to subtract from a fraction -
+ retry the operation by coercing to higher generality"
+
+ ^ aFraction retry:#- coercing:self
+!
+
+productFromInteger:anInteger
+ "the receiver does not know how to multiply an integer -
+ retry the operation by coercing to higher generality"
+
+ ^ anInteger retry:#* coercing:self
+!
+
+productFromFloat:aFloat
+ "the receiver does not know how to multiply a float -
+ retry the operation by coercing to higher generality"
+
+ ^ aFloat retry:#* coercing:self
+!
+
+productFromFraction:aFraction
+ "the receiver does not know how to multiply a fraction -
+ retry the operation by coercing to higher generality"
+
+ ^ aFraction retry:#* coercing:self
+!
+
+quotientFromInteger:anInteger
+ "the receiver does not know how to divide an integer -
+ retry the operation by coercing to higher generality"
+
+ ^ anInteger retry:#/ coercing:self
+!
+
+quotientFromFloat:aFloat
+ "the receiver does not know how to divide a float -
+ retry the operation by coercing to higher generality"
+
+ ^ aFloat retry:#/ coercing:self
+!
+
+quotientFromFraction:aFraction
+ "the receiver does not know how to divide a fraction -
+ retry the operation by coercing to higher generality"
+
+ ^ aFraction retry:#/ coercing:self
+!
+
+lessFromInteger:anInteger
+ "the receiver does not know how to compare to an integer -
+ retry the operation by coercing to higher generality"
+
+ ^ anInteger retry:#< coercing:self
+!
+
+lessFromFloat:aFloat
+ "the receiver does not know how to compare to a float -
+ retry the operation by coercing to higher generality"
+
+ ^ aFloat retry:#< coercing:self
+!
+
+lessFromFraction:aFraction
+ "the receiver does not know how to compare to a fraction -
+ retry the operation by coercing to higher generality"
+
+ ^ aFraction retry:#< coercing:self
+! !
+
+!ArithmeticValue methodsFor:'misc math'!
+
+squared
+ "return receiver * receiver"
+
+ ^ self * self
+!
+
+exp
+ "return e ^ receiver"
+
+ ^ self asFloat exp
+!
+
+ln
+ "return the natural logarithm of the receiver"
+
+ ^ self asFloat ln
+!
+
+log
+ "return log base 10 of the receiver"
+
+ ^ self log:10
+!
+
+log:aNumber
+ "return log base aNumber of the receiver"
+
+ ^ self ln / aNumber ln
+!
+
+sqrt
+ "return the square root of the receiver"
+
+ ^ self asFloat sqrt
+!
+
+floorLog:radix
+ "return the logarithm truncated as an integer"
+
+ ^ (self log:radix) floor
+!
+
+raisedTo:aNumber
+ "return the receiver raised to aNumber"
+
+ aNumber = 0 ifTrue:[^ 1].
+ aNumber = 1 ifTrue:[^ self].
+ aNumber isInteger ifTrue:[
+ ^ self raisedToInteger:aNumber
+ ].
+ ^ self asFloat raisedTo:aNumber
+!
+
+raisedToInteger:anInteger
+ "return the receiver raised to anInteger"
+
+ |count result|
+
+ result := self coerce:1.
+ count := anInteger abs.
+ count timesRepeat:[result := result * self].
+ (anInteger < 0) ifTrue:[
+ ^ 1 / result
+ ].
+ ^ result
+! !
+
+!ArithmeticValue methodsFor:'trigonometric'!
+
+sin
+ "return the sine of the receiver (interpreted as radians)"
+
+ ^ self asFloat sin
+!
+
+cos
+ "return the cosine of the receiver (interpreted as radians)"
+
+ ^ self asFloat cos
+!
+
+tan
+ "return the tangens of the receiver (interpreted as radians)"
+
+ ^ self asFloat tan
+!
+
+arcCos
+ "return the arccosine of the receiver (in radians)"
+
+ ^ self asFloat arcCos
+!
+
+arcSin
+ "return the arcsine of the receiver (in radians)"
+
+ ^ self asFloat arcSin
+!
+
+arcTan
+ "return the arctangens of the receiver (in radians)"
+
+ ^ self asFloat arcTan
+! !
+
+!ArithmeticValue methodsFor:'error handling'!
+
+divideByZeroError
+ "report a division by zero error"
+
+ DivisionByZeroSignal raise
+ "self error:'division by zero'"
+! !
+
+!ArithmeticValue methodsFor:'testing'!
+
+negative
+ "return true, if the receiver is < 0"
+
+ " this would lead to infinite recursion ...
+ ^ (self < 0)
+ "
+ ^ self subclassResponsibility
+!
+
+positive
+ "return true, if the receiver is >= 0"
+
+ ^ self negative not
+!
+
+strictlyPositive
+ "return true, if the receiver is > 0"
+
+ ^ (self > 0)
+!
+
+sign
+ "return the sign of the receiver"
+
+ (self < 0) ifTrue:[^ -1].
+ (self > 0) ifTrue:[^ 1].
+ ^ 0
+!
+
+even
+ "return true if the receiver is divisible by 2"
+
+ ^ self truncated asInteger even
+!
+
+odd
+ "return true if the receiver is not divisible by 2"
+
+ ^ self even not
+!
+
+denominator
+ "return the denominator of the receiver"
+
+ ^ 1
+!
+
+numerator
+ "return the numerator of the receiver."
+
+ ^ self
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ArrColl.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,181 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#ArrayedCollection
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Abstract'
+!
+
+ArrayedCollection comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ArrayedCollections are collections where the elements can be accessed via an integer index.
+
+%W% %E%
+written spring 89 by claus
+'!
+
+!ArrayedCollection class methodsFor:'instance creation'!
+
+with:element
+ "return a new SequenceableCollection with one element:anObject"
+
+ |newCollection|
+
+ newCollection := self new:1.
+ newCollection at:1 put:element.
+ ^newCollection
+!
+
+with:first with:second
+ "return a new SequenceableCollection with two elements"
+
+ |newCollection|
+
+ newCollection := self new:2.
+ newCollection at:1 put:first.
+ newCollection at:2 put:second.
+ ^newCollection
+!
+
+with:first with:second with:third
+ "return a new SequenceableCollection with three elements"
+
+ |newCollection|
+
+ newCollection := self new:3.
+ newCollection at:1 put:first.
+ newCollection at:2 put:second.
+ newCollection at:3 put:third.
+ ^newCollection
+!
+
+with:first with:second with:third with:forth
+ "return a new SequenceableCollection with four elements"
+
+ |newCollection|
+
+ newCollection := self new:4.
+ newCollection at:1 put:first.
+ newCollection at:2 put:second.
+ newCollection at:3 put:third.
+ newCollection at:4 put:forth.
+ ^newCollection
+!
+
+with:one with:two with:three with:four with:five
+ "return a new SequenceableCollection with five elements"
+
+ |newCollection|
+
+ newCollection := self new:5.
+ newCollection at:1 put:one.
+ newCollection at:2 put:two.
+ newCollection at:3 put:three.
+ newCollection at:4 put:four.
+ newCollection at:5 put:five.
+ ^newCollection
+!
+
+withAll:aCollection
+ "return a new Collection with all elements taken from the argument,
+ aCollection"
+
+ |newCollection newSize
+ index "{ Class: SmallInteger }" |
+
+ newSize := aCollection size.
+ newCollection := self new:newSize.
+ (aCollection isKindOf:SequenceableCollection) ifTrue:[
+ "aCollection has indexed elements"
+ newCollection replaceFrom:1 to:newSize with:aCollection startingAt:1
+ ] ifFalse:[
+ "must enumerate the elements"
+ index := 1.
+ aCollection do:[:element |
+ newCollection at:index put:element.
+ index := index + 1
+ ]
+ ].
+ ^ newCollection
+! !
+
+!ArrayedCollection methodsFor:'error handling'!
+
+indexMustBeInteger
+ "report an error that index must be Integer"
+
+ ^ self error:'index must be integer'
+!
+
+indexOutOfRange:theIndex
+ "report an error that index is out of range"
+
+ ^ self error:'index is out of range'
+!
+
+fixedSizeError
+ "report an error that size of the collection cannot be changed"
+
+ ^ self error:'cannot change size'
+! !
+
+!ArrayedCollection methodsFor:'accessing'!
+
+size
+ "return the ArrayedCollections size - redefined since SequenceableCollection
+ does it in a slow way"
+
+ ^ self basicSize
+!
+
+at:index
+ "return the index's element of the collection"
+
+ ^ self basicAt:index
+!
+
+at:index put:anObject
+ "put the argument as index's element into the collection"
+
+ ^ self basicAt:index put:anObject
+! !
+
+!ArrayedCollection methodsFor:'storing'!
+
+storeOn:aStream
+ "output a printed representation (which can be re-read)
+ onto the argument aStream"
+
+ |index "{ Class: SmallInteger }"|
+
+ aStream nextPutAll:'('.
+ aStream nextPutAll:self class name.
+ aStream nextPutAll:' new:'.
+ self size printOn:aStream.
+ aStream nextPutAll:')'.
+ index := 1.
+ self do:[:element |
+ aStream nextPutAll:' at:'.
+ index printOn:aStream.
+ aStream nextPutAll:' put:'.
+ element storeOn:aStream.
+ aStream nextPut:$;.
+ index := index + 1
+ ].
+ index > 1 ifTrue:[aStream nextPutAll:' yourself'].
+ aStream nextPut:$)
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Array.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,626 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ArrayedCollection variableSubclass:#Array
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Indexed'
+!
+
+Array comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+Arrays store general objects; the size is fixed, so add/remove is not
+allowed. Access to the elements is via an Integer index. Since Arrays
+are used very often in the system, some methods have been tuned by
+reimplementation as primitive.
+
+%W% %E%
+
+written spring 89 by claus
+'!
+
+!Array methodsFor:'resizing'!
+
+grow:newSize
+ (newSize ~~ self size) ifTrue:[
+ self fixedSizeError
+ ]
+! !
+
+!Array methodsFor:'accessing'!
+
+size
+ "return the number of indexed elements in the receiver"
+
+%{ /* NOCONTEXT */
+
+ RETURN ( _MKSMALLINT(_arraySize(self) - _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) ));
+%}
+!
+
+at:index
+ "return the indexed instance variable with index, anInteger
+ - added here for speed"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int indx;
+ REGISTER int nIndex;
+
+ if (_isSmallInteger(index)) {
+ indx = _intVal(index) - 1;
+ if (indx >= 0) {
+ nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+ indx += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+ if (indx < nIndex) {
+ RETURN ( _InstPtr(self)->i_instvars[indx] );
+ }
+ }
+ }
+%}
+.
+ ^ super at:index
+!
+
+at:index put:anObject
+ "store the 2nd arg, anObject as indexed instvar with index, anInteger.
+ - added here for speed"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int indx;
+ REGISTER int nIndex;
+
+ if (_isSmallInteger(index)) {
+ indx = _intVal(index) - 1;
+ if (indx >= 0) {
+ nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+ indx += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+ if (indx < nIndex) {
+ _InstPtr(self)->i_instvars[indx] = anObject;
+ __STORE(self, anObject);
+ RETURN ( anObject );
+ }
+ }
+ }
+%}
+.
+ ^ super at:index put:anObject
+! !
+
+!Array methodsFor:'copying'!
+
+copyWith:something
+ "reimplemented for speed if receiver is an Array"
+%{
+ OBJ nObj;
+ int mySize;
+ int i, nIndex;
+ OBJ *op;
+ extern int newSpace;
+
+ if (_qClass(self) == Array) {
+ mySize = _qSize(self);
+ _qAlignedNew(nObj, mySize + sizeof(OBJ), __context);
+ _InstPtr(nObj)->o_class = Array;
+
+ nIndex = (mySize - OHDR_SIZE) / sizeof(OBJ);
+ /* created object is usually in newspace */
+ if (_qSpace(nObj) == newSpace) {
+ /* dont care for store */
+#ifdef bcopy4
+ bcopy4(_ArrayInstPtr(self)->a_element, _ArrayInstPtr(nObj)->a_element, nIndex);
+#else
+ bcopy(_ArrayInstPtr(self)->a_element, _ArrayInstPtr(nObj)->a_element, mySize - OHDR_SIZE);
+#endif
+ _ArrayInstPtr(nObj)->a_element[nIndex] = something;
+ } else {
+ /* must take care of stores ... */
+ op = _ArrayInstPtr(self)->a_element;
+ for (i=0; i<nIndex; i++) {
+ _ArrayInstPtr(nObj)->a_element[i] = *op;
+ __STORE(nObj, *op);
+ op++;
+ }
+ _ArrayInstPtr(nObj)->a_element[i] = something;
+ __STORE(nObj, something);
+ }
+ RETURN ( nObj );
+ }
+%}
+.
+ ^ super copyWith:something
+! !
+
+!Array methodsFor:'filling & replacing'!
+
+from:index1 to:index2 put:anObject
+ "reimplemented for speed if receiver is an Array"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int index;
+ int nIndex;
+ int endIndex;
+ REGISTER OBJ *dst;
+
+ if ((_qClass(self) == Array)
+ && _isSmallInteger(index1)
+ && _isSmallInteger(index2)) {
+ index = _intVal(index1) - 1;
+ if (index >= 0) {
+ nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+ endIndex = _intVal(index2) - 1;
+ if (endIndex < nIndex) {
+ dst = &(_InstPtr(self)->i_instvars[index]);
+#ifdef memset4
+ memset4(dst, anObject, (endIndex-index+1));
+ __STORE(self, anObject);
+#else
+ if ((INT)anObject == 0) {
+ memset(dst, 0, (endIndex-index+1) * sizeof(OBJ));
+ } else {
+ for (; index <= endIndex; index++) {
+ *dst++ = anObject;
+ }
+ __STORE(self, anObject);
+ }
+#endif
+ RETURN ( self );
+ }
+ }
+ }
+%}
+.
+ ^ super from:index1 to:index2 put:anObject
+!
+
+replaceFrom:start to:stop with:aCollection startingAt:repStart
+ "reimplemented for speed if both receiver and aCollection are Arrays"
+
+%{ /* NOCONTEXT */
+
+ int nIndex, repNIndex;
+ int startIndex, stopIndex;
+ REGISTER OBJ *src;
+ REGISTER OBJ *dst;
+ int repStopIndex;
+ REGISTER int repStartIndex;
+ REGISTER OBJ t;
+ REGISTER int count;
+ extern int newSpace;
+
+ if ((_qClass(self) == Array)
+ && (_Class(aCollection) == Array)
+ && _isSmallInteger(start)
+ && _isSmallInteger(stop)
+ && _isSmallInteger(repStart)) {
+ startIndex = _intVal(start) - 1;
+ if (startIndex >= 0) {
+ nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+ stopIndex = _intVal(stop) - 1;
+ count = stopIndex - startIndex + 1;
+ if ((count > 0) && (stopIndex < nIndex)) {
+ repStartIndex = _intVal(repStart) - 1;
+ if (repStartIndex >= 0) {
+ repNIndex = (_qSize(aCollection) - OHDR_SIZE) / sizeof(OBJ);
+ repStopIndex = repStartIndex + (stopIndex - startIndex);
+ if (repStopIndex < repNIndex) {
+ src = &(_InstPtr(aCollection)->i_instvars[repStartIndex]);
+ dst = &(_InstPtr(self)->i_instvars[startIndex]);
+ if (aCollection == self) {
+ /* no need to check stores */
+ /* take care of overlapping copy */
+ if (src < dst) {
+ /* must do a reverse copy */
+ src += count;
+ dst += count;
+ while (count-- > 0) {
+ *--dst = *--src;
+ }
+ RETURN ( self );
+ }
+#ifdef bcopy4
+ bcopy4(src, dst, count);
+#else
+# ifdef FAST_MEMCPY
+ bcopy(src, dst, count*sizeof(OBJ));
+# else
+ while (count >= 4) {
+ MOVE4LONGS(src, dst);
+ count -= 4;
+ }
+ while (count--) {
+ *dst++ = *src++;
+ }
+# endif
+#endif
+ } else {
+ /*
+ * no need for store-check, if dst is in newspace
+ */
+ if (_qSpace(self) == newSpace) {
+#ifdef bcopy4
+ bcopy4(src, dst, count);
+#else
+# ifdef FAST_MEMCPY
+ bcopy(src, dst, count*sizeof(OBJ));
+# else
+ while (count >= 4) {
+ MOVE4LONGS(src, dst);
+ count -= 4;
+ }
+ while (count--) {
+ *dst++ = *src++;
+ }
+# endif
+#endif
+ } else {
+ while (count-- > 0) {
+ t = *src++;
+ *dst++ = t;
+ __STORE(self, t);
+ }
+ }
+ }
+ RETURN ( self );
+ }
+ }
+ }
+ }
+ }
+%}
+.
+ ^ super replaceFrom:start to:stop with:aCollection startingAt:repStart
+! !
+
+!Array methodsFor:'testing'!
+
+includes:anObject
+ "return true, if the argument, anObject is contained in the array
+ - reimplemented for speed"
+
+ |element|
+
+%{ /* NOCONTEXT */
+
+ REGISTER int index;
+ REGISTER OBJ o;
+ int nIndex;
+
+ nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+ index = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+
+ /* quick check using == */
+ o = anObject;
+ while (index < nIndex) {
+ if (_InstPtr(self)->i_instvars[index++] == o) {
+ RETURN ( true );
+ }
+ }
+ if (o == nil) {
+ RETURN ( false );
+ }
+%}
+.
+%{
+ REGISTER int index;
+ int nIndex;
+ extern OBJ __eq;
+ static struct inlineCache eq = _ILC1;
+
+ nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+ index = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+
+ /* slow check using = */
+
+ while (index < nIndex) {
+ element = _InstPtr(self)->i_instvars[index++];
+ if (element != nil) {
+#ifdef PASS_ARG_REF
+ if ((*eq.ilc_func)(anObject,__eq, CON_COMMA nil,&eq,&element)==true)
+#else
+ if ((*eq.ilc_func)(anObject,__eq, CON_COMMA nil,&eq,element)==true)
+#endif
+ {
+ RETURN ( true );
+ }
+ }
+ }
+%}
+.
+ ^ false
+!
+
+indexOf:anElement startingAt:start
+ "search the array for anElement; return index if found, 0 otherwise
+ - reimplemented for speed"
+
+ |element|
+%{
+ REGISTER int index;
+ int nIndex, nInsts;
+ extern OBJ __eq;
+ static struct inlineCache eq = _ILC1;
+
+ if (_isSmallInteger(start)) {
+ index = _intVal(start) - 1;
+ if (index >= 0) {
+ nInsts = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+ index += nInsts;
+ nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+ if (anElement != nil) {
+ while (index < nIndex) {
+ element = _InstPtr(self)->i_instvars[index++];
+ if (element != nil) {
+ if ((element == anElement)
+#ifdef PASS_ARG_REF
+ || ((*eq.ilc_func)(anElement,__eq, CON_COMMA nil,&eq,&element)
+#else
+ || ((*eq.ilc_func)(anElement,__eq, CON_COMMA nil,&eq,element)
+#endif
+ == true)) {
+ RETURN ( _MKSMALLINT(index - nInsts) );
+ }
+ }
+ }
+ } else {
+ /* search for nil */
+ while (index < nIndex) {
+ if (_InstPtr(self)->i_instvars[index++] == nil) {
+ RETURN ( _MKSMALLINT(index - nInsts) );
+ }
+ }
+ }
+ }
+ }
+%}
+.
+ ^ 0
+!
+
+identityIndexOf:anElement startingAt:start
+ "search the array for anElement; return index if found, 0 otherwise
+ - reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int index;
+ REGISTER OBJ el;
+ REGISTER OBJ *op;
+ REGISTER int nIndex;
+ int nInsts;
+
+ if (_isSmallInteger(start)) {
+ index = _intVal(start) - 1;
+ if (index >= 0) {
+ nInsts = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+ index += nInsts;
+ nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+ el = anElement;
+ op = & (_InstPtr(self)->i_instvars[index]);
+ while (index++ < nIndex) {
+ if (*op++ == el) {
+ RETURN ( _MKSMALLINT(index - nInsts) );
+ }
+ }
+ RETURN ( _MKSMALLINT(0) );
+ }
+ }
+%}
+.
+ ^ super identityIndexOf:anElement startingAt:start
+! !
+
+!Array methodsFor:'enumeration'!
+
+do:aBlock
+ "evaluate the argument, aBlock for each element in the collection.
+ - reimplemented for speed"
+
+ |home element|
+%{
+ REGISTER OBJFUNC codeVal;
+ REGISTER int index;
+ int nIndex;
+ extern OBJ _value_, Block;
+ static struct inlineCache val = _ILC1;
+
+ index = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+ nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+ if (_isBlock(aBlock)
+ && ((codeVal = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+ && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+ home = _BlockInstPtr(aBlock)->b_home;
+ for (; index < nIndex; index++) {
+ if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);
+
+ element = _InstPtr(self)->i_instvars[index];
+#ifdef PASS_ARG_REF
+ (*codeVal)(home, CON_COMMA &element);
+#else
+ (*codeVal)(home, CON_COMMA element);
+#endif
+ }
+ } else {
+ for (; index < nIndex; index++) {
+ if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);
+
+ element = _InstPtr(self)->i_instvars[index];
+#ifdef PASS_ARG_REF
+ (*val.ilc_func)(aBlock, _value_, CON_COMMA nil, &val, &element);
+#else
+ (*val.ilc_func)(aBlock, _value_, CON_COMMA nil, &val, element);
+#endif
+ }
+ }
+%}
+.
+ ^ self
+!
+
+reverseDo:aBlock
+ "evaluate the argument, aBlock for each element in the collection in reverse order.
+ - reimplemented for speed"
+
+ |home element|
+%{
+ REGISTER OBJFUNC codeVal;
+ REGISTER int index;
+ int nIndex, endIndex;
+ extern OBJ _value_, Block;
+ static struct inlineCache val = _ILC1;
+
+ endIndex = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+ nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+ if (_isBlock(aBlock)
+ && ((codeVal = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+ && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+ home = _BlockInstPtr(aBlock)->b_home;
+ for (index=nIndex-1; index >= endIndex; index--) {
+ if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);
+
+ element = _InstPtr(self)->i_instvars[index];
+#ifdef PASS_ARG_REF
+ (*codeVal)(home, CON_COMMA &element);
+#else
+ (*codeVal)(home, CON_COMMA element);
+#endif
+ }
+ } else {
+ for (index=nIndex=1; index >= endIndex; index--) {
+ if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);
+
+ element = _InstPtr(self)->i_instvars[index];
+#ifdef PASS_ARG_REF
+ (*val.ilc_func)(aBlock, _value_, CON_COMMA nil, &val, &element);
+#else
+ (*val.ilc_func)(aBlock, _value_, CON_COMMA nil, &val, element);
+#endif
+ }
+ }
+%}
+.
+ ^ self
+!
+
+from:start to:stop do:aBlock
+ "evaluate the argument, aBlock for the elements starting at index start
+ up to (and including) stop in the collection.
+ - reimplemented for speed"
+
+ |home element|
+%{
+ REGISTER OBJFUNC codeVal;
+ REGISTER int index;
+ int nIndex, nInsts;
+ extern OBJ _value_, Block;
+ static struct inlineCache val = _ILC1;
+ int indexLow, indexHigh;
+
+ if (_isSmallInteger(start) && _isSmallInteger(stop)) {
+ indexLow = _intVal(start);
+ if (indexLow > 0) {
+ indexHigh = _intVal(stop);
+ nInsts = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+ indexLow += nInsts;
+ indexHigh += nInsts;
+ nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+ if (indexHigh <= nIndex) {
+ indexLow--;
+ indexHigh--;
+ if (_isBlock(aBlock)
+ && ((codeVal = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+ && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+ home = _BlockInstPtr(aBlock)->b_home;
+ for (index=indexLow; index <= indexHigh; index++) {
+ if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);
+
+#ifdef PASS_ARG_REF
+ element = _InstPtr(self)->i_instvars[index];
+ (*codeVal)(home, CON_COMMA &element);
+#else
+ (*codeVal)(home, CON_COMMA _InstPtr(self)->i_instvars[index]);
+#endif
+ }
+ } else {
+ for (index=indexLow; index <= indexHigh; index++) {
+ if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);
+
+ element = _InstPtr(self)->i_instvars[index];
+#ifdef PASS_ARG_REF
+ (*val.ilc_func)
+ (aBlock, _value_, CON_COMMA nil, &val, &element);
+#else
+ (*val.ilc_func)
+ (aBlock, _value_, CON_COMMA nil, &val, element);
+#endif
+ }
+ }
+ RETURN ( self );
+ }
+ }
+ }
+%}
+.
+ ^ super from:start to:stop do:aBlock
+!
+
+nonNilElementsDo:aBlock
+ "evaluate the argument, aBlock for each non-nil element"
+
+ |home element|
+%{
+ REGISTER OBJFUNC codeVal;
+ REGISTER int index;
+ int nIndex;
+ extern OBJ _value_, Block;
+ static struct inlineCache val = _ILC1;
+
+ index = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+ nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+ if (_isBlock(aBlock)
+ && ((codeVal = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+ && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+ home = _BlockInstPtr(aBlock)->b_home;
+ for (; index < nIndex; index++) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ element = _InstPtr(self)->i_instvars[index];
+ if (element != nil)
+#ifdef PASS_ARG_REF
+ (*codeVal)(home, CON_COMMA &element);
+#else
+ (*codeVal)(home, CON_COMMA element);
+#endif
+ }
+ } else {
+ for (; index < nIndex; index++) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ element = _InstPtr(self)->i_instvars[index];
+ if (element != nil)
+#ifdef PASS_ARG_REF
+ (*val.ilc_func)(aBlock, _value_, CON_COMMA nil, &val, &element);
+#else
+ (*val.ilc_func)(aBlock, _value_, CON_COMMA nil, &val, element);
+#endif
+ }
+ }
+%}
+.
+ ^ self
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ArrayedCollection.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,181 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#ArrayedCollection
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Abstract'
+!
+
+ArrayedCollection comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ArrayedCollections are collections where the elements can be accessed via an integer index.
+
+%W% %E%
+written spring 89 by claus
+'!
+
+!ArrayedCollection class methodsFor:'instance creation'!
+
+with:element
+ "return a new SequenceableCollection with one element:anObject"
+
+ |newCollection|
+
+ newCollection := self new:1.
+ newCollection at:1 put:element.
+ ^newCollection
+!
+
+with:first with:second
+ "return a new SequenceableCollection with two elements"
+
+ |newCollection|
+
+ newCollection := self new:2.
+ newCollection at:1 put:first.
+ newCollection at:2 put:second.
+ ^newCollection
+!
+
+with:first with:second with:third
+ "return a new SequenceableCollection with three elements"
+
+ |newCollection|
+
+ newCollection := self new:3.
+ newCollection at:1 put:first.
+ newCollection at:2 put:second.
+ newCollection at:3 put:third.
+ ^newCollection
+!
+
+with:first with:second with:third with:forth
+ "return a new SequenceableCollection with four elements"
+
+ |newCollection|
+
+ newCollection := self new:4.
+ newCollection at:1 put:first.
+ newCollection at:2 put:second.
+ newCollection at:3 put:third.
+ newCollection at:4 put:forth.
+ ^newCollection
+!
+
+with:one with:two with:three with:four with:five
+ "return a new SequenceableCollection with five elements"
+
+ |newCollection|
+
+ newCollection := self new:5.
+ newCollection at:1 put:one.
+ newCollection at:2 put:two.
+ newCollection at:3 put:three.
+ newCollection at:4 put:four.
+ newCollection at:5 put:five.
+ ^newCollection
+!
+
+withAll:aCollection
+ "return a new Collection with all elements taken from the argument,
+ aCollection"
+
+ |newCollection newSize
+ index "{ Class: SmallInteger }" |
+
+ newSize := aCollection size.
+ newCollection := self new:newSize.
+ (aCollection isKindOf:SequenceableCollection) ifTrue:[
+ "aCollection has indexed elements"
+ newCollection replaceFrom:1 to:newSize with:aCollection startingAt:1
+ ] ifFalse:[
+ "must enumerate the elements"
+ index := 1.
+ aCollection do:[:element |
+ newCollection at:index put:element.
+ index := index + 1
+ ]
+ ].
+ ^ newCollection
+! !
+
+!ArrayedCollection methodsFor:'error handling'!
+
+indexMustBeInteger
+ "report an error that index must be Integer"
+
+ ^ self error:'index must be integer'
+!
+
+indexOutOfRange:theIndex
+ "report an error that index is out of range"
+
+ ^ self error:'index is out of range'
+!
+
+fixedSizeError
+ "report an error that size of the collection cannot be changed"
+
+ ^ self error:'cannot change size'
+! !
+
+!ArrayedCollection methodsFor:'accessing'!
+
+size
+ "return the ArrayedCollections size - redefined since SequenceableCollection
+ does it in a slow way"
+
+ ^ self basicSize
+!
+
+at:index
+ "return the index's element of the collection"
+
+ ^ self basicAt:index
+!
+
+at:index put:anObject
+ "put the argument as index's element into the collection"
+
+ ^ self basicAt:index put:anObject
+! !
+
+!ArrayedCollection methodsFor:'storing'!
+
+storeOn:aStream
+ "output a printed representation (which can be re-read)
+ onto the argument aStream"
+
+ |index "{ Class: SmallInteger }"|
+
+ aStream nextPutAll:'('.
+ aStream nextPutAll:self class name.
+ aStream nextPutAll:' new:'.
+ self size printOn:aStream.
+ aStream nextPutAll:')'.
+ index := 1.
+ self do:[:element |
+ aStream nextPutAll:' at:'.
+ index printOn:aStream.
+ aStream nextPutAll:' put:'.
+ element storeOn:aStream.
+ aStream nextPut:$;.
+ index := index + 1
+ ].
+ index > 1 ifTrue:[aStream nextPutAll:' yourself'].
+ aStream nextPut:$)
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Assoc.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,112 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Association
+ instanceVariableNames:'key value'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Support'
+!
+
+Association comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+Associations are a key-value pair; they are the elements of Dictionaries -
+storing value under the key. By itself, an Association is not very
+useful.
+
+Instance variables:
+
+key <Object> the key
+value <Object> the value
+
+%W% %E%
+'!
+
+!Association class methodsFor:'instance creation'!
+
+key:aKey
+ "return a new Association."
+
+ ^ self basicNew key:aKey
+!
+
+key:aKey value:aValue
+ "return a new Association"
+
+ ^ self basicNew key:aKey value:aValue
+! !
+
+!Association methodsFor:'accessing'!
+
+key
+ "return the key of the association"
+
+ ^ key
+!
+
+key:anObject
+ "set the key of the receiver to be anObject.
+ Return the receiver"
+
+ key := anObject
+!
+
+value
+ "return the value of the association"
+
+ ^ value
+!
+
+value:anObject
+ "set the value of the receiver to be anObject.
+ Return the receiver"
+
+ value := anObject
+!
+
+key:aKey value:aValue
+ "set both the key and value of the receiver.
+ Return the receiver"
+
+ key := aKey.
+ value := aValue
+! !
+
+!Association methodsFor:'comparing'!
+
+= anAssociation
+ (anAssociation isKindOf:Association) ifTrue:[
+ (anAssociation key = key) ifTrue:[
+ ^ anAssociation value = value
+ ]
+ ].
+ ^ false
+! !
+
+!Association methodsFor:'printing & storing'!
+
+printString
+ "return a string containing a printable representation
+ of the receiver"
+
+ ^ key printString , '->' , value printString
+!
+
+displayString
+ "return a string containing a printable representation
+ of the receiver for displaying"
+
+ ^ key displayString , '->' , value displayString
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Association.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,112 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Association
+ instanceVariableNames:'key value'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Support'
+!
+
+Association comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+Associations are a key-value pair; they are the elements of Dictionaries -
+storing value under the key. By itself, an Association is not very
+useful.
+
+Instance variables:
+
+key <Object> the key
+value <Object> the value
+
+%W% %E%
+'!
+
+!Association class methodsFor:'instance creation'!
+
+key:aKey
+ "return a new Association."
+
+ ^ self basicNew key:aKey
+!
+
+key:aKey value:aValue
+ "return a new Association"
+
+ ^ self basicNew key:aKey value:aValue
+! !
+
+!Association methodsFor:'accessing'!
+
+key
+ "return the key of the association"
+
+ ^ key
+!
+
+key:anObject
+ "set the key of the receiver to be anObject.
+ Return the receiver"
+
+ key := anObject
+!
+
+value
+ "return the value of the association"
+
+ ^ value
+!
+
+value:anObject
+ "set the value of the receiver to be anObject.
+ Return the receiver"
+
+ value := anObject
+!
+
+key:aKey value:aValue
+ "set both the key and value of the receiver.
+ Return the receiver"
+
+ key := aKey.
+ value := aValue
+! !
+
+!Association methodsFor:'comparing'!
+
+= anAssociation
+ (anAssociation isKindOf:Association) ifTrue:[
+ (anAssociation key = key) ifTrue:[
+ ^ anAssociation value = value
+ ]
+ ].
+ ^ false
+! !
+
+!Association methodsFor:'printing & storing'!
+
+printString
+ "return a string containing a printable representation
+ of the receiver"
+
+ ^ key printString , '->' , value printString
+!
+
+displayString
+ "return a string containing a printable representation
+ of the receiver for displaying"
+
+ ^ key displayString , '->' , value displayString
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Autoload.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,121 @@
+"
+ COPYRIGHT (c) 1991-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Autoload
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Classes'
+!
+
+Autoload comment:'
+
+COPYRIGHT (c) 1991-93 by Claus Gittinger
+ All Rights Reserved
+
+In memory limited systems (as my 8Mb 386 is) all seldom-used classes are made
+subclasses of this class. Autoload catches all messages and
+files-In the corresponding code when first used. Then the cought message
+is resent to the (now existing) class.
+
+%W% %E%
+written fall 91 by claus
+'!
+
+!Autoload class methodsFor:'loading'!
+
+autoload
+ "use this to force loading
+ - it is defined a noop in all non-autoloading clases"
+
+ |mySelf myName mySym newClass|
+
+ mySelf := self.
+ myName := self name.
+ mySym := myName asSymbol.
+
+ "remove myself - to avoid recompilation"
+ Smalltalk at:mySym put:nil.
+
+ "load"
+ Transcript showCr:('autoloading ', myName , ' ...').
+
+ Smalltalk fileInClass:myName.
+
+ "did it work ?"
+ newClass := Smalltalk at:mySym.
+ Smalltalk at:mySym put:mySelf. "will be undone by become:"
+
+ newClass isNil ifTrue:[
+ "no - reinstall myself"
+ self warn:('autoload of ' , myName , ' failed').
+ ^ nil
+ ].
+
+ "wow - it did"
+
+ self become:newClass.
+ ^ self
+! !
+
+!Autoload class methodsFor:'message catching'!
+
+doesNotUnderstand:aMessage
+ "cought a message; load class and retry"
+
+ |newClass|
+
+ newClass := self autoload.
+ newClass notNil ifTrue:[
+ ^ newClass perform:(aMessage selector)
+ withArguments:(aMessage arguments)
+ ].
+ super doesNotUnderstand:aMessage
+!
+
+new
+ "catch new"
+
+ ^ self doesNotUnderstand:(Message selector:#new)
+!
+
+basicNew
+ "catch basicNew"
+
+ ^ self doesNotUnderstand:(Message selector:#basicew)
+!
+
+new:arg
+ "catch new:"
+
+ ^ self doesNotUnderstand:(Message selector:#new: with:arg)
+!
+
+basicNew:arg
+ "catch basicNew:"
+
+ ^ self doesNotUnderstand:(Message selector:#basicNew: with:arg)
+!
+
+subclass:a1 instanceVariableNames:a2 classVariableNames:a3 poolDictionaries:a4 category:a5
+ |newClass|
+
+ (self == Autoload) ifTrue:[
+ ^ super subclass:a1 instanceVariableNames:a2 classVariableNames:a3 poolDictionaries:a4 category:a5
+ ].
+ newClass := self autoload.
+ newClass notNil ifTrue:[
+ ^ newClass perform:(thisContext selector)
+ withArguments:(thisContext args)
+ ].
+ ^ nil
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BContext.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,106 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Context subclass:#BlockContext
+ instanceVariableNames:'' "do not add instvars here"
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Methods'
+!
+
+BlockContext comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+BlockContexts represent the stack context objects of blocks. The layout is the same
+as for other contexts - this class has been added to avoid a flag in an instance variable.
+(has become necessary with cheap blocks, which have no home).
+
+Warning: layout and size known by compiler and runtime system - do not change.
+
+%W% %E%
+'!
+
+!BlockContext methodsFor:'accessing'!
+
+isBlockContext
+ "return true, iff the receiver is a BlockContext, false otherwise"
+
+ ^ true
+!
+
+methodHome
+ "return the method-home for block contexts"
+
+ |con h|
+
+ home isNil ifTrue:[^ nil]. "XXX will change soon"
+ home isContext ifFalse:[^ nil]. "copying blocks have no method home"
+
+ con := self.
+ h := home.
+ [h notNil] whileTrue:[
+ con := h.
+ h := con home
+ ].
+ ^ con
+!
+
+home
+ "return the immediate home of the receiver.
+ normally this is the methodcontext, where the block was created,
+ for nested block contexts, this is the surrounding blocks context."
+
+ home isContext ifFalse:[^ nil]. "copying blocks have no home"
+ ^ home
+!
+
+selector
+ "return the selector of the context - which is one of the value
+ selectors"
+
+ |nargs|
+
+ nargs := self nargs.
+ (nargs == 0) ifTrue:[^ #value].
+ (nargs == 1) ifTrue:[^ #value:].
+ (nargs == 2) ifTrue:[^ #value:value:].
+ (nargs == 3) ifTrue:[^ #value:value:value:].
+ (nargs == 4) ifTrue:[^ #value:value:value:value:].
+ (nargs == 5) ifTrue:[^ #value:value:value:value:value:].
+ ^ nil
+! !
+
+!BlockContext methodsFor:'printing'!
+
+receiverPrintString
+ home isNil ifTrue:[
+ ^ '[] optimized'
+ ].
+ home isContext ifFalse:[
+ "a copying block"
+
+ "receiverClassName := home selfValue class name."
+ ^ '[] optimized'
+ ].
+
+ ^ '[] in ' , receiver class name , '-' , self methodHome selector
+!
+
+printReceiver
+ self receiverPrintString print
+!
+
+printString
+ ^ self receiverPrintString , ' ' , self selector printString
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Bag.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,144 @@
+"
+ COPYRIGHT (c) 1991-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#Bag
+ instanceVariableNames:'contents'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Unordered'
+!
+
+Bag comment:'
+
+COPYRIGHT (c) 1991-93 by Claus Gittinger
+ All Rights Reserved
+
+Bag implements collections whose elements are unordered and have no
+external keys. Elements may occur more than once.
+
+Instance variables:
+
+contents <Dictionary> for each element, the number of occurrences
+
+%W% %E%
+written jun 91 by claus
+'!
+
+!Bag class methodsFor:'instance creation'!
+
+new
+ "return a new empty Bag"
+
+ ^ super new initContents
+!
+
+new:size
+ "return a new empty Bag with initial space for size elements"
+
+ ^ super new initContents:size
+! !
+
+!Bag methodsFor:'private'!
+
+initContents
+ "set the contents to be an empty Dictionary"
+
+ contents := Dictionary new
+!
+
+initContents:size
+ "set the contents to be an empty Dictionary with initial size"
+
+ contents := Dictionary new:size
+! !
+
+!Bag methodsFor:'accessing'!
+
+at:index
+ "report an error: at: is not allowed for Bags"
+
+ ^ self errorNotKeyed
+!
+
+at:index put:anObject
+ "report an error: at:put: is not allowed for Bags"
+
+ ^ self errorNotKeyed
+! !
+
+!Bag methodsFor:'testing'!
+
+size
+ "return the number of bag elements"
+
+ |count|
+
+ count := 0.
+ contents do:[:element | count := count + element].
+ ^ count
+!
+
+occurrencesOf:anObject
+ "return how many times anObject is in the receiver"
+
+ ^ contents at:anObject ifAbsent:[0]
+!
+
+includes:anObject
+ "return true, if anObject is in the receiver"
+
+ ^ contents includesKey:anObject
+! !
+
+!Bag methodsFor:'adding & removing'!
+
+add:anObject
+ "add the argument, anObject to the receiver"
+
+ ^ self add:anObject withOccurences:1
+!
+
+add:newObject withOccurences:anInteger
+ "add the argument, anObject anInteger times to the receiver"
+
+ contents at:newObject
+ put:(self occurrencesOf:newObject) + anInteger.
+ ^ newObject
+!
+
+remove:oldObject ifAbsent:anExceptionBlock
+ "Remove oldObject from the collection and return it
+ - if it was not present, return the value of the exceptionBlock."
+
+ |count|
+
+ count := self occurrencesOf:oldObject.
+ (count == 0) ifTrue:[^ anExceptionBlock value].
+ (count == 1) ifTrue:[
+ contents removeKey:oldObject
+ ] ifFalse:[
+ contents at:oldObject put:(count - 1)
+ ].
+ ^ oldObject
+! !
+
+!Bag methodsFor:'enumerating'!
+
+do:aBlock
+ "Perform the block for all members in the collection."
+
+ contents associationsDo:[:assoc |
+ assoc value timesRepeat:[
+ aBlock value:(assoc key)
+ ]
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Behavior.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,733 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Behavior
+ instanceVariableNames:'superclass selectors methods
+ instSize flags'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Classes'
+!
+
+Behavior comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+this class provides minimum support for all classes - additional stuff is
+found in Class; Behaviors stuff has been extracted to make generation of
+compact binaries possible.
+(these do not need all of the functionality in Class)
+
+Instance variables:
+
+superclass <Class> the classes superclass
+selectors <Array> the selectors for which inst-methods are defined here
+methods <Array> the inst-methods corresponding to the selectors
+instSize <SmallInteger> the number of instance variables
+flags <SmallInteger> special flag bits coded in a number
+
+NOTICE: layout known by compiler and runtime system; be careful when changing
+
+%W% %E%
+written Dec 88 by claus
+'!
+
+!Behavior class methodsFor:'creating new classes'!
+
+new
+ "creates and return a new class"
+
+ |newClass|
+
+ newClass := self basicNew.
+ newClass setSuperclass:Object
+ selectors:(Array new:0)
+ methods:(Array new:0)
+ instSize:0
+ flags:0.
+ ^ newClass
+! !
+
+!Behavior methodsFor:'initialization'!
+
+initialize
+ "to catch initialize for classes which do not"
+
+ ^ self
+!
+
+reinitialize
+ "to catch reinitialize for classes which do not"
+
+ ^ self
+! !
+
+!Behavior methodsFor:'creating an instance of myself'!
+
+uninitializedNew
+ "same as new"
+
+ ^ self basicNew
+!
+
+uninitializedNew:anInteger
+ "same as new:anInteger"
+
+ ^ self basicNew:anInteger
+!
+
+new
+ "return an instance of myself without indexed variables"
+
+ ^ self basicNew
+!
+
+new:anInteger
+ "return an instance of myself with anInteger indexed variables"
+
+ ^ self basicNew:anInteger
+!
+
+basicNew
+ "return an instance of myself without indexed variables
+ If the receiver-class has indexed instvars, the new object will have
+ a basicSize of zero.
+ ** Do not redefine this method in any class **"
+
+%{ /* NOCONTEXT */
+
+ extern char *newNextPtr, *newEndPtr;
+ OBJ newobj;
+ int instsize;
+ REGISTER int nInstVars;
+#if !defined(memset4)
+# if !defined(FAST_MEMSET) || defined(NEGATIVE_ADDRESSES)
+ REGISTER OBJ *op;
+# endif
+#endif
+
+ nInstVars = _intVal(_INST(instSize));
+ instsize = OHDR_SIZE + nInstVars * sizeof(OBJ);
+ PROTECT(self);
+ _qAlignedNew(newobj, instsize, SENDER);
+ UNPROTECT(self);
+ _InstPtr(newobj)->o_class = self;
+
+ if (nInstVars) {
+#if defined(memset4)
+ memset4(_InstPtr(newobj)->i_instvars, nil, nInstVars);
+#else
+# if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+ /*
+ * knowing that nil is 0
+ */
+ memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+# else
+ op = _InstPtr(newobj)->i_instvars;
+ do {
+ *op++ = nil;
+ } while (--nInstVars);
+# endif
+#endif
+ }
+ RETURN ( newobj );
+%}
+!
+
+basicNew:anInteger
+ "return an instance of myself with anInteger indexed variables.
+ If the receiver-class has no indexed instvars, this is only allowed
+ if the argument, anInteger is zero.
+ ** Do not redefine this method in any class **"
+
+%{ /* NOCONTEXT */
+
+ OBJ newobj;
+ INT instsize, nInstVars, nindexedinstvars;
+ INT flags;
+#if ! defined(FAST_MEMSET) || defined(NEGATIVE_ADDRESSES)
+ REGISTER char *cp;
+ short *sp;
+ long *lp;
+#endif
+ REGISTER OBJ *op;
+ float *fp;
+ double *dp;
+ extern OBJ new();
+
+ if (_isSmallInteger(anInteger)) {
+ nindexedinstvars = _intVal(anInteger);
+ if (nindexedinstvars >= 0) {
+ nInstVars = _intVal(_INST(instSize));
+ flags = _intVal(_INST(flags)) & ARRAYMASK;
+ switch (flags) {
+ case BYTEARRAY:
+ instsize = OHDR_SIZE + nInstVars * sizeof(OBJ) + nindexedinstvars * sizeof(char);
+ PROTECT(self);
+ _qNew(newobj, instsize, SENDER);
+ UNPROTECT(self);
+ _InstPtr(newobj)->o_class = self;
+#if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+ /*
+ * knowing that nil is 0
+ */
+ memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+#else
+ op = _InstPtr(newobj)->i_instvars;
+ while (nInstVars--)
+ *op++ = nil;
+ cp = (char *)op;
+ while (nindexedinstvars >= sizeof(long)) {
+ *(long *)cp = 0;
+ cp += sizeof(long);
+ nindexedinstvars -= sizeof(long);
+ }
+ while (nindexedinstvars--)
+ *cp++ = '\0';
+#endif
+ RETURN ( newobj );
+ break;
+
+ case WORDARRAY:
+ instsize = OHDR_SIZE + nInstVars * sizeof(OBJ) + nindexedinstvars * sizeof(short);
+ PROTECT(self);
+ _qNew(newobj, instsize, SENDER);
+ UNPROTECT(self);
+ _InstPtr(newobj)->o_class = self;
+#if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+ /*
+ * knowing that nil is 0
+ */
+ memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+#else
+ op = _InstPtr(newobj)->i_instvars;
+ while (nInstVars--)
+ *op++ = nil;
+ sp = (short *)op;
+ while (nindexedinstvars--)
+ *sp++ = 0;
+#endif
+ RETURN ( newobj );
+ break;
+
+ case LONGARRAY:
+ instsize = OHDR_SIZE + nInstVars * sizeof(OBJ) + nindexedinstvars * sizeof(long);
+ PROTECT(self);
+ _qNew(newobj, instsize, SENDER);
+ UNPROTECT(self);
+ _InstPtr(newobj)->o_class = self;
+#if defined(memset4) && ! defined(NEGATIVE_ADDRESSES)
+ /*
+ * knowing that nil is 0
+ */
+ memset4(_InstPtr(newobj)->i_instvars, 0, nInstVars + nindexedinstvars);
+#else
+# if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+ /*
+ * knowing that nil is 0
+ */
+ memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+# else
+ op = _InstPtr(newobj)->i_instvars;
+ while (nInstVars--)
+ *op++ = nil;
+ lp = (long *)op;
+ while (nindexedinstvars--)
+ *lp++ = 0;
+# endif
+#endif
+ RETURN ( newobj );
+ break;
+
+ case FLOATARRAY:
+ instsize = OHDR_SIZE + nInstVars * sizeof(OBJ) + nindexedinstvars * sizeof(float);
+ PROTECT(self);
+ _qNew(newobj, instsize, SENDER);
+ UNPROTECT(self);
+ _InstPtr(newobj)->o_class = self;
+ op = _InstPtr(newobj)->i_instvars;
+ while (nInstVars--)
+ *op++ = nil;
+ fp = (float *)op;
+ while (nindexedinstvars--)
+ *fp++ = 0.0;
+ RETURN ( newobj );
+ break;
+
+ case DOUBLEARRAY:
+ instsize = OHDR_SIZE + nInstVars * sizeof(OBJ) + nindexedinstvars * sizeof(double);
+ PROTECT(self);
+ _qNew(newobj, instsize, SENDER);
+ UNPROTECT(self);
+ _InstPtr(newobj)->o_class = self;
+ op = _InstPtr(newobj)->i_instvars;
+ while (nInstVars--)
+ *op++ = nil;
+ dp = (double *)op;
+ while (nindexedinstvars--)
+ *dp++ = 0.0;
+ RETURN ( newobj );
+ break;
+
+ case WKPOINTERARRAY:
+ case POINTERARRAY:
+ nInstVars += nindexedinstvars;
+ instsize = OHDR_SIZE + nInstVars * sizeof(OBJ);
+ PROTECT(self);
+ _qAlignedNew(newobj, instsize, SENDER);
+ UNPROTECT(self);
+ _InstPtr(newobj)->o_class = self;
+#if defined(memset4)
+ memset4(_InstPtr(newobj)->i_instvars, nil, nInstVars);
+#else
+# if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+ /*
+ * knowing that nil is 0
+ */
+ memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+# else
+ op = _InstPtr(newobj)->i_instvars;
+ while (nInstVars--)
+ *op++ = nil;
+# endif
+#endif
+ RETURN ( newobj );
+ break;
+
+ default:
+ /*
+ * new:n for non-variable classes only allowed if
+ * n == 0
+ */
+ if (nindexedinstvars == 0) {
+ instsize = OHDR_SIZE + nInstVars * sizeof(OBJ);
+ PROTECT(self);
+ _qAlignedNew(newobj, instsize, SENDER);
+ UNPROTECT(self);
+ _InstPtr(newobj)->o_class = self;
+ if (nInstVars) {
+#if defined(memset4)
+ memset4(_InstPtr(newobj)->i_instvars, nil, nInstVars);
+#else
+# if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+ /*
+ * knowing that nil is 0
+ */
+ memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+# else
+ op = _InstPtr(newobj)->i_instvars;
+ do {
+ *op++ = nil;
+ } while (--nInstVars);
+# endif
+#endif
+ }
+ RETURN ( newobj );
+ }
+ break;
+ }
+ }
+ }
+%}
+.
+ (anInteger isMemberOf:SmallInteger) ifFalse:[
+ self error:'argument to new: must be Integer'
+ ] ifTrue:[
+ (anInteger >= 0) ifTrue:[
+ "sorry but this class has no indexed instvars - need 'new' "
+ self error:'not indexed - cannot create with new:'
+ ] ifFalse:[
+ self error:'bad argument'
+ ]
+ ]
+! !
+
+!Behavior methodsFor:'accessing'!
+
+superclass
+ "return the receivers superclass"
+
+ ^ superclass
+!
+
+selectors
+ "return the receivers selector array"
+
+ ^ selectors
+!
+
+methods
+ "return the receivers method array"
+
+ ^ methods
+!
+
+methodDictionary
+ "return the receivers method dictionary - since no dictionary is
+ used (for now) just return the method array"
+
+ ^ methods
+!
+
+instSize
+ "return the number of instance variables of the receiver"
+
+ ^ instSize
+!
+
+flags
+ "return the receivers flag bits"
+
+ ^ flags
+!
+
+isVariable
+ "return true, if instances have indexed instance variables"
+
+ "this used to be defined as:
+ ^ (flags bitAnd:16r0F) ~~ 0
+ but then, changes in stc.h would not affect us here. Therefore:"
+
+%{ /* NOCONTEXT */
+ /* this is defined as a primitive to get defines from stc.h */
+
+ RETURN ( (_intVal(_INST(flags)) & ARRAYMASK) ? true : false );
+%}
+!
+
+isFixed
+ "return true, if instances do not have indexed instance variables"
+
+%{ /* NOCONTEXT */
+ /* this is defined as a primitive to get defines from stc.h */
+
+ RETURN ( (_intVal(_INST(flags)) & ARRAYMASK) ? false : true );
+%}
+!
+
+isBits
+ "return true, if instances have indexed byte or short instance variables.
+ Ignore long, float and double arrays, since classes using isBits are probably
+ not prepared to handle them correctly."
+
+%{ /* NOCONTEXT */
+ /* this is defined as a primitive to get defines from stc.h */
+
+ REGISTER int flags;
+
+ RETURN ( (((flags = (_intVal(_INST(flags)) & ARRAYMASK)) == BYTEARRAY)
+ || (flags == WORDARRAY)) ? true : false );
+%}
+!
+
+isBytes
+ "return true, if instances have indexed byte instance variables"
+
+%{ /* NOCONTEXT */
+ /* this is defined as a primitive to get defines from stc.h */
+
+ RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == BYTEARRAY) ? true : false );
+%}
+!
+
+isWords
+ "return true, if instances have indexed short instance variables"
+
+%{ /* NOCONTEXT */
+ /* this is defined as a primitive to get defines from stc.h */
+
+ RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == WORDARRAY) ? true : false );
+%}
+!
+
+isLongs
+ "return true, if instances have indexed long instance variables"
+
+%{ /* NOCONTEXT */
+ /* this is defined as a primitive to get defines from stc.h */
+
+ RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == LONGARRAY) ? true : false );
+%}
+!
+
+isFloats
+ "return true, if instances have indexed float instance variables"
+
+%{ /* NOCONTEXT */
+ RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == FLOATARRAY) ? true : false );
+%}
+!
+
+isDoubles
+ "return true, if instances have indexed double instance variables"
+
+%{ /* NOCONTEXT */
+ RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == DOUBLEARRAY) ? true : false );
+%}
+!
+
+isPointers
+ "return true, if instances have pointer instance variables"
+
+ "question: should we ignore WeakPointers ?"
+%{ /* NOCONTEXT */
+ REGISTER int flags;
+
+ RETURN ( (((flags = (_intVal(_INST(flags)) & ARRAYMASK)) == POINTERARRAY)
+ || (flags == WKPOINTERARRAY)) ? true : false );
+%}
+! !
+
+!Behavior methodsFor:'queries'!
+
+isBehavior
+ "return true, if the receiver describing another objecs behavior
+ i.e. is a class."
+
+ ^ true
+!
+
+hasMultipleSuperclasses
+ "NO multiple inheritance in this system"
+
+ ^ false
+!
+
+superclasses
+ "return a collection of the receivers immediate superclasses
+ - since we have NO multiple inheritance, there is only one"
+
+ ^ Array with:superclass
+!
+
+allSuperclasses
+ "return a collection of the receivers accumulated superclasses"
+
+ |aCollection theSuperClass|
+
+ theSuperClass := superclass.
+ theSuperClass notNil ifTrue:[
+ aCollection := OrderedCollection new.
+ [theSuperClass notNil] whileTrue:[
+ aCollection add:theSuperClass.
+ theSuperClass := theSuperClass superclass
+ ]
+ ].
+ ^ aCollection
+!
+
+withAllSuperclasses
+ "return a collection containing the receiver and all
+ of the receivers accumulated superclasses"
+
+ |aCollection theSuperClass|
+
+ aCollection := OrderedCollection with:self.
+ theSuperClass := superclass.
+ [theSuperClass notNil] whileTrue:[
+ aCollection add:theSuperClass.
+ theSuperClass := theSuperClass superclass
+ ].
+ ^ aCollection
+!
+
+subclasses
+ "return a collection of the direct subclasses of the receiver"
+
+ |newColl|
+
+ newColl := OrderedCollection new.
+ self subclassesDo:[:aClass |
+ newColl add:aClass
+ ].
+ ^ newColl
+!
+
+allSubclasses
+ "return a collection of all subclasses (direct AND indirect) of
+ the receiver"
+
+ |newColl|
+
+ newColl := OrderedCollection new.
+ self allSubclassesDo:[:aClass |
+ newColl add:aClass
+ ].
+ ^ newColl
+!
+
+withAllSubclasses
+ "return a collection containing the receiver and
+ all subclasses (direct AND indirect) of the receiver"
+
+ |newColl|
+
+ newColl := OrderedCollection with:self.
+ self allSubclassesDo:[:aClass |
+ newColl add:aClass
+ ].
+ ^ newColl
+!
+
+isSubclassOf:aClass
+ "return true, if I am a subclass of the argument, aClass"
+
+ |theClass|
+ theClass := superclass.
+ [theClass notNil] whileTrue:[
+ (theClass == aClass) ifTrue:[^ true].
+ theClass := theClass superclass
+ ].
+ ^ false
+!
+
+allInstances
+ "return a collection of all my instances"
+
+ |coll|
+
+ coll := OrderedCollection new.
+ ObjectMemory allObjectsDo:[:anObject |
+ (anObject class == self) ifTrue:[
+ coll add:anObject
+ ]
+ ].
+ ^ coll asArray
+!
+
+instanceCount
+ "return the number of instances of myself"
+
+ |count|
+
+ count := 0.
+ ObjectMemory allObjectsDo:[:anObject |
+ (anObject class == self) ifTrue:[
+ count := count + 1
+ ]
+ ].
+ ^ count
+! !
+
+!Behavior methodsFor:'private accessing'!
+
+setSuperclass:sup selectors:sels methods:m instSize:i flags:f
+ "set some inst vars (private use only)"
+
+ superclass := sup.
+ selectors := sels.
+ methods := m.
+ instSize := i.
+ flags := f
+!
+
+setSuperclass:aClass
+ "set the superclass of the receiver"
+
+ superclass := aClass
+!
+
+instSize:aNumber
+ "set the instance size"
+
+ instSize := aNumber
+!
+
+flags:aNumber
+ "set the flags"
+
+ flags := aNumber
+!
+
+setSelectors:anArray
+ "set the selector array of the receiver"
+
+ selectors := anArray
+!
+
+setMethodDictionary:anArray
+ "set the method array of the receiver"
+
+ methods := anArray
+!
+
+superclass:aClass
+ "set the superclass"
+
+ "must flush caches since lookup chain changes"
+ ObjectMemory flushCaches.
+ superclass := aClass
+!
+
+selectors:selectorArray methods:methodArray
+ "set both selector array and method array of the receiver,
+ and flush caches"
+
+ ObjectMemory flushCaches.
+ selectors := selectorArray.
+ methods := methodArray
+! !
+
+!Behavior methodsFor:'enumeration'!
+
+allInstancesDo:aBlock
+ "evaluate a block for all of my instances"
+
+ ObjectMemory allObjectsDo:[:anObject |
+ (anObject class == self) ifTrue:[
+ aBlock value:anObject
+ ]
+ ]
+!
+
+subclassesDo:aBlock
+ "evaluate the argument, aBlock for all immediate subclasses"
+
+ Smalltalk allClassesDo:[:aClass |
+ (aClass superclass == self) ifTrue:[
+ aBlock value:aClass
+ ]
+ ]
+!
+
+allSubclassesDo:aBlock
+ "evaluate a block for all of my subclasses"
+
+ Smalltalk allClassesDo:[:aClass |
+ (aClass isSubclassOf:self) ifTrue:[
+ aBlock value:aClass
+ ]
+ ]
+!
+
+allSubclassesInOrderDo:aBlock
+ "evaluate a block for all of my subclasses where superclasses come
+ first"
+
+ self subclassesDo:[:aClass |
+ aBlock value:aClass.
+ aClass allSubclassesInOrderDo:aBlock
+ ]
+!
+
+allSuperclassesDo:aBlock
+ "evaluate a block for all of my superclasses"
+
+ |theClass|
+
+ theClass := superclass.
+ [theClass notNil] whileTrue:[
+ aBlock value:theClass.
+ theClass := theClass superclass
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Block.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,733 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Block
+ instanceVariableNames:'code flags byteCode home nargs
+ sourcePos initialPC literals
+ selfValue'
+ classVariableNames:'InvalidNewSignal'
+ poolDictionaries:''
+ category:'Kernel-Methods'
+!
+
+Block comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+
+written spring 89 by claus
+'!
+
+!Block class methodsFor:'documentation'!
+
+documentation
+"
+Blocks are pieces of executable code which can be evaluated by sending
+them a value-message (''value'', ''value:'', ''value:value:'' etc).
+
+Blocks with arguments need a message of type ''value:arg1 ... value:argn''
+for evaluation; the number of arguments passed when evaluating must match
+the number of arguments the block was declared with otherwise an error is
+raised. Blocks without args need a ''value'' message for evaluation.
+
+Blocks keep a reference to the method context where the block was declared -
+this allows blocks to access the methods arguments and/or variables.
+This is also true when the method has already returned - since the
+block keeps this reference, the methods context will NOT die in this case.
+
+A return (via ^-statement) out of a block will force a return from the
+blocks method context (if it is still living) - this make the implementation
+of long-jumps and control structures possible.
+(If the method is not alive (i.e. has already returned), a return out of the block
+is ignored and a simple return from the block is performed).
+
+Long-jump is done by defining a catchBlock as ''[^ self]''
+somewhere up in the calling-tree. Then, to do the long-jump from out of some
+deeply nested method, simply do: ''catchBlock value''.
+
+Instance variables:
+
+code <not_an_object> the function pointer if its a compiled block
+flags <SmallInteger> special flag bits coded in a number
+byteCode <ByteArray> bytecode of home method if its an interpreted block
+home <Context> the context where this block lives
+nargs <SmallInteger> the number of arguments the block expects
+sourcePos <SmallInteger> the character poistion of its source, in chars
+ relative to methods source beginning
+initialPC <SmallInteger> the start position within the byteCode
+literals <Array> the blocks literal array
+selfValue <Object> value to use for self if its a copying block
+
+NOTICE: layout known by runtime system and compiler - do not change
+"
+! !
+
+!Block class methodsFor:'initialization' !
+
+initialize
+ "setup the signals"
+
+ InvalidNewSignal := (Signal new).
+ InvalidNewSignal mayProceed:false.
+ InvalidNewSignal notifierString:'blocks are only created by the system'.
+! !
+
+!Block class methodsFor:'instance creation'!
+
+code:codeAddress byteCode:bCode nargs:numArgs sourcePosition:sourcePos initialPC:initialPC literals:literals
+ "create a new cheap (homeless) block.
+ Not for public use - special hook for the compiler."
+
+ |newBlock|
+
+ newBlock := super basicNew.
+ newBlock code:codeAddress.
+ newBlock byteCode:bCode.
+ newBlock nargs:numArgs.
+ newBlock sourcePosition:sourcePos.
+ newBlock initialPC:initialPC.
+ newBlock literals:literals.
+ ^ newBlock
+!
+
+basicNew
+ "catch creation of blocks - only the system creates blocks"
+
+ InvalidNewSignal raise.
+ ^ nil
+!
+
+basicNew:size
+ "catch creation of blocks - only the system creates blocks"
+
+ InvalidNewSignal raise.
+ ^ nil
+! !
+
+!Block methodsFor:'testing'!
+
+isBlock
+ ^ true
+! !
+
+!Block methodsFor:'accessing'!
+
+instVarAt:index
+ "have to catch instVar access to code - since its no object"
+
+ (index == 1) ifTrue:[^ self code].
+ ^ super instVarAt:index
+!
+
+instVarAt:index put:value
+ "have to catch instVar access to code - since its no object"
+
+ (index == 1) ifTrue:[^ self code:value].
+ ^ super instVarAt:index put:value
+!
+
+code
+ "return the code field. This is not an object but the address of the machine instructions.
+ Therefore an integer representing the code-address is returned"
+
+%{ /* NOCONTEXT */
+
+ if (_INST(code) != nil) {
+ RETURN ( _MKSMALLINT((int)(_INST(code))) )
+ }
+%}
+.
+ ^ nil
+!
+
+byteCode
+ "return the bytecode (a ByteArray) of the block"
+
+ ^ byteCode
+!
+
+nargs
+ "return the number of arguments I expect for evaluation"
+
+ ^ nargs
+!
+
+selfValue
+ "return the copied self"
+
+ ^ selfValue
+! !
+
+!Block methodsFor:'private accessing'!
+
+code:anAddress
+ "set the code field - danger alert.
+ This is not an object but the address of the blocks machine instructions.
+ Therefore the argument must be an integer representing for this address.
+ You can crash Smalltalk very badly when playing around here ..."
+
+%{ /* NOCONTEXT */
+ if (_isSmallInteger(anAddress))
+ _INST(code) = (OBJ)(_intVal(anAddress));
+%}
+!
+
+byteCode:aByteArray
+ "set the bytecode field - danger alert"
+
+ byteCode := aByteArray
+!
+
+nargs:numArgs
+ "set the number of arguments I expect for evaluation - danger alert"
+
+ nargs := numArgs
+!
+
+sourcePosition:position
+ "set the position of the source within my method"
+
+ sourcePos := position
+!
+
+initialPC:initial
+ "set the initial pc for evaluation - danger alert"
+
+ initialPC := initial
+!
+
+literals:aLiteralArray
+ "set the literal array for evaluation - danger alert"
+
+ literals := aLiteralArray
+! !
+
+!Block methodsFor:'error handling'!
+
+argumentCountError:numberGiven
+ "report that the number of arguments given does not match the number expected"
+
+ self error:('Block got ' , numberGiven printString ,
+ ' args while ' , nargs printString , ' where expected')
+!
+
+invalidMethod
+ "this is sent by the bytecode interpreter when the blocks definition is bad.
+ Can only happen when playing around with the blocks instvars
+ or the Compiler/runtime system is buggy"
+
+ self error:'invalid block - not executable'
+!
+
+invalidByteCode
+ "this is sent by the bytecode interpreter when trying to execute
+ an invalid bytecode.
+ Can only happen when playing around with the blocks instvars
+ or the Compiler/runtime system is buggy"
+
+ self error:'invalid byteCode in block - not executable'
+!
+
+receiverNotBoolean
+ "this error is triggered when the bytecode-interpreter tries to
+ execute ifTrue:/ifFalse or whileTrue: type of expressions where the
+ receiver is neither true nor false."
+
+ self error:'if/while on non-boolean receiver'
+! !
+
+!Block methodsFor:'evaluation'!
+
+value
+ "evaluate the receiver with no block args. The receiver must be a block without arguments."
+
+%{ /* NOCONTEXT */
+
+ REGISTER OBJFUNC thecode;
+ OBJ home;
+ extern OBJ interpret();
+
+ if (_INST(nargs) == _MKSMALLINT(0)) {
+#if defined(THIS_CONTEXT)
+ if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+ _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+ home = _BlockInstPtr(self)->b_home;
+ thecode = _BlockInstPtr(self)->b_code;
+ if (thecode != (OBJFUNC)nil) {
+ /* compiled machine code */
+ RETURN ( (*thecode)(home COMMA_SND) );
+ }
+ /* interpreted code */
+ RETURN ( interpret(self, 0, nil, home COMMA_SND, nil) );
+ }
+%}
+.
+ ^ self argumentCountError:0
+!
+
+value:arg
+ "evaluate the receiver with one argument. The receiver must be a 1-arg block."
+
+%{ /* NOCONTEXT */
+
+ REGISTER OBJFUNC thecode;
+ OBJ home;
+ extern OBJ interpret();
+
+ if (_INST(nargs) == _MKSMALLINT(1)) {
+#if defined(THIS_CONTEXT)
+ if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+ _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+ home = _BlockInstPtr(self)->b_home;
+ thecode = _BlockInstPtr(self)->b_code;
+ if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+ RETURN ( (*thecode)(home COMMA_SND, &arg) );
+#else
+ RETURN ( (*thecode)(home COMMA_SND, arg) );
+#endif
+ }
+ /* interpreted code */
+#ifdef PASS_ARG_REF
+ RETURN ( interpret(self, 1, nil, home COMMA_SND, nil, &arg) );
+#else
+ RETURN ( interpret(self, 1, nil, home COMMA_SND, nil, arg) );
+#endif
+ }
+%}
+.
+ ^ self argumentCountError:1
+!
+
+value:arg1 value:arg2
+ "evaluate the receiver with two arguments. The receiver must be a 2-arg block."
+
+%{ /* NOCONTEXT */
+
+ REGISTER OBJFUNC thecode;
+ OBJ home;
+ extern OBJ interpret();
+
+ if (_INST(nargs) == _MKSMALLINT(2)) {
+#if defined(THIS_CONTEXT)
+ if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+ _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+ home = _BlockInstPtr(self)->b_home;
+ thecode = _BlockInstPtr(self)->b_code;
+ if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+ RETURN ( (*thecode)(home COMMA_SND, &arg1) );
+#else
+ RETURN ( (*thecode)(home COMMA_SND, arg1, arg2) );
+#endif
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( interpret(self, 2, nil, home COMMA_SND, nil, &arg1) );
+#else
+ RETURN ( interpret(self, 2, nil, home COMMA_SND, nil, arg1, arg2) );
+#endif
+ }
+%}
+.
+ ^ self argumentCountError:2
+!
+
+value:arg1 value:arg2 value:arg3
+ "evaluate the receiver with three arguments. The receiver must be a 3-arg block."
+
+%{ /* NOCONTEXT */
+
+ REGISTER OBJFUNC thecode;
+ OBJ home;
+ extern OBJ interpret();
+
+ if (_INST(nargs) == _MKSMALLINT(3)) {
+#if defined(THIS_CONTEXT)
+ if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+ _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+ home = _BlockInstPtr(self)->b_home;
+ thecode = _BlockInstPtr(self)->b_code;
+ if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+ RETURN ( (*thecode)(home COMMA_SND, &arg1) );
+#else
+ RETURN ( (*thecode)(home COMMA_SND, arg1, arg2, arg3) );
+#endif
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( interpret(self, 3, nil, home COMMA_SND, nil, &arg1) );
+#else
+ RETURN ( interpret(self, 3, nil, home COMMA_SND, nil, arg1, arg2, arg3) );
+#endif
+ }
+%}
+.
+ ^ self argumentCountError:3
+!
+
+value:arg1 value:arg2 value:arg3 value:arg4
+ "evaluate the receiver with four arguments. The receiver must be a 4-arg block."
+
+%{ /* NOCONTEXT */
+
+ REGISTER OBJFUNC thecode;
+ OBJ home;
+ extern OBJ interpret();
+
+ if (_INST(nargs) == _MKSMALLINT(4)) {
+#if defined(THIS_CONTEXT)
+ if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+ _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+ home = _BlockInstPtr(self)->b_home;
+ thecode = _BlockInstPtr(self)->b_code;
+ if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+ RETURN ( (*thecode)(home COMMA_SND, &arg1) );
+#else
+ RETURN ( (*thecode)(home COMMA_SND, arg1, arg2, arg3, arg4) );
+#endif
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( interpret(self, 4, nil, home COMMA_SND, nil, &arg1) );
+#else
+ RETURN ( interpret(self, 4, nil, home COMMA_SND, nil, arg1, arg2, arg3, arg4) );
+#endif
+ }
+%}
+.
+ ^ self argumentCountError:4
+!
+
+value:arg1 value:arg2 value:arg3 value:arg4 value:arg5
+ "evaluate the receiver with four arguments. The receiver must be a 5-arg block."
+
+%{ /* NOCONTEXT */
+
+ REGISTER OBJFUNC thecode;
+ OBJ home;
+ extern OBJ interpret();
+
+ if (_INST(nargs) == _MKSMALLINT(5)) {
+#if defined(THIS_CONTEXT)
+ if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+ _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+ home = _BlockInstPtr(self)->b_home;
+ thecode = _BlockInstPtr(self)->b_code;
+ if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+ RETURN ( (*thecode)(home COMMA_SND, &arg1) );
+#else
+ RETURN ( (*thecode)(home COMMA_SND, arg1, arg2, arg3, arg4, arg5) );
+#endif
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( interpret(self, 5, nil, home COMMA_SND, nil, &arg1) );
+#else
+ RETURN ( interpret(self, 5, nil, home COMMA_SND, nil, arg1, arg2, arg3, arg4, arg5) );
+#endif
+ }
+%}
+.
+ ^ self argumentCountError:5
+!
+
+value:arg1 value:arg2 value:arg3 value:arg4 value:arg5 value:arg6
+ "evaluate the receiver with four arguments. The receiver must be a 6-arg block."
+
+%{ /* NOCONTEXT */
+
+ REGISTER OBJFUNC thecode;
+ OBJ home;
+ extern OBJ interpret();
+
+ if (_INST(nargs) == _MKSMALLINT(6)) {
+#if defined(THIS_CONTEXT)
+ if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+ _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+ home = _BlockInstPtr(self)->b_home;
+ thecode = _BlockInstPtr(self)->b_code;
+ if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+ RETURN ( (*thecode)(home COMMA_SND, &arg1) );
+#else
+ RETURN ( (*thecode)(home COMMA_SND, arg1, arg2, arg3, arg4, arg5, arg6) );
+#endif
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( interpret(self, 6, nil, home COMMA_SND, nil, &arg1) );
+#else
+ RETURN ( interpret(self, 6, nil, home COMMA_SND, nil, arg1, arg2, arg3, arg4, arg5, arg6) );
+#endif
+ }
+%}
+.
+ ^ self argumentCountError:6
+!
+
+valueWithArguments:argArray
+ "evaluate the receiver with arguments taken from argArray.
+ The size of the argArray must match the number of arguments the receiver expects."
+
+ |a1 a2 a3 a4 a5 a6 a7|
+
+ (argArray class == Array) ifFalse:[
+ ^ self error:'argument must be an array'
+ ].
+ (argArray size == nargs) ifFalse:[
+ ^ self argumentCountError:(argArray size)
+ ].
+%{
+
+ REGISTER OBJFUNC thecode;
+ OBJ home;
+ extern OBJ interpret();
+
+#if defined(THIS_CONTEXT)
+ if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+ _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+ switch (_intVal(_INST(nargs))) {
+ case 7:
+ a7 = _ArrayInstPtr(argArray)->a_element[6];
+ case 6:
+ a6 = _ArrayInstPtr(argArray)->a_element[5];
+ case 5:
+ a5 = _ArrayInstPtr(argArray)->a_element[4];
+ case 4:
+ a4 = _ArrayInstPtr(argArray)->a_element[3];
+ case 3:
+ a3 = _ArrayInstPtr(argArray)->a_element[2];
+ case 2:
+ a2 = _ArrayInstPtr(argArray)->a_element[1];
+ case 1:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+ case 0:
+ break;
+ }
+ home = _BlockInstPtr(self)->b_home;
+ thecode = _BlockInstPtr(self)->b_code;
+ if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+ RETURN ( (*thecode)(home COMMA_SND, &a1) );
+#else
+ RETURN ( (*thecode)(home COMMA_SND, a1, a2, a3, a4, a5, a6, a7) );
+#endif
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( interpret(self, _intVal(_INST(nargs)), nil,
+ home COMMA_SND, nil, &a1) );
+#else
+ RETURN ( interpret(self, _intVal(_INST(nargs)), nil,
+ home COMMA_SND, nil, a1, a2, a3, a4, a5, a6, a7) );
+#endif
+%}
+!
+
+valueNowOrOnUnwindDo:aBlock
+ "evaluate the receiver - after that, or when some method sent within unwinds (i.e. does
+ a long return), evaluate the argument, aBlock.
+ This is used to make certain that cleanup actions (for example closing files etc.) are
+ executed regardless of error actions"
+
+ |v|
+
+ v := self value. "the real logic is in Context"
+ aBlock value.
+ ^ v
+!
+
+valueOnUnwindDo:aBlock
+ "evaluate the receiver - when some method sent within unwinds (i.e. does
+ a long return), evaluate the argument, aBlock.
+ This is used to make certain that cleanup actions (for example closing files etc.) are
+ executed regardless of error actions"
+
+ ^ self value "the real logic is in Context"
+! !
+
+!Block methodsFor:'looping'!
+
+whileTrue:aBlock
+ "evaluate the argument, aBlock while the receiver evaluates to true.
+ - open coded by compiler but needed here for #perform and expression evaluation."
+%{
+ extern OBJ _value;
+ static struct inlineCache bval = _ILC0;
+ static struct inlineCache selfVal = _ILC0;
+
+ while ((*bval.ilc_func)(self, _value, CON_COMMA nil, &bval) == true) {
+ if (InterruptPending != nil) interrupt(CONARG);
+ (*selfVal.ilc_func)(aBlock, _value, CON_COMMA nil, &selfVal);
+ }
+%}
+.
+ ^ nil
+!
+
+whileTrue
+ "evaluate the receiver until it evaluates to false (ST80 compatibility)"
+
+ ^ self whileTrue:[]
+!
+
+whileFalse:aBlock
+ "evaluate the argument while the receiver evaluates to false.
+ - open coded by compiler but needed here for #perform and expression evaluation."
+%{
+ extern OBJ _value;
+ static struct inlineCache bval = _ILC0;
+ static struct inlineCache selfVal = _ILC0;
+
+ while ((*bval.ilc_func)(self, _value, CON_COMMA nil, &bval) == false) {
+ if (InterruptPending != nil) interrupt(CONARG);
+ (*selfVal.ilc_func)(aBlock, _value, CON_COMMA nil, &selfVal);
+ }
+%}
+.
+ ^ nil
+!
+
+whileFalse
+ "evaluate the receiver until it evaluates to true (ST80 compatibility)"
+
+ ^ self whileFalse:[]
+!
+
+doWhile:aBlock
+ "repeat the receiver block until aBlock evaluates to false.
+ The receiver is evaluated at least once."
+
+ self value.
+ [aBlock value] whileTrue:[
+ self value
+ ]
+!
+
+doUntil:aBlock
+ "repeat the receiver block until aBlock evaluates to true.
+ The receiver is evaluated at least once."
+
+ self value.
+ [aBlock value] whileFalse:[
+ self value
+ ]
+!
+
+loop
+ "repeat the receiver forever (should contain a return somewhere).
+ Inspired by a corresponding Self method."
+
+ [true] whileTrue:[self value]
+
+ "[Transcript showCr:'hello'] loop" "must be stopped with interrupt"
+!
+
+valueWithExit
+ "the receiver must be a block of one argument. It is evaluated, and is passed a block,
+ which, if sent a value:-message, will exit the receiver block, returning the parameter of the
+ value:-message. Used for premature returns to the caller.
+ Taken from a manchester goody (also appears in Self)."
+
+ ^ self value: [:exitValue | ^exitValue]
+
+ "[:exit |
+ 1 to:10 do:[:i |
+ i == 5 ifTrue:[exit value:'thats it']
+ ].
+ 'regular block-value; never returned'
+ ] valueWithExit"
+!
+
+loopWithExit
+ "the receiver must be a block of one argument. It is evaluated in a loop forever, and is passed a
+ block, which, if sent a value:-message, will exit the receiver block, returning the parameter of
+ the value:-message. Used for loops with exit in the middle.
+ Inspired by a corresponding Self method."
+
+ |exitBlock|
+
+ exitBlock := [:exitValue | ^ exitValue].
+ [true] whileTrue:[self value:exitBlock]
+
+ "|i|
+ i := 1.
+ [:exit |
+ i == 5 ifTrue:[exit value:'thats it'].
+ i := i + 1
+ ] loopWithExit"
+! !
+
+!Block methodsFor:'process creation'!
+
+newProcess
+ "create a new (unscheduled) process executing the receiver"
+
+ |p pBlock startUp|
+
+ startUp := self.
+ pBlock := [ startUp value. Processor terminate:p ].
+ p := Processor newProcessFor:pBlock.
+ ^ p
+!
+
+fork
+ "create a new process executing the receiver"
+
+ ^ self newProcess resume
+!
+
+forkWith:argumentArray
+ |b|
+
+ b := [self valueWithArguments:argumentArray].
+ b fork
+!
+
+forkAt:priority
+ "create a new process executing the receiver"
+
+ ^ (self newProcess priority:priority) resume
+! !
+
+!Block methodsFor:'printing'!
+
+printString
+ |homeClass|
+
+ home notNil ifTrue:[
+ ^ '[] in ', home printString
+ ].
+ ^ '[] in ???'
+!
+
+printOn:aStream
+ |homeClass|
+
+ aStream nextPutAll:'[] in '.
+ homeClass := home containingClass.
+ homeClass notNil ifTrue:[
+ homeClass name printOn:aStream.
+ aStream space.
+ (homeClass selectorForMethod:home) printOn:aStream
+ ] ifFalse:[
+ aStream nextPutAll:' ???'
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BlockContext.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,106 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Context subclass:#BlockContext
+ instanceVariableNames:'' "do not add instvars here"
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Methods'
+!
+
+BlockContext comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+BlockContexts represent the stack context objects of blocks. The layout is the same
+as for other contexts - this class has been added to avoid a flag in an instance variable.
+(has become necessary with cheap blocks, which have no home).
+
+Warning: layout and size known by compiler and runtime system - do not change.
+
+%W% %E%
+'!
+
+!BlockContext methodsFor:'accessing'!
+
+isBlockContext
+ "return true, iff the receiver is a BlockContext, false otherwise"
+
+ ^ true
+!
+
+methodHome
+ "return the method-home for block contexts"
+
+ |con h|
+
+ home isNil ifTrue:[^ nil]. "XXX will change soon"
+ home isContext ifFalse:[^ nil]. "copying blocks have no method home"
+
+ con := self.
+ h := home.
+ [h notNil] whileTrue:[
+ con := h.
+ h := con home
+ ].
+ ^ con
+!
+
+home
+ "return the immediate home of the receiver.
+ normally this is the methodcontext, where the block was created,
+ for nested block contexts, this is the surrounding blocks context."
+
+ home isContext ifFalse:[^ nil]. "copying blocks have no home"
+ ^ home
+!
+
+selector
+ "return the selector of the context - which is one of the value
+ selectors"
+
+ |nargs|
+
+ nargs := self nargs.
+ (nargs == 0) ifTrue:[^ #value].
+ (nargs == 1) ifTrue:[^ #value:].
+ (nargs == 2) ifTrue:[^ #value:value:].
+ (nargs == 3) ifTrue:[^ #value:value:value:].
+ (nargs == 4) ifTrue:[^ #value:value:value:value:].
+ (nargs == 5) ifTrue:[^ #value:value:value:value:value:].
+ ^ nil
+! !
+
+!BlockContext methodsFor:'printing'!
+
+receiverPrintString
+ home isNil ifTrue:[
+ ^ '[] optimized'
+ ].
+ home isContext ifFalse:[
+ "a copying block"
+
+ "receiverClassName := home selfValue class name."
+ ^ '[] optimized'
+ ].
+
+ ^ '[] in ' , receiver class name , '-' , self methodHome selector
+!
+
+printReceiver
+ self receiverPrintString print
+!
+
+printString
+ ^ self receiverPrintString , ' ' , self selector printString
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Boolean.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,74 @@
+"
+ COPYRIGHT (c) 1988/89/90 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Boolean
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Objects'
+!
+
+Boolean comment:'
+
+COPYRIGHT (c) 1988/89/90 by Claus Gittinger
+ All Rights Reserved
+
+Boolean is an abstract class defining the common protocol for logical
+values. The logical values are represented by its two subclasses True and False.
+
+There are no instances of Boolean in the system and there is only one
+instance of True (which is the global true) and one of False (false).
+
+Boolean catches some messages which deal with copying Booleans to make
+certain there is only one instance of each.
+The system will behave strange if you fiddle around here and create
+new instances of True or False.
+
+written 1988 by claus
+
+@(#)Boolean.st 2.4 92/06/06
+'!
+
+!Boolean class methodsFor:'instance creation'!
+
+basicNew
+ "catch instance creation
+ - there must be exactly on instance of each - no more"
+
+ self error:'new instances of True/False are not allowed'
+! !
+
+!Boolean methodsFor:'copying'!
+
+shallowCopy
+ "return a shallow copy of the receiver
+ - since both true and false are unique, return the receiver"
+
+ ^ self
+!
+
+deepCopy
+ "return a deep copy of the receiver
+ - since both true and false are unique, return the receiver"
+
+ ^ self
+! !
+
+!Boolean methodsFor:'printing & storing'!
+
+storeOn:aStream
+ "append a Character sequence to the argument, aStream from which the
+ receiver can be reconstructed.
+ return the receiver"
+
+ self printOn:aStream
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ByteArray.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,492 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+IntegerArray subclass:#ByteArray
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Indexed'
+!
+
+ByteArray comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ByteArrays store integers in the range 0..255
+unlike Smalltalk/80, my ByteArrays have fixed size - may change
+
+%W% %E%
+
+written spring 89 by claus
+'!
+
+!ByteArray class methodsFor:'instance creation'!
+
+uninitializedNew:anInteger
+ "return a new instance of the receiver with uninitialized
+ (i.e. undefined) contents. The indexed elements have any random
+ value. use, when contents will be set anyway shortly after."
+
+%{ /* NOCONTEXT */
+
+ OBJ newobj;
+ INT instsize, nInstVars, nindexedinstvars;
+ REGISTER OBJ *op;
+ extern OBJ new();
+
+ if (_isSmallInteger(anInteger)) {
+ nindexedinstvars = _intVal(anInteger);
+ if (nindexedinstvars >= 0) {
+ nInstVars = _intVal(_ClassInstPtr(self)->c_ninstvars);
+ if ((_intVal(_ClassInstPtr(self)->c_flags) & ARRAYMASK) == BYTEARRAY) {
+ instsize = OHDR_SIZE + nInstVars * sizeof(OBJ) + nindexedinstvars * sizeof(char);
+ PROTECT(self);
+ _qNew(newobj, instsize, SENDER);
+ UNPROTECT(self);
+ _InstPtr(newobj)->o_class = self;
+#if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+ /*
+ * knowing that nil is 0
+ */
+ memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+#else
+ op = _InstPtr(newobj)->i_instvars;
+ while (nInstVars--)
+ *op++ = nil;
+#endif
+ RETURN ( newobj );
+ }
+ }
+ }
+%}
+.
+ ^ self basicNew:anInteger
+
+! !
+
+!ByteArray methodsFor:'accessing'!
+
+basicAt:index
+ "return the indexed instance variable with index, anInteger
+ - added here for speed i.e. to avoid double send at: - basicAt:"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int indx;
+ int nIndex;
+
+ if ((_qClass(self) == ByteArray) && _isSmallInteger(index)) {
+ indx = _intVal(index);
+ nIndex = _qSize(self) - OHDR_SIZE;
+ if ((indx > 0) && (indx <= nIndex)) {
+ RETURN ( _MKSMALLINT(_ByteArrayInstPtr(self)->ba_element[indx - 1]) );
+ }
+ }
+%}
+.
+ ^ super basicAt:index
+!
+
+basicAt:index put:value
+ "set the indexed instance variable with index, anInteger to value
+ - added here for speed i.e. to avoid double send at:put: - basicAt:put:"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int indx;
+ int nIndex;
+ int val;
+
+ if ((_qClass(self) == ByteArray)
+ && _isSmallInteger(index)
+ && _isSmallInteger(value)) {
+ val = _intVal(value);
+ if ((val >= 0) && (val <= 255)) {
+ indx = _intVal(index);
+ nIndex = _qSize(self) - OHDR_SIZE;
+ if ((indx > 0) && (indx <= nIndex)) {
+ _ByteArrayInstPtr(self)->ba_element[indx - 1] = val;
+ RETURN ( value );
+ }
+ }
+ }
+%}
+.
+ ^ super basicAt:index put:value
+! !
+
+!ByteArray methodsFor:'queries'!
+
+indexOf:aByte startingAt:start
+ "return the index of the first occurrence of the argument, aByte
+ in the receiver starting at start, anInteger; return 0 if not found.
+ - reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ REGISTER unsigned char *cp;
+ REGISTER int index, byteValue;
+ REGISTER int len;
+
+ if (! _isSmallInteger(aByte)) {
+ RETURN ( _MKSMALLINT(0) );
+ }
+
+ byteValue = _intVal(aByte);
+
+ if ((byteValue < 0) || (byteValue > 255)) {
+ RETURN ( _MKSMALLINT(0) );
+ }
+
+ if (_isSmallInteger(start)
+ && (_qClass(self) == ByteArray)) {
+ index = _intVal(start);
+ len = _qSize(self) - OHDR_SIZE;
+ cp = &(_ByteArrayInstPtr(self)->ba_element[0]);
+ cp += index - 1;
+ while (index <= len) {
+ if (*cp == byteValue) {
+ RETURN ( _MKSMALLINT(index) );
+ }
+ index++;
+ cp++;
+ }
+ RETURN ( _MKSMALLINT(0) );
+ }
+%}
+.
+ ^ super indexOf:aByte startingAt:start
+!
+
+usedValues
+ "return a new ByteArray with all used values (actually a kind of Set);
+ needed specially for Image class."
+
+ |result l|
+%{
+ REGISTER unsigned char *cp;
+ REGISTER int len;
+ unsigned char flags[256];
+ static struct inlineCache nw = _ILC1;
+ extern OBJ ByteArray, _new_;
+
+ if (_qClass(self) == ByteArray) {
+ memset(flags, 0, sizeof(flags));
+ len = _qSize(self) - OHDR_SIZE;
+ cp = &(_ByteArrayInstPtr(self)->ba_element[0]);
+
+ /* for each used byte, set flag */
+ while (len > 0) {
+ flags[*cp] = 1;
+ cp++;
+ len--;
+ }
+ /* count 1's */
+ len = 0;
+ for (cp=&flags[255]; cp >= flags; cp--)
+ if (*cp) len++;
+
+ /* create ByteArray of used values */
+#ifdef PASS_ARG_REF
+ l = _MKSMALLINT(len);
+ result = (*nw.ilc_func)(ByteArray, _new_, CON_COMMA nil, &nw, &l);
+#else
+ result = (*nw.ilc_func)(ByteArray, _new_, CON_COMMA nil, &nw, _MKSMALLINT(len));
+#endif
+ if (_Class(result) == ByteArray) {
+ cp = &(_ByteArrayInstPtr(result)->ba_element[0]);
+ for (len=0; len < 256; len++) {
+ if (flags[len])
+ *cp++ = len;
+ }
+ }
+ RETURN ( result );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+usageCounts
+ "return an array filled with value-counts -
+ added for Image handling"
+
+ |counts|
+
+ counts := Array new:256.
+%{
+ REGISTER unsigned char *cp;
+ REGISTER int nByte;
+ REGISTER int index;
+ int icounts[256];
+
+ if ((_qClass(self) == ByteArray) && _isArray(counts)) {
+ /*
+ * zero counts
+ */
+ for (index=0; index<256; index++) {
+ icounts[index] = 0;
+ }
+
+ /*
+ * count
+ */
+ nByte = _qSize(self) - OHDR_SIZE;
+ cp = &(_ByteArrayInstPtr(self)->ba_element[0]);
+ while (nByte--) {
+ icounts[*cp++]++;
+ }
+
+ /*
+ * make it real counts
+ */
+ for (index=0; index<256; index++) {
+ _ArrayInstPtr(counts)->a_element[index] = _MKSMALLINT(icounts[index]);
+ }
+ RETURN ( counts );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+maximumValue
+ "return the maximum value in the receiver -
+ added for sound-player (which needs a fast method for this)"
+
+%{ /* NOCONTEXT */
+
+ REGISTER unsigned char *cp;
+ REGISTER int index, max;
+ int len;
+
+ if (_qClass(self) == ByteArray) {
+ max = 0;
+ index = 0;
+ len = _qSize(self) - OHDR_SIZE;
+ cp = &(_ByteArrayInstPtr(self)->ba_element[0]);
+ while (++index <= len) {
+ if (*cp > max) max = *cp;
+ cp++;
+ }
+ RETURN ( _MKSMALLINT(max) );
+ }
+%}
+.
+ self primitiveFailed
+! !
+
+!ByteArray methodsFor:'filling and replacing'!
+
+replaceFrom:start to:stop with:aCollection startingAt:repStart
+ "reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ int nIndex, repNIndex;
+ int startIndex, stopIndex;
+ REGISTER unsigned char *src;
+ REGISTER int repStartIndex;
+ int repStopIndex, count;
+ REGISTER unsigned char *dst;
+
+ if ((_qClass(self) == ByteArray)
+ && (_Class(aCollection) == ByteArray)
+ && _isSmallInteger(start)
+ && _isSmallInteger(stop)
+ && _isSmallInteger(repStart)) {
+ startIndex = _intVal(start) - 1;
+ if (startIndex >= 0) {
+ nIndex = _qSize(self) - OHDR_SIZE;
+ stopIndex = _intVal(stop) - 1;
+ count = stopIndex - startIndex + 1;
+ if ((count > 0) && (stopIndex < nIndex)) {
+ repStartIndex = _intVal(repStart) - 1;
+ if (repStartIndex >= 0) {
+ repNIndex = _qSize(aCollection) - OHDR_SIZE;
+ repStopIndex = repStartIndex + (stopIndex - startIndex);
+ if (repStopIndex < repNIndex) {
+ src = &(_ByteArrayInstPtr(aCollection)->ba_element[repStartIndex]);
+ dst = &(_ByteArrayInstPtr(self)->ba_element[startIndex]);
+
+ if (aCollection == self) {
+ /* take care of overlapping copy */
+ if (src < dst) {
+ /* must do a reverse copy */
+ src += count;
+ dst += count;
+ while (count-- > 0) {
+ *--dst = *--src;
+ }
+ RETURN ( self );
+ }
+ }
+#ifdef FAST_MEMCPY
+ bcopy(src, dst, count);
+#else
+ while (count-- > 0) {
+ *dst++ = *src++;
+ }
+#endif
+ RETURN ( self );
+ }
+ }
+ }
+ }
+ }
+%}
+.
+ ^ super replaceFrom:start to:stop with:aCollection startingAt:repStart
+! !
+
+!ByteArray methodsFor:'image manipulation'!
+
+invert
+ "invert all bytes - used with image manipulations
+ written as a primitive for speed"
+
+%{ /* NOCONTEXT */
+
+ REGISTER unsigned char *dst;
+ REGISTER unsigned long *ldst;
+ REGISTER int cnt;
+
+ if (_qClass(self) == ByteArray) {
+ cnt = _qSize(self) - OHDR_SIZE;
+ dst = _ByteArrayInstPtr(self)->ba_element;
+ if (! ((int)dst & (sizeof(long)-1))) {
+ ldst = (unsigned long *)dst;
+ while (cnt >= sizeof(long)) {
+ *ldst = ~(*ldst);
+ ldst++;
+ cnt -= sizeof(long);
+ }
+ dst = (unsigned char *)ldst;
+ }
+ while (cnt--) {
+ *dst = ~(*dst);
+ dst++;
+ }
+ RETURN ( self );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+expandPixels:nBitsPerPixel width:width height:height into:aByteArray
+ mapping:aMapByteArray
+ "given the receiver with nBitsPerPixel-depth pixels, expand them into
+ aByteArray with 8-bit pixels. The width/height-arguments are needed
+ to skip any padded src-bits. On the fly, the destination pixels
+ are translated using aMapByteArray if nonnil.
+ - used to display mono, 2-bit and 4-bit bitmaps on grey-scale/color
+ machines"
+
+%{ /* NOCONTEXT */
+
+ {
+ REGISTER unsigned char *src, *dst;
+ REGISTER int wrun;
+ unsigned char *srcNext;
+ int bytesPerRow, mask, shift0, shift;
+ int w, h, hrun;
+ int srcBytes, dstBytes;
+ int bitsPerPixel;
+ int bits;
+ int ncells;
+ unsigned char *map;
+
+ if ((_qClass(self) == ByteArray)
+ && (_qClass(aByteArray) == ByteArray)
+ && _isSmallInteger(nBitsPerPixel)
+ && _isSmallInteger(height)
+ && _isSmallInteger(width)) {
+ if ((aMapByteArray != nil)
+ && (_Class(aMapByteArray) == ByteArray)) {
+ map = _ByteArrayInstPtr(aMapByteArray)->ba_element;
+ } else {
+ map = (unsigned char *)0;
+ }
+
+ bitsPerPixel = _intVal(nBitsPerPixel);
+ w = _intVal(width);
+ h = _intVal(height);
+ src = _ByteArrayInstPtr(self)->ba_element;
+ dst = _ByteArrayInstPtr(aByteArray)->ba_element;
+ switch (bitsPerPixel) {
+ case 1:
+ mask = 0x01;
+ break;
+ case 2:
+ mask = 0x03;
+ break;
+ case 4:
+ mask = 0x0F;
+ break;
+ case 8:
+ mask = 0xFF;
+ break;
+ default:
+ goto fail;
+ }
+ ncells = mask + 1;
+ if (map) {
+ if ((_qSize(aMapByteArray) - OHDR_SIZE) < ncells)
+ goto fail;
+ }
+
+ bytesPerRow = (w * bitsPerPixel + 7) / 8;
+ shift0 = 8 - bitsPerPixel;
+ srcBytes = bytesPerRow * h;
+ dstBytes = w * h;
+
+ if (((_qSize(self) - OHDR_SIZE) >= srcBytes)
+ && ((_qSize(aByteArray) - OHDR_SIZE) >= dstBytes)) {
+ for (hrun=h; hrun; hrun--) {
+ srcNext = src + bytesPerRow;
+ shift = shift0;
+ if (map) {
+ for (wrun=w; wrun; wrun--) {
+ if (shift == shift0) {
+ bits = *src++;
+ }
+ *dst++ = map[(bits >> shift) & mask];
+ shift -= bitsPerPixel;
+ if (shift < 0) {
+ shift = shift0;
+ }
+ }
+ } else {
+ for (wrun=w; wrun; wrun--) {
+ if (shift == shift0) {
+ bits = *src++;
+ }
+ *dst++ = (bits >> shift) & mask;
+ shift -= bitsPerPixel;
+ if (shift < 0) {
+ shift = shift0;
+ }
+ }
+ }
+ src = srcNext;
+ }
+ RETURN ( self );
+ }
+ }
+ }
+fail: ;
+%}
+.
+ self primitiveFailed
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/CCReader.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,81 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#ClassCategoryReader
+ instanceVariableNames:'myClass myCategory'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Support'
+!
+
+ClassCategoryReader comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+a helper class for fileIn - keeps track of class and category to filein for.
+
+%W% %E%
+written 89 by claus
+'!
+
+!ClassCategoryReader class methodsFor:'instance creation'!
+
+class:aClass category:aCategory
+ "return a new ClassCategoryReader to read methods for aClass with
+ methodCategory aCategory"
+
+ ^ self new class:aClass category:aCategory
+! !
+
+!ClassCategoryReader methodsFor:'private'!
+
+class:aClass category:aCategory
+ "set the instance variables"
+
+ myClass := aClass.
+ myCategory := aCategory
+! !
+
+!ClassCategoryReader methodsFor:'fileIn'!
+
+fileInFrom:aStream notifying:requestor
+ "read method-chunks from the input stream, aStream; compile them
+ and add the methods to the class defined by the class-instance var;
+ errors notifications are passed to requestor"
+
+ |aString done method|
+
+ done := false.
+ [done] whileFalse:[
+ done := aStream atEnd.
+ done ifFalse:[
+ aString := aStream nextChunk.
+ done := aString isNil or:[aString isEmpty].
+ done ifFalse:[
+ method := Compiler compile:aString
+ forClass:myClass
+ inCategory:myCategory
+ notifying:requestor
+ install:true
+ skipIfSame:true
+ ]
+ ]
+ ]
+!
+
+fileInFrom:aStream
+ "read method-chunks from the input stream, aStream; compile them
+ and add the methods to the class defined by the class-instance var"
+
+ self fileInFrom:aStream notifying:nil
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Character.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,573 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Magnitude subclass:#Character
+ instanceVariableNames:'asciivalue'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Magnitude-General'
+!
+
+Character comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+Characters are unique; this means that for every asciiValue (0..255) there
+is exactly one instance of Character, which is shared.
+
+Methods marked as (JS) come from the manchester Character goody
+(CharacterComparing) by Jan Steinman, which allow Characters to be used as
+Interval elements (i.e. ($a to:$z) do:[...] ).
+
+WARNING: characters are known by compiler and runtime system -
+do not change the instance layout. (also, its not easy to define
+subclasses of Character since the Compiler always creates Character
+instances for $x and, since equality check on the Character class is
+wired into the system in many places.)
+
+%W% %E%
+
+'!
+
+!Character class methodsFor:'instance creation'!
+
+basicNew
+ "catch new - Characters cannot be created with new"
+
+ ^ self error:'Characters cannot be created with new'
+!
+
+value:anInteger
+ "return a character with asciivalue anInteger"
+
+%{ /* NOCONTEXT */
+
+ int ascii;
+
+ if (_isSmallInteger(anInteger)) {
+ ascii = _intVal(anInteger);
+ if ((ascii >= 0) && (ascii <= 255))
+ RETURN ( _MKCHARACTER(_intVal(anInteger)) );
+ }
+%}
+.
+ (anInteger between:0 and:16rFF) ifTrue:[
+ ^ CharacterTable at:(anInteger + 1)
+ ].
+ (anInteger between:16r100 and:16rFFFF) ifTrue:[
+ ^ super basicNew setAsciiValue:anInteger
+ ].
+ self error:'invalid ascii code for character'
+!
+
+digitValue:anInteger
+ "return a character that corresponds to anInteger.
+ 0-9 map to $0-$9, 10-35 map to $A-$Z"
+
+ (anInteger between:0 and:9) ifTrue:[
+ ^ Character value:(anInteger + ($0 asciiValue))
+ ].
+ (anInteger between:10 and:35) ifTrue:[
+ ^ Character value:(anInteger - 10 + ($A asciiValue))
+ ].
+ ^self error:'value not in range 0 to 35'
+! !
+
+!Character class methodsFor:'primitive input'!
+
+fromUser
+ "return a character from the keyboard
+ - this should only be used for emergency evaluators and the like."
+
+%{ /* NOCONTEXT */
+ int c;
+
+ c = getchar();
+ if (c < 0) {
+ RETURN (nil);
+ }
+ RETURN ( _MKCHARACTER(c & 0xFF) );
+%}
+! !
+
+!Character class methodsFor:'constants'!
+
+bell
+ "return the bell character"
+
+ ^ Character value:7
+!
+
+backspace
+ "return the backspace character"
+
+ ^ Character value:8
+!
+
+nl
+ "return the newline character"
+
+ ^ Character value:10
+!
+
+lf
+ "return the newline/linefeed character"
+
+ ^ Character value:10
+!
+
+cr
+ "return the carriage-return character
+ - actually (in unix) this is also a newline"
+
+ ^ Character value:10
+!
+
+tab
+ "return the tabulator character"
+
+ ^ Character value:9
+!
+
+newPage
+ "return the form-feed character"
+
+ ^ Character value:12
+!
+
+ff
+ "return the form-feed character"
+
+ ^ Character value:12
+!
+
+space
+ "return the blank character"
+
+ ^ Character value:32
+!
+
+esc
+ "return the escape character"
+
+ ^ Character value:27
+!
+
+quote
+ "return the single-quote character"
+
+ ^ Character value:39
+!
+
+doubleQuote
+ "return the double-quote character"
+
+ ^ Character value:34
+!
+
+excla
+ "return the exclamation-mark character"
+ ^ $!!
+! !
+
+!Character methodsFor:'copying'!
+
+shallowCopy
+ "return a shallow copy of myself
+ reimplemented since characters are unique"
+
+ ^ self
+!
+
+deepCopy
+ "return a depp copy of myself
+ reimplemented since characters are unique"
+
+ ^ self
+! !
+
+!Character methodsFor:'private accessing'!
+
+setAsciiValue:anInteger
+ "very private - set the ascii value - only used for
+ characters with codes > 16rFF"
+
+ asciivalue := anInteger
+! !
+
+!Character methodsFor:'accessing'!
+
+asciiValue
+ "return the asciivalue of myself"
+
+ ^asciivalue
+!
+
+instVarAt:index put:anObject
+ "catch instvar access - asciivalue cannot be changed"
+
+ self error:'Characters may not be modified'
+! !
+
+!Character methodsFor:'converting'!
+
+digitValue
+ "return my digitValue for any base"
+
+ (asciivalue between:($0 asciiValue) and:($9 asciiValue)) ifTrue:[
+ ^ asciivalue - $0 asciiValue
+ ].
+ (asciivalue between:($a asciiValue) and:($z asciiValue)) ifTrue:[
+ ^ asciivalue - $a asciiValue + 10
+ ].
+ (asciivalue between:($A asciiValue) and:($Z asciiValue)) ifTrue:[
+ ^ asciivalue - $A asciiValue + 10
+ ].
+ self error:'bad character'
+! !
+
+!Character methodsFor:'comparing'!
+
+= aCharacter
+ "return true, if the argument, aCharacter is the same character
+ redefined to avoid the overhead of [Object =] -> [Object ==]
+ (although the compiler creates a shortcut code for this)"
+
+ ^ (self == aCharacter)
+!
+
+~= aCharacter
+ "return true, if the argument, aCharacter is not the same character
+ redefined to avoid the overhead of [Object ~=] -> [Object not] -> [Object =] -> [Object ==]
+ (although the compiler creates a shortcut code for this)"
+
+ ^ (self ~~ aCharacter)
+!
+
+> aCharacter
+ "return true, if the arguments asciiValue is less than mine"
+
+ ^ (asciivalue > aCharacter asciiValue)
+!
+
+< aCharacter
+ "return true, if the arguments asciiValue is greater than mine"
+
+ ^ (asciivalue < aCharacter asciiValue)
+!
+
+<= aCharacter
+ "return true, if the arguments asciiValue is greater or equal to mine"
+
+ ^ (asciivalue <= aCharacter asciiValue)
+!
+
+>= aCharacter
+ "return true, if the arguments asciiValue is less or equal to mine"
+
+ ^ (asciivalue >= aCharacter asciiValue)
+!
+
+identityHash
+ "return an integer useful for hashing on identity"
+
+ ^ 4096 + asciivalue
+! !
+
+!Character methodsFor: 'arithmetic'!
+
++ aMagnitude
+ "Return the Character that is <aMagnitude> higher than the receiver.
+ Wrap if the resulting value is not a legal Character value. (JS)"
+
+ ^ Character value:(self asInteger + aMagnitude asInteger \\ 256)
+!
+
+- aMagnitude
+ "Return the Character that is <aMagnitude> lower than the receiver.
+ Wrap if the resulting value is not a legal Character value. (JS)"
+
+ ^ Character value:(self asInteger - aMagnitude asInteger \\ 256)
+!
+
+// aMagnitude
+ "Return the Character who's value is the receiver divided by <aMagnitude>.
+ Wrap if the resulting value is not a legal Character value. (JS)"
+
+ ^ Character value:(self asInteger // aMagnitude asInteger \\ 256)
+!
+
+\\ aMagnitude
+ "Return the Character who's value is the receiver modulo <aMagnitude>.
+ Wrap if the resulting value is not a legal Character value. (JS)"
+
+ ^ Character value:(self asInteger \\ aMagnitude asInteger \\ 256)
+! !
+
+!Character methodsFor:'testing'!
+
+isDigit
+ "return true, if I am a digit (i.e. $0 .. $9)"
+
+ ^ asciivalue between:($0 asciiValue) and:($9 asciiValue)
+!
+
+isDigitRadix:r
+ "return true, if I am a digit of a base r number"
+
+ (asciivalue < $0 asciiValue) ifTrue:[^ false].
+ (r > 10) ifTrue:[
+ (asciivalue between:($0 asciiValue) and:($9 asciiValue)) ifTrue:[
+ ^ true
+ ].
+ ((asciivalue - $a asciiValue) between:0 and:(r - 10)) ifTrue:[
+ ^ true
+ ].
+ ^ (asciivalue - $A asciiValue) between:0 and:(r - 10)
+ ].
+ (asciivalue - $0 asciiValue) < r ifTrue:[^ true].
+ ^ false
+!
+
+isLowercase
+ "return true, if I am a lower-case letter"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int val;
+
+ val = _intVal(_INST(asciivalue));
+ RETURN ( ((val >= 'a') && (val <= 'z')) ? true : false );
+%}
+!
+
+isUppercase
+ "return true, if I am an upper-case letter"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int val;
+
+ val = _intVal(_INST(asciivalue));
+ RETURN ( ((val >= 'A') && (val <= 'Z')) ? true : false );
+%}
+!
+
+isLetter
+ "return true, if I am a letter"
+
+%{ /*NOCONTEXT */
+
+ REGISTER int val;
+
+ val = _intVal(_INST(asciivalue));
+ RETURN ( (((val >= 'a') && (val <= 'z')) ||
+ ((val >= 'A') && (val <= 'Z'))) ? true : false );
+%}
+!
+
+isAlphaNumeric
+ "return true, if I am a letter or a digit"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int val;
+
+ val = _intVal(_INST(asciivalue));
+ if ((val >= 'a') && (val <= 'z')) {
+ RETURN ( true );
+ }
+ if ((val >= 'A') && (val <= 'Z')) {
+ RETURN ( true );
+ }
+ if ((val >= '0') && (val <= '9')) {
+ RETURN ( true );
+ }
+ RETURN ( false );
+%}
+!
+
+isVowel
+ "return true, if I am a vowel (lower- or uppercase)"
+
+ (self == $a) ifTrue:[^ true].
+ (self == $e) ifTrue:[^ true].
+ (self == $i) ifTrue:[^ true].
+ (self == $o) ifTrue:[^ true].
+ (self == $u) ifTrue:[^ true].
+ (self == $A) ifTrue:[^ true].
+ (self == $E) ifTrue:[^ true].
+ (self == $I) ifTrue:[^ true].
+ (self == $O) ifTrue:[^ true].
+ (self == $U) ifTrue:[^ true].
+ ^ false
+!
+
+isSeparator
+ "return true if I am a space, cr, tab, nl, or newPage"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int val;
+
+ val = _intVal(_INST(asciivalue));
+ if (val <= ' ') {
+ if ((val == ' ')
+ || (val == '\n')
+ || (val == '\t')
+ || (val == '\r')
+ || (val == '\f')) {
+ RETURN ( true );
+ }
+ }
+%}
+.
+ ^ false
+!
+
+isEndOfLineCharacter
+ "return true if I am a line delimitting character"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int val;
+
+ val = _intVal(_INST(asciivalue));
+ if (val <= ' ') {
+ if ((val == '\n')
+ || (val == '\r')
+ || (val == '\f')) {
+ RETURN ( true );
+ }
+ }
+%}
+.
+ ^ false
+! !
+
+!Character methodsFor:'converting'!
+
+asLowercase
+ "return a character with same letter as myself but lowercase
+ (myself if I am lowercase)"
+
+ self isUppercase ifFalse:[^ self].
+ ^ Character value:(asciivalue + 32)
+!
+
+asUppercase
+ "return a character with same letter as myself but uppercase
+ (myself if I am lowercase)"
+
+ self isLowercase ifFalse:[^ self].
+ ^ Character value:(asciivalue - 32)
+!
+
+asInteger
+ "return an Integer with my ascii-value"
+
+ ^ asciivalue
+!
+
+asSymbol
+ "return a unique symbol which prints like I print"
+
+ ^ Symbol internCharacter:self
+!
+
+asString
+ "return a string of len 1 with myself as contents"
+
+%{ /* NOCONTEXT */
+
+ char buffer[2];
+
+ buffer[0] = (char) _intVal(_characterVal(self));
+ buffer[1] = '\0';
+ RETURN ( _MKSTRING(buffer COMMA_SND) );
+%}
+
+"
+ |newString|
+
+ newString := String new:1.
+ newString at:1 put:self.
+ ^ newString
+"
+!
+
+to:aMagnitude
+ "Return an Interval over the characters from the receiver to <aMagnitude>.
+ Wrap <aMagnitude> if it is not a legal Character value. (JS)"
+
+ ^ Interval from:self to:(aMagnitude \\ 256)
+
+! !
+
+!Character methodsFor:'printing & storing'!
+
+printString
+ "return a string to print me"
+
+ ^ self asString
+!
+
+printOn:aStream
+ "print myself on aStream"
+
+ aStream nextPut:self
+!
+
+print
+ "print myself on stdout"
+
+%{ /* NOCONTEXT */
+
+ putchar(_intVal(_INST(asciivalue)));
+%}
+!
+
+displayString
+ "return a string used when the receiver is to be displayed
+ in an inspector kind-of-thing"
+
+ ^ self storeString
+!
+
+storeString
+ "return a string for storing"
+
+ (asciivalue between:33 and:127) ifFalse:[
+ (self == Character space) ifTrue:[
+ ^ '(Character space)'
+ ].
+ (self == Character cr) ifTrue:[
+ ^ '(Character cr)'
+ ].
+ ^ '(Character value:' , asciivalue printString , ')'
+ ].
+ ^ '$' , self asString
+!
+
+storeOn:aStream
+ "store myself on aStream"
+
+ (asciivalue between:33 and:127) ifFalse:[
+ aStream nextPutAll:'(Character value:'.
+ aStream nextPutAll:(asciivalue printString).
+ aStream nextPutAll:')'
+ ] ifTrue:[
+ aStream nextPut:$$.
+ aStream nextPut:self
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Class.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1651 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ClassDescription subclass:#Class
+ instanceVariableNames:'classvars comment subclasses'
+ classVariableNames:'updatingChanges'
+ poolDictionaries:''
+ category:'Kernel-Classes'
+!
+
+Class comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+This class adds more functionality to classes; minimum stuff has already
+been defined in Behavior; this one adds naming, categories etc.
+also changes management and recompilation is defined here.
+
+For a minimum system, the compiler generates classes as instances of
+Behavior - this excludes all name, source info etc., however, the more
+usual case is to create instances of Class.
+
+Instance variables:
+
+classvars <String> the names of the class variables
+comment <String> the classes comment
+subclasses <Collection> cached collection of subclasses
+ (currently unused - but will be soon)
+
+Class variables:
+
+updatingChanges <Boolean> true if the changes-file shall be updated
+
+WARNING: layout known by compiler and runtime system
+
+%W% %E%
+written Spring 89 by claus
+'!
+
+!Class class methodsFor:'initialization'!
+
+initialize
+ "the classvariable 'updatingChanges' controls if changes are put
+ into the changes-file; normally this variable is set to true, but
+ for example during fileIn or when changes are applied, it is set to false
+ to prevent changes file from getting too much junk."
+
+ updatingChanges := true
+! !
+
+!Class class methodsFor:'creating new classes'!
+
+new
+ "creates and returs a new class"
+
+ |newClass|
+
+ newClass := super new.
+ newClass setComment:(self comment)
+ category:(self category).
+ ^ newClass
+! !
+
+!Class methodsFor:'autoload check'!
+
+autoload
+ "force autoloading - do nothing here; redefined in Autoload;
+ see comment there"
+
+ ^ self
+! !
+
+!Class methodsFor:'subclass creation'!
+
+subclass:t instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+
+ "create a new class as a subclass of an existing class (the receiver).
+ The subclass will have indexed variables if the receiving-class has."
+
+ self isVariable ifFalse:[
+ ^ self class
+ name:t
+ inEnvironment:Smalltalk
+ subclassOf:self
+ instanceVariableNames:f
+ variable:false
+ words:true
+ pointers:true
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+ comment:nil
+ changed:false
+ ].
+ self isPointers ifTrue:[
+ ^ self
+ variableSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+ ].
+ self isBytes ifTrue:[
+ ^ self
+ variableByteSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+ ].
+ self isLongs ifTrue:[
+ ^ self
+ variableLongSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+ ].
+ self isFloats ifTrue:[
+ ^ self
+ variableFloatSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+ ].
+ self isDoubles ifTrue:[
+ ^ self
+ variableDoubleSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+ ].
+ "only word is left over"
+ ^ self
+ variableWordSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+!
+
+variableSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+
+ "create a new class as a subclass of an existing class (the receiver)
+ in which the subclass has indexable pointer variables"
+
+ self isVariable ifTrue:[
+ self isPointers ifFalse:[
+ ^ self error:
+ 'cannot make a variable pointer subclass of a variable non-pointer class'
+ ]
+ ].
+
+ ^ self class
+ name:t
+ inEnvironment:Smalltalk
+ subclassOf:self
+ instanceVariableNames:f
+ variable:true
+ words:false
+ pointers:true
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+ comment:nil
+ changed:false
+!
+
+variableByteSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+
+ "create a new class as a subclass of an existing class (the receiver)
+ in which the subclass has indexable byte-sized nonpointer variables"
+
+ self isVariable ifTrue:[
+ self isBytes ifFalse:[
+ ^ self error:
+ 'cannot make a variable byte subclass of a variable non-byte class'
+ ].
+ ].
+
+ ^ self class
+ name:t
+ inEnvironment:Smalltalk
+ subclassOf:self
+ instanceVariableNames:f
+ variable:true
+ words:false
+ pointers:false
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+ comment:nil
+ changed:false
+!
+
+variableWordSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+
+ "create a new class as a subclass of an existing class (the receiver)
+ in which the subclass has indexable word-sized nonpointer variables"
+
+ self isVariable ifTrue:[
+ self isWords ifFalse:[
+ ^ self error:
+ 'cannot make a variable word subclass of a variable non-word class'
+ ].
+ ].
+
+ ^ self class
+ name:t
+ inEnvironment:Smalltalk
+ subclassOf:self
+ instanceVariableNames:f
+ variable:true
+ words:true
+ pointers:false
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+ comment:nil
+ changed:false
+!
+
+variableLongSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+
+ "create a new class as a subclass of an existing class (the receiver)
+ in which the subclass has indexable long-sized nonpointer variables"
+
+ self isVariable ifTrue:[
+ self isLongs ifFalse:[
+ ^ self error:
+ 'cannot make a variable long subclass of a variable non-long class'
+ ].
+ ].
+
+ ^ self class
+ name:t
+ inEnvironment:Smalltalk
+ subclassOf:self
+ instanceVariableNames:f
+ variable:#long
+ words:false
+ pointers:false
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+ comment:nil
+ changed:false
+!
+
+variableFloatSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+
+ "create a new class as a subclass of an existing class (the receiver)
+ in which the subclass has indexable float-sized nonpointer variables"
+
+ self isVariable ifTrue:[
+ self isFloats ifFalse:[
+ ^ self error:
+ 'cannot make a variable float subclass of a variable non-float class'
+ ].
+ ].
+
+ ^ self class
+ name:t
+ inEnvironment:Smalltalk
+ subclassOf:self
+ instanceVariableNames:f
+ variable:#float
+ words:false
+ pointers:false
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+ comment:nil
+ changed:false
+!
+
+variableDoubleSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+
+ "create a new class as a subclass of an existing class (the receiver)
+ in which the subclass has indexable double-sized nonpointer variables"
+
+ self isVariable ifTrue:[
+ self isDoubles ifFalse:[
+ ^ self error:
+ 'cannot make a variable double subclass of a variable non-double class'
+ ].
+ ].
+
+ ^ self class
+ name:t
+ inEnvironment:Smalltalk
+ subclassOf:self
+ instanceVariableNames:f
+ variable:#double
+ words:false
+ pointers:false
+ classVariableNames:d
+ poolDictionaries:s
+ category:cat
+ comment:nil
+ changed:false
+! !
+
+!Class methodsFor:'ST/V subclass creation'!
+
+subclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+
+ "this methods allows fileIn of ST/V classes
+ (which seem to have no category)"
+
+ ^ self subclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:'ST/V classes'
+!
+
+variableByteSubclass:t
+ classVariableNames:d
+ poolDictionaries:s
+
+ "this methods allows fileIn of ST/V variable byte classes
+ (which seem to have no category and no instvars)"
+
+ ^ self variableByteSubclass:t
+ instanceVariableNames:''
+ classVariableNames:d
+ poolDictionaries:s
+ category:'ST/V classes'
+!
+
+variableSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+
+ "this methods allows fileIn of ST/V variable pointer classes
+ (which seem to have no category)"
+
+ ^ self variableSubclass:t
+ instanceVariableNames:f
+ classVariableNames:d
+ poolDictionaries:s
+ category:'ST/V classes'
+! !
+
+!Class methodsFor:'accessing'!
+
+classVariableString
+ "return a string of the class variables names "
+
+ classvars isNil ifTrue:[^ ''].
+ ^ classvars
+!
+
+classVarNames
+ "return a collection of the class variable name-strings"
+
+ ^ self addClassVarNamesTo:(OrderedCollection new)
+!
+
+allClassVarNames
+ "return a collection of all the class variable name-strings
+ this includes all superclass-class variables"
+
+ ^ self addAllClassVarNamesTo:(OrderedCollection new)
+!
+
+instVarNames
+ "return a collection of the instance variable name-strings"
+
+ ^ self addInstVarNamesTo:(OrderedCollection new)
+!
+
+allInstVarNames
+ "return a collection of all the instance variable name-strings
+ this includes all superclass-instance variables"
+
+ ^ self addAllInstVarNamesTo:(OrderedCollection new)
+!
+
+comment
+ "return the comment (aString) of the class"
+
+ ^ comment
+!
+
+setComment:aString
+ "set the comment of the class to be the argument, aString;
+ do NOT create a change record"
+
+ comment := aString
+!
+
+comment:aString
+ "set the comment of the class to be the argument, aString;
+ create a change record"
+
+ comment := aString.
+ self addChangeRecordForClassComment:self
+!
+
+definition
+ "return an expression-string to define myself"
+
+ |s|
+
+ s := WriteStream on:(String new).
+ self fileOutDefinitionOn:s.
+ ^ s contents
+
+ "Object definition"
+ "Point definition"
+!
+
+setComment:com category:categoryStringOrSymbol
+ "set the comment and category of the class;
+ do NOT create a change record"
+
+ comment := com.
+ category := categoryStringOrSymbol asSymbol
+!
+
+setName:aString
+ "set the classes name"
+
+ name := aString
+!
+
+setClassVariableString:aString
+ "set the classes classvarnames string"
+
+ classvars := aString
+!
+
+classVariableString:aString
+ "set the classes classvarnames string;
+ initialize new class variables with nil, clear and remove
+ old ones"
+
+ |prevVarNames varNames|
+
+ "ignore for metaclasses except the one"
+ (self isMeta "isKindOf:Metaclass") ifTrue:[
+ (self == Metaclass) ifFalse:[
+ ^ self
+ ]
+ ].
+ (classvars = aString) ifFalse:[
+ prevVarNames := self classVarNames.
+ classvars := aString.
+ varNames := self classVarNames.
+
+ "new ones get initialized to nil;
+ - old ones are nilled and removed from Smalltalk"
+
+ varNames do:[:aName |
+ (prevVarNames includes:aName) ifFalse:[
+ "a new one"
+ Smalltalk at:(self name , ':' , aName) asSymbol put:nil.
+ ] ifTrue:[
+ prevVarNames remove:aName
+ ]
+ ].
+ "left overs are gone"
+ prevVarNames do:[:aName |
+ Smalltalk at:(self name , ':' , aName) asSymbol put:nil.
+ Smalltalk removeKey:(self name , ':' , aName) asSymbol
+ ].
+ Smalltalk changed
+ ]
+!
+
+addClassVarName:aString
+ "add a class variable"
+
+ (self classVarNames includes:aString) ifFalse:[
+ self classVariableString:(self classVariableString , ' ' , aString)
+ ]
+! !
+
+!Class methodsFor:'adding/removing'!
+
+addSelector:newSelector withMethod:newMethod
+ "add the method given by 2nd argument under the selector given by
+ 1st argument to the methodDictionary"
+
+ |index oldSelectorArray oldMethodArray
+ newSelectorArray newMethodArray nargs|
+
+ (newSelector isMemberOf:Symbol) ifFalse:[^ self error:'invalid selector'].
+ newMethod isNil ifTrue:[^ self error:'invalid method'].
+
+ index := selectors identityIndexOf:newSelector startingAt:1.
+ (index == 0) ifTrue:[
+ newSelectorArray := selectors copyWith:newSelector.
+ newMethodArray := methods copyWith:newMethod.
+ "keep a reference so they wont go away ..."
+ oldSelectorArray := selectors.
+ oldMethodArray := methods.
+ selectors := newSelectorArray.
+ methods := newMethodArray
+ ] ifFalse:[
+ methods at:index put:newMethod
+ ].
+
+ nargs := newSelector nArgsIfSelector.
+
+ "actually, we would do better with less flushing ..."
+ ObjectMemory flushMethodCache.
+ ObjectMemory flushInlineCachesWithArgs:nargs.
+
+ self addChangeRecordForMethod:newMethod
+!
+
+removeSelector:aSelector
+ "remove the selector, aSelector and its associated method
+ from the methodDictionary"
+
+ |index oldSelectorArray oldMethodArray
+ newSelectorArray newMethodArray nargs|
+
+ index := selectors identityIndexOf:aSelector startingAt:1.
+ (index ~~ 0) ifTrue:[
+ newSelectorArray := selectors copyWithoutIndex:index.
+ newMethodArray := methods copyWithoutIndex:index.
+ oldSelectorArray := selectors.
+ oldMethodArray := methods.
+ selectors := newSelectorArray.
+ methods := newMethodArray.
+"
+ nargs := aSelector nArgsIfSelector.
+ ObjectMemory flushMethodCacheFor:self.
+ ObjectMemory flushInlineCachesWithArgs:nargs.
+"
+ "actually, we would do better with less flushing ..."
+ ObjectMemory flushCaches.
+
+ self addChangeRecordForRemoveSelector:aSelector
+ ]
+! !
+
+!Class methodsFor:'changes management'!
+
+updateChanges:aBoolean
+ "turn on/off changes management"
+
+ |prev|
+
+ prev := updatingChanges.
+ updatingChanges := aBoolean.
+ ^ prev
+!
+
+changesStream
+ "return a Stream for the changes file"
+
+ |aStream|
+
+ updatingChanges ifTrue:[
+ aStream := FileStream oldFileNamed:'changes'.
+ aStream isNil ifTrue:[
+ aStream := FileStream newFileNamed:'changes'.
+ aStream isNil ifTrue:[
+ self error:'cannot update changes file'
+ ]
+ ] ifFalse:[
+ aStream setToEnd
+ ]
+ ].
+ ^ aStream
+!
+
+addChangeRecordForMethod:aMethod
+ "add a method-change-record to the changes file"
+
+ |aStream p|
+
+ aStream := self changesStream.
+ aStream notNil ifTrue:[
+ p := aStream position.
+ self fileOutMethod:aMethod on:aStream.
+ aStream cr.
+ aStream close.
+ Project current notNil ifTrue:[
+ Project current changeSet addMethodChange:aMethod in:self
+ ]
+ ]
+!
+
+addChangeRecordForRemoveSelector:aSelector
+ "add a method-remove-record to the changes file"
+
+ |aStream|
+
+ aStream := self changesStream.
+ aStream notNil ifTrue:[
+ self printClassNameOn:aStream.
+ aStream nextPutAll:(' removeSelector:#' , aSelector).
+ aStream nextPut:(aStream class chunkSeparator).
+ aStream cr.
+ aStream close
+ ]
+!
+
+addChangeRecordForClass:aClass
+ "add a class-definition-record to the changes file"
+
+ |aStream|
+
+ aStream := self changesStream.
+ aStream notNil ifTrue:[
+ aClass fileOutDefinitionOn:aStream.
+ aStream nextPut:(aStream class chunkSeparator).
+ aStream cr.
+ aStream close
+ ]
+!
+
+addChangeRecordForClassComment:aClass
+ "add a class-comment-record to the changes file"
+
+ |aStream|
+
+ aStream := self changesStream.
+ aStream notNil ifTrue:[
+ aClass fileOutCommentOn:aStream.
+ aStream nextPut:$!!.
+ aStream cr.
+ aStream close
+ ]
+!
+
+addChangeRecordForSnapshot
+ "add a snapshot-record to the changes file"
+
+ |aStream|
+
+ aStream := self changesStream.
+ aStream notNil ifTrue:[
+ aStream nextPutAll:('''---- snapshot ' ,
+ Date today printString , ' ' ,
+ Time now printString ,
+ ' ----''!').
+ aStream cr.
+ aStream close
+ ]
+! !
+
+!Class methodsFor:'compiling'!
+
+compile:code
+ "compile code, aString for this class; if sucessful update method
+ dictionary."
+
+ (Smalltalk at:#Compiler) compile:code forClass:self
+!
+
+compile:code notifying:requestor
+ "compile code, aString for this class; on any error, notify
+ requestor, anObject with the error reason"
+
+ (Smalltalk at:#Compiler) compile:code forClass:self notifying:requestor
+!
+
+recompile:aSelector
+ "recompile the method associated with the argument, aSelector;
+ used when a superclass changes instances and we have to recompile
+ subclasses"
+
+ |cat code|
+
+ cat := (self compiledMethodAt:aSelector) category.
+ code := self sourceCodeAt:aSelector.
+ (Smalltalk at:#Compiler) compile:code forClass:self inCategory:cat
+!
+
+recompile
+ "recompile all methods
+ used when a class changes instances and therefore all methods
+ have to be recompiled"
+
+ self selectors do:[:aSelector |
+ self recompile:aSelector
+ ]
+!
+
+recompileAll
+ "recompile this class and all subclasses"
+
+ |subclasses|
+
+ subclasses := self subclasses.
+ self recompile.
+ subclasses do:[:aClass |
+ aClass recompileAll
+ ]
+! !
+
+!Class methodsFor:'queries'!
+
+selectorIndex:aSelector
+ "return the index in the arrays for given selector aSelector"
+
+ ^ selectors identityIndexOf:aSelector startingAt:1
+!
+
+compiledMethodAt:aSelector
+ "return the method for given selector aSelector"
+
+ |index|
+
+ index := selectors identityIndexOf:aSelector startingAt:1.
+ (index == 0) ifTrue:[^ nil].
+ ^ methods at:index
+!
+
+sourceCodeAt:aSelector
+ "return the methods source for given selector aSelector"
+
+ |index|
+
+ index := selectors identityIndexOf:aSelector startingAt:1.
+ (index == 0) ifTrue:[^ nil].
+ ^ (methods at:index) source
+!
+
+hasMethods
+ "return true, if there are any (local) methods in this class"
+
+ methods isNil ifTrue:[^ false].
+ ^ (methods size ~~ 0)
+!
+
+implements:aSelector
+ "Return true, if I implement selector"
+
+ ^ (selectors identityIndexOf:aSelector startingAt:1) ~~ 0
+!
+
+canUnderstand:aSelector
+ "Return true, if I or one of my superclasses implements selector"
+
+ |classToLookAt|
+
+ classToLookAt := self.
+ [classToLookAt notNil] whileTrue:[
+ (classToLookAt implements:aSelector) ifTrue:[^ true].
+ classToLookAt := classToLookAt superclass
+ ].
+ ^ false
+!
+
+whichClassImplements:aSelector
+ "Return the class (the receiver or a class in the superclass-chain)
+ which implements given selector aSelector, if none, return nil"
+
+ |classToLookAt|
+
+ classToLookAt := self.
+ [classToLookAt notNil] whileTrue:[
+ (classToLookAt implements:aSelector) ifTrue:[^ classToLookAt].
+ classToLookAt := classToLookAt superclass
+ ].
+ ^ nil
+!
+
+selectorForMethod:aMethod
+ "Return the selector for given method aMethod"
+
+ |index|
+
+ index := methods identityIndexOf:aMethod startingAt:1.
+ (index == 0) ifTrue:[^ nil].
+ ^ selectors at:index
+!
+
+containsMethod:aMethod
+ "Return true, if aMethod is a method of myself"
+
+ ^ (methods identityIndexOf:aMethod startingAt:1) ~~ 0
+!
+
+categories
+ "Return a Collection of all method-category strings known in class"
+
+ |newList cat|
+
+ newList := OrderedCollection new.
+ methods do:[:aMethod |
+ cat := aMethod category.
+ newList indexOf:cat ifAbsent:[newList add:cat]
+ ].
+ ^ newList
+!
+
+allCategories
+ "Return a Collection of all method-category strings known in class
+ and all superclasses"
+
+ ^ self addAllCategoriesTo:(OrderedCollection new)
+! !
+
+!Class methodsFor:'private'!
+
+addFromString:aString to:aCollection
+ "helper - take individual words from the first argument, aString
+ and add them as strings to the 2nd argument, aCollection.
+ return aCollection"
+
+ |start stop strLen|
+
+ aString isNil ifFalse:[
+ start := 1.
+ strLen := aString size.
+ [start <= strLen] whileTrue:[
+ (aString at:start) isSeparator ifTrue:[
+ start := start + 1
+ ] ifFalse:[
+ stop := aString indexOfSeparatorStartingAt:start.
+ stop == 0 ifTrue:[
+ stop := strLen + 1
+ ].
+ aCollection add:(aString copyFrom:start to:(stop - 1)).
+ start := stop
+ ]
+ ]
+ ].
+ ^ aCollection
+!
+
+addInstVarNamesTo:aCollection
+ "add the name-strings of the instance variables
+ to the argument, aCollection. Return aCollection"
+
+ ^ self addFromString:instvars to:aCollection
+!
+
+addClassVarNamesTo:aCollection
+ "add the name-strings of the class varvariables
+ to the argument, aCollection. Return aCollection"
+
+ ^ self addFromString:classvars to:aCollection
+!
+
+addAllInstVarNamesTo:aCollection
+ "add the name-strings of the instance variables and of the inst-vars
+ of all superclasses to the argument, aCollection. Return aCollection"
+
+ (superclass notNil) ifTrue:[
+ superclass addAllInstVarNamesTo:aCollection
+ ].
+ ^ self addInstVarNamesTo:aCollection
+!
+
+addAllClassVarNamesTo:aCollection
+ "add the name-strings of the class variables and of the class-vars
+ of all superclasses to the argument, aCollection. Return aCollection"
+
+ (superclass notNil) ifTrue:[
+ superclass addAllClassVarNamesTo:aCollection
+ ].
+ ^ self addClassVarNamesTo:aCollection
+!
+
+addCategoriesTo:aCollection
+ "helper - add categories to the argument, aCollection"
+
+ |cat|
+
+ methods do:[:aMethod |
+ cat := aMethod category.
+ (aCollection detect:[:element | cat = element]
+ ifNone:[nil])
+ isNil ifTrue:[
+ aCollection add:cat
+ ]
+ ].
+ ^ aCollection
+!
+
+addAllCategoriesTo:aCollection
+ "helper - add categories and all superclasses categories
+ to the argument, aCollection"
+
+ (superclass notNil) ifTrue:[
+ superclass addAllCategoriesTo:aCollection
+ ].
+ ^ self addCategoriesTo:aCollection
+! !
+
+!Class methodsFor:'fileIn interface'!
+
+methodsFor:aCategory
+ "return a ClassCategoryReader to read in and compile methods for me"
+
+ ^ ClassCategoryReader class:self category:aCategory
+!
+
+publicMethodsFor:aCategory
+ "this method allows fileIn of ENVY methods - currently we do not support method visibility.
+ return a ClassCategoryReader to read in and compile methods for me."
+
+ ^ self methodsFor:aCategory
+!
+
+privateMethodsFor:aCategory
+ "this method allows fileIn of ENVY methods - currently we do not support method visibility.
+ return a ClassCategoryReader to read in and compile methods for me."
+
+ ^ self methodsFor:aCategory
+!
+
+binaryMethods
+ "return a ClassCategoryReader to read in binary methods for me"
+
+ ^ BinaryClassCategoryReader class:self category:'binary'
+!
+
+methods
+ "this method allows fileIn of ST/V methods -
+ return a ClassCategoryReader to read in and compile methods for me."
+
+ ^ ClassCategoryReader class:self category:'ST/V methods'
+! !
+
+!Class methodsFor:'fileOut'!
+
+printClassNameOn:aStream
+ "helper for fileOut - print my name if I am not a Metaclass;
+ otherwise my name without -class followed by space-class"
+
+ (self isMeta "isMemberOf:Metaclass") ifTrue:[
+ aStream nextPutAll:(name copyFrom:1 to:(name size - 5)).
+ aStream nextPutAll:' class'
+ ] ifFalse:[
+ name printOn:aStream
+ ]
+!
+
+printNameArray:anArray on:aStream indent:indent
+ "print an array of strings separated by spaces; when the stream
+ defines a lineLength, break when this limit is reached; indent
+ every line; used to printOut instanve variable names"
+
+ |thisName nextName arraySize lenMax pos mustBreak line spaces|
+
+ arraySize := 0.
+ anArray notNil ifTrue:[
+ arraySize := anArray size
+ ].
+ arraySize ~~ 0 ifTrue:[
+ pos := indent.
+ lenMax := aStream lineLength.
+ thisName := anArray at:1.
+ line := ''.
+ 1 to:arraySize do:[:index |
+ line := line , thisName.
+ pos := pos + thisName size.
+ (index == arraySize) ifFalse:[
+ nextName := anArray at:(index + 1).
+ mustBreak := false.
+ (lenMax > 0) ifTrue:[
+ ((pos + nextName size) > lenMax) ifTrue:[
+ mustBreak := true
+ ]
+ ].
+ mustBreak ifTrue:[
+ aStream nextPutAll:line.
+ aStream cr.
+ spaces isNil ifTrue:[
+ spaces := String new:indent
+ ].
+ line := spaces.
+ pos := indent
+ ] ifFalse:[
+ line := line , ' '.
+ pos := pos + 1
+ ].
+ thisName := nextName
+ ]
+ ].
+ aStream nextPutAll:line
+ ]
+!
+
+printClassVarNamesOn:aStream indent:indent
+ "print the class variable names indented and breaking at line end"
+
+ self printNameArray:(self classVarNames) on:aStream indent:indent
+!
+
+printInstVarNamesOn:aStream indent:indent
+ "print the instance variable names indented and breaking at line end"
+
+ self printNameArray:(self instVarNames) on:aStream indent:indent
+!
+
+printHierarchyOn:aStream
+ "print my class hierarchy on aStream"
+
+ self printHierarchyAnswerIndentOn:aStream
+!
+
+printHierarchyAnswerIndentOn:aStream
+ "print my class hierarchy on aStream - return indent
+ recursively calls itself to print superclass and use returned indent
+ for my description - used in the browser"
+
+ |indent|
+
+ indent := 0.
+ (superclass notNil) ifTrue:[
+ indent := (superclass printHierarchyAnswerIndentOn:aStream) + 2
+ ].
+ aStream nextPutAll:(String new:indent).
+ aStream nextPutAll:name.
+ aStream nextPutAll:' ('.
+ self printInstVarNamesOn:aStream indent:(indent + name size + 2).
+ aStream nextPutAll:')'.
+ aStream cr.
+ ^ indent
+!
+
+printFullHierarchyOn:aStream indent:indent
+ "print myself and all subclasses on aStream.
+ recursively calls itself to print subclasses.
+ Can be used to print hierarchy on the printer."
+
+ aStream nextPutAll:(String new:indent).
+ aStream bold.
+ aStream nextPutAll:name.
+ aStream normal.
+ aStream nextPutAll:' ('.
+ self printInstVarNamesOn:aStream indent:(indent + name size + 2).
+ aStream nextPutAll:')'.
+ aStream cr.
+
+ (self subclasses sort:[:a :b | a name < b name]) do:[:aSubclass |
+ aSubclass printFullHierarchyOn:aStream indent:(indent + 2)
+ ]
+
+ "|printStream|
+ printStream := Printer new.
+ Object printFullHierarchyOn:printStream indent:0.
+ printStream close"
+!
+
+fileOutCommentOn:aStream
+ "print an expression on aStream to define my comment"
+
+ aStream nextPutAll:name.
+ aStream nextPutAll:' comment:'.
+ comment isNil ifTrue:[
+ aStream nextPutAll:''''''
+ ] ifFalse:[
+ aStream nextPutAll:(comment storeString)
+ ].
+ aStream cr
+!
+
+fileOutDefinitionOn:aStream
+ "print an expression to define myself on aStream"
+
+ |isVar line|
+
+ superclass isNil ifTrue:[
+ line := 'Object'
+ ] ifFalse:[
+ line := (superclass name)
+ ].
+ superclass isNil ifTrue:[
+ isVar := self isVariable
+ ] ifFalse:[
+ "I cant remember what this is for ?"
+ isVar := (self isVariable and:[superclass isVariable not])
+ ].
+ isVar ifTrue:[
+ self isPointers ifTrue:[
+ line := line , ' variableSubclass:#'
+ ] ifFalse:[
+ self isBytes ifTrue:[
+ line := line , ' variableByteSubclass:#'
+ ] ifFalse:[
+ self isWords ifTrue:[
+ line := line , ' variableWordSubclass:#'
+ ] ifFalse:[
+ self isLongs ifTrue:[
+ line := line , ' variableLongSubclass:#'
+ ] ifFalse:[
+ self isFloats ifTrue:[
+ line := line , ' variableFloatSubclass:#'
+ ] ifFalse:[
+ line := line , ' variableDoubleSubclass:#'
+ ]
+ ]
+ ]
+ ]
+ ]
+ ] ifFalse:[
+ line := line , ' subclass:#'
+ ].
+ line := line , name.
+ aStream nextPutAll:line.
+
+ aStream crTab.
+ aStream nextPutAll:' instanceVariableNames:'''.
+ self printInstVarNamesOn:aStream indent:16.
+ aStream nextPutAll:''''.
+
+ aStream crTab.
+ aStream nextPutAll:' classVariableNames:'''.
+ self printClassVarNamesOn:aStream indent:16.
+ aStream nextPutAll:''''.
+
+ aStream crTab.
+ aStream nextPutAll:' poolDictionaries:'''''.
+
+ aStream crTab.
+ aStream nextPutAll:' category:'.
+ category isNil ifTrue:[
+ aStream nextPutAll:''''''
+ ] ifFalse:[
+ aStream nextPutAll:(category asString storeString)
+ ].
+ aStream cr
+!
+
+fileOutClassInstVarDefinitionOn:aStream
+ aStream nextPutAll:(name , ' class instanceVariableNames:''').
+ self class printInstVarNamesOn:aStream indent:8.
+ aStream nextPutAll:''''
+!
+
+fileOutCategory:aCategory on:aStream
+ "file out all methods belonging to aCategory, aString onto aStream"
+
+ |nMethods count|
+
+ methods notNil ifTrue:[
+ nMethods := 0.
+ methods do:[:aMethod |
+ (aCategory = aMethod category) ifTrue:[
+ nMethods := nMethods + 1
+ ]
+ ].
+ (nMethods ~~ 0) ifTrue:[
+ aStream nextPut:$!!.
+ self printClassNameOn:aStream.
+ aStream nextPutAll:' methodsFor:'''.
+ aCategory notNil ifTrue:[
+ aStream nextPutAll:aCategory
+ ].
+ aStream nextPut:$'. aStream nextPut:$!!. aStream cr.
+ aStream cr.
+ count := 1.
+ methods do:[:aMethod |
+ (aCategory = aMethod category) ifTrue:[
+ aStream nextChunkPut:(aMethod source).
+ (count ~~ nMethods) ifTrue:[
+ aStream cr.
+ aStream cr
+ ].
+ count := count + 1
+ ]
+ ].
+ aStream space.
+ aStream nextPut:$!!.
+ aStream cr
+ ]
+ ]
+!
+
+fileOutMethod:aMethod on:aStream
+ "file out the method, aMethod onto aStream"
+
+ |cat|
+
+ methods notNil ifTrue:[
+ aStream nextPut:$!!.
+ self printClassNameOn:aStream.
+ aStream nextPutAll:' methodsFor:'''.
+ cat := aMethod category.
+ cat notNil ifTrue:[
+ aStream nextPutAll:cat
+ ].
+ aStream nextPut:$'.
+ aStream nextPut:$!!.
+ aStream cr.
+ aStream cr.
+ aStream nextChunkPut:(aMethod source).
+ aStream space.
+ aStream nextPut:$!!.
+ aStream cr
+ ]
+!
+
+fileOutOn:aStream
+ "file out all methods onto aStream"
+
+ |collectionOfCategories|
+
+ aStream nextPutAll:(Smalltalk timeStamp).
+ aStream nextPut:$!.
+ aStream cr.
+ aStream cr.
+ self fileOutDefinitionOn:aStream.
+ aStream nextPut:$!!.
+ aStream cr.
+ aStream cr.
+ self class instanceVariableString isBlank ifFalse:[
+ self fileOutClassInstVarDefinitionOn:aStream.
+ aStream nextPut:$!!.
+ aStream cr.
+ aStream cr
+ ].
+
+ comment notNil ifTrue:[
+ aStream nextPutAll:name.
+ aStream nextPutAll:' comment:'.
+ aStream nextPutAll:(comment storeString).
+ aStream nextPut:$!!.
+ aStream cr.
+ aStream cr
+ ].
+ collectionOfCategories := self class categories.
+ collectionOfCategories notNil ifTrue:[
+ collectionOfCategories do:[:aCategory |
+ self class fileOutCategory:aCategory on:aStream.
+ aStream cr
+ ]
+ ].
+ collectionOfCategories := self categories.
+ collectionOfCategories notNil ifTrue:[
+ collectionOfCategories do:[:aCategory |
+ self fileOutCategory:aCategory on:aStream.
+ aStream cr
+ ]
+ ].
+ (self class implements:#initialize) ifTrue:[
+ aStream nextPutAll:(name , ' initialize').
+ aStream nextPut:$!!.
+ aStream cr
+ ]
+!
+
+fileOutCategory:aCategory
+ "create a file 'class-category.st' consisting of all methods in aCategory"
+
+ |aStream fileName|
+
+ fileName := name , '-' , aCategory , '.st'.
+ aStream := FileStream newFileNamed:fileName.
+ self fileOutCategory:aCategory on:aStream.
+ aStream close
+!
+
+fileOutMethod:aMethod
+ "create a file 'class-method.st' consisting of the method, aMethod"
+
+ |aStream fileName selector|
+
+ selector := self selectorForMethod:aMethod.
+ selector notNil ifTrue:[
+ fileName := name , '-' , selector, '.st'.
+ aStream := FileStream newFileNamed:fileName.
+ self fileOutMethod:aMethod on:aStream.
+ aStream close
+ ]
+!
+
+fileOut
+ "create a file 'class.st' consisting of all methods in myself"
+
+ |aStream fileName|
+
+ fileName := (Smalltalk fileNameForClass:self name) , '.st'.
+ aStream := FileStream newFileNamed:fileName.
+ aStream isNil ifTrue:[
+ ^ self error:('cannot create source file:', fileName)
+ ].
+ self fileOutOn:aStream.
+ aStream close
+!
+
+fileOutIn:aFileDirectory
+ "create a file 'class.st' consisting of all methods in self in
+ directory aFileDirectory"
+
+ |aStream fileName|
+
+ fileName := (Smalltalk fileNameForClass:self) , '.st'.
+ aStream := FileStream newFileNamed:fileName
+ in:aFileDirectory.
+ aStream isNil ifTrue:[
+ ^ self error:('cannot create source file:', fileName)
+ ].
+ self fileOutOn:aStream.
+ aStream close
+!
+
+binaryFileOutMethodsOn:aStream
+ "binary file out all methods onto aStream"
+
+ |temporaryMethod index|
+
+ methods notNil ifTrue:[
+ aStream nextPut:$!!.
+ self printClassNameOn:aStream.
+ aStream nextPutAll:' binaryMethods'.
+ aStream nextPut:$!!.
+ aStream cr.
+ index := 1.
+ methods do:[:aMethod |
+ (selectors at:index) storeOn:aStream.
+ aStream nextPut:$!!.
+
+ aMethod byteCode isNil ifTrue:[
+ temporaryMethod := Compiler compile:(aMethod source)
+ forClass:self
+ inCategory:(aMethod category)
+ notifying:nil
+ install:false.
+ temporaryMethod binaryFileOutOn:aStream
+ ] ifFalse:[
+ aMethod binaryFileOutOn:aStream
+ ].
+ aStream cr.
+ index := index + 1
+ ].
+ aStream nextPut:$!!.
+ aStream cr
+ ]
+!
+
+binaryFileOutOn:aStream
+ "file out all methods onto aStream"
+
+ aStream nextPut:$'.
+ aStream nextPutAll:('From Smalltalk/X, Version:'
+ , (Smalltalk version)
+ , ' on ').
+ aStream nextPutAll:(Date today printString , ' at ' , Time now printString).
+ aStream nextPut:$'.
+ aStream nextPut:$!!.
+ aStream cr.
+ self fileOutDefinitionOn:aStream.
+ aStream nextPut:$!!.
+ aStream cr.
+ comment notNil ifTrue:[
+ aStream nextPutAll:name.
+ aStream nextPutAll:' comment:'.
+ aStream nextPutAll:(comment storeString).
+ aStream nextPut:$!!.
+ aStream cr
+ ].
+ self class binaryFileOutMethodsOn:aStream.
+ self binaryFileOutMethodsOn:aStream.
+ (self class implements:#initialize) ifTrue:[
+ aStream nextPutAll:(name , ' initialize').
+ aStream nextPut:$!!.
+ aStream cr
+ ]
+!
+
+binaryFileOut
+ "create a file 'class.sb' consisting of all methods in myself"
+
+ |aStream fileName|
+
+ fileName := (Smalltalk fileNameForClass:self name) , '.sb'.
+ aStream := FileStream newFileNamed:fileName.
+ aStream isNil ifTrue:[
+ ^ self error:('cannot create class file:', fileName)
+ ].
+ self binaryFileOutOn:aStream.
+ aStream close
+! !
+
+!Class methodsFor:'printOut'!
+
+printOutDefinitionOn:aPrintStream
+ "print out my definition"
+
+ aPrintStream nextPutAll:'class '.
+ aPrintStream bold.
+ aPrintStream nextPutAll:name.
+ aPrintStream normal.
+ aPrintStream cr.
+
+ aPrintStream nextPutAll:'superclass '.
+ superclass isNil ifTrue:[
+ aPrintStream nextPutAll:'Object'
+ ] ifFalse:[
+ aPrintStream nextPutAll:(superclass name)
+ ].
+ aPrintStream cr.
+
+ aPrintStream nextPutAll:'instance Variables '.
+ self printInstVarNamesOn:aPrintStream indent:21.
+ aPrintStream cr.
+
+ aPrintStream nextPutAll:'class Variables '.
+ self printClassVarNamesOn:aPrintStream indent:21.
+ aPrintStream cr.
+
+ category notNil ifTrue:[
+ aPrintStream nextPutAll:'category '.
+ aPrintStream nextPutAll:(category printString).
+ aPrintStream cr
+ ].
+
+ comment notNil ifTrue:[
+ aPrintStream cr.
+ aPrintStream nextPutAll:'comment:'.
+ aPrintStream cr.
+ aPrintStream italic.
+ aPrintStream nextPutAll:comment.
+ aPrintStream normal.
+ aPrintStream cr
+ ]
+!
+
+printOutSourceProtocol:aString on:aPrintStream
+ "given the source in aString, print the methods message specification
+ and any method comments - without source; used to generate documentation
+ pages"
+
+ |text line nQuote index|
+
+ text := aString asText.
+ (text size < 1) ifTrue:[^self].
+ aPrintStream bold.
+ aPrintStream nextPutAll:(text at:1).
+ aPrintStream cr.
+ (text size >= 2) ifTrue:[
+ aPrintStream italic.
+ line := (text at:2).
+ nQuote := line occurrencesOf:(Character doubleQuote).
+ (nQuote == 2) ifTrue:[
+ aPrintStream nextPutAll:line.
+ aPrintStream cr
+ ] ifFalse:[
+ (nQuote == 1) ifTrue:[
+ aPrintStream nextPutAll:line.
+ aPrintStream cr.
+ index := 3.
+ line := text at:index.
+ nQuote := line occurrencesOf:(Character doubleQuote).
+ [nQuote ~~ 1] whileTrue:[
+ aPrintStream nextPutAll:line.
+ aPrintStream cr.
+ index := index + 1.
+ line := text at:index.
+ nQuote := line occurrencesOf:(Character doubleQuote)
+ ].
+ aPrintStream nextPutAll:(text at:index).
+ aPrintStream cr
+ ]
+ ]
+ ].
+ aPrintStream normal
+!
+
+printOutSource:aString on:aPrintStream
+ "print out a source-string; the message-specification is printed bold,
+ comments are printed italic"
+
+ |text textIndex textSize line lineIndex lineSize inComment aCharacter|
+ text := aString asText.
+ aPrintStream bold.
+ aPrintStream nextPutAll:(text at:1).
+ aPrintStream normal.
+ aPrintStream cr.
+ inComment := false.
+ textSize := text size.
+ textIndex := 2.
+ [textIndex <= textSize] whileTrue:[
+ line := text at:textIndex.
+ ((line occurrencesOf:Character doubleQuote) == 0) ifTrue:[
+ aPrintStream nextPutAll:line
+ ] ifFalse:[
+ lineSize := line size.
+ lineIndex := 1.
+ [lineIndex <= lineSize] whileTrue:[
+ aCharacter := line at:lineIndex.
+ (aCharacter == Character doubleQuote) ifTrue:[
+ inComment ifTrue:[
+ aPrintStream normal.
+ aPrintStream nextPut:aCharacter.
+ inComment := false
+ ] ifFalse:[
+ aPrintStream nextPut:aCharacter.
+ aPrintStream italic.
+ inComment := true
+ ]
+ ] ifFalse:[
+ aPrintStream nextPut:aCharacter
+ ].
+ lineIndex := lineIndex + 1
+ ]
+ ].
+ aPrintStream cr.
+ textIndex := textIndex + 1
+ ]
+!
+
+printOutCategory:aCategory on:aPrintStream
+ "print out all methods in aCategory on aPrintStream should be a PrintStream"
+
+ |any|
+ methods notNil ifTrue:[
+ any := false.
+ methods do:[:aMethod |
+ (aCategory = aMethod category) ifTrue:[
+ any := true
+ ]
+ ].
+ any ifTrue:[
+ aPrintStream italic.
+ aPrintStream nextPutAll:aCategory.
+ aPrintStream normal.
+ aPrintStream cr.
+ aPrintStream cr.
+ methods do:[:aMethod |
+ (aCategory = aMethod category) ifTrue:[
+ self printOutSource:(aMethod source) on:aPrintStream.
+ aPrintStream cr.
+ aPrintStream cr
+ ]
+ ].
+ aPrintStream cr
+ ]
+ ]
+!
+
+printOutOn:aPrintStream
+ "print out all methods on aPrintStream which should be a printStream"
+
+ |collectionOfCategories|
+ self printOutDefinitionOn:aPrintStream.
+ aPrintStream cr.
+ collectionOfCategories := self class categories.
+ collectionOfCategories notNil ifTrue:[
+ aPrintStream nextPutAll:'class protocol'.
+ aPrintStream cr. aPrintStream cr.
+ collectionOfCategories do:[:aCategory |
+ self class printOutCategory:aCategory on:aPrintStream
+ ]
+ ].
+ collectionOfCategories := self categories.
+ collectionOfCategories notNil ifTrue:[
+ aPrintStream nextPutAll:'instance protocol'.
+ aPrintStream cr. aPrintStream cr.
+ collectionOfCategories do:[:aCategory |
+ self printOutCategory:aCategory on:aPrintStream
+ ]
+ ]
+!
+
+printOutCategoryProtocol:aCategory on:aPrintStream
+ |any|
+ methods notNil ifTrue:[
+ any := false.
+ methods do:[:aMethod |
+ (aCategory = aMethod category) ifTrue:[
+ any := true
+ ]
+ ].
+ any ifTrue:[
+ aPrintStream italic.
+ aPrintStream nextPutAll:aCategory.
+ aPrintStream normal.
+ aPrintStream cr.
+ aPrintStream cr.
+ methods do:[:aMethod |
+ (aCategory = aMethod category) ifTrue:[
+ self printOutSourceProtocol:(aMethod source)
+ on:aPrintStream.
+ aPrintStream cr.
+ aPrintStream cr
+ ]
+ ].
+ aPrintStream cr
+ ]
+ ]
+!
+
+printOutProtocolOn:aPrintStream
+ |collectionOfCategories|
+ self printOutDefinitionOn:aPrintStream.
+ aPrintStream cr.
+ collectionOfCategories := self class categories.
+ collectionOfCategories notNil ifTrue:[
+ aPrintStream nextPutAll:'class protocol'.
+ aPrintStream cr. aPrintStream cr.
+ collectionOfCategories do:[:aCategory |
+ self class printOutCategoryProtocol:aCategory on:aPrintStream
+ ]
+ ].
+ collectionOfCategories := self categories.
+ collectionOfCategories notNil ifTrue:[
+ aPrintStream nextPutAll:'instance protocol'.
+ aPrintStream cr. aPrintStream cr.
+ collectionOfCategories do:[:aCategory |
+ self printOutCategoryProtocol:aCategory on:aPrintStream
+ ]
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ClassCategoryReader.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,81 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#ClassCategoryReader
+ instanceVariableNames:'myClass myCategory'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Support'
+!
+
+ClassCategoryReader comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+a helper class for fileIn - keeps track of class and category to filein for.
+
+%W% %E%
+written 89 by claus
+'!
+
+!ClassCategoryReader class methodsFor:'instance creation'!
+
+class:aClass category:aCategory
+ "return a new ClassCategoryReader to read methods for aClass with
+ methodCategory aCategory"
+
+ ^ self new class:aClass category:aCategory
+! !
+
+!ClassCategoryReader methodsFor:'private'!
+
+class:aClass category:aCategory
+ "set the instance variables"
+
+ myClass := aClass.
+ myCategory := aCategory
+! !
+
+!ClassCategoryReader methodsFor:'fileIn'!
+
+fileInFrom:aStream notifying:requestor
+ "read method-chunks from the input stream, aStream; compile them
+ and add the methods to the class defined by the class-instance var;
+ errors notifications are passed to requestor"
+
+ |aString done method|
+
+ done := false.
+ [done] whileFalse:[
+ done := aStream atEnd.
+ done ifFalse:[
+ aString := aStream nextChunk.
+ done := aString isNil or:[aString isEmpty].
+ done ifFalse:[
+ method := Compiler compile:aString
+ forClass:myClass
+ inCategory:myCategory
+ notifying:requestor
+ install:true
+ skipIfSame:true
+ ]
+ ]
+ ]
+!
+
+fileInFrom:aStream
+ "read method-chunks from the input stream, aStream; compile them
+ and add the methods to the class defined by the class-instance var"
+
+ self fileInFrom:aStream notifying:nil
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ClassDescr.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,80 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Behavior subclass:#ClassDescription
+ instanceVariableNames:'name category instvars'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Classes'
+!
+
+ClassDescription comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+this class has been added for ST-80 compatibility only.
+All class stuff used to be in Behavior and Class - but, to be
+able to file in some PD code, it became nescessary to add it.
+
+Instance variables:
+
+name <String> the classes name
+category <String> the classes category
+instvars <String> the names of the instance variables
+
+%W% %E%
+'!
+
+!ClassDescription methodsFor:'accessing'!
+
+instanceVariableString
+ "return a string of the instance variable names"
+
+ instvars isNil ifTrue:[^ ''].
+ ^ instvars
+!
+
+instanceVariableString:aString
+ "set the classes instvarnames string - notice, that this
+ should be used only during class creation; the number of
+ instance variables is determined by another instance
+ (see Behavior)."
+
+ instvars := aString.
+ self changed
+!
+
+setName:aString
+ "set the classes name - be careful, it will be still
+ in the Smalltalk dictionary - under another key"
+
+ name := aString
+!
+
+name
+ "return the name (aString) of the class"
+
+ ^ name
+!
+
+category
+ "return the category (aString) of the class"
+
+ ^ category
+!
+
+category:aStringOrSymbol
+ "set the category of the class to be the argument, aString"
+
+ category := aStringOrSymbol asSymbol
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ClassDescription.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,80 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Behavior subclass:#ClassDescription
+ instanceVariableNames:'name category instvars'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Classes'
+!
+
+ClassDescription comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+this class has been added for ST-80 compatibility only.
+All class stuff used to be in Behavior and Class - but, to be
+able to file in some PD code, it became nescessary to add it.
+
+Instance variables:
+
+name <String> the classes name
+category <String> the classes category
+instvars <String> the names of the instance variables
+
+%W% %E%
+'!
+
+!ClassDescription methodsFor:'accessing'!
+
+instanceVariableString
+ "return a string of the instance variable names"
+
+ instvars isNil ifTrue:[^ ''].
+ ^ instvars
+!
+
+instanceVariableString:aString
+ "set the classes instvarnames string - notice, that this
+ should be used only during class creation; the number of
+ instance variables is determined by another instance
+ (see Behavior)."
+
+ instvars := aString.
+ self changed
+!
+
+setName:aString
+ "set the classes name - be careful, it will be still
+ in the Smalltalk dictionary - under another key"
+
+ name := aString
+!
+
+name
+ "return the name (aString) of the class"
+
+ ^ name
+!
+
+category
+ "return the category (aString) of the class"
+
+ ^ category
+!
+
+category:aStringOrSymbol
+ "set the category of the class to be the argument, aString"
+
+ category := aStringOrSymbol asSymbol
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Coll.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,546 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Collection
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Abstract'
+!
+
+Collection comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+Abstract superclass for all collections
+
+%W% %E%
+'!
+
+!Collection class methodsFor:'instance creation'!
+
+with:anObject
+ "return a new Collection with one element:anObject"
+
+ |newCollection|
+
+ newCollection := self new.
+ newCollection add:anObject.
+ ^ newCollection
+!
+
+with:firstObject with:secondObject
+ "return a new Collection with two elements:firstObject and secondObject"
+
+ |newCollection|
+
+ newCollection := self new.
+ newCollection add:firstObject.
+ newCollection add:secondObject.
+ ^ newCollection
+!
+
+with:firstObject with:secondObject with:thirdObject
+ "return a new Collection with three elements"
+
+ |newCollection|
+
+ newCollection := self new.
+ newCollection add:firstObject.
+ newCollection add:secondObject.
+ newCollection add:thirdObject.
+ ^ newCollection
+!
+
+with:firstObject with:secondObject with:thirdObject with:fourthObject
+ "return a new Collection with four elements"
+
+ |newCollection|
+
+ newCollection := self new.
+ newCollection add:firstObject.
+ newCollection add:secondObject.
+ newCollection add:thirdObject.
+ newCollection add:fourthObject.
+ ^ newCollection
+!
+
+new:size withAll:element
+ "return a new COllection of size, where all elements are
+ initialized to element"
+
+ |newCollection|
+
+ newCollection := self new:size.
+ size timesRepeat:[newCollection add:element]
+!
+
+withAll:aCollection
+ "return a new Collection with all elements taken from the argument,
+ aCollection"
+
+ |newCollection|
+
+ newCollection := self new.
+ newCollection addAll:aCollection.
+ ^newCollection
+! !
+
+!Collection methodsFor:'error handling'!
+
+errorNotFound
+ "report an error that Object was not in the collection"
+
+ self error:'Object is not in the Collection'
+!
+
+errorNotKeyed
+ "report an error that keyed access methods are not allowed"
+
+ self error:(self class name, 's do not respond to keyed accessing messages')
+! !
+
+!Collection methodsFor:'accessing'!
+
+anElement
+ "return any element from the collection"
+
+ self do: [:each | ^ each].
+ ^ nil
+! !
+
+!Collection methodsFor:'adding & removing'!
+
+add:anObject
+ "add the argument, anObject to the receiver"
+
+ ^ self subclassResponsibility
+!
+
+addLast:anObject
+ "add the argument, anObject to the receiver"
+
+ ^ self add:anObject
+!
+
+addAll:aCollection
+ "add all elements of the argument, aCollection to the receiver"
+
+ aCollection do:[:element |
+ self add:element
+ ].
+ ^ aCollection
+!
+
+remove:anObject ifAbsent:exceptionBlock
+ "remove the argument, anObject from the receiver - if it was not
+ in the collection returns the the value of the exceptionBlock"
+
+ ^ self subclassResponsibility
+!
+
+remove:anObject
+ "remove the argument, anObject from the receiver"
+
+ self remove:anObject ifAbsent:[self errorNotFound]
+!
+
+removeAll:aCollection
+ "remove all elements of the argument, aCollection from the receiver"
+
+ aCollection do:[:element | self remove:element].
+ ^ aCollection
+! !
+
+!Collection methodsFor:'growing'!
+
+growSize
+ "return a suitable size increment for growing"
+
+ ^ self size max:2
+!
+
+grow
+ "make the receiver larger"
+
+ self grow:(self size + self growSize)
+!
+
+grow:howBig
+ "change the receivers size"
+
+ ^ self subclassResponsibility
+! !
+
+!Collection methodsFor:'testing'!
+
+isEmpty
+ "return true, if the receiver is empty"
+
+ ^ self size == 0
+!
+
+includes:anElement
+ "return true, if the argument, anObject is in the list"
+
+ self do:[:element |
+ (anElement = element) ifTrue:[^ true].
+ ].
+ ^ false
+!
+
+includesAll:aCollection
+ "return true, if the the receiver includes all elements of
+ the argument, aCollection; false if any is missing"
+
+ aCollection do:[:element |
+ (self includes:element) ifFalse:[^ false].
+ ].
+ ^ true
+!
+
+occurrencesOf:anElement
+ "return the number of occurrences of the argument, anElement in
+ the receiver"
+
+ |count "<SmallInteger>" |
+
+ count := 0.
+ self do:[:element |
+ (anElement = element) ifTrue:[
+ count := count + 1
+ ].
+ ].
+ ^ count
+!
+
+size
+ "return the number of elements in the receiver"
+
+ |count "<SmallInteger>" |
+
+ count := 0.
+ self do:[:element |
+ count := count + 1
+ ].
+ ^ count
+! !
+
+!Collection methodsFor:'enumerating'!
+
+do:aBlock
+ "evaluate the argument, aBlock for each element"
+
+ ^ self subclassResponsibility
+!
+
+collect:aBlock
+ "for each element in the receiver, evaluate the argument, aBlock
+ and return a new collection with the results"
+
+ |newCollection|
+
+ newCollection := self species new.
+ self do:[:each | newCollection add:(aBlock value:each)].
+ ^ newCollection
+!
+
+detect:aBlock
+ "evaluate the argument, aBlock for each element in the receiver until
+ the block returns true; in this case return the element which caused
+ the true evaluation.
+ If none of the evaluations return true, report an error"
+
+ ^ self detect:aBlock ifNone:[self errorNotFound]
+!
+
+detect:aBlock ifNone:exceptionBlock
+ "evaluate the argument, aBlock for each element in the receiver until
+ the block returns true; in this case return the element which caused
+ the true evaluation.
+ If none of the evaluations returns true, return the result of the
+ evaluation of the exceptionBlock"
+
+ self do:[:each |
+ (aBlock value:each) ifTrue:[^ each].
+ ].
+ ^ exceptionBlock value
+!
+
+inject:thisValue into:binaryBlock
+ |nextValue|
+
+ nextValue := thisValue.
+ self do: [:each | nextValue := binaryBlock value:nextValue value:each].
+ ^ nextValue
+!
+
+reject:aBlock
+ "return a new collection with all elements from the receiver, for which
+ the argument aBlock evaluates to false"
+
+ ^ self select:[:element | (aBlock value:element) == false]
+!
+
+select:aBlock
+ "return a new collection with all elements from the receiver, for which
+ the argument aBlock evaluates to true"
+
+ |newCollection|
+
+ newCollection := self species new.
+ self do:[:each |
+ (aBlock value:each) ifTrue:[newCollection add:each].
+ ].
+ ^ newCollection
+! !
+
+!Collection methodsFor:'converting'!
+
+asArray
+ "return a new Array with the collections elements"
+
+ |anArray
+ index "<SmallInteger>" |
+
+ anArray := Array new:(self size).
+ index := 1.
+ self do:[:each |
+ anArray at:index put:each.
+ index := index + 1
+ ].
+ ^ anArray
+!
+
+asByteArray
+ "return a new ByteArray with the collections elements"
+
+ |aByteArray
+ index "<SmallInteger>" |
+
+ aByteArray := ByteArray new:(self size).
+ index := 1.
+ self do:[:each |
+ aByteArray at:index put:each asInteger.
+ index := index + 1
+ ].
+ ^ aByteArray
+!
+
+asString
+ "return a String with the collections elements
+ (which must convertable to characters)"
+
+ |aString
+ index "<SmallInteger>" |
+
+ aString := String new:(self size).
+ index := 1.
+ self do:[:each |
+ aString at:index put:each asCharacter.
+ index := index + 1
+ ].
+ ^ aString
+!
+
+asText
+ "return a new Text-object with the elements printstings"
+
+ |aText
+ index "<SmallInteger>" |
+
+ aText := Text new:(self size).
+ index := 1.
+ self do:[:each |
+ aText at:index put:(each printString).
+ index := index + 1
+ ].
+ ^ aText
+!
+
+asBag
+ "return a new Bag with the receiver collections elements"
+
+ |aBag|
+
+ aBag := Bag new.
+ self do:[:each | aBag add:each].
+ ^ aBag
+!
+
+asOrderedCollection
+ "return a new OrderedCollection with the receiver collections elements"
+
+ |anOrderedCollection|
+
+ anOrderedCollection := OrderedCollection new:self size.
+ self do:[:each | anOrderedCollection addLast:each].
+ ^ anOrderedCollection
+!
+
+asSet
+ "return a new Set with the receiver collections elements"
+
+ |aSet|
+
+ aSet := Set new: self size.
+ self do:[:each | aSet add:each].
+ ^ aSet
+!
+
+asSortedCollection
+ "return a new SortedCollection with the receiver collections elements"
+
+ |aSortedCollection|
+
+ aSortedCollection := SortedCollection new:self size.
+ aSortedCollection addAll:self.
+ ^ aSortedCollection
+!
+
+asSortedCollection:sortBlock
+ "return a new SortedCollection with the receiver collections elements,
+ using sortBlock for comparing"
+
+ |aSortedCollection|
+
+ aSortedCollection := SortedCollection sortBlock:sortBlock.
+ aSortedCollection addAll:self.
+ ^ aSortedCollection
+! !
+
+!Collection methodsFor:'printing & storing'!
+
+maxPrint
+ ^ 5000
+!
+
+printString
+ "return the printString of a big collection can take a long time
+ due to long temporary strings - I use a buffer here collecting some
+ elements to reduce the GC overhead ...
+ "
+
+ |thisString buffer count string noneYet total|
+
+ string := (self class name) , '('.
+ noneYet := true.
+ buffer := ''.
+ count := 0.
+ total := 0.
+ self do: [:element |
+ thisString := element printString.
+ noneYet ifTrue:[
+ noneYet := false.
+ buffer := buffer , thisString
+ ] ifFalse:[
+ buffer := buffer , (' ' , thisString)
+ ].
+ count := count + 1.
+ (count == 20) ifTrue:[
+ string := string , buffer.
+ buffer := ''.
+ count := 0
+ ].
+ total := total + 1.
+ (total > 5000) ifTrue:[
+ string := string , buffer , '... )'.
+ ^string
+ ]
+ ].
+ string := string , buffer , ')'.
+ ^string
+!
+
+displayString
+ "return the printString of a big collection can take a long time
+ due to long temporary strings - I use a buffer here collecting some
+ elements to reduce the GC overhead ...
+ "
+
+ |thisString buffer count string noneYet total|
+
+ string := (self class name) , '('.
+ noneYet := true.
+ buffer := ''.
+ count := 0.
+ total := 0.
+ self do: [:element |
+ thisString := element displayString.
+ noneYet ifTrue:[
+ noneYet := false.
+ buffer := buffer , thisString
+ ] ifFalse:[
+ buffer := buffer , (' ' , thisString)
+ ].
+ count := count + 1.
+ (count == 20) ifTrue:[
+ string := string , buffer.
+ buffer := ''.
+ count := 0
+ ].
+ total := total + 1.
+ (total > 5000) ifTrue:[
+ string := string , buffer , '... )'.
+ ^string
+ ]
+ ].
+ string := string , buffer , ')'.
+ ^string
+!
+
+printOn:aStream
+ |tooMany firstOne noMore|
+
+ tooMany := aStream position + self maxPrint.
+ aStream nextPutAll:self class name.
+ aStream nextPut:$(.
+ firstOne := true.
+ noMore := false.
+ self do:[:element |
+ noMore ifFalse:[
+ firstOne ifFalse:[
+ aStream nextPut:(Character space)
+ ] ifTrue:[
+ firstOne := false
+ ].
+ (aStream position > tooMany) ifTrue:[
+ aStream nextPutAll:'...etc...)'.
+ noMore := true
+ ] ifFalse:[
+ element printOn:aStream
+ ]
+ ].
+ ].
+ aStream nextPut:$)
+!
+
+storeOn:aStream
+ "output a printed representation (which can be re-read)
+ onto the argument aStream"
+
+ |isEmpty|
+
+ aStream nextPutAll:'(('.
+ aStream nextPutAll:(self class name).
+ aStream nextPutAll:' new)'.
+ isEmpty := true.
+ self do:[:element |
+ aStream nextPutAll:' add: '.
+ element storeOn:aStream.
+ aStream nextPut:$;.
+ isEmpty := false
+ ].
+ isEmpty ifFalse:[aStream nextPutAll:' yourself'].
+ aStream nextPut:$)
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Collection.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,546 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Collection
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Abstract'
+!
+
+Collection comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+Abstract superclass for all collections
+
+%W% %E%
+'!
+
+!Collection class methodsFor:'instance creation'!
+
+with:anObject
+ "return a new Collection with one element:anObject"
+
+ |newCollection|
+
+ newCollection := self new.
+ newCollection add:anObject.
+ ^ newCollection
+!
+
+with:firstObject with:secondObject
+ "return a new Collection with two elements:firstObject and secondObject"
+
+ |newCollection|
+
+ newCollection := self new.
+ newCollection add:firstObject.
+ newCollection add:secondObject.
+ ^ newCollection
+!
+
+with:firstObject with:secondObject with:thirdObject
+ "return a new Collection with three elements"
+
+ |newCollection|
+
+ newCollection := self new.
+ newCollection add:firstObject.
+ newCollection add:secondObject.
+ newCollection add:thirdObject.
+ ^ newCollection
+!
+
+with:firstObject with:secondObject with:thirdObject with:fourthObject
+ "return a new Collection with four elements"
+
+ |newCollection|
+
+ newCollection := self new.
+ newCollection add:firstObject.
+ newCollection add:secondObject.
+ newCollection add:thirdObject.
+ newCollection add:fourthObject.
+ ^ newCollection
+!
+
+new:size withAll:element
+ "return a new COllection of size, where all elements are
+ initialized to element"
+
+ |newCollection|
+
+ newCollection := self new:size.
+ size timesRepeat:[newCollection add:element]
+!
+
+withAll:aCollection
+ "return a new Collection with all elements taken from the argument,
+ aCollection"
+
+ |newCollection|
+
+ newCollection := self new.
+ newCollection addAll:aCollection.
+ ^newCollection
+! !
+
+!Collection methodsFor:'error handling'!
+
+errorNotFound
+ "report an error that Object was not in the collection"
+
+ self error:'Object is not in the Collection'
+!
+
+errorNotKeyed
+ "report an error that keyed access methods are not allowed"
+
+ self error:(self class name, 's do not respond to keyed accessing messages')
+! !
+
+!Collection methodsFor:'accessing'!
+
+anElement
+ "return any element from the collection"
+
+ self do: [:each | ^ each].
+ ^ nil
+! !
+
+!Collection methodsFor:'adding & removing'!
+
+add:anObject
+ "add the argument, anObject to the receiver"
+
+ ^ self subclassResponsibility
+!
+
+addLast:anObject
+ "add the argument, anObject to the receiver"
+
+ ^ self add:anObject
+!
+
+addAll:aCollection
+ "add all elements of the argument, aCollection to the receiver"
+
+ aCollection do:[:element |
+ self add:element
+ ].
+ ^ aCollection
+!
+
+remove:anObject ifAbsent:exceptionBlock
+ "remove the argument, anObject from the receiver - if it was not
+ in the collection returns the the value of the exceptionBlock"
+
+ ^ self subclassResponsibility
+!
+
+remove:anObject
+ "remove the argument, anObject from the receiver"
+
+ self remove:anObject ifAbsent:[self errorNotFound]
+!
+
+removeAll:aCollection
+ "remove all elements of the argument, aCollection from the receiver"
+
+ aCollection do:[:element | self remove:element].
+ ^ aCollection
+! !
+
+!Collection methodsFor:'growing'!
+
+growSize
+ "return a suitable size increment for growing"
+
+ ^ self size max:2
+!
+
+grow
+ "make the receiver larger"
+
+ self grow:(self size + self growSize)
+!
+
+grow:howBig
+ "change the receivers size"
+
+ ^ self subclassResponsibility
+! !
+
+!Collection methodsFor:'testing'!
+
+isEmpty
+ "return true, if the receiver is empty"
+
+ ^ self size == 0
+!
+
+includes:anElement
+ "return true, if the argument, anObject is in the list"
+
+ self do:[:element |
+ (anElement = element) ifTrue:[^ true].
+ ].
+ ^ false
+!
+
+includesAll:aCollection
+ "return true, if the the receiver includes all elements of
+ the argument, aCollection; false if any is missing"
+
+ aCollection do:[:element |
+ (self includes:element) ifFalse:[^ false].
+ ].
+ ^ true
+!
+
+occurrencesOf:anElement
+ "return the number of occurrences of the argument, anElement in
+ the receiver"
+
+ |count "<SmallInteger>" |
+
+ count := 0.
+ self do:[:element |
+ (anElement = element) ifTrue:[
+ count := count + 1
+ ].
+ ].
+ ^ count
+!
+
+size
+ "return the number of elements in the receiver"
+
+ |count "<SmallInteger>" |
+
+ count := 0.
+ self do:[:element |
+ count := count + 1
+ ].
+ ^ count
+! !
+
+!Collection methodsFor:'enumerating'!
+
+do:aBlock
+ "evaluate the argument, aBlock for each element"
+
+ ^ self subclassResponsibility
+!
+
+collect:aBlock
+ "for each element in the receiver, evaluate the argument, aBlock
+ and return a new collection with the results"
+
+ |newCollection|
+
+ newCollection := self species new.
+ self do:[:each | newCollection add:(aBlock value:each)].
+ ^ newCollection
+!
+
+detect:aBlock
+ "evaluate the argument, aBlock for each element in the receiver until
+ the block returns true; in this case return the element which caused
+ the true evaluation.
+ If none of the evaluations return true, report an error"
+
+ ^ self detect:aBlock ifNone:[self errorNotFound]
+!
+
+detect:aBlock ifNone:exceptionBlock
+ "evaluate the argument, aBlock for each element in the receiver until
+ the block returns true; in this case return the element which caused
+ the true evaluation.
+ If none of the evaluations returns true, return the result of the
+ evaluation of the exceptionBlock"
+
+ self do:[:each |
+ (aBlock value:each) ifTrue:[^ each].
+ ].
+ ^ exceptionBlock value
+!
+
+inject:thisValue into:binaryBlock
+ |nextValue|
+
+ nextValue := thisValue.
+ self do: [:each | nextValue := binaryBlock value:nextValue value:each].
+ ^ nextValue
+!
+
+reject:aBlock
+ "return a new collection with all elements from the receiver, for which
+ the argument aBlock evaluates to false"
+
+ ^ self select:[:element | (aBlock value:element) == false]
+!
+
+select:aBlock
+ "return a new collection with all elements from the receiver, for which
+ the argument aBlock evaluates to true"
+
+ |newCollection|
+
+ newCollection := self species new.
+ self do:[:each |
+ (aBlock value:each) ifTrue:[newCollection add:each].
+ ].
+ ^ newCollection
+! !
+
+!Collection methodsFor:'converting'!
+
+asArray
+ "return a new Array with the collections elements"
+
+ |anArray
+ index "<SmallInteger>" |
+
+ anArray := Array new:(self size).
+ index := 1.
+ self do:[:each |
+ anArray at:index put:each.
+ index := index + 1
+ ].
+ ^ anArray
+!
+
+asByteArray
+ "return a new ByteArray with the collections elements"
+
+ |aByteArray
+ index "<SmallInteger>" |
+
+ aByteArray := ByteArray new:(self size).
+ index := 1.
+ self do:[:each |
+ aByteArray at:index put:each asInteger.
+ index := index + 1
+ ].
+ ^ aByteArray
+!
+
+asString
+ "return a String with the collections elements
+ (which must convertable to characters)"
+
+ |aString
+ index "<SmallInteger>" |
+
+ aString := String new:(self size).
+ index := 1.
+ self do:[:each |
+ aString at:index put:each asCharacter.
+ index := index + 1
+ ].
+ ^ aString
+!
+
+asText
+ "return a new Text-object with the elements printstings"
+
+ |aText
+ index "<SmallInteger>" |
+
+ aText := Text new:(self size).
+ index := 1.
+ self do:[:each |
+ aText at:index put:(each printString).
+ index := index + 1
+ ].
+ ^ aText
+!
+
+asBag
+ "return a new Bag with the receiver collections elements"
+
+ |aBag|
+
+ aBag := Bag new.
+ self do:[:each | aBag add:each].
+ ^ aBag
+!
+
+asOrderedCollection
+ "return a new OrderedCollection with the receiver collections elements"
+
+ |anOrderedCollection|
+
+ anOrderedCollection := OrderedCollection new:self size.
+ self do:[:each | anOrderedCollection addLast:each].
+ ^ anOrderedCollection
+!
+
+asSet
+ "return a new Set with the receiver collections elements"
+
+ |aSet|
+
+ aSet := Set new: self size.
+ self do:[:each | aSet add:each].
+ ^ aSet
+!
+
+asSortedCollection
+ "return a new SortedCollection with the receiver collections elements"
+
+ |aSortedCollection|
+
+ aSortedCollection := SortedCollection new:self size.
+ aSortedCollection addAll:self.
+ ^ aSortedCollection
+!
+
+asSortedCollection:sortBlock
+ "return a new SortedCollection with the receiver collections elements,
+ using sortBlock for comparing"
+
+ |aSortedCollection|
+
+ aSortedCollection := SortedCollection sortBlock:sortBlock.
+ aSortedCollection addAll:self.
+ ^ aSortedCollection
+! !
+
+!Collection methodsFor:'printing & storing'!
+
+maxPrint
+ ^ 5000
+!
+
+printString
+ "return the printString of a big collection can take a long time
+ due to long temporary strings - I use a buffer here collecting some
+ elements to reduce the GC overhead ...
+ "
+
+ |thisString buffer count string noneYet total|
+
+ string := (self class name) , '('.
+ noneYet := true.
+ buffer := ''.
+ count := 0.
+ total := 0.
+ self do: [:element |
+ thisString := element printString.
+ noneYet ifTrue:[
+ noneYet := false.
+ buffer := buffer , thisString
+ ] ifFalse:[
+ buffer := buffer , (' ' , thisString)
+ ].
+ count := count + 1.
+ (count == 20) ifTrue:[
+ string := string , buffer.
+ buffer := ''.
+ count := 0
+ ].
+ total := total + 1.
+ (total > 5000) ifTrue:[
+ string := string , buffer , '... )'.
+ ^string
+ ]
+ ].
+ string := string , buffer , ')'.
+ ^string
+!
+
+displayString
+ "return the printString of a big collection can take a long time
+ due to long temporary strings - I use a buffer here collecting some
+ elements to reduce the GC overhead ...
+ "
+
+ |thisString buffer count string noneYet total|
+
+ string := (self class name) , '('.
+ noneYet := true.
+ buffer := ''.
+ count := 0.
+ total := 0.
+ self do: [:element |
+ thisString := element displayString.
+ noneYet ifTrue:[
+ noneYet := false.
+ buffer := buffer , thisString
+ ] ifFalse:[
+ buffer := buffer , (' ' , thisString)
+ ].
+ count := count + 1.
+ (count == 20) ifTrue:[
+ string := string , buffer.
+ buffer := ''.
+ count := 0
+ ].
+ total := total + 1.
+ (total > 5000) ifTrue:[
+ string := string , buffer , '... )'.
+ ^string
+ ]
+ ].
+ string := string , buffer , ')'.
+ ^string
+!
+
+printOn:aStream
+ |tooMany firstOne noMore|
+
+ tooMany := aStream position + self maxPrint.
+ aStream nextPutAll:self class name.
+ aStream nextPut:$(.
+ firstOne := true.
+ noMore := false.
+ self do:[:element |
+ noMore ifFalse:[
+ firstOne ifFalse:[
+ aStream nextPut:(Character space)
+ ] ifTrue:[
+ firstOne := false
+ ].
+ (aStream position > tooMany) ifTrue:[
+ aStream nextPutAll:'...etc...)'.
+ noMore := true
+ ] ifFalse:[
+ element printOn:aStream
+ ]
+ ].
+ ].
+ aStream nextPut:$)
+!
+
+storeOn:aStream
+ "output a printed representation (which can be re-read)
+ onto the argument aStream"
+
+ |isEmpty|
+
+ aStream nextPutAll:'(('.
+ aStream nextPutAll:(self class name).
+ aStream nextPutAll:' new)'.
+ isEmpty := true.
+ self do:[:element |
+ aStream nextPutAll:' add: '.
+ element storeOn:aStream.
+ aStream nextPut:$;.
+ isEmpty := false
+ ].
+ isEmpty ifFalse:[aStream nextPutAll:' yourself'].
+ aStream nextPut:$)
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Context.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,393 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object variableSubclass:#Context
+ instanceVariableNames:'flags sender home receiver selector searchClass
+ lineNr retvalTemp handle'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Methods'
+!
+
+Context comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+Context represents the stack context objects; each message send adds a context
+to a chain, which can be traced back via the sender field. (The actual implementation
+uses the machines stack for this, building real contexts when needed only).
+For both method- and block-contexts, the layout is the same. For method contexts, the
+home-field is nil, while for blockcontexts the home-field is either the context of its
+surrounding block (if its a nested block) or of its method. Contexts of cheap blocks do not
+have a home context - their home field is also nil.
+
+Warning: layout and size known by the compiler and runtime system - do not change.
+
+%W% %E%
+'!
+
+!Context methodsFor:'testing'!
+
+isContext
+ "return true, iff the receiver is a Context, false otherwise"
+
+ ^ true
+!
+
+isBlockContext
+ "return true, iff the receiver is a BlockContext, false otherwise"
+
+ ^ false
+! !
+
+!Context methodsFor:'accessing'!
+
+instVarAt:index
+ "have to catch instVar access to retVal and handle - they are invalid"
+
+ (index == 8) ifTrue:[^ nil].
+ (index == 9) ifTrue:[^ nil].
+ ^ super instVarAt:index
+!
+
+instVarAt:index put:value
+ "have to catch instVar access to retVal and handle - they are invalid"
+
+ (index == 8) ifTrue:[^ nil].
+ (index == 9) ifTrue:[^ nil].
+ ^ super instVarAt:index put:value
+!
+
+methodHome
+ "return the method-home - for method contexts this is the receiver"
+
+ ^ self
+!
+
+home
+ "return the immediate home of the receiver.
+ for block contexts, this is the methodcontext, where the block was created,
+ for nested block contexts, its the surrounding blocks context.
+ for method-contexts this is nil."
+
+ ^ nil "home"
+!
+
+method
+ "return the method which corresponds to the receiver"
+
+ |c|
+
+ c := self searchClass whichClassImplements:selector.
+ c notNil ifTrue:[
+ ^ c compiledMethodAt:selector
+ ].
+ ^ nil
+!
+
+sender
+ "return the sender of the context"
+
+ "this special test is for the very first context (startup-context)"
+ (sender isNil or:[sender selector isNil]) ifTrue:[^ nil].
+
+ ^ sender
+!
+
+receiver
+ "return the receiver of the context"
+
+ ^ receiver
+!
+
+searchClass
+ "this is the class where the method-lookup started"
+
+ searchClass notNil ifTrue:[^ searchClass].
+ ^ receiver class
+!
+
+selector
+ "return the selector of the context"
+
+ ^ selector
+!
+
+nargs
+ "return the number of arguments to the Block/Method"
+
+%{ /* NOCONTEXT */
+
+ RETURN ( _MKSMALLINT( (_intVal(_INST(flags)) >> __NARG_SHIFT) & __NARG_MASK) );
+%}
+!
+
+nvars
+ "return the number of variables to the Block/Method"
+
+%{ /* NOCONTEXT */
+
+ RETURN ( _MKSMALLINT( (_intVal(_INST(flags)) >> __NVAR_SHIFT) & __NVAR_MASK) );
+%}
+!
+
+args
+ "return an array filled with the arguments of this context"
+
+ |newArray n "{ Class: SmallInteger }" |
+
+ n := self nargs.
+ newArray := Array new:n.
+ 1 to:n do:[:index |
+ newArray at:index put:(self at:index)
+ ].
+ ^ newArray
+!
+
+argsAndVars
+ "return an array filled with the arguments and variables of this context"
+
+ |newArray mySize "{ Class: SmallInteger }" |
+
+ mySize := self nargs + self nvars.
+ newArray := Array new:mySize.
+ 1 to:mySize do:[:index |
+ newArray at:index put:(self at:index)
+ ].
+ ^ newArray
+!
+
+lineNumber
+ "this returns the lineNumber within the methods source, where the context was
+ interrupted or called another method. (currently, sometimes this information
+ is not available - in this case 0 is returned)"
+
+ ^ lineNr
+! !
+
+!Context methodsFor:'printing'!
+
+argsPrintString
+ |fullString n "{ Class: SmallInteger }" |
+
+ fullString := ''.
+ n := self nargs.
+ 1 to:n do:[:index |
+ fullString := fullString , (' ' , (self at:index) printString)
+ ].
+ ^ fullString
+!
+
+printReceiver
+ |implementorClass|
+
+ (receiver class == SmallInteger "isKindOf:Number") ifTrue:[
+ '(' print. receiver print. ') ' print
+ ].
+ receiver class name print.
+
+ selector notNil ifTrue:[
+ implementorClass := self searchClass whichClassImplements:selector.
+ implementorClass notNil ifTrue: [
+ (implementorClass ~= receiver class) ifTrue: [
+ '>>>' print.
+ implementorClass name print
+ ]
+ ] ifFalse:[
+ '>>>**NONE**' print
+ ]
+ ]
+!
+
+receiverPrintString
+ |newString receiverClassName implementorClass|
+
+ receiverClassName := receiver class name.
+ (receiver class == SmallInteger) ifTrue:[
+ newString := '(' , receiver printString , ') ' , receiverClassName
+ ] ifFalse:[
+ newString := receiverClassName
+ ].
+
+ selector notNil ifTrue:[
+ implementorClass := self searchClass whichClassImplements:selector.
+ implementorClass notNil ifTrue: [
+ (implementorClass ~~ receiver class) ifTrue: [
+ newString := newString , '>>>',
+ implementorClass name printString
+ ]
+ ] ifFalse:[
+ newString := newString , '>>>**NONE**'
+ ]
+ ].
+
+ ^ newString
+!
+
+printString
+ ^ self receiverPrintString , ' ' , self selector printString
+!
+
+printOn:aStream
+ aStream nextPutAll:(self receiverPrintString).
+ aStream space.
+ self selector printOn:aStream
+!
+
+fullPrint
+ self printReceiver.
+ ' ' print.
+ selector print.
+ self size ~~ 0 ifTrue: [
+ ' ' print.
+ self argsPrintString print
+ ].
+ ' [' print. lineNr print. ']' printNewline
+!
+
+fullPrintOn:aStream
+ aStream nextPutAll:self receiverPrintString.
+ aStream space.
+ aStream nextPutAll:selector printString.
+ self size ~~ 0 ifTrue: [
+ aStream space.
+ aStream nextPutAll:self argsPrintString
+ ]
+!
+
+debugPrint
+ | n "{ Class: SmallInteger }" |
+
+ 'context ' print. self address printNewline.
+ 'receiver: ' print. receiver address printNewline.
+ 'selector: ' print. selector address printNewline.
+ n := self size.
+ n ~~ 0 ifTrue:[
+ 1 to:n do:[:index |
+ 'arg ' print. index print. ' : ' print.
+ (self at:index) address printNewline
+ ]
+ ].
+ '' printNewline
+!
+
+fullPrintString
+ |aString|
+
+ aString := self receiverPrintString , ' ' , selector printString.
+ self size ~~ 0 ifTrue: [
+ aString := aString , ' ' , (self argsPrintString)
+ ].
+ ^ aString
+!
+
+printAll
+ |context|
+ context := self.
+ [context notNil] whileTrue: [
+ context print.
+ context := context sender
+ ]
+!
+
+fullPrintAll
+ |context|
+ context := self.
+ [context notNil] whileTrue: [
+ context fullPrint.
+ context := context sender
+ ]
+!
+
+debugPrintAll
+ |context|
+ context := self.
+ [context notNil] whileTrue:[
+ context debugPrint.
+ context := context sender
+ ]
+! !
+
+!Context methodsFor:'non local control flow'!
+
+restart
+ "restart the receiver - i.e. the method is evaluated again.
+ if the context to restart already died - do nothing"
+
+ sender isNil ifTrue:[^ nil].
+%{
+ __RESUMECONTEXT(SND_COMMA self, (OBJ)1);
+
+ /* when we reach here, something went wrong */
+ printf("restart failed\n");
+%}
+.
+ ^ nil
+!
+
+resume
+ "resume the receiver with nil - i.e. return nil from the receiver.
+ if the context to resume already died - do nothing"
+
+ self resume:nil
+!
+
+resume:value
+ "resume the receiver - i.e. return value from the receiver.
+ if the context to resume already died - do nothing. No unwind
+ blocks are evaluated (see unwind: in this class)."
+
+ sender isNil ifTrue:[^ nil].
+%{
+ __RESUMECONTEXT(SND_COMMA self, value);
+
+ /* when we reach here, something went wrong */
+ printf("resume failed\n");
+%}
+.
+ ^ nil
+!
+
+unwind
+ "resume the receiver - i.e. return nil from the receiver.
+ if the context to resume already died - do nothing.
+ Evaluate all unwind-blocks as specified in Block>>valueNowOrOnUnwind:
+ and Block>>valueOnUnwindDo: on the way."
+
+ self unwind:nil
+!
+
+unwind:value
+ "resume the receiver - i.e. return value from the receiver.
+ if the context to resume already died - do nothing.
+ Evaluate all unwind-blocks as specified in Block>>valueNowOrOnUnwind:
+ and Block>>valueOnUnwindDo: on the way."
+
+ |con|
+
+ sender isNil ifTrue:[^ nil].
+
+ "start with this context, moving up"
+ con := thisContext.
+ [con ~~ self] whileTrue:[
+ con isBlockContext ifFalse:[
+ "the way we find those unwind contexts seems kludgy"
+ ((con selector == #valueNowOrOnUnwindDo:) or:[con selector == #valueOnUnwindDo:]) ifTrue:[
+ "the way we evaluate the unwind blocks too"
+ (con args at:1) value
+ ]
+ ].
+ con := con sender
+ ].
+ self resume:value
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Date.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,476 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+
+
+ ----------------------------------------------------------------
+ For code marked as (GNU) the following applies:
+
+ Copyright (C) 1988, 1989 Free Software Foundation, Inc.
+ Written by Steve Byrne.
+
+ This file is part of GNU Smalltalk.
+
+ GNU Smalltalk is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 1, or (at your option) any later version.
+
+ GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Smalltalk; see the file LICENSE. If not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ ----------------------------------------------------------------
+"
+
+Magnitude subclass:#Date
+ instanceVariableNames:'unixTimeLow unixTimeHi'
+ classVariableNames:'dayNames monthNames dayAbbrevs monthAbbrevs
+ environmentChange'
+ poolDictionaries:''
+ category:'Magnitude-General'
+!
+
+Date comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+date represents a particular second in a day; since we depend on
+unix, the second is counted from 1. Jan 1970 NOT as in Smalltalk-80
+from 1. Jan 1901 !
+
+%W% %E%
+
+written Spring 89 by claus
+'!
+
+!Date class methodsFor:'private'!
+
+initNames
+ "read the language specific names"
+
+ dayNames := Resource array:#('DAY_MONDAY'
+ 'DAY_TUESDAY'
+ 'DAY_WEDNESDAY'
+ 'DAY_THURSDAY'
+ 'DAY_FRIDAY'
+ 'DAY_SATURDAY'
+ 'DAY_SUNDAY')
+ defaults:#('monday'
+ 'tuesday'
+ 'wednesday'
+ 'thursday'
+ 'friday'
+ 'saturday'
+ 'sunday')
+ fromFile:'Smalltalk.rs'.
+
+ dayAbbrevs := Resource array:#('DAY_MON'
+ 'DAY_TUE'
+ 'DAY_WED'
+ 'DAY_THU'
+ 'DAY_FRI'
+ 'DAY_SAT'
+ 'DAY_SUN')
+ defaults:#('mon'
+ 'tue'
+ 'wed'
+ 'thu'
+ 'fri'
+ 'sat'
+ 'sun')
+ fromFile:'Smalltalk.rs'.
+
+ monthNames := Resource array:#('MON_JANUARY'
+ 'MON_FEBRUARY'
+ 'MON_MARCH'
+ 'MON_APRIL'
+ 'MON_MAY'
+ 'MON_JUNE'
+ 'MON_JULY'
+ 'MON_AUGUST'
+ 'MON_SEPTEMBER'
+ 'MON_OCTOBER'
+ 'MON_NOVEMBER'
+ 'MON_DECEMBER')
+ defaults:#('january'
+ 'february'
+ 'march'
+ 'april'
+ 'may'
+ 'june'
+ 'july'
+ 'august'
+ 'september'
+ 'october'
+ 'november'
+ 'december')
+ fromFile:'Smalltalk.rs'.
+
+ monthAbbrevs := Resource array:#('MON_JAN'
+ 'MON_FEB'
+ 'MON_MAR'
+ 'MON_APR'
+ 'MON_MAY_ABBREV'
+ 'MON_JUN'
+ 'MON_JUL'
+ 'MON_AUG'
+ 'MON_SEP'
+ 'MON_OCT'
+ 'MON_NOV'
+ 'MON_DEC')
+ defaults:#('jan'
+ 'feb'
+ 'mar'
+ 'apr'
+ 'may'
+ 'jun'
+ 'jul'
+ 'aug'
+ 'sep'
+ 'oct'
+ 'nov'
+ 'dec')
+ fromFile:'Smalltalk.rs'.
+
+ environmentChange := false
+! !
+
+!Date class methodsFor:'handling language changes'!
+
+initialize
+ super initialize.
+ Smalltalk addDependent:self.
+ environmentChange := true
+!
+
+update:something
+ ((something == #Language) or:[something == #LanguageTerritory]) ifTrue:[
+ "just remember change for next access"
+ environmentChange := true
+ ]
+! !
+
+!Date class methodsFor:'general queries'!
+
+dayOfWeek:dayName
+ "given the name of a day (either string or symbol),
+ return the day-index (1 for monday; 7 for sunday).
+ Return 0 for invalid day name"
+
+ environmentChange ifTrue:[
+ self initNames
+ ].
+ ^ dayNames indexOf:dayName
+
+ "Date dayOfWeek:'wednesday'"
+!
+
+indexOfMonth:aMonthString
+ "given the name of a month (either string or symbol),
+ return the month-index (1 for jan; 12 for december).
+ Return 0 for invalid month name"
+
+ |idx name|
+
+ environmentChange ifTrue:[
+ self initNames
+ ].
+ name := aMonthString asLowercase.
+ idx := monthAbbrevs indexOf:name.
+ idx ~~ 0 ifTrue:[^ idx].
+ idx := monthNames indexOf:name.
+ idx ~~ 0 ifTrue:[^ idx].
+
+ name at:1 put:(name at:1) asUppercase.
+ idx := monthAbbrevs indexOf:name.
+ idx ~~ 0 ifTrue:[^ idx].
+ idx := monthNames indexOf:name.
+ idx ~~ 0 ifTrue:[^ idx].
+
+ ^ idx
+
+ "Date indexOfMonth:'jan'"
+ "Date indexOfMonth:'Jan'"
+ "Date indexOfMonth:'December'"
+!
+
+nameOfDay:dayIndex
+ "given a day index (1..7), return the name of the day
+ as a symbol"
+
+ environmentChange ifTrue:[
+ self initNames
+ ].
+ ^ (dayNames at:dayIndex) asSymbol
+
+ "Date nameOfDay:4"
+!
+
+nameOfMonth:monthIndex
+ "given a month index (1..12), return the name of the month
+ as a symbol"
+
+ environmentChange ifTrue:[
+ self initNames
+ ].
+ ^ (monthNames at:monthIndex) asSymbol
+
+ "Date nameOfMonth:11"
+ "Date nameOfMonth:12"
+ "Date nameOfMonth:4"
+!
+
+abbreviatedNameOfDay:dayIndex
+ "given a day index (1..7), return the abbreviated name
+ of the day as a symbol"
+
+ environmentChange ifTrue:[
+ self initNames
+ ].
+ ^ (dayAbbrevs at:dayIndex) asSymbol
+!
+
+abbreviatedNameOfMonth:monthIndex
+ "given a month index (1..12), return the abbreviated name
+ of the month as a symbol"
+
+ environmentChange ifTrue:[
+ self initNames
+ ].
+ ^ (monthAbbrevs at:monthIndex) asSymbol
+!
+
+daysInMonth:monthName forYear:yearInteger
+ "given the name of a minth and a year, return the number
+ of days this month has (modified GNU).
+ return 0 if the month name was invalid."
+
+ |monthIndex|
+
+ monthIndex := self indexOfMonth:monthName.
+ (monthIndex == 0) ifTrue:[
+ ^ 0
+ ].
+ ^ self daysInMonthIndex:monthIndex forYear:yearInteger
+
+ "Date daysInMonth:2 forYear:1980"
+ "Date daysInMonth:2 forYear:1981"
+!
+
+daysUntilMonth:monthName forYear:yearInteger
+ "given the name of a month and a year, return the number
+ of days from 1st of january to last of prev month.
+ return 0 if the month name was invalid."
+
+ |monthIndex sumDays|
+
+ monthIndex := self indexOfMonth:monthName.
+ (monthIndex == 0) ifTrue:[
+ ^ 0
+ ].
+ sumDays := 0.
+ 1 to:monthIndex-1 do:[:m |
+ sumDays := sumDays + (self daysInMonthIndex:m forYear:yearInteger)
+ ].
+ ^ sumDays
+
+ "Date daysUntilMonth:'feb' forYear:1993"
+ "Date daysUntilMonth:'jan' forYear:1993"
+!
+
+daysInYear:yearInteger
+ "return the number of days in a year"
+
+ (self isLeapYear:yearInteger) ifTrue:[^ 366].
+ ^ 365
+
+ "Date daysInYear:1980"
+ "Date daysInYear:1981"
+!
+
+yearAsDays: yearInteger
+ "Returns the number of days since Jan 1, 1901. (GNU)"
+
+ |y|
+
+ y := yearInteger - 1900.
+ ^ (y - 1) * 365
+ + (y // 4)
+ - (y // 100)
+ + (y // 400)
+!
+
+isLeapYear:yearInteger
+ "return true, if a year is a leap year"
+
+ (yearInteger \\ 4 == 0) ifTrue:[
+ (yearInteger \\ 100 ~~ 0) ifTrue:[^ true].
+ (yearInteger \\ 400 == 0) ifTrue:[^ true]
+ ].
+ ^ false
+!
+
+daysInMonthIndex: monthIndex forYear: yearInteger
+ "return the number of days in month monthIndex of
+ year yearInteger (GNU)"
+
+ |days|
+
+ days := #(31 28 31 "Jan Feb Mar"
+ 30 31 30 "Apr May Jun"
+ 31 31 30 "Jul Aug Sep"
+ 31 30 31 "Oct Nov Dec"
+ ) at: monthIndex.
+
+ (monthIndex == 2) ifTrue:[
+ (self isLeapYear:yearInteger) ifTrue:[
+ ^ days + 1
+ ]
+ ].
+ ^ days
+! !
+
+!Date class methodsFor:'instance creation'!
+
+dateAndTimeNow
+ "return an array with the current date and time"
+
+ ^ Array with:(Date today) with:(Time now)
+!
+
+today
+ "return a date, representing today"
+
+ ^ self basicNew setTimeLow:(OperatingSystem getTimeLow)
+ and:(OperatingSystem getTimeHi)
+!
+
+fromDays:dayCount
+ ^ self new setDays:dayCount
+!
+
+newDay:dayCount year:yearInteger
+ ^ self new setDays:(dayCount + self yearAsDays:yearInteger)
+!
+
+newDay:day month:monthName year:yearInteger
+ ^self new setDays:
+ (day + (self daysUntilMonth: monthName forYear: yearInteger)
+ + (self yearAsDays: yearInteger))
+
+ "Date newDay:8 month:'may' year:1993"
+! !
+
+!Date methodsFor:'comparing'!
+
+
+> aDate
+ "return true, if the date represented by the receiver
+ is after the argument, aDate"
+
+ |other|
+
+ OperatingSystem computeDatePartsOf:unixTimeLow and:unixTimeHi
+ for:[:year :month :day |
+ other := aDate year.
+ (year > other) ifTrue:[^ true].
+ (year < other) ifTrue:[^ false].
+ other := aDate month.
+ (month > other) ifTrue:[^ true].
+ (month < other) ifTrue:[^ false].
+ ^ day > aDate day
+ ]
+!
+
+= aDate
+ "return true, if the date represented by the receiver
+ is the same as the one represented by argument, aDate"
+
+ OperatingSystem computeDatePartsOf:unixTimeLow and:unixTimeHi
+ for:[:year :month :day |
+ (year ~~ aDate year) ifTrue:[^ false].
+ (month ~~ aDate month) ifTrue:[^ false].
+ ^ (day == aDate day)
+ ]
+! !
+
+!Date methodsFor:'accessing'!
+
+day
+ "return the day-in-month of the receiver (1..31)"
+
+ OperatingSystem computeDatePartsOf:unixTimeLow and:unixTimeHi
+ for:[:year :month :day |
+ ^ day
+ ]
+!
+
+month
+ "return the month of the receiver (1..12)"
+
+ OperatingSystem computeDatePartsOf:unixTimeLow and:unixTimeHi
+ for:[:year :month :day |
+ ^ month
+ ]
+!
+
+year
+ "return the year of the receiver i.e. 1992"
+
+ OperatingSystem computeDatePartsOf:unixTimeLow and:unixTimeHi
+ for:[:year :month :day |
+ ^ year
+ ]
+! !
+
+!Date methodsFor:'printing'!
+
+printString
+ |string|
+
+ OperatingSystem computeDatePartsOf:unixTimeLow and:unixTimeHi
+ for:[:year :month :day |
+ string := day printString
+ , '-'
+ , (Date abbreviatedNameOfMonth:month)
+ , '-'
+ , year printString
+ ].
+ ^ string
+! !
+
+!Date methodsFor:'storing'!
+
+storeString
+ |string|
+ string := '(' , self class name , ' new settimeLow:'.
+ string := string , unixTimeLow storeString.
+ string := string , ' and:' , unixTimeHi storeString.
+ string := string , ')'.
+ ^ string
+! !
+
+!Date methodsFor:'private'!
+
+setTimeLow:timeLow and:timeHi
+ unixTimeLow := timeLow.
+ unixTimeHi := timeHi
+! !
+
+!Date methodsFor:'converting'!
+
+asSeconds
+ ^ (unixTimeHi * 16r10000) + unixTimeLow
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Dict.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,503 @@
+"
+ COPYRIGHT (c) 1991-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#Dictionary
+ instanceVariableNames:'valueArray keyArray tally'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Unordered'
+!
+
+Dictionary comment:'
+
+COPYRIGHT (c) 1991-93 by Claus Gittinger
+ All Rights Reserved
+
+a Dictionary is (conceptionally) a collection of Associations storing key-value pairs.
+(The implementation uses two array to store the keys and values separately.)
+Searching for an element is done using a hash into the key arrlay.
+
+%W% %E%
+
+written jun 91 by claus
+rewritten 92 to use hash scheme
+'!
+
+!Dictionary class methodsFor:'instance creation'!
+
+new
+ "return a new empty Dictionary"
+
+ ^ self new:5
+!
+
+new:anInteger
+ "return a new empty Dictionary with space for anInteger elements"
+
+ ^ self basicNew setTally:anInteger
+! !
+
+!Dictionary methodsFor:'testing'!
+
+size
+ "return the number of elements in the receiver"
+
+ ^ tally
+!
+
+includesKey:aKey
+ "return true, if the argument, aKey is a key in the receiver"
+
+ ^ (self findKey:aKey ifAbsent:[0]) ~~ 0
+!
+
+isFixedSize
+ "return true if the receiver cannot grow - this will vanish once
+ Arrays and Strings learn how to grow ..."
+
+ ^ false
+! !
+
+!Dictionary methodsFor:'accessing'!
+
+at:aKey
+ "return the element indexed by aKey - report an error if none found"
+
+ |index|
+
+ aKey isNil ifTrue:[
+ self error:'nil is not allowed as key'
+ ] ifFalse:[
+ index := self findKey:aKey ifAbsent:[0].
+ index == 0 ifTrue:[^ self errorKeyNotFound].
+ ^ valueArray basicAt:index
+ ]
+!
+
+at:aKey ifAbsent:exceptionBlock
+ |index|
+
+ aKey isNil ifTrue:[
+ self error:'nil is not allowed as key'
+ ] ifFalse:[
+ index := self findKey:aKey ifAbsent:[0].
+ index == 0 ifTrue:[^ exceptionBlock value].
+ ^ valueArray basicAt:index
+ ]
+!
+
+at:aKey put:anObject
+ "add the argument anObject under key, aKey to the receiver"
+
+ |index element|
+
+ aKey isNil ifTrue:[
+ self error:'nil is not allowed as key'
+ ] ifFalse:[
+ index := self findKeyOrNil:aKey.
+ element := valueArray basicAt:index.
+ element notNil ifTrue:[
+ valueArray basicAt:index put:anObject.
+ ^ anObject
+ ].
+ keyArray basicAt:index put:aKey.
+ valueArray basicAt:index put:anObject.
+ tally := tally + 1.
+
+ "grow if filled more than 70% "
+ tally > (keyArray basicSize * 7 // 10) ifTrue:[
+ self grow
+ ].
+
+ ^ anObject
+ ]
+!
+
+keys
+ "return a collection containing all keys of the receiver"
+
+ ^ keyArray select:[:key | key notNil]
+! !
+
+!Dictionary methodsFor:'adding & removing'!
+
+add:anAssociation
+ "add the argument, anAssociation to the receiver"
+
+ self at:(anAssociation key) put:(anAssociation value).
+ ^ anAssociation
+!
+
+remove:oldObject ifAbsent:aBlock
+ "remove oldObject from the collection and return it.
+ If it was not in the collection return the value of aBlock."
+
+ self shouldNotImplement
+!
+
+removeAssociation:assoc
+ "remove the association from the collection.
+ If it was not in the collection report an error"
+
+ self removeKey:assoc key
+!
+
+removeKey:aKey
+ "remove the association under aKey from the collection.
+ If it was not in the collection report an error"
+
+ |index
+ next "{ Class:SmallInteger }" |
+
+ aKey isNil ifTrue:[
+ self error:'nil is not allowed as key'
+ ] ifFalse:[
+ index := self findKey:aKey ifAbsent:[0].
+ (index == 0) ifTrue:[^ self errorNotFound].
+ valueArray basicAt:index put:nil.
+ keyArray basicAt:index put:nil.
+ tally := tally - 1.
+ tally == 0 ifTrue:[
+ self setTally:0
+ ] ifFalse:[
+ index == keyArray basicSize ifTrue:[
+ next := 1
+ ] ifFalse:[
+ next := index + 1.
+ ].
+ "redundant check to save a send sometimes"
+ (keyArray basicAt:next) notNil ifTrue:[
+ self rehashFrom:next.
+ ]
+ ]
+ ]
+!
+
+removeKey:aKey ifAbsent:aBlock
+ "remove the association under aKey from the collection.
+ If it was not in the collection return result from evaluating aBlock"
+
+ |index
+ next "{ Class:SmallInteger }" |
+
+ aKey isNil ifTrue:[
+ self error:'nil is not allowed as key'
+ ] ifFalse:[
+ index := self findKey:aKey ifAbsent:[0].
+ index == 0 ifTrue:[^ aBlock value].
+ valueArray basicAt:index put:nil.
+ keyArray basicAt:index put:nil.
+ tally := tally - 1.
+ tally == 0 ifTrue:[
+ self setTally:0
+ ] ifFalse:[
+ index == keyArray basicSize ifTrue:[
+ next := 1
+ ] ifFalse:[
+ next := index + 1.
+ ].
+ "redundant check to save a send sometimes"
+ (keyArray basicAt:next) notNil ifTrue:[
+ self rehashFrom:next.
+ ]
+ ]
+ ]
+! !
+
+!Dictionary methodsFor:'enumeration'!
+
+allKeysDo:aBlock
+ "perform the block for all keys in the collection."
+
+ keyArray nonNilElementsDo:aBlock
+!
+
+associationsDo:aBlock
+ "perform the block for all associations in the collection."
+
+ |key|
+
+ tally == 0 ifTrue:[^ self].
+ 1 to:(keyArray basicSize) do:[:index |
+ key := keyArray basicAt:index.
+ key notNil ifTrue:[
+ aBlock value:(Association key:key value:(valueArray basicAt:index))
+ ]
+ ]
+!
+
+do:aBlock
+ "perform the block for all values in the collection."
+
+ |index "{ Class:SmallInteger }" |
+
+ tally == 0 ifTrue:[^ self].
+ index := 1.
+ keyArray do:[:key |
+ key notNil ifTrue:[
+ aBlock value:(valueArray basicAt:index)
+ ].
+ index := index + 1
+ ]
+!
+
+collect:aBlock
+ "for each element in the receiver, evaluate the argument, aBlock
+ and return a Bag with the results"
+
+ |newCollection|
+
+ newCollection := Bag new.
+ self do:[:each |
+ newCollection add:each
+ ].
+ ^ newCollection
+!
+
+select:aBlock
+ "return a new collection with all elements from the receiver, for which
+ the argument aBlock evaluates to true"
+
+ |newCollection|
+
+ newCollection := self species new.
+ self associationsDo:[:assoc |
+ (aBlock value:(assoc value)) ifTrue:[
+ newCollection add:assoc
+ ]
+ ].
+ ^ newCollection
+! !
+
+!Dictionary methodsFor:'private'!
+
+goodSizeFor:arg
+ "return a good array size for the given argument.
+ Returns the next prime after arg"
+
+ arg <= 7 ifTrue:[^ 7].
+ arg <= 16384 ifTrue:[
+ "2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384"
+ ^ #(7 7 11 17 37 67 131 257 521 1031 2053 4099 8209 16411) at:(arg highBit)
+ ].
+ ^ arg bitOr:1
+!
+
+setTally:count
+ "initialize the contents array (for at least count slots)
+ and set tally to zero.
+ The size is increased to the next prime for better hashing behavior."
+
+ |n|
+
+ n := self goodSizeFor:count.
+ valueArray := Array new:n.
+ keyArray := Array new:n.
+ tally := 0
+!
+
+findKeyOrNil:key
+ "Look for the key in the receiver. If it is found, return
+ the index of the association containing the key, otherwise
+ return the index of the first unused slot. Grow the receiver,
+ if key was not found, and no unused slots where present"
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex
+ probe|
+
+ length := keyArray basicSize.
+ startIndex := key hash \\ length + 1.
+
+ index := startIndex.
+ [true] whileTrue:[
+ probe := keyArray basicAt:index.
+ (probe isNil or: [key = probe]) ifTrue:[^ index].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ index == startIndex ifTrue:[^ self grow findKeyOrNil:key]
+ ]
+!
+
+findKey:key ifAbsent:aBlock
+ "Look for the key in the receiver. If it is found, return
+ the index of the association containing the key, otherwise
+ return the value of evaluating aBlock."
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex
+ probe|
+
+ length := keyArray basicSize.
+ length < 10 ifTrue:[
+ "assuming, that for small dictionaries the overhead of hashing
+ is large ... maybe that proves wrong (if overhead of comparing
+ is high)"
+ index := keyArray indexOf:key.
+ index == 0 ifTrue:[
+ ^ aBlock value
+ ].
+ ^ index
+ ].
+
+ startIndex := key hash \\ length + 1.
+
+ index := startIndex.
+ [true] whileTrue:[
+ probe := (keyArray basicAt:index).
+ key = probe ifTrue:[^ index].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ ((probe isNil) or:[index == startIndex]) ifTrue:[
+ ^ aBlock value
+ ]
+ ]
+!
+
+grow
+ "change the number of element slots of the collection to a useful
+ new size"
+
+ self grow:(keyArray basicSize * 2)
+!
+
+grow:newSize
+ "grow the receiver to make space for at least newSize elements.
+ To do this, we have to rehash into the new arrays.
+ (which is done by re-adding all elements to a new, empty key/value array pair)."
+
+ |oldKeys oldValues
+ index "{ Class:SmallInteger }" |
+
+ oldKeys := keyArray.
+ oldValues := valueArray.
+
+ self setTally:newSize.
+
+ index := 1.
+ oldKeys do:[:aKey |
+ aKey notNil ifTrue:[
+ self at:aKey put:(oldValues basicAt:index)
+ ].
+ index := index + 1
+ ]
+!
+
+rehash
+ "rehash contents - is done by re-adding all elements to a new, empty key/value array pair)."
+
+ | oldKeyArray oldValueArray n key
+ newIndex index |
+
+ oldKeyArray := keyArray.
+ oldValueArray := valueArray.
+
+ n := keyArray size.
+ valueArray := Array new:n.
+ keyArray := Array new:n.
+
+ index := 1.
+ oldKeyArray do:[:key |
+ key notNil ifTrue:[
+ newIndex := self findKeyOrNil:key.
+ keyArray basicAt:newIndex put:key.
+ valueArray basicAt:newIndex put:(oldValueArray basicAt:index).
+ ].
+ index := index + 1
+ ]
+!
+
+rehashFrom:startIndex
+ "rehash elements starting at index - after a remove"
+
+ |key i length
+ index "{ Class:SmallInteger }" |
+
+ length := keyArray basicSize.
+ index := startIndex.
+ key := keyArray basicAt:index.
+ [key notNil] whileTrue:[
+ i := self findKeyOrNil:key.
+ i == index ifTrue:[
+ ^ self
+ ].
+ keyArray basicAt:i put:key.
+ valueArray basicAt:i put:(valueArray basicAt:index).
+ keyArray basicAt:index put:nil.
+ valueArray basicAt:index put:nil.
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1.
+ ].
+ key := keyArray basicAt:index.
+ ]
+! !
+
+!Dictionary methodsFor:'printing & storing'!
+
+stringWith:aSelector
+ "common code for printString & displayString"
+
+ |thisString string noneYet|
+
+ string := (self class name) , '('.
+ noneYet := true.
+ self associationsDo:[:element |
+ thisString := element perform:aSelector.
+ noneYet ifTrue:[noneYet := false]
+ ifFalse:[thisString := ' ' , thisString].
+ string := string , thisString
+ ].
+ string := string , ')'.
+ ^string
+!
+
+printString
+ ^ self stringWith:#printString
+!
+
+displayString
+ ^ self stringWith:#displayString
+! !
+
+!Dictionary methodsFor:'inspecting'!
+
+inspect
+ "redefined to launch a DictionaryInspector on the receiver
+ (instead of the default InspectorView)."
+
+ DictionaryInspectorView isNil ifTrue:[
+ super inspect
+ ] ifFalse:[
+ DictionaryInspectorView openOn:self
+ ]
+! !
+
+!Dictionary methodsFor:'error handling'!
+
+errorKeyNotFound
+ "report an error that an element was not found in the collection"
+
+ self error:'the key is not in the collection'
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Dictionary.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,503 @@
+"
+ COPYRIGHT (c) 1991-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#Dictionary
+ instanceVariableNames:'valueArray keyArray tally'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Unordered'
+!
+
+Dictionary comment:'
+
+COPYRIGHT (c) 1991-93 by Claus Gittinger
+ All Rights Reserved
+
+a Dictionary is (conceptionally) a collection of Associations storing key-value pairs.
+(The implementation uses two array to store the keys and values separately.)
+Searching for an element is done using a hash into the key arrlay.
+
+%W% %E%
+
+written jun 91 by claus
+rewritten 92 to use hash scheme
+'!
+
+!Dictionary class methodsFor:'instance creation'!
+
+new
+ "return a new empty Dictionary"
+
+ ^ self new:5
+!
+
+new:anInteger
+ "return a new empty Dictionary with space for anInteger elements"
+
+ ^ self basicNew setTally:anInteger
+! !
+
+!Dictionary methodsFor:'testing'!
+
+size
+ "return the number of elements in the receiver"
+
+ ^ tally
+!
+
+includesKey:aKey
+ "return true, if the argument, aKey is a key in the receiver"
+
+ ^ (self findKey:aKey ifAbsent:[0]) ~~ 0
+!
+
+isFixedSize
+ "return true if the receiver cannot grow - this will vanish once
+ Arrays and Strings learn how to grow ..."
+
+ ^ false
+! !
+
+!Dictionary methodsFor:'accessing'!
+
+at:aKey
+ "return the element indexed by aKey - report an error if none found"
+
+ |index|
+
+ aKey isNil ifTrue:[
+ self error:'nil is not allowed as key'
+ ] ifFalse:[
+ index := self findKey:aKey ifAbsent:[0].
+ index == 0 ifTrue:[^ self errorKeyNotFound].
+ ^ valueArray basicAt:index
+ ]
+!
+
+at:aKey ifAbsent:exceptionBlock
+ |index|
+
+ aKey isNil ifTrue:[
+ self error:'nil is not allowed as key'
+ ] ifFalse:[
+ index := self findKey:aKey ifAbsent:[0].
+ index == 0 ifTrue:[^ exceptionBlock value].
+ ^ valueArray basicAt:index
+ ]
+!
+
+at:aKey put:anObject
+ "add the argument anObject under key, aKey to the receiver"
+
+ |index element|
+
+ aKey isNil ifTrue:[
+ self error:'nil is not allowed as key'
+ ] ifFalse:[
+ index := self findKeyOrNil:aKey.
+ element := valueArray basicAt:index.
+ element notNil ifTrue:[
+ valueArray basicAt:index put:anObject.
+ ^ anObject
+ ].
+ keyArray basicAt:index put:aKey.
+ valueArray basicAt:index put:anObject.
+ tally := tally + 1.
+
+ "grow if filled more than 70% "
+ tally > (keyArray basicSize * 7 // 10) ifTrue:[
+ self grow
+ ].
+
+ ^ anObject
+ ]
+!
+
+keys
+ "return a collection containing all keys of the receiver"
+
+ ^ keyArray select:[:key | key notNil]
+! !
+
+!Dictionary methodsFor:'adding & removing'!
+
+add:anAssociation
+ "add the argument, anAssociation to the receiver"
+
+ self at:(anAssociation key) put:(anAssociation value).
+ ^ anAssociation
+!
+
+remove:oldObject ifAbsent:aBlock
+ "remove oldObject from the collection and return it.
+ If it was not in the collection return the value of aBlock."
+
+ self shouldNotImplement
+!
+
+removeAssociation:assoc
+ "remove the association from the collection.
+ If it was not in the collection report an error"
+
+ self removeKey:assoc key
+!
+
+removeKey:aKey
+ "remove the association under aKey from the collection.
+ If it was not in the collection report an error"
+
+ |index
+ next "{ Class:SmallInteger }" |
+
+ aKey isNil ifTrue:[
+ self error:'nil is not allowed as key'
+ ] ifFalse:[
+ index := self findKey:aKey ifAbsent:[0].
+ (index == 0) ifTrue:[^ self errorNotFound].
+ valueArray basicAt:index put:nil.
+ keyArray basicAt:index put:nil.
+ tally := tally - 1.
+ tally == 0 ifTrue:[
+ self setTally:0
+ ] ifFalse:[
+ index == keyArray basicSize ifTrue:[
+ next := 1
+ ] ifFalse:[
+ next := index + 1.
+ ].
+ "redundant check to save a send sometimes"
+ (keyArray basicAt:next) notNil ifTrue:[
+ self rehashFrom:next.
+ ]
+ ]
+ ]
+!
+
+removeKey:aKey ifAbsent:aBlock
+ "remove the association under aKey from the collection.
+ If it was not in the collection return result from evaluating aBlock"
+
+ |index
+ next "{ Class:SmallInteger }" |
+
+ aKey isNil ifTrue:[
+ self error:'nil is not allowed as key'
+ ] ifFalse:[
+ index := self findKey:aKey ifAbsent:[0].
+ index == 0 ifTrue:[^ aBlock value].
+ valueArray basicAt:index put:nil.
+ keyArray basicAt:index put:nil.
+ tally := tally - 1.
+ tally == 0 ifTrue:[
+ self setTally:0
+ ] ifFalse:[
+ index == keyArray basicSize ifTrue:[
+ next := 1
+ ] ifFalse:[
+ next := index + 1.
+ ].
+ "redundant check to save a send sometimes"
+ (keyArray basicAt:next) notNil ifTrue:[
+ self rehashFrom:next.
+ ]
+ ]
+ ]
+! !
+
+!Dictionary methodsFor:'enumeration'!
+
+allKeysDo:aBlock
+ "perform the block for all keys in the collection."
+
+ keyArray nonNilElementsDo:aBlock
+!
+
+associationsDo:aBlock
+ "perform the block for all associations in the collection."
+
+ |key|
+
+ tally == 0 ifTrue:[^ self].
+ 1 to:(keyArray basicSize) do:[:index |
+ key := keyArray basicAt:index.
+ key notNil ifTrue:[
+ aBlock value:(Association key:key value:(valueArray basicAt:index))
+ ]
+ ]
+!
+
+do:aBlock
+ "perform the block for all values in the collection."
+
+ |index "{ Class:SmallInteger }" |
+
+ tally == 0 ifTrue:[^ self].
+ index := 1.
+ keyArray do:[:key |
+ key notNil ifTrue:[
+ aBlock value:(valueArray basicAt:index)
+ ].
+ index := index + 1
+ ]
+!
+
+collect:aBlock
+ "for each element in the receiver, evaluate the argument, aBlock
+ and return a Bag with the results"
+
+ |newCollection|
+
+ newCollection := Bag new.
+ self do:[:each |
+ newCollection add:each
+ ].
+ ^ newCollection
+!
+
+select:aBlock
+ "return a new collection with all elements from the receiver, for which
+ the argument aBlock evaluates to true"
+
+ |newCollection|
+
+ newCollection := self species new.
+ self associationsDo:[:assoc |
+ (aBlock value:(assoc value)) ifTrue:[
+ newCollection add:assoc
+ ]
+ ].
+ ^ newCollection
+! !
+
+!Dictionary methodsFor:'private'!
+
+goodSizeFor:arg
+ "return a good array size for the given argument.
+ Returns the next prime after arg"
+
+ arg <= 7 ifTrue:[^ 7].
+ arg <= 16384 ifTrue:[
+ "2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384"
+ ^ #(7 7 11 17 37 67 131 257 521 1031 2053 4099 8209 16411) at:(arg highBit)
+ ].
+ ^ arg bitOr:1
+!
+
+setTally:count
+ "initialize the contents array (for at least count slots)
+ and set tally to zero.
+ The size is increased to the next prime for better hashing behavior."
+
+ |n|
+
+ n := self goodSizeFor:count.
+ valueArray := Array new:n.
+ keyArray := Array new:n.
+ tally := 0
+!
+
+findKeyOrNil:key
+ "Look for the key in the receiver. If it is found, return
+ the index of the association containing the key, otherwise
+ return the index of the first unused slot. Grow the receiver,
+ if key was not found, and no unused slots where present"
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex
+ probe|
+
+ length := keyArray basicSize.
+ startIndex := key hash \\ length + 1.
+
+ index := startIndex.
+ [true] whileTrue:[
+ probe := keyArray basicAt:index.
+ (probe isNil or: [key = probe]) ifTrue:[^ index].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ index == startIndex ifTrue:[^ self grow findKeyOrNil:key]
+ ]
+!
+
+findKey:key ifAbsent:aBlock
+ "Look for the key in the receiver. If it is found, return
+ the index of the association containing the key, otherwise
+ return the value of evaluating aBlock."
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex
+ probe|
+
+ length := keyArray basicSize.
+ length < 10 ifTrue:[
+ "assuming, that for small dictionaries the overhead of hashing
+ is large ... maybe that proves wrong (if overhead of comparing
+ is high)"
+ index := keyArray indexOf:key.
+ index == 0 ifTrue:[
+ ^ aBlock value
+ ].
+ ^ index
+ ].
+
+ startIndex := key hash \\ length + 1.
+
+ index := startIndex.
+ [true] whileTrue:[
+ probe := (keyArray basicAt:index).
+ key = probe ifTrue:[^ index].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ ((probe isNil) or:[index == startIndex]) ifTrue:[
+ ^ aBlock value
+ ]
+ ]
+!
+
+grow
+ "change the number of element slots of the collection to a useful
+ new size"
+
+ self grow:(keyArray basicSize * 2)
+!
+
+grow:newSize
+ "grow the receiver to make space for at least newSize elements.
+ To do this, we have to rehash into the new arrays.
+ (which is done by re-adding all elements to a new, empty key/value array pair)."
+
+ |oldKeys oldValues
+ index "{ Class:SmallInteger }" |
+
+ oldKeys := keyArray.
+ oldValues := valueArray.
+
+ self setTally:newSize.
+
+ index := 1.
+ oldKeys do:[:aKey |
+ aKey notNil ifTrue:[
+ self at:aKey put:(oldValues basicAt:index)
+ ].
+ index := index + 1
+ ]
+!
+
+rehash
+ "rehash contents - is done by re-adding all elements to a new, empty key/value array pair)."
+
+ | oldKeyArray oldValueArray n key
+ newIndex index |
+
+ oldKeyArray := keyArray.
+ oldValueArray := valueArray.
+
+ n := keyArray size.
+ valueArray := Array new:n.
+ keyArray := Array new:n.
+
+ index := 1.
+ oldKeyArray do:[:key |
+ key notNil ifTrue:[
+ newIndex := self findKeyOrNil:key.
+ keyArray basicAt:newIndex put:key.
+ valueArray basicAt:newIndex put:(oldValueArray basicAt:index).
+ ].
+ index := index + 1
+ ]
+!
+
+rehashFrom:startIndex
+ "rehash elements starting at index - after a remove"
+
+ |key i length
+ index "{ Class:SmallInteger }" |
+
+ length := keyArray basicSize.
+ index := startIndex.
+ key := keyArray basicAt:index.
+ [key notNil] whileTrue:[
+ i := self findKeyOrNil:key.
+ i == index ifTrue:[
+ ^ self
+ ].
+ keyArray basicAt:i put:key.
+ valueArray basicAt:i put:(valueArray basicAt:index).
+ keyArray basicAt:index put:nil.
+ valueArray basicAt:index put:nil.
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1.
+ ].
+ key := keyArray basicAt:index.
+ ]
+! !
+
+!Dictionary methodsFor:'printing & storing'!
+
+stringWith:aSelector
+ "common code for printString & displayString"
+
+ |thisString string noneYet|
+
+ string := (self class name) , '('.
+ noneYet := true.
+ self associationsDo:[:element |
+ thisString := element perform:aSelector.
+ noneYet ifTrue:[noneYet := false]
+ ifFalse:[thisString := ' ' , thisString].
+ string := string , thisString
+ ].
+ string := string , ')'.
+ ^string
+!
+
+printString
+ ^ self stringWith:#printString
+!
+
+displayString
+ ^ self stringWith:#displayString
+! !
+
+!Dictionary methodsFor:'inspecting'!
+
+inspect
+ "redefined to launch a DictionaryInspector on the receiver
+ (instead of the default InspectorView)."
+
+ DictionaryInspectorView isNil ifTrue:[
+ super inspect
+ ] ifFalse:[
+ DictionaryInspectorView openOn:self
+ ]
+! !
+
+!Dictionary methodsFor:'error handling'!
+
+errorKeyNotFound
+ "report an error that an element was not found in the collection"
+
+ self error:'the key is not in the collection'
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DirStr.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,169 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+FileStream subclass:#DirectoryStream
+ instanceVariableNames:'dirPointer readAhead'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams-External'
+!
+
+DirectoryStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+#ifndef transputer
+# include <sys/types.h>
+# include <sys/stat.h>
+# ifdef OPENDIR
+# include <sys/types.h>
+# ifdef NEXT
+# include <sys/dir.h>
+# else
+# include <dirent.h>
+# endif
+# endif
+#endif
+%}
+
+!DirectoryStream methodsFor:'instance release'!
+
+closeFile
+ "a directoryStream has been collected - close it"
+%{
+#ifdef OPENDIR
+ closedir((DIR *)MKFD(_INST(dirPointer)));
+#endif
+%}
+! !
+
+!DirectoryStream class methodsFor:'instance creation'!
+
+directoryNamed:dirName
+ "return a DirectoryStream for directory named dirName, aString"
+
+ |newStream|
+
+ newStream := (self basicNew) pathName:dirName.
+ newStream openForReading isNil ifTrue:[^nil].
+ ^ newStream
+! !
+
+!DirectoryStream methodsFor:'access reading'!
+
+nextLine
+ "return the next filename as a string"
+
+ |prevEntry nextEntry|
+%{
+#ifdef OPENDIR
+ DIR *d;
+#ifdef NEXT
+ struct direct *dp;
+#else
+ struct dirent *dp;
+#endif
+
+ if (_INST(dirPointer) != nil) {
+ d = (DIR *)MKFD(_INST(dirPointer));
+ dp = readdir(d);
+ if (dp != NULL) {
+ nextEntry = _MKSTRING((char *)(dp->d_name) COMMA_CON);
+ }
+ }
+#endif
+%}
+.
+ prevEntry := readAhead.
+ readAhead := nextEntry.
+ ^ prevEntry
+! !
+
+!DirectoryStream methodsFor:'private'!
+
+openForReading
+ "open the file for readonly"
+
+ |retVal|
+
+ mode := #readonly.
+%{
+#ifdef OPENDIR
+ DIR *d;
+ OBJ path;
+ extern OBJ ErrorNumber, ErrorString;
+ extern errno;
+
+ retVal = false;
+ if (_INST(dirPointer) == nil) {
+ path = _INST(pathName);
+ if (_isString(path)) {
+ d = opendir((char *) _stringVal(path));
+ if (d == NULL) {
+ /* ErrorString = _MKSTRING(perror("popen:") COMMA_CON); */
+ ErrorNumber = _MKSMALLINT(errno);
+ } else {
+ _INST(dirPointer) = MKOBJ(d);
+ retVal = true;
+ }
+ }
+ }
+#endif
+%}
+.
+ retVal isNil ifTrue:[
+ "opendir not avalable - use slow pipe"
+ ^ PipeStream readingFrom:('cd ' , pathName , '; ls -a')
+ ].
+ (retVal == true) ifTrue:[
+ lobby register:self.
+ self nextLine. "read 1st entry into readAhead buffer"
+ ^ self
+ ].
+ ^ nil
+!
+
+reOpen
+ "sent after snapin to reopen streams"
+
+ filePointer notNil ifTrue:[
+ "it was open, when snapped-out"
+ filePointer := nil.
+ 'cannot reopen directorystream' printNewline
+ ]
+! !
+
+!DirectoryStream methodsFor:'testing'!
+
+atEnd
+ "return true, if position is at end"
+
+ ^ readAhead == nil
+! !
+
+!DirectoryStream methodsFor:'closing'!
+
+close
+ "close the stream - tell operating system"
+
+ dirPointer notNil ifTrue:[
+ lobby unregister:self.
+ self closeFile.
+ dirPointer := nil
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DirectoryStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,169 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+FileStream subclass:#DirectoryStream
+ instanceVariableNames:'dirPointer readAhead'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams-External'
+!
+
+DirectoryStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+#ifndef transputer
+# include <sys/types.h>
+# include <sys/stat.h>
+# ifdef OPENDIR
+# include <sys/types.h>
+# ifdef NEXT
+# include <sys/dir.h>
+# else
+# include <dirent.h>
+# endif
+# endif
+#endif
+%}
+
+!DirectoryStream methodsFor:'instance release'!
+
+closeFile
+ "a directoryStream has been collected - close it"
+%{
+#ifdef OPENDIR
+ closedir((DIR *)MKFD(_INST(dirPointer)));
+#endif
+%}
+! !
+
+!DirectoryStream class methodsFor:'instance creation'!
+
+directoryNamed:dirName
+ "return a DirectoryStream for directory named dirName, aString"
+
+ |newStream|
+
+ newStream := (self basicNew) pathName:dirName.
+ newStream openForReading isNil ifTrue:[^nil].
+ ^ newStream
+! !
+
+!DirectoryStream methodsFor:'access reading'!
+
+nextLine
+ "return the next filename as a string"
+
+ |prevEntry nextEntry|
+%{
+#ifdef OPENDIR
+ DIR *d;
+#ifdef NEXT
+ struct direct *dp;
+#else
+ struct dirent *dp;
+#endif
+
+ if (_INST(dirPointer) != nil) {
+ d = (DIR *)MKFD(_INST(dirPointer));
+ dp = readdir(d);
+ if (dp != NULL) {
+ nextEntry = _MKSTRING((char *)(dp->d_name) COMMA_CON);
+ }
+ }
+#endif
+%}
+.
+ prevEntry := readAhead.
+ readAhead := nextEntry.
+ ^ prevEntry
+! !
+
+!DirectoryStream methodsFor:'private'!
+
+openForReading
+ "open the file for readonly"
+
+ |retVal|
+
+ mode := #readonly.
+%{
+#ifdef OPENDIR
+ DIR *d;
+ OBJ path;
+ extern OBJ ErrorNumber, ErrorString;
+ extern errno;
+
+ retVal = false;
+ if (_INST(dirPointer) == nil) {
+ path = _INST(pathName);
+ if (_isString(path)) {
+ d = opendir((char *) _stringVal(path));
+ if (d == NULL) {
+ /* ErrorString = _MKSTRING(perror("popen:") COMMA_CON); */
+ ErrorNumber = _MKSMALLINT(errno);
+ } else {
+ _INST(dirPointer) = MKOBJ(d);
+ retVal = true;
+ }
+ }
+ }
+#endif
+%}
+.
+ retVal isNil ifTrue:[
+ "opendir not avalable - use slow pipe"
+ ^ PipeStream readingFrom:('cd ' , pathName , '; ls -a')
+ ].
+ (retVal == true) ifTrue:[
+ lobby register:self.
+ self nextLine. "read 1st entry into readAhead buffer"
+ ^ self
+ ].
+ ^ nil
+!
+
+reOpen
+ "sent after snapin to reopen streams"
+
+ filePointer notNil ifTrue:[
+ "it was open, when snapped-out"
+ filePointer := nil.
+ 'cannot reopen directorystream' printNewline
+ ]
+! !
+
+!DirectoryStream methodsFor:'testing'!
+
+atEnd
+ "return true, if position is at end"
+
+ ^ readAhead == nil
+! !
+
+!DirectoryStream methodsFor:'closing'!
+
+close
+ "close the stream - tell operating system"
+
+ dirPointer notNil ifTrue:[
+ lobby unregister:self.
+ self closeFile.
+ dirPointer := nil
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DoubleArray.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,49 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ArrayedCollection variableDoubleSubclass:#DoubleArray
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Indexed'
+!
+
+DoubleArray comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+DoubleArrays store double floats (and nothing else).
+They have been added to support heavy duty number crunching somewhat
+more than other smalltalks do. Storing Floats & Doubles in these
+objects many benefits:
+
+1) since the values are stored directly (instead of pointers to them)
+ both access overhead and garbage collect overhead is minimized.
+
+2) they can be much faster passed to c functions (such as graphics
+libraries or heavy duty math packages)
+
+3) they could (in theory) be much more easily be processed by things like
+vector and array processors
+
+Be aware however, that Float- and DoubleArrays are not supported in other
+smalltalks - your program will thus become somewhat less portable.
+(since their protocol is the same as normal arrays filled with floats,
+ they can of course be easily simulated - a bit slower)
+
+See an example use in the GLX interface.
+
+%W% %E%
+
+written june 93 by claus
+'!
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Exception.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,141 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Exception
+ instanceVariableNames:'signal parameter suspendedContext handlerContext
+ resumeBlock rejectBlock'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Exceptions'
+!
+
+Exception comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+Instances of Exception are passed to a Signal handling block as argument.
+The handler block may perform various actions by sending corresponding messages
+to the exception handler. The following actions are possible:
+
+ reject - dont handle this signal;
+ another handler will be searched for,
+ upper in the calling hierarchy
+
+ proceed - continue after the Signal>>raise, returning nil as value
+ proceedWith:val - same, but return val from Signal>>raise
+
+ return - return from the Signal>>handle:do:, returning nil as value
+ return:val - same, but return val from Signal>>handle:do:
+
+ restart - restart the Signal>>handle:do:, after repairing
+
+Via the Exception object, the handler can also query the state of execution,
+where the Signal was raised.
+'!
+
+!Exception methodsFor:'accessing'!
+
+signal
+ "return the signal, that caused the exception"
+
+ ^ signal
+!
+
+parameter:aParameter
+ parameter := aParameter
+!
+
+parameter
+ ^ parameter
+!
+
+suspendedContext
+! !
+
+!Exception methodsFor:'setup'!
+
+signal:aSignal
+ "this is meant to be sent by Signal only"
+
+ signal := aSignal
+!
+
+handlerContext:aContext
+ "this is meant to be sent by Signal only"
+
+ handlerContext := aContext
+!
+
+rejectBlock:aBlock
+ "this is meant to be sent by Signal only"
+
+ rejectBlock := aBlock
+!
+
+resumeBlock:aBlock
+ "this is meant to be sent by Signal only"
+
+ resumeBlock := aBlock
+! !
+
+!Exception methodsFor:'handler actions'!
+
+reject
+ "handler decided not to handle this signal -
+ system will look for another handler"
+
+ rejectBlock value
+!
+
+resume
+ "Continue after the raise - the raise returns nil"
+
+ resumeBlock value:nil
+!
+
+resumeWith:value
+ "Continue after the raise - the raise returns value"
+
+ resumeBlock value:value
+!
+
+proceed
+ "Continue after the raise - the raise returns nil"
+
+ resumeBlock value:nil
+!
+
+proceedWith:value
+ "Continue after the raise - the raise returns value"
+
+ resumeBlock value:value
+!
+
+return
+ "Continue after the handle:do: - the handle:do: returns nil"
+
+ handlerContext resume
+!
+
+returnWith:value
+ "Continue after the handle:do: - the handle:do: returns value"
+
+ handlerContext resume:value
+!
+
+restart
+ "restart the handle:do: - usually after some repair work is done
+ in handler"
+
+ handlerContext restart
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ExtStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1437 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ReadWriteStream subclass:#ExternalStream
+ instanceVariableNames:'filePointer mode unBuffered binary'
+ classVariableNames:'lobby'
+ poolDictionaries:''
+ category:'Streams-External'
+!
+
+ExternalStream comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ExternalStream defines protocol common to Streams which have a file-descriptor and
+represent some File or CommunicationChannel of the underlying OperatingSystem.
+ExternalStream is abstract; concrete classes are FileStream, PipeStream etc.
+ExternalStreams can be in two modes: text (the default) and binary.
+In text-mode, the elements read/written are characters; while in binary-mode the basic
+elements are bytes which read/write as SmallIntegers in the range 0..255.
+
+%W% %E%
+
+written 88 by claus
+'!
+
+%{
+#include <stdio.h>
+%}
+
+!ExternalStream class methodsFor:'initialization'!
+
+initialize
+ lobby isNil ifTrue:[
+ lobby := Registry new.
+
+ "want to get informed when returning from snapshot"
+ ObjectMemory addDependent:self
+ ]
+!
+
+reOpenFiles
+ "reopen all files (if possible) after a snapShot load"
+
+ lobby contentsDo:[:aFileStream |
+ aFileStream reOpen
+ ]
+!
+
+update:something
+ "have to reopen files when returning from snapshot"
+
+ something == #returnFromSnapshot ifTrue:[
+ self reOpenFiles
+ ]
+! !
+
+!ExternalStream methodsFor:'instance release'!
+
+disposed
+ "some Stream has been collected - close the file if not already done"
+
+ self closeFile
+!
+
+closeFile
+ "low level close - may be redefined in subclasses"
+
+%{ /* NOCONTEXT */
+
+ fclose(MKFD(_INST(filePointer)));
+%}
+! !
+
+!ExternalStream methodsFor:'private'!
+
+reOpen
+ "sent after snapin to reopen streams.
+ cannot reopen here since I am abstract and have no device knowledge"
+
+ self class name print. ': cannot reOpen stream - stream closed' printNewline.
+ filePointer := nil.
+ lobby unregister:self.
+! !
+
+!ExternalStream methodsFor:'error handling'!
+
+errorNotOpen
+ "report an error, that the stream has not been opened"
+
+ ^ self error:(self class name , ' not open')
+!
+
+errorReadOnly
+ "report an error, that the stream is a readOnly stream"
+
+ ^ self error:(self class name , ' is readonly')
+!
+
+errorWriteOnly
+ "report an error, that the stream is a writeOnly stream"
+
+ ^ self error:(self class name , ' is writeonly')
+!
+
+errorNotBinary
+ "report an error, that the stream is not in binary mode"
+
+ ^ self error:(self class name , ' is not in binary mode')
+!
+
+argumentMustBeInteger
+ "report an error, that the argument must be an integer"
+
+ ^ self error:'argument must be an integer'
+!
+
+argumentMustBeCharacter
+ "report an error, that the argument must be a character"
+
+ ^ self error:'argument must be a character'
+!
+
+argumentMustBeString
+ "report an error, that the argument must be a string"
+
+ ^ self error:'argument must be a string'
+! !
+
+!ExternalStream methodsFor:'accessing'!
+
+readonly
+ "set access mode to readonly"
+
+ mode := #readonly
+!
+
+writeonly
+ "set access mode to writeonly"
+
+ mode := #writeonly
+!
+
+readwrite
+ "set access mode to readwrite"
+
+ mode := #readwrite
+!
+
+filePointer
+ "return the filePointer of the receiver -
+ notice: for portability stdio is used; this means you will get
+ a FILE * - not a fileDescriptor"
+
+ ^ filePointer
+!
+
+fileDescriptor
+ "return the fileDescriptor of the receiver -
+ notice: this one returns the underlying OSs fileDescriptor -
+ this may not be available on all platforms (i.e. non unix systems)."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+
+ if (_INST(filePointer) != nil) {
+ f = MKFD(_INST(filePointer));
+ RETURN ( MKOBJ(fileno(f)) );
+ }
+%}
+.
+ ^ self errorNotOpen
+!
+
+buffered:aBoolean
+ "turn buffering on or off"
+
+ unBuffered := aBoolean not
+!
+
+binary
+ "switch to binary mode"
+
+ binary := true
+!
+
+text
+ "switch to text mode"
+
+ binary := false
+!
+
+contents
+ "return the contents of the file as a Text-object"
+
+ |text|
+
+ text := Text new.
+ [self atEnd] whileFalse:[
+ text add:(self nextLine)
+ ].
+ ^ text
+! !
+
+!ExternalStream methodsFor:'basic'!
+
+open
+ "open the stream
+ - this must be redefined in subclass"
+
+ ^ self subclassResponsibility
+!
+
+create
+ "create the stream
+ - this must be redefined in subclass"
+
+ ^ self subclassResponsibility
+!
+
+position
+ "return the position
+ - this must be redefined in subclass"
+
+ ^ self subclassResponsibility
+!
+
+position:anInteger
+ "set the position
+ - this must be redefined in subclass"
+
+ ^ self subclassResponsibility
+! !
+
+!ExternalStream methodsFor:'low level I/O'!
+
+ioctl:ioctlNumber with:arg
+ "to provide a simple ioctl facility"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int ret, ioNum, ioArg;
+ extern OBJ ErrorNumber;
+ extern errno;
+
+ if (_INST(filePointer) != nil) {
+ if (_isSmallInteger(ioctlNumber) && _isSmallInteger(arg)) {
+ ioNum = _intVal(ioctlNumber);
+ ioArg = _intVal(arg);
+ f = MKFD(_INST(filePointer));
+ ret = ioctl(fileno(f), ioNum, ioArg);
+ if (ret >= 0) {
+ RETURN ( _MKSMALLINT(ret) );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self primitiveFailed
+!
+
+nextByte
+ "read the next byte return it as an Integer
+ nil on error. Use with care - non object oriented i/o"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ unsigned char byte;
+ int cnt;
+ extern OBJ ErrorNumber;
+ extern errno;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ _immediateInterrupt = 1;
+ if (_INST(unBuffered) == true) {
+ cnt = read(fileno(f), &byte, 1);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fread(&byte, 1, 1, f);
+ }
+ _immediateInterrupt = 0;
+ if (cnt == 1) {
+ if (_INST(position) != nil)
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
+ RETURN ( _MKSMALLINT(byte) );
+ }
+ if (cnt < 0) {
+ ErrorNumber = _MKSMALLINT(errno);
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorWriteOnly
+!
+
+nextBytes:count into:anObject
+ "read the next count bytes into an object and return the number of
+ bytes read or nil on error.
+ Use with care - non object oriented i/o"
+
+ ^ self nextBytes:count into:anObject startingAt:1
+!
+
+nextBytes:count into:anObject startingAt:start
+ "read the next count bytes into an object and return the number of
+ bytes read or nil on error.
+ Use with care - non object oriented i/o"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int cnt, offs;
+ int objSize;
+ char *cp;
+ extern OBJ ErrorNumber;
+ extern errno;
+ OBJ pos;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ if (_isSmallInteger(count) && _isSmallInteger(start)) {
+ cnt = _intVal(count);
+ offs = _intVal(start) - 1;
+ f = MKFD(_INST(filePointer));
+ objSize = _Size(anObject) - OHDR_SIZE;
+ if ((offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs))) {
+ cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs;
+ _immediateInterrupt = 1;
+ if (_INST(unBuffered) == true) {
+ cnt = read(fileno(f), cp, cnt);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fread(cp, 1, cnt, f);
+ }
+ _immediateInterrupt = 0;
+ if (cnt >= 0) {
+ pos = _INST(position);
+ if (pos != nil)
+ _INST(position) = _MKSMALLINT(_intVal(pos) + cnt);
+ RETURN ( _MKSMALLINT(cnt) );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ }
+ }
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ self primitiveFailed
+!
+
+nextPutByte:aByteValue
+ "write a byte"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ char c;
+ extern OBJ ErrorNumber;
+ extern errno;
+ OBJ pos;
+ int cnt;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ if (_isSmallInteger(aByteValue)) {
+ c = _intVal(aByteValue);
+ f = MKFD(_INST(filePointer));
+ _immediateInterrupt = 1;
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(&c, 1, 1, f);
+ _immediateInterrupt = 0;
+ if (cnt == 1) {
+ if (_INST(unBuffered) == true) {
+ fflush(f);
+ }
+ pos = _INST(position);
+ if (pos != nil)
+ _INST(position) = _MKSMALLINT(_intVal(pos) + 1);
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ }
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ self primitiveFailed
+!
+
+nextPutBytes:count from:anObject
+ "write count bytes from an object starting at index start.
+ return the number of bytes written or nil on error.
+ Use with care - non object oriented i/o"
+
+ ^ self nextPutBytes:count from:anObject startingAt:1
+!
+
+nextPutBytes:count from:anObject startingAt:start
+ "write count bytes from an object starting at index start.
+ return the number of bytes written or nil on error.
+ Use with care - non object oriented i/o"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int cnt, offs;
+ int objSize;
+ char *cp;
+ extern OBJ ErrorNumber;
+ extern errno;
+ OBJ pos;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ if (_isSmallInteger(count) && _isSmallInteger(start)) {
+ cnt = _intVal(count);
+ offs = _intVal(start) - 1;
+ f = MKFD(_INST(filePointer));
+
+ objSize = _Size(anObject) - OHDR_SIZE;
+ if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
+ cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs;
+ _immediateInterrupt = 1;
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), cp, cnt);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(cp, 1, cnt, f);
+ }
+ _immediateInterrupt = 0;
+ if (cnt >= 0) {
+ pos = _INST(position);
+ if (pos != nil)
+ _INST(position) = _MKSMALLINT(_intVal(pos) + cnt);
+ RETURN ( _MKSMALLINT(cnt) );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ }
+ }
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ self primitiveFailed
+! !
+
+!ExternalStream methodsFor:'character I/O'!
+
+peek
+ "return the character to be read next without advancing read position"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ REGISTER int c;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ c = getc(f);
+ if (c != EOF) {
+ ungetc(c, f);
+ if (_INST(binary) == true) {
+ RETURN ( _MKSMALLINT(c & 0xFF) );
+ }
+ RETURN ( _MKCHARACTER(c & 0xFF) );
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorWriteOnly
+!
+
+next
+ "return the character; advance read position"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int c;
+ OBJ pos;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ _immediateInterrupt = 1;
+ if (_INST(unBuffered) == true) {
+ if (read(fileno(f), &c, 1) != 1)
+ c = EOF;
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ c = getc(f);
+ }
+ _immediateInterrupt = 0;
+ if (c != EOF) {
+ pos = _INST(position);
+ if (pos != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(pos) + 1);
+ }
+ if (_INST(binary) == true) {
+ RETURN ( _MKSMALLINT(c & 0xFF) );
+ }
+ RETURN ( _MKCHARACTER(c & 0xFF) );
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorWriteOnly
+!
+
+nextPut:aCharacter
+ "write the argument, aCharacter - return nil if failed, self if ok"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ char c;
+ extern OBJ ErrorNumber;
+ extern errno;
+ int cnt;
+ OBJ pos;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ if (_isCharacter(aCharacter)) {
+ c = _intVal(_CharacterInstPtr(aCharacter)->c_asciivalue);
+ doWrite:
+ f = MKFD(_INST(filePointer));
+
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), &c, 1);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(&c, 1, 1, f);
+ }
+ if (cnt == 1) {
+ if (_INST(unBuffered) == true) {
+ fflush(f);
+ }
+ pos = _INST(position);
+ if (pos != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(pos) + 1);
+ }
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ } else {
+ if (_INST(binary) == true) {
+ if (_isSmallInteger(aCharacter)) {
+ c = _intVal(aCharacter);
+ goto doWrite;
+ }
+ }
+ }
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ self argumentMustBeCharacter
+!
+
+nextPutAll:aCollection
+ "write all elements of the argument, aCollection"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ unsigned char *cp;
+ int len, cnt;
+ extern OBJ ErrorNumber;
+ extern errno;
+ OBJ pos;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ if (_isString(aCollection) || _isSymbol(aCollection)) {
+ cp = _stringVal(aCollection);
+ len = _stringSize(aCollection);
+ f = MKFD(_INST(filePointer));
+
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), cp, len);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(cp, 1, len, f);
+ }
+ if (cnt == len) {
+ if (_INST(unBuffered) == true) {
+ fflush(f);
+ }
+ pos = _INST(position);
+ if (pos != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(pos) + len);
+ }
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ }
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+
+ aCollection do:[:element |
+ self nextPut:element
+ ]
+!
+
+nextPut:aCollection from:start to:stop
+ "write a range of elements of the argument, aCollection"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ unsigned char *cp;
+ int len, cnt, index1, index2;
+ extern OBJ ErrorNumber;
+ extern errno;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_isString(aCollection)
+ && _isSmallInteger(start)
+ && _isSmallInteger(stop)) {
+ f = MKFD(_INST(filePointer));
+ cp = _stringVal(aCollection);
+ len = _stringSize(aCollection);
+ index1 = _intVal(start);
+ index2 = _intVal(stop);
+ if ((index1 < 1) || (index2 > len) || (index2 < index1)) {
+ RETURN ( self );
+ }
+ if (index2 > len)
+ index2 = len;
+
+ len = index2 - index1 + 1;
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), cp + index1 - 1, len);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(cp + index1 - 1, 1, len, f);
+ }
+ if (cnt == len) {
+ if (_INST(unBuffered) == true) {
+ fflush(f);
+ }
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + len);
+ }
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+
+ start to:stop do:[:index |
+ self nextPut:(aCollection at:index)
+ ]
+!
+
+cr
+ "reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ extern OBJ ErrorNumber;
+ extern errno;
+ extern int _immediateInterrupt;
+ int cnt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ f = MKFD(_INST(filePointer));
+
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), "\n", 1);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite("\n", 1, 1, f);
+ }
+ if (cnt == 1) {
+ if (_INST(unBuffered) == true) {
+ fflush(f);
+ }
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
+ }
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ return ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorReadOnly
+! !
+
+!ExternalStream methodsFor:'more character I/O'!
+
+nextWord
+ "in text-mode:
+ read the next word (i.e. up to non letter-or-digit).
+ return a string containing those characters.
+ in binary-mode:
+ read two bytes (msb-first) and return the value as a 16-bit unsigned Integer
+ (msb-first for compatibility with other smalltalks)"
+
+%{ /* NOCONTEXT */
+ extern int _immediateInterrupt;
+
+ if (_INST(binary) == true) {
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ FILE *f;
+ int hi, low;
+
+ f = MKFD(_INST(filePointer));
+ hi = getc(f);
+ if (hi == EOF) {
+ RETURN ( nil );
+ }
+ low = getc(f);
+ if (low == EOF) {
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
+ }
+ RETURN ( _MKSMALLINT(hi & 0xFF) );
+ }
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 2);
+ }
+ RETURN ( _MKSMALLINT(((hi & 0xFF)<<8) | (low & 0xFF)) );
+ }
+ }
+ }
+%}
+.
+%{
+ FILE *f;
+ int len;
+ char buffer[1024];
+ int ch;
+ int cnt = 0;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ /* text-mode */
+ for (;;) {
+ ch = getc(f);
+ cnt++;
+
+ if (ch >= ' ') break;
+ if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+ && (ch != '\n') && (ch != 0x0b)) break;
+ }
+ ungetc(ch, f);
+ cnt--;
+
+ len = 0;
+ for (;;) {
+ ch = getc(f);
+ if (ch == EOF)
+ break;
+ ch &= 0xFF;
+ if (! (((ch >= 'a') && (ch <= 'z')) ||
+ ((ch >= 'A') && (ch <= 'Z')) ||
+ ((ch >= '0') && (ch <= '9')))) {
+ ungetc(ch, f);
+ break;
+ }
+ cnt++;
+ buffer[len++] = ch;
+ if (len >= sizeof(buffer)-1) {
+ /* emergency */
+ break;
+ }
+ }
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + cnt);
+ }
+ buffer[len] = '\0';
+ if (len != 0) {
+ RETURN ( _MKSTRING(buffer COMMA_CON) );
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorWriteOnly
+!
+
+nextWordPut:aNumber
+ "only in binary-mode:
+ write the argument, aNumber as two bytes (msb-first)
+ (msb-first for compatibility with other smalltalks)"
+
+%{ /* NOCONTEXT */
+
+ int num;
+ char bytes[2];
+ FILE *f;
+ extern OBJ ErrorNumber;
+ extern errno;
+ extern int _immediateInterrupt;
+
+ if (_INST(binary) == true) {
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ if (_isSmallInteger(aNumber)) {
+ num = _intVal(aNumber);
+ bytes[0] = (num >> 8) & 0xFF;
+ bytes[1] = num & 0xFF;
+
+ f = MKFD(_INST(filePointer));
+ if (fwrite(bytes, 1, 2, f) == 2) {
+ if (_INST(unBuffered) == true) {
+ fflush(f);
+ }
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 2);
+ }
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ return ( nil );
+ }
+ }
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ binary ifFalse:[^ self errorNotBinary].
+ self argumentMustBeInteger
+!
+
+nextLong
+ "in binary-mode:
+ read two bytes (msb-first) and return the value as a 16-bit unsigned Integer
+ (msb-first for compatibility with other smalltalks)"
+
+ |lo hi|
+
+ binary ifFalse:[
+ ^ self error:'method only valid in binary mode'
+ ].
+ hi := self nextWord.
+ lo := self nextWord.
+ ^ hi * 16r10000 + lo
+!
+
+nextLine
+ "read the next line (characters up to newline).
+ Return a string containing those characters excluding the newline.
+ If the previous-to-last character is a cr, this is also removed,
+ so its possible to read alien (i.e. ms-dos) text as well."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int len;
+ char buffer[1024*16];
+ extern int _immediateInterrupt;
+ char *rslt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(filePointer) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ _immediateInterrupt = 1;
+ buffer[0] = 0;
+ rslt = fgets(buffer, sizeof(buffer), f);
+ _immediateInterrupt = 0;
+ if (rslt != NULL) {
+ len = strlen(buffer);
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + len);
+ }
+ /* remove EOL character */
+ if ((len != 0) && (buffer[len-1] == '\n')) {
+ buffer[--len] = '\0';
+ }
+ if ((len != 0) && (buffer[len-1] == '\r')) {
+ buffer[--len] = '\0';
+ }
+ RETURN ( _MKSTRING(buffer COMMA_CON) );
+ }
+ }
+ }
+%}
+.
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ nil
+!
+
+nextPutLine:aString
+ "write the characters in aString and append a newline"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int len, cnt;
+ OBJ pos;
+ char *s;
+ extern OBJ ErrorNumber;
+ extern errno;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ if (_isString(aString)) {
+ f = MKFD(_INST(filePointer));
+ s = (char *) _stringVal(aString);
+ len = _stringSize(aString);
+
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), s, len);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(s, 1, len, f);
+ }
+ if (cnt == len) {
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), "\n", 1);
+ } else {
+ cnt = fwrite("\n", 1, 1, f);
+ }
+ if (cnt == 1) {
+ pos = _INST(position);
+ if (pos != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(pos)+len+1);
+ }
+ RETURN ( self );
+ }
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ }
+ }
+ }
+%}
+.
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self argumentMustBeString
+!
+
+nextPutLinesFrom:aStream upToLineStartingWith:aStringOrNil
+ "used to copy large files
+ - read from aStream up to and including a line starting with aStringOrNil
+ and append it to self. If aStringOrNil is nil or not matched,
+ copy preceeds to the end"
+
+ |srcFilePointer|
+
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ srcFilePointer := aStream filePointer.
+ srcFilePointer isNil ifTrue:[^ aStream errorNotOpen].
+%{
+ FILE *dst, *src;
+ char *matchString;
+ int matchLen = 0;
+ char buffer[1024*16];
+ extern int _immediateInterrupt;
+
+ if (_isSmallInteger(srcFilePointer)) {
+ if ((aStringOrNil == nil)
+ || _isString(aStringOrNil)) {
+ if (aStringOrNil != nil) {
+ matchString = (char *) _stringVal(aStringOrNil);
+ matchLen = _stringSize(aStringOrNil);
+ }
+ dst = MKFD(_INST(filePointer));
+ src = (FILE *)_intVal(srcFilePointer);
+ for (;;) {
+ if (fgets(buffer, sizeof(buffer), src) == NULL) break;
+ if (fputs(buffer, dst) == EOF) break;
+ if (matchLen) {
+ if (strncmp(matchString, buffer, matchLen) == 0)
+ break;
+ }
+ }
+ if (_INST(unBuffered) == true) {
+ fflush(dst);
+ }
+ _INST(position) = nil;
+ RETURN ( self );
+ }
+ }
+%}
+.
+ ^ self primitiveFailed
+!
+
+peekForLineStartingWith:aString
+ "read ahead for next line starting with aString;
+ return the line-string if found, nil otherwise..
+ do not advance position i.e. nextLine will reread this line"
+
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+%{
+ FILE *f;
+ int l;
+ char buffer[1024*10];
+ char *cp;
+ char *matchString;
+ int firstpos, lastpos;
+ extern int _immediateInterrupt;
+
+ if (_isString(aString)) {
+ matchString = (char *) _stringVal(aString);
+ l = _stringSize(aString);
+
+ f = MKFD(_INST(filePointer));
+ firstpos = ftell(f);
+ for (;;) {
+ lastpos = ftell(f);
+ _immediateInterrupt = 1;
+ cp = fgets(buffer, sizeof(buffer), f);
+ _immediateInterrupt = 0;
+ if (cp == NULL) {
+ fseek(f, firstpos, 0);
+ RETURN ( nil );
+ }
+ if (strncmp(cp, matchString, l) == 0) {
+ fseek(f, lastpos, 0);
+ break;
+ }
+ }
+ /* remove EOL character */
+ cp = buffer;
+ while (*cp && (*cp != '\n')) cp++;
+ *cp = '\0';
+ RETURN ( _MKSTRING(buffer COMMA_CON) );
+ }
+%}
+.
+ self argumentMustBeString
+!
+
+peekForLineStartingWithAny:aCollectionOfStrings
+ "read ahead for next line starting with any of aCollectionOfStrings;
+ return the index in aCollection if found, nil otherwise..
+ If no match, do not change position; otherwise advance right before the
+ matched line so that nextLine will return this line."
+
+ |line startPos linePos index|
+
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ startPos := self position.
+ [self atEnd] whileFalse:[
+ linePos := self position.
+ line := self nextLine.
+ index := 1.
+ aCollectionOfStrings do:[:prefix |
+ (line startsWith:prefix) ifTrue:[
+ self position:linePos.
+ ^ index
+ ].
+ index := index + 1
+ ]
+ ].
+ self position:startPos.
+ ^ nil
+! !
+
+!ExternalStream methodsFor:'testing'!
+
+atEnd
+ "return true, if position is at end"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+
+ if (_INST(filePointer) != nil) {
+ f = MKFD(_INST(filePointer));
+ RETURN ( feof(f) ? true : false );
+ }
+%}
+.
+ self errorNotOpen
+! !
+
+!ExternalStream methodsFor:'closing'!
+
+close
+ "close the stream - tell operating system"
+
+ filePointer isNil ifTrue:[^ self].
+ lobby unregister:self.
+ self closeFile.
+ filePointer := nil
+! !
+
+!ExternalStream methodsFor:'reimplemented for speed'!
+
+peekFor:aCharacter
+ "return true and move past if next == something.
+ - reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int c;
+ int peekvalue;
+ extern int _immediateInterrupt;
+
+ if (_isCharacter(aCharacter)) {
+ if (_INST(filePointer) != nil) {
+ peekvalue = _intVal(_CharacterInstPtr(aCharacter)->c_asciivalue);
+ f = MKFD(_INST(filePointer));
+ c = getc(f);
+ if (c == peekvalue) {
+ RETURN ( true );
+ }
+ ungetc(c, f);
+ RETURN ( false );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ super peekFor:aCharacter
+!
+
+skipLine
+ "read the next line (characters up to newline) skip only;
+ return nil if EOF reached"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ char buffer[1024*10];
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ if (fgets(buffer, sizeof(buffer), f) != NULL) {
+ RETURN ( self );
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorWriteOnly
+!
+
+skipToAll:aString
+ "skip for the sequence given by the argument, aCollection;
+ return nil if not found, self otherwise. On a successful match, next read
+ will return characters of aString."
+
+ |oldPos buffer l first idx|
+
+ (aString isKindOf:String) ifTrue:[
+ oldPos := self position.
+ l := aString size.
+ first := aString at:1.
+ buffer := String new:l.
+ [true] whileTrue:[
+ (self nextBytes:l into:buffer) == l ifFalse:[
+ self position:oldPos.
+ ^ nil
+ ].
+ buffer = aString ifTrue:[
+ self position:(self position - l).
+ ^ self
+ ].
+ idx := buffer indexOf:first startingAt:2.
+ idx == 0 ifFalse:[
+ self position:(self position - l + idx - 1)
+ ]
+ ]
+ ].
+ ^ super skipFor:aString
+!
+
+skipSeparators
+ "skip all whitespace; next will return next non-white-space character
+ or nil if endOfFile reached.
+ - reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ REGISTER int c;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ while (1) {
+ if (feof(f)) {
+ RETURN ( nil );
+ }
+ c = getc(f);
+ if (c < 0) {
+ RETURN ( nil );
+ }
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\b':
+ case '\014':
+ break;
+ default:
+ ungetc(c, f);
+ RETURN ( _MKCHARACTER(c & 0xFF) );
+ }
+ }
+ }
+ }
+%}
+.
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ self errorNotOpen
+!
+
+skipSeparatorsExceptCR
+ "skip all whitespace but no newlines;
+ next will return next non-white-space character
+ or nil if endOfFile reached.
+ - reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int c;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ while (1) {
+ if (feof(f)) {
+ RETURN ( nil );
+ }
+ c = getc(f);
+ if (c < 0) {
+ RETURN ( nil );
+ }
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\b':
+ break;
+ default:
+ ungetc(c, f);
+ RETURN ( _MKCHARACTER(c & 0xFF) );
+ }
+ }
+ }
+ }
+%}
+.
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ self errorNotOpen
+!
+
+nextChunk
+ "return the next chunk, i.e. all characters up to the next
+ non-doubled exclamation mark; undouble doubled exclamation marks.
+ - reimplemented for speed"
+ |retVal|
+
+ filePointer isNil ifTrue:[
+ ^ self errorNotOpen
+ ].
+%{
+ FILE *f;
+ int done = 0;
+ REGISTER int c;
+ unsigned char peekC;
+ char *buffer, *newBuffer;
+ REGISTER int index;
+ int currSize;
+ int inComment, inString, inPrimitive = 0;
+ extern int _immediateInterrupt;
+
+ f = MKFD(_INST(filePointer));
+ /*
+ * skip spaces
+ */
+ while (! done) {
+ if (feof(f)) {
+ RETURN ( nil );
+ }
+ c = getc(f);
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\b':
+ case '\014':
+ break;
+
+ case EOF:
+ RETURN ( nil );
+
+ default:
+ ungetc(c, f);
+ done = 1;
+ break;
+ }
+ }
+
+ /*
+ * read chunk into a buffer
+ */
+ buffer = (char *)malloc(3000);
+ currSize = 3000;
+ index = 0;
+ while (! feof(f)) {
+ /* do we have to resize the buffer ? */
+ if ((index+2) >= currSize) {
+ newBuffer = (char *)malloc(currSize * 2);
+ bcopy(buffer, newBuffer, index);
+ free(buffer);
+ buffer = newBuffer;
+ currSize = currSize * 2;
+ }
+ c = getc(f);
+ if (c == '%') {
+ peekC = getc(f);
+ ungetc(peekC, f);
+ if (peekC == '{') {
+ inPrimitive++;
+ } else if (peekC == '}') {
+ inPrimitive--;
+ }
+ } else {
+ if (! inPrimitive) {
+ if (c == '!') {
+ c = getc(f);
+ if (c != '!') {
+ ungetc(c, f);
+ break;
+ }
+ }
+ }
+ }
+ if (c == EOF) break;
+ buffer[index++] = c;
+ }
+ buffer[index] = '\0';
+ /*
+ * make it a string
+ */
+ retVal = _MKSTRING(buffer COMMA_CON);
+ free(buffer);
+%}
+.
+ ^ retVal
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ExternalStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1437 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ReadWriteStream subclass:#ExternalStream
+ instanceVariableNames:'filePointer mode unBuffered binary'
+ classVariableNames:'lobby'
+ poolDictionaries:''
+ category:'Streams-External'
+!
+
+ExternalStream comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ExternalStream defines protocol common to Streams which have a file-descriptor and
+represent some File or CommunicationChannel of the underlying OperatingSystem.
+ExternalStream is abstract; concrete classes are FileStream, PipeStream etc.
+ExternalStreams can be in two modes: text (the default) and binary.
+In text-mode, the elements read/written are characters; while in binary-mode the basic
+elements are bytes which read/write as SmallIntegers in the range 0..255.
+
+%W% %E%
+
+written 88 by claus
+'!
+
+%{
+#include <stdio.h>
+%}
+
+!ExternalStream class methodsFor:'initialization'!
+
+initialize
+ lobby isNil ifTrue:[
+ lobby := Registry new.
+
+ "want to get informed when returning from snapshot"
+ ObjectMemory addDependent:self
+ ]
+!
+
+reOpenFiles
+ "reopen all files (if possible) after a snapShot load"
+
+ lobby contentsDo:[:aFileStream |
+ aFileStream reOpen
+ ]
+!
+
+update:something
+ "have to reopen files when returning from snapshot"
+
+ something == #returnFromSnapshot ifTrue:[
+ self reOpenFiles
+ ]
+! !
+
+!ExternalStream methodsFor:'instance release'!
+
+disposed
+ "some Stream has been collected - close the file if not already done"
+
+ self closeFile
+!
+
+closeFile
+ "low level close - may be redefined in subclasses"
+
+%{ /* NOCONTEXT */
+
+ fclose(MKFD(_INST(filePointer)));
+%}
+! !
+
+!ExternalStream methodsFor:'private'!
+
+reOpen
+ "sent after snapin to reopen streams.
+ cannot reopen here since I am abstract and have no device knowledge"
+
+ self class name print. ': cannot reOpen stream - stream closed' printNewline.
+ filePointer := nil.
+ lobby unregister:self.
+! !
+
+!ExternalStream methodsFor:'error handling'!
+
+errorNotOpen
+ "report an error, that the stream has not been opened"
+
+ ^ self error:(self class name , ' not open')
+!
+
+errorReadOnly
+ "report an error, that the stream is a readOnly stream"
+
+ ^ self error:(self class name , ' is readonly')
+!
+
+errorWriteOnly
+ "report an error, that the stream is a writeOnly stream"
+
+ ^ self error:(self class name , ' is writeonly')
+!
+
+errorNotBinary
+ "report an error, that the stream is not in binary mode"
+
+ ^ self error:(self class name , ' is not in binary mode')
+!
+
+argumentMustBeInteger
+ "report an error, that the argument must be an integer"
+
+ ^ self error:'argument must be an integer'
+!
+
+argumentMustBeCharacter
+ "report an error, that the argument must be a character"
+
+ ^ self error:'argument must be a character'
+!
+
+argumentMustBeString
+ "report an error, that the argument must be a string"
+
+ ^ self error:'argument must be a string'
+! !
+
+!ExternalStream methodsFor:'accessing'!
+
+readonly
+ "set access mode to readonly"
+
+ mode := #readonly
+!
+
+writeonly
+ "set access mode to writeonly"
+
+ mode := #writeonly
+!
+
+readwrite
+ "set access mode to readwrite"
+
+ mode := #readwrite
+!
+
+filePointer
+ "return the filePointer of the receiver -
+ notice: for portability stdio is used; this means you will get
+ a FILE * - not a fileDescriptor"
+
+ ^ filePointer
+!
+
+fileDescriptor
+ "return the fileDescriptor of the receiver -
+ notice: this one returns the underlying OSs fileDescriptor -
+ this may not be available on all platforms (i.e. non unix systems)."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+
+ if (_INST(filePointer) != nil) {
+ f = MKFD(_INST(filePointer));
+ RETURN ( MKOBJ(fileno(f)) );
+ }
+%}
+.
+ ^ self errorNotOpen
+!
+
+buffered:aBoolean
+ "turn buffering on or off"
+
+ unBuffered := aBoolean not
+!
+
+binary
+ "switch to binary mode"
+
+ binary := true
+!
+
+text
+ "switch to text mode"
+
+ binary := false
+!
+
+contents
+ "return the contents of the file as a Text-object"
+
+ |text|
+
+ text := Text new.
+ [self atEnd] whileFalse:[
+ text add:(self nextLine)
+ ].
+ ^ text
+! !
+
+!ExternalStream methodsFor:'basic'!
+
+open
+ "open the stream
+ - this must be redefined in subclass"
+
+ ^ self subclassResponsibility
+!
+
+create
+ "create the stream
+ - this must be redefined in subclass"
+
+ ^ self subclassResponsibility
+!
+
+position
+ "return the position
+ - this must be redefined in subclass"
+
+ ^ self subclassResponsibility
+!
+
+position:anInteger
+ "set the position
+ - this must be redefined in subclass"
+
+ ^ self subclassResponsibility
+! !
+
+!ExternalStream methodsFor:'low level I/O'!
+
+ioctl:ioctlNumber with:arg
+ "to provide a simple ioctl facility"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int ret, ioNum, ioArg;
+ extern OBJ ErrorNumber;
+ extern errno;
+
+ if (_INST(filePointer) != nil) {
+ if (_isSmallInteger(ioctlNumber) && _isSmallInteger(arg)) {
+ ioNum = _intVal(ioctlNumber);
+ ioArg = _intVal(arg);
+ f = MKFD(_INST(filePointer));
+ ret = ioctl(fileno(f), ioNum, ioArg);
+ if (ret >= 0) {
+ RETURN ( _MKSMALLINT(ret) );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self primitiveFailed
+!
+
+nextByte
+ "read the next byte return it as an Integer
+ nil on error. Use with care - non object oriented i/o"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ unsigned char byte;
+ int cnt;
+ extern OBJ ErrorNumber;
+ extern errno;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ _immediateInterrupt = 1;
+ if (_INST(unBuffered) == true) {
+ cnt = read(fileno(f), &byte, 1);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fread(&byte, 1, 1, f);
+ }
+ _immediateInterrupt = 0;
+ if (cnt == 1) {
+ if (_INST(position) != nil)
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
+ RETURN ( _MKSMALLINT(byte) );
+ }
+ if (cnt < 0) {
+ ErrorNumber = _MKSMALLINT(errno);
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorWriteOnly
+!
+
+nextBytes:count into:anObject
+ "read the next count bytes into an object and return the number of
+ bytes read or nil on error.
+ Use with care - non object oriented i/o"
+
+ ^ self nextBytes:count into:anObject startingAt:1
+!
+
+nextBytes:count into:anObject startingAt:start
+ "read the next count bytes into an object and return the number of
+ bytes read or nil on error.
+ Use with care - non object oriented i/o"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int cnt, offs;
+ int objSize;
+ char *cp;
+ extern OBJ ErrorNumber;
+ extern errno;
+ OBJ pos;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ if (_isSmallInteger(count) && _isSmallInteger(start)) {
+ cnt = _intVal(count);
+ offs = _intVal(start) - 1;
+ f = MKFD(_INST(filePointer));
+ objSize = _Size(anObject) - OHDR_SIZE;
+ if ((offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs))) {
+ cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs;
+ _immediateInterrupt = 1;
+ if (_INST(unBuffered) == true) {
+ cnt = read(fileno(f), cp, cnt);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fread(cp, 1, cnt, f);
+ }
+ _immediateInterrupt = 0;
+ if (cnt >= 0) {
+ pos = _INST(position);
+ if (pos != nil)
+ _INST(position) = _MKSMALLINT(_intVal(pos) + cnt);
+ RETURN ( _MKSMALLINT(cnt) );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ }
+ }
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ self primitiveFailed
+!
+
+nextPutByte:aByteValue
+ "write a byte"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ char c;
+ extern OBJ ErrorNumber;
+ extern errno;
+ OBJ pos;
+ int cnt;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ if (_isSmallInteger(aByteValue)) {
+ c = _intVal(aByteValue);
+ f = MKFD(_INST(filePointer));
+ _immediateInterrupt = 1;
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(&c, 1, 1, f);
+ _immediateInterrupt = 0;
+ if (cnt == 1) {
+ if (_INST(unBuffered) == true) {
+ fflush(f);
+ }
+ pos = _INST(position);
+ if (pos != nil)
+ _INST(position) = _MKSMALLINT(_intVal(pos) + 1);
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ }
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ self primitiveFailed
+!
+
+nextPutBytes:count from:anObject
+ "write count bytes from an object starting at index start.
+ return the number of bytes written or nil on error.
+ Use with care - non object oriented i/o"
+
+ ^ self nextPutBytes:count from:anObject startingAt:1
+!
+
+nextPutBytes:count from:anObject startingAt:start
+ "write count bytes from an object starting at index start.
+ return the number of bytes written or nil on error.
+ Use with care - non object oriented i/o"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int cnt, offs;
+ int objSize;
+ char *cp;
+ extern OBJ ErrorNumber;
+ extern errno;
+ OBJ pos;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ if (_isSmallInteger(count) && _isSmallInteger(start)) {
+ cnt = _intVal(count);
+ offs = _intVal(start) - 1;
+ f = MKFD(_INST(filePointer));
+
+ objSize = _Size(anObject) - OHDR_SIZE;
+ if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
+ cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs;
+ _immediateInterrupt = 1;
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), cp, cnt);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(cp, 1, cnt, f);
+ }
+ _immediateInterrupt = 0;
+ if (cnt >= 0) {
+ pos = _INST(position);
+ if (pos != nil)
+ _INST(position) = _MKSMALLINT(_intVal(pos) + cnt);
+ RETURN ( _MKSMALLINT(cnt) );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ }
+ }
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ self primitiveFailed
+! !
+
+!ExternalStream methodsFor:'character I/O'!
+
+peek
+ "return the character to be read next without advancing read position"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ REGISTER int c;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ c = getc(f);
+ if (c != EOF) {
+ ungetc(c, f);
+ if (_INST(binary) == true) {
+ RETURN ( _MKSMALLINT(c & 0xFF) );
+ }
+ RETURN ( _MKCHARACTER(c & 0xFF) );
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorWriteOnly
+!
+
+next
+ "return the character; advance read position"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int c;
+ OBJ pos;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ _immediateInterrupt = 1;
+ if (_INST(unBuffered) == true) {
+ if (read(fileno(f), &c, 1) != 1)
+ c = EOF;
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ c = getc(f);
+ }
+ _immediateInterrupt = 0;
+ if (c != EOF) {
+ pos = _INST(position);
+ if (pos != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(pos) + 1);
+ }
+ if (_INST(binary) == true) {
+ RETURN ( _MKSMALLINT(c & 0xFF) );
+ }
+ RETURN ( _MKCHARACTER(c & 0xFF) );
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorWriteOnly
+!
+
+nextPut:aCharacter
+ "write the argument, aCharacter - return nil if failed, self if ok"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ char c;
+ extern OBJ ErrorNumber;
+ extern errno;
+ int cnt;
+ OBJ pos;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ if (_isCharacter(aCharacter)) {
+ c = _intVal(_CharacterInstPtr(aCharacter)->c_asciivalue);
+ doWrite:
+ f = MKFD(_INST(filePointer));
+
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), &c, 1);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(&c, 1, 1, f);
+ }
+ if (cnt == 1) {
+ if (_INST(unBuffered) == true) {
+ fflush(f);
+ }
+ pos = _INST(position);
+ if (pos != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(pos) + 1);
+ }
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ } else {
+ if (_INST(binary) == true) {
+ if (_isSmallInteger(aCharacter)) {
+ c = _intVal(aCharacter);
+ goto doWrite;
+ }
+ }
+ }
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ self argumentMustBeCharacter
+!
+
+nextPutAll:aCollection
+ "write all elements of the argument, aCollection"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ unsigned char *cp;
+ int len, cnt;
+ extern OBJ ErrorNumber;
+ extern errno;
+ OBJ pos;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ if (_isString(aCollection) || _isSymbol(aCollection)) {
+ cp = _stringVal(aCollection);
+ len = _stringSize(aCollection);
+ f = MKFD(_INST(filePointer));
+
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), cp, len);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(cp, 1, len, f);
+ }
+ if (cnt == len) {
+ if (_INST(unBuffered) == true) {
+ fflush(f);
+ }
+ pos = _INST(position);
+ if (pos != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(pos) + len);
+ }
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ }
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+
+ aCollection do:[:element |
+ self nextPut:element
+ ]
+!
+
+nextPut:aCollection from:start to:stop
+ "write a range of elements of the argument, aCollection"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ unsigned char *cp;
+ int len, cnt, index1, index2;
+ extern OBJ ErrorNumber;
+ extern errno;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_isString(aCollection)
+ && _isSmallInteger(start)
+ && _isSmallInteger(stop)) {
+ f = MKFD(_INST(filePointer));
+ cp = _stringVal(aCollection);
+ len = _stringSize(aCollection);
+ index1 = _intVal(start);
+ index2 = _intVal(stop);
+ if ((index1 < 1) || (index2 > len) || (index2 < index1)) {
+ RETURN ( self );
+ }
+ if (index2 > len)
+ index2 = len;
+
+ len = index2 - index1 + 1;
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), cp + index1 - 1, len);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(cp + index1 - 1, 1, len, f);
+ }
+ if (cnt == len) {
+ if (_INST(unBuffered) == true) {
+ fflush(f);
+ }
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + len);
+ }
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+
+ start to:stop do:[:index |
+ self nextPut:(aCollection at:index)
+ ]
+!
+
+cr
+ "reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ extern OBJ ErrorNumber;
+ extern errno;
+ extern int _immediateInterrupt;
+ int cnt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ f = MKFD(_INST(filePointer));
+
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), "\n", 1);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite("\n", 1, 1, f);
+ }
+ if (cnt == 1) {
+ if (_INST(unBuffered) == true) {
+ fflush(f);
+ }
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
+ }
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ return ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorReadOnly
+! !
+
+!ExternalStream methodsFor:'more character I/O'!
+
+nextWord
+ "in text-mode:
+ read the next word (i.e. up to non letter-or-digit).
+ return a string containing those characters.
+ in binary-mode:
+ read two bytes (msb-first) and return the value as a 16-bit unsigned Integer
+ (msb-first for compatibility with other smalltalks)"
+
+%{ /* NOCONTEXT */
+ extern int _immediateInterrupt;
+
+ if (_INST(binary) == true) {
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ FILE *f;
+ int hi, low;
+
+ f = MKFD(_INST(filePointer));
+ hi = getc(f);
+ if (hi == EOF) {
+ RETURN ( nil );
+ }
+ low = getc(f);
+ if (low == EOF) {
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
+ }
+ RETURN ( _MKSMALLINT(hi & 0xFF) );
+ }
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 2);
+ }
+ RETURN ( _MKSMALLINT(((hi & 0xFF)<<8) | (low & 0xFF)) );
+ }
+ }
+ }
+%}
+.
+%{
+ FILE *f;
+ int len;
+ char buffer[1024];
+ int ch;
+ int cnt = 0;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ /* text-mode */
+ for (;;) {
+ ch = getc(f);
+ cnt++;
+
+ if (ch >= ' ') break;
+ if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+ && (ch != '\n') && (ch != 0x0b)) break;
+ }
+ ungetc(ch, f);
+ cnt--;
+
+ len = 0;
+ for (;;) {
+ ch = getc(f);
+ if (ch == EOF)
+ break;
+ ch &= 0xFF;
+ if (! (((ch >= 'a') && (ch <= 'z')) ||
+ ((ch >= 'A') && (ch <= 'Z')) ||
+ ((ch >= '0') && (ch <= '9')))) {
+ ungetc(ch, f);
+ break;
+ }
+ cnt++;
+ buffer[len++] = ch;
+ if (len >= sizeof(buffer)-1) {
+ /* emergency */
+ break;
+ }
+ }
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + cnt);
+ }
+ buffer[len] = '\0';
+ if (len != 0) {
+ RETURN ( _MKSTRING(buffer COMMA_CON) );
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorWriteOnly
+!
+
+nextWordPut:aNumber
+ "only in binary-mode:
+ write the argument, aNumber as two bytes (msb-first)
+ (msb-first for compatibility with other smalltalks)"
+
+%{ /* NOCONTEXT */
+
+ int num;
+ char bytes[2];
+ FILE *f;
+ extern OBJ ErrorNumber;
+ extern errno;
+ extern int _immediateInterrupt;
+
+ if (_INST(binary) == true) {
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ if (_isSmallInteger(aNumber)) {
+ num = _intVal(aNumber);
+ bytes[0] = (num >> 8) & 0xFF;
+ bytes[1] = num & 0xFF;
+
+ f = MKFD(_INST(filePointer));
+ if (fwrite(bytes, 1, 2, f) == 2) {
+ if (_INST(unBuffered) == true) {
+ fflush(f);
+ }
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 2);
+ }
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ return ( nil );
+ }
+ }
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ binary ifFalse:[^ self errorNotBinary].
+ self argumentMustBeInteger
+!
+
+nextLong
+ "in binary-mode:
+ read two bytes (msb-first) and return the value as a 16-bit unsigned Integer
+ (msb-first for compatibility with other smalltalks)"
+
+ |lo hi|
+
+ binary ifFalse:[
+ ^ self error:'method only valid in binary mode'
+ ].
+ hi := self nextWord.
+ lo := self nextWord.
+ ^ hi * 16r10000 + lo
+!
+
+nextLine
+ "read the next line (characters up to newline).
+ Return a string containing those characters excluding the newline.
+ If the previous-to-last character is a cr, this is also removed,
+ so its possible to read alien (i.e. ms-dos) text as well."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int len;
+ char buffer[1024*16];
+ extern int _immediateInterrupt;
+ char *rslt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(filePointer) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ _immediateInterrupt = 1;
+ buffer[0] = 0;
+ rslt = fgets(buffer, sizeof(buffer), f);
+ _immediateInterrupt = 0;
+ if (rslt != NULL) {
+ len = strlen(buffer);
+ if (_INST(position) != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + len);
+ }
+ /* remove EOL character */
+ if ((len != 0) && (buffer[len-1] == '\n')) {
+ buffer[--len] = '\0';
+ }
+ if ((len != 0) && (buffer[len-1] == '\r')) {
+ buffer[--len] = '\0';
+ }
+ RETURN ( _MKSTRING(buffer COMMA_CON) );
+ }
+ }
+ }
+%}
+.
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ nil
+!
+
+nextPutLine:aString
+ "write the characters in aString and append a newline"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int len, cnt;
+ OBJ pos;
+ char *s;
+ extern OBJ ErrorNumber;
+ extern errno;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _readonly) {
+ if (_isString(aString)) {
+ f = MKFD(_INST(filePointer));
+ s = (char *) _stringVal(aString);
+ len = _stringSize(aString);
+
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), s, len);
+ } else {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(s, 1, len, f);
+ }
+ if (cnt == len) {
+ if (_INST(unBuffered) == true) {
+ cnt = write(fileno(f), "\n", 1);
+ } else {
+ cnt = fwrite("\n", 1, 1, f);
+ }
+ if (cnt == 1) {
+ pos = _INST(position);
+ if (pos != nil) {
+ _INST(position) = _MKSMALLINT(_intVal(pos)+len+1);
+ }
+ RETURN ( self );
+ }
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
+ }
+ }
+ }
+%}
+.
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self argumentMustBeString
+!
+
+nextPutLinesFrom:aStream upToLineStartingWith:aStringOrNil
+ "used to copy large files
+ - read from aStream up to and including a line starting with aStringOrNil
+ and append it to self. If aStringOrNil is nil or not matched,
+ copy preceeds to the end"
+
+ |srcFilePointer|
+
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ srcFilePointer := aStream filePointer.
+ srcFilePointer isNil ifTrue:[^ aStream errorNotOpen].
+%{
+ FILE *dst, *src;
+ char *matchString;
+ int matchLen = 0;
+ char buffer[1024*16];
+ extern int _immediateInterrupt;
+
+ if (_isSmallInteger(srcFilePointer)) {
+ if ((aStringOrNil == nil)
+ || _isString(aStringOrNil)) {
+ if (aStringOrNil != nil) {
+ matchString = (char *) _stringVal(aStringOrNil);
+ matchLen = _stringSize(aStringOrNil);
+ }
+ dst = MKFD(_INST(filePointer));
+ src = (FILE *)_intVal(srcFilePointer);
+ for (;;) {
+ if (fgets(buffer, sizeof(buffer), src) == NULL) break;
+ if (fputs(buffer, dst) == EOF) break;
+ if (matchLen) {
+ if (strncmp(matchString, buffer, matchLen) == 0)
+ break;
+ }
+ }
+ if (_INST(unBuffered) == true) {
+ fflush(dst);
+ }
+ _INST(position) = nil;
+ RETURN ( self );
+ }
+ }
+%}
+.
+ ^ self primitiveFailed
+!
+
+peekForLineStartingWith:aString
+ "read ahead for next line starting with aString;
+ return the line-string if found, nil otherwise..
+ do not advance position i.e. nextLine will reread this line"
+
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+%{
+ FILE *f;
+ int l;
+ char buffer[1024*10];
+ char *cp;
+ char *matchString;
+ int firstpos, lastpos;
+ extern int _immediateInterrupt;
+
+ if (_isString(aString)) {
+ matchString = (char *) _stringVal(aString);
+ l = _stringSize(aString);
+
+ f = MKFD(_INST(filePointer));
+ firstpos = ftell(f);
+ for (;;) {
+ lastpos = ftell(f);
+ _immediateInterrupt = 1;
+ cp = fgets(buffer, sizeof(buffer), f);
+ _immediateInterrupt = 0;
+ if (cp == NULL) {
+ fseek(f, firstpos, 0);
+ RETURN ( nil );
+ }
+ if (strncmp(cp, matchString, l) == 0) {
+ fseek(f, lastpos, 0);
+ break;
+ }
+ }
+ /* remove EOL character */
+ cp = buffer;
+ while (*cp && (*cp != '\n')) cp++;
+ *cp = '\0';
+ RETURN ( _MKSTRING(buffer COMMA_CON) );
+ }
+%}
+.
+ self argumentMustBeString
+!
+
+peekForLineStartingWithAny:aCollectionOfStrings
+ "read ahead for next line starting with any of aCollectionOfStrings;
+ return the index in aCollection if found, nil otherwise..
+ If no match, do not change position; otherwise advance right before the
+ matched line so that nextLine will return this line."
+
+ |line startPos linePos index|
+
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ startPos := self position.
+ [self atEnd] whileFalse:[
+ linePos := self position.
+ line := self nextLine.
+ index := 1.
+ aCollectionOfStrings do:[:prefix |
+ (line startsWith:prefix) ifTrue:[
+ self position:linePos.
+ ^ index
+ ].
+ index := index + 1
+ ]
+ ].
+ self position:startPos.
+ ^ nil
+! !
+
+!ExternalStream methodsFor:'testing'!
+
+atEnd
+ "return true, if position is at end"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+
+ if (_INST(filePointer) != nil) {
+ f = MKFD(_INST(filePointer));
+ RETURN ( feof(f) ? true : false );
+ }
+%}
+.
+ self errorNotOpen
+! !
+
+!ExternalStream methodsFor:'closing'!
+
+close
+ "close the stream - tell operating system"
+
+ filePointer isNil ifTrue:[^ self].
+ lobby unregister:self.
+ self closeFile.
+ filePointer := nil
+! !
+
+!ExternalStream methodsFor:'reimplemented for speed'!
+
+peekFor:aCharacter
+ "return true and move past if next == something.
+ - reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int c;
+ int peekvalue;
+ extern int _immediateInterrupt;
+
+ if (_isCharacter(aCharacter)) {
+ if (_INST(filePointer) != nil) {
+ peekvalue = _intVal(_CharacterInstPtr(aCharacter)->c_asciivalue);
+ f = MKFD(_INST(filePointer));
+ c = getc(f);
+ if (c == peekvalue) {
+ RETURN ( true );
+ }
+ ungetc(c, f);
+ RETURN ( false );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ super peekFor:aCharacter
+!
+
+skipLine
+ "read the next line (characters up to newline) skip only;
+ return nil if EOF reached"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ char buffer[1024*10];
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ if (fgets(buffer, sizeof(buffer), f) != NULL) {
+ RETURN ( self );
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorWriteOnly
+!
+
+skipToAll:aString
+ "skip for the sequence given by the argument, aCollection;
+ return nil if not found, self otherwise. On a successful match, next read
+ will return characters of aString."
+
+ |oldPos buffer l first idx|
+
+ (aString isKindOf:String) ifTrue:[
+ oldPos := self position.
+ l := aString size.
+ first := aString at:1.
+ buffer := String new:l.
+ [true] whileTrue:[
+ (self nextBytes:l into:buffer) == l ifFalse:[
+ self position:oldPos.
+ ^ nil
+ ].
+ buffer = aString ifTrue:[
+ self position:(self position - l).
+ ^ self
+ ].
+ idx := buffer indexOf:first startingAt:2.
+ idx == 0 ifFalse:[
+ self position:(self position - l + idx - 1)
+ ]
+ ]
+ ].
+ ^ super skipFor:aString
+!
+
+skipSeparators
+ "skip all whitespace; next will return next non-white-space character
+ or nil if endOfFile reached.
+ - reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ REGISTER int c;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ while (1) {
+ if (feof(f)) {
+ RETURN ( nil );
+ }
+ c = getc(f);
+ if (c < 0) {
+ RETURN ( nil );
+ }
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\b':
+ case '\014':
+ break;
+ default:
+ ungetc(c, f);
+ RETURN ( _MKCHARACTER(c & 0xFF) );
+ }
+ }
+ }
+ }
+%}
+.
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ self errorNotOpen
+!
+
+skipSeparatorsExceptCR
+ "skip all whitespace but no newlines;
+ next will return next non-white-space character
+ or nil if endOfFile reached.
+ - reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int c;
+ extern int _immediateInterrupt;
+
+ if (_INST(filePointer) != nil) {
+ if (_INST(mode) != _writeonly) {
+ f = MKFD(_INST(filePointer));
+ while (1) {
+ if (feof(f)) {
+ RETURN ( nil );
+ }
+ c = getc(f);
+ if (c < 0) {
+ RETURN ( nil );
+ }
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\b':
+ break;
+ default:
+ ungetc(c, f);
+ RETURN ( _MKCHARACTER(c & 0xFF) );
+ }
+ }
+ }
+ }
+%}
+.
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ self errorNotOpen
+!
+
+nextChunk
+ "return the next chunk, i.e. all characters up to the next
+ non-doubled exclamation mark; undouble doubled exclamation marks.
+ - reimplemented for speed"
+ |retVal|
+
+ filePointer isNil ifTrue:[
+ ^ self errorNotOpen
+ ].
+%{
+ FILE *f;
+ int done = 0;
+ REGISTER int c;
+ unsigned char peekC;
+ char *buffer, *newBuffer;
+ REGISTER int index;
+ int currSize;
+ int inComment, inString, inPrimitive = 0;
+ extern int _immediateInterrupt;
+
+ f = MKFD(_INST(filePointer));
+ /*
+ * skip spaces
+ */
+ while (! done) {
+ if (feof(f)) {
+ RETURN ( nil );
+ }
+ c = getc(f);
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\b':
+ case '\014':
+ break;
+
+ case EOF:
+ RETURN ( nil );
+
+ default:
+ ungetc(c, f);
+ done = 1;
+ break;
+ }
+ }
+
+ /*
+ * read chunk into a buffer
+ */
+ buffer = (char *)malloc(3000);
+ currSize = 3000;
+ index = 0;
+ while (! feof(f)) {
+ /* do we have to resize the buffer ? */
+ if ((index+2) >= currSize) {
+ newBuffer = (char *)malloc(currSize * 2);
+ bcopy(buffer, newBuffer, index);
+ free(buffer);
+ buffer = newBuffer;
+ currSize = currSize * 2;
+ }
+ c = getc(f);
+ if (c == '%') {
+ peekC = getc(f);
+ ungetc(peekC, f);
+ if (peekC == '{') {
+ inPrimitive++;
+ } else if (peekC == '}') {
+ inPrimitive--;
+ }
+ } else {
+ if (! inPrimitive) {
+ if (c == '!') {
+ c = getc(f);
+ if (c != '!') {
+ ungetc(c, f);
+ break;
+ }
+ }
+ }
+ }
+ if (c == EOF) break;
+ buffer[index++] = c;
+ }
+ buffer[index] = '\0';
+ /*
+ * make it a string
+ */
+ retVal = _MKSTRING(buffer COMMA_CON);
+ free(buffer);
+%}
+.
+ ^ retVal
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/False.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,120 @@
+"
+ COPYRIGHT (c) 1988-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Boolean subclass:#False
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Objects'
+!
+
+False comment:'
+
+COPYRIGHT (c) 1988-92 by Claus Gittinger
+ All Rights Reserved
+
+Class False has only one instance, false, representing logical falsehood.
+
+%W% %E%
+'!
+
+!False methodsFor:'logical operations'!
+
+& aBoolean
+ "return true, if both the receiver and the argument are true
+ (since the receiver is false, return false)"
+
+ ^ false
+!
+
+| aBoolean
+ "return true, if either the receiver or the argument is true
+ (since the receiver is false, return the argument)"
+
+ ^ aBoolean
+!
+
+not
+ "return true, if the receiver is false, false otherwise
+ (since the receiver is false, return true)"
+
+ ^ true
+!
+
+eqv:aBoolean
+ "return true, if the receiver and the argument are the same truth value
+ (since the receiver is false, return true if the argument is also false)"
+
+ ^ aBoolean not
+!
+
+xor:aBoolean
+ "return true, if the receiver and the argument are different truth values
+ (since the receiver is false, return true, if the armument is not false)"
+
+ ^ aBoolean
+! !
+
+!False methodsFor:'conditional evaluation'!
+
+and:aBlock
+ "evaluate aBlock if the receiver is true.
+ (since the receiver is false return false).
+ - open coded by compiler"
+
+ ^ self
+!
+
+or:aBlock
+ "evaluate aBlock if the receiver is false.
+ (since the receiver is false return the value of evaluating aBlock).
+ - open coded by compiler"
+
+ ^ aBlock value
+!
+
+ifFalse:aBlock
+ "return the value of evaluating aBlock if the receiver is false.
+ (since the receiver is known to be false always evaluate)
+ - open coded by compiler"
+
+ ^ aBlock value
+!
+
+ifTrue:aBlock
+ "return the false alternative, nil (since the receiver is false)
+ - open coded by compiler"
+
+ ^ nil
+!
+
+ifTrue:trueBlock ifFalse:falseBlock
+ "return the value of evaluating falseBlock (since the receiver is false)
+ - open coded by compiler"
+
+ ^ falseBlock value
+!
+
+ifFalse:falseBlock ifTrue:trueBlock
+ "return the value of evaluating falseBlock (since the receiver is false)
+ - open coded by compiler"
+
+ ^ falseBlock value
+! !
+
+!False methodsFor: 'printing'!
+
+printString
+ "return a Character sequence representing the receiver"
+
+ ^ 'false'
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FileDir.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,553 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#FileDirectory
+ instanceVariableNames:'pathName lazy'
+ classVariableNames:'pathOfCurrentDirectory'
+ poolDictionaries:''
+ category:'Collections-Files'
+!
+
+FileDirectory comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+FileDirectories represent directories in the underlying host system.
+They provide various methods to create/delete and query for files and/or
+directories.
+
+%W% %E%
+
+written winter 89 by claus
+'!
+
+!FileDirectory class methodsFor:'instance creation'!
+
+rootDirectory
+ "create and return a new FileDirectory for the root directory"
+
+ ^ (self basicNew) pathName:'/'
+!
+
+currentDirectory
+ "create and return a new FileDirectory for the current directory"
+
+ ^ (self basicNew) pathName:'.'
+!
+
+directoryNamed:name
+ "create and return a new FileDirectory for the directory
+ with given pathname"
+
+ ^ (self basicNew) pathName:name
+!
+
+directoryNamed:name in:aFileDirectory
+ "create and return a new FileDirectory for the directory with given name
+ in another FileDirectory"
+
+ |baseName|
+
+ ((name at:1) == $/) ifTrue:[
+ ^ self directoryNamed:name
+ ].
+ (aFileDirectory isKindOf:FileDirectory) ifTrue:[
+ baseName := aFileDirectory pathName
+ ] ifFalse:[
+ baseName := aFileDirectory
+ ].
+"
+ (name = '..') ifTrue:[
+ ^ (self basicNew) pathName:(OperatingSystem directoryNameOf:baseName)
+ ].
+"
+ (name = '.') ifTrue:[^ aFileDirectory].
+
+ (baseName = '/') ifFalse:[
+ (baseName endsWith:'/') ifFalse:[
+ baseName := baseName , '/'
+ ]
+ ].
+ ^ (self basicNew) pathName:(baseName , name)
+! !
+
+!FileDirectory methodsFor:'accessing'!
+
+baseName
+ "return my baseName
+ - thats the directory name without leading parent-dirs"
+
+ lazy ifTrue:[self getFullPathName].
+ ^ OperatingSystem baseNameOf:pathName
+!
+
+directoryName
+ "return my directoryName
+ - thats the directory name where I'm in"
+
+ lazy ifTrue:[self getFullPathName].
+ ^ OperatingSystem directoryNameOf:pathName
+!
+
+pathName
+ "return my full pathname"
+
+ lazy ifTrue:[self getFullPathName].
+ ^ pathName
+!
+
+pathName:dirName
+ "set my pathname; return nil if not a valid path; self otherwise"
+
+ pathName := dirName.
+ (dirName startsWith:'/') ifFalse:[
+ lazy := true
+ ] ifTrue:[
+ (dirName includes:$.) ifTrue:[
+ lazy := true
+ ]
+ ].
+ ^ self
+"
+ (OperatingSystem isDirectory:pathName) ifFalse:[^ nil]
+"
+!
+
+contents
+ "return a collection with all files and subdirectories in the receiver"
+
+ |coll|
+
+ coll := OrderedCollection new.
+ self do:[:name |
+ coll add:name
+ ].
+ (coll size ~~ 0) ifTrue:[
+ coll sort
+ ].
+ ^ coll
+!
+
+directories
+ "return a collection with all subdirectories in the receiver directory"
+
+ |coll|
+
+ coll := OrderedCollection new.
+ self directoriesDo:[:name |
+ coll add:name
+ ].
+ (coll size ~~ 0) ifTrue:[
+ coll sort
+ ].
+ ^ coll
+!
+
+files
+ "return a collection with all plain files in the receiver directory"
+
+ |coll|
+
+ coll := OrderedCollection new.
+ self filesDo:[:name |
+ coll add:name
+ ].
+ ^ coll sort
+! !
+
+!FileDirectory methodsFor:'private'!
+
+getFullPathName
+ "make my pathname be a full pathname - i.e. starting at root"
+
+ |aStream command shortPathName fullPathName|
+
+ (pathName = '/') ifTrue:[
+ lazy := false.
+ ^ self
+ ].
+
+ "since currentDirectory is used very often, cache its path here"
+
+ (pathName = '.') ifTrue:[
+ pathOfCurrentDirectory notNil ifTrue:[
+ pathName := pathOfCurrentDirectory.
+ lazy := false.
+ ^ self
+ ]
+ ].
+
+ shortPathName := pathName.
+
+ "sys5.4 and sunos have a convenient function for this ..."
+%{
+#if defined(SYSV4) || defined(sunos)
+# include <stdlib.h>
+# include <sys/param.h>
+
+ char nameBuffer[MAXPATHLEN + 1];
+
+ if (realpath(_stringVal(_INST(pathName)), nameBuffer)) {
+ fullPathName = _MKSTRING(nameBuffer COMMA_CON);
+ }
+#endif
+%}
+.
+ fullPathName notNil ifTrue:[
+ pathName := fullPathName.
+ lazy := false
+ ] ifFalse:[
+ "since there might be symbolic links and other stuff involved,
+ better trust pwd than removing '..' by ourself
+ - although this is very slow"
+
+ command := 'cd ' , pathName , '; pwd'.
+ aStream := PipeStream readingFrom:command.
+ aStream isNil ifFalse:[
+ (aStream atEnd) ifFalse:[
+ fullPathName := aStream nextLine
+ ].
+ aStream close.
+ fullPathName notNil ifTrue:[
+ pathName := fullPathName.
+ lazy := false
+ ]
+ ] ifTrue:[
+ self error:('PipeStream for <' , command , '> failed').
+ "by clearing lazy, we avoid triggering the error again"
+ lazy := false
+ ]
+ ].
+
+ "if it was the current dir, keep name for next query"
+ (shortPathName = '.') ifTrue:[
+ pathOfCurrentDirectory := fullPathName
+ ]
+! !
+
+!FileDirectory methodsFor:'basic'!
+
+createDirectory:newName
+ "create a new filedirectory as a subdirectory of myself;
+ return true if successful"
+
+ |realName|
+
+ (newName = '.') ifFalse:[
+ (newName = '..') ifFalse:[
+ ((newName at:1) == $/) ifTrue:[
+ realName := newName copyFrom:2
+ ] ifFalse:[
+ realName := newName
+ ].
+ (realName startsWith:'/') ifTrue:[
+ ^ OperatingSystem createDirectory:realName
+ ] ifFalse:[
+ ^ OperatingSystem createDirectory:(pathName , '/' , realName)
+ ]
+ ]
+ ].
+ ^ false
+!
+
+removeFile:fileName
+ "remove the file 'fileName' from myself; return true if successful"
+
+ (fileName startsWith:'/') ifTrue:[
+ ^ OperatingSystem removeFile:fileName
+ ].
+ ^ OperatingSystem removeFile:(pathName , '/' , fileName)
+!
+
+removeDirectory:dirName
+ "remove the directory 'dirName' from myself; return true if successful"
+
+ (dirName startsWith:'/') ifTrue:[
+ ^ OperatingSystem removeDirectory:dirName
+ ].
+ ^ OperatingSystem removeDirectory:(pathName , '/' , dirName)
+!
+
+remove:aFileOrDirectoryName
+ "remove the file or directory from myself; return true if successful"
+
+ |path|
+
+ (aFileOrDirectoryName startsWith:'/') ifTrue:[
+ path := aFileOrDirectoryName
+ ] ifFalse:[
+ path := (pathName , '/' , aFileOrDirectoryName)
+ ].
+ (OperatingSystem isDirectory:path) ifTrue:[
+ ^ OperatingSystem removeDirectory:path
+ ].
+ ^ OperatingSystem removeFile:path
+!
+
+link:oldFileName to:newFileName
+ "link oldFileName to newFileName in myself, return true if successful"
+
+ |path1 path2|
+
+ (oldFileName startsWith:'/') ifTrue:[
+ path1 := oldFileName
+ ] ifFalse:[
+ path1 := (pathName , '/' , oldFileName)
+ ].
+ (newFileName startsWith:'/') ifTrue:[
+ path2 := newFileName
+ ] ifFalse:[
+ path2 := (pathName , '/' , newFileName)
+ ].
+ ^ OperatingSystem link:path1 to:path2
+!
+
+renameFile:oldFileName newName:newFileName
+ "rename the file; return true if successful"
+
+ |path1 path2|
+
+ (oldFileName startsWith:'/') ifTrue:[
+ path1 := oldFileName
+ ] ifFalse:[
+ path1 := (pathName , '/' , oldFileName)
+ ].
+ (newFileName startsWith:'/') ifTrue:[
+ path2 := newFileName
+ ] ifFalse:[
+ path2 := (pathName , '/' , newFileName)
+ ].
+ ^ OperatingSystem rename:path1 to:path2
+! !
+
+!FileDirectory methodsFor:'queries'!
+
+id
+ "return the directories file-id (inode number)"
+
+ ^ OperatingSystem idOf:pathName
+!
+
+exists
+ "return true if this directory exists"
+
+ ^ OperatingSystem isDirectory:pathName
+ "(FileDirectory directoryNamed:'fooBar') exists"
+!
+
+infoOf:name
+ "return an array filled with file info for the file 'aFileName';
+ return nil if such a file does not exist"
+
+ (name startsWith:'/') ifTrue:[
+ ^ OperatingSystem infoOf:name
+ ].
+ ^ OperatingSystem infoOf:(pathName , '/' , name)
+!
+
+timeOfLastChange:name
+ "return the timeStamp of a file in myself"
+
+ (name startsWith:'/') ifTrue:[
+ ^ OperatingSystem timeOfLastChange:name
+ ].
+ ^ OperatingSystem timeOfLastChange:(pathName , '/' , name)
+!
+
+timeOfLastChange
+ "return the timeStamp of myself"
+
+ ^ OperatingSystem timeOfLastChange:pathName
+!
+
+accessModeOf:aFileName
+ "return the access-mode bits (rwxrwxrwx) of a file in myself"
+
+ (aFileName startsWith:'/') ifTrue:[
+ ^ OperatingSystem accessModeOf:aFileName
+ ].
+ ^ OperatingSystem accessModeOf:(pathName , '/' , aFileName)
+!
+
+changeAccessModeOf:aFileName to:modeBits
+ "set the access-mode bits (rwxrwxrwx) of a file in myself"
+
+ (aFileName startsWith:'/') ifTrue:[
+ ^ OperatingSystem changeAccessModeOf:aFileName
+ to:modeBits
+ ].
+ ^ OperatingSystem changeAccessModeOf:(pathName , '/' , aFileName)
+ to:modeBits
+!
+
+typeOf:aFileName
+ "return the symbolic type of a file in myself"
+
+ (aFileName startsWith:'/') ifTrue:[
+ ^ OperatingSystem typeOf:aFileName
+ ].
+ ^ OperatingSystem typeOf:(pathName , '/' , aFileName)
+!
+
+isDirectory:name
+ "return true, if the given name is that of a directory in myself"
+
+ (name startsWith:'/') ifTrue:[
+ ^ OperatingSystem isDirectory:name
+ ].
+ ^ OperatingSystem isDirectory:(pathName , '/' , name)
+!
+
+isReadable:name
+ "return true, if the given file is readable"
+
+ (name startsWith:'/') ifTrue:[
+ ^ OperatingSystem isReadable:name
+ ].
+ ^ OperatingSystem isReadable:(pathName , '/' , name)
+!
+
+isWritable:name
+ "return true, if the given file is readable"
+
+ (name startsWith:'/') ifTrue:[
+ ^ OperatingSystem isWritable:name
+ ].
+ ^ OperatingSystem isWritable:(pathName , '/' , name)
+!
+
+isExecutable:name
+ "return true, if the given file is executable"
+
+ (name startsWith:'/') ifTrue:[
+ ^ OperatingSystem isExecutable:name
+ ].
+ ^ OperatingSystem isExecutable:(pathName , '/' , name)
+! !
+
+!FileDirectory methodsFor:'printing & storing'!
+
+printString
+ lazy ifTrue:[self getFullPathName].
+ ^ '(a FileDirectory pathName:' , pathName, ')'
+!
+
+storeOn:aStream
+ lazy ifTrue:[self getFullPathName].
+ aStream nextPutAll:'(FileDirectory directoryNamed:'.
+ aStream nextPutAll:pathName.
+ aStream nextPut:$)
+! !
+
+!FileDirectory methodsFor:'more instance creation'!
+
+directoryNamed:aName
+ ^ self class directoryNamed:aName in:self pathName
+! !
+
+!FileDirectory methodsFor:'enumerating'!
+
+where:testBlock do:aBlock
+ "evaluate the argument, aBlock for every object in the directory
+ for which testBlock evaluates to true."
+
+ |aStream name|
+
+ aStream := DirectoryStream directoryNamed:pathName.
+ aStream isNil ifTrue:[^ nil].
+ [aStream atEnd] whileFalse:[
+ name := aStream nextLine.
+ name notNil ifTrue:[
+ (testBlock value:name) ifTrue:[
+ aBlock value:name
+ ]
+ ]
+ ].
+ aStream close
+!
+
+do:aBlock
+ "evaluate the argument, aBlock for every name in the directory"
+
+ self where:[:name | true] do:aBlock
+!
+
+namesDo:aBlock
+ "evaluate the argument, aBlock for every name in the directory.
+ for ST-80 compatibility"
+
+ self do:aBlock
+!
+
+filesDo:aBlock
+ "evaluate the argument, aBlock for every plain file name in the directory"
+
+ self where:[:name | (self isDirectory:name) not] do:aBlock
+!
+
+directoriesDo:aBlock
+ "evaluate the argument, aBlock for every subdirectory name in the directory"
+
+ self where:[:name | (self isDirectory:name) ifTrue:[
+ ((name ~= '.') and:[name ~= '..'])
+ ] ifFalse:[
+ false
+ ]
+ ] do:aBlock
+!
+
+allFilesDo:aBlock
+ "evaluate the argument, aBlock for every file name in the directory and in all
+ subdirectories"
+
+ |aStream command line|
+
+ lazy ifTrue:[self getFullPathName].
+ command := 'cd ' , pathName , '; find . -print'.
+ aStream := PipeStream readingFrom:command.
+ aStream isNil ifTrue:[^ nil].
+ [aStream atEnd] whileFalse:[
+ line := aStream nextLine.
+ line notNil ifTrue:[
+ (line = '.') ifFalse:[
+ "cut off initial ./"
+ line := line copyFrom:3 to:(line size)
+ ].
+ aBlock value:line
+ ]
+ ].
+ aStream close
+!
+
+allDirectoriesDo:aBlock
+ "evaluate the argument, aBlock for every directory name
+ in the directory and in all subdirectories"
+
+ |aStream command line|
+
+ lazy ifTrue:[self getFullPathName].
+ command := 'cd ' , pathName , '; find . -type d -print'.
+ aStream := PipeStream readingFrom:command.
+ aStream isNil ifTrue:[^ nil].
+ [aStream atEnd] whileFalse:[
+ line := aStream nextLine.
+ line notNil ifTrue:[
+ (line = '.') ifFalse:[
+ "cut off initial ./"
+ line := line copyFrom:3 to:(line size)
+ ].
+ aBlock value:line
+ ]
+ ].
+ aStream close
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FileDirectory.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,553 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#FileDirectory
+ instanceVariableNames:'pathName lazy'
+ classVariableNames:'pathOfCurrentDirectory'
+ poolDictionaries:''
+ category:'Collections-Files'
+!
+
+FileDirectory comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+FileDirectories represent directories in the underlying host system.
+They provide various methods to create/delete and query for files and/or
+directories.
+
+%W% %E%
+
+written winter 89 by claus
+'!
+
+!FileDirectory class methodsFor:'instance creation'!
+
+rootDirectory
+ "create and return a new FileDirectory for the root directory"
+
+ ^ (self basicNew) pathName:'/'
+!
+
+currentDirectory
+ "create and return a new FileDirectory for the current directory"
+
+ ^ (self basicNew) pathName:'.'
+!
+
+directoryNamed:name
+ "create and return a new FileDirectory for the directory
+ with given pathname"
+
+ ^ (self basicNew) pathName:name
+!
+
+directoryNamed:name in:aFileDirectory
+ "create and return a new FileDirectory for the directory with given name
+ in another FileDirectory"
+
+ |baseName|
+
+ ((name at:1) == $/) ifTrue:[
+ ^ self directoryNamed:name
+ ].
+ (aFileDirectory isKindOf:FileDirectory) ifTrue:[
+ baseName := aFileDirectory pathName
+ ] ifFalse:[
+ baseName := aFileDirectory
+ ].
+"
+ (name = '..') ifTrue:[
+ ^ (self basicNew) pathName:(OperatingSystem directoryNameOf:baseName)
+ ].
+"
+ (name = '.') ifTrue:[^ aFileDirectory].
+
+ (baseName = '/') ifFalse:[
+ (baseName endsWith:'/') ifFalse:[
+ baseName := baseName , '/'
+ ]
+ ].
+ ^ (self basicNew) pathName:(baseName , name)
+! !
+
+!FileDirectory methodsFor:'accessing'!
+
+baseName
+ "return my baseName
+ - thats the directory name without leading parent-dirs"
+
+ lazy ifTrue:[self getFullPathName].
+ ^ OperatingSystem baseNameOf:pathName
+!
+
+directoryName
+ "return my directoryName
+ - thats the directory name where I'm in"
+
+ lazy ifTrue:[self getFullPathName].
+ ^ OperatingSystem directoryNameOf:pathName
+!
+
+pathName
+ "return my full pathname"
+
+ lazy ifTrue:[self getFullPathName].
+ ^ pathName
+!
+
+pathName:dirName
+ "set my pathname; return nil if not a valid path; self otherwise"
+
+ pathName := dirName.
+ (dirName startsWith:'/') ifFalse:[
+ lazy := true
+ ] ifTrue:[
+ (dirName includes:$.) ifTrue:[
+ lazy := true
+ ]
+ ].
+ ^ self
+"
+ (OperatingSystem isDirectory:pathName) ifFalse:[^ nil]
+"
+!
+
+contents
+ "return a collection with all files and subdirectories in the receiver"
+
+ |coll|
+
+ coll := OrderedCollection new.
+ self do:[:name |
+ coll add:name
+ ].
+ (coll size ~~ 0) ifTrue:[
+ coll sort
+ ].
+ ^ coll
+!
+
+directories
+ "return a collection with all subdirectories in the receiver directory"
+
+ |coll|
+
+ coll := OrderedCollection new.
+ self directoriesDo:[:name |
+ coll add:name
+ ].
+ (coll size ~~ 0) ifTrue:[
+ coll sort
+ ].
+ ^ coll
+!
+
+files
+ "return a collection with all plain files in the receiver directory"
+
+ |coll|
+
+ coll := OrderedCollection new.
+ self filesDo:[:name |
+ coll add:name
+ ].
+ ^ coll sort
+! !
+
+!FileDirectory methodsFor:'private'!
+
+getFullPathName
+ "make my pathname be a full pathname - i.e. starting at root"
+
+ |aStream command shortPathName fullPathName|
+
+ (pathName = '/') ifTrue:[
+ lazy := false.
+ ^ self
+ ].
+
+ "since currentDirectory is used very often, cache its path here"
+
+ (pathName = '.') ifTrue:[
+ pathOfCurrentDirectory notNil ifTrue:[
+ pathName := pathOfCurrentDirectory.
+ lazy := false.
+ ^ self
+ ]
+ ].
+
+ shortPathName := pathName.
+
+ "sys5.4 and sunos have a convenient function for this ..."
+%{
+#if defined(SYSV4) || defined(sunos)
+# include <stdlib.h>
+# include <sys/param.h>
+
+ char nameBuffer[MAXPATHLEN + 1];
+
+ if (realpath(_stringVal(_INST(pathName)), nameBuffer)) {
+ fullPathName = _MKSTRING(nameBuffer COMMA_CON);
+ }
+#endif
+%}
+.
+ fullPathName notNil ifTrue:[
+ pathName := fullPathName.
+ lazy := false
+ ] ifFalse:[
+ "since there might be symbolic links and other stuff involved,
+ better trust pwd than removing '..' by ourself
+ - although this is very slow"
+
+ command := 'cd ' , pathName , '; pwd'.
+ aStream := PipeStream readingFrom:command.
+ aStream isNil ifFalse:[
+ (aStream atEnd) ifFalse:[
+ fullPathName := aStream nextLine
+ ].
+ aStream close.
+ fullPathName notNil ifTrue:[
+ pathName := fullPathName.
+ lazy := false
+ ]
+ ] ifTrue:[
+ self error:('PipeStream for <' , command , '> failed').
+ "by clearing lazy, we avoid triggering the error again"
+ lazy := false
+ ]
+ ].
+
+ "if it was the current dir, keep name for next query"
+ (shortPathName = '.') ifTrue:[
+ pathOfCurrentDirectory := fullPathName
+ ]
+! !
+
+!FileDirectory methodsFor:'basic'!
+
+createDirectory:newName
+ "create a new filedirectory as a subdirectory of myself;
+ return true if successful"
+
+ |realName|
+
+ (newName = '.') ifFalse:[
+ (newName = '..') ifFalse:[
+ ((newName at:1) == $/) ifTrue:[
+ realName := newName copyFrom:2
+ ] ifFalse:[
+ realName := newName
+ ].
+ (realName startsWith:'/') ifTrue:[
+ ^ OperatingSystem createDirectory:realName
+ ] ifFalse:[
+ ^ OperatingSystem createDirectory:(pathName , '/' , realName)
+ ]
+ ]
+ ].
+ ^ false
+!
+
+removeFile:fileName
+ "remove the file 'fileName' from myself; return true if successful"
+
+ (fileName startsWith:'/') ifTrue:[
+ ^ OperatingSystem removeFile:fileName
+ ].
+ ^ OperatingSystem removeFile:(pathName , '/' , fileName)
+!
+
+removeDirectory:dirName
+ "remove the directory 'dirName' from myself; return true if successful"
+
+ (dirName startsWith:'/') ifTrue:[
+ ^ OperatingSystem removeDirectory:dirName
+ ].
+ ^ OperatingSystem removeDirectory:(pathName , '/' , dirName)
+!
+
+remove:aFileOrDirectoryName
+ "remove the file or directory from myself; return true if successful"
+
+ |path|
+
+ (aFileOrDirectoryName startsWith:'/') ifTrue:[
+ path := aFileOrDirectoryName
+ ] ifFalse:[
+ path := (pathName , '/' , aFileOrDirectoryName)
+ ].
+ (OperatingSystem isDirectory:path) ifTrue:[
+ ^ OperatingSystem removeDirectory:path
+ ].
+ ^ OperatingSystem removeFile:path
+!
+
+link:oldFileName to:newFileName
+ "link oldFileName to newFileName in myself, return true if successful"
+
+ |path1 path2|
+
+ (oldFileName startsWith:'/') ifTrue:[
+ path1 := oldFileName
+ ] ifFalse:[
+ path1 := (pathName , '/' , oldFileName)
+ ].
+ (newFileName startsWith:'/') ifTrue:[
+ path2 := newFileName
+ ] ifFalse:[
+ path2 := (pathName , '/' , newFileName)
+ ].
+ ^ OperatingSystem link:path1 to:path2
+!
+
+renameFile:oldFileName newName:newFileName
+ "rename the file; return true if successful"
+
+ |path1 path2|
+
+ (oldFileName startsWith:'/') ifTrue:[
+ path1 := oldFileName
+ ] ifFalse:[
+ path1 := (pathName , '/' , oldFileName)
+ ].
+ (newFileName startsWith:'/') ifTrue:[
+ path2 := newFileName
+ ] ifFalse:[
+ path2 := (pathName , '/' , newFileName)
+ ].
+ ^ OperatingSystem rename:path1 to:path2
+! !
+
+!FileDirectory methodsFor:'queries'!
+
+id
+ "return the directories file-id (inode number)"
+
+ ^ OperatingSystem idOf:pathName
+!
+
+exists
+ "return true if this directory exists"
+
+ ^ OperatingSystem isDirectory:pathName
+ "(FileDirectory directoryNamed:'fooBar') exists"
+!
+
+infoOf:name
+ "return an array filled with file info for the file 'aFileName';
+ return nil if such a file does not exist"
+
+ (name startsWith:'/') ifTrue:[
+ ^ OperatingSystem infoOf:name
+ ].
+ ^ OperatingSystem infoOf:(pathName , '/' , name)
+!
+
+timeOfLastChange:name
+ "return the timeStamp of a file in myself"
+
+ (name startsWith:'/') ifTrue:[
+ ^ OperatingSystem timeOfLastChange:name
+ ].
+ ^ OperatingSystem timeOfLastChange:(pathName , '/' , name)
+!
+
+timeOfLastChange
+ "return the timeStamp of myself"
+
+ ^ OperatingSystem timeOfLastChange:pathName
+!
+
+accessModeOf:aFileName
+ "return the access-mode bits (rwxrwxrwx) of a file in myself"
+
+ (aFileName startsWith:'/') ifTrue:[
+ ^ OperatingSystem accessModeOf:aFileName
+ ].
+ ^ OperatingSystem accessModeOf:(pathName , '/' , aFileName)
+!
+
+changeAccessModeOf:aFileName to:modeBits
+ "set the access-mode bits (rwxrwxrwx) of a file in myself"
+
+ (aFileName startsWith:'/') ifTrue:[
+ ^ OperatingSystem changeAccessModeOf:aFileName
+ to:modeBits
+ ].
+ ^ OperatingSystem changeAccessModeOf:(pathName , '/' , aFileName)
+ to:modeBits
+!
+
+typeOf:aFileName
+ "return the symbolic type of a file in myself"
+
+ (aFileName startsWith:'/') ifTrue:[
+ ^ OperatingSystem typeOf:aFileName
+ ].
+ ^ OperatingSystem typeOf:(pathName , '/' , aFileName)
+!
+
+isDirectory:name
+ "return true, if the given name is that of a directory in myself"
+
+ (name startsWith:'/') ifTrue:[
+ ^ OperatingSystem isDirectory:name
+ ].
+ ^ OperatingSystem isDirectory:(pathName , '/' , name)
+!
+
+isReadable:name
+ "return true, if the given file is readable"
+
+ (name startsWith:'/') ifTrue:[
+ ^ OperatingSystem isReadable:name
+ ].
+ ^ OperatingSystem isReadable:(pathName , '/' , name)
+!
+
+isWritable:name
+ "return true, if the given file is readable"
+
+ (name startsWith:'/') ifTrue:[
+ ^ OperatingSystem isWritable:name
+ ].
+ ^ OperatingSystem isWritable:(pathName , '/' , name)
+!
+
+isExecutable:name
+ "return true, if the given file is executable"
+
+ (name startsWith:'/') ifTrue:[
+ ^ OperatingSystem isExecutable:name
+ ].
+ ^ OperatingSystem isExecutable:(pathName , '/' , name)
+! !
+
+!FileDirectory methodsFor:'printing & storing'!
+
+printString
+ lazy ifTrue:[self getFullPathName].
+ ^ '(a FileDirectory pathName:' , pathName, ')'
+!
+
+storeOn:aStream
+ lazy ifTrue:[self getFullPathName].
+ aStream nextPutAll:'(FileDirectory directoryNamed:'.
+ aStream nextPutAll:pathName.
+ aStream nextPut:$)
+! !
+
+!FileDirectory methodsFor:'more instance creation'!
+
+directoryNamed:aName
+ ^ self class directoryNamed:aName in:self pathName
+! !
+
+!FileDirectory methodsFor:'enumerating'!
+
+where:testBlock do:aBlock
+ "evaluate the argument, aBlock for every object in the directory
+ for which testBlock evaluates to true."
+
+ |aStream name|
+
+ aStream := DirectoryStream directoryNamed:pathName.
+ aStream isNil ifTrue:[^ nil].
+ [aStream atEnd] whileFalse:[
+ name := aStream nextLine.
+ name notNil ifTrue:[
+ (testBlock value:name) ifTrue:[
+ aBlock value:name
+ ]
+ ]
+ ].
+ aStream close
+!
+
+do:aBlock
+ "evaluate the argument, aBlock for every name in the directory"
+
+ self where:[:name | true] do:aBlock
+!
+
+namesDo:aBlock
+ "evaluate the argument, aBlock for every name in the directory.
+ for ST-80 compatibility"
+
+ self do:aBlock
+!
+
+filesDo:aBlock
+ "evaluate the argument, aBlock for every plain file name in the directory"
+
+ self where:[:name | (self isDirectory:name) not] do:aBlock
+!
+
+directoriesDo:aBlock
+ "evaluate the argument, aBlock for every subdirectory name in the directory"
+
+ self where:[:name | (self isDirectory:name) ifTrue:[
+ ((name ~= '.') and:[name ~= '..'])
+ ] ifFalse:[
+ false
+ ]
+ ] do:aBlock
+!
+
+allFilesDo:aBlock
+ "evaluate the argument, aBlock for every file name in the directory and in all
+ subdirectories"
+
+ |aStream command line|
+
+ lazy ifTrue:[self getFullPathName].
+ command := 'cd ' , pathName , '; find . -print'.
+ aStream := PipeStream readingFrom:command.
+ aStream isNil ifTrue:[^ nil].
+ [aStream atEnd] whileFalse:[
+ line := aStream nextLine.
+ line notNil ifTrue:[
+ (line = '.') ifFalse:[
+ "cut off initial ./"
+ line := line copyFrom:3 to:(line size)
+ ].
+ aBlock value:line
+ ]
+ ].
+ aStream close
+!
+
+allDirectoriesDo:aBlock
+ "evaluate the argument, aBlock for every directory name
+ in the directory and in all subdirectories"
+
+ |aStream command line|
+
+ lazy ifTrue:[self getFullPathName].
+ command := 'cd ' , pathName , '; find . -type d -print'.
+ aStream := PipeStream readingFrom:command.
+ aStream isNil ifTrue:[^ nil].
+ [aStream atEnd] whileFalse:[
+ line := aStream nextLine.
+ line notNil ifTrue:[
+ (line = '.') ifFalse:[
+ "cut off initial ./"
+ line := line copyFrom:3 to:(line size)
+ ].
+ aBlock value:line
+ ]
+ ].
+ aStream close
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FileStr.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,467 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ExternalStream subclass:#FileStream
+ instanceVariableNames:'pathName'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams-External'
+!
+
+FileStream comment:'
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+This class provides access to the operating systems underlying file
+system (i.e. its an interface to the stdio library).
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+
+#ifdef transputer
+# include <iocntrl.h>
+# ifndef fileno
+ /* kludge: inmos forgot fileno */
+# define fileno(f) ((f)->__file)
+# endif
+#else
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+%}
+
+!FileStream class methodsFor:'instance creation'!
+
+newFileNamed:filename
+ "return a FileStream for new file named filename, aString.
+ If the file exists, it is truncated, otherwise created.
+ The file is opened for write access only."
+
+ |newStream|
+ newStream := (self basicNew) pathName:filename.
+ newStream createForWriting isNil ifTrue:[^nil].
+ ^ newStream
+!
+
+newFileNamed:filename in:aDirectory
+ "return a FileStream for new file named filename, aString
+ in aDirectory, a FileDirectory.
+ If the file exists, it is truncated, otherwise created.
+ The file is opened for write access only."
+
+ |newStream|
+ newStream := (self basicNew) pathName:filename in:aDirectory.
+ newStream createForWriting isNil ifTrue:[^nil].
+ ^ newStream
+!
+
+oldFileNamed:filename
+ "return a FileStream for existing file named filename, aString.
+ The file is opened for read/write access."
+
+ |newStream|
+
+ (OperatingSystem isReadable:filename) ifFalse:[^nil].
+ newStream := (self basicNew) pathName:filename.
+ newStream readwrite.
+ newStream openForReadWrite isNil ifTrue:[^nil].
+ newStream readLimit:(newStream size).
+ ^ newStream
+!
+
+oldFileNamed:filename in:aDirectory
+ "return a FileStream for existing file named filename, aString
+ in aDirectory, a FileDirectory.
+ The file is opened for read/write access."
+
+ |newStream|
+ newStream := (self basicNew) pathName:filename in:aDirectory.
+ newStream openForReadWrite isNil ifTrue:[^nil].
+ newStream readLimit:(newStream size).
+ ^ newStream
+!
+
+fileNamed:filename
+ "return a stream on file filename - if the file does not
+ already exist, create it."
+
+ |stream|
+
+ stream := self oldFileNamed:filename.
+ stream isNil ifTrue:[
+ stream := self newFileNamed:filename
+ ].
+ ^ stream
+!
+
+fileNamed:filename in:aDirectory
+ "return a stream on file filename - if the file does not
+ already exist, create it."
+
+ |stream|
+
+ stream := self oldFileNamed:filename in:aDirectory.
+ stream isNil ifTrue:[
+ stream := self newFileNamed:filename in:aDirectory
+ ].
+ ^ stream
+!
+
+readonlyFileNamed:filename
+ "return a readonly FileStream for existing file named filename, aString"
+
+ |newStream|
+
+ (OperatingSystem isReadable:filename) ifFalse:[^nil].
+
+ newStream := (self basicNew) pathName:filename.
+ newStream openForReading isNil ifTrue:[^nil].
+ newStream readLimit:(newStream size).
+ ^ newStream
+!
+
+readonlyFileNamed:filename in:aDirectory
+ "return a readonly FileStream for existing file named filename, aString
+ in aDirectory, a FileDirectory"
+
+ |newStream|
+ newStream := (self basicNew) pathName:filename in:aDirectory.
+ newStream openForReading isNil ifTrue:[^nil].
+ newStream readLimit:(newStream size).
+ ^ newStream
+!
+
+appendingOldFileNamed:filename
+ "return a FileStream for existing file named filename, aString"
+
+ |newStream|
+ newStream := (self basicNew) pathName:filename.
+ newStream openForAppending isNil ifTrue:[^nil].
+ newStream readLimit:(newStream size).
+ ^ newStream
+!
+
+appendingOldFileNamed:filename in:aDirectory
+ "return a FileStream for existing file named filename, aString
+ in aDirectory, a FileDirectory"
+
+ |newStream|
+ newStream := (self basicNew) pathName:filename in:aDirectory.
+ newStream openForAppending isNil ifTrue:[^nil].
+ newStream readLimit:(newStream size).
+ ^ newStream
+! !
+
+!FileStream methodsFor:'accessing'!
+
+store:something
+ "what really should this do"
+
+ self nextPutAll:something
+!
+
+directoryName
+ "return the name of the directory I'm in"
+
+ |path lastIndex index|
+
+ path := pathName.
+ lastIndex := 0.
+ index := path indexOf:$/.
+ [index ~~ 0] whileTrue:[
+ lastIndex := index.
+ index := path indexOf:$/ startingAt:(index + 1)
+ ].
+ (lastIndex == 0) ifTrue:[^ '.'].
+ (lastIndex == 1) ifTrue:[^ '/'].
+ ^ path copyFrom:1 to:(lastIndex - 1)
+!
+
+name
+ "return my name without leading direcory-path"
+
+ |lastIndex index|
+
+ lastIndex := 1.
+ [true] whileTrue:[
+ index := pathName indexOf:$/ startingAt:lastIndex.
+ (index == 0) ifTrue:[
+ ^ pathName copyFrom:lastIndex
+ ].
+ lastIndex := index + 1
+ ]
+!
+
+pathName
+ "return the pathname"
+
+ ^ pathName
+! !
+
+!FileStream methodsFor:'private'!
+
+pathName:filename
+ "set the pathname"
+
+ pathName := filename
+!
+
+pathName:filename in:aDirectory
+ "set the pathname starting at aDirectory, a FileDirectory"
+
+ ((filename at:1) == $/) ifTrue:[
+ "filename may not start with a '/'"
+ pathName := nil
+ ] ifFalse:[
+ pathName := aDirectory pathName.
+ (pathName endsWith:'/') ifFalse:[
+ pathName := pathName , '/'
+ ].
+ pathName := pathName , filename
+ ]
+!
+
+open
+ "open the file"
+
+ pathName isNil ifTrue:[^nil].
+ (mode == #readonly) ifTrue: [
+ ^ self openForReading
+ ].
+ (mode == #writeonly) ifTrue: [
+ ^ self openForWriting
+ ].
+ ^ self openForReadWrite
+!
+
+openWithMode:openmode
+ "open the file; openmode is the string defining the way to open"
+
+ |retVal|
+%{
+ FILE *f;
+ OBJ path;
+ extern OBJ ErrorNumber, Filename;
+ extern errno;
+
+ if (_INST(filePointer) == nil) {
+ path = _INST(pathName);
+ if (_isString(path) || (_Class(path) == Filename)) {
+ f = (FILE *)fopen((char *) _stringVal(path), (char *) _stringVal(openmode));
+ if (f == NULL) {
+ ErrorNumber = _MKSMALLINT(errno);
+ _INST(position) = nil;
+ } else {
+ _INST(filePointer) = _MKSMALLINT((int)f);
+ _INST(position) = _MKSMALLINT(1);
+ retVal = self;
+ }
+ }
+ }
+%}
+.
+ retVal notNil ifTrue:[
+ lobby register:self
+ ].
+ ^ retVal
+!
+
+openForReading
+ "open the file for readonly"
+
+ mode := #readonly.
+ ^ self openWithMode:'r'
+!
+
+openForWriting
+ "open the file for writeonly"
+
+ mode := #writeonly.
+ ^ self openWithMode:'w'
+!
+
+openForAppending
+ "open the file for writeonly appending to the end"
+
+ mode := #writeonly.
+ ^ self openWithMode:'a+'
+!
+
+createForWriting
+ "create/truncate the file for writeonly"
+
+ mode := #writeonly.
+ ^ self openWithMode:'w+'
+!
+
+openForReadWrite
+ "open the file for read/write"
+
+ mode := #readwrite.
+ ^ self openWithMode:'r+w'
+!
+
+createForReadWrite
+ "create/truncate the file for read/write"
+
+ mode := #readwrite.
+ ^ self openWithMode:'rw+'
+!
+
+reOpen
+ "sent after snapin to reopen streams"
+
+ filePointer notNil ifTrue:[
+ "it was open, when snapped-out"
+ filePointer := nil.
+ self open.
+ filePointer isNil ifTrue:[
+ Transcript showCr:('could not reopen file: ', pathName)
+ ] ifFalse:[
+ self position:position
+ ]
+ ]
+!
+
+size
+ "return the size in bytes of the file"
+
+%{ /* NOCONTEXT */
+
+#ifdef transputer
+ FILE *f;
+ int size;
+
+ if (_INST(filePointer) != nil) {
+ f = (FILE *)_intVal(_INST(filePointer));
+ if ((size = filesize(fileno(f))) >= 0) {
+ RETURN ( _MKSMALLINT(size) );
+ }
+ }
+#else
+ FILE *f;
+ struct stat buf;
+
+ if (_INST(filePointer) != nil) {
+ f = (FILE *)_intVal(_INST(filePointer));
+ if (fstat(fileno(f), &buf) >= 0) {
+ RETURN ( _MKSMALLINT(buf.st_size) );
+ }
+ }
+#endif
+%}
+.
+ "could add a fall-back here:
+
+ oldPosition := self position.
+ self setToEnd.
+ sz := self position.
+ self position:oldPosition.
+ ^ sz
+ "
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ self primitiveFailed
+!
+
+position
+ "return the read/write position in the file -
+ notice, in smalltalk indices start at 1 so begin of file is 1""
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ long currentPosition;
+
+ if (_INST(filePointer) != nil) {
+ f = (FILE *)_intVal(_INST(filePointer));
+ currentPosition = ftell(f);
+ if (currentPosition >= 0) {
+ /*
+ * notice: Smalltalk index starts at 1
+ */
+ RETURN ( _MKSMALLINT(currentPosition + 1) );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ self primitiveFailed
+!
+
+position:newPos
+ "set the read/write position in the file"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ extern OBJ ErrorNumber;
+ extern errno;
+
+ if (_INST(filePointer) != nil) {
+ if (_isSmallInteger(newPos)) {
+ f = (FILE *)_intVal(_INST(filePointer));
+ /*
+ * notice: Smalltalk index starts at 1
+ */
+ if (fseek(f, _intVal(newPos) - 1, 0) >= 0) {
+ _INST(position) = newPos;
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ self primitiveFailed
+!
+
+setToEnd
+ "set the read/write position in the file to be at the end of the file"
+
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+%{
+ FILE *f;
+ extern OBJ ErrorNumber;
+ extern errno;
+
+ f = (FILE *)_intVal(_INST(filePointer));
+ _INST(position) = nil;
+ if (fseek(f, 0, 2) >= 0) {
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+ ^ self primitiveFailed
+! !
+
+!FileStream methodsFor:'printing & storing'!
+
+printOn:aStream
+ aStream nextPutAll:'(a FileStream for:'.
+ aStream nextPutAll:pathName.
+ aStream nextPut:$)
+!
+
+storeOn:aStream
+ aStream nextPutAll:'(FileStream oldFileNamed:'.
+ aStream nextPutAll:pathName.
+ (self position ~~ 1) ifTrue:[
+ aStream nextPutAll:'; position:'.
+ self position storeOn:aStream
+ ].
+ aStream nextPut:$)
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FileStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,467 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ExternalStream subclass:#FileStream
+ instanceVariableNames:'pathName'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams-External'
+!
+
+FileStream comment:'
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+This class provides access to the operating systems underlying file
+system (i.e. its an interface to the stdio library).
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+
+#ifdef transputer
+# include <iocntrl.h>
+# ifndef fileno
+ /* kludge: inmos forgot fileno */
+# define fileno(f) ((f)->__file)
+# endif
+#else
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+%}
+
+!FileStream class methodsFor:'instance creation'!
+
+newFileNamed:filename
+ "return a FileStream for new file named filename, aString.
+ If the file exists, it is truncated, otherwise created.
+ The file is opened for write access only."
+
+ |newStream|
+ newStream := (self basicNew) pathName:filename.
+ newStream createForWriting isNil ifTrue:[^nil].
+ ^ newStream
+!
+
+newFileNamed:filename in:aDirectory
+ "return a FileStream for new file named filename, aString
+ in aDirectory, a FileDirectory.
+ If the file exists, it is truncated, otherwise created.
+ The file is opened for write access only."
+
+ |newStream|
+ newStream := (self basicNew) pathName:filename in:aDirectory.
+ newStream createForWriting isNil ifTrue:[^nil].
+ ^ newStream
+!
+
+oldFileNamed:filename
+ "return a FileStream for existing file named filename, aString.
+ The file is opened for read/write access."
+
+ |newStream|
+
+ (OperatingSystem isReadable:filename) ifFalse:[^nil].
+ newStream := (self basicNew) pathName:filename.
+ newStream readwrite.
+ newStream openForReadWrite isNil ifTrue:[^nil].
+ newStream readLimit:(newStream size).
+ ^ newStream
+!
+
+oldFileNamed:filename in:aDirectory
+ "return a FileStream for existing file named filename, aString
+ in aDirectory, a FileDirectory.
+ The file is opened for read/write access."
+
+ |newStream|
+ newStream := (self basicNew) pathName:filename in:aDirectory.
+ newStream openForReadWrite isNil ifTrue:[^nil].
+ newStream readLimit:(newStream size).
+ ^ newStream
+!
+
+fileNamed:filename
+ "return a stream on file filename - if the file does not
+ already exist, create it."
+
+ |stream|
+
+ stream := self oldFileNamed:filename.
+ stream isNil ifTrue:[
+ stream := self newFileNamed:filename
+ ].
+ ^ stream
+!
+
+fileNamed:filename in:aDirectory
+ "return a stream on file filename - if the file does not
+ already exist, create it."
+
+ |stream|
+
+ stream := self oldFileNamed:filename in:aDirectory.
+ stream isNil ifTrue:[
+ stream := self newFileNamed:filename in:aDirectory
+ ].
+ ^ stream
+!
+
+readonlyFileNamed:filename
+ "return a readonly FileStream for existing file named filename, aString"
+
+ |newStream|
+
+ (OperatingSystem isReadable:filename) ifFalse:[^nil].
+
+ newStream := (self basicNew) pathName:filename.
+ newStream openForReading isNil ifTrue:[^nil].
+ newStream readLimit:(newStream size).
+ ^ newStream
+!
+
+readonlyFileNamed:filename in:aDirectory
+ "return a readonly FileStream for existing file named filename, aString
+ in aDirectory, a FileDirectory"
+
+ |newStream|
+ newStream := (self basicNew) pathName:filename in:aDirectory.
+ newStream openForReading isNil ifTrue:[^nil].
+ newStream readLimit:(newStream size).
+ ^ newStream
+!
+
+appendingOldFileNamed:filename
+ "return a FileStream for existing file named filename, aString"
+
+ |newStream|
+ newStream := (self basicNew) pathName:filename.
+ newStream openForAppending isNil ifTrue:[^nil].
+ newStream readLimit:(newStream size).
+ ^ newStream
+!
+
+appendingOldFileNamed:filename in:aDirectory
+ "return a FileStream for existing file named filename, aString
+ in aDirectory, a FileDirectory"
+
+ |newStream|
+ newStream := (self basicNew) pathName:filename in:aDirectory.
+ newStream openForAppending isNil ifTrue:[^nil].
+ newStream readLimit:(newStream size).
+ ^ newStream
+! !
+
+!FileStream methodsFor:'accessing'!
+
+store:something
+ "what really should this do"
+
+ self nextPutAll:something
+!
+
+directoryName
+ "return the name of the directory I'm in"
+
+ |path lastIndex index|
+
+ path := pathName.
+ lastIndex := 0.
+ index := path indexOf:$/.
+ [index ~~ 0] whileTrue:[
+ lastIndex := index.
+ index := path indexOf:$/ startingAt:(index + 1)
+ ].
+ (lastIndex == 0) ifTrue:[^ '.'].
+ (lastIndex == 1) ifTrue:[^ '/'].
+ ^ path copyFrom:1 to:(lastIndex - 1)
+!
+
+name
+ "return my name without leading direcory-path"
+
+ |lastIndex index|
+
+ lastIndex := 1.
+ [true] whileTrue:[
+ index := pathName indexOf:$/ startingAt:lastIndex.
+ (index == 0) ifTrue:[
+ ^ pathName copyFrom:lastIndex
+ ].
+ lastIndex := index + 1
+ ]
+!
+
+pathName
+ "return the pathname"
+
+ ^ pathName
+! !
+
+!FileStream methodsFor:'private'!
+
+pathName:filename
+ "set the pathname"
+
+ pathName := filename
+!
+
+pathName:filename in:aDirectory
+ "set the pathname starting at aDirectory, a FileDirectory"
+
+ ((filename at:1) == $/) ifTrue:[
+ "filename may not start with a '/'"
+ pathName := nil
+ ] ifFalse:[
+ pathName := aDirectory pathName.
+ (pathName endsWith:'/') ifFalse:[
+ pathName := pathName , '/'
+ ].
+ pathName := pathName , filename
+ ]
+!
+
+open
+ "open the file"
+
+ pathName isNil ifTrue:[^nil].
+ (mode == #readonly) ifTrue: [
+ ^ self openForReading
+ ].
+ (mode == #writeonly) ifTrue: [
+ ^ self openForWriting
+ ].
+ ^ self openForReadWrite
+!
+
+openWithMode:openmode
+ "open the file; openmode is the string defining the way to open"
+
+ |retVal|
+%{
+ FILE *f;
+ OBJ path;
+ extern OBJ ErrorNumber, Filename;
+ extern errno;
+
+ if (_INST(filePointer) == nil) {
+ path = _INST(pathName);
+ if (_isString(path) || (_Class(path) == Filename)) {
+ f = (FILE *)fopen((char *) _stringVal(path), (char *) _stringVal(openmode));
+ if (f == NULL) {
+ ErrorNumber = _MKSMALLINT(errno);
+ _INST(position) = nil;
+ } else {
+ _INST(filePointer) = _MKSMALLINT((int)f);
+ _INST(position) = _MKSMALLINT(1);
+ retVal = self;
+ }
+ }
+ }
+%}
+.
+ retVal notNil ifTrue:[
+ lobby register:self
+ ].
+ ^ retVal
+!
+
+openForReading
+ "open the file for readonly"
+
+ mode := #readonly.
+ ^ self openWithMode:'r'
+!
+
+openForWriting
+ "open the file for writeonly"
+
+ mode := #writeonly.
+ ^ self openWithMode:'w'
+!
+
+openForAppending
+ "open the file for writeonly appending to the end"
+
+ mode := #writeonly.
+ ^ self openWithMode:'a+'
+!
+
+createForWriting
+ "create/truncate the file for writeonly"
+
+ mode := #writeonly.
+ ^ self openWithMode:'w+'
+!
+
+openForReadWrite
+ "open the file for read/write"
+
+ mode := #readwrite.
+ ^ self openWithMode:'r+w'
+!
+
+createForReadWrite
+ "create/truncate the file for read/write"
+
+ mode := #readwrite.
+ ^ self openWithMode:'rw+'
+!
+
+reOpen
+ "sent after snapin to reopen streams"
+
+ filePointer notNil ifTrue:[
+ "it was open, when snapped-out"
+ filePointer := nil.
+ self open.
+ filePointer isNil ifTrue:[
+ Transcript showCr:('could not reopen file: ', pathName)
+ ] ifFalse:[
+ self position:position
+ ]
+ ]
+!
+
+size
+ "return the size in bytes of the file"
+
+%{ /* NOCONTEXT */
+
+#ifdef transputer
+ FILE *f;
+ int size;
+
+ if (_INST(filePointer) != nil) {
+ f = (FILE *)_intVal(_INST(filePointer));
+ if ((size = filesize(fileno(f))) >= 0) {
+ RETURN ( _MKSMALLINT(size) );
+ }
+ }
+#else
+ FILE *f;
+ struct stat buf;
+
+ if (_INST(filePointer) != nil) {
+ f = (FILE *)_intVal(_INST(filePointer));
+ if (fstat(fileno(f), &buf) >= 0) {
+ RETURN ( _MKSMALLINT(buf.st_size) );
+ }
+ }
+#endif
+%}
+.
+ "could add a fall-back here:
+
+ oldPosition := self position.
+ self setToEnd.
+ sz := self position.
+ self position:oldPosition.
+ ^ sz
+ "
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ self primitiveFailed
+!
+
+position
+ "return the read/write position in the file -
+ notice, in smalltalk indices start at 1 so begin of file is 1""
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ long currentPosition;
+
+ if (_INST(filePointer) != nil) {
+ f = (FILE *)_intVal(_INST(filePointer));
+ currentPosition = ftell(f);
+ if (currentPosition >= 0) {
+ /*
+ * notice: Smalltalk index starts at 1
+ */
+ RETURN ( _MKSMALLINT(currentPosition + 1) );
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ self primitiveFailed
+!
+
+position:newPos
+ "set the read/write position in the file"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ extern OBJ ErrorNumber;
+ extern errno;
+
+ if (_INST(filePointer) != nil) {
+ if (_isSmallInteger(newPos)) {
+ f = (FILE *)_intVal(_INST(filePointer));
+ /*
+ * notice: Smalltalk index starts at 1
+ */
+ if (fseek(f, _intVal(newPos) - 1, 0) >= 0) {
+ _INST(position) = newPos;
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ }
+ }
+%}
+.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ self primitiveFailed
+!
+
+setToEnd
+ "set the read/write position in the file to be at the end of the file"
+
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+%{
+ FILE *f;
+ extern OBJ ErrorNumber;
+ extern errno;
+
+ f = (FILE *)_intVal(_INST(filePointer));
+ _INST(position) = nil;
+ if (fseek(f, 0, 2) >= 0) {
+ RETURN ( self );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+ ^ self primitiveFailed
+! !
+
+!FileStream methodsFor:'printing & storing'!
+
+printOn:aStream
+ aStream nextPutAll:'(a FileStream for:'.
+ aStream nextPutAll:pathName.
+ aStream nextPut:$)
+!
+
+storeOn:aStream
+ aStream nextPutAll:'(FileStream oldFileNamed:'.
+ aStream nextPutAll:pathName.
+ (self position ~~ 1) ifTrue:[
+ aStream nextPutAll:'; position:'.
+ self position storeOn:aStream
+ ].
+ aStream nextPut:$)
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Filename.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,71 @@
+"
+ COPYRIGHT (c) 1992-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Filename
+ instanceVariableNames:'name'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'ST-80 compatibility'!
+
+Filename comment:'
+
+COPYRIGHT (c) 1992-93 by Claus Gittinger
+ All Rights Reserved
+
+Filenames for ST-80 compatibility. Only the minimum is implemented
+here to make some PD programs happy - I dont know what else there
+is in ST-80.
+
+%W% %E%
+'!
+
+!Filename class methodsFor:'instance creation'!
+
+named:aString
+ ^ (self basicNew) name:aString
+! !
+
+!Filename methodsFor:'converting'!
+
+asString
+ ^ name
+!
+
+asFilename
+ ^ self
+! !
+
+!Filename methodsFor:'private accessing'!
+
+name:aString
+ name := aString
+! !
+
+!Filename methodsFor:'file access'!
+
+exists
+ "return true, if such a file exists"
+
+ ^ OperatingSystem isValidPath:name
+!
+
+fileIn
+ ^ (FileStream readonlyFileNamed:name) fileIn
+!
+
+readStream
+ ^ FileStream readonlyFileNamed:name
+!
+
+writeStream
+ ^ FileStream newFileNamed:name
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Float.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,818 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Number variableByteSubclass:#Float
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Magnitude-Numbers'
+!
+
+Float comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+
+notice, that Floats are defined as Byte-array to prevent garbage collector
+from going into the value ... otherwise I needed a special case in many places.
+'!
+
+!Float class methodsFor:'instance creation'!
+
+new:aNumber
+ "catch this message - not allowed for floats"
+
+ self error:'Floats cannot be created with new:'
+!
+
+basicNew
+ "return a new float - here we return 0.0
+ - floats are usually NOT created this way ...
+ Its implemented here to allow things like binary store & load
+ of floats. (but even this support will go away eventually, its not
+ a good idea to store the bits of a float - the reader might have a
+ totally different representation."
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKFLOAT((0.0) COMMA_SND) );
+%}
+! !
+
+!Float class methodsFor:'constants'!
+
+zero
+ "return the neutral element for addition"
+
+ ^ 0.0
+!
+
+unity
+ "return the neutral element for multiplication"
+
+ ^ 1.0
+!
+
+pi
+ "return the constant pi"
+
+ ^ 3.1415926535897932384626434
+! !
+
+!Float methodsFor:'copying'!
+
+shallowCopy
+ "return a shallow copy of myself
+ - reimplemented here since Floats are kind of kludgy"
+
+ ^ self
+!
+
+deepCopy
+ "return a deep copy of myself
+ - reimplemented here since Floats are kind of kludgy"
+
+ ^ self
+! !
+
+!Float methodsFor:'accessing'!
+
+size
+ "redefined since floats are kludgy (ByteArry)"
+
+ ^ 0
+!
+
+at:index
+ "redefined to prevent access to individual bytes in a float"
+
+ self error:'not allowed for floats'
+!
+
+at:index put:aValue
+ "redefined to prevent access to individual bytes in a float"
+
+ self error:'not allowed for floats'
+! !
+
+!Float methodsFor:'arithmetic'!
+
++ aNumber
+ "return the sum of the receiver and the argument, aNumber"
+
+%{ /* NOCONTEXT */
+ OBJ newFloat;
+ double result;
+
+ if (_isSmallInteger(aNumber)) {
+ result = _floatVal(self) + (double)(_intVal(aNumber));
+retResult:
+ _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+ _InstPtr(newFloat)->o_class = Float;
+ _FloatInstPtr(newFloat)->f_floatvalue = result;
+ RETURN ( newFloat );
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ result = _floatVal(self) + _floatVal(aNumber);
+ goto retResult;
+ }
+%}
+.
+ ^ aNumber sumFromFloat:self
+!
+
+- aNumber
+ "return the difference of the receiver and the argument, aNumber"
+
+%{ /* NOCONTEXT */
+ OBJ newFloat;
+ double result;
+
+ if (_isSmallInteger(aNumber)) {
+ result = _floatVal(self) - (double)(_intVal(aNumber));
+retResult:
+ _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+ _InstPtr(newFloat)->o_class = Float;
+ _FloatInstPtr(newFloat)->f_floatvalue = result;
+ RETURN ( newFloat );
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ result = _floatVal(self) - _floatVal(aNumber);
+ goto retResult;
+ }
+%}
+.
+ ^ aNumber differenceFromFloat:self
+!
+
+* aNumber
+ "return the product of the receiver and the argument, aNumber"
+
+%{ /* NOCONTEXT */
+
+ OBJ newFloat;
+ double result;
+
+ if (_isSmallInteger(aNumber)) {
+ result = _floatVal(self) * (double)(_intVal(aNumber));
+retResult:
+ _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+ _InstPtr(newFloat)->o_class = Float;
+ _FloatInstPtr(newFloat)->f_floatvalue = result;
+ RETURN ( newFloat );
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ result = _floatVal(self) * _floatVal(aNumber);
+ goto retResult;
+ }
+%}
+.
+ ^ aNumber productFromFloat:self
+!
+
+/ aNumber
+ "return the quotient of the receiver and the argument, aNumber"
+
+%{ /* NOCONTEXT */
+
+ OBJ newFloat;
+ double result, val;
+
+ if (_isSmallInteger(aNumber)) {
+ if (aNumber != _MKSMALLINT(0)) {
+ result = _floatVal(self) / ( (double)_intVal(aNumber)) ;
+retResult:
+ _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+ _InstPtr(newFloat)->o_class = Float;
+ _FloatInstPtr(newFloat)->f_floatvalue = result;
+ RETURN ( newFloat );
+ }
+ } else {
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ val = _floatVal(aNumber);
+ if (val != 0.0) {
+ result = _floatVal(self) / val;
+ goto retResult;
+ }
+ }
+ }
+%}
+.
+ ((aNumber == 0) or:[aNumber = 0.0]) ifTrue:[
+ DivisionByZeroSignal raise.
+ ^ self
+ ].
+ ^ aNumber quotientFromFloat:self
+!
+
+// aNumber
+ "return the integer quotient of dividing the receiver by aNumber with
+ truncation towards negative infinity."
+
+ ^ (self / aNumber) floor asInteger
+!
+
+\\ aNumber
+ "return the integer remainder of dividing the receiver by aNumber with
+ truncation towards negative infinity."
+
+ ^ (self - ((self / aNumber) floor * aNumber)) floor asInteger
+!
+
+negated
+ "return myself negated"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKFLOAT(- _floatVal(self) COMMA_SND) );
+%}
+! !
+
+!Float methodsFor:'testing'!
+
+positive
+ "return true if the receiver is greater or equal to zero"
+
+%{ /* NOCONTEXT */
+ RETURN ( (_floatVal(self) >= 0.0) ? true : false );
+%}
+!
+
+negative
+ "return true if the receiver is less than zero"
+
+%{ /* NOCONTEXT */
+ RETURN ( (_floatVal(self) < 0.0) ? true : false );
+%}
+! !
+
+!Float methodsFor:'comparing'!
+
+< aNumber
+ "return true, if the argument is greater"
+
+%{ /* NOCONTEXT */
+ if (_isSmallInteger(aNumber)) {
+ RETURN ( (_floatVal(self) < (double)(_intVal(aNumber))) ? true : false );
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ RETURN ( (_floatVal(self) < _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ aNumber lessFromFloat:self
+!
+
+> aNumber
+ "return true, if the argument is less"
+
+%{ /* NOCONTEXT */
+ if (_isSmallInteger(aNumber)) {
+ RETURN ( (_floatVal(self) > (double)(_intVal(aNumber))) ? true : false );
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ RETURN ( (_floatVal(self) > _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#> coercing:aNumber
+!
+
+<= aNumber
+ "return true, if the argument is greater or equal"
+
+%{ /* NOCONTEXT */
+ if (_isSmallInteger(aNumber)) {
+ RETURN ( (_floatVal(self) <= (double)(_intVal(aNumber))) ? true : false );
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ RETURN ( (_floatVal(self) <= _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#<= coercing:aNumber
+!
+
+>= aNumber
+ "return true, if the argument is less or equal"
+
+%{ /* NOCONTEXT */
+ if (_isSmallInteger(aNumber)) {
+ RETURN ( (_floatVal(self) >= (double)(_intVal(aNumber))) ? true : false );
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ RETURN ( (_floatVal(self) >= _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#>= coercing:aNumber
+!
+
+= aNumber
+ "return true, if the arguments value are equal"
+
+%{ /* NOCONTEXT */
+ if (_isSmallInteger(aNumber)) {
+ RETURN ( (_floatVal(self) == (double)(_intVal(aNumber))) ? true : false );
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ RETURN ( (_floatVal(self) == _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#= coercing:aNumber
+!
+
+~= aNumber
+ "return true, if the arguments value are not equal"
+
+%{ /* NOCONTEXT */
+ if (_isSmallInteger(aNumber)) {
+ RETURN ( (_floatVal(self) != (double)(_intVal(aNumber))) ? true : false );
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ RETURN ( (_floatVal(self) != _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#~= coercing:aNumber
+! !
+
+!Float methodsFor:'coercion and converting'!
+
+coerce:aNumber
+ "return aNumber converted into receivers type"
+
+ ^ aNumber asFloat
+!
+
+generality
+ "return the generality value - see ArithmeticValue>>retry:coercing:"
+ ^ 80
+!
+
+asFloat
+ "return a float with same value - thats me"
+
+ ^ self
+!
+
+asFraction
+ "return a corresponding fraction
+ - notice, that fract asFloat asFract does not always return
+ a good fraction ..."
+
+ |fract digits power num denom|
+
+ "we (indirectly) use printf which knows the precision of floats"
+
+ fract := self fractionPart.
+ digits := fract printString copyFrom:3.
+ power := digits size.
+ num := (self - fract) asInteger.
+ denom := (10 raisedToInteger:power).
+ num := num * denom.
+ num := num + (Integer readFromString:digits).
+ ^ (Fraction numerator:num denominator:denom) reduced
+
+ "0.3 asFraction"
+ "0.5 asFraction"
+ "(1/5) asFloat asFraction"
+ "(1/8) asFloat asFraction"
+ "(1/12) asFloat asFraction -> inexact result due to float errors"
+ "(1/13) asFloat asFraction -> inexact result due to float errors"
+!
+
+asInteger
+ "return an integer with same value - might truncate"
+
+ |l v sign|
+
+%{ /* NOCONTEXT */
+
+ if ((_floatVal(self) >= (double)_MIN_INT)
+ && (_floatVal(self) <= (double)_MAX_INT)) {
+ RETURN ( _MKSMALLINT( (INT)_floatVal(self)) );
+ }
+%}
+.
+ "this is stupid code - rounding errors accumulate; fix later"
+
+ sign := self sign.
+ v := self abs.
+ (v >= 10) ifTrue:[
+ l := (v / 10.0) asInteger * 10
+ ] ifFalse:[
+ l := 0
+ ].
+ v := v - ((v / 10.0) floor * 10.0) floor.
+ l := l + v truncated.
+ ^ l * sign
+
+ "12345.0 asInteger"
+ "1e15 asInteger"
+! !
+
+!Float methodsFor:'double dispatching'!
+
+sumFromFraction:aFraction
+ "sent when a fraction does not know how to add the recevier, a float"
+
+ ^ (self * aFraction denominator + aFraction numerator) / aFraction denominator
+!
+
+differenceFromFraction:aFraction
+ "sent when a fraction does not know how to subtract the recevier, a float"
+
+ ^ (self * aFraction denominator - aFraction numerator) / aFraction denominator
+!
+
+productFromFraction:aFraction
+ "sent when a fraction does not know how to multiply the recevier, a float"
+
+ ^ self * aFraction numerator / aFraction denominator
+!
+
+quotientFromFraction:aFraction
+ "sent when a fraction does not know how to divide by the recevier, a float"
+
+ ^ aFraction numerator / (self * aFraction denominator)
+! !
+
+!Float methodsFor:'truncation and rounding'!
+
+truncated
+ "return the receiver truncated towards zero as Integer"
+
+ |val|
+
+%{
+ double floor(), ceil();
+ double dVal;
+
+ dVal = _floatVal(self);
+ if (dVal < 0.0) {
+ dVal = ceil(dVal);
+ } else {
+ dVal = floor(dVal);
+ }
+
+ /*
+ * mhmh it seems that ST-80 is returning integers if possible.
+ * (at least, some pd programs expect it ...)
+ */
+ if ((dVal >= (double)_MIN_INT) && (dVal <= (double)_MAX_INT)) {
+ RETURN ( _MKSMALLINT( (INT) dVal ) );
+ }
+ val = _MKFLOAT(dVal COMMA_CON);
+%}
+.
+ ^ val asInteger
+!
+
+rounded
+ "return the receiver rounded to the nearest integer as integer"
+
+ |val|
+
+%{
+ double floor();
+ double dVal;
+
+ /*
+ * mhmh it seems that ST-80 is returning integers if possible
+ * at least, some pd programs expect it ...
+ */
+ dVal = floor(_floatVal(self) + 0.5);
+ if ((dVal >= (double)_MIN_INT) && (dVal <= (double)_MAX_INT)) {
+ RETURN ( _MKSMALLINT( (INT) dVal ) );
+ }
+ val = _MKFLOAT(dVal COMMA_CON);
+%}
+.
+ ^ val asInteger
+!
+
+floor
+ "return the biggest integer-valued float less or equal to the receiver"
+
+%{ /* NOCONTEXT */
+
+ double floor();
+
+ RETURN ( _MKFLOAT(floor(_floatVal(self)) COMMA_SND) );
+%}
+!
+
+ceiling
+ "return the smallest integer-valued float greater or equal to the receiver"
+
+%{ /* NOCONTEXT */
+
+ double ceil();
+
+ RETURN ( _MKFLOAT(ceil(_floatVal(self)) COMMA_SND) );
+%}
+!
+
+fractionPart
+ "return a float with value from digits after the decimal point"
+
+ ^ self - self truncated asFloat
+
+ "1234.56789 fractionPart"
+ "1.2345e6 fractionPart"
+! !
+
+!Float methodsFor:'mathematical functions'!
+
+ln
+ "return the natural logarithm of myself"
+
+%{ /* NOCONTEXT */
+
+ double log();
+ double result;
+ extern errno;
+ extern OBJ ErrorNumber;
+
+ errno = 0;
+ result = log(_floatVal(self));
+ if (errno == 0)
+ RETURN ( _MKFLOAT(result COMMA_SND) );
+ ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+ DomainErrorSignal raise
+!
+
+raisedTo:aNumber
+ "return self raised to the power of aNumber"
+ |n|
+
+ n := aNumber asFloat.
+%{
+ double pow();
+ double result;
+ extern errno;
+ extern OBJ ErrorNumber;
+
+ if (_isFloat(n)) {
+ errno = 0;
+ result = pow(_floatVal(self), _floatVal(n));
+ errno = 0; /* XXXX */
+ if (errno == 0)
+ RETURN ( _MKFLOAT(result COMMA_CON) );
+ ErrorNumber = _MKSMALLINT(errno);
+ }
+%}
+.
+ DomainErrorSignal raise
+!
+
+exp
+ "return e raised to the power of the receiver"
+
+%{ /* NOCONTEXT */
+
+ double exp();
+ double result;
+ extern errno;
+ extern OBJ ErrorNumber;
+
+ errno = 0;
+ result = exp(_floatVal(self));
+ if (errno == 0)
+ RETURN ( _MKFLOAT(result COMMA_SND) );
+ ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+ DomainErrorSignal raise
+!
+
+sin
+ "return the sine of myself interpreted as radians"
+
+%{ /* NOCONTEXT */
+
+ double sin();
+ double result;
+ extern errno;
+ extern OBJ ErrorNumber;
+
+ errno = 0;
+ result = sin(_floatVal(self));
+ if (errno == 0)
+ RETURN ( _MKFLOAT(result COMMA_SND) );
+ ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+ DomainErrorSignal raise
+!
+
+cos
+ "return the cosine of myself interpreted as radians"
+
+%{ /* NOCONTEXT */
+
+ double cos();
+ double result;
+ extern errno;
+ extern OBJ ErrorNumber;
+
+ errno = 0;
+ result = cos(_floatVal(self));
+ if (errno == 0)
+ RETURN ( _MKFLOAT(result COMMA_SND) );
+ ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+ DomainErrorSignal raise
+!
+
+tan
+ "return the tangent of myself interpreted as radians"
+
+%{ /* NOCONTEXT */
+
+ double tan();
+ double result;
+ extern errno;
+ extern OBJ ErrorNumber;
+
+ errno = 0;
+ result = tan(_floatVal(self));
+ if (errno == 0)
+ RETURN ( _MKFLOAT(result COMMA_SND) );
+ ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+ DomainErrorSignal raise
+!
+
+arcSin
+ "return the arcsine of myself as radians"
+
+%{ /* NOCONTEXT */
+
+ double asin();
+ double result;
+ extern errno;
+ extern OBJ ErrorNumber;
+
+ errno = 0;
+ result = asin(_floatVal(self));
+ if (errno == 0)
+ RETURN ( _MKFLOAT(result COMMA_SND) );
+ ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+ DomainErrorSignal raise
+!
+
+arcCos
+ "return the arccosine of myself as radians"
+
+%{ /* NOCONTEXT */
+
+ double acos();
+ double result;
+ extern errno;
+ extern OBJ ErrorNumber;
+
+ errno = 0;
+ result = acos(_floatVal(self));
+ if (errno == 0)
+ RETURN ( _MKFLOAT(result COMMA_SND) );
+ ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+ DomainErrorSignal raise
+!
+
+arcTan
+ "return the arctangent of myself as radians"
+
+%{ /* NOCONTEXT */
+
+ double atan();
+ double result;
+ extern errno;
+ extern OBJ ErrorNumber;
+
+ errno = 0;
+ result = atan(_floatVal(self));
+ if (errno == 0)
+ RETURN ( _MKFLOAT(result COMMA_SND) );
+ ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+ DomainErrorSignal raise
+!
+
+sqrt
+ "return the square root of myself"
+
+%{ /* NOCONTEXT */
+
+ double sqrt();
+ double result;
+ extern errno;
+ extern OBJ ErrorNumber;
+
+ errno = 0;
+ result = sqrt(_floatVal(self));
+ if (errno == 0)
+ RETURN ( _MKFLOAT(result COMMA_SND) );
+ ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+ DomainErrorSignal raise
+! !
+
+!Float methodsFor:'printing and storing'!
+
+printString
+ "return a printed representation of the receiver"
+
+%{ /* NOCONTEXT */
+
+ char buffer[256];
+ REGISTER char *cp;
+#ifdef THIS_CONTEXT
+ OBJ sav = __thisContext;
+#endif
+
+#ifdef SYSV
+ sprintf(buffer, "%.6lg", _floatVal(self));
+#else
+ sprintf(buffer, "%.6G", _floatVal(self));
+#endif
+
+#ifdef THIS_CONTEXT
+ __thisContext = sav;
+#endif
+ /*
+ * kludge to make integral floats prints as i.0 (not i as printf does)
+ * (i.e. look if string contains '.' or 'e' and append '.0' if not)
+ */
+ for (cp = buffer; *cp; cp++) {
+ if ((*cp == '.') || (*cp == 'e')) break;
+ }
+ if (! *cp) {
+ *cp++ = '.';
+ *cp++ = '0';
+ *cp = '\0';
+ }
+
+ RETURN ( _MKSTRING(buffer COMMA_SND) );
+%}
+ "1.0 printString"
+ "1.234 printString"
+ "1e10 printString"
+ "1.2e3 printString"
+ "1.2e30 printString"
+!
+
+printfPrintString:formatString
+ "non-portable: return a printed representation of the receiver
+ as specified by formatString, which is defined by printf.
+ If you use this, be aware, that specifying doubles differes on
+ systems; on SYSV machines you have to give something like %lf,
+ while on BSD systems the format string has to be %F."
+
+%{ /* NOCONTEXT */
+ char buffer[256];
+
+ if (_isString(formatString)) {
+#ifdef THIS_CONTEXT
+ OBJ sav = __thisContext;
+#endif
+ sprintf(buffer, _stringVal(formatString), _floatVal(self));
+#ifdef THIS_CONTEXT
+ __thisContext = sav;
+#endif
+ RETURN ( _MKSTRING(buffer COMMA_SND) );
+ }
+%}
+.
+ self primitiveFailed
+
+ "Float pi printfPrintString:'%%lg -> %lg'"
+ "Float pi printfPrintString:'%%lf -> %lf'"
+ "Float pi printfPrintString:'%%7.5lg -> %7.5lg'"
+ "Float pi printfPrintString:'%%7.5lf -> %7.5lf'"
+ "Float pi printfPrintString:'%%G -> %G'"
+ "Float pi printfPrintString:'%%F -> %F'"
+ "Float pi printfPrintString:'%%7.5G -> %7.5G'"
+ "Float pi printfPrintString:'%%7.5F -> %7.5F'"
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FloatArray.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,31 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ArrayedCollection variableFloatSubclass:#FloatArray
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Indexed'
+!
+
+FloatArray comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+FloatArrays store floats (and nothing else).
+See the comment in double array for more information.
+
+%W% %E%
+
+written june 93 by claus
+'!
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Fraction.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,391 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Number subclass:#Fraction
+ instanceVariableNames:'numerator denominator'
+ classVariableNames:'FractionOne FractionZero'
+ poolDictionaries:''
+ category:'Magnitude-Numbers'
+!
+
+Fraction comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+'!
+
+!Fraction class methodsFor:'initialization'!
+
+initialize
+ FractionZero := self numerator:0 denominator:1.
+ FractionOne := self numerator:1 denominator:1
+! !
+
+!Fraction class methodsFor:'constants'!
+
+zero
+ ^ FractionZero
+!
+
+unity
+ ^ FractionOne
+! !
+
+!Fraction class methodsFor:'instance creation'!
+
+new
+ "create and return a new fraction with value 0"
+
+ ^ self basicNew setNumerator:0 denominator:1
+!
+
+numerator:num denominator:den
+ "create and return a new fraction with numerator num and denominator den"
+
+%{ /* NOCONTEXT */
+ extern char *newNextPtr, *newEndPtr;
+
+ if (self == Fraction) {
+ if (_CanDoQuickNew(sizeof(struct fractionstruct))) {
+ OBJ newFraction;
+
+ _qCheckedAlignedNew(newFraction, sizeof(struct fractionstruct), __context);
+ _InstPtr(newFraction)->o_class = Fraction;
+ _FractionInstPtr(newFraction)->f_numerator = num;
+ _FractionInstPtr(newFraction)->f_denominator = den;
+ /* no store check needed - its definitely in newSpace */
+ RETURN ( newFraction );
+ }
+ }
+%}
+.
+ ^ self basicNew setNumerator:num denominator:den
+! !
+
+!Fraction methodsFor:'accessing'!
+
+numerator
+ "return the numerator"
+
+ ^ numerator
+!
+
+denominator
+ "return the denominator"
+
+ ^ denominator
+! !
+
+!Fraction methodsFor:'private'!
+
+setNumerator:num denominator:den
+ "set both numerator and denominator"
+
+ numerator := num.
+ denominator := den
+!
+
+reduced
+ "reduce the receiver"
+
+ |gc|
+
+ denominator == 1 ifTrue:[^ numerator].
+ numerator == 1 ifTrue:[^ self].
+
+ gc := numerator gcd:denominator.
+ (gc == 1) ifFalse:[
+ numerator := numerator // gc.
+ denominator := denominator // gc
+ ].
+ (numerator < 0) ifTrue:[
+ (denominator < 0) ifTrue:[
+ numerator := numerator negated.
+ denominator := denominator negated
+ ]
+ ].
+ (denominator == 1) ifTrue:[^ numerator].
+ ^ self
+! !
+
+!Fraction methodsFor:'coercing'!
+
+coerce:aNumber
+ ^ aNumber asFraction
+!
+
+generality
+ ^ 60
+! !
+
+!Fraction methodsFor:'converting'!
+
+asInteger
+ "return an integer with my value - will usually truncate"
+
+ ^ numerator // denominator
+!
+
+asLargeInteger
+ "return an integer with my value - will usually truncate"
+
+ ^ self asInteger asLargeInteger
+!
+
+asFloat
+ "return a float with (approximately) my value"
+
+ ^ (numerator asFloat) / (denominator asFloat)
+!
+
+asFraction
+ "return the receiver as fraction - thats itself"
+
+ ^ self
+! !
+
+!Fraction methodsFor:'comparing'!
+
+= aNumber
+ "return true, if the argument represents the same numeric value
+ as the receiver, false otherwise"
+
+ (aNumber isMemberOf:SmallInteger) ifTrue:[
+ (denominator = 1) ifFalse:[^ false].
+ ^ numerator = aNumber
+ ].
+ (aNumber isKindOf:Fraction) ifTrue:[
+ (numerator = aNumber numerator) ifFalse:[^ false].
+ ^ denominator = aNumber denominator
+ ].
+ ^ self retry:#= coercing:aNumber
+!
+
+> aNumber
+ "return true if the receiver is greater
+ than aNumber, false otherwise."
+ "optional - could use inherited method ..."
+
+ (aNumber isMemberOf:SmallInteger) ifTrue:[
+ ^ numerator > (denominator * aNumber)
+ ].
+ (aNumber isKindOf:Fraction) ifTrue:[
+ ^ (numerator * aNumber denominator) > (denominator * aNumber numerator)
+ ].
+ ^ self retry:#> coercing:aNumber
+!
+
+< aNumber
+ "return true if the receiver is less
+ than aNumber, false otherwise."
+
+ (aNumber isMemberOf:SmallInteger) ifTrue:[
+ ^ numerator < (denominator * aNumber)
+ ].
+ (aNumber isKindOf:Fraction) ifTrue:[
+ ^ (numerator * aNumber denominator) < (denominator * aNumber numerator)
+ ].
+ ^ aNumber lessFromFraction:self
+! !
+
+!Fraction methodsFor:'testing'!
+
+negative
+ "return true if the receiver is negative"
+
+ (numerator < 0) ifTrue:[
+ ^ (denominator < 0) not
+ ].
+ ^ (denominator < 0)
+! !
+
+!Fraction methodsFor:'arithmetic'!
+
++ aNumber
+ "return the sum of the receiver and the argument, aNumber"
+
+ |n d|
+
+ (aNumber isMemberOf:SmallInteger) ifTrue:[
+ ^ (self class numerator:(numerator + (denominator * aNumber))
+ denominator:denominator) reduced
+ ].
+ (aNumber isKindOf:Fraction) ifTrue:[
+ n := aNumber numerator.
+ d := aNumber denominator.
+
+ "save a multiplication if possible"
+ denominator == d ifTrue:[
+ ^ (self class numerator:(numerator + n) denominator:d) reduced
+ ].
+
+ ^ (self class numerator:((numerator * d) + (n * denominator))
+ denominator:(denominator * d)) reduced
+ ].
+ ^ aNumber sumFromFraction:self
+!
+
+- aNumber
+ "return the difference of the receiver and the argument, aNumber"
+
+ |n d|
+
+ (aNumber isMemberOf:SmallInteger) ifTrue:[
+ ^ (self class numerator:(numerator - (denominator * aNumber))
+ denominator:denominator) reduced
+ ].
+ (aNumber isKindOf:Fraction) ifTrue:[
+ n := aNumber numerator.
+ d := aNumber denominator.
+
+ "save a multiplication if possible"
+ denominator == d ifTrue:[
+ ^ (self class numerator:(numerator - n) denominator:d) reduced
+ ].
+
+ ^ (self class numerator:((numerator * d) - (n * denominator))
+ denominator:(denominator * d)) reduced
+ ].
+ ^ aNumber differenceFromFraction:self
+!
+
+* aNumber
+ "return the product of the receiver and the argument, aNumber"
+
+ |n d|
+
+ (aNumber isMemberOf:SmallInteger) ifTrue:[
+ ^ (self class numerator:(numerator * aNumber)
+ denominator:denominator) reduced
+ ].
+ (aNumber isKindOf:Fraction) ifTrue:[
+ n := numerator * aNumber numerator.
+ d := denominator * aNumber denominator.
+ ^ (self class numerator:n denominator:d) reduced
+ ].
+ ^ aNumber productFromFraction:self
+!
+
+/ aNumber
+ "return the quotient of the receiver and the argument, aNumber"
+
+ |n d|
+
+ (aNumber isKindOf:Fraction) ifTrue:[
+ n := numerator * aNumber denominator.
+ d := denominator * aNumber numerator.
+ ^ (self class numerator:n denominator:d) reduced
+ ].
+ ^ aNumber quotientFromFraction:self
+!
+
+// aNumber
+ "return the integer quotient of the receiver and the argument, aNumber"
+
+ self negative ifTrue:[
+ ^ ((numerator * aNumber denominator) // (denominator * aNumber numerator)) - 1
+ ].
+ ^ (numerator * aNumber denominator) // (denominator * aNumber numerator)
+!
+
+negated
+ "optional - could use inherited method ..."
+
+ ^ self class numerator:(numerator negated)
+ denominator:denominator
+!
+
+reciprocal
+ "optional - could use inherited method ..."
+
+ numerator == 1 ifTrue:[^ denominator].
+ ^ self class numerator:denominator
+ denominator:numerator
+! !
+
+!Fraction methodsFor:'truncation and rounding'!
+
+truncated
+ "return the receiver truncated towards zero as Integer"
+
+ ^ numerator // denominator
+!
+
+rounded
+ "return the receiver rounded to the nearest integer as integer"
+
+ self negative ifTrue:[
+ ^ (self + (1/2)) truncated - 1
+ ].
+ ^ (self + (1/2)) truncated
+! !
+
+!Fraction methodsFor:'double dispatching'!
+
+sumFromInteger:anInteger
+ "sent when an integer does not know how to add the recevier, a fraction"
+
+ ^ (self class numerator:(numerator + (anInteger * denominator))
+ denominator:denominator) reduced
+!
+
+differenceFromInteger:anInteger
+ "sent when an integer does not know how to subtract the recevier, a fraction"
+
+ ^ (self class numerator:((anInteger * denominator) - numerator)
+ denominator:denominator) reduced
+!
+
+productFromInteger:anInteger
+ "sent when an integer does not know how to multiply the recevier, a fraction"
+
+ ^ (self class numerator:(anInteger * numerator)
+ denominator:denominator) reduced
+!
+
+lessFromInteger:anInteger
+ "sent when an integer does not know how to compare to the recevier, a fraction"
+
+ ^ (denominator * anInteger) < numerator
+!
+
+sumFromFloat:aFloat
+ "sent when a float does not know how to add the recevier, a fraction"
+
+ ^ (aFloat * denominator + numerator) / denominator
+!
+
+differenceFromFloat:aFloat
+ "sent when a float does not know how to subtract the recevier, a fraction"
+
+ ^ (aFloat * denominator - numerator) / denominator
+!
+
+productFromFloat:aFloat
+ "sent when a float does not know how to multiply the recevier, a fraction"
+
+ ^ aFloat * numerator / denominator
+!
+
+quotientFromFloat:aFloat
+ "sent when a float does not know how to divide by the recevier, a fraction"
+
+ ^ (aFloat * denominator) / numerator
+! !
+
+!Fraction methodsFor:'printing'!
+
+printString
+ ^ '(' , numerator printString, '/' , denominator printString, ')'
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/IdDict.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,100 @@
+"
+ COPYRIGHT (c) 1992 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Dictionary subclass:#IdentityDictionary
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Unordered'
+!
+
+IdentityDictionary comment:'
+
+COPYRIGHT (c) 1992 by Claus Gittinger
+ All Rights Reserved
+
+same as a Dictionary but key must be identical - not just equal.
+Since compare is on identity keys, hashing is also done via
+identityHash instead of hash.
+
+%W% %E%
+
+written jul 92 by claus
+'!
+
+!IdentityDictionary methodsFor:'private'!
+
+findKeyOrNil:key
+ "Look for the key in the receiver. If it is found, return
+ the index of the association containing the key, otherwise
+ return the index of the first unused slot. Grow the receiver,
+ if key was not found, and no unused slots where present"
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex
+ probe |
+
+ length := keyArray basicSize.
+ startIndex := key identityHash \\ length + 1.
+
+ index := startIndex.
+ [true] whileTrue:[
+ probe := keyArray basicAt:index.
+ (probe isNil or: [probe == key]) ifTrue:[^ index].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ index == startIndex ifTrue:[^ self grow findKeyOrNil:key].
+ ]
+!
+
+findKey:key ifAbsent:aBlock
+ "Look for the key in the receiver. If it is found, return
+ the index of the association containing the key, otherwise
+ return the value of evaluating aBlock."
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex
+ probe|
+
+ length := keyArray basicSize.
+ length < 10 ifTrue:[
+ "assuming, that for small dictionaries the overhead of hashing
+ is large ... maybe that proves wrong (if overhead of comparing
+ is high)"
+ index := keyArray identityIndexOf:key.
+ index == 0 ifTrue:[
+ ^ aBlock value
+ ].
+ ^ index
+ ].
+
+ startIndex := key identityHash \\ length + 1.
+
+ index := startIndex.
+ [true] whileTrue:[
+ probe := keyArray basicAt:index.
+ probe == key ifTrue:[^ index].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ (probe isNil or:[index == startIndex]) ifTrue:[^ aBlock value]
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/IdSet.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,94 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Set subclass:#IdentitySet
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Unordered'
+!
+
+IdentitySet comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+same as a Set but compares elements using == (i.e. they must be identical
+- not just equal in structure).
+Since compare is on identity, hashing is also done via
+identityHash instead of hash.
+
+%W% %E%
+
+written jan 93 by claus
+'!
+
+!IdentitySet methodsFor:'private'!
+
+find:key ifAbsent:aBlock
+ "Look for the key in the receiver. If it is found, return
+ the index of the slot containing the key, otherwise
+ return the value of evaluating aBlock.
+ Redefined to compare for identity instead of equality"
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex "{ Class:SmallInteger }"
+ probe |
+
+ length := contentsArray basicSize.
+ startIndex := key identityHash \\ length + 1.
+
+ index := startIndex.
+ [true] whileTrue:[
+ probe := (contentsArray basicAt:index).
+ probe == key ifTrue:[^ index].
+ probe isNil ifTrue:[^ aBlock value].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ index == startIndex ifTrue:[
+ ^ aBlock value
+ ]
+ ]
+!
+
+findElementOrNil:key
+ "Look for the key in the receiver. Redefined to compare for
+ identity instead of equality"
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex "{ Class:SmallInteger }"
+ probe |
+
+ length := contentsArray basicSize.
+ startIndex := key identityHash \\ length + 1.
+
+ index := startIndex.
+ [true] whileTrue:[
+ probe := contentsArray basicAt:index.
+ (probe isNil or: [key == probe]) ifTrue:[^ index].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ index == startIndex ifTrue: [
+ ^ self grow findElementOrNil:key
+ ]
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/IdentityDictionary.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,100 @@
+"
+ COPYRIGHT (c) 1992 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Dictionary subclass:#IdentityDictionary
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Unordered'
+!
+
+IdentityDictionary comment:'
+
+COPYRIGHT (c) 1992 by Claus Gittinger
+ All Rights Reserved
+
+same as a Dictionary but key must be identical - not just equal.
+Since compare is on identity keys, hashing is also done via
+identityHash instead of hash.
+
+%W% %E%
+
+written jul 92 by claus
+'!
+
+!IdentityDictionary methodsFor:'private'!
+
+findKeyOrNil:key
+ "Look for the key in the receiver. If it is found, return
+ the index of the association containing the key, otherwise
+ return the index of the first unused slot. Grow the receiver,
+ if key was not found, and no unused slots where present"
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex
+ probe |
+
+ length := keyArray basicSize.
+ startIndex := key identityHash \\ length + 1.
+
+ index := startIndex.
+ [true] whileTrue:[
+ probe := keyArray basicAt:index.
+ (probe isNil or: [probe == key]) ifTrue:[^ index].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ index == startIndex ifTrue:[^ self grow findKeyOrNil:key].
+ ]
+!
+
+findKey:key ifAbsent:aBlock
+ "Look for the key in the receiver. If it is found, return
+ the index of the association containing the key, otherwise
+ return the value of evaluating aBlock."
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex
+ probe|
+
+ length := keyArray basicSize.
+ length < 10 ifTrue:[
+ "assuming, that for small dictionaries the overhead of hashing
+ is large ... maybe that proves wrong (if overhead of comparing
+ is high)"
+ index := keyArray identityIndexOf:key.
+ index == 0 ifTrue:[
+ ^ aBlock value
+ ].
+ ^ index
+ ].
+
+ startIndex := key identityHash \\ length + 1.
+
+ index := startIndex.
+ [true] whileTrue:[
+ probe := keyArray basicAt:index.
+ probe == key ifTrue:[^ index].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ (probe isNil or:[index == startIndex]) ifTrue:[^ aBlock value]
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/IdentitySet.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,94 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Set subclass:#IdentitySet
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Unordered'
+!
+
+IdentitySet comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+same as a Set but compares elements using == (i.e. they must be identical
+- not just equal in structure).
+Since compare is on identity, hashing is also done via
+identityHash instead of hash.
+
+%W% %E%
+
+written jan 93 by claus
+'!
+
+!IdentitySet methodsFor:'private'!
+
+find:key ifAbsent:aBlock
+ "Look for the key in the receiver. If it is found, return
+ the index of the slot containing the key, otherwise
+ return the value of evaluating aBlock.
+ Redefined to compare for identity instead of equality"
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex "{ Class:SmallInteger }"
+ probe |
+
+ length := contentsArray basicSize.
+ startIndex := key identityHash \\ length + 1.
+
+ index := startIndex.
+ [true] whileTrue:[
+ probe := (contentsArray basicAt:index).
+ probe == key ifTrue:[^ index].
+ probe isNil ifTrue:[^ aBlock value].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ index == startIndex ifTrue:[
+ ^ aBlock value
+ ]
+ ]
+!
+
+findElementOrNil:key
+ "Look for the key in the receiver. Redefined to compare for
+ identity instead of equality"
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex "{ Class:SmallInteger }"
+ probe |
+
+ length := contentsArray basicSize.
+ startIndex := key identityHash \\ length + 1.
+
+ index := startIndex.
+ [true] whileTrue:[
+ probe := contentsArray basicAt:index.
+ (probe isNil or: [key == probe]) ifTrue:[^ index].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ index == startIndex ifTrue: [
+ ^ self grow findElementOrNil:key
+ ]
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Integer.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,641 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Number subclass:#Integer
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Magnitude-Numbers'
+!
+
+Integer comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+abstract superclass for all integer numbers
+
+%W% %E%
+written 88 by claus
+'!
+
+!Integer class methodsFor:'constants'!
+
+zero
+ ^ 0
+!
+
+unity
+ ^ 1
+! !
+
+!Integer methodsFor:'arithmetic'!
+
+quo:aNumber
+ "Return the integer quotient of dividing the receiver by aNumber with
+ truncation towards zero. For Integers this is same as //"
+
+ ^ self // aNumber
+
+! !
+
+!Integer methodsFor:'double dispatching'!
+
+sumFromFraction:aFraction
+ "sent when a fraction does not know how to add the recevier, an integer"
+
+ ^ (Fraction numerator:(aFraction numerator
+ + (self * aFraction denominator))
+ denominator:aFraction denominator) reduced
+!
+
+differenceFromFraction:aFraction
+ "sent when a fraction does not know how to subtract the recevier, an integer"
+
+ ^ (Fraction numerator:((self * aFraction denominator) - aFraction numerator)
+ denominator:aFraction denominator) reduced
+!
+
+productFromFraction:aFraction
+ "sent when a fraction does not know how to multiply the recevier, an integer"
+
+ ^ (Fraction numerator:(self * aFraction numerator)
+ denominator:aFraction denominator) reduced
+! !
+
+!Integer methodsFor:'truncation & rounding'!
+
+ceiling
+ "I am my ceiling"
+
+ ^ self
+!
+
+floor
+ "I am my floor"
+
+ ^ self
+!
+
+rounded
+ "return the receiver rounded toward the next Integer -
+ for integers this is self"
+
+ ^ self
+!
+
+truncated
+ "return the receiver truncated towards zero -
+ for integers this is self"
+
+ ^ self
+! !
+
+!Integer methodsFor:'queries'!
+
+digitLength
+ "return the number of bytes needed for the binary representation
+ of the receiver"
+
+ ^ (self log:256) ceiling asInteger
+!
+
+digitAt:n
+ "return the n-th byte of the binary representation"
+
+ |num count|
+
+ num := self.
+ count := n.
+ [count > 1] whileTrue:[
+ num := num // 256.
+ count := count - 1
+ ].
+ ^ num \\ 256
+!
+
+isInteger
+ "return true, if the receiver is some kind of integer number"
+
+ ^ true
+! !
+
+!Integer methodsFor:'misc math'!
+
+factorial
+ "return 1*2*3...*self"
+
+ (self > 1) ifTrue:[
+ ^ self * (self - 1) factorial
+ ].
+ ^ self
+!
+
+gcd:anInteger
+ "return the greatest common divisor (Euclid's algorithm)"
+
+ |ttt selfInteger temp|
+
+ ttt := anInteger.
+ selfInteger := self.
+ [ttt ~~ 0] whileTrue:[
+ temp := selfInteger \\ ttt.
+ selfInteger := ttt.
+ ttt := temp
+ ].
+ ^ selfInteger
+!
+
+lcm:anInteger
+ ^(self * anInteger) abs // (self gcd: anInteger)
+!
+
+fib
+ "dont use this method if you need fibionacci numbers -
+ this method is for benchmarking purposes only.
+ (use fastFib instead and dont ever try 60 fib ...)"
+
+ (self > 1) ifTrue:[
+ ^ (self - 1) fib + (self - 2) fib
+ ].
+ ^ 1
+
+ "Time millisecondsToRun:[30 fib]"
+!
+
+fastFib
+ "this method just to show how a changed algorithm can
+ change things much more drastic than tuning ...
+ (compare 30 fib with 30 fastFib / dont even try 60 fib)"
+
+ |fib|
+
+ self <= 1 ifTrue:[^ 1].
+
+ FibCache isNil ifTrue:[
+ FibCache := OrderedCollection new
+ ].
+ FibCache size >= self ifTrue:[
+ ^ FibCache at:self
+ ].
+ fib := (self - 2) fastFib + (self - 1) fastFib.
+
+ FibCache grow:self.
+ FibCache at:self put:fib.
+ ^ fib
+
+ "Time millisecondsToRun:[30 fastFib]"
+!
+
+acker:n
+ "return the value of acker(self, n)"
+
+ (self == 0) ifTrue:[^ n + 1].
+ (n == 0) ifTrue:[^ (self - 1) acker: 1].
+ ^ (self - 1) acker:(self acker:(n - 1))
+
+ "3 acker:2"
+! !
+
+!Integer methodsFor:'coercing and converting'!
+
+asFraction
+ "return a Fraction with same value as receiver"
+
+ ^ Fraction numerator:self denominator:1
+!
+
+asInteger
+ "return the receiver truncated towards zero -
+ for integers this is self"
+
+ ^ self
+! !
+
+!Integer methodsFor:'printing & storing'!
+
+storeString
+ "return a string for storing - printString will do"
+
+ ^ self printString
+!
+
+printString
+ "return a string representation of the receiver"
+
+ ^ self printStringRadix:10
+!
+
+radixPrintStringRadix:aRadix
+ "return a string representation of the receiver in the specified
+ radix; prepend XXr to the string"
+
+ ^ (aRadix printString) , 'r', (self printStringRadix:aRadix)
+
+ "31 radixPrintStringRadix:2 "
+ "31 radixPrintStringRadix:3 "
+ "31 radixPrintStringRadix:36 "
+!
+
+printStringRadix:aRadix
+ "return a string representation of the receiver in the specified
+ radix (without the initial XXr)"
+
+ |leftPart|
+
+ (self = 0) ifTrue:[^ '0'].
+ (self < 0) ifTrue:[
+ ^ '-' , (self negated printStringRadix:aRadix)
+ ].
+ leftPart := self // aRadix.
+ (leftPart ~= 0) ifTrue:[
+ ^ (leftPart printStringRadix:aRadix) copyWith:(Character digitValue:(self \\ aRadix))
+ ].
+ ^ (Character digitValue:self) asString
+!
+
+printStringRadix:aRadix size:sz fill:fillCharacter
+ "return a string representation of the receiver in the specified
+ radix. The string is padded on the left with fillCharacter to make
+ its size as specified in sz."
+
+ |s|
+
+ s := self printStringRadix:aRadix.
+ s size < sz ifTrue:[
+ s := ((String new:(sz - s size)) atAllPut:fillCharacter) , s
+ ].
+ ^ s
+
+ "1024 printStringRadix:16 size:4 fill:$0"
+! !
+
+!Integer class methodsFor:'instance creation'!
+
+readFrom:aStream radix:radix
+ "return the next Integer from the (character-)stream aStream in radix;
+ (assumes that the initial XXR has already been read)
+ no whitespace-skipping; returns 0 if no number available"
+
+ |nextChar value|
+
+ nextChar := aStream peek.
+ value := 0.
+ [nextChar notNil and:[nextChar isDigitRadix:radix]] whileTrue:[
+ value := value * radix + nextChar digitValue.
+ nextChar := aStream nextPeek
+ ].
+ ^ value
+!
+
+readFrom:aStream
+ "return the next Integer from the (character-)stream aStream,
+ handling initial XXr for arbitrary radix numbers and initial
+ sign.
+ skipping all whitespace first; return nil if no number"
+
+ |nextChar value negative|
+
+ nextChar := aStream skipSeparators.
+ (nextChar == $-) ifTrue:[
+ negative := true.
+ nextChar := aStream nextPeek
+ ] ifFalse:[
+ negative := false
+ ].
+ nextChar isDigit ifFalse:[ ^ nil].
+ value := Integer readFrom:aStream radix:10.
+ nextChar := aStream peek.
+ ((nextChar == $r) or:[ nextChar == $R]) ifTrue:[
+ aStream next.
+ value := Integer readFrom:aStream radix:value
+ ].
+ negative ifTrue:[
+ ^ value negated
+ ].
+ ^ value
+! !
+
+!Integer methodsFor:'benchmarking'!
+
+sieve
+ "sieve the primes self times"
+
+ |num i k prime count flags time|
+
+ num := 8191.
+ flags := Array new:num.
+
+ Transcript show:'Sieve running ...'.
+ Transcript cr.
+
+ time := Time millisecondsToRun:[
+ self timesRepeat:[
+ count := 0.
+ flags atAllPut:1.
+ i := 1.
+ num timesRepeat:[
+ (flags at:i) == 1 ifTrue:[
+ prime := i + i + 3.
+ k := i + prime.
+ [k <= num] whileTrue:[
+ flags at:k put:0.
+ k := k + prime
+ ].
+ count := count + 1
+ ].
+ i := i + 1
+ ].
+ ].
+ ].
+ Transcript show:'Sieve in Smalltalk: '.
+ Transcript show:self printString.
+ Transcript showCr:' iteration(s).'.
+ Transcript show:'found '.
+ Transcript show:count printString.
+ Transcript showCr:' primes.' .
+ Transcript show:'time per run: '.
+ Transcript show:(time / self) printString.
+ Transcript showCr:' ms.'
+
+ "1 sieve"
+!
+
+sieveWithIntegers
+ "sieve the primes self times"
+
+ |num "<SmallInteger>"
+ i "<SmallInteger>"
+ k "<SmallInteger>"
+ prime "<SmallInteger>"
+ count "<SmallInteger>"
+ flags time|
+
+ num := 8191.
+ flags := Array new:num.
+
+ Transcript show:'Sieve running ...'.
+ Transcript cr.
+
+ time := Time millisecondsToRun:[
+ self timesRepeat:[
+ count := 0.
+ flags atAllPut:1.
+ i := 1.
+ num timesRepeat:[
+ (flags at:i) == 1 ifTrue:[
+ prime := i + i + 3.
+ k := i + prime.
+ [k <= num] whileTrue:[
+ flags at:k put:0.
+ k := k + prime
+ ].
+ count := count + 1
+ ].
+ i := i + 1
+ ].
+ ].
+ ].
+ Transcript show:'Sieve in Smalltalk: '.
+ Transcript show:self printString.
+ Transcript showCr:' iteration(s).'.
+ Transcript show:'found '.
+ Transcript show:count printString.
+ Transcript showCr:' primes.' .
+ Transcript show:'time per run: '.
+ Transcript show:(time / self) printString.
+ Transcript showCr:' ms.'
+
+ "1 sieveWithIntegers"
+!
+
+recur1:num
+ "actual recursion method for recur1"
+
+ (num = 0) ifTrue:[^ self].
+ self recur1:(num - 1).
+ ^ self recur1:(num - 1)
+!
+
+recur1
+ "lots of recursion for testing send with arg"
+
+ |t|
+
+ t := Time millisecondsToRun:[
+ 1 recur1:15
+ ].
+ Transcript showCr:(t printString)
+
+ "1 recur1"
+!
+
+recur2
+ "lots of recursion for testing send without arg"
+
+ (self > 0) ifTrue:[
+ (self - 1) recur2.
+ ^ (self - 1) recur2
+ ]
+
+ "Transcript showCr:(
+ Time millisecondsToRun:[
+ 15 recur2
+ ]
+ ) printString"
+!
+
+countDown
+ |t index|
+
+ t := Time millisecondsToRun:[
+ index := 100000.
+ [index > 0] whileTrue:[
+ index := index - 1
+ ].
+ ].
+ Transcript showCr:(t printString)
+
+ "1 countDown"
+!
+
+countDown2
+ |t|
+
+ t := Time millisecondsToRun:[
+ |index|
+
+ index := 100000.
+ [index > 0] whileTrue:[
+ index := index - 1
+ ].
+ ].
+ Transcript showCr:(t printString)
+
+ "1 countDown2"
+!
+
+noop
+ ^ self
+!
+
+send:num
+ "lots of message sends"
+
+ |t|
+
+ t := Time millisecondsToRun:[
+ num timesRepeat:[
+ self noop
+ ].
+ ].
+ Transcript showCr:(t printString)
+
+ "1 send:100000"
+!
+
+memory
+ "lots of memory allocation"
+
+ |t|
+
+ t := Time millisecondsToRun:[
+ self timesRepeat:[
+ Array new:500
+ ].
+ ].
+ Transcript showCr:(t printString)
+
+ "10000 memory"
+!
+
+benchArithmetic
+ "arithmetic speed bench"
+
+ |p n m t|
+
+ n := 3.0.
+ m := 5.5.
+
+ t := Time millisecondsToRun:[
+ self timesRepeat:[
+ p := 5 / n + m
+ ]
+ ].
+ Transcript showCr:(t printString)
+
+ "10000 benchArithmetic"
+!
+
+sumTo
+ |val|
+
+ 100 timesRepeat:[
+ val := 0.
+ 1 to:10000 do:[:i |
+ val := val + i
+ ]
+ ].
+ "Time millisecondsToRun:[1 sumTo]"
+!
+
+fastSumTo
+ |val i|
+
+ 100 timesRepeat:[
+ val := 0.
+ i := 1.
+ [i <= 10000] whileTrue:[
+ val := val + i.
+ i := i + 1
+ ].
+ ].
+ "Time millisecondsToRun:[1 fastSumTo]"
+!
+
+nestedLoop
+ |i|
+
+ 100 timesRepeat:[
+ i := 0.
+ 1 to:100 do:[:l1 |
+ 1 to:100 do:[:l2 |
+ i := i + 1
+ ]
+ ]
+ ]
+ "Time millisecondsToRun:[1 nestedLoop]"
+!
+
+atAllPut
+ |vec t|
+
+ vec := Array new:100000.
+ t := Time millisecondsToRun:[
+ 1 to:100000 do:[:i |
+ vec at:i put:7
+ ]
+ ].
+ ^ t
+
+ "1 atAllPut"
+!
+
+atAllPut2
+ |array t|
+
+ array := Array new:100000.
+ t := Time millisecondsToRun:[
+ 1 to:100000 do:[:i |
+ array at:i put:7
+ ]
+ ].
+ ^ t
+
+ "1 atAllPut2"
+!
+
+sumAll
+ |vec t s|
+
+ vec := Array new:100000.
+ 1 to:100000 do:[:i |
+ vec at:i put:7
+ ].
+ s := 0.
+ t := Time millisecondsToRun:[
+ 1 to:100000 do:[:i |
+ s := s + (vec at:i)
+ ]
+ ].
+ ^ t
+
+ "1 sumAll"
+!
+
+sumAll2
+ |array t s|
+
+ array := Array new:100000.
+ 1 to:100000 do:[:i |
+ array at:i put:7
+ ].
+ s := 0.
+ t := Time millisecondsToRun:[
+ 1 to:100000 do:[:i |
+ s := s + (array at:i)
+ ]
+ ].
+ ^ t
+
+ "1 sumAll2"
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Interval.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,198 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#Interval
+ instanceVariableNames:'start stop step'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Sequenceable'
+!
+
+Interval comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+Intervals represent a collection (or range) of numeric values specified by
+a startValue, an endValue and a step. The elements are computed, not stored.
+For example, the interval (1 to:5) containes the elements (1 2 3 4 5) and
+(1 to:6 by:2) contains (1 3 5).
+
+
+%W% %E%
+
+written summer 89 by claus
+'!
+
+!Interval class methodsFor:'instance creation'!
+
+from:start to:stop
+ "return a new interval with elements from start
+ to stop by 1"
+
+ ^ self new
+ setFrom:start
+ to:stop
+ by:1
+!
+
+from:start to:stop by:step
+ "return a new interval with elements from start
+ to stop by step"
+
+ ^ self new
+ setFrom:start
+ to:stop
+ by:step
+! !
+
+!Interval methodsFor:'private'!
+
+setFrom:startInteger to:stopInteger by:stepInteger
+ start := startInteger.
+ stop := stopInteger.
+ step := stepInteger
+! !
+
+!Interval methodsFor:'accessing'!
+
+first
+ "return the first element of the collection"
+
+ ^ start
+!
+
+start
+ "return the first number of the range"
+
+ ^ start
+!
+
+start:aNumber
+ "set the first number of the range"
+
+ start := aNumber
+!
+
+stop
+ "return the end number of the range"
+
+ ^ stop
+!
+
+stop:aNumber
+ "set the end number of the range"
+
+ stop := aNumber
+!
+
+step
+ "return the step increment of the range"
+
+ ^ step
+!
+
+step:aNumber
+ "set the step increment of the range"
+
+ step := aNumber
+!
+
+size
+ "return the number of elements in the collection"
+
+ (step < 0) ifTrue:[
+ (start < stop) ifTrue:[
+ ^ 0
+ ].
+ ^ stop - start // step + 1
+ ].
+ (stop < start) ifTrue:[
+ ^ 0
+ ].
+ ^ stop - start // step + 1
+!
+
+at:index
+ (index between:1 and:self size) ifTrue:[
+ ^ start + (step * (index - 1))
+ ].
+ self errorSubscriptBounds:index
+!
+
+at:index put:anObject
+ self error:'you cannot store into an interval'
+! !
+
+!Interval methodsFor:'adding/removing elements'!
+
+add:newObject
+ self error:'elements cannot be added to an interval'
+!
+
+remove:anObject
+ self error:'elements cannot be removed from an interval'
+! !
+
+!Interval methodsFor:'private'!
+
+species
+ ^ OrderedCollection
+! !
+
+!Interval methodsFor:'enumeration'!
+
+do:aBlock
+ |aValue|
+
+ aValue := start.
+ step < 0 ifTrue:[
+ [stop <= aValue] whileTrue:[
+ aBlock value:aValue.
+ aValue := aValue + step
+ ]
+ ] ifFalse:[
+ [stop >= aValue] whileTrue:[
+ aBlock value:aValue.
+ aValue := aValue + step
+ ]
+ ]
+!
+
+select:aBlock
+ "evaluate the argument, aBlock for every element in the collection
+ and return a collection of all elements for which the block return
+ true. redefined since SeqColl accesses the receiver with at:, which is
+ slow for intervals."
+
+ |newColl|
+
+ newColl := OrderedCollection new:self size.
+ self do:[:each |
+ (aBlock value:each) ifTrue:[newColl add:each]
+ ].
+ ^ newColl
+!
+
+collect:aBlock
+ "evaluate the argument, aBlock for every element in the collection
+ and return a collection of the results - redefined since SeqColl
+ accesses the receiver via at:, which is slow for intervals"
+
+ |newCollection|
+
+ newCollection := self species new:(self size).
+ self do:[:each |
+ newCollection add:(aBlock value:each)
+ ].
+ ^ newCollection
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LargeInt.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,723 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Integer subclass:#LargeInteger
+ instanceVariableNames:'sign digitArray'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Magnitude-Numbers'
+!
+
+LargeInteger comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+This class provides arbitrary precision integers. These are represented as:
+ sign (-1/0/+1) and, if sign ~~ 0
+ an Array of digits with 4 digits per element;
+ least significant 4 digits at index 1 ...
+
+This is definitely not a good (fast) implementation -
+but ok for now, since LargeIntegers are not used very often.
+It will be reimplemented when everything else runs fine and a need arises
+(or someone rewrites it and sends me the code :-).
+
+%W% %E%
+'!
+
+!LargeInteger class methodsFor:'instance creation'!
+
+new:numberOfDigits
+ "catch creation message"
+
+ self error:'LargeIntegers cannot be created with new'
+!
+
+new
+ "catch creation message"
+
+ self error:'LargeIntegers cannot be created with new'
+!
+
+value:aSmallInteger
+ "create and return a new LargeInteger with value taken from
+ the argument, aSmallInteger"
+
+ ^ self basicNew value:aSmallInteger
+
+ "LargeInteger value:3689"
+!
+
+valueLow:lowBits hi:hiBits
+ "create and return a new LargeInteger with value taken from
+ the two 16-bit args"
+
+ hiBits < 0 ifTrue:[
+ ^ ((self value:hiBits negated) * 16r10000 + lowBits) negated
+ ].
+ ^ (self value:hiBits) * 16r10000 + lowBits
+! !
+
+
+!LargeInteger methodsFor:'coercing & converting'!
+
+coerce:aNumber
+ "return the argument as a LargeInteger"
+
+ ^ aNumber asLargeInteger
+!
+
+asLargeInteger
+ "return a LargeInteger with same value as myself - thats me"
+
+ ^ self
+!
+
+asSmallInteger
+ "return a SmallInteger with same value as myself - the result
+ is invalid if the receivers value cannot be represented
+ as a SmallInteger"
+
+ |value|
+
+ value := 0.
+ (sign == 0) ifFalse:[
+ digitArray reverseDo:[:aDigit |
+ value := (value times:10000) + aDigit
+ ].
+ (sign < 0) ifTrue:[
+ value := value negated
+ ]
+ ].
+ ^ value
+!
+
+asFloat
+ "return a Float with same value as myself"
+
+ |newFloat|
+
+ newFloat := 0.0.
+ (sign == 0) ifFalse:[
+ digitArray reverseDo:[:aDigit |
+ newFloat := (newFloat * 10000.0) + aDigit asFloat
+ ].
+ (sign < 0) ifTrue:[
+ newFloat := newFloat negated
+ ]
+ ].
+ ^ newFloat
+!
+
+value:aSmallInteger
+ "return a new LargeInteger with value taken from a SmallInteger"
+
+ |absValue
+ index "{ Class: SmallInteger }"|
+
+ (aSmallInteger == 0) ifTrue: [
+ digitArray := nil.
+ sign := 0.
+ ^ self
+ ].
+ (aSmallInteger < 0) ifTrue: [
+ sign := -1.
+ absValue := aSmallInteger negated
+ ] ifFalse: [
+ sign := 1.
+ absValue := aSmallInteger
+ ].
+ digitArray := Array new:3.
+ index := 1.
+ [absValue == 0] whileFalse: [
+ digitArray at:index put:(absValue \\ 10000).
+ absValue := absValue // 10000.
+ index := index + 1
+ ].
+ [index <= 3] whileTrue:[
+ digitArray at:index put:0.
+ index := index + 1
+ ]
+! !
+
+!LargeInteger methodsFor:'comparing'!
+
+= aNumber
+ "return true, if the argument, aNumber has the same value as
+ the receiver"
+
+ (aNumber class == LargeInteger) ifFalse:[
+ aNumber respondsToArithmetic ifFalse:[ ^ false ].
+ ^ self retry:#= coercing:aNumber
+ ].
+ (aNumber sign == sign) ifFalse:[^ false].
+ ^ self absEq:aNumber
+!
+
+< aNumber
+ "return true, if the argument, aNumber is greater than the receiver"
+
+ |otherSign|
+
+ (aNumber class == LargeInteger) ifFalse:[
+ ^ self retry:#< coercing:aNumber
+ ].
+ otherSign := aNumber sign.
+
+ (sign > 0) ifTrue:[
+ "I am positive"
+ (otherSign > 0) ifTrue:[^ self absLess:aNumber].
+ ^ false "aNumber is <= 0"
+ ].
+ (sign == 0) ifTrue:[
+ (otherSign > 0) ifTrue:[^ true].
+ ^ false
+ ].
+ "I am negative"
+ (otherSign > 0) ifTrue:[^ true].
+ (otherSign == 0) ifTrue:[^ true].
+ ^ (self absLess:aNumber) not
+! !
+
+!LargeInteger methodsFor:'arithmetic'!
+
++ aNumber
+ "return the sum of the receiver and the argument, aNumber"
+
+ |otherSign|
+
+ (aNumber class == LargeInteger) ifFalse:[
+ ^ self retry:#+ coercing:aNumber
+ ].
+ otherSign := aNumber sign.
+
+ (sign > 0) ifTrue:[
+ "I am positive"
+ (otherSign > 0) ifTrue:[^ self absPlus:aNumber].
+ (otherSign < 0) ifTrue:[^ self absMinus:aNumber].
+ ^ self
+ ].
+ (sign == 0) ifTrue:[^ aNumber].
+ "I am negative"
+ (otherSign > 0) ifTrue:[^ aNumber absMinus:self].
+ (otherSign < 0) ifTrue:[^ (self absPlus:aNumber) negated].
+ ^ self
+!
+
+- aNumber
+ "return the difference of the receiver and the argument, aNumber"
+
+ |otherSign|
+
+ (aNumber class == LargeInteger) ifFalse:[
+ ^ self retry:#- coercing:aNumber
+ ].
+ otherSign := aNumber sign.
+ (sign > 0) ifTrue:[
+ "I am positive"
+ (otherSign > 0) ifTrue:[^ self absMinus:aNumber].
+ (otherSign < 0) ifTrue:[^ self absPlus:aNumber].
+ ^ self
+ ].
+ (sign == 0) ifTrue:[^ aNumber negated].
+ "I am negative"
+ (otherSign > 0) ifTrue:[^ (self absPlus:aNumber) negated].
+ (otherSign < 0) ifTrue:[^ (self absMinus:aNumber) negated].
+ ^ self
+!
+
+* aNumber
+ "return the product of the receiver and the argument, aNumber"
+
+ |otherSign|
+
+ (aNumber = 10) ifTrue:[
+ ^ self deepCopy mul10
+ ].
+ (aNumber class == LargeInteger) ifFalse:[
+ ^ self retry:#* coercing:aNumber
+ ].
+ otherSign := aNumber sign.
+
+ (sign == 0) ifTrue:[^ 0].
+ (sign == otherSign) ifTrue:[^ self absMul:aNumber].
+ (otherSign == 0) ifTrue:[^ 0].
+ ^ (self absMul:aNumber) negated
+!
+
+/ aNumber
+ "this is a q&d hack - we loose lots of precision here ..."
+
+ ^ (self asFloat / aNumber asFloat)
+!
+
+// aNumber
+ "return the quotient of the receiver and the argument, aNumber"
+
+ |otherSign|
+
+ (aNumber = 10) ifTrue:[
+ ^ self deepCopy div10
+ ].
+ (aNumber class == LargeInteger) ifFalse:[
+ ^ self retry:#// coercing:aNumber
+ ].
+ otherSign := aNumber sign.
+
+ sign < 0 ifTrue:[
+ (sign == otherSign) ifTrue:[^ (self negated absDiv:aNumber negated) at:1].
+ ^ ((self negated absDiv:aNumber) at:1) negated
+ ].
+ (sign == otherSign) ifTrue:[^ (self absDiv:aNumber) at:1].
+ ^ ((self absDiv:aNumber negated) at:1) negated
+!
+
+\\ aNumber
+ "return the remainder of division of the receiver by the argument, aNumber"
+
+ |otherSign|
+
+ (aNumber class == LargeInteger) ifFalse:[
+ ^ self retry:#\\ coercing:aNumber
+ ].
+
+ otherSign := aNumber sign.
+ sign < 0 ifTrue:[
+ (sign == otherSign) ifTrue:[^ (self negated absDiv:aNumber negated) at:2].
+ ^ ((self negated absDiv:aNumber) at:2) negated
+ ].
+ (sign == otherSign) ifTrue:[^ (self absDiv:aNumber) at:2].
+ ^ ((self absDiv:aNumber negated) at:2) negated
+!
+
+negated
+ "return a LargeInteger with value negated from receivers value"
+
+ |newNumber|
+
+ (sign == 0) ifTrue:[^ 0].
+ newNumber := self shallowCopy.
+ newNumber sign:(sign negated).
+ ^ newNumber
+! !
+
+!LargeInteger methodsFor:'bit operations'!
+
+bitAnd:anInteger
+ "q & d hack to make Random work;
+ this works only correctly, if my value can be represented
+ as a SmallInteger"
+
+ ^ self asSmallInteger bitAnd:anInteger
+! !
+
+!LargeInteger methodsFor:'testing'!
+
+sign
+ "return the sign of the receiver"
+
+ ^ sign
+!
+
+odd
+ "return true if the receiver is odd"
+
+ ^ (digitArray at:1) even
+!
+
+even
+ "return true if the receiver is even"
+
+ ^ (digitArray at:1) even
+!
+
+negative
+ "return true, if the receiver is < 0"
+
+ ^ (sign < 0)
+!
+
+positive
+ "return true, if the receiver is >= 0"
+
+ ^ (sign >= 0)
+!
+
+strictlyPositive
+ "return true, if the receiver is > 0"
+
+ ^ (sign > 0)
+! !
+
+!LargeInteger methodsFor:'private'!
+
+absEq:aLargeInteger
+ "return true, if abs(self) = abs(theArgument)"
+
+ |len1 "{ Class: SmallInteger }"
+ len2 "{ Class: SmallInteger }"
+ d1 "{ Class: SmallInteger }"
+ d2 "{ Class: SmallInteger }"
+ otherDigits |
+
+ len1 := digitArray size.
+ otherDigits := aLargeInteger digits.
+ len2 := otherDigits size.
+
+ [(digitArray basicAt:len1) == 0] whileTrue:[
+ len1 := len1 - 1
+ ].
+ [(otherDigits basicAt:len2) == 0] whileTrue:[
+ len2 := len2 - 1
+ ].
+ (len1 ~~ len2) ifTrue:[^ false].
+ [len1 > 0] whileTrue:[
+ d1 := digitArray basicAt:len1.
+ d2 := otherDigits basicAt:len1.
+ (d1 ~~ d2) ifTrue:[^ false].
+ len1 := len1 - 1
+ ].
+ ^ true
+!
+
+absLess:aLargeInteger
+ "return true, if abs(self) < abs(theArgument)"
+
+ |len1 "{ Class: SmallInteger }"
+ len2 "{ Class: SmallInteger }"
+ d1 "{ Class: SmallInteger }"
+ d2 "{ Class: SmallInteger }"
+ otherDigits |
+
+ len1 := digitArray size.
+ otherDigits := aLargeInteger digits.
+ len2 := otherDigits size.
+
+ [(digitArray basicAt:len1) == 0] whileTrue:[
+ len1 := len1 - 1
+ ].
+ [(otherDigits basicAt:len2) == 0] whileTrue:[
+ len2 := len2 - 1
+ ].
+ (len1 < len2) ifTrue:[^ true].
+ (len1 > len2) ifTrue:[^ false].
+
+ [len1 > 0] whileTrue:[
+ d1 := digitArray basicAt:len1.
+ d2 := otherDigits basicAt:len1.
+ (d1 < d2) ifTrue:[^ true].
+ (d1 > d2) ifTrue:[^ false].
+ len1 := len1 - 1
+ ].
+ ^ false
+!
+
+absPlus:aLargeInteger
+ "return a LargeInteger representing abs(self) + abs(theArgument)"
+
+ |result done otherDigits resultDigits
+ len1 "{ Class: SmallInteger }"
+ len2 "{ Class: SmallInteger }"
+ index "{ Class: SmallInteger }"
+ carry "{ Class: SmallInteger }"
+ sum "{ Class: SmallInteger }" |
+
+ len1 := digitArray size.
+ otherDigits := aLargeInteger digits.
+ len2 := otherDigits size.
+
+ result := LargeInteger basicNew
+ numberOfDigits:((len1 max: len2) + 1).
+ result sign:1.
+ resultDigits := result digits.
+
+ index := 1.
+ carry := 0.
+
+ done := false.
+ [done] whileFalse:[
+ sum := carry.
+ (index <= len1) ifTrue:[
+ sum := sum + (digitArray basicAt:index).
+ (index <= len2) ifTrue:[
+ sum := sum + (otherDigits basicAt:index)
+ ]
+ ] ifFalse:[
+ (index <= len2) ifTrue:[
+ sum := sum + (otherDigits basicAt:index)
+ ] ifFalse:[
+ "end reached"
+ done := true
+ ]
+ ].
+ (sum > 9999) ifTrue:[
+ carry := 1.
+ sum := sum - 10000
+ ] ifFalse:[
+ carry := 0
+ ].
+ resultDigits basicAt:index put:sum.
+ index := index + 1
+ ].
+ ^ result normalize
+!
+
+absMinus:aLargeInteger
+ "return a LargeInteger representing abs(self) - abs(theArgument)"
+
+ |result done
+ otherDigits resultDigits
+ len1 "{ Class: SmallInteger }"
+ len2 "{ Class: SmallInteger }"
+ index "{ Class: SmallInteger }"
+ borrow "{ Class: SmallInteger }"
+ diff "{ Class: SmallInteger }"
+ sum "{ Class: SmallInteger }"
+ carry "{ Class: SmallInteger }" |
+
+ len1 := digitArray size.
+ otherDigits := aLargeInteger digits.
+ len2 := otherDigits size.
+
+ result := LargeInteger basicNew
+ numberOfDigits:((len1 max: len2) + 1).
+ result sign:1.
+ resultDigits := result digits.
+
+ index := 1.
+ borrow := 0.
+
+ done := false.
+ [done] whileFalse:[
+ diff := borrow.
+ (index <= len1) ifTrue:[
+ diff := diff + (digitArray basicAt:index).
+ (index <= len2) ifTrue:[
+ diff := diff - (otherDigits basicAt:index)
+ ]
+ ] ifFalse:[
+ (index <= len2) ifTrue:[
+ diff := diff - (otherDigits basicAt:index)
+ ] ifFalse:[
+ "end reached"
+ done := true
+ ]
+ ].
+ (diff < 0) ifTrue:[
+ borrow := -1.
+ diff := diff + 10000
+ ] ifFalse:[
+ borrow := 0
+ ].
+ resultDigits basicAt:index put:diff.
+ index := index + 1
+ ].
+ (borrow ~~ 0) ifTrue:[
+ result sign: -1.
+ carry := 0.
+ 1 to:(index - 1) do:[:i |
+ sum := ((resultDigits at:i) + carry - 10000) negated.
+ resultDigits at:i put:sum.
+ carry := 1
+ ]
+ ].
+ ^ result normalize
+!
+
+absMul:aLargeInteger
+ "return a LargeInteger representing abs(self) * abs(theArgument)"
+
+ |result otherDigits resultDigits
+ len1 "{ Class: SmallInteger }"
+ len2 "{ Class: SmallInteger }"
+ dstIndex "{ Class: SmallInteger }"
+ carry "{ Class: SmallInteger }"
+ prod "{ Class: SmallInteger }" |
+
+ len1 := digitArray size.
+ otherDigits := aLargeInteger digits.
+ len2 := otherDigits size.
+
+ result := LargeInteger basicNew numberOfDigits:(len1 + len2 + 1).
+ result sign:1.
+ resultDigits := result digits.
+
+ "clear result"
+ resultDigits atAllPut:0.
+
+ 1 to:len1 do:[:index1 |
+ 1 to:len2 do:[:index2 |
+ dstIndex := index1 + index2 - 1.
+ prod := (digitArray basicAt:index1) * (otherDigits basicAt:index2).
+ prod := prod + (resultDigits basicAt:dstIndex).
+ resultDigits basicAt:dstIndex put:(prod \\ 10000).
+ carry := prod // 10000.
+ (carry ~~ 0) ifTrue:[
+ resultDigits basicAt:(dstIndex + 1)
+ put:(resultDigits basicAt:(dstIndex + 1)) + carry
+ ]
+ ]
+ ].
+ ^ result normalize
+!
+
+absDiv:anInteger
+ "return an array with two LargeIntegers representing
+ abs(self) // abs(theArgument) and abs(self) \\ abs(theArgument)"
+
+ |tmp1 tmp2
+ rem
+ count "{ Class: SmallInteger }"
+ digit "{ Class: SmallInteger }" |
+
+ self == 0 ifTrue:[^ 0].
+ anInteger == 0 ifTrue:[^ self divideByZeroError].
+
+ self < anInteger ifTrue:[
+ ^ Array with:0 with:self
+ ].
+
+ tmp1 := self deepCopy.
+ tmp2 := anInteger deepCopy.
+ count := 0.
+ [tmp2 < tmp1] whileTrue:[
+ tmp2 mul10.
+ count := count + 1
+ ].
+
+ tmp2 div10.
+
+ rem := 0 asLargeInteger.
+ [count == 0] whileFalse:[
+ digit := 0.
+ [tmp1 >= tmp2] whileTrue:[
+ digit := digit + 1.
+ tmp1 := tmp1 - tmp2
+ ].
+ rem := rem * 10 + digit.
+ tmp2 div10.
+ count := count - 1
+ ].
+ ^ Array with:rem with:tmp1
+!
+
+mul10
+ "destructively multiply the receiver by 10.
+ private - used for division only"
+
+ |carry "{ Class: SmallInteger }"
+ prod "{ Class: SmallInteger }"|
+
+ carry := 0.
+ 1 to:(digitArray size) do:[:index |
+ prod := (digitArray at:index) * 10 + carry.
+ digitArray at:index put:prod \\ 10000.
+ carry := prod // 10000
+ ].
+ carry ~~ 0 ifTrue:[
+ digitArray := digitArray copyWith:carry
+ ]
+!
+
+div10
+ "destructively divide the receiver by 10.
+ private - used for division only"
+
+ |nDigits|
+
+ nDigits := digitArray size.
+ 1 to:(nDigits - 1) do:[:index |
+ digitArray at:index put:((digitArray at:index) // 10
+ + ((digitArray at:index + 1) \\ 10 * 1000))
+ ].
+ digitArray at:nDigits put:(digitArray at:nDigits) // 10
+!
+
+normalize
+ "if the receiver can be represented as a SmallInteger, return
+ a SmallInteger with my value; otherwise return self with leading
+ zeros removed"
+
+ |index "{ Class: SmallInteger }" |
+
+ index := digitArray size.
+ [(index > 0) and:[(digitArray at:index) == 0]] whileTrue:[
+ index := index - 1
+ ].
+ (index == 1) ifTrue:[
+ ^ (digitArray at:1) * sign
+ ].
+ (index == 2) ifTrue:[
+ ^ ((digitArray at:2) * 10000 + (digitArray at:1)) * sign
+ ].
+ (index == 0) ifTrue:[
+ ^ 0
+ ].
+ (index ~~ digitArray size) ifTrue:[
+ digitArray := digitArray copyFrom:1 to:index
+ ].
+ ^ self
+!
+
+digits
+ ^ digitArray
+!
+
+numberOfDigits
+ ^ digitArray size
+!
+
+numberOfDigits:nDigits
+ digitArray := Array new:nDigits
+!
+
+sign:aNumber
+ sign := aNumber
+! !
+
+!LargeInteger methodsFor:'printing & storing'!
+
+storeString
+ "return a string representation of the receiver, which can be
+ used to reconstruct the receiver"
+
+ ^ self printString , ' asLargeInteger'
+!
+
+printString
+ |aString index fourDigits n|
+
+ index := digitArray size.
+ [(index > 1) and:[(digitArray at:index) == 0]] whileTrue:[
+ index := index - 1
+ ].
+ (sign == 0) ifTrue: [^ '0'].
+ (sign == -1) ifTrue: [
+ aString := '-'
+ ] ifFalse: [
+ aString := ''
+ ].
+
+ aString := aString , (digitArray basicAt:index) printString.
+ index := index - 1.
+ [index > 0] whileTrue:[
+ fourDigits := (digitArray basicAt:index) printString.
+ n := fourDigits size.
+ (n < 4) ifTrue:[
+ aString := aString , ('000' copyFrom:n to:3)
+ ].
+ aString := aString , fourDigits.
+ index := index - 1
+ ].
+ ^ aString
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LargeInteger.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,723 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Integer subclass:#LargeInteger
+ instanceVariableNames:'sign digitArray'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Magnitude-Numbers'
+!
+
+LargeInteger comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+This class provides arbitrary precision integers. These are represented as:
+ sign (-1/0/+1) and, if sign ~~ 0
+ an Array of digits with 4 digits per element;
+ least significant 4 digits at index 1 ...
+
+This is definitely not a good (fast) implementation -
+but ok for now, since LargeIntegers are not used very often.
+It will be reimplemented when everything else runs fine and a need arises
+(or someone rewrites it and sends me the code :-).
+
+%W% %E%
+'!
+
+!LargeInteger class methodsFor:'instance creation'!
+
+new:numberOfDigits
+ "catch creation message"
+
+ self error:'LargeIntegers cannot be created with new'
+!
+
+new
+ "catch creation message"
+
+ self error:'LargeIntegers cannot be created with new'
+!
+
+value:aSmallInteger
+ "create and return a new LargeInteger with value taken from
+ the argument, aSmallInteger"
+
+ ^ self basicNew value:aSmallInteger
+
+ "LargeInteger value:3689"
+!
+
+valueLow:lowBits hi:hiBits
+ "create and return a new LargeInteger with value taken from
+ the two 16-bit args"
+
+ hiBits < 0 ifTrue:[
+ ^ ((self value:hiBits negated) * 16r10000 + lowBits) negated
+ ].
+ ^ (self value:hiBits) * 16r10000 + lowBits
+! !
+
+
+!LargeInteger methodsFor:'coercing & converting'!
+
+coerce:aNumber
+ "return the argument as a LargeInteger"
+
+ ^ aNumber asLargeInteger
+!
+
+asLargeInteger
+ "return a LargeInteger with same value as myself - thats me"
+
+ ^ self
+!
+
+asSmallInteger
+ "return a SmallInteger with same value as myself - the result
+ is invalid if the receivers value cannot be represented
+ as a SmallInteger"
+
+ |value|
+
+ value := 0.
+ (sign == 0) ifFalse:[
+ digitArray reverseDo:[:aDigit |
+ value := (value times:10000) + aDigit
+ ].
+ (sign < 0) ifTrue:[
+ value := value negated
+ ]
+ ].
+ ^ value
+!
+
+asFloat
+ "return a Float with same value as myself"
+
+ |newFloat|
+
+ newFloat := 0.0.
+ (sign == 0) ifFalse:[
+ digitArray reverseDo:[:aDigit |
+ newFloat := (newFloat * 10000.0) + aDigit asFloat
+ ].
+ (sign < 0) ifTrue:[
+ newFloat := newFloat negated
+ ]
+ ].
+ ^ newFloat
+!
+
+value:aSmallInteger
+ "return a new LargeInteger with value taken from a SmallInteger"
+
+ |absValue
+ index "{ Class: SmallInteger }"|
+
+ (aSmallInteger == 0) ifTrue: [
+ digitArray := nil.
+ sign := 0.
+ ^ self
+ ].
+ (aSmallInteger < 0) ifTrue: [
+ sign := -1.
+ absValue := aSmallInteger negated
+ ] ifFalse: [
+ sign := 1.
+ absValue := aSmallInteger
+ ].
+ digitArray := Array new:3.
+ index := 1.
+ [absValue == 0] whileFalse: [
+ digitArray at:index put:(absValue \\ 10000).
+ absValue := absValue // 10000.
+ index := index + 1
+ ].
+ [index <= 3] whileTrue:[
+ digitArray at:index put:0.
+ index := index + 1
+ ]
+! !
+
+!LargeInteger methodsFor:'comparing'!
+
+= aNumber
+ "return true, if the argument, aNumber has the same value as
+ the receiver"
+
+ (aNumber class == LargeInteger) ifFalse:[
+ aNumber respondsToArithmetic ifFalse:[ ^ false ].
+ ^ self retry:#= coercing:aNumber
+ ].
+ (aNumber sign == sign) ifFalse:[^ false].
+ ^ self absEq:aNumber
+!
+
+< aNumber
+ "return true, if the argument, aNumber is greater than the receiver"
+
+ |otherSign|
+
+ (aNumber class == LargeInteger) ifFalse:[
+ ^ self retry:#< coercing:aNumber
+ ].
+ otherSign := aNumber sign.
+
+ (sign > 0) ifTrue:[
+ "I am positive"
+ (otherSign > 0) ifTrue:[^ self absLess:aNumber].
+ ^ false "aNumber is <= 0"
+ ].
+ (sign == 0) ifTrue:[
+ (otherSign > 0) ifTrue:[^ true].
+ ^ false
+ ].
+ "I am negative"
+ (otherSign > 0) ifTrue:[^ true].
+ (otherSign == 0) ifTrue:[^ true].
+ ^ (self absLess:aNumber) not
+! !
+
+!LargeInteger methodsFor:'arithmetic'!
+
++ aNumber
+ "return the sum of the receiver and the argument, aNumber"
+
+ |otherSign|
+
+ (aNumber class == LargeInteger) ifFalse:[
+ ^ self retry:#+ coercing:aNumber
+ ].
+ otherSign := aNumber sign.
+
+ (sign > 0) ifTrue:[
+ "I am positive"
+ (otherSign > 0) ifTrue:[^ self absPlus:aNumber].
+ (otherSign < 0) ifTrue:[^ self absMinus:aNumber].
+ ^ self
+ ].
+ (sign == 0) ifTrue:[^ aNumber].
+ "I am negative"
+ (otherSign > 0) ifTrue:[^ aNumber absMinus:self].
+ (otherSign < 0) ifTrue:[^ (self absPlus:aNumber) negated].
+ ^ self
+!
+
+- aNumber
+ "return the difference of the receiver and the argument, aNumber"
+
+ |otherSign|
+
+ (aNumber class == LargeInteger) ifFalse:[
+ ^ self retry:#- coercing:aNumber
+ ].
+ otherSign := aNumber sign.
+ (sign > 0) ifTrue:[
+ "I am positive"
+ (otherSign > 0) ifTrue:[^ self absMinus:aNumber].
+ (otherSign < 0) ifTrue:[^ self absPlus:aNumber].
+ ^ self
+ ].
+ (sign == 0) ifTrue:[^ aNumber negated].
+ "I am negative"
+ (otherSign > 0) ifTrue:[^ (self absPlus:aNumber) negated].
+ (otherSign < 0) ifTrue:[^ (self absMinus:aNumber) negated].
+ ^ self
+!
+
+* aNumber
+ "return the product of the receiver and the argument, aNumber"
+
+ |otherSign|
+
+ (aNumber = 10) ifTrue:[
+ ^ self deepCopy mul10
+ ].
+ (aNumber class == LargeInteger) ifFalse:[
+ ^ self retry:#* coercing:aNumber
+ ].
+ otherSign := aNumber sign.
+
+ (sign == 0) ifTrue:[^ 0].
+ (sign == otherSign) ifTrue:[^ self absMul:aNumber].
+ (otherSign == 0) ifTrue:[^ 0].
+ ^ (self absMul:aNumber) negated
+!
+
+/ aNumber
+ "this is a q&d hack - we loose lots of precision here ..."
+
+ ^ (self asFloat / aNumber asFloat)
+!
+
+// aNumber
+ "return the quotient of the receiver and the argument, aNumber"
+
+ |otherSign|
+
+ (aNumber = 10) ifTrue:[
+ ^ self deepCopy div10
+ ].
+ (aNumber class == LargeInteger) ifFalse:[
+ ^ self retry:#// coercing:aNumber
+ ].
+ otherSign := aNumber sign.
+
+ sign < 0 ifTrue:[
+ (sign == otherSign) ifTrue:[^ (self negated absDiv:aNumber negated) at:1].
+ ^ ((self negated absDiv:aNumber) at:1) negated
+ ].
+ (sign == otherSign) ifTrue:[^ (self absDiv:aNumber) at:1].
+ ^ ((self absDiv:aNumber negated) at:1) negated
+!
+
+\\ aNumber
+ "return the remainder of division of the receiver by the argument, aNumber"
+
+ |otherSign|
+
+ (aNumber class == LargeInteger) ifFalse:[
+ ^ self retry:#\\ coercing:aNumber
+ ].
+
+ otherSign := aNumber sign.
+ sign < 0 ifTrue:[
+ (sign == otherSign) ifTrue:[^ (self negated absDiv:aNumber negated) at:2].
+ ^ ((self negated absDiv:aNumber) at:2) negated
+ ].
+ (sign == otherSign) ifTrue:[^ (self absDiv:aNumber) at:2].
+ ^ ((self absDiv:aNumber negated) at:2) negated
+!
+
+negated
+ "return a LargeInteger with value negated from receivers value"
+
+ |newNumber|
+
+ (sign == 0) ifTrue:[^ 0].
+ newNumber := self shallowCopy.
+ newNumber sign:(sign negated).
+ ^ newNumber
+! !
+
+!LargeInteger methodsFor:'bit operations'!
+
+bitAnd:anInteger
+ "q & d hack to make Random work;
+ this works only correctly, if my value can be represented
+ as a SmallInteger"
+
+ ^ self asSmallInteger bitAnd:anInteger
+! !
+
+!LargeInteger methodsFor:'testing'!
+
+sign
+ "return the sign of the receiver"
+
+ ^ sign
+!
+
+odd
+ "return true if the receiver is odd"
+
+ ^ (digitArray at:1) even
+!
+
+even
+ "return true if the receiver is even"
+
+ ^ (digitArray at:1) even
+!
+
+negative
+ "return true, if the receiver is < 0"
+
+ ^ (sign < 0)
+!
+
+positive
+ "return true, if the receiver is >= 0"
+
+ ^ (sign >= 0)
+!
+
+strictlyPositive
+ "return true, if the receiver is > 0"
+
+ ^ (sign > 0)
+! !
+
+!LargeInteger methodsFor:'private'!
+
+absEq:aLargeInteger
+ "return true, if abs(self) = abs(theArgument)"
+
+ |len1 "{ Class: SmallInteger }"
+ len2 "{ Class: SmallInteger }"
+ d1 "{ Class: SmallInteger }"
+ d2 "{ Class: SmallInteger }"
+ otherDigits |
+
+ len1 := digitArray size.
+ otherDigits := aLargeInteger digits.
+ len2 := otherDigits size.
+
+ [(digitArray basicAt:len1) == 0] whileTrue:[
+ len1 := len1 - 1
+ ].
+ [(otherDigits basicAt:len2) == 0] whileTrue:[
+ len2 := len2 - 1
+ ].
+ (len1 ~~ len2) ifTrue:[^ false].
+ [len1 > 0] whileTrue:[
+ d1 := digitArray basicAt:len1.
+ d2 := otherDigits basicAt:len1.
+ (d1 ~~ d2) ifTrue:[^ false].
+ len1 := len1 - 1
+ ].
+ ^ true
+!
+
+absLess:aLargeInteger
+ "return true, if abs(self) < abs(theArgument)"
+
+ |len1 "{ Class: SmallInteger }"
+ len2 "{ Class: SmallInteger }"
+ d1 "{ Class: SmallInteger }"
+ d2 "{ Class: SmallInteger }"
+ otherDigits |
+
+ len1 := digitArray size.
+ otherDigits := aLargeInteger digits.
+ len2 := otherDigits size.
+
+ [(digitArray basicAt:len1) == 0] whileTrue:[
+ len1 := len1 - 1
+ ].
+ [(otherDigits basicAt:len2) == 0] whileTrue:[
+ len2 := len2 - 1
+ ].
+ (len1 < len2) ifTrue:[^ true].
+ (len1 > len2) ifTrue:[^ false].
+
+ [len1 > 0] whileTrue:[
+ d1 := digitArray basicAt:len1.
+ d2 := otherDigits basicAt:len1.
+ (d1 < d2) ifTrue:[^ true].
+ (d1 > d2) ifTrue:[^ false].
+ len1 := len1 - 1
+ ].
+ ^ false
+!
+
+absPlus:aLargeInteger
+ "return a LargeInteger representing abs(self) + abs(theArgument)"
+
+ |result done otherDigits resultDigits
+ len1 "{ Class: SmallInteger }"
+ len2 "{ Class: SmallInteger }"
+ index "{ Class: SmallInteger }"
+ carry "{ Class: SmallInteger }"
+ sum "{ Class: SmallInteger }" |
+
+ len1 := digitArray size.
+ otherDigits := aLargeInteger digits.
+ len2 := otherDigits size.
+
+ result := LargeInteger basicNew
+ numberOfDigits:((len1 max: len2) + 1).
+ result sign:1.
+ resultDigits := result digits.
+
+ index := 1.
+ carry := 0.
+
+ done := false.
+ [done] whileFalse:[
+ sum := carry.
+ (index <= len1) ifTrue:[
+ sum := sum + (digitArray basicAt:index).
+ (index <= len2) ifTrue:[
+ sum := sum + (otherDigits basicAt:index)
+ ]
+ ] ifFalse:[
+ (index <= len2) ifTrue:[
+ sum := sum + (otherDigits basicAt:index)
+ ] ifFalse:[
+ "end reached"
+ done := true
+ ]
+ ].
+ (sum > 9999) ifTrue:[
+ carry := 1.
+ sum := sum - 10000
+ ] ifFalse:[
+ carry := 0
+ ].
+ resultDigits basicAt:index put:sum.
+ index := index + 1
+ ].
+ ^ result normalize
+!
+
+absMinus:aLargeInteger
+ "return a LargeInteger representing abs(self) - abs(theArgument)"
+
+ |result done
+ otherDigits resultDigits
+ len1 "{ Class: SmallInteger }"
+ len2 "{ Class: SmallInteger }"
+ index "{ Class: SmallInteger }"
+ borrow "{ Class: SmallInteger }"
+ diff "{ Class: SmallInteger }"
+ sum "{ Class: SmallInteger }"
+ carry "{ Class: SmallInteger }" |
+
+ len1 := digitArray size.
+ otherDigits := aLargeInteger digits.
+ len2 := otherDigits size.
+
+ result := LargeInteger basicNew
+ numberOfDigits:((len1 max: len2) + 1).
+ result sign:1.
+ resultDigits := result digits.
+
+ index := 1.
+ borrow := 0.
+
+ done := false.
+ [done] whileFalse:[
+ diff := borrow.
+ (index <= len1) ifTrue:[
+ diff := diff + (digitArray basicAt:index).
+ (index <= len2) ifTrue:[
+ diff := diff - (otherDigits basicAt:index)
+ ]
+ ] ifFalse:[
+ (index <= len2) ifTrue:[
+ diff := diff - (otherDigits basicAt:index)
+ ] ifFalse:[
+ "end reached"
+ done := true
+ ]
+ ].
+ (diff < 0) ifTrue:[
+ borrow := -1.
+ diff := diff + 10000
+ ] ifFalse:[
+ borrow := 0
+ ].
+ resultDigits basicAt:index put:diff.
+ index := index + 1
+ ].
+ (borrow ~~ 0) ifTrue:[
+ result sign: -1.
+ carry := 0.
+ 1 to:(index - 1) do:[:i |
+ sum := ((resultDigits at:i) + carry - 10000) negated.
+ resultDigits at:i put:sum.
+ carry := 1
+ ]
+ ].
+ ^ result normalize
+!
+
+absMul:aLargeInteger
+ "return a LargeInteger representing abs(self) * abs(theArgument)"
+
+ |result otherDigits resultDigits
+ len1 "{ Class: SmallInteger }"
+ len2 "{ Class: SmallInteger }"
+ dstIndex "{ Class: SmallInteger }"
+ carry "{ Class: SmallInteger }"
+ prod "{ Class: SmallInteger }" |
+
+ len1 := digitArray size.
+ otherDigits := aLargeInteger digits.
+ len2 := otherDigits size.
+
+ result := LargeInteger basicNew numberOfDigits:(len1 + len2 + 1).
+ result sign:1.
+ resultDigits := result digits.
+
+ "clear result"
+ resultDigits atAllPut:0.
+
+ 1 to:len1 do:[:index1 |
+ 1 to:len2 do:[:index2 |
+ dstIndex := index1 + index2 - 1.
+ prod := (digitArray basicAt:index1) * (otherDigits basicAt:index2).
+ prod := prod + (resultDigits basicAt:dstIndex).
+ resultDigits basicAt:dstIndex put:(prod \\ 10000).
+ carry := prod // 10000.
+ (carry ~~ 0) ifTrue:[
+ resultDigits basicAt:(dstIndex + 1)
+ put:(resultDigits basicAt:(dstIndex + 1)) + carry
+ ]
+ ]
+ ].
+ ^ result normalize
+!
+
+absDiv:anInteger
+ "return an array with two LargeIntegers representing
+ abs(self) // abs(theArgument) and abs(self) \\ abs(theArgument)"
+
+ |tmp1 tmp2
+ rem
+ count "{ Class: SmallInteger }"
+ digit "{ Class: SmallInteger }" |
+
+ self == 0 ifTrue:[^ 0].
+ anInteger == 0 ifTrue:[^ self divideByZeroError].
+
+ self < anInteger ifTrue:[
+ ^ Array with:0 with:self
+ ].
+
+ tmp1 := self deepCopy.
+ tmp2 := anInteger deepCopy.
+ count := 0.
+ [tmp2 < tmp1] whileTrue:[
+ tmp2 mul10.
+ count := count + 1
+ ].
+
+ tmp2 div10.
+
+ rem := 0 asLargeInteger.
+ [count == 0] whileFalse:[
+ digit := 0.
+ [tmp1 >= tmp2] whileTrue:[
+ digit := digit + 1.
+ tmp1 := tmp1 - tmp2
+ ].
+ rem := rem * 10 + digit.
+ tmp2 div10.
+ count := count - 1
+ ].
+ ^ Array with:rem with:tmp1
+!
+
+mul10
+ "destructively multiply the receiver by 10.
+ private - used for division only"
+
+ |carry "{ Class: SmallInteger }"
+ prod "{ Class: SmallInteger }"|
+
+ carry := 0.
+ 1 to:(digitArray size) do:[:index |
+ prod := (digitArray at:index) * 10 + carry.
+ digitArray at:index put:prod \\ 10000.
+ carry := prod // 10000
+ ].
+ carry ~~ 0 ifTrue:[
+ digitArray := digitArray copyWith:carry
+ ]
+!
+
+div10
+ "destructively divide the receiver by 10.
+ private - used for division only"
+
+ |nDigits|
+
+ nDigits := digitArray size.
+ 1 to:(nDigits - 1) do:[:index |
+ digitArray at:index put:((digitArray at:index) // 10
+ + ((digitArray at:index + 1) \\ 10 * 1000))
+ ].
+ digitArray at:nDigits put:(digitArray at:nDigits) // 10
+!
+
+normalize
+ "if the receiver can be represented as a SmallInteger, return
+ a SmallInteger with my value; otherwise return self with leading
+ zeros removed"
+
+ |index "{ Class: SmallInteger }" |
+
+ index := digitArray size.
+ [(index > 0) and:[(digitArray at:index) == 0]] whileTrue:[
+ index := index - 1
+ ].
+ (index == 1) ifTrue:[
+ ^ (digitArray at:1) * sign
+ ].
+ (index == 2) ifTrue:[
+ ^ ((digitArray at:2) * 10000 + (digitArray at:1)) * sign
+ ].
+ (index == 0) ifTrue:[
+ ^ 0
+ ].
+ (index ~~ digitArray size) ifTrue:[
+ digitArray := digitArray copyFrom:1 to:index
+ ].
+ ^ self
+!
+
+digits
+ ^ digitArray
+!
+
+numberOfDigits
+ ^ digitArray size
+!
+
+numberOfDigits:nDigits
+ digitArray := Array new:nDigits
+!
+
+sign:aNumber
+ sign := aNumber
+! !
+
+!LargeInteger methodsFor:'printing & storing'!
+
+storeString
+ "return a string representation of the receiver, which can be
+ used to reconstruct the receiver"
+
+ ^ self printString , ' asLargeInteger'
+!
+
+printString
+ |aString index fourDigits n|
+
+ index := digitArray size.
+ [(index > 1) and:[(digitArray at:index) == 0]] whileTrue:[
+ index := index - 1
+ ].
+ (sign == 0) ifTrue: [^ '0'].
+ (sign == -1) ifTrue: [
+ aString := '-'
+ ] ifFalse: [
+ aString := ''
+ ].
+
+ aString := aString , (digitArray basicAt:index) printString.
+ index := index - 1.
+ [index > 0] whileTrue:[
+ fourDigits := (digitArray basicAt:index) printString.
+ n := fourDigits size.
+ (n < 4) ifTrue:[
+ aString := aString , ('000' copyFrom:n to:3)
+ ].
+ aString := aString , fourDigits.
+ index := index - 1
+ ].
+ ^ aString
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Link.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,44 @@
+"
+ COPYRIGHT (c) 1992-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Link
+ instanceVariableNames:'nextLink'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Support'
+!
+
+Link comment:'
+
+COPYRIGHT (c) 1992-93 by Claus Gittinger
+ All Rights Reserved
+
+this class provides the basic functionality for Link-nodes.
+Links are abstract in that they do not provide a place for storing something,
+just the link-chain. For more usability look at ValueLink.
+
+%W% %E%
+'!
+
+!Link methodsFor:'accessing'!
+
+nextLink
+ "return the next link"
+
+ ^ nextLink
+!
+
+nextLink:aLink
+ "set the next link"
+
+ nextLink := aLink
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LinkList.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,231 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#LinkedList
+ instanceVariableNames:'firstLink lastLink nodeClass numberOfNodes'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Sequenceable'
+!
+
+LinkedList comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+this class implements an anchor to a list of Links.
+The data itself is held in the Link elements (see Link and subclasses).
+
+%W% %E%
+'!
+
+!LinkedList class methodsFor:'instance creation'!
+
+new
+ "create and return a new LinkedList"
+
+ ^ super new initialize
+! !
+
+!LinkedList methodsFor:'ininialization'!
+
+initialize
+ numberOfNodes := 0
+! !
+
+!LinkedList methodsFor:'copying'!
+
+deepCopy
+ |newList|
+ newList := self shallowCopy.
+ newList setFirstNode:(firstLink deepCopy).
+ newList setLastNode:(firstLink last).
+ ^ newList
+! !
+
+!LinkedList methodsFor:'accessing'!
+
+setFirstNode:aNode
+ "set the first node to be the argument, aNode"
+
+ firstLink := aNode
+!
+
+setLastNode:aNode
+ "set the last node to be the argument, aNode"
+
+ lastLink := aNode
+!
+
+first
+ "return the first node in the list"
+
+ ^ firstLink
+!
+
+last
+ "return last node in the list"
+
+ ^ lastLink
+!
+
+size
+ "return the size of the LinkedList i.e. the number of nodes"
+
+ ^ numberOfNodes
+! !
+
+!LinkedList methodsFor:'testing'!
+
+includes:anObject
+ "return true, if some nodes contents is anObject"
+
+ |theNode|
+
+ theNode := firstLink.
+ [theNode notNil] whileTrue:[
+ (anObject = theNode) ifTrue:[^ true].
+ theNode := theNode nextLink
+ ].
+ ^ false
+! !
+
+!LinkedList methodsFor:'adding/removing elements'!
+
+addFirst:aLink
+ "adds aLink to the beginning of the sequence. Returns aLink"
+
+ firstLink isNil ifTrue:[
+ firstLink := aLink.
+ lastLink := aLink
+ ] ifFalse: [
+ aLink nextLink:firstLink.
+ firstLink := aLink
+ ].
+ numberOfNodes := numberOfNodes + 1.
+ ^ aLink
+!
+
+add:aLink
+ "adds aLink to the end of the sequence. Returns aLink"
+
+ aLink nextLink:nil.
+ lastLink isNil ifTrue:[
+ firstLink := aLink
+ ] ifFalse: [
+ lastLink nextLink:aLink
+ ].
+ lastLink := aLink.
+ numberOfNodes := numberOfNodes + 1.
+ ^ aLink
+!
+
+add:linkToAdd after:aLink
+ "adds linkToAdd after another link, aLink. If aLink is nil,
+ linkToAdd is inserted at the beginning. Returns linkToAdd."
+
+ |this|
+
+ aLink isNil ifTrue:[ ^ self addFirst:linkToAdd ].
+
+ this := firstLink.
+ [this notNil and:[this ~~ aLink]] whileTrue:[
+ this := this nextLink
+ ].
+ this isNil ifTrue:[ ^ self addLast:linkToAdd ].
+ linkToAdd nextLink:(this nextLink).
+ this nextLink:linkToAdd.
+ ^ linkToAdd
+!
+
+removeFirst
+ "remove and return the first node from the sequence"
+
+ |link|
+
+ firstLink isNil ifTrue:[
+ self errorIsEmpty
+ ] ifFalse:[
+ link := firstLink.
+ (firstLink == lastLink) ifTrue:[
+ firstLink := nil.
+ lastLink := nil
+ ] ifFalse:[
+ firstLink := firstLink nextLink
+ ].
+ numberOfNodes := numberOfNodes - 1
+ ].
+ ^ link
+!
+
+remove:aLink ifAbsent:exceptionBlock
+ "remove the argument, aLink from the sequence; if absent,
+ evaluate the excpetionBlock"
+
+ |prevNode nextNode thisNode|
+
+ thisNode := firstLink.
+ [thisNode notNil] whileTrue:[
+ nextNode := thisNode nextLink.
+ (thisNode == aLink) ifTrue:[
+ prevNode isNil ifTrue:[
+ firstLink := thisNode nextLink
+ ] ifFalse:[
+ prevNode nextLink:(thisNode nextLink)
+ ].
+ (lastLink == thisNode) ifTrue:[
+ thisNode nextLink isNil ifTrue:[
+ lastLink := prevNode
+ ] ifFalse:[
+ lastLink := thisNode nextLink
+ ]
+ ].
+ numberOfNodes := numberOfNodes - 1.
+ ^ self
+ ].
+ prevNode := thisNode.
+ thisNode := nextNode
+ ].
+ ^ exceptionBlock value
+! !
+
+!LinkedList methodsFor:'enumerating'!
+
+do:aBlock
+ "evaluate the argument, aBlock with 1 arg for every element in the list"
+
+ |thisNode|
+
+ thisNode := firstLink.
+ [thisNode notNil] whileTrue:[
+ aBlock value:thisNode.
+ thisNode := thisNode nextLink
+ ]
+!
+
+reverseDo:aBlock fromNode:aNode
+ "helper for reverseDo:"
+
+ aNode notNil ifTrue:[
+ aNode nextLink notNil ifTrue:[
+ self reverseDo:aBlock fromNode:(aNode nextLink)
+ ].
+ aBlock value:aNode
+ ]
+!
+
+reverseDo:aBlock
+ "evaluate the argument, aBlock with 1 arg for every element in the list
+ in the reverse order"
+
+ self reverseDo:aBlock fromNode:firstLink
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LinkedList.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,231 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#LinkedList
+ instanceVariableNames:'firstLink lastLink nodeClass numberOfNodes'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Sequenceable'
+!
+
+LinkedList comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+this class implements an anchor to a list of Links.
+The data itself is held in the Link elements (see Link and subclasses).
+
+%W% %E%
+'!
+
+!LinkedList class methodsFor:'instance creation'!
+
+new
+ "create and return a new LinkedList"
+
+ ^ super new initialize
+! !
+
+!LinkedList methodsFor:'ininialization'!
+
+initialize
+ numberOfNodes := 0
+! !
+
+!LinkedList methodsFor:'copying'!
+
+deepCopy
+ |newList|
+ newList := self shallowCopy.
+ newList setFirstNode:(firstLink deepCopy).
+ newList setLastNode:(firstLink last).
+ ^ newList
+! !
+
+!LinkedList methodsFor:'accessing'!
+
+setFirstNode:aNode
+ "set the first node to be the argument, aNode"
+
+ firstLink := aNode
+!
+
+setLastNode:aNode
+ "set the last node to be the argument, aNode"
+
+ lastLink := aNode
+!
+
+first
+ "return the first node in the list"
+
+ ^ firstLink
+!
+
+last
+ "return last node in the list"
+
+ ^ lastLink
+!
+
+size
+ "return the size of the LinkedList i.e. the number of nodes"
+
+ ^ numberOfNodes
+! !
+
+!LinkedList methodsFor:'testing'!
+
+includes:anObject
+ "return true, if some nodes contents is anObject"
+
+ |theNode|
+
+ theNode := firstLink.
+ [theNode notNil] whileTrue:[
+ (anObject = theNode) ifTrue:[^ true].
+ theNode := theNode nextLink
+ ].
+ ^ false
+! !
+
+!LinkedList methodsFor:'adding/removing elements'!
+
+addFirst:aLink
+ "adds aLink to the beginning of the sequence. Returns aLink"
+
+ firstLink isNil ifTrue:[
+ firstLink := aLink.
+ lastLink := aLink
+ ] ifFalse: [
+ aLink nextLink:firstLink.
+ firstLink := aLink
+ ].
+ numberOfNodes := numberOfNodes + 1.
+ ^ aLink
+!
+
+add:aLink
+ "adds aLink to the end of the sequence. Returns aLink"
+
+ aLink nextLink:nil.
+ lastLink isNil ifTrue:[
+ firstLink := aLink
+ ] ifFalse: [
+ lastLink nextLink:aLink
+ ].
+ lastLink := aLink.
+ numberOfNodes := numberOfNodes + 1.
+ ^ aLink
+!
+
+add:linkToAdd after:aLink
+ "adds linkToAdd after another link, aLink. If aLink is nil,
+ linkToAdd is inserted at the beginning. Returns linkToAdd."
+
+ |this|
+
+ aLink isNil ifTrue:[ ^ self addFirst:linkToAdd ].
+
+ this := firstLink.
+ [this notNil and:[this ~~ aLink]] whileTrue:[
+ this := this nextLink
+ ].
+ this isNil ifTrue:[ ^ self addLast:linkToAdd ].
+ linkToAdd nextLink:(this nextLink).
+ this nextLink:linkToAdd.
+ ^ linkToAdd
+!
+
+removeFirst
+ "remove and return the first node from the sequence"
+
+ |link|
+
+ firstLink isNil ifTrue:[
+ self errorIsEmpty
+ ] ifFalse:[
+ link := firstLink.
+ (firstLink == lastLink) ifTrue:[
+ firstLink := nil.
+ lastLink := nil
+ ] ifFalse:[
+ firstLink := firstLink nextLink
+ ].
+ numberOfNodes := numberOfNodes - 1
+ ].
+ ^ link
+!
+
+remove:aLink ifAbsent:exceptionBlock
+ "remove the argument, aLink from the sequence; if absent,
+ evaluate the excpetionBlock"
+
+ |prevNode nextNode thisNode|
+
+ thisNode := firstLink.
+ [thisNode notNil] whileTrue:[
+ nextNode := thisNode nextLink.
+ (thisNode == aLink) ifTrue:[
+ prevNode isNil ifTrue:[
+ firstLink := thisNode nextLink
+ ] ifFalse:[
+ prevNode nextLink:(thisNode nextLink)
+ ].
+ (lastLink == thisNode) ifTrue:[
+ thisNode nextLink isNil ifTrue:[
+ lastLink := prevNode
+ ] ifFalse:[
+ lastLink := thisNode nextLink
+ ]
+ ].
+ numberOfNodes := numberOfNodes - 1.
+ ^ self
+ ].
+ prevNode := thisNode.
+ thisNode := nextNode
+ ].
+ ^ exceptionBlock value
+! !
+
+!LinkedList methodsFor:'enumerating'!
+
+do:aBlock
+ "evaluate the argument, aBlock with 1 arg for every element in the list"
+
+ |thisNode|
+
+ thisNode := firstLink.
+ [thisNode notNil] whileTrue:[
+ aBlock value:thisNode.
+ thisNode := thisNode nextLink
+ ]
+!
+
+reverseDo:aBlock fromNode:aNode
+ "helper for reverseDo:"
+
+ aNode notNil ifTrue:[
+ aNode nextLink notNil ifTrue:[
+ self reverseDo:aBlock fromNode:(aNode nextLink)
+ ].
+ aBlock value:aNode
+ ]
+!
+
+reverseDo:aBlock
+ "evaluate the argument, aBlock with 1 arg for every element in the list
+ in the reverse order"
+
+ self reverseDo:aBlock fromNode:firstLink
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Magnitude.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,102 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Magnitude
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Magnitude-General'
+!
+
+Magnitude comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+This is an abstract class definining common methods for
+Objects which can be compared by a kind of greater realation.
+
+%W% %E%
+'!
+
+!Magnitude methodsFor:'comparing'!
+
+> aMagnitude
+ "Compare the receiver with the argument and return true if the
+ receiver is greater than the argument. Otherwise return false."
+
+ ^ self subclassResponsibility
+!
+
+= aMagnitude
+ "Compare the receiver with the argument and return true if the
+ receiver is equal to the argument. Otherwise return false."
+
+ ^ self subclassResponsibility
+!
+
+<= aMagnitude
+ "Compare the receiver with the argument and return true if the
+ receiver is less than or equal to the argument. Otherwise return false."
+
+ ^ (self > aMagnitude) not
+!
+
+< aMagnitude
+ "Compare the receiver with the argument and return true if the
+ receiver is less than the argument. Otherwise return false."
+
+ ^ (aMagnitude > self)
+!
+
+>= aMagnitude
+ "Compare the receiver with the argument and return true if the
+ receiver is greater than or equal to the argument.
+ Otherwise return false."
+
+ ^ (aMagnitude > self) not
+! !
+
+!Magnitude methodsFor:'testing'!
+
+between:min and:max
+ "return whether the receiver is less than or equal to the argument max
+ and greater than or equal to the argument min."
+
+ (self < min) ifTrue:[^ false].
+ (self > max) ifTrue:[^ false].
+ ^ true
+!
+
+in:anInterval
+ "return whether the receiver is within the interval bounds"
+
+ (self < anInterval start) ifTrue:[^ false].
+ (self > anInterval stop) ifTrue:[^ false].
+ ^ true
+! !
+
+!Magnitude methodsFor:'misc'!
+
+min:aMagnitude
+ "return the receiver or the argument, whichever has lesser magnitude"
+
+ (self < aMagnitude) ifTrue:[^ self].
+ ^ aMagnitude
+!
+
+max:aMagnitude
+ "return the receiver or the argument, whichever has greater magnitude"
+
+ (self > aMagnitude) ifTrue:[^ self].
+ ^ aMagnitude
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Make.proto Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,390 @@
+# %W% %E%
+#
+# -------------- no need to change anything below ----------
+
+LIBNAME=libbasic
+LIB=libbasic.$(A)
+SUBDIRS=
+
+TOP=..
+I = $(INCLUDE)
+
+STCFLAGS= -H../include -warnGlobalAssign $(STCOPT)
+
+OBJS= Object.$(O) Unix.$(O) Magnitude.$(O) MsgTally.$(O) Point.$(O) \
+ Rectangle.$(O) Assoc.$(O) ArithVal.$(O) \
+ Context.$(O) BContext.$(O) CCReader.$(O) Block.$(O) Message.$(O) Smalltalk.$(O) \
+ UndefObj.$(O) Behavior.$(O) MiniDebug.$(O) MiniIns.$(O) Coll.$(O) \
+ Link.$(O) ValLink.$(O) Boolean.$(O) Stream.$(O) Character.$(O) \
+ Number.$(O) ObjMem.$(O) \
+ Time.$(O) OrdColl.$(O) SeqColl.$(O) Bag.$(O) Set.$(O) \
+ Date.$(O) True.$(O) False.$(O) ClassDescr.$(O) Class.$(O) Method.$(O) \
+ FileDir.$(O) Integer.$(O) \
+ Random.$(O) PosStream.$(O) Metaclass.$(O) Float.$(O) Fraction.$(O) \
+ LargeInt.$(O) \
+ SmallInt.$(O) Interval.$(O) LinkList.$(O) Dict.$(O) ArrColl.$(O) \
+ ReadStr.$(O) WriteStr.$(O) Array.$(O) VarArray.$(O) VarString.$(O) \
+ IntArray.$(O) ByteArray.$(O) RWStream.$(O) IdDict.$(O) ShIdDict.$(O) \
+ ShadowArr.$(O) Text.$(O) String.$(O) ActorStr.$(O) \
+ ExtStream.$(O) ByteString.$(O) \
+ FileText.$(O) Symbol.$(O) NPExtStr.$(O) FileStr.$(O) PipeStr.$(O) \
+ DirStr.$(O) PrintStr.$(O) Socket.$(O) \
+ Autoload.$(O) BCCReader.$(O) ExtBytes.$(O) \
+ Process.$(O) ProcSched.$(O) SortColl.$(O) UIBytes.$(O) \
+ ChangeSet.$(O) Change.$(O) ClsDefChg.$(O) ClsComChg.$(O) MethodChg.$(O) \
+ Signal.$(O) SignalSet.$(O) Exception.$(O) Semaphore.$(O) SharedQueue.$(O) \
+ Registry.$(O) SoundStr.$(O) FloatArray.$(O) DoubleArray.$(O)
+
+AUXOBJS= Polygon.$(O) Filename.$(O) CacheDict.$(O) IdSet.$(O) Project.$(O)
+
+all:: $(OBJTARGET)
+
+objs:: level0 \
+ level1 \
+ level2 \
+ level3 \
+ level4 \
+ level5 \
+ level6 \
+ level7 \
+ level8 \
+ level9
+
+level0:$(P) \
+ Object.$(O)
+
+level1:$(P) \
+ Unix.$(O) \
+ Autoload.$(O) \
+ Magnitude.$(O) \
+ MsgTally.$(O) \
+ Rectangle.$(O) \
+ Assoc.$(O) \
+ Context.$(O) \
+ Signal.$(O) \
+ Exception.$(O) \
+ CCReader.$(O) \
+ Block.$(O) \
+ Message.$(O) \
+ Smalltalk.$(O) \
+ ObjMem.$(O) \
+ ProcSched.$(O) \
+ SharedQueue.$(O) \
+ UndefObj.$(O) \
+ Behavior.$(O) \
+ MiniDebug.$(O) \
+ MiniIns.$(O) \
+ Coll.$(O) \
+ Link.$(O) \
+ Boolean.$(O) \
+ Stream.$(O) \
+ Change.$(O) \
+ Registry.$(O) \
+ Project.$(O)
+
+level2:$(P) \
+ BContext.$(O) \
+ Character.$(O) \
+ Process.$(O) \
+ ArithVal.$(O) \
+ Time.$(O) \
+ Date.$(O) \
+ True.$(O) \
+ False.$(O) \
+ ClassDescr.$(O) \
+ Method.$(O) \
+ SeqColl.$(O) \
+ Set.$(O) \
+ Bag.$(O) \
+ FileDir.$(O) \
+ Random.$(O) \
+ BCCReader.$(O) \
+ ValLink.$(O) \
+ ClassChg.$(O) \
+ PosStream.$(O)
+
+level3:$(P) \
+ Class.$(O) \
+ Point.$(O) \
+ Number.$(O) \
+ Interval.$(O) \
+ LinkList.$(O) \
+ Dict.$(O) \
+ OrdColl.$(O) \
+ ArrColl.$(O) \
+ ReadStr.$(O) \
+ WriteStr.$(O) \
+ MethodChg.$(O) \
+ ClsDefChg.$(O) \
+ ClsComChg.$(O)
+
+level4:$(P) \
+ Metaclass.$(O) \
+ Integer.$(O) \
+ Float.$(O) \
+ Fraction.$(O) \
+ Semaphore.$(O) \
+ Array.$(O) \
+ VarArray.$(O) \
+ VarString.$(O) \
+ IdDict.$(O) \
+ IntArray.$(O) \
+ FloatArray.$(O) \
+ DoubleArray.$(O) \
+ ExtBytes.$(O) \
+ SortColl.$(O) \
+ ChangeSet.$(O) \
+ RWStream.$(O)
+
+level5:$(P) \
+ LargeInt.$(O) \
+ SmallInt.$(O) \
+ ShadowArr.$(O) \
+ ShIdDict.$(O) \
+ Text.$(O) \
+ ByteArray.$(O) \
+ ActorStr.$(O) \
+ ExtStream.$(O)
+
+level6:$(P) \
+ FileText.$(O) \
+ String.$(O) \
+ UIBytes.$(O) \
+ NPExtStr.$(O) \
+ FileStr.$(O)
+
+level7:$(P) \
+ ByteString.$(O) \
+ SoundStr.$(O) \
+ Symbol.$(O) \
+ PipeStr.$(O) \
+ Socket.$(O) \
+ DirStr.$(O)
+
+level8:$(P) \
+ PrintStr.$(O)
+
+level9:$(P)
+
+level10: \
+ Polygon.$(O) \
+ Filename.$(O) \
+ CacheDict.$(O) \
+ IdSet.$(O) \
+ Project.$(O) \
+ PSPrStr.$(O) \
+ HPIIPrStr.$(O) \
+ FX1PrStr.$(O)
+
+level11: \
+ SignalSet.$(O)
+
+auxobjs:: level10 \
+ level11 \
+ $(AUXOBJS)
+
+install::
+ -mkdir $(DESTLIBDIR)
+ -$(INSTALL) $(LIBNAME)$(OBJNAME) $(DESTLIBDIR)
+
+libe: $(LIB)
+
+PSPrStr.o:
+ $(STC) -CC="$(CC)" $(STCFLAGS) +limitSuperInclude $(CFLAGS) -c $*.st
+
+HPIIPrStr.o:
+ $(STC) -CC="$(CC)" $(STCFLAGS) +limitSuperInclude $(CFLAGS) -c $*.st
+
+FX1PrStr.o:
+ $(STC) -CC="$(CC)" $(STCFLAGS) +limitSuperInclude $(CFLAGS) -c $*.st
+
+Class:
+ $(MAKE) Class.o OPT=""
+
+Metaclass:
+ $(MAKE) Metaclass.o OPT=""
+
+i_Class.o:
+ $(MAKE) I_CLASS OPT=""
+
+i_Metaclass.o:
+ $(MAKE) I_METACLASS OPT=""
+
+I_CLASS:
+ $(CC) -c $(CFLAGS) i_Class.c
+
+I_METACLASS:
+ $(CC) -c $(CFLAGS) i_Metaclass.c
+
+cleanjunk::
+ -rm -f *.c *.H
+
+clean::
+ -rm -f *.c *.H
+
+clobber::
+ -rm -f *.c *.H
+
+tar:
+ rm -f $(TOP)/DISTRIB/libbasic.tar*
+ (cd $(TOP); tar cvf DISTRIB/libbasic.tar \
+ libbasic/*.st \
+ libbasic/Make.proto \
+ libbasic/*.stc)
+ compress $(TOP)/DISTRIB/libbasic.tar
+
+#
+# next thing I'll build into stc is a makedepend feature for this ...
+#
+
+STCHDR=$(I)/stc.h $(I)/stcIntern.h
+OBJECT=$(I)/Object.H $(STCHDR)
+BOOLEAN=$(I)/Boolean.H $(OBJECT)
+BEHAVIOR=$(I)/Behavior.H $(OBJECT)
+CLASS=$(I)/Class.H $(I)/ClassDescr.H $(BEHAVIOR)
+
+Object.o: Object.st $(STCHDR)
+UndefObj.o: UndefObj.st $(OBJECT)
+
+Message.o: Message.st $(OBJECT)
+
+Unix.o: Unix.st $(OBJECT)
+Autoload.o: Autoload.st $(OBJECT)
+Smalltalk.o: Smalltalk.st $(OBJECT)
+ObjMem.o: ObjMem.st $(OBJECT)
+Process.o: Process.st $(OBJECT)
+ProcSched.o: ProcSched.st $(OBJECT)
+Semaphore.o: Semaphore.st $(OBJECT)
+SharedQueue.o: SharedQueue.st $(OBJECT)
+MsgTally.o: MsgTally.st $(OBJECT)
+MiniDebug.o: MiniDebug.st $(OBJECT)
+MiniIns.o: MiniIns.st $(OBJECT)
+Context.o: Context.st $(OBJECT)
+BContext.o: BContext.st $(I)/Context.H $(OBJECT)
+Method.o: Method.st $(OBJECT)
+Block.o: Block.st $(OBJECT)
+BlckContext.o: BlckContext.st $(OBJECT)
+
+Signal.o: Signal.st $(OBJECT)
+
+Exception.o: Exception.st $(OBJECT)
+Rectangle.o: Rectangle.st $(OBJECT)
+
+Assoc.o: Assoc.st $(OBJECT)
+Boolean.o: Boolean.st $(OBJECT)
+True.o: True.st $(BOOLEAN)
+False.o: False.st $(BOOLEAN)
+ExtSource.o: ExtSource.st $(OBJECT)
+
+Behavior.o: Behavior.st $(OBJECT)
+ClassDescr.o: ClassDescr.st $(BEHAVIOR)
+Class.o: Class.st $(I)/ClassDescr.H $(BEHAVIOR)
+Metaclass.o: Metaclass.st $(CLASS)
+CCReader.o: CCReader.st $(OBJECT)
+BCCReader.o: BCCReader.st $(I)/CCReader.H $(OBJECT)
+Project.o: Project.st $(OBJECT)
+
+MAGNITUDE=$(I)/Magnitude.H $(OBJECT)
+ARITHVAL=$(I)/ArithVal.H $(MAGNITUDE)
+NUMBER=$(I)/Number.H $(ARITHVAL)
+INTEGER=$(I)/Integer.H $(NUMBER)
+
+Magnitude.o: Magnitude.st $(OBJECT)
+Point.o: Point.st $(MAGNITUDE)
+Character.o: Character.st $(MAGNITUDE)
+ArithVal.o: ArithVal.st $(MAGNITUDE)
+Number.o: Number.st $(ARITHVAL)
+Integer.o: Integer.st $(NUMBER)
+Float.o: Float.st $(NUMBER)
+Fraction.o: Fraction.st $(NUMBER)
+SmallInt.o: SmallInt.st $(INTEGER)
+LargeInt.o: LargeInt.st $(INTEGER)
+Time.o: Time.st $(MAGNITUDE)
+Date.o: Date.st $(MAGNITUDE)
+
+COLL=$(I)/Coll.H $(OBJECT)
+SET=$(I)/Set.H $(COLL)
+DICT=$(I)/Dict.H $(COLL)
+IDDICT=$(I)/IdDict.H $(DICT)
+SHADOWIDDICT=$(I)/ShIdDict.H $(IDDICT)
+SEQCOLL=$(I)/SeqColl.H $(COLL)
+ARRCOLL=$(I)/ArrColl.H $(SEQCOLL)
+ARRAY=$(I)/Array.H $(ARRCOLL)
+VARARRAY=$(I)/VarArray.H $(ARRCOLL)
+INTARRAY=$(I)/IntArray.H $(ARRCOLL)
+BYTEARRAY=$(I)/ByteArray.H $(INTARRAY)
+STRING=$(I)/String.H $(BYTEARRAY)
+
+Registry.o: Registry.st $(OBJECT)
+Coll.o: Coll.st $(OBJECT)
+FileDir.o: FileDir.st $(COLL)
+SeqColl.o: SeqColl.st $(COLL)
+Set.o: Set.st $(COLL)
+IdSet.o: IdSet.st $(SET)
+SignalSet.o: SignalSet.st $(I)/IdSet.H $(SET)
+Bag.o: Bag.st $(COLL)
+Dict.o: Dict.st $(COLL)
+IdDict.o: IdDict.st $(DICT)
+CacheDict.o: CacheDict.st $(DICT)
+ShIdDict.o: ShIdDict.st $(IDDICT)
+OrdColl.o: OrdColl.st $(SEQCOLL)
+SortColl.o: SortColl.st $(I)/OrdColl.H $(SEQCOLL)
+ChangeSet.o: ChangeSet.st $(I)/OrdColl.H $(SEQCOLL)
+Interval.o: Interval.st $(SEQCOLL)
+Node.o: Node.st $(COLL)
+Tree.o: Tree.st $(COLL)
+LinkList.o: LinkList.st $(SEQCOLL)
+Link.o: Link.st $(OBJECT)
+ValLink.o: ValLink.st $(I)/Link.H $(OBJECT)
+ArrColl.o: ArrColl.st $(SEQCOLL)
+VarArray.o: VarArray.st $(ARRCOLL)
+VarString.o: VarString.st $(ARRCOLL)
+Text.o: Text.st $(VARARRAY)
+FileText.o: FileText.st $(I)/Text.H $(VARARRAY)
+Array.o: Array.st $(ARRCOLL)
+Polygon.o: Polygon.st $(ARRAY)
+ShadowArr.o: ShadowArr.st $(ARRAY)
+IntArray.o: IntArray.st $(ARRCOLL)
+FloatArray.o: FloatArray.st $(ARRCOLL)
+DoubleArray.o: DoubleArray.st $(ARRCOLL)
+ExtBytes.o: ExtBytes.st $(ARRCOLL)
+ByteArray.o: ByteArray.st $(INTARRAY)
+UIBytes.o: UIBytes.st $(I)/ByteArray.H $(INTARRAY)
+String.o: String.st $(BYTEARRAY)
+ByteString.o: ByteString.st $(STRING)
+Symbol.o: Symbol.st $(STRING)
+Filename.o: Filename.st $(STRING)
+
+Change.o: Change.st $(OBJECT)
+ClassChg.o: ClassChg.st $(OBJECT)
+ClsDefChg.o: ClsDefChg.st $(OBJECT)
+ClsComChg.o: ClsComChg.st $(OBJECT)
+MethodChg.o: MethodChg.st $(OBJECT)
+
+STREAM=$(I)/Stream.H $(OBJECT)
+POSSTREAM=$(I)/PosStream.H $(STREAM)
+WRITESTREAM=$(I)/WriteStr.H $(POSSTREAM)
+RWSTREAM=$(I)/RWStream.H $(WRITESTREAM)
+EXTSTREAM=$(I)/ExtStream.H $(RWSTREAM)
+NPEXTSTREAM=$(I)/NPExtStr.H $(EXTSTREAM)
+PIPESTREAM=$(I)/PipeStr.H $(NPEXTSTREAM)
+
+Stream.o: Stream.st $(OBJECT)
+Random.o: Random.st $(STREAM)
+PosStream.o: PosStream.st $(STREAM)
+ReadStr.o: ReadStr.st $(POSSTREAM)
+WriteStr.o: WriteStr.st $(POSSTREAM)
+RWStream.o: RWStream.st $(WRITESTREAM)
+ActorStr.o: ActorStr.st $(RWSTREAM)
+ExtStream.o: ExtStream.st $(RWSTREAM)
+NPExtStr.o: NPExtStr.st $(EXTSTREAM)
+TTYStream.o: TTYStream.st $(NPEXTSTREAM)
+PipeStr.o: PipeStr.st $(NPEXTSTREAM)
+Socket.o: Socket.st $(NPEXTSTREAM)
+PrintStr.o: PrintStr.st $(PIPESTREAM)
+PSPrStr.o: PSPrStr.st $(I)/PrintStr.H $(PIPESTREAM)
+HPIIPrStr.o: HPIIPrStr.st $(I)/PrintStr.H $(PIPESTREAM)
+FX1PrStr.o: FX1PrStr.st $(I)/PrintStr.H $(PIPESTREAM)
+FileStr.o: FileStr.st $(EXTSTREAM)
+SoundStr.o: SoundStr.st $(I)/FileStr.H $(EXTSTREAM)
+DirStr.o: DirStr.st $(I)/FileStr.H $(EXTSTREAM)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Message.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,94 @@
+"
+ COPYRIGHT (c) 1988-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Message
+ instanceVariableNames:'selector args'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Methods'
+!
+
+Message comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+these are only created, when a message fails (i.e. is not understood) -
+the selector and arguments are put into an instance of Message, which is then
+passed to doesNotUnderstand:aMessage as argument.
+
+This allows for re-evaluation of the failed send (after some cleanup).
+As an example of its use, see the implementation of the Autoload-class.
+
+%W% %E%
+'!
+
+!Message class methodsFor:'instance creation'!
+
+selector:aSymbol
+ "return a new message object for a send without arguments"
+
+ ^ self basicNew selector:aSymbol
+ arguments:nil
+!
+
+selector:aSymbol with:anArg
+ "return a new message object for a send with one argument"
+
+ ^ self basicNew selector:aSymbol
+ arguments:(Array with:anArg)
+!
+
+selector:aSymbol with:arg1 with:arg2
+ "return a new message object for a send with two arguments"
+
+ ^ self basicNew selector:aSymbol
+ arguments:(Array with:arg1 with:arg2)
+!
+
+selector:aSymbol withAll:argArray
+ "return a new message object for a send with many arguments"
+
+ ^ self basicNew selector:aSymbol
+ arguments:argArray
+! !
+
+!Message methodsFor:'private accessing'!
+
+selector:aSymbol arguments:argArray
+ "set selector and arguments of the receiver"
+
+ selector := aSymbol.
+ args := argArray
+! !
+
+!Message methodsFor:'accessing'!
+
+selector
+ "return the selector of the message"
+
+ ^ selector
+!
+
+arguments
+ "return the arguments of the message"
+
+ ^ args
+! !
+
+!Message methodsFor:'printing'!
+
+printString
+ "return a string for printing the receiver"
+
+ ^ selector printString
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Metaclass.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,396 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Class subclass:#Metaclass
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Classes'
+!
+
+Metaclass comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+every class-class is a subclass of Metaclass
+- this adds support for creating new subclasses or changing the definition
+of an already existing class.
+
+%W% %E%
+'!
+
+!Metaclass methodsFor:'creating classes'!
+
+name:newName inEnvironment:aSystemDictionary
+ subclassOf:aClass
+ instanceVariableNames:stringOfInstVarNames
+ variable:variableBoolean
+ words:wordsBoolean
+ pointers:pointersBoolean
+ classVariableNames:stringOfClassVarNames
+ poolDictionaries:stringOfPoolNames
+ category:categoryString
+ comment:commentString
+ changed:changed
+
+ |newClass newMetaclass nInstVars nameString classSymbol oldClass
+ allSubclasses classVarChange instVarChange superClassChange newComment
+ upd|
+
+ nInstVars := stringOfInstVarNames countWords.
+ nameString := newName asString.
+ classSymbol := nameString asSymbol.
+ newComment := commentString.
+
+ (aSystemDictionary includesKey:classSymbol) ifTrue:[
+ oldClass := aSystemDictionary at:classSymbol.
+ (newComment isNil and:[oldClass isBehavior "isKindOf:Class"]) ifTrue:[
+ newComment := oldClass comment
+ ]
+ ].
+
+ "create the metaclass first"
+ newMetaclass := Metaclass new.
+ newMetaclass setSuperclass:(aClass class).
+ newMetaclass instSize:(aClass class instSize).
+ newMetaclass flags:0. "not indexed"
+ newMetaclass setName:(nameString , 'class').
+ newMetaclass classVariableString:'' "stringOfClassVarNames".
+ newMetaclass setComment:newComment category:categoryString.
+
+ newClass := newMetaclass new.
+ newClass setSuperclass:aClass.
+ newClass instSize:(aClass instSize + nInstVars).
+
+ (variableBoolean == true) ifTrue:[
+ pointersBoolean ifTrue:[
+ newClass flags:4 "pointerarray"
+ ] ifFalse:[
+ wordsBoolean ifTrue:[
+ newClass flags:2 "wordarray"
+ ] ifFalse:[
+ newClass flags:1 "bytearray"
+ ]
+ ]
+ ] ifFalse:[
+ "this is a backward compatible hack"
+
+ (variableBoolean == #float) ifTrue:[
+ newClass flags:6 "float array"
+ ] ifFalse:[
+ (variableBoolean == #double) ifTrue:[
+ newClass flags:7 "double array"
+ ] ifFalse:[
+ (variableBoolean == #long) ifTrue:[
+ newClass flags:3 "long array"
+ ] ifFalse:[
+ newClass flags:0
+ ]
+ ]
+ ].
+ ].
+
+ newClass setName:nameString.
+ (nInstVars ~~ 0) ifTrue:[
+ newClass instanceVariableString:stringOfInstVarNames
+ ].
+ oldClass notNil ifTrue:[
+ "setting first will make new class clear obsolete classvars"
+ newClass setClassVariableString:(oldClass classVariableString)
+ ].
+ newClass classVariableString:stringOfClassVarNames.
+
+ oldClass notNil ifTrue:[
+ "dont have to flush if class is brand-new"
+
+ ObjectMemory flushCaches.
+ ].
+
+ aSystemDictionary at:classSymbol put:newClass.
+
+ self addChangeRecordForClass:newClass.
+
+ oldClass isNil ifTrue:[
+ commentString notNil ifTrue:[
+ newClass comment:commentString
+ ]
+ ] ifFalse:[
+ "if only category/comment has changed, do not recompile .."
+
+ (oldClass superclass == newClass superclass) ifTrue:[
+ (oldClass instSize == newClass instSize) ifTrue:[
+ (oldClass flags == newClass flags) ifTrue:[
+ (oldClass name = newClass name) ifTrue:[
+ (oldClass instanceVariableString = newClass instanceVariableString) ifTrue:[
+ (oldClass classVariableString = newClass classVariableString) ifTrue:[
+ (newComment ~= oldClass comment) ifTrue:[
+ oldClass comment:newComment
+ ].
+ oldClass category:categoryString.
+ aSystemDictionary at:classSymbol put:oldClass.
+ oldClass changed.
+ ^ oldClass
+ ]
+ ]
+ ]
+ ]
+ ]
+ ].
+
+ (newComment ~= oldClass comment) ifTrue:[
+ newClass comment:newComment
+ ].
+
+ upd := Class updateChanges:false.
+
+ superClassChange := oldClass superclass ~~ newClass superclass.
+
+ classVarChange := oldClass classVariableString ~= newClass classVariableString.
+
+ classVarChange ifTrue:[
+ " no need to recompile if classvars are added "
+ classVarChange := (newClass classVariableString startsWith: oldClass classVariableString) not
+ ].
+ classVarChange := classVarChange or:[superClassChange].
+ classVarChange := classVarChange or:[self anyInvalidatedMethodsIn: oldClass class].
+
+ classVarChange ifTrue:[
+ "must recompile class-methods"
+ self copyInvalidatedMethodsFrom:(oldClass class) for:newMetaclass.
+ newMetaclass recompile
+ ] ifFalse:[
+ "class methods still work"
+ self copyMethodsFrom:(oldClass class) for:newMetaclass
+ ].
+
+ instVarChange := oldClass instanceVariableString ~= newClass instanceVariableString.
+ instVarChange ifTrue:[
+ " no need to recompile if instvars are added "
+ instVarChange := (newClass instanceVariableString startsWith: oldClass instanceVariableString) not
+ ].
+ instVarChange := instVarChange or:[superClassChange].
+ instVarChange := instVarChange or:[self anyInvalidatedMethodsIn: oldClass].
+
+ (instVarChange or:[classVarChange]) ifTrue:[
+ "must recompile instance-methods"
+ self copyInvalidatedMethodsFrom:oldClass for:newClass.
+ newClass recompile
+ ] ifFalse:[
+ "instance methods still work"
+ self copyMethodsFrom:oldClass for:newClass
+ ].
+
+ "get list of all subclasses - do before superclass is changed"
+
+ allSubclasses := oldClass allSubclasses.
+
+ "update superclass of immediate subclasses"
+
+ oldClass subclassesDo:[:aClass |
+ aClass superclass:newClass
+ ].
+
+ "update instSizes and recompile all subclasses if needed"
+
+ "for subclasses we must be strict"
+ classVarChange := oldClass classVariableString ~= newClass classVariableString.
+ classVarChange := classVarChange or:[superClassChange].
+
+ "for subclasses we must be strict since offsets change"
+ instVarChange := oldClass instanceVariableString ~= newClass instanceVariableString.
+ instVarChange := instVarChange or:[superClassChange].
+
+ allSubclasses do:[:aClass |
+ aClass instSize:(aClass instSize + (newClass instSize - oldClass instSize)).
+ (classVarChange or:[self anyInvalidatedMethodsIn:aClass class]) ifTrue:[
+ aClass class recompileAll
+ ].
+ (classVarChange or:[instVarChange or:[self anyInvalidatedMethodsIn: aClass]]) ifTrue:[
+ aClass recompileAll
+ ]
+ ].
+
+ ObjectMemory flushCaches.
+ Class updateChanges:upd
+ ].
+ oldClass isNil ifTrue:[
+ Smalltalk changed
+ ] ifFalse:[
+ oldClass setName:(oldClass name , '-old')
+ ].
+ ^ newClass
+!
+
+new
+ "returs a new class class"
+ |newClass|
+
+ newClass := self basicNew.
+ newClass setSuperclass:(Object class)
+ selectors:(Array new:0)
+ methods:(Array new:0)
+ instSize:0
+ flags:0.
+ newClass setComment:(self comment) category:(self category).
+ ^ newClass
+! !
+
+!Metaclass methodsFor:'class instance variables'!
+
+instanceVariableNames:aString
+ "changing / adding class-inst vars -
+ this actually creates a new metaclass and class"
+
+ |newClass newMetaclass nClassInstVars oldClass
+ allSubclasses upd t oldVars sizeChange|
+
+ oldVars := self instanceVariableString.
+ aString = oldVars ifTrue:[^ self].
+
+ nClassInstVars := aString countWords.
+ sizeChange := nClassInstVars ~~ oldVars countWords.
+
+ "create the new metaclass"
+ newMetaclass := Metaclass new.
+ newMetaclass setSuperclass:superclass.
+ newMetaclass instSize:(superclass instSize + nClassInstVars).
+ (nClassInstVars ~~ 0) ifTrue:[
+ newMetaclass instanceVariableString:aString
+ ].
+ newMetaclass flags:0. "not indexed"
+ newMetaclass setName:name.
+ newMetaclass classVariableString:classvars.
+ newMetaclass category:category.
+ newMetaclass setComment:comment.
+
+ "find the class which is my sole instance"
+
+ t := Smalltalk allClasses select:[:element | element class == self].
+ (t size ~~ 1) ifTrue:[
+ self error:'oops - I should have exactly one instance'.
+ ^ nil
+ ].
+ oldClass := t anElement.
+
+ "create a new class"
+ newClass := newMetaclass new.
+ newClass setSuperclass:(oldClass superclass).
+ newClass instSize:(oldClass instSize).
+ newClass flags:(oldClass flags).
+ newClass setName:(oldClass name).
+ newClass instanceVariableString:(oldClass instanceVariableString).
+ newClass classVariableString:(oldClass classVariableString).
+ newClass comment:(oldClass comment).
+ newClass category:(oldClass category).
+
+ ObjectMemory flushCaches.
+
+ Smalltalk at:(oldClass name asSymbol) put:newClass.
+
+ upd := Class updateChanges:false.
+
+ (oldVars isBlank
+ or:[aString startsWith:oldVars]) ifTrue:[
+ "there where none before or a new var has been added
+ - methods still work"
+ self copyMethodsFrom:self for:newMetaclass.
+ self copyMethodsFrom:oldClass for:newClass
+ ] ifFalse:[
+ "recompile class-methods"
+ self copyInvalidatedMethodsFrom:self for:newMetaclass.
+ newMetaclass recompile.
+
+ "recompile instance-methods"
+ self copyInvalidatedMethodsFrom:oldClass for:newClass.
+ newClass recompile
+ ].
+
+ "get list of all subclasses - do before superclass is changed"
+
+ allSubclasses := oldClass allSubclasses.
+
+ "update superclass of immediate subclasses"
+
+ oldClass subclassesDo:[:aClass |
+ aClass superclass:newClass
+ ].
+
+ "update instSizes and recompile all subclasses if needed"
+
+ allSubclasses do:[:aClass |
+ aClass class recompileAll.
+ aClass recompileAll
+ ].
+
+ ObjectMemory flushCaches.
+ Class updateChanges:upd.
+ ^ newMetaclass
+! !
+
+!Metaclass methodsFor:'queries'!
+
+isMeta
+ "return true, if the receiver is some kind of metaclass;
+ true is returned here. Redefines isMeta in Object"
+
+ ^ true
+! !
+
+!Metaclass methodsFor:'private'!
+
+copyMethodsFrom:oldClass for:newClass
+ "when a class has changed, but metaclass is unaffected (i.e. classVars
+ have not changed) there is no need to recompile them"
+
+ newClass selectors:(oldClass selectors) methods:(oldClass methodDictionary)
+!
+
+copyInvalidatedMethodsFrom:oldClass for:newClass
+ "when a class has been changed, copy all old methods into the new class
+ - changing code to a trap method giving an error message;
+ this allows us to keep the source while trapping uncompilable (due to
+ now undefined instvars) methods"
+
+ |trap trapCode trapByteCode|
+
+ trap := Method compiledMethodAt:#invalidMethod.
+ trapCode := trap code.
+ trapByteCode := trap byteCode.
+
+ newClass selectors:(oldClass selectors) methods:(oldClass methodDictionary).
+ newClass methodDictionary do:[:aMethod |
+ aMethod code:trapCode.
+ aMethod literals:nil.
+ aMethod byteCode:trapByteCode
+ ]
+!
+
+anyInvalidatedMethodsIn:aClass
+ "return true, if aClass has any invalidated methods in it"
+
+ |trap trapCode trapByteCode|
+
+ trap := Method compiledMethodAt:#invalidMethod.
+ trapCode := trap code.
+ trapByteCode := trap byteCode.
+
+ aClass methodDictionary do:[:aMethod |
+ trapCode notNil ifTrue:[
+ (aMethod code == trapCode) ifTrue:[^ true]
+ ].
+ trapByteCode notNil ifTrue:[
+ (aMethod byteCode == trapByteCode) ifTrue:[^ true]
+ ]
+ ].
+ ^ false
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Method.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,629 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Method
+ instanceVariableNames:'code flags byteCode literals
+ source sourcePosition category'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Methods'
+!
+
+Method comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+this class defines protocol for executable methods;
+both compiled and interpreted methods are represented by this class.
+Compiled code has a non-nil code field, while interpreted methods have
+a nil code field and non-nil byteCode field.
+
+The methods source-code is represented by source and sourcePosition:
+if sourcePosition is a Number, the source-field is the fileName and
+sourcePosition is the character offset of the source-chunk in this source file.
+If sourcePosition is nil, the source is the string in the source field.
+
+The flags field defines things like the number of method-locals,
+method arguments and stack size need (for interpreted methods).
+
+WARNING: layout known by compiler and runtime system - dont change
+
+%W% %E%
+written spring 89 by claus
+'!
+
+!Method methodsFor:'accessing'!
+
+instVarAt:index
+ "redefined to catch access to code-field - it is a non-object"
+
+ index == 1 ifTrue:[^ nil].
+ ^ super instVarAt:index
+!
+
+instVarAt:index put:value
+ "redefined to catch access to code-field - it is a non-object"
+
+ index == 1 ifTrue:[^ nil].
+ ^ super instVarAt:index put:value
+!
+
+code
+ "return code field - since its a non-object return address as integer"
+%{
+ RETURN ( _MKSMALLINT((int)(_MethodInstPtr(self)->m_code)) );
+%}
+!
+
+code:anAddress
+ "set the code field - you should know what you do if doing this -
+ should only be done by compiler.
+ Smalltalk can crash badly if playing around here ..."
+%{
+ if (_isSmallInteger(anAddress)) {
+ _MethodInstPtr(self)->m_code = (OBJFUNC)_intVal(anAddress);
+ RETURN ( self );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+source
+ "return the sourcestring for the receiver"
+
+ |aStream junk|
+
+ source notNil ifTrue:[
+ sourcePosition isNil ifTrue:[^ source].
+ aStream := Smalltalk systemFileStreamFor:('source/' , source).
+ aStream notNil ifTrue:[
+ aStream position:sourcePosition.
+ junk := aStream nextChunk.
+ aStream close
+ ]
+ ].
+ ^ junk
+!
+
+source:aString
+ "set the methods sourcestring"
+
+ source := aString.
+ sourcePosition := nil
+!
+
+sourceFileName
+ "return the sourcefilename if source is extern; nil otherwise"
+
+ sourcePosition notNil ifTrue:[^ source].
+ ^ nil
+!
+
+sourcePosition
+ "return the sourceposition if source is extern; nil otherwise"
+
+ ^ sourcePosition
+!
+
+sourceFileName:aFileName position:aNumber
+ "set the methods sourcefile/position"
+
+ source := aFileName.
+ sourcePosition := aNumber
+!
+
+literals
+ "return the methods literal array"
+
+ ^ literals
+!
+
+literals:anArray
+ "set the methods literal array"
+
+ literals := anArray
+!
+
+byteCode
+ "return the methods byteCode array"
+
+ ^ byteCode
+!
+
+byteCode:aByteArray
+ "set the methods byteCode array"
+
+ byteCode := aByteArray
+!
+
+category
+ "return the methods category or nil"
+
+ ^ category
+!
+
+category:aStringOrSymbol
+ "set the methods category"
+
+ category := aStringOrSymbol asSymbol
+!
+
+flags
+ "return the flags (number of method variables, stacksize)"
+
+ ^ flags
+!
+
+flags:newFlags
+ "set the flags (number of method variables, stacksize)
+ - should only be done by the compiler"
+
+ flags := newFlags
+!
+
+numberOfMethodVars:aNumber
+ "set the number of method variables
+ - should only be done by the compiler"
+
+ |newFlags|
+
+ newFlags := flags.
+%{
+ /* made this a primitive to get define in stc.h */
+ newFlags = _MKSMALLINT((_intVal(newFlags) & ~F_NVARS)
+ | (_intVal(aNumber) << F_NVARSHIFT));
+%}
+.
+ flags := newFlags
+!
+
+stackSize:aNumber
+ "set the depth of the local stack
+ - should only be done by the compiler"
+
+ |newFlags|
+
+ newFlags := flags.
+%{
+ /* made this a primitive to get define in stc.h */
+ newFlags = _MKSMALLINT((_intVal(newFlags) & ~F_NSTACK)
+ | (_intVal(aNumber) << F_NSTACKSHIFT));
+%}
+.
+ flags := newFlags
+! !
+
+!Method methodsFor:'queries'!
+
+containingClass
+ "return the class I am defined in"
+
+ Smalltalk allClassesDo:[:aClass |
+ (aClass containsMethod:self) ifTrue:[^ aClass]
+ ].
+ ^ nil
+!
+
+methodArgNames
+ "return a collection with the methods argument names.
+ Uses Parser to parse methods source."
+
+ |parser sourceString|
+
+ sourceString := self source.
+ sourceString notNil ifTrue:[
+ parser := Parser parseMethodSpecification:sourceString.
+ parser isNil ifTrue:[^ nil].
+ ^ parser methodArgs
+ ].
+ ^ nil
+
+ "(Method compiledMethodAt:#printOn:) methodArgNames"
+!
+
+methodVarNames
+ "return a collection with the methods local-variable names.
+ Uses Parser to parse methods source."
+
+ |parser sourceString|
+
+ sourceString := self source.
+ sourceString notNil ifTrue:[
+ parser := Parser parseMethodArgAndVarSpecification:sourceString.
+ parser isNil ifTrue:[^ nil].
+ ^ parser methodVars
+ ].
+ ^ nil
+
+ "(Method compiledMethodAt:#printOn:) methodVarNames"
+!
+
+methodArgAndVarNames
+ "return a collection with the methods argument and variable names.
+ Uses Parser to parse methods source."
+
+ |parser sourceString argNames varNames|
+
+ sourceString := self source.
+ sourceString notNil ifTrue:[
+ parser := Parser parseMethodArgAndVarSpecification:sourceString.
+ parser isNil ifTrue:[^ nil].
+ argNames := parser methodArgs.
+ varNames := parser methodVars.
+ argNames isNil ifTrue:[^ varNames].
+ varNames isNil ifTrue:[^ argNames].
+ ^ (argNames , varNames)
+ ].
+ ^ nil
+
+ "(Method compiledMethodAt:#printOn:) methodArgAndVarNames"
+!
+
+methodComment
+ "return the methods comment, nil if there is none"
+
+ |text line nQuote index qIndex qIndex2 comment|
+
+ text := self source asText.
+ (text size < 2) ifTrue:[^nil].
+
+ line := (text at:2).
+ nQuote := line occurrencesOf:(Character doubleQuote).
+ (nQuote == 2) ifTrue:[
+ qIndex := line indexOf:(Character doubleQuote).
+ qIndex2 := line indexOf:(Character doubleQuote) startingAt:(qIndex + 1).
+ ^ line copyFrom:(qIndex + 1) to:(qIndex2 - 1)
+ ].
+ (nQuote == 1) ifTrue:[
+ qIndex := line indexOf:(Character doubleQuote).
+ comment := line copyFrom:(qIndex + 1).
+
+ index := 3.
+ line := text at:index.
+ nQuote := line occurrencesOf:(Character doubleQuote).
+ [nQuote ~~ 1] whileTrue:[
+ comment := comment , Character cr asString , line withoutSpaces.
+ index := index + 1.
+ line := text at:index.
+ nQuote := line occurrencesOf:(Character doubleQuote)
+ ].
+ qIndex := line indexOf:(Character doubleQuote).
+ ^ comment , Character cr asString , (line copyTo:(qIndex - 1)) withoutSpaces
+ ].
+ ^ nil
+
+ "(Method compiledMethodAt:#methodComment) methodComment"
+!
+
+referencesGlobal:aGlobalSymbol
+ "return true, if this method references the global
+ bound to aGlobalSymbol."
+
+ literals isNil ifTrue:[^ false].
+ ^ (literals identityIndexOf:aGlobalSymbol startingAt:1) ~~ 0
+!
+
+sends:aSelectorSymbol
+ "return true, if this method contains a message-send
+ with aSelectorSymbol as selector.
+ - due to the simple check in the literal array, also simple uses
+ of aSelectorSymbol as symbol will return true."
+
+ ^ self referencesGlobal:aSelectorSymbol
+! !
+
+!Method methodsFor:'error handling'!
+
+invalidMethod
+ "this error is triggered by the interpreter when an invalid method
+ is about to be executed.
+ When recompiling classes after a definition-change, all
+ uncompilable methods will be bound to this method here,
+ so that evaluating such an uncompilable method will trigger an error.
+ Can also happen when Compiler/runtime system is broken."
+
+ self error:'invalid method - not executable'
+!
+
+invalidByteCode
+ "this error is triggered when the bytecode-interpreter tries to
+ execute an invalid bytecode.
+ Can only happen when Compiler/runtime system is broken."
+
+ self error:'invalid byteCode in method - not executable'
+!
+
+receiverNotBoolean
+ "this error is triggered when the bytecode-interpreter tries to
+ execute ifTrue:/ifFalse or whileTrue: type of expressions where the
+ receiver is neither true nor false."
+
+ self error:'if/while on non-boolean receiver'
+! !
+
+!Method methodsFor:'executing'!
+
+valueWithReceiver:anObject arguments:argArray
+ "low level call of a methods code - big danger alert.
+ This method is provided for debugging- and breakpoint-support;
+ not for general use. The receiver must be a method compiled in
+ anObjects class or one of its superclasses
+ - otherwise strange things (and also strange crashes) can occur.
+ Be warned."
+
+ |numArgs a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12|
+
+ argArray class == Array ifFalse:[
+ ^ self error:'argumentArray must be an Array'
+ ].
+ numArgs := argArray size.
+ numArgs > 12 ifTrue:[
+ ^ self error:'too many arguments'
+ ].
+%{
+ OBJFUNC code;
+ OBJ searchClass;
+ static struct inlineCache dummy = _DUMMYILC0;
+
+ code = _MethodInstPtr(self)->m_code;
+ searchClass = dummy.ilc_class = _Class(anObject);
+ switch (_intVal(numArgs)) {
+ case 0:
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy) );
+
+ case 1:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+#ifdef PASS_ARG_REF
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1) );
+#endif
+
+ case 2:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+ a2 = _ArrayInstPtr(argArray)->a_element[1];
+#ifdef PASS_ARG_REF
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2) );
+#endif
+
+ case 3:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+ a2 = _ArrayInstPtr(argArray)->a_element[1];
+ a3 = _ArrayInstPtr(argArray)->a_element[2];
+#ifdef PASS_ARG_REF
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3) );
+#endif
+
+ case 4:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+ a2 = _ArrayInstPtr(argArray)->a_element[1];
+ a3 = _ArrayInstPtr(argArray)->a_element[2];
+ a4 = _ArrayInstPtr(argArray)->a_element[3];
+#ifdef PASS_ARG_REF
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4) );
+#endif
+
+ case 5:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+ a2 = _ArrayInstPtr(argArray)->a_element[1];
+ a3 = _ArrayInstPtr(argArray)->a_element[2];
+ a4 = _ArrayInstPtr(argArray)->a_element[3];
+ a5 = _ArrayInstPtr(argArray)->a_element[4];
+#ifdef PASS_ARG_REF
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5) );
+#endif
+
+ case 6:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+ a2 = _ArrayInstPtr(argArray)->a_element[1];
+ a3 = _ArrayInstPtr(argArray)->a_element[2];
+ a4 = _ArrayInstPtr(argArray)->a_element[3];
+ a5 = _ArrayInstPtr(argArray)->a_element[4];
+ a6 = _ArrayInstPtr(argArray)->a_element[5];
+#ifdef PASS_ARG_REF
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6) );
+#endif
+
+ case 7:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+ a2 = _ArrayInstPtr(argArray)->a_element[1];
+ a3 = _ArrayInstPtr(argArray)->a_element[2];
+ a4 = _ArrayInstPtr(argArray)->a_element[3];
+ a5 = _ArrayInstPtr(argArray)->a_element[4];
+ a6 = _ArrayInstPtr(argArray)->a_element[5];
+ a7 = _ArrayInstPtr(argArray)->a_element[6];
+#ifdef PASS_ARG_REF
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6, a7) );
+#endif
+
+ case 8:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+ a2 = _ArrayInstPtr(argArray)->a_element[1];
+ a3 = _ArrayInstPtr(argArray)->a_element[2];
+ a4 = _ArrayInstPtr(argArray)->a_element[3];
+ a5 = _ArrayInstPtr(argArray)->a_element[4];
+ a6 = _ArrayInstPtr(argArray)->a_element[5];
+ a7 = _ArrayInstPtr(argArray)->a_element[6];
+ a8 = _ArrayInstPtr(argArray)->a_element[7];
+#ifdef PASS_ARG_REF
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6, a7, a8) );
+#endif
+
+ case 9:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+ a2 = _ArrayInstPtr(argArray)->a_element[1];
+ a3 = _ArrayInstPtr(argArray)->a_element[2];
+ a4 = _ArrayInstPtr(argArray)->a_element[3];
+ a5 = _ArrayInstPtr(argArray)->a_element[4];
+ a6 = _ArrayInstPtr(argArray)->a_element[5];
+ a7 = _ArrayInstPtr(argArray)->a_element[6];
+ a8 = _ArrayInstPtr(argArray)->a_element[7];
+ a9 = _ArrayInstPtr(argArray)->a_element[8];
+#ifdef PASS_ARG_REF
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6, a7, a8, a9) );
+#endif
+
+ case 10:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+ a2 = _ArrayInstPtr(argArray)->a_element[1];
+ a3 = _ArrayInstPtr(argArray)->a_element[2];
+ a4 = _ArrayInstPtr(argArray)->a_element[3];
+ a5 = _ArrayInstPtr(argArray)->a_element[4];
+ a6 = _ArrayInstPtr(argArray)->a_element[5];
+ a7 = _ArrayInstPtr(argArray)->a_element[6];
+ a8 = _ArrayInstPtr(argArray)->a_element[7];
+ a9 = _ArrayInstPtr(argArray)->a_element[8];
+ a10 = _ArrayInstPtr(argArray)->a_element[9];
+#ifdef PASS_ARG_REF
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) );
+#endif
+
+ case 11:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+ a2 = _ArrayInstPtr(argArray)->a_element[1];
+ a3 = _ArrayInstPtr(argArray)->a_element[2];
+ a4 = _ArrayInstPtr(argArray)->a_element[3];
+ a5 = _ArrayInstPtr(argArray)->a_element[4];
+ a6 = _ArrayInstPtr(argArray)->a_element[5];
+ a7 = _ArrayInstPtr(argArray)->a_element[6];
+ a8 = _ArrayInstPtr(argArray)->a_element[7];
+ a9 = _ArrayInstPtr(argArray)->a_element[8];
+ a10 = _ArrayInstPtr(argArray)->a_element[9];
+ a11 = _ArrayInstPtr(argArray)->a_element[10];
+#ifdef PASS_ARG_REF
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) );
+#endif
+
+ case 12:
+ a1 = _ArrayInstPtr(argArray)->a_element[0];
+ a2 = _ArrayInstPtr(argArray)->a_element[1];
+ a3 = _ArrayInstPtr(argArray)->a_element[2];
+ a4 = _ArrayInstPtr(argArray)->a_element[3];
+ a5 = _ArrayInstPtr(argArray)->a_element[4];
+ a6 = _ArrayInstPtr(argArray)->a_element[5];
+ a7 = _ArrayInstPtr(argArray)->a_element[6];
+ a8 = _ArrayInstPtr(argArray)->a_element[7];
+ a9 = _ArrayInstPtr(argArray)->a_element[8];
+ a10 = _ArrayInstPtr(argArray)->a_element[9];
+ a11 = _ArrayInstPtr(argArray)->a_element[10];
+ a12 = _ArrayInstPtr(argArray)->a_element[11];
+#ifdef PASS_ARG_REF
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+ RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) );
+#endif
+ }
+%}
+ "(String compiledMethodAt:#print) valueWithReceiver:'hello' arguments:#()"
+ "(Float compiledMethodAt:#+) valueWithReceiver:1.0 arguments:#(2.0)"
+! !
+
+!Method methodsFor:'printing'!
+
+printOn:aStream
+ "put a printed representation of the receiver onto aStream"
+
+ |homeClass|
+
+ homeClass := self containingClass.
+ homeClass notNil ifTrue:[
+ aStream nextPutAll:'a Method in '.
+ homeClass name printOn:aStream.
+ aStream nextPutAll:' '.
+ (homeClass selectorForMethod:self) printOn:aStream
+ ] ifFalse:[
+ aStream nextPutAll:'a Method'
+ ]
+! !
+
+!Method methodsFor:'binary fileOut'!
+
+binaryFileOutLiteralsOn:aStream
+ |index n|
+
+ literals isNil ifTrue:[
+ aStream nextPutAll:'0'.
+ aStream nextPut:$!!.
+ ^ self
+ ].
+ aStream nextPutAll:literals size printString.
+ aStream nextPut:$!!.
+
+ index := 1.
+ literals do:[:lit |
+ (lit isKindOf:Number) ifTrue:[
+ lit storeOn:aStream
+ ] ifFalse:[
+ ((lit isKindOf:String) or:[lit isKindOf:Character]) ifTrue:[
+ lit storeOn:aStream
+ ] ifFalse:[
+ (lit isKindOf:Array) ifTrue:[
+ aStream nextPut:$(.
+ lit storeOn:aStream.
+ aStream nextPut:$)
+ ] ifFalse:[
+ (lit isBehavior "isKindOf:Class") ifTrue:[
+ aStream nextPutAll:'(Smalltalk at:#'.
+ n := lit name.
+ (lit isMeta "isKindOf:Metaclass") ifTrue:[
+ n := (n copyFrom:1 to:(n size - 5)) , ') class'
+ ] ifFalse:[
+ n := n , ')'
+ ].
+ aStream nextPutAll:n
+ ] ifFalse:[
+ self error:('invalid literal ' , lit class name)
+ ]
+ ]
+ ]
+ ].
+ aStream nextPut:$!!.
+ index := index + 1
+ ]
+!
+
+binaryFileOutOn:aStream
+ byteCode isNil ifTrue:[
+ self notify:'no bytecodes to fileout'.
+ ^ self
+ ].
+ self binaryFileOutLiteralsOn:aStream.
+
+ flags storeOn:aStream.
+ aStream nextPut:$!!.
+
+ byteCode size storeOn:aStream.
+ aStream nextPut:$!!.
+ aStream nextPutBytes:(byteCode size) from:byteCode
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MiniDebug.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,297 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#MiniDebugger
+ instanceVariableNames:'tracing stepping traceBlock'
+ classVariableNames: 'theOneAndOnlyDebugger'
+ poolDictionaries:''
+ category:'System-Support'
+!
+
+MiniDebugger comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+a primitive (non graphical) debugger for use on systems without
+graphics or when the real debugger dies (i.e. an error occurs in
+the graphical debugger).
+
+%W% %E%
+'!
+
+!MiniDebugger class methodsFor: 'instance creation'!
+
+new
+ theOneAndOnlyDebugger printNL.
+ theOneAndOnlyDebugger isNil ifTrue:[
+ theOneAndOnlyDebugger := self basicNew initialize
+ ].
+ ^ theOneAndOnlyDebugger
+!
+
+singleStep:aBlock
+ |aDebugger|
+
+ aDebugger := self new stepping.
+ StepInterruptHandler := aDebugger.
+ StepInterruptPending := true.
+ InterruptPending := true.
+ aBlock value.
+ StepInterruptPending := nil
+!
+
+trace:aBlock
+ self trace:aBlock with:[:where | where printNewline]
+!
+
+trace:aBlock on:aStream
+ self trace:aBlock with:[:where | where printString printOn:aStream.
+ aStream cr]
+!
+
+trace:aBlock with:aTraceBlock
+ |aDebugger|
+
+ aDebugger := self new tracingWith:aTraceBlock.
+ ObjectMemory flushInlineCaches.
+ StepInterruptHandler := aDebugger.
+ StepInterruptPending := true.
+ InterruptPending := true.
+ aBlock value.
+ StepInterruptPending := nil.
+ ^ nil
+!
+
+enterWithMessage:aString
+ |aDebugger|
+
+ StepInterruptPending := nil.
+ aString printNewline.
+ aDebugger := self new.
+ aDebugger enter.
+ ^ nil
+! !
+
+!MiniDebugger methodsFor: 'initialization'!
+
+initialize
+ traceBlock := nil.
+ tracing := false.
+ stepping := false
+! !
+
+!MiniDebugger methodsFor: 'private'!
+
+stepping
+ traceBlock := nil.
+ tracing := false.
+ stepping := true
+!
+
+tracingWith:aBlockOrNil
+ traceBlock := aBlockOrNil.
+ stepping := false.
+ tracing := true
+!
+
+getContext
+ |backtrace|
+ backtrace := thisContext.
+ (backtrace notNil) ifTrue: [
+ "remove Context getContext frame"
+ backtrace := backtrace sender.
+ "remove Debugger showContext frame"
+ backtrace := backtrace sender.
+ "remove Debugger commandLoop frame"
+ backtrace := backtrace sender.
+ "remove Debugger enter frame"
+ backtrace := backtrace sender
+ ].
+ ^ backtrace
+! !
+
+!MiniDebugger methodsFor: 'interrupt handling'!
+
+stepInterrupt
+ |where|
+
+ where := thisContext. "where is stepInterrupt context"
+ where notNil ifTrue:[
+ where := where sender "where is now interrupted methods context"
+ ].
+ stepping ifTrue:[
+ where notNil ifTrue:[
+ where fullPrint
+ ] ifFalse:[
+ 'stepInterrupt: no context' printNewline
+ ].
+ self enter
+ ] ifFalse:[
+ where notNil ifTrue:[
+ traceBlock notNil ifTrue:[
+ traceBlock value:where
+ ]
+ ] ifFalse:[
+ 'traceInterrupt: no context' printNewline
+ ].
+ ObjectMemory flushInlineCaches.
+ StepInterruptPending := true.
+ InterruptPending := true
+ ]
+!
+
+enter
+ |cmd|
+
+ cmd := self commandLoop.
+ (cmd == $s) ifTrue: [
+ self stepping.
+ ObjectMemory flushInlineCaches.
+ StepInterruptHandler := self.
+ StepInterruptPending := true.
+ InterruptPending := true
+ ].
+ (cmd == $t) ifTrue: [
+ traceBlock := [:where | where fullPrint].
+ ObjectMemory flushInlineCaches.
+ StepInterruptHandler := self.
+ StepInterruptPending := true.
+ InterruptPending := true
+ ].
+ (cmd == $c) ifTrue: [
+ stepping := false.
+ tracing := false.
+ StepInterruptPending := nil.
+ InterruptPending := nil
+ ].
+ ^ nil
+! !
+
+!MiniDebugger methodsFor: 'user commands'!
+
+commandLoop
+ |cmd done valid context|
+
+ done := false.
+ [done] whileFalse:[
+ valid := false.
+ cmd := self getCommand.
+ (cmd == $p) ifTrue:[
+ valid := true.
+ context isNil ifTrue: [
+ context := self getContext
+ ].
+ context notNil ifTrue:[
+ context fullPrintAll
+ ] ifFalse:[
+ 'no context' printNewline
+ ]
+ ].
+ (cmd == $r) ifTrue:[
+ valid := true.
+ context isNil ifTrue: [
+ context := self getContext
+ ].
+ context notNil ifTrue:[
+ "remove Debugger stepinterrupt/halt frame"
+ context sender receiver printNewline
+ ] ifFalse:[
+ 'no context - dont know receiver' printNewline
+ ]
+ ].
+ (cmd == $R) ifTrue:[
+ valid := true.
+ context isNil ifTrue: [
+ context := self getContext
+ ].
+ context notNil ifTrue:[
+ "remove Debugger stepinterrupt/halt frame"
+ context sender receiver storeOn:Stdout
+ ] ifFalse:[
+ 'no context - dont know receiver' printNewline
+ ]
+ ].
+ (cmd == $i) ifTrue:[
+ valid := true.
+ context isNil ifTrue: [
+ context := self getContext
+ ].
+ context notNil ifTrue:[
+ "remove Debugger stepinterrupt/halt frame"
+ context sender receiver inspect
+ ] ifFalse:[
+ 'no context - dont know receiver' printNewline
+ ]
+ ].
+ (cmd == $I) ifTrue:[
+ valid := true.
+ context isNil ifTrue: [
+ context := self getContext
+ ].
+ context notNil ifTrue:[
+ "remove Debugger stepinterrupt/halt frame"
+ self interpreterLoopWith:(context sender receiver)
+ ] ifFalse:[
+ 'no context - dont know receiver' printNewline.
+ self interpreterLoopWith:nil
+ ]
+ ].
+ context := nil.
+ (cmd == $c) ifTrue:[valid := true. done := true].
+ (cmd == $s) ifTrue:[valid := true. done := true].
+ (cmd == $t) ifTrue:[valid := true. done := true].
+ (cmd == $a) ifTrue:[Smalltalk fatalAbort].
+ (cmd == $x) ifTrue:[Smalltalk exit].
+ valid ifFalse: [
+ 'valid commands:' printNewline.
+ ' (c)ontinue' printNewline.
+ ' (s)tep' printNewline.
+ ' (t)race' printNewline.
+ ' (p)rintContext' printNewline.
+ ' (r)eceiver' printNewline.
+ ' (R)eceiver' printNewline.
+ ' (i)nspect' printNewline.
+ ' (I)nterpreter' printNewline.
+ ' (a)bort' printNewline.
+ ' (x)exit Smalltalk' printNewline
+ ]
+ ].
+ ^ cmd
+!
+
+getCommand
+ |cmd c|
+ 'MiniDebugger> ' print.
+ cmd := Character fromUser.
+ c := cmd.
+ [ c isEndOfLineCharacter ] whileFalse: [
+ c := Character fromUser
+ ].
+ ^ cmd
+!
+
+interpreterLoopWith:anObject
+ |line done|
+ 'read-eval-print loop; exit with empty line' printNewline.
+ done := false.
+ [done] whileFalse:[
+ line := Stdin nextLine.
+ (line size == 0) ifTrue:[
+ done := true
+ ] ifFalse:[
+ (Compiler evaluate:line
+ receiver:anObject
+ notifying:nil) printNewline
+ ]
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MiniDebugger.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,297 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#MiniDebugger
+ instanceVariableNames:'tracing stepping traceBlock'
+ classVariableNames: 'theOneAndOnlyDebugger'
+ poolDictionaries:''
+ category:'System-Support'
+!
+
+MiniDebugger comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+a primitive (non graphical) debugger for use on systems without
+graphics or when the real debugger dies (i.e. an error occurs in
+the graphical debugger).
+
+%W% %E%
+'!
+
+!MiniDebugger class methodsFor: 'instance creation'!
+
+new
+ theOneAndOnlyDebugger printNL.
+ theOneAndOnlyDebugger isNil ifTrue:[
+ theOneAndOnlyDebugger := self basicNew initialize
+ ].
+ ^ theOneAndOnlyDebugger
+!
+
+singleStep:aBlock
+ |aDebugger|
+
+ aDebugger := self new stepping.
+ StepInterruptHandler := aDebugger.
+ StepInterruptPending := true.
+ InterruptPending := true.
+ aBlock value.
+ StepInterruptPending := nil
+!
+
+trace:aBlock
+ self trace:aBlock with:[:where | where printNewline]
+!
+
+trace:aBlock on:aStream
+ self trace:aBlock with:[:where | where printString printOn:aStream.
+ aStream cr]
+!
+
+trace:aBlock with:aTraceBlock
+ |aDebugger|
+
+ aDebugger := self new tracingWith:aTraceBlock.
+ ObjectMemory flushInlineCaches.
+ StepInterruptHandler := aDebugger.
+ StepInterruptPending := true.
+ InterruptPending := true.
+ aBlock value.
+ StepInterruptPending := nil.
+ ^ nil
+!
+
+enterWithMessage:aString
+ |aDebugger|
+
+ StepInterruptPending := nil.
+ aString printNewline.
+ aDebugger := self new.
+ aDebugger enter.
+ ^ nil
+! !
+
+!MiniDebugger methodsFor: 'initialization'!
+
+initialize
+ traceBlock := nil.
+ tracing := false.
+ stepping := false
+! !
+
+!MiniDebugger methodsFor: 'private'!
+
+stepping
+ traceBlock := nil.
+ tracing := false.
+ stepping := true
+!
+
+tracingWith:aBlockOrNil
+ traceBlock := aBlockOrNil.
+ stepping := false.
+ tracing := true
+!
+
+getContext
+ |backtrace|
+ backtrace := thisContext.
+ (backtrace notNil) ifTrue: [
+ "remove Context getContext frame"
+ backtrace := backtrace sender.
+ "remove Debugger showContext frame"
+ backtrace := backtrace sender.
+ "remove Debugger commandLoop frame"
+ backtrace := backtrace sender.
+ "remove Debugger enter frame"
+ backtrace := backtrace sender
+ ].
+ ^ backtrace
+! !
+
+!MiniDebugger methodsFor: 'interrupt handling'!
+
+stepInterrupt
+ |where|
+
+ where := thisContext. "where is stepInterrupt context"
+ where notNil ifTrue:[
+ where := where sender "where is now interrupted methods context"
+ ].
+ stepping ifTrue:[
+ where notNil ifTrue:[
+ where fullPrint
+ ] ifFalse:[
+ 'stepInterrupt: no context' printNewline
+ ].
+ self enter
+ ] ifFalse:[
+ where notNil ifTrue:[
+ traceBlock notNil ifTrue:[
+ traceBlock value:where
+ ]
+ ] ifFalse:[
+ 'traceInterrupt: no context' printNewline
+ ].
+ ObjectMemory flushInlineCaches.
+ StepInterruptPending := true.
+ InterruptPending := true
+ ]
+!
+
+enter
+ |cmd|
+
+ cmd := self commandLoop.
+ (cmd == $s) ifTrue: [
+ self stepping.
+ ObjectMemory flushInlineCaches.
+ StepInterruptHandler := self.
+ StepInterruptPending := true.
+ InterruptPending := true
+ ].
+ (cmd == $t) ifTrue: [
+ traceBlock := [:where | where fullPrint].
+ ObjectMemory flushInlineCaches.
+ StepInterruptHandler := self.
+ StepInterruptPending := true.
+ InterruptPending := true
+ ].
+ (cmd == $c) ifTrue: [
+ stepping := false.
+ tracing := false.
+ StepInterruptPending := nil.
+ InterruptPending := nil
+ ].
+ ^ nil
+! !
+
+!MiniDebugger methodsFor: 'user commands'!
+
+commandLoop
+ |cmd done valid context|
+
+ done := false.
+ [done] whileFalse:[
+ valid := false.
+ cmd := self getCommand.
+ (cmd == $p) ifTrue:[
+ valid := true.
+ context isNil ifTrue: [
+ context := self getContext
+ ].
+ context notNil ifTrue:[
+ context fullPrintAll
+ ] ifFalse:[
+ 'no context' printNewline
+ ]
+ ].
+ (cmd == $r) ifTrue:[
+ valid := true.
+ context isNil ifTrue: [
+ context := self getContext
+ ].
+ context notNil ifTrue:[
+ "remove Debugger stepinterrupt/halt frame"
+ context sender receiver printNewline
+ ] ifFalse:[
+ 'no context - dont know receiver' printNewline
+ ]
+ ].
+ (cmd == $R) ifTrue:[
+ valid := true.
+ context isNil ifTrue: [
+ context := self getContext
+ ].
+ context notNil ifTrue:[
+ "remove Debugger stepinterrupt/halt frame"
+ context sender receiver storeOn:Stdout
+ ] ifFalse:[
+ 'no context - dont know receiver' printNewline
+ ]
+ ].
+ (cmd == $i) ifTrue:[
+ valid := true.
+ context isNil ifTrue: [
+ context := self getContext
+ ].
+ context notNil ifTrue:[
+ "remove Debugger stepinterrupt/halt frame"
+ context sender receiver inspect
+ ] ifFalse:[
+ 'no context - dont know receiver' printNewline
+ ]
+ ].
+ (cmd == $I) ifTrue:[
+ valid := true.
+ context isNil ifTrue: [
+ context := self getContext
+ ].
+ context notNil ifTrue:[
+ "remove Debugger stepinterrupt/halt frame"
+ self interpreterLoopWith:(context sender receiver)
+ ] ifFalse:[
+ 'no context - dont know receiver' printNewline.
+ self interpreterLoopWith:nil
+ ]
+ ].
+ context := nil.
+ (cmd == $c) ifTrue:[valid := true. done := true].
+ (cmd == $s) ifTrue:[valid := true. done := true].
+ (cmd == $t) ifTrue:[valid := true. done := true].
+ (cmd == $a) ifTrue:[Smalltalk fatalAbort].
+ (cmd == $x) ifTrue:[Smalltalk exit].
+ valid ifFalse: [
+ 'valid commands:' printNewline.
+ ' (c)ontinue' printNewline.
+ ' (s)tep' printNewline.
+ ' (t)race' printNewline.
+ ' (p)rintContext' printNewline.
+ ' (r)eceiver' printNewline.
+ ' (R)eceiver' printNewline.
+ ' (i)nspect' printNewline.
+ ' (I)nterpreter' printNewline.
+ ' (a)bort' printNewline.
+ ' (x)exit Smalltalk' printNewline
+ ]
+ ].
+ ^ cmd
+!
+
+getCommand
+ |cmd c|
+ 'MiniDebugger> ' print.
+ cmd := Character fromUser.
+ c := cmd.
+ [ c isEndOfLineCharacter ] whileFalse: [
+ c := Character fromUser
+ ].
+ ^ cmd
+!
+
+interpreterLoopWith:anObject
+ |line done|
+ 'read-eval-print loop; exit with empty line' printNewline.
+ done := false.
+ [done] whileFalse:[
+ line := Stdin nextLine.
+ (line size == 0) ifTrue:[
+ done := true
+ ] ifFalse:[
+ (Compiler evaluate:line
+ receiver:anObject
+ notifying:nil) printNewline
+ ]
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MiniIns.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,123 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#MiniInspector
+ instanceVariableNames:'inspectedObject'
+ classVariableNames: ''
+ poolDictionaries:''
+ category:'System-Support'
+!
+
+MiniInspector comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+a primitive (non graphical) inspector for use on systems without
+graphics or when the real inspector dies.
+
+%W% %E%
+'!
+
+!MiniInspector class methodsFor:'instance creation'!
+
+openOn:anObject
+ |anInspector|
+ anInspector := (self new) initializeFor:anObject.
+ anInspector enter
+! !
+
+!MiniInspector methodsFor:'private'!
+
+initializeFor:anObject
+ inspectedObject := anObject.
+ ^self
+!
+
+enter
+ self commandLoop.
+ ^ nil
+!
+
+commandLoop
+ |cmd done valid|
+
+ 'Inspector:' printNewline.
+ ' ' printNewline.
+ done := false.
+ [done] whileFalse:[
+ valid := false.
+ cmd := self getCommand.
+ (cmd == $0) ifTrue:[
+ valid := true.
+ self inspectInstvar:0 of:inspectedObject
+ ].
+ (cmd == $1) ifTrue:[
+ valid := true.
+ self inspectInstvar:1 of:inspectedObject
+ ].
+ (cmd == $2) ifTrue:[
+ valid := true.
+ self inspectInstvar:2 of:inspectedObject
+ ].
+ (cmd == $3) ifTrue:[
+ valid := true.
+ self inspectInstvar:3 of:inspectedObject
+ ].
+ (cmd == $i) ifTrue:[
+ valid := true.
+ self printInstVarsOf:inspectedObject
+ ].
+ (cmd == $p) ifTrue:[
+ valid := true.
+ inspectedObject printNewline
+ ].
+ (cmd == $q) ifTrue:[valid := true. done := true ].
+ valid ifFalse: [
+ 'valid commands:' printNewline.
+ ' (i)nstvars' printNewline.
+ ' (p)rint' printNewline.
+ ' (1-9) inspect instvar' printNewline.
+ ' (q)uit' printNewline
+ ]
+ ].
+ ^ cmd
+!
+
+getCommand
+ |cmd c|
+ 'inspector> ' print.
+ cmd := Character fromUser.
+ c := cmd.
+ [ c isEndOfLineCharacter ] whileFalse: [
+ c := Character fromUser
+ ].
+ ^ cmd
+!
+
+printInstVarsOf:anObject
+ |n|
+ n := anObject class instSize.
+ 'number of instvars: ' print. n printNewline.
+ 1 to:n do:[:i |
+ ' ' print. i print. ': ' print.
+ (anObject instVarAt:i) printNewline
+ ]
+!
+
+inspectInstvar:which of:anObject
+ which > (anObject class instSize) ifTrue:[
+ 'invalid instvar' printNewline
+ ] ifFalse: [
+ (anObject instVarAt:which) inspect
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MiniInspector.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,123 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#MiniInspector
+ instanceVariableNames:'inspectedObject'
+ classVariableNames: ''
+ poolDictionaries:''
+ category:'System-Support'
+!
+
+MiniInspector comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+a primitive (non graphical) inspector for use on systems without
+graphics or when the real inspector dies.
+
+%W% %E%
+'!
+
+!MiniInspector class methodsFor:'instance creation'!
+
+openOn:anObject
+ |anInspector|
+ anInspector := (self new) initializeFor:anObject.
+ anInspector enter
+! !
+
+!MiniInspector methodsFor:'private'!
+
+initializeFor:anObject
+ inspectedObject := anObject.
+ ^self
+!
+
+enter
+ self commandLoop.
+ ^ nil
+!
+
+commandLoop
+ |cmd done valid|
+
+ 'Inspector:' printNewline.
+ ' ' printNewline.
+ done := false.
+ [done] whileFalse:[
+ valid := false.
+ cmd := self getCommand.
+ (cmd == $0) ifTrue:[
+ valid := true.
+ self inspectInstvar:0 of:inspectedObject
+ ].
+ (cmd == $1) ifTrue:[
+ valid := true.
+ self inspectInstvar:1 of:inspectedObject
+ ].
+ (cmd == $2) ifTrue:[
+ valid := true.
+ self inspectInstvar:2 of:inspectedObject
+ ].
+ (cmd == $3) ifTrue:[
+ valid := true.
+ self inspectInstvar:3 of:inspectedObject
+ ].
+ (cmd == $i) ifTrue:[
+ valid := true.
+ self printInstVarsOf:inspectedObject
+ ].
+ (cmd == $p) ifTrue:[
+ valid := true.
+ inspectedObject printNewline
+ ].
+ (cmd == $q) ifTrue:[valid := true. done := true ].
+ valid ifFalse: [
+ 'valid commands:' printNewline.
+ ' (i)nstvars' printNewline.
+ ' (p)rint' printNewline.
+ ' (1-9) inspect instvar' printNewline.
+ ' (q)uit' printNewline
+ ]
+ ].
+ ^ cmd
+!
+
+getCommand
+ |cmd c|
+ 'inspector> ' print.
+ cmd := Character fromUser.
+ c := cmd.
+ [ c isEndOfLineCharacter ] whileFalse: [
+ c := Character fromUser
+ ].
+ ^ cmd
+!
+
+printInstVarsOf:anObject
+ |n|
+ n := anObject class instSize.
+ 'number of instvars: ' print. n printNewline.
+ 1 to:n do:[:i |
+ ' ' print. i print. ': ' print.
+ (anObject instVarAt:i) printNewline
+ ]
+!
+
+inspectInstvar:which of:anObject
+ which > (anObject class instSize) ifTrue:[
+ 'invalid instvar' printNewline
+ ] ifFalse: [
+ (anObject instVarAt:which) inspect
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NPExtStr.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,107 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ExternalStream subclass:#NonPositionableExternalStream
+ instanceVariableNames:'typeIfStandard'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams-External'
+!
+
+NonPositionableExternalStream comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+this one stands all non-positionable external streams; there are
+terminal streams, pipe streams etc.
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+%}
+
+!NonPositionableExternalStream class methodsFor:'instance creation'!
+
+forStdin
+ ^ self basicNew initializeForStdin
+!
+
+forStdout
+ ^ self basicNew initializeForStdout
+!
+
+forStderr
+ ^ self basicNew initializeForStderr
+! !
+
+!NonPositionableExternalStream methodsFor:'private'!
+
+initializeForStdin
+ mode := #readonly.
+ typeIfStandard := #Stdin.
+%{
+ _INST(filePointer) = MKOBJ(stdin);
+%}
+!
+
+initializeForStdout
+ mode := #readwrite.
+ unBuffered := true.
+ typeIfStandard := #Stdout.
+%{
+ _INST(filePointer) = MKOBJ(stdout);
+%}
+!
+
+initializeForStderr
+ mode := #readwrite.
+ unBuffered := true.
+ typeIfStandard := #Stderr.
+%{
+ _INST(filePointer) = MKOBJ(stderr);
+%}
+!
+
+reOpen
+ "if I am one of the standard streams, reopen is easy"
+
+'reopen NPExtStr' printNewline.
+ (typeIfStandard == #Stdin) ifTrue:[
+ ^ self initializeForStdin
+ ].
+ (typeIfStandard == #Stdout) ifTrue:[
+ ^ self initializeForStdout
+ ].
+ (typeIfStandard == #Stderr) ifTrue:[
+ ^ self initializeForStderr
+ ].
+ ^ super reOpen
+! !
+
+!NonPositionableExternalStream methodsFor:'error handling'!
+
+errorNotPositionable
+ ^ self error:'positioning not allowd'
+! !
+
+!NonPositionableExternalStream methodsFor:'positioning'!
+
+position
+ ^ 0
+!
+
+position:anInteger
+ self errorNotPositionable
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NonPositionableExternalStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,107 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ExternalStream subclass:#NonPositionableExternalStream
+ instanceVariableNames:'typeIfStandard'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams-External'
+!
+
+NonPositionableExternalStream comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+this one stands all non-positionable external streams; there are
+terminal streams, pipe streams etc.
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+%}
+
+!NonPositionableExternalStream class methodsFor:'instance creation'!
+
+forStdin
+ ^ self basicNew initializeForStdin
+!
+
+forStdout
+ ^ self basicNew initializeForStdout
+!
+
+forStderr
+ ^ self basicNew initializeForStderr
+! !
+
+!NonPositionableExternalStream methodsFor:'private'!
+
+initializeForStdin
+ mode := #readonly.
+ typeIfStandard := #Stdin.
+%{
+ _INST(filePointer) = MKOBJ(stdin);
+%}
+!
+
+initializeForStdout
+ mode := #readwrite.
+ unBuffered := true.
+ typeIfStandard := #Stdout.
+%{
+ _INST(filePointer) = MKOBJ(stdout);
+%}
+!
+
+initializeForStderr
+ mode := #readwrite.
+ unBuffered := true.
+ typeIfStandard := #Stderr.
+%{
+ _INST(filePointer) = MKOBJ(stderr);
+%}
+!
+
+reOpen
+ "if I am one of the standard streams, reopen is easy"
+
+'reopen NPExtStr' printNewline.
+ (typeIfStandard == #Stdin) ifTrue:[
+ ^ self initializeForStdin
+ ].
+ (typeIfStandard == #Stdout) ifTrue:[
+ ^ self initializeForStdout
+ ].
+ (typeIfStandard == #Stderr) ifTrue:[
+ ^ self initializeForStderr
+ ].
+ ^ super reOpen
+! !
+
+!NonPositionableExternalStream methodsFor:'error handling'!
+
+errorNotPositionable
+ ^ self error:'positioning not allowd'
+! !
+
+!NonPositionableExternalStream methodsFor:'positioning'!
+
+position
+ ^ 0
+!
+
+position:anInteger
+ self errorNotPositionable
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Number.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,275 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ArithmeticValue subclass:#Number
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Magnitude-Numbers'
+!
+
+Number comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+abstract superclass for all kinds of numbers
+
+%W% %E%
+'!
+
+! Number methodsFor:'converting' !
+
+@ aNumber
+ "return a Point with the receiver as x-coordinate and the argument
+ as y-coordinate"
+
+%{ /* NOCONTEXT */
+
+ extern char *newNextPtr, *newEndPtr;
+
+ if (_CanDoQuickNew(sizeof(struct point))) {
+ OBJ newPoint;
+
+ _qCheckedAlignedNew(newPoint, sizeof(struct point), __context);
+ _InstPtr(newPoint)->o_class = Point;
+ _PointInstPtr(newPoint)->p_x = self;
+ _PointInstPtr(newPoint)->p_y = aNumber;
+ /* no store check needed - its definitely in newSpace */
+ RETURN ( newPoint );
+ }
+%}
+.
+ ^ Point x:self y:aNumber
+!
+
+asPoint
+ "return a new Point with the receiver as all coordinates;
+ often used to supply the same value in two dimensions, as with
+ symmetrical gridding or scaling."
+
+%{ /* NOCONTEXT */
+
+ extern char *newNextPtr, *newEndPtr;
+
+ if (_CanDoQuickNew(sizeof(struct point))) {
+ OBJ newPoint;
+
+ _qCheckedAlignedNew(newPoint, sizeof(struct point), __context);
+ _InstPtr(newPoint)->o_class = Point;
+ _PointInstPtr(newPoint)->p_x = self;
+ _PointInstPtr(newPoint)->p_y = self;
+ /* no store check needed - its definitely in newSpace */
+ RETURN ( newPoint );
+ }
+%}
+.
+ ^ Point x:self y:self
+!
+
+degreesToRadians
+ "interpreting the receiver as radians, return the degrees"
+
+ ^ (self * (Float pi)) / 180.0
+!
+
+radiansToDegrees
+ "interpreting the receiver as degrees, return the radians"
+
+ ^ (self * 180.0) / (Float pi)
+!
+
+coerce:aNumber
+ ^ self subclassResponsibility
+!
+
+generality
+ ^ 40
+!
+
+retry:aSymbol coercing:aNumber
+ "Arithmetic represented by the binary operator, aSymbol,
+ could not be performed with the receiver and the argument,
+ aNumber, because of the differences in representation. Coerce either
+ the receiver or the argument, depending on which has higher generality, and
+ try again. If the operation is comapre for same value (=), return false if
+ the argument is not a Number.
+ If the generalities are the same, create an error message."
+
+ |myGenerality otherGenerality|
+
+ (aSymbol == #=) ifTrue:[
+ (aNumber respondsTo:#generality) ifFalse:[^ false]
+ ] ifFalse:[
+ (aNumber respondsTo:#generality) ifFalse:[
+ self error:'retry:coercing: argument is not a number'.
+ ^ self
+ ]
+ ].
+ myGenerality := self generality.
+ otherGenerality := aNumber generality.
+ (myGenerality > otherGenerality) ifTrue:[
+ ^ self perform:aSymbol with:(self coerce:aNumber)
+ ].
+ (myGenerality < otherGenerality) ifTrue:[
+ ^ (aNumber coerce:self) perform:aSymbol with:aNumber
+ ].
+ self error:'retry:coercing: oops - same generality'
+! !
+
+!Number methodsFor:'printing & storing'!
+
+storeString
+ ^ self printString
+! !
+
+!Number methodsFor:'intervals'!
+
+to:stop
+ "return an interval from receiver up to the argument, incrementing by 1"
+
+ ^ Interval from:self to:stop
+!
+
+to:stop by:step
+ "return an interval from receiver up to the argument, incrementing by step"
+
+ ^ Interval from:self to:stop by:step
+! !
+
+!Number methodsFor:'iteration'!
+
+timesRepeat:aBlock
+ "evaluate the argument, aBlock self times"
+
+ |count|
+
+ count := self.
+ [count > 0] whileTrue:[
+ aBlock value.
+ count := count - 1
+ ]
+!
+
+to:stop do:aBlock
+ "create an interval from the receiver up to the argument, incrementing by 1.
+ For each element of the interval, evaluate aBlock"
+
+ |tmp|
+
+ tmp := self.
+ [tmp <= stop] whileTrue:[
+ aBlock value:tmp.
+ tmp := tmp+1
+ ]
+!
+
+to:stop by:incr do:aBlock
+ "create an interval from the receiver up to the argument stop, incrementing
+ by step. For each element of the interval, evaluate aBlock"
+
+ |tmp|
+
+ tmp := self.
+ (incr > 0) ifTrue:[
+ [tmp <= stop] whileTrue:[
+ aBlock value:tmp.
+ tmp := tmp+incr
+ ]
+ ] ifFalse:[
+ [tmp >= stop] whileTrue:[
+ aBlock value:tmp.
+ tmp := tmp+incr
+ ]
+ ]
+! !
+
+!Number class methodsFor:'private'!
+
+readMantissaFrom:aStream radix:radix
+ "helper for readFrom: -
+ return the mantissa from the (character-)stream aStream;
+ no whitespace-skipping; error if no number available"
+
+ |nextChar value factor|
+
+ value := 0.0.
+ factor := 1.0 / radix.
+ nextChar := aStream peek.
+ [nextChar notNil and:[nextChar isDigitRadix:radix]] whileTrue:[
+ value := value + (nextChar digitValue * factor).
+ factor := factor / radix.
+ nextChar := aStream nextPeek
+ ].
+ ^ value
+! !
+
+!Number class methodsFor:'instance creation'!
+
+readFrom:aStream
+ "return the next Number from the (character-)stream aStream;
+ skipping all whitespace first; return nil if no number"
+
+ |nextChar radix value negative signExp|
+
+ nextChar := aStream skipSeparators.
+ (nextChar == $-) ifTrue:[
+ negative := true.
+ nextChar := aStream nextPeek
+ ] ifFalse:[
+ negative := false
+ ].
+ nextChar isDigit ifFalse:[ ^ nil].
+ value := Integer readFrom:aStream radix:10.
+ nextChar := aStream peek.
+ ((nextChar == $r) or:[ nextChar == $R]) ifTrue:[
+ aStream next.
+ radix := value.
+ value := Integer readFrom:aStream radix:radix
+ ] ifFalse:[
+ radix := 10
+ ].
+ (nextChar == $.) ifTrue:[
+ nextChar := aStream nextPeek.
+ (nextChar notNil and:[nextChar isDigitRadix:radix]) ifTrue:[
+ value := value asFloat + (Number readMantissaFrom:aStream
+ radix:radix).
+ nextChar := aStream peek
+ ]
+ ].
+ ((nextChar == $e) or:[nextChar == $E]) ifTrue:[
+ nextChar := aStream nextPeek.
+ signExp := 1.
+ (nextChar == $+) ifTrue:[
+ nextChar := aStream nextPeek
+ ] ifFalse:[
+ (nextChar == $-) ifTrue:[
+ nextChar := aStream nextPeek.
+ signExp := -1
+ ]
+ ].
+ (nextChar notNil and:[(nextChar isDigitRadix:radix)]) ifTrue:[
+ value := value asFloat
+ * (10.0 raisedToInteger:
+ ((Integer readFrom:aStream
+ radix:radix) * signExp))
+ ]
+ ].
+ negative ifTrue:[
+ ^ value negated
+ ].
+ ^ value
+
+ "Number readFromString:'54.32e-01'"
+ "Number readFromString:'12345678901234567890'"
+ "Number readFromString:'16rAAAAFFFFAAAAFFFF'"
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjMem.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,367 @@
+"
+ COPYRIGHT (c) 1992-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#ObjectMemory
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'System-Support'
+!
+
+ObjectMemory comment:'
+
+COPYRIGHT (c) 1992 -93 by Claus Gittinger
+ All Rights Reserved
+
+This class contains access methods to the system memory -
+in previous versions this stuff used to be in the Smalltalk class.
+It has been separated for better overall structure.
+
+%W% %E%
+'!
+
+!ObjectMemory class methodsFor:'cache management'!
+
+flushInlineCachesForClass:aClass
+ "flush inlinecaches for calls to aClass"
+
+%{ /* NOCONTEXT */
+ __flushInlineCachesFor(aClass);
+%}
+!
+
+flushInlineCachesWithArgs:nargs
+ "flush inlinecaches for calls with nargs arguments"
+
+%{ /* NOCONTEXT */
+ __flushInlineCaches(_intVal(nargs));
+%}
+!
+
+flushInlineCachesFor:aClass withArgs:nargs
+ "flush inlinecaches for calls to aClass with nargs arguments"
+
+%{ /* NOCONTEXT */
+ __flushInlineCachesForAndNargs(aClass, _intVal(nargs));
+%}
+!
+
+flushInlineCaches
+ "flush all inlinecaches"
+
+%{ /* NOCONTEXT */
+ __flushAllInlineCaches();
+%}
+!
+
+flushMethodCacheFor:aClass
+ "flush the method cache for sends to aClass"
+
+%{ /* NOCONTEXT */
+ __flushMethodCacheFor(aClass);
+%}
+!
+
+flushMethodCache
+ "flush the method cache"
+
+%{ /* NOCONTEXT */
+ __flushMethodCache();
+%}
+!
+
+flushCaches
+ "flush method and inline caches"
+
+%{ /* NOCONTEXT */
+ __flushMethodCache();
+ __flushAllInlineCaches();
+%}
+! !
+
+!ObjectMemory class methodsFor:'enumeration'!
+
+allObjectsDo:aBlock
+ "evaluate the argument, aBlock for all objects in the system"
+%{
+#ifdef THIS_CONTEXT
+ __allObjectsDo(&aBlock);
+#else
+ __allObjectsDo(&aBlock, __context);
+#endif
+%}
+! !
+
+!ObjectMemory class methodsFor:'queries'!
+
+newSpaceUsed
+ "return the number of bytes allocated for new objects"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(__newSpaceUsed()) );
+%}
+ "ObjectMemory newSpaceUsed"
+!
+
+oldSpaceUsed
+ "return the number of bytes allocated for old objects"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(__oldSpaceUsed()) );
+%}
+ "ObjectMemory oldSpaceUsed"
+!
+
+bytesUsed
+ "return the number of bytes allocated for objects -
+ this number is not exact, since some objects may be dead"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(__oldSpaceUsed() + __newSpaceUsed()) );
+%}
+ "ObjectMemory bytesUsed"
+!
+
+numberOfObjects
+ "return the number of objects in the system"
+
+ |tally|
+
+ tally := 0.
+ self allObjectsDo:[:obj | tally := tally + 1].
+ ^ tally
+
+ "ObjectMemory numberOfObjects"
+!
+
+printReferences:anObject
+ "debugging: print referents to anObject"
+
+%{
+ _printRefChain(__context, anObject);
+%}
+!
+
+whoReferences:anObject
+ "return a collection of objects referencing the argument, anObject"
+
+ |aCollection|
+
+ aCollection := IdentitySet new.
+ self allObjectsDo:[:o |
+ (o references:anObject) ifTrue:[
+ aCollection add:o
+ ]
+ ].
+ (aCollection size == 0) ifTrue:[
+ "actually this cannot happen - there is always one"
+ ^ nil
+ ].
+ ^ aCollection
+! !
+
+!ObjectMemory class methodsFor:'garbage collector control'!
+
+garbageCollect
+ "search for and free garbage in the oldSpace
+ (newSpace is cleaned automatically)
+ - can take a long time if paging is involved
+ - when no paging is involved, its faster than I thought :-)"
+%{
+ __garbageCollect(__context);
+%}
+
+ "ObjectMemory garbageCollect"
+!
+
+scavenge
+ "for debugging only - collect newspace stuff"
+%{
+ nonTenuringScavenge(__context);
+%}
+
+ "ObjectMemory scavenge"
+!
+
+tenure
+ "forcae all new stuff into old-space"
+%{
+ tenure(__context);
+%}
+
+ "ObjectMemory tenure"
+!
+
+markAndSweep
+ "mark/sweep garbage collector"
+
+%{
+ markAndSweep(__context);
+%}
+
+ "ObjectMemory markAndSweep"
+!
+
+gcStep
+ "one incremental garbage collect step"
+%{
+ incrGCstep(__context);
+%}
+!
+
+turnOffGarbageCollector
+ "turn off garbage collector.
+ this method is somewhat dangerous: if collector is turned off,
+ and too many objects are created, the system may run into trouble.
+ Use this only for measurement purposes or when realtime behavior
+ is required for a limited time period. No waranty"
+%{
+ allocForceSpace(0);
+%}
+!
+
+turnOnGarbageCollector
+ "turn it on again"
+
+%{
+ allocForceSpace(1);
+%}
+! !
+
+!ObjectMemory class methodsFor:'system management'!
+
+loadClassBinary:aClassName
+ "find the object file for aClassName and -if found - load it;
+ this one loads precompiled object files"
+
+ |fName newClass upd|
+
+ fName := self fileNameForClass:aClassName.
+ fName notNil ifTrue:[
+ upd := Class updateChanges:false.
+ [
+ self loadBinary:(fName , '.o')
+ ] valueNowOrOnUnwindDo:[
+ Class updateChanges:upd
+ ].
+ newClass := self at:(aClassName asSymbol).
+ (newClass notNil and:[newClass implements:#initialize]) ifTrue:[
+ newClass initialize
+ ]
+ ]
+!
+
+snapShot
+ "create a snapshot"
+
+ ImageName isNil ifTrue:[
+ ImageName := 'st.img'
+ ].
+ self snapShotOn:ImageName
+
+ "ObjectMemory snapShot"
+!
+
+snapShotOn:aFileName
+ "create a snapshot in the given file"
+
+ "give others a chance to fix things"
+ self changed:#save.
+%{
+ OBJ __snapShotOn();
+
+ if (_isString(aFileName)) {
+ RETURN ( __snapShotOn(__context, _stringVal(aFileName)) );
+ }
+%}
+.
+ ^ self primitiveFailed
+
+ "ObjectMemory snapShotOn:'myimage.img'"
+!
+
+applicationImageOn:aFileName for:startupClass selector:startupSelector
+ "create a snapshot which will come up without any views
+ but starts up an application by sending startupClass the startupSelector"
+
+ |viewsKnown savedIdleBlocks savedTimeoutBlocks savedTranscript|
+
+ viewsKnown := Display knownViews.
+ savedIdleBlocks := Display idleBlocks.
+ savedTimeoutBlocks := Display timeOutBlocks.
+ savedTranscript := Transcript.
+
+ "a kludge: save image with modified knownViews ..."
+
+ Display knownViews:nil.
+ Display idleBlocks:nil.
+ Display timeOutBlocks:nil.
+ Transcript := Stderr.
+ StartupClass := startupClass.
+ StartupSelector := startupSelector.
+
+ self snapShotOn:aFileName.
+
+ StartupClass := nil.
+ StartupSelector := nil.
+ Transcript := savedTranscript.
+ Display knownViews:viewsKnown.
+ Display idleBlocks:savedIdleBlocks.
+ Display timeOutBlocks:savedTimeoutBlocks
+
+ "ObjectMemory applicationImageOn:'draw.img' for:DrawTool selector:#start"
+ "ObjectMemory applicationImageOn:'pm.img' for:PMSimulator selector:#start"
+!
+
+minimumApplicationImageOn:aFileName for:startupClass selector:startupSelector
+ "create a snapshot which will come up without any views
+ but starts up an application by sending startupClass the startupSelector.
+ All unneeded info is stripped from the saved image."
+
+ "create a temporary image, for continuation"
+ self snapShotOn:'temp.img'.
+
+ Display knownViews do:[:aView |
+ aView notNil ifTrue:[
+ aView superView isNil ifTrue:[
+ aView destroy
+ ]
+ ]
+ ].
+
+ self stripImage.
+
+ self applicationImageOn:aFileName for:startupClass selector:startupSelector.
+
+ "continue in old image"
+
+ OperatingSystem exec:(Arguments at:1)
+ withArguments:#('smalltalk' '-i' 'temp.img') , (Arguments copyFrom:2)
+
+ "ObjectMemory minimumApplicationImageOn:'clock1.img' for:Clock selector:#start"
+ "ObjectMemory applicationImageOn:'clock2.img' for:Clock selector:#start"
+!
+
+stripImage
+ "remove all unneeded stuff from the image - much more is possible here"
+
+ "remove all class comments"
+
+ Smalltalk allClassesDo:[:aClass |
+ aClass setComment:nil.
+ aClass methodDictionary do:[:aMethod |
+ aMethod source:''.
+ aMethod category:#none
+ ]
+ ].
+ self garbageCollect
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Object.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1863 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Object
+ instanceVariableNames:''
+ classVariableNames:'ErrorSignal HaltSignal
+ MessageNotUnderstoodSignal UserInterruptSignal
+ RecursionInterruptSignal ExceptionInterruptSignal
+ SubscriptOutOfBoundSignal NonIntegerIndexSignal
+ InformationSignal'
+ poolDictionaries:''
+ category:'Kernel-Objects'
+!
+
+Object comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+Class Object is the superclass of all other classes. Protocol common to
+all objects is defined here.
+Also some utility stuff (like notify) and error handling is implemented here.
+
+%W% %E%
+'!
+
+Smalltalk at:#ErrorRecursion put:false!
+Smalltalk at:#ErrorActive put:false!
+Smalltalk at:#ErrorHandler put:nil!
+Smalltalk at:#Dependencies put:nil!
+Smalltalk at:#SystemNotifier put:nil!
+Smalltalk at:#SystemWarningBox put:nil!
+Smalltalk at:#SystemInfoBox put:nil!
+Smalltalk at:#SystemConfirmer put:nil!
+
+!Object class methodsFor:'initialization'!
+
+initialize
+ "called only once - initialize signals"
+
+ ErrorSignal isNil ifTrue:[
+ ErrorSignal := (Signal new) mayProceed:true.
+ ErrorSignal notifierString:'error'.
+
+ HaltSignal := (Signal new) mayProceed:true.
+ HaltSignal notifierString:'halt encountered'.
+
+ MessageNotUnderstoodSignal := (Signal new) mayProceed:true.
+ MessageNotUnderstoodSignal notifierString:'message not understood'.
+
+ UserInterruptSignal := (Signal new) mayProceed:true.
+ UserInterruptSignal notifierString:'user Interrupt'.
+
+ RecursionInterruptSignal := (Signal new) mayProceed:false.
+ RecursionInterruptSignal notifierString:'recursion interrupt'.
+
+ ExceptionInterruptSignal := (Signal new) mayProceed:true.
+ ExceptionInterruptSignal notifierString:'exception Interrupt'.
+
+ SubscriptOutOfBoundSignal := (Signal new) mayProceed:false.
+ SubscriptOutOfBoundSignal notifierString:'subscript out of bounds'.
+
+ NonIntegerIndexSignal := (Signal new) mayProceed:false.
+ NonIntegerIndexSignal notifierString:'index must be integer'.
+
+ InformationSignal := (Signal new) mayProceed:true.
+ InformationSignal notifierString:'information'.
+
+ Dependencies := IdentityDictionary new.
+ ]
+
+ "Object initialize"
+! !
+
+!Object class methodsFor:'signal access'!
+
+errorSignal
+ "return the signal used for error/error: - messages"
+
+ ^ ErrorSignal
+!
+
+haltSignal
+ "return the signal used for halt/halt: - messages"
+
+ ^ HaltSignal
+!
+
+messageNotUnderstoodSignal
+ "return the signal used for doesNotUnderstand: - messages"
+
+ ^ MessageNotUnderstoodSignal
+!
+
+userInterruptSignal
+ "return the signal used for ^C interrupts"
+
+ ^ UserInterruptSignal
+!
+
+recursionInterruptSignal
+ "return the signal used for recursion overflow reporting"
+
+ ^ RecursionInterruptSignal
+!
+
+exceptionInterruptSignal
+ "return the signal used for exception (display errors) reporting"
+
+ ^ ExceptionInterruptSignal
+!
+
+subscriptOutOfBoundSignal
+ "return the signal used for subscript error reporting"
+
+ ^ SubscriptOutOfBoundSignal
+!
+
+nonIntegerIndexSignal
+ "return the signal used for bad subscript error reporting"
+
+ ^ NonIntegerIndexSignal
+!
+
+informationSignal
+ "return the signal used for informations"
+
+ ^ InformationSignal
+! !
+
+!Object methodsFor:'initialization'!
+
+initialize
+ "just to ignore initialize to objects which do not need it"
+
+ ^ self
+! !
+
+!Object methodsFor:'instance creation'!
+
+readFromString:aString
+ "create an object from its printed representation"
+
+ ^ self readFrom:(ReadStream on:aString)
+!
+
+readFrom:aStream
+ "read an objects printed representation from the argument,
+ aStream and return it."
+
+ |newObject|
+ newObject := Compiler evaluate:aStream.
+ (newObject isKindOf:self) ifFalse:[
+ self error:('expected ' , self name)
+ ].
+ ^ newObject
+! !
+
+!Object methodsFor:'system primitives'!
+
+become:anotherObject
+ "make all references to the receiver become references to anotherObject and vice-versa.
+ This may be an expensive (i.e. slow) operation, since in the worst case, the whole memory
+ has to be searched for references to the two objects. In general, using become: should be
+ avoided if possible since it may produce many strange effects.
+ This method fails, if the receiver or the argument is a SmallInteger or nil, or is a context
+ of a living method (i.e. one that has not already returned).
+ (notice that become: is not used by the system - the Collection classes have been rewritten
+ to not use it.)"
+%{
+ if (primBecome(self, anotherObject))
+ RETURN ( self );
+%}
+.
+ self primitiveFailed
+!
+
+address
+ "return the core address as an integer
+ - since objects may move around the returned value is invalid after the
+ next scavenge/collect, therefore use only for debugging."
+
+%{ /* NOCONTEXT */
+
+ if (! _isNonNilObject(self)) {
+ RETURN ( nil );
+ }
+ if ((_qSpace(self) != OLDSPACE) && (_qSpace(self) != STACKSPACE)) {
+ RETURN ( nil );
+ }
+ RETURN ( _MKSMALLINT( (int)self ) );
+%}
+! !
+
+!Object methodsFor:'queries'!
+
+size
+ "return the number of the receivers indexed instance variables;
+ this method may be redefined in subclasses"
+
+ ^ self basicSize
+!
+
+basicSize
+ "return the number of the receivers indexed instance variables
+ this method should NOT be redefined in any subclass"
+
+%{ /* NOCONTEXT */
+
+ register int nbytes;
+ register OBJ myClass;
+
+ /*
+ * notice the missing test for self beeing a nonNilObject -
+ * this can be done since basicSize is defined both in UndefinedObject
+ * and SmallInteger
+ */
+ myClass = _qClass(self);
+ nbytes = _qSize(self)
+ - OHDR_SIZE
+ - _intVal(_ClassInstPtr(myClass)->c_ninstvars) * sizeof(OBJ);
+
+ switch (_intVal(_ClassInstPtr(myClass)->c_flags) & ARRAYMASK) {
+ case BYTEARRAY:
+ RETURN ( _MKSMALLINT(nbytes / sizeof(char)) );
+
+ case WORDARRAY:
+ RETURN ( _MKSMALLINT(nbytes / sizeof(short)) );
+
+ case LONGARRAY:
+ RETURN ( _MKSMALLINT(nbytes / sizeof(long)) );
+
+ case FLOATARRAY:
+ RETURN ( _MKSMALLINT(nbytes / sizeof(float)) );
+
+ case DOUBLEARRAY:
+ RETURN ( _MKSMALLINT(nbytes / sizeof(double)) );
+
+ case WKPOINTERARRAY:
+ case POINTERARRAY:
+ RETURN ( _MKSMALLINT(nbytes / sizeof(OBJ)) );
+ }
+%}
+.
+ ^ 0
+!
+
+objectSize
+ "return the size of the receiver in bytes - for debugging only"
+
+%{ /* NOCONTEXT */
+
+ RETURN ( _isNonNilObject(self) ? _MKSMALLINT(_qSize(self))
+ : _MKSMALLINT(0) )
+%}
+!
+
+isVariable
+ "return true if the receiver has indexed instance variables,
+ false otherwise"
+
+ ^ self class isVariable
+!
+
+isFixedSize
+ "return true if the receiver cannot grow - this will vanish once things
+ like Array and String learn how to grow ..."
+
+ ^ true
+!
+
+class
+ "return the receivers class"
+
+%{ /* NOCONTEXT */
+
+ RETURN ( _Class(self) );
+%}
+!
+
+species
+ "return a class which is similar to (or the same as) the receivers class.
+ This is used to create an appropriate object when creating derived
+ copies in the Collection classes (sometimes redefined)."
+
+ ^ self class
+!
+
+yourself
+ "return the receiver - used for cascades to return self at the end"
+
+ ^ self
+!
+
+isBehavior
+ "return true, if the receiver is some kind of class (i.e. behavior);
+ false is returned here - the method is redefined in Behavior"
+
+ ^ false
+!
+
+isMeta
+ "return true, if the receiver is some kind of metaclass;
+ false is returned here - the method is redefined in Metaclass"
+
+ ^ false
+!
+
+isBlock
+ "return true, iff the receiver is some kind of Block;
+ false returned here - the method is redefined in Block."
+
+ ^ false
+!
+
+isContext
+ "return true, iff the receiver is some kind of Context;
+ false returned here - the method is redefined in Context."
+
+ ^ false
+!
+
+isStream
+ "return true, if the receiver is some kind of stream;
+ false is returned here - the method is redefined in Stream"
+
+ ^ false
+!
+
+isInteger
+ "return true, if the receiver is some kind of integer number;
+ false is returned here - the method is redefined in Integer"
+
+ ^ false
+!
+
+respondsToArithmetic
+ "return true, if the receiver responds to arithmetic messages.
+ false is returned here - the method is redefined in ArithmeticValue"
+
+ ^ false
+!
+
+isMemberOf:aClass
+ "return true, if the receiver is an instance of aClass, false otherwise"
+
+ ^ (self class) == aClass
+!
+
+isKindOf:aClass
+ "return true, if the receiver is an instance of aClass or one of its
+ subclasses, false otherwise"
+
+%{ /* NOCONTEXT */
+
+ register OBJ thisClass;
+
+ thisClass = _Class(self);
+ while (thisClass != nil) {
+ if (thisClass == aClass) {
+ RETURN ( true );
+ }
+ thisClass = _ClassInstPtr(thisClass)->c_superclass;
+ }
+%}
+.
+ ^ false
+!
+
+respondsTo:aSelector
+ "return true, if the receiver implements a method with selector equal
+ to aSelector; i.e. if there is a method for aSelector in either the
+ receivers class or one of its superclasses"
+
+%{ /* NOCONTEXT */
+
+ extern OBJ lookup();
+
+ if (lookup(_Class(self), aSelector) == nil) {
+ RETURN ( false );
+ }
+ RETURN ( true );
+%}
+.
+ ^ self class canUnderstand:aSelector
+!
+
+references:anObject
+ "return true, if the receiver refers to the argument, anObject.
+ - for debugging only"
+
+ |myClass
+ numInst "{ Class: SmallInteger }" |
+
+ myClass := self class.
+
+ "check the class"
+ (myClass == anObject) ifTrue:[^ true].
+
+ "check the instance variables"
+ numInst := myClass instSize.
+ 1 to:numInst do:[:i |
+ ((self instVarAt:i) == anObject) ifTrue:[^ true]
+ ].
+
+ "check the indexed variables"
+ myClass isVariable ifTrue:[
+ numInst := myClass basicSize.
+ 1 to:numInst do:[:i |
+ ((self basicAt:i) == anObject) ifTrue:[^ true]
+ ]
+ ].
+ ^ false
+!
+
+allOwners
+ "return a collection of all objects referencing the receiver"
+
+ ^ ObjectMemory whoReferences:self
+! !
+
+!Object methodsFor:'misc'!
+
+-> anObject
+ "return an association with the receiver as key and
+ the argument as value"
+
+ ^ Association key:self value:anObject
+! !
+
+!Object methodsFor:'copying'!
+
+copy
+ "return a copy of the receiver - defaults to shallowcopy here"
+
+ ^ self shallowCopy
+!
+
+shallowCopy
+ "return a copy of the object with shared subobjects i.e. shallow copies
+ of its instance objects.
+ This method does NOT handle cycles"
+
+ |myClass aCopy
+ sz "{ Class: SmallInteger }" |
+
+ myClass := self class.
+ myClass isVariable ifTrue:[
+ sz := self basicSize.
+ aCopy := myClass basicNew:sz.
+
+ "copy the indexed variables"
+ 1 to:sz do:[:i |
+ aCopy basicAt:i put:(self basicAt:i)
+ ]
+ ] ifFalse:[
+ aCopy := myClass basicNew
+ ].
+
+ "copy the instance variables"
+ sz := myClass instSize.
+ 1 to:sz do:[:i |
+ aCopy instVarAt:i put:(self instVarAt:i)
+ ].
+
+ ^ aCopy
+!
+
+deepCopy
+ "return a copy of the object with all subobjects also copied.
+ This method does NOT handle cycles"
+
+ |myClass aCopy
+ sz "{ Class: SmallInteger }" |
+
+ myClass := self class.
+ myClass isVariable ifTrue:[
+ sz := self basicSize.
+ aCopy := myClass basicNew:sz.
+
+ "copy the indexed variables"
+ 1 to:sz do:[:i |
+ aCopy basicAt:i put:((self basicAt:i) deepCopy)
+ ]
+ ] ifFalse:[
+ aCopy := myClass basicNew
+ ].
+
+ "copy the instance variables"
+ sz := myClass instSize.
+ 1 to:sz do:[:i |
+ aCopy instVarAt:i put:((self instVarAt:i) deepCopy)
+ ].
+
+ ^ aCopy
+! !
+
+!Object methodsFor:'comparing'!
+
+== anObject
+ "return true, if the receiver and the arg are the same object"
+
+%{ /* NOCONTEXT */
+
+ RETURN ( (self == anObject) ? true : false );
+%}
+!
+
+~~ anObject
+ "return true, if the receiver and the arg are not the same object"
+
+%{ /* NOCONTEXT */
+
+ RETURN ( (self == anObject) ? false : true );
+%}
+!
+
+= anObject
+ "return true, if the receiver and the arg have the same structure"
+
+ ^ self == anObject
+!
+
+~= anObject
+ "return true, if the receiver and the arg do not have the same structure"
+
+ ^ (self = anObject) not
+!
+
+isNil
+ "return true, if the receiver is nil"
+
+ ^ false
+!
+
+notNil
+ "return true, if the receiver is not nil"
+
+ ^ true
+!
+
+hash
+ "return an Integer useful as a hash key for the receiver.
+ This hash should return same values for objects with same
+ contents (i.e. use this to hash on structure)"
+
+ ^ self identityHash
+!
+
+identityHash
+ "return an Integer useful as a hash key for the receiver.
+ This hash should return same values for the same object (i.e. use
+ this to hash on identity of objects).
+
+ We cannot use the Objects address (as other smalltalks do) since
+ no object-table exists and the hashval must not change when objects
+ are moved by the collector. Therefore we assign each object a unique
+ Id in the object header itself as its hashed upon.
+ (luckily we have 12 bits spare to do this - unluckily its only 12 bits).
+ To expand the range a bit, these 12 hashBits are concatenated to the
+ receivers class hashBits, to form a 24bit hashvalue (which will not
+ help, if many objects of the same class are hashed upon ...)"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int v1, v2;
+ static int nextHash = 0;
+ OBJ cls;
+
+ if (_isObject(self)) {
+ v1 = ((self->o_age & ~AGE_MASK) >> 5) << 8;
+ v1 |= (self->o_hashLow);
+ if (v1 == 0) {
+ v1 = nextHash++;
+ if (v1 == 0)
+ v1 = nextHash++;
+ self->o_hashLow = v1 & 0xFF;
+ self->o_age |= (v1 >> 8) << 5;
+ }
+
+ cls = _qClass(self);
+ v2 = ((cls->o_age & ~AGE_MASK) >> 5) << 8;
+ v2 |= (cls->o_hashLow);
+ if (v2 == 0) {
+ v2 = nextHash++;
+ if (v2 == 0)
+ v2 = nextHash++;
+ cls->o_hashLow = v2 & 0xFF;
+ cls->o_age |= (v2 >> 8) << 5;
+ }
+ RETURN ( _MKSMALLINT((v2<<12) | v1) );
+ }
+%}
+.
+ ^ 0
+! !
+
+!Object methodsFor:'interrupt handling'!
+
+userInterrupt
+ "user (^c) interrupt - enter debugger"
+
+ self error:'user Interrupt'
+!
+
+ioInterrupt
+ "io (SIGIO/SIGPOLL) interrupt and no handler - enter debugger"
+
+ self error:'user Interrupt'
+!
+
+spyInterrupt
+ "spy interrupt and no handler - enter debugger"
+
+ self error:'spy Interrupt'
+!
+
+timerInterrupt
+ "timer interrupt and no handler - enter debugger"
+
+ self error:'timer Interrupt'
+!
+
+errorInterrupt
+ "x-error interrupt and no handler - enter debugger"
+
+ self error:'error Interrupt:' , (Display lastError)
+!
+
+memoryInterrupt
+ "out-of-memory interrupt and no handler - enter debugger"
+
+ self error:'almost out of memory'
+!
+
+fpExceptionInterrupt
+ "a floating point exception occured - this one
+ has to be handled differently since they come asynchronous
+ on some machines"
+
+ self error:'a floating point exception occured'
+!
+
+signalInterrupt:signalNumber
+ "unix signal occured"
+
+ |box|
+
+ (Smalltalk at:#SignalCatchBlock) notNil ifTrue:[
+ box := OptionBox title:('Signal ' ,
+ signalNumber printString ,
+ ' cought')
+ numberOfOptions:5.
+
+ box buttonTitles:#('ignore' 'debug' 'restart' 'dump' 'exit').
+ box actions:(Array with:[^ nil]
+ with:[Debugger enterWithMessage:'Signal ', signalNumber printString. ^nil]
+ with:[SignalCatchBlock value. ^nil]
+ with:[Smalltalk fatalAbort]
+ with:[Smalltalk exit]).
+ box showAtPointer
+ ].
+
+ self error:('signal ' , signalNumber printString)
+!
+
+recursionInterrupt
+ "recursion limit interrupt - enter debugger"
+
+ self error:'recursion limit reached'
+!
+
+exceptionInterrupt
+ "exception interrupt - enter debugger"
+
+ self error:'exception Interrupt'
+! !
+
+!Object methodsFor:'error handling'!
+
+subscriptBoundsError:badIndex
+ "report error that badIndex is out of bounds"
+
+ SubscriptOutOfBoundSignal raise
+"
+ ^ self error:('index ' , badIndex printString , ' is out of bounds')
+"
+!
+
+indexNotInteger
+ "report error that index is not an Integer"
+
+ NonIntegerIndexSignal raise
+"
+ ^ self error:'index must be integer'
+"
+!
+
+elementNotInteger
+ "report error that object to be stored is no Integer"
+
+ ^ self error:'element must be an Integer'
+!
+
+elementNotCharacter
+ "report error that object to be stored is no Character"
+
+ ^ self error:'element must be a Character'
+!
+
+elementOutOfBounds
+ "report error that object to be stored is not valid"
+
+ ^ self error:'element out of bounds'
+!
+
+mustBeRectangle
+ "report an argument-not-rectangle-error"
+
+ ^ self error:'argument must be a Rectangle'
+!
+
+mustBeString
+ "report an argument-not-string-error"
+
+ ^ self error:'argument must be a String'
+!
+
+notIndexed
+ "report error that receiver has no indexed instance variables"
+
+ ^ self error:'receiver has no indexed variables'
+!
+
+typeCheckError
+ "generated when a variable declared with a type hint gets a bad
+ value assigned"
+
+ ^ self error:'bad assign to typed variable'
+!
+
+primitiveFailed
+ "report error that primitive code failed"
+
+ ^ self error:'primitive failed'
+!
+
+subclassResponsibility
+ "report error that this message should have been reimplemented in a
+ subclass"
+
+ ^ self error:'method must be reimplemented in subclass'
+!
+
+shouldNotImplement
+ "report error that this message should not be implemented"
+
+ ^ self error:'method not appropriate for this class'
+!
+
+error
+ "report error that an error occured"
+
+ ^ self error:'error encountered'
+!
+
+halt
+ "enter debugger with halt-message"
+
+ ^ self halt:'halt encountered'
+!
+
+fatalError:aMessage
+ "report a fatal-error; system dumps a backtrace and exits with core dump"
+%{
+ /*
+ * do not use any message calls here
+ * - since this might lead to infinite recursion ...
+ */
+ if (_isString(aMessage))
+ printf("%s\n", _stringVal(aMessage));
+ printStack(__context);
+ exit(1);
+%}
+!
+
+checkForRecursiveError
+ "helper for all error-methods; catch error while in Debugger.
+ If Debugger is DebugView, try switching to MiniDebugger (as
+ a last chance) otherwise abort.
+ There should not be an error in the debugger, this will only
+ happen if some classes has been changed badly."
+
+ ErrorActive ifTrue:[
+ (Debugger == MiniDebugger) ifTrue:[
+ ErrorRecursion ifFalse:[
+%{
+ printf("recursive error ...\n");
+ printStack(__context);
+ mainExit(0);
+%}
+ ]
+ ].
+ "set to MiniDebugger - and go on"
+ ^ MiniDebugger
+ ].
+ ^ Debugger
+!
+
+error:aString
+ "enter debugger with error-message aString;
+ if nonNil, the global ErrorHandler is informed instead -
+ this gives a chance for private error handling and error catching
+ within debugger"
+
+ |retVal debugger|
+
+ ErrorHandler notNil ifTrue:[
+ ^ ErrorHandler catch:#error: with:aString for:self
+ ].
+ Debugger isNil ifTrue:[
+ 'error: ' print. aString printNewline.
+ self fatalError:'no Debugger defined'
+ ].
+ debugger := self checkForRecursiveError.
+ ErrorActive := true.
+ retVal := debugger enterWithMessage:aString.
+ ErrorActive := false.
+ ^ retVal
+!
+
+doesNotUnderstand:aMessage
+ "enter debugger with does-not-understand-message;
+ if nonNil, the global ErrorHandler is informed instead -
+ this gives a chance for private error handling and error catching
+ within debugger"
+
+ |retVal debugger|
+
+ ErrorHandler notNil ifTrue:[
+ ^ ErrorHandler catch:#doesNotUnderstand: with:aMessage for:self
+ ].
+ Debugger isNil ifTrue:[
+ 'doesNotUnderstand:' print. aMessage selector printNewline.
+ self fatalError:'no Debugger defined'
+ ].
+ debugger := self checkForRecursiveError.
+ ErrorActive := true.
+ retVal := debugger enterWithMessage:(self class name ,
+ ' does not understand:' ,
+ aMessage printString).
+ ErrorActive := false.
+ ^ retVal
+!
+
+halt:aString
+ "enter debugger with halt-message;
+ the global ErrorHandler if nonNil is informed instead -
+ this gives a chance for private error handling and error catching
+ within debugger"
+
+ |retVal debugger|
+
+ ErrorHandler notNil ifTrue:[
+ ^ ErrorHandler catch:#halt: with:aString for:self
+ ].
+ Debugger isNil ifTrue:[
+ 'halt encountered:' print. aString printNewline.
+ self fatalError:'no Debugger defined'
+ ].
+ debugger := self checkForRecursiveError.
+ ErrorActive := true.
+ retVal := debugger enterWithMessage:aString.
+ ErrorActive := false.
+ ^ retVal
+! !
+
+!Object methodsFor:'debugging'!
+
+notify:aString
+ "launch a Notifier, telling user something"
+
+ SystemNotifier isNil ifTrue:[
+ Notifier isNil ifTrue:[
+ Transcript showCr:aString.
+ ^ self
+ ].
+ SystemNotifier := Notifier new
+ ].
+ SystemNotifier title:aString.
+ SystemNotifier showAtPointer
+!
+
+information:aString
+ "launch an InfoBox, telling user something"
+
+ SystemInfoBox isNil ifTrue:[
+ InfoBox isNil ifTrue:[
+ Transcript showCr:aString.
+ ^ self
+ ].
+ SystemInfoBox := InfoBox new
+ ].
+ SystemInfoBox title:aString.
+ SystemInfoBox showAtPointer
+!
+
+warn:aString
+ "launch a WarningBox, telling user something"
+
+ SystemWarningBox isNil ifTrue:[
+ WarningBox isNil ifTrue:[
+ Transcript showCr:aString.
+ ^ self
+ ].
+ SystemWarningBox := WarningBox new
+ ].
+ SystemWarningBox title:aString.
+ SystemWarningBox showAtPointer
+!
+
+confirm:aString
+ "launch a confirmer, which allows user to enter yes or no.
+ return true for yes, false for no"
+
+ SystemConfirmer isNil ifTrue:[
+ YesNoBox isNil ifTrue:[
+ Transcript show:'no YesNoBox. '.
+ Transcript showCr:aString.
+ ^ true
+ ].
+ SystemConfirmer := YesNoBox new
+ ].
+ SystemConfirmer title:aString.
+ SystemConfirmer yesAction:[^ true] noAction:[^ false].
+ SystemConfirmer showAtPointer
+!
+
+basicInspect
+ "launch an inspector on the receiver.
+ this method should NOT be redefined in subclasses."
+
+ Inspector isNil ifTrue:[
+ Transcript showCr:'no Inspector defined'
+ ] ifFalse:[
+ Inspector openOn:self
+ ]
+!
+
+inspect
+ "launch an inspector on the receiver.
+ this method can be redefined in subclasses."
+
+ ^ self basicInspect
+! !
+
+!Object methodsFor:'accessing'!
+
+at:index
+ "return the indexed instance variable with index, anInteger;
+ this method can be redefined in subclasses."
+
+ ^ self basicAt:index
+!
+
+basicAt:index
+ "return the indexed instance variable with index, anInteger.
+ Trigger an error if the receiver has no indexed instance variables.
+ This method should NOT be redefined in any subclass"
+
+%{ /* NOCONTEXT */
+
+ register int nbytes, indx;
+ OBJ myClass;
+ register char *pFirst;
+ unsigned char *cp;
+ unsigned short *sp;
+ long *lp;
+ OBJ *op;
+ int nInstBytes, ninstvars;
+ extern OBJ _makeLarge();
+
+
+ /*
+ * notice the missing test for self beeing a nonNilObject -
+ * this can be done since basicAt: is defined both in UndefinedObject
+ * and SmallInteger
+ */
+ if (_isSmallInteger(index)) {
+ myClass = _qClass(self);
+ indx = _intVal(index) - 1;
+ ninstvars = _intVal(_ClassInstPtr(myClass)->c_ninstvars);
+ nInstBytes = OHDR_SIZE + ninstvars * sizeof(OBJ);
+ nbytes = _qSize(self) - nInstBytes;
+ pFirst = (char *)(_InstPtr(self)) + nInstBytes;
+
+ switch (_intVal(_ClassInstPtr(myClass)->c_flags) & ARRAYMASK) {
+ case BYTEARRAY:
+ if ((indx >= 0) && (indx < (nbytes / sizeof(char)))) {
+ cp = (unsigned char *)pFirst + indx;
+ RETURN ( _MKSMALLINT(*cp & 0xFF) );
+ }
+ break;
+
+ case WORDARRAY:
+ if ((indx >= 0) && (indx < (nbytes / sizeof(short)))) {
+ sp = (unsigned short *)pFirst + indx;
+ RETURN ( _MKSMALLINT(*sp & 0xFFFF) );
+ }
+ break;
+
+ case LONGARRAY:
+ if ((indx >= 0) && (indx < (nbytes / sizeof(long)))) {
+ lp = (long *)pFirst + indx;
+ if ((*lp >= _MIN_INT) && (*lp <= _MAX_INT))
+ RETURN ( _MKSMALLINT(*lp) );
+ RETURN ( _makeLarge(*lp) );
+ }
+ break;
+
+ case FLOATARRAY:
+ if ((indx >= 0) && (indx < (nbytes / sizeof(float)))) {
+ float *fp;
+
+ fp = (float *)pFirst + indx;
+ RETURN ( _MKFLOAT((double)(*fp)) COMMA_CON );
+ }
+ break;
+
+ case DOUBLEARRAY:
+ if ((indx >= 0) && (indx < (nbytes / sizeof(double)))) {
+ double *dp;
+
+ dp = (double *)pFirst + indx;
+ RETURN ( _MKFLOAT(*dp) COMMA_CON );
+ }
+ break;
+
+ case WKPOINTERARRAY:
+ case POINTERARRAY:
+ if ((indx >= 0) && (indx < (nbytes / sizeof(OBJ)))) {
+ op = (OBJ *)pFirst + indx;
+ RETURN ( *op );
+ }
+ break;
+ }
+ }
+%}
+.
+ (index isMemberOf:SmallInteger) ifTrue:[
+ ^ self subscriptBoundsError:index
+ ].
+ ^ self indexNotInteger
+!
+
+at:index put:anObject
+ "store the 2nd arg, anObject as indexed instvar with index, anInteger.
+ this method can be redefined in subclasses."
+
+ ^ self basicAt:index put:anObject
+!
+
+basicAt:index put:anObject
+ "store the 2nd arg, anObject as indexed instvar with index, anInteger.
+ Trigger an error if the receiver has no indexed instance variables.
+ This method should NOT be redefined in any subclass"
+
+%{ /* NOCONTEXT */
+
+ register int nbytes, indx;
+ OBJ myClass;
+ register char *pFirst;
+ char *cp;
+ short *sp;
+ long *lp;
+ OBJ *op;
+ int nInstBytes, ninstvars;
+ int val;
+
+ /* notice the missing test for self beeing a nonNilObject -
+ this an be done since basicAt: is defined both in UndefinedObject
+ and SmallInteger */
+
+ if (_isSmallInteger(index)) {
+ indx = _intVal(index) - 1;
+ myClass = _qClass(self);
+ ninstvars = _intVal(_ClassInstPtr(myClass)->c_ninstvars);
+ nInstBytes = OHDR_SIZE + ninstvars * sizeof(OBJ);
+ nbytes = _qSize(self) - nInstBytes;
+ pFirst = (char *)(_InstPtr(self)) + nInstBytes;
+
+ switch (_intVal(_ClassInstPtr(myClass)->c_flags) & ARRAYMASK) {
+ case BYTEARRAY:
+ if (_isSmallInteger(anObject)) {
+ val = _intVal(anObject);
+ if ((val >= 0) && (val <= 255)) {
+ if ((indx >= 0) && (indx < (nbytes / sizeof(char)))) {
+ cp = pFirst + indx;
+ *cp = val;
+ RETURN ( anObject );
+ }
+ }
+ }
+ break;
+
+ case WORDARRAY:
+ if (_isSmallInteger(anObject)) {
+ val = _intVal(anObject);
+ if ((val >= 0) && (val <= 0xFFFF)) {
+ if ((indx >= 0) && (indx < (nbytes / sizeof(short)))) {
+ sp = (short *)pFirst + indx;
+ *sp = val;
+ RETURN ( anObject );
+ }
+ }
+ }
+ break;
+
+ case LONGARRAY:
+ if (_isSmallInteger(anObject)) {
+ if ((indx >= 0) && (indx < (nbytes / sizeof(long)))) {
+ lp = (long *)pFirst + indx;
+ *lp = _intVal(anObject);
+ RETURN ( anObject );
+ }
+ }
+ /* XXX
+ * XXX must add possibility to put in a large number here
+ * XXX
+ */
+ break;
+
+ case FLOATARRAY:
+ if (_isFloat(anObject)) {
+ if ((indx >= 0) && (indx < (nbytes / sizeof(float)))) {
+ float *fp;
+
+ fp = (float *)pFirst + indx;
+ *fp = _floatVal(anObject);
+ RETURN ( anObject );
+ }
+ }
+ break;
+
+ case DOUBLEARRAY:
+ if (_isFloat(anObject)) {
+ if ((indx >= 0) && (indx < (nbytes / sizeof(double)))) {
+ double *dp;
+
+ dp = (double *)pFirst + indx;
+ *dp = _floatVal(anObject);
+ RETURN ( anObject );
+ }
+ }
+ break;
+
+ case WKPOINTERARRAY:
+ case POINTERARRAY:
+ if ((indx >= 0) && (indx < (nbytes / sizeof(OBJ)))) {
+ op = (OBJ *)pFirst + indx;
+ *op = anObject;
+ __STORE(self, anObject);
+ RETURN ( anObject );
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+%}
+.
+ (index isMemberOf:SmallInteger) ifFalse:[
+ ^ self indexNotInteger
+ ].
+ (index between:1 and:self size) ifFalse:[
+ ^ self subscriptBoundsError:index
+ ].
+ ^ self elementNotInteger
+!
+
+instVarAt:index
+ "return a non-indexed instance variable;
+ this is not very object oriented - use with care (needed for inspector)"
+
+%{ /* NOCONTEXT */
+
+ OBJ myClass;
+ int idx, ninstvars;
+
+ if (_isSmallInteger(index)) {
+ myClass = _Class(self);
+ ninstvars = _intVal(_ClassInstPtr(myClass)->c_ninstvars);
+ idx = _intVal(index) - 1;
+ if ((idx >= 0) && (idx < ninstvars)) {
+ RETURN ( _InstPtr(self)->i_instvars[idx] );
+ }
+ }
+%}
+.
+ index isInteger ifFalse:[
+ ^ self indexNotInteger
+ ].
+ ^ self subscriptBoundsError:index
+!
+
+instVarAt:index put:value
+ "change a non-indexed instance variable;
+ this is not very object oriented - use with care (needed for inspector)"
+
+%{ /* NOCONTEXT */
+
+ OBJ myClass;
+ int idx, ninstvars;
+
+ if (_isSmallInteger(index)) {
+ myClass = _Class(self);
+ ninstvars = _intVal(_ClassInstPtr(myClass)->c_ninstvars);
+ idx = _intVal(index) - 1;
+ if ((idx >= 0) && (idx < ninstvars)) {
+ _InstPtr(self)->i_instvars[idx] = value;
+ __STORE(self, value);
+ RETURN ( value );
+ }
+ }
+%}
+.
+ index isInteger ifFalse:[
+ ^ self indexNotInteger
+ ].
+ ^ self subscriptBoundsError:index
+! !
+
+!Object methodsFor:'dependents access'!
+
+dependents
+ "return a Collection of dependents - nil if there is none.
+ The default implementation here uses a global Dictionary to store
+ dependents - some classes (Model) redefine this for better performance."
+
+ ^ Dependencies at:self ifAbsent:[]
+!
+
+dependents:aCollection
+ "set the collection of dependents.
+ The default implementation here uses a global Dictionary to store
+ dependents - some classes (Model) redefine this for better performance."
+
+ (aCollection isNil or:[aCollection isEmpty]) ifTrue:[
+ Dependencies removeKey:self ifAbsent:[]
+ ] ifFalse:[
+ Dependencies at:self put:aCollection
+ ]
+!
+
+addDependent:anObject
+ "make the argument, anObject be a dependent of the receiver"
+
+ |deps|
+
+ deps := self dependents.
+ deps isNil ifTrue:[
+ deps := IdentitySet with:anObject.
+ self dependents:deps
+ ] ifFalse:[
+ deps add:anObject
+ ]
+!
+
+removeDependent:anObject
+ "make the argument, anObject be independent of the receiver"
+
+ |deps|
+
+ deps := self dependents.
+ deps notNil ifTrue:[
+ deps remove:anObject ifAbsent:[].
+ deps isEmpty ifTrue:[
+ self dependents:nil
+ ]
+ ]
+!
+
+release
+ "remove all dependencies from the receiver"
+
+ self dependents:nil
+! !
+
+!Object methodsFor:'change and update'!
+
+changed
+ "notify all dependents that the receiver has changed.
+ Each dependent gets a '#update' message."
+
+ |deps|
+
+ deps := self dependents.
+ deps notNil ifTrue:[
+ deps do:[:dependent |
+ dependent update:self
+ ]
+ ]
+!
+
+changed:aParameter
+ "notify all dependents that the receiver has changed somehow.
+ Each dependent gets a '#update:aParameter' message."
+
+ |deps|
+
+ deps := self dependents.
+ deps notNil ifTrue:[
+ deps do:[:dependent |
+ dependent update:aParameter
+ ]
+ ]
+!
+
+changed:aParameter with:arguments
+ "notify all dependents that the receiver has changed somehow.
+ sending update:with: to each dependent with an additional arguments"
+
+ |deps|
+
+ deps := self dependents.
+ deps notNil ifTrue:[
+ deps do:[:dependent |
+ dependent update:aParameter with:arguments
+ ]
+ ]
+!
+
+broadcast:aSymbol
+ "send the argument, aSelectorSymbol to all my dependents"
+
+ |deps|
+
+ deps := self dependents.
+ deps notNil ifTrue:[
+ deps do:[:dependent |
+ dependent perform:aSymbol
+ ]
+ ]
+!
+
+broadcast:aSymbol with:anObject
+ "send the argument, aSelectorSymbol of a 1 argument message
+ to all my dependents with the second argument, anObject as argument"
+
+ |deps|
+
+ deps := self dependents.
+ deps notNil ifTrue:[
+ deps do:[:dependent |
+ dependent perform:aSymbol with:anObject
+ ]
+ ]
+!
+
+update:aParameter
+ "dependent is notified of some change -
+ Default behavior is to do nothing"
+
+ ^ self
+!
+
+update:aParameter with:anArgument
+ "dependent is notified of some change -
+ Default is to try simple update"
+
+ ^ self update:aParameter
+!
+
+update:aParameter with:anArgument from:sender
+ "dependent is notified of some change -
+ Default is to try simple update"
+
+ ^ self update:aParameter with:anArgument
+! !
+
+!Object methodsFor:'message sending'!
+
+perform:aSelector
+ "send the message aSelector to the receiver"
+
+%{ /* NOCONTEXT */
+
+ static struct inlineCache ilc = _ILC0;
+ static OBJ lastSelector = nil;
+
+#ifdef XXXTHIS_CONTEXT
+ /*
+ * must set lineno by hand here ...
+ */
+ if (_intVal(__pilc->ilc_lineNo) > 0)
+ _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+
+ if (aSelector != lastSelector) {
+ ilc.ilc_func = _SEND0;
+ lastSelector = aSelector;
+ }
+#ifdef THIS_CONTEXT
+ ilc.ilc_lineNo = __pilc->ilc_lineNo;
+#endif
+
+ RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc) );
+%}
+!
+
+perform:aSelector with:anObject
+ "send the one-arg-message aSelector to the receiver"
+
+%{ /* NOCONTEXT */
+
+ static struct inlineCache ilc = _ILC1;
+ static OBJ lastSelector = nil;
+
+#ifdef XXXTHIS_CONTEXT
+ /*
+ * must set lineno by hand here ...
+ */
+ if (_intVal(__pilc->ilc_lineNo) > 0)
+ _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+ if (aSelector != lastSelector) {
+ ilc.ilc_func = _SEND1;
+ lastSelector = aSelector;
+ }
+#ifdef THIS_CONTEXT
+ ilc.ilc_lineNo = __pilc->ilc_lineNo;
+#endif
+
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc, &anObject) );
+#else
+ RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc, anObject) );
+#endif
+%}
+!
+
+perform:aSelector with:firstObject with:secondObject
+ "send the two-arg-message aSelector to the receiver"
+
+%{ /* NOCONTEXT */
+
+ static struct inlineCache ilc = _ILC2;
+ static OBJ lastSelector = nil;
+
+#ifdef XXXTHIS_CONTEXT
+ /*
+ * must set lineno by hand here ...
+ */
+ if (_intVal(__pilc->ilc_lineNo) > 0)
+ _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+ if (aSelector != lastSelector) {
+ ilc.ilc_func = _SEND2;
+ lastSelector = aSelector;
+ }
+#ifdef THIS_CONTEXT
+ ilc.ilc_lineNo = __pilc->ilc_lineNo;
+#endif
+
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc, &firstObject) );
+#else
+ RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc, firstObject, secondObject) );
+#endif
+%}
+!
+
+perform:aSelector with:firstObject with:secondObject with:thirdObject
+ "send the three-arg-message aSelector to the receiver"
+
+%{ /* NOCONTEXT */
+
+ static struct inlineCache ilc = _ILC3;
+ static OBJ lastSelector = nil;
+
+#ifdef XXXTHIS_CONTEXT
+ /*
+ * must set lineno by hand here ...
+ */
+ if (_intVal(__pilc->ilc_lineNo) > 0)
+ _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+ if (aSelector != lastSelector) {
+ ilc.ilc_func = _SEND3;
+ lastSelector = aSelector;
+ }
+#ifdef THIS_CONTEXT
+ ilc.ilc_lineNo = __pilc->ilc_lineNo;
+#endif
+
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc, &firstObject) );
+#else
+ RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc, firstObject, secondObject, thirdObject) );
+#endif
+%}
+!
+
+perform:aSelector withArguments:argArray
+ "send the message aSelector with all args taken from argArray
+ to the receiver"
+
+ |numberOfArgs a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12|
+
+ numberOfArgs := argArray size.
+%{
+ extern OBJ Array;
+ REGISTER OBJ *argP;
+ OBJ T;
+ int nargs, i;
+ static OBJ last0 = nil; static struct inlineCache ilc0 = _ILC0;
+ static OBJ last1 = nil; static struct inlineCache ilc1 = _ILC1;
+ static OBJ last2 = nil; static struct inlineCache ilc2 = _ILC2;
+ static OBJ last3 = nil; static struct inlineCache ilc3 = _ILC3;
+ static OBJ last4 = nil; static struct inlineCache ilc4 = _ILC4;
+ static OBJ last5 = nil; static struct inlineCache ilc5 = _ILC5;
+ static OBJ last6 = nil; static struct inlineCache ilc6 = _ILC6;
+ static OBJ last7 = nil; static struct inlineCache ilc7 = _ILC7;
+ static OBJ last8 = nil; static struct inlineCache ilc8 = _ILC8;
+ static OBJ last9 = nil; static struct inlineCache ilc9 = _ILC9;
+ static OBJ last10 = nil; static struct inlineCache ilc10 = _ILC10;
+ static OBJ last11 = nil; static struct inlineCache ilc11 = _ILC11;
+ static OBJ last12 = nil; static struct inlineCache ilc12 = _ILC12;
+
+ if (_isSmallInteger(numberOfArgs)) {
+ nargs = _intVal(numberOfArgs);
+ if (nargs) {
+ argP = (OBJ *)(&a1);
+ if (_Class(argArray) == Array) {
+ for (i=0; i < nargs; i++) {
+ *argP++ = _ArrayInstPtr(argArray)->a_element[i];
+ }
+ } else {
+ for (i=1; i <= nargs; i++) {
+#ifdef PASS_ARG_REF
+ T = _MKSMALLINT(i);
+ *argP++ = _AT_(argArray, CON_COMMA &T);
+#else
+ *argP++ = _AT_(argArray, CON_COMMA _MKSMALLINT(i));
+#endif
+ }
+ }
+ }
+#ifdef XXXTHIS_CONTEXT
+ /*
+ * must set lineno by hand here ...
+ */
+ if (_intVal(__pilc->ilc_lineNo) > 0)
+ _ContextInstPtr(__context->c_sender)->c_lineno = __pilc->ilc_lineNo;
+#endif
+ switch (nargs) {
+ case 0:
+ if (aSelector != last0) {
+ ilc0.ilc_func = _SEND0;
+ last0 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc0.ilc_func)(self, aSelector, CON_COMMA nil, &ilc0, &a1) );
+#else
+ RETURN ( (*ilc0.ilc_func)(self, aSelector, CON_COMMA nil, &ilc0, a1, a2) );
+#endif
+
+ case 1:
+ if (aSelector != last1) {
+ ilc1.ilc_func = _SEND1;
+ last1 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc1.ilc_func)(self, aSelector, CON_COMMA nil, &ilc1, &a1));
+#else
+ RETURN ( (*ilc1.ilc_func)(self, aSelector, CON_COMMA nil, &ilc1, a1, a2));
+#endif
+
+ case 2:
+ if (aSelector != last2) {
+ ilc2.ilc_func = _SEND2;
+ last2 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc2.ilc_func)(self, aSelector, CON_COMMA nil, &ilc2, &a1));
+#else
+ RETURN ( (*ilc2.ilc_func)(self, aSelector, CON_COMMA nil, &ilc2, a1, a2));
+#endif
+
+ case 3:
+ if (aSelector != last3) {
+ ilc3.ilc_func = _SEND3;
+ last3 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc3.ilc_func)(self, aSelector, CON_COMMA nil, &ilc3, &a1));
+#else
+ RETURN ( (*ilc3.ilc_func)(self, aSelector, CON_COMMA nil, &ilc3, a1, a2, a3));
+#endif
+
+ case 4:
+ if (aSelector != last4) {
+ ilc4.ilc_func = _SEND4;
+ last4 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc4.ilc_func)(self, aSelector, CON_COMMA nil, &ilc4, &a1));
+#else
+ RETURN ( (*ilc4.ilc_func)(self, aSelector, CON_COMMA nil, &ilc4, a1, a2, a3, a4));
+#endif
+
+ case 5:
+ if (aSelector != last5) {
+ ilc5.ilc_func = _SEND5;
+ last5 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc5.ilc_func)(self, aSelector, CON_COMMA nil, &ilc5, &a1));
+#else
+ RETURN ( (*ilc5.ilc_func)(self, aSelector, CON_COMMA nil, &ilc5, a1, a2, a3, a4, a5));
+#endif
+
+ case 6:
+ if (aSelector != last6) {
+ ilc6.ilc_func = _SEND6;
+ last6 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc6.ilc_func)(self, aSelector, CON_COMMA nil, &ilc6, &a1));
+#else
+ RETURN ( (*ilc6.ilc_func)(self, aSelector, CON_COMMA nil, &ilc6, a1, a2, a3, a4, a5, a6));
+#endif
+
+ case 7:
+ if (aSelector != last7) {
+ ilc7.ilc_func = _SEND7;
+ last7 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc7.ilc_func)(self, aSelector, CON_COMMA nil, &ilc7, &a1));
+#else
+ RETURN ( (*ilc7.ilc_func)(self, aSelector, CON_COMMA nil, &ilc7, a1, a2, a3, a4, a5, a6, a7));
+#endif
+
+ case 8:
+ if (aSelector != last8) {
+ ilc8.ilc_func = _SEND8;
+ last8 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc8.ilc_func)(self, aSelector, CON_COMMA nil, &ilc8, &a1));
+#else
+ RETURN ( (*ilc8.ilc_func)(self, aSelector, CON_COMMA nil, &ilc8, a1, a2, a3, a4, a5, a6, a7, a8));
+#endif
+
+ case 9:
+ if (aSelector != last9) {
+ ilc9.ilc_func = _SEND9;
+ last9 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc9.ilc_func)(self, aSelector, CON_COMMA nil, &ilc9, &a1));
+#else
+ RETURN ( (*ilc9.ilc_func)(self, aSelector, CON_COMMA nil, &ilc9, a1, a2, a3, a4, a5, a6, a7, a8, a9));
+#endif
+
+ case 10:
+ if (aSelector != last10) {
+ ilc10.ilc_func = _SEND10;
+ last10 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc10.ilc_func)(self, aSelector, CON_COMMA nil, &ilc10, &a1));
+#else
+ RETURN ( (*ilc10.ilc_func)(self, aSelector, CON_COMMA nil, &ilc10, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10));
+#endif
+
+ case 11:
+ if (aSelector != last11) {
+ ilc11.ilc_func = _SEND11;
+ last11 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc11.ilc_func)(self, aSelector, CON_COMMA nil, &ilc11, &a1));
+#else
+ RETURN ( (*ilc11.ilc_func)(self, aSelector, CON_COMMA nil, &ilc11, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11));
+#endif
+
+ case 12:
+ if (aSelector != last12) {
+ ilc12.ilc_func = _SEND12;
+ last12 = aSelector;
+ }
+#ifdef PASS_ARG_REF
+ RETURN ( (*ilc12.ilc_func)(self, aSelector, CON_COMMA nil, &ilc12, &a1));
+#else
+ RETURN ( (*ilc12.ilc_func)(self, aSelector, CON_COMMA nil, &ilc12, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12));
+#endif
+ }
+ }
+%}
+.
+ ^ self primitiveFailed
+! !
+
+!Object methodsFor:'printing & storing'!
+
+className
+ "return the classname of the receivers class"
+
+ ^ self class name
+!
+
+classNameWithArticle
+ "return a string consisting of classname preceeded by an article"
+
+ |article classname firstChar|
+
+ classname := self className.
+ firstChar := (classname at:1) asLowercase.
+ (firstChar isVowel or:[firstChar == $x]) ifTrue:[
+ article := 'an '
+ ] ifFalse:[
+ article := 'a '
+ ].
+ ^ (article , classname)
+!
+
+printString
+ "return a string for printing the receiver.
+ Default printString is the classname preceeded by an article -
+ is redefined in many subclasses"
+
+ ^ self classNameWithArticle
+!
+
+print
+ "print the receiver on the standard output stream"
+
+ self printString print
+!
+
+printOn:aStream
+ "print the receiver on the argument-stream"
+
+ aStream nextPutAll:(self printString)
+!
+
+printStringRightAdjustLen:fieldSize
+ "return my printString as a right-adjusted string of length fieldSize"
+
+ |thePrintString len spaces|
+
+ thePrintString := self printString.
+ len := thePrintString size.
+ (len < fieldSize) ifTrue:[
+ spaces := String new:(fieldSize - len).
+ ^ spaces , thePrintString
+ ].
+ ^ thePrintString
+!
+
+printRightAdjustLen:fieldSize
+ "print the receiver right adjusted in a field of fieldSize
+ characters"
+
+ (self printStringRightAdjustLen:fieldSize) printOn:Stdout
+!
+
+printNL
+ "print the receiver followed by a cr
+ - for GNU Smalltalk compatibility"
+
+ ^ self printNewline
+!
+
+printNewline
+ "print the receiver followed by a cr"
+
+ self print.
+ Character nl print
+!
+
+displayString
+ "return a string used when displaying the receiver in a view,
+ for example an Inspector. This is usually the same as printString"
+
+ ^ self printString
+!
+
+storeString
+ "return a string representing an expression to reconstruct the receiver"
+
+ | stream myClass hasSemi
+ sz "{ Class: SmallInteger }" |
+
+ myClass := self class.
+ stream := WriteStream on:(String new).
+ stream nextPut:$(.
+ stream nextPutAll:self class name.
+ hasSemi := false.
+ myClass isVariable ifTrue:[
+ stream nextPutAll:' basicNew:'.
+ self basicSize printOn:stream
+ ] ifFalse:[
+ stream nextPutAll:' basicNew'
+ ].
+ sz := myClass instSize.
+ 1 to:sz do:[:i |
+ stream nextPutAll:' instVarAt:'.
+ i printOn:stream.
+ stream nextPutAll:' put:'.
+ (self instVarAt:i) storeOn:stream.
+ stream nextPut:$;.
+ hasSemi := true
+ ].
+ myClass isVariable ifTrue:[
+ sz := self basicSize.
+ 1 to:sz do:[:i |
+ stream nextPutAll:' basicAt:'.
+ i printOn:stream.
+ stream nextPutAll:' put:'.
+ (self basicAt:i) storeOn:stream.
+ stream nextPut:$;.
+ hasSemi := true
+ ]
+ ].
+ hasSemi ifTrue:[
+ stream nextPutAll:' yourself'
+ ].
+ stream nextPut:$).
+ ^ stream contents
+!
+
+storeOn:aStream
+ "store the receiver on aStream; i.e. print an expression which will
+ reconstruct the receiver"
+
+ aStream nextPutAll:(self storeString)
+!
+
+store
+ "store the receiver on standard output"
+
+ self storeOn:Stdout
+!
+
+storeNl
+ "store the receiver on standard output; append a newline"
+
+ self store.
+ Character nl print
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectMemory.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,367 @@
+"
+ COPYRIGHT (c) 1992-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#ObjectMemory
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'System-Support'
+!
+
+ObjectMemory comment:'
+
+COPYRIGHT (c) 1992 -93 by Claus Gittinger
+ All Rights Reserved
+
+This class contains access methods to the system memory -
+in previous versions this stuff used to be in the Smalltalk class.
+It has been separated for better overall structure.
+
+%W% %E%
+'!
+
+!ObjectMemory class methodsFor:'cache management'!
+
+flushInlineCachesForClass:aClass
+ "flush inlinecaches for calls to aClass"
+
+%{ /* NOCONTEXT */
+ __flushInlineCachesFor(aClass);
+%}
+!
+
+flushInlineCachesWithArgs:nargs
+ "flush inlinecaches for calls with nargs arguments"
+
+%{ /* NOCONTEXT */
+ __flushInlineCaches(_intVal(nargs));
+%}
+!
+
+flushInlineCachesFor:aClass withArgs:nargs
+ "flush inlinecaches for calls to aClass with nargs arguments"
+
+%{ /* NOCONTEXT */
+ __flushInlineCachesForAndNargs(aClass, _intVal(nargs));
+%}
+!
+
+flushInlineCaches
+ "flush all inlinecaches"
+
+%{ /* NOCONTEXT */
+ __flushAllInlineCaches();
+%}
+!
+
+flushMethodCacheFor:aClass
+ "flush the method cache for sends to aClass"
+
+%{ /* NOCONTEXT */
+ __flushMethodCacheFor(aClass);
+%}
+!
+
+flushMethodCache
+ "flush the method cache"
+
+%{ /* NOCONTEXT */
+ __flushMethodCache();
+%}
+!
+
+flushCaches
+ "flush method and inline caches"
+
+%{ /* NOCONTEXT */
+ __flushMethodCache();
+ __flushAllInlineCaches();
+%}
+! !
+
+!ObjectMemory class methodsFor:'enumeration'!
+
+allObjectsDo:aBlock
+ "evaluate the argument, aBlock for all objects in the system"
+%{
+#ifdef THIS_CONTEXT
+ __allObjectsDo(&aBlock);
+#else
+ __allObjectsDo(&aBlock, __context);
+#endif
+%}
+! !
+
+!ObjectMemory class methodsFor:'queries'!
+
+newSpaceUsed
+ "return the number of bytes allocated for new objects"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(__newSpaceUsed()) );
+%}
+ "ObjectMemory newSpaceUsed"
+!
+
+oldSpaceUsed
+ "return the number of bytes allocated for old objects"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(__oldSpaceUsed()) );
+%}
+ "ObjectMemory oldSpaceUsed"
+!
+
+bytesUsed
+ "return the number of bytes allocated for objects -
+ this number is not exact, since some objects may be dead"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(__oldSpaceUsed() + __newSpaceUsed()) );
+%}
+ "ObjectMemory bytesUsed"
+!
+
+numberOfObjects
+ "return the number of objects in the system"
+
+ |tally|
+
+ tally := 0.
+ self allObjectsDo:[:obj | tally := tally + 1].
+ ^ tally
+
+ "ObjectMemory numberOfObjects"
+!
+
+printReferences:anObject
+ "debugging: print referents to anObject"
+
+%{
+ _printRefChain(__context, anObject);
+%}
+!
+
+whoReferences:anObject
+ "return a collection of objects referencing the argument, anObject"
+
+ |aCollection|
+
+ aCollection := IdentitySet new.
+ self allObjectsDo:[:o |
+ (o references:anObject) ifTrue:[
+ aCollection add:o
+ ]
+ ].
+ (aCollection size == 0) ifTrue:[
+ "actually this cannot happen - there is always one"
+ ^ nil
+ ].
+ ^ aCollection
+! !
+
+!ObjectMemory class methodsFor:'garbage collector control'!
+
+garbageCollect
+ "search for and free garbage in the oldSpace
+ (newSpace is cleaned automatically)
+ - can take a long time if paging is involved
+ - when no paging is involved, its faster than I thought :-)"
+%{
+ __garbageCollect(__context);
+%}
+
+ "ObjectMemory garbageCollect"
+!
+
+scavenge
+ "for debugging only - collect newspace stuff"
+%{
+ nonTenuringScavenge(__context);
+%}
+
+ "ObjectMemory scavenge"
+!
+
+tenure
+ "forcae all new stuff into old-space"
+%{
+ tenure(__context);
+%}
+
+ "ObjectMemory tenure"
+!
+
+markAndSweep
+ "mark/sweep garbage collector"
+
+%{
+ markAndSweep(__context);
+%}
+
+ "ObjectMemory markAndSweep"
+!
+
+gcStep
+ "one incremental garbage collect step"
+%{
+ incrGCstep(__context);
+%}
+!
+
+turnOffGarbageCollector
+ "turn off garbage collector.
+ this method is somewhat dangerous: if collector is turned off,
+ and too many objects are created, the system may run into trouble.
+ Use this only for measurement purposes or when realtime behavior
+ is required for a limited time period. No waranty"
+%{
+ allocForceSpace(0);
+%}
+!
+
+turnOnGarbageCollector
+ "turn it on again"
+
+%{
+ allocForceSpace(1);
+%}
+! !
+
+!ObjectMemory class methodsFor:'system management'!
+
+loadClassBinary:aClassName
+ "find the object file for aClassName and -if found - load it;
+ this one loads precompiled object files"
+
+ |fName newClass upd|
+
+ fName := self fileNameForClass:aClassName.
+ fName notNil ifTrue:[
+ upd := Class updateChanges:false.
+ [
+ self loadBinary:(fName , '.o')
+ ] valueNowOrOnUnwindDo:[
+ Class updateChanges:upd
+ ].
+ newClass := self at:(aClassName asSymbol).
+ (newClass notNil and:[newClass implements:#initialize]) ifTrue:[
+ newClass initialize
+ ]
+ ]
+!
+
+snapShot
+ "create a snapshot"
+
+ ImageName isNil ifTrue:[
+ ImageName := 'st.img'
+ ].
+ self snapShotOn:ImageName
+
+ "ObjectMemory snapShot"
+!
+
+snapShotOn:aFileName
+ "create a snapshot in the given file"
+
+ "give others a chance to fix things"
+ self changed:#save.
+%{
+ OBJ __snapShotOn();
+
+ if (_isString(aFileName)) {
+ RETURN ( __snapShotOn(__context, _stringVal(aFileName)) );
+ }
+%}
+.
+ ^ self primitiveFailed
+
+ "ObjectMemory snapShotOn:'myimage.img'"
+!
+
+applicationImageOn:aFileName for:startupClass selector:startupSelector
+ "create a snapshot which will come up without any views
+ but starts up an application by sending startupClass the startupSelector"
+
+ |viewsKnown savedIdleBlocks savedTimeoutBlocks savedTranscript|
+
+ viewsKnown := Display knownViews.
+ savedIdleBlocks := Display idleBlocks.
+ savedTimeoutBlocks := Display timeOutBlocks.
+ savedTranscript := Transcript.
+
+ "a kludge: save image with modified knownViews ..."
+
+ Display knownViews:nil.
+ Display idleBlocks:nil.
+ Display timeOutBlocks:nil.
+ Transcript := Stderr.
+ StartupClass := startupClass.
+ StartupSelector := startupSelector.
+
+ self snapShotOn:aFileName.
+
+ StartupClass := nil.
+ StartupSelector := nil.
+ Transcript := savedTranscript.
+ Display knownViews:viewsKnown.
+ Display idleBlocks:savedIdleBlocks.
+ Display timeOutBlocks:savedTimeoutBlocks
+
+ "ObjectMemory applicationImageOn:'draw.img' for:DrawTool selector:#start"
+ "ObjectMemory applicationImageOn:'pm.img' for:PMSimulator selector:#start"
+!
+
+minimumApplicationImageOn:aFileName for:startupClass selector:startupSelector
+ "create a snapshot which will come up without any views
+ but starts up an application by sending startupClass the startupSelector.
+ All unneeded info is stripped from the saved image."
+
+ "create a temporary image, for continuation"
+ self snapShotOn:'temp.img'.
+
+ Display knownViews do:[:aView |
+ aView notNil ifTrue:[
+ aView superView isNil ifTrue:[
+ aView destroy
+ ]
+ ]
+ ].
+
+ self stripImage.
+
+ self applicationImageOn:aFileName for:startupClass selector:startupSelector.
+
+ "continue in old image"
+
+ OperatingSystem exec:(Arguments at:1)
+ withArguments:#('smalltalk' '-i' 'temp.img') , (Arguments copyFrom:2)
+
+ "ObjectMemory minimumApplicationImageOn:'clock1.img' for:Clock selector:#start"
+ "ObjectMemory applicationImageOn:'clock2.img' for:Clock selector:#start"
+!
+
+stripImage
+ "remove all unneeded stuff from the image - much more is possible here"
+
+ "remove all class comments"
+
+ Smalltalk allClassesDo:[:aClass |
+ aClass setComment:nil.
+ aClass methodDictionary do:[:aMethod |
+ aMethod source:''.
+ aMethod category:#none
+ ]
+ ].
+ self garbageCollect
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/OrdColl.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,375 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#OrderedCollection
+ instanceVariableNames:'contentsArray firstIndex lastIndex'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Sequenceable'
+!
+
+OrderedCollection comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+OrderedCollection have ordered elements. Insertion and removal at both ends
+is possible - therefore they can be used for queues and stacks.
+
+Instance variables:
+
+contentsArray <Array> the actual contents
+firstIndex <SmallInteger> index of first valid element
+lastIndex <SmallInteger> index of last valid element
+
+%W% %E%
+written spring 89 by claus
+'!
+
+!OrderedCollection class methodsFor:'instance creation'!
+
+new:size
+ "create a new OrderedCollection"
+
+ ^ (self basicNew) initContents:size
+!
+
+new
+ "create a new OrderedCollection"
+
+ ^ (self basicNew) initContents:10
+! !
+
+!OrderedCollection methodsFor:'testing'!
+
+size
+ "return the number of elements in the collection"
+
+ ^ lastIndex - firstIndex + 1
+!
+
+isFixedSize
+ "return true if the receiver cannot grow - this will vanish once
+ Arrays and Strings learn how to grow ..."
+
+ ^ false
+! !
+
+!OrderedCollection methodsFor:'copying'!
+
+reverse
+ "return a new collection with all elements of the receiver
+ in reverse order"
+
+ |newCollection
+ size "{ Class:SmallInteger }"
+ dstIndex "{ Class:SmallInteger }" |
+
+ size := self size.
+ newCollection := self species new:size.
+ newCollection setFirstIndex:firstIndex lastIndex:lastIndex.
+ dstIndex := size.
+ 1 to:size do:[:srcIndex |
+ newCollection at:dstIndex put:(self at:srcIndex).
+ dstIndex := dstIndex - 1
+ ].
+ ^ newCollection
+!
+
+, aCollection
+ "return a new collection formed from concatenating the receiver with
+ the argument"
+
+ |newCollection|
+
+ newCollection := self species new:(self size + aCollection size).
+ self do:[:element |
+ newCollection add:element
+ ].
+ aCollection do:[:element |
+ newCollection add:element
+ ].
+ ^ newCollection
+! !
+
+!OrderedCollection methodsFor:'adding & removing'!
+
+removeFirst
+ "remove the first element from the collection; return the element"
+
+ |anObject |
+
+ anObject := contentsArray at:firstIndex.
+ firstIndex := firstIndex + 1.
+ ^ anObject
+!
+
+removeLast
+ "remove the last element from the collection; return the element"
+
+ |anObject |
+
+ anObject := contentsArray at:lastIndex.
+ lastIndex := lastIndex - 1.
+ ^ anObject
+!
+
+remove:anObject ifAbsent:exceptionBlock
+ "remove the first occurrence of anObject from the collection;
+ return the value of exceptionBlock if anObject is not in
+ the collection"
+
+ |index "{ Class:SmallInteger }"|
+
+ index := firstIndex.
+ [index <= lastIndex] whileTrue:[
+ anObject = (contentsArray at:index) ifTrue:[
+ contentsArray replaceFrom:index to:(contentsArray size - 1)
+ with:contentsArray startingAt:(index + 1).
+ lastIndex := lastIndex - 1.
+ ^ anObject
+ ].
+ index := index + 1
+ ].
+ ^ exceptionBlock value
+!
+
+add:anObject
+ "add the argument, anObject to the end of the collection
+ Return the argument, anObject."
+
+ ^ self addLast:anObject
+!
+
+addLast:anObject
+ "add the argument, anObject to the end of the collection
+ Return the argument, anObject."
+
+ (lastIndex == contentsArray size) ifTrue:[
+ self makeRoomAtLast
+ ].
+ lastIndex := lastIndex + 1.
+ contentsArray at:lastIndex put:anObject.
+ ^ anObject
+!
+
+addFirst:anObject
+ "add the argument, anObject to the beginning of the collection.
+ Return the argument, anObject."
+
+ (firstIndex == 1) ifTrue:[
+ self makeRoomAtFront
+ ].
+ firstIndex := firstIndex - 1.
+ contentsArray at:firstIndex put:anObject.
+ ^ anObject
+!
+
+add:anObject beforeIndex:index
+ "insert the argument, anObject to become located at index.
+ Return the argument, anObject."
+
+ self makeRoomAtIndex:(index - firstIndex + 1).
+ contentsArray at:(index - firstIndex + 1) put:anObject.
+ ^ anObject
+! !
+
+!OrderedCollection methodsFor:'grow & shrink'!
+
+grow:newSize
+ "return the number of elements in the collection"
+
+ |newContents|
+
+ newSize <= (lastIndex - firstIndex + 1) ifTrue:[
+ lastIndex := firstIndex + newSize - 1
+ ] ifFalse:[
+ newContents := Array new:newSize.
+ newContents replaceFrom:1 to:(lastIndex - firstIndex + 1) with:contentsArray.
+ contentsArray := newContents.
+ firstIndex := 1.
+ lastIndex := newSize
+ ]
+! !
+
+!OrderedCollection methodsFor:'accessing'!
+
+at:anInteger
+ "return the element at index, anInteger"
+
+ ((anInteger < 1) or:[(anInteger + firstIndex - 1) > lastIndex])
+ ifTrue:[
+ self errorNoSuchElement
+ ] ifFalse:[
+ ^ contentsArray at:(anInteger + firstIndex - 1)
+ ]
+!
+
+at:anInteger put:anObject
+ "set the element at index, to be anInteger"
+
+ ((anInteger < 1) or:[(anInteger + firstIndex - 1) > lastIndex])
+ ifTrue:[
+ self errorNoSuchElement
+ ] ifFalse:[
+ ^ contentsArray at:(anInteger + firstIndex - 1) put:anObject
+ ]
+! !
+
+!OrderedCollection methodsFor:'private'!
+
+errorNoSuchElement
+ self error:'indexing non existing element'
+!
+
+setFirstIndex:newFirstIndex lastIndex:newLastIndex
+ firstIndex := newFirstIndex.
+ lastIndex := newLastIndex.
+!
+
+makeRoomAtLast
+ "grow the contents for more room at the end"
+
+ |newContents
+ oldSize "{ Class:SmallInteger }" |
+
+ oldSize := contentsArray size.
+ newContents := Array new:(oldSize * 2).
+ newContents replaceFrom:1 to:oldSize with:contentsArray.
+ contentsArray := newContents
+!
+
+makeRoomAtFront
+ "grow the contents for more room at the beginning"
+
+ |newContents
+ oldSize "{ Class:SmallInteger }" |
+
+ oldSize := contentsArray size.
+ newContents := Array new:(oldSize * 2).
+ newContents replaceFrom:(oldSize + 1) to:(oldSize * 2)
+ with:contentsArray startingAt:1.
+ contentsArray := newContents.
+ firstIndex := firstIndex + oldSize.
+ lastIndex := lastIndex + oldSize
+!
+
+makeRoomAtIndex:index
+ "grow the contents for inserting at index
+ i.e.
+ #(1 2 3 4 5 6) asOrderedCollection makeRoomAtIndex:3 -> #(1 2 nil 3 4 5 6)
+ #(1 2 3 4 5 6) asOrderedCollection makeRoomAtIndex:1 -> #(nil 1 2 3 4 5 6)
+ #(1 2 3 4 5 6) asOrderedCollection makeRoomAtIndex:7 -> #(1 2 3 4 5 6 nil)"
+
+ |newContents
+ newSize "{ Class:SmallInteger }"
+ oldSize "{ Class:SmallInteger }" |
+
+ oldSize := contentsArray size.
+ newSize := oldSize + 1.
+ newContents := Array new:newSize.
+ index == 1 ifFalse:[
+ newContents replaceFrom:1 to:index-1 with:contentsArray startingAt:1.
+ ].
+ index == newSize ifFalse:[
+ newContents replaceFrom:index + 1 to:newSize with:contentsArray startingAt:index.
+ ].
+ contentsArray := newContents.
+ lastIndex := lastIndex + 1
+!
+
+initContents:size
+ contentsArray := Array new:size.
+ firstIndex := 1.
+ lastIndex := 0
+! !
+
+!OrderedCollection methodsFor:'testing'!
+
+includes:anObject
+ "return true if the object is in the collection"
+
+ |index|
+
+ index := contentsArray indexOf:anObject.
+ ^ index between:firstIndex and:lastIndex
+!
+
+identityIndexOf:anObject
+ "return the index of the object, 0 if not in the collection"
+
+ |index|
+
+ index := contentsArray identityIndexOf:anObject.
+ index < firstIndex ifTrue:[^ 0].
+ index > lastIndex ifTrue:[^ 0].
+ ^ index - firstIndex + 1
+!
+
+identityIndexOf:anObject startingAt:startIndex
+ "return the index of the object, 0 if not in the collection"
+
+ |index|
+
+ index := contentsArray identityIndexOf:anObject startingAt:(startIndex + firstIndex - 1).
+ index < firstIndex ifTrue:[^ 0].
+ index > lastIndex ifTrue:[^ 0].
+ ^ index - firstIndex + 1
+!
+
+indexOf:anObject
+ "return the index of the object, 0 if not in the collection"
+
+ |index|
+
+ index := contentsArray indexOf:anObject.
+ index < firstIndex ifTrue:[^ 0].
+ index > lastIndex ifTrue:[^ 0].
+ ^ index - firstIndex + 1
+!
+
+indexOf:anObject startingAt:startIndex
+ "return the index of the object, 0 if not in the collection"
+
+ |index|
+
+ index := contentsArray indexOf:anObject startingAt:(startIndex + firstIndex - 1).
+ index < firstIndex ifTrue:[^ 0].
+ index > lastIndex ifTrue:[^ 0].
+ ^ index - firstIndex + 1
+! !
+
+!OrderedCollection methodsFor:'enumeration'!
+
+do:aBlock
+ "evaluate the argument, aBlock for every element in the collection."
+
+ contentsArray from:firstIndex to:lastIndex do:aBlock
+!
+
+collect:aBlock
+ "evaluate the argument, aBlock for every element in the collection
+ and return a collection of the results"
+
+ |newCollection
+ index "{ Class:SmallInteger }"
+ end "{ Class:SmallInteger }" |
+
+ end := lastIndex.
+ newCollection := (self species new).
+ index := firstIndex.
+ [index <= end] whileTrue:[
+ newCollection add:(aBlock value:(contentsArray at:index)).
+ index := index + 1
+ ].
+ ^ newCollection
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/OrderedCollection.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,375 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#OrderedCollection
+ instanceVariableNames:'contentsArray firstIndex lastIndex'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Sequenceable'
+!
+
+OrderedCollection comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+OrderedCollection have ordered elements. Insertion and removal at both ends
+is possible - therefore they can be used for queues and stacks.
+
+Instance variables:
+
+contentsArray <Array> the actual contents
+firstIndex <SmallInteger> index of first valid element
+lastIndex <SmallInteger> index of last valid element
+
+%W% %E%
+written spring 89 by claus
+'!
+
+!OrderedCollection class methodsFor:'instance creation'!
+
+new:size
+ "create a new OrderedCollection"
+
+ ^ (self basicNew) initContents:size
+!
+
+new
+ "create a new OrderedCollection"
+
+ ^ (self basicNew) initContents:10
+! !
+
+!OrderedCollection methodsFor:'testing'!
+
+size
+ "return the number of elements in the collection"
+
+ ^ lastIndex - firstIndex + 1
+!
+
+isFixedSize
+ "return true if the receiver cannot grow - this will vanish once
+ Arrays and Strings learn how to grow ..."
+
+ ^ false
+! !
+
+!OrderedCollection methodsFor:'copying'!
+
+reverse
+ "return a new collection with all elements of the receiver
+ in reverse order"
+
+ |newCollection
+ size "{ Class:SmallInteger }"
+ dstIndex "{ Class:SmallInteger }" |
+
+ size := self size.
+ newCollection := self species new:size.
+ newCollection setFirstIndex:firstIndex lastIndex:lastIndex.
+ dstIndex := size.
+ 1 to:size do:[:srcIndex |
+ newCollection at:dstIndex put:(self at:srcIndex).
+ dstIndex := dstIndex - 1
+ ].
+ ^ newCollection
+!
+
+, aCollection
+ "return a new collection formed from concatenating the receiver with
+ the argument"
+
+ |newCollection|
+
+ newCollection := self species new:(self size + aCollection size).
+ self do:[:element |
+ newCollection add:element
+ ].
+ aCollection do:[:element |
+ newCollection add:element
+ ].
+ ^ newCollection
+! !
+
+!OrderedCollection methodsFor:'adding & removing'!
+
+removeFirst
+ "remove the first element from the collection; return the element"
+
+ |anObject |
+
+ anObject := contentsArray at:firstIndex.
+ firstIndex := firstIndex + 1.
+ ^ anObject
+!
+
+removeLast
+ "remove the last element from the collection; return the element"
+
+ |anObject |
+
+ anObject := contentsArray at:lastIndex.
+ lastIndex := lastIndex - 1.
+ ^ anObject
+!
+
+remove:anObject ifAbsent:exceptionBlock
+ "remove the first occurrence of anObject from the collection;
+ return the value of exceptionBlock if anObject is not in
+ the collection"
+
+ |index "{ Class:SmallInteger }"|
+
+ index := firstIndex.
+ [index <= lastIndex] whileTrue:[
+ anObject = (contentsArray at:index) ifTrue:[
+ contentsArray replaceFrom:index to:(contentsArray size - 1)
+ with:contentsArray startingAt:(index + 1).
+ lastIndex := lastIndex - 1.
+ ^ anObject
+ ].
+ index := index + 1
+ ].
+ ^ exceptionBlock value
+!
+
+add:anObject
+ "add the argument, anObject to the end of the collection
+ Return the argument, anObject."
+
+ ^ self addLast:anObject
+!
+
+addLast:anObject
+ "add the argument, anObject to the end of the collection
+ Return the argument, anObject."
+
+ (lastIndex == contentsArray size) ifTrue:[
+ self makeRoomAtLast
+ ].
+ lastIndex := lastIndex + 1.
+ contentsArray at:lastIndex put:anObject.
+ ^ anObject
+!
+
+addFirst:anObject
+ "add the argument, anObject to the beginning of the collection.
+ Return the argument, anObject."
+
+ (firstIndex == 1) ifTrue:[
+ self makeRoomAtFront
+ ].
+ firstIndex := firstIndex - 1.
+ contentsArray at:firstIndex put:anObject.
+ ^ anObject
+!
+
+add:anObject beforeIndex:index
+ "insert the argument, anObject to become located at index.
+ Return the argument, anObject."
+
+ self makeRoomAtIndex:(index - firstIndex + 1).
+ contentsArray at:(index - firstIndex + 1) put:anObject.
+ ^ anObject
+! !
+
+!OrderedCollection methodsFor:'grow & shrink'!
+
+grow:newSize
+ "return the number of elements in the collection"
+
+ |newContents|
+
+ newSize <= (lastIndex - firstIndex + 1) ifTrue:[
+ lastIndex := firstIndex + newSize - 1
+ ] ifFalse:[
+ newContents := Array new:newSize.
+ newContents replaceFrom:1 to:(lastIndex - firstIndex + 1) with:contentsArray.
+ contentsArray := newContents.
+ firstIndex := 1.
+ lastIndex := newSize
+ ]
+! !
+
+!OrderedCollection methodsFor:'accessing'!
+
+at:anInteger
+ "return the element at index, anInteger"
+
+ ((anInteger < 1) or:[(anInteger + firstIndex - 1) > lastIndex])
+ ifTrue:[
+ self errorNoSuchElement
+ ] ifFalse:[
+ ^ contentsArray at:(anInteger + firstIndex - 1)
+ ]
+!
+
+at:anInteger put:anObject
+ "set the element at index, to be anInteger"
+
+ ((anInteger < 1) or:[(anInteger + firstIndex - 1) > lastIndex])
+ ifTrue:[
+ self errorNoSuchElement
+ ] ifFalse:[
+ ^ contentsArray at:(anInteger + firstIndex - 1) put:anObject
+ ]
+! !
+
+!OrderedCollection methodsFor:'private'!
+
+errorNoSuchElement
+ self error:'indexing non existing element'
+!
+
+setFirstIndex:newFirstIndex lastIndex:newLastIndex
+ firstIndex := newFirstIndex.
+ lastIndex := newLastIndex.
+!
+
+makeRoomAtLast
+ "grow the contents for more room at the end"
+
+ |newContents
+ oldSize "{ Class:SmallInteger }" |
+
+ oldSize := contentsArray size.
+ newContents := Array new:(oldSize * 2).
+ newContents replaceFrom:1 to:oldSize with:contentsArray.
+ contentsArray := newContents
+!
+
+makeRoomAtFront
+ "grow the contents for more room at the beginning"
+
+ |newContents
+ oldSize "{ Class:SmallInteger }" |
+
+ oldSize := contentsArray size.
+ newContents := Array new:(oldSize * 2).
+ newContents replaceFrom:(oldSize + 1) to:(oldSize * 2)
+ with:contentsArray startingAt:1.
+ contentsArray := newContents.
+ firstIndex := firstIndex + oldSize.
+ lastIndex := lastIndex + oldSize
+!
+
+makeRoomAtIndex:index
+ "grow the contents for inserting at index
+ i.e.
+ #(1 2 3 4 5 6) asOrderedCollection makeRoomAtIndex:3 -> #(1 2 nil 3 4 5 6)
+ #(1 2 3 4 5 6) asOrderedCollection makeRoomAtIndex:1 -> #(nil 1 2 3 4 5 6)
+ #(1 2 3 4 5 6) asOrderedCollection makeRoomAtIndex:7 -> #(1 2 3 4 5 6 nil)"
+
+ |newContents
+ newSize "{ Class:SmallInteger }"
+ oldSize "{ Class:SmallInteger }" |
+
+ oldSize := contentsArray size.
+ newSize := oldSize + 1.
+ newContents := Array new:newSize.
+ index == 1 ifFalse:[
+ newContents replaceFrom:1 to:index-1 with:contentsArray startingAt:1.
+ ].
+ index == newSize ifFalse:[
+ newContents replaceFrom:index + 1 to:newSize with:contentsArray startingAt:index.
+ ].
+ contentsArray := newContents.
+ lastIndex := lastIndex + 1
+!
+
+initContents:size
+ contentsArray := Array new:size.
+ firstIndex := 1.
+ lastIndex := 0
+! !
+
+!OrderedCollection methodsFor:'testing'!
+
+includes:anObject
+ "return true if the object is in the collection"
+
+ |index|
+
+ index := contentsArray indexOf:anObject.
+ ^ index between:firstIndex and:lastIndex
+!
+
+identityIndexOf:anObject
+ "return the index of the object, 0 if not in the collection"
+
+ |index|
+
+ index := contentsArray identityIndexOf:anObject.
+ index < firstIndex ifTrue:[^ 0].
+ index > lastIndex ifTrue:[^ 0].
+ ^ index - firstIndex + 1
+!
+
+identityIndexOf:anObject startingAt:startIndex
+ "return the index of the object, 0 if not in the collection"
+
+ |index|
+
+ index := contentsArray identityIndexOf:anObject startingAt:(startIndex + firstIndex - 1).
+ index < firstIndex ifTrue:[^ 0].
+ index > lastIndex ifTrue:[^ 0].
+ ^ index - firstIndex + 1
+!
+
+indexOf:anObject
+ "return the index of the object, 0 if not in the collection"
+
+ |index|
+
+ index := contentsArray indexOf:anObject.
+ index < firstIndex ifTrue:[^ 0].
+ index > lastIndex ifTrue:[^ 0].
+ ^ index - firstIndex + 1
+!
+
+indexOf:anObject startingAt:startIndex
+ "return the index of the object, 0 if not in the collection"
+
+ |index|
+
+ index := contentsArray indexOf:anObject startingAt:(startIndex + firstIndex - 1).
+ index < firstIndex ifTrue:[^ 0].
+ index > lastIndex ifTrue:[^ 0].
+ ^ index - firstIndex + 1
+! !
+
+!OrderedCollection methodsFor:'enumeration'!
+
+do:aBlock
+ "evaluate the argument, aBlock for every element in the collection."
+
+ contentsArray from:firstIndex to:lastIndex do:aBlock
+!
+
+collect:aBlock
+ "evaluate the argument, aBlock for every element in the collection
+ and return a collection of the results"
+
+ |newCollection
+ index "{ Class:SmallInteger }"
+ end "{ Class:SmallInteger }" |
+
+ end := lastIndex.
+ newCollection := (self species new).
+ index := firstIndex.
+ [index <= end] whileTrue:[
+ newCollection add:(aBlock value:(contentsArray at:index)).
+ index := index + 1
+ ].
+ ^ newCollection
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/PipeStr.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,135 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+NonPositionableExternalStream subclass:#PipeStream
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams-External'
+!
+
+PipeStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+#ifndef transputer
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+%}
+
+!PipeStream class methodsFor:'instance creation'!
+
+writingTo:command
+ "create and return a new pipeStream which can write to the unix command
+ given by command."
+
+ ^ (self basicNew) writingTo:command
+
+ "PipeStream writingTo:'sort'"
+!
+
+readingFrom:command
+ "create and return a new pipeStream which can read from the unix command
+ given by command."
+
+ ^ (self basicNew) readingFrom:command
+
+ "PipeStream readingFrom:'ls'"
+! !
+
+!PipeStream methodsFor:'instance release'!
+
+closeFile
+ "low level close - redefined since we close a pipe here"
+
+%{ /* NOCONTEXT */
+#ifndef transputer
+ pclose(MKFD(_INST(filePointer)));
+#endif
+%}
+! !
+
+!PipeStream methodsFor:'private'!
+
+openPipeFor:commandString withMode:mode
+ "open a pipe to commandString; mode may be 'r' or 'w'"
+
+ |retVal|
+
+ unBuffered := true.
+%{
+#ifndef transputer
+ {
+ FILE *f;
+ extern OBJ ErrorNumber;
+ extern errno;
+
+ if (_isString(commandString) && _isString(mode)) {
+ f = (FILE *)popen((char *) _stringVal(commandString),
+ (char *) _stringVal(mode));
+ if (f == NULL) {
+ ErrorNumber = _MKSMALLINT(errno);
+ } else {
+ _INST(filePointer) = MKOBJ(f);
+ retVal = self;
+ }
+ }
+ }
+#endif
+%}
+.
+ retVal notNil ifTrue:[
+ lobby register:self
+ ].
+ ^ retVal
+!
+
+readingFrom:command
+ "setup the receiver to read from command"
+
+ self readonly.
+ ^ self openPipeFor:command withMode:'r'
+!
+
+writingTo:command
+ "setup the receiver to write to command"
+
+ self writeonly.
+ ^ self openPipeFor:command withMode:'w'
+! !
+
+!PipeStream methodsFor:'redefined basic'!
+
+size
+ "redefined since pipes have no size"
+
+ ^ self shouldNotImplement
+!
+
+position:newpos
+ "redefined since pipes cannot be positioned"
+
+ ^ self shouldNotImplement
+!
+
+position
+ "redefined since pipes have no position"
+
+ ^ self shouldNotImplement
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/PipeStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,135 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+NonPositionableExternalStream subclass:#PipeStream
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams-External'
+!
+
+PipeStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+#ifndef transputer
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+%}
+
+!PipeStream class methodsFor:'instance creation'!
+
+writingTo:command
+ "create and return a new pipeStream which can write to the unix command
+ given by command."
+
+ ^ (self basicNew) writingTo:command
+
+ "PipeStream writingTo:'sort'"
+!
+
+readingFrom:command
+ "create and return a new pipeStream which can read from the unix command
+ given by command."
+
+ ^ (self basicNew) readingFrom:command
+
+ "PipeStream readingFrom:'ls'"
+! !
+
+!PipeStream methodsFor:'instance release'!
+
+closeFile
+ "low level close - redefined since we close a pipe here"
+
+%{ /* NOCONTEXT */
+#ifndef transputer
+ pclose(MKFD(_INST(filePointer)));
+#endif
+%}
+! !
+
+!PipeStream methodsFor:'private'!
+
+openPipeFor:commandString withMode:mode
+ "open a pipe to commandString; mode may be 'r' or 'w'"
+
+ |retVal|
+
+ unBuffered := true.
+%{
+#ifndef transputer
+ {
+ FILE *f;
+ extern OBJ ErrorNumber;
+ extern errno;
+
+ if (_isString(commandString) && _isString(mode)) {
+ f = (FILE *)popen((char *) _stringVal(commandString),
+ (char *) _stringVal(mode));
+ if (f == NULL) {
+ ErrorNumber = _MKSMALLINT(errno);
+ } else {
+ _INST(filePointer) = MKOBJ(f);
+ retVal = self;
+ }
+ }
+ }
+#endif
+%}
+.
+ retVal notNil ifTrue:[
+ lobby register:self
+ ].
+ ^ retVal
+!
+
+readingFrom:command
+ "setup the receiver to read from command"
+
+ self readonly.
+ ^ self openPipeFor:command withMode:'r'
+!
+
+writingTo:command
+ "setup the receiver to write to command"
+
+ self writeonly.
+ ^ self openPipeFor:command withMode:'w'
+! !
+
+!PipeStream methodsFor:'redefined basic'!
+
+size
+ "redefined since pipes have no size"
+
+ ^ self shouldNotImplement
+!
+
+position:newpos
+ "redefined since pipes cannot be positioned"
+
+ ^ self shouldNotImplement
+!
+
+position
+ "redefined since pipes have no position"
+
+ ^ self shouldNotImplement
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Point.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,398 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ArithmeticValue subclass:#Point
+ instanceVariableNames:'x y'
+ classVariableNames:'PointZero PointOne'
+ poolDictionaries:''
+ category:'Graphics-Primitives'
+!
+
+Point comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+I represent a point in 2D space. Or I can be used to represent
+an extent (of a rectangle for example), in which case my x-coordinate
+represents the width, and y-coordinate the height of something.
+
+The x and y coordinate are usually numbers.
+
+Instance variables:
+
+x <Number> the x-coordinate of myself
+y <Number> the y-coordinate of myself
+
+%W% %E%
+written 89 by claus
+'!
+
+!Point class methodsFor:'initialization'!
+
+initialize
+ PointZero := 0 @ 0.
+ PointOne := 1 @ 1
+! !
+
+!Point class methodsFor:'constants'!
+
+zero
+ ^ PointZero
+!
+
+unity
+ ^ PointOne
+! !
+
+!Point class methodsFor:'instance creation'!
+
+x:newX y:newY
+ "create and return a new point with coordinates newX and newY"
+
+%{ /* NOCONTEXT */
+ extern char *newNextPtr, *newEndPtr;
+
+ if (self == Point) {
+ if (_CanDoQuickNew(sizeof(struct point))) {
+ OBJ newPoint;
+
+ _qCheckedAlignedNew(newPoint, sizeof(struct point), __context);
+ _InstPtr(newPoint)->o_class = Point;
+ _PointInstPtr(newPoint)->p_x = newX;
+ _PointInstPtr(newPoint)->p_y = newY;
+ /* no store check needed - its definitely in newSpace */
+ RETURN ( newPoint );
+ }
+ }
+%}
+.
+ ^ (self basicNew) x:newX y:newY
+!
+
+readFrom:aStream
+ "return the next Point from the (character-)stream aStream;
+ skipping all whitespace first; return nil if no point"
+
+ |newX newY|
+
+ newX := Number readFrom:aStream.
+ newX isNil ifTrue:[^ nil].
+ (aStream skipSeparators ~~ $@) ifTrue:[^nil].
+ aStream next.
+ newY := Number readFrom:aStream.
+ newY isNil ifTrue:[^ nil].
+ ^ (self basicNew) x:newX y:newY
+! !
+
+!Point methodsFor:'accessing'!
+
+x
+ "return the x coordinate"
+
+ ^x
+!
+
+y
+ "return the y coordinate"
+
+ ^y
+!
+
+x:newX
+ "set the x coordinate to be the argument, aNumber"
+
+ x := newX
+!
+
+y:newY
+ "set the y coordinate to be the argument, aNumber"
+
+ y := newY
+!
+
+x:newX y:newY
+ "set both the x and y coordinates"
+
+ x := newX.
+ y := newY
+! !
+
+!Point methodsFor:'comparing'!
+
+hash
+ "return a number for hashing"
+
+ ^ (x hash) bitXor:(y hash)
+!
+
+< aPoint
+ "return true if the receiver is above and to the left
+ of the argument, aPoint"
+
+ |p|
+
+ p := aPoint asPoint.
+ x >= (p x) ifTrue:[^ false].
+ y >= (p y) ifTrue:[^ false].
+ ^ true
+!
+
+> aPoint
+ "return true if the receiver is below and to the right
+ of the argument, aPoint"
+
+ |p|
+
+ p := aPoint asPoint.
+ x <= (p x) ifTrue:[^ false].
+ y <= (p y) ifTrue:[^ false].
+ ^ true
+!
+
+= aPoint
+ "return true if the receiver represents the same point as
+ the argument, aPoint"
+
+ |p|
+
+ (aPoint isMemberOf:Point) ifTrue:[
+ x ~~ (aPoint x) ifTrue:[^ false].
+ y ~~ (aPoint y) ifTrue:[^ false].
+ ^ true
+ ].
+ aPoint respondsToArithmetic ifFalse:[ ^ false].
+ p := aPoint asPoint.
+ x ~~ (p x) ifTrue:[^ false].
+ y ~~ (p y) ifTrue:[^ false].
+ ^ true
+!
+
+max:aPoint
+ "return the lower right corner of the rectangle uniquely defined by
+ the receiver and the argument, aPoint"
+
+ |p maxX maxY|
+
+ p := aPoint asPoint.
+ maxX := x max:(p x).
+ maxY := y max:(p y).
+ ^ maxX @ maxY
+!
+
+min:aPoint
+ "return the upper left corner of the rectangle uniquely defined by
+ the receiver and the argument, aPoint"
+
+ |p minX minY|
+
+ p := aPoint asPoint.
+ minX := x min:(p x).
+ minY := y min:(p y).
+ ^ minX @ minY
+! !
+
+!Point methodsFor:'converting'!
+
+generality
+ ^ 120
+!
+
+coerce:anObject
+ ^ anObject asPoint
+!
+
+asPoint
+ "return the receiver as Point - this is the receiver"
+
+ ^ self
+!
+
+asRectangle
+ "return a zero-width rectangle consisting of origin
+ and corner beeing the receiver"
+
+ ^ self corner:self
+! !
+
+!Point methodsFor:'creating rectangles'!
+
+corner:aPoint
+ "return a rectangle whose origin is self and corner is aPoint"
+
+ ^ Rectangle origin:self corner:aPoint
+!
+
+extent:aPoint
+ "return a rectangle whose origin is self and extent is aPoint"
+
+ ^ Rectangle origin:self extent:aPoint
+! !
+
+!Point methodsFor:'transformations'!
+
++ scale
+ "Return a new Point that is the sum of the
+ receiver and scale (which is a Point or Number)."
+
+ |scalePoint|
+
+ scalePoint := scale asPoint.
+ ^ (x + scalePoint x) @ (y + scalePoint y)
+!
+
+- scale
+ "Return a new Point that is the difference of the
+ receiver and scale (which is a Point or Number)."
+
+ |scalePoint|
+
+ scalePoint := scale asPoint.
+ ^ (x - scalePoint x) @ (y - scalePoint y)
+!
+
+* scale
+ "Return a new Point that is the product of the
+ receiver and scale (which is a Point or Number)."
+
+ |scalePoint|
+
+ scalePoint := scale asPoint.
+ ^ (x * scalePoint x) @ (y * scalePoint y)
+!
+
+/ scale
+ "Return a new Point that is the integer quotient of the
+ receiver and scale (which is a Point or Number)."
+
+ |scalePoint|
+
+ scalePoint := scale asPoint.
+ ^ (x / scalePoint x) @ (y / scalePoint y)
+!
+
+// scale
+ "Return a new Point that is the quotient of the
+ receiver and scale (which is a Point or Number)."
+
+ |scalePoint|
+
+ scalePoint := scale asPoint.
+ ^ (x // scalePoint x) @ (y // scalePoint y)
+!
+
+reciprocal
+ "return a new point where the coordinates are
+ the reciproce of mine"
+
+ ^ (1 / x) @ (1 / y)
+!
+
+negated
+ "return a new point with my coordinates negated i.e.
+ the receiver mirrored at the origin"
+
+ ^ (x negated) @ (y negated)
+!
+
+scaleBy:aScale
+ "Return a new Point that is the product of the
+ receiver and scale (which is a Point or Number)."
+
+ ^ self * aScale
+!
+
+translateBy:anOffset
+ "Return a new Point that is the sum of the
+ receiver and scale (which is a Point or Number)."
+
+ ^ self + anOffset
+! !
+
+!Point methodsFor:'misc'!
+
+dist:aPoint
+ "return the distance between aPoint and the receiver."
+
+ ^ (aPoint - self) r
+!
+
+dotProduct:aPoint
+ "return a Number that is the dot product of the receiver and
+ the argument, aPoint. That is, the two points are
+ multipled and the coordinates of the result summed."
+
+ |temp|
+
+ temp := self * aPoint.
+ ^ temp x abs + temp y abs
+!
+
+r
+ "return the receiver's radius in polar coordinate system."
+
+ ^ (self dotProduct:self) sqrt
+!
+
+abs
+ "return a new point with my coordinates taken from the absolute values"
+
+ ^ (x abs) @ (y abs)
+!
+
+truncated
+ "return a new point with my coordinates truncated as integer"
+
+ ^ (x truncated) @ (y truncated)
+!
+
+rounded
+ "return a new point with my coordinates rounded to the next integer
+ coordinated (use for gridding)"
+
+ ^ (x rounded) @ (y rounded)
+!
+
+grid:gridPoint
+ "return a new point with coordinates grided (i.e. rounded to the
+ nearest point on the grid)"
+
+ |newX newY gridX gridY|
+
+ gridX := gridPoint x.
+ (gridX <= 1) ifTrue:[
+ newX := x asInteger
+ ] ifFalse:[
+ newX := ((x + (gridX // 2)) // gridX) * gridX
+ ].
+ gridY := gridPoint y.
+ (gridY <= 1) ifTrue:[
+ newY := y asInteger
+ ] ifFalse:[
+ newY := ((y + (gridY // 2)) // gridY) * gridY
+ ].
+ ^ newX @ newY
+! !
+
+!Point methodsFor:'printing & storing'!
+
+printString
+ "return my printString"
+
+ ^ x printString , '@' , y printString
+!
+
+storeString
+ "return my storeString"
+
+ ^ self printString
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/PosStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,486 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Stream subclass:#PositionableStream
+ instanceVariableNames:'collection position readLimit continueBlock abortBlock'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams'
+!
+
+PositionableStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+Instances of myself allow positioning the read pointer.
+I also add methods for source-chunk reading and writing
+and for filing-in/out of source code.
+
+%W% %E%
+
+TODO
+ change to use signals for error handling during fileIn
+ (get rid of continue/abort blocks)
+'!
+
+!PositionableStream class methodsFor:'constants'!
+
+chunkSeparator
+ "return the chunk-separation character"
+
+ ^ $!!
+! !
+
+!PositionableStream class methodsFor:'instance creation'!
+
+on:aCollection
+ "return a new PositionableStream streaming on aCollection"
+
+ ^ (self basicNew) on:aCollection
+!
+
+on:aCollection from:first to:last
+ "return a new PositionableStream streaming on aCollection
+ from first to last"
+
+ |newStream|
+
+ newStream := (self basicNew) on:aCollection.
+ newStream position:first.
+ newStream readLimit:last.
+ ^ newStream
+! !
+
+!PositionableStream methodsFor:'private'!
+
+on:aCollection
+ "setup for streaming on aCollection"
+
+ collection := aCollection.
+ readLimit := aCollection size.
+ position := 1
+!
+
+positionError
+ "report an error when positioning past the end"
+
+ ^ self error:'cannot position past end of collection'
+! !
+
+!PositionableStream methodsFor:'accessing'!
+
+contents
+ "return the entire contents of the stream"
+
+ ^ collection
+!
+
+peek
+ "look ahead for and return the next element"
+
+ |peekObject|
+
+ peekObject := self next.
+ self position:(self position - 1).
+ ^ peekObject
+!
+
+peekFor:something
+ "return true and move past if next == something"
+
+ self next == something ifTrue:[
+ ^ true
+ ].
+ self position:(self position - 1).
+ ^ false
+!
+
+readLimit:aNumber
+ "set the read-limit"
+
+ readLimit := aNumber
+!
+
+upTo:element
+ "return a collection of the elements up-to
+ (but excluding) the argument, element.
+ Return nil if the stream-end is reached before."
+
+ |newColl e|
+
+ newColl := collection species new.
+ e := self next.
+ [e = element] whileFalse:[
+ newColl := newColl copyWith:e.
+ e := self next.
+ self atEnd ifTrue:[^ nil]
+ ].
+ ^ newColl
+
+ "(ReadStream on:'1234567890') upTo:$5"
+ "(ReadStream on:'123456') upTo:$7"
+! !
+
+!PositionableStream methodsFor:'testing'!
+
+atEnd
+ "return true, if the read-position is at the end"
+
+ ^ position > readLimit
+!
+
+isEmpty
+ "return true, if the contents of the stream is empty"
+
+ ^ readLimit == 0
+! !
+
+!PositionableStream methodsFor:'positioning'!
+
+position
+ "return the read position"
+
+ ^ position
+!
+
+position:index
+ "set the read position"
+
+ (index > (readLimit + 1)) ifTrue: [^ self positionError].
+ position := index
+!
+
+reset
+ "set the read position to the beginning of the collection"
+
+ position := 1
+!
+
+setToEnd
+ "set the read position to the end of the collection"
+
+ position := readLimit
+!
+
+skip:numberToSkip
+ "skip the next numberToSkip elements"
+
+ self position:(position + numberToSkip)
+! !
+
+!PositionableStream methodsFor:'fileIn-Out'!
+
+skipSeparators
+ "skip all whitespace; so that next will return next non-white-space
+ element"
+
+ |nextOne|
+
+ nextOne := self peek.
+ [(nextOne notNil) and:[nextOne isSeparator]] whileTrue:[
+ self next.
+ nextOne := self peek
+ ].
+ ^ nextOne
+!
+
+skipSeparatorsExceptCR
+ "skip all whitespace except newlines;
+ next will return next non-white-space element"
+
+ |nextOne|
+
+ nextOne := self peek.
+ [(nextOne notNil) and:[nextOne isSeparator]] whileTrue:[
+ nextOne isEndOfLineCharacter ifTrue:[^ nextOne].
+ self next.
+ nextOne := self peek
+ ].
+ ^ nextOne
+!
+
+skipFor:anObject
+ "skip all objects up-to and including anObject; return the element
+ after"
+
+ |nextOne|
+
+ nextOne := self next.
+ [nextOne ~~ anObject] whileTrue:[
+ self atEnd ifTrue:[^ nil].
+ self next.
+ nextOne := self peek
+ ].
+ ^ self next
+!
+
+nextChunk
+ "return the next chunk, i.e. all characters up to the next
+ non-doubled exclamation mark; undouble doubled exclamation marks"
+
+ |theString sep newString done thisChar nextChar inPrimitive
+ index "{ Class:SmallInteger }"
+ currSize "{ Class:SmallInteger }" |
+
+ sep := self class chunkSeparator.
+ theString := String new:500.
+ currSize := 500.
+ thisChar := self skipSeparators.
+ thisChar := self next.
+ index := 0.
+ done := false.
+ inPrimitive := false.
+
+ [done] whileFalse:[
+ ((index + 2) <= currSize) ifFalse:[
+ newString := String new:(currSize * 2).
+ newString replaceFrom:1 to:currSize with:theString.
+ currSize := currSize * 2.
+ theString := newString
+ ].
+ thisChar isNil ifTrue:[
+ done := true
+ ] ifFalse:[
+ (thisChar == $% ) ifTrue:[
+ nextChar := self peek.
+ (nextChar == ${ ) ifTrue:[
+ inPrimitive := true.
+ index := index + 1.
+ theString at:index put:thisChar.
+ thisChar := self next
+ ] ifFalse:[
+ (nextChar == $} ) ifTrue:[
+ inPrimitive := false.
+ index := index + 1.
+ theString at:index put:thisChar.
+ thisChar := self next
+ ]
+ ]
+ ] ifFalse:[
+ inPrimitive ifFalse:[
+ (thisChar == sep) ifTrue:[
+ (self peek == sep) ifFalse:[
+ done := true
+ ] ifTrue:[
+ self next
+ ]
+ ]
+ ]
+ ]
+ ].
+ done ifFalse:[
+ index := index + 1.
+ theString at:index put:thisChar.
+ thisChar := self next
+ ]
+ ].
+ (index == 0) ifTrue:[^ ''].
+ ^ theString copyFrom:1 to:index
+!
+
+nextChunkPut:aString
+ "put aString as a chunk onto the receiver;
+ double all exclamation marks and append an exclamation mark"
+
+ |sep gotPercent inPrimitive character
+ index "{ Class:SmallInteger }"
+ endIndex "{ Class:SmallInteger }"
+ next "{ Class:SmallInteger }" |
+
+ sep := self class chunkSeparator.
+ inPrimitive := false.
+ gotPercent := false.
+ index := 1.
+ endIndex := aString size.
+
+ [index <= endIndex] whileTrue:[
+ next := aString indexOf:$% startingAt:index ifAbsent:[endIndex + 1].
+ next := next min:
+ (aString indexOf:${ startingAt:index ifAbsent:[endIndex + 1]).
+ next := next min:
+ (aString indexOf:$} startingAt:index ifAbsent:[endIndex + 1]).
+ next := next min:
+ (aString indexOf:sep startingAt:index ifAbsent:[endIndex + 1]).
+
+ ((index == 1) and:[next == (endIndex + 1)]) ifTrue:[
+ self nextPutAll:aString
+ ] ifFalse:[
+ self nextPutAll:(aString copyFrom:index to:(next - 1))
+ ].
+
+ index := next.
+ (index <= endIndex) ifTrue:[
+ character := aString at:index.
+ (character == $% ) ifTrue:[
+ gotPercent := true
+ ] ifFalse:[
+ (character == ${ ) ifTrue:[
+ gotPercent ifTrue:[
+ inPrimitive := true
+ ]
+ ] ifFalse:[
+ (character == $} ) ifTrue:[
+ gotPercent ifTrue:[
+ inPrimitive := false
+ ]
+ ] ifFalse:[
+ inPrimitive ifFalse:[
+ (character == sep) ifTrue:[
+ self nextPut:sep
+ ]
+ ]
+ ]
+ ].
+ gotPercent := false
+ ].
+ self nextPut:character.
+ index := index + 1
+ ]
+ ].
+ self nextPut:sep
+!
+
+fileInNextChunkNotifying:someone
+ "read next chunk, evaluate it and return the result;
+ someone is notified of errors"
+
+ |aString sawExcla sep|
+
+ sep := self class chunkSeparator.
+ self skipSeparators.
+ self atEnd ifFalse:[
+ sawExcla := self peekFor:sep.
+ aString := self nextChunk.
+ aString size ~~ 0 ifTrue:[
+ sawExcla ifFalse:[
+ ^ Compiler evaluate:aString notifying:someone
+ ].
+ ^ (Compiler evaluate:aString notifying:someone)
+ fileInFrom:self notifying:someone
+ ]
+ ].
+ ^ nil
+!
+
+fileInNextChunk
+ "read next chunk, evaluate it and return the result"
+
+ ^ self fileInNextChunkNotifying:nil
+!
+
+fileInNotifying:someone
+ "file in from the receiver, i.e. read chunks and evaluate them -
+ return the value of the last chunk; someone is notified of errors"
+
+ |lastValue|
+
+ self position:1.
+ abortBlock := [^ nil].
+ continueBlock := [].
+ Smalltalk at:#ErrorHandler put:self.
+ [self atEnd] whileFalse:[
+ lastValue := self fileInNextChunkNotifying:someone
+ ].
+ Smalltalk at:#ErrorHandler put:nil.
+ ^ lastValue
+!
+
+fileIn
+ "file in from the receiver, i.e. read chunks and evaluate them -
+ return the value of the last chunk"
+
+ ^ self fileInNotifying:self
+!
+
+askForDebug:message
+ |box|
+
+ box := OptionBox title:message numberOfOptions:3.
+ box actions:(Array with:[^ #abort]
+ with:[^ #debug]
+ with:[^ #continue]).
+ box buttonTitles:#('abort' 'debug' 'continue').
+ box showAtPointer.
+ ^ #abort
+!
+
+catch:aSymbol with:aMessage for:anObject
+ "this one is sent when an error occurs while filing in -
+ we dont want a debugger to come up but simply notify
+ the error (also on the Transcript so you have a trace of it)"
+
+ |message action|
+
+ Smalltalk at:#ErrorHandler put:nil.
+ (aSymbol == #doesNotUnderstand:) ifTrue:[
+ anObject isNil ifTrue:[
+ "try to give a bit more detail on what went wrong"
+ (Metaclass respondsTo:(aMessage selector)) ifTrue:[
+ ('subclass:*' match:(aMessage selector)) ifTrue:[
+ message := 'no superclass for ' , (aMessage arguments at:1)
+ ] ifFalse:[
+ message := 'definitions for nonexisting class'
+ ]
+ ] ifFalse:[
+ message := 'bad message: ' , aMessage selector, ' to UndefinedObject'
+ ]
+ ] ifFalse:[
+ message := 'bad message: ' , aMessage selector ,
+ ' to ' , anObject classNameWithArticle
+ ]
+ ] ifFalse:[
+ (aSymbol == #error:) ifTrue:[
+ message := aMessage
+ ] ifFalse:[
+ message := 'during fileIn'
+ ]
+ ].
+ message := 'Error: ' , message.
+ Transcript showCr:message.
+
+ YesNoBox notNil ifTrue:[
+ action := self askForDebug:message.
+ action == #debug ifTrue:[
+ Debugger enterWithMessage:message
+ ].
+ action == #continue ifTrue:[
+ continueBlock value
+ ].
+ ] ifFalse:[
+ self notify:message
+ ].
+
+ abortBlock value.
+ ^ nil
+!
+
+error:aMessage position:position to:endPos
+ "error notification during fileIn with no requestor"
+
+ position printOn:Transcript.
+ Transcript show:' '.
+ Transcript showCr:aMessage.
+ ^ false
+!
+
+correctableError:aMessage position:position to:endPos
+ "error notification during fileIn with no requestor"
+
+ ^ self error:aMessage position:position to:endPos
+!
+
+warning:aMessage position:position to:endPos
+ "warning notification during fileIn with no requestor - ignore it"
+
+ ^ self
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/PositionableStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,486 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Stream subclass:#PositionableStream
+ instanceVariableNames:'collection position readLimit continueBlock abortBlock'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams'
+!
+
+PositionableStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+Instances of myself allow positioning the read pointer.
+I also add methods for source-chunk reading and writing
+and for filing-in/out of source code.
+
+%W% %E%
+
+TODO
+ change to use signals for error handling during fileIn
+ (get rid of continue/abort blocks)
+'!
+
+!PositionableStream class methodsFor:'constants'!
+
+chunkSeparator
+ "return the chunk-separation character"
+
+ ^ $!!
+! !
+
+!PositionableStream class methodsFor:'instance creation'!
+
+on:aCollection
+ "return a new PositionableStream streaming on aCollection"
+
+ ^ (self basicNew) on:aCollection
+!
+
+on:aCollection from:first to:last
+ "return a new PositionableStream streaming on aCollection
+ from first to last"
+
+ |newStream|
+
+ newStream := (self basicNew) on:aCollection.
+ newStream position:first.
+ newStream readLimit:last.
+ ^ newStream
+! !
+
+!PositionableStream methodsFor:'private'!
+
+on:aCollection
+ "setup for streaming on aCollection"
+
+ collection := aCollection.
+ readLimit := aCollection size.
+ position := 1
+!
+
+positionError
+ "report an error when positioning past the end"
+
+ ^ self error:'cannot position past end of collection'
+! !
+
+!PositionableStream methodsFor:'accessing'!
+
+contents
+ "return the entire contents of the stream"
+
+ ^ collection
+!
+
+peek
+ "look ahead for and return the next element"
+
+ |peekObject|
+
+ peekObject := self next.
+ self position:(self position - 1).
+ ^ peekObject
+!
+
+peekFor:something
+ "return true and move past if next == something"
+
+ self next == something ifTrue:[
+ ^ true
+ ].
+ self position:(self position - 1).
+ ^ false
+!
+
+readLimit:aNumber
+ "set the read-limit"
+
+ readLimit := aNumber
+!
+
+upTo:element
+ "return a collection of the elements up-to
+ (but excluding) the argument, element.
+ Return nil if the stream-end is reached before."
+
+ |newColl e|
+
+ newColl := collection species new.
+ e := self next.
+ [e = element] whileFalse:[
+ newColl := newColl copyWith:e.
+ e := self next.
+ self atEnd ifTrue:[^ nil]
+ ].
+ ^ newColl
+
+ "(ReadStream on:'1234567890') upTo:$5"
+ "(ReadStream on:'123456') upTo:$7"
+! !
+
+!PositionableStream methodsFor:'testing'!
+
+atEnd
+ "return true, if the read-position is at the end"
+
+ ^ position > readLimit
+!
+
+isEmpty
+ "return true, if the contents of the stream is empty"
+
+ ^ readLimit == 0
+! !
+
+!PositionableStream methodsFor:'positioning'!
+
+position
+ "return the read position"
+
+ ^ position
+!
+
+position:index
+ "set the read position"
+
+ (index > (readLimit + 1)) ifTrue: [^ self positionError].
+ position := index
+!
+
+reset
+ "set the read position to the beginning of the collection"
+
+ position := 1
+!
+
+setToEnd
+ "set the read position to the end of the collection"
+
+ position := readLimit
+!
+
+skip:numberToSkip
+ "skip the next numberToSkip elements"
+
+ self position:(position + numberToSkip)
+! !
+
+!PositionableStream methodsFor:'fileIn-Out'!
+
+skipSeparators
+ "skip all whitespace; so that next will return next non-white-space
+ element"
+
+ |nextOne|
+
+ nextOne := self peek.
+ [(nextOne notNil) and:[nextOne isSeparator]] whileTrue:[
+ self next.
+ nextOne := self peek
+ ].
+ ^ nextOne
+!
+
+skipSeparatorsExceptCR
+ "skip all whitespace except newlines;
+ next will return next non-white-space element"
+
+ |nextOne|
+
+ nextOne := self peek.
+ [(nextOne notNil) and:[nextOne isSeparator]] whileTrue:[
+ nextOne isEndOfLineCharacter ifTrue:[^ nextOne].
+ self next.
+ nextOne := self peek
+ ].
+ ^ nextOne
+!
+
+skipFor:anObject
+ "skip all objects up-to and including anObject; return the element
+ after"
+
+ |nextOne|
+
+ nextOne := self next.
+ [nextOne ~~ anObject] whileTrue:[
+ self atEnd ifTrue:[^ nil].
+ self next.
+ nextOne := self peek
+ ].
+ ^ self next
+!
+
+nextChunk
+ "return the next chunk, i.e. all characters up to the next
+ non-doubled exclamation mark; undouble doubled exclamation marks"
+
+ |theString sep newString done thisChar nextChar inPrimitive
+ index "{ Class:SmallInteger }"
+ currSize "{ Class:SmallInteger }" |
+
+ sep := self class chunkSeparator.
+ theString := String new:500.
+ currSize := 500.
+ thisChar := self skipSeparators.
+ thisChar := self next.
+ index := 0.
+ done := false.
+ inPrimitive := false.
+
+ [done] whileFalse:[
+ ((index + 2) <= currSize) ifFalse:[
+ newString := String new:(currSize * 2).
+ newString replaceFrom:1 to:currSize with:theString.
+ currSize := currSize * 2.
+ theString := newString
+ ].
+ thisChar isNil ifTrue:[
+ done := true
+ ] ifFalse:[
+ (thisChar == $% ) ifTrue:[
+ nextChar := self peek.
+ (nextChar == ${ ) ifTrue:[
+ inPrimitive := true.
+ index := index + 1.
+ theString at:index put:thisChar.
+ thisChar := self next
+ ] ifFalse:[
+ (nextChar == $} ) ifTrue:[
+ inPrimitive := false.
+ index := index + 1.
+ theString at:index put:thisChar.
+ thisChar := self next
+ ]
+ ]
+ ] ifFalse:[
+ inPrimitive ifFalse:[
+ (thisChar == sep) ifTrue:[
+ (self peek == sep) ifFalse:[
+ done := true
+ ] ifTrue:[
+ self next
+ ]
+ ]
+ ]
+ ]
+ ].
+ done ifFalse:[
+ index := index + 1.
+ theString at:index put:thisChar.
+ thisChar := self next
+ ]
+ ].
+ (index == 0) ifTrue:[^ ''].
+ ^ theString copyFrom:1 to:index
+!
+
+nextChunkPut:aString
+ "put aString as a chunk onto the receiver;
+ double all exclamation marks and append an exclamation mark"
+
+ |sep gotPercent inPrimitive character
+ index "{ Class:SmallInteger }"
+ endIndex "{ Class:SmallInteger }"
+ next "{ Class:SmallInteger }" |
+
+ sep := self class chunkSeparator.
+ inPrimitive := false.
+ gotPercent := false.
+ index := 1.
+ endIndex := aString size.
+
+ [index <= endIndex] whileTrue:[
+ next := aString indexOf:$% startingAt:index ifAbsent:[endIndex + 1].
+ next := next min:
+ (aString indexOf:${ startingAt:index ifAbsent:[endIndex + 1]).
+ next := next min:
+ (aString indexOf:$} startingAt:index ifAbsent:[endIndex + 1]).
+ next := next min:
+ (aString indexOf:sep startingAt:index ifAbsent:[endIndex + 1]).
+
+ ((index == 1) and:[next == (endIndex + 1)]) ifTrue:[
+ self nextPutAll:aString
+ ] ifFalse:[
+ self nextPutAll:(aString copyFrom:index to:(next - 1))
+ ].
+
+ index := next.
+ (index <= endIndex) ifTrue:[
+ character := aString at:index.
+ (character == $% ) ifTrue:[
+ gotPercent := true
+ ] ifFalse:[
+ (character == ${ ) ifTrue:[
+ gotPercent ifTrue:[
+ inPrimitive := true
+ ]
+ ] ifFalse:[
+ (character == $} ) ifTrue:[
+ gotPercent ifTrue:[
+ inPrimitive := false
+ ]
+ ] ifFalse:[
+ inPrimitive ifFalse:[
+ (character == sep) ifTrue:[
+ self nextPut:sep
+ ]
+ ]
+ ]
+ ].
+ gotPercent := false
+ ].
+ self nextPut:character.
+ index := index + 1
+ ]
+ ].
+ self nextPut:sep
+!
+
+fileInNextChunkNotifying:someone
+ "read next chunk, evaluate it and return the result;
+ someone is notified of errors"
+
+ |aString sawExcla sep|
+
+ sep := self class chunkSeparator.
+ self skipSeparators.
+ self atEnd ifFalse:[
+ sawExcla := self peekFor:sep.
+ aString := self nextChunk.
+ aString size ~~ 0 ifTrue:[
+ sawExcla ifFalse:[
+ ^ Compiler evaluate:aString notifying:someone
+ ].
+ ^ (Compiler evaluate:aString notifying:someone)
+ fileInFrom:self notifying:someone
+ ]
+ ].
+ ^ nil
+!
+
+fileInNextChunk
+ "read next chunk, evaluate it and return the result"
+
+ ^ self fileInNextChunkNotifying:nil
+!
+
+fileInNotifying:someone
+ "file in from the receiver, i.e. read chunks and evaluate them -
+ return the value of the last chunk; someone is notified of errors"
+
+ |lastValue|
+
+ self position:1.
+ abortBlock := [^ nil].
+ continueBlock := [].
+ Smalltalk at:#ErrorHandler put:self.
+ [self atEnd] whileFalse:[
+ lastValue := self fileInNextChunkNotifying:someone
+ ].
+ Smalltalk at:#ErrorHandler put:nil.
+ ^ lastValue
+!
+
+fileIn
+ "file in from the receiver, i.e. read chunks and evaluate them -
+ return the value of the last chunk"
+
+ ^ self fileInNotifying:self
+!
+
+askForDebug:message
+ |box|
+
+ box := OptionBox title:message numberOfOptions:3.
+ box actions:(Array with:[^ #abort]
+ with:[^ #debug]
+ with:[^ #continue]).
+ box buttonTitles:#('abort' 'debug' 'continue').
+ box showAtPointer.
+ ^ #abort
+!
+
+catch:aSymbol with:aMessage for:anObject
+ "this one is sent when an error occurs while filing in -
+ we dont want a debugger to come up but simply notify
+ the error (also on the Transcript so you have a trace of it)"
+
+ |message action|
+
+ Smalltalk at:#ErrorHandler put:nil.
+ (aSymbol == #doesNotUnderstand:) ifTrue:[
+ anObject isNil ifTrue:[
+ "try to give a bit more detail on what went wrong"
+ (Metaclass respondsTo:(aMessage selector)) ifTrue:[
+ ('subclass:*' match:(aMessage selector)) ifTrue:[
+ message := 'no superclass for ' , (aMessage arguments at:1)
+ ] ifFalse:[
+ message := 'definitions for nonexisting class'
+ ]
+ ] ifFalse:[
+ message := 'bad message: ' , aMessage selector, ' to UndefinedObject'
+ ]
+ ] ifFalse:[
+ message := 'bad message: ' , aMessage selector ,
+ ' to ' , anObject classNameWithArticle
+ ]
+ ] ifFalse:[
+ (aSymbol == #error:) ifTrue:[
+ message := aMessage
+ ] ifFalse:[
+ message := 'during fileIn'
+ ]
+ ].
+ message := 'Error: ' , message.
+ Transcript showCr:message.
+
+ YesNoBox notNil ifTrue:[
+ action := self askForDebug:message.
+ action == #debug ifTrue:[
+ Debugger enterWithMessage:message
+ ].
+ action == #continue ifTrue:[
+ continueBlock value
+ ].
+ ] ifFalse:[
+ self notify:message
+ ].
+
+ abortBlock value.
+ ^ nil
+!
+
+error:aMessage position:position to:endPos
+ "error notification during fileIn with no requestor"
+
+ position printOn:Transcript.
+ Transcript show:' '.
+ Transcript showCr:aMessage.
+ ^ false
+!
+
+correctableError:aMessage position:position to:endPos
+ "error notification during fileIn with no requestor"
+
+ ^ self error:aMessage position:position to:endPos
+!
+
+warning:aMessage position:position to:endPos
+ "warning notification during fileIn with no requestor - ignore it"
+
+ ^ self
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ProcSched.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,599 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#ProcessorScheduler
+ instanceVariableNames:'runnable zombie
+ currentProcess currentPriority
+ fileDescriptors fileHandlers fileSelectors
+ timeoutTimes timeHandlers timeSelectors'
+ classVariableNames:'KnownProcesses KnownProcessIds'
+ poolDictionaries:''
+ category:'Kernel-Processes'
+!
+
+ProcessorScheduler comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+'!
+
+Smalltalk at:#Processor put:nil!
+
+!ProcessorScheduler class methodsFor:'initialization'!
+
+initialize
+ KnownProcesses isNil ifTrue:[
+ KnownProcesses := ShadowArray new:5.
+ KnownProcesses watcher:self.
+ KnownProcessIds := OrderedCollection new.
+
+ "want to get informed when returning from snapshot"
+ ObjectMemory addDependent:self
+ ].
+
+ "create the one and only processor"
+
+ Processor := self new
+!
+
+update:something
+ something == #returnFromSnapshot ifTrue:[
+ self reinstallProcesses
+ ]
+!
+
+reinstallProcesses
+ "recreate all processes after a snapShot load"
+
+ KnownProcesses do:[:p |
+ p notNil ifTrue:[
+ "how, exactly should this be done ?"
+
+ p id ~~ 0 ifTrue:[
+ Transcript showCr:'process restart in preparation'
+ ]
+ ]
+ ]
+! !
+
+!ProcessorScheduler class methodsFor:'private process primitives'!
+
+threadCreate:aBlock
+ "physical creation of a process executing aBlock.
+ (warning: low level entry, no administration done)"
+%{
+ int tid;
+ extern int __threadCreate();
+
+ tid = __threadCreate(aBlock);
+ if (tid != 0) {
+ RETURN ( _MKSMALLINT(tid));
+ }
+ RETURN (nil);
+%}
+!
+
+threadDestroy:id
+ "physical destroy other process ...
+ (warning: low level entry, no administration done)"
+
+%{
+ if (_isSmallInteger(id)) {
+ __threadDestroy(_intVal(id));
+ }
+%}
+.
+ self primitiveFailed
+! !
+
+!ProcessorScheduler class methodsFor:'instance release'!
+
+informDispose
+ "some Process has been collected - terminate the underlying thread"
+
+ |id
+ index "<SmallInteger>"
+ sz "<SmallInteger>"|
+
+ index := 1.
+ sz := KnownProcessIds size.
+ [index <= sz] whileTrue:[
+ (KnownProcesses at:index) isNil ifTrue:[
+ id := KnownProcessIds at:index.
+ id notNil ifTrue:[
+ Transcript showCr:('terminate thread (no longer refd) ', id printString).
+ self threadDestroy:id.
+ KnownProcessIds at:index put:nil.
+ ]
+ ].
+ index := index + 1
+ ]
+! !
+
+!ProcessorScheduler class methodsFor:'instance creation'!
+
+new
+ "there is (currently) only one processor ..."
+
+ Processor notNil ifTrue:[^ Processor].
+ ^ self basicNew initialize.
+! !
+
+!ProcessorScheduler methodsFor:'constants'!
+
+minPriority
+ ^ 1
+!
+
+maxPriority
+ ^ 31
+!
+
+userInterruptPriority
+ ^ 24
+!
+
+timingPriority
+ ^ 16
+!
+
+userSchedulingPriority
+ ^ 8
+!
+
+userBackgroundPriority
+ ^ 6
+!
+
+systemBackgroundPriority
+ ^ 4
+! !
+
+!ProcessorScheduler methodsFor:'private initializing'!
+
+initialize
+ "initialize the one-and-only ProcessorScheduler"
+
+ |nPrios l|
+
+ nPrios := self maxPriority - self minPriority + 1.
+
+ runnable := Array new:nPrios.
+
+ "setup the first (init-) process"
+ currentProcess := Process new.
+ currentProcess id:0.
+ currentProcess state:#running.
+ currentPriority := self userSchedulingPriority.
+ currentProcess setPriority:currentPriority.
+
+ l := LinkedList new.
+ l add:currentProcess.
+
+ runnable at:currentPriority put:l.
+
+ IOInterruptHandler := self.
+ OperatingSystem enableIOInterrupts.
+! !
+
+!ProcessorScheduler methodsFor:'private'!
+
+remember:aProcess
+ |newShadow newSize oldSize oldId
+ index "<SmallInteger>"
+ sz "<SmallInteger>" |
+
+ index := 1.
+ sz := KnownProcessIds size.
+ [index <= sz] whileTrue:[
+ (KnownProcesses at:index) isNil ifTrue:[
+ oldId := KnownIds at:index.
+ oldId notNil ifTrue:[
+ self class terminateProcess:oldId
+ ].
+ KnownProcesses at:index put:aProcess.
+ KnownProcessIds at:index put:aProcess id.
+ ^ self
+ ].
+ index := index + 1
+ ].
+
+ KnownProcessIds grow:index.
+ KnownProcessIds at:index put:aProcess id.
+
+ oldSize := KnownProcesses size.
+ (index > oldSize) ifTrue:[
+ newShadow := ShadowArray new:(oldSize * 2).
+ newShadow watcher:(KnownProcesses watcher).
+ newShadow replaceFrom:1 with:KnownProcesses.
+ KnownProcesses := newShadow
+ ].
+ KnownProcesses at:index put:aProcess
+! !
+
+!ProcessorScheduler methodsFor:'process creation'!
+
+newProcessFor:aBlock
+ "create a new process executing aBlock. Return a process (or
+ nil if fail). The new process is not scheduled. To start it
+ running, it needs a Process>>resume."
+
+ |id p|
+
+ id := self class threadCreate:aBlock.
+ id notNil ifTrue:[
+ p := Process new.
+ p id:id.
+ p startBlock:aBlock.
+ p state:#suspended.
+ p setPriority:currentPriority.
+ self remember:p.
+ ].
+ ^ p
+! !
+
+!ProcessorScheduler methodsFor:'scheduling'!
+
+switchTo:aProcess
+ "continue execution in aProcess."
+
+ |id pri|
+
+ aProcess isNil ifTrue:[^ self].
+ aProcess == currentProcess ifTrue:[^ self].
+
+ id := aProcess id.
+ pri := aProcess priority.
+ currentProcess state:#runnable.
+
+ "no interrupts now ..."
+ currentProcess := aProcess.
+ currentProcess state:#running.
+ currentPriority := pri.
+%{
+ __threadSwitch(__context, _intVal(id));
+%}
+.
+ zombie notNil ifTrue:[
+ self class threadDestroy:zombie.
+ zombie := nil
+ ]
+!
+
+reschedule
+ "switch to the highest prio runnable process"
+
+ |l|
+
+ self maxPriority to:self minPriority by:-1 do:[:prio |
+ l := runnable at:prio.
+ l notNil ifTrue:[
+ ^ self switchTo:(l first)
+ ]
+ ].
+ "no process to run - wait to next time event"
+
+ 'wait' printNewline.
+ self waitForNextTimeout
+!
+
+yield
+ "move the currently running process to the end of the currentList
+ and reschedule to the first in the list, thus switching to the
+ next same-prio-process."
+
+ |l|
+
+ l := runnable at:currentPriority.
+ l isNil ifTrue:[
+ 'oops - nil runnable list' printNewline.
+ ^ self
+ ].
+ l removeFirst.
+ l addLast:currentProcess.
+ self reschedule
+!
+
+suspend:aProcess
+ "remove the argument, aProcess from the list of runnable processes
+ and put it to the list of suspended ones. If the process is the
+ currentProcess, reschedule."
+
+ |pri l|
+
+ aProcess isNil ifTrue:[self error:'nil suspend'. ^ self].
+ pri := aProcess priority.
+
+ l := runnable at:pri.
+ l isNil ifTrue:[self error:'bad suspend'. ^ self].
+
+ aProcess state:#suspended.
+ l remove:aProcess ifAbsent:[self error:'bad suspend'. ^ self].
+
+ (aProcess == currentProcess) ifTrue:[
+ self reschedule
+ ]
+!
+
+resume:aProcess
+ "set aProcess runnable - if its prio is higher than the currently running prio,
+ reschedule."
+
+ |l pri|
+
+ aProcess == currentProcess ifTrue:[^ self].
+ aProcess isNil ifTrue:[^ self].
+ pri := aProcess priority.
+
+ aProcess state:#runnable.
+ l := runnable at:pri.
+ l isNil ifTrue:[
+ l := LinkedList new.
+ runnable at:pri put:l
+ ].
+ l addLast:aProcess.
+
+ (pri > currentPriority) ifTrue:[
+ self reschedule
+ ]
+!
+
+processTermination
+ "current process finished its startup block without termination,
+ lay him to rest now"
+
+ self terminate:currentProcess
+!
+
+terminate:aProcess
+ "terminate aProcess. If its not the current process, its simply
+ removed from its list and destroyed. Otherwise, a switch is forced
+ and the process is destroyed by the next running process."
+
+ |pri id l|
+
+ aProcess isNil ifTrue:[^ self].
+ id := aProcess id.
+ id isNil ifTrue:[^ self]. "already dead"
+
+ pri := aProcess priority.
+
+ "easy, if currently suspended"
+ ((aProcess state ~~ #runnable) and:[aProcess state ~~ #running]) ifTrue:[
+ aProcess id:nil.
+ aProcess state:#dead.
+ aProcess startBlock:nil.
+ self class threadDestroy:id.
+ ^ self
+ ].
+
+ (aProcess state ~~ #runnable) ifTrue:[
+ l := runnable at:pri.
+ (l notNil and:[l includes:aProcess]) ifTrue:[
+ l remove:aProcess.
+ aProcess state:#dead.
+ l isEmpty ifTrue:[runnable at:pri put:nil].
+ aProcess == currentProcess ifFalse:[
+ self class threadDestroy:id.
+ ]
+ ].
+ ^ self
+ ].
+
+ (aProcess state ~~ #running) ifTrue:[
+ "hard case - its the currently running process
+ we must have the next active process destroy this one
+ "
+ aProcess state:#dead.
+ zombie := id.
+ self reschedule
+ ]
+!
+
+changePriority:newPrio for:aProcess
+ "change the priority of aProcess"
+
+ |oldList newList oldPrio s|
+
+ oldPrio := aProcess priority.
+ oldPrio == newPrio ifTrue:[^ self].
+ aProcess setPriority:newPrio.
+ s := aProcess state.
+ s == #runnable ifTrue:[
+ oldList := runnable at:oldPrio.
+ (oldList includes:aProcess) ifTrue:[
+ oldList remove:aProcess
+ ].
+
+ newList := runnable at:newPrio.
+ newList isNil ifTrue:[
+ newList := LinkedList new.
+ runnable at:newPrio put:newList
+ ].
+ newList addLast:aProcess.
+ (aProcess ~~ currentProcess and:[newPrio > currentPriority]) ifTrue:[
+ self reschedule.
+ ].
+ ^ self
+ ]
+! !
+
+!ProcessorScheduler class methodsFor:'testing'!
+
+test1
+ |scheduler|
+
+ scheduler := ProcessorScheduler new.
+ scheduler addFileDescriptor:(Stdin fileDescriptor) withHandler:self selector:#inputAvailable.
+ scheduler addFileDescriptor:(Display displayFileDescriptor) withHandler:self selector:#xInputAvailable.
+ scheduler loop
+
+ "ProcessorScheduler test1"
+!
+
+inputAvailable
+ Transcript showCr:(Stdin nextLine)
+!
+
+xInputAvailable
+ Transcript showCr:'x event'.
+ Display dispatchEvent
+! !
+
+!ProcessorScheduler methodsFor:'scheduling'!
+
+loop
+ |looping nextTime waitTime fd index|
+
+ looping := true.
+ [looping] whileTrue:[
+ "look if any timeouts are due to be evaluated"
+
+ nextTime := nil.
+ timeoutTimes notNil ifTrue:[
+ nextTime := self evaluateTimeouts
+ ].
+
+ nextTime notNil ifTrue:[
+ waitTime := OperatingSystem millisecondTimeDeltaBetween:nextTime
+ and:OperatingSystem getMillisecondTime
+ ] ifFalse:[
+ waitTime := nil
+ ].
+
+ (fileDescriptors size == 0) ifTrue:[
+ waitTime isNil ifTrue:[
+ Transcript showCr:'nothing to schedule'.
+ ^ self
+ ].
+
+ "no fd to wait for - hard wait till next timeout has to come"
+ OperatingSystem millisecondDelay:waitTime
+ ] ifFalse:[
+ "wait for any fd to become ready or next timeout has to come"
+ waitTime isNil ifTrue:[waitTime := 10000].
+ fd := OperatingSystem selectOnAnyReadable:fileDescriptors withTimeOut:(waitTime / 1000).
+ fd isNil ifTrue:[
+ "an interrupt or timeout occured"
+ Transcript showCr:'interrupt or timeout'
+ ] ifFalse:[
+ "notify the handler"
+ index := fileDescriptors identityIndexOf:fd.
+ (fileHandlers at:index) perform:(fileSelectors at:index)
+ ]
+ ]
+ ]
+!
+
+evaluateTimeouts
+ "evaluate all timeouts that need to be .. and return the time of the
+ next pending timeout"
+
+ |now thisTime index endIndex handler selector nextTime|
+
+ nextTime := nil.
+ endIndex := timeoutTimes size.
+ (endIndex ~~ 0) ifTrue:[
+ now := OperatingSystem getMillisecondTime.
+ index := 1.
+ [index <= endIndex] whileTrue:[
+ thisTime := timeoutTimes at:index.
+ (OperatingSystem millisecondTime:thisTime isAfter:now) ifFalse:[
+ handler := timeHandlers at:index.
+ selector := timeSelectors at:index.
+ timeoutTimes at:index put:nil.
+ timeHandlers at:index put:nil.
+ timeSelectors at:index put:nil.
+ handler perform:selector
+ ] ifTrue:[
+ nextTime isNil ifTrue:[
+ nextTime := thisTime
+ ] ifFalse:[
+ (OperatingSystem millisecondTime:nextTime isAfter:thisTime) ifTrue:[
+ nextTime := thisTime
+ ]
+ ]
+ ].
+ index := index + 1
+ ]
+ ].
+ ^ nextTime
+! !
+
+!ProcessorScheduler methodsFor:'accessing'!
+
+currentPriority
+ ^ currentPriority
+
+ "Processor currentPriority"
+!
+
+currentProcess
+ ^ currentProcess
+
+ "Processor currentProcess"
+!
+
+addFileDescriptor:fd withHandler:handler selector:selector
+ |index|
+
+ fileDescriptors isNil ifTrue:[
+ fileDescriptors := Array with:fd.
+ fileHandlers := Array with:handler.
+ fileSelectors := Array with:selector
+ ] ifFalse:[
+ index := fileDescriptors indexOf:nil.
+ (index ~~ 0) ifTrue:[
+ fileDescriptors at:index put:fd.
+ fileHandlers at:index put:handler.
+ fileSelectors at:index put:selector
+ ] ifFalse:[
+ fileDescriptors := fileDescriptors copyWith:fd.
+ fileHandlers := fileHandlers copyWith:handler.
+ fileSelectors := fileSelectors copyWith:selector
+ ]
+ ]
+!
+
+addTimeoutAfter:millis withHandler:handler selector:selector
+ |index|
+
+ fileDescriptors isNil ifTrue:[
+ timeoutTimes := Array with:millis.
+ timeHandlers := Array with:handler.
+ timeSelectors := Array with:selector
+ ] ifFalse:[
+ index := timeoutTimes indexOf:nil.
+ (index ~~ 0) ifTrue:[
+ timeoutTimes at:index put:millis.
+ timeHandlers at:index put:handler.
+ timeSelectors at:index put:selector
+ ] ifFalse:[
+ timeoutTimes := fileDescriptors copyWith:millis.
+ timeHandlers := fileHandlers copyWith:handler.
+ timeSelectors := fileSelectors copyWith:selector
+ ]
+ ]
+!
+
+removeFileDescriptor:fd
+ |index|
+
+ index := fileDescriptors indexOf:nil.
+ (index ~~ 0) ifTrue:[
+ fileDescriptors at:index put:nil.
+ fileHandlers at:index put:nil.
+ fileSelectors at:index put:nil
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Process.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,94 @@
+"
+ COPYRIGHT (c) 1992-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Link subclass:#Process
+ instanceVariableNames:'id prio state startBlock'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Processes'
+!
+
+Process comment:'
+
+COPYRIGHT (c) 1992-93 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+'!
+
+!Process methodsFor:'accessing'!
+
+state
+ ^ state
+!
+
+state:aSymbol
+ state := aSymbol
+!
+
+startBlock:aBlock
+ startBlock := aBlock
+!
+
+priority
+ "return the receivers priority"
+
+ ^ prio
+!
+
+priority:aNumber
+ "set my priority"
+
+ Processor changePriority:aNumber for:self
+!
+
+setPriority:aNumber
+ "set priority without telling processor - no public use"
+
+ prio := aNumber
+!
+
+id
+ ^ id
+!
+
+id:aNumber
+ id := aNumber
+!
+
+suspendedContext
+%{
+ extern OBJ __threadContext();
+
+ RETURN (__threadContext(_intVal(_INST(id))));
+%}
+! !
+
+!Process methodsFor:'suspend / resume'!
+
+suspend
+ Processor suspend:self
+!
+
+resume
+ Processor resume:self
+!
+
+terminate
+ Processor terminate:self
+! !
+
+!Process methodsFor:'printing'!
+
+printString
+ ^ 'a Process with id:' , id printString
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ProcessorScheduler.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,599 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#ProcessorScheduler
+ instanceVariableNames:'runnable zombie
+ currentProcess currentPriority
+ fileDescriptors fileHandlers fileSelectors
+ timeoutTimes timeHandlers timeSelectors'
+ classVariableNames:'KnownProcesses KnownProcessIds'
+ poolDictionaries:''
+ category:'Kernel-Processes'
+!
+
+ProcessorScheduler comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+'!
+
+Smalltalk at:#Processor put:nil!
+
+!ProcessorScheduler class methodsFor:'initialization'!
+
+initialize
+ KnownProcesses isNil ifTrue:[
+ KnownProcesses := ShadowArray new:5.
+ KnownProcesses watcher:self.
+ KnownProcessIds := OrderedCollection new.
+
+ "want to get informed when returning from snapshot"
+ ObjectMemory addDependent:self
+ ].
+
+ "create the one and only processor"
+
+ Processor := self new
+!
+
+update:something
+ something == #returnFromSnapshot ifTrue:[
+ self reinstallProcesses
+ ]
+!
+
+reinstallProcesses
+ "recreate all processes after a snapShot load"
+
+ KnownProcesses do:[:p |
+ p notNil ifTrue:[
+ "how, exactly should this be done ?"
+
+ p id ~~ 0 ifTrue:[
+ Transcript showCr:'process restart in preparation'
+ ]
+ ]
+ ]
+! !
+
+!ProcessorScheduler class methodsFor:'private process primitives'!
+
+threadCreate:aBlock
+ "physical creation of a process executing aBlock.
+ (warning: low level entry, no administration done)"
+%{
+ int tid;
+ extern int __threadCreate();
+
+ tid = __threadCreate(aBlock);
+ if (tid != 0) {
+ RETURN ( _MKSMALLINT(tid));
+ }
+ RETURN (nil);
+%}
+!
+
+threadDestroy:id
+ "physical destroy other process ...
+ (warning: low level entry, no administration done)"
+
+%{
+ if (_isSmallInteger(id)) {
+ __threadDestroy(_intVal(id));
+ }
+%}
+.
+ self primitiveFailed
+! !
+
+!ProcessorScheduler class methodsFor:'instance release'!
+
+informDispose
+ "some Process has been collected - terminate the underlying thread"
+
+ |id
+ index "<SmallInteger>"
+ sz "<SmallInteger>"|
+
+ index := 1.
+ sz := KnownProcessIds size.
+ [index <= sz] whileTrue:[
+ (KnownProcesses at:index) isNil ifTrue:[
+ id := KnownProcessIds at:index.
+ id notNil ifTrue:[
+ Transcript showCr:('terminate thread (no longer refd) ', id printString).
+ self threadDestroy:id.
+ KnownProcessIds at:index put:nil.
+ ]
+ ].
+ index := index + 1
+ ]
+! !
+
+!ProcessorScheduler class methodsFor:'instance creation'!
+
+new
+ "there is (currently) only one processor ..."
+
+ Processor notNil ifTrue:[^ Processor].
+ ^ self basicNew initialize.
+! !
+
+!ProcessorScheduler methodsFor:'constants'!
+
+minPriority
+ ^ 1
+!
+
+maxPriority
+ ^ 31
+!
+
+userInterruptPriority
+ ^ 24
+!
+
+timingPriority
+ ^ 16
+!
+
+userSchedulingPriority
+ ^ 8
+!
+
+userBackgroundPriority
+ ^ 6
+!
+
+systemBackgroundPriority
+ ^ 4
+! !
+
+!ProcessorScheduler methodsFor:'private initializing'!
+
+initialize
+ "initialize the one-and-only ProcessorScheduler"
+
+ |nPrios l|
+
+ nPrios := self maxPriority - self minPriority + 1.
+
+ runnable := Array new:nPrios.
+
+ "setup the first (init-) process"
+ currentProcess := Process new.
+ currentProcess id:0.
+ currentProcess state:#running.
+ currentPriority := self userSchedulingPriority.
+ currentProcess setPriority:currentPriority.
+
+ l := LinkedList new.
+ l add:currentProcess.
+
+ runnable at:currentPriority put:l.
+
+ IOInterruptHandler := self.
+ OperatingSystem enableIOInterrupts.
+! !
+
+!ProcessorScheduler methodsFor:'private'!
+
+remember:aProcess
+ |newShadow newSize oldSize oldId
+ index "<SmallInteger>"
+ sz "<SmallInteger>" |
+
+ index := 1.
+ sz := KnownProcessIds size.
+ [index <= sz] whileTrue:[
+ (KnownProcesses at:index) isNil ifTrue:[
+ oldId := KnownIds at:index.
+ oldId notNil ifTrue:[
+ self class terminateProcess:oldId
+ ].
+ KnownProcesses at:index put:aProcess.
+ KnownProcessIds at:index put:aProcess id.
+ ^ self
+ ].
+ index := index + 1
+ ].
+
+ KnownProcessIds grow:index.
+ KnownProcessIds at:index put:aProcess id.
+
+ oldSize := KnownProcesses size.
+ (index > oldSize) ifTrue:[
+ newShadow := ShadowArray new:(oldSize * 2).
+ newShadow watcher:(KnownProcesses watcher).
+ newShadow replaceFrom:1 with:KnownProcesses.
+ KnownProcesses := newShadow
+ ].
+ KnownProcesses at:index put:aProcess
+! !
+
+!ProcessorScheduler methodsFor:'process creation'!
+
+newProcessFor:aBlock
+ "create a new process executing aBlock. Return a process (or
+ nil if fail). The new process is not scheduled. To start it
+ running, it needs a Process>>resume."
+
+ |id p|
+
+ id := self class threadCreate:aBlock.
+ id notNil ifTrue:[
+ p := Process new.
+ p id:id.
+ p startBlock:aBlock.
+ p state:#suspended.
+ p setPriority:currentPriority.
+ self remember:p.
+ ].
+ ^ p
+! !
+
+!ProcessorScheduler methodsFor:'scheduling'!
+
+switchTo:aProcess
+ "continue execution in aProcess."
+
+ |id pri|
+
+ aProcess isNil ifTrue:[^ self].
+ aProcess == currentProcess ifTrue:[^ self].
+
+ id := aProcess id.
+ pri := aProcess priority.
+ currentProcess state:#runnable.
+
+ "no interrupts now ..."
+ currentProcess := aProcess.
+ currentProcess state:#running.
+ currentPriority := pri.
+%{
+ __threadSwitch(__context, _intVal(id));
+%}
+.
+ zombie notNil ifTrue:[
+ self class threadDestroy:zombie.
+ zombie := nil
+ ]
+!
+
+reschedule
+ "switch to the highest prio runnable process"
+
+ |l|
+
+ self maxPriority to:self minPriority by:-1 do:[:prio |
+ l := runnable at:prio.
+ l notNil ifTrue:[
+ ^ self switchTo:(l first)
+ ]
+ ].
+ "no process to run - wait to next time event"
+
+ 'wait' printNewline.
+ self waitForNextTimeout
+!
+
+yield
+ "move the currently running process to the end of the currentList
+ and reschedule to the first in the list, thus switching to the
+ next same-prio-process."
+
+ |l|
+
+ l := runnable at:currentPriority.
+ l isNil ifTrue:[
+ 'oops - nil runnable list' printNewline.
+ ^ self
+ ].
+ l removeFirst.
+ l addLast:currentProcess.
+ self reschedule
+!
+
+suspend:aProcess
+ "remove the argument, aProcess from the list of runnable processes
+ and put it to the list of suspended ones. If the process is the
+ currentProcess, reschedule."
+
+ |pri l|
+
+ aProcess isNil ifTrue:[self error:'nil suspend'. ^ self].
+ pri := aProcess priority.
+
+ l := runnable at:pri.
+ l isNil ifTrue:[self error:'bad suspend'. ^ self].
+
+ aProcess state:#suspended.
+ l remove:aProcess ifAbsent:[self error:'bad suspend'. ^ self].
+
+ (aProcess == currentProcess) ifTrue:[
+ self reschedule
+ ]
+!
+
+resume:aProcess
+ "set aProcess runnable - if its prio is higher than the currently running prio,
+ reschedule."
+
+ |l pri|
+
+ aProcess == currentProcess ifTrue:[^ self].
+ aProcess isNil ifTrue:[^ self].
+ pri := aProcess priority.
+
+ aProcess state:#runnable.
+ l := runnable at:pri.
+ l isNil ifTrue:[
+ l := LinkedList new.
+ runnable at:pri put:l
+ ].
+ l addLast:aProcess.
+
+ (pri > currentPriority) ifTrue:[
+ self reschedule
+ ]
+!
+
+processTermination
+ "current process finished its startup block without termination,
+ lay him to rest now"
+
+ self terminate:currentProcess
+!
+
+terminate:aProcess
+ "terminate aProcess. If its not the current process, its simply
+ removed from its list and destroyed. Otherwise, a switch is forced
+ and the process is destroyed by the next running process."
+
+ |pri id l|
+
+ aProcess isNil ifTrue:[^ self].
+ id := aProcess id.
+ id isNil ifTrue:[^ self]. "already dead"
+
+ pri := aProcess priority.
+
+ "easy, if currently suspended"
+ ((aProcess state ~~ #runnable) and:[aProcess state ~~ #running]) ifTrue:[
+ aProcess id:nil.
+ aProcess state:#dead.
+ aProcess startBlock:nil.
+ self class threadDestroy:id.
+ ^ self
+ ].
+
+ (aProcess state ~~ #runnable) ifTrue:[
+ l := runnable at:pri.
+ (l notNil and:[l includes:aProcess]) ifTrue:[
+ l remove:aProcess.
+ aProcess state:#dead.
+ l isEmpty ifTrue:[runnable at:pri put:nil].
+ aProcess == currentProcess ifFalse:[
+ self class threadDestroy:id.
+ ]
+ ].
+ ^ self
+ ].
+
+ (aProcess state ~~ #running) ifTrue:[
+ "hard case - its the currently running process
+ we must have the next active process destroy this one
+ "
+ aProcess state:#dead.
+ zombie := id.
+ self reschedule
+ ]
+!
+
+changePriority:newPrio for:aProcess
+ "change the priority of aProcess"
+
+ |oldList newList oldPrio s|
+
+ oldPrio := aProcess priority.
+ oldPrio == newPrio ifTrue:[^ self].
+ aProcess setPriority:newPrio.
+ s := aProcess state.
+ s == #runnable ifTrue:[
+ oldList := runnable at:oldPrio.
+ (oldList includes:aProcess) ifTrue:[
+ oldList remove:aProcess
+ ].
+
+ newList := runnable at:newPrio.
+ newList isNil ifTrue:[
+ newList := LinkedList new.
+ runnable at:newPrio put:newList
+ ].
+ newList addLast:aProcess.
+ (aProcess ~~ currentProcess and:[newPrio > currentPriority]) ifTrue:[
+ self reschedule.
+ ].
+ ^ self
+ ]
+! !
+
+!ProcessorScheduler class methodsFor:'testing'!
+
+test1
+ |scheduler|
+
+ scheduler := ProcessorScheduler new.
+ scheduler addFileDescriptor:(Stdin fileDescriptor) withHandler:self selector:#inputAvailable.
+ scheduler addFileDescriptor:(Display displayFileDescriptor) withHandler:self selector:#xInputAvailable.
+ scheduler loop
+
+ "ProcessorScheduler test1"
+!
+
+inputAvailable
+ Transcript showCr:(Stdin nextLine)
+!
+
+xInputAvailable
+ Transcript showCr:'x event'.
+ Display dispatchEvent
+! !
+
+!ProcessorScheduler methodsFor:'scheduling'!
+
+loop
+ |looping nextTime waitTime fd index|
+
+ looping := true.
+ [looping] whileTrue:[
+ "look if any timeouts are due to be evaluated"
+
+ nextTime := nil.
+ timeoutTimes notNil ifTrue:[
+ nextTime := self evaluateTimeouts
+ ].
+
+ nextTime notNil ifTrue:[
+ waitTime := OperatingSystem millisecondTimeDeltaBetween:nextTime
+ and:OperatingSystem getMillisecondTime
+ ] ifFalse:[
+ waitTime := nil
+ ].
+
+ (fileDescriptors size == 0) ifTrue:[
+ waitTime isNil ifTrue:[
+ Transcript showCr:'nothing to schedule'.
+ ^ self
+ ].
+
+ "no fd to wait for - hard wait till next timeout has to come"
+ OperatingSystem millisecondDelay:waitTime
+ ] ifFalse:[
+ "wait for any fd to become ready or next timeout has to come"
+ waitTime isNil ifTrue:[waitTime := 10000].
+ fd := OperatingSystem selectOnAnyReadable:fileDescriptors withTimeOut:(waitTime / 1000).
+ fd isNil ifTrue:[
+ "an interrupt or timeout occured"
+ Transcript showCr:'interrupt or timeout'
+ ] ifFalse:[
+ "notify the handler"
+ index := fileDescriptors identityIndexOf:fd.
+ (fileHandlers at:index) perform:(fileSelectors at:index)
+ ]
+ ]
+ ]
+!
+
+evaluateTimeouts
+ "evaluate all timeouts that need to be .. and return the time of the
+ next pending timeout"
+
+ |now thisTime index endIndex handler selector nextTime|
+
+ nextTime := nil.
+ endIndex := timeoutTimes size.
+ (endIndex ~~ 0) ifTrue:[
+ now := OperatingSystem getMillisecondTime.
+ index := 1.
+ [index <= endIndex] whileTrue:[
+ thisTime := timeoutTimes at:index.
+ (OperatingSystem millisecondTime:thisTime isAfter:now) ifFalse:[
+ handler := timeHandlers at:index.
+ selector := timeSelectors at:index.
+ timeoutTimes at:index put:nil.
+ timeHandlers at:index put:nil.
+ timeSelectors at:index put:nil.
+ handler perform:selector
+ ] ifTrue:[
+ nextTime isNil ifTrue:[
+ nextTime := thisTime
+ ] ifFalse:[
+ (OperatingSystem millisecondTime:nextTime isAfter:thisTime) ifTrue:[
+ nextTime := thisTime
+ ]
+ ]
+ ].
+ index := index + 1
+ ]
+ ].
+ ^ nextTime
+! !
+
+!ProcessorScheduler methodsFor:'accessing'!
+
+currentPriority
+ ^ currentPriority
+
+ "Processor currentPriority"
+!
+
+currentProcess
+ ^ currentProcess
+
+ "Processor currentProcess"
+!
+
+addFileDescriptor:fd withHandler:handler selector:selector
+ |index|
+
+ fileDescriptors isNil ifTrue:[
+ fileDescriptors := Array with:fd.
+ fileHandlers := Array with:handler.
+ fileSelectors := Array with:selector
+ ] ifFalse:[
+ index := fileDescriptors indexOf:nil.
+ (index ~~ 0) ifTrue:[
+ fileDescriptors at:index put:fd.
+ fileHandlers at:index put:handler.
+ fileSelectors at:index put:selector
+ ] ifFalse:[
+ fileDescriptors := fileDescriptors copyWith:fd.
+ fileHandlers := fileHandlers copyWith:handler.
+ fileSelectors := fileSelectors copyWith:selector
+ ]
+ ]
+!
+
+addTimeoutAfter:millis withHandler:handler selector:selector
+ |index|
+
+ fileDescriptors isNil ifTrue:[
+ timeoutTimes := Array with:millis.
+ timeHandlers := Array with:handler.
+ timeSelectors := Array with:selector
+ ] ifFalse:[
+ index := timeoutTimes indexOf:nil.
+ (index ~~ 0) ifTrue:[
+ timeoutTimes at:index put:millis.
+ timeHandlers at:index put:handler.
+ timeSelectors at:index put:selector
+ ] ifFalse:[
+ timeoutTimes := fileDescriptors copyWith:millis.
+ timeHandlers := fileHandlers copyWith:handler.
+ timeSelectors := fileSelectors copyWith:selector
+ ]
+ ]
+!
+
+removeFileDescriptor:fd
+ |index|
+
+ index := fileDescriptors indexOf:nil.
+ (index ~~ 0) ifTrue:[
+ fileDescriptors at:index put:nil.
+ fileHandlers at:index put:nil.
+ fileSelectors at:index put:nil
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Project.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,78 @@
+'From Smalltalk/X, Version:2.6.4 on 27-Apr-1993 at 20:02:37'!
+
+Object subclass:#Project
+ instanceVariableNames:'name changeSet views'
+ classVariableNames:'CurrentProject'
+ poolDictionaries:''
+ category:'System-Support'
+!
+
+!Project class methodsFor:'accessing'!
+
+current
+ ^ CurrentProject
+
+ "Project current"
+!
+
+current:aProject
+ CurrentProject := aProject
+! !
+
+!Project class methodsFor:'instance creation'!
+
+new
+ |newProject|
+
+ newProject := self basicNew.
+ newProject views:(OrderedCollection new).
+ newProject name:'a new Project'.
+ newProject changeSet:(ChangeSet new).
+ ^ newProject
+! !
+
+!Project class methodsFor:'initialization'!
+
+initialize
+ CurrentProject isNil ifTrue:[
+ CurrentProject := self new name:'System'
+ ]
+
+ "Project initialize"
+! !
+
+!Project methodsFor:'accessing'!
+
+views
+ ^ views
+!
+
+views:aSetOfViews
+ views := aSetOfViews
+!
+
+addView:aView
+ views add:aView
+!
+
+removeView:aView
+ views remove:aView ifAbsent:[]
+!
+
+changeSet
+ ^ changeSet
+!
+
+changeSet:aChangeSet
+ changeSet := aChangeSet
+!
+
+name
+ ^ name
+!
+
+name:aString
+ name := aString
+! !
+
+Project initialize!
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RWStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,41 @@
+"
+ COPYRIGHT (c) 1989/90/91 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+WriteStream subclass:#ReadWriteStream
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams'
+!
+
+ReadWriteStream comment:'
+
+COPYRIGHT (c) 1989/90/91 by Claus Gittinger
+ All Rights Reserved
+
+@(#)RWStream.st 2.3 92/06/06
+'!
+
+!ReadWriteStream methodsFor: 'access-reading'!
+
+peek
+ (position > readLimit) ifTrue: [ ^ nil ].
+ ^ collection at:position
+!
+
+next
+ |p|
+ (position > readLimit) ifTrue: [ ^ nil ].
+ p := position.
+ position := position + 1.
+ ^ collection at:p
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ReadStr.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,407 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+PositionableStream subclass:#ReadStream
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams'
+!
+
+ReadStream comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ReadStream defines protocol for reading streamwise over collections. Code here
+is specially tuned for streaming over strings.
+
+%W% %E%
+'!
+
+!ReadStream methodsFor:'access-reading'!
+
+peek
+ "return the next element; do NOT advance read pointer.
+ - reimplemented for speed on String-Streams"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int pos;
+ unsigned ch;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+
+ pos = _intVal(_INST(position));
+ if (pos > _intVal(_INST(readLimit))) {
+ RETURN ( nil );
+ }
+ if ((pos > 0)
+ && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+ ch = _stringVal(_INST(collection))[pos-1];
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ }
+%}
+.
+ (position > readLimit) ifTrue:[^ nil].
+ ^ collection at:position
+!
+
+next
+ "return the next element; advance read pointer.
+ - reimplemented for speed on String-Streams"
+
+ |ret|
+
+%{ /* NOCONTEXT */
+
+ REGISTER int pos;
+ unsigned ch;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+
+ pos = _intVal(_INST(position));
+ if (pos > _intVal(_INST(readLimit))) {
+ RETURN ( nil );
+ }
+ if ((pos > 0)
+ && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+ ch = _stringVal(_INST(collection))[pos-1];
+ _INST(position) = _MKSMALLINT(pos + 1);
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ }
+%}
+.
+ (position > readLimit) ifTrue:[^ nil].
+ ret := collection at:position.
+ position := position + 1.
+ ^ ret
+!
+
+nextPeek
+ "advance read pointer return the peek element.
+ this is equivalent to (self next; peek).
+ - reimplemented for speed on String-Streams"
+
+%{ /* NOCONTEXT */
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ REGISTER int pos;
+ unsigned ch;
+
+ pos = _intVal(_INST(position));
+ if (pos > _intVal(_INST(readLimit))) {
+ RETURN ( nil );
+ }
+ if ((pos > 0)
+ && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+ _INST(position) = _MKSMALLINT(pos + 1);
+ pos = pos + 1;
+ if (pos < _qSize(_INST(collection)) - OHDR_SIZE) {
+ ch = _stringVal(_INST(collection))[pos-1];
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ (position > readLimit) ifTrue:[^ nil].
+ position := position + 1.
+ (position > readLimit) ifTrue:[^ nil].
+ ^ collection at:position
+!
+
+nextDecimalInteger
+ "read the next integer in radix 10. dont skip whitespace.
+ - reimplemented for speed on String-Streams"
+
+ |value|
+%{
+ int pos, limit, sz;
+ register unsigned char *cp;
+ register unsigned ch;
+ int val = 0;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ pos = _intVal(_INST(position));
+ limit = _intVal(_INST(readLimit));
+ sz = _qSize(_INST(collection)) - OHDR_SIZE;
+ if (sz < limit)
+ limit = sz;
+ cp = _stringVal(_INST(collection)) + pos - 1;
+
+ for (;;) {
+ if (pos > limit) break;
+ ch = *cp;
+
+ if ((ch < '0') || (ch > '9')) break;
+ val = val * 10 + (ch - '0');
+ pos++;
+ if (val > (_MAX_INT / 10)) goto oops;
+ cp++;
+ }
+ _INST(position) = _MKSMALLINT(pos);
+ return _MKSMALLINT(val);
+ }
+oops:
+ value = _MKSMALLINT(val);
+%}
+.
+ [self peek notNil and:[self peek isDigitRadix:10]] whileTrue:[
+ value = (value * 10) + self peek digitValue.
+ self next
+ ].
+ ^ value
+!
+
+nextWord
+ "read the next word (i.e. up to non letter-or-digit).
+ return a string containing those characters.
+ - reimplemented for speed on String-Streams"
+%{
+ /* speedup, if collection is a string */
+
+ int pos, limit, sz;
+ int len;
+ char buffer[1024];
+ register unsigned char *cp;
+ register unsigned ch;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ pos = _intVal(_INST(position));
+ limit = _intVal(_INST(readLimit));
+ sz = _qSize(_INST(collection)) - OHDR_SIZE;
+ if (sz < limit)
+ limit = sz;
+ cp = _stringVal(_INST(collection)) + pos - 1;
+
+ for (;;) {
+ if (pos > limit) break;
+ ch = *cp;
+
+ if (ch > ' ') break;
+ if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+ && (ch != '\n') && (ch != 0x0b)) break;
+ cp++;
+ pos++;
+ }
+
+ len = 0;
+ for (;;) {
+ if (pos > limit) break;
+ ch = *cp & 0xFF;
+
+ if (! (((ch >= 'a') && (ch <= 'z')) ||
+ ((ch >= 'A') && (ch <= 'Z')) ||
+ ((ch >= '0') && (ch <= '9'))))
+ break;
+ buffer[len++] = ch;
+ if (len >= (sizeof(buffer)-1)) {
+ /* emergency */
+ break;
+ }
+ pos++;
+ cp++;
+ }
+
+ _INST(position) = _MKSMALLINT(pos);
+ buffer[len] = '\0';
+ RETURN ( (len != 0) ? _MKSTRING(buffer COMMA_CON) : nil );
+ }
+%}
+.
+ ^ super nextWord
+!
+
+nextSymbol
+ "read the next selector-symbol (i.e. up to non letter-or-digit).
+ return a string containing those characters.
+ - reimplemented for speed on String-Streams"
+%{
+ int pos, limit, sz;
+ int len;
+ char buffer[1024];
+ register unsigned char *cp;
+ register unsigned ch;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ pos = _intVal(_INST(position));
+ limit = _intVal(_INST(readLimit));
+ sz = _qSize(_INST(collection)) - OHDR_SIZE;
+ if (sz < limit)
+ limit = sz;
+ cp = _stringVal(_INST(collection)) + pos - 1;
+
+ len = 0;
+ for (;;) {
+ if (pos > limit) break;
+ ch = *cp;
+
+ if (! (((ch >= 'a') && (ch <= 'z')) ||
+ ((ch >= 'A') && (ch <= 'Z')) ||
+ ((ch >= '0') && (ch <= '9')) ||
+ (ch == ':')))
+ break;
+ buffer[len++] = ch;
+ if (len >= (sizeof(buffer)-1)) {
+ /* emergency */
+ break;
+ }
+ pos++;
+ cp++;
+ }
+
+ _INST(position) = _MKSMALLINT(pos);
+ buffer[len] = '\0';
+ RETURN ( (len != 0) ? _MKSTRING(buffer COMMA_CON) : nil );
+ }
+%}
+.
+ ^ super nextSymbol
+!
+
+skipFor:anObject
+ "skip all objects up-to and including anObject;
+ return the element after anObject.
+ - reimplemented for speed on String-Streams"
+
+%{ /* NOCONTEXT */
+
+ if (_isString(_INST(collection))
+ && _isCharacter(anObject)
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ REGISTER unsigned char *chars;
+ REGISTER int pos, limit;
+ unsigned ch;
+
+ pos = _intVal(_INST(position));
+ if (pos <= 0) {
+ RETURN ( nil );
+ }
+
+ limit = _intVal(_INST(readLimit));
+ if (limit > (_qSize(_INST(collection)) - OHDR_SIZE))
+ limit = _qSize(_INST(collection)) - OHDR_SIZE;
+
+ chars = (unsigned char *)(_stringVal(_INST(collection)) + pos - 1);
+ ch = _intVal(_characterVal(anObject)) & 0xFF;
+ while (pos < limit) {
+ if (*chars == ch) {
+ ch = *++chars;
+ pos++;
+ _INST(position) = _MKSMALLINT(pos);
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ chars++;
+ pos++;
+ }
+ _INST(position) = _MKSMALLINT(pos);
+ RETURN ( nil );
+ }
+%}
+.
+ ^ super skipFor:anObject
+!
+
+
+skipSeparators
+ "skip all whitespace; next will return next non-white-space element.
+ - reimplemented for speed on String-Streams"
+
+%{ /* NOCONTEXT */
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ REGISTER unsigned char *chars;
+ REGISTER unsigned ch;
+ REGISTER int pos;
+ int limit;
+
+ pos = _intVal(_INST(position));
+ if (pos <= 0) {
+ RETURN ( nil );
+ }
+
+ limit = _intVal(_INST(readLimit));
+ if (limit > (_qSize(_INST(collection)) - OHDR_SIZE))
+ limit = _qSize(_INST(collection)) - OHDR_SIZE;
+
+ chars = (unsigned char *)(_stringVal(_INST(collection)) + pos - 1);
+ while (pos <= limit) {
+ ch = *chars++;
+ if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+ && (ch != '\n') && (ch != 0x0B)) {
+ _INST(position) = _MKSMALLINT(pos);
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ pos++;
+ }
+ _INST(position) = _MKSMALLINT(pos);
+ RETURN ( nil );
+ }
+%}
+.
+ ^ super skipSeparators
+!
+
+skipToAll:aCollection
+ "skip for the sequence given by the argument, aCollection;
+ return nil if not found, self otherwise. On a successful match, next read
+ will return elements of aCollection."
+
+ |oldPos buffer l first idx|
+
+ oldPos := self position.
+ l := aCollection size.
+ first := aCollection at:1.
+ [self atEnd] whileFalse:[
+ buffer := self next:l.
+ buffer = aCollection ifTrue:[
+ self position:(self position - l).
+ ^ self
+ ].
+ idx := buffer indexOf:first startingAt:2.
+ idx == 0 ifFalse:[
+ self position:(self position - l + idx - 1)
+ ]
+ ].
+ self position:oldPos.
+ ^ nil
+
+ "|s|
+ s := ReadStream on:'12345678901234567890'.
+ s skipToAll:'901'.
+ s next:4"
+! !
+
+!ReadStream methodsFor:'access-writing'!
+
+nextPut:anElement
+ ^ self error:'ReadStreams cannot write'
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ReadStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,407 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+PositionableStream subclass:#ReadStream
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams'
+!
+
+ReadStream comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ReadStream defines protocol for reading streamwise over collections. Code here
+is specially tuned for streaming over strings.
+
+%W% %E%
+'!
+
+!ReadStream methodsFor:'access-reading'!
+
+peek
+ "return the next element; do NOT advance read pointer.
+ - reimplemented for speed on String-Streams"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int pos;
+ unsigned ch;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+
+ pos = _intVal(_INST(position));
+ if (pos > _intVal(_INST(readLimit))) {
+ RETURN ( nil );
+ }
+ if ((pos > 0)
+ && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+ ch = _stringVal(_INST(collection))[pos-1];
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ }
+%}
+.
+ (position > readLimit) ifTrue:[^ nil].
+ ^ collection at:position
+!
+
+next
+ "return the next element; advance read pointer.
+ - reimplemented for speed on String-Streams"
+
+ |ret|
+
+%{ /* NOCONTEXT */
+
+ REGISTER int pos;
+ unsigned ch;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+
+ pos = _intVal(_INST(position));
+ if (pos > _intVal(_INST(readLimit))) {
+ RETURN ( nil );
+ }
+ if ((pos > 0)
+ && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+ ch = _stringVal(_INST(collection))[pos-1];
+ _INST(position) = _MKSMALLINT(pos + 1);
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ }
+%}
+.
+ (position > readLimit) ifTrue:[^ nil].
+ ret := collection at:position.
+ position := position + 1.
+ ^ ret
+!
+
+nextPeek
+ "advance read pointer return the peek element.
+ this is equivalent to (self next; peek).
+ - reimplemented for speed on String-Streams"
+
+%{ /* NOCONTEXT */
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ REGISTER int pos;
+ unsigned ch;
+
+ pos = _intVal(_INST(position));
+ if (pos > _intVal(_INST(readLimit))) {
+ RETURN ( nil );
+ }
+ if ((pos > 0)
+ && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+ _INST(position) = _MKSMALLINT(pos + 1);
+ pos = pos + 1;
+ if (pos < _qSize(_INST(collection)) - OHDR_SIZE) {
+ ch = _stringVal(_INST(collection))[pos-1];
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ (position > readLimit) ifTrue:[^ nil].
+ position := position + 1.
+ (position > readLimit) ifTrue:[^ nil].
+ ^ collection at:position
+!
+
+nextDecimalInteger
+ "read the next integer in radix 10. dont skip whitespace.
+ - reimplemented for speed on String-Streams"
+
+ |value|
+%{
+ int pos, limit, sz;
+ register unsigned char *cp;
+ register unsigned ch;
+ int val = 0;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ pos = _intVal(_INST(position));
+ limit = _intVal(_INST(readLimit));
+ sz = _qSize(_INST(collection)) - OHDR_SIZE;
+ if (sz < limit)
+ limit = sz;
+ cp = _stringVal(_INST(collection)) + pos - 1;
+
+ for (;;) {
+ if (pos > limit) break;
+ ch = *cp;
+
+ if ((ch < '0') || (ch > '9')) break;
+ val = val * 10 + (ch - '0');
+ pos++;
+ if (val > (_MAX_INT / 10)) goto oops;
+ cp++;
+ }
+ _INST(position) = _MKSMALLINT(pos);
+ return _MKSMALLINT(val);
+ }
+oops:
+ value = _MKSMALLINT(val);
+%}
+.
+ [self peek notNil and:[self peek isDigitRadix:10]] whileTrue:[
+ value = (value * 10) + self peek digitValue.
+ self next
+ ].
+ ^ value
+!
+
+nextWord
+ "read the next word (i.e. up to non letter-or-digit).
+ return a string containing those characters.
+ - reimplemented for speed on String-Streams"
+%{
+ /* speedup, if collection is a string */
+
+ int pos, limit, sz;
+ int len;
+ char buffer[1024];
+ register unsigned char *cp;
+ register unsigned ch;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ pos = _intVal(_INST(position));
+ limit = _intVal(_INST(readLimit));
+ sz = _qSize(_INST(collection)) - OHDR_SIZE;
+ if (sz < limit)
+ limit = sz;
+ cp = _stringVal(_INST(collection)) + pos - 1;
+
+ for (;;) {
+ if (pos > limit) break;
+ ch = *cp;
+
+ if (ch > ' ') break;
+ if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+ && (ch != '\n') && (ch != 0x0b)) break;
+ cp++;
+ pos++;
+ }
+
+ len = 0;
+ for (;;) {
+ if (pos > limit) break;
+ ch = *cp & 0xFF;
+
+ if (! (((ch >= 'a') && (ch <= 'z')) ||
+ ((ch >= 'A') && (ch <= 'Z')) ||
+ ((ch >= '0') && (ch <= '9'))))
+ break;
+ buffer[len++] = ch;
+ if (len >= (sizeof(buffer)-1)) {
+ /* emergency */
+ break;
+ }
+ pos++;
+ cp++;
+ }
+
+ _INST(position) = _MKSMALLINT(pos);
+ buffer[len] = '\0';
+ RETURN ( (len != 0) ? _MKSTRING(buffer COMMA_CON) : nil );
+ }
+%}
+.
+ ^ super nextWord
+!
+
+nextSymbol
+ "read the next selector-symbol (i.e. up to non letter-or-digit).
+ return a string containing those characters.
+ - reimplemented for speed on String-Streams"
+%{
+ int pos, limit, sz;
+ int len;
+ char buffer[1024];
+ register unsigned char *cp;
+ register unsigned ch;
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ pos = _intVal(_INST(position));
+ limit = _intVal(_INST(readLimit));
+ sz = _qSize(_INST(collection)) - OHDR_SIZE;
+ if (sz < limit)
+ limit = sz;
+ cp = _stringVal(_INST(collection)) + pos - 1;
+
+ len = 0;
+ for (;;) {
+ if (pos > limit) break;
+ ch = *cp;
+
+ if (! (((ch >= 'a') && (ch <= 'z')) ||
+ ((ch >= 'A') && (ch <= 'Z')) ||
+ ((ch >= '0') && (ch <= '9')) ||
+ (ch == ':')))
+ break;
+ buffer[len++] = ch;
+ if (len >= (sizeof(buffer)-1)) {
+ /* emergency */
+ break;
+ }
+ pos++;
+ cp++;
+ }
+
+ _INST(position) = _MKSMALLINT(pos);
+ buffer[len] = '\0';
+ RETURN ( (len != 0) ? _MKSTRING(buffer COMMA_CON) : nil );
+ }
+%}
+.
+ ^ super nextSymbol
+!
+
+skipFor:anObject
+ "skip all objects up-to and including anObject;
+ return the element after anObject.
+ - reimplemented for speed on String-Streams"
+
+%{ /* NOCONTEXT */
+
+ if (_isString(_INST(collection))
+ && _isCharacter(anObject)
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ REGISTER unsigned char *chars;
+ REGISTER int pos, limit;
+ unsigned ch;
+
+ pos = _intVal(_INST(position));
+ if (pos <= 0) {
+ RETURN ( nil );
+ }
+
+ limit = _intVal(_INST(readLimit));
+ if (limit > (_qSize(_INST(collection)) - OHDR_SIZE))
+ limit = _qSize(_INST(collection)) - OHDR_SIZE;
+
+ chars = (unsigned char *)(_stringVal(_INST(collection)) + pos - 1);
+ ch = _intVal(_characterVal(anObject)) & 0xFF;
+ while (pos < limit) {
+ if (*chars == ch) {
+ ch = *++chars;
+ pos++;
+ _INST(position) = _MKSMALLINT(pos);
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ chars++;
+ pos++;
+ }
+ _INST(position) = _MKSMALLINT(pos);
+ RETURN ( nil );
+ }
+%}
+.
+ ^ super skipFor:anObject
+!
+
+
+skipSeparators
+ "skip all whitespace; next will return next non-white-space element.
+ - reimplemented for speed on String-Streams"
+
+%{ /* NOCONTEXT */
+
+ if (_isString(_INST(collection))
+ && _isSmallInteger(_INST(position))
+ && _isSmallInteger(_INST(readLimit))) {
+ REGISTER unsigned char *chars;
+ REGISTER unsigned ch;
+ REGISTER int pos;
+ int limit;
+
+ pos = _intVal(_INST(position));
+ if (pos <= 0) {
+ RETURN ( nil );
+ }
+
+ limit = _intVal(_INST(readLimit));
+ if (limit > (_qSize(_INST(collection)) - OHDR_SIZE))
+ limit = _qSize(_INST(collection)) - OHDR_SIZE;
+
+ chars = (unsigned char *)(_stringVal(_INST(collection)) + pos - 1);
+ while (pos <= limit) {
+ ch = *chars++;
+ if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+ && (ch != '\n') && (ch != 0x0B)) {
+ _INST(position) = _MKSMALLINT(pos);
+ RETURN ( _MKCHARACTER(ch) );
+ }
+ pos++;
+ }
+ _INST(position) = _MKSMALLINT(pos);
+ RETURN ( nil );
+ }
+%}
+.
+ ^ super skipSeparators
+!
+
+skipToAll:aCollection
+ "skip for the sequence given by the argument, aCollection;
+ return nil if not found, self otherwise. On a successful match, next read
+ will return elements of aCollection."
+
+ |oldPos buffer l first idx|
+
+ oldPos := self position.
+ l := aCollection size.
+ first := aCollection at:1.
+ [self atEnd] whileFalse:[
+ buffer := self next:l.
+ buffer = aCollection ifTrue:[
+ self position:(self position - l).
+ ^ self
+ ].
+ idx := buffer indexOf:first startingAt:2.
+ idx == 0 ifFalse:[
+ self position:(self position - l + idx - 1)
+ ]
+ ].
+ self position:oldPos.
+ ^ nil
+
+ "|s|
+ s := ReadStream on:'12345678901234567890'.
+ s skipToAll:'901'.
+ s next:4"
+! !
+
+!ReadStream methodsFor:'access-writing'!
+
+nextPut:anElement
+ ^ self error:'ReadStreams cannot write'
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ReadWriteStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,41 @@
+"
+ COPYRIGHT (c) 1989/90/91 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+WriteStream subclass:#ReadWriteStream
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams'
+!
+
+ReadWriteStream comment:'
+
+COPYRIGHT (c) 1989/90/91 by Claus Gittinger
+ All Rights Reserved
+
+@(#)RWStream.st 2.3 92/06/06
+'!
+
+!ReadWriteStream methodsFor: 'access-reading'!
+
+peek
+ (position > readLimit) ifTrue: [ ^ nil ].
+ ^ collection at:position
+!
+
+next
+ |p|
+ (position > readLimit) ifTrue: [ ^ nil ].
+ p := position.
+ position := position + 1.
+ ^ collection at:p
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Rectangle.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,616 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Rectangle
+ instanceVariableNames:'left top width height'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Graphics-Primitives'
+!
+
+Rectangle comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+ All Rights Reserved
+
+Rectangles represent a rectangular area in 2D space.
+
+notice, my implementation does not use origin/corner as instance objects
+but left/top/width/height to save space and allocations. This means, that my
+Rectangles cannot be used to represent Rectangles in a higher than 2D
+space. (i.e. only valid if origin/corner are 2D Points)
+
+(aside from that, you will not see any difference from the outside)
+
+Instance variables:
+
+left <Number> the left coordinate (i.e origin x)
+top <Number> the top coordinate (i.e origin y)
+width <Number> the width of the rectangle
+height <Number> the height of the rectangle
+
+%W% %E%
+
+written 89 by claus
+'!
+
+!Rectangle class methodsFor:'instance creation'!
+
+origin:origin corner:corner
+ "create and return a new Rectangle giving top-left and bottom-right points"
+
+ ^ (self basicNew) origin:origin corner:corner
+!
+
+origin:origin extent:extent
+ "create and return a new Rectangle giving top-left point and extent point"
+
+%{ /* NOCONTEXT */
+ OBJ newRect;
+ extern char *newNextPtr, *newEndPtr;
+ extern OBJ Point;
+
+ /* short cut - rectangles are created so often ... */
+ if (_CanDoQuickNew(OHDR_SIZE + 4*sizeof(OBJ))) {
+ if ((self == Rectangle)
+ && _isPoint(origin)
+ && _isPoint(extent)) {
+ _qCheckedAlignedNew(newRect, OHDR_SIZE + 4*sizeof(OBJ), __context);
+ _InstPtr(newRect)->o_class = Rectangle;
+ _InstPtr(newRect)->i_instvars[0] = _PointInstPtr(origin)->p_x;
+ _InstPtr(newRect)->i_instvars[1] = _PointInstPtr(origin)->p_y;
+ _InstPtr(newRect)->i_instvars[2] = _PointInstPtr(extent)->p_x;
+ _InstPtr(newRect)->i_instvars[3] = _PointInstPtr(extent)->p_y;
+ /* no STOREs needed - newRect is in newSpace */
+ RETURN ( newRect );
+ }
+ }
+%}
+.
+ ^ (self basicNew) origin:origin extent:extent
+!
+
+left:left top:top width:w height:h
+ "create and return a new Rectangle giving left and top coordinates
+ and width, height"
+
+%{ /* NOCONTEXT */
+ OBJ newRect;
+ extern char *newNextPtr, *newEndPtr;
+
+ if (_CanDoQuickNew(OHDR_SIZE + 4*sizeof(OBJ))) {
+ /* short cut - rectangles are created so often ... */
+ if (self == Rectangle) {
+ _qCheckedAlignedNew(newRect, OHDR_SIZE + 4*sizeof(OBJ), __context);
+ _InstPtr(newRect)->o_class = Rectangle;
+ _InstPtr(newRect)->i_instvars[0] = left;
+ _InstPtr(newRect)->i_instvars[1] = top;
+ _InstPtr(newRect)->i_instvars[2] = w;
+ _InstPtr(newRect)->i_instvars[3] = h;
+ /* no STOREs needed - newRect is in newSpace */
+ RETURN ( newRect );
+ }
+ }
+%}
+.
+ ^ (self basicNew) left:left top:top width:w height:h
+!
+
+left:left right:right top:top bottom:bottom
+ "create and return a new Rectangle giving left, top, right and bottom coordinates"
+
+ ^ (self basicNew) left:left right:right top:top bottom:bottom
+!
+
+left:left top:top right:right bottom:bottom
+ "create and return a new Rectangle giving left, top, right and bottom coordinates"
+
+ ^ (self basicNew) left:left top:top right:right bottom:bottom
+! !
+
+!Rectangle methodsFor:'accessing'!
+
+top
+ "return the y-coordinate of the top-left origin"
+
+ ^ top
+!
+
+left
+ "return the x-coordinate of the top-left origin"
+
+ ^ left
+!
+
+width
+ "return the width of the rectangle"
+
+ ^ width
+!
+
+height
+ "return the height of the rectangle"
+
+ ^ height
+!
+
+left:newLeft right:right top:newTop bottom:bottom
+ "set the rectangle given left, top, right and bottom coordinates"
+
+ left := newLeft.
+ top := newTop.
+ width := right - left.
+ height := bottom - top
+!
+
+left:newLeft top:newTop right:right bottom:bottom
+ "set the rectangle given left, top, right and bottom coordinates"
+
+ left := newLeft.
+ top := newTop.
+ width := right - left.
+ height := bottom - top
+!
+
+left:newLeft top:newTop width:newWidth height:newHeight
+ "set the rectangle given left, top coordinates and width, height"
+
+ left := newLeft.
+ top := newTop.
+ width := newWidth.
+ height := newHeight
+!
+
+origin:aPoint
+ "set the top-left origin"
+
+ left := aPoint x.
+ top := aPoint y
+!
+
+corner:aPoint
+ "set the bottom-right corner"
+
+ width := aPoint x - left.
+ height := aPoint y - top
+!
+
+top:aNumber
+ "set the top edge"
+
+ height := height + (top - aNumber).
+ top := aNumber
+!
+
+bottom:aNumber
+ "set the bottom edge"
+
+ height := aNumber - top
+!
+
+origin:origin corner:corner
+ "set both origin and corner"
+
+ left := origin x.
+ top := origin y.
+ width := corner x - left.
+ height := corner y - top
+!
+
+extent:aPoint
+ "set the extent from the argument, aPoint with width taken from aPoint x
+ and height taken from aPoint y"
+
+ width := aPoint x.
+ height := aPoint y
+!
+
+origin:origin extent:extent
+ "set both origin and extent"
+
+ left := origin x.
+ top := origin y.
+ width := extent x.
+ height := extent y
+!
+
+origin
+ "return the origin"
+
+ ^ Point x:left y:top
+!
+
+left:aNumber
+ "set the left edge"
+
+ width := width + (left - aNumber).
+ left := aNumber
+!
+
+right:aNumber
+ "set the right edge"
+
+ width := aNumber - left
+!
+
+corner
+ "return the corner"
+
+ ^ Point x:(left + width) y:(top + height)
+!
+
+extent
+ "return the extent"
+
+ ^ Point x:width y:height
+!
+
+topLeft
+ "return the top-left point"
+
+ ^ Point x:left y:top
+!
+
+topRight
+ "return the top-right point"
+
+ ^ Point x:(left + width) y:top
+!
+
+bottomLeft
+ "return the bottom-left point"
+
+ ^ Point x:left y:(top + height)
+!
+
+bottomRight
+ "return the bottom-right point"
+
+ ^ Point x:(left + width) y:(top + height)
+!
+
+bottom
+ "return the y coordinate of the bottom"
+
+ ^ (top + height)
+!
+
+right
+ "return the x coordinate of the right"
+
+ ^ (left + width)
+!
+
+center
+ "return the point in the center of the receiver"
+
+ ^ Point x:(left + (width // 2)) y:(top + (height // 2))
+!
+
+leftCenter
+ "return the left center point"
+
+ ^ Point x:left y:(top + (height // 2))
+!
+
+rightCenter
+ "return the right center point"
+
+ ^ Point x:(left + width) y:(top + (height // 2))
+!
+
+area
+ "return the area (- for screen Rectangles this is the number
+ of pixels)"
+
+ ^ width * height
+! !
+
+!Rectangle methodsFor:'comparing'!
+
+= aRectangle
+ "return true, if the argument aRectangle represents the same
+ rectangle as the receiver"
+
+%{ /* NOCONTEXT */
+ static struct inlineCache eq = _ILC1;
+
+ if (_isNonNilObject(aRectangle) && _qClass(aRectangle) == Rectangle) {
+ if (( _InstPtr(self)->i_instvars[0] == _InstPtr(aRectangle)->i_instvars[0] )
+ && ( _InstPtr(self)->i_instvars[1] == _InstPtr(aRectangle)->i_instvars[1] )
+ && ( _InstPtr(self)->i_instvars[2] == _InstPtr(aRectangle)->i_instvars[2] )
+ && ( _InstPtr(self)->i_instvars[3] == _InstPtr(aRectangle)->i_instvars[3] )) {
+ RETURN ( true );
+ }
+ }
+%}
+.
+ (aRectangle isKindOf:Rectangle) ifFalse:[^ false].
+
+ left = aRectangle left ifFalse:[^ false].
+ top = aRectangle top ifFalse:[^ false].
+ width = aRectangle width ifFalse:[^ false].
+ height = aRectangle height ifFalse:[^ false].
+ ^ true
+! !
+
+!Rectangle methodsFor:'testing'!
+
+containsPoint:aPoint
+ "return true, if the argument, aPoint is contained in the receiver"
+
+ |px py|
+
+ px := aPoint x.
+ (px < left) ifTrue:[^ false].
+ (px > (left + width)) ifTrue:[^ false].
+ py := aPoint y.
+ (py < top) ifTrue:[^ false].
+ (py > (top + height)) ifTrue:[^ false].
+ ^ true
+!
+
+intersects:aRectangle
+ "return true, if the intersection between the argument, aRectangle
+ and the receiver is not empty"
+
+ |b r|
+
+ (aRectangle right) < left ifTrue:[^ false].
+ (aRectangle bottom) < top ifTrue:[^ false].
+ r := left + width.
+ (aRectangle left) > r ifTrue:[^ false].
+ b := top + height.
+ (aRectangle top) > b ifTrue:[^ false].
+ ^ true
+!
+
+contains:aRectangle
+ "return true, if the argument, aRectangle is equal to or
+ is contained fully within the receiver"
+
+ (left <= aRectangle left) ifTrue:[
+ ((left + width) >= aRectangle right) ifTrue:[
+ (top <= aRectangle top) ifTrue:[
+ ((top + height) >= aRectangle bottom) ifTrue:[
+ ^ true
+ ]
+ ]
+ ]
+ ].
+ ^ false
+!
+
+isContainedIn:aRectangle
+ "return true, if the receiver is fully contained within
+ the argument, aRectangle"
+
+ (aRectangle left <= left) ifTrue:[
+ (aRectangle right >= (left + width)) ifTrue:[
+ (aRectangle top <= top) ifTrue:[
+ (aRectangle bottom >= (top + height)) ifTrue:[
+ ^ true
+ ]
+ ]
+ ]
+ ].
+ ^ false
+! !
+
+!Rectangle methodsFor:'rectangle operations'!
+
+intersect:aRectangle
+ "return a new rectangle covering the intersection of the receiver
+ and the argument, aRectangle.
+ the rectangles must intersect for a valid return"
+
+ ^ Rectangle left:(left max:(aRectangle left))
+ right:((left + width) min:(aRectangle right))
+ top:(top max:(aRectangle top))
+ bottom:((top + height) min:(aRectangle bottom))
+!
+
+merge:aRectangle
+ "return a new rectangle covering both the receiver
+ and the argument, aRectangle"
+
+ ^ Rectangle left:(left min:(aRectangle left))
+ right:((left + width) max:(aRectangle right))
+ top:(top min:(aRectangle top))
+ bottom:((top + height) max:(aRectangle bottom))
+!
+
++ aPoint
+ "return a Rectangle with same extent as receiver but
+ origin translated by the argument, aPoint"
+
+ ^ Rectangle origin:(self origin + aPoint) extent:(self extent)
+!
+
+rounded
+ ^ Rectangle left:(left rounded) top:(top rounded)
+ width:(width rounded) height:(height rounded)
+!
+
+expandBy:amount
+ "return a new rectangle which is expanded in all directions
+ by amount, aPoint or Number"
+
+ |amountPoint|
+
+ amountPoint := amount asPoint.
+ ^ Rectangle left:(left - amountPoint x) top:(top - amountPoint y)
+ width:(width + (2 * amountPoint x))
+ height:(height + (2 * amountPoint y))
+!
+
+insetBy: delta
+ "return a new rectangle which is inset in all directions
+ by delta, aPoint or Rectangle"
+
+ | newrect |
+
+ newrect := delta asRectangle.
+ ^Rectangle origin: (self origin + (newrect origin))
+ corner: (self corner - (newrect corner))
+!
+
+translateBy:amount
+ "return a new rectangle which is translated (i.e. moved)
+ by amount, aPoint or Number"
+
+ |amountPoint|
+
+ amountPoint := amount asPoint.
+ ^ Rectangle left:(left + amountPoint x) top:(top + amountPoint y)
+ width:(width)
+ height:(height)
+!
+
+moveTo: aPoint
+ "destructively translate the rectangle"
+
+ | diff |
+
+ diff := aPoint - self origin.
+ self origin:aPoint corner:self corner + diff
+!
+
+moveBy: aPoint
+ "destructively translate the rectangle
+ sorry for the name inconsistency - but GNU-ST named it that way"
+
+ left := left + aPoint x.
+ top := top + aPoint y
+!
+
+scaleBy:scale
+ "destructively scale the receiver rectangle by scale"
+
+ |scalePoint|
+
+ scalePoint := scale asPoint.
+ width := (width * scalePoint x).
+ height := (height * scalePoint y)
+!
+
+scaledBy:scale
+ "return a new rectangle which is the receiver
+ scaled by scale"
+
+ |scalePoint|
+
+ scalePoint := scale asPoint.
+ ^ Rectangle left:left top:top
+ width:(width * scalePoint x)
+ height:(height * scalePoint y)
+!
+
+amountToTranslateWithin: aRectangle
+ "for GNU-ST compatibility"
+
+ ^(aRectangle origin) - self origin
+!
+
+areasOutside: aRectangle
+ "----------------------------------------------------------------
+ | added for GNU-ST compatibility
+ |
+ | author: Doug McCallum <uunet!!ico.isc.com!!dougm>
+ |
+ |areasOutside: aRectangle
+ | most complicated of the Rectangle primitives
+ | The basic methodology is to first determine that there is an
+ | intersection by finding the overlapping rectangle. From the
+ | overlapping rectangle, first determine if it runs along an edge.
+ | If it doesn't, extend the rectangle up to the top edge and add
+ | the new rectangle to the collection and start the rest of the
+ | process. If the left edge does not touch the left edge of self,
+ | extend it to the edge saving the new rectangle. Then do the
+ | same to the right edge. Then check top and bottom edges. Most
+ | of the time only 2 or 3 rectangles get formed, occasionally 4.
+ | It should be possible to never get more than 3 but requires more
+ | work.
+ ----------------------------------------------------------------"
+
+ | collect iRect tmp |
+ iRect _ self intersect: aRectangle.
+ (iRect = nil) ifTrue: [^nil]. "case of no intersection"
+ "the collect collection gathers Rectangles"
+ collect _ OrderedCollection new: 4.
+ "is it floating or on the edge?"
+ (((((iRect top) ~= self top)
+ and: [ (iRect bottom) ~= self bottom ])
+ and: [ (iRect left) ~= self left ])
+ and: [ (iRect right) ~= self right ] )
+ ifTrue: "entirely in the center."
+ [tmp _ Rectangle origin: (Point x: iRect left y: self top)
+ corner: iRect bottomRight.
+ collect add: tmp.
+ iRect _ iRect merge: tmp].
+ ((iRect left) ~= self left)
+ ifTrue: "doesn't touch left edge so make it touch"
+ [tmp _ Rectangle origin: (Point x: self left y: iRect top)
+ corner: iRect bottomLeft.
+ collect add: tmp.
+ "merge new (tmp) with overlap to keep track"
+ iRect _ iRect merge: tmp].
+ ((iRect right) ~= self right)
+ ifTrue: "doesn't touch right edge so extend it"
+ [tmp _ Rectangle origin: iRect topRight
+ corner: (Point x: self right y: iRect bottom).
+ collect add: tmp.
+ iRect _ iRect merge: tmp].
+ (((iRect left) ~= self left) || [(iRect top) ~= self top])
+ ifTrue: "whole top part can be taken now"
+ [tmp _ Rectangle origin: self origin corner: iRect topRight.
+ collect add: tmp].
+ (((iRect right) ~= self right) || [(iRect bottom) ~= self bottom])
+ ifTrue: "whole bottom open and can be taken"
+ [tmp _ Rectangle origin: iRect bottomLeft corner: self corner.
+ collect add: tmp].
+ ^collect
+
+! !
+
+!Rectangle methodsFor:'printing'!
+
+printString
+ "return a string for printing"
+
+ ^ 'Rectangle origin:'
+ , self origin printString
+ , ' corner:'
+ , self corner printString
+!
+
+printOn:aStream
+ "print the receiver on aStream"
+
+ aStream nextPutAll:'Rectangle origin:'.
+ (self origin) printOn:aStream.
+ aStream nextPutAll:' corner:'.
+ (self corner) printOn:aStream
+!
+
+storeOn:aStream
+ "store the receiver on aStream; i.e. print an expression which will
+ reconstruct the receiver"
+
+ aStream nextPutAll:'('.
+ aStream nextPutAll:(self class name).
+ aStream nextPutAll:' new origin:'.
+ aStream nextPutAll:(self origin printString).
+ aStream nextPutAll:' corner:'.
+ aStream nextPutAll:(self corner printString).
+ aStream nextPutAll:'('
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Registry.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,165 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Registry
+ instanceVariableNames:'registeredObjects phantoms'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'System-Support'
+!
+
+Registry comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+written jun 93 by claus
+'!
+
+!Registry class methodsFor:'documentation'!
+
+documentation
+"
+Registries provide an easy interface to using ShadowArrays.
+A class, which wants to be informed of instance-death, can put a created object
+into a registry. The registry will create a copy of the object, and
+watch out for death of the registered object. When it dies, the copy will
+be sent the message >>disposed.
+The trick with the shallow copy is especially nice, you can think of it as
+beeing the original object which died.
+
+All objects, which keep external resources (such as fileDescriptors, fonts,
+colormap-entries etc.) should be registered, so that the underlying resource
+can be freed when the object goes away.
+
+Of course, you too can use it to do whatever you need to do in case of the
+death of an object.
+"
+! !
+
+!Registry methodsFor:'dispose handling'!
+
+informDispose
+ "an instance has been destroyed - look which one it was"
+
+ |phantom|
+
+ 1 to:phantoms size do:[:index |
+ (registeredObjects at:index) isNil ifTrue:[
+ (phantoms at:index) notNil ifTrue:[
+ phantom := phantoms at:index.
+ phantoms at:index put:nil.
+ phantom disposed
+ ]
+ ]
+ ]
+! !
+
+!Registry methodsFor:'enumeration'!
+
+contentsDo:aBlock
+ "evaluate aBlock for each registered object"
+
+ registeredObjects notNil ifTrue:[
+ registeredObjects do:[:o |
+ o notNil ifTrue:[
+ aBlock value:o
+ ]
+ ]
+ ]
+! !
+
+!Registry methodsFor:'accessing'!
+
+contents
+ "return the collection of registered objects"
+
+ ^ registeredObjects
+!
+
+changed:anObject
+ "a registered object has changed, create a new phantom"
+
+ |index|
+
+ index := registeredObjects identityIndexOf:anObject ifAbsent:[0].
+ index ~~ 0 ifTrue:[
+ phantoms at:index put:anObject shallowCopy.
+ ]
+!
+
+register:anObject
+ "register anObject, so that a copy of it gets the disposed message
+ when anObject dies (somewhere in the future)"
+
+ |phantom newColl count p index|
+
+ phantom := anObject shallowCopy.
+
+ registeredObjects isNil ifTrue:[
+ registeredObjects := ShadowArray new:10.
+ registeredObjects watcher:self.
+ registeredObjects at:1 put:anObject.
+ phantoms := VariableArray with:phantom.
+ ^ self
+ ].
+
+ index := registeredObjects identityIndexOf:anObject ifAbsent:[0].
+ index ~~ 0 ifTrue:[
+ self error:'bad register'.
+ phantoms at:index put:phantom.
+ ^ self
+ ].
+
+ "search for a free slot, on the fly look for leftovers"
+ count := phantoms size.
+ 1 to:count do:[:index |
+ (registeredObjects at:index) isNil ifTrue:[
+ "is there a leftover ?"
+ (phantoms at:index) notNil ifTrue:[
+ p := phantoms at:index.
+ phantoms at:index put:nil.
+ p disposed
+ ].
+ (phantoms at:index) isNil ifTrue:[
+ registeredObjects at:index put:anObject.
+ phantoms at:index put:phantom.
+ ^ self
+ ]
+ ]
+ ].
+
+ "no free slot, add at the end"
+
+ newColl := ShadowArray new:(count * 2).
+ newColl replaceFrom:1 to:count with:registeredObjects.
+ registeredObjects := newColl.
+ registeredObjects watcher:self.
+
+ registeredObjects at:(count + 1) put:anObject.
+ phantoms add:phantom
+!
+
+unregister:anObject
+ "remove registration of anObject, without telling the phantom;
+ should be sent, if we are no more interrested in destruction of
+ anObject (i.e. it no longer holds external resources)."
+
+ |index|
+
+ index := registeredObjects identityIndexOf:anObject ifAbsent:[0].
+ index ~~ 0 ifTrue:[
+ phantoms at:index put:nil.
+ registeredObjects at:index put:nil
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Semaphore.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,112 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+LinkedList subclass:#Semaphore
+ instanceVariableNames:'count waitingProcesses'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Processes'!
+
+Semaphore comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+Semaphores are used to synchronize processes providing a nonBusy wait
+mechanism. A process can wait for the availability of some resource by
+performing a Semaphore-wait, which will suspend the process until the
+resource becomes available which is signalled by (another process performing)
+Semaphore-signal.
+If the resource has been alrady available before the wait, no suspending is
+done, but the resource immediately allocated.
+
+See samples in doc/coding.
+
+%W% %E%
+'!
+
+!Semaphore class methodsFor:'instance creation'!
+
+new
+ "create & return a new semaphore which blocks until a signal is sent"
+
+ ^ super new setCount:0
+!
+
+new:n
+ "create & return a new semaphore which allows n waits before
+ blocking"
+
+ ^ super new setCount:n
+!
+
+forMutualExclusion
+ "create & return a new semaphore which allows exactly one process to
+ wait on it without blocking"
+
+ ^ self new:1
+! !
+
+!Semaphore methodsFor:'private accessing'!
+
+setCount:n
+ count := n
+! !
+
+!Semaphore methodsFor:'wait & signal'!
+
+wait
+ "wait for the semaphore"
+
+ |current|
+
+ "
+ need a while-loop here, since more than one process may
+ wait for it and another one may also wake up.
+ Thus count is not always non-zero after returning from
+ suspend.
+ "
+
+ [count == 0] whileTrue:[
+ current := Processor currentProcess.
+ waitingProcesses isNil ifTrue:[
+ waitingProcesses := OrderedCollection with:current
+ ] ifFalse:[
+ waitingProcesses add:current
+ ].
+ current suspend
+ ].
+ count := count - 1
+!
+
+signal
+ "waking up waiters"
+
+ count := count + 1.
+ waitingProcesses notNil ifTrue:[
+ waitingProcesses isEmpty ifFalse:[
+ waitingProcesses removeFirst resume
+ ]
+ ]
+!
+
+critical:aBlock
+ "evaluate aBlock as a critical region; the receiver must be
+ created using Semaphore-forMutualExclusion"
+
+ |value|
+
+ self wait.
+ value := aBlock value.
+ self signal.
+ ^ value
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SeqColl.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,955 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#SequenceableCollection
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Abstract'
+!
+
+SequenceableCollection comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+SequenceableCollections have ordered elements which can be accessed via
+an index. SequenceableCollection is an abstract class - there are no
+instances of it in the system.
+
+%W% %E%
+
+written spring 89 by claus
+'!
+
+!SequenceableCollection class methodsFor:'instance creation'!
+
+new:size withAll:element
+ "return a new Collection of size, where all elements are
+ initialized to element"
+
+ |newCollection|
+
+ newCollection := self new:size.
+ newCollection atAllPut:element.
+ ^ newCollection
+! !
+
+!SequenceableCollection methodsFor:'accessing'!
+
+first
+ "return the first element"
+
+ ^ self at:1
+!
+
+last
+ "return the last element"
+
+ ^ self at:(self size)
+! !
+
+!SequenceableCollection methodsFor:'comparing'!
+
+= aCollection
+ "return true if the receiver and aCollection represent collections
+ with equal contents."
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ (aCollection == self) ifTrue:[^true].
+ (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].
+
+ stop := self size.
+ stop == (aCollection size) ifFalse:[^false].
+ index := 1.
+ [index <= stop] whileTrue:[
+ (self at:index) = (aCollection at:index) ifFalse:[^false].
+ index := index + 1
+ ].
+ ^ true
+!
+
+startsWith:aCollection
+ "return true, if the receivers first elements match those
+ of aCollection"
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ (aCollection == self) ifTrue:[^true].
+ (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].
+
+ stop := aCollection size.
+ stop > self size ifTrue:[^false].
+
+ index := 1.
+ [index <= stop] whileTrue:[
+ (self at:index) = (aCollection at:index) ifFalse:[^false].
+ index := index + 1
+ ].
+ ^ true
+
+ "'abcde' startsWith:#($a $b $c)"
+ "#[1 2 3 4] startsWith:#(1 2 3)"
+ "#(1 2 3 4) asOrderedCollection startsWith:#(1 2 3)"
+!
+
+endsWith:aCollection
+ "return true, if the receivers last elements match those
+ of aCollection"
+
+ |index1 "{ Class: SmallInteger }"
+ index2 "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ (aCollection == self) ifTrue:[^true].
+ (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].
+
+ stop := aCollection size.
+ stop > self size ifTrue:[^false].
+
+ index1 := self size.
+ index2 := aCollection size.
+ [index2 > 0] whileTrue:[
+ (self at:index1) = (aCollection at:index2) ifFalse:[^false].
+ index1 := index1 - 1.
+ index2 := index2 - 1
+ ].
+ ^ true
+
+ "'abcde' endsWith:#($d $e)"
+ "#[1 2 3 4] endsWith:#(3 4)"
+ "#(1 2 3 4) asOrderedCollection endsWith:#(3 4)"
+! !
+
+!SequenceableCollection methodsFor:'testing'!
+
+size
+ "return the number of elements in the collection.
+ concrete implementations must define this"
+
+ ^ self subclassResponsibility
+! !
+
+!SequenceableCollection methodsFor:'copying'!
+
+, aCollection
+ "return a new collection formed from concatenating the receiver with
+ the argument"
+
+ |newCollection
+ mySize "{ Class: SmallInteger }"
+ newSize "{ Class: SmallInteger }"
+ otherSize "{ Class: SmallInteger }"
+ dstIndex "{ Class: SmallInteger }"|
+
+ mySize := self size.
+ otherSize := aCollection size.
+ newSize := mySize + otherSize.
+ newCollection := self species new:newSize.
+
+ newCollection replaceFrom:1 to:mySize with:self startingAt:1.
+ dstIndex := mySize + 1.
+ (aCollection isKindOf:SequenceableCollection) ifTrue:[
+ "yes, aCollection has indexed elements"
+ newCollection replaceFrom:dstIndex to:newSize
+ with:aCollection startingAt:1.
+ ^ newCollection
+ ] ifFalse:[
+ "no, enumerate aCollection"
+ aCollection do:[:element |
+ newCollection at:dstIndex put:element.
+ dstIndex := dstIndex + 1
+ ]
+ ].
+ ^ newCollection
+!
+
+copyWith:newElement
+ "return a new collection consisting of receivers elements
+ plus the argument"
+
+ |newCollection mySize newSize|
+
+ mySize := self size.
+ newSize := mySize + 1.
+ newCollection := self species new:newSize.
+ newCollection replaceFrom:1 to:mySize with:self startingAt:1.
+ newCollection at:newSize put:newElement.
+ ^newCollection
+!
+
+copyWithout:anElement
+ "return a new collection consisting of receivers elements
+ without anElement (if it was present)"
+
+ |newCollection skipIndex
+ dstIndex "{ Class: SmallInteger }"
+ index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ skipIndex := self indexOf:anElement startingAt:1.
+ (skipIndex == 0) ifTrue:[^ self copy].
+ stop := self size.
+ newCollection := self class new:(stop - 1).
+ dstIndex := 1.
+ index := 1.
+ [index <= stop] whileTrue:[
+ (index ~~ skipIndex) ifTrue:[
+ newCollection at:dstIndex put:(self at:index).
+ dstIndex := dstIndex + 1
+ ].
+ index := index + 1
+ ].
+ ^ newCollection
+!
+
+copyFrom:start to:stop
+ "return a new collection consisting of receivers elements
+ between start and stop"
+
+ |newCollection newSize|
+
+ newSize := stop - start + 1.
+ newCollection := self class new:newSize.
+ newCollection replaceFrom:1 to:newSize with:self startingAt:start.
+ ^ newCollection
+!
+
+copyFrom:start
+ "return a new collection consisting of receivers elements
+ from start to the end of the collection"
+
+ ^ self copyFrom:start to:(self size)
+!
+
+copyTo:stop
+ "return a new collection consisting of receivers elements
+ from 1 up to index stop"
+
+ ^ self copyFrom:1 to:stop
+!
+
+copyWithoutIndex:omitIndex
+ "return a new collection consisting of receivers elements
+ without the argument stored at omitIndex"
+
+ |copy|
+
+ copy := self class new:(self size - 1).
+ copy replaceFrom:1 to:(omitIndex - 1) with:self startingAt:1.
+ copy replaceFrom:omitIndex to:(copy size)
+ with:self startingAt:(omitIndex + 1).
+ ^ copy
+! !
+
+!SequenceableCollection methodsFor:'filling and replacing'!
+
+from:index1 to:index2 put:anObject
+ "replace the elements from index1 to index2 of the collection
+ by the argument, anObject"
+
+ |index "{ Class: SmallInteger }"
+ end "{ Class: SmallInteger }"|
+
+ index := index1.
+ end := index2.
+ [index <= end] whileTrue:[
+ self at:index put:anObject.
+ index := index + 1
+ ]
+!
+
+atAllPut:anObject
+ "replace all elements of the collection by the argument, anObject"
+
+ self from:1 to:(self size) put:anObject
+!
+
+atAll:indexCollection put:anObject
+ "put anObject into all indexes from indexCollection in the receiver"
+
+ indexCollection do:[:index | self at:index put:anObject]
+
+ "(Array new:10) atAll:(1 to:5) put:0"
+ "(Array new:10) atAll:#(1 5 6 9) put:0"
+!
+
+replaceAll:oldObject by:newObject
+ "replace all oldObjects by newObject in the receiver"
+
+ 1 to:self size do:[:index |
+ (self at:index) = oldObject ifTrue:[
+ self at:index put:newObject
+ ]
+ ]
+!
+
+replaceFrom:start with:replacementCollection
+ "replace elements starting at start with elements
+ taken from replacementCollection (starting at 1)"
+
+ ^ self replaceFrom:start
+ to:(start + replacementCollection size - 1)
+ with:replacementCollection
+ startingAt:1
+!
+
+replaceFrom:start to:stop with:replacementCollection
+ "replace elements between index start and stop with elements
+ taken from replacementCollection (starting at 1)"
+
+ ^ self replaceFrom:start
+ to:stop
+ with:replacementCollection
+ startingAt:1
+!
+
+replaceFrom:start to:stop with:replacementCollection startingAt:repStart
+ "replace elements between index start and stop with elements
+ taken from replacementCollection (starting at repStart)"
+
+ |srcIndex "{ Class: SmallInteger }"
+ dstIndex "{ Class: SmallInteger }"
+ end "{ Class: SmallInteger }" |
+
+ (replacementCollection == self) ifTrue:[
+ (repStart < start) ifTrue:[
+ " must do reverse copy "
+ srcIndex := repStart + (stop - start).
+ dstIndex := stop.
+ end := start.
+ [dstIndex >= end] whileTrue:[
+ self at:dstIndex put:(replacementCollection at:srcIndex).
+ srcIndex := srcIndex - 1.
+ dstIndex := dstIndex - 1
+ ].
+ ^ self
+ ]
+ ].
+
+ srcIndex := repStart.
+ dstIndex := start.
+ end := stop.
+ [dstIndex <= end] whileTrue:[
+ self at:dstIndex put:(replacementCollection at:srcIndex).
+ srcIndex := srcIndex + 1.
+ dstIndex := dstIndex + 1
+ ]
+!
+
+withCRs
+ "return a new collection consisting of receivers elements
+ with all \-characters replaced by cr-characters"
+
+ |newCollection
+ size "{ Class: SmallInteger }" |
+
+ newCollection := self copy.
+ size := self size.
+ 1 to:size do:[:index |
+ ((self at:index) == $\) ifTrue:[
+ newCollection at:index put:(Character cr)
+ ]
+ ].
+ ^ newCollection
+!
+
+withoutCRs
+ "return a new collection consisting of receivers elements
+ with all cr-characters replaced by \-characters"
+
+ |newCollection
+ size "{ Class: SmallInteger }" |
+
+ newCollection := self copy.
+ size := self size.
+ 1 to:size do:[:index|
+ ((self at:index) == Character cr) ifTrue:[
+ newCollection at:index put:$\
+ ]
+ ].
+ ^ newCollection
+! !
+
+!SequenceableCollection methodsFor:'adding & removing'!
+
+addFirst:anElement
+ "prepend the argument, anElement to the collection"
+
+ |newSize|
+
+ newSize := self size + 1.
+ self grow:newSize.
+ self replaceFrom:2 to:newSize with:self startingAt:1.
+ self at:1 put:anElement
+!
+
+add:anElement
+ "append the argument, anElement to the collection"
+
+ |newSize|
+
+ newSize := self size + 1.
+ self grow:newSize.
+ self at:newSize put:anElement
+!
+
+add:anElement beforeIndex:index
+ "insert the first argument, anObject into the collection before slot index"
+
+ |newSize|
+
+ newSize := self size + 1.
+ self grow:newSize.
+ self replaceFrom:index + 1 to:newSize with:self startingAt:index.
+ self at:index put:anElement
+!
+
+remove:anElement ifAbsent:aBlock
+ "search for anElement and, if present remove it; if not present
+ return the value of evaluating aBlock"
+
+ |any
+ dstIndex "{ Class: SmallInteger }"
+ sz "{ Class: SmallInteger }"|
+
+ dstIndex := 1.
+ any := false.
+ sz := self size.
+ 1 to:sz do:[:srcIndex |
+ (anElement = (self at:srcIndex)) ifTrue:[
+ any := true
+ ] ifFalse:[
+ (dstIndex ~~ srcIndex) ifTrue:[
+ self at:dstIndex put:(self at:srcIndex)
+ ].
+ dstIndex := dstIndex + 1
+ ]
+ ].
+ any ifTrue:[
+ self grow:dstIndex - 1
+ ] ifFalse:[
+ aBlock value
+ ]
+!
+
+removeFromIndex:startIndex toIndex:endIndex
+ "remove the elements stored at indexes between startIndex and endIndex"
+
+ |newSize|
+
+ newSize := self size - endIndex + startIndex - 1.
+ self replaceFrom:startIndex to:newSize with:self startingAt:(endIndex + 1).
+ self grow:newSize
+!
+
+removeIndex:index
+ "remove the argument stored at index"
+
+ self removeFromIndex:index toIndex:index
+! !
+
+!SequenceableCollection methodsFor:'searching'!
+
+detect:aBlock ifNone:exceptionBlock
+ "find the first element, for which evaluation of the argument, aBlock
+ return true; if none does so, return the evaluation of exceptionBlock
+
+ reimplemented here for speed"
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }"
+ element|
+
+ stop := self size.
+ index := 1.
+ [index <= stop] whileTrue:[
+ element := self at:index.
+ (aBlock value:element) ifTrue:[
+ ^ element
+ ].
+ index := index + 1
+ ].
+ ^ exceptionBlock value
+!
+
+indexOf:anElement
+ "search the collection for anElement;
+ if found, return the index otherwise return 0.
+ The comparison is done using = (i.e. equality test)."
+
+ ^ self indexOf:anElement startingAt:1
+!
+
+indexOf:anElement ifAbsent:exceptionBlock
+ "search the collection for anElement;
+ if found, return the index otherwise return the value of the
+ exceptionBlock.
+ The comparison is done using = (i.e. equality test)."
+
+ |index|
+
+ index := self indexOf:anElement startingAt:1.
+ (index == 0) ifTrue:[^ exceptionBlock value].
+ ^ index
+!
+
+indexOf:anElement startingAt:start
+ "search the collection for anElement staring search at index start;
+ if found, return the index otherwise return 0.
+ The comparison is done using = (i.e. equality test)."
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ index := start.
+ stop := self size.
+ [index <= stop] whileTrue:[
+ anElement = (self at:index) ifTrue:[^ index].
+ index := index + 1
+ ].
+ ^ 0
+!
+
+indexOf:anElement startingAt:start ifAbsent:exceptionBlock
+ "search the collection for anElement starting search at start;
+ if found, return the index otherwise return the value of the
+ exceptionBlock.
+ The comparison is done using = (i.e. equality test)."
+
+ |index|
+
+ index := self indexOf:anElement startingAt:start.
+ (index == 0) ifTrue:[^ exceptionBlock value].
+ ^ index
+!
+
+identityIndexOf:anElement
+ "search the collection for anElement using identity compare (i.e. ==);
+ if found, return the index otherwise return 0."
+
+ ^ self identityIndexOf:anElement startingAt:1
+!
+
+identityIndexOf:anElement ifAbsent:exceptionBlock
+ "search the collection for anElement using identity compare (i.e. ==);
+ if found, return the index otherwise return the value of the
+ exceptionBlock."
+
+ |index|
+
+ index := self identityIndexOf:anElement startingAt:1.
+ (index == 0) ifTrue:[^ exceptionBlock value].
+ ^ index
+!
+
+identityIndexOf:anElement startingAt:start
+ "search the collection for anElement staring search at index start
+ using identity compare (i.e. ==);
+ if found, return the index otherwise return 0."
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ index := start.
+ stop := self size.
+ [index <= stop] whileTrue:[
+ anElement == (self at:index) ifTrue:[^ index].
+ index := index + 1
+ ].
+ ^ 0
+!
+
+identityIndexOf:anElement startingAt:start ifAbsent:exceptionBlock
+ "search the collection for anElement starting search at start;
+ if found, return the index otherwise return the value of the
+ exceptionBlock.
+ This one searches for identical objects (i.e. ==)."
+
+ |index|
+
+ index := self identityIndexOf:anElement startingAt:start.
+ (index == 0) ifTrue:[^ exceptionBlock value].
+ ^ index
+!
+
+findFirst:aBlock
+ "find the first element, for which evaluation of the argument, aBlock
+ return true; return its index or 0 if none detected."
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ stop := self size.
+ index := 1.
+ [index <= stop] whileTrue:[
+ (aBlock value:(self at:index)) ifTrue:[^ index].
+ index := index + 1
+ ].
+ ^ 0
+
+ "#(1 2 3 4 5 6) findFirst:[:x | (x > 3) and:[x even]]"
+!
+
+includes:anElement
+ "return true if the collection contains anElement; false otherwise.
+ Comparison is done using equality compare (i.e. =)."
+
+ ((self indexOf:anElement startingAt:1) == 0) ifTrue:[^ false].
+ ^ true
+! !
+
+!SequenceableCollection methodsFor:'sorting & reordering'!
+
+reverse
+ "reverse the order of the arguments inplace"
+
+ |lowIndex "{ Class: SmallInteger }"
+ hiIndex "{ Class: SmallInteger }"
+ t|
+
+ hiIndex := self size.
+ lowIndex := 1.
+ [lowIndex < hiIndex] whileTrue:[
+ t := self at:lowIndex.
+ self at:lowIndex put:(self at:hiIndex).
+ self at:hiIndex put:t.
+ lowIndex := lowIndex + 1.
+ hiIndex := hiIndex - 1
+ ]
+ "#(4 5 6 7 7) reverse"
+!
+
+quickSortFrom:begin to:end
+ "actual quicksort worker for sort-message"
+
+ |b "{ Class: SmallInteger }"
+ e "{ Class: SmallInteger }"
+ middleElement temp |
+
+ b := begin.
+ e := end.
+ middleElement := self at:((b + e) // 2).
+
+ [b < e] whileTrue:[
+ [b < end and:[(self at:b) < middleElement]] whileTrue:[b := b + 1].
+ [e > begin and:[middleElement < (self at:e)]] whileTrue:[e := e - 1].
+
+ (b <= e) ifTrue:[
+ (b == e) ifFalse:[
+ temp := self at:b.
+ self at:b put:(self at:e).
+ self at:e put:temp
+ ].
+ b := b + 1.
+ e := e - 1
+ ]
+ ].
+ (begin < e) ifTrue:[self quickSortFrom:begin to:e].
+ (b < end) ifTrue:[self quickSortFrom:b to:end]
+!
+
+quickSortFrom:begin to:end with:aCollection
+ "actual quicksort worker for sortWith-message"
+
+ |b "{ Class: SmallInteger }"
+ e "{ Class: SmallInteger }"
+ middleElement temp |
+
+ b := begin.
+ e := end.
+ middleElement := self at:((b + e) // 2).
+
+ [b < e] whileTrue:[
+ [b < end and:[(self at:b) < middleElement]] whileTrue:[b := b + 1].
+ [e > begin and:[middleElement < (self at:e)]] whileTrue:[e := e - 1].
+
+ (b <= e) ifTrue:[
+ (b == e) ifFalse:[
+ temp := self at:b.
+ self at:b put:(self at:e).
+ self at:e put:temp.
+ temp := aCollection at:b.
+ aCollection at:b put:(aCollection at:e).
+ aCollection at:e put:temp
+ ].
+ b := b + 1.
+ e := e - 1
+ ]
+ ].
+ (begin < e) ifTrue:[self quickSortFrom:begin to:e with:aCollection].
+ (b < end) ifTrue:[self quickSortFrom:b to:end with:aCollection]
+!
+
+quickSortFrom:begin to:end sortBlock:sortBlock
+ "actual quicksort worker for sort:-message"
+
+ |b "{ Class: SmallInteger }"
+ e "{ Class: SmallInteger }"
+ middleElement temp |
+
+ b := begin.
+ e := end.
+ middleElement := self at:((b + e) // 2).
+
+ [b < e] whileTrue:[
+ [b < end and:[sortBlock value:(self at:b) value:middleElement]] whileTrue:[b := b + 1].
+ [e > begin and:[sortBlock value:middleElement value:(self at:e)]] whileTrue:[e := e - 1].
+
+ (b <= e) ifTrue:[
+ (b == e) ifFalse:[
+ temp := self at:b.
+ self at:b put:(self at:e).
+ self at:e put:temp
+ ].
+ b := b + 1.
+ e := e - 1
+ ]
+ ].
+ (begin < e) ifTrue:[self quickSortFrom:begin to:e sortBlock:sortBlock].
+ (b < end) ifTrue:[self quickSortFrom:b to:end sortBlock:sortBlock]
+!
+
+quickSortFrom:begin to:end sortBlock:sortBlock with:aCollection
+ "actual quicksort worker for sort:with:-message"
+
+ |b "{ Class: SmallInteger }"
+ e "{ Class: SmallInteger }"
+ middleElement temp |
+
+ b := begin.
+ e := end.
+ middleElement := self at:((b + e) // 2).
+
+ [b < e] whileTrue:[
+ [b < end and:[sortBlock value:(self at:b) value:middleElement]] whileTrue:[b := b + 1].
+ [e > begin and:[sortBlock value:middleElement value:(self at:e)]] whileTrue:[e := e - 1].
+
+ (b <= e) ifTrue:[
+ (b == e) ifFalse:[
+ temp := self at:b.
+ self at:b put:(self at:e).
+ self at:e put:temp.
+ temp := aCollection at:b.
+ aCollection at:b put:(aCollection at:e).
+ aCollection at:e put:temp
+ ].
+ b := b + 1.
+ e := e - 1
+ ]
+ ].
+ (begin < e) ifTrue:[self quickSortFrom:begin to:e sortBlock:sortBlock with:aCollection].
+ (b < end) ifTrue:[self quickSortFrom:b to:end sortBlock:sortBlock with:aCollection]
+!
+
+bubbleSort
+ "sort the collection inplace using bubbleSort (sloooow)
+ - this one makes only sense to sort after inserting an element into
+ an already sorted collection (if at all)"
+
+ |index "{ Class: SmallInteger }"
+ index2 "{ Class: SmallInteger }"
+ end "{ Class: SmallInteger }"
+ smallest smallestIndex thisOne|
+
+ end := self size.
+ index := 1.
+ [index <= end] whileTrue:[
+ smallest := self at:index.
+ smallestIndex := index.
+ index2 := index + 1.
+ [index2 <= end] whileTrue:[
+ (self at:index2) < smallest ifTrue:[
+ smallestIndex := index2.
+ smallest := self at:index2
+ ].
+ index2 := index2 + 1
+ ].
+ (smallestIndex ~~ index) ifTrue:[
+ thisOne := self at:index.
+ self at:index put:smallest.
+ self at:smallestIndex put:thisOne
+ ].
+ index := index + 1
+ ]
+
+ "#(1 16 7 98 3 19 4 0) bubbleSort"
+!
+
+sort
+ "sort the collection inplace. The elements are compared using
+ > and < i.e. they should offer a magnitude-like protocol."
+ |sz|
+
+ sz := self size.
+ (sz > 1) ifTrue:[
+ self quickSortFrom:1 to:sz
+ ]
+
+ "#(1 16 7 98 3 19 4 0) sort"
+!
+
+sortWith:aCollection
+ "sort the receiver collection inplace, also sort aCollection with it.
+ Use, when you have a key collection to sort another collection with."
+
+ |sz|
+
+ sz := self size.
+ (sz > 1) ifTrue:[
+ self quickSortFrom:1 to:sz with:aCollection
+ ]
+
+ "|c1 c2|
+ c1 := #(1 16 7 9).
+ c2 := #('one' 'sixteen' 'seven' 'nine').
+ c1 sortWith:c2.
+ c1 printNewline.
+ c2 printNewline"
+!
+
+sort:sortBlock
+ "sort the collection inplace using the 2-arg block sortBlock
+ for comparison. This allows any sort criteria to be implemented."
+
+ |sz|
+
+ sz := self size.
+ (sz > 1) ifTrue:[
+ self quickSortFrom:1 to:sz sortBlock:sortBlock
+ ]
+
+ "#(1 16 7 98 3 19 4 0) sort:[:a :b | a < b]"
+ "#(1 16 7 98 3 19 4 0) sort:[:a :b | a > b]"
+!
+
+sort:sortBlock with:aCollection
+ "sort the collection inplace using the 2-arg block sortBlock
+ for comparison. Also reorder the elements in aCollection"
+
+ |sz|
+
+ sz := self size.
+ (sz > 1) ifTrue:[
+ self quickSortFrom:1 to:sz sortBlock:sortBlock with:aCollection
+ ]
+
+ "|c1 c2|
+ c1 := #(1 16 7 9).
+ c2 := #('one' 'sixteen' 'seven' 'nine').
+ c1 sort:[:a :b | a > b] with:c2.
+ c1 printNewline.
+ c2 printNewline"
+! !
+
+!SequenceableCollection methodsFor:'enumerating'!
+
+do:aBlock
+ "evaluate the argument, aBlock for every element in the collection."
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"|
+
+ index := 1.
+ length := self size.
+ [index <= length] whileTrue:[
+ aBlock value:(self at:index).
+ index := index + 1
+ ]
+!
+
+from:index1 to:index2 do:aBlock
+ "evaluate the argument, aBlock for the elements with index index1 to
+ index2 in the collection"
+
+ |index "{ Class:SmallInteger }"
+ stop "{ Class:SmallInteger }" |
+
+ index := index1.
+ stop := index2.
+ [index <= stop] whileTrue:[
+ aBlock value:(self at:index).
+ index := index + 1
+ ]
+!
+
+with:aCollection do:aBlock
+ "evaluate the argument, aBlock for successive elements from
+ each of the two collections self and aCollection.
+ aBlock must be a two-argument block"
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ index := 1.
+ stop := self size.
+ [index <= stop] whileTrue:[
+ aBlock value:(self at:index) value:(aCollection at:index).
+ index := index + 1
+ ]
+!
+
+reverseDo:aBlock
+ "evaluate the argument, aBlock for every element in the collection
+ in reverse order"
+
+ |index "{ Class:SmallInteger }" |
+
+ index := self size.
+ [index > 0] whileTrue:[
+ aBlock value:(self at:index).
+ index := index - 1
+ ]
+!
+
+collect:aBlock
+ "evaluate the argument, aBlock for every element in the collection
+ and return a collection of the results"
+
+ |newCollection
+ index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }" |
+
+ length := self size.
+ newCollection := self species new:length.
+ index := 1.
+ [index <= length] whileTrue:[
+ newCollection at:index put:(aBlock value:(self at:index)).
+ index := index + 1
+ ].
+ ^ newCollection
+!
+
+select:aBlock
+ "evaluate the argument, aBlock for every element in the collection
+ and return a collection of all elements for which the block return
+ true"
+
+ |element newColl
+ index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }" |
+
+ length := self size.
+ newColl := OrderedCollection new:length.
+ index := 1.
+ [index <= length] whileTrue:[
+ element := self at:index.
+ (aBlock value:element) ifTrue:[
+ newColl add:element
+ ].
+ index := index + 1
+ ].
+ ^ newColl
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SequenceableCollection.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,955 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#SequenceableCollection
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Abstract'
+!
+
+SequenceableCollection comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+SequenceableCollections have ordered elements which can be accessed via
+an index. SequenceableCollection is an abstract class - there are no
+instances of it in the system.
+
+%W% %E%
+
+written spring 89 by claus
+'!
+
+!SequenceableCollection class methodsFor:'instance creation'!
+
+new:size withAll:element
+ "return a new Collection of size, where all elements are
+ initialized to element"
+
+ |newCollection|
+
+ newCollection := self new:size.
+ newCollection atAllPut:element.
+ ^ newCollection
+! !
+
+!SequenceableCollection methodsFor:'accessing'!
+
+first
+ "return the first element"
+
+ ^ self at:1
+!
+
+last
+ "return the last element"
+
+ ^ self at:(self size)
+! !
+
+!SequenceableCollection methodsFor:'comparing'!
+
+= aCollection
+ "return true if the receiver and aCollection represent collections
+ with equal contents."
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ (aCollection == self) ifTrue:[^true].
+ (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].
+
+ stop := self size.
+ stop == (aCollection size) ifFalse:[^false].
+ index := 1.
+ [index <= stop] whileTrue:[
+ (self at:index) = (aCollection at:index) ifFalse:[^false].
+ index := index + 1
+ ].
+ ^ true
+!
+
+startsWith:aCollection
+ "return true, if the receivers first elements match those
+ of aCollection"
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ (aCollection == self) ifTrue:[^true].
+ (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].
+
+ stop := aCollection size.
+ stop > self size ifTrue:[^false].
+
+ index := 1.
+ [index <= stop] whileTrue:[
+ (self at:index) = (aCollection at:index) ifFalse:[^false].
+ index := index + 1
+ ].
+ ^ true
+
+ "'abcde' startsWith:#($a $b $c)"
+ "#[1 2 3 4] startsWith:#(1 2 3)"
+ "#(1 2 3 4) asOrderedCollection startsWith:#(1 2 3)"
+!
+
+endsWith:aCollection
+ "return true, if the receivers last elements match those
+ of aCollection"
+
+ |index1 "{ Class: SmallInteger }"
+ index2 "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ (aCollection == self) ifTrue:[^true].
+ (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].
+
+ stop := aCollection size.
+ stop > self size ifTrue:[^false].
+
+ index1 := self size.
+ index2 := aCollection size.
+ [index2 > 0] whileTrue:[
+ (self at:index1) = (aCollection at:index2) ifFalse:[^false].
+ index1 := index1 - 1.
+ index2 := index2 - 1
+ ].
+ ^ true
+
+ "'abcde' endsWith:#($d $e)"
+ "#[1 2 3 4] endsWith:#(3 4)"
+ "#(1 2 3 4) asOrderedCollection endsWith:#(3 4)"
+! !
+
+!SequenceableCollection methodsFor:'testing'!
+
+size
+ "return the number of elements in the collection.
+ concrete implementations must define this"
+
+ ^ self subclassResponsibility
+! !
+
+!SequenceableCollection methodsFor:'copying'!
+
+, aCollection
+ "return a new collection formed from concatenating the receiver with
+ the argument"
+
+ |newCollection
+ mySize "{ Class: SmallInteger }"
+ newSize "{ Class: SmallInteger }"
+ otherSize "{ Class: SmallInteger }"
+ dstIndex "{ Class: SmallInteger }"|
+
+ mySize := self size.
+ otherSize := aCollection size.
+ newSize := mySize + otherSize.
+ newCollection := self species new:newSize.
+
+ newCollection replaceFrom:1 to:mySize with:self startingAt:1.
+ dstIndex := mySize + 1.
+ (aCollection isKindOf:SequenceableCollection) ifTrue:[
+ "yes, aCollection has indexed elements"
+ newCollection replaceFrom:dstIndex to:newSize
+ with:aCollection startingAt:1.
+ ^ newCollection
+ ] ifFalse:[
+ "no, enumerate aCollection"
+ aCollection do:[:element |
+ newCollection at:dstIndex put:element.
+ dstIndex := dstIndex + 1
+ ]
+ ].
+ ^ newCollection
+!
+
+copyWith:newElement
+ "return a new collection consisting of receivers elements
+ plus the argument"
+
+ |newCollection mySize newSize|
+
+ mySize := self size.
+ newSize := mySize + 1.
+ newCollection := self species new:newSize.
+ newCollection replaceFrom:1 to:mySize with:self startingAt:1.
+ newCollection at:newSize put:newElement.
+ ^newCollection
+!
+
+copyWithout:anElement
+ "return a new collection consisting of receivers elements
+ without anElement (if it was present)"
+
+ |newCollection skipIndex
+ dstIndex "{ Class: SmallInteger }"
+ index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ skipIndex := self indexOf:anElement startingAt:1.
+ (skipIndex == 0) ifTrue:[^ self copy].
+ stop := self size.
+ newCollection := self class new:(stop - 1).
+ dstIndex := 1.
+ index := 1.
+ [index <= stop] whileTrue:[
+ (index ~~ skipIndex) ifTrue:[
+ newCollection at:dstIndex put:(self at:index).
+ dstIndex := dstIndex + 1
+ ].
+ index := index + 1
+ ].
+ ^ newCollection
+!
+
+copyFrom:start to:stop
+ "return a new collection consisting of receivers elements
+ between start and stop"
+
+ |newCollection newSize|
+
+ newSize := stop - start + 1.
+ newCollection := self class new:newSize.
+ newCollection replaceFrom:1 to:newSize with:self startingAt:start.
+ ^ newCollection
+!
+
+copyFrom:start
+ "return a new collection consisting of receivers elements
+ from start to the end of the collection"
+
+ ^ self copyFrom:start to:(self size)
+!
+
+copyTo:stop
+ "return a new collection consisting of receivers elements
+ from 1 up to index stop"
+
+ ^ self copyFrom:1 to:stop
+!
+
+copyWithoutIndex:omitIndex
+ "return a new collection consisting of receivers elements
+ without the argument stored at omitIndex"
+
+ |copy|
+
+ copy := self class new:(self size - 1).
+ copy replaceFrom:1 to:(omitIndex - 1) with:self startingAt:1.
+ copy replaceFrom:omitIndex to:(copy size)
+ with:self startingAt:(omitIndex + 1).
+ ^ copy
+! !
+
+!SequenceableCollection methodsFor:'filling and replacing'!
+
+from:index1 to:index2 put:anObject
+ "replace the elements from index1 to index2 of the collection
+ by the argument, anObject"
+
+ |index "{ Class: SmallInteger }"
+ end "{ Class: SmallInteger }"|
+
+ index := index1.
+ end := index2.
+ [index <= end] whileTrue:[
+ self at:index put:anObject.
+ index := index + 1
+ ]
+!
+
+atAllPut:anObject
+ "replace all elements of the collection by the argument, anObject"
+
+ self from:1 to:(self size) put:anObject
+!
+
+atAll:indexCollection put:anObject
+ "put anObject into all indexes from indexCollection in the receiver"
+
+ indexCollection do:[:index | self at:index put:anObject]
+
+ "(Array new:10) atAll:(1 to:5) put:0"
+ "(Array new:10) atAll:#(1 5 6 9) put:0"
+!
+
+replaceAll:oldObject by:newObject
+ "replace all oldObjects by newObject in the receiver"
+
+ 1 to:self size do:[:index |
+ (self at:index) = oldObject ifTrue:[
+ self at:index put:newObject
+ ]
+ ]
+!
+
+replaceFrom:start with:replacementCollection
+ "replace elements starting at start with elements
+ taken from replacementCollection (starting at 1)"
+
+ ^ self replaceFrom:start
+ to:(start + replacementCollection size - 1)
+ with:replacementCollection
+ startingAt:1
+!
+
+replaceFrom:start to:stop with:replacementCollection
+ "replace elements between index start and stop with elements
+ taken from replacementCollection (starting at 1)"
+
+ ^ self replaceFrom:start
+ to:stop
+ with:replacementCollection
+ startingAt:1
+!
+
+replaceFrom:start to:stop with:replacementCollection startingAt:repStart
+ "replace elements between index start and stop with elements
+ taken from replacementCollection (starting at repStart)"
+
+ |srcIndex "{ Class: SmallInteger }"
+ dstIndex "{ Class: SmallInteger }"
+ end "{ Class: SmallInteger }" |
+
+ (replacementCollection == self) ifTrue:[
+ (repStart < start) ifTrue:[
+ " must do reverse copy "
+ srcIndex := repStart + (stop - start).
+ dstIndex := stop.
+ end := start.
+ [dstIndex >= end] whileTrue:[
+ self at:dstIndex put:(replacementCollection at:srcIndex).
+ srcIndex := srcIndex - 1.
+ dstIndex := dstIndex - 1
+ ].
+ ^ self
+ ]
+ ].
+
+ srcIndex := repStart.
+ dstIndex := start.
+ end := stop.
+ [dstIndex <= end] whileTrue:[
+ self at:dstIndex put:(replacementCollection at:srcIndex).
+ srcIndex := srcIndex + 1.
+ dstIndex := dstIndex + 1
+ ]
+!
+
+withCRs
+ "return a new collection consisting of receivers elements
+ with all \-characters replaced by cr-characters"
+
+ |newCollection
+ size "{ Class: SmallInteger }" |
+
+ newCollection := self copy.
+ size := self size.
+ 1 to:size do:[:index |
+ ((self at:index) == $\) ifTrue:[
+ newCollection at:index put:(Character cr)
+ ]
+ ].
+ ^ newCollection
+!
+
+withoutCRs
+ "return a new collection consisting of receivers elements
+ with all cr-characters replaced by \-characters"
+
+ |newCollection
+ size "{ Class: SmallInteger }" |
+
+ newCollection := self copy.
+ size := self size.
+ 1 to:size do:[:index|
+ ((self at:index) == Character cr) ifTrue:[
+ newCollection at:index put:$\
+ ]
+ ].
+ ^ newCollection
+! !
+
+!SequenceableCollection methodsFor:'adding & removing'!
+
+addFirst:anElement
+ "prepend the argument, anElement to the collection"
+
+ |newSize|
+
+ newSize := self size + 1.
+ self grow:newSize.
+ self replaceFrom:2 to:newSize with:self startingAt:1.
+ self at:1 put:anElement
+!
+
+add:anElement
+ "append the argument, anElement to the collection"
+
+ |newSize|
+
+ newSize := self size + 1.
+ self grow:newSize.
+ self at:newSize put:anElement
+!
+
+add:anElement beforeIndex:index
+ "insert the first argument, anObject into the collection before slot index"
+
+ |newSize|
+
+ newSize := self size + 1.
+ self grow:newSize.
+ self replaceFrom:index + 1 to:newSize with:self startingAt:index.
+ self at:index put:anElement
+!
+
+remove:anElement ifAbsent:aBlock
+ "search for anElement and, if present remove it; if not present
+ return the value of evaluating aBlock"
+
+ |any
+ dstIndex "{ Class: SmallInteger }"
+ sz "{ Class: SmallInteger }"|
+
+ dstIndex := 1.
+ any := false.
+ sz := self size.
+ 1 to:sz do:[:srcIndex |
+ (anElement = (self at:srcIndex)) ifTrue:[
+ any := true
+ ] ifFalse:[
+ (dstIndex ~~ srcIndex) ifTrue:[
+ self at:dstIndex put:(self at:srcIndex)
+ ].
+ dstIndex := dstIndex + 1
+ ]
+ ].
+ any ifTrue:[
+ self grow:dstIndex - 1
+ ] ifFalse:[
+ aBlock value
+ ]
+!
+
+removeFromIndex:startIndex toIndex:endIndex
+ "remove the elements stored at indexes between startIndex and endIndex"
+
+ |newSize|
+
+ newSize := self size - endIndex + startIndex - 1.
+ self replaceFrom:startIndex to:newSize with:self startingAt:(endIndex + 1).
+ self grow:newSize
+!
+
+removeIndex:index
+ "remove the argument stored at index"
+
+ self removeFromIndex:index toIndex:index
+! !
+
+!SequenceableCollection methodsFor:'searching'!
+
+detect:aBlock ifNone:exceptionBlock
+ "find the first element, for which evaluation of the argument, aBlock
+ return true; if none does so, return the evaluation of exceptionBlock
+
+ reimplemented here for speed"
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }"
+ element|
+
+ stop := self size.
+ index := 1.
+ [index <= stop] whileTrue:[
+ element := self at:index.
+ (aBlock value:element) ifTrue:[
+ ^ element
+ ].
+ index := index + 1
+ ].
+ ^ exceptionBlock value
+!
+
+indexOf:anElement
+ "search the collection for anElement;
+ if found, return the index otherwise return 0.
+ The comparison is done using = (i.e. equality test)."
+
+ ^ self indexOf:anElement startingAt:1
+!
+
+indexOf:anElement ifAbsent:exceptionBlock
+ "search the collection for anElement;
+ if found, return the index otherwise return the value of the
+ exceptionBlock.
+ The comparison is done using = (i.e. equality test)."
+
+ |index|
+
+ index := self indexOf:anElement startingAt:1.
+ (index == 0) ifTrue:[^ exceptionBlock value].
+ ^ index
+!
+
+indexOf:anElement startingAt:start
+ "search the collection for anElement staring search at index start;
+ if found, return the index otherwise return 0.
+ The comparison is done using = (i.e. equality test)."
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ index := start.
+ stop := self size.
+ [index <= stop] whileTrue:[
+ anElement = (self at:index) ifTrue:[^ index].
+ index := index + 1
+ ].
+ ^ 0
+!
+
+indexOf:anElement startingAt:start ifAbsent:exceptionBlock
+ "search the collection for anElement starting search at start;
+ if found, return the index otherwise return the value of the
+ exceptionBlock.
+ The comparison is done using = (i.e. equality test)."
+
+ |index|
+
+ index := self indexOf:anElement startingAt:start.
+ (index == 0) ifTrue:[^ exceptionBlock value].
+ ^ index
+!
+
+identityIndexOf:anElement
+ "search the collection for anElement using identity compare (i.e. ==);
+ if found, return the index otherwise return 0."
+
+ ^ self identityIndexOf:anElement startingAt:1
+!
+
+identityIndexOf:anElement ifAbsent:exceptionBlock
+ "search the collection for anElement using identity compare (i.e. ==);
+ if found, return the index otherwise return the value of the
+ exceptionBlock."
+
+ |index|
+
+ index := self identityIndexOf:anElement startingAt:1.
+ (index == 0) ifTrue:[^ exceptionBlock value].
+ ^ index
+!
+
+identityIndexOf:anElement startingAt:start
+ "search the collection for anElement staring search at index start
+ using identity compare (i.e. ==);
+ if found, return the index otherwise return 0."
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ index := start.
+ stop := self size.
+ [index <= stop] whileTrue:[
+ anElement == (self at:index) ifTrue:[^ index].
+ index := index + 1
+ ].
+ ^ 0
+!
+
+identityIndexOf:anElement startingAt:start ifAbsent:exceptionBlock
+ "search the collection for anElement starting search at start;
+ if found, return the index otherwise return the value of the
+ exceptionBlock.
+ This one searches for identical objects (i.e. ==)."
+
+ |index|
+
+ index := self identityIndexOf:anElement startingAt:start.
+ (index == 0) ifTrue:[^ exceptionBlock value].
+ ^ index
+!
+
+findFirst:aBlock
+ "find the first element, for which evaluation of the argument, aBlock
+ return true; return its index or 0 if none detected."
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ stop := self size.
+ index := 1.
+ [index <= stop] whileTrue:[
+ (aBlock value:(self at:index)) ifTrue:[^ index].
+ index := index + 1
+ ].
+ ^ 0
+
+ "#(1 2 3 4 5 6) findFirst:[:x | (x > 3) and:[x even]]"
+!
+
+includes:anElement
+ "return true if the collection contains anElement; false otherwise.
+ Comparison is done using equality compare (i.e. =)."
+
+ ((self indexOf:anElement startingAt:1) == 0) ifTrue:[^ false].
+ ^ true
+! !
+
+!SequenceableCollection methodsFor:'sorting & reordering'!
+
+reverse
+ "reverse the order of the arguments inplace"
+
+ |lowIndex "{ Class: SmallInteger }"
+ hiIndex "{ Class: SmallInteger }"
+ t|
+
+ hiIndex := self size.
+ lowIndex := 1.
+ [lowIndex < hiIndex] whileTrue:[
+ t := self at:lowIndex.
+ self at:lowIndex put:(self at:hiIndex).
+ self at:hiIndex put:t.
+ lowIndex := lowIndex + 1.
+ hiIndex := hiIndex - 1
+ ]
+ "#(4 5 6 7 7) reverse"
+!
+
+quickSortFrom:begin to:end
+ "actual quicksort worker for sort-message"
+
+ |b "{ Class: SmallInteger }"
+ e "{ Class: SmallInteger }"
+ middleElement temp |
+
+ b := begin.
+ e := end.
+ middleElement := self at:((b + e) // 2).
+
+ [b < e] whileTrue:[
+ [b < end and:[(self at:b) < middleElement]] whileTrue:[b := b + 1].
+ [e > begin and:[middleElement < (self at:e)]] whileTrue:[e := e - 1].
+
+ (b <= e) ifTrue:[
+ (b == e) ifFalse:[
+ temp := self at:b.
+ self at:b put:(self at:e).
+ self at:e put:temp
+ ].
+ b := b + 1.
+ e := e - 1
+ ]
+ ].
+ (begin < e) ifTrue:[self quickSortFrom:begin to:e].
+ (b < end) ifTrue:[self quickSortFrom:b to:end]
+!
+
+quickSortFrom:begin to:end with:aCollection
+ "actual quicksort worker for sortWith-message"
+
+ |b "{ Class: SmallInteger }"
+ e "{ Class: SmallInteger }"
+ middleElement temp |
+
+ b := begin.
+ e := end.
+ middleElement := self at:((b + e) // 2).
+
+ [b < e] whileTrue:[
+ [b < end and:[(self at:b) < middleElement]] whileTrue:[b := b + 1].
+ [e > begin and:[middleElement < (self at:e)]] whileTrue:[e := e - 1].
+
+ (b <= e) ifTrue:[
+ (b == e) ifFalse:[
+ temp := self at:b.
+ self at:b put:(self at:e).
+ self at:e put:temp.
+ temp := aCollection at:b.
+ aCollection at:b put:(aCollection at:e).
+ aCollection at:e put:temp
+ ].
+ b := b + 1.
+ e := e - 1
+ ]
+ ].
+ (begin < e) ifTrue:[self quickSortFrom:begin to:e with:aCollection].
+ (b < end) ifTrue:[self quickSortFrom:b to:end with:aCollection]
+!
+
+quickSortFrom:begin to:end sortBlock:sortBlock
+ "actual quicksort worker for sort:-message"
+
+ |b "{ Class: SmallInteger }"
+ e "{ Class: SmallInteger }"
+ middleElement temp |
+
+ b := begin.
+ e := end.
+ middleElement := self at:((b + e) // 2).
+
+ [b < e] whileTrue:[
+ [b < end and:[sortBlock value:(self at:b) value:middleElement]] whileTrue:[b := b + 1].
+ [e > begin and:[sortBlock value:middleElement value:(self at:e)]] whileTrue:[e := e - 1].
+
+ (b <= e) ifTrue:[
+ (b == e) ifFalse:[
+ temp := self at:b.
+ self at:b put:(self at:e).
+ self at:e put:temp
+ ].
+ b := b + 1.
+ e := e - 1
+ ]
+ ].
+ (begin < e) ifTrue:[self quickSortFrom:begin to:e sortBlock:sortBlock].
+ (b < end) ifTrue:[self quickSortFrom:b to:end sortBlock:sortBlock]
+!
+
+quickSortFrom:begin to:end sortBlock:sortBlock with:aCollection
+ "actual quicksort worker for sort:with:-message"
+
+ |b "{ Class: SmallInteger }"
+ e "{ Class: SmallInteger }"
+ middleElement temp |
+
+ b := begin.
+ e := end.
+ middleElement := self at:((b + e) // 2).
+
+ [b < e] whileTrue:[
+ [b < end and:[sortBlock value:(self at:b) value:middleElement]] whileTrue:[b := b + 1].
+ [e > begin and:[sortBlock value:middleElement value:(self at:e)]] whileTrue:[e := e - 1].
+
+ (b <= e) ifTrue:[
+ (b == e) ifFalse:[
+ temp := self at:b.
+ self at:b put:(self at:e).
+ self at:e put:temp.
+ temp := aCollection at:b.
+ aCollection at:b put:(aCollection at:e).
+ aCollection at:e put:temp
+ ].
+ b := b + 1.
+ e := e - 1
+ ]
+ ].
+ (begin < e) ifTrue:[self quickSortFrom:begin to:e sortBlock:sortBlock with:aCollection].
+ (b < end) ifTrue:[self quickSortFrom:b to:end sortBlock:sortBlock with:aCollection]
+!
+
+bubbleSort
+ "sort the collection inplace using bubbleSort (sloooow)
+ - this one makes only sense to sort after inserting an element into
+ an already sorted collection (if at all)"
+
+ |index "{ Class: SmallInteger }"
+ index2 "{ Class: SmallInteger }"
+ end "{ Class: SmallInteger }"
+ smallest smallestIndex thisOne|
+
+ end := self size.
+ index := 1.
+ [index <= end] whileTrue:[
+ smallest := self at:index.
+ smallestIndex := index.
+ index2 := index + 1.
+ [index2 <= end] whileTrue:[
+ (self at:index2) < smallest ifTrue:[
+ smallestIndex := index2.
+ smallest := self at:index2
+ ].
+ index2 := index2 + 1
+ ].
+ (smallestIndex ~~ index) ifTrue:[
+ thisOne := self at:index.
+ self at:index put:smallest.
+ self at:smallestIndex put:thisOne
+ ].
+ index := index + 1
+ ]
+
+ "#(1 16 7 98 3 19 4 0) bubbleSort"
+!
+
+sort
+ "sort the collection inplace. The elements are compared using
+ > and < i.e. they should offer a magnitude-like protocol."
+ |sz|
+
+ sz := self size.
+ (sz > 1) ifTrue:[
+ self quickSortFrom:1 to:sz
+ ]
+
+ "#(1 16 7 98 3 19 4 0) sort"
+!
+
+sortWith:aCollection
+ "sort the receiver collection inplace, also sort aCollection with it.
+ Use, when you have a key collection to sort another collection with."
+
+ |sz|
+
+ sz := self size.
+ (sz > 1) ifTrue:[
+ self quickSortFrom:1 to:sz with:aCollection
+ ]
+
+ "|c1 c2|
+ c1 := #(1 16 7 9).
+ c2 := #('one' 'sixteen' 'seven' 'nine').
+ c1 sortWith:c2.
+ c1 printNewline.
+ c2 printNewline"
+!
+
+sort:sortBlock
+ "sort the collection inplace using the 2-arg block sortBlock
+ for comparison. This allows any sort criteria to be implemented."
+
+ |sz|
+
+ sz := self size.
+ (sz > 1) ifTrue:[
+ self quickSortFrom:1 to:sz sortBlock:sortBlock
+ ]
+
+ "#(1 16 7 98 3 19 4 0) sort:[:a :b | a < b]"
+ "#(1 16 7 98 3 19 4 0) sort:[:a :b | a > b]"
+!
+
+sort:sortBlock with:aCollection
+ "sort the collection inplace using the 2-arg block sortBlock
+ for comparison. Also reorder the elements in aCollection"
+
+ |sz|
+
+ sz := self size.
+ (sz > 1) ifTrue:[
+ self quickSortFrom:1 to:sz sortBlock:sortBlock with:aCollection
+ ]
+
+ "|c1 c2|
+ c1 := #(1 16 7 9).
+ c2 := #('one' 'sixteen' 'seven' 'nine').
+ c1 sort:[:a :b | a > b] with:c2.
+ c1 printNewline.
+ c2 printNewline"
+! !
+
+!SequenceableCollection methodsFor:'enumerating'!
+
+do:aBlock
+ "evaluate the argument, aBlock for every element in the collection."
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"|
+
+ index := 1.
+ length := self size.
+ [index <= length] whileTrue:[
+ aBlock value:(self at:index).
+ index := index + 1
+ ]
+!
+
+from:index1 to:index2 do:aBlock
+ "evaluate the argument, aBlock for the elements with index index1 to
+ index2 in the collection"
+
+ |index "{ Class:SmallInteger }"
+ stop "{ Class:SmallInteger }" |
+
+ index := index1.
+ stop := index2.
+ [index <= stop] whileTrue:[
+ aBlock value:(self at:index).
+ index := index + 1
+ ]
+!
+
+with:aCollection do:aBlock
+ "evaluate the argument, aBlock for successive elements from
+ each of the two collections self and aCollection.
+ aBlock must be a two-argument block"
+
+ |index "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }" |
+
+ index := 1.
+ stop := self size.
+ [index <= stop] whileTrue:[
+ aBlock value:(self at:index) value:(aCollection at:index).
+ index := index + 1
+ ]
+!
+
+reverseDo:aBlock
+ "evaluate the argument, aBlock for every element in the collection
+ in reverse order"
+
+ |index "{ Class:SmallInteger }" |
+
+ index := self size.
+ [index > 0] whileTrue:[
+ aBlock value:(self at:index).
+ index := index - 1
+ ]
+!
+
+collect:aBlock
+ "evaluate the argument, aBlock for every element in the collection
+ and return a collection of the results"
+
+ |newCollection
+ index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }" |
+
+ length := self size.
+ newCollection := self species new:length.
+ index := 1.
+ [index <= length] whileTrue:[
+ newCollection at:index put:(aBlock value:(self at:index)).
+ index := index + 1
+ ].
+ ^ newCollection
+!
+
+select:aBlock
+ "evaluate the argument, aBlock for every element in the collection
+ and return a collection of all elements for which the block return
+ true"
+
+ |element newColl
+ index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }" |
+
+ length := self size.
+ newColl := OrderedCollection new:length.
+ index := 1.
+ [index <= length] whileTrue:[
+ element := self at:index.
+ (aBlock value:element) ifTrue:[
+ newColl add:element
+ ].
+ index := index + 1
+ ].
+ ^ newColl
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Set.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,326 @@
+"
+ COPYRIGHT (c) 1991-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#Set
+ instanceVariableNames:'tally contentsArray'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Unordered'
+!
+
+Set comment:'
+
+COPYRIGHT (c) 1991-93 by Claus Gittinger
+ All Rights Reserved
+
+a Set is a collection where each element occurs at most once.
+
+%W% %E%
+written jun 91 by claus
+jan 93 claus: changed to use hashing
+'!
+
+!Set class methodsFor:'instance creation'!
+
+new
+ "return a new empty Set"
+
+ ^ self new:7
+!
+
+new:anInteger
+ "return a new empty Set with space for anInteger elements"
+
+ ^ self basicNew setTally:anInteger
+! !
+
+!Set methodsFor:'private'!
+
+goodSizeFor:arg
+ "return a good array size for the given argument.
+ Returns the next prime after arg"
+
+ arg <= 7 ifTrue:[^ 7].
+ arg <= 16384 ifTrue:[
+ "2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384"
+ ^ #(7 7 11 17 37 67 131 257 521 1031 2053 4099 8209 16411) at:(arg highBit)
+ ].
+ ^ arg bitOr:1
+!
+
+setTally:count
+ "initialize the contents array (for at least count slots)
+ and set tally to zero.
+ The size is increased to the next prime for better hashing behavior."
+
+ contentsArray := Array new:(self goodSizeFor:count).
+ tally := 0
+!
+
+find:key ifAbsent:aBlock
+ "Look for the key in the receiver. If it is found, return
+ the index of the slot containing the key, otherwise
+ return the value of evaluating aBlock."
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex "{ Class:SmallInteger }"
+ probe|
+
+ length := contentsArray basicSize.
+ startIndex := key hash \\ length + 1.
+ index := startIndex.
+
+ [true] whileTrue:[
+ probe := (contentsArray basicAt:index).
+ key = probe ifTrue:[^ index].
+ probe isNil ifTrue:[^ aBlock value].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ index == startIndex ifTrue:[^ aBlock value].
+ ]
+!
+
+findElementOrNil:key
+ "Look for the key in the receiver. If it is found, return
+ the index of the slot containing the key, otherwise
+ return the index of the first unused slot. Grow the receiver,
+ if key was not found, and no unused slots where present"
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"
+ startIndex "{ Class:SmallInteger }"
+ probe|
+
+ length := contentsArray basicSize.
+ startIndex := key hash \\ length + 1.
+ index := startIndex.
+
+ [true] whileTrue:[
+ probe := contentsArray basicAt:index.
+ (probe isNil or: [key = probe]) ifTrue:[^ index].
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ index == startIndex ifTrue:[^ self grow findElementOrNil:key].
+ ]
+!
+
+findNil:key
+ "Look for the next slot usable for key. This method assumes that
+ key is not already in the receiver - used only while growing/rehashing"
+
+ |index "{ Class:SmallInteger }"
+ length "{ Class:SmallInteger }"|
+
+ length := contentsArray basicSize.
+ index := key hash \\ length + 1.
+
+ [(contentsArray basicAt:index) notNil] whileTrue:[
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1
+ ].
+ "notice: no check for no nil found - we must find one since
+ this is only called after growing"
+ ].
+ ^ index
+!
+
+grow
+ "change the number of element slots of the collection to a useful
+ new size"
+
+ self grow:(contentsArray basicSize * 2)
+!
+
+grow:newSize
+ "change the number of element slots of the collection - to do this,
+ we have to rehash (which is done by re-adding all elements to a new
+ empty set)."
+
+ |oldElements oldSize
+ srcIndex "{ Class:SmallInteger }"|
+
+ oldElements := contentsArray.
+ oldSize := tally.
+ self setTally:newSize.
+
+ srcIndex := 1.
+ oldElements do:[:elem |
+ elem notNil ifTrue:[
+ "cannot be already there"
+ contentsArray basicAt:(self findNil:elem) put:elem
+ ].
+ srcIndex := srcIndex + 1
+ ].
+ tally := oldSize
+!
+
+rehash
+ "rehash is done by re-adding all elements to a new empty set."
+
+ | oldArray |
+
+ oldArray := contentsArray.
+ contentsArray := Array new:(contentsArray size).
+ oldArray do:[:element |
+ element notNil ifTrue:[
+ "cannot be already there"
+ contentsArray basicAt:(self findNil:element) put:element
+ ].
+ ]
+!
+
+rehashFrom:startIndex
+ "rehash elements starting at index - after a remove"
+
+ |element i length
+ index "{ Class:SmallInteger }" |
+
+ length := contentsArray basicSize.
+ index := startIndex.
+ element := contentsArray basicAt:index.
+ [element notNil] whileTrue:[
+ i := self findNil:element.
+ i == index ifTrue:[
+ ^ self
+ ].
+ contentsArray basicAt:i put:element.
+ contentsArray basicAt:index put:nil.
+
+ index == length ifTrue:[
+ index := 1
+ ] ifFalse:[
+ index := index + 1.
+ ].
+ element := contentsArray basicAt:index.
+ ]
+! !
+
+!Set methodsFor:'accessing'!
+
+at:index
+ "report an error: at: is not allowed for Sets"
+
+ ^ self errorNotKeyed
+!
+
+at:index put:anObject
+ "report an error: at:put: is not allowed for Sets"
+
+ ^ self errorNotKeyed
+! !
+
+!Set methodsFor:'testing'!
+
+size
+ "return the number of set elements"
+
+ ^ tally
+!
+
+includes:anObject
+ "return true if the argument anObject is in the receiver"
+
+ ^ (self find:anObject ifAbsent:[0]) ~~ 0
+!
+
+isEmpty
+ "return true if the receiver is empty"
+
+ ^ tally == 0
+!
+
+occurrencesOf:anObject
+ "return the number of occurrences of anObject in the receiver"
+
+ (self find:anObject ifAbsent:[0]) == 0 ifTrue:[^ 0].
+ ^ 1
+!
+
+isFixedSize
+ "return true if the receiver cannot grow - this will vanish once
+ Arrays and Strings learn how to grow ..."
+
+ ^ false
+! !
+
+!Set methodsFor:'adding & removing'!
+
+add:anObject
+ "add the argument, anObject to the receiver"
+
+ |index|
+
+ anObject notNil ifTrue:[
+ index := self findElementOrNil:anObject.
+ (contentsArray basicAt:index) isNil ifTrue:[
+ contentsArray basicAt:index put:anObject.
+ tally := tally + 1.
+
+ "grow if filled more than 70% "
+ tally > (contentsArray basicSize * 7 // 10) ifTrue:[
+ self grow
+ ]
+ ]
+ ].
+ ^ anObject
+!
+
+remove:oldObject ifAbsent:exceptionBlock
+ "remove oldObject from the collection and return it.
+ If it was not in the collection return the value of exceptionBlock."
+
+ |index next|
+
+ index := self find:oldObject ifAbsent:[^ exceptionBlock value].
+ contentsArray basicAt:index put:nil.
+ tally := tally - 1.
+ tally == 0 ifTrue:[
+ contentsArray := Array new:(self goodSizeFor:0).
+ ] ifFalse:[
+ index == contentsArray basicSize ifTrue:[
+ next := 1
+ ] ifFalse:[
+ next := index + 1.
+ ].
+ "this check is redundant - is also done in rehashFrom:,
+ however, since there is some probability that the next
+ element is nil, this saves a send sometimes
+ "
+ (contentsArray basicAt:next) notNil ifTrue:[
+ self rehashFrom:next.
+ ]
+ ].
+ ^ oldObject
+! !
+
+!Set methodsFor:'enumerating'!
+
+do:aBlock
+ "perform the block for all members in the collection."
+
+ contentsArray do:[:each |
+ each notNil ifTrue:[
+ aBlock value:each
+ ]
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Signal.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,184 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Signal
+ instanceVariableNames:'mayProceed notifierString nameClass message'
+ classVariableNames:'NoHandlerSignal'
+ poolDictionaries:''
+ category:'Kernel-Exceptions'
+!
+
+Signal comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+Signal and Exception provide a framework for exception handling.
+A Signal object is usually defined somewhere up in the calling chain
+and associated with some abnormal event. When the event is raised
+(by Signal>>raise) the control will be either given to a debugger
+or - if a handler was defined - to the handler. The handler will
+get a description of what (and where) happened in an Exception object.
+
+This Signal implementation has been modeled after what some PD
+programs seem to expect - it may not be perfect currently.
+
+See samples in doc/coding.
+
+%W% %E%
+'!
+
+!Signal class methodsFor:'initialization'!
+
+initialize
+ NoHandlerSignal := (Signal new).
+ NoHandlerSignal mayProceed:true.
+ NoHandlerSignal notifierString:'no Handler'
+! !
+
+!Signal class methodsFor:'instance creation'!
+
+new
+ "return a new signal"
+
+ ^ self basicNew notifierString:'signal'
+! !
+
+!Signal class methodsFor:'signal access'!
+
+noHandlerSignal
+ ^ NoHandlerSignal
+! !
+
+!Signal methodsFor:'instance creation'!
+
+newSignalMayProceed:aBoolean
+ "create a new signal, using the receiver as a prototype"
+
+ ^ (self copy) mayProceed:aBoolean
+!
+
+newSignal
+ "create a new signal, using the receiver as a prototype"
+
+ ^ (self copy)
+! !
+
+!Signal methodsFor:'accessing'!
+
+nameClass:aClass message:aSelector
+ "I dont know what that is used for (yet)"
+
+ nameClass := aClass.
+ message := aSelector
+!
+
+mayProceed:aBoolean
+ "set/clear the signals ability to proceed"
+
+ mayProceed := aBoolean
+!
+
+notifierString:aString
+ "set the notifier string"
+
+ notifierString := aString
+!
+
+notifierString
+ "return the notifier string"
+
+ ^ notifierString
+! !
+
+!Signal methodsFor:'save evaluation'!
+
+handle:handleBlock do:aBlock
+ "evaluate the argument, aBlock.
+ If the receiver-signal is raised during evaluation,
+ evaluate the handleBlock passing it an Exception argument.
+ The handler may decide how to react to the signal by sending
+ a corresponding message to the exception (see there).
+ If the signal is not raised, return the value of evaluating
+ aBlock."
+
+ ^ aBlock value "the real logic is in raise/Exception"
+! !
+
+!Signal methodsFor:'raising'!
+
+raise
+ "raise a signal - create an Exception object
+ and call the handler with this as argument."
+
+ |ex|
+
+ ex := Exception new signal:self.
+ ex resumeBlock:[:value | ^ value].
+ self evaluateHandlerWith:ex.
+
+ "mmhh - no handler found (will eventually raise a noHandlerSignal)"
+ self error:('unhandled exception: ' , notifierString).
+ ^ nil
+!
+
+raiseRequestWith:aParameter
+ "raise a signal - create an Exception object with aParameter
+ and call the handler with this as argument."
+
+ |ex|
+
+ ex := Exception new signal:self.
+ ex parameter:aParameter.
+ ex resumeBlock:[:value | ^ value].
+ self evaluateHandlerWith:ex.
+
+ "mmhh - no handler found (will eventually raise a noHandlerSignal)"
+ self error:('unhandled exception: ' , notifierString).
+ ^ nil
+! !
+
+!Signal methodsFor:'private'!
+
+evaluateHandlerWith:anException
+ "search through the context-calling chain for a
+ handle:do: frame to the receiver or a SignalSet which includes
+ the receiver."
+
+ |con|
+
+ con := thisContext sender.
+ [con notNil] whileTrue:[
+ (con selector == #handle:do:) ifTrue:[
+ "
+ if this is the Signal>>handle:do: context
+ or a SignalSet>>handle:do: context with self in it,
+ call the handler
+ "
+ ((con receiver == self)
+ or:[(con receiver isMemberOf:SignalSet) and:[con receiver includes:self]]) ifTrue:[
+ "call the handler"
+ anException handlerContext:con.
+ self doCallHandler:(con args at:1) with:anException.
+ "if the handler rejects or falls through we arrive here"
+ "continue search for another handler"
+ ].
+ ].
+ con := con sender
+ ]
+!
+
+doCallHandler:aHandler with:ex
+ ex rejectBlock:[^ self].
+ aHandler value:ex.
+ "handler return - is just like a reject"
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SignalSet.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,72 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+IdentitySet subclass:#SignalSet
+ instanceVariableNames:'special'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Exceptions'
+!
+
+SignalSet comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+SignalSet allows catching of multiple signals. A SignalSet consists of
+a number of signals and also implements the handle:do: method just as
+signals do. However, any signal from the SignalSet will, if signalled, lead
+into the handler.
+For more detail, see comment in Signal and examples in doc/coding.
+
+%W% %E%
+'!
+
+!SignalSet class methodsFor:'instance creation'!
+
+anySignal
+ "return a new signalSet catching any signal"
+
+ ^ self basicNew special:#any
+! !
+
+!SignalSet methodsFor:'save evaluation'!
+
+handle:handleBlock do:aBlock
+ "evaluate the argument, aBlock.
+ If any of the signals in the receiver is raised during evaluation,
+ evaluate the handleBlock passing it an Exception argument.
+ The handler may decide how to react to the signal by sending
+ a corresponding message to the exception (see there).
+ If the signal is not raised, return the value of evaluating
+ aBlock."
+
+ ^ aBlock value "the real logic is in raise/Exception"
+! !
+
+!SignalSet methodsFor:'queries'!
+
+includes:aSignal
+ "return true, if the receiver contains the argument, aSignal.
+ The special any-Set includes every signal."
+
+ (special == #any) ifTrue:[
+ ^ aSignal isKindOf:Signal
+ ].
+ ^ super includes:aSignal
+! !
+
+!SignalSet methodsFor:'private'!
+
+special:how
+ special := how
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SmallInt.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1362 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Integer subclass:#SmallInteger
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Magnitude-Numbers'
+!
+
+SmallInteger comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+
+SmallIntegers are Integers in the range of +/- 2^30 (i.e. 31 bits).
+These are no real objects - they have no instances (not even storage !)
+and cannot be subclassed (sorry)
+
+The reason is to save both storage and runtime by not collecting
+SmallIntegers in the system. SmallInts are marked by having the TAG_INT
+bit set in contrast to Objects which have not. Since this knowledge is
+hardwired into the system (an there is no class-field stored with
+SmallIntegers) there can be no subclass of SmallInteger (sorry).
+'!
+
+!SmallInteger class methodsFor:'instance creation'!
+
+basicNew
+ "catch instance creation
+ - SmallIntegers cannot be created with new"
+
+ self error:'instances of SmallInteger cannot be created with new'
+!
+
+basicNew:size
+ "catch instance creation
+ - SmallIntegers cannot be created with new"
+
+ self error:'instances of SmallInteger cannot be created with new'
+! !
+
+!SmallInteger class methodsFor:'constants'!
+
+maxBits
+ "return the number of bits in instances of me"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(N_INT_BITS) );
+%}
+!
+
+maxBytes
+ "return the number of bytes in instances of me"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(N_INT_BITS / 8 + 1) );
+%}
+!
+
+minVal
+ "return the smallest Integer representable as SmallInteger"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(_MIN_INT) );
+%}
+!
+
+maxVal
+ "return the largest Integer representable as SmallInteger"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(_MAX_INT) );
+%}
+! !
+
+!SmallInteger methodsFor:'error catching'!
+
+at:index
+ "catch indexed access - report an error
+ defined here since at: in Object ommits the SmallInteger check"
+
+ self notIndexed
+!
+
+basicAt:index
+ "catch indexed access - report an error
+ defined here since basicAt: in Object ommits the SmallInteger check"
+
+ self notIndexed
+!
+
+at:index put:anObject
+ "catch indexed access - report an error
+ defined here since at:put: in Object ommits the SmallInteger check"
+
+ self notIndexed
+!
+
+basicAt:index put:anObject
+ "catch indexed access - report an error
+ defined here since basicAt:put: in Object ommits the SmallInteger check"
+
+ self notIndexed
+!
+
+size
+ "return the number of indexed instvars - SmallIntegers have none
+ defined here since size in Object ommits the SmallInteger check"
+
+ ^ 0
+!
+
+basicSize
+ "return the number of indexed instvars - SmallIntegers have none
+ defined here since basicSize in Object ommits the SmallInteger check"
+
+ ^ 0
+! !
+
+!SmallInteger methodsFor:'copying'!
+
+shallowCopy
+ "return a shallow copy of myself
+ - reimplemented here since numbers are unique"
+
+ ^ self
+!
+
+deepCopy
+ "return a deep copy of myself
+ - reimplemented here since numbers are unique"
+
+ ^ self
+! !
+
+!SmallInteger methodsFor:'comparing'!
+
+= aNumber
+ "return true, if the arguments value is equal to mine"
+
+%{ /* NOCONTEXT */
+
+ if (aNumber == self) {
+ RETURN ( true );
+ }
+ if (! _isNonNilObject(aNumber)) {
+ RETURN ( false );
+ }
+
+ if (_qClass(aNumber) == Float) {
+ RETURN ( ((double)_intVal(self) == _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ aNumber respondsToArithmetic ifFalse:[^ false].
+ ^ self retry:#= coercing:aNumber
+!
+
+~= aNumber
+ "return true, if the arguments value is not equal to mine"
+
+%{ /* NOCONTEXT */
+
+ if (aNumber == self) {
+ RETURN ( false );
+ }
+ if (! _isNonNilObject(aNumber)) {
+ RETURN ( true );
+ }
+
+ if (_qClass(aNumber) == Float) {
+ RETURN ( ((double)_intVal(self) == _floatVal(aNumber)) ? false : true );
+ }
+%}
+.
+ aNumber respondsToArithmetic ifFalse:[^ true].
+ ^ self retry:#~= coercing:aNumber
+!
+
+< aNumber
+ "return true, if the argument is greater than the receiver"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) < _intVal(aNumber)) ? true : false );
+#else
+ /* tag bit does not change ordering */
+ RETURN ( ((INT)self < (INT)aNumber) ? true : false );
+#endif
+ }
+ if (_isFloat(aNumber)) {
+ RETURN ( ((double)_intVal(self) < _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ aNumber lessFromInteger:self
+ "^ self retry:#< coercing:aNumber"
+!
+
+> aNumber
+ "return true, if the argument is less than the receiver"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) > _intVal(aNumber)) ? true : false );
+#else
+ /* tag bit does not change ordering */
+ RETURN ( ((INT)self > (INT)aNumber) ? true : false );
+#endif
+ }
+ if (_isFloat(aNumber)) {
+ RETURN ( ((double)_intVal(self) > _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#> coercing:aNumber
+!
+
+>= aNumber
+ "return true, if the argument is less or equal"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) >= _intVal(aNumber)) ? true : false );
+#else
+ /* tag bit does not change ordering */
+ RETURN ( ((INT)self >= (INT)aNumber) ? true : false );
+#endif
+ }
+ if (_isFloat(aNumber)) {
+ RETURN ( ((double)_intVal(self) >= _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#>= coercing:aNumber
+!
+
+<= aNumber
+ "return true, if the argument is greater or equal"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) <= _intVal(aNumber)) ? true : false );
+#else
+ /* tag bit does not change ordering */
+ RETURN ( ((INT)self <= (INT)aNumber) ? true : false );
+#endif
+ }
+ if (_isFloat(aNumber)) {
+ RETURN ( ((double)_intVal(self) <= _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#<= coercing:aNumber
+!
+
+identityHash
+ "return an integer useful for hashing on identity"
+
+ self > 0 ifTrue:[
+ ^ self + 8192
+ ].
+ ^ self negated + 8192
+!
+
+min:aNumber
+ "return the receiver or the argument, whichever is smaller"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+ if (_intVal(self) < _intVal(aNumber)) {
+#else
+ /* tag bit does not change ordering */
+ if ((INT)(self) < (INT)(aNumber)) {
+#endif
+ RETURN ( self );
+ }
+ RETURN ( aNumber );
+ }
+ if (_isFloat(aNumber)) {
+ if ( (double)_intVal(self) < _floatVal(aNumber) ) {
+ RETURN ( self );
+ }
+ RETURN ( aNumber );
+ }
+%}
+.
+ (self < aNumber) ifTrue:[^ self].
+ ^ aNumber
+!
+
+max:aNumber
+ "return the receiver or the argument, whichever is greater"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+ if (_intVal(self) > _intVal(aNumber)) {
+#else
+ /* tag bit does not change ordering */
+ if ((INT)(self) > (INT)(aNumber)) {
+#endif
+ RETURN ( self );
+ }
+ RETURN ( aNumber );
+ }
+ if (_isFloat(aNumber)) {
+ if ( (double)_intVal(self) > _floatVal(aNumber) ) {
+ RETURN ( self );
+ }
+ RETURN ( aNumber );
+ }
+%}
+.
+ (self > aNumber) ifTrue:[^ self].
+ ^ aNumber
+! !
+
+!SmallInteger methodsFor:'testing'!
+
+negative
+ "return true, if the receiver is less than zero
+ reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) < 0) ? true : false );
+#else
+ /* tag bit does not change sign */
+ RETURN ( ((INT)(self) < 0) ? true : false );
+#endif
+%}
+!
+
+positive
+ "return true, if the receiver is not negative
+ reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) >= 0) ? true : false );
+#else
+ /* tag bit does not change sign */
+ RETURN ( ((INT)(self) >= 0) ? true : false );
+#endif
+%}
+!
+
+strictlyPositive
+ "return true, if the receiver is greater than zero
+ reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) > 0) ? true : false );
+#else
+ /* tag bit does not change sign */
+ RETURN ( ((INT)(self) > 0) ? true : false );
+#endif
+%}
+!
+
+sign
+ "return the sign of the receiver
+ reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ INT val = _intVal(self);
+
+ if (val < 0) {
+ RETURN ( _MKSMALLINT(-1) );
+ }
+ if (val > 0) {
+ RETURN ( _MKSMALLINT(1) );
+ }
+ RETURN ( _MKSMALLINT(0) );
+%}
+!
+
+between:min and:max
+ "return true if the receiver is less than or equal to the argument max
+ and greater than or equal to the argument min.
+ - reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(min) && _isSmallInteger(max)) {
+ REGISTER INT selfVal;
+
+ selfVal = _intVal(self);
+ if (selfVal < _intVal(min)) {
+ RETURN ( false );
+ }
+ if (selfVal > _intVal(max)) {
+ RETURN ( false );
+ }
+ RETURN ( true );
+ }
+%}
+.
+ (self < min) ifTrue:[^ false].
+ (self > max) ifTrue:[^ false].
+ ^ true
+!
+
+even
+ "return true, if the receiver is even"
+
+%{ /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( ((INT)self & 1) ? false : true );
+#else
+ RETURN ( ((INT)self & ((INT)_MKSMALLINT(1) & ~TAG_INT)) ? false : true );
+#endif
+%}
+!
+
+odd
+ "return true, if the receiver is odd"
+
+%{ /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( ((INT)self & 1) ? true : false );
+#else
+ RETURN ( ((INT)self & ((INT)_MKSMALLINT(1) & ~TAG_INT)) ? true : false );
+#endif
+%}
+! !
+
+!SmallInteger methodsFor:'arithmetic'!
+
++ aNumber
+ "return the sum of the receivers value and the arguments value"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef _ADD_IO_IO
+ RETURN ( _ADD_IO_IO(self, aNumber) );
+#else
+ REGISTER INT sum;
+ extern OBJ _makeLarge();
+
+ sum = _intVal(self) + _intVal(aNumber);
+ if ((sum >= _MIN_INT) && (sum <= _MAX_INT)) {
+ RETURN ( _MKSMALLINT(sum) );
+ }
+ RETURN ( _makeLarge(sum) );
+#endif
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ extern char *newNextPtr, *newEndPtr;
+ OBJ newFloat;
+ double val;
+
+ val = _floatVal(aNumber);
+ _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+ _InstPtr(newFloat)->o_class = Float;
+ _FloatInstPtr(newFloat)->f_floatvalue = (double)(_intVal(self)) + val;
+ RETURN ( newFloat );
+ }
+%}
+.
+ ^ aNumber sumFromInteger:self
+!
+
+- aNumber
+ "return the difference of the receivers value and the arguments value"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef _SUB_IO_IO
+ RETURN ( _SUB_IO_IO(self, aNumber) );
+#else
+ REGISTER INT diff;
+ extern OBJ _makeLarge();
+
+ diff = _intVal(self) - _intVal(aNumber);
+ if ((diff >= _MIN_INT) && (diff <= _MAX_INT)) {
+ RETURN ( _MKSMALLINT(diff) );
+ }
+ RETURN ( _makeLarge(diff) );
+#endif
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ extern char *newNextPtr, *newEndPtr;
+ OBJ newFloat;
+ double val;
+
+ val = _floatVal(aNumber);
+ _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+ _InstPtr(newFloat)->o_class = Float;
+ _FloatInstPtr(newFloat)->f_floatvalue = (double)(_intVal(self)) - val;
+ RETURN ( newFloat );
+ }
+%}
+.
+ ^ aNumber differenceFromInteger:self
+!
+
+* aNumber
+ "return the product of the receivers value and the arguments value"
+
+ |aLarge|
+
+%{ /* NOCONTEXT */
+
+ REGISTER INT myValue, otherValue;
+ unsigned INT pHH, pHL, pLH, pLL;
+
+ if (_isSmallInteger(aNumber)) {
+ /* this is too slow:
+ * since most machines can do 32*32 to 64 bit multiply,
+ * (or at least 32*32 with Overflow check)
+ * its better to do it this way .. - need an assembler (inline) function here
+ */
+ myValue = _intVal(self);
+ if (myValue < 0) myValue = -myValue;
+ otherValue = _intVal(aNumber);
+ if (otherValue < 0) otherValue = -otherValue;
+#ifdef NOTDEF
+ if (! ((myValue & ~0x7FFF) || (otherValue & ~0x7FFF))) {
+#else
+ pHH = ((myValue >> 16) & 0xFFFF) * ((otherValue >> 16) & 0xFFFF);
+ pHL = ((myValue >> 16) & 0xFFFF) * (otherValue & 0xFFFF);
+ pLH = (myValue & 0xFFFF) * ((otherValue >> 16) & 0xFFFF);
+ pLL = (myValue & 0xFFFF) * (otherValue & 0xFFFF);
+ if (! (pHH || (pHL & 0xFFFFc000) || (pLH & 0xFFFFc000) || (pLL & 0xc0000000))) {
+#endif
+ RETURN ( _MKSMALLINT(_intVal(self) * _intVal(aNumber)) );
+ }
+ } else if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ extern char *newNextPtr, *newEndPtr;
+ OBJ newFloat;
+ double val;
+
+ val = _floatVal(aNumber);
+ _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+ _InstPtr(newFloat)->o_class = Float;
+ _FloatInstPtr(newFloat)->f_floatvalue = (double)(_intVal(self)) * val;
+ RETURN ( newFloat );
+ }
+%}
+.
+%{
+ extern OBJ LargeInteger, __mu, _value_;
+ static struct inlineCache val = _ILC1;
+ static struct inlineCache mu = _ILC1;
+
+ if (_isSmallInteger(aNumber)) {
+ /*
+ * non overflow case has already been checked
+ */
+#ifdef PASS_ARG_REF
+ aLarge = (*val.ilc_func)(LargeInteger, _value_, CON_COMMA nil, &val, &self);
+ RETURN ( (*mu.ilc_func)(aLarge, __mu, CON_COMMA nil, &mu, &aNumber) );
+#else
+ aLarge = (*val.ilc_func)(LargeInteger, _value_, CON_COMMA nil, &val, self);
+ RETURN ( (*mu.ilc_func)(aLarge, __mu, CON_COMMA nil, &mu, aNumber) );
+#endif
+ }
+%}
+.
+ ^ aNumber productFromInteger:self
+!
+
+/ aNumber
+ "return the quotient of the receivers value and the arguments value"
+
+%{ /* NOCONTEXT */
+
+ INT me, t, val;
+ double dval;
+
+ if (_isSmallInteger(aNumber)) {
+ val = _intVal(aNumber);
+ if (val != 0) {
+ me = _intVal(self);
+ t = me / val;
+#ifdef GOOD_OPTIMIZER
+ if (me % val) {
+#else
+ /* this is stupid - all I want is to look for a remainder ...
+ but most compilers are too stupid and generate an extra mod instr.
+ for "if (me % val)" even if most div instructions also compute
+ the remainder.
+ therefore I use a multiplication which is faster than a modulu
+ on most machines
+ */
+ if ((t * val) == me) {
+#endif
+ RETURN ( _MKSMALLINT(t) );
+ }
+/*
+ * now disabled - Fractions work
+ *
+ RETURN ( _MKFLOAT((double)_intVal(self) / (double)val, __context) );
+*/
+ }
+ } else {
+ if (_isFloat(aNumber)) {
+ dval = _floatVal(aNumber);
+ if (dval != 0.0) {
+ me = _intVal(self);
+ RETURN ( _MKFLOAT((double)me / dval COMMA_CON) );
+ }
+ }
+ }
+%}
+.
+ aNumber isInteger ifTrue:[
+ aNumber = 0 ifTrue:[
+ DivisionByZeroSignal raise.
+ ^ self
+ ].
+ ^ Fraction numerator:self denominator:aNumber
+ ].
+ ^ aNumber quotientFromInteger:self
+!
+
+// anInteger
+ "return the integer part of the quotient of the receivers value
+ and the arguments value"
+
+%{ /* NOCONTEXT */
+ INT val;
+
+ if (_isSmallInteger(anInteger)) {
+ val = _intVal(anInteger);
+ if (val != 0) {
+ RETURN ( _MKSMALLINT(_intVal(self) / val) );
+ }
+ }
+%}
+.
+ (anInteger = 0) ifTrue:[
+ DivisionByZeroSignal raise.
+ ^ self
+ ].
+ ^ self retry:#// coercing:anInteger
+!
+
+\\ anInteger
+ "return the integer rest of the receivers value
+ divided by the arguments value"
+
+%{ /* NOCONTEXT */
+ INT mySelf, val;
+
+ if (_isSmallInteger(anInteger)) {
+ mySelf = _intVal(self);
+ if (mySelf < 0) mySelf = -mySelf;
+ val = _intVal(anInteger);
+ if (val != 0) {
+ if (val < 0) {
+ RETURN ( _MKSMALLINT(-(mySelf % -val)) );
+ }
+ RETURN ( _MKSMALLINT(mySelf % val) );
+ }
+ }
+%}
+.
+ (anInteger = 0) ifTrue:[
+ DivisionByZeroSignal raise.
+ ^ self
+ ].
+ ^ self retry:#\\ coercing:anInteger
+!
+
+abs
+ "return the absolute value of the receiver
+ reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ INT val = _intVal(self);
+
+ if (val != _MIN_INT) {
+ RETURN ( (val < 0) ? _MKSMALLINT(-val) : self );
+ }
+%}
+.
+ "only reached for minVal"
+ ^ self negated
+!
+
+negated
+ "return the negative value of the receiver
+ reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ INT val = _intVal(self);
+
+ if (val != _MIN_INT) {
+ RETURN ( _MKSMALLINT(- val) );
+ }
+%}
+.
+ ^ (LargeInteger value:(SmallInteger maxVal)) + 1
+! !
+
+!SmallInteger methodsFor:'modulu arithmetic'!
+
+times:aNumber
+ "return the product of the receiver and the argument as SmallInteger.
+ If the result overflows integer range the value modulu the SmallInteger
+ range is returned.
+ This is of course not always correct, but some code does a modulu anyway
+ and can therefore speed things up by not going through LargeIntegers."
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+ RETURN ( _MKSMALLINT((_intVal(self) * _intVal(aNumber)) & 0x7FFFFFFF) );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+plus:aNumber
+ "return the sum of the receiver and the argument as SmallInteger.
+ If the result overflows integer range, the value modulu the SmallInteger
+ range is returned.
+ This is of course not always correct, but some code does a modulu anyway
+ and can therefore speed things up by not going through LargeIntegers."
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+ RETURN ( _MKSMALLINT((_intVal(self) + _intVal(aNumber)) & 0x7FFFFFFF) );
+ }
+%}
+.
+ self primitiveFailed
+! !
+
+!SmallInteger class methodsFor:'bit mask constants'!
+
+bitMaskFor:index
+ "return a bitmask for the index's bit (index starts at 1)"
+
+ (index between:1 and:SmallInteger maxBits) ifFalse:[
+ ^ self error:'index out of bounds'
+ ].
+ ^ 1 bitShift:(index - 1)
+! !
+
+!SmallInteger methodsFor:'bit operators'!
+
+bitAt:index
+ "return the value of the index's bit (index starts at 1)"
+
+ |mask|
+
+ (index between:1 and:SmallInteger maxBits) ifFalse:[
+ ^ self error:'index out of bounds'
+ ].
+ mask := 1 bitShift:(index - 1).
+ ((self bitAnd:mask) == 0) ifTrue:[^ 0].
+ ^ 1
+!
+
+allMask:anInteger
+ "True if all bits in anInteger are 1 in the receiver"
+
+ ^(self bitAnd:anInteger) == anInteger
+!
+
+anyMask:anInteger
+ "True if any 1 bits in anInteger are 1 in the receiver"
+
+ ^(self bitAnd:anInteger) ~~ 0
+!
+
+noMask:anInteger
+ "True if no 1 bits in anInteger are 1 in the receiver"
+
+ ^(self bitAnd:anInteger) == 0
+!
+
+highBit
+ "return the bitIndex of the highest bit set"
+
+%{ /* NOCONTEXT */
+
+ INT mask, index, bits;
+
+ bits = _intVal(self);
+ if (bits == 0) {
+ RETURN ( _MKSMALLINT(-1) );
+ }
+#ifdef alpha
+ mask = 0x2000000000000000;
+ index = 62;
+#else
+ mask = 0x20000000;
+ index = 30;
+#endif
+ while (index) {
+ if (bits & mask) break;
+ mask = mask >> 1;
+ index--;
+ }
+ RETURN ( _MKSMALLINT(index) );
+%}
+!
+
+lowBit
+ "return the bitIndex of the lowest bit set"
+%{ /* NOCONTEXT */
+
+ INT mask, index, bits;
+
+ bits = _intVal(self);
+ if (bits == 0) {
+ RETURN ( _MKSMALLINT(-1) );
+ }
+ mask = 1;
+ index = 1;
+#ifdef alpha
+ while (index != 63) {
+#else
+ while (index != 31) {
+#endif
+ if (bits & mask) {
+ RETURN ( _MKSMALLINT(index) );
+ }
+ mask = mask << 1;
+ index++;
+ }
+ RETURN ( _MKSMALLINT(-1) );
+ /* notreached */
+%}
+!
+
+bitShift:shiftCount
+ "return the value of the receiver shifted by shiftCount bits;
+ leftShift if shiftCount > 0; rightShift otherwise"
+
+%{ /* NOCONTEXT */
+
+ INT bits, count;
+
+ if (_isSmallInteger(shiftCount)) {
+ count = _intVal(shiftCount);
+ bits = _intVal(self);
+ if (count > 0) {
+ RETURN ( _MKSMALLINT(bits << count) );
+ }
+ if (count < 0) {
+ RETURN ( _MKSMALLINT(bits >> -count) );
+ }
+ RETURN (self );
+ }
+%}
+.
+ ^ self bitShift:(shiftCount coerce:1)
+!
+
+bitOr:anInteger
+ "return the bit-or of the receiver and the argument, anInteger"
+
+%{ /* NOCONTEXT */
+
+ /* oring the tags doesn't change it */
+ if (_isSmallInteger(anInteger)) {
+ RETURN ( ((OBJ) ((INT)self | (INT)anInteger)) );
+ }
+%}
+.
+ ^ self retry:#bitOr coercing:anInteger
+!
+
+bitAnd:anInteger
+ "return the bit-and of the receiver and the argument, anInteger"
+
+%{ /* NOCONTEXT */
+
+ /* anding the tags doesn't change it */
+ if (_isSmallInteger(anInteger)) {
+ RETURN ( ((OBJ) ((INT)self & (INT)anInteger)) );
+ }
+%}
+.
+ ^ self retry:#bitAnd coercing:anInteger
+!
+
+bitXor:anInteger
+ "return the bit-exclusive-or of the receiver and the argument, anInteger"
+
+%{ /* NOCONTEXT */
+
+ /* xoring the tags turns it off - or it in again */
+ if (_isSmallInteger(anInteger)) {
+ RETURN ( (OBJ)( ((INT)self ^ (INT)anInteger) | TAG_INT) );
+ }
+%}
+.
+ ^ self retry:#bitXor coercing:anInteger
+!
+
+bitInvert
+ "return the value of the receiver with all bits inverted"
+
+%{ /* NOCONTEXT */
+
+ /* invert anything except tag bits */
+ RETURN ( ((OBJ) ((INT)self ^ ~TAG_MASK)) );
+%}
+!
+
+bitTest:aMask
+ "return true, if any bit from aMask is set in the receiver"
+
+%{ /* NOCONTEXT */
+
+ /* and all bits except tag */
+ if (_isSmallInteger(aMask)) {
+ RETURN ( ((INT)self & ((INT)aMask & ~TAG_MASK)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#bitTest coercing:aMask
+! !
+
+!SmallInteger methodsFor:'byte access'!
+
+digitLength
+ "return the number bytes used by this Integer"
+
+ ^ self abs highBit - 1 // 8 + 1
+!
+
+digitAt:index
+ "return 8 bits of value, starting at byte index"
+
+%{ /* NOCONTEXT */
+
+ INT val;
+
+ if (_isSmallInteger(index)) {
+ val = _intVal(self);
+ if (val < 0)
+ val = -val;
+ switch (_intVal(index)) {
+ case 1:
+ RETURN ( _MKSMALLINT( val & 0xFF) );
+ case 2:
+ RETURN ( _MKSMALLINT( (val >> 8) & 0xFF) );
+ case 3:
+ RETURN ( _MKSMALLINT( (val >> 16) & 0xFF) );
+ case 4:
+ RETURN ( _MKSMALLINT( (val >> 24) & 0xFF) );
+#ifdef alpha
+ case 5:
+ RETURN ( _MKSMALLINT( (val >> 32) & 0xFF) );
+ case 6:
+ RETURN ( _MKSMALLINT( (val >> 40) & 0xFF) );
+ case 7:
+ RETURN ( _MKSMALLINT( (val >> 48) & 0xFF) );
+ case 8:
+ RETURN ( _MKSMALLINT( (val >> 56) & 0xFF) );
+#endif
+ }
+ }
+%}
+.
+ self primitiveFailed
+! !
+
+!SmallInteger methodsFor:'misc math functions'!
+
+gcd:anInteger
+ "return the greatest common divisor (Euclid's algorithm).
+ This has been redefined here for more speed since due to the
+ use of gcd in Fraction code, it has become time-critical for
+ some code. (thanx to MessageTally)"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(anInteger)) {
+ INT orgArg, ttt, selfInt, temp;
+
+ ttt = orgArg = _intVal(anInteger);
+ if (ttt) {
+ selfInt = _intVal(self);
+ while (ttt != 0) {
+ temp = selfInt % ttt;
+ selfInt = ttt;
+ ttt = temp;
+ }
+ /*
+ * since its not defined in what the sign of
+ * a modulu result is when the arg is negative,
+ * change it explicitely here ...
+ */
+ if (orgArg < 0) {
+ /* result should be negative */
+ if (selfInt > 0) selfInt = -selfInt;
+ } else {
+ /* result should be positive */
+ if (selfInt < 0) selfInt = -selfInt;
+ }
+ RETURN ( _MKSMALLINT(selfInt) );
+ }
+ }
+%}
+.
+ ^ super gcd:anInteger
+!
+
+intlog10
+ "return the truncation of log10 of the receiver -
+ stupid implementation; used to find out the number of digits needed
+ to print a number/and for conversion to a LargeInteger"
+
+ self <= 0 ifTrue:[
+ self error:'logarithm of negative integer'
+ ].
+ self < 10 ifTrue:[^ 1].
+ self < 100 ifTrue:[^ 2].
+ self < 1000 ifTrue:[^ 3].
+ self < 10000 ifTrue:[^ 4].
+ self < 100000 ifTrue:[^ 5].
+ self < 1000000 ifTrue:[^ 6].
+ self < 10000000 ifTrue:[^ 7].
+ self < 100000000 ifTrue:[^ 8].
+ self < 1000000000 ifTrue:[^ 9].
+ ^ 10
+! !
+
+!SmallInteger methodsFor:'coercing and converting'!
+
+coerce:aNumber
+ ^ aNumber asInteger
+!
+
+generality
+ ^ 20
+!
+
+asFloat
+ "return a Float with same value as receiver"
+
+%{ /* NOCONTEXT */
+
+ OBJ newFloat;
+
+ _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+ _InstPtr(newFloat)->o_class = Float;
+ _FloatInstPtr(newFloat)->f_floatvalue = _intVal(self);
+ RETURN ( newFloat );
+%}
+!
+
+asLargeInteger
+ "return a LargeInteger with same value as receiver"
+
+ ^ LargeInteger value:self
+!
+
+asCharacter
+ "Return self as an ascii character"
+
+ ^ Character value:self
+! !
+
+!SmallInteger methodsFor:'iterators'!
+
+timesRepeat:aBlock
+ "evaluate the argument, aBlock self times"
+
+ |count "{ Class: SmallInteger }" |
+
+ count := self.
+ [count > 0] whileTrue:[
+ aBlock value.
+ count := count - 1
+ ]
+!
+
+to:stop do:aBlock
+ "reimplemented for speed"
+
+ |home index|
+%{
+ REGISTER INT tmp;
+ INT final;
+ REGISTER OBJFUNC code;
+ extern OBJ Block, _value_;
+ static struct inlineCache blockVal = _ILC1;
+#ifdef UPDATE_WHOLE_STACK
+ REGISTER OBJ rHome;
+# undef home
+# define home rHome
+#endif
+
+ if (_isSmallInteger(stop)) {
+ tmp = _intVal(self);
+ final = _intVal(stop);
+ if (_isBlock(aBlock)
+ && ((code = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+ && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+ /*
+ * arg is a compiled block -
+ * directly call it without going through "Block-value"
+ */
+ home = _BlockInstPtr(aBlock)->b_home;
+ while (tmp <= final) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+ (*code)(home, CON_COMMA &index);
+#else
+ (*code)(home, CON_COMMA index);
+#endif
+ tmp++;
+ }
+ } else {
+ /*
+ * arg is something else - call it with Block-value"
+ */
+ while (tmp <= final) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+ (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, &index);
+#else
+ (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, index);
+#endif
+ tmp++;
+ }
+ }
+ RETURN ( self );
+ }
+%}
+.
+ ^super to:stop do:aBlock
+!
+
+to:stop by:incr do:aBlock
+ "reimplemented for speed"
+
+ |home index|
+%{
+ REGISTER INT tmp, step;
+ REGISTER INT final;
+ REGISTER OBJFUNC code;
+ extern OBJ Block, _value_;
+ static struct inlineCache blockVal = _ILC1;
+#ifdef UPDATE_WHOLE_STACK
+ REGISTER OBJ rHome;
+# undef home
+# define home rHome
+#endif
+
+ if (_isSmallInteger(incr)
+ && _isSmallInteger(stop)) {
+ tmp = _intVal(self);
+ final = _intVal(stop);
+ step = _intVal(incr);
+ if (_isBlock(aBlock)
+ && ((code = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+ && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+ /*
+ * arg is a compiled block -
+ * directly call it without going through "Block-value"
+ */
+ home = _BlockInstPtr(aBlock)->b_home;
+ if (step < 0) {
+ while (tmp >= final) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+ (*code)(home, CON_COMMA &index);
+#else
+ (*code)(home, CON_COMMA index);
+#endif
+ tmp += step;
+ }
+ } else {
+ while (tmp <= final) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+ (*code)(home, CON_COMMA &index);
+#else
+ (*code)(home, CON_COMMA index);
+#endif
+ tmp += step;
+ }
+ }
+ } else {
+ /*
+ * arg is something else - call it with Block-value"
+ */
+ if (step < 0) {
+ while (tmp >= final) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+ (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, &index);
+#else
+ (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, index);
+#endif
+ tmp += step;
+ }
+ } else {
+ while (tmp <= final) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+ (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, &index);
+#else
+ (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, index);
+#endif
+ tmp += step;
+ }
+ }
+ }
+ RETURN ( self );
+ }
+%}
+.
+ ^super to:stop do:aBlock
+! !
+
+!SmallInteger methodsFor:'printing & storing'!
+
+printString
+ "return my printstring (base 10)"
+
+%{ /* NOCONTEXT */
+
+ extern char *newNextPtr, *newEndPtr;
+ char buffer[30];
+ OBJ newString;
+#ifdef THIS_CONTEXT
+ OBJ sav = __thisContext;
+#endif
+
+ sprintf(buffer, "%d", _intVal(self));
+#ifdef THIS_CONTEXT
+ __thisContext = sav;
+#endif
+ _qNew(newString, sizeof(struct stringheader) + strlen(buffer) + 1, SENDER);
+ _InstPtr(newString)->o_class = String;
+ strcpy(_stringVal(newString), buffer);
+ RETURN (newString);
+%}
+!
+
+printStringRadix:radix
+ "return my printstring (base 10)"
+
+%{ /* NOCONTEXT */
+
+ extern char *newNextPtr, *newEndPtr;
+ char *format = (char *)0;
+ char buffer[30];
+ OBJ newString;
+
+ if (_isSmallInteger(radix)) {
+ switch (_intVal(radix)) {
+ case 10:
+ format = "%d";
+ break;
+ case 16:
+ format = "%x";
+ break;
+ case 8:
+ format = "%o";
+ break;
+ }
+ }
+
+ if (format) {
+#ifdef THIS_CONTEXT
+ OBJ sav = __thisContext;
+#endif
+ sprintf(buffer, format, _intVal(self));
+#ifdef THIS_CONTEXT
+ __thisContext = sav;
+#endif
+ _qNew(newString, sizeof(struct stringheader) + strlen(buffer) + 1, SENDER);
+ _InstPtr(newString)->o_class = String;
+ strcpy(_stringVal(newString), buffer);
+ RETURN (newString);
+ }
+%}
+.
+ ^ super printStringRadix:radix
+!
+
+printfPrintString:formatString
+ "non-portable, but sometimes useful.
+ return a printed representation of the receiver
+ as specified by formatString, which is defined by printf.
+ No checking for string overrun - must be shorter than 256 chars or else ..."
+
+%{ /* NOCONTEXT */
+
+ char buffer[256];
+
+ if (_isString(formatString)) {
+#ifdef THIS_CONTEXT
+ OBJ sav = __thisContext;
+#endif
+ sprintf(buffer, _stringVal(formatString), _intVal(self));
+#ifdef THIS_CONTEXT
+ __thisContext = sav;
+#endif
+ RETURN ( _MKSTRING(buffer COMMA_SND) );
+ }
+%}
+.
+ self primitiveFailed
+
+ "123 printfPrintString:'%%d -> %d'"
+ "123 printfPrintString:'%%6d -> %6d'"
+ "123 printfPrintString:'%%x -> %x'"
+ "123 printfPrintString:'%%4x -> %4x'"
+ "123 printfPrintString:'%%04x -> %04x'"
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SmallInteger.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1362 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Integer subclass:#SmallInteger
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Magnitude-Numbers'
+!
+
+SmallInteger comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+
+SmallIntegers are Integers in the range of +/- 2^30 (i.e. 31 bits).
+These are no real objects - they have no instances (not even storage !)
+and cannot be subclassed (sorry)
+
+The reason is to save both storage and runtime by not collecting
+SmallIntegers in the system. SmallInts are marked by having the TAG_INT
+bit set in contrast to Objects which have not. Since this knowledge is
+hardwired into the system (an there is no class-field stored with
+SmallIntegers) there can be no subclass of SmallInteger (sorry).
+'!
+
+!SmallInteger class methodsFor:'instance creation'!
+
+basicNew
+ "catch instance creation
+ - SmallIntegers cannot be created with new"
+
+ self error:'instances of SmallInteger cannot be created with new'
+!
+
+basicNew:size
+ "catch instance creation
+ - SmallIntegers cannot be created with new"
+
+ self error:'instances of SmallInteger cannot be created with new'
+! !
+
+!SmallInteger class methodsFor:'constants'!
+
+maxBits
+ "return the number of bits in instances of me"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(N_INT_BITS) );
+%}
+!
+
+maxBytes
+ "return the number of bytes in instances of me"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(N_INT_BITS / 8 + 1) );
+%}
+!
+
+minVal
+ "return the smallest Integer representable as SmallInteger"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(_MIN_INT) );
+%}
+!
+
+maxVal
+ "return the largest Integer representable as SmallInteger"
+
+%{ /* NOCONTEXT */
+ RETURN ( _MKSMALLINT(_MAX_INT) );
+%}
+! !
+
+!SmallInteger methodsFor:'error catching'!
+
+at:index
+ "catch indexed access - report an error
+ defined here since at: in Object ommits the SmallInteger check"
+
+ self notIndexed
+!
+
+basicAt:index
+ "catch indexed access - report an error
+ defined here since basicAt: in Object ommits the SmallInteger check"
+
+ self notIndexed
+!
+
+at:index put:anObject
+ "catch indexed access - report an error
+ defined here since at:put: in Object ommits the SmallInteger check"
+
+ self notIndexed
+!
+
+basicAt:index put:anObject
+ "catch indexed access - report an error
+ defined here since basicAt:put: in Object ommits the SmallInteger check"
+
+ self notIndexed
+!
+
+size
+ "return the number of indexed instvars - SmallIntegers have none
+ defined here since size in Object ommits the SmallInteger check"
+
+ ^ 0
+!
+
+basicSize
+ "return the number of indexed instvars - SmallIntegers have none
+ defined here since basicSize in Object ommits the SmallInteger check"
+
+ ^ 0
+! !
+
+!SmallInteger methodsFor:'copying'!
+
+shallowCopy
+ "return a shallow copy of myself
+ - reimplemented here since numbers are unique"
+
+ ^ self
+!
+
+deepCopy
+ "return a deep copy of myself
+ - reimplemented here since numbers are unique"
+
+ ^ self
+! !
+
+!SmallInteger methodsFor:'comparing'!
+
+= aNumber
+ "return true, if the arguments value is equal to mine"
+
+%{ /* NOCONTEXT */
+
+ if (aNumber == self) {
+ RETURN ( true );
+ }
+ if (! _isNonNilObject(aNumber)) {
+ RETURN ( false );
+ }
+
+ if (_qClass(aNumber) == Float) {
+ RETURN ( ((double)_intVal(self) == _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ aNumber respondsToArithmetic ifFalse:[^ false].
+ ^ self retry:#= coercing:aNumber
+!
+
+~= aNumber
+ "return true, if the arguments value is not equal to mine"
+
+%{ /* NOCONTEXT */
+
+ if (aNumber == self) {
+ RETURN ( false );
+ }
+ if (! _isNonNilObject(aNumber)) {
+ RETURN ( true );
+ }
+
+ if (_qClass(aNumber) == Float) {
+ RETURN ( ((double)_intVal(self) == _floatVal(aNumber)) ? false : true );
+ }
+%}
+.
+ aNumber respondsToArithmetic ifFalse:[^ true].
+ ^ self retry:#~= coercing:aNumber
+!
+
+< aNumber
+ "return true, if the argument is greater than the receiver"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) < _intVal(aNumber)) ? true : false );
+#else
+ /* tag bit does not change ordering */
+ RETURN ( ((INT)self < (INT)aNumber) ? true : false );
+#endif
+ }
+ if (_isFloat(aNumber)) {
+ RETURN ( ((double)_intVal(self) < _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ aNumber lessFromInteger:self
+ "^ self retry:#< coercing:aNumber"
+!
+
+> aNumber
+ "return true, if the argument is less than the receiver"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) > _intVal(aNumber)) ? true : false );
+#else
+ /* tag bit does not change ordering */
+ RETURN ( ((INT)self > (INT)aNumber) ? true : false );
+#endif
+ }
+ if (_isFloat(aNumber)) {
+ RETURN ( ((double)_intVal(self) > _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#> coercing:aNumber
+!
+
+>= aNumber
+ "return true, if the argument is less or equal"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) >= _intVal(aNumber)) ? true : false );
+#else
+ /* tag bit does not change ordering */
+ RETURN ( ((INT)self >= (INT)aNumber) ? true : false );
+#endif
+ }
+ if (_isFloat(aNumber)) {
+ RETURN ( ((double)_intVal(self) >= _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#>= coercing:aNumber
+!
+
+<= aNumber
+ "return true, if the argument is greater or equal"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) <= _intVal(aNumber)) ? true : false );
+#else
+ /* tag bit does not change ordering */
+ RETURN ( ((INT)self <= (INT)aNumber) ? true : false );
+#endif
+ }
+ if (_isFloat(aNumber)) {
+ RETURN ( ((double)_intVal(self) <= _floatVal(aNumber)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#<= coercing:aNumber
+!
+
+identityHash
+ "return an integer useful for hashing on identity"
+
+ self > 0 ifTrue:[
+ ^ self + 8192
+ ].
+ ^ self negated + 8192
+!
+
+min:aNumber
+ "return the receiver or the argument, whichever is smaller"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+ if (_intVal(self) < _intVal(aNumber)) {
+#else
+ /* tag bit does not change ordering */
+ if ((INT)(self) < (INT)(aNumber)) {
+#endif
+ RETURN ( self );
+ }
+ RETURN ( aNumber );
+ }
+ if (_isFloat(aNumber)) {
+ if ( (double)_intVal(self) < _floatVal(aNumber) ) {
+ RETURN ( self );
+ }
+ RETURN ( aNumber );
+ }
+%}
+.
+ (self < aNumber) ifTrue:[^ self].
+ ^ aNumber
+!
+
+max:aNumber
+ "return the receiver or the argument, whichever is greater"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+ if (_intVal(self) > _intVal(aNumber)) {
+#else
+ /* tag bit does not change ordering */
+ if ((INT)(self) > (INT)(aNumber)) {
+#endif
+ RETURN ( self );
+ }
+ RETURN ( aNumber );
+ }
+ if (_isFloat(aNumber)) {
+ if ( (double)_intVal(self) > _floatVal(aNumber) ) {
+ RETURN ( self );
+ }
+ RETURN ( aNumber );
+ }
+%}
+.
+ (self > aNumber) ifTrue:[^ self].
+ ^ aNumber
+! !
+
+!SmallInteger methodsFor:'testing'!
+
+negative
+ "return true, if the receiver is less than zero
+ reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) < 0) ? true : false );
+#else
+ /* tag bit does not change sign */
+ RETURN ( ((INT)(self) < 0) ? true : false );
+#endif
+%}
+!
+
+positive
+ "return true, if the receiver is not negative
+ reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) >= 0) ? true : false );
+#else
+ /* tag bit does not change sign */
+ RETURN ( ((INT)(self) >= 0) ? true : false );
+#endif
+%}
+!
+
+strictlyPositive
+ "return true, if the receiver is greater than zero
+ reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( (_intVal(self) > 0) ? true : false );
+#else
+ /* tag bit does not change sign */
+ RETURN ( ((INT)(self) > 0) ? true : false );
+#endif
+%}
+!
+
+sign
+ "return the sign of the receiver
+ reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ INT val = _intVal(self);
+
+ if (val < 0) {
+ RETURN ( _MKSMALLINT(-1) );
+ }
+ if (val > 0) {
+ RETURN ( _MKSMALLINT(1) );
+ }
+ RETURN ( _MKSMALLINT(0) );
+%}
+!
+
+between:min and:max
+ "return true if the receiver is less than or equal to the argument max
+ and greater than or equal to the argument min.
+ - reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(min) && _isSmallInteger(max)) {
+ REGISTER INT selfVal;
+
+ selfVal = _intVal(self);
+ if (selfVal < _intVal(min)) {
+ RETURN ( false );
+ }
+ if (selfVal > _intVal(max)) {
+ RETURN ( false );
+ }
+ RETURN ( true );
+ }
+%}
+.
+ (self < min) ifTrue:[^ false].
+ (self > max) ifTrue:[^ false].
+ ^ true
+!
+
+even
+ "return true, if the receiver is even"
+
+%{ /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( ((INT)self & 1) ? false : true );
+#else
+ RETURN ( ((INT)self & ((INT)_MKSMALLINT(1) & ~TAG_INT)) ? false : true );
+#endif
+%}
+!
+
+odd
+ "return true, if the receiver is odd"
+
+%{ /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+ RETURN ( ((INT)self & 1) ? true : false );
+#else
+ RETURN ( ((INT)self & ((INT)_MKSMALLINT(1) & ~TAG_INT)) ? true : false );
+#endif
+%}
+! !
+
+!SmallInteger methodsFor:'arithmetic'!
+
++ aNumber
+ "return the sum of the receivers value and the arguments value"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef _ADD_IO_IO
+ RETURN ( _ADD_IO_IO(self, aNumber) );
+#else
+ REGISTER INT sum;
+ extern OBJ _makeLarge();
+
+ sum = _intVal(self) + _intVal(aNumber);
+ if ((sum >= _MIN_INT) && (sum <= _MAX_INT)) {
+ RETURN ( _MKSMALLINT(sum) );
+ }
+ RETURN ( _makeLarge(sum) );
+#endif
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ extern char *newNextPtr, *newEndPtr;
+ OBJ newFloat;
+ double val;
+
+ val = _floatVal(aNumber);
+ _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+ _InstPtr(newFloat)->o_class = Float;
+ _FloatInstPtr(newFloat)->f_floatvalue = (double)(_intVal(self)) + val;
+ RETURN ( newFloat );
+ }
+%}
+.
+ ^ aNumber sumFromInteger:self
+!
+
+- aNumber
+ "return the difference of the receivers value and the arguments value"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+#ifdef _SUB_IO_IO
+ RETURN ( _SUB_IO_IO(self, aNumber) );
+#else
+ REGISTER INT diff;
+ extern OBJ _makeLarge();
+
+ diff = _intVal(self) - _intVal(aNumber);
+ if ((diff >= _MIN_INT) && (diff <= _MAX_INT)) {
+ RETURN ( _MKSMALLINT(diff) );
+ }
+ RETURN ( _makeLarge(diff) );
+#endif
+ }
+ if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ extern char *newNextPtr, *newEndPtr;
+ OBJ newFloat;
+ double val;
+
+ val = _floatVal(aNumber);
+ _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+ _InstPtr(newFloat)->o_class = Float;
+ _FloatInstPtr(newFloat)->f_floatvalue = (double)(_intVal(self)) - val;
+ RETURN ( newFloat );
+ }
+%}
+.
+ ^ aNumber differenceFromInteger:self
+!
+
+* aNumber
+ "return the product of the receivers value and the arguments value"
+
+ |aLarge|
+
+%{ /* NOCONTEXT */
+
+ REGISTER INT myValue, otherValue;
+ unsigned INT pHH, pHL, pLH, pLL;
+
+ if (_isSmallInteger(aNumber)) {
+ /* this is too slow:
+ * since most machines can do 32*32 to 64 bit multiply,
+ * (or at least 32*32 with Overflow check)
+ * its better to do it this way .. - need an assembler (inline) function here
+ */
+ myValue = _intVal(self);
+ if (myValue < 0) myValue = -myValue;
+ otherValue = _intVal(aNumber);
+ if (otherValue < 0) otherValue = -otherValue;
+#ifdef NOTDEF
+ if (! ((myValue & ~0x7FFF) || (otherValue & ~0x7FFF))) {
+#else
+ pHH = ((myValue >> 16) & 0xFFFF) * ((otherValue >> 16) & 0xFFFF);
+ pHL = ((myValue >> 16) & 0xFFFF) * (otherValue & 0xFFFF);
+ pLH = (myValue & 0xFFFF) * ((otherValue >> 16) & 0xFFFF);
+ pLL = (myValue & 0xFFFF) * (otherValue & 0xFFFF);
+ if (! (pHH || (pHL & 0xFFFFc000) || (pLH & 0xFFFFc000) || (pLL & 0xc0000000))) {
+#endif
+ RETURN ( _MKSMALLINT(_intVal(self) * _intVal(aNumber)) );
+ }
+ } else if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+ extern char *newNextPtr, *newEndPtr;
+ OBJ newFloat;
+ double val;
+
+ val = _floatVal(aNumber);
+ _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+ _InstPtr(newFloat)->o_class = Float;
+ _FloatInstPtr(newFloat)->f_floatvalue = (double)(_intVal(self)) * val;
+ RETURN ( newFloat );
+ }
+%}
+.
+%{
+ extern OBJ LargeInteger, __mu, _value_;
+ static struct inlineCache val = _ILC1;
+ static struct inlineCache mu = _ILC1;
+
+ if (_isSmallInteger(aNumber)) {
+ /*
+ * non overflow case has already been checked
+ */
+#ifdef PASS_ARG_REF
+ aLarge = (*val.ilc_func)(LargeInteger, _value_, CON_COMMA nil, &val, &self);
+ RETURN ( (*mu.ilc_func)(aLarge, __mu, CON_COMMA nil, &mu, &aNumber) );
+#else
+ aLarge = (*val.ilc_func)(LargeInteger, _value_, CON_COMMA nil, &val, self);
+ RETURN ( (*mu.ilc_func)(aLarge, __mu, CON_COMMA nil, &mu, aNumber) );
+#endif
+ }
+%}
+.
+ ^ aNumber productFromInteger:self
+!
+
+/ aNumber
+ "return the quotient of the receivers value and the arguments value"
+
+%{ /* NOCONTEXT */
+
+ INT me, t, val;
+ double dval;
+
+ if (_isSmallInteger(aNumber)) {
+ val = _intVal(aNumber);
+ if (val != 0) {
+ me = _intVal(self);
+ t = me / val;
+#ifdef GOOD_OPTIMIZER
+ if (me % val) {
+#else
+ /* this is stupid - all I want is to look for a remainder ...
+ but most compilers are too stupid and generate an extra mod instr.
+ for "if (me % val)" even if most div instructions also compute
+ the remainder.
+ therefore I use a multiplication which is faster than a modulu
+ on most machines
+ */
+ if ((t * val) == me) {
+#endif
+ RETURN ( _MKSMALLINT(t) );
+ }
+/*
+ * now disabled - Fractions work
+ *
+ RETURN ( _MKFLOAT((double)_intVal(self) / (double)val, __context) );
+*/
+ }
+ } else {
+ if (_isFloat(aNumber)) {
+ dval = _floatVal(aNumber);
+ if (dval != 0.0) {
+ me = _intVal(self);
+ RETURN ( _MKFLOAT((double)me / dval COMMA_CON) );
+ }
+ }
+ }
+%}
+.
+ aNumber isInteger ifTrue:[
+ aNumber = 0 ifTrue:[
+ DivisionByZeroSignal raise.
+ ^ self
+ ].
+ ^ Fraction numerator:self denominator:aNumber
+ ].
+ ^ aNumber quotientFromInteger:self
+!
+
+// anInteger
+ "return the integer part of the quotient of the receivers value
+ and the arguments value"
+
+%{ /* NOCONTEXT */
+ INT val;
+
+ if (_isSmallInteger(anInteger)) {
+ val = _intVal(anInteger);
+ if (val != 0) {
+ RETURN ( _MKSMALLINT(_intVal(self) / val) );
+ }
+ }
+%}
+.
+ (anInteger = 0) ifTrue:[
+ DivisionByZeroSignal raise.
+ ^ self
+ ].
+ ^ self retry:#// coercing:anInteger
+!
+
+\\ anInteger
+ "return the integer rest of the receivers value
+ divided by the arguments value"
+
+%{ /* NOCONTEXT */
+ INT mySelf, val;
+
+ if (_isSmallInteger(anInteger)) {
+ mySelf = _intVal(self);
+ if (mySelf < 0) mySelf = -mySelf;
+ val = _intVal(anInteger);
+ if (val != 0) {
+ if (val < 0) {
+ RETURN ( _MKSMALLINT(-(mySelf % -val)) );
+ }
+ RETURN ( _MKSMALLINT(mySelf % val) );
+ }
+ }
+%}
+.
+ (anInteger = 0) ifTrue:[
+ DivisionByZeroSignal raise.
+ ^ self
+ ].
+ ^ self retry:#\\ coercing:anInteger
+!
+
+abs
+ "return the absolute value of the receiver
+ reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ INT val = _intVal(self);
+
+ if (val != _MIN_INT) {
+ RETURN ( (val < 0) ? _MKSMALLINT(-val) : self );
+ }
+%}
+.
+ "only reached for minVal"
+ ^ self negated
+!
+
+negated
+ "return the negative value of the receiver
+ reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ INT val = _intVal(self);
+
+ if (val != _MIN_INT) {
+ RETURN ( _MKSMALLINT(- val) );
+ }
+%}
+.
+ ^ (LargeInteger value:(SmallInteger maxVal)) + 1
+! !
+
+!SmallInteger methodsFor:'modulu arithmetic'!
+
+times:aNumber
+ "return the product of the receiver and the argument as SmallInteger.
+ If the result overflows integer range the value modulu the SmallInteger
+ range is returned.
+ This is of course not always correct, but some code does a modulu anyway
+ and can therefore speed things up by not going through LargeIntegers."
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+ RETURN ( _MKSMALLINT((_intVal(self) * _intVal(aNumber)) & 0x7FFFFFFF) );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+plus:aNumber
+ "return the sum of the receiver and the argument as SmallInteger.
+ If the result overflows integer range, the value modulu the SmallInteger
+ range is returned.
+ This is of course not always correct, but some code does a modulu anyway
+ and can therefore speed things up by not going through LargeIntegers."
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(aNumber)) {
+ RETURN ( _MKSMALLINT((_intVal(self) + _intVal(aNumber)) & 0x7FFFFFFF) );
+ }
+%}
+.
+ self primitiveFailed
+! !
+
+!SmallInteger class methodsFor:'bit mask constants'!
+
+bitMaskFor:index
+ "return a bitmask for the index's bit (index starts at 1)"
+
+ (index between:1 and:SmallInteger maxBits) ifFalse:[
+ ^ self error:'index out of bounds'
+ ].
+ ^ 1 bitShift:(index - 1)
+! !
+
+!SmallInteger methodsFor:'bit operators'!
+
+bitAt:index
+ "return the value of the index's bit (index starts at 1)"
+
+ |mask|
+
+ (index between:1 and:SmallInteger maxBits) ifFalse:[
+ ^ self error:'index out of bounds'
+ ].
+ mask := 1 bitShift:(index - 1).
+ ((self bitAnd:mask) == 0) ifTrue:[^ 0].
+ ^ 1
+!
+
+allMask:anInteger
+ "True if all bits in anInteger are 1 in the receiver"
+
+ ^(self bitAnd:anInteger) == anInteger
+!
+
+anyMask:anInteger
+ "True if any 1 bits in anInteger are 1 in the receiver"
+
+ ^(self bitAnd:anInteger) ~~ 0
+!
+
+noMask:anInteger
+ "True if no 1 bits in anInteger are 1 in the receiver"
+
+ ^(self bitAnd:anInteger) == 0
+!
+
+highBit
+ "return the bitIndex of the highest bit set"
+
+%{ /* NOCONTEXT */
+
+ INT mask, index, bits;
+
+ bits = _intVal(self);
+ if (bits == 0) {
+ RETURN ( _MKSMALLINT(-1) );
+ }
+#ifdef alpha
+ mask = 0x2000000000000000;
+ index = 62;
+#else
+ mask = 0x20000000;
+ index = 30;
+#endif
+ while (index) {
+ if (bits & mask) break;
+ mask = mask >> 1;
+ index--;
+ }
+ RETURN ( _MKSMALLINT(index) );
+%}
+!
+
+lowBit
+ "return the bitIndex of the lowest bit set"
+%{ /* NOCONTEXT */
+
+ INT mask, index, bits;
+
+ bits = _intVal(self);
+ if (bits == 0) {
+ RETURN ( _MKSMALLINT(-1) );
+ }
+ mask = 1;
+ index = 1;
+#ifdef alpha
+ while (index != 63) {
+#else
+ while (index != 31) {
+#endif
+ if (bits & mask) {
+ RETURN ( _MKSMALLINT(index) );
+ }
+ mask = mask << 1;
+ index++;
+ }
+ RETURN ( _MKSMALLINT(-1) );
+ /* notreached */
+%}
+!
+
+bitShift:shiftCount
+ "return the value of the receiver shifted by shiftCount bits;
+ leftShift if shiftCount > 0; rightShift otherwise"
+
+%{ /* NOCONTEXT */
+
+ INT bits, count;
+
+ if (_isSmallInteger(shiftCount)) {
+ count = _intVal(shiftCount);
+ bits = _intVal(self);
+ if (count > 0) {
+ RETURN ( _MKSMALLINT(bits << count) );
+ }
+ if (count < 0) {
+ RETURN ( _MKSMALLINT(bits >> -count) );
+ }
+ RETURN (self );
+ }
+%}
+.
+ ^ self bitShift:(shiftCount coerce:1)
+!
+
+bitOr:anInteger
+ "return the bit-or of the receiver and the argument, anInteger"
+
+%{ /* NOCONTEXT */
+
+ /* oring the tags doesn't change it */
+ if (_isSmallInteger(anInteger)) {
+ RETURN ( ((OBJ) ((INT)self | (INT)anInteger)) );
+ }
+%}
+.
+ ^ self retry:#bitOr coercing:anInteger
+!
+
+bitAnd:anInteger
+ "return the bit-and of the receiver and the argument, anInteger"
+
+%{ /* NOCONTEXT */
+
+ /* anding the tags doesn't change it */
+ if (_isSmallInteger(anInteger)) {
+ RETURN ( ((OBJ) ((INT)self & (INT)anInteger)) );
+ }
+%}
+.
+ ^ self retry:#bitAnd coercing:anInteger
+!
+
+bitXor:anInteger
+ "return the bit-exclusive-or of the receiver and the argument, anInteger"
+
+%{ /* NOCONTEXT */
+
+ /* xoring the tags turns it off - or it in again */
+ if (_isSmallInteger(anInteger)) {
+ RETURN ( (OBJ)( ((INT)self ^ (INT)anInteger) | TAG_INT) );
+ }
+%}
+.
+ ^ self retry:#bitXor coercing:anInteger
+!
+
+bitInvert
+ "return the value of the receiver with all bits inverted"
+
+%{ /* NOCONTEXT */
+
+ /* invert anything except tag bits */
+ RETURN ( ((OBJ) ((INT)self ^ ~TAG_MASK)) );
+%}
+!
+
+bitTest:aMask
+ "return true, if any bit from aMask is set in the receiver"
+
+%{ /* NOCONTEXT */
+
+ /* and all bits except tag */
+ if (_isSmallInteger(aMask)) {
+ RETURN ( ((INT)self & ((INT)aMask & ~TAG_MASK)) ? true : false );
+ }
+%}
+.
+ ^ self retry:#bitTest coercing:aMask
+! !
+
+!SmallInteger methodsFor:'byte access'!
+
+digitLength
+ "return the number bytes used by this Integer"
+
+ ^ self abs highBit - 1 // 8 + 1
+!
+
+digitAt:index
+ "return 8 bits of value, starting at byte index"
+
+%{ /* NOCONTEXT */
+
+ INT val;
+
+ if (_isSmallInteger(index)) {
+ val = _intVal(self);
+ if (val < 0)
+ val = -val;
+ switch (_intVal(index)) {
+ case 1:
+ RETURN ( _MKSMALLINT( val & 0xFF) );
+ case 2:
+ RETURN ( _MKSMALLINT( (val >> 8) & 0xFF) );
+ case 3:
+ RETURN ( _MKSMALLINT( (val >> 16) & 0xFF) );
+ case 4:
+ RETURN ( _MKSMALLINT( (val >> 24) & 0xFF) );
+#ifdef alpha
+ case 5:
+ RETURN ( _MKSMALLINT( (val >> 32) & 0xFF) );
+ case 6:
+ RETURN ( _MKSMALLINT( (val >> 40) & 0xFF) );
+ case 7:
+ RETURN ( _MKSMALLINT( (val >> 48) & 0xFF) );
+ case 8:
+ RETURN ( _MKSMALLINT( (val >> 56) & 0xFF) );
+#endif
+ }
+ }
+%}
+.
+ self primitiveFailed
+! !
+
+!SmallInteger methodsFor:'misc math functions'!
+
+gcd:anInteger
+ "return the greatest common divisor (Euclid's algorithm).
+ This has been redefined here for more speed since due to the
+ use of gcd in Fraction code, it has become time-critical for
+ some code. (thanx to MessageTally)"
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(anInteger)) {
+ INT orgArg, ttt, selfInt, temp;
+
+ ttt = orgArg = _intVal(anInteger);
+ if (ttt) {
+ selfInt = _intVal(self);
+ while (ttt != 0) {
+ temp = selfInt % ttt;
+ selfInt = ttt;
+ ttt = temp;
+ }
+ /*
+ * since its not defined in what the sign of
+ * a modulu result is when the arg is negative,
+ * change it explicitely here ...
+ */
+ if (orgArg < 0) {
+ /* result should be negative */
+ if (selfInt > 0) selfInt = -selfInt;
+ } else {
+ /* result should be positive */
+ if (selfInt < 0) selfInt = -selfInt;
+ }
+ RETURN ( _MKSMALLINT(selfInt) );
+ }
+ }
+%}
+.
+ ^ super gcd:anInteger
+!
+
+intlog10
+ "return the truncation of log10 of the receiver -
+ stupid implementation; used to find out the number of digits needed
+ to print a number/and for conversion to a LargeInteger"
+
+ self <= 0 ifTrue:[
+ self error:'logarithm of negative integer'
+ ].
+ self < 10 ifTrue:[^ 1].
+ self < 100 ifTrue:[^ 2].
+ self < 1000 ifTrue:[^ 3].
+ self < 10000 ifTrue:[^ 4].
+ self < 100000 ifTrue:[^ 5].
+ self < 1000000 ifTrue:[^ 6].
+ self < 10000000 ifTrue:[^ 7].
+ self < 100000000 ifTrue:[^ 8].
+ self < 1000000000 ifTrue:[^ 9].
+ ^ 10
+! !
+
+!SmallInteger methodsFor:'coercing and converting'!
+
+coerce:aNumber
+ ^ aNumber asInteger
+!
+
+generality
+ ^ 20
+!
+
+asFloat
+ "return a Float with same value as receiver"
+
+%{ /* NOCONTEXT */
+
+ OBJ newFloat;
+
+ _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+ _InstPtr(newFloat)->o_class = Float;
+ _FloatInstPtr(newFloat)->f_floatvalue = _intVal(self);
+ RETURN ( newFloat );
+%}
+!
+
+asLargeInteger
+ "return a LargeInteger with same value as receiver"
+
+ ^ LargeInteger value:self
+!
+
+asCharacter
+ "Return self as an ascii character"
+
+ ^ Character value:self
+! !
+
+!SmallInteger methodsFor:'iterators'!
+
+timesRepeat:aBlock
+ "evaluate the argument, aBlock self times"
+
+ |count "{ Class: SmallInteger }" |
+
+ count := self.
+ [count > 0] whileTrue:[
+ aBlock value.
+ count := count - 1
+ ]
+!
+
+to:stop do:aBlock
+ "reimplemented for speed"
+
+ |home index|
+%{
+ REGISTER INT tmp;
+ INT final;
+ REGISTER OBJFUNC code;
+ extern OBJ Block, _value_;
+ static struct inlineCache blockVal = _ILC1;
+#ifdef UPDATE_WHOLE_STACK
+ REGISTER OBJ rHome;
+# undef home
+# define home rHome
+#endif
+
+ if (_isSmallInteger(stop)) {
+ tmp = _intVal(self);
+ final = _intVal(stop);
+ if (_isBlock(aBlock)
+ && ((code = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+ && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+ /*
+ * arg is a compiled block -
+ * directly call it without going through "Block-value"
+ */
+ home = _BlockInstPtr(aBlock)->b_home;
+ while (tmp <= final) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+ (*code)(home, CON_COMMA &index);
+#else
+ (*code)(home, CON_COMMA index);
+#endif
+ tmp++;
+ }
+ } else {
+ /*
+ * arg is something else - call it with Block-value"
+ */
+ while (tmp <= final) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+ (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, &index);
+#else
+ (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, index);
+#endif
+ tmp++;
+ }
+ }
+ RETURN ( self );
+ }
+%}
+.
+ ^super to:stop do:aBlock
+!
+
+to:stop by:incr do:aBlock
+ "reimplemented for speed"
+
+ |home index|
+%{
+ REGISTER INT tmp, step;
+ REGISTER INT final;
+ REGISTER OBJFUNC code;
+ extern OBJ Block, _value_;
+ static struct inlineCache blockVal = _ILC1;
+#ifdef UPDATE_WHOLE_STACK
+ REGISTER OBJ rHome;
+# undef home
+# define home rHome
+#endif
+
+ if (_isSmallInteger(incr)
+ && _isSmallInteger(stop)) {
+ tmp = _intVal(self);
+ final = _intVal(stop);
+ step = _intVal(incr);
+ if (_isBlock(aBlock)
+ && ((code = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+ && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+ /*
+ * arg is a compiled block -
+ * directly call it without going through "Block-value"
+ */
+ home = _BlockInstPtr(aBlock)->b_home;
+ if (step < 0) {
+ while (tmp >= final) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+ (*code)(home, CON_COMMA &index);
+#else
+ (*code)(home, CON_COMMA index);
+#endif
+ tmp += step;
+ }
+ } else {
+ while (tmp <= final) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+ (*code)(home, CON_COMMA &index);
+#else
+ (*code)(home, CON_COMMA index);
+#endif
+ tmp += step;
+ }
+ }
+ } else {
+ /*
+ * arg is something else - call it with Block-value"
+ */
+ if (step < 0) {
+ while (tmp >= final) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+ (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, &index);
+#else
+ (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, index);
+#endif
+ tmp += step;
+ }
+ } else {
+ while (tmp <= final) {
+ if (InterruptPending != nil) interrupt(CONARG);
+
+ index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+ (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, &index);
+#else
+ (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, index);
+#endif
+ tmp += step;
+ }
+ }
+ }
+ RETURN ( self );
+ }
+%}
+.
+ ^super to:stop do:aBlock
+! !
+
+!SmallInteger methodsFor:'printing & storing'!
+
+printString
+ "return my printstring (base 10)"
+
+%{ /* NOCONTEXT */
+
+ extern char *newNextPtr, *newEndPtr;
+ char buffer[30];
+ OBJ newString;
+#ifdef THIS_CONTEXT
+ OBJ sav = __thisContext;
+#endif
+
+ sprintf(buffer, "%d", _intVal(self));
+#ifdef THIS_CONTEXT
+ __thisContext = sav;
+#endif
+ _qNew(newString, sizeof(struct stringheader) + strlen(buffer) + 1, SENDER);
+ _InstPtr(newString)->o_class = String;
+ strcpy(_stringVal(newString), buffer);
+ RETURN (newString);
+%}
+!
+
+printStringRadix:radix
+ "return my printstring (base 10)"
+
+%{ /* NOCONTEXT */
+
+ extern char *newNextPtr, *newEndPtr;
+ char *format = (char *)0;
+ char buffer[30];
+ OBJ newString;
+
+ if (_isSmallInteger(radix)) {
+ switch (_intVal(radix)) {
+ case 10:
+ format = "%d";
+ break;
+ case 16:
+ format = "%x";
+ break;
+ case 8:
+ format = "%o";
+ break;
+ }
+ }
+
+ if (format) {
+#ifdef THIS_CONTEXT
+ OBJ sav = __thisContext;
+#endif
+ sprintf(buffer, format, _intVal(self));
+#ifdef THIS_CONTEXT
+ __thisContext = sav;
+#endif
+ _qNew(newString, sizeof(struct stringheader) + strlen(buffer) + 1, SENDER);
+ _InstPtr(newString)->o_class = String;
+ strcpy(_stringVal(newString), buffer);
+ RETURN (newString);
+ }
+%}
+.
+ ^ super printStringRadix:radix
+!
+
+printfPrintString:formatString
+ "non-portable, but sometimes useful.
+ return a printed representation of the receiver
+ as specified by formatString, which is defined by printf.
+ No checking for string overrun - must be shorter than 256 chars or else ..."
+
+%{ /* NOCONTEXT */
+
+ char buffer[256];
+
+ if (_isString(formatString)) {
+#ifdef THIS_CONTEXT
+ OBJ sav = __thisContext;
+#endif
+ sprintf(buffer, _stringVal(formatString), _intVal(self));
+#ifdef THIS_CONTEXT
+ __thisContext = sav;
+#endif
+ RETURN ( _MKSTRING(buffer COMMA_SND) );
+ }
+%}
+.
+ self primitiveFailed
+
+ "123 printfPrintString:'%%d -> %d'"
+ "123 printfPrintString:'%%6d -> %6d'"
+ "123 printfPrintString:'%%x -> %x'"
+ "123 printfPrintString:'%%4x -> %4x'"
+ "123 printfPrintString:'%%04x -> %04x'"
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Smalltalk.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1123 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Smalltalk
+ instanceVariableNames:''
+ classVariableNames:'exitBlocks CachedClasses'
+ poolDictionaries:''
+ category:'System-Support'
+!
+
+Smalltalk comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+This is one of the central classes in the system;
+it provides all system-startup, shutdown and maintenance support.
+Also global variables are kept here.
+
+As you will notice, this is NOT a Dictionary
+ - my implementation of globals is totally different
+ (due to the need to be able to access globals from c-code as well).
+
+%W% %E%
+'!
+
+Smalltalk at:#ErrorNumber put:nil!
+Smalltalk at:#ErrorString put:nil!
+Smalltalk at:#Language put:#english!
+Smalltalk at:#LanguageTerritory put:#usa!
+Smalltalk at:#Initializing put:false!
+Smalltalk at:#SilentLoading put:false!
+Smalltalk at:#RecursionLimit put:nil!
+Smalltalk at:#MemoryLimit put:nil!
+Smalltalk at:#SystemPath put:nil!
+Smalltalk at:#StartupClass put:nil!
+Smalltalk at:#StartupSelector put:nil!
+Smalltalk at:#SignalCatchBlock put:nil!
+
+!Smalltalk class methodsFor:'time-versions'!
+
+majorVersion
+ "return the major version number"
+
+ ^ 2
+
+ "Smalltalk majorVersion"
+!
+
+minorVersion
+ "return the minor version number"
+
+ ^ 7
+
+ "Smalltalk minorVersion"
+!
+
+revision
+ "return the revision number"
+
+ ^ 1
+
+ "Smalltalk revision"
+!
+
+version
+ "return the version string"
+
+ ^ (self majorVersion printString ,
+ '.',
+ self minorVersion printString ,
+ '.',
+ self revision printString)
+
+ "Smalltalk version"
+!
+
+versionDate
+ "return the version date"
+
+ ^ '9-Jul-1993'
+
+ "Smalltalk versionDate"
+!
+
+copyright
+ "return a copyright string"
+
+ ^ 'Copyright (c) 1988-93 by Claus Gittinger'
+
+ "Smalltalk copyright"
+!
+
+hello
+ "return a greeting string"
+
+ (Language == #german) ifTrue:[
+ ^ 'Willkommen bei Smalltalk/X version '
+ , self version , ' vom ' , self versionDate
+ ].
+ ^ 'Hello World - here is Smalltalk/X version '
+ , self version , ' of ' , self versionDate
+
+ "Smalltalk hello"
+!
+
+timeStamp
+ "return a string useful for timestamping a file"
+
+ ^ '''From Smalltalk/X, Version:' , (Smalltalk version) , ' on '
+ , Date today printString , ' at ' , Time now printString
+ , ''''
+! !
+
+!Smalltalk class methodsFor:'initialization'!
+
+initialize
+ "this one is called from init - initialize all other classes"
+
+ self initGlobalsFromEnvironment.
+
+ "sorry - there are some, which MUST be initialized before ..
+ reason: if any error happens during init, we need Stdout to be there"
+
+ Object initialize.
+
+ ExternalStream initialize.
+ self initStandardStreams.
+
+ "sorry, path must be set before ...
+ reason: some classes need it during initialize"
+
+ self initSystemPath.
+
+ "must init display here - some classes (Color) need it during
+ initialize"
+
+ Workstation notNil ifTrue:[
+ Workstation initialize
+ ].
+
+ Inspector := MiniInspector.
+ Debugger := MiniDebugger.
+ Compiler := ByteCodeCompiler.
+ Compiler isNil ifTrue:[
+ "this allows at least immediate evaluations"
+ Compiler := Parser
+ ].
+
+ self allClassesDo:[:aClass |
+ "aviod never-ending story ..."
+ (aClass ~~ Smalltalk) ifTrue:[
+ aClass initialize
+ ]
+ ].
+ self initStandardTools.
+ self initInterrupts
+
+ "Smalltalk initialize"
+!
+
+initGlobalsFromEnvironment
+ "setup globals from the shell-environment"
+
+ |envString firstChar i langString terrString|
+
+ "extract Language and LanguageTerritory from LANG variable.
+ the language and territory must not be abbreviated,
+ valid is for example: english_usa
+ english
+ german
+ german_austria"
+
+ envString := OperatingSystem getEnvironment:'LANG'.
+ envString notNil ifTrue:[
+ i := envString indexOf:$_.
+ (i == 0) ifTrue:[
+ langString := envString.
+ terrString := envString
+ ] ifFalse:[
+ langString := envString copyFrom:1 to:(i - 1).
+ terrString := envString copyFrom:(i + 1)
+ ].
+ Language := langString asSymbol.
+ LanguageTerritory := terrString asSymbol
+ ].
+
+ envString := OperatingSystem getEnvironment:'VIEW3D'.
+ envString notNil ifTrue:[
+ firstChar := (envString at:1) asLowercase.
+ (firstChar == $t) ifTrue:[
+ Smalltalk at:#View3D put:true
+ ] ifFalse: [
+ Smalltalk at:#View3D put:false
+ ]
+ ]
+ "Smalltalk initGlobalsFromEnvironment"
+!
+
+initStandardTools
+ "predefine some tools we will need later
+ - if the view-classes exist,
+ they will redefine Inspector and Debugger for graphical interfaces"
+
+ "redefine debug-tools, if view-classes exist"
+
+ (Smalltalk at:#Display) notNil ifTrue:[
+ (Smalltalk at:#InspectorView) notNil ifTrue:[
+ Inspector := Smalltalk at:#InspectorView
+ ].
+ (Smalltalk at:#DebugView) notNil ifTrue:[
+ Debugger := Smalltalk at:#DebugView
+ ].
+ Display initialize
+ ]
+ "Smalltalk initStandardTools"
+!
+
+initStandardStreams
+ "initialize some well-known streams"
+
+ Stdout := NonPositionableExternalStream forStdout.
+ Stderr := NonPositionableExternalStream forStderr.
+ Stdin := NonPositionableExternalStream forStdin.
+ Printer := PrinterStream.
+ Transcript := Stderr
+
+ "Smalltalk initStandardStreams"
+!
+
+initInterrupts
+ "initialize interrupts"
+
+ UserInterruptHandler := self.
+ ErrorInterruptHandler := self.
+ MemoryInterruptHandler := self.
+ SignalInterruptHandler := self.
+ ExceptionInterruptHandler := self.
+ OperatingSystem enableUserInterrupts.
+ OperatingSystem enableSignalInterrupts.
+ OperatingSystem enableFpExceptionInterrupts
+
+ "Smalltalk initInterrupts"
+!
+
+initSystemPath
+ "setup path to search for system files"
+
+ |p|
+
+ "the path is set to search files first locally
+ - this allows private stuff to override global stuff"
+
+ SystemPath := OrderedCollection new.
+ SystemPath add:'.'.
+ SystemPath add:'..'.
+ SystemPath add:(OperatingSystem getHomeDirectory).
+ (OperatingSystem isDirectory:(OperatingSystem getHomeDirectory , '/.smalltalk')) ifTrue:[
+ SystemPath add:(OperatingSystem getHomeDirectory , '/.smalltalk')
+ ].
+ p := OperatingSystem getEnvironment:'SMALLTALK_LIBDIR'.
+ p notNil ifTrue:[
+ SystemPath add:p
+ ].
+ (OperatingSystem isDirectory:'/usr/local/lib/smalltalk') ifTrue:[
+ SystemPath add:'/usr/local/lib/smalltalk'
+ ].
+ (OperatingSystem isDirectory:'/usr/lib/smalltalk') ifTrue:[
+ SystemPath add:'/usr/lib/smalltalk'
+ ].
+
+ "Smalltalk initSystemPath"
+ "SystemPath"
+!
+
+start
+ "main startup, if there is a Display, initialize it
+ and start dispatching; otherwise go into a read-eval-print loop"
+
+ Initializing := true.
+
+ "read patches- and rc-file, do not add things into change-file"
+
+ Class updateChanges:false.
+ [
+ self fileIn:'patches'.
+
+ (self fileIn:((Arguments at:1) , '.rc')) ifFalse:[
+ "no .rc file where executable is; try default smalltalk.rc"
+ self fileIn:'smalltalk.rc'
+ ]
+ ] valueNowOrOnUnwindDo:[Class updateChanges:true].
+
+ SilentLoading ifFalse:[
+ Transcript showCr:(self hello).
+ Transcript showCr:(self copyright).
+ Transcript cr
+ ].
+
+ Initializing := false.
+ DemoMode ifTrue:[
+ Transcript showCr:'Unlicensed demo mode with limitations.'
+ ].
+
+ [self saveMainLoop] whileTrue:[ ].
+
+ "done"
+
+ self exit
+!
+
+restart
+ "startup after an image has been loaded
+ "
+ |deb insp|
+
+ Initializing := true.
+
+ "temporary switch back to dumb interface"
+
+ insp := Inspector.
+ deb := Debugger.
+ Inspector := MiniInspector.
+ Debugger := MiniDebugger.
+
+ ObjectMemory changed:#restarted.
+
+ "
+ some must be reinitialized before ...
+ - sorry, but order is important
+ "
+
+ Workstation reinitialize.
+ View reinitialize.
+
+ ObjectMemory changed:#returnFromSnapshot.
+
+ OperatingSystem enableUserInterrupts.
+ OperatingSystem enableSignalInterrupts.
+
+ Inspector := insp.
+ Debugger := deb.
+
+ Initializing := false.
+
+
+ "
+ if there is no Transcript, go to stderr
+ "
+ Transcript isNil ifTrue:[
+ self initStandardStreams.
+ Transcript := Stderr
+ ].
+
+ Transcript cr.
+ Transcript showCr:('Smalltalk restarted from:' , ImageName).
+ DemoMode ifTrue:[
+ Transcript showCr:'Unlicensed demo mode with limitations.'
+ ].
+
+ "this allows firing an application by defining
+ these two globals during snapshot ..."
+
+ StartupClass notNil ifTrue:[
+ StartupSelector notNil ifTrue:[
+
+ "allow customization by reading an image specific rc-file"
+ ImageName notNil ifTrue:[
+ (ImageName endsWith:'.img') ifTrue:[
+ self fileIn:((ImageName copyFrom:1 to:(ImageName size - 4)), '.rc')
+ ] ifFalse:[
+ self fileIn:(ImageName , '.rc')
+ ]
+ ].
+ StartupClass perform:StartupSelector
+ ]
+ ].
+
+ Display notNil ifTrue:[
+ Display dispatch
+ ] ifFalse:[
+ self readEvalPrint
+ ].
+
+ self exit
+!
+
+saveMainLoop
+ "main dispatching loop - exits with true for a bad exit (to restart),
+ false for real exit"
+
+ Smalltalk at:#SignalCatchBlock put:[^ true].
+
+ "if view-classes exist, start dispatching;
+ otherwise go into a read-eval-print loop"
+
+ Display notNil ifTrue:[
+ Display dispatch
+ ] ifFalse:[
+ self readEvalPrint
+ ].
+ ^ false
+!
+
+readEvalPrint
+ "simple read-eval-print loop for non-graphical Tinytalk"
+
+ |text|
+
+ 'ST- ' print.
+ Stdin skipSeparators.
+ text := Stdin nextChunk.
+ [text notNil] whileTrue:[
+ (Compiler evaluate:text) printNewline.
+ 'ST- ' print.
+ text := Stdin nextChunk
+ ].
+ '' printNewline
+! !
+
+!Smalltalk class methodsFor:'accessing'!
+
+at:aKey
+ "retrieve the value stored under aKey, a symbol"
+
+%{ /* NOCONTEXT */
+ extern OBJ _GETGLOBAL();
+
+ RETURN ( _GETGLOBAL(aKey) );
+%}
+!
+
+at:aKey ifAbsent:aBlock
+ "retrieve the value stored under aKey.
+ If there is none stored this key, return the value of
+ the evaluation of aBlock"
+
+ (self includesKey:aKey) ifTrue:[
+ ^ self at:aKey
+ ].
+ ^ aBlock value
+!
+
+at:aKey put:aValue
+ "store the argument aValue under aKey, a symbol"
+
+ CachedClasses := nil.
+
+%{ /* NOCONTEXT */
+ extern OBJ _SETGLOBAL();
+
+ RETURN ( _SETGLOBAL(aKey, aValue, (OBJ *)0) );
+%}
+!
+
+removeKey:aKey
+ "remove the argument from the globals dictionary"
+
+ CachedClasses := nil.
+
+%{ /* NOCONTEXT */
+ extern OBJ _REMOVEGLOBAL();
+
+ RETURN ( _REMOVEGLOBAL(aKey) );
+%}
+!
+
+includesKey:aKey
+ "return true, if the key is known"
+
+%{ /* NOCONTEXT */
+ extern OBJ _KEYKNOWN();
+
+ RETURN ( _KEYKNOWN(aKey) );
+%}
+!
+
+keyAtValue:anObject
+ "return the symbol under which anObject is stored - or nil"
+
+ self allKeysDo:[:aKey |
+ (self at:aKey) == anObject ifTrue:[^ aKey]
+ ]
+
+ "Smalltalk keyAtValue:Object"
+!
+
+keys
+ "return a collection with all keys in the Smalltalk dictionary"
+
+ |keys|
+
+ keys := OrderedCollection new.
+ self allKeysDo:[:k | keys add:k].
+ ^ keys
+! !
+
+!Smalltalk class methodsFor:'copying'!
+
+shallowCopy
+ "redefine copy - there is only one Smalltalk dictionary"
+
+ ^ self
+!
+
+deepCopy
+ "redefine copy - there is only one Smalltalk dictionary"
+
+ ^ self
+! !
+
+!Smalltalk class methodsFor:'inspecting'!
+
+inspect
+ "redefined to launch a DictionaryInspector on the receiver
+ (instead of the default InspectorView)."
+
+ DictionaryInspectorView isNil ifTrue:[
+ super inspect
+ ] ifFalse:[
+ DictionaryInspectorView openOn:self
+ ]
+! !
+
+!Smalltalk class methodsFor:'misc stuff'!
+
+addExitBlock:aBlock
+ "add a block to be executed when Smalltalk finishes"
+
+ exitBlocks isNil ifTrue:[
+ exitBlocks := Array with:aBlock
+ ] ifFalse:[
+ exitBlocks add:aBlock
+ ]
+!
+
+exit
+ "finish Smalltalk system"
+
+ exitBlocks notNil ifTrue:[
+ exitBlocks do:[:aBlock |
+ aBlock value
+ ]
+ ].
+%{
+ mainExit(0);
+%}
+.
+ OperatingSystem exit
+
+ "Smalltalk exit"
+!
+
+sleep:aDelay
+ "wait for aDelay seconds"
+
+ OperatingSystem sleep:aDelay
+! !
+
+!Smalltalk class methodsFor:'debugging'!
+
+printStackBacktrace
+ "print a stack backtrace"
+
+%{
+ printStack(__context);
+%}
+!
+
+fatalAbort
+ "abort program and dump core"
+%{
+ fatal0(__context, "abort");
+%}
+!
+
+statistic
+ "print some statistic data"
+%{
+ statistic();
+%}
+!
+
+debugOn
+ "temporary"
+
+ "LookupTrace := true. "
+ MessageTrace := true.
+ "AllocTrace := true. "
+ ObjectMemory flushInlineCaches
+!
+
+debugOff
+ "temporary"
+
+ LookupTrace := nil.
+ MessageTrace := nil
+ ". AllocTrace := nil "
+!
+
+allocDebugOn
+ "temporary"
+
+ AllocTrace := true
+!
+
+allocDebugOff
+ "temporary"
+
+ AllocTrace := nil
+!
+
+executionDebugOn
+ "temporary"
+
+ ExecutionTrace := true
+!
+
+executionDebugOff
+ "temporary"
+
+ ExecutionTrace := nil
+! !
+
+!Smalltalk class methodsFor:'looping'!
+
+do:aBlock
+ "evaluate the argument, aBlock for all values in the Smalltalk dictionary"
+%{
+ __allGlobalsDo(&aBlock COMMA_CON);
+%}
+!
+
+allKeysDo:aBlock
+ "evaluate the argument, aBlock for all keys in the Smalltalk dictionary"
+%{
+ __allKeysDo(&aBlock COMMA_CON);
+%}
+!
+
+allClassesDo:aBlock
+ "evaluate the argument, aBlock for all classes in the system"
+
+ self allClasses do:aBlock
+!
+
+associationsDo:aBlock
+ "evaluate the argument, aBlock for all key/value pairs
+ in the Smalltalk dictionary"
+
+ self allKeysDo:[:aKey |
+ aBlock value:(aKey -> (self at:aKey))
+ ]
+
+ "Smalltalk associationsDo:[:assoc | assoc printNewline]"
+! !
+
+!Smalltalk class methodsFor:'queries'!
+
+numberOfGlobals
+ "return the number of global variables in the system"
+
+ |tally|
+
+ tally := 0.
+ self do:[:obj | tally := tally + 1].
+ ^ tally
+
+ "Smalltalk numberOfGlobals"
+!
+
+cellAt:aName
+ "return the address of a global cell
+ - used internally for compiler only"
+
+%{ /* NOCONTEXT */
+ extern OBJ _GETGLOBALCELL();
+
+ RETURN ( _GETGLOBALCELL(aName) );
+%}
+!
+
+references:anObject
+ "return true, if I refer to the argument, anObject
+ must be reimplemented since Smalltalk is no real collection"
+
+ self do:[:o |
+ (o == anObject) ifTrue:[^ true]
+ ].
+ ^ false
+!
+
+allClasses
+ "return a collection of all classes in the system"
+
+ CachedClasses isNil ifTrue:[
+ CachedClasses := IdentitySet new:400.
+ self do:[:anObject |
+ anObject notNil ifTrue:[
+ (anObject isBehavior) ifTrue:[
+ CachedClasses add:anObject
+ ]
+ ]
+ ]
+ ].
+ ^ CachedClasses
+
+ "Smalltalk allClasses"
+!
+
+classNames
+ "return a collection of all classNames in the system"
+
+
+ ^ self allClasses collect:[:aClass | aClass name]
+! !
+
+!Smalltalk class methodsFor:'system management'!
+
+removeClass:aClass
+ "remove the argument, aClass from the smalltalk dictionary;
+ we have to flush the caches since these methods are now void"
+
+ |sym|
+
+ sym := aClass name asSymbol.
+ ((self at:sym) == aClass) ifTrue:[
+ self at:sym put:nil. "nil it out for compiled accesses"
+ " self removeKey:sym. "
+"
+ actually could get along with less flushing
+ (entries for aClass and subclasses only)
+
+ ObjectMemory flushInlineCachesForClass:aClass.
+ ObjectMemory flushMethodCacheFor:aClass
+"
+ ObjectMemory flushInlineCaches.
+ ObjectMemory flushMethodCache
+ ]
+!
+
+browseChanges
+ "startup a changes browser"
+
+ (self at:#ChangesBrowser) notNil ifTrue:[
+ ChangesBrowser start
+ ] ifFalse:[
+ self error:'no ChangesBrowser'
+ ]
+
+ "Smalltalk browseChanges "
+!
+
+browseAllSelect:aBlock
+ "startup a browser for all methods for which aBlock returns true"
+
+ SystemBrowser browseAllSelect:aBlock
+
+ " Smalltalk browseAllSelect:[:m | m literals isNil] "
+!
+
+browseImplementorsOf:aSelectorSymbol
+ "startup a browser for all methods implementing a particular message"
+
+ SystemBrowser browseImplementorsOf:aSelectorSymbol
+
+ " Smalltalk browseImplementorsOf:#at:put: "
+!
+
+browseAllCallsOn:aSelectorSymbol
+ "startup a browser for all methods sending a particular message"
+
+ SystemBrowser browseAllCallsOn:aSelectorSymbol
+
+ " Smalltalk browseAllCallsOn:#at:put: "
+!
+
+createSourceFilesIn:aFileDirectory
+ "create a new set of sources in aFileDirectory"
+
+ |aStream|
+
+ aStream := FileStream newFileNamed:'List.proto' in:aFileDirectory.
+ aStream isNil ifTrue:[
+ ^ self error:'cannot create prototype fileList:List.proto'
+ ].
+ self allClassesDo:[:aClass |
+ (aClass isMeta) ifFalse:[
+ Transcript show:('creating source for:' , aClass name , '...').
+
+ aStream nextPutAll:(aClass name , '.o').
+ aStream cr.
+
+ aClass fileOutIn:aFileDirectory.
+
+ Transcript cr
+ ]
+ ].
+ aStream close
+!
+
+createMakefileIn:aFileDirectory
+ "create a new Makefile in aFileDirectory"
+
+ |aStream classes fileNames superIndex count onum first
+ numClasses "{ Class: SmallInteger }" |
+
+ classes := VariableArray new:200.
+ classes grow:0.
+ fileNames := VariableArray new:200.
+ fileNames grow:0.
+
+ Transcript show:'building class tree ...'.
+
+ classes add:Object.
+ fileNames add:'Object'.
+ Object allSubclassesInOrderDo:[:aClass |
+ ((classes identityIndexOf:aClass startingAt:1) == 0) ifTrue:[
+ classes add:aClass.
+ fileNames add:(Smalltalk fileNameForClass:aClass name)
+ ]
+ ].
+ Transcript cr.
+ numClasses := classes size.
+
+ aStream := FileStream newFileNamed:'Makefile' in:aFileDirectory.
+ aStream isNil ifTrue:[
+ ^ self error:'cannot create Makefile'
+ ].
+
+ aStream nextPutAll:'LIBTOP=/usr/local/lib/smalltalk'. aStream cr.
+ aStream nextPutAll:'#LIBTOP=../..'. aStream cr.
+
+ aStream nextPutAll:'INCL=include'. aStream cr.
+ aStream nextPutAll:'#INCL2=../../include'. aStream cr.
+ aStream nextPutAll:'INCL2=/usr/include/smalltalk'. aStream cr.
+
+ aStream nextPutAll:'STC=/usr/local/bin/stc'. aStream cr.
+ aStream nextPutAll:'#STC=../../stc/stc'. aStream cr.
+
+ aStream nextPutAll:'#CFLAGS=-O'. aStream cr.
+ aStream nextPutAll:'STCOPT=+optinline +optspace'. aStream cr.
+ aStream nextPutAll:'STCFLAGS=-H$(INCL) -I$(INCL) -I$(INCL2)'. aStream cr.
+
+ aStream cr.
+ aStream nextPutAll:'smalltalk: $(INCLUDE) objs main.o'. aStream cr.
+ aStream cr.
+
+ aStream nextPutAll:'main.o: $(LIBTOP)/librun/main.c'. aStream cr.
+ aStream cr.
+
+ aStream nextPutAll:'$(INCLUDE):'. aStream cr.
+ aStream tab. aStream nextPutAll:'mkdir $(INCLUDE)'. aStream cr.
+ aStream cr.
+
+ aStream nextPutAll:'.SUFFIXES: .st .o'. aStream cr.
+ aStream nextPutAll:'.st.o:'. aStream cr.
+ aStream tab. aStream nextPutAll:'$(STC) $(STCFLAGS) $(CFLAGS) -c $*.st'.
+ aStream cr.
+ aStream cr.
+
+ aStream nextPutAll:'.SUFFIXES: .st .c'. aStream cr.
+ aStream nextPutAll:'.st.c:'. aStream cr.
+ aStream tab. aStream nextPutAll:'$(STC) $(STCFLAGS) $(CFLAGS) -C $*.st'.
+ aStream cr.
+ aStream cr.
+
+
+ onum := 1.
+ count := 0.
+
+ Transcript show:'appending o-file entries ...'.
+ 1 to:numClasses do:[:index |
+ (count == 0) ifTrue:[
+ aStream nextPutAll:'objs'.
+ aStream nextPutAll:(onum printString).
+ aStream nextPutAll:':'.
+ first := true
+ ].
+ first ifFalse:[
+ aStream nextPutAll:' \'. aStream cr
+ ] ifTrue:[
+ first := false
+ ].
+ aStream tab.
+ aStream nextPutAll:((fileNames at:index) , '.o').
+ count := count + 1.
+ (count == 10) ifTrue:[
+ aStream cr.
+ count := 0.
+ onum := onum + 1
+ ]
+ ].
+ aStream cr.
+ aStream cr.
+
+ aStream nextPutAll:'objs:'.
+ first := true.
+ 1 to:onum do:[:i |
+ first ifFalse:[
+ aStream nextPutAll:' \'. aStream cr
+ ] ifTrue:[
+ first := false
+ ].
+
+ aStream tab.
+ aStream nextPutAll:'objs'.
+ aStream nextPutAll:(i printString)
+ ].
+ aStream cr.
+ aStream cr.
+
+ Transcript cr.
+
+ "create dependency info"
+ Transcript show:'append dependency entries ...'.
+
+ 1 to:numClasses do:[:index |
+ aStream nextPutAll:((fileNames at:index) , '.o:').
+ aStream tab.
+ aStream nextPutAll:((fileNames at:index) , '.st').
+ first := true.
+ (classes at:index) allSuperclassesDo:[:superClass |
+ first ifFalse:[
+ aStream nextPutAll:' \'. aStream cr
+ ] ifTrue:[
+ first := false
+ ].
+
+ superIndex := classes indexOf:superClass.
+ aStream tab.
+ aStream nextPutAll:'$(INCLUDE)/'.
+ aStream nextPutAll:((fileNames at:superIndex) , '.H')
+ ].
+ aStream cr.
+ aStream cr
+ ].
+
+ Transcript cr.
+ aStream close.
+
+ "create abbreviation file"
+ aStream := FileStream newFileNamed:'abbrev.stc' in:aFileDirectory.
+ aStream isNil ifTrue:[
+ ^ self error:'cannot create abbrev.stc'
+ ].
+ 1 to:numClasses do:[:index |
+ ((classes at:index) name ~= (fileNames at:index)) ifTrue:[
+ aStream nextPutAll:(classes at:index) name.
+ aStream tab.
+ aStream nextPutAll:(fileNames at:index).
+ aStream cr
+ ]
+ ].
+ aStream close.
+
+ "create classlist file"
+ aStream := FileStream newFileNamed:'classList.stc' in:aFileDirectory.
+ aStream isNil ifTrue:[
+ ^ self error:'cannot create classList.stc'
+ ].
+ classes do:[:aClass |
+ aStream nextPutAll:aClass name.
+ aStream cr
+ ].
+ aStream close
+
+ " Smalltalk createMakefileIn:(FileDirectory directoryNamed:'source2.6') "
+!
+
+createNewSources
+ "create a new source directory, and fileOut all classes into this"
+
+ |nextVersion dirName here fileDirectory|
+
+ nextVersion := self minorVersion + 1.
+ dirName := 'source'
+ , self majorVersion printString
+ , '.'
+ , nextVersion printString.
+ here := FileDirectory currentDirectory.
+ (here createDirectory:dirName) ifFalse:[
+ self error:'cannot create new source directory'
+ ].
+ Transcript showCr:('creating sources in ' , dirName); endEntry.
+
+ fileDirectory := FileDirectory directoryNamed:dirName in:here.
+ self createSourceFilesIn:fileDirectory.
+ self createMakefileIn:fileDirectory
+
+ " Smalltalk createNewSources "
+!
+
+systemFileStreamFor:aFileName
+ "search aFileName in some standard places;
+ return a fileStream or nil if not found"
+
+ |aStream|
+
+ SystemPath do:[:dirName |
+ aStream := FileStream readonlyFileNamed:(dirName , '/' , aFileName).
+ aStream notNil ifTrue:[^ aStream]
+ ].
+ ^ nil
+!
+
+fileNameForClass:aClassName
+ "return a good filename for aClassName -
+ using abbreviation file if there is one"
+
+ |fileName aStream abbrev line thisName index|
+
+ fileName := aClassName.
+
+ fileName size < 10 ifTrue:[^ fileName].
+
+ "too bad - look for abbreviation"
+
+ aStream := self systemFileStreamFor:'abbrev.stc'.
+ aStream notNil ifTrue:[
+ [aStream atEnd] whileFalse:[
+ line := aStream nextLine.
+ line notNil ifTrue:[
+ (line countWords == 2) ifTrue:[
+ index := line indexOfSeparatorStartingAt:1.
+ (index ~~ 0) ifTrue:[
+ thisName := line copyFrom:1 to:(index - 1).
+ (thisName = fileName) ifTrue:[
+ abbrev := (line copyFrom:index) withoutSeparators.
+ aStream close.
+ ^ abbrev
+ ]
+ ]
+ ]
+ ]
+ ].
+ aStream close
+ ].
+
+ "no file found"
+ OperatingSystem maxFileNameLength >= (fileName size + 3) ifTrue:[
+ " self warn:'filename ' , fileName , ' will not work on sys5 machines' "
+ ] ifFalse:[
+ self error:'cant find short for ' , fileName , ' in abbreviation file'
+ ].
+ ^ fileName
+!
+
+fileInClassObject:aClassName from:aFileName
+ "read in the named object file - look for it in some standard places;
+ return true if ok, false if failed"
+
+ |aStream|
+
+ aStream := self systemFileStreamFor:aFileName.
+ aStream isNil ifTrue:[^ false].
+ aStream close.
+
+ (ObjectFileLoader loadClass:aClassName fromObjectFile:aFileName) isNil ifTrue:[^ false].
+ ^ true
+
+ " Smalltalk fileInClassObject:'AbstractPath' from:'../goodies/Paths/AbstrPath.o' "
+!
+
+fileIn:aFileName
+ "read in the named file - look for it in some standard places;
+ return true if ok, false if failed"
+
+ |aStream|
+
+ aStream := self systemFileStreamFor:aFileName.
+ aStream isNil ifTrue:[^ false].
+
+ [aStream fileIn] valueNowOrOnUnwindDo:[aStream close].
+ ^ true
+
+ " Smalltalk fileIn:'games/TicTacToe.st' "
+!
+
+fileInChanges
+ "read in the last changes file - bringing the system to the state it
+ had when left the last time"
+
+ |upd|
+
+ "tell Class to NOT update the changes file now ..."
+ upd := Class updateChanges:false.
+ [self fileIn:'changes'] valueNowOrOnUnwindDo:[Class updateChanges:upd]
+
+ "Smalltalk fileInChanges "
+!
+
+fileInClass:aClassName
+ "find a source/object file for aClassName and -if found - load it"
+
+ |fName newClass upd|
+
+ fName := self fileNameForClass:aClassName.
+ fName notNil ifTrue:[
+ upd := Class updateChanges:false.
+ [
+ (self fileIn:('fileIn/' , fName , '.ld')) ifFalse:[
+ (self fileInClassObject:aClassName from:('binary/' , fName, '.so')) ifFalse:[
+ (self fileInClassObject:aClassName from:('binary/' , fName, '.o')) ifFalse:[
+ self fileIn:(fName , '.st')
+ ]
+ ]
+ ]
+ ] valueNowOrOnUnwindDo:[Class updateChanges:upd].
+ newClass := self at:(aClassName asSymbol).
+ (newClass notNil
+ and:[newClass implements:#initialize]) ifTrue:[newClass initialize]
+ ]
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SortColl.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,234 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+OrderedCollection subclass:#SortedCollection
+ instanceVariableNames:'sortBlock'
+ classVariableNames:'DefaultSortBlock'
+ poolDictionaries:''
+ category:'Collections-Ordered'
+!
+
+SortedCollection comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+I keep my elements sorted. The sort order is defined by a sortblock,
+which is a two-argument block which, when given two elements of the
+collection, returns true if the element given as first arg has to
+come before the element given as second arg.
+Thus a sortBlock of [:a :b | a < b] defines ascending sort-order,
+while [:a :b | a > b] defines descening order.
+
+%W% %E%
+'!
+
+!SortedCollection class methodsFor:'initialization'!
+
+initialize
+ DefaultSortBlock := [:a :b | a < b ]
+
+ "SortedCollection initialize"
+! !
+
+!SortedCollection class methodsFor:'instance creation'!
+
+new
+ ^ super new setSortBlock:DefaultSortBlock
+!
+
+new:size
+ ^ (super new:size) setSortBlock:DefaultSortBlock
+!
+
+sortBlock:aBlock
+ ^ super new setSortBlock:aBlock
+! !
+
+!SortedCollection methodsFor:'adding & removing'!
+
+addFirst:anObject
+ self shouldNotImplement
+!
+
+addLast:anObject
+ self shouldNotImplement
+!
+
+at:index put:anObject
+ self shouldNotImplement
+!
+
+add:newObject after:oldObject
+ self shouldNotImplement
+!
+
+add:newObject before:oldObject
+ self shouldNotImplement
+!
+
+addAll:aCollection
+ "add all elements of the argument, aCollection to the
+ receiver"
+
+ |mySize "{ Class: SmallInteger }"
+ otherSize "{ Class: SmallInteger }"
+ dstIndex "{ Class: SmallInteger }"
+ newSize newContents|
+
+ "if aCollection is bigger than a threshhold, its faster
+ to add all and resort - question: what is a good treshhold ?"
+
+ mySize := self size.
+ otherSize := aCollection size.
+ ((mySize == 0) or:[otherSize > 5]) ifTrue:[
+ newSize := mySize + otherSize.
+ newContents := Array new:newSize.
+ newContents replaceFrom:1 to:mySize with:contentsArray startingAt:1.
+ (aCollection isKindOf:SequenceableCollection) ifTrue:[
+ "maybe we can do it in one big move"
+ newContents replaceFrom:(mySize + 1) to:newSize with:aCollection startingAt:1.
+ ] ifFalse:[
+ dstIndex := mySize + 1.
+ aCollection do:[:element |
+ newContents at:dstIndex put:element.
+ dstIndex := dstIndex + 1
+ ]
+ ].
+ firstIndex := 1.
+ lastIndex := newSize.
+ contentsArray := newContents.
+ contentsArray sort:sortBlock.
+ ^ self
+ ].
+ super addAll:aCollection
+
+ "#(7 3 9 10 99) asSortedCollection addAll:#(77 0 1 16 5)"
+!
+
+add:anObject
+ "add the argument, anObject at the proper place in the
+ receiver"
+
+ |sz index|
+
+ sz := self size.
+ sz == 0 ifTrue:[
+ super addLast:anObject
+ ] ifFalse:[
+ index := self findIndexFor:anObject.
+ self makeRoomAtIndex:index.
+ contentsArray at:index put:anObject
+ ].
+ ^ anObject
+! !
+
+!SortedCollection methodsFor:'testing'!
+
+includes:anObject
+ "return true, if the argument, anObject is in the collection.
+ (can reduce the number of compares since we are sorted)"
+
+ |index|
+
+ index := self findIndexFor:anObject.
+ ^ (index <= self size) and:[(contentsArray at:index) = anObject]
+!
+
+occurrencesOf:anObject
+ "return how many times the argument, anObject is in the collection.
+ (can reduce the number of compares since we are sorted)"
+
+ |index "{ Class: SmallInteger }"
+ mySize "{ Class: SmallInteger }"
+ tally "{ Class: SmallInteger }" |
+
+ index := self findIndexFor:anObject.
+ mySize := self size.
+ index > mySize ifTrue:[^ 0].
+ tally := 0.
+ [(index <= mySize) and:[(contentsArray at:index) = anObject]] whileTrue:[
+ tally := tally + 1.
+ index := index + 1
+ ].
+ ^ tally
+! !
+
+!SortedCollection methodsFor:'instance protocol'!
+
+sortBlock
+ "return the block used for sorting"
+
+ ^ sortBlock
+!
+
+sortBlock:aSortBlock
+ "change the sort criteria for a sorted collection, resort the elements of
+ the collection, and return the receiver. The argument, aSortBlock must
+ be a two-argument block which returns true if its arg1 has to come before
+ its arg2 in the collection."
+
+ sortBlock := aSortBlock.
+ lastIndex > firstIndex ifTrue:[
+ contentsArray quickSortFrom:firstIndex to:lastIndex sortBlock:aSortBlock
+ ]
+
+ "#(9 8 7 6 5 4 3) asSortedCollection"
+ "#(9 8 7 6 5 4 3) asSortedCollection sortBlock:[:a : b | a > b]"
+ "#($f $G $z $Y $o $H) asSortedCollectionSortedCollection"
+ "#($f $G $z $Y $o $H) asSortedCollection sortBlock:[:a : b | a asUppercase < b asUppercase]"
+! !
+
+!SortedCollection methodsFor:'enumerating'!
+
+collect: aBlock
+ | newOrderedCollection |
+ newOrderedCollection := OrderedCollection new.
+ self do: [ :element | newOrderedCollection add: (aBlock value: element) ].
+ ^newOrderedCollection
+! !
+
+!SortedCollection methodsFor:'private'!
+
+setSortBlock: aSortBlock
+ "set the sortblock without resorting - private only"
+
+ sortBlock := aSortBlock
+!
+
+findIndexFor:anObject
+ "search the index at which to insert anObject. Can also be used
+ to search for an existing element by checking if the element at
+ the returned index is the one we look for."
+
+ |low "{ Class: SmallInteger}"
+ high "{ Class: SmallInteger}"
+ middle "{ Class: SmallInteger}"
+ element|
+
+ low := firstIndex.
+ high := lastIndex.
+ [low <= high] whileTrue:[
+ middle := (low + high) // 2.
+ element := super at:middle.
+ (sortBlock value:element value:anObject) ifTrue:[
+ "middleelement is smaller than object"
+ low := middle + 1
+ ] ifFalse:[
+ high := middle - 1
+ ]
+ ].
+ ^ low
+
+ "#(1 2 3 4 7 99 1313 981989 898989898) asSortedCollection"
+
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SortedCollection.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,234 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+OrderedCollection subclass:#SortedCollection
+ instanceVariableNames:'sortBlock'
+ classVariableNames:'DefaultSortBlock'
+ poolDictionaries:''
+ category:'Collections-Ordered'
+!
+
+SortedCollection comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+I keep my elements sorted. The sort order is defined by a sortblock,
+which is a two-argument block which, when given two elements of the
+collection, returns true if the element given as first arg has to
+come before the element given as second arg.
+Thus a sortBlock of [:a :b | a < b] defines ascending sort-order,
+while [:a :b | a > b] defines descening order.
+
+%W% %E%
+'!
+
+!SortedCollection class methodsFor:'initialization'!
+
+initialize
+ DefaultSortBlock := [:a :b | a < b ]
+
+ "SortedCollection initialize"
+! !
+
+!SortedCollection class methodsFor:'instance creation'!
+
+new
+ ^ super new setSortBlock:DefaultSortBlock
+!
+
+new:size
+ ^ (super new:size) setSortBlock:DefaultSortBlock
+!
+
+sortBlock:aBlock
+ ^ super new setSortBlock:aBlock
+! !
+
+!SortedCollection methodsFor:'adding & removing'!
+
+addFirst:anObject
+ self shouldNotImplement
+!
+
+addLast:anObject
+ self shouldNotImplement
+!
+
+at:index put:anObject
+ self shouldNotImplement
+!
+
+add:newObject after:oldObject
+ self shouldNotImplement
+!
+
+add:newObject before:oldObject
+ self shouldNotImplement
+!
+
+addAll:aCollection
+ "add all elements of the argument, aCollection to the
+ receiver"
+
+ |mySize "{ Class: SmallInteger }"
+ otherSize "{ Class: SmallInteger }"
+ dstIndex "{ Class: SmallInteger }"
+ newSize newContents|
+
+ "if aCollection is bigger than a threshhold, its faster
+ to add all and resort - question: what is a good treshhold ?"
+
+ mySize := self size.
+ otherSize := aCollection size.
+ ((mySize == 0) or:[otherSize > 5]) ifTrue:[
+ newSize := mySize + otherSize.
+ newContents := Array new:newSize.
+ newContents replaceFrom:1 to:mySize with:contentsArray startingAt:1.
+ (aCollection isKindOf:SequenceableCollection) ifTrue:[
+ "maybe we can do it in one big move"
+ newContents replaceFrom:(mySize + 1) to:newSize with:aCollection startingAt:1.
+ ] ifFalse:[
+ dstIndex := mySize + 1.
+ aCollection do:[:element |
+ newContents at:dstIndex put:element.
+ dstIndex := dstIndex + 1
+ ]
+ ].
+ firstIndex := 1.
+ lastIndex := newSize.
+ contentsArray := newContents.
+ contentsArray sort:sortBlock.
+ ^ self
+ ].
+ super addAll:aCollection
+
+ "#(7 3 9 10 99) asSortedCollection addAll:#(77 0 1 16 5)"
+!
+
+add:anObject
+ "add the argument, anObject at the proper place in the
+ receiver"
+
+ |sz index|
+
+ sz := self size.
+ sz == 0 ifTrue:[
+ super addLast:anObject
+ ] ifFalse:[
+ index := self findIndexFor:anObject.
+ self makeRoomAtIndex:index.
+ contentsArray at:index put:anObject
+ ].
+ ^ anObject
+! !
+
+!SortedCollection methodsFor:'testing'!
+
+includes:anObject
+ "return true, if the argument, anObject is in the collection.
+ (can reduce the number of compares since we are sorted)"
+
+ |index|
+
+ index := self findIndexFor:anObject.
+ ^ (index <= self size) and:[(contentsArray at:index) = anObject]
+!
+
+occurrencesOf:anObject
+ "return how many times the argument, anObject is in the collection.
+ (can reduce the number of compares since we are sorted)"
+
+ |index "{ Class: SmallInteger }"
+ mySize "{ Class: SmallInteger }"
+ tally "{ Class: SmallInteger }" |
+
+ index := self findIndexFor:anObject.
+ mySize := self size.
+ index > mySize ifTrue:[^ 0].
+ tally := 0.
+ [(index <= mySize) and:[(contentsArray at:index) = anObject]] whileTrue:[
+ tally := tally + 1.
+ index := index + 1
+ ].
+ ^ tally
+! !
+
+!SortedCollection methodsFor:'instance protocol'!
+
+sortBlock
+ "return the block used for sorting"
+
+ ^ sortBlock
+!
+
+sortBlock:aSortBlock
+ "change the sort criteria for a sorted collection, resort the elements of
+ the collection, and return the receiver. The argument, aSortBlock must
+ be a two-argument block which returns true if its arg1 has to come before
+ its arg2 in the collection."
+
+ sortBlock := aSortBlock.
+ lastIndex > firstIndex ifTrue:[
+ contentsArray quickSortFrom:firstIndex to:lastIndex sortBlock:aSortBlock
+ ]
+
+ "#(9 8 7 6 5 4 3) asSortedCollection"
+ "#(9 8 7 6 5 4 3) asSortedCollection sortBlock:[:a : b | a > b]"
+ "#($f $G $z $Y $o $H) asSortedCollectionSortedCollection"
+ "#($f $G $z $Y $o $H) asSortedCollection sortBlock:[:a : b | a asUppercase < b asUppercase]"
+! !
+
+!SortedCollection methodsFor:'enumerating'!
+
+collect: aBlock
+ | newOrderedCollection |
+ newOrderedCollection := OrderedCollection new.
+ self do: [ :element | newOrderedCollection add: (aBlock value: element) ].
+ ^newOrderedCollection
+! !
+
+!SortedCollection methodsFor:'private'!
+
+setSortBlock: aSortBlock
+ "set the sortblock without resorting - private only"
+
+ sortBlock := aSortBlock
+!
+
+findIndexFor:anObject
+ "search the index at which to insert anObject. Can also be used
+ to search for an existing element by checking if the element at
+ the returned index is the one we look for."
+
+ |low "{ Class: SmallInteger}"
+ high "{ Class: SmallInteger}"
+ middle "{ Class: SmallInteger}"
+ element|
+
+ low := firstIndex.
+ high := lastIndex.
+ [low <= high] whileTrue:[
+ middle := (low + high) // 2.
+ element := super at:middle.
+ (sortBlock value:element value:anObject) ifTrue:[
+ "middleelement is smaller than object"
+ low := middle + 1
+ ] ifFalse:[
+ high := middle - 1
+ ]
+ ].
+ ^ low
+
+ "#(1 2 3 4 7 99 1313 981989 898989898) asSortedCollection"
+
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Stream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,180 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Stream
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams'
+!
+
+Stream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+'!
+
+!Stream class methodsFor:'instance creation'!
+
+new
+ "report an error - Streams are created using on:-messages"
+
+ ^ self error:'Streams cannot be created with new'
+! !
+
+!Stream methodsFor:'accessing'!
+
+contents
+ "return the contents of the stream
+ - we do not know here how to do it, it must be redefined in subclass"
+
+ ^ self subclassResponsibility
+! !
+
+!Stream methodsFor:'accessing-reading'!
+
+next
+ "return the next element of the stream
+ - we do not know here how to do it, it must be redefined in subclass"
+
+ ^ self subclassResponsibility
+!
+
+next:count
+ "return the next count elements of the stream as a Collection"
+
+ |array|
+
+ array := Array new:count.
+ 1 to:count do: [:index |
+ array at:index put:(self next)
+ ].
+ ^ array
+!
+
+nextPeek
+ "advance to next element and return the peeked element"
+
+ self next.
+ ^ self peek
+!
+
+nextMatchFor:anObject
+ "read from the receiver, searching for the argument, anObject.
+ if the end is reached, return nil;
+ otherwise return the argument, anObject"
+
+ |nextElement|
+
+ [self atEnd] whileFalse:[
+ nextElement := self next.
+ (nextElement == anObject) ifTrue: [ ^ nextElement ]
+ ].
+ ^ nil
+! !
+
+!Stream methodsFor:'accessing-writing'!
+
+nextPut:anObject
+ "put the argument, anObject onto the receiver
+ - we do not know here how to do it, it must be redefined in subclass"
+
+ ^ self subclassResponsibility
+!
+
+next:count put:anObject
+ "put the argument, anObject count times onto the receiver"
+
+ count timesRepeat:[self nextPut:anObject].
+ ^ anObject
+!
+
+nextPutAll:aCollection
+ "put all elements of the argument, aCollection onto the receiver"
+
+ aCollection do:[:element |
+ self nextPut:element
+ ].
+ ^ aCollection
+!
+
+nextPutAll:aCollection startingAt:first to:last
+ "put the elements with index from first to last
+ of the argument, aCollection onto the receiver"
+
+ aCollection from:first to:last do:[:element |
+ self nextPut:element
+ ].
+ ^ aCollection
+!
+
+show:aString
+ "put all elements of the argument, aString onto the receiver;
+ this makes streams somewhat compatible to TextCollectors and
+ allows you to say: Smalltalk at:#Transcript put:Stdout"
+
+ ^ self nextPutAll:aString printString
+!
+
+showCr:aString
+ "put all elements of the argument, aString onto the receiver;
+ and append a newline.
+ for compatibility with TextCollectors"
+
+ self show:aString.
+ self cr
+! !
+
+!Stream methodsFor:'closing'!
+
+close
+ "close the stream - nothing done here"
+
+ ^ self
+! !
+
+!Stream methodsFor:'testing'!
+
+atEnd
+ "return true if the end of the stream has been reached;
+ - we do not know here how to do it, it must be redefined in subclass"
+
+ ^ self subclassResponsibility
+! !
+
+!Stream methodsFor:'enumerating'!
+
+do:aBlock
+ "evaluate the argument, aBlock for every element up to the end of the
+ stream"
+
+ [self atEnd] whileFalse:[
+ aBlock value:(self next)
+ ]
+! !
+
+!Stream methodsFor:'queries'!
+
+isStream
+ "return true, if the receiver is some kind of Stream."
+
+ ^ true
+!
+
+lineLength
+ "this is just a layout hint for prettyprinting functions
+ - for compatibility with TextCollectors"
+
+ ^ 80
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/String.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1761 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ByteArray subclass:#String
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Text'
+!
+
+String comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+#include <ctype.h>
+%}
+
+!String class methodsFor:'documentation'!
+
+documentation
+"
+Strings are ByteArrays storing Characters.
+
+Strings are kind of kludgy: to allow for easy handling by c-functions,
+there is always one 0-byte added at the end, which is not counted
+in size. also, the at:put: method does not allow for storing 0-bytes.
+(to do this, the basicAt:put: and basicNew: methods are redefined)
+
+You cannot add any instvars to String, since the code (also in the run time
+system & compiler) knows that strings have no named instvars. If you need
+to, you have to create a subclass.
+"
+! !
+
+!String class methodsFor:'instance creation'!
+
+basicNew:anInteger
+ "return a new empty string with anInteger characters"
+
+%{ /* NOCONTEXT */
+
+ OBJ newString;
+ REGISTER int len;
+ REGISTER unsigned char *cp;
+ REGISTER OBJ *op;
+ int nInstVars, instsize;
+ extern OBJ new();
+
+ if (_isSmallInteger(anInteger)) {
+ len = _intVal(anInteger);
+ if (len >= 0) {
+ nInstVars = _intVal(_ClassInstPtr(self)->c_ninstvars);
+ instsize = OHDR_SIZE + (nInstVars * sizeof(OBJ)) + len + 1;
+ PROTECT(self);
+ _qNew(newString, instsize, SENDER);
+ UNPROTECT(self);
+ _InstPtr(newString)->o_class = self;
+
+ if (nInstVars) {
+#if defined(memset4)
+ memset4(_InstPtr(newString)->i_instvars, nil, nInstVars);
+#else
+# if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+ /*
+ * knowing that nil is 0
+ */
+ memset(_InstPtr(newString)->i_instvars, 0, instsize - OHDR_SIZE);
+# else
+ op = _InstPtr(newString)->i_instvars;
+ do {
+ *op++ = nil;
+ } while (--nInstVars);
+# endif
+#endif
+ cp = _stringVal(newString) + (nInstVars * sizeof(OBJ));
+ } else
+ cp = _stringVal(newString);
+
+#ifdef FAST_MEMSET
+ memset(cp, ' ', len);
+ *(cp + len) = '\0';
+#else
+ while (len--)
+ *cp++ = ' ';
+ *cp = '\0';
+#endif
+ RETURN (newString);
+ }
+ }
+%}
+.
+ ^ (super basicNew:anInteger) atAllPut:(Character space)
+!
+
+new:anInteger
+ "same as basicNew - to avoid another send"
+
+ ^ self basicNew:anInteger
+!
+
+basicNew
+ "return a new empty string"
+
+ ^ self basicNew:0
+!
+
+new
+ "return a new empty string"
+
+ ^ self basicNew:0
+!
+
+unititializedNew:anInteger
+ "redefine it back - strings must have a 0-byte at the end"
+
+ ^ self basicNew:anInteger
+!
+
+fromString:aString
+ "return a copy of the argument, aString"
+
+ ^ aString copyFrom:1 to:(aString size)
+! !
+
+!String methodsFor:'accessing'!
+
+basicSize
+ "return the number of characters in myself"
+
+%{ /* NOCONTEXT */
+
+ if ((_qClass(self) == String) || (_qClass(self) == Symbol)) {
+ RETURN ( _MKSMALLINT(_stringSize(self)) );
+ }
+%}
+.
+ ^ super basicSize - 1
+!
+
+size
+ "return the number of characters in myself
+ - reimplemented here to avoid double send (size -> basicSize)"
+
+%{ /* NOCONTEXT */
+
+ if ((_qClass(self) == String) || (_qClass(self) == Symbol)) {
+ RETURN ( _MKSMALLINT(_stringSize(self)) );
+ }
+%}
+.
+ ^ super basicSize - 1
+!
+
+basicAt:index
+ "return the character at position index, an Integer
+ - reimplemented here since we return characters"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int indx;
+ int len;
+
+ if (_isSmallInteger(index)) {
+ indx = _intVal(index);
+ if (_qClass(self) != String)
+ indx += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+ len = _stringSize(self);
+ if ((indx > 0) && (indx <= len)) {
+ RETURN ( _MKCHARACTER(_stringVal(self)[indx-1] & 0xFF) );
+ }
+ }
+%}
+.
+ self subscriptBoundsError:index
+!
+
+basicAt:index put:aCharacter
+ "store the argument, aCharacter at position index, an Integer
+ - reimplemented here since we store characters"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int value, indx;
+ int len;
+
+ if (_isCharacter(aCharacter)) {
+ value = _intVal(_characterVal(aCharacter));
+ if (value && _isSmallInteger(index)) {
+ indx = _intVal(index);
+ if (_qClass(self) != String)
+ indx += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+ len = _stringSize(self);
+ if ((indx > 0) && (indx <= len)) {
+ _stringVal(self)[indx-1] = value;
+ RETURN ( aCharacter );
+ }
+ }
+ }
+%}
+.
+ (aCharacter isMemberOf:Character) ifFalse:[
+ self elementNotCharacter
+ ] ifTrue:[
+ (aCharacter asciiValue == 0) ifTrue:[
+ self error:'0-character not allowed in strings'
+ ] ifFalse:[
+ self subscriptBoundsError:index
+ ]
+ ]
+! !
+
+!String methodsFor:'converting'!
+
+asUppercase
+ "return a copy of myself in uppercase letters"
+
+ |newStr
+ mySize "{ Class: SmallInteger }" |
+
+ mySize := self size.
+ newStr := self species new:mySize.
+ 1 to:mySize do:[:i |
+ newStr at:i put:(self at:i) asUppercase
+ ].
+ ^newStr
+!
+
+asLowercase
+ "return a copy of myself in lowercase letters"
+
+ |newStr
+ mySize "{ Class: SmallInteger }" |
+
+ mySize := self size.
+ newStr := self species new:mySize.
+ 1 to:mySize do:[:i |
+ newStr at:i put:(self at:i) asLowercase
+ ].
+ ^newStr
+!
+
+asString
+ "return myself - I am a string"
+
+ ^ self
+!
+
+asSymbol
+ "return a unique symbol with name taken from myself.
+ The argument must be a String, subclass instances are not allowed."
+%{
+ if (_qClass(self) == String) {
+ RETURN ( _MKSYMBOL(_stringVal(self), (OBJ *)0, __context) );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+asText
+ "return a Text-object (collection of lines) from myself"
+
+ ^ Text from:self
+!
+
+asNumber
+ "read a number from the receiver"
+
+ ^ Number readFromString:self
+
+ "'123' asNumber"
+ "'123.567' asNumber"
+ "'(5/6)' asNumber"
+!
+
+asFilename
+ "return a Filename with pathname taken from the receiver"
+
+ ^ Filename named:self
+! !
+
+!String methodsFor:'printing & storing'!
+
+printOn:aStream
+ "print the receiver on aStream"
+
+ aStream nextPutAll:self
+!
+
+printString
+ "return a string for printing - thats myself"
+
+ ^ self
+!
+
+print
+ "print the receiver on standard output - for debugging only"
+
+%{ /* NOCONTEXT */
+
+ if (_qClass(self) == String) {
+ printf("%s", _stringVal(self));
+ RETURN (self);
+ }
+%}
+.
+ super print
+!
+
+printfPrintString:formatString
+ "non-portable but sometimes useful.
+ return a printed representation of the receiver
+ as specified by formatString, which is defined by printf.
+ No checking on overrunning the buffer - the result must be shorter than 8k chars"
+
+%{ /* NOCONTEXT */
+
+ char buffer[8192];
+ char *cp;
+
+ if (_isString(formatString)) {
+#ifdef THIS_CONTEXT
+ /* mhmh - sprintf seems to destroy thisContext (if its in a register) */
+ OBJ sav = __thisContext;
+#endif
+ cp = (char *)_stringVal(self);
+ if (_qClass(self) != String)
+ cp += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+ sprintf(buffer, (char *)_stringVal(formatString), cp);
+#ifdef THIS_CONTEXT
+ __thisContext = sav;
+#endif
+ RETURN ( _MKSTRING(buffer COMMA_SND) );
+ }
+%}
+.
+ self primitiveFailed
+
+ "'hello' printfPrintString:'%%s -> %s'"
+ "'hello' printfPrintString:'%%10s -> %10s'"
+ "'hello' printfPrintString:'%%-10s -> %-10s'"
+!
+
+displayString
+ "return a string to display the receiver - use storeString to have
+ quotes around"
+
+ ^ self storeString
+!
+
+storeString
+ "return a String for storing myself"
+
+ |s|
+
+ (self includes:$') ifTrue:[
+ s := ''''.
+ self do:[:thisChar |
+ (thisChar == $') ifTrue:[s := s , ''''].
+ s := s copyWith:thisChar
+ ].
+ s := s , ''''.
+ ^ s
+ ].
+ ^ '''' asString , self , '''' asString
+!
+
+storeOn:aStream
+ "put the storeString of myself on aStream"
+
+ aStream nextPut: $'.
+ (self includes:$') ifTrue:[
+ self do:[:thisChar |
+ (thisChar == $') ifTrue:[aStream nextPut:thisChar].
+ aStream nextPut:thisChar
+ ]
+ ] ifFalse:[
+ aStream nextPutAll:self
+ ].
+ aStream nextPut:$'
+! !
+
+!String methodsFor:'comparing'!
+
+hash
+ "return an integer useful as a hash-key"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int g, val;
+ REGISTER unsigned char *cp, *cp0;
+ int l;
+
+ cp = _stringVal(self);
+ l = _stringSize(self);
+ if (_qClass(self) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+ cp += n;
+ l -= n;
+ }
+
+ /*
+ * this is the dragon-book algorithm with a funny start
+ * value (to give short strings a number above 8192)
+ */
+ val = 12345;
+ for (cp0 = cp, cp += l - 1; cp >= cp0; cp--) {
+ val = (val << 5) + (*cp & 0x1F);
+ if (g = (val & 0x3E000000))
+ val ^= g >> 25 /* 23 */ /* 25 */;
+ val &= 0x3FFFFFFF;
+ }
+
+ if (l) {
+ l |= 1;
+ val = (val * l) & 0x3FFFFFFF;
+ }
+
+ RETURN ( _MKSMALLINT(val) );
+%}
+!
+
+<= something
+ "Compare the receiver with the argument and return true if the
+ receiver is less than or equal to the argument. Otherwise return false."
+
+ ^ (self > something) not
+!
+
+< something
+ "Compare the receiver with the argument and return true if the
+ receiver is less than the argument. Otherwise return false."
+
+ ^ (something > self)
+!
+
+>= something
+ "Compare the receiver with the argument and return true if the
+ receiver is greater than or equal to the argument.
+ Otherwise return false."
+
+ ^ (something > self) not
+!
+
+> aString
+ "Compare the receiver with the argument and return true if the
+ receiver is greater than the argument. Otherwise return false."
+
+%{ /* NOCONTEXT */
+
+ int len1, len2, cmp;
+ REGISTER OBJ s = aString;
+ char *cp1, *cp2;
+
+ if (_isNonNilObject(s)
+ && ((_qClass(s) == String) || (_qClass(s) == Symbol) || (_qClass(s) == _qClass(self)))) {
+ cp1 = (char *) _stringVal(self);
+ len1 = _stringSize(self);
+ if (_qClass(self) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+ cp1 += n;
+ len1 -= n;
+ }
+
+ cp2 = (char *) _stringVal(s);
+ len2 = _stringSize(s);
+ if (_qClass(s) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(s))->c_ninstvars) * sizeof(OBJ);
+
+ cp2 += n;
+ len2 -= n;
+ }
+
+ if (len1 <= len2)
+ cmp = strncmp(cp1, cp2, len1);
+ else
+ cmp = strncmp(cp1, cp2, len2);
+
+ if (cmp > 0) {
+ RETURN ( true );
+ }
+ if ((cmp == 0) && (len1 > len2)) {
+ RETURN ( true );
+ }
+ RETURN ( false );
+ }
+%}
+.
+ ^ super > aString
+!
+
+= aString
+ "Compare the receiver with the argument and return true if the
+ receiver is equal to the argument. Otherwise return false."
+
+%{ /* NOCONTEXT */
+
+ int l1, l2;
+ REGISTER OBJ s = aString;
+ char *cp1, *cp2;
+
+ if (s == self) {
+ RETURN ( true );
+ }
+ if (! _isNonNilObject(s)) {
+ RETURN ( false );
+ }
+
+ if ((_qClass(s) == String) || (_qClass(s) == Symbol) || (_qClass(s) == _qClass(self))) {
+ cp1 = (char *) _stringVal(self);
+ l1 = _stringSize(self);
+ if (_qClass(self) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+ cp1 += n;
+ l1 -= n;
+ }
+
+ cp2 = (char *) _stringVal(s);
+ l2 = _stringSize(s);
+ if (_qClass(s) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(s))->c_ninstvars) * sizeof(OBJ);
+
+ cp2 += n;
+ l2 -= n;
+ }
+
+ if (l1 != l2) {
+ RETURN ( false );
+ }
+ RETURN ( (strncmp(cp1, cp2, l1) == 0) ? true : false );
+ }
+%}
+.
+ ^ super = aString
+!
+
+~= aString
+ "Compare the receiver with the argument and return true if the
+ receiver is not equal to the argument. Otherwise return false."
+
+%{ /* NOCONTEXT */
+
+ int l1, l2;
+ REGISTER OBJ s = aString;
+ char *cp1, *cp2;
+
+ if (s == self) {
+ RETURN ( false );
+ }
+ if (! _isNonNilObject(s)) {
+ RETURN ( true );
+ }
+ if ((_qClass(s) == String) || (_qClass(s) == Symbol) || (_qClass(s) == _qClass(self))) {
+ cp1 = (char *) _stringVal(self);
+ l1 = _stringSize(self);
+ if (_qClass(self) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+ cp1 += n;
+ l1 -= n;
+ }
+
+ cp2 = (char *) _stringVal(s);
+ l2 = _stringSize(s);
+ if (_qClass(s) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(s))->c_ninstvars) * sizeof(OBJ);
+
+ cp2 += n;
+ l2 -= n;
+ }
+
+ if (l1 != l2) {
+ RETURN ( true );
+ }
+ RETURN ( (strncmp(cp1, cp2, l1) == 0) ? false : true );
+ }
+%}
+.
+ ^ super ~= aString
+! !
+
+!String methodsFor:'testing'!
+
+occurrencesOf:aCharacter
+ "count the occurrences of the argument, aCharacter in myself
+ - reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ REGISTER unsigned char *cp;
+ REGISTER int byteValue;
+ REGISTER int count;
+
+ if (_isCharacter(aCharacter)) {
+ count = 0;
+ byteValue = _intVal(_characterVal(aCharacter));
+ cp = _stringVal(self);
+ if (_qClass(self) != String)
+ cp += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+ while (*cp) {
+ if (*cp++ == byteValue) count++;
+ }
+ RETURN ( _MKSMALLINT(count) );
+ }
+%}
+.
+ ^ 0
+!
+
+includes:aCharacter
+ "return true if the argument, aCharacter is included in the receiver
+ - reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ REGISTER unsigned char *cp;
+ REGISTER int byteValue;
+ extern char *strchr();
+
+ if (_isCharacter(aCharacter)) {
+ byteValue = _intVal(_characterVal(aCharacter));
+ cp = _stringVal(self);
+ if (_qClass(self) != String)
+ cp += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+#ifdef FAST_STRCHR
+ cp = (unsigned char *) strchr(cp, _intVal(_characterVal(aCharacter)));
+ if (cp) {
+ RETURN ( true );
+ }
+#else
+ while (*cp) {
+ if (*cp == byteValue) {
+ RETURN ( true );
+ }
+ cp++;
+ }
+#endif
+ }
+%}
+.
+ ^ false
+!
+
+indexOf:aCharacter
+ "return the index of the first occurrences of the argument, aCharacter
+ in the receiver or 0 if not found - reimplemented here for speed."
+
+%{ /* NOCONTEXT */
+
+ REGISTER unsigned char *cp;
+#ifdef FAST_STRCHR
+ char *strchr();
+#else
+ REGISTER int byteValue;
+ REGISTER int index;
+#endif
+
+ if (_isCharacter(aCharacter)) {
+ cp = _stringVal(self);
+ if (_qClass(self) != String)
+ cp += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+#ifdef FAST_STRCHR
+ cp = (unsigned char *) strchr(cp, _intVal(_characterVal(aCharacter)));
+ if (cp) {
+ RETURN ( _MKSMALLINT(cp - _stringVal(self) + 1) );
+ }
+#else
+ byteValue = _intVal(_characterVal(aCharacter));
+ index = 1;
+ while (*cp) {
+ if (*cp++ == byteValue) {
+ RETURN ( _MKSMALLINT(index) );
+ }
+ index++;
+ }
+#endif
+ }
+%}
+.
+ ^ 0
+!
+
+indexOf:aCharacter startingAt:start
+ "return the index of the first occurrence of the argument, aCharacter
+ in myself starting at start, anInteger or 0 if not found;
+ - reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ REGISTER unsigned char *cp;
+ REGISTER int index, byteValue;
+#ifdef FAST_STRCHR
+ char *strchr();
+#endif
+ int len;
+
+ if (_isSmallInteger(start)) {
+ if (_isCharacter(aCharacter)) {
+ byteValue = _intVal(_characterVal(aCharacter));
+ index = _intVal(start);
+ if (index <= 0)
+ index = 1;
+ if (_qClass(self) != String)
+ index += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+ len = _stringSize(self);
+ if (index <= len) {
+ cp = _stringVal(self) + index - 1;
+#ifdef FAST_STRCHR
+ cp = (unsigned char *) strchr(cp, byteValue);
+ if (cp) {
+ RETURN ( _MKSMALLINT(cp - _stringVal(self) + 1) );
+ }
+#else
+ while (*cp) {
+ if (*cp++ == byteValue) {
+ RETURN ( _MKSMALLINT(index) );
+ }
+ index++;
+ }
+#endif
+ }
+ }
+ RETURN ( _MKSMALLINT(0) );
+ }
+%}
+.
+ ^ super indexOf:aCharacter startingAt:start
+!
+
+indexOfSeparatorStartingAt:start
+ "return the index of the next separator character"
+
+%{ /* NOCONTEXT */
+
+ REGISTER unsigned char *cp;
+ REGISTER char c;
+ int len, index;
+
+ index = _intVal(start);
+ if (index <= 0) {
+ index = 1;
+ }
+ if (_qClass(self) != String)
+ index += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+ len = _stringSize(self);
+ if (index > len) {
+ RETURN ( _MKSMALLINT(0) );
+ }
+ cp = _stringVal(self) + index - 1;
+ while (c = *cp++) {
+#ifdef ASCII
+ if (c <= ' ')
+#endif
+ if ((c == ' ') || (c == '\t') || (c == '\n')
+ || (c == '\r') || (c == '\f')) {
+ RETURN ( _MKSMALLINT(cp - _stringVal(self)) );
+ }
+ }
+%}
+.
+ ^ 0
+!
+
+includesMatchCharacters
+ "return true if the receiver includes any meta characters (i.e. $* or $#)
+ for match operations; false if not"
+
+ (self includes:$*) ifTrue:[^ true].
+ ^ (self includes:$#)
+!
+
+from:matchStart to:matchStop match:aString from:start to:stop
+ "helper for match; return true if the characters from start to stop in
+ aString are matching the receivers characters from matchStart to matchStop.
+ The receiver may contain meta-match characters $* (to match any string)
+ or $# (to match any character)."
+
+ |matchChar mStart mStop sStart sStop mSize sSize index cont matchLast|
+
+ mStart := matchStart.
+ mStop := matchStop.
+ sStart := start.
+ sStop := stop.
+
+ [true] whileTrue:[
+ mSize := mStop - mStart + 1.
+ sSize := sStop - sStart + 1.
+
+ "empty strings match"
+ (mSize == 0) ifTrue:[^ (sSize == 0)].
+
+ matchChar := self at:mStart.
+
+ (matchChar == $#) ifTrue:[
+ "testString empty -> no match"
+ (sSize == 0) ifTrue:[^ false].
+ "# matches single character"
+ ((sSize == 1) and:[mSize == 1]) ifTrue:[^ true].
+ "cut off 1st chars and continue"
+ mStart := mStart + 1.
+ sStart := sStart + 1
+ ] ifFalse:[
+ (matchChar == $*) ifTrue:[
+ "testString empty -> we have a match"
+ (sSize == 0) ifTrue:[^ true].
+ "* matches anything"
+ (mSize == 1) ifTrue:[^ true].
+
+ "try to avoid some of the recursion by checking last
+ character and continue with shortened strings if possible"
+ cont := false.
+ (mStop >= mStart) ifTrue:[
+ matchLast := self at:mStop.
+ (matchLast ~~ $*) ifTrue:[
+ (matchLast == $#) ifTrue:[
+ cont := true
+ ] ifFalse:[
+ (matchLast == (aString at:sStop)) ifTrue:[
+ cont := true
+ ]
+ ]
+ ]
+ ].
+ cont ifFalse:[
+ index := sStart.
+ [index <= sStop] whileTrue:[
+ (self from:(mStart + 1) to:mStop match:aString
+ from:index to:sStop) ifTrue:[
+ ^ true
+ ].
+ index := index + 1
+ ].
+ ^ false
+ ].
+ mStop := mStop - 1.
+ sStop := sStop - 1
+ ] ifFalse:[
+
+ "testString empty ?"
+ (sSize == 0) ifTrue:[^ false].
+
+ "first characters equal ?"
+ ((aString at:sStart) ~~ matchChar) ifTrue:[^ false].
+
+ "avoid recursion if possible"
+ ((sSize == mSize) and:[self = aString]) ifTrue:[^ true].
+
+ "cut off 1st chars and continue"
+ mStart := mStart + 1.
+ sStart := sStart + 1
+ ]
+ ]
+ ]
+!
+
+match:aString
+ "return true if aString matches self, where self may contain meta-match
+ characters $* (to match any string) or $# (to match any character)."
+
+ ^ self from:1 to:(self size) match:aString from:1 to:(aString size)
+
+ " '*ute*' match:'computer' "
+ " '*uter' match:'computer' "
+ " 'uter*' match:'computer' "
+!
+
+startsWith:aString
+ "return true, if the receiver starts with something, aString"
+
+ (aString isKindOf:String) ifFalse: [
+ (aString isMemberOf:Character) ifTrue:[
+ self isEmpty ifTrue:[^ false].
+ ^ (self at:1) == aString
+ ].
+ ^ super startsWith:aString
+ ].
+%{
+ int len1, len2;
+ REGISTER unsigned char *src1, *src2;
+ REGISTER OBJ s = aString;
+
+ len1 = _qSize(self);
+ src1 = _stringVal(self);
+ if (_qClass(self) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+ len1 -= n;
+ src1 += n;
+ }
+ len2 = _qSize(s);
+ src2 = _stringVal(s);
+ if (_qClass(s) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(s))->c_ninstvars) * sizeof(OBJ);
+ len2 -= n;
+ src2 += n;
+ }
+ if (len1 < len2) {
+ RETURN ( false );
+ }
+ while (*src2)
+ if (*src2++ != *src1++) {
+ RETURN ( false );
+ }
+%}
+.
+ ^ true
+
+ "'hello world' startsWith:'hello'"
+ "'hello world' startsWith:'hi'"
+!
+
+endsWith:aString
+ "return true, if the receiver end with something, aString"
+
+ (aString isKindOf:String) ifFalse: [
+ (aString isMemberOf:Character) ifTrue:[
+ self isEmpty ifTrue:[^ false].
+ ^ (self at:(self size)) == aString
+ ].
+ ^ super endsWith:aString
+ ].
+%{
+ int len1, len2;
+ REGISTER unsigned char *src1, *src2;
+ REGISTER OBJ s = aString;
+
+ len1 = _qSize(self);
+ src1 = _stringVal(self);
+ if (_qClass(self) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+ len1 -= n;
+ src1 += n;
+ }
+ len2 = _qSize(s);
+ src2 = _stringVal(s);
+ if (_qClass(s) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(s))->c_ninstvars) * sizeof(OBJ);
+ len2 -= n;
+ src2 += n;
+ }
+ if (len1 < len2) {
+ RETURN ( false );
+ }
+ src1 = _stringVal(self) + len1 - len2;
+ src2 = _stringVal(aString);
+ while (*src2)
+ if (*src2++ != *src1++) {
+ RETURN ( false );
+ }
+%}
+.
+ ^ true
+
+ "'hello world' endsWith:'world'"
+ "'hello world' endsWith:'earth'"
+!
+
+isBlank
+ "return true, if the receiver contains spaces only"
+
+%{ /* NOCONTEXT */
+
+ REGISTER unsigned char *src;
+
+ src = _stringVal(self);
+ if (_qClass(self) != String)
+ src += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+ while (*src)
+ if (*src++ != ' ') {
+ RETURN ( false );
+ }
+%}
+.
+ ^ true
+!
+
+countWords
+ "return the number of words, which are separated by separators"
+
+ |tally start stop mySize|
+
+ tally := 0.
+ start := 1.
+ mySize := self size.
+ [start <= mySize] whileTrue:[
+ (self at:start) isSeparator ifTrue:[
+ start := start + 1
+ ] ifFalse:[
+ stop := self indexOfSeparatorStartingAt:start.
+ (stop == 0) ifTrue:[
+ stop := mySize + 1
+ ].
+ tally := tally + 1.
+ start := stop
+ ]
+ ].
+ ^ tally
+!
+
+asCollectionOfWords
+ "return a collection containing the words (separated by whitespace) of the receiver"
+
+ |words start stop mySize|
+
+ words := OrderedCollection new.
+ start := 1.
+ mySize := self size.
+ [start <= mySize] whileTrue:[
+ (self at:start) isSeparator ifTrue:[
+ start := start + 1
+ ] ifFalse:[
+ stop := self indexOfSeparatorStartingAt:start.
+ stop == 0 ifTrue:[
+ words add:(self copyFrom:start to:mySize).
+ ^ words
+ ].
+ words add:(self copyFrom:start to:(stop - 1)).
+ start := stop
+ ]
+ ].
+ ^ words
+!
+
+levenshteinTo:aString
+ "return the levenshtein distance to the argument, aString;
+ this value corrensponds to the number of replacements that have to be
+ made to get aString from the receiver.
+ see IEEE transactions on Computers 1976 Pg 172 ff."
+
+ ^ self levenshteinTo:aString s:4 c:1 i:2 d:6
+!
+
+levenshteinTo:aString s:substWeight c:caseWeight i:insrtWeight d:deleteWeight
+ "parametrized levenshtein. arguments are the costs for
+ substitution, case-change, insertion and deletion of a character."
+
+ |d "delta matrix"
+ len1 len2 dim prevRow row col dimPlus1
+ min pp c1 c2|
+
+%{ /* NOCONTEXT */
+
+ /*
+ * this is very heavy used when correcting errors
+ * (all symbols are searched for best match) - therefore it must be fast
+ */
+{
+ unsigned short *data;
+ int l1, l2;
+ REGISTER int sz;
+ unsigned char *s1, *s2;
+ int v1, v2, v3, m;
+ REGISTER unsigned short *dp;
+ REGISTER int delta;
+ REGISTER int j;
+ int i;
+ int iW, cW, sW, dW;
+# define FASTSIZE 30
+ short fastData[(FASTSIZE + 1) * (FASTSIZE + 1)];
+
+ if ((_isString(self) || _isSymbol(self))
+ && (_isString(aString) || _isSymbol(aString))
+ && _isSmallInteger(insrtWeight) && _isSmallInteger(caseWeight)
+ && _isSmallInteger(substWeight) && _isSmallInteger(deleteWeight)) {
+ iW = _intVal(insrtWeight);
+ cW = _intVal(caseWeight);
+ sW = _intVal(substWeight);
+ dW = _intVal(deleteWeight);
+ s1 = _stringVal(self);
+ s2 = _stringVal(aString);
+ l1 = strlen(s1);
+ l2 = strlen(s2);
+
+ sz = (l1 < l2) ? l2 : l1;
+ delta = sz + 1;
+ if (sz <= FASTSIZE) {
+ data = fastData;
+ } else {
+ /* add ifdef ALLOCA here ... */
+ data = (unsigned short *)malloc(delta * delta * sizeof(short));
+ }
+
+ data[0] = 0;
+ dp = data+1;
+ for (j=1, dp=data+1; j<=sz; j++, dp++)
+ *dp = *(dp-1) + iW;
+
+ for (i=1, dp=data+delta; i<=sz; i++, dp+=delta)
+ *dp = *(dp-delta) + dW;
+
+ for (i=1; i<=l1; i++) {
+ for (j=1; j<=l2; j++) {
+ dp = data + (i*delta) + j;
+ if (s1[i] != s2[j]) {
+ if (tolower(s1[i]) == tolower(s2[j])) {
+ m = cW;
+ } else {
+ m = sW;
+ }
+ } else
+ m = 0;
+
+ v2 = *(dp - 1) + iW;
+ v3 = *(dp - delta) + dW;
+ v1 = *(dp - delta - 1) + m;
+ if (v1 < v2)
+ if (v1 < v3)
+ m = v1;
+ else
+ m = v3;
+ else
+ if (v2 < v3)
+ m = v2;
+ else
+ m = v3;
+ *dp = m;
+ }
+ }
+ m = data[l1 * delta + l2];
+ if (sz > FASTSIZE)
+ free(data);
+ RETURN ( _MKSMALLINT(m) );
+ }
+}
+%}
+.
+ len1 := self size.
+ len2 := aString size.
+
+ "create the help-matrix"
+
+ dim := len1 max:len2.
+ dimPlus1 := dim + 1.
+
+ d := Array new:dimPlus1.
+ 1 to:dimPlus1 do:[:i |
+ d at:i put:(Array new:dimPlus1)
+ ].
+
+ "init help-matrix"
+
+ (d at:1) at:1 put:0.
+ row := d at:1.
+ 1 to:dim do:[:j |
+ row at:(j + 1) put:( (row at:j) + insrtWeight )
+ ].
+
+ 1 to:dim do:[:i |
+ (d at:(i + 1)) at:1 put:( ((d at:i) at:1) + deleteWeight )
+ ].
+
+ 1 to:len1 do:[:i |
+ c1 := self at:i.
+ 1 to:len2 do:[:j |
+ c2 := aString at:j.
+ (c1 == c2) ifTrue:[
+ pp := 0
+ ] ifFalse:[
+ (c1 asLowercase == c2 asLowercase) ifTrue:[
+ pp := caseWeight
+ ] ifFalse:[
+ pp := substWeight
+ ]
+ ].
+ prevRow := d at:i.
+ row := d at:(i + 1).
+ col := j + 1.
+ min := (prevRow at:j) + pp.
+ min := min min:( (row at:j) + insrtWeight).
+ min := min min:( (prevRow at:col) + deleteWeight).
+ row at:col put: min
+ ]
+ ].
+
+ ^ (d at:(len1 + 1)) at:(len2 + 1)
+
+ "'ocmprt' levenshteinTo:'computer'
+ 'computer' levenshteinTo:'computer'
+ 'ocmputer' levenshteinTo:'computer'
+ 'cmputer' levenshteinTo:'computer'
+ 'Computer' levenshteinTo:'computer'"
+! !
+
+!String methodsFor:'copying'!
+
+shallowCopy
+ "return a copy of the receiver
+ - redefined for more speed"
+
+ ^ self copyFrom:1
+!
+
+deepCopy
+ "return a copy of the receiver
+ - redefined for speed"
+
+ ^ self copyFrom:1
+!
+
+, aString
+ "return the concatenation of myself and the argument, aString
+ - reimplemented here for speed"
+
+ |newString|
+%{
+ int l1, l2;
+ char *cp1, *cp2;
+ REGISTER unsigned char *dstp;
+ REGISTER OBJ s = aString;
+ OBJ new();
+
+ if ((_qClass(s) == String) || (_qClass(s) == Symbol) || (_qClass(s) == _qClass(self))) {
+ cp1 = (char *) _stringVal(self);
+ l1 = _stringSize(self);
+ if (_qClass(self) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+ cp1 += n;
+ l1 -= n;
+ }
+
+ cp2 = (char *) _stringVal(s);
+ l2 = _stringSize(s);
+ if (_qClass(s) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(s))->c_ninstvars) * sizeof(OBJ);
+
+ cp2 += n;
+ l2 -= n;
+ }
+
+ _qNew(newString, OHDR_SIZE + l1 + l2 + 1, __context);
+ _InstPtr(newString)->o_class = String;
+ dstp = _stringVal(newString);
+#ifdef FAST_MEMCPY
+ bcopy(cp1, dstp, l1);
+ bcopy(cp2, dstp + l1, l2+1);
+#else
+# ifdef FAST_STRCPY
+ strcpy(dstp, cp1);
+ strcpy(dstp + l1, cp2);
+# else
+ while ((*dstp++ = *cp1++) != '\0') ;
+ dstp--;
+ while ((*dstp++ = *cp2++) != '\0') ;
+# endif
+#endif
+ RETURN ( newString );
+ }
+%}
+.
+ ^ super , aString
+!
+
+concatenate:string1 and:string2
+ "return the concatenation of myself and the arguments, string1 and string2.
+ This is equivalent to self , string1 , string2
+ - generated by compiler when such a construct is detected"
+
+ |newString|
+%{
+ int len1, len2, len3;
+#if !defined(FAST_MEMCPY) && !defined(FAST_STRCPY)
+ REGISTER unsigned char *srcp;
+#endif
+ REGISTER unsigned char *dstp;
+ OBJ new();
+
+ if ((_isString(self) || _isSymbol(self))
+ && (_isString(string1) || _isSymbol(string1))
+ && (_isString(string2) || _isSymbol(string2))) {
+ len1 = _stringSize(self);
+ len2 = _stringSize(string1);
+ len3 = _stringSize(string2);
+ _qNew(newString, OHDR_SIZE + len1 + len2 + len3 + 1, __context);
+ _InstPtr(newString)->o_class = String;
+ dstp = _stringVal(newString);
+#ifdef FAST_MEMCPY
+ bcopy(_stringVal(self), dstp, len1);
+ bcopy(_stringVal(string1), dstp + len1, len2);
+ bcopy(_stringVal(string2), dstp + len1 + len2, len3+1);
+#else
+# ifdef FAST_STRCPY
+ strcpy(dstp, _stringVal(self));
+ strcpy(dstp + len1, _stringVal(string1));
+ strcpy(dstp + len1 + len2, _stringVal(string2));
+# else
+ srcp = _stringVal(self);
+ while ((*dstp++ = *srcp++) != '\0') ;
+ dstp--;
+ srcp = _stringVal(string1);
+ while ((*dstp++ = *srcp++) != '\0') ;
+ dstp--;
+ srcp = _stringVal(string2);
+ while ((*dstp++ = *srcp++) != '\0') ;
+# endif
+#endif
+ RETURN ( newString );
+ }
+%}
+.
+ ^ super , string1 , string2
+!
+
+concatenate:string1 and:string2 and:string3
+ "return the concatenation of myself and the string arguments.
+ This is equivalent to self , string1 , string2 , string3
+ - generated by compiler when such a construct is detected"
+
+ |newString|
+%{
+ int len1, len2, len3, len4;
+#if !defined(FAST_MEMCPY) && !defined(FAST_STRCPY)
+ REGISTER unsigned char *srcp;
+#endif
+ REGISTER unsigned char *dstp;
+ OBJ new();
+
+ if ((_isString(self) || _isSymbol(self))
+ && (_isString(string1) || _isSymbol(string1))
+ && (_isString(string2) || _isSymbol(string2))
+ && (_isString(string3) || _isSymbol(string3))) {
+ len1 = _stringSize(self);
+ len2 = _stringSize(string1);
+ len3 = _stringSize(string2);
+ len4 = _stringSize(string3);
+ _qNew(newString, OHDR_SIZE + len1 + len2 + len3 + len4 + 1, __context);
+ _InstPtr(newString)->o_class = String;
+ dstp = _stringVal(newString);
+#ifdef FAST_MEMCPY
+ bcopy(_stringVal(self), dstp, len1);
+ bcopy(_stringVal(string1), dstp + len1, len2);
+ bcopy(_stringVal(string2), dstp + len1 + len2, len3);
+ bcopy(_stringVal(string3), dstp + len1 + len2 + len3, len4+1);
+#else
+# ifdef FAST_STRCPY
+ strcpy(dstp, _stringVal(self));
+ strcpy(dstp + len1, _stringVal(string1));
+ strcpy(dstp + len1 + len2, _stringVal(string2));
+ strcpy(dstp + len1 + len2 + len3, _stringVal(string3));
+# else
+ srcp = _stringVal(self);
+ while ((*dstp++ = *srcp++) != '\0') ;
+ dstp--;
+ srcp = _stringVal(string1);
+ while ((*dstp++ = *srcp++) != '\0') ;
+ dstp--;
+ srcp = _stringVal(string2);
+ while ((*dstp++ = *srcp++) != '\0') ;
+ dstp--;
+ srcp = _stringVal(string3);
+ while ((*dstp++ = *srcp++) != '\0') ;
+# endif
+#endif
+ RETURN ( newString );
+ }
+%}
+.
+ ^ super , string1 , string2 , string3
+!
+
+copyWith:aCharacter
+ "return the concatenation of myself and the argument, aCharacter
+ - reimplemented here for speed"
+
+ |newString|
+
+ (aCharacter isMemberOf:Character) ifFalse:[
+ ^ super copyWith:aCharacter
+ ].
+%{
+ OBJ new();
+ int sz;
+ REGISTER unsigned char *dstp;
+ int offs;
+
+ sz = _qSize(self) + 1;
+ if (_qClass(self) != String) {
+ offs = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+ sz -= offs;
+ } else
+ offs = 0;
+
+ _qNew(newString, sz, __context);
+ _InstPtr(newString)->o_class = String;
+ dstp = _stringVal(newString);
+#ifdef FAST_MEMCPY
+ sz = sz - OHDR_SIZE - 1 - 1;
+ bcopy(_stringVal(self) + offs, dstp, sz);
+ dstp += sz;
+#else
+# ifdef FAST_STRCPY
+ strcpy(dstp, _stringVal(self) + offs);
+ dstp += sz - OHDR_SIZE - 1 - 1;
+# else
+ {
+ REGISTER unsigned char *srcp;
+
+ srcp = _stringVal(self) + offs;
+ while ((*dstp = *srcp++) != '\0')
+ dstp++;
+ }
+# endif
+#endif
+ *dstp++ = _intVal(_characterVal(aCharacter));
+ *dstp = '\0';
+%}
+.
+ ^ newString
+!
+
+copyFrom:start to:stop
+ "return the substring starting at index start, anInteger and ending
+ at stop, anInteger.
+ - reimplemented here for speed"
+
+ |newString|
+%{
+ OBJ new();
+#if !defined(FAST_MEMCPY)
+ REGISTER unsigned char *srcp;
+#endif
+ REGISTER unsigned char *dstp;
+ REGISTER int count;
+ int len, index1, index2;
+
+ if (_isSmallInteger(start) && _isSmallInteger(stop)) {
+ len = _stringSize(self);
+ index1 = _intVal(start);
+ index2 = _intVal(stop);
+
+ if ((index1 <= index2) && (index1 > 0)) {
+ if (_qClass(self) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+ index1 += n;
+ index2 += n;
+ }
+ if (index2 <= len) {
+ count = index2 - index1 + 1;
+ _qNew(newString, OHDR_SIZE+count+1, __context);
+ _InstPtr(newString)->o_class = String;
+ dstp = _stringVal(newString);
+#ifdef FAST_MEMCPY
+ bcopy(_stringVal(self) + index1 - 1, dstp, count);
+ dstp[count] = '\0';
+#else
+ srcp = _stringVal(self) + index1 - 1;
+ while (count--) {
+ *dstp++ = *srcp++;
+ }
+ *dstp = '\0';
+#endif
+ RETURN ( newString );
+ }
+ }
+ }
+%}
+.
+ ^ super copyFrom:start to:stop
+!
+
+copyFrom:start
+ "return the substring from start, anInteger to the end
+ - reimplemented here for speed"
+
+ |newString|
+%{
+ OBJ new();
+#if !defined(FAST_MEMCPY)
+ REGISTER unsigned char *srcp;
+#endif
+ REGISTER unsigned char *dstp;
+ REGISTER int count;
+ int len, index1;
+
+ if (_isSmallInteger(start)) {
+ len = _stringSize(self);
+ index1 = _intVal(start);
+ if (index1 > 0) {
+ if (_qClass(self) != String) {
+ int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+ index1 += n;
+ }
+ if (index1 <= len) {
+ count = len - index1 + 1;
+ _qNew(newString, OHDR_SIZE+count+1, __context);
+ _InstPtr(newString)->o_class = String;
+ dstp = _stringVal(newString);
+#ifdef FAST_MEMCPY
+ bcopy(_stringVal(self) + index1 - 1, dstp, count);
+ dstp[count] = '\0';
+#else
+ srcp = _stringVal(self) + index1 - 1;
+ while (count--) {
+ *dstp++ = *srcp++;
+ }
+ *dstp = '\0';
+#endif
+ RETURN ( newString );
+ }
+ }
+ }
+%}
+.
+ ^ super copyFrom:start
+! !
+
+!String methodsFor:'filling and replacing'!
+
+replaceFrom:start to:stop with:aString startingAt:repStart
+ "replace the characters starting at index start, anInteger and ending
+ at stop, anInteger with characters from aString starting at repStart.
+
+ - reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ REGISTER unsigned char *srcp, *dstp;
+ REGISTER int count;
+ int len, index1, index2;
+ int repLen, repIndex;
+
+ if ((_isString(aString) || _isSymbol(aString))
+ && _isString(self)
+ && _isSmallInteger(start)
+ && _isSmallInteger(stop)) {
+ len = _stringSize(self);
+ index1 = _intVal(start);
+ index2 = _intVal(stop);
+ count = index2 - index1 + 1;
+ if (count <= 0) {
+ RETURN (self);
+ }
+ if ((index2 <= len) && (index1 > 0)) {
+ repLen = _stringSize(aString);
+ repIndex = _intVal(repStart);
+ if ((repIndex > 0) && ((repIndex + count - 1) <= repLen)) {
+ srcp = _stringVal(aString) + repIndex - 1;
+ dstp = _stringVal(self) + index1 - 1;
+ if (aString == self) {
+ /* take care of overlapping copy */
+ if (srcp < dstp) {
+ /* must do a reverse copy */
+ srcp += count;
+ dstp += count;
+ while (count-- > 0) {
+ *--dstp = *--srcp;
+ }
+ RETURN (self);
+ }
+ }
+#ifdef FAST_MEMCPY
+ bcopy(srcp, dstp, count);
+#else
+ while (count-- > 0) {
+ *dstp++ = *srcp++;
+ }
+#endif
+ RETURN (self);
+ }
+ }
+ }
+%}
+.
+ ^ super replaceFrom:start to:stop with:aString startingAt:repStart
+!
+
+replaceAll:oldCharacter by:newCharacter
+ "replace all oldCharacters by newCharacter in the receiver"
+
+%{ /* NOCONTEXT */
+
+ REGISTER unsigned char *srcp;
+ REGISTER unsigned oldVal, newVal;
+
+ if (_isCharacter(oldCharacter)
+ && _isCharacter(newCharacter)
+ && _isString(self)) {
+ srcp = (unsigned char *)_stringVal(self);
+ oldVal = _intVal(_characterVal(oldCharacter));
+ newVal = _intVal(_characterVal(newCharacter));
+ while (*srcp) {
+ if (*srcp == oldVal)
+ *srcp = newVal;
+ srcp++;
+ }
+ RETURN ( self );
+ }
+%}
+.
+ ^ super replaceAll:oldCharacter by:newCharacter
+!
+
+reverse
+ "in-place reverse the characters of the string"
+
+%{ /* NOCONTEXT */
+
+ REGISTER char c;
+ REGISTER unsigned char *hip, *lowp;
+
+ if (_isString(self)) {
+ lowp = _stringVal(self);
+ hip = lowp + _stringSize(self) - 1;
+ while (lowp < hip) {
+ c = *lowp;
+ *lowp = *hip;
+ *hip = c;
+ lowp++;
+ hip--;
+ }
+ RETURN ( self );
+ }
+%}
+.
+ ^ super reverse
+!
+
+withCRs
+ "return a copy of the receiver, where
+ all \-characters are replaced by newline characters
+ - reimplemented here for speed"
+
+ |newString|
+%{
+ OBJ new();
+ REGISTER char c;
+ REGISTER unsigned char *srcp, *dstp;
+ int len, offs;
+
+ len = _qSize(self);
+ if (_qClass(self) != String) {
+ offs = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+ len -= offs;
+ } else
+ offs = 0;
+
+ _qNew(newString, len, __context);
+ _InstPtr(newString)->o_class = String;
+ srcp = _stringVal(self) + offs;
+ dstp = _stringVal(newString);
+ while (c = *srcp++)
+ if (c == '\\')
+ *dstp++ = '\n';
+ else
+ *dstp++ = c;
+ *dstp++ = '\0';
+ RETURN ( newString );
+%}
+!
+
+atAllPut:aCharacter
+ "replace all characters with aCharacter
+ - reimplemented here for speed"
+
+%{ /* NOCONTEXT */
+
+ REGISTER int byteValue;
+#ifndef FAST_MEMSET
+ REGISTER unsigned char *dst;
+#endif
+
+ if (_isCharacter(aCharacter) && _isString(self)) {
+ byteValue = _intVal(_characterVal(aCharacter));
+#ifdef FAST_MEMSET
+ memset(_stringVal(self), byteValue, _qSize(self) - OHDR_SIZE - 1);
+#else
+ dst = _stringVal(self);
+ while (*dst != '\0')
+ *dst++ = byteValue;
+#endif
+ RETURN ( self );
+ }
+%}
+.
+ ^ super atAllPut:aCharacter
+!
+
+withoutSpaces
+ "return a copy of myself without leading and trailing spaces"
+
+ |startIndex endIndex blank|
+
+ startIndex := 1.
+ endIndex := self size.
+ blank := Character space.
+ [(startIndex < endIndex) and:[(self at:startIndex) == blank]] whileTrue:[
+ startIndex := startIndex + 1
+ ].
+ [(endIndex > 1) and:[(self at:endIndex) == blank]] whileTrue:[
+ endIndex := endIndex - 1
+ ].
+ startIndex > endIndex ifTrue:[
+ ^ ''
+ ].
+ ((startIndex == 1) and:[endIndex == self size]) ifTrue:[
+ ^ self
+ ].
+ ^ self copyFrom:startIndex to:endIndex
+!
+
+withoutSeparators
+ "return a copy of myself without leading and trailing whitespace"
+
+ |startIndex endIndex|
+
+ startIndex := 1.
+ endIndex := self size.
+ [(startIndex < endIndex) and:[(self at:startIndex) isSeparator]] whileTrue:[
+ startIndex := startIndex + 1
+ ].
+ [(endIndex > 1) and:[(self at:endIndex) isSeparator]] whileTrue:[
+ endIndex := endIndex - 1
+ ].
+ startIndex > endIndex ifTrue:[
+ ^ ''
+ ].
+ ((startIndex == 1) and:[endIndex == self size]) ifTrue:[
+ ^ self
+ ].
+ ^ self copyFrom:startIndex to:endIndex
+! !
+
+!String methodsFor:'queries'!
+
+encoding
+ "assume iso8859 encoding"
+
+ ^ #iso8859
+!
+
+knownAsSymbol
+ "return true, if there is a symbol with same characters in the
+ system - use to check for existance of a symbol without creating one"
+
+%{ /* NOCONTEXT */
+ extern OBJ _KNOWNASSYMBOL();
+
+ RETURN ( _KNOWNASSYMBOL(_stringVal(self)) );
+%}
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/StringCollection.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,162 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+VariableArray subclass:#Text
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Text'
+!
+
+Text comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+text is an array of lines which are strings.
+this is just temporary - may change in the future.
+
+%W% %E%
+'!
+
+!Text class methodsFor:'instance creation'!
+
+from:aString
+ "return a new text object with lines taken from the argument, aString"
+
+ ^ (self new:1) from:aString
+!
+
+fromArray:anArray
+ "return a new text object with lines taken from the argument, an array
+ of strings"
+
+ |newText
+ size "{ Class: SmallInteger }" |
+
+ size := anArray size.
+ newText := self new:size.
+ 1 to:size do:[:line |
+ newText at:line put:(anArray at:line)
+ ].
+ ^ newText
+! !
+
+!Text methodsFor:'growing'!
+
+grow:newSize
+ "grow to newsize - new elements are initialized with empty strings -
+ not nil"
+
+ |oldSize "{ Class:SmallInteger }"|
+
+ oldSize := tally.
+ super grow:newSize.
+ (oldSize < newSize) ifTrue:[
+ contentsArray from:(oldSize + 1) to:newSize put:''
+ ]
+!
+
+add:aString
+ "append the argument, aString to myself -
+ we increase physical size by 50 to avoid lots of copying around"
+
+ |oldSize "{ Class:SmallInteger }"|
+
+ oldSize := tally.
+ super grow:(oldSize + 50).
+ tally := oldSize + 1.
+ contentsArray at:tally put:aString
+! !
+
+!Text methodsFor:'converting'!
+
+asString
+ "return myself as a string with embedded cr's"
+
+ |totalLength "{ Class:SmallInteger }"
+ pos "{ Class:SmallInteger }"
+ newString |
+
+ totalLength := 0.
+ self do:[:lineString |
+ lineString isNil ifTrue:[
+ totalLength := totalLength + 1
+ ] ifFalse: [
+ totalLength := totalLength + lineString size + 1
+ ].
+ 0
+ ].
+ newString := String new:totalLength.
+ pos := 1.
+ self do:[:lineString |
+ lineString isNil ifFalse:[
+ newString replaceFrom:pos with:lineString.
+ pos := pos + (lineString size)
+ ].
+ newString at:pos put:(Character cr).
+ pos := pos + 1
+ ].
+ ^ newString
+!
+
+from:aString
+ "setup my contents from the argument, aString"
+
+ |numberOfLines "{ Class:SmallInteger }"
+ start "{ Class:SmallInteger }"
+ stop "{ Class:SmallInteger }" |
+
+ numberOfLines := aString occurrencesOf:(Character cr).
+ numberOfLines := numberOfLines + 1.
+ self grow:numberOfLines.
+ start := 1.
+ 1 to:numberOfLines do:[:lineNr |
+ stop := aString indexOf:(Character cr)
+ startingAt:start
+ ifAbsent:[aString size + 1].
+ stop := stop - 1.
+ (stop < start) ifTrue: [
+ self at:lineNr put:(String new:0)
+ ] ifFalse: [
+ self at:lineNr put:(aString copyFrom:start to:stop)
+ ].
+ start := stop + 2
+ ]
+!
+
+asText
+ ^ self
+! !
+
+!Text methodsFor:'printing'!
+
+printString
+ ^ self asString
+! !
+
+!Text methodsFor:'searching'!
+
+indexOfLineStartingWith:aString
+ "return the index of the first line starting with the argument, aString"
+
+ |index "{ Class:SmallInteger }" |
+
+ index := 1.
+ [index <= self size] whileTrue:[
+ ((self at:index) startsWith:aString) ifTrue:[
+ ^ index
+ ].
+ index := index + 1
+ ].
+ ^ 0
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Symbol.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,159 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+String subclass:#Symbol
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Text'
+!
+
+Symbol comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+Symbols represent unique strings - every symbol with same printString
+exists exactly once in the system; Symbols are used for selectors, global
+variable-keys etc.
+
+%W% %E%
+'!
+
+!Symbol class methodsFor:'instance creation'!
+
+basicNew:size
+ "catch instance creation - symbols are not created with new"
+
+ self error:'symbols may not be created with new:'
+!
+
+intern:aString
+ "return a unique symbol with printname taken from the String-argument"
+
+%{
+ if (_isString(aString)) {
+ RETURN ( _MKSYMBOL(_stringVal(aString), (OBJ *)0, __context) );
+ }
+%}
+.
+ ^ self mustBeString
+!
+
+internCharacter:aCharacter
+ "return a unique symbol with printname taken from the Character-argument"
+
+ ^ self intern:(aCharacter asString)
+! !
+
+!Symbol class methodsFor:'queries'!
+
+hasInterned:aString ifTrue:trueBlock
+ "for ST-80 compatibility - if the argument, aString is known
+ as Symbol, evaluate the block with the corresponding symbol
+ as argument and return true; otherwise return false"
+
+ aString knownAsSymbol ifTrue:[
+ trueBlock value:(aString asSymbol).
+ ^ true
+ ].
+ ^ false
+! !
+
+!Symbol methodsFor:'accessing'!
+
+basicAt:index put:something
+ "report an error - symbols may not be changed"
+
+ self error:'symbols may not be changed'
+!
+
+at:index put:something
+ "report an error - symbols may not be changed"
+
+ self error:'symbols may not be changed'
+! !
+
+!Symbol methodsFor:'copying'!
+
+deepCopy
+ "return a copy of myself
+ - reimplemented here since symbols are unique"
+
+ ^ self
+! !
+
+!Symbol methodsFor:'converting'!
+
+asString
+ "return a string with printname taken from mine"
+
+ ^ self printString
+!
+
+asSymbol
+ "I am a symbol - just return myself"
+
+ ^ self
+! !
+
+!Symbol methodsFor:'misc'!
+
+species
+ ^ String
+!
+
+nArgsIfSelector
+ "if symbol is used as a selector, how many arguments would it take"
+
+ |binopChars|
+
+ (self size > 2) ifFalse:[
+ binopChars := '|&-+=*/\<>~@,'.
+ (self size == 1) ifTrue:[
+ ((binopChars occurrencesOf:(self at:1)) == 0) ifTrue:[^ 0].
+ ^ 1
+ ].
+ ((binopChars occurrencesOf:(self at:1)) == 0) ifFalse:[
+ ((binopChars occurrencesOf:(self at:2)) == 0) ifFalse:[^ 1]
+ ]
+ ].
+ ^ self occurrencesOf:$:
+! !
+
+!Symbol methodsFor:'printing & storing'!
+
+printString
+%{
+ RETURN ( _MKSTRING(_stringVal(self) COMMA_CON) );
+%}
+!
+
+printOn:aStream
+ "aStream nextPut:$#."
+ aStream nextPutAll:(self printString)
+!
+
+displayString
+ ^ self storeString
+!
+
+storeString
+ "return a String for storing myself"
+
+ ^ $# asString , self
+!
+
+storeOn:aStream
+ aStream nextPut:$#.
+ aStream nextPutAll:(self printString)
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Time.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,219 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Magnitude subclass:#Time
+ instanceVariableNames:'secondsLow secondsHi'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Magnitude-General'
+!
+
+Time comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+time represents a particular second in a day; since we depend on
+unix, the seconds are counted from 1. Jan 1970 NOT as in Smalltalk-80
+from 1. Jan 1901; since unix-time is 32 bit which does not fit into
+a SmallInteger, we keep low and hi 16bit of the time separately.
+
+%W% %E%
+'!
+
+!Time class methodsFor:'instance creation'!
+
+secondClock
+ "return seconds of now - for GNU-ST compatibility"
+
+ ^ OperatingSystem getTime
+!
+
+fromUnixTimeLow:low and:hi
+ ^ self basicNew setSecondsLow:low and:hi
+!
+
+now
+ "return an instance of Time representing this moment"
+
+ ^ self basicNew setSecondsLow:(OperatingSystem getTimeLow)
+ and:(OperatingSystem getTimeHi)
+!
+
+millisecondClockValue
+ "return the millisecond clock - since this one overruns
+ regularly, use only for short timing deltas"
+
+ ^ OperatingSystem getMillisecondTime.
+! !
+
+!Time class methodsFor:'timing evaluations'!
+
+secondsToRun:aBlock
+ "evaluate the argument, aBlock; return the number of seconds it took"
+
+ |startTime endTime|
+
+ startTime := self now.
+ aBlock value.
+ endTime := self now.
+ ^ endTime - startTime
+!
+
+millisecondsToRun:aBlock
+ "evaluate the argument, aBlock; return the number of milliseconds it took"
+
+ |startTime endTime|
+
+ startTime := OperatingSystem getMillisecondTime.
+ aBlock value.
+ endTime := OperatingSystem getMillisecondTime.
+ ^ endTime - startTime
+! !
+
+!Time class methodsFor:'ST-80 compatibility'!
+
+totalSeconds
+ ^ self secondClock
+! !
+
+!Time methodsFor:'accessing'!
+
+hourInDay
+ "return the hour-part"
+
+ |hr|
+
+ OperatingSystem computeTimePartsOf:secondsLow and:secondsHi for:[
+ :hours :minutes :secs |
+
+ hr := hours
+ ].
+ ^ hr
+!
+
+minuteInDay
+ "return the minute-part"
+
+ |m|
+
+ OperatingSystem computeTimePartsOf:secondsLow and:secondsHi for:[
+ :hours :minutes :secs |
+
+ m := minutes
+ ].
+ ^ m
+!
+
+secondInDay
+ "return the second-part"
+
+ |s|
+
+ OperatingSystem computeTimePartsOf:secondsLow and:secondsHi for:[
+ :hours :minutes :secs |
+
+ s := secs
+ ].
+ ^ s
+! !
+
+!Time methodsFor:'comparing'!
+
+> aTime
+ (aTime isMemberOf:Time) ifTrue:[
+ (secondsHi > aTime secondsHi) ifTrue:[^ true].
+ (secondsHi < aTime secondsHi) ifTrue:[^ false].
+ (secondsLow > aTime secondsLow) ifTrue:[^ true].
+ ^ false
+ ].
+ ^ self getSeconds > aTime getSeconds
+!
+
+< aTime
+ (aTime isMemberOf:Time) ifTrue:[
+ (secondsHi < aTime secondsHi) ifTrue:[^ true].
+ (secondsHi > aTime secondsHi) ifTrue:[^ false].
+ (secondsLow < aTime secondsLow) ifTrue:[^ true].
+ ^ false
+ ].
+ ^ self getSeconds < aTime getSeconds
+!
+
+= aTime
+ (aTime isMemberOf:Time) ifTrue:[
+ ^ ((secondsLow == aTime secondsLow) and:[secondsHi == aTime secondsHi])
+ ].
+ ^ self getSeconds = aTime getSeconds
+! !
+
+!Time methodsFor:'arithmetic'!
+
+- aTime
+ "return delta in seconds between 2 times"
+
+ ^ self getSeconds - (aTime getSeconds)
+!
+
+addTime:timeAmount
+ "return a new Time timeAmount seconds from myself"
+
+ ^Time new setSeconds:(self getSeconds + timeAmount)
+!
+
+subtractTime:timeAmount
+ "return a new Time timeAmount seconds before myself"
+
+ ^Time new setSeconds:(self getSeconds - timeAmount)
+! !
+
+!Time methodsFor:'printing'!
+
+printString
+ |aString|
+
+ OperatingSystem computeTimePartsOf:secondsLow and:secondsHi for:[
+ :hours :minutes :secs |
+
+ aString := hours printString , ':' .
+ (minutes < 10) ifTrue:[aString := aString , '0'].
+ aString := aString , minutes printString.
+ aString := aString , ':'.
+ (secs < 10) ifTrue:[aString := aString , '0'].
+ aString := aString , secs printString
+ ].
+ ^ aString
+! !
+
+!Time methodsFor:'private'!
+
+secondsLow
+ ^ secondsLow
+!
+
+secondsHi
+ ^ secondsHi
+!
+
+getSeconds
+ ^ (secondsHi * 16r10000) + secondsLow
+!
+
+setSeconds:secs
+ secondsHi := secs // 16r10000.
+ secondsLow := secs \\ 16r10000
+!
+
+setSecondsLow:secsLow and:secsHi
+ secondsHi := secsHi.
+ secondsLow := secsLow
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/True.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,120 @@
+"
+ COPYRIGHT (c) 1988-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Boolean subclass:#True
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Objects'
+!
+
+True comment:'
+
+COPYRIGHT (c) 1988-92 by Claus Gittinger
+ All Rights Reserved
+
+Class True has only one instance, true, representing logical truth.
+
+%W% %E%
+'!
+
+!True methodsFor: 'logical operations'!
+
+& aBoolean
+ "return true if both the receiver and the argument are true
+ (since the receiver is true, return the argument, aBoolen)"
+
+ ^ aBoolean
+!
+
+| aBoolean
+ "return true if either the receiver or the argument are true
+ (since the receiver is true, return true)"
+
+ ^ true
+!
+
+not
+ "return true if the receiver is false, false otherwise
+ (since the receiver is true, return false)"
+
+ ^ false
+!
+
+eqv:aBoolean
+ "return true if both the receiver and the argument are the same truth value
+ (since the receiver is true, return true if the argument is also)"
+
+ ^ aBoolean
+!
+
+xor:aBoolean
+ "return true if the receiver and the argument are different truth values
+ (since the receiver is true, return true, if the argument is not)"
+
+ ^ aBoolean not
+! !
+
+!True methodsFor: 'conditional evaluation'!
+
+and:aBlock
+ "evaluate aBlock if the receiver is true.
+ since the receiver is known to be true,
+ return the value of evaluating the block.
+ - open coded by compiler"
+
+ ^ aBlock value
+!
+
+or:aBlock
+ "evaluate aBlock if the receiver is false.
+ since the receiver is known to be true simply return true.
+ - open coded by compiler"
+
+ ^ self
+!
+
+ifTrue:aBlock
+ "return the value of evaluating aBlock (since the receiver is true)
+ - open coded by compiler"
+
+ ^ aBlock value
+!
+
+ifFalse:aBlock
+ "return the true alternative, nil (since the receiver is true)
+ - open coded by compiler"
+
+ ^ nil
+!
+
+ifTrue:trueBlock ifFalse:falseBlock
+ "return the value of evaluating trueBlock (since the receiver is true)
+ - open coded by compiler"
+
+ ^ trueBlock value
+!
+
+ifFalse:falseBlock ifTrue:trueBlock
+ "return the value of evaluating trueBlock (since the receiver is true)
+ - open coded by compiler"
+
+ ^ trueBlock value
+! !
+
+!True methodsFor: 'printing'!
+
+printString
+ "return a Character sequence representing the receiver"
+
+ ^ 'true'
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/TwoByteString.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,61 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+WordArray subclass:#TwoByteString
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Collections-Text'
+!
+
+TwoByteString comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+TwoByteStrings are like strings, but storing 16bits per character.
+The integration of them into the system is not completed ....
+
+%W% %E%
+'!
+
+!TwoByteString class methodsFor:'instance creation'!
+
+basicNew:anInteger
+ "return a new empty string with anInteger characters"
+
+ ^ super basicNew:anInteger atAllPut:(Character space)
+! !
+
+!TwoByteString methodsFor:'accessing'!
+
+basicAt:index
+ "return the character at position index, an Integer
+ - reimplemented here since we return characters"
+
+ ^ Character value:(super basicAt:index)
+!
+
+basicAt:index put:aCharacter
+ "store the argument, aCharacter at position index, an Integer
+ - reimplemented here since we store characters"
+
+ super basicAt:index put:(aCharacter asciiValue)
+! !
+
+!TwoByteString methodsFor:'converting'!
+
+asString
+ "return myself - I am a string"
+
+ ^ self
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/UIBytes.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,63 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ByteArray subclass:#UninterpretedBytes
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'System-Support'
+!
+
+UninterpretedBytes comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+this class has been added for ST-80 compatibility.
+
+%W% %E%
+'!
+
+!UninterpretedBytes class methodsFor:'queries'!
+
+isBigEndian
+ "return true, if words/shorts store the most-significant
+ byte last. I.e. true if LSB-first (vax, intel),
+ false if MSB-first (m68k, sun)."
+
+%{ /* NOCONTEXT */
+
+ /*
+ * I dont like ifdefs - you always forget some ...
+ * therefore we look into a structure at run-time
+ */
+ union {
+ unsigned int u_l;
+ char u_c[sizeof(int)];
+ } u;
+
+ u.u_l = 0x87654321;
+ if (u.u_c[0] == 0x21) RETURN (true);
+ RETURN (false );
+%}
+ "UninterpretedBytes isBigEndian"
+! !
+
+!UninterpretedBytes methodsFor:'accessing'!
+
+byteAt:index
+ ^ self basicAt:index
+!
+
+byteAt:index put:value
+ ^ self basicAt:index put:value
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/UndefObj.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,142 @@
+"
+ COPYRIGHT (c) 1988-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#UndefinedObject
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Objects'
+!
+
+UndefinedObject comment:'
+
+COPYRIGHT (c) 1988-92 by Claus Gittinger
+ All Rights Reserved
+
+there is only one instance of this class: nil
+
+%W% %E%
+'!
+
+!UndefinedObject class methodsFor:'instance creation'!
+
+basicNew
+ "catch new - there MUST be only one nil in the system"
+
+ ^ nil
+!
+
+basicNew:size
+ "catch new - there MUST be only one nil in the system"
+
+ ^ nil
+! !
+
+!UndefinedObject methodsFor:'error catching'!
+
+basicAt:index
+ "catch array access - its illegal
+ defined here since basicAt: in Object ommits the nil-check"
+
+ ^ self notIndexed
+!
+
+at:index
+ "catch array access - its illegal
+ defined here since at: in Object ommits the nil-check"
+
+ ^ self notIndexed
+!
+
+basicAt:index put:anObject
+ "catch array access - its illegal
+ defined here since basicAt:put: in Object ommits the nil-check"
+
+ ^ self notIndexed
+!
+
+at:index put:anObject
+ "catch array access - its illegal
+ defined here since at:put: in Object ommits the nil-check"
+
+ ^ self notIndexed
+! !
+
+!UndefinedObject methodsFor:'testing'!
+
+isNil
+ "return true if I am nil - since I am, return true"
+
+ ^ true
+!
+
+notNil
+ "return true if I am not nil - since I am nil, return false"
+
+ ^ false
+!
+
+size
+ "return the number of indexed instvars
+ defined here since size in Object ommits the nil-check"
+
+ ^ 0
+!
+
+basicSize
+ "return the number of indexed instvars
+ defined here since size in Object ommits the nil-check"
+
+ ^ 0
+!
+
+hash
+ "return an integer useful as a hash key"
+
+ ^ 0
+!
+
+identityHash
+ "return an integer useful as a hash key"
+
+ ^ 0
+! !
+
+!UndefinedObject methodsFor:'copying'!
+
+shallowCopy
+ "return a shallow copy of myself
+ - since there is only one nil in the system return self"
+
+ ^ self
+!
+
+deepCopy
+ "return a deep copy of myself
+ - since there is only one nil in the system return self"
+
+ ^ self
+! !
+
+!UndefinedObject methodsFor:'printing & storing'!
+
+printString
+ "return a string for printing myself"
+
+ ^ 'nil'
+!
+
+storeString
+ "return a string for storing myself"
+
+ ^ 'nil'
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/UndefinedObject.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,142 @@
+"
+ COPYRIGHT (c) 1988-92 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#UndefinedObject
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Objects'
+!
+
+UndefinedObject comment:'
+
+COPYRIGHT (c) 1988-92 by Claus Gittinger
+ All Rights Reserved
+
+there is only one instance of this class: nil
+
+%W% %E%
+'!
+
+!UndefinedObject class methodsFor:'instance creation'!
+
+basicNew
+ "catch new - there MUST be only one nil in the system"
+
+ ^ nil
+!
+
+basicNew:size
+ "catch new - there MUST be only one nil in the system"
+
+ ^ nil
+! !
+
+!UndefinedObject methodsFor:'error catching'!
+
+basicAt:index
+ "catch array access - its illegal
+ defined here since basicAt: in Object ommits the nil-check"
+
+ ^ self notIndexed
+!
+
+at:index
+ "catch array access - its illegal
+ defined here since at: in Object ommits the nil-check"
+
+ ^ self notIndexed
+!
+
+basicAt:index put:anObject
+ "catch array access - its illegal
+ defined here since basicAt:put: in Object ommits the nil-check"
+
+ ^ self notIndexed
+!
+
+at:index put:anObject
+ "catch array access - its illegal
+ defined here since at:put: in Object ommits the nil-check"
+
+ ^ self notIndexed
+! !
+
+!UndefinedObject methodsFor:'testing'!
+
+isNil
+ "return true if I am nil - since I am, return true"
+
+ ^ true
+!
+
+notNil
+ "return true if I am not nil - since I am nil, return false"
+
+ ^ false
+!
+
+size
+ "return the number of indexed instvars
+ defined here since size in Object ommits the nil-check"
+
+ ^ 0
+!
+
+basicSize
+ "return the number of indexed instvars
+ defined here since size in Object ommits the nil-check"
+
+ ^ 0
+!
+
+hash
+ "return an integer useful as a hash key"
+
+ ^ 0
+!
+
+identityHash
+ "return an integer useful as a hash key"
+
+ ^ 0
+! !
+
+!UndefinedObject methodsFor:'copying'!
+
+shallowCopy
+ "return a shallow copy of myself
+ - since there is only one nil in the system return self"
+
+ ^ self
+!
+
+deepCopy
+ "return a deep copy of myself
+ - since there is only one nil in the system return self"
+
+ ^ self
+! !
+
+!UndefinedObject methodsFor:'printing & storing'!
+
+printString
+ "return a string for printing myself"
+
+ ^ 'nil'
+!
+
+storeString
+ "return a string for storing myself"
+
+ ^ 'nil'
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/UninterpretedBytes.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,63 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+ByteArray subclass:#UninterpretedBytes
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'System-Support'
+!
+
+UninterpretedBytes comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+ All Rights Reserved
+
+this class has been added for ST-80 compatibility.
+
+%W% %E%
+'!
+
+!UninterpretedBytes class methodsFor:'queries'!
+
+isBigEndian
+ "return true, if words/shorts store the most-significant
+ byte last. I.e. true if LSB-first (vax, intel),
+ false if MSB-first (m68k, sun)."
+
+%{ /* NOCONTEXT */
+
+ /*
+ * I dont like ifdefs - you always forget some ...
+ * therefore we look into a structure at run-time
+ */
+ union {
+ unsigned int u_l;
+ char u_c[sizeof(int)];
+ } u;
+
+ u.u_l = 0x87654321;
+ if (u.u_c[0] == 0x21) RETURN (true);
+ RETURN (false );
+%}
+ "UninterpretedBytes isBigEndian"
+! !
+
+!UninterpretedBytes methodsFor:'accessing'!
+
+byteAt:index
+ ^ self basicAt:index
+!
+
+byteAt:index put:value
+ ^ self basicAt:index put:value
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Unix.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1542 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#OperatingSystem
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'System-Support'
+!
+
+OperatingSystem comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+ All Rights Reserved
+
+this class gives access to some operating system services;
+some of it is very specific for unix.
+
+%W% %E%
+
+written 1988 by claus
+'!
+
+%{
+
+#ifdef transputer
+# define unlink(f) ((remove(f) == 0) ? 0 : -1)
+#else
+# include <signal.h>
+
+# ifdef SYSV
+# include <sys/types.h>
+# include <sys/param.h>
+# include <sys/times.h>
+# if ! defined(sco3_2)
+# include <unistd.h>
+# endif
+# if defined(isc3_2) || defined(sco3_2)
+# include <sys/time.h>
+# endif
+# if !defined(isc3_2)
+# if defined(PCS) && defined(mips)
+# include "/usr/include/bsd/sys/time.h"
+# include "/usr/include/sys/time.h"
+# else
+# include <time.h>
+# endif
+# endif
+# if defined(isc3_2)
+# include <sys/bsdtypes.h>
+# endif
+# ifdef FAST_TIMER
+# include <ft.h>
+ static int timer_fd = -1;
+# endif
+# else /* not SYSV */
+# include <sys/time.h>
+# include <sys/types.h>
+# endif
+# include <pwd.h>
+# include <grp.h>
+
+# include <sys/stat.h>
+# ifndef S_IXUSR
+# define S_IXUSR S_IEXEC
+# define S_IXGRP (S_IEXEC>>3)
+# define S_IXOTH (S_IEXEC>>6)
+# endif
+
+#endif
+
+#ifndef errno
+ extern errno;
+#endif
+
+#include <stdio.h>
+#ifndef R_OK
+# define R_OK 4 /* Test for Read permission */
+# define W_OK 2 /* Test for Write permission */
+# define X_OK 1 /* Test for eXecute permission */
+# define F_OK 0 /* Test for existence of File */
+#endif
+%}
+
+!OperatingSystem class methodsFor:'misc'!
+
+exit
+ "shutdown smalltalk immediately"
+
+%{ /* NOCONTEXT */
+ mainExit(0);
+%}
+ "OperatingSystem exit - dont evaluate this"
+!
+
+exit:exitCode
+ "shutdown smalltalk immediately returning an exit-code"
+
+%{ /* NOCONTEXT */
+ if (! _isSmallInteger(exitCode))
+ exit(1);
+ mainExit(_intVal(exitCode));
+%}
+ "OperatingSystem exit:1 - dont evaluate this"
+!
+
+getEnvironment:aString
+ "get an environment string"
+
+%{ /* NOCONTEXT */
+
+ char *env;
+
+ if (_isString(aString)) {
+ env = (char *)getenv(_stringVal(aString));
+ if (env) {
+ RETURN ( _MKSTRING(env COMMA_CON) );
+ }
+ }
+%}
+.
+ ^ nil
+
+ "OperatingSystem getEnvironment:'LANG'"
+!
+
+getLoginName
+ "return a string with the users name"
+
+%{ /* NOCONTEXT */
+
+ char *name = "you";
+#ifndef transputer
+ name = (char *)getlogin();
+ if (! name) {
+ name = (char *)getenv("LOGNAME");
+ }
+#endif
+ RETURN ( _MKSTRING(name COMMA_CON) );
+%}
+ "OperatingSystem getLogin"
+!
+
+getUserNameFromID:aNumber
+ "return the user-name-string for a given numeric user-id"
+
+%{ /* NOCONTEXT */
+
+#ifndef transputer
+ struct passwd *p;
+
+ if (_isSmallInteger(aNumber)) {
+ p = getpwuid(_intVal(aNumber));
+ if (p) {
+ RETURN ( _MKSTRING(p->pw_name COMMA_CON) );
+ }
+ }
+#endif
+%}
+.
+ ^ '???'
+
+ "OperatingSystem getUserNameFromID:0"
+!
+
+getGroupNameFromID:aNumber
+ "return the group-name-string for a given numeric group-id"
+
+%{ /* NOCONTEXT */
+
+#ifndef transputer
+ struct group *g;
+
+ if (_isSmallInteger(aNumber)) {
+ g = getgrgid(_intVal(aNumber));
+ if (g) {
+ RETURN ( _MKSTRING(g->gr_name COMMA_CON) );
+ }
+ }
+#endif
+%}
+.
+ ^ '???'
+
+ "OperatingSystem getGroupNameFromID:0"
+!
+
+getHomeDirectory
+ "return the name of the home directory"
+
+ ^ OperatingSystem getEnvironment:'HOME'
+
+ "OperatingSystem getHomeDirectory"
+!
+
+getProcessId
+ "return the processId"
+
+%{ /* NOCONTEXT */
+
+ int pid = 0;
+#ifndef transputer
+ pid = getpid();
+#endif
+ RETURN ( _MKSMALLINT(pid) );
+%}
+ "OperatingSystem getProcessId"
+!
+
+getCharacter
+ "read a character from keyboard"
+
+%{ /* NOCONTEXT */
+
+ RETURN ( _MKSMALLINT(getchar()) );
+%}
+!
+
+getCPUType
+ "return a string giving the type of machine we're running on"
+
+ |cpu|
+
+ cpu := 'unknown'.
+
+%{ /* NOCONTEXT */
+#ifdef vax
+ cpu = _MKSTRING("vax" COMMA_CON);
+#endif
+#ifdef mips
+ cpu = _MKSTRING("mips" COMMA_CON);
+#endif
+#ifdef i386
+ cpu = _MKSTRING("i386" COMMA_CON);
+#endif
+#ifdef ns32k
+ cpu = _MKSTRING("ns32k" COMMA_CON);
+#endif
+#ifdef mc68k
+ cpu = _MKSTRING("mc68k" COMMA_CON);
+#endif
+#ifdef sparc
+ cpu = _MKSTRING("sparc" COMMA_CON);
+#endif
+#ifdef snake
+ cpu = _MKSTRING("snake" COMMA_CON);
+#endif
+#ifdef rs6000
+ cpu = _MKSTRING("rs6000" COMMA_CON);
+#endif
+#ifdef alpha
+ cpu = _MKSTRING("alpha" COMMA_CON);
+#endif
+#ifdef transputer
+ cpu = _MKSTRING("transputer" COMMA_CON);
+#endif
+%}
+.
+ ^ cpu
+
+ "OperatingSystem getCPUType"
+!
+
+getOSType
+ "return a string giving the type of os we're running on"
+
+ |os|
+
+ os := 'unknown'.
+
+%{ /* NOCONTEXT */
+#ifdef sinix
+ os = _MKSTRING("sinix" COMMA_CON);
+#endif
+
+#ifdef ultrix
+ os = _MKSTRING("ultrix" COMMA_CON);
+#endif
+
+#ifdef sco
+ os = _MKSTRING("sco" COMMA_CON);
+#endif
+
+#ifdef LINUX
+ os = _MKSTRING("linux" COMMA_CON);
+#endif
+
+#ifdef BSD
+# ifdef MACH
+ os = _MKSTRING("mach" COMMA_CON);
+# endif
+
+# ifdef sunos
+ os = _MKSTRING("sunos" COMMA_CON);
+# endif
+
+# ifdef IRIS
+ os = _MKSTRING("irix" COMMA_CON);
+# endif
+
+ if (os == nil) os = _MKSTRING("bsd" COMMA_CON);
+#endif
+
+#ifdef SYSV
+# ifdef SYSV3
+ os = _MKSTRING("sys5.3" COMMA_CON);
+# else
+# ifdef SYSV4
+ os = _MKSTRING("sys5.4" COMMA_CON);
+# else
+ os = _MKSTRING("sys5" COMMA_CON);
+# endif
+# endif
+#endif
+%}
+.
+ ^ os
+
+ "OperatingSystem getOSType"
+!
+
+getSystemType
+ "return a string giving the type of system we're running on"
+
+ |sys|
+
+ sys := 'unknown'.
+
+%{ /* NOCONTEXT */
+#ifdef sinix
+ sys = _MKSTRING("sinix" COMMA_CON);
+#endif
+
+#ifdef ultrix
+ sys = _MKSTRING("ultrix" COMMA_CON);
+#endif
+
+#ifdef sco
+ sys = _MKSTRING("sco" COMMA_CON);
+#endif
+
+#ifdef sunos
+ sys = _MKSTRING("sunos" COMMA_CON);
+#endif
+
+#ifdef solaris
+ sys = _MKSTRING("solaris" COMMA_CON);
+#endif
+
+#ifdef NEXT
+ sys = _MKSTRING("next" COMMA_CON);
+#endif
+
+#ifdef IRIS
+ sys = _MKSTRING("iris" COMMA_CON);
+#endif
+
+#ifdef LINUX
+ sys = _MKSTRING("linux" COMMA_CON);
+#endif
+
+#ifdef BSD
+# ifdef MACH
+ if (sys == nil) sys = _MKSTRING("mach" COMMA_CON);
+# endif
+ if (sys == nil) sys = _MKSTRING("bsd" COMMA_CON);
+#endif
+
+#ifdef SYSV
+# ifdef SYSV3
+ if (sys == nil) sys = _MKSTRING("sys5.3" COMMA_CON);
+# else
+# ifdef SYSV4
+ if (sys == nil) sys = _MKSTRING("sys5.4" COMMA_CON);
+# else
+ if (sys == nil) sys = _MKSTRING("sys5" COMMA_CON);
+# endif
+# endif
+#endif
+%}
+.
+ ^ sys
+
+ "OperatingSystem getSystemType"
+!
+
+isBSDlike
+ "return true, if the OS we're running on is a real unix."
+
+%{ /* NOCONTEXT */
+
+#ifdef BSD
+ RETURN ( true );
+#endif
+#ifdef SYSV4
+ RETURN ( true );
+#endif
+%}
+.
+ ^ false
+!
+
+maxFileNameLength
+ "return the max number of characters in a filename."
+
+%{ /* NOCONTEXT */
+#if defined(BSD) || defined(SYSV4) || defined(LINUX)
+ RETURN ( _MKSMALLINT(255) );
+#else
+# ifdef SYSV
+ RETURN ( _MKSMALLINT(14) );
+# endif
+# ifdef MSDOS
+ RETURN ( _MKSMALLINT(9) );
+# endif
+#endif
+%}
+.
+ ^ 14
+! !
+
+!OperatingSystem class methodsFor:'error messages'!
+
+errorTextForNumber:errNr
+ "return a message string from a unix errorNumber
+ (as returned by a system call). Should be replaced by
+ a resource lookup."
+
+ |msg messages|
+
+ (Language == #german) ifTrue:[
+ messages := #('keine superuser Berechtigung'
+ 'ungueltiger Datei- oder VerzeichnisName'
+ nil "'ungueltige Prozessnummer' "
+ nil "'unterbrochener systemcall' "
+ 'E/A Fehler'
+ nil "'Geraet existiert nicht' "
+ 'zu viele Argumente'
+ 'nicht ausfuehrbar'
+ nil "'falscher FileDescriptor'"
+ nil "'kein Kindprozess' "
+ 'zu viele Prozesse oder zu wenig Speicher'
+ 'zu wenig Speicher'
+ 'keine ZugriffsBerechtigung'
+ nil "'falsche Adresse' "
+ nil "'kein Blockgeraet' "
+ nil "'Platte noch im Zugriff' "
+ 'Datei existiert bereits'
+ nil "'Link ueber Plattengrenzen hinweg' "
+ 'Geraet existiert nicht'
+ 'ist kein Verzeichnis'
+ 'ist ein Verzeichnis'
+ nil "'ungueltiges Argument' "
+ 'zu viele Dateien offen'
+ 'zu viele Dateien offen'
+ nil "'kein Terminalgeraet' "
+ 'Datei wird gerade ausgefuehrt'
+ 'Datei zu gross'
+ 'Platte ist voll'
+ 'ungueltige Positionierung'
+ 'Platte ist schreibgeschuetzt'
+ 'zu viele Links'
+ 'Pipe unterbrochen'
+ 'argument nicht im gueltigen Bereich'
+ 'Ergebnis nicht im gueltigen Bereich')
+ ] ifFalse:[
+ messages := #('Not super-user'
+ 'No such file or directory'
+ nil "'No such process' "
+ nil "'interrupted system call' "
+ 'I/O error'
+ nil "'No such device or address' "
+ 'Arg list too long'
+ 'Exec format error'
+ nil "'Bad file number'"
+ nil "'No children' "
+ 'No more processes'
+ 'Not enough core'
+ 'Permission denied'
+ nil "'Bad address' "
+ nil "'Block device required' "
+ nil "'Mount device busy' "
+ 'File exists'
+ nil "'Cross-device link' "
+ 'No such device'
+ 'Not a directory'
+ 'Is a directory'
+ nil 'Invalid argument'
+ 'File table overflow'
+ 'Too many open files'
+ nil "'Not a typewriter' "
+ 'Text file busy'
+ 'File too large'
+ 'No space left on device'
+ 'Illegal seek'
+ 'Read only file system'
+ 'Too many links'
+ 'Broken pipe'
+ 'Math arg out of domain of func'
+ 'Math result not representable')
+ ].
+
+ (errNr between:1 and:messages size) ifTrue:[
+ msg := messages at:errNr
+ ].
+ msg isNil ifTrue:[
+ ^ ('ErrorNr: ' , errNr printString)
+ ].
+ ^ msg
+! !
+
+!OperatingSystem class methodsFor:'interrupts'!
+
+enableUserInterrupts
+ "enable userInterrupt (^C) handling;
+ after enabling, ^C will send the message 'userInterrupt'
+ to the UserInterruptHandler object."
+
+%{ /* NOCONTEXT */
+ extern void userInterrupt(), exceptionInterrupt();
+
+ signal(SIGINT, userInterrupt);
+ /* signal(SIGQUIT, userInterrupt); */
+%}
+!
+
+enableFpExceptionInterrupts
+ "enable floating point exception interrupts (if the
+ architecture supports it).
+ after enabling, fpu-exceptions will send the message
+ 'fpuExceptionInterrupt' to the FPUExceptionInterruptHandler object."
+
+%{ /* NOCONTEXT */
+ extern void fpExceptionInterrupt();
+
+ signal(SIGFPE, fpExceptionInterrupt);
+%}
+!
+
+enableSignalInterrupts
+ "enable signal exception interrupts (trap, buserror & segm. violation).
+ after enabling, these exceptions will send the message
+ 'signalInterrupt' to the SignalInterruptHandler object."
+
+%{ /* NOCONTEXT */
+ extern void signalPIPEInterrupt();
+ extern void signalBUSInterrupt();
+ extern void signalSEGVInterrupt();
+
+ signal(SIGPIPE, signalPIPEInterrupt);
+#ifdef SIGBUS
+ signal(SIGBUS, signalBUSInterrupt);
+#endif
+ signal(SIGSEGV, signalSEGVInterrupt);
+%}
+!
+
+enableIOInterrupts
+ "enable IO availability interrupts
+ (SIGPOLL/SIGIO, if the architecture supports it).
+ after enabling, these signals will send the message
+ 'ioInterrupt' to the IOInterruptHandler object."
+
+%{ /* NOCONTEXT */
+ extern void ioInterrupt();
+
+#ifdef SIGPOLL
+ signal(SIGPOLL, ioInterrupt);
+#endif
+#ifdef SIGIO
+ signal(SIGIO, ioInterrupt);
+#endif
+%}
+!
+
+startSpyTimer
+ "trigger a spyInterrupt, to be signalled after some (short) time.
+ This is used by MessageTally for profiling."
+
+%{ /* NOCONTEXT */
+
+ extern void spyInterrupt();
+#ifdef FAST_TIMER
+ /*
+ * using PD ft-driver for tick-resolution signals
+ */
+ if (timer_fd < 0) {
+ timer_fd = open("/dev/ft0", 0);
+ }
+ if (timer_fd > 0) {
+ ioctl(timer_fd, FTIOCSET, (2<<16) | SIGALRM);
+ }
+#else
+# if defined(BSD) || defined(isc3_2) || defined(SYSV4) || defined(LINUX)
+ struct itimerval dt;
+
+ dt.it_interval.tv_sec = 0;
+ dt.it_interval.tv_usec = 0;
+ dt.it_value.tv_sec = 0;
+ dt.it_value.tv_usec = 10000; /* 100 Hz */
+ setitimer(ITIMER_VIRTUAL, &dt, 0);
+# ifdef BSD
+# ifndef SYSV4
+ sigsetmask(0);
+# endif
+# endif
+# endif
+#endif
+
+#ifdef SIGVTALRM
+ signal(SIGVTALRM, spyInterrupt);
+#else
+ signal(SIGALRM, spyInterrupt);
+#endif
+%}
+!
+
+stopSpyTimer
+ "stop spy timing"
+
+%{ /* NOCONTEXT */
+
+#ifdef FAST_TIMER
+ if (timer_fd > 0) {
+ ioctl(timer_fd, FTIOCANCEL, 0);
+ close(timer_fd);
+ timer_fd = -1;
+ }
+#else
+# if defined(BSD) || defined(isc3_2) || defined(SYSV4) || defined(LINUX)
+ struct itimerval dt;
+
+ dt.it_interval.tv_sec = 0;
+ dt.it_interval.tv_usec = 0;
+ dt.it_value.tv_sec = 0;
+ dt.it_value.tv_usec = 0;
+ setitimer(ITIMER_VIRTUAL, &dt, 0);
+# endif
+#endif
+%}
+! !
+
+!OperatingSystem class methodsFor:'time and date'!
+
+getTimeLow
+ "return low 16 bits of current time.
+ Obsolete: Dont use this method, use getTimeParts below.
+ This method will not always return the correct time
+ if used together with getTimeHi.
+ (a wrap between the two getTimeXXX calls could occur)"
+
+%{ /* NOCONTEXT */
+
+ RETURN ( _MKSMALLINT(time(0) & 0xFFFF) );
+%}
+.
+ self primitiveFailed
+
+ "OperatingSystem getTimeLow"
+!
+
+getTimeHi
+ "return hi 16 bits of current time.
+ Obsolete: Dont use this method, use getTimeParts below.
+ This method will NOT always return the correct time
+ if used together with getTimeHi.
+ (a wrap between the two getTimeXXX calls could occur)"
+
+%{ /* NOCONTEXT */
+
+ RETURN ( _MKSMALLINT((time(0) >> 16) & 0xFFFF) );
+%}
+.
+ self primitiveFailed
+
+ "OperatingSystem getTimeHi"
+!
+
+getTimeInto:aBlock
+ "evaluate the argument aBlock, passing the time-parts of
+ the current time as arguments."
+
+ |low hi|
+%{
+ int now;
+
+ now = time(0);
+ hi = _MKSMALLINT((now >> 16) & 0xFFFF);
+ low = _MKSMALLINT(now & 0xFFFF);
+%}
+.
+ aBlock value:low value:hi
+
+ "OperatingSystem getTimeTimeInto:[:low :hi | low printNewline. hi printNewline]"
+!
+
+getTime
+ "return current Time (in seconds since 1970).
+ This might return a LargeInteger some time."
+
+ ^ self getTimeHi * 16r10000 + self getTimeLow
+
+ "OperatingSystem getTime"
+!
+
+computeDatePartsOf:timeLow and:timeHi for:aBlock
+ "compute year, month and day from the time-parts timeLow and
+ timeHi and evaluate the argument, a 3-arg block with these.
+ This method was added to avoid LargeInteger arithmetic; the time-parts
+ are those returned by getTimeLow and getTimeHi."
+
+ |year month day|
+
+ ((timeLow isMemberOf:SmallInteger) and:[timeHi isMemberOf:SmallInteger])
+ ifFalse:[
+ ^ self primitiveFailed
+ ].
+%{
+ struct tm *tmPtr;
+ long t;
+
+ t = (_intVal(timeHi) << 16) | _intVal(timeLow);
+ tmPtr = localtime(&t);
+ year = _MKSMALLINT(tmPtr->tm_year + 1900);
+ month = _MKSMALLINT(tmPtr->tm_mon + 1);
+ day = _MKSMALLINT(tmPtr->tm_mday);
+%}
+.
+ aBlock value:year value:month value:day
+!
+
+computeTimePartsOf:timeLow and:timeHi for:aBlock
+ "compute hours, minutes and seconds from the time-parts timeLow and
+ timeHi and evaluate the argument, a 3-arg block with these."
+
+ |hours minutes seconds|
+
+ ((timeLow isMemberOf:SmallInteger) and:[timeHi isMemberOf:SmallInteger])
+ ifFalse:[
+ ^ self primitiveFailed
+ ].
+%{
+ struct tm *tmPtr;
+ long t;
+
+ t = (_intVal(timeHi) << 16) | _intVal(timeLow);
+ tmPtr = localtime(&t);
+ hours = _MKSMALLINT(tmPtr->tm_hour);
+ minutes = _MKSMALLINT(tmPtr->tm_min);
+ seconds = _MKSMALLINT(tmPtr->tm_sec);
+%}
+.
+ aBlock value:hours value:minutes value:seconds
+!
+
+getMillisecondTime
+ "since range is limited to 0..1ffffff and value is wrapping around
+ at 1fffffff, this can only be used for relative time deltas.
+ Use methods below to compare and add time deltas (should move to Time)"
+
+%{ /* NOCONTEXT */
+
+ long t;
+#ifdef SYSV
+# ifdef HZ
+ /* sys5 time */
+ long ticks;
+ struct tms tb;
+
+ ticks = times(&tb);
+ t = (ticks * 1000) / HZ;
+# endif
+#else
+ /* bsd time */
+ struct timeval tb;
+ struct timezone tzb;
+
+ gettimeofday(&tb, &tzb);
+ t = tb.tv_sec*1000 + tb.tv_usec/1000;
+#endif
+ RETURN ( _MKSMALLINT(t & 0x0FFFFFFF) );
+%}
+.
+ self error:'time not available'
+!
+
+millisecondTimeDeltaBetween:msTime1 and:msTime2
+ "subtract two millisecond times (such as returned getMillisecondTime).
+ The returned value is msTime1 - msTime2 where a wrap occurs at:16r0FFFFFFF."
+
+ (msTime1 > msTime2) ifTrue:[
+ ^ msTime1 - msTime2
+ ].
+ ^ msTime1 + 16r10000000 - msTime2
+!
+
+millisecondTime:msTime1 isAfter:msTime2
+ "return true if msTime1 is after msTime2, false if not.
+ handling wrap at 16r0FFFFFFF. The two arguments are
+ millisecond times (such as returned getMillisecondTime)."
+
+ (msTime1 > msTime2) ifTrue:[
+ ((msTime1 - msTime2) > 16r08000000) ifTrue:[
+ ^ false
+ ].
+ ^ true
+ ].
+ ((msTime2 - msTime1) > 16r08000000) ifTrue:[
+ ^ true
+ ].
+ ^ false
+!
+
+millisecondTimeAdd:msTime1 and:msTime2
+ "add two millisecond times (such as returned getMillisecondTime).
+ The returned value is msTime1 + msTime2 where a wrap occurs at:16r0FFFFFFF."
+
+ |sum|
+
+ sum := msTime1 + msTime2.
+ (sum > 16r0FFFFFFF) ifTrue:[^ sum - 16r10000000].
+ (sum < 0) ifTrue:[^ sum + 16r10000000].
+ ^ sum
+!
+
+millisecondDelay:millis
+ "delay execution for millis milliseconds."
+
+ self selectOn:nil withTimeOut:(millis * 0.001)
+
+ "OperatingSystem millisecondDelay:1000"
+!
+
+sleep:numberOfSeconds
+ "cease any action for some time.
+ Not really useful since not even low-prio processes and interrupt
+ handling will run during the sleep - use millisecondDelay:."
+
+%{ /* NOCONTEXT */
+
+ if (_isSmallInteger(numberOfSeconds)) {
+ sleep(_intVal(numberOfSeconds));
+ RETURN ( self );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+selectOn:aFileDescriptor withTimeOut:seconds
+ "wait for aFileDesriptor to become ready; timeout after t seconds.
+ Return true, if i/o ok, false if timed-out or interrupted.
+ With 0 as timeout argument, this can be used to check for availability
+ of read-data.
+ Experimental."
+
+ |millis|
+
+ millis := (seconds * 1000) rounded.
+%{
+ fd_set rset, wset, eset;
+ int t, fd, i, lX, bX;
+ struct timeval wt;
+
+ if ((aFileDescriptor == nil) || _isSmallInteger(aFileDescriptor)) {
+ if (_isSmallInteger(millis)) {
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&eset);
+ if (aFileDescriptor != nil) {
+ fd = _intVal(aFileDescriptor);
+ if ((fd >= 0) && (fd < FD_SETSIZE))
+ FD_SET(fd, &rset);
+ } else
+ fd = 0;
+ t = _intVal(millis);
+ wt.tv_sec = t / 1000;
+ wt.tv_usec = (t % 1000) * 1000;
+ RETURN ( (select(fd+1, &rset, &wset, &eset, &wt) == 0) ? false
+ : true );
+ }
+ }
+%}
+.
+ self primitiveFailed
+!
+
+selectOn:fd1 and:fd2 withTimeOut:seconds
+ "wait for any fd to become ready; timeout after t seconds.
+ Return fd if i/o ok, nil if timed-out or interrupted.
+ Experimental."
+
+ |millis|
+
+ millis := (seconds * 1000) rounded.
+%{
+ fd_set rset, wset, eset;
+ int t, f1, f2, i, lX, bX;
+ struct timeval wt;
+ OBJ retFd;
+
+ if (((fd1 == nil) || _isSmallInteger(fd1))
+ && ((fd2 == nil) || _isSmallInteger(fd2))) {
+ if (_isSmallInteger(millis)) {
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&eset);
+ if (fd1 != nil) {
+ f1 = _intVal(fd1);
+ if ((f1 >= 0) && (f1 < FD_SETSIZE))
+ FD_SET(f1, &rset);
+ } else
+ f1 = 0;
+ if (fd2 != nil) {
+ f2 = _intVal(fd2);
+ if ((f2 >= 0) && (f2 < FD_SETSIZE))
+ FD_SET(f2, &rset);
+ } else
+ f2 = 0;
+ if (f2 > f1)
+ f1 = f2;
+ t = _intVal(millis);
+ wt.tv_sec = t / 1000;
+ wt.tv_usec = (t % 1000) * 1000;
+ if (select(f1+1, &rset, &wset, &eset, &wt)) {
+ if (FD_ISSET(f1, &rset)) retFd = fd1;
+ else if (FD_ISSET(f1, &wset)) retFd = fd1;
+ else if (FD_ISSET(f1, &eset)) retFd = fd1;
+ else if (FD_ISSET(f2, &rset)) retFd = fd2;
+ else if (FD_ISSET(f2, &wset)) retFd = fd2;
+ else if (FD_ISSET(f2, &eset)) retFd = fd2;
+ RETURN ( retFd );
+ }
+ RETURN ( nil );
+ }
+ }
+%}
+.
+ self primitiveFailed
+!
+
+selectOnAnyReadable:fdArray withTimeOut:seconds
+ "wait for any fd in fdArray (an Array of integers) to become ready;
+ timeout after t seconds. An empty set will always wait.
+ Return first ready fd if i/o ok, nil if timed-out or interrupted.
+ Experimental."
+
+ |millis count|
+
+ millis := (seconds * 1000) rounded asInteger.
+ (fdArray class == Array) ifFalse:[
+ ^ self error:'argument must be an Array'
+ ].
+ count := fdArray size.
+%{
+ fd_set rset, wset, eset;
+ int t, f, maxF, i, lX, bX;
+ struct timeval wt;
+ OBJ fd, retFd;
+
+ if (_isSmallInteger(millis)) {
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&eset);
+
+ maxF = 0;
+ for (i=0; i<_intVal(count);i++) {
+ fd = _ArrayInstPtr(fdArray)->a_element[i];
+ if (fd != nil) {
+ f = _intVal(fd);
+ if ((f >= 0) && (f < FD_SETSIZE)) {
+ FD_SET(f, &rset);
+/* FD_SET(f, &wset); */
+/* FD_SET(f, &eset); */
+ if (f > maxF) maxF = f;
+ }
+ }
+ }
+ t = _intVal(millis);
+ wt.tv_sec = t / 1000;
+ wt.tv_usec = (t % 1000) * 1000;
+ if (select(maxF+1, &rset, &wset, &eset, &wt)) {
+ for (i=0; i <= maxF; i++) {
+ if (FD_ISSET(i, &rset)
+/* || FD_ISSET(i, &wset)
+ || FD_ISSET(i, &eset) */ ) {
+ RETURN ( _MKSMALLINT(i) );
+ }
+ }
+ }
+ RETURN ( nil );
+ }
+%}
+.
+ self primitiveFailed
+! !
+
+!OperatingSystem class methodsFor:'executing commands'!
+
+fork
+ "fork a new process"
+
+%{ /* NOCONTEXT */
+
+ int pid;
+
+ pid = fork();
+ RETURN ( _MKSMALLINT(pid) );
+%}
+.
+ self primitiveFailed
+!
+
+exec:aPath withArguments:argArray
+ "execute the unix command specified by the argument, aPath, with
+ arguments in argArray.
+ If successful, this method does not return and smalltalk is gone.
+ If not sucessfull, false is returned. Normal use is with fork."
+
+%{
+ char *argv[64];
+ int nargs, i;
+ OBJ arg;
+
+ if (_isString(aPath) && _isArray(argArray)) {
+ nargs = _arraySize(argArray);
+ for (i=0; i < nargs; i++) {
+ arg = _ArrayInstPtr(argArray)->a_element[i];
+ if (_isString(arg)) {
+ argv[i] = (char *) _stringVal(arg);
+ }
+ }
+ argv[i] = NULL;
+ execv(_stringVal(aPath), argv);
+ /* should not be reached */
+ RETURN ( false );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+executeCommand:aCommandString
+ "execute the unix command specified by the argument, aCommandString.
+ Return true if successful, false otherwise. Smalltalk is suspended,
+ while the command is executing."
+
+%{ /* NOCONTEXT */
+
+ int status;
+ extern OBJ ErrorNumber;
+
+ if (_isString(aCommandString)) {
+ status = system((char *) _stringVal(aCommandString));
+ if (status == 0) {
+ RETURN ( true );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( false );
+ }
+%}
+.
+ self primitiveFailed
+
+ "OperatingSystem executeCommand:'pwd'"
+ "OperatingSystem executeCommand:'ls -l'"
+ "OperatingSystem executeCommand:'invalidCommand'"
+! !
+
+!OperatingSystem class methodsFor:'file access'!
+
+baseNameOf:aPath
+ "return the baseName of the argument, aPath
+ - thats the file/directory name without leading parent-dirs
+ (i.e. OperatingSystem baseNameOf:'/usr/lib/st/file' -> 'file'
+ and OperatingSystem baseNameOf:'/usr/lib' -> lib).
+ This method does not check if the path is valid (i.e. if these directories
+ really exist)."
+
+ |prev index|
+
+ (aPath = '/') ifTrue:[^ aPath].
+ prev := 1.
+ [true] whileTrue:[
+ index := aPath indexOf:$/
+ startingAt:prev
+ ifAbsent:[^ aPath copyFrom:prev].
+ prev := index + 1
+ ]
+!
+
+directoryNameOf:aPath
+ "return the directoryName of the argument, aPath
+ - thats the name of the directory where aPath is
+ (i.e. OperatingSystem directoryNameOf:'/usr/lib/st/file' -> '/usr/lib/st'
+ and OperatingSystem directoryNameOf:'/usr/lib' -> /usr').
+ This method does not check if the path is valid (i.e. if these directories
+ really exist)."
+
+ |last|
+
+ (aPath = '/') ifTrue:[^ aPath].
+ (aPath startsWith:'/') ifFalse:[
+ (aPath endsWith:'/') ifTrue:[
+ ^ aPath copyFrom:1 to:(aPath size - 1)
+ ].
+ ].
+ last := 1.
+ [true] whileTrue:[
+ last := aPath indexOf:$/
+ startingAt:(last + 1)
+ ifAbsent:[(last == 1) ifTrue:[^ '/'].
+ ^ aPath copyFrom:1 to:(last - 1)
+ ]
+ ]
+
+ "OperatingSystem directoryNameOf:'/fee/foo/bar'"
+ "OperatingSystem directoryNameOf:'foo/bar'"
+ "OperatingSystem directoryNameOf:'../../foo/bar'"
+!
+
+isValidPath:aPathName
+ "return true, if 'aPathName' is a valid path name
+ (i.e. the file or directory exists)"
+
+%{ /* NOCONTEXT */
+
+ struct stat buf;
+
+ if (_isString(aPathName)) {
+ RETURN ( (stat((char *) _stringVal(aPathName), &buf) < 0) ? false : true );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+isDirectory:aPathName
+ "return true, if 'aPathName' is a valid directory path name.
+ (i.e. exists and is a directory)"
+
+%{ /* NOCONTEXT */
+
+ struct stat buf;
+
+ if (_isString(aPathName)) {
+ if ((stat((char *) _stringVal(aPathName), &buf) < 0)
+ || ((buf.st_mode & S_IFMT) != S_IFDIR)) {
+ RETURN ( false );
+ }
+ RETURN ( true );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+isReadable:aPathName
+ "return true, if the file/dir 'aPathName' is readable."
+
+%{ /* NOCONTEXT */
+
+ extern OBJ ErrorNumber;
+
+ if (_isString(aPathName)) {
+ if (access(_stringVal(aPathName), R_OK) == 0) {
+ RETURN ( true );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( false );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+isWritable:aPathName
+ "return true, if the given file is writable"
+
+%{ /* NOCONTEXT */
+
+ extern OBJ ErrorNumber;
+
+ if (_isString(aPathName)) {
+ if (access(_stringVal(aPathName), W_OK) == 0) {
+ RETURN ( true );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( false );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+isExecutable:aPathName
+ "return true, if the given file is executable"
+
+%{ /* NOCONTEXT */
+
+ extern OBJ ErrorNumber;
+
+ if (_isString(aPathName)) {
+ if (access(_stringVal(aPathName), X_OK) == 0) {
+ RETURN ( true );
+ }
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( false );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+infoOf:aPathName
+ "return an dictionary filled with info for the file 'aPathName';
+ info is: (type->t mode->n uid->u gid->g size->s id->ino).
+ return nil if such a file does not exist. A dictionary is returned,
+ since we might need to add more info in the future without affecting
+ existing applications."
+
+ |info type mode uid gid size id|
+
+ "{ Symbol: directory }"
+ "{ Symbol: regular }"
+ "{ Symbol: characterSpecial }"
+ "{ Symbol: blockSpecial }"
+ "{ Symbol: fifo }"
+ "{ Symbol: socket }"
+ "{ Symbol: symbolicLink }"
+ "{ Symbol: unknown }"
+
+%{
+ struct stat buf;
+
+ if (_isString(aPathName)) {
+ if (stat((char *) _stringVal(aPathName), &buf) < 0) {
+ RETURN ( nil );
+ }
+ switch (buf.st_mode & S_IFMT) {
+ case S_IFDIR:
+ type = _directory;
+ break;
+ case S_IFCHR:
+ type = _characterSpecial;
+ break;
+ case S_IFBLK:
+ type = _blockSpecial;
+ break;
+ case S_IFREG:
+ type = _regular;
+ break;
+#ifdef S_IFLNK
+ case S_IFLNK:
+ type = _symbolicLink;
+ break;
+#endif
+#ifdef S_IFSOCK
+ case S_IFSOCK:
+ type = _socket;
+ break;
+#endif
+#ifdef S_IFIFO
+ case S_IFIFO:
+ type = _fifo;
+ break;
+#endif
+ default:
+ type = _unknown;
+ break;
+ }
+ mode = _MKSMALLINT(buf.st_mode & 0777);
+ uid = _MKSMALLINT(buf.st_uid);
+ gid = _MKSMALLINT(buf.st_gid);
+ size = _MKSMALLINT(buf.st_size);
+ id = _MKSMALLINT(buf.st_ino);
+ }
+%}
+.
+ mode notNil ifTrue:[
+ info := IdentityDictionary new.
+ info at:#type put:type.
+ info at:#mode put:mode.
+ info at:#uid put:uid.
+ info at:#gid put:gid.
+ info at:#size put:size.
+ info at:#id put:id.
+ ^ info
+ ].
+ self primitiveFailed
+
+ "OperatingSystem infoOf:'/'"
+ "(OperatingSystem infoOf:'/') at:#uid"
+!
+
+accessModeOf:aPathName
+ "return a number representing access rights rwxrwxrwx for owner,
+ group and others. return nil if such a file does not exist."
+
+ "
+ this could have been implemented as:
+ (self infoOf:aPathName) at:#mode
+ but for huge directory searches the code below is faster
+ "
+
+%{ /* NOCONTEXT */
+
+ struct stat buf;
+
+ if (_isString(aPathName)) {
+ if (stat((char *) _stringVal(aPathName), &buf) < 0) {
+ RETURN ( nil );
+ }
+ RETURN ( _MKSMALLINT(buf.st_mode & 0777) );
+ }
+%}
+.
+ self primitiveFailed
+
+ "(OperatingSystem accessModeOf:'/') printStringRadix:8"
+!
+
+changeAccessModeOf:aPathName to:modeBits
+ "change the access rights rwxrwxrwx for owner,
+ group and others of aPathName. return true if changed, false
+ if such a file does not exist or change was not allowd."
+
+%{ /* NOCONTEXT */
+
+ if (_isString(aPathName) && _isSmallInteger(modeBits)) {
+ RETURN ( (chmod((char *) _stringVal(aPathName), _intVal(modeBits) ) < 0) ?
+ false : true );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+timeOfLastChange:aPathName
+ "return the timeStamp of a file"
+
+ |timeLow timeHi|
+%{
+ struct stat buf;
+ time_t mtime;
+
+ if (_isString(aPathName)) {
+ if (stat((char *) _stringVal(aPathName), &buf) >= 0) {
+ timeLow = _MKSMALLINT(buf.st_mtime & 0xFFFF);
+ timeHi = _MKSMALLINT((buf.st_mtime >> 16) & 0xFFFF);
+ }
+ }
+%}
+.
+ timeLow notNil ifTrue:[^ Time fromUnixTimeLow:timeLow and:timeHi].
+ self primitiveFailed
+
+ "OperatingSystem timeOfLastChange:'/'"
+!
+
+idOf:aPathName
+ "return the fileNumber (i.e. inode number) of a file"
+
+ "
+ this could have been implemented as:
+ (self infoOf:aPathName) at:#id
+ but for huge directory searches the code below is faster
+ "
+
+%{ /* NOCONTEXT */
+
+ struct stat buf;
+
+ if (_isString(aPathName)) {
+ if (stat((char *) _stringVal(aPathName), &buf) >= 0) {
+ RETURN (_MKSMALLINT(buf.st_ino));
+ }
+ }
+%}
+.
+ self primitiveFailed
+
+ "OperatingSystem idOf:'/'"
+!
+
+typeOf:aPathName
+ "return the type of a file as a symbol"
+
+ "
+ this could have been implemented as:
+ (self infoOf:aPathName) at:#type
+ but for huge directory searches the code below is faster
+ "
+
+%{ /* NOCONTEXT */
+
+ struct stat buf;
+
+ if (_isString(aPathName)) {
+ if (stat((char *) _stringVal(aPathName), &buf) < 0) {
+ RETURN ( nil );
+ }
+ switch (buf.st_mode & S_IFMT) {
+ case S_IFDIR:
+ RETURN ( _directory );
+ case S_IFCHR:
+ RETURN ( _characterSpecial );
+ case S_IFBLK:
+ RETURN ( _blockSpecial );
+ case S_IFREG:
+ RETURN ( _regular );
+#ifdef S_IFLNK
+ case S_IFLNK:
+ RETURN ( _symbolicLink );
+#endif
+#ifdef S_IFSOCK
+ case S_IFSOCK:
+ RETURN ( _socket );
+#endif
+#ifdef S_IFIFO
+ case S_IFIFO:
+ RETURN ( _fifo );
+#endif
+ default:
+ RETURN ( _unknown );
+ }
+ }
+%}
+.
+ self primitiveFailed
+
+ "OperatingSystem typeOf:'/'"
+!
+
+createDirectory:newPathName
+ "create a new directory with name 'newPathName'.
+ Return true if successful, false if failed."
+
+ "since createDirectory is not used too often,
+ you'll forgive me using mkdir ..."
+
+ ^ self executeCommand:('mkdir ' , newPathName)
+
+ "OperatingSystem createDirectory:'foo'"
+!
+
+recursiveCreateDirectory:dirName
+ "create a directory - with all parent dirs if needed.
+ Return true if successful, false otherwise. If false
+ is returned, a partial created tree may be left,
+ which is not cleaned-up here."
+
+ self createDirectory:dirName.
+ (self isDirectory:dirName) ifFalse:[
+ (self recursiveCreateDirectory:(self directoryNameOf:dirName)) ifFalse:[^ false].
+ ^ self createDirectory:dirName
+ ].
+ ^ (self isDirectory:dirName)
+
+ "OperatingSystem recursiveCreateDirectory:'foo/bar/baz'"
+!
+
+removeFile:fullPathName
+ "remove the file named 'fullPathName'; return true if successful"
+
+%{ /* NOCONTEXT */
+
+ if (_isString(fullPathName)) {
+ RETURN ( (unlink((char *) _stringVal(fullPathName)) >= 0) ? true : false );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+removeDirectory:fullPathName
+ "remove the directory named 'fullPathName'.
+ Return true if successful, false if directory is not empty or no permission"
+
+%{ /* NOCONTEXT */
+
+ if (_isString(fullPathName)) {
+ RETURN ( (rmdir((char *) _stringVal(fullPathName)) >= 0) ? true : false );
+ }
+%}
+.
+ self primitiveFailed
+!
+
+link:oldPath to:newPath
+ "link the file 'oldPath' to 'newPath'. The link will be a hard link.
+ Return true if successful, false if not."
+
+%{ /* NOCONTEXT */
+
+ if (_isString(oldPath) && _isString(newPath)) {
+ RETURN ( (link((char *) _stringVal(oldPath), (char *) _stringVal(newPath)) >= 0) ?
+ true : false );
+ }
+%}
+.
+ self primitiveFailed
+
+ "OperatingSystem link:'foo' to:'bar'"
+!
+
+rename:oldPath to:newPath
+ "rename the file 'oldPath' to 'newPath'.
+ Return true if sucessfull, false if not"
+
+%{ /* NOCONTEXT */
+
+ if (_isString(oldPath) && _isString(newPath)) {
+#if defined(BSD)
+ if (rename((char *) _stringVal(oldPath), (char *) _stringVal(newPath)) >= 0) {
+ RETURN ( true );
+ }
+#else
+ if (link((char *) _stringVal(oldPath), (char *) _stringVal(newPath)) >= 0) {
+ if (unlink((char *) _stringVal(oldPath)) >= 0) {
+ RETURN ( true );
+ }
+ unlink((char *) _stringVal(newPath));
+ }
+#endif
+ RETURN ( false );
+ }
+%}
+.
+ self primitiveFailed
+
+ "OperatingSystem rename:'foo' to:'bar'"
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/WriteStr.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,199 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+PositionableStream subclass:#WriteStream
+ instanceVariableNames:'writeLimit'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams'
+!
+
+WriteStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+Streams for writing into - this implementation currently DOES change the identity if the
+streamed-upon collection if it cannot grow. Thus its slightly incompatible to ST-80 since
+aStream contents does not always return the original collection. This will change soon.
+
+%W% %E%
+'!
+
+!WriteStream class methodsFor:'instance creation'!
+
+on:aCollection from:start to:last
+ "create and return a new stream for writing onto aCollection, where
+ writing is limited to the elements in the range start to last."
+
+ |newStream|
+ newStream := super on:aCollection from:start to:last.
+ newStream writeLimit:last.
+ ^ newStream
+! !
+
+!WriteStream methodsFor:'private'!
+
+on:aCollection
+ "create and return a new stream for writing onto aCollection"
+
+ super on:aCollection.
+ writeLimit := readLimit
+! !
+
+!WriteStream methodsFor:'accessing'!
+
+contents
+ "return the current contents (a collection) of the stream"
+
+ collection size == (position - 1) ifFalse:[
+ collection isFixedSize ifTrue:[
+ collection := collection copyFrom:1 to:(position - 1)
+ ] ifFalse:[
+ collection grow:(position - 1)
+ ]
+ ].
+ ^ collection
+!
+
+writeLimit:aNumber
+ "set the writeLimit, thats the position after which writing is
+ prohibited."
+
+ (aNumber < collection size) ifTrue:[writeLimit := aNumber]
+! !
+
+!WriteStream methodsFor:'reading/writing'!
+
+next
+ "catch read access to write stream"
+
+ ^ self error:'WriteStreams cannot read'
+!
+
+peek
+ "catch read access to write stream"
+
+ ^ self error:'WriteStreams cannot read'
+!
+
+nextPut:anObject
+ "append the argument, anObject to the stream"
+
+ (position > collection size) ifTrue:[self growCollection].
+ collection at:position put:anObject.
+ (position > readLimit) ifTrue:[readLimit := position].
+ position := position + 1.
+ ^anObject
+!
+
+nextPutAll:aCollection
+ "append all elements of the argument, aCollection to the stream"
+
+ |nMore final|
+
+ nMore := aCollection size.
+ final := position + nMore - 1.
+ (final > collection size) ifTrue:[
+ self growCollection:final
+ ].
+ collection replaceFrom:position
+ to:final
+ with:aCollection
+ startingAt:1.
+
+ position := position + nMore.
+ (position > readLimit) ifTrue:[readLimit := position - 1].
+ ^ aCollection
+!
+
+cr
+ "append a carriage-return to the stream"
+
+ self nextPut:(Character cr)
+!
+
+tab
+ "append a tab-character to the stream"
+
+ self nextPut:(Character tab)
+!
+
+crTab
+ "append a carriage-return followed by a tab to the stream"
+
+ self nextPut:(Character cr).
+ self nextPut:(Character tab)
+!
+
+space
+ "append a space character to the receiver-stream"
+
+ self nextPut:(Character space)
+!
+
+spaces:count
+ "append count space-characters to the receiver-stream"
+
+ 1 to:count do:[:dummy |
+ self nextPut:(Character space)
+ ]
+!
+
+ff
+ "append a form-feed (new-pagee) to the receiver-stream"
+
+ self nextPut:(Character ff)
+! !
+
+!WriteStream methodsFor:'private'!
+
+growCollection
+ |oldSize newSize newColl|
+
+ oldSize := collection size.
+ (oldSize == 0) ifTrue:[
+ newSize := 10
+ ] ifFalse:[
+ newSize := oldSize * 2
+ ].
+ collection isFixedSize ifTrue:[
+ newColl := collection species new:newSize.
+ newColl replaceFrom:1 to:oldSize with:collection startingAt:1.
+ collection := newColl
+ ] ifFalse:[
+ collection grow:newSize.
+ ].
+ writeLimit := newSize
+!
+
+growCollection:minNewSize
+ |oldSize newSize newColl|
+
+ oldSize := collection size.
+ (oldSize == 0) ifTrue:[
+ newSize := minNewSize
+ ] ifFalse:[
+ newSize := oldSize * 2.
+ [newSize < minNewSize] whileTrue:[
+ newSize := newSize * 2
+ ]
+ ].
+ collection isFixedSize ifTrue:[
+ newColl := collection species new:newSize.
+ newColl replaceFrom:1 to:oldSize with:collection startingAt:1.
+ collection := newColl
+ ] ifFalse:[
+ collection grow:newSize
+ ].
+ writeLimit := newSize
+! !
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/WriteStream.st Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,199 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+
+PositionableStream subclass:#WriteStream
+ instanceVariableNames:'writeLimit'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Streams'
+!
+
+WriteStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+ All Rights Reserved
+
+Streams for writing into - this implementation currently DOES change the identity if the
+streamed-upon collection if it cannot grow. Thus its slightly incompatible to ST-80 since
+aStream contents does not always return the original collection. This will change soon.
+
+%W% %E%
+'!
+
+!WriteStream class methodsFor:'instance creation'!
+
+on:aCollection from:start to:last
+ "create and return a new stream for writing onto aCollection, where
+ writing is limited to the elements in the range start to last."
+
+ |newStream|
+ newStream := super on:aCollection from:start to:last.
+ newStream writeLimit:last.
+ ^ newStream
+! !
+
+!WriteStream methodsFor:'private'!
+
+on:aCollection
+ "create and return a new stream for writing onto aCollection"
+
+ super on:aCollection.
+ writeLimit := readLimit
+! !
+
+!WriteStream methodsFor:'accessing'!
+
+contents
+ "return the current contents (a collection) of the stream"
+
+ collection size == (position - 1) ifFalse:[
+ collection isFixedSize ifTrue:[
+ collection := collection copyFrom:1 to:(position - 1)
+ ] ifFalse:[
+ collection grow:(position - 1)
+ ]
+ ].
+ ^ collection
+!
+
+writeLimit:aNumber
+ "set the writeLimit, thats the position after which writing is
+ prohibited."
+
+ (aNumber < collection size) ifTrue:[writeLimit := aNumber]
+! !
+
+!WriteStream methodsFor:'reading/writing'!
+
+next
+ "catch read access to write stream"
+
+ ^ self error:'WriteStreams cannot read'
+!
+
+peek
+ "catch read access to write stream"
+
+ ^ self error:'WriteStreams cannot read'
+!
+
+nextPut:anObject
+ "append the argument, anObject to the stream"
+
+ (position > collection size) ifTrue:[self growCollection].
+ collection at:position put:anObject.
+ (position > readLimit) ifTrue:[readLimit := position].
+ position := position + 1.
+ ^anObject
+!
+
+nextPutAll:aCollection
+ "append all elements of the argument, aCollection to the stream"
+
+ |nMore final|
+
+ nMore := aCollection size.
+ final := position + nMore - 1.
+ (final > collection size) ifTrue:[
+ self growCollection:final
+ ].
+ collection replaceFrom:position
+ to:final
+ with:aCollection
+ startingAt:1.
+
+ position := position + nMore.
+ (position > readLimit) ifTrue:[readLimit := position - 1].
+ ^ aCollection
+!
+
+cr
+ "append a carriage-return to the stream"
+
+ self nextPut:(Character cr)
+!
+
+tab
+ "append a tab-character to the stream"
+
+ self nextPut:(Character tab)
+!
+
+crTab
+ "append a carriage-return followed by a tab to the stream"
+
+ self nextPut:(Character cr).
+ self nextPut:(Character tab)
+!
+
+space
+ "append a space character to the receiver-stream"
+
+ self nextPut:(Character space)
+!
+
+spaces:count
+ "append count space-characters to the receiver-stream"
+
+ 1 to:count do:[:dummy |
+ self nextPut:(Character space)
+ ]
+!
+
+ff
+ "append a form-feed (new-pagee) to the receiver-stream"
+
+ self nextPut:(Character ff)
+! !
+
+!WriteStream methodsFor:'private'!
+
+growCollection
+ |oldSize newSize newColl|
+
+ oldSize := collection size.
+ (oldSize == 0) ifTrue:[
+ newSize := 10
+ ] ifFalse:[
+ newSize := oldSize * 2
+ ].
+ collection isFixedSize ifTrue:[
+ newColl := collection species new:newSize.
+ newColl replaceFrom:1 to:oldSize with:collection startingAt:1.
+ collection := newColl
+ ] ifFalse:[
+ collection grow:newSize.
+ ].
+ writeLimit := newSize
+!
+
+growCollection:minNewSize
+ |oldSize newSize newColl|
+
+ oldSize := collection size.
+ (oldSize == 0) ifTrue:[
+ newSize := minNewSize
+ ] ifFalse:[
+ newSize := oldSize * 2.
+ [newSize < minNewSize] whileTrue:[
+ newSize := newSize * 2
+ ]
+ ].
+ collection isFixedSize ifTrue:[
+ newColl := collection species new:newSize.
+ newColl replaceFrom:1 to:oldSize with:collection startingAt:1.
+ collection := newColl
+ ] ifFalse:[
+ collection grow:newSize
+ ].
+ writeLimit := newSize
+! !