rebolred-lang

how to check if char conforms to charset?


tl;dr can't get parse to work with single letter

I'm currently learning Red, so I may be missing basic things, but in general I want to check if a password contains uppercase characters; this was my first attempt (I know it's logically invalid):

num-uppercase: (length? (difference/case password (lowercase password))) / 2

The idea was to convert to a lowercase string and then compare each, but difference seems to convert a string to a set(?).

Now I moved on from that to looping over each letter like so:

is-uppercase: false
foreach letter password [
    if letter <> lowercase letter [
        is-uppercase: true
        break
    ]
]

But then I wanted to check if a single char! letter is a special character:

foreach letter password [
    if parse letter special [
        is-special: true
    ]
]

But I get an error:

*** Script Error: parse does not allow char! for its input argument
*** Where: parse
*** Stack:  

I've been looking for something to be able to check if a char conforms to a charset, but wasn't able.


Solution

  • Parse works with almost any kind of series: that is, with a collection of items put in a succession. string! is such a series, but char! is not.

    >> series? #"."
    == false
    >> scalar? #"."
    == true
    

    To check if your password contains uppercase characters, you first need to create a bitset! that represents such characters:

    >> upper: charset [#"A" - #"Z"]
    == make bitset! #{00000000000000007FFFFFE0}
    

    After that, you can use it to construct a Parse rule and check the conformity of input; for example, is password all uppercased? Some inputs will match, some will not.

    >> parse "YES" [some upper]
    == true
    >> parse "no" [some upper]
    == false
    

    Is it written in sentence-case? Different grammars you'll come up with can yield the same result.

    >> parse "Sentence" [upper to end]
    == true
    >> parse "Sentence" [upper any [not upper skip]]
    == true
    

    Finally, if you want to check if there's at least one uppercase letter, something like this should do:

    >> parse "aBc" [to upper to end]
    == true
    >> parse "abc" [to upper to end]
    == false
    

    If you are looking for password with only one single uppercased letter and no more, then:

    >> parse "aBc" [thru upper any [not upper skip]]
    == true
    >> parse "aBC" [thru upper any [not upper skip]]
    == false
    

    To answer the original question directly: if you have a charset! and a char!, then you can check if the latter belongs to the former with:

    >> pick upper #"a"
    == false
    >> pick upper #"A"
    == true
    >> pick upper [#"A" #"b"]
    == false
    >> pick upper [#"A" #"B"]
    == true
    >> pick upper "Ab"
    == false
    >> pick upper "AB"
    == true
    

    I strongly recommend getting your bearings with Red prior to tackling Parse (which FYI has an official documentation). The best way to do that is by joining Gitter chat to get promptly help with your questions and code, and by studying community-provided learning resources; Rebol/Core User Guide is a good starting point.