Overriding 'next' in Streams - smalltalk

Here is basically what I want to do (I have implemented the Stream sub-class WordStream):
| aStream |
aStream := WordStream on: 'Test My Word Stream Class!'.
self assert: (aStream next) = 'Test'.
self assert: (aStream next) = 'My'.
self assert: (aStream next) = 'Word'.
self assert: (aStream next) = 'Stream'.
self assert: (aStream next) = 'Class'.
self assert: aStream atEnd
I've got everything stored correctly in my stream, but I can only figure out how to get the next word by doing:
next
|tmpStream|
tmpStream := ReadStream on: myStream contents.
^tmpStream nextDelimited: Character space.
This works for only the first word (obviously) - when I try to use my instance variable myStream, it just keeps returning ''.
i.e., I can't get this to work:
next
^myStream nextDelimited: Character space.
Could someone give me a hand?
edit:
This is how I implemented on:
on: inString
|tmpStream|
myStream := ReadWriteStream on: String new.
tmpStream := ReadStream on: inString.
[ tmpStream atEnd ] whileFalse: [ myStream nextPutAll: (tmpStream nextDelimited: Character space); nextPut: $ ].

PositionableStream defines #nextDelimited: already, but that uses #next. One fairly horrible thing you could do is copy PositionableStream >> #nextDelimited:'s implementation, and replace the self next call with your own get-a-single-character method.

Related

Generate test method with their body in pharo

I advanced a little in my code but I find myself facing another problem for two days. I would like to generate a test method using only the source code. But I have no idea how to do it.
I have a method that allows me to build the name of a test method but I can't write in it.
buildSelectorFor: aMethod
^ String streamContents: [:i || capitalize |
capitalize := true.
i << 'test'.
aMethod selector do: [:charactar |
charactar= $:
ifTrue: [ capitalize := true ]
ifFalse: [ capitalize
ifTrue: [
capitalize := false.
i << charactar asUppercase. ]
ifFalse:[ i << charactar ]]]]
so if I execute this method with this for example:
buildSelectorFor:Car>>#speed:mark:
I get this:
testSpeedMark
my goal is to get something like
testSpeedMark
self assert:....equals:...
I added a method writeTestMethod.
writeTestMethod: aMethod with: anObject
^(self buildTestSelectorFor: aMethod),'
|classMethod setter instObject method|
classMethod := aMethod methodClass.
setter := (classMethod allSelectorsInProtocol: #setter) asArray.
instObject := classMethod new.
(setter with: anObject do: [:set :ivar | instObject perform: set with: ivar]).
self assert: instObject class equals: (classMethod new) class.'
So here is what I get:
I don't know how to integrate the parameters of writetestMethod in the code I want to generate

Creating a key value message in Smalltalk/Pharo that take blocks as argument

I have a scenario where a class holds two instance variables that are mutually exclusive. That is only one can be instantiated at a time. To be precise, I have a Promise class (trying to add promises to Pharo) and it holds promiseError and promiseValue instance variables. I then want to implement the method "then: catch:".
This method should work as follows:
promiseObject := [10/0] promiseValue.
promiseObject then : [ : result | Transcript crShow : result ]
catch : [ : failure | Transcript crShow : failure ] .
I got an idea on how to implement methods that take a block as an argument from method that accepts a block and the block accepts an argument.
My attempt below will obviously not work but I have no idea on how to make it work.
then:aBlock catch: anotherBlock
|segment|
promiseValue ifNil: [ segment := promiseError ] ifNotNil: [ segment := promiseValue ].
promiseValue ifNil: [ segment := promiseValue ] ifNotNil: [ segment := promiseError ].
aBlock value:segment.
anotherBlock value: segment
This should work analogously to a try-catch block.
Have you tried something like this?
then: aBlock catch: anotherBlock
promiseError notNil ifTrue: [^anotherBlock value: promiseError].
^aBlock value: promiseValue
Note that the code does not rely on promiseValue being nil or not because nil could be a valid answer of the promise. However, if there is some promiseError, we know the promise failed, and succeeded otherwise.
Of course, here I'm assuming that this message will get sent once the promise has been successfully or unsuccessfully finished. If this is not the case, then the code should be waiting on the promise semaphore.

How to concatenate a string in a do block?

I'm trying to go though a array and add characters from that array to another object. The problem is I keep getting a error "Instances of character are not indexable". However when I run tag := tag,char outside of the do block then it works.
|data startTag tag|.
data := '123456778'
startTag := false.
tag := ''.
data asArray do: [:char |
tag := tag,char]
The , is defined as
Collection>>, aCollection
^self copy addAll: aCollection; yourself
so that tries to operate on your single character as if it were a collection. That explains the error.
For larger collections you do not want to build up using , because of the copy that happens each time. Therefore use the streaming protocol:
|data tag|
data := '123456778'.
tag := String streamContents: [:s |
data do: [ :char |
s nextPut: char]]
Also take a look at Collection>>do:separatedBy: to add separators between your data.
[edit] Ah, ok, that's something like
|data tag tags state|
data := '<html>bla 12 <h1/></html>'.
state := #outside.
tags := OrderedCollection new.
tag := ''.
data do: [ :char |
state = #outside ifTrue: [
char = $< ifTrue: [
state := #inside.
tag := '' ]]
ifFalse: [
char = $> ifTrue: [
state := #outside.
tags add: tag]
ifFalse: [ tag := tag, (char asString)]]].
tags
"an OrderedCollection('html' 'h1/' '/html')"

