Explainer.st
author Stefan Vogel <sv@exept.de>
Thu, 20 Jun 1996 00:29:27 +0200
changeset 290 d1650b17b9e9
parent 263 3b21d0991eff
child 336 b99c14f17afc
permissions -rw-r--r--
Change the name of a classes Metaclass from e.g. "SmallIntegerclass" to "SmallInteger class".

"
 COPYRIGHT (c) 1993 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

'From Smalltalk/X, Version:2.10.9 on 18-jun-1996 at 15:30:47'                   !

Parser subclass:#Explainer
	instanceVariableNames:''
	classVariableNames:''
	poolDictionaries:''
	category:'System-Compiler'
!

!Explainer class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1993 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    a very simple explainer - much more should be added ...
    This one is involved, when the 'explain' function is used in a 
    codeView (popup or via CMD-E).

    [author:]
        Claus Gittinger
"
! !

!Explainer class methodsFor:'explaining'!

commonSuperClassOf:listOfClassNames
    |common found|

    listOfClassNames do:[:className |
        |class|

        ((className endsWith:' class') and:[className ~= 'Metaclass']) ifTrue:[
            class := (Smalltalk at:(className copyWithoutLast:6 "copyTo:(className size - 5)") asSymbol) class
        ] ifFalse:[
            class := Smalltalk at:(className asSymbol).
        ].
        common isNil ifTrue:[
            common := class
        ] ifFalse:[
            (class isSubclassOf:common) ifTrue:[
                "keep common"
            ] ifFalse:[
                (common isSubclassOf:class) ifTrue:[
                    common := class
                ] ifFalse:[
                    "walk up, checking"
                    found := false.
                    common allSuperclassesDo:[:sup |
                        (class isSubclassOf:sup) ifTrue:[
                            common := sup
                        ]
                    ].
                    found ifFalse:[
                        class allSuperclassesDo:[:sup |
                            (common isSubclassOf:sup) ifTrue:[
                                common := sup
                            ]
                        ].
                    ].
                ]
            ].
        ].
        common == Object ifTrue:[^ common]
    ].
    ^ common

    "Modified: 17.6.1996 / 17:09:21 / stefan"
!

explain:someText in:source forClass:aClass
    "Given a source and a substring of it, return a string containing
     an explanation.
     This is just a q&d implementation - to be correct, it should use the parser,
     and explain from the parsetree (instead of doing string matches).
     This leads to some wrong explanations, for example if some string is
     used as selector within a string.
     Also, there could be much more detailed explanations."

    |parser variables v c string sym list count tmp
     commonSuperClass|

    string := someText withoutSeparators.

    "
     ask parser for variable names
    "
    parser := self parseMethod:source in:aClass ignoreErrors:true ignoreWarnings:true.
    parser notNil ifTrue:[
        "look for variables"

        variables := parser methodVars.
        (variables notNil and:[variables includes:string]) ifTrue:[
            ^ string , ' is a method variable'
        ].
        variables := parser methodArgs.
        (variables notNil and:[variables includes:string]) ifTrue:[
            ^ string , ' is a method argument'
        ]
    ].

    parser isNil ifTrue:[
        parser := self for:(ReadStream on:source) in:aClass
    ].

    "instvars"
    c := aClass whichClassDefinesInstVar:string.
    c notNil ifTrue:[
        ^ string , ' is an instance variable in ' , c name
    ].

"/    variables := aClass allInstVarNames.
"/    (variables notNil and:[variables includes:string]) ifTrue:[
"/        "where is it"
"/        c := aClass.
"/        [c notNil] whileTrue:[
"/            v := c instVarNames.
"/            (v notNil and:[v includes:string]) ifTrue:[
"/                ^ string , ' is an instance variable in ' , c name
"/            ].
"/            c := c superclass
"/        ].
"/        self error:'oops'
"/    ].

    "class instvars"
    variables := aClass class allInstVarNames.
    (variables notNil and:[variables includes:string]) ifTrue:[
        "where is it"
        c := aClass.
        [c notNil] whileTrue:[
            v := c class instVarNames.
            (v notNil and:[v includes:string]) ifTrue:[
                ^ string , ' is a class instance variable in ' , c name
            ].
            c := c superclass
        ].
        self error:'oops'
    ].

    "classvars"
    c := parser inWhichClassIsClassVar:string.
    c notNil ifTrue:[
        ^ string , ' is a class variable in ' , c name
    ].

    string knownAsSymbol ifTrue:[
        "globals & symbols"

        tmp := self explainKnownSymbol:string.
        tmp notNil ifTrue:[ ^ tmp].
    ].

    "try for some obvious things"
    tmp := self explainPseudoVariable:string in:aClass.
    tmp notNil ifTrue:[ ^ tmp].

    "try syntax ..."

    tmp := self explainSyntax:string.
    tmp notNil ifTrue:[ ^ tmp].

    parser isNil ifTrue:[
        ^ 'parse error - no explanation'
    ].
    ^ 'cannot explain this - select individual tokens for an explanation.'

    "Created: 3.12.1995 / 12:47:37 / cg"
    "Modified: 3.12.1995 / 12:48:59 / cg"
