læran91
læran91

Reputation: 293

lexical error in string/charcter literal at character ' '?

I am a newbie to Haskell and am trying to cipher the plain text by using shifting the ASCII values and if my plain text contains a number,then it has to encode each digit by putting a special symbol in place of the digit like (0=*,1=',2=~,3=!,4=@,5=#,6=$, 7=%,8=^,9=&). so here is my code for encryption

import Data.Char

canEncrypt :: Char -> Bool
canEncrypt c = isLower(c) && isAscii(c)

encryptChar :: Char -> Char -> Char
encryptChar shift c
 | canEncrypt c = chr(ord(c)+ord(shift))
 | isUpper c = c
 | isNumber c = if (c == 0) then '*'
                 else if (c == 1) then '\'
                       else if (c == 2) then '~'
                             else if (c == 3) then '!'
                                   else if (c == 4) then '@'
                                         else if (c==5) then '#'
                                               else if (c==6) then '$'
                                                     else if (c==7) then '%'
                                                           else if (c==8) then '^'
                                                                 else '&'

and yeah it's a little spaghetti code but when I compiling it it showing an error like below

    |
    | lexical error in string/character literal at character ' '
21  | isNumber c = blah blah blah..
    |

I think am doing something terribly wrong here that I don't know what.so don't hesitate for such silly question am a just beginner so any help would be appreciated and also is there any other implementation to implement this kind of if-else problem? Thank you

Upvotes: 2

Views: 1651

Answers (2)

Ben
Ben

Reputation: 71495

Here's your problem:

if (c == 1) then '\'

The backslash is a special character in string and character literals; it's used to escape the following character, so that you can use other special characters inside string and character literals.

So '\' is read by the Haskell parser as a single quote beginning a character literal, followed by a backslash-escaped single quote inside the character literal, but then there's no unescaped single quote to properly end the character literal, which is why it complains that the next character is a "lexical error in string/character literal".

If you're trying to write a character literal for the backslash character, then you need to use a backslash to escape the backslash to have Haskell read it as a backslash in a character literal, rather than a backslash modifying the interpretation of the next character. So: '\\'

If you're trying to write a character literal for a single quote character, then you had the backslash-escaped single quote character correct for the quote inside the character literal, but you still need to add a non-escaped quote to terminate the character literal. So: '\''

Upvotes: 5

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476709

You write:

else if (c == 1) then '\'

Based on your question, you want to return a quote ('). You do this by escaping the quote, but you need an end of char marker. So we should write '\''

else if (c == 1) then '\''

That being said, this is a nice cascade of if-then-elses.

Since c is a Char, you can not perform a c == 1, since the equivalence function (==) :: Eq a => a -> a -> Bool thus compares two as (so the same type). So we need to write it like:

else if (c == '1') then '\''

Now we have a long cascade of if-then-elses. This is not very elegant, and hard to understand. We can use pattern matching for this:

encryptChar :: Char -> Char -> Char
encryptChar shift c
 | canEncrypt c = chr (ord c + ord shift)
 | isUpper c = c
encryptChar _ '0' = '*'
encryptChar _ '1' = '\''
encryptChar _ '2' = '~'
encryptChar _ '3' = '!'
encryptChar _ '4' = '@'
encryptChar _ '5' = '#'
encryptChar _ '6' = '$'
encryptChar _ '7' = '%'
encryptChar _ '8' = '^'
encryptChar _ '9' = '&'

We can also define a list of values, a list of Chars is a String:

digittrans = "*'~!@#$%^&"

and then we can lookup the i-th index with: digittrans !! i. So by parsing a string "4" to the Int counterpart with read "4", we can obtain the correct value:

encryptChar :: Char -> Char -> Char
encryptChar shift c
 | canEncrypt c = chr (ord c + ord shift)
 | isUpper c = c
 | isDigit c = digittrans !! read [c]
    where digittrans = "*'~!@#$%^&"

We also need to think of a resultion mechanism in case all the above checks fail (something is not encryptable, nor uppercase, nor a digit). In that case we can for instance decide to return the character itself:

encryptChar :: Char -> Char -> Char
encryptChar shift c
 | canEncrypt c = chr (ord c + ord shift)
 | isUpper c = c
 | isDigit c = digittrans !! read [c]
    where digittrans = "*'~!@#$%^&"
encryptChar _ c = c

Upvotes: 7

Related Questions