Smalltalk Input/Output

I am having trouble regarding Smalltalk. I am attempting to populate an array with the numbers that are read from the file, but it doesn't seem to work. I've tried numerous options and I was hoping someone would explain to me what I'm doing wrong.
Object subclass: #MyStack
instanceVariableNames:'anArray aStack'
classVariableNames:''
poolDictionaries:''
!
MyStack class comment: 'Creates a Stack Class.'
!
!
MyStack methodsFor: 'initialize Stack'
!
new "instance creation"
^ super new.
!
init "initialization"
anArray := Array new: 32.
aStack := 0.
! !
!MyStack methodsFor: 'methods for stacks' !
pop "Removes the top entry from the stack"
| item |
item := anArray at: aStack.
aStack := aStack - 1.
!
push: x "Pushes a new entry onto the stack"
aStack := aStack + 1.
anArray at:aStack put:x.
!
top "Returns the current top of the stack"
^anArray at: aStack.
!
empty "True if the stack is empty"
^aStack = 0.
!
full "True if the stack is full"
^aStack = 32.
!
printOn: aStream "Prints entire stack one entry per line, starting the top entry"
aStream show: 'Stack:'.
aStack to:1 by:-1 do:[:i |(anArray at:i) printOn:aStream. ].
aStream show: ''
! !
"----------------------------------------------------------------------------------"
Object subclass: #IOExample
instanceVariableNames: 'input output'
classVariableNames: ''
poolDictionaries: ''
!
IOExample class comment: '
basic I/O.
'
!
!
IOExample methodsFor: 'initialize'
!
new
^ super new.
!
init
[ input := FileSelectionBrowser open asFilename readStream. ]
on: Error
do: [ :exception |
Dialog warn: 'Unable to open file'.
exception retry.
].
[ output := FileSelectionBrowser open asFilename writeStream. ]
on: Error
do: [ :exception |
Dialog warn: 'Unable to open file'.
exception retry.
].
! !
!
IOExample methodsFor: 'copy input to output turning :: into :'
!
copy
| data lookAhead theStack myStack|
[ input atEnd ] whileFalse: [
data := input next.
(data isKindOf: Integer)
ifTrue: [
(input atEnd) ifFalse: [
"myStack push: data."
lookAhead = input peek.
(lookAhead asCharacter isDigit)
ifTrue: [
]
].
].
output show: myStack.
].
input close.
output close.
! !
Did you try to run this code? If you did, I'm surprised you didn't get a compilation warning due to #2 below.
There are a number of problems in #copy (besides the fact that I don't understand exactly what it's trying to do)...
First you seems to expect the data to be numbers: data isKindOf: Integer. But then later you treat it as a stream of Characters: lookAhead asCharacter isDigit. If the first condition is true to get you past that point, the second one never can be, as you would've matched [0-9], which aren't ASCII values for digits.
lookAhead = input peek. Here you're comparing uninitialized lookAhead (nil) with the peeked value, and then throwing away the result. I assume you meant lookAhead := input peek.
Then there is the empty inner condition ifTrue: [ ]. What are you trying to do there?
Then there's the odd protocol name, 'copy input to output turning :: into :'. What does that mean, and what does that have to do with copying numbers between streams?
Justin, let me try to help you with the class MyStack and defer to another answer any comments on your example.
I've divided your code into fragments and appended my comments.
Fragment A:
Object subclass: #MyStack
instanceVariableNames:'anArray aStack'
classVariableNames:''
poolDictionaries:''
Comments for A:
A Smalltalker would have used instance variable names without indeterminate articles a or an
Object subclass: #MyStack
instanceVariableNames:'array stack'
classVariableNames:''
poolDictionaries:''
Fragment B:
MyStack class comment: 'Creates a Stack Class.'
Comments for B:
This is weird. I would have expected this instead (with no class):
MyStack comment: 'Creates a Stack Class.'
Fragment C:
MyStack methodsFor: 'initialize Stack'
new "instance creation"
^ super new.
Comments for C:*
This code puts new on the instance side of the class, which makes no sense because you usually send new to the class rather than its instances. The correct form requires adding class:
MyStack class methodsFor: 'initialize Stack'
new
^super new.
You forgot to send the initialization method (however, see Fragment D below)
new
^super new init.
Fragment D:
init "initialization"
anArray := Array new: 32.
aStack := 0.
Comments for D:
In Smalltalk people use the selector initialize so it can send super first
initialize
super initialize.
array := Array new: 32.
stack := 0.
Note that this change would require also writing new as
new
^super new initialize.
However, if your dialect already sends the initialize method by default, you should remove the implementation of new from your class.
Fragment E:
pop "Removes the top entry from the stack"
| item |
item := anArray at: aStack.
aStack := aStack - 1.
Comments for E:
You forgot to answer the item just popped out
pop
| item |
item := array at: stack.
stack := stack - 1.
^item
Fragment F:
push: x "Pushes a new entry onto the stack"
aStack := aStack + 1.
anArray at:aStack put:x.
Comments for F:
This is ok. Note however that the stack will refuse to push any item beyond the limit of 32.
push: x
stack := stack + 1.
array at: stack put: x.
Fragment G:
top "Returns the current top of the stack"
^anArray at: aStack.
empty "True if the stack is empty"
^aStack = 0.
full "True if the stack is full"
^aStack = 32.
Comments for G:
These are ok too. However, a more appropraite name for empty would have been isEmpty because all collections understand this polymorphic message. Similarly, the recommended selector for full would be isFull:
top
^array at: aStack.
isEmpty
^stack = 0.
isFull
^stack = 32.
Note also that isFull repeats the magic constant 32, which you used in the initialization code. That's not a good idea because if you change your mind in the future and decide to change 32 with, say, 64 you will have to modify two methods an not just one. You can eliminate this duplication in this way
isFull
^stack = array size.
Fragment H:
printOn: aStream
"Prints entire stack one entry per line, starting the top entry"
aStream show: 'Stack:'.
aStack to:1 by:-1 do:[:i |(anArray at:i) printOn:aStream. ].
aStream show: ''
Comments for H:
The last line of this code is superfluous and I would get rid of it. However, you may want to separate every item from the next with a space
printOn: aStream
stream show: 'Stack:'.
stack to: 1 by: -1 do:[:i |
aStream space.
(array at: i) printOn: aStream].

Blocks and ifTrue statement

I want to create a method that gets a block as an argument, and the block gets a parameter as well.
If the block returns true it should do something ( for example return 1), and if it returns false it should do something else.
this is what I did.. but I am getting syntax error on the ifTrue...
is this the way I should get as a parameter a block that receives an argument?
Mymethod: Block
Block value: 'argument'
ifTrue: [ ^1].
ifFalse: [^2].
and the call to the method :
object := myClass new.
argument :=1
boolValue := object Mymethod : [:argument | argument ==1 ]
the way you wrote it means that #value:ifTrue: message to the Block, and then you are sending #ifFalse: message to nothing (which is not possible at all. If you want to do it in one line, you should use parenthesis:
(Block value: 'argument')
ifTrue: [ ^1]
ifFalse: [^2]
Also in smalltalk it's a convention to name variables with uncapitalized, like block or aBlock