Stream assigment in pharo - smalltalk

I have a problem here.
I have a data variable of type an OrderedCollection.
this variable shows me this when I do a DoIt: an OrderedCollection ('3c7lwmdva2b8jbt39ls4pz3sl' '3c7lwmbf36tamw1m45riirdze' 8 February 1994).
Now I would like this:
object:=String streamContents:
[:stream|
stream
nextPutAll: 'data:= ';cr;
print:data asArray.]
But when I run, it shows me this:
data := an Array End of statement list encounteencountered -> ('3c7lwmdva2b8jbt39ls4pz3sl' '3c7lwmbf36tamw1m45riirdze' 8 February 1994).
So month I wanted to get this:
data := #('3c7lwmdva2b8jbt39ls4pz3sl' '3c7lwmbf36tamw1m45riirdze' 8 February 1994).
How to do please?

result := String streamContents: [:stream |
stream nextPutAll: 'data := #('.
data
do: [:string | stream nextPut: $'; nextPutAll: string asString; nextPut: $']
separatedBy: [stream space].
stream nextPut: $)]
Since my answer has been downvoted, I'll explain the solution.
What's in data? The question says that data prints as (original formatting, sorry about that)
an OrderedCollection ('3c7lwmdva2b8jbt39ls4pz3sl' '3c7lwmbf36tamw1m45riirdze' 8 February 1994).
which indicates that data is an OrderedCollection with two strings and a Date.
What is the OP trying to compute? It is not clear. The use of String streamContents: seems to indicate that the OP is trying to produce a String, more precisely an assignment sentence where data is assigned the OrderedCollection converted to an Array.
Solution to 2. Assuming my guess in 2 is right, my code above produces such a sentence.
What other interpretation can we give to this unclear question?
Well, may the the OP is just looking for a method that would convert the OrderedCollection into an Array. In this case, the answer would have been simply
object := data asArray.
However, given a previous post, where the same OP was trying to do some metaprogramming, the actual intention remains unclear.

Related

how to write string literal with new lines in Pharo

