Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented overloadable string literals using an in-scope fromString. #321

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Fay/Compiler.hs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ compileModuleFromAST (Module _ modulename _pragmas Nothing _exports imports decl
imported <- fmap concat (mapM compileImport imports)
modify $ \s -> s { stateModuleName = modulename
, stateModuleScope = fromMaybe (error $ "Could not find stateModuleScope for " ++ show modulename) $ M.lookup modulename $ stateModuleScopes s
, stateUseFromString = useFromString _pragmas
}
current <- compileDecls True decls

Expand All @@ -175,6 +176,12 @@ compileModuleFromAST (Module _ modulename _pragmas Nothing _exports imports decl
else stmts
compileModuleFromAST mod = throwError (UnsupportedModuleSyntax mod)

useFromString :: [ModulePragma] -> Bool
useFromString pragmas = any (hasPragma "OverloadedStrings") pragmas
&& any (hasPragma "RebindableSyntax") pragmas
where hasPragma p (LanguagePragma _ q) | p `elem` q = True
hasPragma _ _ = False

instance CompilesTo Module [JsStmt] where compileTo = compileModuleFromAST


Expand Down
1 change: 1 addition & 0 deletions src/Fay/Compiler/Defaults.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@ defaultCompileState = do
, stateModuleScope = def
, stateModuleScopes = M.empty
, stateJsModulePaths = S.empty
, stateUseFromString = False
}
7 changes: 5 additions & 2 deletions src/Fay/Compiler/Exp.hs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,11 @@ compileLit lit =
Frac rational -> return (JsLit (JsFloating (fromRational rational)))
-- TODO: Use real JS strings instead of array, probably it will
-- lead to the same result.
String string -> return (JsApp (JsName (JsBuiltIn "list"))
[JsLit (JsStr string)])
String string -> do
fromString <- gets stateUseFromString
if fromString
then return (JsLit (JsStr string))
else return (JsApp (JsName (JsBuiltIn "list")) [JsLit (JsStr string)])
lit -> throwError (UnsupportedLiteral lit)

-- | Compile simple application.
Expand Down
4 changes: 3 additions & 1 deletion src/Fay/FFI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module Fay.FFI
,ffi)
where

import Data.String (IsString)
import Fay.Types
import Prelude (Bool, Char, Double, Int, Maybe, String, error)

Expand Down Expand Up @@ -44,6 +45,7 @@ type Ptr a = a
type Automatic a = a

-- | Declare a foreign action.
ffi :: String -- ^ The foreign value.
ffi :: IsString s
=> s -- ^ The foreign value.
-> a -- ^ Bottom.
ffi = error "Fay.FFI.ffi: Used foreign function outside a JS engine context."
21 changes: 11 additions & 10 deletions src/Fay/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,18 @@ mkModulePathFromQName _ = error "mkModulePathFromQName: Not a qualified name"

-- | State of the compiler.
data CompileState = CompileState
{ _stateExports :: Map ModuleName (Set QName) -- ^ Collects exports from modules
, stateRecordTypes :: [(QName,[QName])] -- ^ Map types to constructors
, stateRecords :: [(QName,[QName])] -- ^ Map constructors to fields
, stateNewtypes :: [(QName, Maybe QName, Type)] -- ^ Newtype constructor, destructor, wrapped type tuple
, stateImported :: [(ModuleName,FilePath)] -- ^ Map of all imported modules and their source locations.
, stateNameDepth :: Integer -- ^ Depth of the current lexical scope.
, stateLocalScope :: Set Name -- ^ Names in the current lexical scope.
, stateModuleScope :: ModuleScope -- ^ Names in the module scope.
, stateModuleScopes :: Map ModuleName ModuleScope
, stateModuleName :: ModuleName -- ^ Name of the module currently being compiled.
{ _stateExports :: Map ModuleName (Set QName) -- ^ Collects exports from modules
, stateRecordTypes :: [(QName,[QName])] -- ^ Map types to constructors
, stateRecords :: [(QName,[QName])] -- ^ Map constructors to fields
, stateNewtypes :: [(QName, Maybe QName, Type)] -- ^ Newtype constructor, destructor, wrapped type tuple
, stateImported :: [(ModuleName,FilePath)] -- ^ Map of all imported modules and their source locations.
, stateNameDepth :: Integer -- ^ Depth of the current lexical scope.
, stateLocalScope :: Set Name -- ^ Names in the current lexical scope.
, stateModuleScope :: ModuleScope -- ^ Names in the module scope.
, stateModuleScopes :: Map ModuleName ModuleScope
, stateModuleName :: ModuleName -- ^ Name of the module currently being compiled.
, stateJsModulePaths :: Set ModulePath
, stateUseFromString :: Bool
} deriving (Show)

-- | Things written out by the compiler.
Expand Down
15 changes: 15 additions & 0 deletions tests/FromString.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{-# LANGUAGE OverloadedStrings, RebindableSyntax #-}
module FromString where

import Prelude
import FromString.FayText
import FromString.Dep (myString, depTest)

main :: Fay ()
main = do
print ("This is not a String" :: Text)
print "This is not a String"
putStrLn myString
print myString
depTest

5 changes: 5 additions & 0 deletions tests/FromString.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
This is not a String
This is not a String
test
[ 't', 'e', 's', 't' ]
This is also not a String
11 changes: 11 additions & 0 deletions tests/FromString/Dep.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module FromString.Dep where

import Prelude
import FromString.DepDep (myText)

myString :: String
myString = "test"

depTest :: Fay ()
depTest = print myText

Empty file added tests/FromString/Dep.res
Empty file.
9 changes: 9 additions & 0 deletions tests/FromString/DepDep.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{-# LANGUAGE OverloadedStrings, RebindableSyntax #-}
module FromString.DepDep where

import Prelude
import FromString.FayText

myText :: Text
myText = "This is also not a String"

Empty file added tests/FromString/DepDep.res
Empty file.
43 changes: 43 additions & 0 deletions tests/FromString/FayText.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE CPP #-}
-- | Module to be shared between server and client.
--
-- This module must be valid for both GHC and Fay.
module FayText where

import Prelude
#ifdef FAY
import FFI
#else
import Fay.FFI
#endif
import Data.Data

#ifdef FAY

data Text = Text
deriving (Show, Read, Eq, Typeable, Data)

pack :: String -> Text
pack = ffi "%1"

unpack :: Text -> String
unpack = ffi "%1"

#else

import qualified Data.Text as T

type Text = T.Text

pack :: String -> Text
pack = T.pack

unpack :: Text -> String
unpack = T.unpack

#endif

fromString :: String -> Text
fromString = pack

Empty file added tests/FromString/FayText.res
Empty file.