module Agda.Syntax.Parser.StringLiterals
( litString, litChar
) where
import Data.Bifunctor
import Data.Char
import qualified Data.Text as T
import Agda.Syntax.Common (pattern Ranged)
import Agda.Syntax.Parser.Alex
import Agda.Syntax.Parser.Monad
import Agda.Syntax.Parser.Tokens
import Agda.Syntax.Parser.LookAhead
import Agda.Syntax.Position
import Agda.Syntax.Literal
litString :: LexAction Token
litString :: LexAction Token
litString = forall tok.
Char -> (Interval -> String -> Parser tok) -> LexAction tok
stringToken Char
'"' forall a b. (a -> b) -> a -> b
$ \ Interval
i String
s ->
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ RLiteral -> Token
TokLiteral forall a b. (a -> b) -> a -> b
$ forall a. Range -> a -> Ranged a
Ranged (forall a. HasRange a => a -> Range
getRange Interval
i) forall a b. (a -> b) -> a -> b
$ Text -> Literal
LitString forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
s
litChar :: LexAction Token
litChar :: LexAction Token
litChar = forall tok.
Char -> (Interval -> String -> Parser tok) -> LexAction tok
stringToken Char
'\'' forall a b. (a -> b) -> a -> b
$ \ Interval
i -> \case
[Char
c] -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ RLiteral -> Token
TokLiteral forall a b. (a -> b) -> a -> b
$ forall a. Range -> a -> Ranged a
Ranged (forall a. HasRange a => a -> Range
getRange Interval
i) forall a b. (a -> b) -> a -> b
$ Char -> Literal
LitChar Char
c
String
_ -> forall a. String -> Parser a
lexError String
"character literal must contain a single character"
litError :: String -> LookAhead a
litError :: forall a. String -> LookAhead a
litError String
msg =
do LookAhead ()
sync
forall a. Parser a -> LookAhead a
liftP forall a b. (a -> b) -> a -> b
$ forall a. String -> Parser a
lexError forall a b. (a -> b) -> a -> b
$
String
"Lexical error in string or character literal: " forall a. [a] -> [a] -> [a]
++ String
msg
stringToken :: Char -> (Interval -> String -> Parser tok) -> LexAction tok
stringToken :: forall tok.
Char -> (Interval -> String -> Parser tok) -> LexAction tok
stringToken Char
del Interval -> String -> Parser tok
mkTok = forall r.
(PreviousInput -> PreviousInput -> Int -> Parser r) -> LexAction r
LexAction forall a b. (a -> b) -> a -> b
$ \ PreviousInput
inp PreviousInput
inp' Int
n ->
do PositionWithoutFile -> Parser ()
setLastPos (forall a. Position' a -> Position' a
backupPos forall a b. (a -> b) -> a -> b
$ PreviousInput -> PositionWithoutFile
lexPos PreviousInput
inp')
PreviousInput -> Parser ()
setLexInput PreviousInput
inp'
String
tok <- forall a.
(forall a. String -> LookAhead a) -> LookAhead a -> Parser a
runLookAhead forall a. String -> LookAhead a
litError forall a b. (a -> b) -> a -> b
$ Char -> String -> LookAhead String
lexString Char
del String
""
Interval
i <- Parser Interval
getParseInterval
Interval -> String -> Parser tok
mkTok Interval
i String
tok
lexString :: Char -> String -> LookAhead String
lexString :: Char -> String -> LookAhead String
lexString Char
del String
s =
do Char
c <- LookAhead Char
nextChar
case Char
c of
Char
c | Char
c forall a. Eq a => a -> a -> Bool
== Char
del -> LookAhead ()
sync forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall (m :: * -> *) a. Monad m => a -> m a
return (forall a. [a] -> [a]
reverse String
s)
Char
'\\' ->
do Char
c' <- LookAhead Char
nextChar
case Char
c' of
Char
'&' -> LookAhead ()
sync forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Char -> String -> LookAhead String
lexString Char
del String
s
Char
c | Char -> Bool
isSpace Char
c -> LookAhead ()
sync forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Char -> String -> LookAhead String
lexStringGap Char
del String
s
Char
_ -> LookAhead String
normalChar
Char
_ -> LookAhead String
normalChar
where
normalChar :: LookAhead String
normalChar =
do LookAhead ()
rollback
Char
c <- LookAhead Char
lexChar
Char -> String -> LookAhead String
lexString Char
del (Char
cforall a. a -> [a] -> [a]
:String
s)
lexStringGap :: Char -> String -> LookAhead String
lexStringGap :: Char -> String -> LookAhead String
lexStringGap Char
del String
s =
do Char
c <- LookAhead Char
eatNextChar
case Char
c of
Char
'\\' -> Char -> String -> LookAhead String
lexString Char
del String
s
Char
c | Char -> Bool
isSpace Char
c -> Char -> String -> LookAhead String
lexStringGap Char
del String
s
Char
_ -> forall a. String -> LookAhead a
lookAheadError String
"non-space in string gap"
lexChar :: LookAhead Char
lexChar :: LookAhead Char
lexChar =
do Char
c <- LookAhead Char
eatNextChar
case Char
c of
Char
'\\' -> LookAhead Char
lexEscape
Char
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return Char
c
lexEscape :: LookAhead Char
lexEscape :: LookAhead Char
lexEscape =
do Char
c <- LookAhead Char
eatNextChar
case Char
c of
Char
'^' -> do Char
c <- LookAhead Char
eatNextChar
if Char
c forall a. Ord a => a -> a -> Bool
>= Char
'@' Bool -> Bool -> Bool
&& Char
c forall a. Ord a => a -> a -> Bool
<= Char
'_'
then forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> Char
chr (Char -> Int
ord Char
c forall a. Num a => a -> a -> a
- Char -> Int
ord Char
'@'))
else forall a. String -> LookAhead a
lookAheadError String
"invalid control character"
Char
'x' -> (Char -> Bool) -> Int -> (Char -> Int) -> LookAhead Char
readNum Char -> Bool
isHexDigit Int
16 Char -> Int
digitToInt
Char
'o' -> (Char -> Bool) -> Int -> (Char -> Int) -> LookAhead Char
readNum Char -> Bool
isOctDigit Int
8 Char -> Int
digitToInt
Char
x | Char -> Bool
isDigit Char
x
-> (Char -> Bool) -> Int -> (Char -> Int) -> Int -> LookAhead Char
readNumAcc Char -> Bool
isDigit Int
10 Char -> Int
digitToInt (Char -> Int
digitToInt Char
x)
Char
c ->
do Char
esc <- forall a.
Char -> [(String, LookAhead a)] -> LookAhead a -> LookAhead a
match' Char
c (forall a b. (a -> b) -> [a] -> [b]
map (forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second forall (m :: * -> *) a. Monad m => a -> m a
return) [(String, Char)]
sillyEscapeChars)
(forall a. String -> LookAhead a
lookAheadError String
"bad escape code")
LookAhead ()
sync
forall (m :: * -> *) a. Monad m => a -> m a
return Char
esc
readNum :: (Char -> Bool) -> Int -> (Char -> Int) -> LookAhead Char
readNum :: (Char -> Bool) -> Int -> (Char -> Int) -> LookAhead Char
readNum Char -> Bool
isDigit Int
base Char -> Int
conv =
do Char
c <- LookAhead Char
eatNextChar
if Char -> Bool
isDigit Char
c
then (Char -> Bool) -> Int -> (Char -> Int) -> Int -> LookAhead Char
readNumAcc Char -> Bool
isDigit Int
base Char -> Int
conv (Char -> Int
conv Char
c)
else forall a. String -> LookAhead a
lookAheadError String
"non-digit in numeral"
readNumAcc :: (Char -> Bool) -> Int -> (Char -> Int) -> Int -> LookAhead Char
readNumAcc :: (Char -> Bool) -> Int -> (Char -> Int) -> Int -> LookAhead Char
readNumAcc Char -> Bool
isDigit Int
base Char -> Int
conv Int
i = Int -> LookAhead Char
scan Int
i
where
scan :: Int -> LookAhead Char
scan Int
i =
do PreviousInput
inp <- LookAhead PreviousInput
getInput
Char
c <- LookAhead Char
nextChar
case Char
c of
Char
c | Char -> Bool
isDigit Char
c -> Int -> LookAhead Char
scan (Int
iforall a. Num a => a -> a -> a
*Int
base forall a. Num a => a -> a -> a
+ Char -> Int
conv Char
c)
Char
_ ->
do PreviousInput -> LookAhead ()
setInput PreviousInput
inp
LookAhead ()
sync
if Int
i forall a. Ord a => a -> a -> Bool
>= Char -> Int
ord forall a. Bounded a => a
minBound Bool -> Bool -> Bool
&& Int
i forall a. Ord a => a -> a -> Bool
<= Char -> Int
ord forall a. Bounded a => a
maxBound
then forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> Char
chr Int
i)
else forall a. String -> LookAhead a
lookAheadError String
"character literal out of bounds"
sillyEscapeChars :: [(String, Char)]
sillyEscapeChars :: [(String, Char)]
sillyEscapeChars =
[ (String
"a", Char
'\a')
, (String
"b", Char
'\b')
, (String
"f", Char
'\f')
, (String
"n", Char
'\n')
, (String
"r", Char
'\r')
, (String
"t", Char
'\t')
, (String
"v", Char
'\v')
, (String
"\\", Char
'\\')
, (String
"\"", Char
'\"')
, (String
"'", Char
'\'')
, (String
"NUL", Char
'\NUL')
, (String
"SOH", Char
'\SOH')
, (String
"STX", Char
'\STX')
, (String
"ETX", Char
'\ETX')
, (String
"EOT", Char
'\EOT')
, (String
"ENQ", Char
'\ENQ')
, (String
"ACK", Char
'\ACK')
, (String
"BEL", Char
'\BEL')
, (String
"BS", Char
'\BS')
, (String
"HT", Char
'\HT')
, (String
"LF", Char
'\LF')
, (String
"VT", Char
'\VT')
, (String
"FF", Char
'\FF')
, (String
"CR", Char
'\CR')
, (String
"SO", Char
'\SO')
, (String
"SI", Char
'\SI')
, (String
"DLE", Char
'\DLE')
, (String
"DC1", Char
'\DC1')
, (String
"DC2", Char
'\DC2')
, (String
"DC3", Char
'\DC3')
, (String
"DC4", Char
'\DC4')
, (String
"NAK", Char
'\NAK')
, (String
"SYN", Char
'\SYN')
, (String
"ETB", Char
'\ETB')
, (String
"CAN", Char
'\CAN')
, (String
"EM", Char
'\EM')
, (String
"SUB", Char
'\SUB')
, (String
"ESC", Char
'\ESC')
, (String
"FS", Char
'\FS')
, (String
"GS", Char
'\GS')
, (String
"RS", Char
'\RS')
, (String
"US", Char
'\US')
, (String
"SP", Char
'\SP')
, (String
"DEL", Char
'\DEL')
]