From 85bd05bf91940ff27f9faffb85438a0de127555c Mon Sep 17 00:00:00 2001 From: Sebastiaan Visser Date: Mon, 26 Aug 2013 16:23:54 +0200 Subject: [PATCH] Implemented overloadable string literals using an in-scope fromString. Uses a combination of OverloadedStrings and RebindableSyntax. --- src/Fay/Compiler.hs | 7 ++++++ src/Fay/Compiler/Defaults.hs | 1 + src/Fay/Compiler/Exp.hs | 7 ++++-- src/Fay/FFI.hs | 4 +++- src/Fay/Types.hs | 21 +++++++++--------- tests/FromString.hs | 15 +++++++++++++ tests/FromString.res | 5 +++++ tests/FromString/Dep.hs | 11 +++++++++ tests/FromString/Dep.res | 0 tests/FromString/DepDep.hs | 9 ++++++++ tests/FromString/DepDep.res | 0 tests/FromString/FayText.hs | 43 ++++++++++++++++++++++++++++++++++++ tests/FromString/FayText.res | 0 13 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 tests/FromString.hs create mode 100644 tests/FromString.res create mode 100644 tests/FromString/Dep.hs create mode 100644 tests/FromString/Dep.res create mode 100644 tests/FromString/DepDep.hs create mode 100644 tests/FromString/DepDep.res create mode 100644 tests/FromString/FayText.hs create mode 100644 tests/FromString/FayText.res diff --git a/src/Fay/Compiler.hs b/src/Fay/Compiler.hs index ef193d6..50d9c66 100644 --- a/src/Fay/Compiler.hs +++ b/src/Fay/Compiler.hs @@ -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 @@ -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 diff --git a/src/Fay/Compiler/Defaults.hs b/src/Fay/Compiler/Defaults.hs index 91101ce..b995c28 100644 --- a/src/Fay/Compiler/Defaults.hs +++ b/src/Fay/Compiler/Defaults.hs @@ -41,4 +41,5 @@ defaultCompileState = do , stateModuleScope = def , stateModuleScopes = M.empty , stateJsModulePaths = S.empty + , stateUseFromString = False } diff --git a/src/Fay/Compiler/Exp.hs b/src/Fay/Compiler/Exp.hs index 15d1648..de8afbd 100644 --- a/src/Fay/Compiler/Exp.hs +++ b/src/Fay/Compiler/Exp.hs @@ -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. diff --git a/src/Fay/FFI.hs b/src/Fay/FFI.hs index 42b0af4..7edbaaa 100644 --- a/src/Fay/FFI.hs +++ b/src/Fay/FFI.hs @@ -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) @@ -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." diff --git a/src/Fay/Types.hs b/src/Fay/Types.hs index 47df1e2..ec51a0d 100644 --- a/src/Fay/Types.hs +++ b/src/Fay/Types.hs @@ -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. diff --git a/tests/FromString.hs b/tests/FromString.hs new file mode 100644 index 0000000..10f71ff --- /dev/null +++ b/tests/FromString.hs @@ -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 + diff --git a/tests/FromString.res b/tests/FromString.res new file mode 100644 index 0000000..c40e07d --- /dev/null +++ b/tests/FromString.res @@ -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 diff --git a/tests/FromString/Dep.hs b/tests/FromString/Dep.hs new file mode 100644 index 0000000..47ef42d --- /dev/null +++ b/tests/FromString/Dep.hs @@ -0,0 +1,11 @@ +module FromString.Dep where + +import Prelude +import FromString.DepDep (myText) + +myString :: String +myString = "test" + +depTest :: Fay () +depTest = print myText + diff --git a/tests/FromString/Dep.res b/tests/FromString/Dep.res new file mode 100644 index 0000000..e69de29 diff --git a/tests/FromString/DepDep.hs b/tests/FromString/DepDep.hs new file mode 100644 index 0000000..d397064 --- /dev/null +++ b/tests/FromString/DepDep.hs @@ -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" + diff --git a/tests/FromString/DepDep.res b/tests/FromString/DepDep.res new file mode 100644 index 0000000..e69de29 diff --git a/tests/FromString/FayText.hs b/tests/FromString/FayText.hs new file mode 100644 index 0000000..15ca899 --- /dev/null +++ b/tests/FromString/FayText.hs @@ -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 + diff --git a/tests/FromString/FayText.res b/tests/FromString/FayText.res new file mode 100644 index 0000000..e69de29