@@ -12,11 +12,13 @@ module Parse.Primitives
1212 , Space(..), whitespace
1313 , docComment
1414 , string, character
15− , digit
15+ , digit, number
1616 )
1717 where
1818
1919import Prelude hiding (length)
20+import qualified Control.Applicative as Applicative ( Applicative(..), Alternative(..) )
21+import Control.Monad
2022import qualified Data.Char as Char
2123import qualified Data.Set as Set
2224import Data.Text (Text)
@@ -28,10 +30,10 @@ import qualified Data.Text.Lazy as LT
2830import qualified Data.Text.Lazy.Builder as LB
2931import Data.Text.Internal.Encoding.Utf16 (chr2)
3032import Data.Text.Internal.Unsafe.Char (unsafeChr)
31−import qualified Control.Applicative as Applicative ( Applicative(..), Alternative(..) )
32−import Control.Monad
33+import GHC.Word (Word16(..))
3334
3435import qualified AST.Helpers as Help (isSymbol)
36+import qualified AST.Literal as L
3537import qualified Reporting.Region as Region
3638
3739
@@ -774,13 +776,16 @@ eatHex array offset length n =
774776character :: Parser Char
775777character =
776778 Parser $ \(State array offset length indent row col) cok cerr _ eerr ->
777− if length <= 3 then
779+ if length == 0 then
778780 eerr (Error row col)
779781
780782 else
781783 if Text.unsafeIndex array offset /= 0x0027 {- ' -} then
782784 eerr (Error row col)
783785
786+ else if length < 3 then
787+ cerr (Error row col)
788+
784789 else
785790 case characterHelp array (offset + 1) (length - 1) row (col + 1) of
786791 Left err ->
@@ -826,7 +831,7 @@ characterHelp array offset length row col =
826831
827832
828833
829−-- NUMBERS
834+-- DIGITS
830835
831836
832837digit :: Parser Int
@@ -838,11 +843,228 @@ digit =
838843 else
839844 let
840845 !word = Text.unsafeIndex array offset
841− !isDigit = word <= 0x0039 {- 9 -} && word >= 0x0030 {- 0 -}
842846 in
843− if isDigit then
847+ if isDigit word then
844848 cok
845849 (fromIntegral (word - 0x0030))
846850 (State array (offset + 1) (length - 1) indent row (col + 1))
847851 else
848− eerr (Error row col)
852+ eerr (Error row col)
853+
854+
855+
856+-- NUMBERS
857+
858+
859+number :: Parser L.Literal
860+number =
861+ Parser $ \(State array offset length indent row col) cok cerr _ eerr ->
862+ if length == 0 then
863+ eerr (Error row col)
864+
865+ else
866+ let
867+ !word = Text.unsafeIndex array offset
868+ in
869+ if not (isDigit word) then
870+ eerr (Error row col)
871+
872+ else
873+ let
874+ chompResults =
875+ if word == 0x0030 {- 0 -} then
876+ chompZero array offset (offset + 1) (length - 1)
877+ else
878+ chompInt array offset (offset + 1) (length - 1)
879+ in
880+ case chompResults of
881+ Left err ->
882+ cerr err
883+
884+ Right (newOffset, newLength, literal) ->
885+ if isDirtyEnd array newOffset newLength then
886+ cerr (Error row col)
887+
888+ else
889+ cok literal (State array newOffset newLength indent row (col + (newOffset - offset)))
890+
891+
892+isDirtyEnd :: Text.Array -> Int -> Int -> Bool
893+isDirtyEnd array offset length =
894+ if length == 0 then
895+ False
896+
897+ else
898+ let
899+ !char = peekChar array offset
900+ in
901+ Char.isAlpha char || char == '_'
902+
903+
904+chompInt :: Text.Array -> Int -> Int -> Int -> Either Error (Int, Int, L.Literal)
905+chompInt array startOffset offset length =
906+ if length == 0 then
907+
908+ Right ( offset, length, readInt array startOffset offset )
909+
910+ else
911+
912+ let
913+ !word = Text.unsafeIndex array offset
914+ in
915+ if isDigit word then
916+ chompInt array startOffset (offset + 1) (length - 1)
917+
918+ else if word == 0x002E {- . -} then
919+ chompFraction array startOffset (offset + 1) (length - 1)
920+
921+ else
922+ Right ( offset, length, readInt array startOffset offset )
923+
924+
925+chompFraction :: Text.Array -> Int -> Int -> Int -> Either Error (Int, Int, L.Literal)
926+chompFraction array startOffset offset length =
927+ if length == 0 then
928+ Left (error "TODO chompFraction")
929+
930+ else if isDigit (Text.unsafeIndex array offset) then
931+ chompFractionHelp array startOffset (offset + 1) (length - 1)
932+
933+ else
934+ Left (error "TODO must be a number after a decimal point")
935+
936+
937+chompFractionHelp :: Text.Array -> Int -> Int -> Int -> Either Error (Int, Int, L.Literal)
938+chompFractionHelp array startOffset offset length =
939+ if length == 0 then
940+ Right (offset, length, readFloat array startOffset offset)
941+
942+ else
943+ let
944+ !word = Text.unsafeIndex array offset
945+ in
946+ if isDigit word then
947+ chompFractionHelp array startOffset (offset + 1) (length - 1)
948+
949+ else if word == 0x0065 {- e -} || word == 0x0045 {- E -} then
950+ chompExponent array startOffset (offset + 1) (length - 1)
951+
952+ else
953+ Right (offset, length, readFloat array startOffset offset)
954+
955+
956+chompExponent :: Text.Array -> Int -> Int -> Int -> Either Error (Int, Int, L.Literal)
957+chompExponent array startOffset offset length =
958+ if length == 0 then
959+ Right (offset, length, readFloat array startOffset offset)
960+
961+ else
962+ let
963+ !word = Text.unsafeIndex array offset
964+ in
965+ if isDigit word then
966+ chompExponentHelp array startOffset (offset + 1) (length - 1)
967+
968+ else if word == 0x002B {- + -} || word == 0x002D {- - -} then
969+
970+ if length > 1 && isDigit (Text.unsafeIndex array (offset + 1)) then
971+ chompExponentHelp array startOffset (offset + 2) (length - 2)
972+ else
973+ Left (error "TODO digits after +/-")
974+
975+ else
976+ Left (error "TODO exponent is messed up")
977+
978+
979+chompExponentHelp :: Text.Array -> Int -> Int -> Int -> Either Error (Int, Int, L.Literal)
980+chompExponentHelp array startOffset offset length =
981+ if length == 0 then
982+ Right (offset, length, readFloat array startOffset offset)
983+
984+ else if isDigit (Text.unsafeIndex array offset) then
985+ chompExponentHelp array startOffset (offset + 1) (length - 1)
986+
987+ else
988+ Right (offset, length, readFloat array startOffset offset)
989+
990+
991+chompZero :: Text.Array -> Int -> Int -> Int -> Either Error (Int, Int, L.Literal)
992+chompZero array startOffset offset length =
993+ if length == 0 then
994+ Right ( offset, length, L.IntNum 0 )
995+
996+ else
997+ let
998+ !word = Text.unsafeIndex array offset
999+ in
1000+ if word == 0x0078 {- x -} then
1001+ chompHex array startOffset (offset + 1) (length - 1)
1002+
1003+ else if word == 0x002E {- . -} then
1004+ chompFraction array startOffset (offset + 1) (length - 1)
1005+
1006+ else if isDigit word then
1007+ Left (error "TODO zero followed by more numbers")
1008+
1009+ else
1010+ Right ( offset, length, L.IntNum 0 )
1011+
1012+
1013+chompHex :: Text.Array -> Int -> Int -> Int -> Either Error (Int, Int, L.Literal)
1014+chompHex array startOffset offset length =
1015+ if length == 0 then
1016+ Left (error "TODO need hex digits after 0x")
1017+
1018+ else if isHex (Text.unsafeIndex array offset) then
1019+ chompHexHelp array startOffset (offset + 1) (length - 1)
1020+
1021+ else
1022+ Left (error "TODO need hex digits!")
1023+
1024+
1025+chompHexHelp :: Text.Array -> Int -> Int -> Int -> Either Error (Int, Int, L.Literal)
1026+chompHexHelp array startOffset offset length =
1027+ if length == 0 then
1028+ Right ( offset, length, readInt array startOffset offset )
1029+
1030+ else if isHex (Text.unsafeIndex array offset) then
1031+ chompHexHelp array startOffset (offset + 1) (length - 1)
1032+
1033+ else
1034+ Right ( offset, length, readInt array startOffset offset )
1035+
1036+
1037+
1038+-- NUMBER HELPERS
1039+
1040+
1041+{-# INLINE isDigit #-}
1042+isDigit :: Word16 -> Bool
1043+isDigit word =
1044+ word <= 0x0039 {- 9 -} && word >= 0x0030 {- 0 -}
1045+
1046+
1047+{-# INLINE isHex #-}
1048+isHex :: Word16 -> Bool
1049+isHex word =
1050+ word <= 0x0066 {- f -} && (word >= 0x0061 {- a -} || isDigit word || isCapHex word)
1051+
1052+
1053+{-# INLINE isCapHex #-}
1054+isCapHex :: Word16 -> Bool
1055+isCapHex word =
1056+ word <= 0x0046 {- F -} && word <= 0x0041 {- A -}
1057+
1058+
1059+{-# INLINE readInt #-}
1060+readInt :: Text.Array -> Int -> Int -> L.Literal
1061+readInt array startOffset endOffset =
1062+ L.IntNum $ read $ Text.unpack $
1063+ Text.Text array startOffset (endOffset - startOffset)
1064+
1065+
1066+{-# INLINE readFloat #-}
1067+readFloat :: Text.Array -> Int -> Int -> L.Literal
1068+readFloat array startOffset endOffset =
1069+ L.FloatNum $ read $ Text.unpack $
1070+ Text.Text array startOffset (endOffset - startOffset)