Smalltalk inspect is a powerful tool. Is there any (easy) way to get the information from inspect and show it in Transcript window or write into a file instead of showing it in new window?
I need it because I want to create a kind of debbuger for a program that runs as unix process (not a 'window' program) and logs information into a log file.
Thanks for help!
If you're asking whether something is built in, then I don't think so (although it would help if you tagged the question with which Smalltalk you are using).
Although it would be pretty easy to walk over the inst vars and roll your own (although maybe not for immediate objects), the "easiest" way might be to look at the inspector code and see how it operates. For example, in Pharo 4.0 one could (very basically) leverage the inspector code like so:
i := EyeInspector inspector: 1.
Transcript show: i objectClass; cr.
i elements do: [ :e | Transcript show: e; cr ].
which would print:
SmallInteger
'self'->1
'hex'->1
'octal'->1
'binary'->1
'character'->Character home
In Pharo, you can also get all of the Transcript output going to the console with:
NonInteractiveTranscript stdout install
If you are about debugging, you can have debugger interactions dump things into files (of course, you'll not be able to step in there but it can be useful for headless systems):
NonInteractiveUIManager compile: 'openDebuggerOn: process context: context label: title contents: contentsStringOrNil fullView: bool
| out |
out := VTermOutputDriver stdout.
out
<< ''NonInteractive Debugger: '';
<< title;
cr.
contentsStringOrNil ifNotNil: [ out << contentsStringOrNil; cr ].
(context stackOfSize: 20) do: [:s | out << s printString; cr ].
out << ''------------------------------''; cr; cr.
^ self nonInteractiveWarning: ''Opening Debugger''' classified: #'ui-requests'.
This and Sean's answer should go a long way.
You can get back to normal with the Transcript with:
ThreadSafeTranscript install.
Pharo 3.0 here.
Related
This Python code shows how to call some process in Windows 10 and to send to it string commands, to read its string responses through stdin, stdout pipes of the process:
Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from subprocess import *
>>> p = Popen("c:/python38/python.exe", stdin=PIPE, stdout=PIPE)
>>> p.stdin.write(b"print(1+9)\n")
11
>>> p.communicate()
(b'10\r\n', None)
>>>
As you can see the python.exe process returned 10 as an answer to print(1+9). Now I want to do the same in Pharo (or Squeak): in Windows 10 OS - I suppose something similar, i.e. short, simple, understandable, really working.
I installed OSProcess, ProcessWrapper (they were missing in Pharo, also its strange that I got warning that they are not marked for Pharo 8.0 and were not checked to work in Pharo 8.0, but OK), and I tried ProcessWrapper, PipeableOSProcess (copy-pasted different snippets from the Web), etc - with zero success! The results were:
nothing happens, python.exe was not started
VM errors console was opened (white console in the bottom of the Pharo, which is controlled with F2 menu)
different exceptions
etc
Would somebody show me simple working example how to start a process and to to send it commands, read answers, then send again, and so on in some loop - I plan to have such communication in a detached thread and to use it as some service, because Pharo, Smalltalk in general is missing most bindings, so then I will use subprocess communication like in "good" old days...
I know how to call a command and to get its output:
out := LibC resultOfCommand: 'dir ', aDir.
but I am talking about another scenario: a communication with a running process interactively (for example, with SSH or similar like in the example above - python.exe).
PS. Maybe it's possible to do it with LibC #pipe:mode even?
Let me start with that the PipeableOsProcess is probably broken on Windows. I have tried it and it just opened a command line and nothing else (it does not freeze my Pharo 8). The whole OSProcess does not work correctly in my eyes.
So I took a shot at LibC which is supposed to not work with Windows.
I’m a module defining access to standard LibC. I’m available under Linux and OSX, but not under Windows for obvious reasons :)
Next is to say that Python's Windows support is probably much better than Pharo's.
The solution, which is more like a workaround using files, is to use LibC and #runCommand: (I tried to come up with a similar example as you had shown above):
| count command result outputFile errorFile |
count := 9+1. "The counting"
command := 'echo ', count asString. "command run at the command line"
outputFile := 'output'. "a file into which the output is redirected"
errorFile := 'error'. "a file where the error output is redirected "
result := LibC runCommand: command, "run the command "
' >', outputFile, "redirect the output to output file"
' 2>', errorFile.
"reading back the value from output file"
outputFile asFileReference contents lines.
"reading back the value from the error file - which is empty in this case"
errorFile asFileReference contents lines.
I am trying to use smalltalk with smalltalk/x-jv branch. I have following simple code:
Object subclass: Myclass[
|mainval|
init [mainval := 555]
getmainval [^mainval]
]
gc := Myclass new.
gc init.
gc getmainval printNl.
I am trying to run it on command line with stc command of smalltalk/x-jv, but it it not working. Following is the error:
$ ./stc testsrc.st
testsrc.st, line 1: Error: syntax error in 'class definition' near "Myclass" (char/token=286 / 0x11e) (fileIn expression)
Where is the problem and how can it be solved? Thanks for your help.
Edit - Adding information about stc and stx
I'm afraid you can't use the GNU Smalltalk code directly within Smalltalk/X(-jv branch). Also it would be nice to see what is your final goal during the Smalltalk question series.
What is important for you to understand that Smalltalk has been designed to work within the IDE if you want to build an application you should use the IDE provided. If you want to build a sample application there is even guide for that for Smalltalk/X. That, of course, do not mean you are unable to start a script from command line (Smalltalk/X is powerfull at shell).
That being said there is a Smalltalk/X highlighting package file for Sublime Text 3 done by myself hosted at BitBucket. I have created it mainly for Smalltalk and its embedded C highlighting.
First you are probably using stx executable and not stc. stc is a shorcut for smalltalk-to-C compiler. stc produces a C code which can then be compiled by a C compiler into an object file which then can be linked with a final executable (together with other smalltalk classes and runtime).
smalltalk or stx is a launcher that can execute smalltalk scripts or open a full-blown IDE. If you're familiar with Java, think of stc as of javac and smalltalk or stx as of java.
You can use the launcher provided called smalltalk (a bash script for *nix and batch/powershell for windows), which is using the stx.com at the end, but providing some additional functionality.
Use smalltalk --help the see the commandline options.
First I will start with a simple one-liner which you can use:
stx.com -I --quick --eval "Transcript showCR: 'A message on stdout on Transcript'
A message on stdout on Transcript
on windows you if you use smalltalk you get more information:
smalltalk -I --quick --eval "Transcript showCR: 'A message on stdout on Transcript'
"[INFO] PowerShell detected: ->TRUE<-.
"[INFO] The latest latest_powershell_version found: 5.1.16299.1004."
"[INFO] With the runtime being: v4.0.30319."
VERBOSE: [INFO] Manual switch detected - configuration is ignored
VERBOSE: [INFO] Executing asynchronously command: C:\prg_sdk\stx8-jv_swing\build\stx\projects\smalltalk\stx.com -I
--quick --eval "Transcript showCR: 'A message on stdout on Transcript'" | Out-null
VERBOSE: A message on stdout on Transcript
VERBOSE:
VERBOSE: [INFO] Exiting from PowerShell with code 0
VERBOSE: [INFO] End. Exiting correctly.
Now lets move to your scripting question
At the beginning the best way is to create the class in IDE and do a fileOut of it. You will then see the correct structure the .st file should have.
I have create a simple file script.st for you (this is simlilar what you would get on a fileOut from IDE):
"{ NameSpace: Smalltalk }"
Object subclass:#MyClass
instanceVariableNames:'mainValue'
classVariableNames:''
poolDictionaries:''
category:''
!
!MyClass methodsFor:'accessing'!
mainValue
^ mainValue
!
mainValue: newValue
mainValue := newValue
! !
!MyClass methodsFor:'initialization & release'!
initialize
super initialize.
mainValue := 555.
! !
gc := MyClass new.
gc initialize.
Transcript showCR: gc mainValue.
How do you run such a sript?
smalltalk --execute script.st
The output will be: 555
If you want to script without "objects" (well everything is object in Smalltalk, but you don't define a class here) you can do simple transcript.st:
| mainValue |
mainValue := 555.
Transcript showCR: mainValue.
again execute it as: smalltalk --execute transcript.st to get identical result.
In bash shell, if you hit up or down arrows, the shell will show you your previous or next command that you entered, and you can edit those commands to be new shell commands.
In perl6, if you do
my $name = prompt("Enter name: ");
it will print "Enter name: " and then ask for input; is there a way to have perl6 give you a default value and then you just edit the default to be the new value. E.g.:
my $name = prompt("Your name:", "John Doe");
and it prints
Your name: John Doe
where the John Doe part is editable, and when you hit enter, the edited string is the value of $name.
https://docs.raku.org/routine/prompt does not show how to do it.
This is useful if you have to enter many long strings each of which is just a few chars different from others.
Thanks.
To get the editing part going, you could use the Linenoise module:
zef install Linenoise
(https://github.com/hoelzro/p6-linenoise)
Then, in your code, do:
use Linenoise;
sub prompt($p) {
my $l = linenoise $p;
linenoiseHistoryAdd($l);
$l
}
Then you can do your loop with prompt. Remember, basically all Perl 6 builtin functions can be overridden lexically. Now, how to fill in the original string, that I haven't figure out just yet. Perhaps the libreadline docs can help you with that.
Well by default, programs are completely unaware of their terminals.
You need your program to communicate with the terminal to do things like pre-fill an input line, and it's unreasonable to expect Perl 6 to handle something like this as part of the core language.
That said, your exact case is handled by the Readline library as long as you have a compatible terminal.
It doesn't look like the perl 6 Readline has pre-input hooks setup so you need to handle the callback and read loop yourself, unfortunately. Here's my rough attempt that does exactly what you want:
use v6;
use Readline;
sub prompt-prefill($question, $suggestion) {
my $rl = Readline.new;
my $answer;
my sub line-handler( Str $line ) {
rl_callback_handler_remove();
$answer = $line;
}
rl_callback_handler_install( "$question ", &line-handler );
$rl.insert-text($suggestion);
$rl.redisplay;
while (!$answer) {
$rl.callback-read-char();
}
return $answer;
}
my $name = prompt-prefill("What's your name?", "Bob");
say "Hi $name. Go away.";
If you are still set on using Linenoise, you might find the 'hints' feature good enough for your needs (it's used extensively by the redis-cli application if you want a demo). See the hint callback used with linenoiseSetHintsCallback in the linenoise example.c file. If that's not good enough you'll have to start digging into the guts of linenoise.
Another solution :
Use io-prompt
With that you can set a default value and even a default type:
my $a = ask( "Life, the universe and everything?", 42, type => Num );
Life, the universe and everything? [42]
Int $a = 42
You can install it with:
zef install IO::Prompt
However, if just a default value is not enough. Then it is better you use the approach Liz has suggested.
I am working with Pharo 3 and I use the Transcript to record operations.
However the size of the current buffer is short for my needs. How to increase it? There is characterLimit but this is a method constant and therefore not easy to set up without changing a core package.
I do not want to use NonInteractiveTranscript because I want to stay in the image.
No, there is no other way to change the buffer length of the Transcript then to modify #characterLimit (usually of ThreadSafeTranscript). However, try inspecting ThreadSafeTranscript allInstances and you'll see that the underlying stream is much longer (50000 something is the write limit there). So, whatever you're printing to Transcript is not actually lost but just not visible.
That being said, using Transcript for extensive output is generally not a good idea because:
output is cut off (as you've already seen)
Transcript is really slow when called repeatedly:
try
1 to: 10000 do: [ :i | Transcript show: i ]
vs.
Transcript show: (String streamContents: [ :stream |
1 to: 10000 do: [ :i | stream nextPutAll: i asString ] ])
you can't use the output somewhere else (e.g. to write to file or pass along to a method)
In my opinion Transcript is ok for occasional quick debugging but shouldn't be used for anything application related.
Can I launch Squeak as a REPL (no GUI), where I can enter and evaluate Smalltalk expressions? I know the default image don't allow this. Is there any documentation on how to build a minimum image that can be accessed from a command-line shell?
Here is a (hackish) solution:
First, you need OSProcess, so run this in a Workspace:
Gofer new squeaksource:'OSProcess'; package:'OSProcess';load.
Next, put this in the file repl.st:
OSProcess thisOSProcess stdOut
nextPutAll: 'Welcome to the simple Smalltalk REPL';
nextPut: Character lf; nextPut: $>; flush.
[ |input|
[ input := OSProcess readFromStdIn.
input size > 0 ifTrue: [
OSProcess thisOSProcess stdOut
nextPutAll: ((Compiler evaluate: input) asString;
nextPut: Character lf; nextPut: $>; flush
]
] repeat.
]forkAt: (Processor userBackgroundPriority)
And last, run this command:
squeak -headless path/to/squeak.image /absolute/path/to/repl.st
You can now have fun with a Smalltalk REPL. Dont forget to type in the command:
Smalltalk snapshot:true andQuit:true
if you want to save your changes.
Now, onto the explanation of this solution:
OSProcess is a package that allows to run other processes, read from stdin, and write to stdout and stderr. You can access the stdout AttachableFileStream with OSProcess thisOSProcess (the current process, aka squeak).
Next, you run an infinite loop at userBackgroundPriority (to let other processes run). In this infinite loop, you use Compiler evaluate: to execute the input.
And you run this in a script with a headless image.
As of Pharo 2.0 (and 1.3/1.4 with the fix described below), there are no more hacks necessary. The following snippet will turn your vanilla Pharo image into a REPL server...
From https://gist.github.com/2604215:
"Works out of the box in Pharo 2.0. For prior versions (definitely works in 1.3 and 1.4), first file in https://gist.github.com/2602113"
| command |
[
command := FileStream stdin nextLine.
command ~= 'exit' ] whileTrue: [ | result |
result := Compiler evaluate: command.
FileStream stdout nextPutAll: result asString; lf ].
Smalltalk snapshot: false andQuit: true.
If you want the image to always be a REPL, put the code in a #startup: method; otherwise, pass the script at the command line when you want REPL mode, like:
"/path/to/vm" -headless "/path/to/Pharo-2.0.image" "/path/to/gistfile1.st"
Please visit:
http://map.squeak.org/package/2c3b916b-75e2-455b-b25d-eba1bbc94b84
and Run Smalltalk on server without GUI?
The project http://www.squeaksource.com/SecureSqueak.html includes a REPL package that may provide much of what you are looking for.