class: ReadEvalPrintLoop
authorClaus Gittinger <cg@exept.de>
Thu, 20 Feb 2014 08:44:35 +0100
changeset 16087 ba981c147e6c
parent 16086 9b40fde96544
child 16088 37f0dd85a293
class: ReadEvalPrintLoop class definition added: #basicReadEvalPrintLoopWithInput:output:error:compiler:prompt:print: #cmd_read: comment/format in:9 methods changed:7 methods
ReadEvalPrintLoop.st
--- a/ReadEvalPrintLoop.st	Thu Feb 20 08:03:34 2014 +0100
+++ b/ReadEvalPrintLoop.st	Thu Feb 20 08:44:35 2014 +0100
@@ -13,7 +13,8 @@
 
 Object subclass:#ReadEvalPrintLoop
 	instanceVariableNames:'inputStream outputStream errorStream compiler prompt
-		doChunkFormat traceFlag timingFlag exitAction'
+		doChunkFormat traceFlag timingFlag printFlag exitAction
+		currentDirectory'
 	classVariableNames:''
 	poolDictionaries:''
 	category:'System-Support'
@@ -44,65 +45,94 @@
     Lines starting with '#' are directives:
         #exit   - exit the rep-loop
 
+    The input can be in one of two formats:
+        1) traditional chunk format (bang-separated chunks, bangs duplicated)
+          this is the traditional fileIn format, as generated by fileOut from the browser
+
+        2) interactive line mode. Chunks are any number of lines up to either an empty line or
+          a line ending in a period. This is more useful for an interactive REPL, where statements/expressions
+          are entered linewise by a user.
+
+    The input can is switched to non-chunk format whenever a line with a '#' in the first column appears.
 "
 ! !
 
 !ReadEvalPrintLoop methodsFor:'accessing'!
 
 compiler:something
+    "assign a compiler to use;could be used to change the language"
+
     compiler := something.
 !
 
 doChunkFormat
+    "true if currently reading chunk format"
+
     ^ doChunkFormat ? true
 
     "Created: / 07-12-2006 / 18:24:04 / cg"
 !
 
-doChunkFormat:something
-    doChunkFormat := something.
+doChunkFormat:aBoolean
+    "enable/disable chunk format"
+
+    doChunkFormat := aBoolean.
 
     "Created: / 07-12-2006 / 18:24:04 / cg"
 !
 
-error:something
-    errorStream := something.
+error:aStream
+    "assign an error stream"
+
+    errorStream := aStream.
 
     "Created: / 07-12-2006 / 17:33:39 / cg"
 !
 
 errorStream
-    ^ errorStream ? Transcript ? Stderr
+    "return the current error stream"
+
+    ^ errorStream ? Stderr
 
     "Created: / 07-12-2006 / 19:11:56 / cg"
 !
 
-input:something
-    inputStream := something.
+input:aStream
+    "assign an input stream"
+
+    inputStream := aStream asLineNumberReadStream.
 
     "Modified: / 07-12-2006 / 17:33:31 / cg"
 !
 
 inputStream
+    "get the current input stream"
+
     ^ inputStream ? Stdin
 
     "Created: / 07-12-2006 / 19:12:13 / cg"
 !
 
-output:something
-    outputStream := something.
+output:aStream
+    "assign an output stream"
+
+    outputStream := aStream.
 
     "Created: / 07-12-2006 / 17:27:48 / cg"
 !
 
 outputStream
+    "return the current outpt stream"
+
     ^ outputStream ? Stdout
 
     "Created: / 07-12-2006 / 19:12:27 / cg"
 !
 
-prompt:something
-    prompt := something.
+prompt:aString
+    "set the prompt"
+
+    prompt := aString.
 ! !
 
 !ReadEvalPrintLoop methodsFor:'compiler interface-error handling'!
@@ -169,8 +199,11 @@
     #help ............... this text
     #exit ............... exit interpreter loop
     #use <package>....... use (load) a package
-        stx:libwidg ........ GUI package
-        stx:libtool ........ IDE tool package
+        stx:libwidg .............. GUI package
+        stx:libtool .............. IDE tool package
+        stx:goodies/regex ........ regex package
+        stx:goodies/petitparser .. peg parser package
+    #read <filename>..... read another script or source file
     #show <what> ........ show info
         variables .......... interpreter variables
         processes .......... processes
@@ -182,12 +215,53 @@
         timing ............. timing execution
         chunkFormat ........ traditional bang chunk format input mode
 
