mirror of
https://github.com/idris-lang/Idris2.git
synced 2024-11-28 02:23:44 +03:00
Everything but the IDE protocol
This commit is contained in:
parent
ea41bb9abe
commit
14d480b971
266
src/Compiler/ANF.idr
Normal file
266
src/Compiler/ANF.idr
Normal file
@ -0,0 +1,266 @@
|
||||
module Compiler.ANF
|
||||
|
||||
import Compiler.LambdaLift
|
||||
|
||||
import Core.CompileExpr
|
||||
import Core.Context
|
||||
import Core.Core
|
||||
import Core.TT
|
||||
|
||||
import Data.List
|
||||
import Data.Vect
|
||||
|
||||
%default covering
|
||||
|
||||
-- Convert the lambda lifted form to ANF, with variable names made explicit.
|
||||
-- i.e. turn intermediate expressions into let bindings. Every argument is
|
||||
-- a variable as a result.
|
||||
|
||||
mutual
|
||||
public export
|
||||
data AVar : Type where
|
||||
ALocal : Int -> AVar
|
||||
ANull : AVar
|
||||
|
||||
public export
|
||||
data ANF : Type where
|
||||
AV : FC -> AVar -> ANF
|
||||
AAppName : FC -> Name -> List AVar -> ANF
|
||||
AUnderApp : FC -> Name -> (missing : Nat) -> (args : List AVar) -> ANF
|
||||
AApp : FC -> (closure : AVar) -> (arg : AVar) -> ANF
|
||||
ALet : FC -> (var : Int) -> ANF -> ANF -> ANF
|
||||
ACon : FC -> Name -> (tag : Maybe Int) -> List AVar -> ANF
|
||||
AOp : FC -> PrimFn arity -> Vect arity AVar -> ANF
|
||||
AExtPrim : FC -> Name -> List AVar -> ANF
|
||||
AConCase : FC -> AVar -> List AConAlt -> Maybe ANF -> ANF
|
||||
AConstCase : FC -> AVar -> List AConstAlt -> Maybe ANF -> ANF
|
||||
APrimVal : FC -> Constant -> ANF
|
||||
AErased : FC -> ANF
|
||||
ACrash : FC -> String -> ANF
|
||||
|
||||
public export
|
||||
data AConAlt : Type where
|
||||
MkAConAlt : Name -> (tag : Maybe Int) -> (args : List Int) ->
|
||||
ANF -> AConAlt
|
||||
|
||||
public export
|
||||
data AConstAlt : Type where
|
||||
MkAConstAlt : Constant -> ANF -> AConstAlt
|
||||
|
||||
public export
|
||||
data ANFDef : Type where
|
||||
MkAFun : (args : List Int) -> ANF -> ANFDef
|
||||
MkACon : (tag : Maybe Int) -> (arity : Nat) -> ANFDef
|
||||
MkAForeign : (ccs : List String) -> (fargs : List CFType) ->
|
||||
CFType -> ANFDef
|
||||
MkAError : ANF -> ANFDef
|
||||
|
||||
mutual
|
||||
export
|
||||
Show AVar where
|
||||
show (ALocal i) = "v" ++ show i
|
||||
show ANull = "[__]"
|
||||
|
||||
export
|
||||
Show ANF where
|
||||
show (AV _ v) = show v
|
||||
show (AAppName fc n args)
|
||||
= show n ++ "(" ++ showSep ", " (map show args) ++ ")"
|
||||
show (AUnderApp fc n m args)
|
||||
= "<" ++ show n ++ " underapp " ++ show m ++ ">(" ++
|
||||
showSep ", " (map show args) ++ ")"
|
||||
show (AApp fc c arg)
|
||||
= show c ++ " @ (" ++ show arg ++ ")"
|
||||
show (ALet fc x val sc)
|
||||
= "%let v" ++ show x ++ " = " ++ show val ++ " in " ++ show sc
|
||||
show (ACon fc n t args)
|
||||
= "%con " ++ show n ++ "(" ++ showSep ", " (map show args) ++ ")"
|
||||
show (AOp fc op args)
|
||||
= "%op " ++ show op ++ "(" ++ showSep ", " (toList (map show args)) ++ ")"
|
||||
show (AExtPrim fc p args)
|
||||
= "%extprim " ++ show p ++ "(" ++ showSep ", " (map show args) ++ ")"
|
||||
show (AConCase fc sc alts def)
|
||||
= "%case " ++ show sc ++ " of { "
|
||||
++ showSep "| " (map show alts) ++ " " ++ show def
|
||||
show (AConstCase fc sc alts def)
|
||||
= "%case " ++ show sc ++ " of { "
|
||||
++ showSep "| " (map show alts) ++ " " ++ show def
|
||||
show (APrimVal _ x) = show x
|
||||
show (AErased _) = "___"
|
||||
show (ACrash _ x) = "%CRASH(" ++ show x ++ ")"
|
||||
|
||||
export
|
||||
Show AConAlt where
|
||||
show (MkAConAlt n t args sc)
|
||||
= "%conalt " ++ show n ++
|
||||
"(" ++ showSep ", " (map showArg args) ++ ") => " ++ show sc
|
||||
where
|
||||
showArg : Int -> String
|
||||
showArg i = "v" ++ show i
|
||||
|
||||
export
|
||||
Show AConstAlt where
|
||||
show (MkAConstAlt c sc)
|
||||
= "%constalt(" ++ show c ++ ") => " ++ show sc
|
||||
|
||||
export
|
||||
Show ANFDef where
|
||||
show (MkAFun args exp) = show args ++ ": " ++ show exp
|
||||
show (MkACon tag arity)
|
||||
= "Constructor tag " ++ show tag ++ " arity " ++ show arity
|
||||
show (MkAForeign ccs args ret)
|
||||
= "Foreign call " ++ show ccs ++ " " ++
|
||||
show args ++ " -> " ++ show ret
|
||||
show (MkAError exp) = "Error: " ++ show exp
|
||||
|
||||
data AVars : List Name -> Type where
|
||||
Nil : AVars []
|
||||
(::) : Int -> AVars xs -> AVars (x :: xs)
|
||||
|
||||
data Next : Type where
|
||||
|
||||
nextVar : {auto v : Ref Next Int} ->
|
||||
Core Int
|
||||
nextVar
|
||||
= do i <- get Next
|
||||
put Next (i + 1)
|
||||
pure i
|
||||
|
||||
lookup : {idx : _} -> (0 p : IsVar x idx vs) -> AVars vs -> Int
|
||||
lookup First (x :: xs) = x
|
||||
lookup (Later p) (x :: xs) = lookup p xs
|
||||
|
||||
bindArgs : {auto v : Ref Next Int} ->
|
||||
List ANF -> Core (List (AVar, Maybe ANF))
|
||||
bindArgs [] = pure []
|
||||
bindArgs (AV fc var :: xs)
|
||||
= do xs' <- bindArgs xs
|
||||
pure $ (var, Nothing) :: xs'
|
||||
bindArgs (AErased fc :: xs)
|
||||
= do xs' <- bindArgs xs
|
||||
pure $ (ANull, Nothing) :: xs'
|
||||
bindArgs (x :: xs)
|
||||
= do i <- nextVar
|
||||
xs' <- bindArgs xs
|
||||
pure $ (ALocal i, Just x) :: xs'
|
||||
|
||||
letBind : {auto v : Ref Next Int} ->
|
||||
FC -> List ANF -> (List AVar -> ANF) -> Core ANF
|
||||
letBind fc args f
|
||||
= do bargs <- bindArgs args
|
||||
pure $ doBind [] bargs
|
||||
where
|
||||
doBind : List AVar -> List (AVar, Maybe ANF) -> ANF
|
||||
doBind vs [] = f (reverse vs)
|
||||
doBind vs ((ALocal i, Just t) :: xs)
|
||||
= ALet fc i t (doBind (ALocal i :: vs) xs)
|
||||
doBind vs ((var, _) :: xs) = doBind (var :: vs) xs
|
||||
|
||||
toVect : (n : Nat) -> List a -> Maybe (Vect n a)
|
||||
toVect Z [] = Just []
|
||||
toVect (S k) (x :: xs)
|
||||
= do xs' <- toVect k xs
|
||||
pure (x :: xs')
|
||||
toVect _ _ = Nothing
|
||||
|
||||
mlet : {auto v : Ref Next Int} ->
|
||||
FC -> ANF -> (AVar -> ANF) -> Core ANF
|
||||
mlet fc (AV _ var) sc = pure $ sc var
|
||||
mlet fc val sc
|
||||
= do i <- nextVar
|
||||
pure $ ALet fc i val (sc (ALocal i))
|
||||
|
||||
mutual
|
||||
anfArgs : {vars : _} ->
|
||||
{auto v : Ref Next Int} ->
|
||||
FC -> AVars vars ->
|
||||
List (Lifted vars) -> (List AVar -> ANF) -> Core ANF
|
||||
anfArgs fc vs args f
|
||||
= do args' <- traverse (anf vs) args
|
||||
letBind fc args' f
|
||||
|
||||
anf : {vars : _} ->
|
||||
{auto v : Ref Next Int} ->
|
||||
AVars vars -> Lifted vars -> Core ANF
|
||||
anf vs (LLocal fc p) = pure $ AV fc (ALocal (lookup p vs))
|
||||
anf vs (LAppName fc n args)
|
||||
= anfArgs fc vs args (AAppName fc n)
|
||||
anf vs (LUnderApp fc n m args)
|
||||
= anfArgs fc vs args (AUnderApp fc n m)
|
||||
anf vs (LApp fc f a)
|
||||
= anfArgs fc vs [f, a]
|
||||
(\args => case args of
|
||||
[fvar, avar] => AApp fc fvar avar
|
||||
_ => ACrash fc "Can't happen (AApp)")
|
||||
anf vs (LLet fc x val sc)
|
||||
= do i <- nextVar
|
||||
let vs' = i :: vs
|
||||
pure $ ALet fc i !(anf vs val) !(anf vs' sc)
|
||||
anf vs (LCon fc n t args)
|
||||
= anfArgs fc vs args (ACon fc n t)
|
||||
anf vs (LOp {arity} fc op args)
|
||||
= do args' <- traverse (anf vs) (toList args)
|
||||
letBind fc args'
|
||||
(\args => case toVect arity args of
|
||||
Nothing => ACrash fc "Can't happen (AOp)"
|
||||
Just argsv => AOp fc op argsv)
|
||||
anf vs (LExtPrim fc p args)
|
||||
= anfArgs fc vs args (AExtPrim fc p)
|
||||
anf vs (LConCase fc scr alts def)
|
||||
= do scr' <- anf vs scr
|
||||
alts' <- traverse (anfConAlt vs) alts
|
||||
def' <- traverseOpt (anf vs) def
|
||||
mlet fc scr' (\x => AConCase fc x alts' def')
|
||||
anf vs (LConstCase fc scr alts def)
|
||||
= do scr' <- anf vs scr
|
||||
alts' <- traverse (anfConstAlt vs) alts
|
||||
def' <- traverseOpt (anf vs) def
|
||||
mlet fc scr' (\x => AConstCase fc x alts' def')
|
||||
anf vs (LPrimVal fc c) = pure $ APrimVal fc c
|
||||
anf vs (LErased fc) = pure $ AErased fc
|
||||
anf vs (LCrash fc err) = pure $ ACrash fc err
|
||||
|
||||
anfConAlt : {vars : _} ->
|
||||
{auto v : Ref Next Int} ->
|
||||
AVars vars -> LiftedConAlt vars -> Core AConAlt
|
||||
anfConAlt vs (MkLConAlt n t args sc)
|
||||
= do (is, vs') <- bindArgs args vs
|
||||
pure $ MkAConAlt n t is !(anf vs' sc)
|
||||
where
|
||||
bindArgs : (args : List Name) -> AVars vars' ->
|
||||
Core (List Int, AVars (args ++ vars'))
|
||||
bindArgs [] vs = pure ([], vs)
|
||||
bindArgs (n :: ns) vs
|
||||
= do i <- nextVar
|
||||
(is, vs') <- bindArgs ns vs
|
||||
pure (i :: is, i :: vs')
|
||||
|
||||
anfConstAlt : {vars : _} ->
|
||||
{auto v : Ref Next Int} ->
|
||||
AVars vars -> LiftedConstAlt vars -> Core AConstAlt
|
||||
anfConstAlt vs (MkLConstAlt c sc)
|
||||
= pure $ MkAConstAlt c !(anf vs sc)
|
||||
|
||||
export
|
||||
toANF : LiftedDef -> Core ANFDef
|
||||
toANF (MkLFun args scope sc)
|
||||
= do v <- newRef Next (the Int 0)
|
||||
(iargs, vsNil) <- bindArgs args []
|
||||
let vs : AVars args = rewrite sym (appendNilRightNeutral args) in
|
||||
vsNil
|
||||
(iargs', vs) <- bindArgs scope vs
|
||||
pure $ MkAFun (iargs ++ reverse iargs') !(anf vs sc)
|
||||
where
|
||||
bindArgs : {auto v : Ref Next Int} ->
|
||||
(args : List Name) -> AVars vars' ->
|
||||
Core (List Int, AVars (args ++ vars'))
|
||||
bindArgs [] vs = pure ([], vs)
|
||||
bindArgs (n :: ns) vs
|
||||
= do i <- nextVar
|
||||
(is, vs') <- bindArgs ns vs
|
||||
pure (i :: is, i :: vs')
|
||||
toANF (MkLCon t a ns) = pure $ MkACon t a
|
||||
toANF (MkLForeign ccs fargs t) = pure $ MkAForeign ccs fargs t
|
||||
toANF (MkLError err)
|
||||
= do v <- newRef Next (the Int 0)
|
||||
pure $ MkAError !(anf [] err)
|
397
src/Compiler/Common.idr
Normal file
397
src/Compiler/Common.idr
Normal file
@ -0,0 +1,397 @@
|
||||
module Compiler.Common
|
||||
|
||||
import Compiler.ANF
|
||||
import Compiler.CompileExpr
|
||||
import Compiler.Inline
|
||||
import Compiler.LambdaLift
|
||||
import Compiler.VMCode
|
||||
|
||||
import Core.Context
|
||||
import Core.Directory
|
||||
import Core.Options
|
||||
import Core.TT
|
||||
import Core.TTC
|
||||
import Utils.Binary
|
||||
|
||||
import Data.IOArray
|
||||
import Data.List
|
||||
import Data.NameMap
|
||||
import Data.Strings
|
||||
|
||||
import System.Directory
|
||||
import System.Info
|
||||
import System.File
|
||||
|
||||
||| Generic interface to some code generator
|
||||
public export
|
||||
record Codegen where
|
||||
constructor MkCG
|
||||
||| Compile an Idris 2 expression, saving it to a file.
|
||||
compileExpr : Ref Ctxt Defs -> (execDir : String) ->
|
||||
ClosedTerm -> (outfile : String) -> Core (Maybe String)
|
||||
||| Execute an Idris 2 expression directly.
|
||||
executeExpr : Ref Ctxt Defs -> (execDir : String) -> ClosedTerm -> Core ()
|
||||
|
||||
-- Say which phase of compilation is the last one to use - it saves time if
|
||||
-- you only ask for what you need.
|
||||
public export
|
||||
data UsePhase = Cases | Lifted | ANF | VMCode
|
||||
|
||||
Eq UsePhase where
|
||||
(==) Cases Cases = True
|
||||
(==) Lifted Lifted = True
|
||||
(==) ANF ANF = True
|
||||
(==) VMCode VMCode = True
|
||||
(==) _ _ = False
|
||||
|
||||
Ord UsePhase where
|
||||
compare x y = compare (tag x) (tag y)
|
||||
where
|
||||
tag : UsePhase -> Int
|
||||
tag Cases = 0
|
||||
tag Lifted = 0
|
||||
tag ANF = 0
|
||||
tag VMCode = 0
|
||||
|
||||
public export
|
||||
record CompileData where
|
||||
constructor MkCompileData
|
||||
mainExpr : CExp [] -- main expression to execute. This also appears in
|
||||
-- the definitions below as MN "__mainExpression" 0
|
||||
namedDefs : List (Name, FC, NamedDef)
|
||||
lambdaLifted : List (Name, LiftedDef)
|
||||
-- ^ lambda lifted definitions, if required. Only the top level names
|
||||
-- will be in the context, and (for the moment...) I don't expect to
|
||||
-- need to look anything up, so it's just an alist.
|
||||
anf : List (Name, ANFDef)
|
||||
-- ^ lambda lifted and converted to ANF (all arguments to functions
|
||||
-- and constructors transformed to either variables or Null if erased)
|
||||
vmcode : List (Name, VMDef)
|
||||
-- ^ A much simplified virtual machine code, suitable for passing
|
||||
-- to a more low level target such as C
|
||||
|
||||
||| compile
|
||||
||| Given a value of type Codegen, produce a standalone function
|
||||
||| that executes the `compileExpr` method of the Codegen
|
||||
export
|
||||
compile : {auto c : Ref Ctxt Defs} ->
|
||||
Codegen ->
|
||||
ClosedTerm -> (outfile : String) -> Core (Maybe String)
|
||||
compile {c} cg tm out
|
||||
= do makeExecDirectory
|
||||
d <- getDirs
|
||||
logTime "Code generation overall" $
|
||||
compileExpr cg c (exec_dir d) tm out
|
||||
|
||||
||| execute
|
||||
||| As with `compile`, produce a functon that executes
|
||||
||| the `executeExpr` method of the given Codegen
|
||||
export
|
||||
execute : {auto c : Ref Ctxt Defs} ->
|
||||
Codegen -> ClosedTerm -> Core ()
|
||||
execute {c} cg tm
|
||||
= do makeExecDirectory
|
||||
d <- getDirs
|
||||
executeExpr cg c (exec_dir d) tm
|
||||
pure ()
|
||||
|
||||
-- If an entry isn't already decoded, get the minimal entry we need for
|
||||
-- compilation, and record the Binary so that we can put it back when we're
|
||||
-- done (so that we don't obliterate the definition)
|
||||
getMinimalDef : ContextEntry -> Core (GlobalDef, Maybe Binary)
|
||||
getMinimalDef (Decoded def) = pure (def, Nothing)
|
||||
getMinimalDef (Coded bin)
|
||||
= do b <- newRef Bin bin
|
||||
cdef <- fromBuf b
|
||||
refsRList <- fromBuf b
|
||||
let refsR = map fromList refsRList
|
||||
fc <- fromBuf b
|
||||
mul <- fromBuf b
|
||||
name <- fromBuf b
|
||||
let def
|
||||
= MkGlobalDef fc name (Erased fc False) [] [] [] [] mul
|
||||
[] Public (MkTotality Unchecked IsCovering)
|
||||
[] Nothing refsR False False True
|
||||
None cdef Nothing []
|
||||
pure (def, Just bin)
|
||||
|
||||
-- ||| Recursively get all calls in a function definition
|
||||
getAllDesc : {auto c : Ref Ctxt Defs} ->
|
||||
List Name -> -- calls to check
|
||||
IOArray (Int, Maybe Binary) ->
|
||||
-- which nodes have been visited. If the entry is
|
||||
-- present, it's visited. Keep the binary entry, if
|
||||
-- we partially decoded it, so that we can put back
|
||||
-- the full definition later.
|
||||
-- (We only need to decode the case tree IR, and
|
||||
-- it's expensive to decode the whole thing)
|
||||
Defs -> Core ()
|
||||
getAllDesc [] arr defs = pure ()
|
||||
getAllDesc (n@(Resolved i) :: rest) arr defs
|
||||
= do Nothing <- coreLift $ readArray arr i
|
||||
| Just _ => getAllDesc rest arr defs
|
||||
case !(lookupContextEntry n (gamma defs)) of
|
||||
Nothing => getAllDesc rest arr defs
|
||||
Just (_, entry) =>
|
||||
do (def, bin) <- getMinimalDef entry
|
||||
addDef n def
|
||||
let refs = refersToRuntime def
|
||||
if multiplicity def /= erased
|
||||
then do coreLift $ writeArray arr i (i, bin)
|
||||
let refs = refersToRuntime def
|
||||
refs' <- traverse toResolvedNames (keys refs)
|
||||
getAllDesc (refs' ++ rest) arr defs
|
||||
else getAllDesc rest arr defs
|
||||
getAllDesc (n :: rest) arr defs
|
||||
= getAllDesc rest arr defs
|
||||
|
||||
getNamedDef : {auto c : Ref Ctxt Defs} ->
|
||||
Name -> Core (Maybe (Name, FC, NamedDef))
|
||||
getNamedDef n
|
||||
= do defs <- get Ctxt
|
||||
case !(lookupCtxtExact n (gamma defs)) of
|
||||
Nothing => pure Nothing
|
||||
Just def => case namedcompexpr def of
|
||||
Nothing => pure Nothing
|
||||
Just d => pure (Just (n, location def, d))
|
||||
|
||||
replaceEntry : {auto c : Ref Ctxt Defs} ->
|
||||
(Int, Maybe Binary) -> Core ()
|
||||
replaceEntry (i, Nothing) = pure ()
|
||||
replaceEntry (i, Just b)
|
||||
= do addContextEntry (Resolved i) b
|
||||
pure ()
|
||||
|
||||
natHackNames : List Name
|
||||
natHackNames
|
||||
= [UN "prim__add_Integer",
|
||||
UN "prim__sub_Integer",
|
||||
UN "prim__mul_Integer",
|
||||
NS ["Prelude"] (UN "natToInteger"),
|
||||
NS ["Prelude"] (UN "integerToNat")]
|
||||
|
||||
-- Hmm, these dump functions are all very similar aren't they...
|
||||
dumpCases : Defs -> String -> List Name ->
|
||||
Core ()
|
||||
dumpCases defs fn cns
|
||||
= do cstrs <- traverse dumpCase cns
|
||||
Right () <- coreLift $ writeFile fn (fastAppend cstrs)
|
||||
| Left err => throw (FileErr fn err)
|
||||
pure ()
|
||||
where
|
||||
fullShow : Name -> String
|
||||
fullShow (DN _ n) = show n
|
||||
fullShow n = show n
|
||||
|
||||
dumpCase : Name -> Core String
|
||||
dumpCase n
|
||||
= case !(lookupCtxtExact n (gamma defs)) of
|
||||
Nothing => pure ""
|
||||
Just d =>
|
||||
case namedcompexpr d of
|
||||
Nothing => pure ""
|
||||
Just def => pure (fullShow n ++ " = " ++ show def ++ "\n")
|
||||
|
||||
dumpLifted : String -> List (Name, LiftedDef) -> Core ()
|
||||
dumpLifted fn lns
|
||||
= do let cstrs = map dumpDef lns
|
||||
Right () <- coreLift $ writeFile fn (fastAppend cstrs)
|
||||
| Left err => throw (FileErr fn err)
|
||||
pure ()
|
||||
where
|
||||
fullShow : Name -> String
|
||||
fullShow (DN _ n) = show n
|
||||
fullShow n = show n
|
||||
|
||||
dumpDef : (Name, LiftedDef) -> String
|
||||
dumpDef (n, d) = fullShow n ++ " = " ++ show d ++ "\n"
|
||||
|
||||
dumpANF : String -> List (Name, ANFDef) -> Core ()
|
||||
dumpANF fn lns
|
||||
= do let cstrs = map dumpDef lns
|
||||
Right () <- coreLift $ writeFile fn (fastAppend cstrs)
|
||||
| Left err => throw (FileErr fn err)
|
||||
pure ()
|
||||
where
|
||||
fullShow : Name -> String
|
||||
fullShow (DN _ n) = show n
|
||||
fullShow n = show n
|
||||
|
||||
dumpDef : (Name, ANFDef) -> String
|
||||
dumpDef (n, d) = fullShow n ++ " = " ++ show d ++ "\n"
|
||||
|
||||
dumpVMCode : String -> List (Name, VMDef) -> Core ()
|
||||
dumpVMCode fn lns
|
||||
= do let cstrs = map dumpDef lns
|
||||
Right () <- coreLift $ writeFile fn (fastAppend cstrs)
|
||||
| Left err => throw (FileErr fn err)
|
||||
pure ()
|
||||
where
|
||||
fullShow : Name -> String
|
||||
fullShow (DN _ n) = show n
|
||||
fullShow n = show n
|
||||
|
||||
dumpDef : (Name, VMDef) -> String
|
||||
dumpDef (n, d) = fullShow n ++ " = " ++ show d ++ "\n"
|
||||
|
||||
-- Find all the names which need compiling, from a given expression, and compile
|
||||
-- them to CExp form (and update that in the Defs).
|
||||
-- Return the names, the type tags, and a compiled version of the expression
|
||||
export
|
||||
getCompileData : {auto c : Ref Ctxt Defs} ->
|
||||
UsePhase -> ClosedTerm -> Core CompileData
|
||||
getCompileData phase tm_in
|
||||
= do defs <- get Ctxt
|
||||
sopts <- getSession
|
||||
let ns = getRefs (Resolved (-1)) tm_in
|
||||
tm <- toFullNames tm_in
|
||||
natHackNames' <- traverse toResolvedNames natHackNames
|
||||
-- make an array of Bools to hold which names we've found (quicker
|
||||
-- to check than a NameMap!)
|
||||
asize <- getNextEntry
|
||||
arr <- coreLift $ newArray asize
|
||||
logTime "Get names" $ getAllDesc (natHackNames' ++ keys ns) arr defs
|
||||
|
||||
let entries = mapMaybe id !(coreLift (toList arr))
|
||||
let allNs = map (Resolved . fst) entries
|
||||
cns <- traverse toFullNames allNs
|
||||
|
||||
-- Do a round of merging/arity fixing for any names which were
|
||||
-- unknown due to cyclic modules (i.e. declared in one, defined in
|
||||
-- another)
|
||||
rcns <- filterM nonErased cns
|
||||
logTime "Merge lambda" $ traverse_ mergeLamDef rcns
|
||||
logTime "Fix arity" $ traverse_ fixArityDef rcns
|
||||
logTime "Forget names" $ traverse_ mkForgetDef rcns
|
||||
|
||||
compiledtm <- fixArityExp !(compileExp tm)
|
||||
let mainname = MN "__mainExpression" 0
|
||||
(liftedtm, ldefs) <- liftBody mainname compiledtm
|
||||
|
||||
namedefs <- traverse getNamedDef rcns
|
||||
lifted_in <- if phase >= Lifted
|
||||
then logTime "Lambda lift" $ traverse lambdaLift rcns
|
||||
else pure []
|
||||
|
||||
let lifted = (mainname, MkLFun [] [] liftedtm) ::
|
||||
ldefs ++ concat lifted_in
|
||||
|
||||
anf <- if phase >= ANF
|
||||
then logTime "Get ANF" $ traverse (\ (n, d) => pure (n, !(toANF d))) lifted
|
||||
else pure []
|
||||
vmcode <- if phase >= VMCode
|
||||
then logTime "Get VM Code" $ pure (allDefs anf)
|
||||
else pure []
|
||||
|
||||
defs <- get Ctxt
|
||||
maybe (pure ())
|
||||
(\f => do coreLift $ putStrLn $ "Dumping case trees to " ++ f
|
||||
dumpCases defs f rcns)
|
||||
(dumpcases sopts)
|
||||
maybe (pure ())
|
||||
(\f => do coreLift $ putStrLn $ "Dumping lambda lifted defs to " ++ f
|
||||
dumpLifted f lifted)
|
||||
(dumplifted sopts)
|
||||
maybe (pure ())
|
||||
(\f => do coreLift $ putStrLn $ "Dumping ANF defs to " ++ f
|
||||
dumpANF f anf)
|
||||
(dumpanf sopts)
|
||||
maybe (pure ())
|
||||
(\f => do coreLift $ putStrLn $ "Dumping VM defs to " ++ f
|
||||
dumpVMCode f vmcode)
|
||||
(dumpvmcode sopts)
|
||||
|
||||
-- We're done with our minimal context now, so put it back the way
|
||||
-- it was. Back ends shouldn't look at the global context, because
|
||||
-- it'll have to decode the definitions again.
|
||||
traverse_ replaceEntry entries
|
||||
pure (MkCompileData compiledtm
|
||||
(mapMaybe id namedefs)
|
||||
lifted anf vmcode)
|
||||
where
|
||||
nonErased : Name -> Core Bool
|
||||
nonErased n
|
||||
= do defs <- get Ctxt
|
||||
Just gdef <- lookupCtxtExact n (gamma defs)
|
||||
| Nothing => pure True
|
||||
pure (multiplicity gdef /= erased)
|
||||
|
||||
-- Some things missing from Prelude.File
|
||||
|
||||
||| check to see if a given file exists
|
||||
export
|
||||
exists : String -> IO Bool
|
||||
exists f
|
||||
= do Right ok <- openFile f Read
|
||||
| Left err => pure False
|
||||
closeFile ok
|
||||
pure True
|
||||
|
||||
-- Parse a calling convention into a backend/target for the call, and
|
||||
-- a comma separated list of any other location data.
|
||||
-- e.g. "scheme:display" - call the scheme function 'display'
|
||||
-- "C:puts,libc,stdio.h" - call the C function 'puts' which is in
|
||||
-- the library libc and the header stdio.h
|
||||
-- Returns Nothing if the string is empty (which a backend can interpret
|
||||
-- however it likes)
|
||||
export
|
||||
parseCC : String -> Maybe (String, List String)
|
||||
parseCC "" = Nothing
|
||||
parseCC str
|
||||
= case span (/= ':') str of
|
||||
(target, "") => Just (trim target, [])
|
||||
(target, opts) => Just (trim target,
|
||||
map trim (getOpts
|
||||
(assert_total (strTail opts))))
|
||||
where
|
||||
getOpts : String -> List String
|
||||
getOpts "" = []
|
||||
getOpts str
|
||||
= case span (/= ',') str of
|
||||
(opt, "") => [opt]
|
||||
(opt, rest) => opt :: getOpts (assert_total (strTail rest))
|
||||
|
||||
export
|
||||
dylib_suffix : String
|
||||
dylib_suffix
|
||||
= cond [(os `elem` ["windows", "mingw32", "cygwin32"], "dll"),
|
||||
(os == "darwin", "dylib")]
|
||||
"so"
|
||||
|
||||
export
|
||||
locate : {auto c : Ref Ctxt Defs} ->
|
||||
String -> Core (String, String)
|
||||
locate libspec
|
||||
= do -- Attempt to turn libspec into an appropriate filename for the system
|
||||
let fname
|
||||
= case words libspec of
|
||||
[] => ""
|
||||
[fn] => if '.' `elem` unpack fn
|
||||
then fn -- full filename given
|
||||
else -- add system extension
|
||||
fn ++ "." ++ dylib_suffix
|
||||
(fn :: ver :: _) =>
|
||||
-- library and version given, build path name as
|
||||
-- appropriate for the system
|
||||
cond [(dylib_suffix == "dll",
|
||||
fn ++ "-" ++ ver ++ ".dll"),
|
||||
(dylib_suffix == "dylib",
|
||||
fn ++ "." ++ ver ++ ".dylib")]
|
||||
(fn ++ "." ++ dylib_suffix ++ "." ++ ver)
|
||||
|
||||
fullname <- catch (findLibraryFile fname)
|
||||
(\err => -- assume a system library so not
|
||||
-- in our library path
|
||||
pure fname)
|
||||
pure (fname, fullname)
|
||||
|
||||
export
|
||||
copyLib : (String, String) -> Core ()
|
||||
copyLib (lib, fullname)
|
||||
= if lib == fullname
|
||||
then pure ()
|
||||
else do Right bin <- coreLift $ readFromFile fullname
|
||||
| Left err => throw (FileErr fullname err)
|
||||
Right _ <- coreLift $ writeToFile lib bin
|
||||
| Left err => throw (FileErr lib err)
|
||||
pure ()
|
247
src/Compiler/LambdaLift.idr
Normal file
247
src/Compiler/LambdaLift.idr
Normal file
@ -0,0 +1,247 @@
|
||||
module Compiler.LambdaLift
|
||||
|
||||
import Core.CompileExpr
|
||||
import Core.Context
|
||||
import Core.Core
|
||||
import Core.TT
|
||||
|
||||
import Data.List
|
||||
import Data.Vect
|
||||
|
||||
%default covering
|
||||
|
||||
mutual
|
||||
public export
|
||||
data Lifted : List Name -> Type where
|
||||
LLocal : {idx : Nat} -> FC -> (0 p : IsVar x idx vars) -> Lifted vars
|
||||
-- A known function applied to exactly the right number of arguments,
|
||||
-- so the runtime can Just Go
|
||||
LAppName : FC -> Name -> List (Lifted vars) -> Lifted vars
|
||||
-- A known function applied to too few arguments, so the runtime should
|
||||
-- make a closure and wait for the remaining arguments
|
||||
LUnderApp : FC -> Name -> (missing : Nat) ->
|
||||
(args : List (Lifted vars)) -> Lifted vars
|
||||
-- A closure applied to one more argument (so, for example a closure
|
||||
-- which is waiting for another argument before it can run).
|
||||
-- The runtime should add the argument to the closure and run the result
|
||||
-- if it is now fully applied.
|
||||
LApp : FC -> (closure : Lifted vars) -> (arg : Lifted vars) -> Lifted vars
|
||||
LLet : FC -> (x : Name) -> Lifted vars ->
|
||||
Lifted (x :: vars) -> Lifted vars
|
||||
LCon : FC -> Name -> (tag : Maybe Int) -> List (Lifted vars) -> Lifted vars
|
||||
LOp : {arity : _} ->
|
||||
FC -> PrimFn arity -> Vect arity (Lifted vars) -> Lifted vars
|
||||
LExtPrim : FC -> (p : Name) -> List (Lifted vars) -> Lifted vars
|
||||
LConCase : FC -> Lifted vars ->
|
||||
List (LiftedConAlt vars) ->
|
||||
Maybe (Lifted vars) -> Lifted vars
|
||||
LConstCase : FC -> Lifted vars ->
|
||||
List (LiftedConstAlt vars) ->
|
||||
Maybe (Lifted vars) -> Lifted vars
|
||||
LPrimVal : FC -> Constant -> Lifted vars
|
||||
LErased : FC -> Lifted vars
|
||||
LCrash : FC -> String -> Lifted vars
|
||||
|
||||
public export
|
||||
data LiftedConAlt : List Name -> Type where
|
||||
MkLConAlt : Name -> (tag : Maybe Int) -> (args : List Name) ->
|
||||
Lifted (args ++ vars) -> LiftedConAlt vars
|
||||
|
||||
public export
|
||||
data LiftedConstAlt : List Name -> Type where
|
||||
MkLConstAlt : Constant -> Lifted vars -> LiftedConstAlt vars
|
||||
|
||||
public export
|
||||
data LiftedDef : Type where
|
||||
-- We take the outer scope and the function arguments separately so that
|
||||
-- we don't have to reshuffle de Bruijn indices, which is expensive.
|
||||
-- This should be compiled as a function which takes 'args' first,
|
||||
-- then 'reverse scope'.
|
||||
-- (Sorry for the awkward API - it's to do with how the indices are
|
||||
-- arranged for the variables, and it oculd be expensive to reshuffle them!
|
||||
-- See Compiler.ANF for an example of how they get resolved to names)
|
||||
MkLFun : (args : List Name) -> -- function arguments
|
||||
(scope : List Name) -> -- outer scope
|
||||
Lifted (scope ++ args) -> LiftedDef
|
||||
MkLCon : (tag : Maybe Int) -> (arity : Nat) -> (nt : Maybe Nat) -> LiftedDef
|
||||
MkLForeign : (ccs : List String) ->
|
||||
(fargs : List CFType) ->
|
||||
CFType ->
|
||||
LiftedDef
|
||||
MkLError : Lifted [] -> LiftedDef
|
||||
|
||||
mutual
|
||||
export
|
||||
{vs : _} -> Show (Lifted vs) where
|
||||
show (LLocal {idx} _ p) = "!" ++ show (nameAt idx p)
|
||||
show (LAppName fc n args)
|
||||
= show n ++ "(" ++ showSep ", " (map show args) ++ ")"
|
||||
show (LUnderApp fc n m args)
|
||||
= "<" ++ show n ++ " underapp " ++ show m ++ ">(" ++
|
||||
showSep ", " (map show args) ++ ")"
|
||||
show (LApp fc c arg)
|
||||
= show c ++ " @ (" ++ show arg ++ ")"
|
||||
show (LLet fc x val sc)
|
||||
= "%let " ++ show x ++ " = " ++ show val ++ " in " ++ show sc
|
||||
show (LCon fc n t args)
|
||||
= "%con " ++ show n ++ "(" ++ showSep ", " (map show args) ++ ")"
|
||||
show (LOp fc op args)
|
||||
= "%op " ++ show op ++ "(" ++ showSep ", " (toList (map show args)) ++ ")"
|
||||
show (LExtPrim fc p args)
|
||||
= "%extprim " ++ show p ++ "(" ++ showSep ", " (map show args) ++ ")"
|
||||
show (LConCase fc sc alts def)
|
||||
= "%case " ++ show sc ++ " of { "
|
||||
++ showSep "| " (map show alts) ++ " " ++ show def
|
||||
show (LConstCase fc sc alts def)
|
||||
= "%case " ++ show sc ++ " of { "
|
||||
++ showSep "| " (map show alts) ++ " " ++ show def
|
||||
show (LPrimVal _ x) = show x
|
||||
show (LErased _) = "___"
|
||||
show (LCrash _ x) = "%CRASH(" ++ show x ++ ")"
|
||||
|
||||
export
|
||||
{vs : _} -> Show (LiftedConAlt vs) where
|
||||
show (MkLConAlt n t args sc)
|
||||
= "%conalt " ++ show n ++
|
||||
"(" ++ showSep ", " (map show args) ++ ") => " ++ show sc
|
||||
|
||||
export
|
||||
{vs : _} -> Show (LiftedConstAlt vs) where
|
||||
show (MkLConstAlt c sc)
|
||||
= "%constalt(" ++ show c ++ ") => " ++ show sc
|
||||
|
||||
export
|
||||
Show LiftedDef where
|
||||
show (MkLFun args scope exp)
|
||||
= show args ++ show (reverse scope) ++ ": " ++ show exp
|
||||
show (MkLCon tag arity pos)
|
||||
= "Constructor tag " ++ show tag ++ " arity " ++ show arity ++
|
||||
maybe "" (\n => " (newtype by " ++ show n ++ ")") pos
|
||||
show (MkLForeign ccs args ret)
|
||||
= "Foreign call " ++ show ccs ++ " " ++
|
||||
show args ++ " -> " ++ show ret
|
||||
show (MkLError exp) = "Error: " ++ show exp
|
||||
|
||||
|
||||
data Lifts : Type where
|
||||
|
||||
record LDefs where
|
||||
constructor MkLDefs
|
||||
basename : Name -- top level name we're lifting from
|
||||
defs : List (Name, LiftedDef) -- new definitions we made
|
||||
nextName : Int -- name of next definition to lift
|
||||
|
||||
genName : {auto l : Ref Lifts LDefs} ->
|
||||
Core Name
|
||||
genName
|
||||
= do ldefs <- get Lifts
|
||||
let i = nextName ldefs
|
||||
put Lifts (record { nextName = i + 1 } ldefs)
|
||||
pure $ mkName (basename ldefs) i
|
||||
where
|
||||
mkName : Name -> Int -> Name
|
||||
mkName (NS ns b) i = NS ns (mkName b i)
|
||||
mkName (UN n) i = MN n i
|
||||
mkName (DN _ n) i = mkName n i
|
||||
mkName n i = MN (show n) i
|
||||
|
||||
unload : FC -> Lifted vars -> List (Lifted vars) -> Core (Lifted vars)
|
||||
unload fc f [] = pure f
|
||||
unload fc f (a :: as) = unload fc (LApp fc f a) as
|
||||
|
||||
mutual
|
||||
makeLam : {auto l : Ref Lifts LDefs} ->
|
||||
{vars : _} ->
|
||||
FC -> (bound : List Name) ->
|
||||
CExp (bound ++ vars) -> Core (Lifted vars)
|
||||
makeLam fc bound (CLam _ x sc') = makeLam fc (x :: bound) sc'
|
||||
makeLam {vars} fc bound sc
|
||||
= do scl <- liftExp sc
|
||||
n <- genName
|
||||
ldefs <- get Lifts
|
||||
put Lifts (record { defs $= ((n, MkLFun vars bound scl) ::) } ldefs)
|
||||
-- TODO: an optimisation here would be to spot which variables
|
||||
-- aren't used in the new definition, and not abstract over them
|
||||
-- in the new definition. Given that we have to do some messing
|
||||
-- about with indices anyway, it's probably not costly to do.
|
||||
pure $ LUnderApp fc n (length bound) (allVars vars)
|
||||
where
|
||||
allPrfs : (vs : List Name) -> List (Var vs)
|
||||
allPrfs [] = []
|
||||
allPrfs (v :: vs) = MkVar First :: map weaken (allPrfs vs)
|
||||
|
||||
-- apply to all the variables. 'First' will be first in the last, which
|
||||
-- is good, because the most recently bound name is the first argument to
|
||||
-- the resulting function
|
||||
allVars : (vs : List Name) -> List (Lifted vs)
|
||||
allVars vs = map (\ (MkVar p) => LLocal fc p) (allPrfs vs)
|
||||
|
||||
liftExp : {vars : _} ->
|
||||
{auto l : Ref Lifts LDefs} ->
|
||||
CExp vars -> Core (Lifted vars)
|
||||
liftExp (CLocal fc prf) = pure $ LLocal fc prf
|
||||
liftExp (CRef fc n) = pure $ LAppName fc n [] -- probably shouldn't happen!
|
||||
liftExp (CLam fc x sc) = makeLam fc [x] sc
|
||||
liftExp (CLet fc x _ val sc) = pure $ LLet fc x !(liftExp val) !(liftExp sc)
|
||||
liftExp (CApp fc (CRef _ n) args) -- names are applied exactly in compileExp
|
||||
= pure $ LAppName fc n !(traverse liftExp args)
|
||||
liftExp (CApp fc f args)
|
||||
= unload fc !(liftExp f) !(traverse liftExp args)
|
||||
liftExp (CCon fc n t args) = pure $ LCon fc n t !(traverse liftExp args)
|
||||
liftExp (COp fc op args)
|
||||
= pure $ LOp fc op !(traverseArgs args)
|
||||
where
|
||||
traverseArgs : Vect n (CExp vars) -> Core (Vect n (Lifted vars))
|
||||
traverseArgs [] = pure []
|
||||
traverseArgs (a :: as) = pure $ !(liftExp a) :: !(traverseArgs as)
|
||||
liftExp (CExtPrim fc p args) = pure $ LExtPrim fc p !(traverse liftExp args)
|
||||
liftExp (CForce fc tm) = liftExp (CApp fc tm [CErased fc])
|
||||
liftExp (CDelay fc tm) = liftExp (CLam fc (MN "act" 0) (weaken tm))
|
||||
liftExp (CConCase fc sc alts def)
|
||||
= pure $ LConCase fc !(liftExp sc) !(traverse liftConAlt alts)
|
||||
!(traverseOpt liftExp def)
|
||||
where
|
||||
liftConAlt : CConAlt vars -> Core (LiftedConAlt vars)
|
||||
liftConAlt (MkConAlt n t args sc) = pure $ MkLConAlt n t args !(liftExp sc)
|
||||
liftExp (CConstCase fc sc alts def)
|
||||
= pure $ LConstCase fc !(liftExp sc) !(traverse liftConstAlt alts)
|
||||
!(traverseOpt liftExp def)
|
||||
where
|
||||
liftConstAlt : CConstAlt vars -> Core (LiftedConstAlt vars)
|
||||
liftConstAlt (MkConstAlt c sc) = pure $ MkLConstAlt c !(liftExp sc)
|
||||
liftExp (CPrimVal fc c) = pure $ LPrimVal fc c
|
||||
liftExp (CErased fc) = pure $ LErased fc
|
||||
liftExp (CCrash fc str) = pure $ LCrash fc str
|
||||
|
||||
export
|
||||
liftBody : {vars : _} ->
|
||||
Name -> CExp vars -> Core (Lifted vars, List (Name, LiftedDef))
|
||||
liftBody n tm
|
||||
= do l <- newRef Lifts (MkLDefs n [] 0)
|
||||
tml <- liftExp {l} tm
|
||||
ldata <- get Lifts
|
||||
pure (tml, defs ldata)
|
||||
|
||||
lambdaLiftDef : Name -> CDef -> Core (List (Name, LiftedDef))
|
||||
lambdaLiftDef n (MkFun args exp)
|
||||
= do (expl, defs) <- liftBody n exp
|
||||
pure ((n, MkLFun args [] expl) :: defs)
|
||||
lambdaLiftDef n (MkCon t a nt) = pure [(n, MkLCon t a nt)]
|
||||
lambdaLiftDef n (MkForeign ccs fargs ty) = pure [(n, MkLForeign ccs fargs ty)]
|
||||
lambdaLiftDef n (MkError exp)
|
||||
= do (expl, defs) <- liftBody n exp
|
||||
pure ((n, MkLError expl) :: defs)
|
||||
|
||||
-- Return the lambda lifted definitions required for the given name.
|
||||
-- If the name hasn't been compiled yet (via CompileExpr.compileDef) then
|
||||
-- this will return an empty list
|
||||
-- An empty list an error, because on success you will always get at least
|
||||
-- one definition, the lifted definition for the given name.
|
||||
export
|
||||
lambdaLift : {auto c : Ref Ctxt Defs} ->
|
||||
Name -> Core (List (Name, LiftedDef))
|
||||
lambdaLift n
|
||||
= do defs <- get Ctxt
|
||||
Just def <- lookupCtxtExact n (gamma defs) | Nothing => pure []
|
||||
let Just cexpr = compexpr def | Nothing => pure []
|
||||
lambdaLiftDef n cexpr
|
397
src/Compiler/Scheme/Chez.idr
Normal file
397
src/Compiler/Scheme/Chez.idr
Normal file
@ -0,0 +1,397 @@
|
||||
module Compiler.Scheme.Chez
|
||||
|
||||
import Compiler.Common
|
||||
import Compiler.CompileExpr
|
||||
import Compiler.Inline
|
||||
import Compiler.Scheme.Common
|
||||
|
||||
import Core.Context
|
||||
import Core.Directory
|
||||
import Core.Name
|
||||
import Core.Options
|
||||
import Core.TT
|
||||
import Utils.Hex
|
||||
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.NameMap
|
||||
import Data.Strings
|
||||
import Data.Vect
|
||||
import System
|
||||
import System.Info
|
||||
|
||||
%default covering
|
||||
|
||||
|
||||
pathLookup : IO String
|
||||
pathLookup
|
||||
= do path <- getEnv "PATH"
|
||||
let pathList = split (== ':') $ fromMaybe "/usr/bin:/usr/local/bin" path
|
||||
let candidates = [p ++ "/" ++ x | p <- pathList,
|
||||
x <- ["chez", "chezscheme9.5", "scheme"]]
|
||||
e <- firstExists candidates
|
||||
pure $ fromMaybe "/usr/bin/env scheme" e
|
||||
|
||||
findChez : IO String
|
||||
findChez
|
||||
= do Just chez <- getEnv "CHEZ" | Nothing => pathLookup
|
||||
pure chez
|
||||
|
||||
-- Given the chez compiler directives, return a list of pairs of:
|
||||
-- - the library file name
|
||||
-- - the full absolute path of the library file name, if it's in one
|
||||
-- of the library paths managed by Idris
|
||||
-- If it can't be found, we'll assume it's a system library and that chez
|
||||
-- will thus be able to find it.
|
||||
findLibs : {auto c : Ref Ctxt Defs} ->
|
||||
List String -> Core (List (String, String))
|
||||
findLibs ds
|
||||
= do let libs = mapMaybe (isLib . trim) ds
|
||||
traverse locate (nub libs)
|
||||
where
|
||||
isLib : String -> Maybe String
|
||||
isLib d
|
||||
= if isPrefixOf "lib" d
|
||||
then Just (trim (substr 3 (length d) d))
|
||||
else Nothing
|
||||
|
||||
escapeQuotes : String -> String
|
||||
escapeQuotes s = pack $ foldr escape [] $ unpack s
|
||||
where
|
||||
escape : Char -> List Char -> List Char
|
||||
escape '"' cs = '\\' :: '\"' :: cs
|
||||
escape c cs = c :: cs
|
||||
|
||||
schHeader : String -> List String -> String
|
||||
schHeader chez libs
|
||||
= (if os /= "windows" then "#!" ++ chez ++ " --script\n\n" else "") ++
|
||||
"(import (chezscheme))\n" ++
|
||||
"(case (machine-type)\n" ++
|
||||
" [(i3le ti3le a6le ta6le) (load-shared-object \"libc.so.6\")]\n" ++
|
||||
" [(i3osx ti3osx a6osx ta6osx) (load-shared-object \"libc.dylib\")]\n" ++
|
||||
" [(i3nt ti3nt a6nt ta6nt) (load-shared-object \"msvcrt.dll\")]\n" ++
|
||||
" [else (load-shared-object \"libc.so\")])\n\n" ++
|
||||
showSep "\n" (map (\x => "(load-shared-object \"" ++ escapeQuotes x ++ "\")") libs) ++ "\n\n" ++
|
||||
"(let ()\n"
|
||||
|
||||
schFooter : String
|
||||
schFooter = ")"
|
||||
|
||||
showChezChar : Char -> String -> String
|
||||
showChezChar '\\' = ("\\\\" ++)
|
||||
showChezChar c
|
||||
= if c < chr 32 || c > chr 126
|
||||
then (("\\x" ++ asHex (cast c) ++ ";") ++)
|
||||
else strCons c
|
||||
|
||||
showChezString : List Char -> String -> String
|
||||
showChezString [] = id
|
||||
showChezString ('"'::cs) = ("\\\"" ++) . showChezString cs
|
||||
showChezString (c ::cs) = (showChezChar c) . showChezString cs
|
||||
|
||||
chezString : String -> String
|
||||
chezString cs = strCons '"' (showChezString (unpack cs) "\"")
|
||||
|
||||
mutual
|
||||
tySpec : NamedCExp -> Core String
|
||||
-- Primitive types have been converted to names for the purpose of matching
|
||||
-- on types
|
||||
tySpec (NmCon fc (UN "Int") _ []) = pure "int"
|
||||
tySpec (NmCon fc (UN "String") _ []) = pure "string"
|
||||
tySpec (NmCon fc (UN "Double") _ []) = pure "double"
|
||||
tySpec (NmCon fc (UN "Char") _ []) = pure "char"
|
||||
tySpec (NmCon fc (NS _ n) _ [_])
|
||||
= cond [(n == UN "Ptr", pure "void*")]
|
||||
(throw (GenericMsg fc ("Can't pass argument of type " ++ show n ++ " to foreign function")))
|
||||
tySpec (NmCon fc (NS _ n) _ [])
|
||||
= cond [(n == UN "Unit", pure "void"),
|
||||
(n == UN "AnyPtr", pure "void*")]
|
||||
(throw (GenericMsg fc ("Can't pass argument of type " ++ show n ++ " to foreign function")))
|
||||
tySpec ty = throw (GenericMsg (getFC ty) ("Can't pass argument of type " ++ show ty ++ " to foreign function"))
|
||||
|
||||
handleRet : String -> String -> String
|
||||
handleRet "void" op = op ++ " " ++ mkWorld (schConstructor chezString (UN "") (Just 0) [])
|
||||
handleRet _ op = mkWorld op
|
||||
|
||||
getFArgs : NamedCExp -> Core (List (NamedCExp, NamedCExp))
|
||||
getFArgs (NmCon fc _ (Just 0) _) = pure []
|
||||
getFArgs (NmCon fc _ (Just 1) [ty, val, rest]) = pure $ (ty, val) :: !(getFArgs rest)
|
||||
getFArgs arg = throw (GenericMsg (getFC arg) ("Badly formed c call argument list " ++ show arg))
|
||||
|
||||
chezExtPrim : Int -> ExtPrim -> List NamedCExp -> Core String
|
||||
chezExtPrim i CCall [ret, NmPrimVal fc (Str fn), fargs, world]
|
||||
= do args <- getFArgs fargs
|
||||
argTypes <- traverse tySpec (map fst args)
|
||||
retType <- tySpec ret
|
||||
argsc <- traverse (schExp chezExtPrim chezString 0) (map snd args)
|
||||
pure $ handleRet retType ("((foreign-procedure #f " ++ show fn ++ " ("
|
||||
++ showSep " " argTypes ++ ") " ++ retType ++ ") "
|
||||
++ showSep " " argsc ++ ")")
|
||||
chezExtPrim i CCall [ret, fn, args, world]
|
||||
= pure "(error \"bad ffi call\")"
|
||||
-- throw (InternalError ("C FFI calls must be to statically known functions (" ++ show fn ++ ")"))
|
||||
chezExtPrim i GetField [NmPrimVal _ (Str s), _, _, struct,
|
||||
NmPrimVal _ (Str fld), _]
|
||||
= do structsc <- schExp chezExtPrim chezString 0 struct
|
||||
pure $ "(ftype-ref " ++ s ++ " (" ++ fld ++ ") " ++ structsc ++ ")"
|
||||
chezExtPrim i GetField [_,_,_,_,_,_]
|
||||
= pure "(error \"bad getField\")"
|
||||
chezExtPrim i SetField [NmPrimVal _ (Str s), _, _, struct,
|
||||
NmPrimVal _ (Str fld), _, val, world]
|
||||
= do structsc <- schExp chezExtPrim chezString 0 struct
|
||||
valsc <- schExp chezExtPrim chezString 0 val
|
||||
pure $ mkWorld $
|
||||
"(ftype-set! " ++ s ++ " (" ++ fld ++ ") " ++ structsc ++
|
||||
" " ++ valsc ++ ")"
|
||||
chezExtPrim i SetField [_,_,_,_,_,_,_,_]
|
||||
= pure "(error \"bad setField\")"
|
||||
chezExtPrim i SysCodegen []
|
||||
= pure $ "\"chez\""
|
||||
chezExtPrim i prim args
|
||||
= schExtCommon chezExtPrim chezString i prim args
|
||||
|
||||
-- Reference label for keeping track of loaded external libraries
|
||||
data Loaded : Type where
|
||||
|
||||
-- Label for noting which struct types are declared
|
||||
data Structs : Type where
|
||||
|
||||
cftySpec : FC -> CFType -> Core String
|
||||
cftySpec fc CFUnit = pure "void"
|
||||
cftySpec fc CFInt = pure "int"
|
||||
cftySpec fc CFString = pure "string"
|
||||
cftySpec fc CFDouble = pure "double"
|
||||
cftySpec fc CFChar = pure "char"
|
||||
cftySpec fc CFPtr = pure "void*"
|
||||
cftySpec fc (CFFun s t) = pure "void*"
|
||||
cftySpec fc (CFIORes t) = cftySpec fc t
|
||||
cftySpec fc (CFStruct n t) = pure $ "(* " ++ n ++ ")"
|
||||
cftySpec fc t = throw (GenericMsg fc ("Can't pass argument of type " ++ show t ++
|
||||
" to foreign function"))
|
||||
|
||||
cCall : {auto c : Ref Ctxt Defs} ->
|
||||
{auto l : Ref Loaded (List String)} ->
|
||||
String -> FC -> (cfn : String) -> (clib : String) ->
|
||||
List (Name, CFType) -> CFType -> Core (String, String)
|
||||
cCall appdir fc cfn clib args ret
|
||||
= do loaded <- get Loaded
|
||||
lib <- if clib `elem` loaded
|
||||
then pure ""
|
||||
else do (fname, fullname) <- locate clib
|
||||
copyLib (appdir ++ dirSep ++ fname, fullname)
|
||||
put Loaded (clib :: loaded)
|
||||
pure $ "(load-shared-object \""
|
||||
++ escapeQuotes fname
|
||||
++ "\")\n"
|
||||
argTypes <- traverse (\a => cftySpec fc (snd a)) args
|
||||
retType <- cftySpec fc ret
|
||||
let call = "((foreign-procedure #f " ++ show cfn ++ " ("
|
||||
++ showSep " " argTypes ++ ") " ++ retType ++ ") "
|
||||
++ showSep " " !(traverse buildArg args) ++ ")"
|
||||
|
||||
pure (lib, case ret of
|
||||
CFIORes _ => handleRet retType call
|
||||
_ => call)
|
||||
where
|
||||
mkNs : Int -> List CFType -> List (Maybe String)
|
||||
mkNs i [] = []
|
||||
mkNs i (CFWorld :: xs) = Nothing :: mkNs i xs
|
||||
mkNs i (x :: xs) = Just ("cb" ++ show i) :: mkNs (i + 1) xs
|
||||
|
||||
applyLams : String -> List (Maybe String) -> String
|
||||
applyLams n [] = n
|
||||
applyLams n (Nothing :: as) = applyLams ("(" ++ n ++ " #f)") as
|
||||
applyLams n (Just a :: as) = applyLams ("(" ++ n ++ " " ++ a ++ ")") as
|
||||
|
||||
getVal : String -> String
|
||||
getVal str = "(vector-ref " ++ str ++ "1)"
|
||||
|
||||
mkFun : List CFType -> CFType -> String -> String
|
||||
mkFun args ret n
|
||||
= let argns = mkNs 0 args in
|
||||
"(lambda (" ++ showSep " " (mapMaybe id argns) ++ ") " ++
|
||||
(applyLams n argns ++ ")")
|
||||
|
||||
notWorld : CFType -> Bool
|
||||
notWorld CFWorld = False
|
||||
notWorld _ = True
|
||||
|
||||
callback : String -> List CFType -> CFType -> Core String
|
||||
callback n args (CFFun s t) = callback n (s :: args) t
|
||||
callback n args_rev retty
|
||||
= do let args = reverse args_rev
|
||||
argTypes <- traverse (cftySpec fc) (filter notWorld args)
|
||||
retType <- cftySpec fc retty
|
||||
pure $
|
||||
"(let ([c-code (foreign-callable #f " ++
|
||||
mkFun args retty n ++
|
||||
" (" ++ showSep " " argTypes ++ ") " ++ retType ++ ")])" ++
|
||||
" (lock-object c-code) (foreign-callable-entry-point c-code))"
|
||||
|
||||
buildArg : (Name, CFType) -> Core String
|
||||
buildArg (n, CFFun s t) = callback (schName n) [s] t
|
||||
buildArg (n, _) = pure $ schName n
|
||||
|
||||
schemeCall : FC -> (sfn : String) ->
|
||||
List Name -> CFType -> Core String
|
||||
schemeCall fc sfn argns ret
|
||||
= let call = "(" ++ sfn ++ " " ++ showSep " " (map schName argns) ++ ")" in
|
||||
case ret of
|
||||
CFIORes _ => pure $ mkWorld call
|
||||
_ => pure call
|
||||
|
||||
-- Use a calling convention to compile a foreign def.
|
||||
-- Returns any preamble needed for loading libraries, and the body of the
|
||||
-- function call.
|
||||
useCC : {auto c : Ref Ctxt Defs} ->
|
||||
{auto l : Ref Loaded (List String)} ->
|
||||
String -> FC -> List String -> List (Name, CFType) -> CFType -> Core (String, String)
|
||||
useCC appdir fc [] args ret
|
||||
= throw (GenericMsg fc "No recognised foreign calling convention")
|
||||
useCC appdir fc (cc :: ccs) args ret
|
||||
= case parseCC cc of
|
||||
Nothing => useCC appdir fc ccs args ret
|
||||
Just ("scheme", [sfn]) =>
|
||||
do body <- schemeCall fc sfn (map fst args) ret
|
||||
pure ("", body)
|
||||
Just ("C", [cfn, clib]) => cCall appdir fc cfn clib args ret
|
||||
Just ("C", [cfn, clib, chdr]) => cCall appdir fc cfn clib args ret
|
||||
_ => useCC appdir fc ccs args ret
|
||||
|
||||
-- For every foreign arg type, return a name, and whether to pass it to the
|
||||
-- foreign call (we don't pass '%World')
|
||||
mkArgs : Int -> List CFType -> List (Name, Bool)
|
||||
mkArgs i [] = []
|
||||
mkArgs i (CFWorld :: cs) = (MN "farg" i, False) :: mkArgs i cs
|
||||
mkArgs i (c :: cs) = (MN "farg" i, True) :: mkArgs (i + 1) cs
|
||||
|
||||
mkStruct : {auto s : Ref Structs (List String)} ->
|
||||
CFType -> Core String
|
||||
mkStruct (CFStruct n flds)
|
||||
= do defs <- traverse mkStruct (map snd flds)
|
||||
strs <- get Structs
|
||||
if n `elem` strs
|
||||
then pure (concat defs)
|
||||
else do put Structs (n :: strs)
|
||||
pure $ concat defs ++ "(define-ftype " ++ n ++ " (struct\n\t"
|
||||
++ showSep "\n\t" !(traverse showFld flds) ++ "))\n"
|
||||
where
|
||||
showFld : (String, CFType) -> Core String
|
||||
showFld (n, ty) = pure $ "[" ++ n ++ " " ++ !(cftySpec emptyFC ty) ++ "]"
|
||||
mkStruct (CFIORes t) = mkStruct t
|
||||
mkStruct (CFFun a b) = do mkStruct a; mkStruct b
|
||||
mkStruct _ = pure ""
|
||||
|
||||
schFgnDef : {auto c : Ref Ctxt Defs} ->
|
||||
{auto l : Ref Loaded (List String)} ->
|
||||
{auto s : Ref Structs (List String)} ->
|
||||
String -> FC -> Name -> NamedDef -> Core (String, String)
|
||||
schFgnDef appdir fc n (MkNmForeign cs args ret)
|
||||
= do let argns = mkArgs 0 args
|
||||
let allargns = map fst argns
|
||||
let useargns = map fst (filter snd argns)
|
||||
argStrs <- traverse mkStruct args
|
||||
retStr <- mkStruct ret
|
||||
(load, body) <- useCC appdir fc cs (zip useargns args) ret
|
||||
defs <- get Ctxt
|
||||
pure (load,
|
||||
concat argStrs ++ retStr ++
|
||||
"(define " ++ schName !(full (gamma defs) n) ++
|
||||
" (lambda (" ++ showSep " " (map schName allargns) ++ ") " ++
|
||||
body ++ "))\n")
|
||||
schFgnDef _ _ _ _ = pure ("", "")
|
||||
|
||||
getFgnCall : {auto c : Ref Ctxt Defs} ->
|
||||
{auto l : Ref Loaded (List String)} ->
|
||||
{auto s : Ref Structs (List String)} ->
|
||||
String -> (Name, FC, NamedDef) -> Core (String, String)
|
||||
getFgnCall appdir (n, fc, d) = schFgnDef appdir fc n d
|
||||
|
||||
startChez : String -> String
|
||||
startChez target = unlines
|
||||
[ "#!/bin/sh"
|
||||
, ""
|
||||
, "export LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH:$(dirname \"" ++ target ++ "\")\""
|
||||
, "\"" ++ target ++ "\" \"$@\""
|
||||
]
|
||||
|
||||
||| Compile a TT expression to Chez Scheme
|
||||
compileToSS : Ref Ctxt Defs ->
|
||||
String -> ClosedTerm -> (outfile : String) -> Core ()
|
||||
compileToSS c appdir tm outfile
|
||||
= do ds <- getDirectives Chez
|
||||
libs <- findLibs ds
|
||||
traverse_ copyLib libs
|
||||
cdata <- getCompileData Cases tm
|
||||
let ndefs = namedDefs cdata
|
||||
let ctm = forget (mainExpr cdata)
|
||||
|
||||
defs <- get Ctxt
|
||||
l <- newRef {t = List String} Loaded ["libc", "libc 6"]
|
||||
s <- newRef {t = List String} Structs []
|
||||
fgndefs <- traverse (getFgnCall appdir) ndefs
|
||||
compdefs <- traverse (getScheme chezExtPrim chezString) ndefs
|
||||
let code = fastAppend (map snd fgndefs ++ compdefs)
|
||||
main <- schExp chezExtPrim chezString 0 ctm
|
||||
chez <- coreLift findChez
|
||||
support <- readDataFile "chez/support.ss"
|
||||
let scm = schHeader chez (map snd libs) ++
|
||||
support ++ code ++
|
||||
concat (map fst fgndefs) ++
|
||||
main ++ schFooter
|
||||
Right () <- coreLift $ writeFile outfile scm
|
||||
| Left err => throw (FileErr outfile err)
|
||||
coreLift $ chmodRaw outfile 0o755
|
||||
pure ()
|
||||
|
||||
||| Compile a Chez Scheme source file to an executable, daringly with runtime checks off.
|
||||
compileToSO : {auto c : Ref Ctxt Defs} ->
|
||||
(appDirRel : String) -> (outSsAbs : String) -> Core ()
|
||||
compileToSO appDirRel outSsAbs
|
||||
= do let tmpFileAbs = appDirRel ++ dirSep ++ "compileChez"
|
||||
chez <- coreLift $ findChez
|
||||
let build= "#!" ++ chez ++ " --script\n" ++
|
||||
"(parameterize ([optimize-level 3]) (compile-program \"" ++
|
||||
outSsAbs ++ "\"))"
|
||||
Right () <- coreLift $ writeFile tmpFileAbs build
|
||||
| Left err => throw (FileErr tmpFileAbs err)
|
||||
coreLift $ chmodRaw tmpFileAbs 0o755
|
||||
coreLift $ system tmpFileAbs
|
||||
pure ()
|
||||
|
||||
makeSh : String -> String -> Core ()
|
||||
makeSh outShRel outAbs
|
||||
= do Right () <- coreLift $ writeFile outShRel (startChez outAbs)
|
||||
| Left err => throw (FileErr outShRel err)
|
||||
pure ()
|
||||
|
||||
||| Chez Scheme implementation of the `compileExpr` interface.
|
||||
compileExpr : Bool -> Ref Ctxt Defs -> (execDir : String) ->
|
||||
ClosedTerm -> (outfile : String) -> Core (Maybe String)
|
||||
compileExpr makeitso c execDir tm outfile
|
||||
= do let appDirRel = execDir ++ dirSep ++ outfile ++ "_app"
|
||||
coreLift $ mkdirs (splitDir appDirRel)
|
||||
Just cwd <- coreLift currentDir
|
||||
| Nothing => throw (InternalError "Can't get current directory")
|
||||
let outSsAbs = cwd ++ dirSep ++ appDirRel ++ dirSep ++ outfile ++ ".ss"
|
||||
let outSoAbs = cwd ++ dirSep ++ appDirRel ++ dirSep ++ outfile ++ ".so"
|
||||
compileToSS c appDirRel tm outSsAbs
|
||||
logTime "Make SO" $ when makeitso $ compileToSO appDirRel outSsAbs
|
||||
let outShRel = execDir ++ dirSep ++ outfile
|
||||
makeSh outShRel (if makeitso then outSoAbs else outSsAbs)
|
||||
coreLift $ chmodRaw outShRel 0o755
|
||||
pure (Just outShRel)
|
||||
|
||||
||| Chez Scheme implementation of the `executeExpr` interface.
|
||||
||| This implementation simply runs the usual compiler, saving it to a temp file, then interpreting it.
|
||||
executeExpr : Ref Ctxt Defs -> (execDir : String) -> ClosedTerm -> Core ()
|
||||
executeExpr c execDir tm
|
||||
= do Just sh <- compileExpr False c execDir tm "_tmpchez"
|
||||
| Nothing => throw (InternalError "compileExpr returned Nothing")
|
||||
coreLift $ system sh
|
||||
pure ()
|
||||
|
||||
||| Codegen wrapper for Chez scheme implementation.
|
||||
export
|
||||
codegenChez : Codegen
|
||||
codegenChez = MkCG (compileExpr True) executeExpr
|
430
src/Compiler/Scheme/Common.idr
Normal file
430
src/Compiler/Scheme/Common.idr
Normal file
@ -0,0 +1,430 @@
|
||||
module Compiler.Scheme.Common
|
||||
|
||||
import Compiler.Common
|
||||
import Compiler.CompileExpr
|
||||
import Compiler.Inline
|
||||
|
||||
import Core.Context
|
||||
import Core.Name
|
||||
import Core.TT
|
||||
|
||||
import Data.Bool.Extra
|
||||
import Data.List
|
||||
import Data.Vect
|
||||
|
||||
import System.Info
|
||||
|
||||
%default covering
|
||||
|
||||
export
|
||||
firstExists : List String -> IO (Maybe String)
|
||||
firstExists [] = pure Nothing
|
||||
firstExists (x :: xs) = if !(exists x) then pure (Just x) else firstExists xs
|
||||
|
||||
schString : String -> String
|
||||
schString s = concatMap okchar (unpack s)
|
||||
where
|
||||
okchar : Char -> String
|
||||
okchar c = if isAlphaNum c || c =='_'
|
||||
then cast c
|
||||
else "C-" ++ show (cast {to=Int} c)
|
||||
|
||||
export
|
||||
schName : Name -> String
|
||||
schName (NS ns n) = showSep "-" ns ++ "-" ++ schName n
|
||||
schName (UN n) = schString n
|
||||
schName (MN n i) = schString n ++ "-" ++ show i
|
||||
schName (PV n d) = "pat--" ++ schName n
|
||||
schName (DN _ n) = schName n
|
||||
schName (RF n) = "rf--" ++ schString n
|
||||
schName (Nested (i, x) n) = "n--" ++ show i ++ "-" ++ show x ++ "-" ++ schName n
|
||||
schName (CaseBlock x y) = "case--" ++ show x ++ "-" ++ show y
|
||||
schName (WithBlock x y) = "with--" ++ show x ++ "-" ++ show y
|
||||
schName (Resolved i) = "fn--" ++ show i
|
||||
|
||||
export
|
||||
schConstructor : (String -> String) -> Name -> Maybe Int -> List String -> String
|
||||
schConstructor _ _ (Just t) args
|
||||
= "(vector " ++ show t ++ " " ++ showSep " " args ++ ")"
|
||||
schConstructor schString n Nothing args
|
||||
= "(vector " ++ schString (show n) ++ " " ++ showSep " " args ++ ")"
|
||||
|
||||
||| Generate scheme for a plain function.
|
||||
op : String -> List String -> String
|
||||
op o args = "(" ++ o ++ " " ++ showSep " " args ++ ")"
|
||||
|
||||
||| Generate scheme for a boolean operation.
|
||||
boolop : String -> List String -> String
|
||||
boolop o args = "(or (and " ++ op o args ++ " 1) 0)"
|
||||
|
||||
||| Generate scheme for a primitive function.
|
||||
schOp : PrimFn arity -> Vect arity String -> String
|
||||
schOp (Add IntType) [x, y] = op "b+" [x, y, "63"]
|
||||
schOp (Sub IntType) [x, y] = op "b-" [x, y, "63"]
|
||||
schOp (Mul IntType) [x, y] = op "b*" [x, y, "63"]
|
||||
schOp (Div IntType) [x, y] = op "b/" [x, y, "63"]
|
||||
schOp (Add ty) [x, y] = op "+" [x, y]
|
||||
schOp (Sub ty) [x, y] = op "-" [x, y]
|
||||
schOp (Mul ty) [x, y] = op "*" [x, y]
|
||||
schOp (Div IntegerType) [x, y] = op "quotient" [x, y]
|
||||
schOp (Div ty) [x, y] = op "/" [x, y]
|
||||
schOp (Mod ty) [x, y] = op "remainder" [x, y]
|
||||
schOp (Neg ty) [x] = op "-" [x]
|
||||
schOp (ShiftL ty) [x, y] = op "blodwen-shl" [x, y]
|
||||
schOp (ShiftR ty) [x, y] = op "blodwen-shr" [x, y]
|
||||
schOp (BAnd ty) [x, y] = op "blodwen-and" [x, y]
|
||||
schOp (BOr ty) [x, y] = op "blodwen-or" [x, y]
|
||||
schOp (BXOr ty) [x, y] = op "blodwen-xor" [x, y]
|
||||
schOp (LT CharType) [x, y] = boolop "char<?" [x, y]
|
||||
schOp (LTE CharType) [x, y] = boolop "char<=?" [x, y]
|
||||
schOp (EQ CharType) [x, y] = boolop "char=?" [x, y]
|
||||
schOp (GTE CharType) [x, y] = boolop "char>=?" [x, y]
|
||||
schOp (GT CharType) [x, y] = boolop "char>?" [x, y]
|
||||
schOp (LT StringType) [x, y] = boolop "string<?" [x, y]
|
||||
schOp (LTE StringType) [x, y] = boolop "string<=?" [x, y]
|
||||
schOp (EQ StringType) [x, y] = boolop "string=?" [x, y]
|
||||
schOp (GTE StringType) [x, y] = boolop "string>=?" [x, y]
|
||||
schOp (GT StringType) [x, y] = boolop "string>?" [x, y]
|
||||
schOp (LT ty) [x, y] = boolop "<" [x, y]
|
||||
schOp (LTE ty) [x, y] = boolop "<=" [x, y]
|
||||
schOp (EQ ty) [x, y] = boolop "=" [x, y]
|
||||
schOp (GTE ty) [x, y] = boolop ">=" [x, y]
|
||||
schOp (GT ty) [x, y] = boolop ">" [x, y]
|
||||
schOp StrLength [x] = op "string-length" [x]
|
||||
schOp StrHead [x] = op "string-ref" [x, "0"]
|
||||
schOp StrTail [x] = op "substring" [x, "1", op "string-length" [x]]
|
||||
schOp StrIndex [x, i] = op "string-ref" [x, i]
|
||||
schOp StrCons [x, y] = op "string-cons" [x, y]
|
||||
schOp StrAppend [x, y] = op "string-append" [x, y]
|
||||
schOp StrReverse [x] = op "string-reverse" [x]
|
||||
schOp StrSubstr [x, y, z] = op "string-substr" [x, y, z]
|
||||
|
||||
-- `e` is Euler's number, which approximates to: 2.718281828459045
|
||||
schOp DoubleExp [x] = op "exp" [x] -- Base is `e`. Same as: `pow(e, x)`
|
||||
schOp DoubleLog [x] = op "log" [x] -- Base is `e`.
|
||||
schOp DoubleSin [x] = op "sin" [x]
|
||||
schOp DoubleCos [x] = op "cos" [x]
|
||||
schOp DoubleTan [x] = op "tan" [x]
|
||||
schOp DoubleASin [x] = op "asin" [x]
|
||||
schOp DoubleACos [x] = op "acos" [x]
|
||||
schOp DoubleATan [x] = op "atan" [x]
|
||||
schOp DoubleSqrt [x] = op "sqrt" [x]
|
||||
schOp DoubleFloor [x] = op "floor" [x]
|
||||
schOp DoubleCeiling [x] = op "ceiling" [x]
|
||||
|
||||
schOp (Cast IntType StringType) [x] = op "number->string" [x]
|
||||
schOp (Cast IntegerType StringType) [x] = op "number->string" [x]
|
||||
schOp (Cast DoubleType StringType) [x] = op "number->string" [x]
|
||||
schOp (Cast CharType StringType) [x] = op "string" [x]
|
||||
|
||||
schOp (Cast IntType IntegerType) [x] = x
|
||||
schOp (Cast DoubleType IntegerType) [x] = op "exact-floor" [x]
|
||||
schOp (Cast CharType IntegerType) [x] = op "char->integer" [x]
|
||||
schOp (Cast StringType IntegerType) [x] = op "cast-string-int" [x]
|
||||
|
||||
schOp (Cast IntegerType IntType) [x] = x
|
||||
schOp (Cast DoubleType IntType) [x] = op "exact-floor" [x]
|
||||
schOp (Cast StringType IntType) [x] = op "cast-string-int" [x]
|
||||
schOp (Cast CharType IntType) [x] = op "char->integer" [x]
|
||||
|
||||
schOp (Cast IntegerType DoubleType) [x] = op "exact->inexact" [x]
|
||||
schOp (Cast IntType DoubleType) [x] = op "exact->inexact" [x]
|
||||
schOp (Cast StringType DoubleType) [x] = op "cast-string-double" [x]
|
||||
|
||||
schOp (Cast IntType CharType) [x] = op "integer->char" [x]
|
||||
|
||||
schOp (Cast from to) [x] = "(blodwen-error-quit \"Invalid cast " ++ show from ++ "->" ++ show to ++ "\")"
|
||||
|
||||
schOp BelieveMe [_,_,x] = x
|
||||
schOp Crash [_,msg] = "(blodwen-error-quit (string-append \"ERROR: \" " ++ msg ++ "))"
|
||||
|
||||
||| Extended primitives for the scheme backend, outside the standard set of primFn
|
||||
public export
|
||||
data ExtPrim = CCall | SchemeCall
|
||||
| NewIORef | ReadIORef | WriteIORef
|
||||
| NewArray | ArrayGet | ArraySet
|
||||
| GetField | SetField
|
||||
| VoidElim
|
||||
| SysOS | SysCodegen
|
||||
| Unknown Name
|
||||
|
||||
export
|
||||
Show ExtPrim where
|
||||
show CCall = "CCall"
|
||||
show SchemeCall = "SchemeCall"
|
||||
show NewIORef = "NewIORef"
|
||||
show ReadIORef = "ReadIORef"
|
||||
show WriteIORef = "WriteIORef"
|
||||
show NewArray = "NewArray"
|
||||
show ArrayGet = "ArrayGet"
|
||||
show ArraySet = "ArraySet"
|
||||
show GetField = "GetField"
|
||||
show SetField = "SetField"
|
||||
show VoidElim = "VoidElim"
|
||||
show SysOS = "SysOS"
|
||||
show SysCodegen = "SysCodegen"
|
||||
show (Unknown n) = "Unknown " ++ show n
|
||||
|
||||
||| Match on a user given name to get the scheme primitive
|
||||
toPrim : Name -> ExtPrim
|
||||
toPrim pn@(NS _ n)
|
||||
= cond [(n == UN "prim__schemeCall", SchemeCall),
|
||||
(n == UN "prim__cCall", CCall),
|
||||
(n == UN "prim__newIORef", NewIORef),
|
||||
(n == UN "prim__readIORef", ReadIORef),
|
||||
(n == UN "prim__writeIORef", WriteIORef),
|
||||
(n == UN "prim__newArray", NewArray),
|
||||
(n == UN "prim__arrayGet", ArrayGet),
|
||||
(n == UN "prim__arraySet", ArraySet),
|
||||
(n == UN "prim__getField", GetField),
|
||||
(n == UN "prim__setField", SetField),
|
||||
(n == UN "void", VoidElim),
|
||||
(n == UN "prim__os", SysOS),
|
||||
(n == UN "prim__codegen", SysCodegen)
|
||||
]
|
||||
(Unknown pn)
|
||||
toPrim pn = Unknown pn
|
||||
|
||||
export
|
||||
mkWorld : String -> String
|
||||
mkWorld res = res -- MkIORes is a newtype now! schConstructor 0 [res, "#f"] -- MkIORes
|
||||
|
||||
schConstant : (String -> String) -> Constant -> String
|
||||
schConstant _ (I x) = show x
|
||||
schConstant _ (BI x) = show x
|
||||
schConstant schString (Str x) = schString x
|
||||
schConstant _ (Ch x)
|
||||
= if (the Int (cast x) >= 32 && the Int (cast x) < 127)
|
||||
then "#\\" ++ cast x
|
||||
else "(integer->char " ++ show (the Int (cast x)) ++ ")"
|
||||
schConstant _ (Db x) = show x
|
||||
schConstant _ WorldVal = "#f"
|
||||
schConstant _ IntType = "#t"
|
||||
schConstant _ IntegerType = "#t"
|
||||
schConstant _ StringType = "#t"
|
||||
schConstant _ CharType = "#t"
|
||||
schConstant _ DoubleType = "#t"
|
||||
schConstant _ WorldType = "#t"
|
||||
|
||||
schCaseDef : Maybe String -> String
|
||||
schCaseDef Nothing = ""
|
||||
schCaseDef (Just tm) = "(else " ++ tm ++ ")"
|
||||
|
||||
export
|
||||
schArglist : List Name -> String
|
||||
schArglist [] = ""
|
||||
schArglist [x] = schName x
|
||||
schArglist (x :: xs) = schName x ++ " " ++ schArglist xs
|
||||
|
||||
mutual
|
||||
used : Name -> NamedCExp -> Bool
|
||||
used n (NmLocal fc n') = n == n'
|
||||
used n (NmRef _ _) = False
|
||||
used n (NmLam _ _ sc) = used n sc
|
||||
used n (NmLet _ _ v sc) = used n v || used n sc
|
||||
used n (NmApp _ f args) = used n f || anyTrue (map (used n) args)
|
||||
used n (NmCon _ _ _ args) = anyTrue (map (used n) args)
|
||||
used n (NmOp _ _ args) = anyTrue (toList (map (used n) args))
|
||||
used n (NmExtPrim _ _ args) = anyTrue (map (used n) args)
|
||||
used n (NmForce _ t) = used n t
|
||||
used n (NmDelay _ t) = used n t
|
||||
used n (NmConCase _ sc alts def)
|
||||
= used n sc || anyTrue (map (usedCon n) alts)
|
||||
|| maybe False (used n) def
|
||||
used n (NmConstCase _ sc alts def)
|
||||
= used n sc || anyTrue (map (usedConst n) alts)
|
||||
|| maybe False (used n) def
|
||||
used n _ = False
|
||||
|
||||
usedCon : Name -> NamedConAlt -> Bool
|
||||
usedCon n (MkNConAlt _ _ _ sc) = used n sc
|
||||
|
||||
usedConst : Name -> NamedConstAlt -> Bool
|
||||
usedConst n (MkNConstAlt _ sc) = used n sc
|
||||
|
||||
parameters (schExtPrim : Int -> ExtPrim -> List NamedCExp -> Core String,
|
||||
schString : String -> String)
|
||||
showTag : Name -> Maybe Int -> String
|
||||
showTag n (Just i) = show i
|
||||
showTag n Nothing = schString (show n)
|
||||
|
||||
mutual
|
||||
schConAlt : Int -> String -> NamedConAlt -> Core String
|
||||
schConAlt i target (MkNConAlt n tag args sc)
|
||||
= pure $ "((" ++ showTag n tag ++ ") "
|
||||
++ bindArgs 1 args !(schExp i sc) ++ ")"
|
||||
where
|
||||
bindArgs : Int -> (ns : List Name) -> String -> String
|
||||
bindArgs i [] body = body
|
||||
bindArgs i (n :: ns) body
|
||||
= if used n sc
|
||||
then "(let ((" ++ schName n ++ " " ++ "(vector-ref " ++ target ++ " " ++ show i ++ "))) "
|
||||
++ bindArgs (i + 1) ns body ++ ")"
|
||||
else bindArgs (i + 1) ns body
|
||||
|
||||
schConUncheckedAlt : Int -> String -> NamedConAlt -> Core String
|
||||
schConUncheckedAlt i target (MkNConAlt n tag args sc)
|
||||
= pure $ bindArgs 1 args !(schExp i sc)
|
||||
where
|
||||
bindArgs : Int -> (ns : List Name) -> String -> String
|
||||
bindArgs i [] body = body
|
||||
bindArgs i (n :: ns) body
|
||||
= if used n sc
|
||||
then "(let ((" ++ schName n ++ " " ++ "(vector-ref " ++ target ++ " " ++ show i ++ "))) "
|
||||
++ bindArgs (i + 1) ns body ++ ")"
|
||||
else bindArgs (i + 1) ns body
|
||||
|
||||
schConstAlt : Int -> String -> NamedConstAlt -> Core String
|
||||
schConstAlt i target (MkNConstAlt c exp)
|
||||
= pure $ "((equal? " ++ target ++ " " ++ schConstant schString c ++ ") " ++ !(schExp i exp) ++ ")"
|
||||
|
||||
-- oops, no traverse for Vect in Core
|
||||
schArgs : Int -> Vect n NamedCExp -> Core (Vect n String)
|
||||
schArgs i [] = pure []
|
||||
schArgs i (arg :: args) = pure $ !(schExp i arg) :: !(schArgs i args)
|
||||
|
||||
export
|
||||
schExp : Int -> NamedCExp -> Core String
|
||||
schExp i (NmLocal fc n) = pure $ schName n
|
||||
schExp i (NmRef fc n) = pure $ schName n
|
||||
schExp i (NmLam fc x sc)
|
||||
= do sc' <- schExp i sc
|
||||
pure $ "(lambda (" ++ schName x ++ ") " ++ sc' ++ ")"
|
||||
schExp i (NmLet fc x val sc)
|
||||
= do val' <- schExp i val
|
||||
sc' <- schExp i sc
|
||||
pure $ "(let ((" ++ schName x ++ " " ++ val' ++ ")) " ++ sc' ++ ")"
|
||||
schExp i (NmApp fc x [])
|
||||
= pure $ "(" ++ !(schExp i x) ++ ")"
|
||||
schExp i (NmApp fc x args)
|
||||
= pure $ "(" ++ !(schExp i x) ++ " " ++ showSep " " !(traverse (schExp i) args) ++ ")"
|
||||
schExp i (NmCon fc x tag args)
|
||||
= pure $ schConstructor schString x tag !(traverse (schExp i) args)
|
||||
schExp i (NmOp fc op args)
|
||||
= pure $ schOp op !(schArgs i args)
|
||||
schExp i (NmExtPrim fc p args)
|
||||
= schExtPrim i (toPrim p) args
|
||||
schExp i (NmForce fc t) = pure $ "(" ++ !(schExp i t) ++ ")"
|
||||
schExp i (NmDelay fc t) = pure $ "(lambda () " ++ !(schExp i t) ++ ")"
|
||||
schExp i (NmConCase fc sc [] def)
|
||||
= do tcode <- schExp (i+1) sc
|
||||
defc <- maybe (pure "'erased") (schExp i) def
|
||||
let n = "sc" ++ show i
|
||||
pure $ "(let ((" ++ n ++ " " ++ tcode ++ ")) "
|
||||
++ defc ++ ")"
|
||||
schExp i (NmConCase fc sc [alt] Nothing)
|
||||
= do tcode <- schExp (i+1) sc
|
||||
let n = "sc" ++ show i
|
||||
pure $ "(let ((" ++ n ++ " " ++ tcode ++ ")) " ++
|
||||
!(schConUncheckedAlt (i+1) n alt) ++ ")"
|
||||
schExp i (NmConCase fc sc alts Nothing)
|
||||
= do tcode <- schExp (i+1) sc
|
||||
let n = "sc" ++ show i
|
||||
pure $ "(let ((" ++ n ++ " " ++ tcode ++ ")) (case (vector-ref " ++ n ++ " 0) "
|
||||
++ !(showAlts n alts) ++
|
||||
"))"
|
||||
where
|
||||
showAlts : String -> List NamedConAlt -> Core String
|
||||
showAlts n [] = pure ""
|
||||
showAlts n [alt]
|
||||
= pure $ "(else " ++ !(schConUncheckedAlt (i + 1) n alt) ++ ")"
|
||||
showAlts n (alt :: alts)
|
||||
= pure $ !(schConAlt (i + 1) n alt) ++ " " ++
|
||||
!(showAlts n alts)
|
||||
schExp i (NmConCase fc sc alts def)
|
||||
= do tcode <- schExp (i+1) sc
|
||||
defc <- maybe (pure Nothing) (\v => pure (Just !(schExp i v))) def
|
||||
let n = "sc" ++ show i
|
||||
pure $ "(let ((" ++ n ++ " " ++ tcode ++ ")) (case (vector-ref " ++ n ++ " 0) "
|
||||
++ showSep " " !(traverse (schConAlt (i+1) n) alts)
|
||||
++ schCaseDef defc ++ "))"
|
||||
schExp i (NmConstCase fc sc alts Nothing)
|
||||
= do tcode <- schExp (i+1) sc
|
||||
let n = "sc" ++ show i
|
||||
pure $ "(let ((" ++ n ++ " " ++ tcode ++ ")) (cond "
|
||||
++ !(showConstAlts n alts)
|
||||
++ "))"
|
||||
where
|
||||
showConstAlts : String -> List NamedConstAlt -> Core String
|
||||
showConstAlts n [] = pure ""
|
||||
showConstAlts n [MkNConstAlt c exp]
|
||||
= pure $ "(else " ++ !(schExp (i + 1) exp) ++ ")"
|
||||
showConstAlts n (alt :: alts)
|
||||
= pure $ !(schConstAlt (i + 1) n alt) ++ " " ++
|
||||
!(showConstAlts n alts)
|
||||
schExp i (NmConstCase fc sc alts def)
|
||||
= do defc <- maybe (pure Nothing) (\v => pure (Just !(schExp i v))) def
|
||||
tcode <- schExp (i+1) sc
|
||||
let n = "sc" ++ show i
|
||||
pure $ "(let ((" ++ n ++ " " ++ tcode ++ ")) (cond "
|
||||
++ showSep " " !(traverse (schConstAlt (i+1) n) alts)
|
||||
++ schCaseDef defc ++ "))"
|
||||
schExp i (NmPrimVal fc c) = pure $ schConstant schString c
|
||||
schExp i (NmErased fc) = pure "'erased"
|
||||
schExp i (NmCrash fc msg) = pure $ "(blodwen-error-quit " ++ show msg ++ ")"
|
||||
|
||||
-- Need to convert the argument (a list of scheme arguments that may
|
||||
-- have been constructed at run time) to a scheme list to be passed to apply
|
||||
readArgs : Int -> NamedCExp -> Core String
|
||||
readArgs i tm = pure $ "(blodwen-read-args " ++ !(schExp i tm) ++ ")"
|
||||
|
||||
fileOp : String -> String
|
||||
fileOp op = "(blodwen-file-op (lambda () " ++ op ++ "))"
|
||||
|
||||
-- External primitives which are common to the scheme codegens (they can be
|
||||
-- overridden)
|
||||
export
|
||||
schExtCommon : Int -> ExtPrim -> List NamedCExp -> Core String
|
||||
schExtCommon i SchemeCall [ret, NmPrimVal fc (Str fn), args, world]
|
||||
= pure $ mkWorld ("(apply " ++ fn ++" "
|
||||
++ !(readArgs i args) ++ ")")
|
||||
schExtCommon i SchemeCall [ret, fn, args, world]
|
||||
= pure $ mkWorld ("(apply (eval (string->symbol " ++ !(schExp i fn) ++")) "
|
||||
++ !(readArgs i args) ++ ")")
|
||||
schExtCommon i NewIORef [_, val, world]
|
||||
= pure $ mkWorld $ "(box " ++ !(schExp i val) ++ ")"
|
||||
schExtCommon i ReadIORef [_, ref, world]
|
||||
= pure $ mkWorld $ "(unbox " ++ !(schExp i ref) ++ ")"
|
||||
schExtCommon i WriteIORef [_, ref, val, world]
|
||||
= pure $ mkWorld $ "(set-box! "
|
||||
++ !(schExp i ref) ++ " "
|
||||
++ !(schExp i val) ++ ")"
|
||||
schExtCommon i NewArray [_, size, val, world]
|
||||
= pure $ mkWorld $ "(make-vector " ++ !(schExp i size) ++ " "
|
||||
++ !(schExp i val) ++ ")"
|
||||
schExtCommon i ArrayGet [_, arr, pos, world]
|
||||
= pure $ mkWorld $ "(vector-ref " ++ !(schExp i arr) ++ " "
|
||||
++ !(schExp i pos) ++ ")"
|
||||
schExtCommon i ArraySet [_, arr, pos, val, world]
|
||||
= pure $ mkWorld $ "(vector-set! " ++ !(schExp i arr) ++ " "
|
||||
++ !(schExp i pos) ++ " "
|
||||
++ !(schExp i val) ++ ")"
|
||||
schExtCommon i VoidElim [_, _]
|
||||
= pure "(display \"Error: Executed 'void'\")"
|
||||
schExtCommon i SysOS []
|
||||
= pure $ show os
|
||||
schExtCommon i (Unknown n) args
|
||||
= throw (InternalError ("Can't compile unknown external primitive " ++ show n))
|
||||
schExtCommon i prim args
|
||||
= throw (InternalError ("Badly formed external primitive " ++ show prim
|
||||
++ " " ++ show args))
|
||||
|
||||
schDef : {auto c : Ref Ctxt Defs} ->
|
||||
Name -> NamedDef -> Core String
|
||||
schDef n (MkNmFun args exp)
|
||||
= pure $ "(define " ++ schName !(getFullName n) ++ " (lambda (" ++ schArglist args ++ ") "
|
||||
++ !(schExp 0 exp) ++ "))\n"
|
||||
schDef n (MkNmError exp)
|
||||
= pure $ "(define (" ++ schName !(getFullName n) ++ " . any-args) " ++ !(schExp 0 exp) ++ ")\n"
|
||||
schDef n (MkNmForeign _ _ _) = pure "" -- compiled by specific back end
|
||||
schDef n (MkNmCon t a _) = pure "" -- Nothing to compile here
|
||||
|
||||
-- Convert the name to scheme code
|
||||
-- (There may be no code generated, for example if it's a constructor)
|
||||
export
|
||||
getScheme : {auto c : Ref Ctxt Defs} ->
|
||||
(schExtPrim : Int -> ExtPrim -> List NamedCExp -> Core String) ->
|
||||
(schString : String -> String) ->
|
||||
(Name, FC, NamedDef) -> Core String
|
||||
getScheme schExtPrim schString (n, fc, d)
|
||||
= schDef schExtPrim schString n d
|
319
src/Compiler/Scheme/Gambit.idr
Normal file
319
src/Compiler/Scheme/Gambit.idr
Normal file
@ -0,0 +1,319 @@
|
||||
module Compiler.Scheme.Gambit
|
||||
|
||||
import Compiler.Common
|
||||
import Compiler.CompileExpr
|
||||
import Compiler.Inline
|
||||
import Compiler.Scheme.Common
|
||||
|
||||
import Core.Context
|
||||
import Core.Directory
|
||||
import Core.Name
|
||||
import Core.Options
|
||||
import Core.TT
|
||||
import Utils.Hex
|
||||
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.NameMap
|
||||
import Data.Strings
|
||||
import Data.Vect
|
||||
import System
|
||||
import System.Info
|
||||
|
||||
%default covering
|
||||
|
||||
-- TODO Look for gsi-script, then gsi
|
||||
findGSI : IO String
|
||||
findGSI =
|
||||
do env <- getEnv "GAMBIT_GSI"
|
||||
pure $ fromMaybe "/usr/bin/env -S gsi-script" env
|
||||
|
||||
-- TODO Look for gsc-script, then gsc
|
||||
findGSC : IO String
|
||||
findGSC =
|
||||
do env <- getEnv "GAMBIT_GSC"
|
||||
pure $ fromMaybe "/usr/bin/env -S gsc-script" env
|
||||
|
||||
schHeader : String
|
||||
schHeader = "(declare (block)
|
||||
(inlining-limit 450)
|
||||
(standard-bindings)
|
||||
(extended-bindings)
|
||||
(not safe)
|
||||
(optimize-dead-definitions))\n"
|
||||
|
||||
showGambitChar : Char -> String -> String
|
||||
showGambitChar '\\' = ("\\\\" ++)
|
||||
showGambitChar c
|
||||
= if c < chr 32 -- XXX
|
||||
then (("\\x" ++ asHex (cast c) ++ ";") ++)
|
||||
else strCons c
|
||||
|
||||
showGambitString : List Char -> String -> String
|
||||
showGambitString [] = id
|
||||
showGambitString ('"'::cs) = ("\\\"" ++) . showGambitString cs
|
||||
showGambitString (c::cs) = (showGambitChar c) . showGambitString cs
|
||||
|
||||
gambitString : String -> String
|
||||
gambitString cs = strCons '"' (showGambitString (unpack cs) "\"")
|
||||
|
||||
mutual
|
||||
-- Primitive types have been converted to names for the purpose of matching
|
||||
-- on types
|
||||
tySpec : NamedCExp -> Core String
|
||||
tySpec (NmCon fc (UN "Int") _ []) = pure "int"
|
||||
tySpec (NmCon fc (UN "String") _ []) = pure "UTF-8-string"
|
||||
tySpec (NmCon fc (UN "Double") _ []) = pure "double"
|
||||
tySpec (NmCon fc (UN "Char") _ []) = pure "char"
|
||||
tySpec (NmCon fc (NS _ n) _ [_])
|
||||
= cond [(n == UN "Ptr", pure "(pointer void)")]
|
||||
(throw (GenericMsg fc ("Can't pass argument of type " ++ show n ++ " to foreign function")))
|
||||
tySpec (NmCon fc (NS _ n) _ [])
|
||||
= cond [(n == UN "Unit", pure "void"),
|
||||
(n == UN "AnyPtr", pure "(pointer void)")]
|
||||
(throw (GenericMsg fc ("Can't pass argument of type " ++ show n ++ " to foreign function")))
|
||||
tySpec ty = throw (GenericMsg (getFC ty) ("Can't pass argument of type " ++ show ty ++ " to foreign function"))
|
||||
|
||||
handleRet : String -> String -> String
|
||||
handleRet "void" op = op ++ " " ++ mkWorld (schConstructor gambitString (UN "") (Just 0) [])
|
||||
handleRet _ op = mkWorld op
|
||||
|
||||
getFArgs : NamedCExp -> Core (List (NamedCExp, NamedCExp))
|
||||
getFArgs (NmCon fc _ (Just 0) _) = pure []
|
||||
getFArgs (NmCon fc _ (Just 1) [ty, val, rest]) = pure $ (ty, val) :: !(getFArgs rest)
|
||||
getFArgs arg = throw (GenericMsg (getFC arg) ("Badly formed c call argument list " ++ show arg))
|
||||
|
||||
gambitPrim : Int -> ExtPrim -> List NamedCExp -> Core String
|
||||
gambitPrim i CCall [ret, NmPrimVal fc (Str fn), fargs, world]
|
||||
= do args <- getFArgs fargs
|
||||
argTypes <- traverse tySpec (map fst args)
|
||||
retType <- tySpec ret
|
||||
argsc <- traverse (schExp gambitPrim gambitString 0) (map snd args)
|
||||
pure $ handleRet retType ("((c-lambda (" ++ showSep " " argTypes ++ ") "
|
||||
++ retType ++ " " ++ show fn ++ ") "
|
||||
++ showSep " " argsc ++ ")")
|
||||
gambitPrim i CCall [ret, fn, args, world]
|
||||
= pure "(error \"bad ffi call\")"
|
||||
gambitPrim i GetField [NmPrimVal _ (Str s), _, _, struct,
|
||||
NmPrimVal _ (Str fld), _]
|
||||
= do structsc <- schExp gambitPrim gambitString 0 struct
|
||||
pure $ "(" ++ s ++ "-" ++ fld ++ " " ++ structsc ++ ")"
|
||||
gambitPrim i GetField [_,_,_,_,_,_]
|
||||
= pure "(error \"bad getField\")"
|
||||
gambitPrim i SetField [NmPrimVal _ (Str s), _, _, struct,
|
||||
NmPrimVal _ (Str fld), _, val, world]
|
||||
= do structsc <- schExp gambitPrim gambitString 0 struct
|
||||
valsc <- schExp gambitPrim gambitString 0 val
|
||||
pure $ mkWorld $
|
||||
"(" ++ s ++ "-" ++ fld ++ "-set! " ++ structsc ++ " " ++ valsc ++ ")"
|
||||
gambitPrim i SetField [_,_,_,_,_,_,_,_]
|
||||
= pure "(error \"bad setField\")"
|
||||
gambitPrim i SysCodegen []
|
||||
= pure $ "\"gambit\""
|
||||
gambitPrim i prim args
|
||||
= schExtCommon gambitPrim gambitString i prim args
|
||||
|
||||
-- Reference label for keeping track of loaded external libraries
|
||||
data Loaded : Type where
|
||||
|
||||
-- Label for noting which struct types are declared
|
||||
data Structs : Type where
|
||||
|
||||
cftySpec : FC -> CFType -> Core String
|
||||
cftySpec fc CFUnit = pure "void"
|
||||
cftySpec fc CFInt = pure "int"
|
||||
cftySpec fc CFString = pure "UTF-8-string"
|
||||
cftySpec fc CFDouble = pure "double"
|
||||
cftySpec fc CFChar = pure "char"
|
||||
cftySpec fc CFPtr = pure "(pointer void)"
|
||||
cftySpec fc (CFIORes t) = cftySpec fc t
|
||||
cftySpec fc (CFStruct n t) = pure $ n ++ "*/nonnull"
|
||||
cftySpec fc (CFFun s t) = funTySpec [s] t
|
||||
where
|
||||
funTySpec : List CFType -> CFType -> Core String
|
||||
funTySpec args (CFFun CFWorld t) = funTySpec args t
|
||||
funTySpec args (CFFun s t) = funTySpec (s :: args) t
|
||||
funTySpec args retty
|
||||
= do rtyspec <- cftySpec fc retty
|
||||
argspecs <- traverse (cftySpec fc) (reverse args)
|
||||
pure $ "(function (" ++ showSep " " argspecs ++ ") " ++ rtyspec ++ ")"
|
||||
cftySpec fc t = throw (GenericMsg fc ("Can't pass argument of type " ++ show t ++
|
||||
" to foreign function"))
|
||||
|
||||
cCall : {auto c : Ref Ctxt Defs} ->
|
||||
{auto l : Ref Loaded (List String)} ->
|
||||
FC -> (cfn : String) -> (clib : String) ->
|
||||
List (Name, CFType) -> CFType -> Core (String, String)
|
||||
cCall fc cfn clib args ret
|
||||
= do -- loaded <- get Loaded
|
||||
-- lib <- if clib `elem` loaded
|
||||
-- then pure ""
|
||||
-- else do (fname, fullname) <- locate clib
|
||||
-- copyLib (fname, fullname)
|
||||
-- put Loaded (clib :: loaded)
|
||||
-- pure ""
|
||||
argTypes <- traverse (\a => cftySpec fc (snd a)) args
|
||||
retType <- cftySpec fc ret
|
||||
let call = "((c-lambda (" ++ showSep " " argTypes ++ ") "
|
||||
++ retType ++ " " ++ show cfn ++ ") "
|
||||
++ showSep " " !(traverse buildArg args) ++ ")"
|
||||
|
||||
pure ("", case ret of -- XXX
|
||||
CFIORes _ => handleRet retType call
|
||||
_ => call)
|
||||
where
|
||||
mkNs : Int -> List CFType -> List (Maybe String)
|
||||
mkNs i [] = []
|
||||
mkNs i (CFWorld :: xs) = Nothing :: mkNs i xs
|
||||
mkNs i (x :: xs) = Just ("cb" ++ show i) :: mkNs (i + 1) xs
|
||||
|
||||
applyLams : String -> List (Maybe String) -> String
|
||||
applyLams n [] = n
|
||||
applyLams n (Nothing :: as) = applyLams ("(" ++ n ++ " #f)") as
|
||||
applyLams n (Just a :: as) = applyLams ("(" ++ n ++ " " ++ a ++ ")") as
|
||||
|
||||
mkFun : List CFType -> CFType -> String -> String
|
||||
mkFun args ret n
|
||||
= let argns = mkNs 0 args in
|
||||
"(lambda (" ++ showSep " " (mapMaybe id argns) ++ ") "
|
||||
++ (applyLams n argns ++ ")")
|
||||
|
||||
notWorld : CFType -> Bool
|
||||
notWorld CFWorld = False
|
||||
notWorld _ = True
|
||||
|
||||
callback : String -> List CFType -> CFType -> Core String
|
||||
callback n args (CFFun s t) = callback n (s :: args) t
|
||||
callback n args_rev retty
|
||||
= do let args = reverse args_rev
|
||||
argTypes <- traverse (cftySpec fc) (filter notWorld args)
|
||||
retType <- cftySpec fc retty
|
||||
pure $ mkFun args retty n -- FIXME Needs a top-level c-define
|
||||
|
||||
buildArg : (Name, CFType) -> Core String
|
||||
buildArg (n, CFFun s t) = callback (schName n) [s] t
|
||||
buildArg (n, _) = pure $ schName n
|
||||
|
||||
schemeCall : FC -> (sfn : String) ->
|
||||
List Name -> CFType -> Core String
|
||||
schemeCall fc sfn argns ret
|
||||
= let call = "(" ++ sfn ++ " " ++ showSep " " (map schName argns) ++ ")" in
|
||||
case ret of
|
||||
CFIORes _ => pure $ mkWorld call
|
||||
_ => pure call
|
||||
|
||||
-- Use a calling convention to compile a foreign def.
|
||||
-- Returns any preamble needed for loading libraries, and the body of the
|
||||
-- function call.
|
||||
useCC : {auto c : Ref Ctxt Defs} ->
|
||||
{auto l : Ref Loaded (List String)} ->
|
||||
FC -> List String -> List (Name, CFType) -> CFType -> Core (String, String)
|
||||
useCC fc [] args ret
|
||||
= throw (GenericMsg fc "No recognised foreign calling convention")
|
||||
useCC fc (cc :: ccs) args ret
|
||||
= case parseCC cc of
|
||||
Nothing => useCC fc ccs args ret
|
||||
Just ("scheme", [sfn]) =>
|
||||
do body <- schemeCall fc sfn (map fst args) ret
|
||||
pure ("", body)
|
||||
Just ("C", [cfn, clib]) => cCall fc cfn clib args ret
|
||||
Just ("C", [cfn, clib, chdr]) => cCall fc cfn clib args ret
|
||||
_ => useCC fc ccs args ret
|
||||
|
||||
-- For every foreign arg type, return a name, and whether to pass it to the
|
||||
-- foreign call (we don't pass '%World')
|
||||
mkArgs : Int -> List CFType -> List (Name, Bool)
|
||||
mkArgs i [] = []
|
||||
mkArgs i (CFWorld :: cs) = (MN "farg" i, False) :: mkArgs i cs
|
||||
mkArgs i (c :: cs) = (MN "farg" i, True) :: mkArgs (i + 1) cs
|
||||
|
||||
mkStruct : {auto s : Ref Structs (List String)} ->
|
||||
CFType -> Core String
|
||||
mkStruct (CFStruct n flds)
|
||||
= do defs <- traverse mkStruct (map snd flds)
|
||||
strs <- get Structs
|
||||
if n `elem` strs
|
||||
then pure (concat defs)
|
||||
else do put Structs (n :: strs)
|
||||
pure $ concat defs ++ "(define-c-struct " ++ n ++ " "
|
||||
++ showSep " " !(traverse showFld flds) ++ ")\n"
|
||||
where
|
||||
showFld : (String, CFType) -> Core String
|
||||
showFld (n, ty) = pure $ "(" ++ n ++ " " ++ !(cftySpec emptyFC ty) ++ ")"
|
||||
mkStruct (CFIORes t) = mkStruct t
|
||||
mkStruct (CFFun a b) = do mkStruct a; mkStruct b
|
||||
mkStruct _ = pure ""
|
||||
|
||||
schFgnDef : {auto c : Ref Ctxt Defs} ->
|
||||
{auto l : Ref Loaded (List String)} ->
|
||||
{auto s : Ref Structs (List String)} ->
|
||||
FC -> Name -> NamedDef -> Core (String, String)
|
||||
schFgnDef fc n (MkNmForeign cs args ret)
|
||||
= do let argns = mkArgs 0 args
|
||||
let allargns = map fst argns
|
||||
let useargns = map fst (filter snd argns)
|
||||
argStrs <- traverse mkStruct args
|
||||
retStr <- mkStruct ret
|
||||
(load, body) <- useCC fc cs (zip useargns args) ret
|
||||
defs <- get Ctxt
|
||||
pure (load,
|
||||
concat argStrs ++ retStr ++
|
||||
"(define " ++ schName !(full (gamma defs) n) ++
|
||||
" (lambda (" ++ showSep " " (map schName allargns) ++ ") " ++
|
||||
body ++ "))\n")
|
||||
schFgnDef _ _ _ = pure ("", "")
|
||||
|
||||
getFgnCall : {auto c : Ref Ctxt Defs} ->
|
||||
{auto l : Ref Loaded (List String)} ->
|
||||
{auto s : Ref Structs (List String)} ->
|
||||
(Name, FC, NamedDef) -> Core (String, String)
|
||||
getFgnCall (n, fc, d) = schFgnDef fc n d
|
||||
|
||||
-- TODO Include libraries from the directives
|
||||
compileToSCM : Ref Ctxt Defs ->
|
||||
ClosedTerm -> (outfile : String) -> Core ()
|
||||
compileToSCM c tm outfile
|
||||
= do cdata <- getCompileData Cases tm
|
||||
let ndefs = namedDefs cdata
|
||||
-- let tags = nameTags cdata
|
||||
let ctm = forget (mainExpr cdata)
|
||||
|
||||
defs <- get Ctxt
|
||||
l <- newRef {t = List String} Loaded []
|
||||
s <- newRef {t = List String} Structs []
|
||||
fgndefs <- traverse getFgnCall ndefs
|
||||
compdefs <- traverse (getScheme gambitPrim gambitString) ndefs
|
||||
let code = fastAppend (map snd fgndefs ++ compdefs) ++
|
||||
concat (map fst fgndefs)
|
||||
main <- schExp gambitPrim gambitString 0 ctm
|
||||
support <- readDataFile "gambit/support.scm"
|
||||
foreign <- readDataFile "gambit/foreign.scm"
|
||||
let scm = showSep "\n" [schHeader, support, foreign, code, main]
|
||||
Right () <- coreLift $ writeFile outfile scm
|
||||
| Left err => throw (FileErr outfile err)
|
||||
pure ()
|
||||
|
||||
-- TODO Include external libraries on compilation
|
||||
compileExpr : Ref Ctxt Defs -> (execDir : String) ->
|
||||
ClosedTerm -> (outfile : String) -> Core (Maybe String)
|
||||
compileExpr c execDir tm outfile
|
||||
= do let outn = execDir ++ dirSep ++ outfile ++ ".scm"
|
||||
compileToSCM c tm outn
|
||||
gsc <- coreLift findGSC
|
||||
ok <- coreLift $ system (gsc ++ " -exe " ++ outn)
|
||||
if ok == 0
|
||||
then pure (Just outfile)
|
||||
else pure Nothing
|
||||
|
||||
executeExpr : Ref Ctxt Defs -> (execDir : String) -> ClosedTerm -> Core ()
|
||||
executeExpr c execDir tm
|
||||
= do let tmp = execDir ++ dirSep ++ "_tmpgambit"
|
||||
let outn = tmp ++ ".scm"
|
||||
compileToSCM c tm outn
|
||||
gsi <- coreLift findGSI
|
||||
coreLift $ system (gsi ++ " " ++ outn)
|
||||
pure ()
|
||||
|
||||
export
|
||||
codegenGambit : Codegen
|
||||
codegenGambit = MkCG compileExpr executeExpr
|
343
src/Compiler/Scheme/Racket.idr
Normal file
343
src/Compiler/Scheme/Racket.idr
Normal file
@ -0,0 +1,343 @@
|
||||
module Compiler.Scheme.Racket
|
||||
|
||||
import Compiler.Common
|
||||
import Compiler.CompileExpr
|
||||
import Compiler.Inline
|
||||
import Compiler.Scheme.Common
|
||||
|
||||
import Core.Options
|
||||
import Core.Context
|
||||
import Core.Directory
|
||||
import Core.Name
|
||||
import Core.TT
|
||||
import Utils.Hex
|
||||
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.NameMap
|
||||
import Data.Nat
|
||||
import Data.Strings
|
||||
import Data.Vect
|
||||
import System
|
||||
import System.Info
|
||||
|
||||
%default covering
|
||||
|
||||
findRacket : IO String
|
||||
findRacket =
|
||||
do env <- getEnv "RACKET"
|
||||
pure $ fromMaybe "/usr/bin/env racket" env
|
||||
|
||||
findRacoExe : IO String
|
||||
findRacoExe =
|
||||
do env <- getEnv "RACKET_RACO"
|
||||
pure $ (fromMaybe "/usr/bin/env raco" env) ++ " exe"
|
||||
|
||||
schHeader : String -> String
|
||||
schHeader libs
|
||||
= "#lang racket/base\n" ++
|
||||
"(require racket/math)\n" ++ -- for math ops
|
||||
"(require racket/system)\n" ++ -- for system
|
||||
"(require srfi/19)\n" ++ -- for file handling and data
|
||||
"(require ffi/unsafe ffi/unsafe/define)\n" ++ -- for calling C
|
||||
libs ++
|
||||
"(let ()\n"
|
||||
|
||||
schFooter : String
|
||||
schFooter = ")"
|
||||
|
||||
showRacketChar : Char -> String -> String
|
||||
showRacketChar '\\' = ("\\\\" ++)
|
||||
showRacketChar c
|
||||
= if c < chr 32 || c > chr 126
|
||||
then (("\\u" ++ pad (asHex (cast c))) ++)
|
||||
else strCons c
|
||||
where
|
||||
pad : String -> String
|
||||
pad str
|
||||
= case isLTE (length str) 4 of
|
||||
Yes _ => pack (List.replicate (minus 4 (length str)) '0') ++ str
|
||||
No _ => str
|
||||
|
||||
showRacketString : List Char -> String -> String
|
||||
showRacketString [] = id
|
||||
showRacketString ('"'::cs) = ("\\\"" ++) . showRacketString cs
|
||||
showRacketString (c ::cs) = (showRacketChar c) . showRacketString cs
|
||||
|
||||
racketString : String -> String
|
||||
racketString cs = strCons '"' (showRacketString (unpack cs) "\"")
|
||||
|
||||
mutual
|
||||
racketPrim : Int -> ExtPrim -> List NamedCExp -> Core String
|
||||
racketPrim i CCall [ret, fn, args, world]
|
||||
= throw (InternalError ("Can't compile C FFI calls to Racket yet"))
|
||||
racketPrim i GetField [NmPrimVal _ (Str s), _, _, struct,
|
||||
NmPrimVal _ (Str fld), _]
|
||||
= do structsc <- schExp racketPrim racketString 0 struct
|
||||
pure $ "(" ++ s ++ "-" ++ fld ++ " " ++ structsc ++ ")"
|
||||
racketPrim i GetField [_,_,_,_,_,_]
|
||||
= pure "(error \"bad getField\")"
|
||||
racketPrim i SetField [NmPrimVal _ (Str s), _, _, struct,
|
||||
NmPrimVal _ (Str fld), _, val, world]
|
||||
= do structsc <- schExp racketPrim racketString 0 struct
|
||||
valsc <- schExp racketPrim racketString 0 val
|
||||
pure $ mkWorld $
|
||||
"(set-" ++ s ++ "-" ++ fld ++ "! " ++ structsc ++ " " ++ valsc ++ ")"
|
||||
racketPrim i SetField [_,_,_,_,_,_,_,_]
|
||||
= pure "(error \"bad setField\")"
|
||||
racketPrim i SysCodegen []
|
||||
= pure $ "\"racket\""
|
||||
racketPrim i prim args
|
||||
= schExtCommon racketPrim racketString i prim args
|
||||
|
||||
-- Reference label for keeping track of loaded external libraries
|
||||
data Loaded : Type where
|
||||
|
||||
-- Label for noting which struct types are declared
|
||||
data Structs : Type where
|
||||
|
||||
-- Label for noting which foreign names are declared
|
||||
data Done : Type where
|
||||
|
||||
cftySpec : FC -> CFType -> Core String
|
||||
cftySpec fc CFUnit = pure "_void"
|
||||
cftySpec fc CFInt = pure "_int"
|
||||
cftySpec fc CFString = pure "_string/utf-8"
|
||||
cftySpec fc CFDouble = pure "_double"
|
||||
cftySpec fc CFChar = pure "_int8"
|
||||
cftySpec fc CFPtr = pure "_pointer"
|
||||
cftySpec fc (CFIORes t) = cftySpec fc t
|
||||
cftySpec fc (CFStruct n t) = pure $ "_" ++ n ++ "-pointer"
|
||||
cftySpec fc (CFFun s t) = funTySpec [s] t
|
||||
where
|
||||
funTySpec : List CFType -> CFType -> Core String
|
||||
funTySpec args (CFFun CFWorld t) = funTySpec args t
|
||||
funTySpec args (CFFun s t) = funTySpec (s :: args) t
|
||||
funTySpec args retty
|
||||
= do rtyspec <- cftySpec fc retty
|
||||
argspecs <- traverse (cftySpec fc) (reverse args)
|
||||
pure $ "(_fun " ++ showSep " " argspecs ++ " -> " ++ rtyspec ++ ")"
|
||||
cftySpec fc t = throw (GenericMsg fc ("Can't pass argument of type " ++ show t ++
|
||||
" to foreign function"))
|
||||
|
||||
loadlib : String -> String -> String
|
||||
loadlib libn ver
|
||||
= "(define-ffi-definer define-" ++ libn ++
|
||||
" (ffi-lib \"" ++ libn ++ "\" " ++ ver ++ "))\n"
|
||||
|
||||
getLibVers : String -> (String, String)
|
||||
getLibVers libspec
|
||||
= case words libspec of
|
||||
[] => ("", "")
|
||||
[fn] => case span (/='.') libspec of
|
||||
(root, rest) => (root, "")
|
||||
(fn :: vers) =>
|
||||
(fst (span (/='.') fn),
|
||||
"'(" ++ showSep " " (map show vers) ++ " #f)" )
|
||||
|
||||
cToRkt : CFType -> String -> String
|
||||
cToRkt CFChar op = "(integer->char " ++ op ++ ")"
|
||||
cToRkt _ op = op
|
||||
|
||||
rktToC : CFType -> String -> String
|
||||
rktToC CFChar op = "(char->integer " ++ op ++ ")"
|
||||
rktToC _ op = op
|
||||
|
||||
handleRet : CFType -> String -> String
|
||||
handleRet CFUnit op = op ++ " " ++ mkWorld (schConstructor racketString (UN "") (Just 0) [])
|
||||
handleRet ret op = mkWorld (cToRkt ret op)
|
||||
|
||||
cCall : {auto f : Ref Done (List String) } ->
|
||||
{auto c : Ref Ctxt Defs} ->
|
||||
{auto l : Ref Loaded (List String)} ->
|
||||
FC -> (cfn : String) -> (clib : String) ->
|
||||
List (Name, CFType) -> CFType -> Core (String, String)
|
||||
cCall fc cfn libspec args ret
|
||||
= do loaded <- get Loaded
|
||||
bound <- get Done
|
||||
|
||||
let (libn, vers) = getLibVers libspec
|
||||
lib <- if libn `elem` loaded
|
||||
then pure ""
|
||||
else do put Loaded (libn :: loaded)
|
||||
ldata <- locate libspec
|
||||
copyLib ldata
|
||||
pure (loadlib libn vers)
|
||||
|
||||
argTypes <- traverse (\a => do s <- cftySpec fc (snd a)
|
||||
pure (a, s)) args
|
||||
retType <- cftySpec fc ret
|
||||
cbind <- if cfn `elem` bound
|
||||
then pure ""
|
||||
else do put Done (cfn :: bound)
|
||||
pure $ "(define-" ++ libn ++ " " ++ cfn ++
|
||||
" (_fun " ++ showSep " " (map snd argTypes) ++ " -> " ++
|
||||
retType ++ "))\n"
|
||||
let call = "(" ++ cfn ++ " " ++
|
||||
showSep " " !(traverse useArg argTypes) ++ ")"
|
||||
|
||||
pure (lib ++ cbind, case ret of
|
||||
CFIORes rt => handleRet rt call
|
||||
_ => call)
|
||||
where
|
||||
mkNs : Int -> List CFType -> List (Maybe (String, CFType))
|
||||
mkNs i [] = []
|
||||
mkNs i (CFWorld :: xs) = Nothing :: mkNs i xs
|
||||
mkNs i (x :: xs) = Just ("cb" ++ show i, x) :: mkNs (i + 1) xs
|
||||
|
||||
applyLams : String -> List (Maybe (String, CFType)) -> String
|
||||
applyLams n [] = n
|
||||
applyLams n (Nothing :: as) = applyLams ("(" ++ n ++ " #f)") as
|
||||
applyLams n (Just (a, ty) :: as)
|
||||
= applyLams ("(" ++ n ++ " " ++ cToRkt ty a ++ ")") as
|
||||
|
||||
mkFun : List CFType -> CFType -> String -> String
|
||||
mkFun args ret n
|
||||
= let argns = mkNs 0 args in
|
||||
"(lambda (" ++ showSep " " (map fst (mapMaybe id argns)) ++ ") " ++
|
||||
(applyLams n argns ++ ")")
|
||||
|
||||
notWorld : CFType -> Bool
|
||||
notWorld CFWorld = False
|
||||
notWorld _ = True
|
||||
|
||||
callback : String -> List CFType -> CFType -> Core String
|
||||
callback n args (CFFun s t) = callback n (s :: args) t
|
||||
callback n args_rev retty
|
||||
= do let args = reverse args_rev
|
||||
argTypes <- traverse (cftySpec fc) (filter notWorld args)
|
||||
retType <- cftySpec fc retty
|
||||
pure $ mkFun args retty n
|
||||
|
||||
useArg : ((Name, CFType), String) -> Core String
|
||||
useArg ((n, CFFun s t), _) = callback (schName n) [s] t
|
||||
useArg ((n, ty), _)
|
||||
= pure $ rktToC ty (schName n)
|
||||
|
||||
schemeCall : FC -> (sfn : String) ->
|
||||
List Name -> CFType -> Core String
|
||||
schemeCall fc sfn argns ret
|
||||
= let call = "(" ++ sfn ++ " " ++ showSep " " (map schName argns) ++ ")" in
|
||||
case ret of
|
||||
CFIORes _ => pure $ mkWorld call
|
||||
_ => pure call
|
||||
|
||||
-- Use a calling convention to compile a foreign def.
|
||||
-- Returns any preamble needed for loading libraries, and the body of the
|
||||
-- function call.
|
||||
useCC : {auto f : Ref Done (List String) } ->
|
||||
{auto c : Ref Ctxt Defs} ->
|
||||
{auto l : Ref Loaded (List String)} ->
|
||||
FC -> List String -> List (Name, CFType) -> CFType -> Core (String, String)
|
||||
useCC fc [] args ret
|
||||
= throw (GenericMsg fc "No recognised foreign calling convention")
|
||||
useCC fc (cc :: ccs) args ret
|
||||
= case parseCC cc of
|
||||
Nothing => useCC fc ccs args ret
|
||||
Just ("scheme", [sfn]) =>
|
||||
do body <- schemeCall fc sfn (map fst args) ret
|
||||
pure ("", body)
|
||||
Just ("C", [cfn, clib]) => cCall fc cfn clib args ret
|
||||
Just ("C", [cfn, clib, chdr]) => cCall fc cfn clib args ret
|
||||
_ => useCC fc ccs args ret
|
||||
|
||||
-- For every foreign arg type, return a name, and whether to pass it to the
|
||||
-- foreign call (we don't pass '%World')
|
||||
mkArgs : Int -> List CFType -> List (Name, Bool)
|
||||
mkArgs i [] = []
|
||||
mkArgs i (CFWorld :: cs) = (MN "farg" i, False) :: mkArgs i cs
|
||||
mkArgs i (c :: cs) = (MN "farg" i, True) :: mkArgs (i + 1) cs
|
||||
|
||||
mkStruct : {auto s : Ref Structs (List String)} ->
|
||||
CFType -> Core String
|
||||
mkStruct (CFStruct n flds)
|
||||
= do defs <- traverse mkStruct (map snd flds)
|
||||
strs <- get Structs
|
||||
if n `elem` strs
|
||||
then pure (concat defs)
|
||||
else do put Structs (n :: strs)
|
||||
pure $ concat defs ++ "(define-cstruct _" ++ n ++ " ("
|
||||
++ showSep "\n\t" !(traverse showFld flds) ++ "))\n"
|
||||
where
|
||||
showFld : (String, CFType) -> Core String
|
||||
showFld (n, ty) = pure $ "[" ++ n ++ " " ++ !(cftySpec emptyFC ty) ++ "]"
|
||||
mkStruct (CFIORes t) = mkStruct t
|
||||
mkStruct (CFFun a b) = do mkStruct a; mkStruct b
|
||||
mkStruct _ = pure ""
|
||||
|
||||
schFgnDef : {auto f : Ref Done (List String) } ->
|
||||
{auto c : Ref Ctxt Defs} ->
|
||||
{auto l : Ref Loaded (List String)} ->
|
||||
{auto s : Ref Structs (List String)} ->
|
||||
FC -> Name -> NamedDef -> Core (String, String)
|
||||
schFgnDef fc n (MkNmForeign cs args ret)
|
||||
= do let argns = mkArgs 0 args
|
||||
let allargns = map fst argns
|
||||
let useargns = map fst (filter snd argns)
|
||||
argStrs <- traverse mkStruct args
|
||||
retStr <- mkStruct ret
|
||||
(load, body) <- useCC fc cs (zip useargns args) ret
|
||||
defs <- get Ctxt
|
||||
pure (concat argStrs ++ retStr ++ load,
|
||||
"(define " ++ schName !(full (gamma defs) n) ++
|
||||
" (lambda (" ++ showSep " " (map schName allargns) ++ ") " ++
|
||||
body ++ "))\n")
|
||||
schFgnDef _ _ _ = pure ("", "")
|
||||
|
||||
getFgnCall : {auto f : Ref Done (List String) } ->
|
||||
{auto c : Ref Ctxt Defs} ->
|
||||
{auto l : Ref Loaded (List String)} ->
|
||||
{auto s : Ref Structs (List String)} ->
|
||||
(Name, FC, NamedDef) -> Core (String, String)
|
||||
getFgnCall (n, fc, d) = schFgnDef fc n d
|
||||
|
||||
compileToRKT : Ref Ctxt Defs ->
|
||||
ClosedTerm -> (outfile : String) -> Core ()
|
||||
compileToRKT c tm outfile
|
||||
= do cdata <- getCompileData Cases tm
|
||||
let ndefs = namedDefs cdata
|
||||
let ctm = forget (mainExpr cdata)
|
||||
|
||||
defs <- get Ctxt
|
||||
f <- newRef {t = List String} Done empty
|
||||
l <- newRef {t = List String} Loaded []
|
||||
s <- newRef {t = List String} Structs []
|
||||
fgndefs <- traverse getFgnCall ndefs
|
||||
compdefs <- traverse (getScheme racketPrim racketString) ndefs
|
||||
let code = fastAppend (map snd fgndefs ++ compdefs)
|
||||
main <- schExp racketPrim racketString 0 ctm
|
||||
support <- readDataFile "racket/support.rkt"
|
||||
let scm = schHeader (concat (map fst fgndefs)) ++
|
||||
support ++ code ++
|
||||
"(void " ++ main ++ ")\n" ++
|
||||
schFooter
|
||||
Right () <- coreLift $ writeFile outfile scm
|
||||
| Left err => throw (FileErr outfile err)
|
||||
coreLift $ chmodRaw outfile 0o755
|
||||
pure ()
|
||||
|
||||
compileExpr : Ref Ctxt Defs -> (execDir : String) ->
|
||||
ClosedTerm -> (outfile : String) -> Core (Maybe String)
|
||||
compileExpr c execDir tm outfile
|
||||
= do let outSs = execDir ++ dirSep ++ outfile ++ ".rkt"
|
||||
let outBin = execDir ++ dirSep ++ outfile
|
||||
compileToRKT c tm outSs
|
||||
raco <- coreLift findRacoExe
|
||||
ok <- coreLift $ system (raco ++ " -o " ++ outBin ++ " " ++ outSs)
|
||||
if ok == 0
|
||||
then pure (Just outfile)
|
||||
else pure Nothing
|
||||
|
||||
executeExpr : Ref Ctxt Defs -> (execDir : String) -> ClosedTerm -> Core ()
|
||||
executeExpr c execDir tm
|
||||
= do let tmp = execDir ++ dirSep ++ "_tmpracket"
|
||||
let outn = tmp ++ ".rkt"
|
||||
compileToRKT c tm outn
|
||||
racket <- coreLift findRacket
|
||||
coreLift $ system (racket ++ " " ++ outn)
|
||||
pure ()
|
||||
|
||||
export
|
||||
codegenRacket : Codegen
|
||||
codegenRacket = MkCG compileExpr executeExpr
|
||||
|
198
src/Compiler/VMCode.idr
Normal file
198
src/Compiler/VMCode.idr
Normal file
@ -0,0 +1,198 @@
|
||||
module Compiler.VMCode
|
||||
|
||||
import Compiler.ANF
|
||||
|
||||
import Core.CompileExpr
|
||||
import Core.Context
|
||||
import Core.Core
|
||||
import Core.TT
|
||||
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.Vect
|
||||
|
||||
%default covering
|
||||
|
||||
public export
|
||||
data Reg : Type where
|
||||
RVal : Reg
|
||||
Loc : Int -> Reg
|
||||
Discard : Reg
|
||||
|
||||
-- VM instructions - first Reg is where the result goes, unless stated
|
||||
-- otherwise.
|
||||
|
||||
-- As long as you have a representation of closures, and an 'apply' function
|
||||
-- which adds an argument and evaluates if it's fully applied, then you can
|
||||
-- translate this directly to a target language program.
|
||||
public export
|
||||
data VMInst : Type where
|
||||
DECLARE : Reg -> VMInst
|
||||
START : VMInst -- start of the main body of the function
|
||||
ASSIGN : Reg -> Reg -> VMInst
|
||||
MKCON : Reg -> (tag : Maybe Int) -> (args : List Reg) -> VMInst
|
||||
MKCLOSURE : Reg -> Name -> (missing : Nat) -> (args : List Reg) -> VMInst
|
||||
MKCONSTANT : Reg -> Constant -> VMInst
|
||||
|
||||
APPLY : Reg -> (f : Reg) -> (a : Reg) -> VMInst
|
||||
CALL : Reg -> (tailpos : Bool) -> Name -> (args : List Reg) -> VMInst
|
||||
OP : Reg -> PrimFn arity -> Vect arity Reg -> VMInst
|
||||
EXTPRIM : Reg -> Name -> List Reg -> VMInst
|
||||
|
||||
CASE : Reg -> -- scrutinee
|
||||
(alts : List (Either Int Name, List VMInst)) -> -- based on constructor tag
|
||||
(def : Maybe (List VMInst)) ->
|
||||
VMInst
|
||||
CONSTCASE : Reg -> -- scrutinee
|
||||
(alts : List (Constant, List VMInst)) ->
|
||||
(def : Maybe (List VMInst)) ->
|
||||
VMInst
|
||||
PROJECT : Reg -> (value : Reg) -> (pos : Int) -> VMInst
|
||||
NULL : Reg -> VMInst
|
||||
|
||||
ERROR : String -> VMInst
|
||||
|
||||
public export
|
||||
data VMDef : Type where
|
||||
MkVMFun : (args : List Int) -> List VMInst -> VMDef
|
||||
MkVMError : List VMInst -> VMDef
|
||||
|
||||
export
|
||||
Show Reg where
|
||||
show RVal = "RVAL"
|
||||
show (Loc i) = "v" ++ show i
|
||||
show Discard = "DISCARD"
|
||||
|
||||
export
|
||||
Show VMInst where
|
||||
show (DECLARE r) = "DECLARE " ++ show r
|
||||
show START = "START"
|
||||
show (ASSIGN r v) = show r ++ " := " ++ show v
|
||||
show (MKCON r t args)
|
||||
= show r ++ " := MKCON " ++ show t ++ " (" ++
|
||||
showSep ", " (map show args) ++ ")"
|
||||
show (MKCLOSURE r n m args)
|
||||
= show r ++ " := MKCLOSURE " ++ show n ++ " " ++ show m ++ " (" ++
|
||||
showSep ", " (map show args) ++ ")"
|
||||
show (MKCONSTANT r c) = show r ++ " := MKCONSTANT " ++ show c
|
||||
show (APPLY r f a) = show r ++ " := " ++ show f ++ " @ " ++ show a
|
||||
show (CALL r t n args)
|
||||
= show r ++ " := " ++ (if t then "TAILCALL " else "CALL ") ++
|
||||
show n ++ "(" ++ showSep ", " (map show args) ++ ")"
|
||||
show (OP r op args)
|
||||
= show r ++ " := " ++ "OP " ++
|
||||
show op ++ "(" ++ showSep ", " (map show (toList args)) ++ ")"
|
||||
show (EXTPRIM r n args)
|
||||
= show r ++ " := " ++ "EXTPRIM " ++
|
||||
show n ++ "(" ++ showSep ", " (map show args) ++ ")"
|
||||
|
||||
show (CASE scr alts def)
|
||||
= "CASE " ++ show scr ++ " " ++ show alts ++ " {default: " ++ show def ++ "}"
|
||||
show (CONSTCASE scr alts def)
|
||||
= "CASE " ++ show scr ++ " " ++ show alts ++ " {default: " ++ show def ++ "}"
|
||||
|
||||
show (PROJECT r val pos)
|
||||
= show r ++ " := PROJECT(" ++ show val ++ ", " ++ show pos ++ ")"
|
||||
show (NULL r) = show r ++ " := NULL"
|
||||
show (ERROR str) = "ERROR " ++ show str
|
||||
|
||||
export
|
||||
Show VMDef where
|
||||
show (MkVMFun args body) = show args ++ ": " ++ show body
|
||||
show (MkVMError err) = "Error: " ++ show err
|
||||
|
||||
toReg : AVar -> Reg
|
||||
toReg (ALocal i) = Loc i
|
||||
toReg ANull = Discard
|
||||
|
||||
toVM : (tailpos : Bool) -> (target : Reg) -> ANF -> List VMInst
|
||||
toVM t Discard _ = []
|
||||
toVM t res (AV fc (ALocal i))
|
||||
= [ASSIGN res (Loc i)]
|
||||
toVM t res (AAppName fc n args)
|
||||
= [CALL res t n (map toReg args)]
|
||||
toVM t res (AUnderApp fc n m args)
|
||||
= [MKCLOSURE res n m (map toReg args)]
|
||||
toVM t res (AApp fc f a)
|
||||
= [APPLY res (toReg f) (toReg a)]
|
||||
toVM t res (ALet fc var val body)
|
||||
= toVM False (Loc var) val ++ toVM t res body
|
||||
toVM t res (ACon fc n tag args)
|
||||
= [MKCON res tag (map toReg args)]
|
||||
toVM t res (AOp fc op args)
|
||||
= [OP res op (map toReg args)]
|
||||
toVM t res (AExtPrim fc p args)
|
||||
= [EXTPRIM res p (map toReg args)]
|
||||
toVM t res (AConCase fc (ALocal scr) alts def)
|
||||
= [CASE (Loc scr) (map toVMConAlt alts) (map (toVM t res) def)]
|
||||
where
|
||||
projectArgs : Int -> List Int -> List VMInst
|
||||
projectArgs i [] = []
|
||||
projectArgs i (arg :: args)
|
||||
= PROJECT (Loc arg) (Loc scr) i :: projectArgs (i + 1) args
|
||||
|
||||
toVMConAlt : AConAlt -> (Either Int Name, List VMInst)
|
||||
toVMConAlt (MkAConAlt n (Just tag) args code)
|
||||
= (Left tag, projectArgs 0 args ++ toVM t res code)
|
||||
toVMConAlt (MkAConAlt n Nothing args code)
|
||||
= (Right n, projectArgs 0 args ++ toVM t res code)
|
||||
toVM t res (AConstCase fc (ALocal scr) alts def)
|
||||
= [CONSTCASE (Loc scr) (map toVMConstAlt alts) (map (toVM t res) def)]
|
||||
where
|
||||
toVMConstAlt : AConstAlt -> (Constant, List VMInst)
|
||||
toVMConstAlt (MkAConstAlt c code)
|
||||
= (c, toVM t res code)
|
||||
toVM t res (APrimVal fc c)
|
||||
= [MKCONSTANT res c]
|
||||
toVM t res (AErased fc)
|
||||
= [NULL res]
|
||||
toVM t res (ACrash fc err)
|
||||
= [ERROR err]
|
||||
toVM t res _
|
||||
= [NULL res]
|
||||
|
||||
findVars : VMInst -> List Int
|
||||
findVars (ASSIGN (Loc r) _) = [r]
|
||||
findVars (MKCON (Loc r) _ _) = [r]
|
||||
findVars (MKCLOSURE (Loc r) _ _ _) = [r]
|
||||
findVars (MKCONSTANT (Loc r) _) = [r]
|
||||
findVars (APPLY (Loc r) _ _) = [r]
|
||||
findVars (CALL (Loc r) _ _ _) = [r]
|
||||
findVars (OP (Loc r) _ _) = [r]
|
||||
findVars (EXTPRIM (Loc r) _ _) = [r]
|
||||
findVars (CASE _ alts d)
|
||||
= concatMap findVarAlt alts ++ fromMaybe [] (map (concatMap findVars) d)
|
||||
where
|
||||
findVarAlt : (Either Int Name, List VMInst) -> List Int
|
||||
findVarAlt (t, code) = concatMap findVars code
|
||||
findVars (CONSTCASE _ alts d)
|
||||
= concatMap findConstVarAlt alts ++ fromMaybe [] (map (concatMap findVars) d)
|
||||
where
|
||||
findConstVarAlt : (Constant, List VMInst) -> List Int
|
||||
findConstVarAlt (t, code) = concatMap findVars code
|
||||
findVars (PROJECT (Loc r) _ _) = [r]
|
||||
findVars _ = []
|
||||
|
||||
declareVars : List Int -> List VMInst -> List VMInst
|
||||
declareVars got code
|
||||
= let vs = concatMap findVars code in
|
||||
declareAll got vs
|
||||
where
|
||||
declareAll : List Int -> List Int -> List VMInst
|
||||
declareAll got [] = START :: code
|
||||
declareAll got (i :: is)
|
||||
= if i `elem` got
|
||||
then declareAll got is
|
||||
else DECLARE (Loc i) :: declareAll (i :: got) is
|
||||
|
||||
export
|
||||
toVMDef : ANFDef -> Maybe VMDef
|
||||
toVMDef (MkAFun args body)
|
||||
= Just $ MkVMFun args (declareVars args (toVM True RVal body))
|
||||
toVMDef (MkAError body)
|
||||
= Just $ MkVMError (declareVars [] (toVM True RVal body))
|
||||
toVMDef _ = Nothing
|
||||
|
||||
export
|
||||
allDefs : List (Name, ANFDef) -> List (Name, VMDef)
|
||||
allDefs = mapMaybe (\ (n, d) => do d' <- toVMDef d; pure (n, d'))
|
66
src/Data/StringTrie.idr
Normal file
66
src/Data/StringTrie.idr
Normal file
@ -0,0 +1,66 @@
|
||||
module Data.StringTrie
|
||||
|
||||
import Data.These
|
||||
import Data.StringMap
|
||||
|
||||
%default total
|
||||
|
||||
-- prefix tree specialised to use `String`s as keys
|
||||
|
||||
public export
|
||||
record StringTrie a where
|
||||
constructor MkStringTrie
|
||||
node : These a (StringMap (StringTrie a))
|
||||
|
||||
public export
|
||||
Show a => Show (StringTrie a) where
|
||||
show (MkStringTrie node) = assert_total $ show node
|
||||
|
||||
public export
|
||||
Functor StringTrie where
|
||||
map f (MkStringTrie node) = MkStringTrie $ assert_total $ bimap f (map (map f)) node
|
||||
|
||||
public export
|
||||
empty : StringTrie a
|
||||
empty = MkStringTrie $ That empty
|
||||
|
||||
public export
|
||||
singleton : List String -> a -> StringTrie a
|
||||
singleton [] v = MkStringTrie $ This v
|
||||
singleton (k::ks) v = MkStringTrie $ That $ singleton k (singleton ks v)
|
||||
|
||||
-- insert using supplied function to resolve clashes
|
||||
public export
|
||||
insertWith : List String -> (Maybe a -> a) -> StringTrie a -> StringTrie a
|
||||
insertWith [] f (MkStringTrie nd) =
|
||||
MkStringTrie $ these (This . f . Just) (Both (f Nothing)) (Both . f . Just) nd
|
||||
insertWith (k::ks) f (MkStringTrie nd) =
|
||||
MkStringTrie $ these (\x => Both x (singleton k end)) (That . rec) (\x => Both x . rec) nd
|
||||
where
|
||||
end : StringTrie a
|
||||
end = singleton ks (f Nothing)
|
||||
rec : StringMap (StringTrie a) -> StringMap (StringTrie a)
|
||||
rec sm = maybe (insert k end sm) (\tm => insert k (insertWith ks f tm) sm) (lookup k sm)
|
||||
|
||||
public export
|
||||
insert : List String -> a -> StringTrie a -> StringTrie a
|
||||
insert ks v = insertWith ks (const v)
|
||||
|
||||
-- fold the trie in a depth-first fashion performing monadic actions on values, then keys
|
||||
-- note that for `Both` the action on node values will be performed first because of `bitraverse` implementation
|
||||
public export
|
||||
foldWithKeysM : (Monad m, Monoid b) => (List String -> m b) -> (List String -> a -> m b) -> StringTrie a -> m b
|
||||
foldWithKeysM {a} {m} {b} fk fv = go []
|
||||
where
|
||||
go : List String -> StringTrie a -> m b
|
||||
go ks (MkStringTrie nd) =
|
||||
bifold <$> bitraverse
|
||||
(fv ks)
|
||||
(\sm => foldlM
|
||||
(\x, (k, vs) => do let ks' = ks++[k]
|
||||
y <- assert_total $ go ks' vs
|
||||
z <- fk ks'
|
||||
pure $ x <+> y <+> z)
|
||||
neutral
|
||||
(toList sm))
|
||||
nd
|
48
src/Data/These.idr
Normal file
48
src/Data/These.idr
Normal file
@ -0,0 +1,48 @@
|
||||
module Data.These
|
||||
|
||||
%default total
|
||||
|
||||
public export
|
||||
data These a b = This a | That b | Both a b
|
||||
|
||||
public export
|
||||
fromEither : Either a b -> These a b
|
||||
fromEither = either This That
|
||||
|
||||
public export
|
||||
these : (a -> c) -> (b -> c) -> (a -> b -> c) -> These a b -> c
|
||||
these l r lr (This a) = l a
|
||||
these l r lr (That b) = r b
|
||||
these l r lr (Both a b) = lr a b
|
||||
|
||||
public export
|
||||
bimap : (f : a -> b) -> (g : c -> d) -> These a c -> These b d
|
||||
bimap f g (This a) = This (f a)
|
||||
bimap f g (That b) = That (g b)
|
||||
bimap f g (Both a b) = Both (f a) (g b)
|
||||
|
||||
public export
|
||||
(Show a, Show b) => Show (These a b) where
|
||||
showPrec d (This x) = showCon d "This" $ showArg x
|
||||
showPrec d (That x) = showCon d "That" $ showArg x
|
||||
showPrec d (Both x y) = showCon d "Both" $ showArg x ++ showArg y
|
||||
|
||||
public export
|
||||
Functor (These a) where
|
||||
map = bimap id
|
||||
|
||||
public export
|
||||
mapFst : (f : a -> b) -> These a c -> These b c
|
||||
mapFst f = bimap f id
|
||||
|
||||
public export
|
||||
bifold : Monoid m => These m m -> m
|
||||
bifold (This a) = a
|
||||
bifold (That b) = b
|
||||
bifold (Both a b) = a <+> b
|
||||
|
||||
public export
|
||||
bitraverse : Applicative f => (a -> f c) -> (b -> f d) -> These a b -> f (These c d)
|
||||
bitraverse f g (This a) = [| This (f a) |]
|
||||
bitraverse f g (That b) = [| That (g b) |]
|
||||
bitraverse f g (Both a b) = [| Both (f a) (g b) |]
|
259
src/Idris/CommandLine.idr
Normal file
259
src/Idris/CommandLine.idr
Normal file
@ -0,0 +1,259 @@
|
||||
module Idris.CommandLine
|
||||
|
||||
import IdrisPaths
|
||||
|
||||
import Idris.Version
|
||||
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.Strings
|
||||
|
||||
import System
|
||||
|
||||
%default total
|
||||
|
||||
public export
|
||||
data PkgCommand
|
||||
= Build
|
||||
| Install
|
||||
| Clean
|
||||
| REPL
|
||||
|
||||
export
|
||||
Show PkgCommand where
|
||||
show Build = "--build"
|
||||
show Install = "--install"
|
||||
show Clean = "--clean"
|
||||
show REPL = "--repl"
|
||||
|
||||
public export
|
||||
data DirCommand
|
||||
= LibDir -- show top level package directory
|
||||
|
||||
export
|
||||
Show DirCommand where
|
||||
show LibDir = "--libdir"
|
||||
|
||||
||| CLOpt - possible command line options
|
||||
public export
|
||||
data CLOpt
|
||||
=
|
||||
||| Only typecheck the given file
|
||||
CheckOnly |
|
||||
||| The output file from the code generator
|
||||
OutputFile String |
|
||||
||| Execute a given function after checking the source file
|
||||
ExecFn String |
|
||||
||| Use a specific code generator (default chez)
|
||||
SetCG String |
|
||||
||| Don't implicitly import Prelude
|
||||
NoPrelude |
|
||||
||| Show the installation prefix
|
||||
ShowPrefix |
|
||||
||| Display Idris version
|
||||
Version |
|
||||
||| Display help text
|
||||
Help |
|
||||
||| Suppress the banner
|
||||
NoBanner |
|
||||
||| Run Idris 2 in quiet mode
|
||||
Quiet |
|
||||
||| Run Idris 2 in verbose mode (cancels quiet if it's the default)
|
||||
Verbose |
|
||||
||| Add a package as a dependency
|
||||
PkgPath String |
|
||||
||| Build or install a given package, depending on PkgCommand
|
||||
Package PkgCommand String |
|
||||
||| Show locations of data/library directories
|
||||
Directory DirCommand |
|
||||
||| The input Idris file
|
||||
InputFile String |
|
||||
||| Whether or not to run in IdeMode (easily parsable for other tools)
|
||||
IdeMode |
|
||||
||| Whether or not to run IdeMode (using a socket instead of stdin/stdout)
|
||||
IdeModeSocket String |
|
||||
||| Run as a checker for the core language TTImp
|
||||
Yaffle String |
|
||||
||| Dump metadata from a .ttm file
|
||||
Metadata String |
|
||||
||| Dump cases before compiling
|
||||
DumpCases String |
|
||||
||| Dump lambda lifted defs before compiling
|
||||
DumpLifted String |
|
||||
||| Dump ANF defs before compiling
|
||||
DumpANF String |
|
||||
||| Dump VM code defs before compiling
|
||||
DumpVMCode String |
|
||||
||| Run a REPL command then exit immediately
|
||||
RunREPL String |
|
||||
FindIPKG |
|
||||
Timing |
|
||||
DebugElabCheck |
|
||||
BlodwenPaths
|
||||
|
||||
|
||||
ActType : List String -> Type
|
||||
ActType [] = List CLOpt
|
||||
ActType (a :: as) = String -> ActType as
|
||||
|
||||
record OptDesc where
|
||||
constructor MkOpt
|
||||
flags : List String
|
||||
argdescs : List String
|
||||
action : ActType argdescs
|
||||
help : Maybe String
|
||||
|
||||
options : List OptDesc
|
||||
options = [MkOpt ["--check", "-c"] [] [CheckOnly]
|
||||
(Just "Exit after checking source file"),
|
||||
MkOpt ["--output", "-o"] ["file"] (\f => [OutputFile f, Quiet])
|
||||
(Just "Specify output file"),
|
||||
MkOpt ["--exec", "-x"] ["name"] (\f => [ExecFn f, Quiet])
|
||||
(Just "Execute function after checking source file"),
|
||||
MkOpt ["--no-prelude"] [] [NoPrelude]
|
||||
(Just "Don't implicitly import Prelude"),
|
||||
MkOpt ["--codegen", "--cg"] ["backend"] (\f => [SetCG f])
|
||||
(Just "Set code generator (default chez)"),
|
||||
MkOpt ["--package", "-p"] ["package"] (\f => [PkgPath f])
|
||||
(Just "Add a package as a dependency"),
|
||||
|
||||
MkOpt ["--ide-mode"] [] [IdeMode]
|
||||
(Just "Run the REPL with machine-readable syntax"),
|
||||
|
||||
MkOpt ["--ide-mode-socket"] [] [IdeModeSocket "localhost:38398"]
|
||||
(Just "Run the ide socket mode on default host and port (localhost:38398)"),
|
||||
|
||||
MkOpt ["--ide-mode-socket-with"] ["host:port"] (\hp => [IdeModeSocket hp])
|
||||
(Just "Run the ide socket mode on given host and port"),
|
||||
|
||||
MkOpt ["--prefix"] [] [ShowPrefix]
|
||||
(Just "Show installation prefix"),
|
||||
MkOpt ["--paths"] [] [BlodwenPaths]
|
||||
(Just "Show paths"),
|
||||
MkOpt ["--build"] ["package file"] (\f => [Package Build f])
|
||||
(Just "Build modules/executable for the given package"),
|
||||
MkOpt ["--install"] ["package file"] (\f => [Package Install f])
|
||||
(Just "Install the given package"),
|
||||
MkOpt ["--clean"] ["package file"] (\f => [Package Clean f])
|
||||
(Just "Clean intermediate files/executables for the given package"),
|
||||
|
||||
MkOpt ["--libdir"] [] [Directory LibDir]
|
||||
(Just "Show library directory"),
|
||||
MkOpt ["--no-banner"] [] [NoBanner]
|
||||
(Just "Suppress the banner"),
|
||||
MkOpt ["--quiet", "-q"] [] [Quiet]
|
||||
(Just "Quiet mode; display fewer messages"),
|
||||
MkOpt ["--verbose"] [] [Verbose]
|
||||
(Just "Verbose mode (default)"),
|
||||
MkOpt ["--version", "-v"] [] [Version]
|
||||
(Just "Display version string"),
|
||||
MkOpt ["--help", "-h", "-?"] [] [Help]
|
||||
(Just "Display help text"),
|
||||
MkOpt ["--timing"] [] [Timing]
|
||||
(Just "Display timing logs"),
|
||||
MkOpt ["--find-ipkg"] [] [FindIPKG]
|
||||
(Just "Find and use an .ipkg file in a parent directory"),
|
||||
MkOpt ["--client"] ["REPL command"] (\f => [RunREPL f])
|
||||
(Just "Run a REPL command then quit immediately"),
|
||||
-- Internal debugging options
|
||||
MkOpt ["--yaffle", "--ttimp"] ["ttimp file"] (\f => [Yaffle f])
|
||||
Nothing, -- run ttimp REPL rather than full Idris
|
||||
MkOpt ["--ttm" ] ["ttimp file"] (\f => [Metadata f])
|
||||
Nothing, -- dump metadata information from the given ttm file
|
||||
MkOpt ["--dumpcases"] ["output file"] (\f => [DumpCases f])
|
||||
Nothing, -- dump case trees to the given file
|
||||
MkOpt ["--dumplifted"] ["output file"] (\f => [DumpLifted f])
|
||||
Nothing, -- dump lambda lifted trees to the given file
|
||||
MkOpt ["--dumpanf"] ["output file"] (\f => [DumpANF f])
|
||||
Nothing, -- dump ANF to the given file
|
||||
MkOpt ["--dumpvmcode"] ["output file"] (\f => [DumpVMCode f])
|
||||
Nothing, -- dump VM Code to the given file
|
||||
MkOpt ["--debug-elab-check"] [] [DebugElabCheck]
|
||||
Nothing -- do more elaborator checks (currently conversion in LinearCheck)
|
||||
]
|
||||
|
||||
optUsage : OptDesc -> String
|
||||
optUsage d
|
||||
= maybe "" -- Don't show anything if there's no help string (that means
|
||||
-- it's an internal option)
|
||||
(\h => " " ++
|
||||
let optshow = showSep "," (flags d) ++ " " ++
|
||||
showSep " " (map (\x => "<" ++ x ++ ">") (argdescs d)) in
|
||||
optshow ++ pack (List.replicate (minus 26 (length optshow)) ' ')
|
||||
++ h ++ "\n") (help d)
|
||||
where
|
||||
showSep : String -> List String -> String
|
||||
showSep sep [] = ""
|
||||
showSep sep [x] = x
|
||||
showSep sep (x :: xs) = x ++ sep ++ showSep sep xs
|
||||
|
||||
export
|
||||
versionMsg : String
|
||||
versionMsg = "Idris 2, version " ++ showVersion True version
|
||||
|
||||
export
|
||||
usage : String
|
||||
usage = versionMsg ++ "\n" ++
|
||||
"Usage: idris2 [options] [input file]\n\n" ++
|
||||
"Available options:\n" ++
|
||||
concatMap optUsage options
|
||||
|
||||
processArgs : String -> (args : List String) -> List String -> ActType args ->
|
||||
Either String (List CLOpt, List String)
|
||||
processArgs flag [] xs f = Right (f, xs)
|
||||
processArgs flag (a :: as) [] f
|
||||
= Left $ "Missing argument <" ++ a ++ "> for flag " ++ flag
|
||||
processArgs flag (a :: as) (x :: xs) f
|
||||
= processArgs flag as xs (f x)
|
||||
|
||||
matchFlag : (d : OptDesc) -> List String ->
|
||||
Either String (Maybe (List CLOpt, List String))
|
||||
matchFlag d [] = Right Nothing -- Nothing left to match
|
||||
matchFlag d (x :: xs)
|
||||
= if x `elem` flags d
|
||||
then do args <- processArgs x (argdescs d) xs (action d)
|
||||
Right (Just args)
|
||||
else Right Nothing
|
||||
|
||||
findMatch : List OptDesc -> List String ->
|
||||
Either String (List CLOpt, List String)
|
||||
findMatch [] [] = Right ([], [])
|
||||
findMatch [] (f :: args) = Right ([InputFile f], args)
|
||||
findMatch (d :: ds) args
|
||||
= case !(matchFlag d args) of
|
||||
Nothing => findMatch ds args
|
||||
Just res => Right res
|
||||
|
||||
parseOpts : List OptDesc -> List String -> Either String (List CLOpt)
|
||||
parseOpts opts [] = Right []
|
||||
parseOpts opts args
|
||||
= do (cl, rest) <- findMatch opts args
|
||||
cls <- assert_total (parseOpts opts rest) -- 'rest' smaller than 'args'
|
||||
pure (cl ++ cls)
|
||||
|
||||
export
|
||||
getOpts : List String -> Either String (List CLOpt)
|
||||
getOpts opts = parseOpts options opts
|
||||
|
||||
|
||||
export covering
|
||||
getCmdOpts : IO (Either String (List CLOpt))
|
||||
getCmdOpts = do (_ :: opts) <- getArgs
|
||||
| _ => pure (Left "Invalid command line")
|
||||
pure $ getOpts opts
|
||||
|
||||
portPart : String -> Maybe String
|
||||
portPart p = if p == ""
|
||||
then Nothing
|
||||
else Just $ assert_total $ prim__strTail p
|
||||
|
||||
||| Extract the host and port to bind the IDE socket to
|
||||
public export
|
||||
ideSocketModeHostPort : List CLOpt -> (String, Int)
|
||||
ideSocketModeHostPort [] = ("localhost", 38398)
|
||||
ideSocketModeHostPort (IdeModeSocket hp :: _) =
|
||||
let (h, p) = Strings.break (== ':') hp
|
||||
port = fromMaybe 38398 (portPart p >>= parsePositive)
|
||||
host = if h == "" then "localhost" else h
|
||||
in (host, port)
|
||||
ideSocketModeHostPort (_ :: rest) = ideSocketModeHostPort rest
|
190
src/Idris/IDEMode/CaseSplit.idr
Normal file
190
src/Idris/IDEMode/CaseSplit.idr
Normal file
@ -0,0 +1,190 @@
|
||||
module Idris.IDEMode.CaseSplit
|
||||
|
||||
import Core.Context
|
||||
import Core.Env
|
||||
import Core.Metadata
|
||||
import Core.TT
|
||||
import Core.Value
|
||||
|
||||
import Parser.Lexer
|
||||
import Parser.Unlit
|
||||
|
||||
import TTImp.Interactive.CaseSplit
|
||||
import TTImp.TTImp
|
||||
import TTImp.Utils
|
||||
|
||||
import Idris.IDEMode.TokenLine
|
||||
import Idris.REPLOpts
|
||||
import Idris.Resugar
|
||||
import Idris.Syntax
|
||||
|
||||
import Data.List
|
||||
import Data.Strings
|
||||
import System.File
|
||||
|
||||
%default covering
|
||||
|
||||
getLine : Nat -> List String -> Maybe String
|
||||
getLine Z (l :: ls) = Just l
|
||||
getLine (S k) (l :: ls) = getLine k ls
|
||||
getLine _ [] = Nothing
|
||||
|
||||
toStrUpdate : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
(Name, RawImp) -> Core (List (String, String))
|
||||
toStrUpdate (UN n, term)
|
||||
= do clause <- pterm term
|
||||
pure [(n, show (bracket clause))]
|
||||
where
|
||||
bracket : PTerm -> PTerm
|
||||
bracket tm@(PRef _ _) = tm
|
||||
bracket tm@(PList _ _) = tm
|
||||
bracket tm@(PPair _ _ _) = tm
|
||||
bracket tm@(PUnit _) = tm
|
||||
bracket tm@(PComprehension _ _ _) = tm
|
||||
bracket tm@(PPrimVal _ _) = tm
|
||||
bracket tm = PBracketed emptyFC tm
|
||||
toStrUpdate _ = pure [] -- can't replace non user names
|
||||
|
||||
dump : SourcePart -> String
|
||||
dump (Whitespace str) = str
|
||||
dump (Name n) = n
|
||||
dump (HoleName n) = "?" ++ n
|
||||
dump LBrace = "{"
|
||||
dump RBrace = "}"
|
||||
dump Equal = "="
|
||||
dump (Other str) = str
|
||||
|
||||
data UPD : Type where
|
||||
|
||||
doUpdates : {auto u : Ref UPD (List String)} ->
|
||||
Defs -> List (String, String) -> List SourcePart ->
|
||||
Core (List SourcePart)
|
||||
doUpdates defs ups [] = pure []
|
||||
doUpdates defs ups (LBrace :: xs)
|
||||
= case dropSpace xs of
|
||||
Name n :: RBrace :: rest =>
|
||||
pure (LBrace :: Name n ::
|
||||
Whitespace " " :: Equal :: Whitespace " " ::
|
||||
!(doUpdates defs ups (Name n :: RBrace :: rest)))
|
||||
Name n :: Equal :: rest =>
|
||||
pure (LBrace :: Name n ::
|
||||
Whitespace " " :: Equal :: Whitespace " " ::
|
||||
!(doUpdates defs ups rest))
|
||||
_ => pure (LBrace :: !(doUpdates defs ups xs))
|
||||
where
|
||||
dropSpace : List SourcePart -> List SourcePart
|
||||
dropSpace [] = []
|
||||
dropSpace (RBrace :: xs) = RBrace :: xs
|
||||
dropSpace (Whitespace _ :: xs) = dropSpace xs
|
||||
dropSpace (x :: xs) = x :: dropSpace xs
|
||||
doUpdates defs ups (Name n :: xs)
|
||||
= case lookup n ups of
|
||||
Nothing => pure (Name n :: !(doUpdates defs ups xs))
|
||||
Just up => pure (Other up :: !(doUpdates defs ups xs))
|
||||
doUpdates defs ups (HoleName n :: xs)
|
||||
= do used <- get UPD
|
||||
n' <- uniqueName defs used n
|
||||
put UPD (n' :: used)
|
||||
pure $ HoleName n' :: !(doUpdates defs ups xs)
|
||||
doUpdates defs ups (x :: xs)
|
||||
= pure $ x :: !(doUpdates defs ups xs)
|
||||
|
||||
-- State here is a list of new hole names we generated (so as not to reuse any).
|
||||
-- Update the token list with the string replacements for each match, and return
|
||||
-- the newly generated strings.
|
||||
updateAll : {auto u : Ref UPD (List String)} ->
|
||||
Defs -> List SourcePart -> List (List (String, String)) ->
|
||||
Core (List String)
|
||||
updateAll defs l [] = pure []
|
||||
updateAll defs l (rs :: rss)
|
||||
= do l' <- doUpdates defs rs l
|
||||
rss' <- updateAll defs l rss
|
||||
pure (concat (map dump l') :: rss')
|
||||
|
||||
-- Turn the replacements we got from 'getSplits' into a list of actual string
|
||||
-- replacements
|
||||
getReplaces : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
List (Name, RawImp) -> Core (List (String, String))
|
||||
getReplaces updates
|
||||
= do strups <- traverse toStrUpdate updates
|
||||
pure (concat strups)
|
||||
|
||||
showImpossible : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
RawImp -> Core String
|
||||
showImpossible lhs
|
||||
= do clause <- pterm lhs
|
||||
pure (show clause ++ " impossible")
|
||||
|
||||
-- Given a list of updates and a line and column, find the relevant line in
|
||||
-- the source file and return the lines to replace it with
|
||||
export
|
||||
updateCase : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
List ClauseUpdate -> Int -> Int ->
|
||||
Core (List String)
|
||||
updateCase splits line col
|
||||
= do opts <- get ROpts
|
||||
case mainfile opts of
|
||||
Nothing => throw (InternalError "No file loaded")
|
||||
Just f =>
|
||||
do Right file <- coreLift $ readFile f
|
||||
| Left err => throw (FileErr f err)
|
||||
let thisline = getLine (integerToNat (cast line)) (lines file)
|
||||
case thisline of
|
||||
Nothing => throw (InternalError "File too short!")
|
||||
Just l =>
|
||||
do let valid = mapMaybe getValid splits
|
||||
let bad = mapMaybe getBad splits
|
||||
if isNil valid
|
||||
then traverse showImpossible bad
|
||||
else do rs <- traverse getReplaces valid
|
||||
let stok = tokens l
|
||||
defs <- get Ctxt
|
||||
u <- newRef UPD (the (List String) [])
|
||||
updateAll defs stok rs
|
||||
where
|
||||
getValid : ClauseUpdate -> Maybe (List (Name, RawImp))
|
||||
getValid (Valid _ ups) = Just ups
|
||||
getValid _ = Nothing
|
||||
|
||||
getBad : ClauseUpdate -> Maybe RawImp
|
||||
getBad (Impossible lhs) = Just lhs
|
||||
getBad _ = Nothing
|
||||
|
||||
fnName : Bool -> Name -> String
|
||||
fnName lhs (UN n)
|
||||
= if isIdentNormal n then n
|
||||
else if lhs then "(" ++ n ++ ")"
|
||||
else "op"
|
||||
fnName lhs (NS _ n) = fnName lhs n
|
||||
fnName lhs (DN s _) = s
|
||||
fnName lhs n = show n
|
||||
|
||||
|
||||
export
|
||||
getClause : {auto c : Ref Ctxt Defs} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
Int -> Name -> Core (Maybe String)
|
||||
getClause l n
|
||||
= do defs <- get Ctxt
|
||||
Just (loc, nidx, envlen, ty) <- findTyDeclAt (\p, n => onLine (l-1) p)
|
||||
| Nothing => pure Nothing
|
||||
n <- getFullName nidx
|
||||
argns <- getEnvArgNames defs envlen !(nf defs [] ty)
|
||||
Just srcLine <- getSourceLine l
|
||||
| Nothing => pure Nothing
|
||||
let (mark, src) = isLitLine srcLine
|
||||
pure (Just (indent mark loc ++ fnName True n ++ concat (map (" " ++) argns) ++
|
||||
" = ?" ++ fnName False n ++ "_rhs"))
|
||||
where
|
||||
indent : Maybe String -> FC -> String
|
||||
indent (Just mark) fc
|
||||
= relit (Just mark) $ pack (replicate (integerToNat (cast (max 0 (snd (startPos fc) - 1)))) ' ')
|
||||
indent Nothing fc = pack (replicate (integerToNat (cast (snd (startPos fc)))) ' ')
|
52
src/Idris/IDEMode/MakeClause.idr
Normal file
52
src/Idris/IDEMode/MakeClause.idr
Normal file
@ -0,0 +1,52 @@
|
||||
module Idris.IDEMode.MakeClause
|
||||
|
||||
import Core.Name
|
||||
import Parser.Lexer
|
||||
import Parser.Unlit
|
||||
|
||||
import Data.List
|
||||
import Data.Nat
|
||||
import Data.Strings
|
||||
|
||||
-- Implement make-with and make-case from the IDE mode
|
||||
|
||||
showRHSName : Name -> String
|
||||
showRHSName n
|
||||
= let fn = show (dropNS n) in
|
||||
if any isOpChar (unpack fn)
|
||||
then "op"
|
||||
else fn
|
||||
|
||||
export
|
||||
makeWith : Name -> String -> String
|
||||
makeWith n srcline
|
||||
= let (markerM, src) = isLitLine srcline
|
||||
isrc : (Nat, String) =
|
||||
case span isSpace src of
|
||||
(spc, rest) => (length spc, rest)
|
||||
indent = fst isrc
|
||||
src = snd isrc
|
||||
lhs = pack (readLHS 0 (unpack src)) in
|
||||
mkWithArg markerM indent lhs ++ "\n" ++
|
||||
mkWithPat markerM indent lhs ++ "\n"
|
||||
where
|
||||
readLHS : (brackets : Nat) -> List Char -> List Char
|
||||
readLHS Z ('=' :: rest) = []
|
||||
readLHS n ('(' :: rest) = '(' :: readLHS (S n) rest
|
||||
readLHS n ('{' :: rest) = '{' :: readLHS (S n) rest
|
||||
readLHS n (')' :: rest) = ')' :: readLHS (pred n) rest
|
||||
readLHS n ('}' :: rest) = '}' :: readLHS (pred n) rest
|
||||
readLHS n (x :: rest) = x :: readLHS n rest
|
||||
readLHS n [] = []
|
||||
|
||||
pref : Maybe String -> Nat -> String
|
||||
pref mark ind = relit mark $ pack (replicate ind ' ')
|
||||
|
||||
mkWithArg : Maybe String -> Nat -> String -> String
|
||||
mkWithArg mark indent lhs
|
||||
= pref mark indent ++ lhs ++ "with (_)"
|
||||
|
||||
mkWithPat : Maybe String -> Nat -> String -> String
|
||||
mkWithPat mark indent lhs
|
||||
= pref mark (indent + 2) ++ lhs ++ "| with_pat = ?" ++
|
||||
showRHSName n ++ "_rhs"
|
79
src/Idris/IDEMode/Parser.idr
Normal file
79
src/Idris/IDEMode/Parser.idr
Normal file
@ -0,0 +1,79 @@
|
||||
||| Slightly different lexer than the source language because we are more free
|
||||
||| as to what can be identifiers, and fewer tokens are supported. But otherwise,
|
||||
||| we can reuse the standard stuff
|
||||
module Idris.IDEMode.Parser
|
||||
|
||||
import Idris.IDEMode.Commands
|
||||
|
||||
import Text.Parser
|
||||
import Parser.Lexer
|
||||
import Parser.Support
|
||||
import Text.Lexer
|
||||
import Utils.String
|
||||
|
||||
import Data.List
|
||||
import Data.Strings
|
||||
|
||||
%hide Text.Lexer.symbols
|
||||
%hide Parser.Lexer.symbols
|
||||
|
||||
symbols : List String
|
||||
symbols = ["(", ":", ")"]
|
||||
|
||||
ideTokens : TokenMap Token
|
||||
ideTokens =
|
||||
map (\x => (exact x, Symbol)) symbols ++
|
||||
[(digits, \x => Literal (cast x)),
|
||||
(stringLit, \x => StrLit (stripQuotes x)),
|
||||
(identAllowDashes, \x => NSIdent [x]),
|
||||
(space, Comment)]
|
||||
|
||||
idelex : String -> Either (Int, Int, String) (List (TokenData Token))
|
||||
idelex str
|
||||
= case lex ideTokens str of
|
||||
-- Add the EndInput token so that we'll have a line and column
|
||||
-- number to read when storing spans in the file
|
||||
(tok, (l, c, "")) => Right (filter notComment tok ++
|
||||
[MkToken l c EndInput])
|
||||
(_, fail) => Left fail
|
||||
where
|
||||
notComment : TokenData Token -> Bool
|
||||
notComment t = case tok t of
|
||||
Comment _ => False
|
||||
_ => True
|
||||
|
||||
sexp : Rule SExp
|
||||
sexp
|
||||
= do symbol ":"; exactIdent "True"
|
||||
pure (BoolAtom True)
|
||||
<|> do symbol ":"; exactIdent "False"
|
||||
pure (BoolAtom False)
|
||||
<|> do i <- intLit
|
||||
pure (IntegerAtom i)
|
||||
<|> do str <- strLit
|
||||
pure (StringAtom str)
|
||||
<|> do symbol ":"; x <- unqualifiedName
|
||||
pure (SymbolAtom x)
|
||||
<|> do symbol "("
|
||||
xs <- many sexp
|
||||
symbol ")"
|
||||
pure (SExpList xs)
|
||||
|
||||
ideParser : {e : _} ->
|
||||
String -> Grammar (TokenData Token) e ty -> Either ParseError ty
|
||||
ideParser str p
|
||||
= case idelex str of
|
||||
Left err => Left $ LexFail err
|
||||
Right toks =>
|
||||
case parse p toks of
|
||||
Left (Error err []) =>
|
||||
Left $ ParseFail err Nothing []
|
||||
Left (Error err (t :: ts)) =>
|
||||
Left $ ParseFail err (Just (line t, col t))
|
||||
(map tok (t :: ts))
|
||||
Right (val, _) => Right val
|
||||
|
||||
export
|
||||
parseSExp : String -> Either ParseError SExp
|
||||
parseSExp inp
|
||||
= ideParser inp (do c <- sexp; eoi; pure c)
|
325
src/Idris/IDEMode/REPL.idr
Normal file
325
src/Idris/IDEMode/REPL.idr
Normal file
@ -0,0 +1,325 @@
|
||||
module Idris.IDEMode.REPL
|
||||
|
||||
import Compiler.Scheme.Chez
|
||||
import Compiler.Scheme.Racket
|
||||
import Compiler.Scheme.Gambit
|
||||
import Compiler.Common
|
||||
|
||||
import Core.AutoSearch
|
||||
import Core.CompileExpr
|
||||
import Core.Context
|
||||
import Core.InitPrimitives
|
||||
import Core.Metadata
|
||||
import Core.Normalise
|
||||
import Core.Options
|
||||
import Core.TT
|
||||
import Core.Unify
|
||||
|
||||
import Idris.Desugar
|
||||
import Idris.Error
|
||||
import Idris.ModTree
|
||||
import Idris.Parser
|
||||
import Idris.Resugar
|
||||
import Idris.REPL
|
||||
import Idris.Syntax
|
||||
import Idris.Version
|
||||
|
||||
import Idris.IDEMode.Parser
|
||||
import Idris.IDEMode.Commands
|
||||
import Idris.IDEMode.SyntaxHighlight
|
||||
|
||||
import TTImp.Interactive.CaseSplit
|
||||
import TTImp.Elab
|
||||
import TTImp.TTImp
|
||||
import TTImp.ProcessDecls
|
||||
|
||||
import Utils.Hex
|
||||
|
||||
import System
|
||||
|
||||
-- import Idris.Socket
|
||||
-- import Idris.Socket.Data
|
||||
|
||||
{-
|
||||
export
|
||||
socketToFile : Socket -> IO (Either String File)
|
||||
socketToFile (MkSocket f _ _ _) = do
|
||||
file <- map FHandle $ foreign FFI_C "fdopen" (Int -> String -> IO Ptr) f "r+"
|
||||
if !(ferror file) then do
|
||||
pure (Left "Failed to fdopen socket file descriptor")
|
||||
else pure (Right file)
|
||||
|
||||
export
|
||||
initIDESocketFile : String -> Int -> IO (Either String File)
|
||||
initIDESocketFile h p = do
|
||||
osock <- socket AF_INET Stream 0
|
||||
case osock of
|
||||
Left fail => do
|
||||
putStrLn (show fail)
|
||||
putStrLn "Failed to open socket"
|
||||
exit 1
|
||||
Right sock => do
|
||||
res <- bind sock (Just (Hostname h)) p
|
||||
if res /= 0
|
||||
then
|
||||
pure (Left ("Failed to bind socket with error: " ++ show res))
|
||||
else do
|
||||
res <- listen sock
|
||||
if res /= 0
|
||||
then
|
||||
pure (Left ("Failed to listen on socket with error: " ++ show res))
|
||||
else do
|
||||
putStrLn (show p)
|
||||
res <- accept sock
|
||||
case res of
|
||||
Left err =>
|
||||
pure (Left ("Failed to accept on socket with error: " ++ show err))
|
||||
Right (s, _) =>
|
||||
socketToFile s
|
||||
|
||||
getChar : File -> IO Char
|
||||
getChar (FHandle h) = do
|
||||
if !(fEOF (FHandle h)) then do
|
||||
putStrLn "Alas the file is done, aborting"
|
||||
exit 1
|
||||
else do
|
||||
chr <- map cast $ foreign FFI_C "fgetc" (Ptr -> IO Int) h
|
||||
if !(ferror (FHandle h)) then do
|
||||
putStrLn "Failed to read a character"
|
||||
exit 1
|
||||
else pure chr
|
||||
|
||||
getFLine : File -> IO String
|
||||
getFLine (FHandle h) = do
|
||||
str <- prim_fread h
|
||||
if !(ferror (FHandle h)) then do
|
||||
putStrLn "Failed to read a line"
|
||||
exit 1
|
||||
else pure str
|
||||
|
||||
getNChars : File -> Nat -> IO (List Char)
|
||||
getNChars i Z = pure []
|
||||
getNChars i (S k)
|
||||
= do x <- getChar i
|
||||
xs <- getNChars i k
|
||||
pure (x :: xs)
|
||||
|
||||
-- Read 6 characters. If they're a hex number, read that many characters.
|
||||
-- Otherwise, just read to newline
|
||||
getInput : File -> IO String
|
||||
getInput f
|
||||
= do x <- getNChars f 6
|
||||
case fromHexChars (reverse x) of
|
||||
Nothing =>
|
||||
do rest <- getFLine f
|
||||
pure (pack x ++ rest)
|
||||
Just num =>
|
||||
do inp <- getNChars f (cast num)
|
||||
pure (pack inp)
|
||||
|
||||
|
||||
process : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
IDECommand -> Core REPLResult
|
||||
process (Interpret cmd)
|
||||
= interpret cmd
|
||||
process (LoadFile fname _)
|
||||
= Idris.REPL.process (Load fname) >>= outputSyntaxHighlighting fname
|
||||
process (TypeOf n Nothing)
|
||||
= Idris.REPL.process (Check (PRef replFC (UN n)))
|
||||
process (TypeOf n (Just (l, c)))
|
||||
= Idris.REPL.process (Editing (TypeAt (fromInteger l) (fromInteger c) (UN n)))
|
||||
process (CaseSplit l c n)
|
||||
= Idris.REPL.process (Editing (CaseSplit False (fromInteger l) (fromInteger c) (UN n)))
|
||||
process (AddClause l n)
|
||||
= Idris.REPL.process (Editing (AddClause False (fromInteger l) (UN n)))
|
||||
process (ExprSearch l n hs all)
|
||||
= Idris.REPL.process (Editing (ExprSearch False (fromInteger l) (UN n)
|
||||
(map UN hs) all))
|
||||
process (GenerateDef l n)
|
||||
= Idris.REPL.process (Editing (GenerateDef False (fromInteger l) (UN n)))
|
||||
process (MakeLemma l n)
|
||||
= Idris.REPL.process (Editing (MakeLemma False (fromInteger l) (UN n)))
|
||||
process (MakeCase l n)
|
||||
= Idris.REPL.process (Editing (MakeCase False (fromInteger l) (UN n)))
|
||||
process (MakeWith l n)
|
||||
= Idris.REPL.process (Editing (MakeWith False (fromInteger l) (UN n)))
|
||||
process Version
|
||||
= Idris.REPL.process ShowVersion
|
||||
process (Metavariables _)
|
||||
= Idris.REPL.process Metavars
|
||||
process GetOptions
|
||||
= Idris.REPL.process GetOpts
|
||||
|
||||
processCatch : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
IDECommand -> Core REPLResult
|
||||
processCatch cmd
|
||||
= do c' <- branch
|
||||
u' <- get UST
|
||||
s' <- get Syn
|
||||
o' <- get ROpts
|
||||
catch (do res <- process cmd
|
||||
commit
|
||||
pure res)
|
||||
(\err => do put Ctxt c'
|
||||
put UST u'
|
||||
put Syn s'
|
||||
put ROpts o'
|
||||
msg <- perror err
|
||||
pure $ REPLError msg)
|
||||
|
||||
idePutStrLn : File -> Integer -> String -> Core ()
|
||||
idePutStrLn outf i msg
|
||||
= send outf (SExpList [SymbolAtom "write-string",
|
||||
toSExp msg, toSExp i])
|
||||
|
||||
returnFromIDE : File -> Integer -> SExp -> Core ()
|
||||
returnFromIDE outf i msg
|
||||
= do send outf (SExpList [SymbolAtom "return", msg, toSExp i])
|
||||
|
||||
printIDEResult : File -> Integer -> SExp -> Core ()
|
||||
printIDEResult outf i msg = returnFromIDE outf i (SExpList [SymbolAtom "ok", toSExp msg])
|
||||
|
||||
printIDEResultWithHighlight : File -> Integer -> SExp -> Core ()
|
||||
printIDEResultWithHighlight outf i msg = returnFromIDE outf i (SExpList [SymbolAtom "ok", toSExp msg
|
||||
-- TODO return syntax highlighted result
|
||||
, SExpList []])
|
||||
|
||||
printIDEError : File -> Integer -> String -> Core ()
|
||||
printIDEError outf i msg = returnFromIDE outf i (SExpList [SymbolAtom "error", toSExp msg ])
|
||||
|
||||
SExpable REPLEval where
|
||||
toSExp EvalTC = SymbolAtom "typecheck"
|
||||
toSExp NormaliseAll = SymbolAtom "normalise"
|
||||
toSExp Execute = SymbolAtom "execute"
|
||||
|
||||
SExpable REPLOpt where
|
||||
toSExp (ShowImplicits impl) = SExpList [ SymbolAtom "show-implicits", toSExp impl ]
|
||||
toSExp (ShowNamespace ns) = SExpList [ SymbolAtom "show-namespace", toSExp ns ]
|
||||
toSExp (ShowTypes typs) = SExpList [ SymbolAtom "show-types", toSExp typs ]
|
||||
toSExp (EvalMode mod) = SExpList [ SymbolAtom "eval", toSExp mod ]
|
||||
toSExp (Editor editor) = SExpList [ SymbolAtom "editor", toSExp editor ]
|
||||
toSExp (CG str) = SExpList [ SymbolAtom "cg", toSExp str ]
|
||||
|
||||
|
||||
sexpName : Name -> SExp
|
||||
sexpName n = SExpList [ StringAtom (show n), SExpList [], SExpList [] ]
|
||||
|
||||
displayIDEResult : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
File -> Integer -> REPLResult -> Core ()
|
||||
displayIDEResult outf i (REPLError err) = printIDEError outf i err
|
||||
displayIDEResult outf i (Evaluated x Nothing) = printIDEResultWithHighlight outf i $ StringAtom $ show x
|
||||
displayIDEResult outf i (Evaluated x (Just y)) = printIDEResultWithHighlight outf i $ StringAtom $ show x ++ " : " ++ show y
|
||||
displayIDEResult outf i (Printed xs) = printIDEResultWithHighlight outf i $ StringAtom $ showSep "\n" xs
|
||||
displayIDEResult outf i (TermChecked x y) = printIDEResultWithHighlight outf i $ StringAtom $ show x ++ " : " ++ show y
|
||||
displayIDEResult outf i (FileLoaded x) = printIDEResult outf i $ SExpList []
|
||||
displayIDEResult outf i (ErrorLoadingFile x err) = printIDEError outf i $ "Error loading file " ++ x ++ ": " ++ show err
|
||||
displayIDEResult outf i (ErrorsBuildingFile x errs) = printIDEError outf i $ "Error(s) building file " ++ x ++ ": " ++ (showSep "\n" $ map show errs)
|
||||
displayIDEResult outf i NoFileLoaded = printIDEError outf i "No file can be reloaded"
|
||||
displayIDEResult outf i (CurrentDirectory dir) = printIDEResult outf i $ StringAtom $ "Current working directory is '" ++ dir ++ "'"
|
||||
displayIDEResult outf i CompilationFailed = printIDEError outf i "Compilation failed"
|
||||
displayIDEResult outf i (Compiled f) = printIDEResult outf i $ StringAtom $ "File " ++ f ++ " written"
|
||||
displayIDEResult outf i (ProofFound x) = printIDEResult outf i $ StringAtom $ show x
|
||||
--displayIDEResult outf i (Missed cases) = printIDEResult outf i $ showSep "\n" $ map handleMissing cases
|
||||
displayIDEResult outf i (CheckedTotal xs) = printIDEResult outf i $ StringAtom $ showSep "\n" $ map (\ (fn, tot) => (show fn ++ " is " ++ show tot)) xs
|
||||
displayIDEResult outf i (FoundHoles []) = printIDEResult outf i $ SExpList []
|
||||
displayIDEResult outf i (FoundHoles xs) = printIDEResult outf i $ holesSexp
|
||||
where
|
||||
holesSexp : SExp
|
||||
holesSexp = SExpList $ map sexpName xs
|
||||
|
||||
displayIDEResult outf i (LogLevelSet k) = printIDEResult outf i $ StringAtom $ "Set loglevel to " ++ show k
|
||||
displayIDEResult outf i (OptionsSet opts) = printIDEResult outf i optionsSexp
|
||||
where
|
||||
optionsSexp : SExp
|
||||
optionsSexp = SExpList $ map toSExp opts
|
||||
displayIDEResult outf i (VersionIs x) = printIDEResult outf i versionSExp
|
||||
where
|
||||
semverSexp : SExp
|
||||
semverSexp = case (semVer x) of
|
||||
(maj, min, patch) => SExpList (map toSExp [maj, min, patch])
|
||||
tagSexp : SExp
|
||||
tagSexp = case versionTag x of
|
||||
Nothing => SExpList [ StringAtom "" ]
|
||||
Just t => SExpList [ StringAtom t ]
|
||||
versionSExp : SExp
|
||||
versionSExp = SExpList [ semverSexp, tagSexp ]
|
||||
|
||||
|
||||
displayIDEResult outf i (Edited (DisplayEdit xs)) = printIDEResult outf i $ StringAtom $ showSep "\n" xs
|
||||
displayIDEResult outf i (Edited (EditError x)) = printIDEError outf i x
|
||||
displayIDEResult outf i (Edited (MadeLemma lit name pty pappstr)) =
|
||||
printIDEResult outf i $ StringAtom $ (relit lit $ show name ++ " : " ++ show pty ++ "\n") ++ pappstr
|
||||
displayIDEResult outf i _ = pure ()
|
||||
|
||||
|
||||
handleIDEResult : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
File -> Integer -> REPLResult -> Core ()
|
||||
handleIDEResult outf i Exited = idePutStrLn outf i "Bye for now!"
|
||||
handleIDEResult outf i other = displayIDEResult outf i other
|
||||
|
||||
loop : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
Core ()
|
||||
loop
|
||||
= do res <- getOutput
|
||||
case res of
|
||||
REPL _ => printError "Running idemode but output isn't"
|
||||
IDEMode idx inf outf => do
|
||||
inp <- coreLift $ getInput inf
|
||||
end <- coreLift $ fEOF inf
|
||||
if end then pure ()
|
||||
else case parseSExp inp of
|
||||
Left err =>
|
||||
do printIDEError outf idx ("Parse error: " ++ show err)
|
||||
loop
|
||||
Right sexp =>
|
||||
case getMsg sexp of
|
||||
Just (cmd, i) =>
|
||||
do updateOutput i
|
||||
res <- processCatch cmd
|
||||
handleIDEResult outf i res
|
||||
loop
|
||||
Nothing =>
|
||||
do printIDEError outf idx ("Unrecognised command: " ++ show sexp)
|
||||
loop
|
||||
where
|
||||
updateOutput : Integer -> Core ()
|
||||
updateOutput idx
|
||||
= do IDEMode _ i o <- getOutput
|
||||
| _ => pure ()
|
||||
setOutput (IDEMode idx i o)
|
||||
-}
|
||||
|
||||
export
|
||||
replIDE : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
Core ()
|
||||
replIDE = throw (InternalError "Not implemented yet")
|
||||
-- = do res <- getOutput
|
||||
-- case res of
|
||||
-- REPL _ => printError "Running idemode but output isn't"
|
||||
-- IDEMode _ inf outf => do
|
||||
-- send outf (version 2 0)
|
||||
-- loop
|
126
src/Idris/IDEMode/SyntaxHighlight.idr
Normal file
126
src/Idris/IDEMode/SyntaxHighlight.idr
Normal file
@ -0,0 +1,126 @@
|
||||
module Idris.IDEMode.SyntaxHighlight
|
||||
|
||||
import Core.Context
|
||||
import Core.InitPrimitives
|
||||
import Core.Metadata
|
||||
import Core.TT
|
||||
|
||||
import Idris.REPL
|
||||
import Idris.IDEMode.Commands
|
||||
|
||||
import Data.List
|
||||
|
||||
data Decoration : Type where
|
||||
Typ : Decoration
|
||||
Function : Decoration
|
||||
Data : Decoration
|
||||
Keyword : Decoration
|
||||
Bound : Decoration
|
||||
|
||||
SExpable Decoration where
|
||||
toSExp Typ = SymbolAtom "type"
|
||||
toSExp Function = SymbolAtom "function"
|
||||
toSExp Data = SymbolAtom "data"
|
||||
toSExp Keyword = SymbolAtom "keyword"
|
||||
toSExp Bound = SymbolAtom "bound"
|
||||
|
||||
record Highlight where
|
||||
constructor MkHighlight
|
||||
location : FC
|
||||
name : Name
|
||||
isImplicit : Bool
|
||||
key : String
|
||||
decor : Decoration
|
||||
docOverview : String
|
||||
typ : String
|
||||
ns : String
|
||||
|
||||
SExpable FC where
|
||||
toSExp (MkFC fname (startLine, startCol) (endLine, endCol))
|
||||
= SExpList [ SExpList [ SymbolAtom "filename", StringAtom fname ]
|
||||
, SExpList [ SymbolAtom "start", IntegerAtom (cast startLine + 1), IntegerAtom (cast startCol + 1) ]
|
||||
, SExpList [ SymbolAtom "end", IntegerAtom (cast endLine + 1), IntegerAtom (cast endCol + 1) ]
|
||||
]
|
||||
toSExp EmptyFC = SExpList []
|
||||
|
||||
SExpable Highlight where
|
||||
toSExp (MkHighlight loc nam impl k dec doc t ns)
|
||||
= SExpList [ toSExp loc
|
||||
, SExpList [ SExpList [ SymbolAtom "name", StringAtom (show nam) ]
|
||||
, SExpList [ SymbolAtom "namespace", StringAtom ns ]
|
||||
, SExpList [ SymbolAtom "decor", toSExp dec ]
|
||||
, SExpList [ SymbolAtom "implicit", toSExp impl ]
|
||||
, SExpList [ SymbolAtom "key", StringAtom k ]
|
||||
, SExpList [ SymbolAtom "doc-overview", StringAtom doc ]
|
||||
, SExpList [ SymbolAtom "type", StringAtom t ]
|
||||
]
|
||||
]
|
||||
|
||||
inFile : String -> (FC, (Name, Nat, ClosedTerm)) -> Bool
|
||||
inFile fname (MkFC file _ _, _) = file == fname
|
||||
|
||||
||| Output some data using current dialog index
|
||||
export
|
||||
printOutput : {auto o : Ref ROpts REPLOpts} ->
|
||||
SExp -> Core ()
|
||||
printOutput msg
|
||||
= do opts <- get ROpts
|
||||
case idemode opts of
|
||||
REPL _ => pure ()
|
||||
IDEMode i _ f =>
|
||||
send f (SExpList [SymbolAtom "output",
|
||||
msg, toSExp i])
|
||||
|
||||
|
||||
outputHighlight : {auto opts : Ref ROpts REPLOpts} ->
|
||||
Highlight -> Core ()
|
||||
outputHighlight h =
|
||||
printOutput $ SExpList [ SymbolAtom "ok"
|
||||
, SExpList [ SymbolAtom "highlight-source"
|
||||
, toSExp hlt
|
||||
]
|
||||
]
|
||||
where
|
||||
hlt : List Highlight
|
||||
hlt = [h]
|
||||
|
||||
outputNameSyntax : {auto opts : Ref ROpts REPLOpts} ->
|
||||
(FC, (Name, Nat, ClosedTerm)) -> Core ()
|
||||
outputNameSyntax (fc, (name, _, term)) =
|
||||
let dec = case term of
|
||||
(Local fc x idx y) => Just Bound
|
||||
|
||||
-- See definition of NameType in Core.TT for possible values of Ref's nametype field
|
||||
-- data NameType : Type where
|
||||
-- Bound : NameType
|
||||
-- Func : NameType
|
||||
-- DataCon : (tag : Int) -> (arity : Nat) -> NameType
|
||||
-- TyCon : (tag : Int) -> (arity : Nat) -> NameType
|
||||
(Ref fc Bound name) => Just Bound
|
||||
(Ref fc Func name) => Just Function
|
||||
(Ref fc (DataCon tag arity) name) => Just Data
|
||||
(Ref fc (TyCon tag arity) name) => Just Typ
|
||||
(Meta fc x y xs) => Just Bound
|
||||
(Bind fc x b scope) => Just Bound
|
||||
(App fc fn arg) => Just Bound
|
||||
(As fc x as pat) => Just Bound
|
||||
(TDelayed fc x y) => Nothing
|
||||
(TDelay fc x ty arg) => Nothing
|
||||
(TForce fc x y) => Nothing
|
||||
(PrimVal fc c) => Just Typ
|
||||
(Erased fc imp) => Just Bound
|
||||
(TType fc) => Just Typ
|
||||
hilite = Prelude.map (\ d => MkHighlight fc name False "" d "" (show term) "") dec
|
||||
in maybe (pure ()) outputHighlight hilite
|
||||
|
||||
export
|
||||
outputSyntaxHighlighting : {auto m : Ref MD Metadata} ->
|
||||
{auto opts : Ref ROpts REPLOpts} ->
|
||||
String ->
|
||||
REPLResult ->
|
||||
Core REPLResult
|
||||
outputSyntaxHighlighting fname loadResult = do
|
||||
allNames <- filter (inFile fname) . names <$> get MD
|
||||
-- decls <- filter (inFile fname) . tydecls <$> get MD
|
||||
_ <- traverse outputNameSyntax allNames -- ++ decls)
|
||||
pure loadResult
|
37
src/Idris/IDEMode/TokenLine.idr
Normal file
37
src/Idris/IDEMode/TokenLine.idr
Normal file
@ -0,0 +1,37 @@
|
||||
||| Tokenise a source line for easier processing
|
||||
module Idris.IDEMode.TokenLine
|
||||
|
||||
import Parser.Lexer
|
||||
import Text.Lexer
|
||||
|
||||
public export
|
||||
data SourcePart
|
||||
= Whitespace String
|
||||
| Name String
|
||||
| HoleName String
|
||||
| LBrace
|
||||
| RBrace
|
||||
| Equal
|
||||
| Other String
|
||||
|
||||
holeIdent : Lexer
|
||||
holeIdent = is '?' <+> identNormal
|
||||
|
||||
srcTokens : TokenMap SourcePart
|
||||
srcTokens =
|
||||
[(identNormal, Name),
|
||||
(holeIdent, \x => HoleName (assert_total (prim__strTail x))),
|
||||
(space, Whitespace),
|
||||
(is '{', const LBrace),
|
||||
(is '}', const RBrace),
|
||||
(is '=', const Equal),
|
||||
(any, Other)]
|
||||
|
||||
export
|
||||
tokens : String -> List SourcePart
|
||||
tokens str
|
||||
= case lex srcTokens str of
|
||||
-- Add the EndInput token so that we'll have a line and column
|
||||
-- number to read when storing spans in the file
|
||||
(srctoks, (l, c, rest)) =>
|
||||
map tok srctoks ++ (if rest == "" then [] else [Other rest])
|
233
src/Idris/Main.idr
Normal file
233
src/Idris/Main.idr
Normal file
@ -0,0 +1,233 @@
|
||||
module Main
|
||||
|
||||
import Core.Binary
|
||||
import Core.Context
|
||||
import Core.Core
|
||||
import Core.Directory
|
||||
import Core.InitPrimitives
|
||||
import Core.Metadata
|
||||
import Core.Options
|
||||
import Core.Unify
|
||||
|
||||
import Idris.CommandLine
|
||||
import Idris.Desugar
|
||||
import Idris.IDEMode.REPL
|
||||
import Idris.ModTree
|
||||
import Idris.Package
|
||||
import Idris.Parser
|
||||
import Idris.ProcessIdr
|
||||
import Idris.REPL
|
||||
import Idris.SetOptions
|
||||
import Idris.Syntax
|
||||
import Idris.Version
|
||||
|
||||
import Data.List
|
||||
import Data.Strings
|
||||
import Data.Vect
|
||||
import System
|
||||
|
||||
import Yaffle.Main
|
||||
|
||||
import IdrisPaths
|
||||
|
||||
%default covering
|
||||
|
||||
yprefix : String
|
||||
yprefix = "/home/edwin/.idris2" -- TODO! unsafePerformIO (foreign FFI_C "getIdris2_prefix" (IO String))
|
||||
|
||||
findInput : List CLOpt -> Maybe String
|
||||
findInput [] = Nothing
|
||||
findInput (InputFile f :: fs) = Just f
|
||||
findInput (_ :: fs) = findInput fs
|
||||
|
||||
-- Add extra library directories from the "BLODWEN_PATH"
|
||||
-- environment variable
|
||||
updatePaths : {auto c : Ref Ctxt Defs} ->
|
||||
Core ()
|
||||
updatePaths
|
||||
= do bprefix <- coreLift $ getEnv "IDRIS2_PREFIX"
|
||||
the (Core ()) $ case bprefix of
|
||||
Just p => setPrefix p
|
||||
Nothing => setPrefix yprefix
|
||||
bpath <- coreLift $ getEnv "IDRIS2_PATH"
|
||||
the (Core ()) $ case bpath of
|
||||
Just path => do traverse_ addExtraDir (map trim (split (==pathSep) path))
|
||||
Nothing => pure ()
|
||||
bdata <- coreLift $ getEnv "IDRIS2_DATA"
|
||||
the (Core ()) $ case bdata of
|
||||
Just path => do traverse_ addDataDir (map trim (split (==pathSep) path))
|
||||
Nothing => pure ()
|
||||
blibs <- coreLift $ getEnv "IDRIS2_LIBS"
|
||||
the (Core ()) $ case blibs of
|
||||
Just path => do traverse_ addLibDir (map trim (split (==pathSep) path))
|
||||
Nothing => pure ()
|
||||
-- IDRIS2_PATH goes first so that it overrides this if there's
|
||||
-- any conflicts. In particular, that means that setting IDRIS2_PATH
|
||||
-- for the tests means they test the local version not the installed
|
||||
-- version
|
||||
defs <- get Ctxt
|
||||
addPkgDir "prelude"
|
||||
addPkgDir "base"
|
||||
addDataDir (dir_prefix (dirs (options defs)) ++ dirSep ++
|
||||
"idris2-" ++ showVersion False version ++ dirSep ++ "support")
|
||||
addLibDir (dir_prefix (dirs (options defs)) ++ dirSep ++
|
||||
"idris2-" ++ showVersion False version ++ dirSep ++ "lib")
|
||||
Just cwd <- coreLift $ currentDir
|
||||
| Nothing => throw (InternalError "Can't get current directory")
|
||||
addLibDir cwd
|
||||
|
||||
updateREPLOpts : {auto o : Ref ROpts REPLOpts} ->
|
||||
Core ()
|
||||
updateREPLOpts
|
||||
= do opts <- get ROpts
|
||||
ed <- coreLift $ getEnv "EDITOR"
|
||||
case ed of
|
||||
Just e => put ROpts (record { editor = e } opts)
|
||||
Nothing => pure ()
|
||||
|
||||
showInfo : {auto c : Ref Ctxt Defs}
|
||||
-> {auto o : Ref ROpts REPLOpts}
|
||||
-> List CLOpt
|
||||
-> Core Bool
|
||||
showInfo Nil = pure False
|
||||
showInfo (BlodwenPaths :: _)
|
||||
= do defs <- get Ctxt
|
||||
iputStrLn (toString (dirs (options defs)))
|
||||
pure True
|
||||
showInfo (_::rest) = showInfo rest
|
||||
|
||||
tryYaffle : List CLOpt -> Core Bool
|
||||
tryYaffle [] = pure False
|
||||
tryYaffle (Yaffle f :: _) = do yaffleMain f []
|
||||
pure True
|
||||
tryYaffle (c :: cs) = tryYaffle cs
|
||||
|
||||
tryTTM : List CLOpt -> Core Bool
|
||||
tryTTM [] = pure False
|
||||
tryTTM (Metadata f :: _) = do dumpTTM f
|
||||
pure True
|
||||
tryTTM (c :: cs) = tryTTM cs
|
||||
|
||||
|
||||
banner : String
|
||||
banner = " ____ __ _ ___ \n" ++
|
||||
" / _/___/ /____(_)____ |__ \\ \n" ++
|
||||
" / // __ / ___/ / ___/ __/ / Version " ++ showVersion True version ++ "\n" ++
|
||||
" _/ // /_/ / / / (__ ) / __/ https://www.idris-lang.org \n" ++
|
||||
" /___/\\__,_/_/ /_/____/ /____/ Type :? for help \n" ++
|
||||
"\n" ++
|
||||
"Welcome to Idris 2. Enjoy yourself!"
|
||||
|
||||
checkVerbose : List CLOpt -> Bool
|
||||
checkVerbose [] = False
|
||||
checkVerbose (Verbose :: _) = True
|
||||
checkVerbose (_ :: xs) = checkVerbose xs
|
||||
|
||||
stMain : List CLOpt -> Core ()
|
||||
stMain opts
|
||||
= do False <- tryYaffle opts
|
||||
| True => pure ()
|
||||
False <- tryTTM opts
|
||||
| True => pure ()
|
||||
defs <- initDefs
|
||||
c <- newRef Ctxt defs
|
||||
s <- newRef Syn initSyntax
|
||||
m <- newRef MD initMetadata
|
||||
addPrimitives
|
||||
|
||||
setWorkingDir "."
|
||||
updatePaths
|
||||
let ide = ideMode opts
|
||||
let ideSocket = ideModeSocket opts
|
||||
let outmode = if ide then IDEMode 0 stdin stdout else REPL False
|
||||
let fname = findInput opts
|
||||
o <- newRef ROpts (REPLOpts.defaultOpts fname outmode)
|
||||
|
||||
finish <- showInfo opts
|
||||
if finish
|
||||
then pure ()
|
||||
else do
|
||||
|
||||
-- If there's a --build or --install, just do that then quit
|
||||
done <- processPackageOpts opts
|
||||
|
||||
when (not done) $
|
||||
do True <- preOptions opts
|
||||
| False => pure ()
|
||||
|
||||
when (checkVerbose opts) $ -- override Quiet if implicitly set
|
||||
setOutput (REPL False)
|
||||
u <- newRef UST initUState
|
||||
updateREPLOpts
|
||||
session <- getSession
|
||||
when (not $ nobanner session) $
|
||||
iputStrLn banner
|
||||
fname <- if findipkg session
|
||||
then findIpkg fname
|
||||
else pure fname
|
||||
the (Core ()) $ case fname of
|
||||
Nothing => logTime "Loading prelude" $
|
||||
when (not $ noprelude session) $
|
||||
readPrelude
|
||||
Just f => logTime "Loading main file" $
|
||||
(loadMainFile f >>= displayErrors)
|
||||
|
||||
doRepl <- postOptions opts
|
||||
if doRepl then
|
||||
if ide || ideSocket then
|
||||
if not ideSocket
|
||||
then do
|
||||
setOutput (IDEMode 0 stdin stdout)
|
||||
replIDE {c} {u} {m}
|
||||
else do
|
||||
throw (InternalError "Not implemeted yet")
|
||||
-- let (host, port) = ideSocketModeHostPort opts
|
||||
-- f <- coreLift $ initIDESocketFile host port
|
||||
-- case f of
|
||||
-- Left err => do
|
||||
-- coreLift $ putStrLn err
|
||||
-- coreLift $ exit 1
|
||||
-- Right file => do
|
||||
-- setOutput (IDEMode 0 file file)
|
||||
-- replIDE {c} {u} {m}
|
||||
else do
|
||||
repl {c} {u} {m}
|
||||
showTimeRecord
|
||||
else
|
||||
-- exit with an error code if there was an error, otherwise
|
||||
-- just exit
|
||||
do ropts <- get ROpts
|
||||
showTimeRecord
|
||||
case errorLine ropts of
|
||||
Nothing => pure ()
|
||||
Just _ => coreLift $ exitWith (ExitFailure 1)
|
||||
|
||||
-- Run any options (such as --version or --help) which imply printing a
|
||||
-- message then exiting. Returns wheter the program should continue
|
||||
quitOpts : List CLOpt -> IO Bool
|
||||
quitOpts [] = pure True
|
||||
quitOpts (Version :: _)
|
||||
= do putStrLn versionMsg
|
||||
pure False
|
||||
quitOpts (Help :: _)
|
||||
= do putStrLn usage
|
||||
pure False
|
||||
quitOpts (ShowPrefix :: _)
|
||||
= do putStrLn yprefix
|
||||
pure False
|
||||
quitOpts (_ :: opts) = quitOpts opts
|
||||
|
||||
main : IO ()
|
||||
main = do Right opts <- getCmdOpts
|
||||
| Left err =>
|
||||
do putStrLn err
|
||||
putStrLn usage
|
||||
continue <- quitOpts opts
|
||||
if continue
|
||||
then
|
||||
coreRun (stMain opts)
|
||||
(\err : Error =>
|
||||
do putStrLn ("Uncaught error: " ++ show err)
|
||||
exitWith (ExitFailure 1))
|
||||
(\res => pure ())
|
||||
else pure ()
|
512
src/Idris/Package.idr
Normal file
512
src/Idris/Package.idr
Normal file
@ -0,0 +1,512 @@
|
||||
module Idris.Package
|
||||
|
||||
import Compiler.Common
|
||||
|
||||
import Core.Context
|
||||
import Core.Core
|
||||
import Core.Directory
|
||||
import Core.Metadata
|
||||
import Core.Options
|
||||
import Core.Unify
|
||||
|
||||
import Data.List
|
||||
import Data.StringMap
|
||||
import Data.Strings
|
||||
import Data.StringTrie
|
||||
import Data.These
|
||||
|
||||
import Idris.CommandLine
|
||||
import Idris.ModTree
|
||||
import Idris.ProcessIdr
|
||||
import Idris.REPL
|
||||
import Idris.REPLOpts
|
||||
import Idris.SetOptions
|
||||
import Idris.Syntax
|
||||
import Idris.Version
|
||||
import Parser.Lexer
|
||||
import Parser.Support
|
||||
import Utils.Binary
|
||||
|
||||
import System
|
||||
import Text.Parser
|
||||
|
||||
import IdrisPaths
|
||||
|
||||
%default covering
|
||||
|
||||
record PkgDesc where
|
||||
constructor MkPkgDesc
|
||||
name : String
|
||||
version : String
|
||||
authors : String
|
||||
maintainers : Maybe String
|
||||
license : Maybe String
|
||||
brief : Maybe String
|
||||
readme : Maybe String
|
||||
homepage : Maybe String
|
||||
sourceloc : Maybe String
|
||||
bugtracker : Maybe String
|
||||
depends : List String -- packages to add to search path
|
||||
modules : List (List String, String) -- modules to install (namespace, filename)
|
||||
mainmod : Maybe (List String, String) -- main file (i.e. file to load at REPL)
|
||||
executable : Maybe String -- name of executable
|
||||
options : Maybe (FC, String)
|
||||
sourcedir : Maybe String
|
||||
prebuild : Maybe (FC, String) -- Script to run before building
|
||||
postbuild : Maybe (FC, String) -- Script to run after building
|
||||
preinstall : Maybe (FC, String) -- Script to run after building, before installing
|
||||
postinstall : Maybe (FC, String) -- Script to run after installing
|
||||
preclean : Maybe (FC, String) -- Script to run before cleaning
|
||||
postclean : Maybe (FC, String) -- Script to run after cleaning
|
||||
|
||||
Show PkgDesc where
|
||||
show pkg = "Package: " ++ name pkg ++ "\n" ++
|
||||
"Version: " ++ version pkg ++ "\n" ++
|
||||
"Authors: " ++ authors pkg ++ "\n" ++
|
||||
maybe "" (\m => "Maintainers: " ++ m ++ "\n") (maintainers pkg) ++
|
||||
maybe "" (\m => "License: " ++ m ++ "\n") (license pkg) ++
|
||||
maybe "" (\m => "Brief: " ++ m ++ "\n") (brief pkg) ++
|
||||
maybe "" (\m => "ReadMe: " ++ m ++ "\n") (readme pkg) ++
|
||||
maybe "" (\m => "HomePage: " ++ m ++ "\n") (homepage pkg) ++
|
||||
maybe "" (\m => "SourceLoc: " ++ m ++ "\n") (sourceloc pkg) ++
|
||||
maybe "" (\m => "BugTracker: " ++ m ++ "\n") (bugtracker pkg) ++
|
||||
"Depends: " ++ show (depends pkg) ++ "\n" ++
|
||||
"Modules: " ++ show (map snd (modules pkg)) ++ "\n" ++
|
||||
maybe "" (\m => "Main: " ++ snd m ++ "\n") (mainmod pkg) ++
|
||||
maybe "" (\m => "Exec: " ++ m ++ "\n") (executable pkg) ++
|
||||
maybe "" (\m => "Opts: " ++ snd m ++ "\n") (options pkg) ++
|
||||
maybe "" (\m => "SourceDir: " ++ m ++ "\n") (sourcedir pkg) ++
|
||||
maybe "" (\m => "Prebuild: " ++ snd m ++ "\n") (prebuild pkg) ++
|
||||
maybe "" (\m => "Postbuild: " ++ snd m ++ "\n") (postbuild pkg) ++
|
||||
maybe "" (\m => "Preinstall: " ++ snd m ++ "\n") (preinstall pkg) ++
|
||||
maybe "" (\m => "Postinstall: " ++ snd m ++ "\n") (postinstall pkg) ++
|
||||
maybe "" (\m => "Preclean: " ++ snd m ++ "\n") (preclean pkg) ++
|
||||
maybe "" (\m => "Postclean: " ++ snd m ++ "\n") (postclean pkg)
|
||||
|
||||
initPkgDesc : String -> PkgDesc
|
||||
initPkgDesc pname
|
||||
= MkPkgDesc pname "0" "Anonymous" Nothing Nothing
|
||||
Nothing Nothing Nothing Nothing Nothing
|
||||
[] []
|
||||
Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
|
||||
Nothing Nothing
|
||||
|
||||
data DescField : Type where
|
||||
PVersion : FC -> String -> DescField
|
||||
PAuthors : FC -> String -> DescField
|
||||
PMaintainers : FC -> String -> DescField
|
||||
PLicense : FC -> String -> DescField
|
||||
PBrief : FC -> String -> DescField
|
||||
PReadMe : FC -> String -> DescField
|
||||
PHomePage : FC -> String -> DescField
|
||||
PSourceLoc : FC -> String -> DescField
|
||||
PBugTracker : FC -> String -> DescField
|
||||
PDepends : List String -> DescField
|
||||
PModules : List (FC, List String) -> DescField
|
||||
PMainMod : FC -> List String -> DescField
|
||||
PExec : String -> DescField
|
||||
POpts : FC -> String -> DescField
|
||||
PSourceDir : FC -> String -> DescField
|
||||
PPrebuild : FC -> String -> DescField
|
||||
PPostbuild : FC -> String -> DescField
|
||||
PPreinstall : FC -> String -> DescField
|
||||
PPostinstall : FC -> String -> DescField
|
||||
PPreclean : FC -> String -> DescField
|
||||
PPostclean : FC -> String -> DescField
|
||||
|
||||
field : String -> Rule DescField
|
||||
field fname
|
||||
= strField PVersion "version"
|
||||
<|> strField PAuthors "authors"
|
||||
<|> strField PMaintainers "maintainers"
|
||||
<|> strField PLicense "license"
|
||||
<|> strField PBrief "brief"
|
||||
<|> strField PReadMe "readme"
|
||||
<|> strField PHomePage "homepage"
|
||||
<|> strField PSourceLoc "sourceloc"
|
||||
<|> strField PBugTracker "bugtracker"
|
||||
<|> strField POpts "options"
|
||||
<|> strField POpts "opts"
|
||||
<|> strField PSourceDir "sourcedir"
|
||||
<|> strField PPrebuild "prebuild"
|
||||
<|> strField PPostbuild "postbuild"
|
||||
<|> strField PPreinstall "preinstall"
|
||||
<|> strField PPostinstall "postinstall"
|
||||
<|> strField PPreclean "preclean"
|
||||
<|> strField PPostclean "postclean"
|
||||
<|> do exactIdent "depends"; symbol "="
|
||||
ds <- sepBy1 (symbol ",") unqualifiedName
|
||||
pure (PDepends ds)
|
||||
<|> do exactIdent "modules"; symbol "="
|
||||
ms <- sepBy1 (symbol ",")
|
||||
(do start <- location
|
||||
ns <- nsIdent
|
||||
end <- location
|
||||
pure (MkFC fname start end, ns))
|
||||
pure (PModules ms)
|
||||
<|> do exactIdent "main"; symbol "="
|
||||
start <- location
|
||||
m <- nsIdent
|
||||
end <- location
|
||||
pure (PMainMod (MkFC fname start end) m)
|
||||
<|> do exactIdent "executable"; symbol "="
|
||||
e <- unqualifiedName
|
||||
pure (PExec e)
|
||||
where
|
||||
getStr : (FC -> String -> DescField) -> FC ->
|
||||
String -> Constant -> EmptyRule DescField
|
||||
getStr p fc fld (Str s) = pure (p fc s)
|
||||
getStr p fc fld _ = fail $ fld ++ " field must be a string"
|
||||
|
||||
strField : (FC -> String -> DescField) -> String -> Rule DescField
|
||||
strField p f
|
||||
= do start <- location
|
||||
exactIdent f
|
||||
symbol "="
|
||||
c <- constant
|
||||
end <- location
|
||||
getStr p (MkFC fname start end) f c
|
||||
|
||||
parsePkgDesc : String -> Rule (String, List DescField)
|
||||
parsePkgDesc fname
|
||||
= do exactIdent "package"
|
||||
name <- unqualifiedName
|
||||
fields <- many (field fname)
|
||||
pure (name, fields)
|
||||
|
||||
data ParsedMods : Type where
|
||||
|
||||
data MainMod : Type where
|
||||
|
||||
addField : {auto c : Ref Ctxt Defs} ->
|
||||
{auto p : Ref ParsedMods (List (FC, List String))} ->
|
||||
{auto m : Ref MainMod (Maybe (FC, List String))} ->
|
||||
DescField -> PkgDesc -> Core PkgDesc
|
||||
addField (PVersion fc n) pkg = pure $ record { version = n } pkg
|
||||
addField (PAuthors fc a) pkg = pure $ record { authors = a } pkg
|
||||
addField (PMaintainers fc a) pkg = pure $ record { maintainers = Just a } pkg
|
||||
addField (PLicense fc a) pkg = pure $ record { license = Just a } pkg
|
||||
addField (PBrief fc a) pkg = pure $ record { brief = Just a } pkg
|
||||
addField (PReadMe fc a) pkg = pure $ record { readme = Just a } pkg
|
||||
addField (PHomePage fc a) pkg = pure $ record { homepage = Just a } pkg
|
||||
addField (PSourceLoc fc a) pkg = pure $ record { sourceloc = Just a } pkg
|
||||
addField (PBugTracker fc a) pkg = pure $ record { bugtracker = Just a } pkg
|
||||
addField (PDepends ds) pkg = pure $ record { depends = ds } pkg
|
||||
-- we can't resolve source files for modules without knowing the source directory,
|
||||
-- so we save them for the second pass
|
||||
addField (PModules ms) pkg = do put ParsedMods ms
|
||||
pure pkg
|
||||
addField (PMainMod loc n) pkg = do put MainMod (Just (loc, n))
|
||||
pure pkg
|
||||
addField (PExec e) pkg = pure $ record { executable = Just e } pkg
|
||||
addField (POpts fc e) pkg = pure $ record { options = Just (fc, e) } pkg
|
||||
addField (PSourceDir fc a) pkg = pure $ record { sourcedir = Just a } pkg
|
||||
addField (PPrebuild fc e) pkg = pure $ record { prebuild = Just (fc, e) } pkg
|
||||
addField (PPostbuild fc e) pkg = pure $ record { postbuild = Just (fc, e) } pkg
|
||||
addField (PPreinstall fc e) pkg = pure $ record { preinstall = Just (fc, e) } pkg
|
||||
addField (PPostinstall fc e) pkg = pure $ record { postinstall = Just (fc, e) } pkg
|
||||
addField (PPreclean fc e) pkg = pure $ record { preclean = Just (fc, e) } pkg
|
||||
addField (PPostclean fc e) pkg = pure $ record { postclean = Just (fc, e) } pkg
|
||||
|
||||
addFields : {auto c : Ref Ctxt Defs} ->
|
||||
List DescField -> PkgDesc -> Core PkgDesc
|
||||
addFields xs desc = do p <- newRef ParsedMods []
|
||||
m <- newRef MainMod Nothing
|
||||
added <- go {p} {m} xs desc
|
||||
setSourceDir (sourcedir added)
|
||||
ms <- get ParsedMods
|
||||
mmod <- get MainMod
|
||||
pure $ record { modules = !(traverse toSource ms)
|
||||
, mainmod = !(traverseOpt toSource mmod)
|
||||
} added
|
||||
where
|
||||
toSource : (FC, List String) -> Core (List String, String)
|
||||
toSource (loc, ns) = pure (ns, !(nsToSource loc ns))
|
||||
go : {auto p : Ref ParsedMods (List (FC, List String))} ->
|
||||
{auto m : Ref MainMod (Maybe (FC, List String))} ->
|
||||
List DescField -> PkgDesc -> Core PkgDesc
|
||||
go [] dsc = pure dsc
|
||||
go (x :: xs) dsc = go xs !(addField x dsc)
|
||||
|
||||
runScript : Maybe (FC, String) -> Core ()
|
||||
runScript Nothing = pure ()
|
||||
runScript (Just (fc, s))
|
||||
= do res <- coreLift $ system s
|
||||
when (res /= 0) $
|
||||
throw (GenericMsg fc "Script failed")
|
||||
|
||||
addDeps : {auto c : Ref Ctxt Defs} ->
|
||||
PkgDesc -> Core ()
|
||||
addDeps pkg
|
||||
= do defs <- get Ctxt
|
||||
traverse_ addPkgDir (depends pkg)
|
||||
|
||||
processOptions : {auto c : Ref Ctxt Defs} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
Maybe (FC, String) -> Core ()
|
||||
processOptions Nothing = pure ()
|
||||
processOptions (Just (fc, opts))
|
||||
= do let Right clopts = getOpts (words opts)
|
||||
| Left err => throw (GenericMsg fc err)
|
||||
preOptions clopts
|
||||
pure ()
|
||||
|
||||
compileMain : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
Name -> String -> String -> Core ()
|
||||
compileMain mainn mmod exec
|
||||
= do m <- newRef MD initMetadata
|
||||
u <- newRef UST initUState
|
||||
|
||||
loadMainFile mmod
|
||||
compileExp (PRef replFC mainn) exec
|
||||
pure ()
|
||||
|
||||
build : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
PkgDesc -> Core (List Error)
|
||||
build pkg
|
||||
= do defs <- get Ctxt
|
||||
addDeps pkg
|
||||
processOptions (options pkg)
|
||||
runScript (prebuild pkg)
|
||||
let toBuild = maybe (map snd (modules pkg))
|
||||
(\m => snd m :: map snd (modules pkg))
|
||||
(mainmod pkg)
|
||||
[] <- buildAll toBuild
|
||||
| errs => pure errs
|
||||
|
||||
case executable pkg of
|
||||
Nothing => pure ()
|
||||
Just exec =>
|
||||
do let Just (mainns, mmod) = mainmod pkg
|
||||
| Nothing => throw (GenericMsg emptyFC "No main module given")
|
||||
let mainn = NS ["Main"] (UN "main")
|
||||
compileMain mainn mmod exec
|
||||
runScript (postbuild pkg)
|
||||
pure []
|
||||
|
||||
copyFile : String -> String -> IO (Either FileError ())
|
||||
copyFile src dest
|
||||
= do Right buf <- readFromFile src
|
||||
| Left err => pure (Left err)
|
||||
writeToFile dest buf
|
||||
|
||||
installFrom : {auto c : Ref Ctxt Defs} ->
|
||||
String -> String -> String -> List String -> Core ()
|
||||
installFrom _ _ _ [] = pure ()
|
||||
installFrom pname builddir destdir ns@(m :: dns)
|
||||
= do let ttcfile = showSep dirSep (reverse ns)
|
||||
let ttcPath = builddir ++ dirSep ++ "ttc" ++ dirSep ++ ttcfile ++ ".ttc"
|
||||
let destPath = destdir ++ dirSep ++ showSep dirSep (reverse dns)
|
||||
let destFile = destdir ++ dirSep ++ ttcfile ++ ".ttc"
|
||||
Right _ <- coreLift $ mkdirs (reverse dns)
|
||||
| Left err => throw (FileErr pname err)
|
||||
coreLift $ putStrLn $ "Installing " ++ ttcPath ++ " to " ++ destPath
|
||||
Right _ <- coreLift $ copyFile ttcPath destFile
|
||||
| Left err => throw (FileErr pname err)
|
||||
pure ()
|
||||
|
||||
-- Install all the built modules in prefix/package/
|
||||
-- We've already built and checked for success, so if any don't exist that's
|
||||
-- an internal error.
|
||||
install : {auto c : Ref Ctxt Defs} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
PkgDesc -> Core ()
|
||||
install pkg
|
||||
= do defs <- get Ctxt
|
||||
let build = build_dir (dirs (options defs))
|
||||
runScript (preinstall pkg)
|
||||
let toInstall = maybe (map fst (modules pkg))
|
||||
(\m => fst m :: map fst (modules pkg))
|
||||
(mainmod pkg)
|
||||
Just srcdir <- coreLift currentDir
|
||||
| Nothing => throw (InternalError "Can't get current directory")
|
||||
-- Make the package installation directory
|
||||
let installPrefix = dir_prefix (dirs (options defs)) ++
|
||||
dirSep ++ "idris2-" ++ showVersion False version
|
||||
True <- coreLift $ changeDir installPrefix
|
||||
| False => throw (FileErr (name pkg) FileReadError)
|
||||
Right _ <- coreLift $ mkdirs [name pkg]
|
||||
| Left err => throw (FileErr (name pkg) err)
|
||||
True <- coreLift $ changeDir (name pkg)
|
||||
| False => throw (FileErr (name pkg) FileReadError)
|
||||
|
||||
-- We're in that directory now, so copy the files from
|
||||
-- srcdir/build into it
|
||||
traverse (installFrom (name pkg)
|
||||
(srcdir ++ dirSep ++ build)
|
||||
(installPrefix ++ dirSep ++ name pkg)) toInstall
|
||||
coreLift $ changeDir srcdir
|
||||
runScript (postinstall pkg)
|
||||
|
||||
-- Data.These.bitraverse hand specialised for Core
|
||||
bitraverseC : (a -> Core c) -> (b -> Core d) -> These a b -> Core (These c d)
|
||||
bitraverseC f g (This a) = [| This (f a) |]
|
||||
bitraverseC f g (That b) = [| That (g b) |]
|
||||
bitraverseC f g (Both a b) = [| Both (f a) (g b) |]
|
||||
|
||||
-- Prelude.Monad.foldlM hand specialised for Core
|
||||
foldlC : Foldable t => (a -> b -> Core a) -> a -> t b -> Core a
|
||||
foldlC fm a0 = foldl (\ma,b => ma >>= flip fm b) (pure a0)
|
||||
|
||||
-- Data.StringTrie.foldWithKeysM hand specialised for Core
|
||||
foldWithKeysC : Monoid b => (List String -> Core b) -> (List String -> a -> Core b) -> StringTrie a -> Core b
|
||||
foldWithKeysC {a} {b} fk fv = go []
|
||||
where
|
||||
go : List String -> StringTrie a -> Core b
|
||||
go ks (MkStringTrie nd) =
|
||||
map bifold $ bitraverseC
|
||||
(fv ks)
|
||||
(\sm => foldlC
|
||||
(\x, (k, vs) =>
|
||||
do let ks' = ks++[k]
|
||||
y <- assert_total $ go ks' vs
|
||||
z <- fk ks'
|
||||
pure $ x <+> y <+> z)
|
||||
neutral
|
||||
(StringMap.toList sm))
|
||||
nd
|
||||
|
||||
Semigroup () where
|
||||
(<+>) _ _ = ()
|
||||
|
||||
Monoid () where
|
||||
neutral = ()
|
||||
|
||||
clean : {auto c : Ref Ctxt Defs} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
PkgDesc -> Core ()
|
||||
clean pkg
|
||||
= do defs <- get Ctxt
|
||||
let build = build_dir (dirs (options defs))
|
||||
let exec = exec_dir (dirs (options defs))
|
||||
runScript (preclean pkg)
|
||||
let pkgmods = maybe
|
||||
(map fst (modules pkg))
|
||||
(\m => fst m :: map fst (modules pkg))
|
||||
(mainmod pkg)
|
||||
let toClean : List (List String, String)
|
||||
= mapMaybe (\ks => case ks of
|
||||
[] => Nothing
|
||||
(x :: xs) => Just (xs, x)) pkgmods
|
||||
Just srcdir <- coreLift currentDir
|
||||
| Nothing => throw (InternalError "Can't get current directory")
|
||||
let builddir = srcdir ++ dirSep ++ build ++ dirSep ++ "ttc"
|
||||
let execdir = srcdir ++ dirSep ++ exec
|
||||
-- the usual pair syntax breaks with `No such variable a` here for some reason
|
||||
let pkgTrie = the (StringTrie (List String)) $
|
||||
foldl (\trie, ksv =>
|
||||
let ks = Builtin.fst ksv
|
||||
v = Builtin.snd ksv
|
||||
in
|
||||
insertWith (reverse ks) (maybe [v] (v::)) trie) empty toClean
|
||||
foldWithKeysC (deleteFolder builddir)
|
||||
(\ks => map concat . traverse (deleteBin builddir ks))
|
||||
pkgTrie
|
||||
deleteFolder builddir []
|
||||
maybe (pure ()) (\e => delete (execdir ++ dirSep ++ e))
|
||||
(executable pkg)
|
||||
runScript (postclean pkg)
|
||||
where
|
||||
delete : String -> Core ()
|
||||
delete path = do Right () <- coreLift $ fileRemove path
|
||||
| Left err => throw (FileErr (name pkg) err)
|
||||
coreLift $ putStrLn $ "Removed: " ++ path
|
||||
|
||||
deleteFolder : String -> List String -> Core ()
|
||||
deleteFolder builddir ns = delete $ builddir ++ dirSep ++ showSep dirSep ns
|
||||
|
||||
deleteBin : String -> List String -> String -> Core ()
|
||||
deleteBin builddir ns mod
|
||||
= do let ttFile = builddir ++ dirSep ++ showSep dirSep ns ++ dirSep ++ mod
|
||||
delete $ ttFile ++ ".ttc"
|
||||
delete $ ttFile ++ ".ttm"
|
||||
|
||||
-- Just load the 'Main' module, if it exists, which will involve building
|
||||
-- it if necessary
|
||||
runRepl : {auto c : Ref Ctxt Defs} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
PkgDesc -> Core ()
|
||||
runRepl pkg
|
||||
= do addDeps pkg
|
||||
processOptions (options pkg)
|
||||
throw (InternalError "Not implemented")
|
||||
|
||||
processPackage : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
PkgCommand -> String -> Core ()
|
||||
processPackage cmd file
|
||||
= do Right (pname, fs) <- coreLift $ parseFile file
|
||||
(do desc <- parsePkgDesc file
|
||||
eoi
|
||||
pure desc)
|
||||
| Left err => throw (ParseFail (getParseErrorLoc file err) err)
|
||||
pkg <- addFields fs (initPkgDesc pname)
|
||||
case cmd of
|
||||
Build => do [] <- build pkg
|
||||
| errs => coreLift (exitWith (ExitFailure 1))
|
||||
pure ()
|
||||
Install => do [] <- build pkg
|
||||
| errs => coreLift (exitWith (ExitFailure 1))
|
||||
install pkg
|
||||
Clean => clean pkg
|
||||
REPL => runRepl pkg
|
||||
|
||||
rejectPackageOpts : List CLOpt -> Core Bool
|
||||
rejectPackageOpts (Package cmd f :: _)
|
||||
= do coreLift $ putStrLn ("Package commands (--build, --install, --clean, --repl) must be the only option given")
|
||||
pure True -- Done, quit here
|
||||
rejectPackageOpts (_ :: xs) = rejectPackageOpts xs
|
||||
rejectPackageOpts [] = pure False
|
||||
|
||||
-- If there's a package option, it must be the only option, so reject if
|
||||
-- it's not
|
||||
export
|
||||
processPackageOpts : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
List CLOpt -> Core Bool
|
||||
processPackageOpts [Package cmd f]
|
||||
= do processPackage cmd f
|
||||
pure True
|
||||
processPackageOpts opts = rejectPackageOpts opts
|
||||
|
||||
-- find an ipkg file in one of the parent directories
|
||||
-- If it exists, read it, set the current directory to the root of the source
|
||||
-- tree, and set the relevant command line options before proceeding
|
||||
export
|
||||
findIpkg : {auto c : Ref Ctxt Defs} ->
|
||||
{auto r : Ref ROpts REPLOpts} ->
|
||||
Maybe String -> Core (Maybe String)
|
||||
findIpkg fname
|
||||
= do Just (dir, ipkgn, up) <- coreLift findIpkgFile
|
||||
| Nothing => pure fname
|
||||
coreLift $ changeDir dir
|
||||
Right (pname, fs) <- coreLift $ parseFile ipkgn
|
||||
(do desc <- parsePkgDesc ipkgn
|
||||
eoi
|
||||
pure desc)
|
||||
| Left err => throw (ParseFail (getParseErrorLoc ipkgn err) err)
|
||||
pkg <- addFields fs (initPkgDesc pname)
|
||||
setSourceDir (sourcedir pkg)
|
||||
processOptions (options pkg)
|
||||
loadDependencies (depends pkg)
|
||||
case fname of
|
||||
Nothing => pure Nothing
|
||||
Just src =>
|
||||
do let src' = showSep dirSep (up ++ [src])
|
||||
setSource src'
|
||||
opts <- get ROpts
|
||||
put ROpts (record { mainfile = Just src' } opts)
|
||||
pure (Just src')
|
||||
where
|
||||
dropHead : String -> List String -> List String
|
||||
dropHead str [] = []
|
||||
dropHead str (x :: xs)
|
||||
= if x == str then xs else x :: xs
|
||||
loadDependencies : List String -> Core ()
|
||||
loadDependencies = traverse_ addPkgDir
|
896
src/Idris/REPL.idr
Normal file
896
src/Idris/REPL.idr
Normal file
@ -0,0 +1,896 @@
|
||||
module Idris.REPL
|
||||
|
||||
import Compiler.Scheme.Chez
|
||||
import Compiler.Scheme.Racket
|
||||
import Compiler.Scheme.Gambit
|
||||
import Compiler.Common
|
||||
|
||||
import Core.AutoSearch
|
||||
import Core.CaseTree
|
||||
import Core.CompileExpr
|
||||
import Core.Context
|
||||
import Core.Env
|
||||
import Core.InitPrimitives
|
||||
import Core.LinearCheck
|
||||
import Core.Metadata
|
||||
import Core.Normalise
|
||||
import Core.Options
|
||||
import Core.Termination
|
||||
import Core.TT
|
||||
import Core.Unify
|
||||
|
||||
import Parser.Unlit
|
||||
|
||||
import Idris.Desugar
|
||||
import Idris.Error
|
||||
import Idris.IDEMode.CaseSplit
|
||||
import Idris.IDEMode.Commands
|
||||
import Idris.IDEMode.MakeClause
|
||||
import Idris.ModTree
|
||||
import Idris.Parser
|
||||
import Idris.Resugar
|
||||
import public Idris.REPLCommon
|
||||
import Idris.Syntax
|
||||
import Idris.Version
|
||||
|
||||
import TTImp.Elab
|
||||
import TTImp.Elab.Check
|
||||
import TTImp.Interactive.CaseSplit
|
||||
import TTImp.Interactive.ExprSearch
|
||||
import TTImp.Interactive.GenerateDef
|
||||
import TTImp.Interactive.MakeLemma
|
||||
import TTImp.TTImp
|
||||
import TTImp.ProcessDecls
|
||||
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Data.NameMap
|
||||
import Data.Stream
|
||||
import Data.Strings
|
||||
|
||||
import System
|
||||
|
||||
%default covering
|
||||
|
||||
showInfo : {auto c : Ref Ctxt Defs} ->
|
||||
(Name, Int, GlobalDef) -> Core ()
|
||||
showInfo (n, idx, d)
|
||||
= do coreLift $ putStrLn (show (fullname d) ++ " ==> " ++
|
||||
show !(toFullNames (definition d)))
|
||||
coreLift $ putStrLn (show (multiplicity d))
|
||||
coreLift $ putStrLn ("Erasable args: " ++ show (eraseArgs d))
|
||||
coreLift $ putStrLn ("Detaggable arg types: " ++ show (safeErase d))
|
||||
coreLift $ putStrLn ("Specialise args: " ++ show (specArgs d))
|
||||
coreLift $ putStrLn ("Inferrable args: " ++ show (inferrable d))
|
||||
case compexpr d of
|
||||
Nothing => pure ()
|
||||
Just expr => coreLift $ putStrLn ("Compiled: " ++ show expr)
|
||||
coreLift $ putStrLn ("Refers to: " ++
|
||||
show !(traverse getFullName (keys (refersTo d))))
|
||||
coreLift $ putStrLn ("Refers to (runtime): " ++
|
||||
show !(traverse getFullName (keys (refersToRuntime d))))
|
||||
when (not (isNil (sizeChange d))) $
|
||||
let scinfo = map (\s => show (fnCall s) ++ ": " ++
|
||||
show (fnArgs s)) !(traverse toFullNames (sizeChange d)) in
|
||||
coreLift $ putStrLn $
|
||||
"Size change: " ++ showSep ", " scinfo
|
||||
|
||||
isHole : GlobalDef -> Maybe Nat
|
||||
isHole def
|
||||
= case definition def of
|
||||
Hole locs _ => Just locs
|
||||
PMDef pi _ _ _ _ =>
|
||||
case holeInfo pi of
|
||||
NotHole => Nothing
|
||||
SolvedHole n => Just n
|
||||
_ => Nothing
|
||||
|
||||
showCount : RigCount -> String
|
||||
showCount = elimSemi
|
||||
" 0 "
|
||||
" 1 "
|
||||
(const " ")
|
||||
|
||||
impBracket : Bool -> String -> String
|
||||
impBracket False str = str
|
||||
impBracket True str = "{" ++ str ++ "}"
|
||||
|
||||
showName : Name -> Bool
|
||||
showName (UN "_") = False
|
||||
showName (MN _ _) = False
|
||||
showName _ = True
|
||||
|
||||
tidy : Name -> String
|
||||
tidy (MN n _) = n
|
||||
tidy n = show n
|
||||
|
||||
showEnv : {vars : _} ->
|
||||
{auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
Defs -> Env Term vars -> Name -> Nat -> Term vars ->
|
||||
Core (List (Name, String), String)
|
||||
showEnv defs env fn (S args) (Bind fc x (Let c val ty) sc)
|
||||
= showEnv defs env fn args (subst val sc)
|
||||
showEnv defs env fn (S args) (Bind fc x b sc)
|
||||
= do ity <- resugar env !(normalise defs env (binderType b))
|
||||
let pre = if showName x
|
||||
then REPL.showCount (multiplicity b) ++
|
||||
impBracket (implicitBind b) (tidy x ++ " : " ++ show ity) ++ "\n"
|
||||
else ""
|
||||
(envstr, ret) <- showEnv defs (b :: env) fn args sc
|
||||
pure ((x, pre) :: envstr, ret)
|
||||
where
|
||||
implicitBind : Binder (Term vars) -> Bool
|
||||
implicitBind (Pi _ Explicit _) = False
|
||||
implicitBind (Pi _ _ _) = True
|
||||
implicitBind (Lam _ Explicit _) = False
|
||||
implicitBind (Lam _ _ _) = True
|
||||
implicitBind _ = False
|
||||
showEnv defs env fn args ty
|
||||
= do ity <- resugar env !(normalise defs env ty)
|
||||
pure ([], "-------------------------------------\n" ++
|
||||
nameRoot fn ++ " : " ++ show ity)
|
||||
|
||||
showHole : {vars : _} ->
|
||||
{auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
Defs -> Env Term vars -> Name -> Nat -> Term vars ->
|
||||
Core String
|
||||
showHole gam env fn args ty
|
||||
= do (envs, ret) <- showEnv gam env fn args ty
|
||||
pp <- getPPrint
|
||||
let envs' = if showImplicits pp
|
||||
then envs
|
||||
else dropShadows envs
|
||||
pure (concat (map snd envs') ++ ret)
|
||||
where
|
||||
dropShadows : List (Name, a) -> List (Name, a)
|
||||
dropShadows [] = []
|
||||
dropShadows ((n, ty) :: ns)
|
||||
= if n `elem` map fst ns
|
||||
then dropShadows ns
|
||||
else (n, ty) :: dropShadows ns
|
||||
|
||||
displayType : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
Defs -> (Name, Int, GlobalDef) ->
|
||||
Core String
|
||||
displayType defs (n, i, gdef)
|
||||
= maybe (do tm <- resugar [] !(normaliseHoles defs [] (type gdef))
|
||||
pure (show (fullname gdef) ++ " : " ++ show tm))
|
||||
(\num => showHole defs [] n num (type gdef))
|
||||
(isHole gdef)
|
||||
|
||||
getEnvTerm : {vars : _} ->
|
||||
List Name -> Env Term vars -> Term vars ->
|
||||
(vars' ** (Env Term vars', Term vars'))
|
||||
getEnvTerm (n :: ns) env (Bind fc x b sc)
|
||||
= if n == x
|
||||
then getEnvTerm ns (b :: env) sc
|
||||
else (_ ** (env, Bind fc x b sc))
|
||||
getEnvTerm _ env tm = (_ ** (env, tm))
|
||||
|
||||
displayTerm : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
Defs -> ClosedTerm ->
|
||||
Core String
|
||||
displayTerm defs tm
|
||||
= do ptm <- resugar [] !(normaliseHoles defs [] tm)
|
||||
pure (show ptm)
|
||||
|
||||
displayPatTerm : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
Defs -> ClosedTerm ->
|
||||
Core String
|
||||
displayPatTerm defs tm
|
||||
= do ptm <- resugarNoPatvars [] !(normaliseHoles defs [] tm)
|
||||
pure (show ptm)
|
||||
|
||||
displayClause : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
Defs -> (vs ** (Env Term vs, Term vs, Term vs)) ->
|
||||
Core String
|
||||
displayClause defs (vs ** (env, lhs, rhs))
|
||||
= do lhstm <- resugar env !(normaliseHoles defs env lhs)
|
||||
rhstm <- resugar env !(normaliseHoles defs env rhs)
|
||||
pure (show lhstm ++ " = " ++ show rhstm)
|
||||
|
||||
displayPats : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
Defs -> (Name, Int, GlobalDef) ->
|
||||
Core String
|
||||
displayPats defs (n, idx, gdef)
|
||||
= case definition gdef of
|
||||
PMDef _ _ _ _ pats
|
||||
=> do ty <- displayType defs (n, idx, gdef)
|
||||
ps <- traverse (displayClause defs) pats
|
||||
pure (ty ++ "\n" ++ showSep "\n" ps)
|
||||
_ => pure (show n ++ " is not a pattern matching definition")
|
||||
|
||||
setOpt : {auto c : Ref Ctxt Defs} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
REPLOpt -> Core ()
|
||||
setOpt (ShowImplicits t)
|
||||
= do pp <- getPPrint
|
||||
setPPrint (record { showImplicits = t } pp)
|
||||
setOpt (ShowNamespace t)
|
||||
= do pp <- getPPrint
|
||||
setPPrint (record { fullNamespace = t } pp)
|
||||
setOpt (ShowTypes t)
|
||||
= do opts <- get ROpts
|
||||
put ROpts (record { showTypes = t } opts)
|
||||
setOpt (EvalMode m)
|
||||
= do opts <- get ROpts
|
||||
put ROpts (record { evalMode = m } opts)
|
||||
setOpt (Editor e)
|
||||
= do opts <- get ROpts
|
||||
put ROpts (record { editor = e } opts)
|
||||
setOpt (CG e)
|
||||
= case getCG e of
|
||||
Just cg => setCG cg
|
||||
Nothing => iputStrLn "No such code generator available"
|
||||
|
||||
getOptions : {auto c : Ref Ctxt Defs} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
Core (List REPLOpt)
|
||||
getOptions = do
|
||||
pp <- getPPrint
|
||||
opts <- get ROpts
|
||||
pure $ [ ShowImplicits (showImplicits pp), ShowNamespace (fullNamespace pp)
|
||||
, ShowTypes (showTypes opts), EvalMode (evalMode opts)
|
||||
, Editor (editor opts)
|
||||
]
|
||||
|
||||
export
|
||||
findCG : {auto c : Ref Ctxt Defs} -> Core Codegen
|
||||
findCG
|
||||
= do defs <- get Ctxt
|
||||
case codegen (session (options defs)) of
|
||||
Chez => pure codegenChez
|
||||
Racket => pure codegenRacket
|
||||
Gambit => pure codegenGambit
|
||||
|
||||
anyAt : (FC -> Bool) -> FC -> a -> Bool
|
||||
anyAt p loc y = p loc
|
||||
|
||||
printClause : {auto c : Ref Ctxt Defs} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
Maybe String -> Nat -> ImpClause ->
|
||||
Core String
|
||||
printClause l i (PatClause _ lhsraw rhsraw)
|
||||
= do lhs <- pterm lhsraw
|
||||
rhs <- pterm rhsraw
|
||||
pure (relit l (pack (replicate i ' ') ++ show lhs ++ " = " ++ show rhs))
|
||||
printClause l i (WithClause _ lhsraw wvraw csraw)
|
||||
= do lhs <- pterm lhsraw
|
||||
wval <- pterm wvraw
|
||||
cs <- traverse (printClause l (i + 2)) csraw
|
||||
pure ((relit l ((pack (replicate i ' ') ++ show lhs ++ " with (" ++ show wval ++ ")\n")) ++
|
||||
showSep "\n" cs))
|
||||
printClause l i (ImpossibleClause _ lhsraw)
|
||||
= do lhs <- pterm lhsraw
|
||||
pure (relit l (pack (replicate i ' ') ++ show lhs ++ " impossible"))
|
||||
|
||||
|
||||
lookupDefTyName : Name -> Context ->
|
||||
Core (List (Name, Int, (Def, ClosedTerm)))
|
||||
lookupDefTyName = lookupNameBy (\g => (definition g, type g))
|
||||
|
||||
public export
|
||||
data EditResult : Type where
|
||||
DisplayEdit : List String -> EditResult
|
||||
EditError : String -> EditResult
|
||||
MadeLemma : Maybe String -> Name -> PTerm -> String -> EditResult
|
||||
|
||||
updateFile : {auto r : Ref ROpts REPLOpts} ->
|
||||
(List String -> List String) -> Core EditResult
|
||||
updateFile update
|
||||
= do opts <- get ROpts
|
||||
let Just f = mainfile opts
|
||||
| Nothing => pure (DisplayEdit []) -- no file, nothing to do
|
||||
Right content <- coreLift $ readFile f
|
||||
| Left err => throw (FileErr f err)
|
||||
coreLift $ writeFile (f ++ "~") content
|
||||
coreLift $ writeFile f (unlines (update (lines content)))
|
||||
pure (DisplayEdit [])
|
||||
|
||||
rtrim : String -> String
|
||||
rtrim str = reverse (ltrim (reverse str))
|
||||
|
||||
addClause : String -> Nat -> List String -> List String
|
||||
addClause c Z xs = rtrim c :: xs
|
||||
addClause c (S k) (x :: xs) = x :: addClause c k xs
|
||||
addClause c (S k) [] = [c]
|
||||
|
||||
caseSplit : String -> Nat -> List String -> List String
|
||||
caseSplit c Z (x :: xs) = rtrim c :: xs
|
||||
caseSplit c (S k) (x :: xs) = x :: caseSplit c k xs
|
||||
caseSplit c _ [] = [c]
|
||||
|
||||
proofSearch : Name -> String -> Nat -> List String -> List String
|
||||
proofSearch n res Z (x :: xs) = replaceStr ("?" ++ show n) res x :: xs
|
||||
where
|
||||
replaceStr : String -> String -> String -> String
|
||||
replaceStr rep new "" = ""
|
||||
replaceStr rep new str
|
||||
= if isPrefixOf rep str
|
||||
then new ++ pack (drop (length rep) (unpack str))
|
||||
else assert_total $ strCons (prim__strHead str)
|
||||
(replaceStr rep new (prim__strTail str))
|
||||
proofSearch n res (S k) (x :: xs) = x :: proofSearch n res k xs
|
||||
proofSearch n res _ [] = []
|
||||
|
||||
addMadeLemma : Maybe String -> Name -> String -> String -> Nat -> List String -> List String
|
||||
addMadeLemma lit n ty app line content
|
||||
= addApp lit line [] (proofSearch n app line content)
|
||||
where
|
||||
-- Put n : ty in the first blank line
|
||||
insertInBlank : Maybe String -> List String -> List String
|
||||
insertInBlank lit [] = [relit lit $ show n ++ " : " ++ ty ++ "\n"]
|
||||
insertInBlank lit (x :: xs)
|
||||
= if trim x == ""
|
||||
then ("\n" ++ (relit lit $ show n ++ " : " ++ ty ++ "\n")) :: xs
|
||||
else x :: insertInBlank lit xs
|
||||
|
||||
addApp : Maybe String -> Nat -> List String -> List String -> List String
|
||||
addApp lit Z acc rest = reverse (insertInBlank lit acc) ++ rest
|
||||
addApp lit (S k) acc (x :: xs) = addApp lit k (x :: acc) xs
|
||||
addApp _ (S k) acc [] = reverse acc
|
||||
|
||||
processEdit : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
EditCmd -> Core EditResult
|
||||
processEdit (TypeAt line col name)
|
||||
= do defs <- get Ctxt
|
||||
glob <- lookupCtxtName name (gamma defs)
|
||||
res <- the (Core String) $ case glob of
|
||||
[] => pure ""
|
||||
ts => do tys <- traverse (displayType defs) ts
|
||||
pure (showSep "\n" tys)
|
||||
Just (n, num, t) <- findTypeAt (\p, n => within (line-1, col-1) p)
|
||||
| Nothing => if res == ""
|
||||
then throw (UndefinedName (MkFC "(interactive)" (0,0) (0,0)) name)
|
||||
else pure (DisplayEdit [res])
|
||||
if res == ""
|
||||
then pure (DisplayEdit [ nameRoot n ++ " : " ++
|
||||
!(displayTerm defs t)])
|
||||
else pure (DisplayEdit []) -- ? Why () This means there is a global name and a type at (line,col)
|
||||
processEdit (CaseSplit upd line col name)
|
||||
= do let find = if col > 0
|
||||
then within (line-1, col-1)
|
||||
else onLine (line-1)
|
||||
OK splits <- getSplits (anyAt find) name
|
||||
| SplitFail err => pure (EditError (show err))
|
||||
lines <- updateCase splits (line-1) (col-1)
|
||||
if upd
|
||||
then updateFile (caseSplit (unlines lines) (integerToNat (cast (line - 1))))
|
||||
else pure $ DisplayEdit lines
|
||||
processEdit (AddClause upd line name)
|
||||
= do Just c <- getClause line name
|
||||
| Nothing => pure (EditError (show name ++ " not defined here"))
|
||||
if upd
|
||||
then updateFile (addClause c (integerToNat (cast line)))
|
||||
else pure $ DisplayEdit [c]
|
||||
processEdit (ExprSearch upd line name hints all)
|
||||
= do defs <- get Ctxt
|
||||
syn <- get Syn
|
||||
let brack = elemBy (\x, y => dropNS x == dropNS y) name (bracketholes syn)
|
||||
case !(lookupDefName name (gamma defs)) of
|
||||
[(n, nidx, Hole locs _)] =>
|
||||
do tms <- exprSearch replFC name []
|
||||
defs <- get Ctxt
|
||||
restms <- traverse (normaliseHoles defs []) tms
|
||||
itms <- the (Core (List PTerm))
|
||||
(traverse (\tm =>
|
||||
do let (_ ** (env, tm')) = dropLams locs [] tm
|
||||
resugar env tm') restms)
|
||||
if all
|
||||
then pure $ DisplayEdit (map show itms)
|
||||
else case itms of
|
||||
[] => pure $ EditError "No search results"
|
||||
(x :: xs) =>
|
||||
let res = show (the PTerm (if brack
|
||||
then addBracket replFC x
|
||||
else x)) in
|
||||
if upd
|
||||
then updateFile (proofSearch name res (integerToNat (cast (line - 1))))
|
||||
else pure $ DisplayEdit [res]
|
||||
[(n, nidx, PMDef pi [] (STerm tm) _ _)] =>
|
||||
case holeInfo pi of
|
||||
NotHole => pure $ EditError "Not a searchable hole"
|
||||
SolvedHole locs =>
|
||||
do let (_ ** (env, tm')) = dropLams locs [] tm
|
||||
itm <- resugar env tm'
|
||||
let res = show (the PTerm (if brack
|
||||
then addBracket replFC itm
|
||||
else itm))
|
||||
if upd
|
||||
then updateFile (proofSearch name res (integerToNat (cast (line - 1))))
|
||||
else pure $ DisplayEdit [res]
|
||||
[] => pure $ EditError $ "Unknown name " ++ show name
|
||||
_ => pure $ EditError "Not a searchable hole"
|
||||
where
|
||||
dropLams : {vars : _} ->
|
||||
Nat -> Env Term vars -> Term vars ->
|
||||
(vars' ** (Env Term vars', Term vars'))
|
||||
dropLams Z env tm = (_ ** (env, tm))
|
||||
dropLams (S k) env (Bind _ _ b sc) = dropLams k (b :: env) sc
|
||||
dropLams _ env tm = (_ ** (env, tm))
|
||||
processEdit (GenerateDef upd line name)
|
||||
= do defs <- get Ctxt
|
||||
Just (_, n', _, _) <- findTyDeclAt (\p, n => onLine line p)
|
||||
| Nothing => pure (EditError ("Can't find declaration for " ++ show name ++ " on line " ++ show line))
|
||||
case !(lookupDefExact n' (gamma defs)) of
|
||||
Just None =>
|
||||
catch
|
||||
(do Just (fc, cs) <- makeDef (\p, n => onLine line p) n'
|
||||
| Nothing => processEdit (AddClause upd line name)
|
||||
Just srcLine <- getSourceLine line
|
||||
| Nothing => pure (EditError "Source line not found")
|
||||
let (markM, srcLineUnlit) = isLitLine srcLine
|
||||
let l : Nat = integerToNat (cast (snd (startPos fc)))
|
||||
ls <- traverse (printClause markM l) cs
|
||||
if upd
|
||||
then updateFile (addClause (unlines ls) (integerToNat (cast line)))
|
||||
else pure $ DisplayEdit ls)
|
||||
(\err => pure $ EditError $ "Can't find a definition for " ++ show n' ++ ": " ++ show err)
|
||||
Just _ => pure $ EditError "Already defined"
|
||||
Nothing => pure $ EditError $ "Can't find declaration for " ++ show name
|
||||
processEdit (MakeLemma upd line name)
|
||||
= do defs <- get Ctxt
|
||||
syn <- get Syn
|
||||
let brack = elemBy (\x, y => dropNS x == dropNS y) name (bracketholes syn)
|
||||
case !(lookupDefTyName name (gamma defs)) of
|
||||
[(n, nidx, Hole locs _, ty)] =>
|
||||
do (lty, lapp) <- makeLemma replFC name locs ty
|
||||
pty <- pterm lty
|
||||
papp <- pterm lapp
|
||||
opts <- get ROpts
|
||||
let pappstr = show (the PTerm (if brack
|
||||
then addBracket replFC papp
|
||||
else papp))
|
||||
Just srcLine <- getSourceLine line
|
||||
| Nothing => pure (EditError "Source line not found")
|
||||
let (markM,_) = isLitLine srcLine
|
||||
let markML : Nat = length (fromMaybe "" markM)
|
||||
if upd
|
||||
then updateFile (addMadeLemma markM name (show pty) pappstr
|
||||
(max 0 (integerToNat (cast (line - 1)))))
|
||||
else pure $ MadeLemma markM name pty pappstr
|
||||
_ => pure $ EditError "Can't make lifted definition"
|
||||
processEdit (MakeCase upd line name)
|
||||
= pure $ EditError "Not implemented yet"
|
||||
processEdit (MakeWith upd line name)
|
||||
= do Just l <- getSourceLine line
|
||||
| Nothing => pure (EditError "Source line not available")
|
||||
pure $ DisplayEdit [makeWith name l]
|
||||
|
||||
public export
|
||||
data MissedResult : Type where
|
||||
CasesMissing : Name -> List String -> MissedResult
|
||||
CallsNonCovering : Name -> List Name -> MissedResult
|
||||
AllCasesCovered : Name -> MissedResult
|
||||
|
||||
public export
|
||||
data REPLResult : Type where
|
||||
Done : REPLResult
|
||||
REPLError : String -> REPLResult
|
||||
Executed : PTerm -> REPLResult
|
||||
RequestedHelp : REPLResult
|
||||
Evaluated : PTerm -> (Maybe PTerm) -> REPLResult
|
||||
Printed : List String -> REPLResult
|
||||
TermChecked : PTerm -> PTerm -> REPLResult
|
||||
FileLoaded : String -> REPLResult
|
||||
ErrorLoadingFile : String -> FileError -> REPLResult
|
||||
ErrorsBuildingFile : String -> List Error -> REPLResult
|
||||
NoFileLoaded : REPLResult
|
||||
CurrentDirectory : String -> REPLResult
|
||||
CompilationFailed: REPLResult
|
||||
Compiled : String -> REPLResult
|
||||
ProofFound : PTerm -> REPLResult
|
||||
Missed : List MissedResult -> REPLResult
|
||||
CheckedTotal : List (Name, Totality) -> REPLResult
|
||||
FoundHoles : List Name -> REPLResult
|
||||
OptionsSet : List REPLOpt -> REPLResult
|
||||
LogLevelSet : Nat -> REPLResult
|
||||
VersionIs : Version -> REPLResult
|
||||
Exited : REPLResult
|
||||
Edited : EditResult -> REPLResult
|
||||
|
||||
export
|
||||
execExp : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
PTerm -> Core REPLResult
|
||||
execExp ctm
|
||||
= do ttimp <- desugar AnyExpr [] (PApp replFC (PRef replFC (UN "unsafePerformIO")) ctm)
|
||||
inidx <- resolveName (UN "[input]")
|
||||
(tm, ty) <- elabTerm inidx InExpr [] (MkNested [])
|
||||
[] ttimp Nothing
|
||||
tm_erased <- linearCheck replFC linear True [] tm
|
||||
execute !findCG tm_erased
|
||||
pure $ Executed ctm
|
||||
|
||||
|
||||
export
|
||||
compileExp : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
PTerm -> String -> Core REPLResult
|
||||
compileExp ctm outfile
|
||||
= do inidx <- resolveName (UN "[input]")
|
||||
ttimp <- desugar AnyExpr [] (PApp replFC (PRef replFC (UN "unsafePerformIO")) ctm)
|
||||
(tm, gty) <- elabTerm inidx InExpr [] (MkNested [])
|
||||
[] ttimp Nothing
|
||||
tm_erased <- linearCheck replFC linear True [] tm
|
||||
ok <- compile !findCG tm_erased outfile
|
||||
maybe (pure CompilationFailed)
|
||||
(pure . Compiled)
|
||||
ok
|
||||
|
||||
export
|
||||
loadMainFile : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
String -> Core REPLResult
|
||||
loadMainFile f
|
||||
= do resetContext
|
||||
Right res <- coreLift (readFile f)
|
||||
| Left err => do setSource ""
|
||||
pure (ErrorLoadingFile f err)
|
||||
errs <- logTime "Build deps" $ buildDeps f
|
||||
updateErrorLine errs
|
||||
setSource res
|
||||
case errs of
|
||||
[] => pure (FileLoaded f)
|
||||
_ => pure (ErrorsBuildingFile f errs)
|
||||
|
||||
|
||||
||| Process a single `REPLCmd`
|
||||
|||
|
||||
||| Returns `REPLResult` for display by the higher level shell which
|
||||
||| is invoking this interactive command processing.
|
||||
export
|
||||
process : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
REPLCmd -> Core REPLResult
|
||||
process (Eval itm)
|
||||
= do opts <- get ROpts
|
||||
case evalMode opts of
|
||||
Execute => do execExp itm; pure (Executed itm)
|
||||
_ =>
|
||||
do ttimp <- desugar AnyExpr [] itm
|
||||
inidx <- resolveName (UN "[input]")
|
||||
-- a TMP HACK to prioritise list syntax for List: hide
|
||||
-- foreign argument lists. TODO: once the new FFI is fully
|
||||
-- up and running we won't need this. Also, if we add
|
||||
-- 'with' disambiguation we can use that instead.
|
||||
catch (do hide replFC (NS ["PrimIO"] (UN "::"))
|
||||
hide replFC (NS ["PrimIO"] (UN "Nil")))
|
||||
(\err => pure ())
|
||||
(tm, gty) <- elabTerm inidx (emode (evalMode opts)) [] (MkNested [])
|
||||
[] ttimp Nothing
|
||||
defs <- get Ctxt
|
||||
opts <- get ROpts
|
||||
let norm = nfun (evalMode opts)
|
||||
ntm <- norm defs [] tm
|
||||
itm <- resugar [] ntm
|
||||
logTermNF 5 "Normalised" [] ntm
|
||||
if showTypes opts
|
||||
then do ty <- getTerm gty
|
||||
ity <- resugar [] !(norm defs [] ty)
|
||||
pure (Evaluated itm (Just ity))
|
||||
else pure (Evaluated itm Nothing)
|
||||
where
|
||||
emode : REPLEval -> ElabMode
|
||||
emode EvalTC = InType
|
||||
emode _ = InExpr
|
||||
|
||||
nfun : {vs : _} ->
|
||||
REPLEval -> Defs -> Env Term vs -> Term vs -> Core (Term vs)
|
||||
nfun NormaliseAll = normaliseAll
|
||||
nfun _ = normalise
|
||||
process (Check (PRef fc fn))
|
||||
= do defs <- get Ctxt
|
||||
case !(lookupCtxtName fn (gamma defs)) of
|
||||
[] => throw (UndefinedName fc fn)
|
||||
ts => do tys <- traverse (displayType defs) ts
|
||||
pure (Printed tys)
|
||||
process (Check itm)
|
||||
= do inidx <- resolveName (UN "[input]")
|
||||
ttimp <- desugar AnyExpr [] itm
|
||||
(tm, gty) <- elabTerm inidx InExpr [] (MkNested [])
|
||||
[] ttimp Nothing
|
||||
defs <- get Ctxt
|
||||
itm <- resugar [] !(normaliseHoles defs [] tm)
|
||||
ty <- getTerm gty
|
||||
ity <- resugar [] !(normaliseScope defs [] ty)
|
||||
pure (TermChecked itm ity)
|
||||
process (PrintDef fn)
|
||||
= do defs <- get Ctxt
|
||||
case !(lookupCtxtName fn (gamma defs)) of
|
||||
[] => throw (UndefinedName replFC fn)
|
||||
ts => do defs <- traverse (displayPats defs) ts
|
||||
pure (Printed defs)
|
||||
process Reload
|
||||
= do opts <- get ROpts
|
||||
case mainfile opts of
|
||||
Nothing => pure NoFileLoaded
|
||||
Just f => loadMainFile f
|
||||
process (Load f)
|
||||
= do opts <- get ROpts
|
||||
put ROpts (record { mainfile = Just f } opts)
|
||||
-- Clear the context and load again
|
||||
loadMainFile f
|
||||
process (CD dir)
|
||||
= do setWorkingDir dir
|
||||
workDir <- getWorkingDir
|
||||
pure (CurrentDirectory workDir)
|
||||
process CWD
|
||||
= do workDir <- getWorkingDir
|
||||
pure (CurrentDirectory workDir)
|
||||
process Edit
|
||||
= do opts <- get ROpts
|
||||
case mainfile opts of
|
||||
Nothing => pure NoFileLoaded
|
||||
Just f =>
|
||||
do let line = maybe "" (\i => " +" ++ show (i + 1)) (errorLine opts)
|
||||
coreLift $ system (editor opts ++ " " ++ f ++ line)
|
||||
loadMainFile f
|
||||
process (Compile ctm outfile)
|
||||
= compileExp ctm outfile
|
||||
process (Exec ctm)
|
||||
= execExp ctm
|
||||
process Help
|
||||
= pure RequestedHelp
|
||||
process (ProofSearch n_in)
|
||||
= do defs <- get Ctxt
|
||||
[(n, i, ty)] <- lookupTyName n_in (gamma defs)
|
||||
| [] => throw (UndefinedName replFC n_in)
|
||||
| ns => throw (AmbiguousName replFC (map fst ns))
|
||||
tm <- search replFC top False 1000 n ty []
|
||||
itm <- resugar [] !(normaliseHoles defs [] tm)
|
||||
pure $ ProofFound itm
|
||||
process (Missing n)
|
||||
= do defs <- get Ctxt
|
||||
case !(lookupCtxtName n (gamma defs)) of
|
||||
[] => throw (UndefinedName replFC n)
|
||||
ts => map Missed $ traverse (\fn =>
|
||||
do tot <- getTotality replFC fn
|
||||
the (Core MissedResult) $ case isCovering tot of
|
||||
MissingCases cs =>
|
||||
do tms <- traverse (displayPatTerm defs) cs
|
||||
pure $ CasesMissing fn tms
|
||||
NonCoveringCall ns_in =>
|
||||
do ns <- traverse getFullName ns_in
|
||||
pure $ CallsNonCovering fn ns
|
||||
_ => pure $ AllCasesCovered fn)
|
||||
(map fst ts)
|
||||
process (Total n)
|
||||
= do defs <- get Ctxt
|
||||
case !(lookupCtxtName n (gamma defs)) of
|
||||
[] => throw (UndefinedName replFC n)
|
||||
ts => map CheckedTotal $
|
||||
traverse (\fn =>
|
||||
do checkTotal replFC fn
|
||||
tot <- getTotality replFC fn >>= toFullNames
|
||||
pure $ (fn, tot))
|
||||
(map fst ts)
|
||||
process (DebugInfo n)
|
||||
= do defs <- get Ctxt
|
||||
traverse_ showInfo !(lookupCtxtName n (gamma defs))
|
||||
pure Done
|
||||
process (SetOpt opt)
|
||||
= do setOpt opt
|
||||
pure Done
|
||||
process GetOpts
|
||||
= do opts <- getOptions
|
||||
pure $ OptionsSet opts
|
||||
process (SetLog lvl)
|
||||
= do setLogLevel lvl
|
||||
pure $ LogLevelSet lvl
|
||||
process Metavars
|
||||
= do ms <- getUserHoles
|
||||
pure $ FoundHoles ms
|
||||
process (Editing cmd)
|
||||
= do ppopts <- getPPrint
|
||||
-- Since we're working in a local environment, don't do the usual
|
||||
-- thing of printing out the full environment for parameterised
|
||||
-- calls or calls in where blocks
|
||||
setPPrint (record { showFullEnv = False } ppopts)
|
||||
res <- processEdit cmd
|
||||
setPPrint ppopts
|
||||
pure $ Edited res
|
||||
process Quit
|
||||
= pure Exited
|
||||
process NOP
|
||||
= pure Done
|
||||
process ShowVersion
|
||||
= pure $ VersionIs version
|
||||
|
||||
processCatch : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
REPLCmd -> Core REPLResult
|
||||
processCatch cmd
|
||||
= do c' <- branch
|
||||
u' <- get UST
|
||||
s' <- get Syn
|
||||
o' <- get ROpts
|
||||
catch (do ust <- get UST
|
||||
r <- process cmd
|
||||
commit
|
||||
pure r)
|
||||
(\err => do put Ctxt c'
|
||||
put UST u'
|
||||
put Syn s'
|
||||
put ROpts o'
|
||||
pure $ REPLError !(display err)
|
||||
)
|
||||
|
||||
parseEmptyCmd : EmptyRule (Maybe REPLCmd)
|
||||
parseEmptyCmd = eoi *> (pure Nothing)
|
||||
|
||||
parseCmd : EmptyRule (Maybe REPLCmd)
|
||||
parseCmd = do c <- command; eoi; pure $ Just c
|
||||
|
||||
export
|
||||
parseRepl : String -> Either ParseError (Maybe REPLCmd)
|
||||
parseRepl inp
|
||||
= case fnameCmd [(":load ", Load), (":l ", Load), (":cd ", CD)] inp of
|
||||
Nothing => runParser Nothing inp (parseEmptyCmd <|> parseCmd)
|
||||
Just cmd => Right $ Just cmd
|
||||
where
|
||||
-- a right load of hackery - we can't tokenise the filename using the
|
||||
-- ordinary parser. There's probably a better way...
|
||||
getLoad : Nat -> (String -> REPLCmd) -> String -> Maybe REPLCmd
|
||||
getLoad n cmd str = Just (cmd (trim (substr n (length str) str)))
|
||||
|
||||
fnameCmd : List (String, String -> REPLCmd) -> String -> Maybe REPLCmd
|
||||
fnameCmd [] inp = Nothing
|
||||
fnameCmd ((pre, cmd) :: rest) inp
|
||||
= if isPrefixOf pre inp
|
||||
then getLoad (length pre) cmd inp
|
||||
else fnameCmd rest inp
|
||||
|
||||
export
|
||||
interpret : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
String -> Core REPLResult
|
||||
interpret inp
|
||||
= case parseRepl inp of
|
||||
Left err => pure $ REPLError (show err)
|
||||
Right Nothing => pure Done
|
||||
Right (Just cmd) => processCatch cmd
|
||||
|
||||
mutual
|
||||
export
|
||||
replCmd : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
String -> Core ()
|
||||
replCmd "" = pure ()
|
||||
replCmd cmd
|
||||
= do res <- interpret cmd
|
||||
displayResult res
|
||||
|
||||
export
|
||||
repl : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
Core ()
|
||||
repl
|
||||
= do ns <- getNS
|
||||
opts <- get ROpts
|
||||
coreLift (putStr (prompt (evalMode opts) ++ showSep "." (reverse ns) ++ "> "))
|
||||
inp <- coreLift getLine
|
||||
end <- coreLift $ fEOF stdin
|
||||
if end
|
||||
then do
|
||||
-- start a new line in REPL mode (not relevant in IDE mode)
|
||||
coreLift $ putStrLn ""
|
||||
iputStrLn "Bye for now!"
|
||||
else do res <- interpret inp
|
||||
handleResult res
|
||||
|
||||
where
|
||||
prompt : REPLEval -> String
|
||||
prompt EvalTC = "[tc] "
|
||||
prompt NormaliseAll = ""
|
||||
prompt Execute = "[exec] "
|
||||
|
||||
handleMissing : MissedResult -> String
|
||||
handleMissing (CasesMissing x xs) = show x ++ ":\n" ++ showSep "\n" xs
|
||||
handleMissing (CallsNonCovering fn ns) = (show fn ++ ": Calls non covering function"
|
||||
++ (case ns of
|
||||
[f] => " " ++ show f
|
||||
_ => "s: " ++ showSep ", " (map show ns)))
|
||||
handleMissing (AllCasesCovered fn) = show fn ++ ": All cases covered"
|
||||
|
||||
export
|
||||
handleResult : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} -> REPLResult -> Core ()
|
||||
handleResult Exited = iputStrLn "Bye for now!"
|
||||
handleResult other = do { displayResult other ; repl }
|
||||
|
||||
export
|
||||
displayResult : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} -> REPLResult -> Core ()
|
||||
displayResult (REPLError err) = printError err
|
||||
displayResult (Evaluated x Nothing) = printResult $ show x
|
||||
displayResult (Evaluated x (Just y)) = printResult $ show x ++ " : " ++ show y
|
||||
displayResult (Printed xs) = printResult (showSep "\n" xs)
|
||||
displayResult (TermChecked x y) = printResult $ show x ++ " : " ++ show y
|
||||
displayResult (FileLoaded x) = printResult $ "Loaded file " ++ x
|
||||
displayResult (ErrorLoadingFile x err) = printError $ "Error loading file " ++ x ++ ": " ++ show err
|
||||
displayResult (ErrorsBuildingFile x errs) = printError $ "Error(s) building file " ++ x -- messages already displayed while building
|
||||
displayResult NoFileLoaded = printError "No file can be reloaded"
|
||||
displayResult (CurrentDirectory dir) = printResult ("Current working directory is '" ++ dir ++ "'")
|
||||
displayResult CompilationFailed = printError "Compilation failed"
|
||||
displayResult (Compiled f) = printResult $ "File " ++ f ++ " written"
|
||||
displayResult (ProofFound x) = printResult $ show x
|
||||
displayResult (Missed cases) = printResult $ showSep "\n" $ map handleMissing cases
|
||||
displayResult (CheckedTotal xs) = printResult $ showSep "\n" $ map (\ (fn, tot) => (show fn ++ " is " ++ show tot)) xs
|
||||
displayResult (FoundHoles []) = printResult $ "No holes"
|
||||
displayResult (FoundHoles [x]) = printResult $ "1 hole: " ++ show x
|
||||
displayResult (FoundHoles xs) = printResult $ show (length xs) ++ " holes: " ++
|
||||
showSep ", " (map show xs)
|
||||
displayResult (LogLevelSet k) = printResult $ "Set loglevel to " ++ show k
|
||||
displayResult (VersionIs x) = printResult $ showVersion True x
|
||||
displayResult (RequestedHelp) = printResult displayHelp
|
||||
displayResult (Edited (DisplayEdit [])) = pure ()
|
||||
displayResult (Edited (DisplayEdit xs)) = printResult $ showSep "\n" xs
|
||||
displayResult (Edited (EditError x)) = printError x
|
||||
displayResult (Edited (MadeLemma lit name pty pappstr)) = printResult (relit lit (show name ++ " : " ++ show pty ++ "\n") ++ pappstr)
|
||||
displayResult (OptionsSet opts) = printResult $ showSep "\n" $ map show opts
|
||||
displayResult _ = pure ()
|
||||
|
||||
displayHelp : String
|
||||
displayHelp =
|
||||
showSep "\n" $ map cmdInfo help
|
||||
where
|
||||
makeSpace : Nat -> String
|
||||
makeSpace n = pack $ take n (repeat ' ')
|
||||
|
||||
col : Nat -> Nat -> String -> String -> String -> String
|
||||
col c1 c2 l m r =
|
||||
l ++ (makeSpace $ c1 `minus` length l) ++
|
||||
m ++ (makeSpace $ c2 `minus` length m) ++ r
|
||||
|
||||
cmdInfo : (List String, CmdArg, String) -> String
|
||||
cmdInfo (cmds, args, text) = " " ++ col 16 12 (showSep " " cmds) (show args) text
|
||||
|
||||
export
|
||||
displayErrors : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} -> REPLResult -> Core ()
|
||||
displayErrors (ErrorLoadingFile x err) = printError $ "File error in " ++ x ++ ": " ++ show err
|
||||
displayErrors _ = pure ()
|
139
src/Idris/SetOptions.idr
Normal file
139
src/Idris/SetOptions.idr
Normal file
@ -0,0 +1,139 @@
|
||||
module Idris.SetOptions
|
||||
|
||||
import Core.Context
|
||||
import Core.Directory
|
||||
import Core.Metadata
|
||||
import Core.Options
|
||||
import Core.Unify
|
||||
|
||||
import Idris.CommandLine
|
||||
import Idris.REPL
|
||||
import Idris.Syntax
|
||||
import Idris.Version
|
||||
|
||||
import IdrisPaths
|
||||
|
||||
import System
|
||||
|
||||
-- TODO: Version numbers on dependencies
|
||||
export
|
||||
addPkgDir : {auto c : Ref Ctxt Defs} ->
|
||||
String -> Core ()
|
||||
addPkgDir p
|
||||
= do defs <- get Ctxt
|
||||
addExtraDir (dir_prefix (dirs (options defs)) ++ dirSep ++
|
||||
"idris2-" ++ showVersion False version ++ dirSep ++ p)
|
||||
|
||||
dirOption : Dirs -> DirCommand -> Core ()
|
||||
dirOption dirs LibDir
|
||||
= coreLift $ putStrLn
|
||||
(dir_prefix dirs ++ dirSep ++ "idris2-" ++ showVersion False version ++ dirSep)
|
||||
|
||||
-- Options to be processed before type checking. Return whether to continue.
|
||||
export
|
||||
preOptions : {auto c : Ref Ctxt Defs} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
List CLOpt -> Core Bool
|
||||
preOptions [] = pure True
|
||||
preOptions (NoBanner :: opts)
|
||||
= do setSession (record { nobanner = True } !getSession)
|
||||
preOptions opts
|
||||
-- These things are processed later, but imply nobanner too
|
||||
preOptions (OutputFile _ :: opts)
|
||||
= do setSession (record { nobanner = True } !getSession)
|
||||
preOptions opts
|
||||
preOptions (ExecFn _ :: opts)
|
||||
= do setSession (record { nobanner = True } !getSession)
|
||||
preOptions opts
|
||||
preOptions (IdeMode :: opts)
|
||||
= do setSession (record { nobanner = True } !getSession)
|
||||
preOptions opts
|
||||
preOptions (CheckOnly :: opts)
|
||||
= do setSession (record { nobanner = True } !getSession)
|
||||
preOptions opts
|
||||
preOptions (Quiet :: opts)
|
||||
= do setOutput (REPL True)
|
||||
preOptions opts
|
||||
preOptions (NoPrelude :: opts)
|
||||
= do setSession (record { noprelude = True } !getSession)
|
||||
preOptions opts
|
||||
preOptions (SetCG e :: opts)
|
||||
= case getCG e of
|
||||
Just cg => do setCG cg
|
||||
preOptions opts
|
||||
Nothing =>
|
||||
do coreLift $ putStrLn "No such code generator"
|
||||
coreLift $ putStrLn $ "Code generators available: " ++
|
||||
showSep ", " (map fst availableCGs)
|
||||
coreLift $ exitWith (ExitFailure 1)
|
||||
preOptions (PkgPath p :: opts)
|
||||
= do addPkgDir p
|
||||
preOptions opts
|
||||
preOptions (Directory d :: opts)
|
||||
= do defs <- get Ctxt
|
||||
dirOption (dirs (options defs)) d
|
||||
pure False
|
||||
preOptions (Timing :: opts)
|
||||
= do setLogTimings True
|
||||
preOptions opts
|
||||
preOptions (DebugElabCheck :: opts)
|
||||
= do setDebugElabCheck True
|
||||
preOptions opts
|
||||
preOptions (RunREPL _ :: opts)
|
||||
= do setOutput (REPL True)
|
||||
setSession (record { nobanner = True } !getSession)
|
||||
preOptions opts
|
||||
preOptions (FindIPKG :: opts)
|
||||
= do setSession (record { findipkg = True } !getSession)
|
||||
preOptions opts
|
||||
preOptions (DumpCases f :: opts)
|
||||
= do setSession (record { dumpcases = Just f } !getSession)
|
||||
preOptions opts
|
||||
preOptions (DumpLifted f :: opts)
|
||||
= do setSession (record { dumplifted = Just f } !getSession)
|
||||
preOptions opts
|
||||
preOptions (DumpANF f :: opts)
|
||||
= do setSession (record { dumpanf = Just f } !getSession)
|
||||
preOptions opts
|
||||
preOptions (DumpVMCode f :: opts)
|
||||
= do setSession (record { dumpvmcode = Just f } !getSession)
|
||||
preOptions opts
|
||||
preOptions (_ :: opts) = preOptions opts
|
||||
|
||||
-- Options to be processed after type checking. Returns whether execution
|
||||
-- should continue (i.e., whether to start a REPL)
|
||||
export
|
||||
postOptions : {auto c : Ref Ctxt Defs} ->
|
||||
{auto u : Ref UST UState} ->
|
||||
{auto s : Ref Syn SyntaxInfo} ->
|
||||
{auto m : Ref MD Metadata} ->
|
||||
{auto o : Ref ROpts REPLOpts} ->
|
||||
List CLOpt -> Core Bool
|
||||
postOptions [] = pure True
|
||||
postOptions (OutputFile outfile :: rest)
|
||||
= do compileExp (PRef (MkFC "(script)" (0, 0) (0, 0)) (UN "main")) outfile
|
||||
postOptions rest
|
||||
pure False
|
||||
postOptions (ExecFn str :: rest)
|
||||
= do execExp (PRef (MkFC "(script)" (0, 0) (0, 0)) (UN str))
|
||||
postOptions rest
|
||||
pure False
|
||||
postOptions (CheckOnly :: rest)
|
||||
= do postOptions rest
|
||||
pure False
|
||||
postOptions (RunREPL str :: rest)
|
||||
= do replCmd str
|
||||
pure False
|
||||
postOptions (_ :: rest) = postOptions rest
|
||||
|
||||
export
|
||||
ideMode : List CLOpt -> Bool
|
||||
ideMode [] = False
|
||||
ideMode (IdeMode :: _) = True
|
||||
ideMode (_ :: xs) = ideMode xs
|
||||
|
||||
export
|
||||
ideModeSocket : List CLOpt -> Bool
|
||||
ideModeSocket [] = False
|
||||
ideModeSocket (IdeModeSocket _ :: _) = True
|
||||
ideModeSocket (_ :: xs) = ideModeSocket xs
|
Loading…
Reference in New Issue
Block a user