How do you write a string literal with new line characters in Pharo 9? I tried the following but neither of them inserted the new line:
a := 'paragraph1\n\nparagraph2'.
a := 'paragraph1\\n\\nparagraph2'.
The only way I could see to do it was through concatenation like so:
a := 'paragraph' ,
(String with: Character cr with: Character cr),
'new paragraph' ,
(String with: Character cr with: Character cr)
Is there a simpler (and shorter) way to do this?
You just do your line:
multiLineString := 'paragraph1
paragraph2
paragraph3'.
Pharo (as any other Smalltalk AFAIK) has multiline strings, you do not need any special notation as in Python or others.
EDIT: Note that while my example will be a literal, yours will not (there will be 2 literals there, and the resulting string will not be a literal.
EDIT 2: There is also String cr.
EDIT 3: It can also be constructed with streams:
myMultiLineString := String streamContents: [ :stream |
stream
nextPutAll: 'paragraph1'; cr;
nextPutAll: 'paragraph2'; cr ]
You can use the <n> placeholder in your String and send it the expandMacros message - this will expand the placeholder to the platform line separator(s):
a := 'paragraph1<n>paragraph2' expandMacros.
expandMacros and its variants also accept placeholders for tabs, cr, lf and parameters. See the comment to String>>expandMacrosWithArguments: for more details.

Pharo: how to make Cmd+d ("do it") execute the whole multi-line statement by default instead of just the current line

Suppose I have a multi-line statement like this:
1 to: 5 do: [:i|
Transcript show: i.
Transcript cr].
Currently, when I put a text cursor on some line (without selecting anything) and press Cmd+d, Pharo tries to execute the current line. But it would be more convenient for me if by default (when nothing is selected) Pharo would execute the current statement (i.e. all this three-line statement), not just the current line. Because this is a much more frequent case ("I want to execute the whole statement") than "I want to execute this particular line inside a statement" (which in most cases just doesn't make sense syntactically, as 1st and 3rd lines here). And in these rear occasions (when I need to execute a line inside a statement) I would pre-select this line manually.
How can I achieve this?
To answer your question: Take a look at the text component. It has some method for evaluate-selection-and-do. And if nothing is selected, it tries to select the current line.
You may change this implementation to find the top most statement "scope".
It could be possible if you work with the code AST instead of the text. I worked once with this, to make it smarter for code expressions inside of comments.(that didn't work for all situations because the context for getting the method AST isn't always the same for this text component,in different tools (browser/workspace/and other))
Here is the idea for an algorithm. You will need to improve and complete it.
Define a class ExpressionFinder for finding the proper expression in your text.
In my sketch this class has the following ivars
string:
the complete string in your pane (playground/transcript/whatever)
compiler:
the compiler used by your pane to evaluate text
lines: the collection of associations pos->line, where pos is the position of the line inside string
index: current index to the lines collection used by the algorithm
interval: the output interval if any, otherwise nil
Assume you are given the string, the compiler and the current position of the cursor on string. Do the following:
string: aString position: anInteger compiler: aCompiler
string := aString.
compiler := aCompiler.
self computeLines.
index := lines findLast: [:assoc | assoc key <= anInteger]
Here is how you compute the collection of lines:
computeLines
| reader |
lines := OrderedCollection new.
reader := string readStream.
[reader atEnd]
whileFalse: [lines add: reader position + 1 -> reader nextLine]
With all of this you have everything you need to find the appropriate fragment. Here is a simple idea (which you should improve):
Start at the current line index and find the fragment by adding a line at a time. If found, end. If not, decrease the index and try again from the line above.
Here is the code
find
| i |
i := index.
[
i <= 0 ifTrue: [^self].
assoc := lines at: i.
self findFrom: assoc key]
whileFalse: [i := i - 1]
where
findFrom: start
| i end success |
i := index.
[| assoc fragment |
assoc := lines at: i + 1 ifAbsent: [string size + 1 -> nil].
end := assoc key - 1.
fragment := string copyFrom: start to: end.
success := self canCompile: fragment.
success not and: [end < string size]]
whileTrue: [i := i + 1].
success ifTrue: [interval := start to: end].
^success
The code for canCompile: fragment is dialect-dependent, on the lines of
canCompile: fragment
^(compiler compileExpression: fragment) notNil
If your compiler signals CompilationErrors, you will need to put a handler in canCompile: to avoid them. Also you might take advantage of such errors. For instance, if the compilation error refers to an undeclared variable, you know that you will not find its definition in the lines below, so you should exit the loop in findFrom: so to try with the line above and so on.

How to read/write objects to a file?

I would like to write an object (a simple collection) to a file. I've been looking around and found this question and this question. I also went through a lot of sites with broken links etc, but I don't seem to be able to find a way to write to a file in smalltalk. I tried this (and other things, but they come down to the same):
out := 'newFile' asFileName writeStream.
d associationsDo: [ :assoc | out
nextPutAll: assoc key asString;
nextPut: $, ;
nextPutAll: assoc value asString; cr. ]
out close.
as suggested in the linked questions, but it does not seem to be doing anything. It does not throw errors, but I don't find any files either.
The only thing I want to do is persist my object (binary or textual does not really matter), so how could I do this?
Thanks in advance
What you are doing is creating a write stream on a string. That actually works but the information is stored on a string object, no files will be written.
This works in both Squeak and Pharo (and probably other dialects):
FileStream
forceNewFileNamed: 'filename.ext'
do: [ :stream |
d associationsDo: [ :assoc |
stream
ascii; "data is text, not binary"
nextPutAll: assoc key asString;
nextPut: $, ;
nextPutAll: assoc value asString;
cr ] ].
In Pharo you could write:
'filename.ext' asFileReference writeStreamDo: [ :stream |
... ].
Note however that there are better ways to store structured data in files, e.g. STON (Smalltalk Object Notation, Smalltalk version of JSON) or XML. If you want to persist objects than you might want to checkout Fuel, StOMP (probably no longer supported) or any of the other object serializers.
Lastly there's also ImageSegment, a VM based object serializer (no extra packages needed), but you'll probably need some help with that.
The traditional Smalltalk serialization format uses the storeOn: and readFrom: methods. E.g.
d1 := {'a'->1. 'b'->2. 'c'->'3'} as: Dictionary.
"store"
FileStream forceNewFileNamed: 'mydict.st' do: [:out | d1 storeOn: out].
"read"
d2 := FileStream oldFileNamed: 'mydict.st' do: [:in | Object readFrom: in].
This is a textual format and gets inefficient for larger data sets. Also, it cannot store cyclical references. For that, check out the more advanced serialization options as listed in the other answers.
Solution:
| d out |
d := Dictionary new.
d at: 'green' put: 'vert'.
d at: 'blue' put: 'bleu'.
d at: 'red' put: 'rouge'.
d at: 'white' put: 'blanc'.
out := FileStream fileNamed: 'dict-out.txt'.
d associationsDo: [ :assoc | out
nextPutAll: assoc key asString;
nextPut: $, ;
nextPutAll: assoc value asString; cr.].
out close.
See also:
http://wiki.squeak.org/squeak/1583
http://wiki.squeak.org/squeak/6338
It seems that you are using a syntax for an extension, but not the base.
At least in Pharo, 'newFile' asFileName is a string, and #writeStream provides you a stream on the same string, not on a file.
Try with FileStream newFileNamed: 'newFile' or something like that.
And most of all: when something strange happens, inspect. Inspect partial evaluations and check all your assumptions. Or better yet, debug and see where is the code going.
The equivalent in Fuel would be
FLSerializer serialize: d toFileNamed: 'filename.ext'.
And
d := FLMaterializer materializeFromFileNamed: 'filename.ext'

How to print multiple outputs in Smalltalk

|X Y A B C D|
Y:= 7.
X:= 6.
(X = Y)
ifTrue: [X := 0]
ifFalse:[X := 1].
B:=2.
C:=5.
D:=1.
A:= (B squared)*(C-D).
"print both A and X to screen here"
Simple enough little smalltalk example. I'm just curious how I can get this to print X and A as outputs? is there any way to do it w/o having to perform a 'print it' on the top 6 lines and a seperate 'print it' on the bottom 5 lines? if it could print out on just a 'do it' or a single 'print it' please let me know!
You should define what is "printing" and what is X and A.
If "printing" is a result of the "print it" action, then you are talking in general about returning X and A, as "print it" prints the return result of the selected code. This way you have to think about an object which will represent X and A. For this object you can define a printString method or printOn: and get the result printed. Or you can cheat a bit and return a point by doing X#A.
If you are talking about actually printing the thing somewhere then you have to tell more about where do you want to do it. You can print it in Transcript or similar, but there you have to explicitly send a message to the Transcript with what you want to be printed.
Now if you want to use this for "debugging/testing" reasons, it can be easier to go with "inspect it". In your code you can send inspect messages to the objects that you want to look at, and during the execution inspectors will open showing this objects.
Also I encourage you to follow conventions and make your variable names start with lowercase letter.
Smalltalk has no equivalent of print() or println() or the like, since most Smalltalk environments live in a window environment. There are ways to write output to stdout or std error, but this is very dialect specific.
One of the places that somehow replaces stdout in most dialects is a place/stream/window called Transcript, in most dialects this is the window that launches first when your start the IDE.
To write something there you simple do:
Transcript show: 'A=', A asString, ' ; X=', X asString.
(please note that in Smalltalk, Strings and Collections are concatenated with a comma)
You can also write a newLine by sending the message cr to the Transcript like so:
Transcript cr.
Does this answer your question?
A hint for further learning/investigation: Transcript is just a Variable that holds a Stream object. show: is a message that writes some String onto that Stream. asString is a method that returns a String representation of an object.

How to identify binary and text files using Smalltalk

I want to verify that a given file in a path is of type text file, i.e. not binary, i.e. readable by a human. I guess reading first characters and check each character with :
isAlphaNumeric
isSpecial
isSeparator
isOctetCharacter ???
but joining all those testing methods with and: [ ... and: [ ... and: [ ] ] ] seems not to be very smalltalkish. Any suggestion for a more elegant way?
(There is a Python version here How to identify binary and text files using Python? which could be useful but syntax and implementation looks like C.)
only heuristics; you can never be really certain...
For ascii, the following may do:
|isPlausibleAscii numChecked|
isPlausibleAscii :=
[:char |
((char codePoint between:32 and:127)
or:[ char isSeparator ])
].
numChecked := text size min: 1024.
isPossiblyText := text from:1 to:numChecked conform: isPlausibleAscii.
For unicode (UTF8 ?) things become more difficult; you could then try to convert. If there is a conversion error, assume binary.
PS: if you don't have from:to:conform:, replace by (copyFrom:to:) conform:
PPS: if you don't have conform: , try allSatisfy:
All text contains more space than you'd expect to see in a binary file, and some encodings (UTF16/32) will contain lots of 0's for common languages.
A smalltalky solution would be to hide the gory details in method on Standard/MultiByte-FileStream, #isProbablyText would probably be a good choice.
It would essentially do the following:
- store current state if you intend to use it later, reset to start (Set Latin1 converter if you use a MultiByteStream)
Iterate over N next characters (where N is an appropriate number)
Encounter a non-printable ascii char? It's probably binary, so return false. (not a special selector, use a map, implement a new method on Character or something)
Increase 2 counters if appropriate, one for space characters, and another for zero characters.
If loop finishes, return whether either of the counters have been read a statistically significant amount
TLDR; Use a method to hide the gory details, otherwise it's pretty much the same.