All Downloads are FREE. Search and download functionalities are using the official Maven repository.

Compiler.Scheme.Chez.idr Maven / Gradle / Ivy

The newest version!
module Compiler.Scheme.Chez

import Compiler.Common
import Compiler.CompileExpr
import Compiler.Generated
import Compiler.Opts.ToplevelConstants
import Compiler.Scheme.Common

import Core.Context
import Core.Context.Log
import Core.Directory
import Core.Name
import Core.Options
import Core.TT
import Protocol.Hex
import Libraries.Utils.Path
import Libraries.Data.SortedSet
import Libraries.Data.String.Builder

import Data.List
import Data.List1
import Data.Maybe
import Data.String
import Data.Vect

import Idris.Env
import Idris.Syntax

import System
import System.Directory
import System.Info

import Libraries.Data.Version
import Libraries.Utils.String

%default covering

export
findChez : IO String
findChez
    = do Nothing <- idrisGetEnv "CHEZ"
            | Just chez => pure chez
         path <- pathLookup ["chez", "chezscheme", "chez-scheme", "chezscheme9.5", "scheme"]
         pure $ fromMaybe "/usr/bin/env scheme" path

||| Returns the chez scheme version for given executable
|||
||| This uses `chez --version` which unfortunately writes the version
||| on `stderr` thus requiring suffixing the command which shell redirection
||| which does not seem very portable.
export
chezVersion : String -> IO (Maybe Version)
chezVersion chez = do
    Right fh <- popen cmd Read
        | Left err => pure Nothing
    Right output <- fGetLine fh
        | Left err => pure Nothing
    ignore $ pclose fh
    pure $ parseVersion output
  where
  cmd : String
  cmd = chez ++ " --version 2>&1"

unsupportedCallingConvention : Maybe Version -> Bool
unsupportedCallingConvention Nothing = True
unsupportedCallingConvention (Just version) = version < MkVersion (9,5,0) Nothing

-- 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.
export
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

schHeader : String -> List String -> Bool -> Builder
schHeader chez libs whole
  = fromString $
    (if os /= "windows"
        then "#!" ++ chez ++ (if whole then " --program\n\n" else " --script\n\n")
        else "") ++ """
    ;; \{ generatedString "Chez" }
    (import (chezscheme))
    (case (machine-type)
      [(i3fb ti3fb a6fb ta6fb) #f]
      [(i3le ti3le a6le ta6le tarm64le) (load-shared-object "libc.so.6")]
      [(i3osx ti3osx a6osx ta6osx tarm64osx) (load-shared-object "libc.dylib")]
      [(i3nt ti3nt a6nt ta6nt) (load-shared-object "msvcrt.dll")]
      [else (load-shared-object "libc.so")])

    \{ showSep "\n" (map (\x => "(load-shared-object \"" ++ escapeStringChez x ++ "\")") libs) }

    \{ ifThenElse whole
                  "(let ()"
                  "(source-directories (cons (getenv \"IDRIS2_INC_SRC\") (source-directories)))"
     }

    """

schFooter : Bool -> Bool -> Builder
schFooter prof whole = fromString """

    (collect 4)
    (blodwen-run-finalisers)
    \{ ifThenElse prof "(profile-dump-html)" "" }
    \{ ifThenElse whole ")" "" }
  """

showChezChar : Char -> Builder -> Builder
showChezChar '\\' acc = "\\\\" ++ acc
showChezChar c acc
   = if ord c < 32 || ord c > 126
        then fromString ("\\x" ++ asHex (cast c) ++ ";") ++ acc
        else char c ++ acc

showChezString : List Char -> Builder -> Builder
showChezString [] acc = acc
showChezString ('"' :: cs) acc = "\\\"" ++ showChezString cs acc
showChezString (c :: cs) acc = showChezChar c $ showChezString cs acc

export
chezString : String -> Builder
chezString cs = "\"" ++ showChezString (unpack cs) "\""