-MiniDebugger shows its help with "?".
+The MiniDebugger (if entered) shows its own help with "?".
 '
 
     "Created: / 07-12-2006 / 18:54:20 / cg"
 !
 
+cmd_read:lineStream
+    |filename newInput savedPrompt savedPrint savedInput savedCurrentDirectory|
+
+    lineStream skipSeparators.
+    filename := lineStream upToEnd withoutSeparators.
+
+    filename := filename asFilename.
+    filename isAbsolute ifFalse:[
+        filename := currentDirectory construct:filename.
+    ].
+
+    newInput := filename readStream.
+    newInput isNil ifTrue:[
+        ('Could not find file: "',filename,'"') errorPrintCR.
+        ^ self.
+    ].
+
+    [
+        savedCurrentDirectory := currentDirectory.
+        savedInput := inputStream.
+        savedPrint := printFlag.
+        savedPrompt := prompt.
+
+        currentDirectory := filename directory.
+        inputStream := newInput.
+
+        self 
+            basicReadEvalPrintLoopWithInput:newInput
+            output:outputStream
+            error:errorStream 
+            compiler:compiler 
+            prompt:false
+            print:false.
+    ] ensure:[
+        currentDirectory := savedCurrentDirectory.
+        inputStream := savedInput.
+        printFlag := savedPrint.
+        prompt := savedPrompt
+    ].
+!
+
 cmd_set:lineStream
     self cmd_setOrClear:lineStream to:true
 
@@ -218,11 +292,13 @@
 !
 
 cmd_show:lineStream
-    |what all printModule|
+    |errStream what all printModule|
 
 "
  self basicNew cmd_show:'packages' readStream
 "
