classoopsmalltalkgnu-smalltalk

Class and methods to trim string not even starting


I am trying to create a class with string trimming functions:

Object subclass: Trimmer [
    trimleading: str [ |ch ret|
        ch := (str first: 1).           "get first character"
        ret := str.                     "make a copy of sent string"
        [ch = ' '] whileTrue: [         "while first char is space"
            ret := (ret copyFrom: 2).   "copy from 2nd char"
            ch := ret first: 1.         "again test first char"
            ].
        ^ret                            "return value is modified string"
        ].
    trim: str [ |ret|
        ret := str. 
        ret := (trimleading value: ret).           "trim leading spaces"
        ret := (trimleading value: (ret reverse)). "reverse string and repeat trim leading"
        ^(ret reverse)                             "return reverse string"
        ]
].

oristr := '        this is a test  '
('ORI..>>',oristr,'<<') displayNl.
('FINAL>>',((Trimmer new) trim: oristr),'<<') displayNl.

However, it is not running and giving following error:

$ gst trimstring_class.st

trimstring_class.st:10: invalid class body element
trimstring_class.st:17: expected expression

Where is the problem and how can this be solved?

If I remove the . after trimleading method block, as in following code:

Object subclass: Trimmer [
    trimleading: str [ |ch ret flag|
        ret := str.                     "make a copy of sent string"
        flag := true.
        [flag] whileTrue: [         "while first char is space"
            ch := ret first: 1.         "again test first char"
            ch = ' '
            ifTrue: [ ret := (ret copyFrom: 2 to: ret size)]    "copy from 2nd char"
            ifFalse: [flag := false] 
            ].
        ^ret                                "value is modified string"
        ]     "<<<<<<< PERIOD/DOT REMOVED FROM HERE."
    trim: str [ |ret|
        ret := str. 
        ret := (trimleading value: ret).           "trim leading spaces"
        ret := (trimleading value: (ret reverse)). "reverse string and repeat trim leading"
        ^(ret reverse)                      "return reverse string"
        ]
].

Then the code starts to run but stops with following error:

$ gst trimstring_class.st 
trimstring_class.st:15: undefined variable trimleading referenced
ORI..>>        this is a test  <<
Object: Trimmer new "<0x7f1c787b4750>" error: did not understand #trim:
MessageNotUnderstood(Exception)>>signal (ExcHandling.st:254)
Trimmer(Object)>>doesNotUnderstand: #trim: (SysExcept.st:1448)
UndefinedObject>>executeStatements (trimstring_class.st:23)

Why trimleading method is undefined now and why gnu-smalltalk did not understand #trim:?


Solution

  • Usually it is wise for a such common use case to check if such functionallity was already implemented. You can take an inspiration from it for your code (you will improve as Smalltalk programmer too). Take a look at trimBlanksFrom: from sports.st:

     SpStringUtilities class >> trimBlanksFrom: aString [
        "^a String
         I return a copy of aString with all leading and trailing blanks removed."
    
        <category: 'services'>
        | first last |
        first := 1.
        last := aString size.
        [last > 0 and: [(aString at: last) isSeparator]] 
            whileTrue: [last := last - 1].
        ^last == 0 
            ifTrue: [String new]
            ifFalse: [
                [first < last and: [(aString at: first) isSeparator]] 
                    whileTrue: [first := first + 1].
                aString copyFrom: first to: last
           ]
    ]
    

    If you want to trim only leading spaces you can just take the second part, where it is trimming the leading spaces.

    EDIT The OP own code a fixes applied:

    Object subclass: Trimmer [
        trimleading: str [ |ch ret flag|
            ret := str.                     "make a copy of sent string"
            flag := true.
            [flag] whileTrue: [         "while first char is space"
                ch := ret first: 1.         "again test first char"
                ch = ' '
                ifTrue: [ ret := (ret copyFrom: 2 to: ret size) ]    "copy from 2nd char"
                ifFalse:  [flag := false ] 
                ].
            ^ret                                "value is modified string"
            ]     "<<<<<<< PERIOD/DOT REMOVED FROM HERE."
        trim: str [ |ret|
            ret := str. 
            ret := self trimleading: (ret copy).           "trim leading spaces"
            ret := self trimleading: (ret copy reverse). "reverse string and repeat trim leading"
            ^ (ret reverse)                      "return reverse string"
            ]
    ].
    
    oristr := '        this is a test  '
    ('ORI..>>',oristr,'<<') displayNl.
    ('FINAL>>',((Trimmer new) trim: oristr),'<<') displayNl.
    

    The were some mistakes that needed to be fixed. If you want to address a selector #trimleading: you have to use self keyword which searches the local class (for own classes or inherited). Next you should not change a variable that you are assigning to you should use a #copy otherwise strange result can be expected.