Skip to content

Commit

Permalink
Basic Geb integration (#1748)
Browse files Browse the repository at this point in the history
This PR:

- Closes #1647 

It gives compilation errors for language features that require more
substantial support (recursion, polymorphism). The additional features
are to be implemented in future separate PRs.
* Adds a new target `geb` to the CLI command `juvix dev core compile`,
which produces a `*.geb` output file in the `.juvix-build` directory.
* Adds a few tests. These are not yet checked automatically because
there is no GEB evaluator; checking the `*.geb` output would be too
brittle.
  • Loading branch information
lukaszcz authored Feb 1, 2023
1 parent a5623c5 commit a5d19c5
Show file tree
Hide file tree
Showing 36 changed files with 1,129 additions and 39 deletions.
1 change: 1 addition & 0 deletions app/Commands/Dev/Asm/Compile.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ runCommand opts = do
TargetWasm32Wasi -> Backend.TargetCWasm32Wasi
TargetNative64 -> Backend.TargetCNative64
TargetC -> Backend.TargetCWasm32Wasi
TargetGeb -> error "GEB target not supported for JuvixAsm"

inputCFile :: (Members '[App] r) => Path Abs File -> Sem r (Path Abs File)
inputCFile inputFileCompile = do
Expand Down
54 changes: 41 additions & 13 deletions app/Commands/Dev/Core/Compile.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,65 @@ import Data.Text.IO qualified as TIO
import Juvix.Compiler.Asm.Options qualified as Asm
import Juvix.Compiler.Backend qualified as Backend
import Juvix.Compiler.Backend.C qualified as C
import Juvix.Compiler.Backend.Geb qualified as Geb
import Juvix.Compiler.Core.Data.InfoTable qualified as Core
import Juvix.Compiler.Core.Translation.FromSource qualified as Core

data PipelineArg = PipelineArg
{ _pipelineArgOptions :: CoreCompileOptions,
_pipelineArgFile :: Path Abs File,
_pipelineArgInfoTable :: Core.InfoTable
}

makeLenses ''PipelineArg

runCommand :: forall r. (Members '[Embed IO, App] r) => CoreCompileOptions -> Sem r ()
runCommand opts = do
file <- getFile
s <- embed (readFile (toFilePath file))
tab <- getRight (mapLeft JuvixError (Core.runParserMain file Core.emptyInfoTable s))
C.MiniCResult {..} <- getRight (run (runError (coreToMiniC asmOpts tab :: Sem '[Error JuvixError] C.MiniCResult)))
buildDir <- askBuildDir
ensureDir buildDir
cFile <- inputCFile file
embed $ TIO.writeFile (toFilePath cFile) _resultCCode
Compile.runCommand opts {_compileInputFile = AppPath (Abs cFile) False}
case opts ^. compileTarget of
TargetGeb -> runGebPipeline (PipelineArg opts file tab)
TargetWasm32Wasi -> runCPipeline (PipelineArg opts file tab)
TargetNative64 -> runCPipeline (PipelineArg opts file tab)
TargetC -> runCPipeline (PipelineArg opts file tab)
where
getFile :: Sem r (Path Abs File)
getFile = someBaseToAbs' (opts ^. compileInputFile . pathPath)

runCPipeline ::
forall r.
(Members '[Embed IO, App] r) =>
PipelineArg ->
Sem r ()
runCPipeline PipelineArg {..} = do
C.MiniCResult {..} <- getRight (run (runError (coreToMiniC asmOpts _pipelineArgInfoTable :: Sem '[Error JuvixError] C.MiniCResult)))
cFile <- inputFile _pipelineArgFile ".c"
embed $ TIO.writeFile (toFilePath cFile) _resultCCode
Compile.runCommand _pipelineArgOptions {_compileInputFile = AppPath (Abs cFile) False}
where
asmOpts :: Asm.Options
asmOpts = Asm.makeOptions (asmTarget (opts ^. compileTarget)) (opts ^. compileDebug)
asmOpts = Asm.makeOptions (asmTarget (_pipelineArgOptions ^. compileTarget)) (_pipelineArgOptions ^. compileDebug)

asmTarget :: CompileTarget -> Backend.Target
asmTarget = \case
TargetWasm32Wasi -> Backend.TargetCWasm32Wasi
TargetNative64 -> Backend.TargetCNative64
TargetC -> Backend.TargetCWasm32Wasi
TargetGeb -> impossible

runGebPipeline ::
forall r.
(Members '[Embed IO, App] r) =>
PipelineArg ->
Sem r ()
runGebPipeline PipelineArg {..} = do
Geb.Result {..} <- getRight (run (runError (coreToGeb _pipelineArgInfoTable :: Sem '[Error JuvixError] Geb.Result)))
gebFile <- inputFile _pipelineArgFile ".geb"
embed $ TIO.writeFile (toFilePath gebFile) _resultCode

inputCFile :: (Members '[App] r) => Path Abs File -> Sem r (Path Abs File)
inputCFile inputFileCompile = do
inputFile :: (Members '[Embed IO, App] r) => Path Abs File -> String -> Sem r (Path Abs File)
inputFile inputFileCompile ext = do
buildDir <- askBuildDir
return (buildDir <//> outputMiniCFile)
where
outputMiniCFile :: Path Rel File
outputMiniCFile = replaceExtension' ".c" (filename inputFileCompile)
ensureDir buildDir
return (buildDir <//> replaceExtension' ext (filename inputFileCompile))
2 changes: 2 additions & 0 deletions app/Commands/Extra/Compile.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ runCompile inputFile o = do
TargetWasm32Wasi -> runError (clangWasmWasiCompile inputFile o)
TargetNative64 -> runError (clangNativeCompile inputFile o)
TargetC -> return $ Right ()
TargetGeb -> return $ Right ()

prepareRuntime :: forall r. (Members '[App, Embed IO] r) => Path Abs Dir -> CompileOptions -> Sem r ()
prepareRuntime buildDir o = do
Expand All @@ -40,6 +41,7 @@ prepareRuntime buildDir o = do
TargetNative64 | o ^. compileDebug -> writeRuntime nativeDebugRuntime
TargetNative64 -> writeRuntime nativeReleaseRuntime
TargetC -> return ()
TargetGeb -> return ()
where
wasiReleaseRuntime :: BS.ByteString
wasiReleaseRuntime = $(FE.makeRelativeToProject "runtime/_build.wasm32-wasi/libjuvix.a" >>= FE.embedFile)
Expand Down
6 changes: 5 additions & 1 deletion app/Commands/Extra/Compile/Options.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ data CompileTarget
= TargetWasm32Wasi
| TargetNative64
| TargetC
| TargetGeb
deriving stock (Show, Data)

data CompileOptions = CompileOptions
Expand Down Expand Up @@ -54,17 +55,20 @@ optCompileTarget =
<> metavar "TARGET"
<> value TargetNative64
<> showDefaultWith targetShow
<> help "select a target: wasm32-wasi, native, c"
<> help "select a target: wasm32-wasi, native, c, geb"
)
where
parseTarget :: String -> Either String CompileTarget
parseTarget = \case
"wasm32-wasi" -> Right TargetWasm32Wasi
"native" -> Right TargetNative64
"c" -> Right TargetC
"geb" -> Right TargetGeb
s -> Left $ "unrecognised target: " <> s

targetShow :: CompileTarget -> String
targetShow = \case
TargetWasm32Wasi -> "wasm32-wasi"
TargetNative64 -> "native"
TargetC -> "c"
TargetGeb -> "geb"
4 changes: 3 additions & 1 deletion cntlines.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
RUNTIME=`find runtime/src/ -name '*.c' -or -name '*.h' | xargs wc -l | tail -1 | tr -d ' toal'`

BACKENDC=`find src/Juvix/Compiler/Backend/C/ -name '*.hs' | xargs wc -l | tail -1 | tr -d ' toal'`
GEB=`find src/Juvix/Compiler/Backend/Geb/ -name '*.hs' | xargs wc -l | tail -1 | tr -d ' toal'`
REG=`find src/Juvix/Compiler/Reg/ -name '*.hs' -print | xargs wc -l | tail -1 | tr -d ' toal'`
ASM=`find src/Juvix/Compiler/Asm/ -name '*.hs' -print | xargs wc -l | tail -1 | tr -d ' toal'`
CORE=`find src/Juvix/Compiler/Core/ -name '*.hs' -print | xargs wc -l | tail -1 | tr -d ' toal'`
Expand All @@ -20,7 +21,7 @@ DATA=`find src/Juvix/Data/ -name '*.hs' | xargs wc -l | tail -1 | tr -d ' toal'`
PRELUDE=`find src/Juvix/Prelude/ -name '*.hs' | xargs wc -l | tail -1 | tr -d ' toal'`

FRONT=$((CONCRETE + ABSTRACT + INTERNAL + BUILTINS + PIPELINE))
BACK=$((BACKENDC + REG + ASM + CORE))
BACK=$((BACKENDC + GEB + REG + ASM + CORE))
OTHER=$((APP + HTML + EXTRA + DATA + PRELUDE))

echo "Front end: $FRONT LOC"
Expand All @@ -31,6 +32,7 @@ echo " Builtins: $BUILTINS LOC"
echo " Pipeline: $PIPELINE LOC"
echo "Middle and back end: $BACK LOC"
echo " C backend: $BACKENDC LOC"
echo " GEB backend: $GEB LOC"
echo " JuvixReg: $REG LOC"
echo " JuvixAsm: $ASM LOC"
echo " JuvixCore: $CORE LOC"
Expand Down
6 changes: 0 additions & 6 deletions src/Juvix/Compiler/Asm/Pretty/Base.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ doc opts =
class PrettyCode c where
ppCode :: (Member (Reader Options) r) => c -> Sem r (Doc Ann)

runPrettyCode :: (PrettyCode c) => Options -> c -> Doc Ann
runPrettyCode opts = run . runReader opts . ppCode

wrapCore ::
forall r' c.
(Member (Reader Options) r') =>
Expand Down Expand Up @@ -373,9 +370,6 @@ instance PrettyCode InfoTable where
{--------------------------------------------------------------------------------}
{- helper functions -}

parensIf :: Bool -> Doc Ann -> Doc Ann
parensIf t = if t then parens else id

braces' :: Doc Ann -> Doc Ann
braces' d = braces (line <> indent' d <> line)

Expand Down
4 changes: 4 additions & 0 deletions src/Juvix/Compiler/Backend/Geb.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Juvix.Compiler.Backend.Geb (module Juvix.Compiler.Backend.Geb.Language, module Juvix.Compiler.Backend.Geb.Translation) where

import Juvix.Compiler.Backend.Geb.Language
import Juvix.Compiler.Backend.Geb.Translation
10 changes: 10 additions & 0 deletions src/Juvix/Compiler/Backend/Geb/Extra.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Juvix.Compiler.Backend.Geb.Extra where

import Juvix.Compiler.Backend.Geb.Language

-- | Destructs a product in a right-associative way, e.g., (a, (b, c)) is
-- destructed to [a, b, c]
destructProduct :: Object -> [Object]
destructProduct = \case
ObjectProduct Product {..} -> _productLeft : destructProduct _productRight
x -> [x]
137 changes: 137 additions & 0 deletions src/Juvix/Compiler/Backend/Geb/Language.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
module Juvix.Compiler.Backend.Geb.Language
( module Juvix.Compiler.Backend.Geb.Language,
module Juvix.Prelude,
)
where

import Juvix.Prelude hiding (First, Product)

{-
The following datatypes correspond to GEB types for terms
(https://github.com/anoma/geb/blob/main/src/specs/lambda.lisp) and types
(https://github.com/anoma/geb/blob/main/src/specs/geb.lisp).
-}

-- | Represents GEB's `case-on`. `_caseOn` is the value matched on of type
-- `Dom`, `_caseLeft` has type `_caseLeftType -> _caseCodomainType` and
-- `_caseRight` has type `_caseRightType -> _caseCodomainType`.
data Case = Case
{ _caseLeftType :: Object,
_caseRightType :: Object,
_caseCodomainType :: Object,
_caseOn :: Morphism,
_caseLeft :: Morphism,
_caseRight :: Morphism
}

data Pair = Pair
{ _pairLeftType :: Object,
_pairRightType :: Object,
_pairLeft :: Morphism,
_pairRight :: Morphism
}

data First = First
{ _firstLeftType :: Object,
_firstRightType :: Object,
_firstValue :: Morphism
}

data Second = Second
{ _secondLeftType :: Object,
_secondRightType :: Object,
_secondValue :: Morphism
}

data Lambda = Lambda
{ _lambdaVarType :: Object,
_lambdaBodyType :: Object,
_lambdaBody :: Morphism
}

data Application = Application
{ _applicationDomainType :: Object,
_applicationCodomainType :: Object,
_applicationLeft :: Morphism,
_applicationRight :: Morphism
}

newtype Var = Var {_varIndex :: Int}

-- | Corresponds to the GEB type for morphisms (terms): `stlc`
-- (https://github.com/anoma/geb/blob/main/src/specs/lambda.lisp).
data Morphism
= MorphismAbsurd Morphism
| MorphismUnit
| MorphismLeft Morphism
| MorphismRight Morphism
| MorphismCase Case
| MorphismPair Pair
| MorphismFirst First
| MorphismSecond Second
| MorphismLambda Lambda
| MorphismApplication Application
| MorphismVar Var

data Product = Product
{ _productLeft :: Object,
_productRight :: Object
}

data Coproduct = Coproduct
{ _coproductLeft :: Object,
_coproductRight :: Object
}

-- | Function type
data Hom = Hom
{ _homDomain :: Object,
_homCodomain :: Object
}

-- | Corresponds to the GEB type for types (objects of the category): `substobj`
-- (https://github.com/anoma/geb/blob/main/src/specs/geb.lisp).
data Object
= -- | empty type
ObjectInitial
| -- | unit type
ObjectTerminal
| ObjectProduct Product
| ObjectCoproduct Coproduct
| -- | function type
ObjectHom Hom

instance HasAtomicity Morphism where
atomicity = \case
MorphismAbsurd {} -> Aggregate appFixity
MorphismUnit -> Atom
MorphismLeft {} -> Aggregate appFixity
MorphismRight {} -> Aggregate appFixity
MorphismCase {} -> Aggregate appFixity
MorphismPair {} -> Aggregate appFixity
MorphismFirst {} -> Aggregate appFixity
MorphismSecond {} -> Aggregate appFixity
MorphismLambda {} -> Aggregate appFixity
MorphismApplication {} -> Aggregate appFixity
MorphismVar {} -> Aggregate appFixity

instance HasAtomicity Object where
atomicity = \case
ObjectInitial -> Atom
ObjectTerminal -> Atom
ObjectProduct {} -> Aggregate appFixity
ObjectCoproduct {} -> Aggregate appFixity
ObjectHom {} -> Aggregate appFixity

makeLenses ''Case
makeLenses ''Pair
makeLenses ''First
makeLenses ''Second
makeLenses ''Lambda
makeLenses ''Var
makeLenses ''Application
makeLenses ''Morphism
makeLenses ''Product
makeLenses ''Coproduct
makeLenses ''Hom
makeLenses ''Object
28 changes: 28 additions & 0 deletions src/Juvix/Compiler/Backend/Geb/Pretty.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module Juvix.Compiler.Backend.Geb.Pretty
( module Juvix.Compiler.Backend.Geb.Pretty,
module Juvix.Compiler.Backend.Geb.Pretty.Base,
module Juvix.Compiler.Backend.Geb.Pretty.Options,
module Juvix.Data.PPOutput,
)
where

import Juvix.Compiler.Backend.Geb.Pretty.Base
import Juvix.Compiler.Backend.Geb.Pretty.Options
import Juvix.Data.PPOutput
import Juvix.Prelude
import Prettyprinter.Render.Terminal qualified as Ansi

ppOutDefault :: (HasAtomicity c, PrettyCode c) => c -> AnsiText
ppOutDefault = AnsiText . PPOutput . doc defaultOptions

ppOut :: (CanonicalProjection a Options, HasAtomicity c, PrettyCode c) => a -> c -> AnsiText
ppOut o = AnsiText . PPOutput . doc (project o)

ppTrace' :: (CanonicalProjection a Options, HasAtomicity c, PrettyCode c) => a -> c -> Text
ppTrace' opts = Ansi.renderStrict . reAnnotateS stylize . layoutPretty defaultLayoutOptions . doc (project opts)

ppTrace :: (HasAtomicity c, PrettyCode c) => c -> Text
ppTrace = ppTrace' traceOptions

ppPrint :: (HasAtomicity c, PrettyCode c) => c -> Text
ppPrint = show . ppOutDefault
Loading

0 comments on commit a5d19c5

Please sign in to comment.