+    errStream := self errorStream.
+
     lineStream skipSeparators.
     what := lineStream nextAlphaNumericWord.
     (what startsWith:'var') ifTrue:[
@@ -235,18 +311,18 @@
     (what startsWith:'mod') ifTrue:[
         printModule := 
             [:mod |
-                self errorStream
+                errStream
                     nextPutAll:'  ';
                     nextPutAll:(mod package "libraryName");
                     nextPutLine:' (',(mod type),')'.
             ].
 
-        self errorStream nextPutLine:'builtIn:'.
+        errStream nextPutLine:'builtIn:'.
         ((ObjectMemory binaryModuleInfo 
             reject:[:m | m dynamic])
                 asSortedCollection:[:a :b | a name < b name]) do:printModule.
 
-        self errorStream nextPutLine:'dynamic:'.
+        errStream nextPutLine:'dynamic:'.
         ((ObjectMemory binaryModuleInfo 
             select:[:m | m dynamic])
                 asSortedCollection:[:a :b | a name < b name]) do:printModule.
@@ -256,7 +332,7 @@
     (what startsWith:'mem') ifTrue:[
         all := ObjectMemory oldSpaceUsed + ObjectMemory symSpaceUsed
                                          + ObjectMemory newSpaceUsed.
-        self errorStream 
+        errStream
             nextPutLine:('overall: ',(all // 1024) printString,' Kb');
             nextPutLine:('in use : ',(ObjectMemory bytesUsed // 1024) printString,' Kb');
             nextPutLine:('free   : ',(ObjectMemory freeSpace // 1024) printString,' Kb');
@@ -265,14 +341,14 @@
         ^ self.
     ].
     (what startsWith:'flag') ifTrue:[
-        self errorStream 
+        errStream
             nextPutLine:('trace :      ',traceFlag printString);
             nextPutLine:('timing:      ',timingFlag printString);
             nextPutLine:('chunkFormat: ',doChunkFormat printString).
         ^ self.
     ].
 
-    self errorStream nextPutLine:'?? show what ?'.
+    errStream nextPutLine:'?? show what ?'.
 
     "Modified: / 07-12-2011 / 22:15:07 / cg"
 !
@@ -290,7 +366,7 @@
         ].
     ].
     ok ifFalse:[
-        ('Failed to load package: "',pkg,'"') infoPrintCR.
+        self errorStream nextPutLine:('Failed to load package: "',pkg,'"').
     ].
 
     "Created: / 07-12-2006 / 19:07:56 / cg"
@@ -304,27 +380,141 @@
     s skipSeparators.
 
     cmd := s nextAlphaNumericWord.
-    self 
-        perform:('cmd_',cmd,':') asSymbol with:s 
-        ifNotUnderstood:[   
-            self errorStream  
-                nextPutAll:'?? invalid command: ';
-                nextPutAll:cmd;
-                nextPutAll:'. Type "#help" for help.';
-                cr.
-        ].
+    cmd notNil ifTrue:[
+        self 
+            perform:('cmd_',cmd,':') asSymbol with:s 
+            ifNotUnderstood:[   
+                self errorStream  
+                    nextPutAll:'?? invalid command: ';
+                    nextPutAll:cmd;
+                    nextPutAll:'. Type "#help" for help.';
+                    cr.
+            ].
+    ].
 
     "Created: / 07-12-2006 / 18:49:17 / cg"
 ! !
 
 !ReadEvalPrintLoop methodsFor:'evaluation'!
 
+basicReadEvalPrintLoopWithInput:input output:output error:error 
+    compiler:compilerClass prompt:prompt print:doPrint
+
+    "{ Pragma: +optSpace }"
+
+    "the core of the interpreter loop; extracted and parametrized, so it can be called recursive
+     for included scripts.
+     If chunkFormat is true, chunks are read.
+     Otherwise, lines up to an empty line (or EOF) or a line ending in '.' are read.
+     A '#' character appearing in the first column of the first line turns off chunkmode."
+
+    [
+        |lines chunk|
+
+        prompt notNil ifTrue:[
+            error nextPutAll:prompt.
+        ].
+
+        input atEnd ifTrue:[
+            ^ self.
+        ].
+
+        input peek == $# ifTrue:[
+            self doChunkFormat:false.
+        ].
+
+        self doChunkFormat ifTrue:[
+            input skipSeparators.
+            chunk := input nextChunk.
+        ] ifFalse:[
+            lines := OrderedCollection new.
+            [
+                |line|
+
+                line := input nextLine.
+                line notEmptyOrNil ifTrue:[
+                    line = '?' ifTrue:[
+                        self cmd_help:nil.
+                        prompt notNil ifTrue:[
+                            error nextPutAll:prompt.
+                        ].
+                    ] ifFalse:[
+                        (line startsWith:'#') ifTrue:[
+                            self directive:line.
+                            prompt notNil ifTrue:[
+                                error nextPutAll:prompt.
+                            ].
+                        ] ifFalse:[
+                            lines add:line.
+                        ]
+                    ]
+                ].
+                line notEmptyOrNil and:[(line endsWith:$.) not].
+            ] whileTrue.
+            chunk := lines asStringWith:Character cr.
+        ].
+
+        chunk notEmptyOrNil ifTrue:[
+            "abortAll is handled, but not asked for here!!"
+            AbortAllOperationRequest handle:[:ex |
+                error nextPutLine:('Evaluation aborted: ', ex description)
+            ] do:[ 
+                (Error, ControlInterrupt) handle:[:ex |
+                    prompt isNil ifTrue:[
+                        ex reject
+                    ].
+                    MiniDebugger enterWithMessage:(ex errorString) mayProceed:true.
+                    ex mayProceed ifTrue:[
+                        ex proceed.
+                    ].
+                    error nextPutLine:('Evaluation aborted: ', ex description).
+                    ex return.
+                ] do:[
+                    |value ms us|
+
+                    ms := Time millisecondsToRun:[
+                        us := Time microsecondsToRun:[
+                            value := (compilerClass new requestor:self) evaluate:chunk compile:true.
+                        ].
+                    ].
+                    doPrint ifTrue:[
+                        value printOn:output. output cr.
+                    ].
+
+                    timingFlag == true ifTrue:[
+                        'execution time: ' printOn:error.
+                        ms < 1 ifTrue:[
+                            us < 1 ifTrue:[
+                                'too small to measure (<1us)' printOn:error.
+                            ] ifFalse:[
+                                us printOn:output. 'us' printOn:error.
+                            ]
+                        ] ifFalse:[
+                            ms printOn:output. 'ms' printOn:error.
+                        ].
+                        error cr.
+                    ].
+                ].
+            ].
+        ].
+    ] loop.
+
+    "
+     (ReadEvalPrintLoop new prompt:'>') readEvalPrintLoop
+    "
+
+    "Created: / 07-12-2006 / 17:27:21 / cg"
+    "Modified: / 06-12-2011 / 15:29:03 / cg"
+!
+
 readEvalPrintLoop
     "{ Pragma: +optSpace }"
 
     "simple read-eval-print loop for non-graphical Minitalk.
      If the chunkFormat-argument is true, chunks are read.
-     Otherwise, lines up to an empty line (or EOF) are read."
+     Otherwise, lines up to an empty line (or EOF) are read.
+     A '#' character appearing in the first column of the first line
+     switches to chunkmode."
 
     exitAction := [^ self].
 
@@ -332,95 +522,21 @@
         self errorStream nextPutLine:('Cought: ', ex description).
         ex restart.
     ] do:[
-        [
-            |input output error lines chunk compilerClass|
-
-            "/ re-evaluate these in the loop, so they can be changed dynamically
-            input := self inputStream.
-            output := self outputStream.
-            error := self errorStream.
-            compilerClass := compiler ? Compiler ? Parser.
-            compilerClass isNil ifTrue:[
-                self errorStream nextPutLine:('oops - no Compiler class found').
-                ^ self.
-            ].
+        |input output error compilerClass|
 
-            prompt notNil ifTrue:[
-                error nextPutAll:prompt.
-            ].
-
-            input atEnd ifTrue:[
-                ^ self.
-            ].
-
-            self doChunkFormat ifTrue:[
-                input skipSeparators.
-                chunk := input nextChunk.
-            ] ifFalse:[
-                lines := OrderedCollection new.
-                [
-                    |line|
+        "/ re-evaluate these in the loop, so they can be changed dynamically
+        input := self inputStream.
+        output := self outputStream.
+        error := self errorStream.
 
-                    line := input nextLine.
-                    line notEmptyOrNil ifTrue:[
-                        line = '?' ifTrue:[
-                            self cmd_help:nil.
-                            prompt notNil ifTrue:[
-                                error nextPutAll:prompt.
-                            ].
-                        ] ifFalse:[
-                            (line startsWith:'#') ifTrue:[
-                                self directive:line.
-                                prompt notNil ifTrue:[
-                                    error nextPutAll:prompt.
-                                ].
-                            ] ifFalse:[
-                                lines add:line.
-                            ]
-                        ]
-                    ].
-                    line notEmptyOrNil and:[(line endsWith:$.) not].
-                ] whileTrue.
-                chunk := lines asStringWith:Character cr.
-            ].
-
-            chunk notEmptyOrNil ifTrue:[
-                "abortAll is handled, but not asked for here!!"
-                AbortAllOperationRequest handle:[:ex |
-                    error nextPutLine:('Evaluation aborted: ', ex description)
-                ] do:[ 
-                    (Error, ControlInterrupt) handle:[:ex |
-                        prompt isNil ifTrue:[
-                            ex reject
-                        ].
-                        MiniDebugger enterWithMessage:(ex errorString) mayProceed:true.
-                        ex mayProceed ifTrue:[
-                            ex proceed.
-                        ].
-                        error nextPutLine:('Evaluation aborted: ', ex description).
-                        ex return.
-                    ] do:[
-                        |value t|
-
-                        t := Time millisecondsToRun:[
-                            value := (compilerClass new requestor:self) evaluate:chunk compile:true.
-                        ].
-                        value printOn:output.
-                        output cr.
-                        timingFlag == true ifTrue:[
-                            'execution time: ' printOn:output.
-                            t = 0 ifTrue:[
-                                'too small to measure (<1ms)' printOn:output.
-                            ] ifFalse:[
-                                t printOn:output.
-                                'ms' printOn:output.
-                            ].
-                            output cr.
-                        ].
-                    ].
-                ].
-            ].
-        ] loop.
+        compilerClass := compiler ? Compiler ? Parser.
+        compilerClass isNil ifTrue:[
+            self errorStream nextPutLine:('oops - no Compiler class found').
+            ^ self.
+        ].
+        self 
+            basicReadEvalPrintLoopWithInput:input output:output error:error 
+            compiler:compilerClass prompt:prompt print:(printFlag ? true).
     ]
 
     "
@@ -434,10 +550,10 @@
 !ReadEvalPrintLoop class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic/ReadEvalPrintLoop.st,v 1.43 2014-02-19 22:29:15 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/ReadEvalPrintLoop.st,v 1.44 2014-02-20 07:44:35 cg Exp $'
 !
 
 version_CVS
-    ^ '$Header: /cvs/stx/stx/libbasic/ReadEvalPrintLoop.st,v 1.43 2014-02-19 22:29:15 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/ReadEvalPrintLoop.st,v 1.44 2014-02-20 07:44:35 cg Exp $'
 ! !