{- |
Description: Syntax checking
Copyright: Copyright (C) 2023 Yoo Chung
License: GPL-3.0-or-later
Maintainer: dev@chungyc.org

Some solutions to "Problems.P96" of Ninety-Nine Haskell "Problems".
-}
module Solutions.P96 (isIdentifier) where

import qualified Data.Set as Set

{- |
Identifiers in the [Ada programming language](https://www.adaic.org/resources/add_content/standards/05rm/html/RM-2-3.html)
have the syntax described by the diagram below.

![Ada identifier syntax as explained in https://perso.telecom-paristech.fr/pautet/Ada95/chap01.htm](images/Miscellaneous/Ada.svg)

Write a function which checks whether a given string is a legal identifier.
-}
isIdentifier :: String -> Bool
isIdentifier :: String -> Bool
isIdentifier = String -> Bool
consumeFirstLetter

consumeFirstLetter :: String -> Bool
consumeFirstLetter :: String -> Bool
consumeFirstLetter [] = Bool
False
consumeFirstLetter [Char
x]
  | Char -> Bool
isLetter Char
x = Bool
True
  | Bool
otherwise  = Bool
False
consumeFirstLetter (Char
x:Char
'_':String
xs)
  | Char -> Bool
isLetter Char
x = String -> Bool
consumeLetterDigit String
xs
  | Bool
otherwise  = Bool
False
consumeFirstLetter (Char
x:String
xs)
  | Char -> Bool
isLetter Char
x = String -> Bool
consumeLetterDigit String
xs
  | Bool
otherwise  = Bool
False

consumeLetterDigit :: String -> Bool
consumeLetterDigit :: String -> Bool
consumeLetterDigit [] = Bool
False
consumeLetterDigit [Char
x]
  | Char -> Bool
isLetter Char
x = Bool
True
  | Char -> Bool
isDigit Char
x  = Bool
True
  | Bool
otherwise  = Bool
False
consumeLetterDigit (Char
x:Char
'_':String
xs)
  | Char -> Bool
isLetter Char
x = String -> Bool
consumeLetterDigit String
xs
  | Char -> Bool
isDigit Char
x  = String -> Bool
consumeLetterDigit String
xs
  | Bool
otherwise  = Bool
False
consumeLetterDigit (Char
x:String
xs)
  | Char -> Bool
isLetter Char
x = String -> Bool
consumeLetterDigit String
xs
  | Char -> Bool
isDigit Char
x  = String -> Bool
consumeLetterDigit String
xs
  | Bool
otherwise  = Bool
False

isLetter :: Char -> Bool
isLetter :: Char -> Bool
isLetter Char
x = Char -> Set Char -> Bool
forall a. Ord a => a -> Set a -> Bool
Set.member Char
x Set Char
letters
  where letters :: Set Char
letters = String -> Set Char
forall a. Ord a => [a] -> Set a
Set.fromList (String -> Set Char) -> String -> Set Char
forall a b. (a -> b) -> a -> b
$ [Char
'A'..Char
'Z'] String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Char
'a'..Char
'z']

isDigit :: Char -> Bool
isDigit :: Char -> Bool
isDigit Char
x = Char -> Set Char -> Bool
forall a. Ord a => a -> Set a -> Bool
Set.member Char
x Set Char
digits
  where digits :: Set Char
digits = String -> Set Char
forall a. Ord a => [a] -> Set a
Set.fromList [Char
'0'..Char
'9']