CurryInfo: smtlib-3.0.0 / Language.SMTLIB.Scanner

classes:

              
documentation:
--- ----------------------------------------------------------------------------
--- This module provides a simple scanner for the SMT-LIB language (v2.6).
---
--- @author  Jan Tikovsky (with changes by Niels Bunkenburg)
--- @version May 2021
--- ----------------------------------------------------------------------------
name:
Language.SMTLIB.Scanner
operations:
scan
sourcecode:
module Language.SMTLIB.Scanner (Token (..), scan) where

import Data.Char
import Data.Map (Map, fromList, lookup)

data Token   -- symbols
           = LParen
           | RParen
           | Semi
           | Bang
           | Underscore
           | Colon
           | Comma
           | DQuote
             -- reserved keywords
           | KW_as
           | KW_BINARY
           | KW_DECIMAL
           | KW_error
           | KW_exists
           | KW_HEXADEC
           | KW_forall
           | KW_let
           | KW_model   -- not part of the SMTLib standard
           | KW_NUMERAL
           | KW_par
           | KW_sat
           | KW_STRING
           | KW_success
           | KW_unknown
           | KW_unsat
           | KW_unsupported
             -- command names
           | KW_assert
           | KW_check_sat
           | KW_check_sat_assuming
           | KW_decl_const
           | KW_decl_datatypes -- not part of the SMTLib standard
           | KW_decl_fun
           | KW_decl_sort
           | KW_def_fun
           | KW_def_fun_rec
           | KW_def_funs_rec
           | KW_def_sort
           | KW_echo
           | KW_exit
           | KW_get_assertions
           | KW_get_assignment
           | KW_get_info
           | KW_get_model
           | KW_get_option
           | KW_get_proof
           | KW_get_unsat_assumptions
           | KW_get_unsat_core
           | KW_get_value
           | KW_pop
           | KW_push
           | KW_reset
           | KW_reset_assertions
           | KW_set_info
           | KW_set_logic
           | KW_set_option
             -- info responses
           | KW_assertion_stack_levels
           | KW_authors
           | KW_error_behavior
           | KW_name
           | KW_reason_unknown
           | KW_version
             -- error behaviors
           | KW_immediate_exit
           | KW_continued_execution
             -- reason unknown
           | KW_memout
           | KW_incomplete
             -- identifier, numeral, bvalue
           | Id String
           | Num Int
           | BVal Bool
  deriving (Eq, Show)

keywords :: Map String Token
keywords = fromList
  [ ("as"                    , KW_as)
  , ("BINARY"                , KW_BINARY)
  , ("DECIMAL"               , KW_DECIMAL)
  , ("error"                 , KW_error)
  , ("exists"                , KW_exists)
  , ("HEXADECIMAL"           , KW_HEXADEC)
  , ("forall"                , KW_forall)
  , ("let"                   , KW_let)
  , ("model"                 , KW_model)
  , ("NUMERAL"               , KW_NUMERAL)
  , ("par"                   , KW_par)
  , ("sat"                   , KW_sat)
  , ("STRING"                , KW_STRING)
  , ("success"               , KW_success)
  , ("unknown"               , KW_unknown)
  , ("unsat"                 , KW_unsat)
  , ("unsupported"           , KW_unsupported)
  -- commands
  , ("assert"                , KW_assert)
  , ("check-sat"             , KW_check_sat)
  , ("check-sat-assuming"    , KW_check_sat_assuming)
  , ("declare-const"         , KW_decl_const)
  , ("declare-datatypes"     , KW_decl_datatypes)
  , ("declare-fun"           , KW_decl_fun)
  , ("declare-sort"          , KW_decl_sort)
  , ("define-fun"            , KW_def_fun)
  , ("define-fun-rec"        , KW_def_fun_rec)
  , ("define-funs-rec"       , KW_def_funs_rec)
  , ("define-sort"           , KW_def_sort)
  , ("echo"                  , KW_echo)
  , ("exit"                  , KW_exit)
  , ("get-assertions"        , KW_get_assertions)
  , ("get-assignment"        , KW_get_assignment)
  , ("get-info"              , KW_get_info)
  , ("get-model"             , KW_get_model)
  , ("get-option"            , KW_get_option)
  , ("get-proof"             , KW_get_proof)
  , ("get-unsat-assumptions" , KW_get_unsat_assumptions)
  , ("get-unsat-core"        , KW_get_unsat_core)
  , ("get-value"             , KW_get_value)
  , ("pop"                   , KW_pop)
  , ("push"                  , KW_push)
  , ("reset"                 , KW_reset)
  , ("reset-assertions"      , KW_reset_assertions)
  , ("set-info"              , KW_set_info)
  , ("set-logic"             , KW_set_logic)
  , ("set-option"            , KW_set_option)
  -- info responses
  , ("assertion-stack-levels", KW_assertion_stack_levels)
  , ("authors"               , KW_authors)
  , ("error-behavior"        , KW_error_behavior)
  , ("name"                  , KW_name)
  , ("reason-unknown"        , KW_reason_unknown)
  , ("version"               , KW_version)
  -- error behaviors
  , ("immediate-exit"        , KW_immediate_exit)
  , ("continued-execution"   , KW_continued_execution)
  -- reason unknown
  , ("memout"                , KW_memout)
  , ("incomplete"            , KW_incomplete)
  ]

scan :: String -> [Token]
scan ""     = []
scan s@(c:cs) = case c of
  '('                          -> LParen     : scan cs
  ')'                          -> RParen     : scan cs
  '!'                          -> Bang       : scan cs
  '_'                          -> Underscore : scan cs
  ':'                          -> Colon      : scan cs
  '"'                          -> DQuote     : scan cs
  ','                          -> Comma      : scan cs
  ';'                          -> scanLineCmt cs
  _ | isInt   c                -> scanInt s
    | isAlpha c || isSpecial c -> scanKWOrId s
    | isSpace c                -> scan cs
    | otherwise                -> error $ "Scanner.scan: Unexpected character "
                                     ++ [c] ++ "\nRemaining tokens: " ++ cs

isInt :: Char -> Bool
isInt c = isDigit c || c == '-'

isSpecial :: Char -> Bool
isSpecial c = c `elem` [ '+', '-', '/', '*', '=', '!', '.', '$', '_', '~', '&'
                       , '^', '<', '>', '@', '#', '\''
                       ]

--- line comments are ignored by the scanner
scanLineCmt :: String -> [Token]
scanLineCmt ""     = []
scanLineCmt (c:cs) = case c of
  '\n' -> scan cs
  _    -> scanLineCmt cs

scanNum :: String -> [Token]
scanNum cs = let (n, rest) = span isDigit cs
             in Num (read n) : scan rest

scanInt :: String -> [Token]
scanInt cs = case cs of
  '-' : rs -> let num = dropWhile (not . isDigit) rs
              in case scanNum num of
                   Num n : ts -> Num (- n) : ts
                   ts         -> error $ "Scanner.scanInt: Expected token sequence starting with Num n but got: " ++ show ts
  _        -> scanNum cs

scanKWOrId :: String -> [Token]
scanKWOrId cs = let (i, rest) = span (\c -> isAlphaNum c || isSpecial c) cs
                in kwOrId i : scan rest

kwOrId :: String -> Token
kwOrId s = maybe (idOrBVal s) id (Data.Map.lookup s keywords)

idOrBVal :: String -> Token
idOrBVal s = case s of
  "true"  -> BVal True
  "false" -> BVal False
  _       -> Id s
types:
Token
unsafe:
safe