!

explainHereIn:aClass
    ^ 'like self, here refers to the object which received the message.

However, when sending a message to here the search for methods
implementing this message will start in the defining class (' , aClass name , ')
instead of the receivers class (' , aClass name , ' or subclass).
Thus, using here, redefined methods will NOT be reached with a here-send.'
!

explainKnownSymbol:string
    "return explanation or nil"

    |sym list count tmp commonSuperClass val|

    sym := string asSymbol.

    "try globals"

    (Smalltalk includesKey:sym) ifTrue:[
        tmp := string , ' is a global variable.'.
        val := Smalltalk at:sym.
        val isBehavior ifTrue:[
            val name = string ifTrue:[
                tmp := tmp , '

' , string , ' is a class categorized as ' , val category , '
in the ''' , val package , ''' package.'
            ] ifFalse:[
                tmp := tmp , '

' , string , ' is bound to the class ' , val name ,
' in category ' , val category , '.'
            ]
        ] ifFalse:[
            tmp := tmp , '

Its current value is ' , val classNameWithArticle , '.'
        ].
        ^ tmp.
    ].

    "
     try selectors
     look who implements it
    "
    list := Set new.
    Smalltalk allBehaviorsDo:[:c|
        (c implements:sym) ifTrue:[
            list add:(c name)
        ].
        (c class implements:sym) ifTrue:[
            list add:(c name , ' class')
        ]
    ].

    count := list size.
    (count ~~ 0) ifTrue:[
        "
         for up-to 4 implementing classes,
         list them
        "
        list := list asOrderedCollection sort.
        tmp := ' is a selector implemented in '.
        (count == 1) ifTrue:[
            ^ string , tmp , (list at:1) , '.'
        ].

        (count == 2) ifTrue:[
            ^ string , tmp , (list at:1) , ' and ' , (list at:2) , '.'
        ].
        (count == 3) ifTrue:[
            ^ string , tmp , '
' , (list at:1) , ', ' , (list at:2) , ' and ' , (list at:3) , '.'
        ].
        (count == 4) ifTrue:[
            ^ string , tmp , '
' , (list at:1) , ', ' , (list at:2) , ', ' , (list at:3), ' and ' , (list at:4) , '.'
        ].

        "
         if there are more, look for a common
         superclass and show it ...
        "
        commonSuperClass := self commonSuperClassOf:list.
        commonSuperClass ~~ Object ifTrue:[
            (list includes:commonSuperClass) ifTrue:[
                ^ string , tmp, count printString , commonSuperClass name 
                         , ' and redefined in ' , (count - 1) printString  
                         , ' subclasses'
            ].
            ^ string , tmp, count printString , ' subclasses of ' , commonSuperClass name
        ].

        "
         otherwise just give the number.
        "
        ^ string , tmp , count printString , ' classes.'
    ].

    ^ nil

    "Modified: 17.6.1996 / 17:09:30 / stefan"
!

explainPseudoVariable:string in:aClass
    "return explanation for the pseudoVariables self, super etc."

    (string = 'self') ifTrue:[
	^ self explainSelfIn:aClass
    ].

    (string = 'super') ifTrue:[
	^ self explainSuperIn:aClass
    ].

    (string = 'here') ifTrue:[
	^ self explainHereIn:aClass
    ].

    (string = 'thisContext') ifTrue:[
	^ 'thisContext is a pseudo variable (i.e. it is built in).

ThisContext always refers to the context object for the currently executed method or
block (an instance of Context or BlockContext respectively). The calling chain and calling
receivers/selectors can be accessed via thisContext.'
    ].

    (string = 'true') ifTrue:[
	^ 'true is a pseudo variable (i.e. it is built in).

True represents logical truth. It is the one and only instance of class True.'
    ].

    (string = 'false') ifTrue:[
	^ 'false is a pseudo variable (i.e. it is built in).

False represents logical falseness. It is the one and only instance of class False.'
    ].

    (string = 'nil') ifTrue:[
	^ 'nil is a pseudo variable (i.e. it is built in).

Nil is used for unitialized variables (among other uses).
Nil is the one and only instance of class UndefinedObject.'
    ].
    ^ nil
!

