"======================================================================
|
|   Character Method Definitions
|
|
 ======================================================================"


"======================================================================
|
| Copyright 1988,92,94,95,99,2000,2001,2002,2005
| Free Software Foundation, Inc.
| Written by Steve Byrne.
|
| This file is part of the GNU Smalltalk class library.
|
| The GNU Smalltalk class library is free software; you can redistribute it
| and/or modify it under the terms of the GNU Lesser General Public License
| as published by the Free Software Foundation; either version 2.1, or (at
| your option) any later version.
| 
| The GNU Smalltalk class library is distributed in the hope that it will be
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
| General Public License for more details.
| 
| You should have received a copy of the GNU Lesser General Public License
| along with the GNU Smalltalk class library; see the file COPYING.LIB.
| If not, write to the Free Software Foundation, 59 Temple Place - Suite
| 330, Boston, MA 02110-1301, USA.  
|
 ======================================================================"

Magnitude subclass: #Character 
	  instanceVariableNames: 'asciiValue'
	  classVariableNames: 'Table'
	  poolDictionaries: ''
	  category: 'Language-Data types'
!

Character comment: 
'My instances represent the 256 characters of the character set.  I provide
messages to translate between integers and character objects, and provide
names for some of the common unprintable characters.' !


!Character class methodsFor: 'testing'!

isIdentity
    "Answer whether x = y implies x == y for instances of the receiver"
    ^true
!

isImmediate
    "Answer whether, if x is an instance of the receiver, x copy == x"
    ^true
! !


!Character class methodsFor: 'initializing lookup tables'!

initialize
    "Initialize the lookup table which is used to make case and digit-to-char
     conversions faster.
     Indices in Table are ASCII values incremented by one. Indices 1-256
     classify chars (0 = nothing special, 2 = separator, 48 = digit,
     55 = uppercase, 3 = lowercase), indices 257-512 map to lowercase chars,
     indices 513-768 map to uppercase chars."

    Table := ByteArray new: 768.

    1 to: 256 do: [ :value |
	Table
	    at: value + 256 put: value - 1;
	    at: value + 512 put: value - 1.

	(value between: 49 and: 58)
	    ifTrue: [ Table at: value put: 48 ].

	(value between: 66 and: 91)
	    ifTrue: [ Table at: value put: 55; at: value + 512 put: value + 31 ].

	(value between: 98 and: 123)
	    ifTrue: [ Table at: value put: 3; at: value + 256 put: value - 33 ]
    ].
    Table
	at: Character space	value + 1 put: 2;
	at: Character cr	value + 1 put: 2;
	at: Character tab	value + 1 put: 2;
	at: Character nl	value + 1 put: 2;
	at: Character newPage	value + 1 put: 2;
	at: $.			value + 1 put: 4;
	at: $,			value + 1 put: 4;
	at: $:			value + 1 put: 4;
	at: $;			value + 1 put: 4;
	at: $!			value + 1 put: 4;
	at: $?			value + 1 put: 4.
! !


!Character class methodsFor: 'constants'!

backspace
    "Returns the character 'backspace'"
    ^##(Character value: 8)
!

cr
    "Returns the character 'cr'"
    ^##(Character value: 13)
!

bell
    "Returns the character 'bel' "
    ^##(Character value: 7)
!

eof
    "Returns the character 'eof', also known as 'sub' "
    ^##(Character value: 26)
!

eot
    "Returns the character 'eot', also known as 'Ctrl-D' "
    ^##(Character value: 4)
!

esc
    "Returns the character 'esc'"
    ^##(Character value: 27)
!

nul
    "Returns the character 'nul'"
    ^##(Character value: 0)
!

tab
    "Returns the character 'tab'"
    ^##(Character value: 9)
!

nl
    "Returns the character 'nl', also known as 'lf'"
    ^##(Character value: 10)
!

lf
    "Returns the character 'lf', also known as 'nl'"
    ^##(Character value: 10)
!

newPage
    "Returns the character 'newPage', also known as 'ff'"
    ^##(Character value: 12)
!

space
    "Returns the character 'space'"
    ^($ )
! !



!Character class methodsFor: 'instance creation'!

digitValue: anInteger
    "Returns a character that corresponds to anInteger.  0-9 map to $0-$9,
    10-35 map to $A-$Z"
    ^'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' at: anInteger + 1
! !



!Character methodsFor: 'converting'!

