17 |
17 |
18 Object subclass:#ReadEvalPrintLoop |
18 Object subclass:#ReadEvalPrintLoop |
19 instanceVariableNames:'inputStream outputStream errorStream compiler prompt |
19 instanceVariableNames:'inputStream outputStream errorStream compiler prompt |
20 doChunkFormat traceFlag timingFlag profilingFlag printFlag |
20 doChunkFormat traceFlag timingFlag profilingFlag printFlag |
21 exitAction currentDirectory lastEditedClass lastEditedSelector |
21 exitAction currentDirectory lastEditedClass lastEditedSelector |
22 editorCommand' |
22 editorCommand confirmDebugger debuggerUsed' |
23 classVariableNames:'' |
23 classVariableNames:'' |
24 poolDictionaries:'' |
24 poolDictionaries:'' |
25 category:'System-Support' |
25 category:'System-Support' |
26 ! |
26 ! |
27 |
27 |
42 ! |
42 ! |
43 |
43 |
44 documentation |
44 documentation |
45 " |
45 " |
46 A simple read-eval-print loop for non-GUI or stscript operation. |
46 A simple read-eval-print loop for non-GUI or stscript operation. |
47 Invoked, for example if stx is started with a --repl argument. |
47 Invoked, for example if stx is started with a --repl argument, |
|
48 or by the MiniDebugger with the 'I' command. |
48 |
49 |
49 A line starting with '?' shows the usage message. |
50 A line starting with '?' shows the usage message. |
50 Lines starting with '#' are directives: |
51 Lines starting with '#' are directives: |
51 #exit - exit the rep-loop |
52 #exit - exit the rep-loop |
52 #show ... - show various infos |
53 #show ... - show various infos |
75 |
76 |
76 compiler:something |
77 compiler:something |
77 "assign a compiler to use;could be used to change the language" |
78 "assign a compiler to use;could be used to change the language" |
78 |
79 |
79 compiler := something. |
80 compiler := something. |
|
81 ! |
|
82 |
|
83 confirmDebugger |
|
84 "true if the user is asked for a debugger in case of errors" |
|
85 |
|
86 ^ confirmDebugger ? true |
|
87 ! |
|
88 |
|
89 confirmDebugger:aBoolean |
|
90 "true if the user is asked for a debugger in case of errors" |
|
91 |
|
92 confirmDebugger := aBoolean |
|
93 ! |
|
94 |
|
95 debuggerUsed |
|
96 "by default, the miniDebugger is given control in case of an error; |
|
97 you may want to write (subclass) your own ;-)" |
|
98 |
|
99 ^ debuggerUsed ? MiniDebugger |
|
100 ! |
|
101 |
|
102 debuggerUsed:aDebuggerClass |
|
103 "by default, the miniDebugger is given control in case of an error; |
|
104 you may want to write (subclass) your own ;-)" |
|
105 |
|
106 debuggerUsed := aDebuggerClass |
80 ! |
107 ! |
81 |
108 |
82 doChunkFormat |
109 doChunkFormat |
83 "true if currently reading chunk format" |
110 "true if currently reading chunk format" |
84 |
111 |
219 ^ self |
246 ^ self |
220 ! ! |
247 ! ! |
221 |
248 |
222 !ReadEvalPrintLoop methodsFor:'directives'! |
249 !ReadEvalPrintLoop methodsFor:'directives'! |
223 |
250 |
|
251 askYesNo:message |
|
252 self errorStream show:message. |
|
253 ^ (self inputStream nextLine withoutSeparators startsWith:'y'). |
|
254 ! |
|
255 |
|
256 cmd_apropos:lineStream |
|
257 "apropos directive; i.e. |
|
258 #apropos collection [;more] |
|
259 " |
|
260 |
|
261 |words classNamesMatching selectorsMatching showList| |
|
262 |
|
263 lineStream skipSeparators. |
|
264 words := lineStream upToEnd asCollectionOfSubstringsSeparatedBy:$;. |
|
265 words := words select:[:each | each notEmpty]. |
|
266 words := words select:[:each | each isBlank not]. |
|
267 |
|
268 (words isEmpty) ifTrue:[ |
|
269 self errorStream showCR:'? usage: #apropos <word> [; morewords]'. |
|
270 ^ self. |
|
271 ]. |
|
272 |
|
273 "/ search in classes: |
|
274 classNamesMatching := Smalltalk allClasses |
|
275 select:[:cls | |
|
276 cls isPrivate not |
|
277 and:[ words conform:[:word | |
|
278 cls name matches:word caseSensitive:false]]] |
|
279 thenCollect:#name. |
|
280 "/ search in method names: |
|
281 selectorsMatching := (Smalltalk allClasses |
|
282 collectAll:[:cls | |
|
283 cls isPrivate |
|
284 ifTrue:[#()] |
|
285 ifFalse:[ |
|
286 cls selectors |
|
287 select:[:sel | |
|
288 words conform:[:word | |
|
289 sel matches:word caseSensitive:false]]]] |
|
290 ) asSet. |
|
291 |
|
292 showList := |
|
293 [:list :listName | |
|
294 |showIt sortedList longest limit numCols| |
|
295 |
|
296 showIt := true. |
|
297 list notEmpty ifTrue:[ |
|
298 list size > 20 ifTrue:[ |
|
299 showIt := self askYesNo:( |
|
300 'apropos: there are %1 matching %2; list them all (y/n)? ' |
|
301 bindWith:list size |
|
302 with:listName) |
|
303 ] ifFalse:[ |
|
304 self errorStream showCR:'matching %1:' with:listName. |
|
305 ]. |
|
306 showIt ifTrue:[ |
|
307 sortedList := list asOrderedCollection sort. |
|
308 longest := (list collect:[:nm | nm size]) max. |
|
309 limit := 78. |
|
310 numCols := (80 // (longest min:limit)) max:1. |
|
311 sortedList slicesOf:numCols do:[:eachGroupOfN | |
|
312 self errorStream |
|
313 spaces:2; |
|
314 nextPutLine:( |
|
315 (eachGroupOfN |
|
316 collect:[:nm | |
|
317 (nm contractTo:limit) paddedTo:limit |
|
318 ] |
|
319 ) asStringWith:' '). |
|
320 ]. |
|
321 ]. |
|
322 ]. |
|
323 ]. |
|
324 |
|
325 showList value:classNamesMatching value:'classes'. |
|
326 showList value:selectorsMatching value:'method names'. |
|
327 " |
|
328 self basicNew |
|
329 input:Stdin; |
|
330 cmd_apropos:'Array' readStream |
|
331 |
|
332 self basicNew |
|
333 input:Stdin; |
|
334 cmd_apropos:'at:' readStream |
|
335 |
|
336 self basicNew |
|
337 input:Stdin; |
|
338 cmd_apropos:'*at:' readStream |
|
339 " |
|
340 ! |
|
341 |
224 cmd_clear:lineStream |
342 cmd_clear:lineStream |
225 self cmd_setOrClear:lineStream to:false |
343 self cmd_setOrClear:lineStream to:false |
226 |
344 |
227 "Created: / 07-12-2006 / 19:04:50 / cg" |
345 "Created: / 07-12-2006 / 19:04:50 / cg" |
228 ! |
346 ! |
244 lineStream skipSeparators. |
362 lineStream skipSeparators. |
245 lineStream atEnd ifTrue:[ |
363 lineStream atEnd ifTrue:[ |
246 cls := lastEditedClass. |
364 cls := lastEditedClass. |
247 methodName := lastEditedSelector. |
365 methodName := lastEditedSelector. |
248 ] ifFalse:[ |
366 ] ifFalse:[ |
249 |
|
250 classOrMethodName := lineStream |
367 classOrMethodName := lineStream |
251 upToElementForWhich:[:ch | |
368 upToElementForWhich:[:ch | |
252 ch isLetterOrDigit not and:[ch ~~ $_] |
369 ch isLetterOrDigit not and:[ch ~~ $_] |
253 ]. |
370 ]. |
254 "/ |
371 "/ |
271 ' bindWith:classOrMethodName. |
388 ' bindWith:classOrMethodName. |
272 ] ifFalse:[ |
389 ] ifFalse:[ |
273 lineStream skipSeparators. |
390 lineStream skipSeparators. |
274 lineStream atEnd ifFalse:[ |
391 lineStream atEnd ifFalse:[ |
275 methodName := lineStream upToSeparator. |
392 methodName := lineStream upToSeparator. |
|
393 methodName = 'class' ifTrue:[ |
|
394 cls := cls theMetaclass. |
|
395 lineStream skipSeparators. |
|
396 methodName := lineStream upToSeparator. |
|
397 ]. |
276 ]. |
398 ]. |
277 ]. |
399 ]. |
278 ] ifFalse:[ |
400 ] ifFalse:[ |
279 methodName := classOrMethodName |
401 methodName := classOrMethodName |
280 ]. |
402 ]. |
297 editFullClass := true. |
419 editFullClass := true. |
298 code := cls source asString |
420 code := cls source asString |
299 ] ifFalse:[ |
421 ] ifFalse:[ |
300 ((selector := methodName asSymbolIfInterned) isNil |
422 ((selector := methodName asSymbolIfInterned) isNil |
301 or:[ (cls implements:selector) not]) ifTrue:[ |
423 or:[ (cls implements:selector) not]) ifTrue:[ |
302 errStream show:('"',methodName,'" is a new method; create (y/n)? '). |
424 (self askYesNo:('"',methodName,'" is a new method; create (y/n)? ')) ifFalse:[^ self]. |
303 (self inputStream nextLine withoutSeparators startsWith:'y') ifFalse:[^ self]. |
425 |
304 code := |
426 code := |
305 '"/ change the code as required, then save and exit the editor. |
427 '"/ change the code as required, then save and exit the editor. |
306 "/ To cancel this edit, leave the editor WITHOUT saving. |
428 "/ To cancel this edit, leave the editor WITHOUT saving. |
307 |
429 |
308 %1 |
430 %1 |
413 #debug ................. enter a MiniDebugger |
539 #debug ................. enter a MiniDebugger |
414 #edit <what> ........ open an external editor |
540 #edit <what> ........ open an external editor |
415 class .............. on a class |
541 class .............. on a class |
416 class selector ..... on a method |
542 class selector ..... on a method |
417 <empty> ............ on previously edited method/last class |
543 <empty> ............ on previously edited method/last class |
|
544 #apropos word ....... list classes/selectors matching word |
|
545 #list <what> ........ show source |
|
546 class .............. class definition and comment |
|
547 class selector ..... method source |
418 |
548 |
419 The MiniDebugger (if entered) shows its own help with "?". |
549 The MiniDebugger (if entered) shows its own help with "?". |
420 ' |
550 ' |
421 |
551 |
422 "Created: / 07-12-2006 / 18:54:20 / cg" |
552 "Created: / 07-12-2006 / 18:54:20 / cg" |
423 "Modified: / 08-11-2016 / 22:53:53 / cg" |
553 "Modified: / 08-11-2016 / 22:53:53 / cg" |
|
554 ! |
|
555 |
|
556 cmd_list:lineStream |
|
557 "list directive; i.e. |
|
558 #list <classname> ['class'] <selector> |
|
559 " |
|
560 |
|
561 |class selector source errStream| |
|
562 |
|
563 errStream := self errorStream. |
|
564 |
|
565 (self |
|
566 getClassNameAndSelectorFrom:lineStream |
|
567 into:[:classArg :selectorArg | |
|
568 class := classArg. |
|
569 selector := selectorArg. |
|
570 ]) ifFalse:[^ self]. |
|
571 |
|
572 selector isNil ifTrue:[ |
|
573 errStream nextPutAll:(class definition). |
|
574 errStream nextPutAll:(class commentOrDocumentationString). |
|
575 ] ifFalse:[ |
|
576 source := class sourceCodeAt:selector asSymbol. |
|
577 source isEmptyOrNil ifTrue:[ |
|
578 errStream nextPutLine:'Sorry, no sourcecode found' |
|
579 ] ifFalse:[ |
|
580 errStream nextPutAll:source |
|
581 ]. |
|
582 ]. |
|
583 |
|
584 " |
|
585 self basicNew |
|
586 input:Stdin; |
|
587 cmd_list:'Array' readStream |
|
588 |
|
589 self basicNew |
|
590 input:Stdin; |
|
591 cmd_list:'Array at:put:' readStream |
|
592 " |
424 ! |
593 ! |
425 |
594 |
426 cmd_read:lineStream |
595 cmd_read:lineStream |
427 "read directive; i.e. |
596 "read directive; i.e. |
428 #read scriptFile |
597 #read scriptFile |
515 ] ifFalse:[ |
686 ] ifFalse:[ |
516 editorCommand := nil. |
687 editorCommand := nil. |
517 ]. |
688 ]. |
518 ^ self. |
689 ^ self. |
519 ]. |
690 ]. |
520 ]. |
691 (what startsWith:'con') ifTrue:[ |
521 self errorStream showCR:'? usage: set/clear <flag>'. |
692 confirmDebugger := aBoolean. |
522 self errorStream showCR:'? (<flag> must be one of: trace, times, profile, chunk, editor)'. |
693 ^ self. |
|
694 ]. |
|
695 ]. |
|
696 self errorStream |
|
697 showCR:'? usage: set/clear <flag>'; |
|
698 showCR:'? (<flag> must be one of: trace, times, profile, chunk, editor, confirmDebug)'. |
523 |
699 |
524 "Modified: / 08-11-2016 / 22:49:17 / cg" |
700 "Modified: / 08-11-2016 / 22:49:17 / cg" |
525 ! |
701 ! |
526 |
702 |
527 cmd_show:lineStream |
703 cmd_show:lineStream |
588 errStream |
764 errStream |
589 showCR:('trace : ',(traceFlag ? false) printString); |
765 showCR:('trace : ',(traceFlag ? false) printString); |
590 showCR:('timing: ',(timingFlag ? false) printString); |
766 showCR:('timing: ',(timingFlag ? false) printString); |
591 showCR:('profiling: ',(profilingFlag ? false) printString); |
767 showCR:('profiling: ',(profilingFlag ? false) printString); |
592 showCR:('chunkFormat: ',(doChunkFormat ? false) printString); |
768 showCR:('chunkFormat: ',(doChunkFormat ? false) printString); |
593 showCR:('editor: ',self editorCommand printString). |
769 showCR:('editor: ',self editorCommand printString); |
|
770 showCR:('confirmDebug:',self confirmDebugger printString). |
594 ok := true. |
771 ok := true. |
595 ]. |
772 ]. |
596 ]. |
773 ]. |
597 |
774 |
598 ok ifFalse:[ |
775 ok ifFalse:[ |
666 ] do:[ |
843 ] do:[ |
667 self |
844 self |
668 perform:('cmd_',cmd) asMutator with:s |
845 perform:('cmd_',cmd) asMutator with:s |
669 ifNotUnderstood:[ |
846 ifNotUnderstood:[ |
670 self errorStream |
847 self errorStream |
671 show:'?? invalid command: '; show:cmd; |
848 show:'?? invalid command: %1. Type "#help" for help.' with:cmd. |
672 showCR:'. Type "#help" for help.' |
|
673 ]. |
849 ]. |
674 ]. |
850 ]. |
675 ]. |
851 ]. |
676 ]. |
852 ]. |
677 ]. |
853 ]. |
678 |
854 |
679 "Created: / 07-12-2006 / 18:49:17 / cg" |
855 "Created: / 07-12-2006 / 18:49:17 / cg" |
680 "Modified: / 08-11-2016 / 21:59:16 / cg" |
856 "Modified: / 08-11-2016 / 21:59:16 / cg" |
|
857 ! |
|
858 |
|
859 getClassNameAndSelectorFrom:lineStream into:aBlock |
|
860 "a helper for list and edit; parses class and selector name. |
|
861 returns false if nothing reasonable was entered" |
|
862 |
|
863 |words wordStream className class selector| |
|
864 |
|
865 lineStream skipSeparators. |
|
866 words := lineStream upToEnd asCollectionOfWords. |
|
867 |
|
868 (words isEmpty) ifTrue:[ |
|
869 ^ false. |
|
870 ]. |
|
871 |
|
872 wordStream := words readStream. |
|
873 |
|
874 "/ search in classes: |
|
875 className := wordStream next. |
|
876 class := Smalltalk classNamed:className. |
|
877 class isNil ifTrue:[ |
|
878 self errorStream showCR:'no such class: ',className. |
|
879 ^ false. |
|
880 ]. |
|
881 |
|
882 (wordStream atEnd not and:[wordStream peek = 'class']) ifTrue:[ |
|
883 wordStream next. |
|
884 class := class theMetaclass |
|
885 ]. |
|
886 (wordStream atEnd) ifFalse:[ |
|
887 selector := wordStream next. |
|
888 ]. |
|
889 aBlock value:class value:selector. |
|
890 ^ true |
681 ! |
891 ! |
682 |
892 |
683 showModules |
893 showModules |
684 |errStream printModule| |
894 |errStream printModule| |
685 |
895 |
752 basicReadEvalPrintLoopWithInput:input output:output error:error |
962 basicReadEvalPrintLoopWithInput:input output:output error:error |
753 compiler:compilerClass prompt:prompt print:doPrint |
963 compiler:compilerClass prompt:prompt print:doPrint |
754 |
964 |
755 "{ Pragma: +optSpace }" |
965 "{ Pragma: +optSpace }" |
756 |
966 |
757 "the core of the interpreter loop; extracted and parametrized, so it can be called recursive |
967 "the core of the interpreter loop; |
758 for included scripts. |
968 extracted and parametrized, so it can be called recursively for included scripts. |
759 If chunkFormat is true, chunks are read. |
969 If chunkFormat is true, chunks are read. |
760 Otherwise, lines up to an empty line (or EOF) or a line ending in '.' are read. |
970 Otherwise, lines up to an empty line (or EOF) or a line ending in '.' are read. |
761 A '#' character appearing in the first column of the first line turns off chunkmode, |
971 A '#' character appearing in the first column of the first line turns off chunkmode, |
762 which allows for convenient shell scripts containing a #/bin/stx as the first line." |
972 which allows for convenient shell scripts containing a #/bin/stx as the first line." |
763 |
973 |