mutual
  handleRet : CFType -> Builder -> Builder
  handleRet CFUnit op = op ++ " " ++ mkWorld (schConstructor chezString (UN Underscore) (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))

  export
  chezExtPrim : SortedSet Name -> Nat -> ExtPrim -> List NamedCExp -> Core Builder
  chezExtPrim cs i GetField [NmPrimVal _ (Str s), _, _, struct,
                          NmPrimVal _ (Str fld), _]
      = do structsc <- schExp cs (chezExtPrim cs) chezString 0 struct
           pure $ "(ftype-ref " ++ fromString s ++ " (" ++ fromString fld ++ ") " ++ structsc ++ ")"
  chezExtPrim cs i GetField [_,_,_,_,_,_]
      = pure "(blodwen-error-quit \"bad getField\")"
  chezExtPrim cs i SetField [NmPrimVal _ (Str s), _, _, struct,
                          NmPrimVal _ (Str fld), _, val, world]
      = do structsc <- schExp cs (chezExtPrim cs) chezString 0 struct
           valsc <- schExp cs (chezExtPrim cs) chezString 0 val
           pure $ mkWorld $
              "(ftype-set! " ++ fromString s ++ " (" ++ fromString fld ++ ") " ++ structsc ++
              " " ++ valsc ++ ")"
  chezExtPrim cs i SetField [_,_,_,_,_,_,_,_]
      = pure "(blodwen-error-quit \"bad setField\")"
  chezExtPrim cs i SysCodegen []
      = pure $ "\"chez\""
  chezExtPrim cs i OnCollect [_, p, c, world]
      = do p' <- schExp cs (chezExtPrim cs) chezString 0 p
           c' <- schExp cs (chezExtPrim cs) chezString 0 c
           pure $ mkWorld $ "(blodwen-register-object " ++ p' ++ " " ++ c' ++ ")"
  chezExtPrim cs i OnCollectAny [p, c, world]
      = do p' <- schExp cs (chezExtPrim cs) chezString 0 p
           c' <- schExp cs (chezExtPrim cs) chezString 0 c
           pure $ mkWorld $ "(blodwen-register-object " ++ p' ++ " " ++ c' ++ ")"
  chezExtPrim cs i MakeFuture [_, work]
      = do work' <- schExp cs (chezExtPrim cs) chezString 0 work
           pure $ "(blodwen-make-future " ++ work' ++ ")"
  chezExtPrim cs i prim args
      = schExtCommon cs (chezExtPrim cs) chezString i prim args

-- Reference label for keeping track of loaded external libraries
export
data Loaded : Type where

-- Label for noting which struct types are declared
export
data Structs : Type where

cftySpec : FC -> CFType -> Core Builder
cftySpec fc CFUnit = pure "void"
cftySpec fc CFInt = pure "int"
cftySpec fc CFInt8 = pure "integer-8"
cftySpec fc CFInt16 = pure "integer-16"
cftySpec fc CFInt32 = pure "integer-32"
cftySpec fc CFInt64 = pure "integer-64"
cftySpec fc CFUnsigned8 = pure "unsigned-8"
cftySpec fc CFUnsigned16 = pure "unsigned-16"
cftySpec fc CFUnsigned32 = pure "unsigned-32"
cftySpec fc CFUnsigned64 = pure "unsigned-64"
cftySpec fc CFString = pure "string"
cftySpec fc CFDouble = pure "double"
cftySpec fc CFChar = pure "char"
cftySpec fc CFPtr = pure "void*"
cftySpec fc CFGCPtr = pure "void*"
cftySpec fc CFBuffer = pure "u8*"
cftySpec fc (CFFun s t) = pure "void*"
cftySpec fc (CFIORes t) = cftySpec fc t
cftySpec fc (CFStruct n t) = pure $ "(* " ++ fromString n ++ ")"
cftySpec fc t = throw (GenericMsg fc ("Can't pass argument of type " ++ show t ++
                         " to foreign function"))

locateLib : {auto c : Ref Ctxt Defs} -> String -> String -> Core String
locateLib appdir clib
    = do (fname, fullname) <- locate clib
         copyLib (appdir  fname, fullname)
         pure fname

export
loadLib : {auto c : Ref Ctxt Defs} ->
          String -> String -> Core String
loadLib appdir clib
    = do fname <- locateLib appdir clib
         pure $ "(load-shared-object \""
                                    ++ escapeStringChez fname
                                    ++ "\")\n"

loadSO : {auto c : Ref Ctxt Defs} ->
         String -> String -> Core String
loadSO appdir "" = pure ""
loadSO appdir mod
    = do d <- getDirs
         bdir <- ttcBuildDirectory
         allDirs <- extraSearchDirectories
         let fs = map (\p => p  mod) (bdir :: allDirs)
         Just fname <- firstAvailable fs
            | Nothing => throw (InternalError ("Missing .so:" ++ mod))
         -- Easier to put them all in the same directory, so we don't need
         -- to traverse a directory tree when installing the executable. So,
         -- separate with '-' rather than directory separators.
         let modfname = fastConcat (intersperse "-" (splitPath mod))
         copyLib (appdir  modfname, fname)
         pure $ "(load \"" ++ escapeStringChez modfname ++ "\")\n"

