Merge branch 'master' into topic/universals

This commit is contained in:
Paul Chiusano 2019-03-14 15:50:13 -04:00
commit 37f25fd12c
13 changed files with 347 additions and 126 deletions

View File

@ -14,7 +14,7 @@ module Unison.Codebase.Editor where
import Data.Char ( toLower )
import Data.List (sortOn, isSuffixOf, isPrefixOf)
import Control.Monad ( forM_, forM, foldM, filterM)
import Control.Monad ( forM_, forM, foldM, filterM, void)
import Control.Monad.Extra ( ifM )
import Data.Foldable (toList)
import Data.Bifunctor ( bimap, second )
@ -147,7 +147,7 @@ data Input
| ChooseUpdateForTermI Referent Referent
| ChooseUpdateForTypeI Reference Reference
-- execute an IO object with arguments
-- | ExecuteI Name [String]
| ExecuteI String
-- other
| SlurpFileI AllowUpdates
| ListBranchesI
@ -280,7 +280,8 @@ data Command i v a where
-> UF.TypecheckedUnisonFile v Ann
-> Command i v (SlurpResult v)
Typecheck :: Branch
Typecheck :: [Type.AnnotatedType v Ann]
-> Branch
-> SourceName
-> Source
-> Command i v (TypecheckingResult v)
@ -356,7 +357,7 @@ data Command i v a where
Propagate :: Branch -> Command i v Branch
-- Execute :: Reference.Id -> Command i v (IO ())
Execute :: Branch -> UF.UnisonFile v Ann -> Command i v ()
data Outcome
-- New definition that was added to the branch
@ -599,13 +600,14 @@ fileToBranch handleCollisions codebase branch uf = do
typecheck
:: (Monad m, Var v)
=> Codebase m v Ann
=> [Type.AnnotatedType v Ann]
-> Codebase m v Ann
-> Names
-> SourceName
-> Text
-> m (TypecheckingResult v)
typecheck codebase names sourceName src =
Result.getResult $ parseAndSynthesizeFile
typecheck ambient codebase names sourceName src =
Result.getResult $ parseAndSynthesizeFile ambient
(((<> B.typeLookup) <$>) . Codebase.typeLookupForDependencies codebase)
names
(unpack sourceName)
@ -661,16 +663,9 @@ commandLine awaitInput rt notifyUser codebase command = do
Notify output -> notifyUser output
SlurpFile handler branch unisonFile ->
fileToBranch handler codebase branch unisonFile
Typecheck branch sourceName source ->
typecheck codebase (Branch.toNames branch) sourceName source
Evaluate branch unisonFile -> do
let codeLookup = Codebase.toCodeLookup codebase
selfContained <- Codebase.makeSelfContained'
codeLookup
(Branch.head branch)
unisonFile
let noCache = const (pure Nothing)
Runtime.evaluateWatches codeLookup noCache rt selfContained
Typecheck ambient branch sourceName source ->
typecheck ambient codebase (Branch.toNames branch) sourceName source
Evaluate branch unisonFile -> evalUnisonFile branch unisonFile
ListBranches -> Codebase.branches codebase
LoadBranch branchName -> Codebase.getBranch codebase branchName
NewBranch branchName -> newBranch codebase branchName
@ -693,6 +688,14 @@ commandLine awaitInput rt notifyUser codebase command = do
Propagate b -> do
b0 <- Codebase.propagate codebase (Branch.head b)
pure $ Branch.append b0 b
Execute branch uf -> void $ evalUnisonFile branch uf
evalUnisonFile branch unisonFile = do
let codeLookup = Codebase.toCodeLookup codebase
selfContained <- Codebase.makeSelfContained' codeLookup
(Branch.head branch)
unisonFile
let noCache = const (pure Nothing)
Runtime.evaluateWatches codeLookup noCache rt selfContained
doTodo :: Monad m => Codebase m v a -> Branch0 -> m (TodoOutput v a)
doTodo code b = do

View File

