git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Tidying up: represent PureScript strings as sequence of Word16
45478c2Harry Garrood
examples/passing/RecordLabels.pursdeleted−5
@@ -1,5 +0,0 @@
1−module Main where
2−
3−import RecordLabels as RecordLabels
4−
5−main = RecordLabels.main
examples/passing/RecordLabels/RecordLabels.pursdeleted−25
@@ -1,25 +0,0 @@
1−module RecordLabels where
2−
3−import Prelude
4−import Data.Generic (class Generic, gShow)
5−import Control.Monad.Eff.Console (log)
6−import Test.Assert (assert')
7−
8−newtype AstralKeys = AstralKeys { "💡" :: Int, "💢" :: Int }
9−newtype LoneSurrogateKeys = LoneSurrogateKeys { "\xdf06" :: Int, "\xd834" :: Int }
10−
11−derive instance genericAstralKeys :: Generic AstralKeys
12−derive instance genericLoneSurrogateKeys :: Generic LoneSurrogateKeys
13−
14−loneSurrogateKeys =
15− gShow (LoneSurrogateKeys { "\xdf06": 0, "\xd834": 1 }) ==
16− """LoneSurrogateKeys { "\xdf06": 0, "\xd834": 1 }"""
17−
18−astralKeys =
19− gShow (AstralKeys { "💡": 0, "💢": 1 }) ==
20− """AstralKeys { "💡": 0, "💢": 1 }"""
21−
22−main = do
23− assert' "lone surrogate keys" loneSurrogateKeys
24− assert' "astral keys" astralKeys
25− log "Done"
examples/passing/StringEdgeCases.pursnew file+9
@@ -0,0 +1,9 @@
1+module Main where
2+
3+import Prelude
4+import Records as Records
5+import Symbols as Symbols
6+
7+main = do
8+ Records.main
9+ Symbols.main
examples/passing/StringEdgeCases/Records.pursnew file+66
@@ -0,0 +1,66 @@
1+module Records where
2+
3+import Prelude
4+import Data.Generic (class Generic, toSpine, GenericSpine(..))
5+import Control.Monad.Eff.Console (log)
6+import Test.Assert (assert')
7+
8+newtype AstralKeys = AstralKeys { "💡" :: Int, "💢" :: Int }
9+newtype LoneSurrogateKeys = LoneSurrogateKeys { "\xdf06" :: Int, "\xd834" :: Int }
10+
11+derive instance genericAstralKeys :: Generic AstralKeys
12+derive instance genericLoneSurrogateKeys :: Generic LoneSurrogateKeys
13+
14+spineOf :: forall a. Generic a => a -> Unit -> GenericSpine
15+spineOf x _ = toSpine x
16+
17+testLoneSurrogateKeys =
18+ let
19+ expected = 5
20+ actual = (_."\xd801" <<< helper) { "\xd800": 5 }
21+ in
22+ assert' ("lone surrogate keys: " <> show actual) (expected == actual)
23+
24+ where
25+ helper :: { "\xd800" :: Int } -> { "\xd801" :: Int }
26+ helper o =
27+ case o."\xd800" of
28+ x -> { "\xd801": x }
29+
30+testAstralKeys =
31+ let
32+ expected = 5
33+ actual = (_."💢" <<< helper) { "💡": 5 }
34+ in
35+ assert' ("astral keys: " <> show actual) (expected == actual)
36+
37+ where
38+ helper :: { "💡" :: Int } -> { "💢" :: Int }
39+ helper o =
40+ case o."💡" of
41+ x -> { "💢": x }
42+
43+testGenericLoneSurrogateKeys = do
44+ let expected = SProd "Records.LoneSurrogateKeys"
45+ [ \_ -> SRecord [ {recLabel: "\xd834", recValue: spineOf 1}
46+ , {recLabel: "\xdf06", recValue: spineOf 0}
47+ ]
48+ ]
49+ actual = toSpine (LoneSurrogateKeys { "\xdf06": 0, "\xd834": 1 })
50+ assert' ("generic lone surrogate keys: " <> show actual) (expected == actual)
51+
52+testGenericAstralKeys = do
53+ let expected = SProd "Records.AstralKeys"
54+ [ \_ -> SRecord [ {recLabel: "💡", recValue: spineOf 0}
55+ , {recLabel: "💢", recValue: spineOf 1}
56+ ]
57+ ]
58+ actual = toSpine (AstralKeys { "💡": 0, "💢": 1 })
59+ assert' ("generic astral keys: " <> show actual) (expected == actual)
60+
61+main = do
62+ testLoneSurrogateKeys
63+ testAstralKeys
64+ testGenericLoneSurrogateKeys
65+ testGenericAstralKeys
66+ log "Done"
examples/passing/StringEdgeCases/Symbols.pursnew file+30
@@ -0,0 +1,30 @@
1+-- This is similar to StringEscapes except we are performing the same tests
2+-- with Symbols (at the type level).
3+
4+module Symbols where
5+
6+import Prelude
7+import Control.Monad.Eff.Console (log)
8+import Type.Data.Symbol (SProxy(..), class AppendSymbol, appendSymbol, reflectSymbol)
9+import Test.Assert (assert')
10+
11+highS :: SProxy "\xd834"
12+highS = SProxy
13+
14+lowS :: SProxy "\xdf06"
15+lowS = SProxy
16+
17+loneSurrogates :: Boolean
18+loneSurrogates = reflectSymbol (appendSymbol highS lowS) == "\x1d306"
19+
20+outOfOrderSurrogates :: Boolean
21+outOfOrderSurrogates = reflectSymbol (appendSymbol lowS highS) == "\xdf06\xd834"
22+
23+notReplacing :: Boolean
24+notReplacing = reflectSymbol lowS /= "\xfffd"
25+
26+main = do
27+ assert' "lone surrogates may be combined into a surrogate pair" loneSurrogates
28+ assert' "lone surrogates may be combined out of order to remain lone surrogates" outOfOrderSurrogates
29+ assert' "lone surrogates are not replaced with the Unicode replacement character U+FFFD" notReplacing
30+ log "Done"
src/Language/PureScript/CodeGen/JS.hs+8−5
@@ -35,7 +35,7 @@ import Language.PureScript.Errors (ErrorMessageHint(..), SimpleErrorMessage(..),
35 errorMessage, rethrowWithPosition, addHint)
35 errorMessage, rethrowWithPosition, addHint)
36import Language.PureScript.Names
36import Language.PureScript.Names
37import Language.PureScript.Options
37import Language.PureScript.Options
38−import Language.PureScript.PSString (PSString, mkString, codePoints)
38+import Language.PureScript.PSString (PSString, mkString, decodeString)
39import Language.PureScript.Traversals (sndM)
39import Language.PureScript.Traversals (sndM)
40import qualified Language.PureScript.Constants as C
40import qualified Language.PureScript.Constants as C
41
41
@@ -183,8 +183,11 @@ moduleToJs (Module coms mn imps exps foreigns decls) foreign_ =
183
183
184 accessorString :: PSString -> JS -> JS
184 accessorString :: PSString -> JS -> JS
185 accessorString prop =
185 accessorString prop =
186− let quoted = JSIndexer Nothing (JSStringLiteral Nothing prop) in
186+ case decodeString prop of
187− either (const quoted) (\t -> if identNeedsEscaping t then quoted else JSAccessor Nothing prop) $ codePoints prop
187+ Just s | not (identNeedsEscaping s) ->
188+ JSAccessor Nothing s
189+ _ ->
190+ JSIndexer Nothing (JSStringLiteral Nothing prop)
188
191
189 -- |
192 -- |
190 -- Generate code in the simplified Javascript intermediate representation for a value or expression.
193 -- Generate code in the simplified Javascript intermediate representation for a value or expression.
@@ -259,7 +262,7 @@ moduleToJs (Module coms mn imps exps foreigns decls) foreign_ =
259 (JSUnary Nothing JSNew $ JSApp Nothing (JSVar Nothing (properToJs ctor)) []) ]
262 (JSUnary Nothing JSNew $ JSApp Nothing (JSVar Nothing (properToJs ctor)) []) ]
260 valueToJs' (Constructor _ _ (ProperName ctor) fields) =
263 valueToJs' (Constructor _ _ (ProperName ctor) fields) =
261 let constructor =
264 let constructor =
262− let body = [ JSAssignment Nothing (JSAccessor Nothing (mkString $ identToJs f) (JSVar Nothing "this")) (var f) | f <- fields ]
265+ let body = [ JSAssignment Nothing (JSAccessor Nothing (identToJs f) (JSVar Nothing "this")) (var f) | f <- fields ]
263 in JSFunction Nothing (Just (properToJs ctor)) (identToJs `map` fields) (JSBlock Nothing body)
266 in JSFunction Nothing (Just (properToJs ctor)) (identToJs `map` fields) (JSBlock Nothing body)
264 createFn =
267 createFn =
265 let body = JSUnary Nothing JSNew $ JSApp Nothing (JSVar Nothing (properToJs ctor)) (var `map` fields)
268 let body = JSUnary Nothing JSNew $ JSApp Nothing (JSVar Nothing (properToJs ctor)) (var `map` fields)
@@ -394,7 +397,7 @@ moduleToJs (Module coms mn imps exps foreigns decls) foreign_ =
394 argVar <- freshName
397 argVar <- freshName
395 done'' <- go remain done'
398 done'' <- go remain done'
396 js <- binderToJs argVar done'' binder
399 js <- binderToJs argVar done'' binder
397− return (JSVariableIntroduction Nothing argVar (Just (JSAccessor Nothing (mkString $ identToJs field) (JSVar Nothing varName))) : js)
400+ return (JSVariableIntroduction Nothing argVar (Just (JSAccessor Nothing (identToJs field) (JSVar Nothing varName))) : js)
398 binderToJs' _ _ ConstructorBinder{} =
401 binderToJs' _ _ ConstructorBinder{} =
399 internalError "binderToJs: Invalid ConstructorBinder in binderToJs"
402 internalError "binderToJs: Invalid ConstructorBinder in binderToJs"
400 binderToJs' varName done (NamedBinder _ ident binder) = do
403 binderToJs' varName done (NamedBinder _ ident binder) = do
src/Language/PureScript/CodeGen/JS/AST.hs+1−1
@@ -161,7 +161,7 @@ data JS
161 -- |
161 -- |
162 -- An object property accessor expression
162 -- An object property accessor expression
163 --
163 --
164− | JSAccessor (Maybe SourceSpan) PSString JS
164+ | JSAccessor (Maybe SourceSpan) Text JS
165 -- |
165 -- |
166 -- A function introduction (optional name, arguments, body)
166 -- A function introduction (optional name, arguments, body)
167 --
167 --
src/Language/PureScript/CodeGen/JS/Optimizer/Common.hs+2−2
@@ -72,13 +72,13 @@ removeFromBlock _ js = js
72
72
73isFn :: (Text, Text) -> JS -> Bool
73isFn :: (Text, Text) -> JS -> Bool
74isFn (moduleName, fnName) (JSAccessor _ x (JSVar _ y)) =
74isFn (moduleName, fnName) (JSAccessor _ x (JSVar _ y)) =
75− x == mkString fnName && y == moduleName
75+ x == fnName && y == moduleName
76isFn (moduleName, fnName) (JSIndexer _ (JSStringLiteral _ x) (JSVar _ y)) =
76isFn (moduleName, fnName) (JSIndexer _ (JSStringLiteral _ x) (JSVar _ y)) =
77 x == mkString fnName && y == moduleName
77 x == mkString fnName && y == moduleName
78isFn _ _ = False
78isFn _ _ = False
79
79
80isDict :: (Text, Text) -> JS -> Bool
80isDict :: (Text, Text) -> JS -> Bool
81−isDict (moduleName, dictName) (JSAccessor _ x (JSVar _ y)) = x == mkString dictName && y == moduleName
81+isDict (moduleName, dictName) (JSAccessor _ x (JSVar _ y)) = x == dictName && y == moduleName
82isDict _ _ = False
82isDict _ _ = False
83
83
84isDict' :: [(Text, Text)] -> JS -> Bool
84isDict' :: [(Text, Text)] -> JS -> Bool
src/Language/PureScript/CodeGen/JS/Optimizer/Inliner.hs+3−4
@@ -23,7 +23,6 @@ import qualified Data.Text as T
23import Language.PureScript.CodeGen.JS.AST
23import Language.PureScript.CodeGen.JS.AST
24import Language.PureScript.CodeGen.JS.Optimizer.Common
24import Language.PureScript.CodeGen.JS.Optimizer.Common
25import qualified Language.PureScript.Constants as C
25import qualified Language.PureScript.Constants as C
26−import Language.PureScript.PSString (mkString)
27
26
28-- TODO: Potential bug:
27-- TODO: Potential bug:
29-- Shouldn't just inline this case: { var x = 0; x.toFixed(10); }
28-- Shouldn't just inline this case: { var x = 0; x.toFixed(10); }
@@ -214,7 +213,7 @@ inlineCommonOperators = applyAll $
214
213
215 isNFn :: Text -> Int -> JS -> Bool
214 isNFn :: Text -> Int -> JS -> Bool
216 isNFn prefix n (JSVar _ name) = name == (prefix <> T.pack (show n))
215 isNFn prefix n (JSVar _ name) = name == (prefix <> T.pack (show n))
217− isNFn prefix n (JSAccessor _ name (JSVar _ dataFunctionUncurried)) | dataFunctionUncurried == C.dataFunctionUncurried = name == mkString (prefix <> T.pack (show n))
216+ isNFn prefix n (JSAccessor _ name (JSVar _ dataFunctionUncurried)) | dataFunctionUncurried == C.dataFunctionUncurried = name == (prefix <> T.pack (show n))
218 isNFn _ _ _ = False
217 isNFn _ _ _ = False
219
218
220 runFn :: Int -> JS -> JS
219 runFn :: Int -> JS -> JS
@@ -236,11 +235,11 @@ inlineCommonOperators = applyAll $
236 convert other = other
235 convert other = other
237
236
238 isModFn :: (Text, Text) -> JS -> Bool
237 isModFn :: (Text, Text) -> JS -> Bool
239− isModFn (m, op) (JSAccessor _ op' (JSVar _ m')) = m == m' && mkString op == op'
238+ isModFn (m, op) (JSAccessor _ op' (JSVar _ m')) = m == m' && op == op'
240 isModFn _ _ = False
239 isModFn _ _ = False
241
240
242 isModFnWithDict :: (Text, Text) -> JS -> Bool
241 isModFnWithDict :: (Text, Text) -> JS -> Bool
243− isModFnWithDict (m, op) (JSApp _ (JSAccessor _ op' (JSVar _ m')) [JSVar _ _]) = m == m' && mkString op == op'
242+ isModFnWithDict (m, op) (JSApp _ (JSAccessor _ op' (JSVar _ m')) [JSVar _ _]) = m == m' && op == op'
244 isModFnWithDict _ _ = False
243 isModFnWithDict _ _ = False
245
244
246-- (f <<< g $ x) = f (g x)
245-- (f <<< g $ x) = f (g x)
src/Language/PureScript/CodeGen/JS/Optimizer/MagicDo.hs+5−5
@@ -68,7 +68,7 @@ magicDo' = everywhereOnJS undo . everywhereOnJSTopDown convert
68 -- Check if an expression represents the polymorphic pure or return function
68 -- Check if an expression represents the polymorphic pure or return function
69 isPurePoly = isFn (C.controlApplicative, C.pure')
69 isPurePoly = isFn (C.controlApplicative, C.pure')
70 -- Check if an expression represents a function in the Eff module
70 -- Check if an expression represents a function in the Eff module
71− isEffFunc name (JSAccessor _ name' (JSVar _ eff)) = eff == C.eff && mkString name == name'
71+ isEffFunc name (JSAccessor _ name' (JSVar _ eff)) = eff == C.eff && name == name'
72 isEffFunc _ _ = False
72 isEffFunc _ _ = False
73
73
74 -- Remove __do function applications which remain after desugaring
74 -- Remove __do function applications which remain after desugaring
@@ -107,14 +107,14 @@ inlineST = everywhereOnJS convertBlock
107 convert agg (JSApp s1 f [arg]) | isSTFunc C.newSTRef f =
107 convert agg (JSApp s1 f [arg]) | isSTFunc C.newSTRef f =
108 JSFunction s1 Nothing [] (JSBlock s1 [JSReturn s1 $ if agg then arg else JSObjectLiteral s1 [(mkString C.stRefValue, arg)]])
108 JSFunction s1 Nothing [] (JSBlock s1 [JSReturn s1 $ if agg then arg else JSObjectLiteral s1 [(mkString C.stRefValue, arg)]])
109 convert agg (JSApp _ (JSApp s1 f [ref]) []) | isSTFunc C.readSTRef f =
109 convert agg (JSApp _ (JSApp s1 f [ref]) []) | isSTFunc C.readSTRef f =
110− if agg then ref else JSAccessor s1 (mkString C.stRefValue) ref
110+ if agg then ref else JSAccessor s1 C.stRefValue ref
111 convert agg (JSApp _ (JSApp _ (JSApp s1 f [ref]) [arg]) []) | isSTFunc C.writeSTRef f =
111 convert agg (JSApp _ (JSApp _ (JSApp s1 f [ref]) [arg]) []) | isSTFunc C.writeSTRef f =
112− if agg then JSAssignment s1 ref arg else JSAssignment s1 (JSAccessor s1 (mkString C.stRefValue) ref) arg
112+ if agg then JSAssignment s1 ref arg else JSAssignment s1 (JSAccessor s1 C.stRefValue ref) arg
113 convert agg (JSApp _ (JSApp _ (JSApp s1 f [ref]) [func]) []) | isSTFunc C.modifySTRef f =
113 convert agg (JSApp _ (JSApp _ (JSApp s1 f [ref]) [func]) []) | isSTFunc C.modifySTRef f =
114− if agg then JSAssignment s1 ref (JSApp s1 func [ref]) else JSAssignment s1 (JSAccessor s1 (mkString C.stRefValue) ref) (JSApp s1 func [JSAccessor s1 (mkString C.stRefValue) ref])
114+ if agg then JSAssignment s1 ref (JSApp s1 func [ref]) else JSAssignment s1 (JSAccessor s1 C.stRefValue ref) (JSApp s1 func [JSAccessor s1 C.stRefValue ref])
115 convert _ other = other
115 convert _ other = other
116 -- Check if an expression represents a function in the ST module
116 -- Check if an expression represents a function in the ST module
117− isSTFunc name (JSAccessor _ name' (JSVar _ st)) = st == C.st && mkString name == name'
117+ isSTFunc name (JSAccessor _ name' (JSVar _ st)) = st == C.st && name == name'
118 isSTFunc _ _ = False
118 isSTFunc _ _ = False
119 -- Find all ST Refs initialized in this block
119 -- Find all ST Refs initialized in this block
120 findSTRefsIn = everythingOnJS (++) isSTRef
120 findSTRefsIn = everythingOnJS (++) isSTRef
src/Language/PureScript/CoreFn/ToJSON.hs+2−2
@@ -17,7 +17,7 @@ import qualified Data.Text as T
17import Language.PureScript.AST.Literals
17import Language.PureScript.AST.Literals
18import Language.PureScript.CoreFn
18import Language.PureScript.CoreFn
19import Language.PureScript.Names
19import Language.PureScript.Names
20−import Language.PureScript.PSString (PSString, codePoints)
20+import Language.PureScript.PSString (PSString)
21
21
22literalToJSON :: (a -> Value) -> Literal a -> Value
22literalToJSON :: (a -> Value) -> Literal a -> Value
23literalToJSON _ (NumericLiteral (Left n)) = toJSON ("IntLiteral", n)
23literalToJSON _ (NumericLiteral (Left n)) = toJSON ("IntLiteral", n)
@@ -53,7 +53,7 @@ bindToJSON (NonRec _ n e) = object [ runIdent n .= exprToJSON e ]
53bindToJSON (Rec bs) = object $ map (\((_, n), e) -> runIdent n .= exprToJSON e) bs
53bindToJSON (Rec bs) = object $ map (\((_, n), e) -> runIdent n .= exprToJSON e) bs
54
54
55recordToJSON :: (a -> Value) -> [(PSString, a)] -> Value
55recordToJSON :: (a -> Value) -> [(PSString, a)] -> Value
56−recordToJSON f = object . map (\(key, a) -> either T.pack id (codePoints key) .= f a)
56+recordToJSON f = toJSON . map (\(key, a) -> (toJSON key, f a))
57
57
58exprToJSON :: Expr a -> Value
58exprToJSON :: Expr a -> Value
59exprToJSON (Var _ i) = toJSON ( "Var"
59exprToJSON (Var _ i) = toJSON ( "Var"
src/Language/PureScript/Docs/RenderedCode/Render.hs+5−4
@@ -26,8 +26,9 @@ import Language.PureScript.Environment
26import Language.PureScript.Kinds
26import Language.PureScript.Kinds
27import Language.PureScript.Names
27import Language.PureScript.Names
28import Language.PureScript.Pretty.Kinds
28import Language.PureScript.Pretty.Kinds
29+import Language.PureScript.Pretty.Types
29import Language.PureScript.Types
30import Language.PureScript.Types
30−import Language.PureScript.Label (Label(..), renderPSLabel)
31+import Language.PureScript.Label (Label)
31
32
32typeLiterals :: Pattern () Type RenderedCode
33typeLiterals :: Pattern () Type RenderedCode
33typeLiterals = mkPattern match
34typeLiterals = mkPattern match
@@ -86,7 +87,7 @@ renderHead = mintersperse (syntax "," <> sp) . map renderLabel
86renderLabel :: (Label, Type) -> RenderedCode
87renderLabel :: (Label, Type) -> RenderedCode
87renderLabel (label, ty) =
88renderLabel (label, ty) =
88 mintersperse sp
89 mintersperse sp
89− [ syntax $ renderPSLabel label
90+ [ syntax $ prettyPrintLabel label
90 , syntax "::"
91 , syntax "::"
91 , renderType ty
92 , renderType ty
92 ]
93 ]
@@ -126,9 +127,9 @@ explicitParens = mkPattern match
126 match _ = Nothing
127 match _ = Nothing
127
128
128matchTypeAtom :: Pattern () Type RenderedCode
129matchTypeAtom :: Pattern () Type RenderedCode
129−matchTypeAtom = typeLiterals <+> fmap parens matchType
130+matchTypeAtom = typeLiterals <+> fmap parens_ matchType
130 where
131 where
131− parens x = syntax "(" <> x <> syntax ")"
132+ parens_ x = syntax "(" <> x <> syntax ")"
132
133
133matchType :: Pattern () Type RenderedCode
134matchType :: Pattern () Type RenderedCode
134matchType = buildPrettyPrinter operators matchTypeAtom
135matchType = buildPrettyPrinter operators matchTypeAtom
src/Language/PureScript/Errors.hs+7−8
@@ -32,7 +32,6 @@ import Language.PureScript.Names
32import Language.PureScript.Pretty
32import Language.PureScript.Pretty
33import Language.PureScript.Traversals
33import Language.PureScript.Traversals
34import Language.PureScript.Types
34import Language.PureScript.Types
35−import Language.PureScript.PSString (renderPSString)
36import Language.PureScript.Label (Label(..))
35import Language.PureScript.Label (Label(..))
37import Language.PureScript.Pretty.Common (before, endWith)
36import Language.PureScript.Pretty.Common (before, endWith)
38import qualified Language.PureScript.Bundle as Bundle
37import qualified Language.PureScript.Bundle as Bundle
@@ -671,8 +670,8 @@ prettyPrintSingleError (PPEOptions codeColor full level showDocs) e = flip evalS
671 ]
670 ]
672 renderSimpleErrorMessage (CannotFindDerivingType nm) =
671 renderSimpleErrorMessage (CannotFindDerivingType nm) =
673 line $ "Cannot derive a type class instance, because the type declaration for " <> markCode (runProperName nm) <> " could not be found."
672 line $ "Cannot derive a type class instance, because the type declaration for " <> markCode (runProperName nm) <> " could not be found."
674− renderSimpleErrorMessage (DuplicateLabel (Label l) expr) =
673+ renderSimpleErrorMessage (DuplicateLabel l expr) =
675− paras $ [ line $ "Label " <> T.pack (show l) <> " appears more than once in a row type." ]
674+ paras $ [ line $ "Label " <> prettyPrintLabel l <> " appears more than once in a row type." ]
676 <> foldMap (\expr' -> [ line "Relevant expression: "
675 <> foldMap (\expr' -> [ line "Relevant expression: "
677 , markCodeBox $ indent $ prettyPrintValue valueDepth expr'
676 , markCodeBox $ indent $ prettyPrintValue valueDepth expr'
678 ]) expr
677 ]) expr
@@ -704,10 +703,10 @@ prettyPrintSingleError (PPEOptions codeColor full level showDocs) e = flip evalS
704 , line "does not have type"
703 , line "does not have type"
705 , markCodeBox $ indent $ typeAsBox ty
704 , markCodeBox $ indent $ typeAsBox ty
706 ]
705 ]
707− renderSimpleErrorMessage (PropertyIsMissing (Label prop)) =
706+ renderSimpleErrorMessage (PropertyIsMissing prop) =
708− line $ "Type of expression lacks required label " <> T.pack (show prop) <> "."
707+ line $ "Type of expression lacks required label " <> prettyPrintLabel prop <> "."
709− renderSimpleErrorMessage (AdditionalProperty (Label prop)) =
708+ renderSimpleErrorMessage (AdditionalProperty prop) =
710− line $ "Type of expression contains additional label " <> T.pack (show prop) <> "."
709+ line $ "Type of expression contains additional label " <> prettyPrintLabel prop <> "."
711 renderSimpleErrorMessage TypeSynonymInstance =
710 renderSimpleErrorMessage TypeSynonymInstance =
712 line "Type class instances for type synonyms are disallowed."
711 line "Type class instances for type synonyms are disallowed."
713 renderSimpleErrorMessage (OrphanInstance nm cnm ts) =
712 renderSimpleErrorMessage (OrphanInstance nm cnm ts) =
@@ -1264,7 +1263,7 @@ renderBox = unlines
1264 whiteSpace = all isSpace
1263 whiteSpace = all isSpace
1265
1264
1266toTypelevelString :: Type -> Maybe Box.Box
1265toTypelevelString :: Type -> Maybe Box.Box
1267−toTypelevelString (TypeLevelString s) = Just $ Box.text $ T.unpack $ renderPSString s
1266+toTypelevelString (TypeLevelString s) = Just $ Box.text $ T.unpack $ prettyPrintString s
1268toTypelevelString (TypeApp (TypeConstructor f) x)
1267toTypelevelString (TypeApp (TypeConstructor f) x)
1269 | f == primName "TypeString" = Just $ typeAsBox x
1268 | f == primName "TypeString" = Just $ typeAsBox x
1270toTypelevelString (TypeApp (TypeApp (TypeConstructor f) x) ret)
1269toTypelevelString (TypeApp (TypeApp (TypeConstructor f) x) ret)
src/Language/PureScript/Label.hs+3−17
@@ -1,30 +1,16 @@
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2−module Language.PureScript.Label (Label(..), renderPSLabel) where
2+module Language.PureScript.Label (Label(..)) where
3
3
4import Prelude.Compat hiding (lex)
4import Prelude.Compat hiding (lex)
5import Data.Monoid ()
5import Data.Monoid ()
6import Data.String (IsString(..))
6import Data.String (IsString(..))
7−import Data.Text (Text)
8−import Text.Parsec.Combinator (eof)
9import qualified Data.Aeson as A
7import qualified Data.Aeson as A
10
8
11−import Language.PureScript.Parser.Common (runTokenParser)
9+import Language.PureScript.PSString (PSString)
12−import Language.PureScript.Parser.Lexer (TokenParser, lname, lex)
13−import Language.PureScript.PSString (PSString, codePoints, renderPSString)
14
10
15-- |
11-- |
16-- Labels are used as record keys and row entry names. Labels newtype PSString
12-- Labels are used as record keys and row entry names. Labels newtype PSString
17-- because records are indexable by PureScript strings at runtime.
13-- because records are indexable by PureScript strings at runtime.
18--
14--
19−newtype Label = Label PSString
15+newtype Label = Label { runLabel :: PSString }
20 deriving (Show, Eq, Ord, IsString, Monoid, A.ToJSON, A.FromJSON)
16 deriving (Show, Eq, Ord, IsString, Monoid, A.ToJSON, A.FromJSON)
21−
22−renderPSLabel :: Label -> Text
23−renderPSLabel (Label s) =
24− let quoted = const $ renderPSString s in
25− either quoted (\t -> either quoted (const t) $ runParser lname t) $ codePoints s
26−
27−runParser :: TokenParser a -> Text -> Either String a
28−runParser p s = either (Left . show) Right $ do
29− ts <- lex "" s
30− runTokenParser "" (p <* eof) ts
src/Language/PureScript/PSString.hs+83−28
@@ -1,19 +1,35 @@
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
3−module Language.PureScript.PSString (PSString, mkString, renderPSString, renderJSON, toUTF16CodeUnits, codePoints, containsLoneSurrogates) where
3+module Language.PureScript.PSString
4+ ( PSString
5+ , toUTF16CodeUnits
6+ , decodeString
7+ , decodeStringEither
8+ , prettyPrintString
9+ , prettyPrintStringJS
10+ , mkString
11+ ) where
4
12
5import Prelude.Compat
13import Prelude.Compat
6−import Numeric (showHex)
14+import Control.Exception (try, evaluate)
15+import Data.Char (chr)
16+import Data.Bits (shiftR)
7import Data.List (unfoldr)
17import Data.List (unfoldr)
8import Data.Monoid ((<>))
18import Data.Monoid ((<>))
9import Data.Scientific (toBoundedInteger)
19import Data.Scientific (toBoundedInteger)
10import Data.String (IsString(..))
20import Data.String (IsString(..))
21+import Data.ByteString (ByteString)
22+import qualified Data.ByteString as BS
11import Data.Text (Text)
23import Data.Text (Text)
24+import qualified Data.Text as T
25+import Data.Text.Encoding (decodeUtf16BE)
26+import Data.Text.Encoding.Error (UnicodeException)
27+import qualified Data.Vector as V
12−import Data.Word (Word16)
28+import Data.Word (Word16, Word8)
29+import Numeric (showHex)
30+import System.IO.Unsafe (unsafePerformIO)
13import qualified Data.Aeson as A
31import qualified Data.Aeson as A
14import qualified Data.Aeson.Types as A
32import qualified Data.Aeson.Types as A
15−import qualified Data.Vector as V
16−import qualified Data.Text as T
17
33
18-- |
34-- |
19-- Strings in PureScript are sequences of UTF-16 code units, which do not
35-- Strings in PureScript are sequences of UTF-16 code units, which do not
@@ -21,36 +37,71 @@ import qualified Data.Text as T
21-- for a string to contain *lone surrogates,* i.e. characters in the range
37-- for a string to contain *lone surrogates,* i.e. characters in the range
22-- U+D800 to U+DFFF which do not appear as a part of a surrogate pair.
38-- U+D800 to U+DFFF which do not appear as a part of a surrogate pair.
23--
39--
40+-- The Show instance for PSString produces a string literal which would
41+-- represent the same data were it inserted into a PureScript source file.
24--
42--
25−newtype PSString = PSString [Word16]
43+newtype PSString = PSString { toUTF16CodeUnits :: [Word16] }
26 deriving (Eq, Ord, Monoid)
44 deriving (Eq, Ord, Monoid)
27
45
28instance Show PSString where
46instance Show PSString where
29− show = either show show . codePoints
47+ show = show . codePoints
30
48
31−renderPSString :: PSString -> Text
49+-- Decode a PSString to a String, representing any lone surrogates as the
32−renderPSString = T.pack . either show show . codePoints
50+-- reserved code point with that index. Warning: if there are any lone
51+-- surrogates, converting the result to Text via Data.Text.pack will result in
52+-- loss of information as those lone surrogates will be replaced with U+FFFD
53+-- REPLACEMENT CHARACTER. Because this function requires care to use correctly,
54+-- we do not export it.
55+codePoints :: PSString -> String
56+codePoints = map (either (chr . fromIntegral) id) . decodeStringEither
33
57
58+-- |
34−-- NOTE: lone surrogates in the given PSString are represented in the resulting
59+-- Decode a PSString as UTF-16. Lone surrogates in the input are represented in
35−-- String as the reserved code point with that index
60+-- the output with the Left constructor; characters which were successfully
36−codePoints :: PSString -> Either String Text
61+-- decoded are represented with the Right constructor.
62+--
63+decodeStringEither :: PSString -> [Either Word16 Char]
37−codePoints s = (if containsLoneSurrogates s then Left . unfoldr decode else Right . T.unfoldr decode) $ toUTF16CodeUnits s
64+decodeStringEither = unfoldr decode . toUTF16CodeUnits
38 where
65 where
39− decode :: [Word16] -> Maybe (Char, [Word16])
66+ decode :: [Word16] -> Maybe (Either Word16 Char, [Word16])
40− decode (h:l:rest) | isLead h && isTrail l = Just (unsurrogate h l, rest)
67+ decode (h:l:rest) | isLead h && isTrail l = Just (Right (unsurrogate h l), rest)
68+ decode (c:rest) | isSurrogate c = Just (Left c, rest)
41− decode (c:rest) = Just (toChar c, rest)
69+ decode (c:rest) = Just (Right (toChar c), rest)
42 decode [] = Nothing
70 decode [] = Nothing
43
71
44 unsurrogate :: Word16 -> Word16 -> Char
72 unsurrogate :: Word16 -> Word16 -> Char
45 unsurrogate h l = toEnum ((toInt h - 0xD800) * 0x400 + (toInt l - 0xDC00) + 0x10000)
73 unsurrogate h l = toEnum ((toInt h - 0xD800) * 0x400 + (toInt l - 0xDC00) + 0x10000)
46
74
47−containsLoneSurrogates :: PSString -> Bool
75+-- |
48−containsLoneSurrogates = or . unfoldr headIsLoneSurrogate . toUTF16CodeUnits
76+-- Pretty print a PSString, using Haskell/PureScript escape sequences.
77+-- This is identical to the Show instance except that we get a Text out instead
78+-- of a String.
79+--
80+prettyPrintString :: PSString -> Text
81+prettyPrintString = T.pack . show
82+
83+-- |
84+-- Attempt to decode a PSString as UTF-16 text. This will fail (returning
85+-- Nothing) if the argument contains lone surrogates.
86+--
87+decodeString :: PSString -> Maybe Text
88+decodeString = hush . decodeEither . BS.pack . concatMap unpair . toUTF16CodeUnits
49 where
89 where
50− headIsLoneSurrogate :: [Word16] -> Maybe (Bool, [Word16])
90+ unpair w = [highByte w, lowByte w]
51− headIsLoneSurrogate (h:l:rest) | isLead h && isTrail l = Just (False, rest)
91+
52− headIsLoneSurrogate (c:rest) = Just (isLead c || isTrail c, rest)
92+ lowByte :: Word16 -> Word8
53− headIsLoneSurrogate [] = Nothing
93+ lowByte = fromIntegral
94+
95+ highByte :: Word16 -> Word8
96+ highByte = fromIntegral . (`shiftR` 8)
97+
98+ -- Based on a similar function from Data.Text.Encoding for utf8. This is a
99+ -- safe usage of unsafePerformIO because there are no side effects after
100+ -- handling any thrown UnicodeExceptions.
101+ decodeEither :: ByteString -> Either UnicodeException Text
102+ decodeEither = unsafePerformIO . try . evaluate . decodeUtf16BE
103+
104+ hush = either (const Nothing) Just
54
105
55instance IsString PSString where
106instance IsString PSString where
56 fromString a = PSString $ concatMap encodeUTF16 a
107 fromString a = PSString $ concatMap encodeUTF16 a
@@ -71,12 +122,16 @@ instance A.FromJSON PSString where
71 parseJSON a = PSString <$> parseArrayOfCodeUnits a
122 parseJSON a = PSString <$> parseArrayOfCodeUnits a
72 where
123 where
73 parseArrayOfCodeUnits :: A.Value -> A.Parser [Word16]
124 parseArrayOfCodeUnits :: A.Value -> A.Parser [Word16]
74− parseArrayOfCodeUnits = A.withArray "array of UTF-16 code units" $ \b -> sequence (parseCodeUnit <$> V.toList b)
125+ parseArrayOfCodeUnits = A.withArray "array of UTF-16 code units" (traverse parseCodeUnit . V.toList)
75 parseCodeUnit :: A.Value -> A.Parser Word16
126 parseCodeUnit :: A.Value -> A.Parser Word16
76 parseCodeUnit b = A.withScientific "two-byte non-negative integer" (maybe (A.typeMismatch "" b) return . toBoundedInteger) b
127 parseCodeUnit b = A.withScientific "two-byte non-negative integer" (maybe (A.typeMismatch "" b) return . toBoundedInteger) b
77
128
129+-- |
130+-- Pretty print a PSString, using JavaScript escape sequences. Intended for
131+-- use in compiled JS output.
132+--
78−renderJSON :: PSString -> Text
133+prettyPrintStringJS :: PSString -> Text
79−renderJSON s = "\"" <> foldMap encodeChar (toUTF16CodeUnits s) <> "\""
134+prettyPrintStringJS s = "\"" <> foldMap encodeChar (toUTF16CodeUnits s) <> "\""
80 where
135 where
81 encodeChar :: Word16 -> Text
136 encodeChar :: Word16 -> Text
82 encodeChar c | c > 0xFF = "\\u" <> hex 4 c
137 encodeChar c | c > 0xFF = "\\u" <> hex 4 c
@@ -102,6 +157,9 @@ isLead h = h >= 0xD800 && h <= 0xDBFF
102isTrail :: Word16 -> Bool
157isTrail :: Word16 -> Bool
103isTrail l = l >= 0xDC00 && l <= 0xDFFF
158isTrail l = l >= 0xDC00 && l <= 0xDFFF
104
159
160+isSurrogate :: Word16 -> Bool
161+isSurrogate c = isLead c || isTrail c
162+
105toChar :: Word16 -> Char
163toChar :: Word16 -> Char
106toChar = toEnum . fromIntegral
164toChar = toEnum . fromIntegral
107
165
@@ -113,6 +171,3 @@ toInt = fromIntegral
113
171
114mkString :: Text -> PSString
172mkString :: Text -> PSString
115mkString = fromString . T.unpack
173mkString = fromString . T.unpack
116−
117−toUTF16CodeUnits :: PSString -> [Word16]
118−toUTF16CodeUnits (PSString s) = s
src/Language/PureScript/Pretty.hs+1
@@ -15,3 +15,4 @@ import Language.PureScript.Pretty.JS as P
15import Language.PureScript.Pretty.Kinds as P
15import Language.PureScript.Pretty.Kinds as P
16import Language.PureScript.Pretty.Types as P
16import Language.PureScript.Pretty.Types as P
17import Language.PureScript.Pretty.Values as P
17import Language.PureScript.Pretty.Values as P
18+import Language.PureScript.PSString as P (prettyPrintString)
src/Language/PureScript/Pretty/Common.hs+4−10
@@ -15,8 +15,7 @@ import Data.Text (Text)
15import qualified Data.Text as T
15import qualified Data.Text as T
16
16
17import Language.PureScript.AST (SourcePos(..), SourceSpan(..))
17import Language.PureScript.AST (SourcePos(..), SourceSpan(..))
18−import Language.PureScript.PSString (PSString)
18+import Language.PureScript.Parser.Lexer (isUnquotedKey, reservedPsNames)
19−import Language.PureScript.Label (Label(..), renderPSLabel)
20
19
21import Text.PrettyPrint.Boxes hiding ((<>))
20import Text.PrettyPrint.Boxes hiding ((<>))
22import qualified Text.PrettyPrint.Boxes as Box
21import qualified Text.PrettyPrint.Boxes as Box
@@ -147,14 +146,9 @@ prettyPrintMany f xs = do
147 indentString <- currentIndent
146 indentString <- currentIndent
148 return $ intercalate (emit "\n") $ map (mappend indentString) ss
147 return $ intercalate (emit "\n") $ map (mappend indentString) ss
149
148
150−-- |
149+objectKeyRequiresQuoting :: Text -> Bool
151−-- Prints an object key, escaping reserved names.
150+objectKeyRequiresQuoting s =
152−--
151+ s `elem` reservedPsNames || isUnquotedKey s
153−prettyPrintObjectKey :: PSString -> Text
154−prettyPrintObjectKey = prettyPrintLabel . Label
155−
156−prettyPrintLabel :: Label -> Text
157−prettyPrintLabel = renderPSLabel
158
152
159-- | Place a box before another, vertically when the first box takes up multiple lines.
153-- | Place a box before another, vertically when the first box takes up multiple lines.
160before :: Box -> Box -> Box
154before :: Box -> Box -> Box
src/Language/PureScript/Pretty/JS.hs+8−5
@@ -25,7 +25,7 @@ import Language.PureScript.CodeGen.JS.Common
25import Language.PureScript.Comments
25import Language.PureScript.Comments
26import Language.PureScript.Crash
26import Language.PureScript.Crash
27import Language.PureScript.Pretty.Common
27import Language.PureScript.Pretty.Common
28−import Language.PureScript.PSString (PSString, codePoints, renderJSON)
28+import Language.PureScript.PSString (PSString, decodeString, prettyPrintStringJS)
29
29
30-- TODO (Christoph): Get rid of T.unpack / pack
30-- TODO (Christoph): Get rid of T.unpack / pack
31
31
@@ -37,7 +37,7 @@ literals = mkPattern' match'
37
37
38 match :: (Emit gen) => JS -> StateT PrinterState Maybe gen
38 match :: (Emit gen) => JS -> StateT PrinterState Maybe gen
39 match (JSNumericLiteral _ n) = return $ emit $ T.pack $ either show show n
39 match (JSNumericLiteral _ n) = return $ emit $ T.pack $ either show show n
40− match (JSStringLiteral _ s) = return $ emit $ renderJSON s
40+ match (JSStringLiteral _ s) = return $ emit $ prettyPrintStringJS s
41 match (JSBooleanLiteral _ True) = return $ emit "true"
41 match (JSBooleanLiteral _ True) = return $ emit "true"
42 match (JSBooleanLiteral _ False) = return $ emit "false"
42 match (JSBooleanLiteral _ False) = return $ emit "false"
43 match (JSArrayLiteral _ xs) = mconcat <$> sequence
43 match (JSArrayLiteral _ xs) = mconcat <$> sequence
@@ -59,8 +59,11 @@ literals = mkPattern' match'
59 where
59 where
60 objectPropertyToString :: (Emit gen) => PSString -> gen
60 objectPropertyToString :: (Emit gen) => PSString -> gen
61 objectPropertyToString s =
61 objectPropertyToString s =
62− let quoted = renderJSON s in
62+ emit $ case decodeString s of
63− emit $ either (const quoted) (\t -> if identNeedsEscaping t then quoted else t) $ codePoints s
63+ Just s' | not (identNeedsEscaping s') ->
64+ s'
65+ _ ->
66+ prettyPrintStringJS s
64 match (JSBlock _ sts) = mconcat <$> sequence
67 match (JSBlock _ sts) = mconcat <$> sequence
65 [ return $ emit "{\n"
68 [ return $ emit "{\n"
66 , withIndent $ prettyStatements sts
69 , withIndent $ prettyStatements sts
@@ -160,7 +163,7 @@ accessor :: (Emit gen) => Pattern PrinterState JS (gen, JS)
160accessor = mkPattern match
163accessor = mkPattern match
161 where
164 where
162 -- WARN: if `prop` does not match the `IdentifierName` grammar, this will generate invalid code; see #2513
165 -- WARN: if `prop` does not match the `IdentifierName` grammar, this will generate invalid code; see #2513
163− match (JSAccessor _ prop val) = either (const Nothing) (\t -> Just (emit t, val)) $ codePoints prop
166+ match (JSAccessor _ prop val) = Just (emit prop, val)
164 match _ = Nothing
167 match _ = Nothing
165
168
166indexer :: (Emit gen) => Pattern PrinterState JS (gen, JS)
169indexer :: (Emit gen) => Pattern PrinterState JS (gen, JS)
src/Language/PureScript/Pretty/Types.hs+17−3
@@ -10,6 +10,8 @@ module Language.PureScript.Pretty.Types
10 , prettyPrintTypeAtom
10 , prettyPrintTypeAtom
11 , prettyPrintRowWith
11 , prettyPrintRowWith
12 , prettyPrintRow
12 , prettyPrintRow
13+ , prettyPrintLabel
14+ , prettyPrintObjectKey
13 ) where
15 ) where
14
16
15import Prelude.Compat
17import Prelude.Compat
@@ -18,6 +20,7 @@ import Control.Arrow ((<+>))
18import Control.PatternArrows as PA
20import Control.PatternArrows as PA
19
21
20import Data.Maybe (fromMaybe)
22import Data.Maybe (fromMaybe)
23+import Data.Text (Text)
21import qualified Data.Text as T
24import qualified Data.Text as T
22
25
23import Language.PureScript.Crash
26import Language.PureScript.Crash
@@ -27,8 +30,8 @@ import Language.PureScript.Names
27import Language.PureScript.Pretty.Common
30import Language.PureScript.Pretty.Common
28import Language.PureScript.Pretty.Kinds
31import Language.PureScript.Pretty.Kinds
29import Language.PureScript.Types
32import Language.PureScript.Types
30−import Language.PureScript.PSString (renderPSString)
33+import Language.PureScript.PSString (PSString, prettyPrintString, decodeString)
31−import Language.PureScript.Label (Label)
34+import Language.PureScript.Label (Label(..))
32
35
33import Text.PrettyPrint.Boxes hiding ((<+>))
36import Text.PrettyPrint.Boxes hiding ((<+>))
34
37
@@ -118,7 +121,7 @@ matchTypeAtom suggesting =
118 typeLiterals = mkPattern match where
121 typeLiterals = mkPattern match where
119 match TypeWildcard{} = Just $ text "_"
122 match TypeWildcard{} = Just $ text "_"
120 match (TypeVar var) = Just $ text $ T.unpack var
123 match (TypeVar var) = Just $ text $ T.unpack var
121− match (TypeLevelString s) = Just . text $ T.unpack $ renderPSString s
124+ match (TypeLevelString s) = Just $ text $ T.unpack $ prettyPrintString s
122 match (PrettyPrintObject row) = Just $ prettyPrintRowWith '{' '}' row
125 match (PrettyPrintObject row) = Just $ prettyPrintRowWith '{' '}' row
123 match (TypeConstructor ctor) = Just $ text $ T.unpack $ runProperName $ disqualify ctor
126 match (TypeConstructor ctor) = Just $ text $ T.unpack $ runProperName $ disqualify ctor
124 match (TUnknown u)
127 match (TUnknown u)
@@ -188,3 +191,14 @@ prettyPrintType = render . typeAsBoxImpl False
188-- | Generate a pretty-printed string representing a suggested 'Type'
191-- | Generate a pretty-printed string representing a suggested 'Type'
189prettyPrintSuggestedType :: Type -> String
192prettyPrintSuggestedType :: Type -> String
190prettyPrintSuggestedType = render . typeAsBoxImpl True
193prettyPrintSuggestedType = render . typeAsBoxImpl True
194+
195+prettyPrintLabel :: Label -> Text
196+prettyPrintLabel (Label s) =
197+ case decodeString s of
198+ Just s' | not (objectKeyRequiresQuoting s') ->
199+ s'
200+ _ ->
201+ prettyPrintString s
202+
203+prettyPrintObjectKey :: PSString -> Text
204+prettyPrintObjectKey = prettyPrintLabel . Label
src/Language/PureScript/Pretty/Values.hs+4−4
@@ -20,9 +20,9 @@ import Language.PureScript.AST
20import Language.PureScript.Crash
20import Language.PureScript.Crash
21import Language.PureScript.Names
21import Language.PureScript.Names
22import Language.PureScript.Pretty.Common
22import Language.PureScript.Pretty.Common
23−import Language.PureScript.Pretty.Types (typeAsBox, typeAtomAsBox)
23+import Language.PureScript.Pretty.Types (typeAsBox, typeAtomAsBox, prettyPrintObjectKey)
24import Language.PureScript.Types (Constraint(..))
24import Language.PureScript.Types (Constraint(..))
25−import Language.PureScript.PSString (PSString, renderPSString)
25+import Language.PureScript.PSString (PSString, prettyPrintString)
26
26
27import Text.PrettyPrint.Boxes
27import Text.PrettyPrint.Boxes
28
28
@@ -106,7 +106,7 @@ prettyPrintValueAtom d expr = (text "(" <> prettyPrintValue d expr) `before` tex
106
106
107prettyPrintLiteralValue :: Int -> Literal Expr -> Box
107prettyPrintLiteralValue :: Int -> Literal Expr -> Box
108prettyPrintLiteralValue _ (NumericLiteral n) = text $ either show show n
108prettyPrintLiteralValue _ (NumericLiteral n) = text $ either show show n
109−prettyPrintLiteralValue _ (StringLiteral s) = text $ T.unpack $ renderPSString s
109+prettyPrintLiteralValue _ (StringLiteral s) = text $ T.unpack $ prettyPrintString s
110prettyPrintLiteralValue _ (CharLiteral c) = text $ show c
110prettyPrintLiteralValue _ (CharLiteral c) = text $ show c
111prettyPrintLiteralValue _ (BooleanLiteral True) = text "true"
111prettyPrintLiteralValue _ (BooleanLiteral True) = text "true"
112prettyPrintLiteralValue _ (BooleanLiteral False) = text "false"
112prettyPrintLiteralValue _ (BooleanLiteral False) = text "false"
@@ -170,7 +170,7 @@ prettyPrintBinderAtom (BinaryNoParensBinder op b1 b2) =
170prettyPrintBinderAtom (ParensInBinder b) = parensT (prettyPrintBinder b)
170prettyPrintBinderAtom (ParensInBinder b) = parensT (prettyPrintBinder b)
171
171
172prettyPrintLiteralBinder :: Literal Binder -> Text
172prettyPrintLiteralBinder :: Literal Binder -> Text
173−prettyPrintLiteralBinder (StringLiteral str) = renderPSString str
173+prettyPrintLiteralBinder (StringLiteral str) = prettyPrintString str
174prettyPrintLiteralBinder (CharLiteral c) = T.pack (show c)
174prettyPrintLiteralBinder (CharLiteral c) = T.pack (show c)
175prettyPrintLiteralBinder (NumericLiteral num) = either (T.pack . show) (T.pack . show) num
175prettyPrintLiteralBinder (NumericLiteral num) = either (T.pack . show) (T.pack . show) num
176prettyPrintLiteralBinder (BooleanLiteral True) = "true"
176prettyPrintLiteralBinder (BooleanLiteral True) = "true"
src/Language/PureScript/Sugar/TypeClasses/Deriving.hs+12−3
@@ -11,6 +11,7 @@ import Control.Monad.Error.Class (MonadError(..))
11import Control.Monad.Supply.Class (MonadSupply)
11import Control.Monad.Supply.Class (MonadSupply)
12import Data.List (foldl', find, sortBy, unzip5)
12import Data.List (foldl', find, sortBy, unzip5)
13import qualified Data.Map as M
13import qualified Data.Map as M
14+import Data.Monoid ((<>))
14import Data.Maybe (fromMaybe, mapMaybe)
15import Data.Maybe (fromMaybe, mapMaybe)
15import Data.Ord (comparing)
16import Data.Ord (comparing)
16import Data.Text (Text)
17import Data.Text (Text)
@@ -24,7 +25,7 @@ import Language.PureScript.Externs
24import Language.PureScript.Kinds
25import Language.PureScript.Kinds
25import Language.PureScript.Names
26import Language.PureScript.Names
26import Language.PureScript.Label (Label(..))
27import Language.PureScript.Label (Label(..))
27−import Language.PureScript.PSString (mkString, codePoints)
28+import Language.PureScript.PSString (mkString, decodeStringEither)
28import Language.PureScript.Types
29import Language.PureScript.Types
29import Language.PureScript.TypeChecker (checkNewtype)
30import Language.PureScript.TypeChecker (checkNewtype)
30import Language.PureScript.TypeChecker.Synonyms (SynonymMap, replaceAllTypeSynonymsM)
31import Language.PureScript.TypeChecker.Synonyms (SynonymMap, replaceAllTypeSynonymsM)
@@ -433,7 +434,7 @@ deriveGenericRep mn syns ds tyConNm tyConArgs repTy = do
433 makeArg :: Type -> m (Type, Binder, Expr, Binder, Expr)
434 makeArg :: Type -> m (Type, Binder, Expr, Binder, Expr)
434 makeArg arg | Just rec <- objectType arg = do
435 makeArg arg | Just rec <- objectType arg = do
435 let fields = decomposeRec rec
436 let fields = decomposeRec rec
436− fieldNames <- traverse freshIdent (map ((\(Label s) -> either T.pack id $ codePoints s) . fst) fields)
437+ fieldNames <- traverse freshIdent (map (runIdent . labelToIdent . fst) fields)
437 pure ( TypeApp (TypeConstructor record)
438 pure ( TypeApp (TypeConstructor record)
438 (foldr1 (\f -> TypeApp (TypeApp (TypeConstructor productName) f))
439 (foldr1 (\f -> TypeApp (TypeApp (TypeConstructor productName) f))
439 (map (\((Label name), ty) ->
440 (map (\((Label name), ty) ->
@@ -736,8 +737,16 @@ mkVarMn mn = Var . Qualified mn
736mkVar :: Ident -> Expr
737mkVar :: Ident -> Expr
737mkVar = mkVarMn Nothing
738mkVar = mkVarMn Nothing
738
739
740+-- This function may seem a little obtuse, but it's only this way to ensure
741+-- that it is injective. Injectivity is important here; without it, we can end
742+-- up with accidental variable shadowing in the generated code.
739labelToIdent :: Label -> Ident
743labelToIdent :: Label -> Ident
740−labelToIdent (Label l) = Ident $ either T.pack id $ codePoints l
744+labelToIdent =
745+ Ident . foldMap (either loneSurrogate char) . decodeStringEither . runLabel
746+ where
747+ char '_' = "__"
748+ char c = T.singleton c
749+ loneSurrogate x = "_" <> T.pack (show x) <> "_"
741
750
742objectType :: Type -> Maybe Type
751objectType :: Type -> Maybe Type
743objectType (TypeApp (TypeConstructor (Qualified (Just (ModuleName [ProperName "Prim"])) (ProperName "Record"))) rec) = Just rec
752objectType (TypeApp (TypeConstructor (Qualified (Just (ModuleName [ProperName "Prim"])) (ProperName "Record"))) rec) = Just rec
src/Language/PureScript/Types.hs+1−1
@@ -2,7 +2,7 @@
2{-# LANGUAGE TemplateHaskell #-}
2{-# LANGUAGE TemplateHaskell #-}
3
3
4-- |
4-- |
5−-- Data types for PureScript types and compile-time representation of PureScript terms
5+-- Data types for types
6--
6--
7module Language.PureScript.Types where
7module Language.PureScript.Types where
8
8