cCall : {auto c : Ref Ctxt Defs}
     -> {auto l : Ref Loaded (List String)}
     -> FC
     -> (cfn : String)
     -> (clib : String)
     -> List (Name, CFType)
     -> CFType
     -> (collectSafe : Bool)
     -> Core (Maybe String, Builder)
cCall fc cfn clib args (CFIORes CFGCPtr) _
    = throw (GenericMsg fc "Can't return GCPtr from a foreign function")
cCall fc cfn clib args CFGCPtr _
    = throw (GenericMsg fc "Can't return GCPtr from a foreign function")
cCall fc cfn clib args (CFIORes CFBuffer) _
    = throw (GenericMsg fc "Can't return Buffer from a foreign function")
cCall fc cfn clib args CFBuffer _
    = throw (GenericMsg fc "Can't return Buffer from a foreign function")
cCall fc cfn clib args ret collectSafe
    = do loaded <- get Loaded
         lib <- if clib `elem` loaded
                   then pure Nothing
                   else do put Loaded (clib :: loaded)
                           pure (Just clib)
         argTypes <- traverse (cftySpec fc . snd) args
         retType <- cftySpec fc ret
         let callConv = if collectSafe then " __collect_safe" else ""
         let call = "((foreign-procedure" ++ callConv ++ " " ++ showB cfn ++ " ("
                      ++ sepBy " " argTypes ++ ") " ++ retType ++ ") "
                      ++ sepBy " " !(traverse buildArg args) ++ ")"

         pure (lib, case ret of
                         CFIORes _ => handleRet ret call
                         _ => call)
  where
    mkNs : Int -> List CFType -> List (Maybe Builder)
    mkNs i [] = []
    mkNs i (CFWorld :: xs) = Nothing :: mkNs i xs
    mkNs i (x :: xs) = Just (fromString $ "cb" ++ show i) :: mkNs (i + 1) xs

    applyLams : Builder -> List (Maybe Builder) -> Builder
    applyLams n [] = n
    applyLams n (Nothing :: as) = applyLams ("(" ++ n ++ " #f)") as
    applyLams n (Just a :: as) = applyLams ("(" ++ n ++ " " ++ a ++ ")") as

    getVal : Builder -> Builder
    getVal str = "(vector-ref " ++ str ++ "1)"

    mkFun : List CFType -> CFType -> Builder -> Builder
    mkFun args ret n
        = let argns = mkNs 0 args in
              "(lambda (" ++ sepBy " " (catMaybes argns) ++ ") " ++
              (applyLams n argns ++ ")")

    notWorld : CFType -> Bool
    notWorld CFWorld = False
    notWorld _ = True

    callback : Builder -> List CFType -> CFType -> Core Builder
    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 ++
                       " (" ++ sepBy " " argTypes ++ ") " ++ retType ++ ")])" ++
                       " (lock-object c-code) (foreign-callable-entry-point c-code))"

    buildArg : (Name, CFType) -> Core Builder
    buildArg (n, CFFun s t) = callback (schName n) [s] t
    buildArg (n, CFGCPtr) = pure $ "(car " ++ schName n ++ ")"
    buildArg (n, _) = pure $ schName n

schemeCall : FC -> (sfn : String) ->
             List Name -> CFType -> Core Builder