@ -1,3 +1,4 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DoAndIfThenElse #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PatternSynonyms #-}
@ -42,18 +43,20 @@ import Unison.Codebase.Editor ( Command(..)
( NameChangeResult)
, collateReferences
)
import qualified Unison.Codebase.Editor as Editor
import qualified Unison.HashQualified as HQ
import qualified Unison.Codebase.Editor as Editor
import qualified Unison.DataDeclaration as DD
import qualified Unison.HashQualified as HQ
import Unison.Name ( Name )
import Unison.Names ( NameTarget )
import qualified Unison.Names as Names
import Unison.Parser ( Ann )
import Unison.Parser ( Ann(..) )
import qualified Unison.PrettyPrintEnv as PPE
import Unison.Reference ( Reference )
import qualified Unison.Reference as Reference
import qualified Unison.Referent as Referent
import Unison.Result (pattern Result)
import qualified Unison.Term as Term
import qualified Unison.Term as Term
import qualified Unison.Type as Type
import qualified Unison.Result as Result
import qualified Unison.UnisonFile as UF
import Unison.Util.Free ( Free )
@ -88,6 +91,21 @@ loop = do
latestFile' <- use latestFile
currentBranch' <- use currentBranch
e <- eval Input
let withFile ambient sourceName text k = do
Result notes r <- eval $
Typecheck ambient (view currentBranch s) sourceName text
case r of
-- Parsing failed
Nothing ->
respond $ ParseErrors
text
[ err | Result.Parsing err <- toList notes ]
Just (errorEnv, r) ->
let h = respond $ TypeErrors
text
errorEnv
[ err | Result.TypeError err <- toList notes ]
in maybe h (k errorEnv) r
case e of
Left (UnisonBranchChanged names) ->
when (Set.member currentBranchName' names)
@ -98,40 +116,21 @@ loop = do
then modifying latestFile $ (fmap (const False) <$>)
else do
eval (Notify $ FileChangeEvent sourceName text)
Result notes r <- eval
(Typecheck (view currentBranch s) sourceName text)
case r of
-- Parsing failed
Nothing ->
respond $ ParseErrors
text
[ err | Result.Parsing err <- toList notes ]
Just (errorEnv, r) -> case r of
-- Typing failed
Nothing ->
respond $ TypeErrors
text
errorEnv
[ err | Result.TypeError err <- toList notes ]
-- A unison file has changed
Just unisonFile -> do
eval (Notify $ Typechecked sourceName errorEnv unisonFile)
(bindings, e) <-
eval
( Evaluate (view currentBranch s)
$ UF.discardTypes unisonFile
)
let e' = Map.map go e
go (ann, _hash, _uneval, eval, isHit) = (ann, eval, isHit)
-- todo: this would be a good spot to update the cache
-- with all the (hash, eval) pairs, even if it's just an
-- in-memory cache
eval . Notify $ Evaluated text
(errorEnv <> Branch.prettyPrintEnv (Branch.head currentBranch'))
bindings
e'
latestFile .= Just (Text.unpack sourceName, False)
latestTypecheckedFile .= Just unisonFile
withFile [] sourceName text $ \errorEnv unisonFile -> do
eval (Notify $ Typechecked sourceName errorEnv unisonFile)
(bindings, e) <-
eval . Evaluate (view currentBranch s) $ UF.discardTypes unisonFile
let e' = Map.map go e
go (ann, _hash, _uneval, eval, isHit) = (ann, eval, isHit)
-- todo: this would be a good spot to update the cache
-- with all the (hash, eval) pairs, even if it's just an
-- in-memory cache
eval . Notify $ Evaluated text
(errorEnv <> Branch.prettyPrintEnv (Branch.head currentBranch'))
bindings
e'
latestFile .= Just (Text.unpack sourceName, False)
latestTypecheckedFile .= Just unisonFile
Right input -> case input of
-- ls with no arguments
SearchByNameI [] ->
@ -248,8 +247,13 @@ loop = do
_ <- eval $ SyncBranch currentBranchName' b
_ <- success
currentBranch .= b
-- ExecuteI name args ->
ExecuteI input ->
withFile [Type.ref External $ Reference.DerivedId DD.ioHash]
"execute command"
("main_ = " <> Text.pack input) $
\_ unisonFile ->
eval . Execute (view currentBranch s) $
UF.discardTypes unisonFile
QuitI -> quit
where
success = respond $ Success input

View File

@ -132,6 +132,11 @@ validInputs =
(\ws -> if not $ null ws
then Left $ warn "`todo` doesn't take any arguments."
else pure $ TodoI)
, InputPattern "execute" [] [(True, noCompletions)]
"`execute foo` evaluates the Unison expression `foo` of type `()` with access to the `IO` ability."
(\ws -> if null ws
then Left $ warn "`execute` needs a Unison language expression."
else pure . ExecuteI $ intercalate " " ws)
, InputPattern "quit" ["exit"] []
"Exits the Unison command line interface."
(\case

View File

@ -9,36 +9,40 @@
module Unison.DataDeclaration where
import Safe (atMay)
import Data.List (sortOn)
import Unison.Hash (Hash)
import Safe ( atMay )
import Data.List ( sortOn )
import Unison.Hash ( Hash )
import Data.Functor
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Set (Set)
import qualified Data.Set as Set
import Prelude hiding (cycle)
import Prelude.Extras (Show1)
import qualified Unison.ABT as ABT
import Unison.Hashable (Accumulate, Hashable1)
import qualified Unison.Hashable as Hashable
import qualified Unison.Name as Name
import Unison.Reference (Reference)
import qualified Unison.Reference as Reference
import Unison.Referent (Referent)
import qualified Unison.Referent as Referent
import qualified Unison.Term as Term
import Unison.Term (AnnotatedTerm, AnnotatedTerm2)
import Unison.Type (AnnotatedType)
import qualified Unison.Type as Type
import Unison.Var (Var)
import Data.Text (Text)
import qualified Unison.Var as Var
import Unison.Names (Names)
import Unison.Names as Names
import Unison.Symbol (Symbol)
import qualified Unison.Pattern as Pattern
-- import Debug.Trace
import Data.Map ( Map )
import qualified Data.Map as Map
import Data.Set ( Set )
import qualified Data.Set as Set
import Prelude hiding ( cycle )
import Prelude.Extras ( Show1 )
import qualified Unison.ABT as ABT
import Unison.Hash as Hash
import Unison.Hashable ( Accumulate
, Hashable1
)
import qualified Unison.Hashable as Hashable
import qualified Unison.Name as Name
import Unison.Reference ( Reference )
import qualified Unison.Reference as Reference
import Unison.Referent ( Referent )
import qualified Unison.Referent as Referent
import qualified Unison.Term as Term
import Unison.Term ( AnnotatedTerm
, AnnotatedTerm2
)
import Unison.Type ( AnnotatedType )
import qualified Unison.Type as Type
import Unison.Var ( Var )
import Data.Text ( Text )
import qualified Unison.Var as Var
import Unison.Names ( Names )
import Unison.Names as Names
import Unison.Symbol ( Symbol )
import qualified Unison.Pattern as Pattern
type DataDeclaration v = DataDeclaration' v ()
@ -287,6 +291,14 @@ builtinDataDecls = hashDecls $
(var "a" `arr` Type.app' (var "Optional") (var "a")))
]
ioHash :: Reference.Id
ioHash = Reference.Id
(Hash.unsafeFromBase58
"525sNixZKpeYWYAr8UFEUkYmATSbfuYkEWgnCa6xFr33JrZxra8jJtShxhtytDisdBSoCE6gtqDRkw67nRnSQXDx"
)
0
1
pattern UnitRef <- (unUnitRef -> True)
pattern PairRef <- (unPairRef -> True)
pattern OptionalRef <- (unOptionalRef -> True)

View File

@ -79,7 +79,8 @@ convertNotes (Typechecker.Notes es is) =
parseAndSynthesizeFile
:: (Var v, Monad m)
=> (Set Reference -> m (TL.TypeLookup v Ann))
=> [Type v]
-> (Set Reference -> m (TL.TypeLookup v Ann))
-> Names
-> FilePath
-> Text
@ -87,22 +88,24 @@ parseAndSynthesizeFile
(Seq (Note v Ann))
m
(PPE.PrettyPrintEnv, Maybe (UF.TypecheckedUnisonFile v Ann))
parseAndSynthesizeFile typeLookupf names filePath src = do
parseAndSynthesizeFile ambient typeLookupf names filePath src = do
(errorEnv, parsedUnisonFile) <- Result.fromParsing
$ Parsers.parseFile filePath (unpack src) names
let refs = UF.dependencies parsedUnisonFile names
typeLookup <- lift . lift $ typeLookupf refs
let (Result notes' r) = synthesizeFile typeLookup names parsedUnisonFile
let (Result notes' r) =
synthesizeFile ambient typeLookup names parsedUnisonFile
tell notes' *> pure (errorEnv, r)
synthesizeFile
:: forall v
. Var v
=> TL.TypeLookup v Ann
=> [Type v]
-> TL.TypeLookup v Ann
-> Names
-> UnisonFile v
-> Result (Seq (Note v Ann)) (UF.TypecheckedUnisonFile v Ann)
synthesizeFile preexistingTypes preexistingNames unisonFile = do
synthesizeFile ambient preexistingTypes preexistingNames unisonFile = do
let
-- substitute builtins into the datas/effects/body of unisonFile
uf@(UnisonFile dds0 eds0 _terms _watches) = unisonFile
@ -119,7 +122,7 @@ synthesizeFile preexistingTypes preexistingNames unisonFile = do
-- substitute Blanks for any remaining free vars in UF body
tdnrTerm = Term.prepareTDNR $ term
lookupTypes = localTypes <> preexistingTypes
env0 = (Typechecker.Env Intrinsic [] lookupTypes fqnsByShortName)
env0 = (Typechecker.Env Intrinsic ambient lookupTypes fqnsByShortName)
where
fqnsByShortName :: Map Name [Typechecker.NamedReference v Ann]
fqnsByShortName = Map.fromListWith mappend

View File

@ -641,14 +641,14 @@ run ioHandler env ir = do
m <- MV.clone m2
m <- MV.grow m 256
pure (size2, m)
loop (RRequest (Req ref cid vs k)) = do
ioResult <- ioHandler ref cid vs
x <- callContinuation 0 m0 k ioResult
loop x
loop a = pure a
r <- go 0 m0 ir
case r of
RRequest (Req ref cid vs k) -> do
ioResult <- ioHandler ref cid vs
callContinuation 0 m0 k ioResult
a -> pure a
loop r
instance Show ExternalFunction where
show _ = "ExternalFunction"

View File

@ -26,6 +26,10 @@ import System.IO ( Handle
, IOMode(..)
, openFile
, hClose
, hPutStr
, stdin
, stdout
, stderr
)
import Unison.Symbol
import qualified Unison.Reference as R
@ -102,15 +106,6 @@ constructorName' cl hash cid = do
go (DataDeclaration _ _ ctors) =
pure . Var.name $ view _2 $ genericIndex ctors cid
-- TODO: Put the actual hashes of these types in here
ioHash :: R.Id
ioHash = R.Id
(Hash.unsafeFromBase58
"3aEd7hZ5DUwcKcTij4Ba8fUzs6B85euZ9Zcs2iNHxyG9UyDYUzXqgENLo9HNzqRKgXBg7B1eA2nNB1sxMcbqCa15"
)
0
1
ioModeHash :: R.Id
ioModeHash = R.Id (Hash.unsafeFromBase58 "abracadabra1") 0 1
@ -131,10 +126,20 @@ handleIO cid = (constructorName ioHash cid >>=) . flip go
liftIO $ maybe (pure ()) hClose hh
deleteUnisonHandle handle
pure IR.unit
go "IO.printLine" [IR.T string] = do
liftIO . putStrLn $ Text.unpack string
go "IO.putText" [IR.Data _ _ [IR.T handle], IR.T string] = do
hh <- getHaskellHandle handle
case hh of
Just h -> liftIO . hPutStr h $ Text.unpack string
Nothing -> pure ()
pure IR.unit
go _ _ = undefined
go a b =
error
$ "IO handler called with cid "
<> show cid
<> " and "
<> show a
<> " args "
<> show b
runtime :: Runtime Symbol
runtime = Runtime terminate eval
@ -148,8 +153,9 @@ runtime = Runtime terminate eval
-> IO (Term.Term Symbol)
eval cl term = do
-- traceM $ Pretty.render 80 (prettyTop mempty term)
cenv <- RT.compilationEnv cl term -- in `m`
mmap <- newMVar mempty
cenv <- RT.compilationEnv cl term -- in `m`
mmap <- newMVar
$ Map.fromList [("stdin", stdin), ("stdout", stdout), ("stderr", stderr)]
RT.RDone result <- RT.run (handleIO' $ S mmap cl)
cenv
(IR.compile cenv $ Term.amap (const ()) term)

View File

@ -205,9 +205,11 @@ placeholder :: Var v => TermP v
placeholder = (\t -> Term.placeholder (ann t) (L.payload t)) <$> blank
vector :: Var v => TermP v -> TermP v
vector p = f <$> reserved "[" <*> elements <*> reserved "]"
vector p = f <$> reserved "[" <*> elements <*> trailing
where
elements = sepBy (reserved ",") p
trailing = optional semi *> reserved "]"
sep = P.try $ optional semi *> reserved "," <* optional semi
elements = sepBy sep p
f open elems close = Term.vector (ann open <> ann close) elems
termLeaf :: forall v. Var v => TermP v

View File

@ -30,7 +30,7 @@ file
-> Result
(Seq (Note Symbol Ann))
(PPE.PrettyPrintEnv, Maybe (TypecheckedUnisonFile Symbol Ann))
file = parseAndSynthesizeAsFile ""
file = parseAndSynthesizeAsFile [] ""
t :: String -> Type Symbol
t = B.t
@ -43,13 +43,15 @@ env = Typechecker.Env Intrinsic [] B.typeLookup mempty
parseAndSynthesizeAsFile
:: Var v
=> FilePath
=> [Type v]
-> FilePath
-> String
-> Result (Seq (Note v Ann))
(PPE.PrettyPrintEnv, Maybe (TypecheckedUnisonFile v Ann))
parseAndSynthesizeAsFile filename s =
FP.parseAndSynthesizeFile
(\_deps -> pure B.typeLookup)
B.names
filename
(Text.pack s)
-> Result
(Seq (Note v Ann))
(PPE.PrettyPrintEnv, Maybe (TypecheckedUnisonFile v Ann))
parseAndSynthesizeAsFile ambient filename s = FP.parseAndSynthesizeFile
ambient
(\_deps -> pure B.typeLookup)
B.names
filename
(Text.pack s)

View File

@ -104,7 +104,7 @@ makePassingTest
makePassingTest rt how filepath = scope shortName $ do
let valueFile = replaceExtension filepath "ur"
source <- io $ unpack <$> Data.Text.IO.readFile filepath
let r = decodeResult source $ parseAndSynthesizeAsFile shortName source
let r = decodeResult source $ parseAndSynthesizeAsFile [] shortName source
rFileExists <- io $ doesFileExist valueFile
case (rFileExists, r) of
(True, Right file) -> do

View File

@ -52,7 +52,7 @@ noYieldsError s ex = not $ yieldsError s ex
yieldsError :: forall v a. Var v => String -> ErrorExtractor v Ann a -> Bool
yieldsError s ex = let
Result notes (Just _) = Common.parseAndSynthesizeAsFile "> test" s
Result notes (Just _) = Common.parseAndSynthesizeAsFile [] "> test" s
notes' :: [C.ErrorNote v Ann]
notes' = [ n | Result.TypeError n <- toList notes ]
in any (isJust . Ex.extract ex) notes'

165
unison-src/IO.u Normal file
View File

@ -0,0 +1,165 @@
-- Handles are unique identifiers.
-- The implementation of IO in the runtime will supply Haskell
-- file handles and map those to Unison handles.
-- A pure implementation of I/O might use some kind of pure supply
-- of unique IDs instead.
type Handle = Handle Text
-- Ditto for sockets
type Socket = Socket Text
-- Builtin handles: standard in, out, error
namespace IO where
stdin: Handle
stdin = Handle "stdin"
stdout: Handle
stdout = Handle "stdout"
stderr: Handle
stderr = Handle "stderr"
printLine : Text -> {IO} ()
printLine t =
IO.putText stdout t
IO.putText stdout "\n"
-- IO Modes from the Haskell API
type IOMode = Read | Write | Append | ReadWrite
-- IO error types from the Haskell API
type IOErrorType
= AlreadyExists
| NoSuchThing
| ResourceBusy
| ResourceExhausted
| EOF
| IllegalOperation
| PermissionDenied
| UserError
type ErrorLocation = ErrorLocation Text
type ErrorDescription = ErrorDescription Text
type FilePath = FilePath Text
type IOError =
IOError
(Optional Handle)
IOErrorType
ErrorLocation
ErrorDescription
(Optional FilePath)
type SeekMode = Absolute | Relative | FromEnd
-- If the buffer size is not specified,
-- use an implementation-specific size.
type BufferMode = Line | Block (Optional Nat)
type EpochTime = EpochTime Nat
-- Either a host name e.g., "unisonweb.org" or a numeric host address
-- string consisting of a dotted decimal IPv4 address or an IPv6 address
-- e.g., "192.168.0.1".
type HostName = HostName Text
type PortNumber = Nat
-- Represents a 32-bit host address
type HostAddress = HostAddress Int
-- Internet protocol v4 socket address
type SocketAddress = SocketAddress HostAddress PortNumber
ability IO where
-- Basic file IO
openFile : FilePath -> IOMode ->{IO} Handle
closeFile : Handle ->{IO} ()
isEOF : Handle ->{IO} Boolean
isFileOpen : Handle ->{IO} Boolean
-- Text input and output
--getChar : Handle ->{IO} Char
getLine : Handle ->{IO} Text
-- Get the entire contents of the file as text
getText : Handle ->{IO} Text
-- putChar : Handle -> Char ->{IO} ()
putText : Handle -> Text ->{IO} ()
-- Handling I/O errors.
-- Question: can we do better?
throw : IOError ->{IO} a
catch : '{IO} a -> (IOError ->{IO} a) ->{IO} a
-- File positioning
isSeekable : Handle ->{IO} Boolean
seek : Handle -> SeekMode -> Int ->{IO} ()
position : Handle ->{IO} Int
-- File buffering
getBuffering : Handle ->{IO} (Optional BufferMode)
setBuffering : Handle -> Optional BufferMode ->{IO} ()
-- Should we expose mutable arrays for byte buffering?
-- Inclined to say no, although that sounds a lot like
-- a decision to just be slow.
-- We'll need a byte buffer manipulation library in that case.
-- getBytes : Handle -> Nat ->{IO} Bytes
-- putBytes : Handle -> Bytes ->{IO} ()
-- getBytes : Handle -> Nat -> ByteArray ->{IO} Nat
-- putBytes : Handle -> Nat -> ByteArray ->{IO} ()
systemTime : {IO} EpochTime
-- File system operations
getCurrentDirectory : {IO} FilePath
setCurrentDirectory : FilePath ->{IO} ()
directoryContents : FilePath ->{IO} [FilePath]
fileExists : FilePath -> {IO} Boolean
isDirectory : FilePath ->{IO} Boolean
createDirectory : FilePath ->{IO} ()
removeDirectory : FilePath ->{IO} ()
renameDirectory : FilePath -> FilePath -> {IO} ()
removeFile : FilePath ->{IO} ()
renameFile : FilePath -> FilePath ->{IO} ()
getFileTimestamp : FilePath ->{IO} EpochTime
getFileSize : FilePath ->{IO} Nat
-- Network I/O
-- Glossing over address families (ipv4, ipv6),
-- and socket types (stream, raw, etc)
-- Creates a socket and binds it to a the given local port
serverSocket : SocketAddress -> {IO} Socket
-- Creates a socket connected to the given remote address
clientSocket : SocketAddress -> {IO} Socket
socketToHandle : Socket ->{IO} Handle
handleToSocket : Handle ->{IO} Socket
closeSocket : Socket ->{IO} ()
-- Accept a connection on a socket.
-- Returns a socket that can send and receive data on a new connection,
-- together with the remote host information.
accept : Socket ->{IO} (Socket, SocketAddress)
-- Returns the number of bytes actually sent
-- send : Socket -> Bytes ->{IO} Int
-- scatter/gather mode network I/O
-- sendMany : Socket -> [Bytes] ->{IO} Int
-- Read the spefified number of bytes from the socket.
-- receive : Socket -> Int ->{IO} Bytes

View File

@ -0,0 +1,19 @@
a = [1,2,3]
b = [1 ,2 ,3
]
c = [ 1 , 2 , 3 ]
d = [ 1
, 2
, 3 ]
e = [ 1
, 2
, 3
]
f =
[ 1
, 2
, 3
]
g = [ 1
, 2,
3 ]