explainSelfIn:aClass
    |sub|

    sub := aClass allSubclasses collect:[:c | c name].
    sub size == 0 ifTrue:[
	^ 'self refers to the object which received the message.

In this case, it will be an instance of ' , aClass name , '.'
    ].

    sub size <= 5 ifTrue:[
	^ 'self refers to the object which received the message.

In this case, it will be an instance of ' , aClass name , '
or one of its subclasses:

' , sub asStringCollection asString
    ].

    ^ 'self refers to the object which received the message.

In this case, it will be an instance of ' , aClass name , '
or one of its ' , sub size printString , ' subclasses.'
!

explainSuperIn:aClass
    ^ 'like self, super refers to the object which received the message.

However, when sending a message to super the search for methods
implementing this message will start in the superclass (' , aClass superclass name , ')
instead of the receivers class (' , aClass name , ' or subclass).
Thus, using super, a redefined method can call the original method in its superclass.'
!

explainSyntax:string
    "try syntax ...; return explanation or nil"

    ((string = ':=') or:[string = '_']) ifTrue:[
	^ '<variable> := <expression>

:= and _ (which is left-arrow in some fonts) mean assignment.
The variable is bound to (i.e. points to) the value of <expression>.'
    ].

    (string = '^') ifTrue:[
	^ '^ <expression>

return the value of <expression> as value from the method.
A return from within a block exits the method where the block is defined.'
    ].

    (string = ';') ifTrue:[
	^ '<expression> ; selector1 ; .... ; selectorN

a cascade expression; evaluate expression, and send messages 
<selector1> ... <selectorN> to the first expressions receiver. 
Returns the value of the last send. The cascade sends may also have arguments.'
    ].

    (string = '|') ifTrue:[
	^ '| locals |  or: [:arg | statements]

| is used to mark a local variable declaration or separates arguments
from the statements in a block. Notice, that in a block-argument declaration
these must be prefixed by a colon character.
| is also a selector understood by Booleans.'
    ].

    ((string startsWith:'(') or:[string endsWith:')']) ifTrue:[
	^ '(<expression>)

expression grouping.'
    ].

    ((string startsWith:'[') or:[string endsWith:']']) ifTrue:[
	^ '[arguments | statements]

defines a block. 
Blocks represent pieces of executable code. Definition of a block does
not evaluate it. The block is evaluated by sending it a value/value:
message.
Blocks are often passed as arguments to Booleans (i.e. ifTrue:[...]) or
collections (i.e. do:[...]).'
    ].

    (string = ':') ifTrue:[
	^ 'colons have different meaning depending on context:

1) they separate keyword-parts in symbols and keyword-messages as in:

    #at:put:                     a constant keyword symbol

    rec at:index put:value       sends the #at:put: message to rec,
				 passing index and value as arguments.

2) within block-argument declarations as in:

    [:arg1 :arg2 | statements]
'
    ].

    (string = '.') ifTrue:[
	^ 'statement. "<- period here"
statement

within a method or block, individual statements are separated by periods.
'
    ].

    (string startsWith:'#' ) ifTrue:[
	(string startsWith:'#(' ) ifTrue:[
	    ^ 'is a constant Array.

The elements of a constant Array must be Number-constants, nil, true or false.
(notice, that not all Smalltalk implementations allow true, false and nil as
 constant-Array elements).'
	].

	(string startsWith:'#[') ifTrue:[
	    ^ 'is a constant ByteArray.

The elements of a constant ByteArray must be Integer constants in the range
0 .. 255.
(notice, that not all Smalltalk implementations support constant ByteArrays).'
	].

	(string startsWith:'#''') ifTrue:[
	    ^ 'is a constant symbol containing non-alphanumeric characters.

Symbols are unique strings, meaning that there exists
exactly one instance of a given symbol. Therefore symbols can
be compared using == (identity compare) in addition to = (contents compare).
Beside this, Symbols behave mostly like Strings.

Notice, that not all Smalltalk implementations support this kind of symbols.'
	].

	^ 'is a symbol.

Symbols are unique strings, meaning that there exists
exactly one instance of a given symbol. Therefore symbols can
be compared using == (identity compare) in addition to = (contents compare).
Beside this, Symbols behave mostly like Strings.'
    ].

    "/ is it a symbol without hash-character ?
    "/
    string knownAsSymbol ifTrue:[
	^ 'is nothing, but #' , string , ' is known as a symbol.

Symbols are unique strings, meaning that there exists
exactly one instance of a given symbol. Therefore symbols can
be compared using == (identity compare) in addition to = (contents compare).
Beside this, Symbols behave mostly like Strings.'
    ].

    ^ nil
! !

!Explainer class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libcomp/Explainer.st,v 1.18 1996-06-19 22:29:26 stefan Exp $'
! !