asCharacter
    "Return the receiver, since it is already a character."
    ^self
!

digitValue
    "Returns the value of self interpreted as a digit.  Here, 'digit' means
    either 0-9, or A-Z, which maps to 10-35."
    | result |
    result := Table at: 1 + asciiValue.
    ^result > 32
	ifTrue: [ asciiValue - result ]
	ifFalse: [ SystemExceptions.InvalidValue signalOn: self reason: 'Not a digit character' ]
! !



!Character methodsFor: 'comparing'!

< aCharacter
    "Compare the character's ASCII value. Answer whether the receiver's
     is the least."
    ^asciiValue < aCharacter asciiValue
!

<= aCharacter
    "Compare the character's ASCII value. Answer whether the receiver's
     is the least or their equal."
    ^asciiValue <= aCharacter asciiValue
!

> aCharacter
    "Compare the character's ASCII value. Answer whether the receiver's
     is the greatest."
    ^asciiValue > aCharacter asciiValue
!

>= aCharacter
    "Compare the character's ASCII value. Answer whether the receiver's
     is the greatest or their equal."
    ^asciiValue >= aCharacter asciiValue
! !



!Character methodsFor: 'testing'!

isDigit
    "True if self is a 0-9 digit"
    ^(Table at: 1 + asciiValue) = 48
!

isLetter
    "True if self is an upper- or lowercase letter"
    ^((Table at: 1 + asciiValue) bitAnd: 1) = 1
!

isAlphaNumeric
    "True if self is a letter or a digit"
    ^((Table at: 1 + self asciiValue) bitAnd: 17) > 0
!

isLowercase
    "True if self is a lowercase letter"
    ^(Table at: 1 + asciiValue) = 3
!

isUppercase
    "True if self is uppercase"
    ^(Table at: 1 + asciiValue) = 55
!

isSeparator
    "Returns true if self is a space, cr, tab, nl, or newPage"
    ^(Table at: 1 + asciiValue) = 2
!

isPunctuation
    "Returns true if self is one of '.,:;!?'"
    ^(Table at: 1 + asciiValue) = 4
!

isVowel
    "Returns true if self is a, e, i, o, or u; case insensitive"
    | char |
    "So rare it isn't worth optimization"
    char := self asLowercase.
    char = $a ifTrue: [ ^true ].    
    char = $e ifTrue: [ ^true ].    
    char = $i ifTrue: [ ^true ].    
    char = $o ifTrue: [ ^true ].    
    char = $u ifTrue: [ ^true ].    
    ^false
! 

isDigit: radix
    "Answer whether the receiver is a valid character in the given radix."
    | result |
    (radix between: 2 and: 36)
	ifFalse: [ SystemExceptions.ArgumentOutOfRange signalOn: radix mustBeBetween: 2 and: 36 ].
    result := Table at: 1 + asciiValue.
    ^result > 32 and: [ asciiValue - result between: 0 and: radix - 1 ]
! !


!Character methodsFor: 'coercion methods'!

asLowercase
    "Returns self as a lowercase character if it's an uppercase letter,
     otherwise returns the character unchanged."
    ^Character value: (Table at: 513 + asciiValue)
!

asUppercase
    "Returns self as a uppercase character if it's an lowercase letter,
     otherwise returns the character unchanged."
    ^Character value: (Table at: 257 + asciiValue)
!

asString
    "Returns the character self as a string."
    ^String with: self
!

asSymbol
    "Returns the character self as a symbol."
    ^Symbol internCharacter: self
! !



!Character methodsFor: 'printing'!

displayOn: aStream
    "Print a representation of the receiver on aStream. Unlike
     #printOn:, this method strips the leading dollar."
    aStream nextPut: self
!

printOn: aStream
    "Store a representation of the receiver on aStream"
    self storeOn: aStream
! !



!Character methodsFor: 'storing'!

storeOn: aStream
    "Store Smalltalk code compiling to the receiver on aStream"
    aStream nextPut: $$.
    aStream nextPut: self
! !


!Character methodsFor: 'private'!

asLowercaseValue
    "Returns the ASCII value of the receiver converted to a lowercase character
     if it's an uppercase letter, otherwise returns the ASCII value unchanged."
    ^Table at: 513 + asciiValue
! !


!Character methodsFor: 'testing functionality'!

isCharacter
    "Answer True. We're definitely characters"
    ^true
! !

Character initialize!
