packages feed

api-tools-0.8.0.1: src/Data/API/Scan.x

{
{-# OPTIONS_GHC -w #-}

module Data.API.Scan
    ( scan
    , PToken
    , AlexPosn(..)
    , Token(..)
    , keywords
    ) where

import           Data.API.Types
import           Data.API.Utils

import           Data.Char
import           Data.Time
import           Safe
}

%wrapper "posn"

$digit = 0-9            -- digits
$lower = [a-z_]         -- lower case & _
$upper = [A-Z]          -- upper case letters

@d2    = $digit{2}
@d4    = $digit{4}


tokens :-
    $white+                             ;
    "--".*                              ;
    "{-"(\n|[^\-]|\-[^\}])*"-}"         ;
    ";"                                 { simple    Semi            }
    "|"                                 { simple    Bar             }
    "["                                 { simple    Bra             }
    "]"                                 { simple    Ket             }
    "::"                                { simple    ColCol          }
    ":"                                 { simple    Colon           }
    "="                                 { simple    Equals          }
    "<="                                { simple    LtEq            }
    ">="                                { simple    GtEq            }
    "?"                                 { simple    Query           }
    ","                                 { simple    Comma           }
    version                             { simple    Version         } -- N.B. extend the 'keywords list below
    with                                { simple    With            } -- when adding new keywords!
    integer                             { simple    Integer         }
    boolean                             { simple    Boolean         }
    utc                                 { simple    UTC             }
    string                              { simple    String          }
    binary                              { simple    BInary          }
    json                                { simple    Json            }
    record                              { simple    Record          }
    union                               { simple    Union           }
    enum                                { simple    Enum            }
    basic                               { simple    Basic           }
    changes                             { simple    Changes         }
    added                               { simple    Added           }
    removed                             { simple    Removed         }
    renamed                             { simple    Renamed         }
    changed                             { simple    Changed         }
    default                             { simple    Default         }
    field                               { simple    Field           }
    alternative                         { simple    Alternative     }
    migration                           { simple    Migration       }
    to                                  { simple    To              }
    nothing                             { simple    NOTHING         }
    true                                { simple    TRUE            }
    false                               { simple    FALSE           }
    read\-only                          { simple    Readonly        }
    @d4\-@d2\-(@d2)T@d2\:@d2(:@d2)?Z    { utc_                      }
      $upper [$lower $upper $digit]*    { mk        TypeIden        }
    \'$upper [$lower $upper $digit]*\'  { strip_qs  TypeIden        }
      $lower [$lower $upper $digit]*    { mk        VarIden         }
    \'$lower [$lower $upper $digit]*\'  { strip_qs  VarIden         }
    \"([^\\\"]|\\[\\\'\"])*\"           { string                    }
    \-?$digit+                          { intg                      }
    "//".*                              { line_comment              }
    "(*"(\n|[^\*]|\*[^\)])*"*)"         { block_comment             }

{

keywords :: [String]
keywords = [ "version", "with", "integer", "boolean", "utc", "string"
           , "binary", "json", "record", "union", "enum", "basic", "changes"
           , "added", "removed", "renamed", "changed", "default", "field"
           , "alternative", "migration", "to", "nothing", "true", "false"
           , "read-only"
           ]

type PToken = (AlexPosn,Token)

data Token
    = Semi
    | Bar
    | BInary
    | Bra
    | Ket
    | ColCol
    | Colon
    | Comma
    | Equals
    | LtEq
    | GtEq
    | Boolean
    | Integer
    | UTC
    | Query
    | Record
    | String
    | Json
    | Union
    | Version
    | With
    | Enum
    | Basic
    | Changes
    | Added
    | Removed
    | Renamed
    | Changed
    | Default
    | Field
    | Alternative
    | Migration
    | To
    | NOTHING
    | TRUE
    | FALSE
    | Readonly
    | UTCTIME  UTCTime
    | Comment  String
    | TypeIden String
    | VarIden  String
    | Intg     Int
    | Strg     String
    | ERROR
    deriving (Eq,Show)

utc_ :: AlexPosn -> String -> PToken
utc_ = mk $ \s -> maybe ERROR UTCTIME $ parseUTC_ s

line_comment :: AlexPosn -> String -> PToken
line_comment = mk $ Comment . munch_ws . tailSafe . tailSafe 

block_comment :: AlexPosn -> String -> PToken
block_comment p (_:_:str) = 
    case reverse $ munch_ws str of
      _:_:rc -> (p,Comment $ reverse $ munch_ws rc)
      _      -> error "Scan.line_comment"
block_comment _ _ = error "Scan.line_comment" 

strip_qs :: (String->Token) -> AlexPosn -> String -> PToken
strip_qs f p (_:s) = (p,f $ initNote "Scan.strip_qs" s)
strip_qs _ _ _     = error "Scan.strip_qs"

munch_ws :: String -> String
munch_ws = dropWhile isSpace

simple :: Token -> AlexPosn -> String -> PToken
simple tk = mk $ const tk

intg :: AlexPosn -> String -> PToken
intg p s = (p,Intg $ readNote "Data.API.Scan.intg" s)

string :: AlexPosn -> String -> PToken
string = mk (Strg . f . chop)
  where
    f ""    = ""
    f (c:s) = case c of
                '\\' -> g s
                _    -> c : f s

    g ""    = ""
    g (c:s) = c : f s

chop :: String -> String
chop ""    = ""
chop (c:s) =
    case reverse s of
      ""   -> ""
      _:rs -> reverse rs

mk :: (String->Token) -> AlexPosn -> String -> PToken
mk f p s = (p,f s)

scan :: String -> [PToken]
scan = pp . alexScanTokens

pp :: [PToken] -> [PToken]
pp [] = []
pp (pt@(p@(AlexPn _ _ cn),_):inp) =
    case cn of
      1 -> (p,Semi):pt:pp inp
      _ ->          pt:pp inp

test :: IO ()
test = 
 do s <- getContents
    print (scan s)
}