#QUALITY by cg
authorClaus Gittinger <cg@exept.de>
Tue, 20 Jun 2017 17:59:59 +0200
changeset 4434 92084009a524
parent 4433 37d85359188d
child 4435 96ba42c85b3e
#QUALITY by cg class: PrintfScanf class added: #format comment/format in: #absDecimalPrintFloat:on:digits: changed: #printArgFrom:to:arguments: check for valid *-argument
PrintfScanf.st
--- a/PrintfScanf.st	Tue Jun 20 14:17:14 2017 +0200
+++ b/PrintfScanf.st	Tue Jun 20 17:59:59 2017 +0200
@@ -115,6 +115,113 @@
     '%f\n' printf:{ 1234.0 asLongFloat } on:Transcript        
     '%f\n' printf:{ 1234.0 asQDouble } on:Transcript        
 "
+!
+
+format
+"
+    Format specifier:
+    
+        required: leading '%'
+        optional: '-' (POSIX refers to this as the <<flags>>)
+        optional: positive number or '*' (POSIX <<width>>)
+        optional: period followed by positive number or * (POSIX <<precision>>)
+        optional: an h or l to indicate size of data (POSIX <<length>>)
+        required: character describing output behavior (POSIX <<conversion specifier>>)
+
+    Various implementations of printf have added different functionality.
+
+    ANSI standards up through C99:
+
+        more flags '+' ' ' '0' '#'
+        more lengths 'L' 'hh' 'll' 'j' 'z' 't'
+        more conversions 'F' 'a' 'A' 'n'
+
+    The POSIX specification of printf added:
+
+        positional parameters to identify argument indices
+        more flags ''' (single quote)
+        more conversions 'C' 'S'
+        clarifications regarding corner cases and 'undefined behavior'
+
+    BSD implementations added:
+
+        more lengths 'q'
+        more conversions 'D' 'U' 'O'
+
+    glibc (GNU) added:
+
+        more lengths 'Z'
+        more conversions 'm'
+
+    Windows C Runtime (CRT) added:
+
+        more lengths 'I' 'I32' 'I64' 'w'
+
+    glibc and CRT both added 'Z'. 
+    glibc uses 'Z' for the length size_t. 
+    CRT uses Z as a conversion for length-prefixed strings. 
+    This implementation takes the former approach, handling 'Z' in the same way as 'z'.
+
+    BSD and IBM C library both added 'D'. 
+    BSD uses D as a conversion, namely as an alias of 'ld'. 
+    IBM uses 'D' for the length for _Decimal64, a decimal floating point type, in accordance with ISO/IEC TR 24732. 
+    This implementation takes the former approach.
+
+    This implementation also adds new conversions:
+
+        'b' and 'B' for binary (base-2) integer renderings
+        'y' and 'Y' for true/false and yes/no Boolean conversions
+        'J' for JSON
+        'T' and 'V' for JS typeof and valueOf inspection
+
+    Conversions (upper case same as lower case):
+        'a'     (not implemented) base-2 floating point exp form
+        'b'     binary (base 2)
+        'c'     character or (first char of string)
+        'd'     character or (first char of string)
+        'e'     base-10 floating point exp form (scientific)
+        'f'     base-10 floating point decimal form (non-scientific)
+        'g'     'e' or 'f', whichever looks more appropriate (based on value)
+        'i'     integer (alias for 'd')
+        'j'     (not implemented) JSON format
+        'n'     (not implemented) stores number of characters written into arg 
+        'o'     base-8 octal
+        'p'     (not implemented) pointer 
+        's'     string
+        't'     type (i.e. class name)
+        'u'     (not implemented) unsigned (negative values are converted)
+        'v'     (not implemented) store string
+        'x'     base-16 hex
+
+    Parameter selection (not implemented):
+
+        <n>$    take n'th parameter
+
+    Dynamic width/precision (consumed in order as presented):
+
+        *       take width/parameter from next argument
+
+        PrintfScanf printf:'|%s|'  arguments:{ 'abc' }        -> '|abc|'
+        PrintfScanf printf:'|%5s|'  arguments:{ 'abc' }       -> '|  abc|'
+        PrintfScanf printf:'|%*s|'  arguments:{ 5 . 'abc' }   -> '|  abc|'
+
+        PrintfScanf printf:'|%8f|'  arguments:{ 1.234 }       -> '|   1.234|'
+        PrintfScanf printf:'|%*f|'  arguments:{ 8 . 1.234 }   -> '|   1.234|'
+
+
+    Negative width will fill at the right:
+
+        PrintfScanf printf:'|%5s|'  arguments:{ 'abc' }      -> '|  abc|'
+        PrintfScanf printf:'|%-5s|' arguments:{ 'abc' }      -> '|abc  |'
+        PrintfScanf printf:'|%-*s|' arguments:{ 5 . 'abc' }  -> '|abc  |'
+        PrintfScanf printf:'|%*s|'  arguments:{ -5 . 'abc' } -> '|abc  |'
+
+        PrintfScanf printf:'|%*f|'  arguments:{ -8 . 1.234 }  -> '|1.234   |'
+        PrintfScanf printf:'|%-8f|'  arguments:{ 1.234 }      -> '|1.234   |'
+        PrintfScanf printf:'|%-*f|'  arguments:{ 8 . 1.234 }  -> '|1.234   |'
+        PrintfScanf printf:'|%-*f|'  arguments:{ -8 . 1.234 } -> '|1.234   |'
+
+"
 ! !
 
 !PrintfScanf class methodsFor:'instance creation'!
@@ -165,6 +272,8 @@
         ^ outStream nextPut: formatStream next
     ].
 
+    "/ flags:
+    
     char == $- ifTrue:[
         ljust := true.  
         formatStream next.  
@@ -189,23 +298,39 @@
         char := formatStream peek
     ].
 
+    "/ possibly a width
     char == $* ifTrue:[
-        width := nextArg value.  
+        width := nextArg value. 
+        width isInteger ifFalse:[
+            self error:'non integer width argument in printf'
+        ].
+        width < 0 ifTrue:[
+            ljust := true.
+            width := width negated
+        ].    
         formatStream next.  
         char := formatStream peek
     ].
 
     char isDigit ifTrue:[
         char == $0 ifTrue: [pad := $0].
-        width := Integer readFrom: formatStream.  
+        width := Integer readFrom: formatStream onError:0.  
         char := formatStream peek
     ].
 
+    "/ precision separator
+    
     char == $. ifTrue:[
         formatStream next.  char := formatStream peek.
-        char == $*
-            ifTrue: [precision := nextArg value.  formatStream next.]
-            ifFalse: [precision := Integer readFrom: formatStream.].
+        char == $* ifTrue: [
+            precision := nextArg value.  
+            precision isInteger ifFalse:[
+                self error:'non integer precision argument in printf'
+            ].    
+            formatStream next.
+        ] ifFalse: [
+            precision := Integer readFrom: formatStream.
+        ].
         char := formatStream peek
     ].
 
@@ -297,7 +422,7 @@
     ljust ifFalse: [outStream nextPutAll: (arg copyFrom: 1 to: precision)].
     ^ formatStream next
 
-    "Modified (comment): / 19-06-2017 / 15:46:33 / cg"
+    "Modified: / 20-06-2017 / 15:26:02 / cg"
 !
 
 printf:formatString argument:arg 
@@ -478,10 +603,13 @@
      self printf:'%20.10f\n' on:Transcript arguments: { 0.0 asLongFloat log10  }
      self printf:'%20.10f\n' on:Transcript arguments: { 0.0 asQDouble log10  }
 
-     self printf:'%20.10f\n' on:Transcript arguments: { -1.0 log10   }
-     self printf:'%20.10f\n' on:Transcript arguments: { -1.0 asShortFloat log10  }
-     self printf:'%20.10f\n' on:Transcript arguments: { -1.0 asLongFloat log10  }
-     self printf:'%20.10f\n' on:Transcript arguments: { -1.0 asQDouble log10  }
+     self printf:'%20.10f\n' on:Transcript arguments: { DomainError ignoreIn:[ -1.0 log10 ]   }
+     self printf:'%20.10f\n' on:Transcript arguments: { DomainError ignoreIn:[ -1.0 asShortFloat log10 ]  }
+     self printf:'%20.10f\n' on:Transcript arguments: { DomainError ignoreIn:[ -1.0 asLongFloat log10]   }
+     self printf:'%20.10f\n' on:Transcript arguments: { DomainError ignoreIn:[ -1.0 asQDouble log10]   }
+
+     self printf:'%10s\n' on:Transcript arguments:{ 'hello' }
+     self printf:'%*s\n' on:Transcript arguments:{ 10 . 'hello' }
     "
 
     |absVal exp x fuzz i|
@@ -546,7 +674,7 @@
         ]
     ]
 
-    "Modified (comment): / 20-06-2017 / 13:33:40 / cg"
+    "Modified (comment): / 20-06-2017 / 15:00:22 / cg"
 !
 
 absPrintFloat:aFloat on:aStream digits:digits