schemeCall fc sfn argns ret
    = let call = "(" ++ fromString sfn ++ " " ++ sepBy " " (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 ->
        Maybe Version -> Core (Maybe String, Builder)
useCC fc ccs args ret version
    = case parseCC ["scheme,chez", "scheme", "C__collect_safe", "C"] ccs of
           Just ("scheme,chez", [sfn]) =>
               do body <- schemeCall fc sfn (map fst args) ret
                  pure (Nothing, body)
           Just ("scheme", [sfn]) =>
               do body <- schemeCall fc sfn (map fst args) ret
                  pure (Nothing, body)
           Just ("C__collect_safe", (cfn :: clib :: _)) => do
             if unsupportedCallingConvention version
               then cCall fc cfn clib args ret False
               else cCall fc cfn clib args ret True
           Just ("C", (cfn :: clib :: _)) =>
             cCall fc cfn clib args ret False
           _ => throw (NoForeignCC fc ccs)

-- 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 Builder
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 " ++ fromString n ++ " (struct\n\t"
                           ++ sepBy "\n\t" !(traverse showFld flds) ++ "))\n"
  where
    showFld : (String, CFType) -> Core Builder
    showFld (n, ty) = pure $ "[" ++ fromString 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 -> Maybe Version ->
            Core (Maybe String, Builder)
schFgnDef fc n (MkNmForeign cs args ret) version
    = 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 version
         defs <- get Ctxt
         pure (load,
                concat argStrs ++ retStr ++
                "(define " ++ schName !(full (gamma defs) n) ++
                " (lambda (" ++ sepBy " " (map schName allargns) ++ ") " ++
                body ++ "))\n")
schFgnDef _ _ _ _ = pure (Nothing, "")

export
getFgnCall : {auto c : Ref Ctxt Defs} ->
             {auto l : Ref Loaded (List String)} ->
             {auto s : Ref Structs (List String)} ->
             Maybe Version -> (Name, FC, NamedDef) ->
             Core (Maybe String, Builder)
getFgnCall version (n, fc, d) = schFgnDef fc n d version

export
startChezPreamble : String
startChezPreamble = """
  #!/bin/sh
  # \{ generatedString "Chez" }

  set -e # exit on any error

  if [ "$(uname)" = Darwin ]; then
    DIR=$(zsh -c 'printf %s "$0:A:h"' "$0")
  else
    DIR=$(dirname "$(readlink -f -- "$0")")
  fi

  """

startChez : String -> String -> String
startChez appdir target = startChezPreamble ++ """
  export LD_LIBRARY_PATH="$DIR/\{ appdir }:$LD_LIBRARY_PATH"
  export DYLD_LIBRARY_PATH="$DIR/\{ appdir }:$DYLD_LIBRARY_PATH"
  export IDRIS2_INC_SRC="$DIR/\{ appdir }"

  "$DIR/\{ target }" "$@"
  """

startChezCmd : String -> String -> String -> String -> String
startChezCmd chez appdir target progType = """
  @echo off

  rem \{ generatedString "Chez" }

  set APPDIR=%~dp0
  set PATH=%APPDIR%\{ appdir };%PATH%
  set IDRIS2_INC_SRC=%APPDIR%\{ appdir }

  "\{ chez }" \{ progType } "%APPDIR%\{ target }" %*
  """

startChezWinSh : String -> String -> String -> String -> String
startChezWinSh chez appdir target progType = """
  #!/bin/sh
  # \{ generatedString "Chez" }

  set -e # exit on any error

  DIR=$(dirname "$(readlink -f -- "$0" || cygpath -a -- "$0")")
  PATH="$DIR/\{ appdir }:$PATH"

  export IDRIS2_INC_SRC="$DIR/\{ appdir }"

  "\{ chez }" \{ progType } "$DIR/\{ target }" "$@"
  """

||| Compile a TT expression to Chez Scheme
compileToSS : Ref Ctxt Defs ->
              Bool -> -- profiling
              String -> ClosedTerm -> (outfile : String) -> Core ()
compileToSS c prof appdir tm outfile
    = do ds <- getDirectives Chez
         libs <- findLibs ds
         traverse_ copyLib libs
         cdata <- getCompileData False 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 []
         chez <- coreLift findChez
         version <- coreLift $ chezVersion chez
         fgndefs <- traverse (getFgnCall version) ndefs
         loadlibs <- traverse (locateLib appdir) (mapMaybe fst fgndefs)

         (sortedDefs, constants) <- sortDefs ndefs
         compdefs <- logTime 3 "Print as scheme" $ traverse (getScheme constants (chezExtPrim constants) chezString) sortedDefs
         let code = concat (map snd fgndefs) ++ concat compdefs
         main <- schExp constants (chezExtPrim constants) chezString 0 ctm
         support <- readDataFile "chez/support.ss"
         extraRuntime <- getExtraRuntime ds
         let scm = concat $ the (List _)
                   [ schHeader chez (map snd libs ++ loadlibs) True
                   , fromString support
                   , fromString extraRuntime
                   , code
                   , "(collect-request-handler (lambda () (collect) (blodwen-run-finalisers)))\n"
                   , main
                   , schFooter prof True
                   ]
         Right () <- coreLift $ writeFile outfile $ build scm
            | Left err => throw (FileErr outfile err)
         coreLift_ $ chmodRaw outfile 0o755

||| Compile a Chez Scheme source file to an executable, daringly with runtime checks off.
compileToSO : {auto c : Ref Ctxt Defs} ->
              Bool -> -- profiling
              String -> (appDirRel : String) -> (outSsAbs : String) -> Core ()
compileToSO prof chez appDirRel outSsAbs
    = do let tmpFileAbs = appDirRel  "compileChez"
         let build = "(parameterize ([optimize-level 3] "
                     ++ (if prof then "[compile-profile #t] "
                                else "") ++
                     "[compile-file-message #f]) (compile-program " ++
                    show outSsAbs ++ "))"
         Right () <- coreLift $ writeFile tmpFileAbs build
            | Left err => throw (FileErr tmpFileAbs err)
         coreLift_ $ chmodRaw tmpFileAbs 0o755
         coreLift_ $ system [chez, "--script", tmpFileAbs]
         pure ()

||| Compile a TT expression to Chez Scheme using incremental module builds
compileToSSInc : Ref Ctxt Defs ->
                 List String -> -- module so files
                 List String -> -- libraries to find and load
                 String -> ClosedTerm -> (outfile : String) -> Core ()
compileToSSInc c mods libs appdir tm outfile
    = do chez <- coreLift findChez
         tmcexp <- compileTerm tm
         let ctm = forget tmcexp

         loadlibs <- traverse (map fromString . loadLib appdir) (nub libs)
         loadsos <- traverse (map fromString . loadSO appdir) (nub mods)

         main <- schExp empty (chezExtPrim empty) chezString 0 ctm
         support <- readDataFile "chez/support.ss"

         let scm = schHeader chez [] False ++
                   fromString support ++
                   concat loadlibs ++
                   concat loadsos ++
                   "(collect-request-handler (lambda () (collect) (blodwen-run-finalisers)))\n" ++
                   main ++ schFooter False False

         Right () <- coreLift $ writeFile outfile $ build scm
            | Left err => throw (FileErr outfile err)
         coreLift_ $ chmodRaw outfile 0o755
         pure ()


makeSh : String -> String -> String -> Core ()
makeSh outShRel appdir outAbs
    = do Right () <- coreLift $ writeFile outShRel (startChez appdir outAbs)
            | Left err => throw (FileErr outShRel err)
         pure ()

||| Make Windows start scripts, one for bash environments and one batch file
makeShWindows : String -> String -> String -> String -> String -> Core ()
makeShWindows chez outShRel appdir outAbs progType
    = do let cmdFile = outShRel ++ ".cmd"
         Right () <- coreLift $ writeFile cmdFile (startChezCmd chez appdir outAbs progType)
            | Left err => throw (FileErr cmdFile err)
         Right () <- coreLift $ writeFile outShRel (startChezWinSh chez appdir outAbs progType)
            | Left err => throw (FileErr outShRel err)
         pure ()

compileExprWhole :
  Bool ->
  Ref Ctxt Defs ->
  Ref Syn SyntaxInfo ->
  (tmpDir : String) -> (outputDir : String) ->
  ClosedTerm -> (outfile : String) -> Core (Maybe String)
compileExprWhole makeitso c s tmpDir outputDir tm outfile
    = do let appDirRel = outfile ++ "_app" -- relative to build dir
         let appDirGen = outputDir  appDirRel -- relative to here
         coreLift_ $ mkdirAll appDirGen
         Just cwd <- coreLift currentDir
              | Nothing => throw (InternalError "Can't get current directory")
         let outSsFile = appDirRel  outfile <.> "ss"
         let outSoFile = appDirRel  outfile <.> "so"
         let outSsAbs = cwd  outputDir  outSsFile
         let outSoAbs = cwd  outputDir  outSoFile
         chez <- coreLift $ findChez
         let prof = profile !getSession
         logTime 2 "Compile to scheme" $ compileToSS c (makeitso && prof) appDirGen tm outSsAbs
         logTime 2 "Make SO" $ when makeitso $
           compileToSO prof chez appDirGen outSsAbs
         let outShRel = outputDir  outfile
         if isWindows
            then makeShWindows chez outShRel appDirRel (if makeitso then outSoFile else outSsFile) "--program"
            else makeSh outShRel appDirRel (if makeitso then outSoFile else outSsFile)
         coreLift_ $ chmodRaw outShRel 0o755
         pure (Just outShRel)

compileExprInc :
  Bool ->
  Ref Ctxt Defs ->
  Ref Syn SyntaxInfo ->
  (tmpDir : String) -> (outputDir : String) ->
  ClosedTerm -> (outfile : String) -> Core (Maybe String)
compileExprInc makeitso c s tmpDir outputDir tm outfile
    = do defs <- get Ctxt
         let Just (mods, libs) = lookup Chez (allIncData defs)
             | Nothing =>
                 do coreLift $ putStrLn $ "Missing incremental compile data, reverting to whole program compilation"
                    compileExprWhole makeitso c s tmpDir outputDir tm outfile
         let appDirRel = outfile ++ "_app" -- relative to build dir
         let appDirGen = outputDir  appDirRel -- relative to here
         coreLift_ $ mkdirAll appDirGen
         Just cwd <- coreLift currentDir
              | Nothing => throw (InternalError "Can't get current directory")
         let outSsFile = appDirRel  outfile <.> "ss"
         let outSoFile = appDirRel  outfile <.> "so"
         let outSsAbs = cwd  outputDir  outSsFile
         let outSoAbs = cwd  outputDir  outSoFile
         chez <- coreLift $ findChez
         compileToSSInc c mods libs appDirGen tm outSsAbs
         let outShRel = outputDir  outfile
         if isWindows
            then makeShWindows chez outShRel appDirRel outSsFile "--script"
            else makeSh outShRel appDirRel outSsFile
         coreLift_ $ chmodRaw outShRel 0o755
         pure (Just outShRel)

||| Chez Scheme implementation of the `compileExpr` interface.
compileExpr :
  Bool ->
  Ref Ctxt Defs ->
  Ref Syn SyntaxInfo ->
  (tmpDir : String) -> (outputDir : String) ->
  ClosedTerm -> (outfile : String) -> Core (Maybe String)
compileExpr makeitso c s tmpDir outputDir tm outfile
    = do sesh <- getSession
         if not (wholeProgram sesh) && (Chez `elem` incrementalCGs sesh)
            then compileExprInc makeitso c s tmpDir outputDir tm outfile
            else compileExprWhole makeitso c s tmpDir outputDir tm outfile

||| 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 ->
  Ref Syn SyntaxInfo ->
  (tmpDir : String) -> ClosedTerm -> Core ()
executeExpr c s tmpDir tm
    = do Just sh <- compileExpr False c s tmpDir tmpDir tm "_tmpchez"
            | Nothing => throw (InternalError "compileExpr returned Nothing")
         coreLift_ $ system [sh]

incCompile :
  Ref Ctxt Defs ->
  Ref Syn SyntaxInfo ->
  (sourceFile : String) -> Core (Maybe (String, List String))
incCompile c s sourceFile
    = do
         ssFile <- getTTCFileName sourceFile "ss"
         soFile <- getTTCFileName sourceFile "so"
         soFilename <- getObjFileName sourceFile "so"
         cdata <- getIncCompileData False Cases

         d <- getDirs
         outputDir <- ttcBuildDirectory

         let ndefs = namedDefs cdata
         if isNil ndefs
            then pure (Just ("", []))
                      -- ^ no code to generate, but still recored that the
                      -- module has been compiled, with no output needed.
            else do
               l <- newRef {t = List String} Loaded ["libc", "libc 6"]
               s <- newRef {t = List String} Structs []
               chez <- coreLift findChez
               version <- coreLift $ chezVersion chez
               fgndefs <- traverse (getFgnCall version) ndefs
               (sortedDefs, constants) <- sortDefs ndefs
               compdefs <- traverse (getScheme constants (chezExtPrim constants) chezString) sortedDefs
               let code = concat $ map snd fgndefs ++ compdefs
               Right () <- coreLift $ writeFile ssFile $ build code
                  | Left err => throw (FileErr ssFile err)

               -- Compile to .so
               let tmpFileAbs = outputDir  "compileChez"
               let build = "(parameterize ([optimize-level 3] " ++
                           "[compile-file-message #f]) (compile-file " ++
                          show ssFile ++ "))"
               Right () <- coreLift $ writeFile tmpFileAbs build
                  | Left err => throw (FileErr tmpFileAbs err)
               coreLift_ $ system [chez, "--script", tmpFileAbs]
               pure (Just (soFilename, mapMaybe fst fgndefs))

||| Codegen wrapper for Chez scheme implementation.
export
codegenChez : Codegen
codegenChez = MkCG (compileExpr True) executeExpr (Just incCompile) (Just "so")




© 2015 - 2024 Weber Informatics LLC | Privacy Policy