1
1
mirror of https://github.com/anoma/juvix.git synced 2024-10-05 20:47:36 +03:00

Rust backend (#2787)

* Implements code generation through Rust.
* CLI: adds two `dev` compilation targets: 
  1. `rust` for generating Rust code
  2. `native-rust` for generating a native executable via Rust
* Adds end-to-end tests for compilation from Juvix to native executable
via Rust.
* A target for RISC0 needs to be added in a separate PR building on this
one.
This commit is contained in:
Łukasz Czajka 2024-05-29 13:34:04 +02:00 committed by GitHub
parent 9faa88d4da
commit 55598e0f95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
174 changed files with 3585 additions and 85 deletions

View File

@ -4,8 +4,10 @@ import Commands.Base
import Commands.Dev.DevCompile.Asm qualified as Asm
import Commands.Dev.DevCompile.Casm qualified as Casm
import Commands.Dev.DevCompile.Core qualified as Core
import Commands.Dev.DevCompile.NativeRust qualified as NativeRust
import Commands.Dev.DevCompile.Options
import Commands.Dev.DevCompile.Reg qualified as Reg
import Commands.Dev.DevCompile.Rust qualified as Rust
import Commands.Dev.DevCompile.Tree qualified as Tree
runCommand :: (Members '[App, EmbedIO, TaggedLock] r) => DevCompileCommand -> Sem r ()
@ -15,3 +17,5 @@ runCommand = \case
Asm opts -> Asm.runCommand opts
Tree opts -> Tree.runCommand opts
Casm opts -> Casm.runCommand opts
Rust opts -> Rust.runCommand opts
NativeRust opts -> NativeRust.runCommand opts

View File

@ -0,0 +1,75 @@
module Commands.Dev.DevCompile.NativeRust where
import Commands.Base
import Commands.Dev.DevCompile.NativeRust.Options
import Commands.Extra.Rust
import Data.ByteString qualified as BS
import Data.FileEmbed qualified as FE
import Juvix.Compiler.Backend.Rust.Data.Result
runCommand ::
(Members '[App, EmbedIO, TaggedLock] r) =>
NativeRustOptions 'InputMain ->
Sem r ()
runCommand opts = do
let opts' = opts ^. nativeRustCompileCommonOptions
inputFile = opts' ^. compileInputFile
moutputFile = opts' ^. compileOutputFile
mainFile <- getMainFile inputFile
Result {..} <- runPipeline opts inputFile upToRust
rustFile <- inputRustFile mainFile
writeFileEnsureLn rustFile _resultRustCode
buildDir <- askBuildDir
ensureDir buildDir
prepareRuntime
outputFile <- nativeOutputFile mainFile moutputFile
let args =
RustArgs
{ _rustDebug = opts' ^. compileDebug,
_rustInputFile = rustFile,
_rustOutputFile = outputFile,
_rustOptimizationLevel = fmap (min 3 . (+ 1)) (opts' ^. compileOptimizationLevel)
}
rustCompile args
where
prepareRuntime ::
forall s.
(Members '[App, EmbedIO] s) =>
Sem s ()
prepareRuntime = writeRuntime runtime
where
runtime :: BS.ByteString
runtime
| opts ^. nativeRustCompileCommonOptions . compileDebug = rustDebugRuntime
| otherwise = rustReleaseRuntime
where
rustReleaseRuntime :: BS.ByteString
rustReleaseRuntime = $(FE.makeRelativeToProject "runtime/rust/target/release/libjuvix.rlib" >>= FE.embedFile)
rustDebugRuntime :: BS.ByteString
rustDebugRuntime = $(FE.makeRelativeToProject "runtime/rust/target/debug/libjuvix.rlib" >>= FE.embedFile)
inputRustFile :: (Members '[App, EmbedIO] r) => Path Abs File -> Sem r (Path Abs File)
inputRustFile inputFileCompile = do
buildDir <- askBuildDir
ensureDir buildDir
return (buildDir <//> replaceExtension' ".rs" (filename inputFileCompile))
nativeOutputFile :: forall r. (Member App r) => Path Abs File -> Maybe (AppPath File) -> Sem r (Path Abs File)
nativeOutputFile inputFile moutputFile =
case moutputFile of
Just f -> fromAppFile f
Nothing -> do
invokeDir <- askInvokeDir
let baseOutputFile = invokeDir <//> filename inputFile
return $ removeExtension' baseOutputFile
writeRuntime ::
forall r.
(Members '[App, EmbedIO] r) =>
BS.ByteString ->
Sem r ()
writeRuntime runtime = do
buildDir <- askBuildDir
liftIO $
BS.writeFile (toFilePath (buildDir <//> $(mkRelFile "libjuvix.rlib"))) runtime

View File

@ -0,0 +1,28 @@
{-# LANGUAGE UndecidableInstances #-}
module Commands.Dev.DevCompile.NativeRust.Options
( module Commands.Dev.DevCompile.NativeRust.Options,
module Commands.Compile.CommonOptions,
)
where
import Commands.Compile.CommonOptions
import CommonOptions
data NativeRustOptions (k :: InputKind) = NativeRustOptions
{ _nativeRustCompileCommonOptions :: CompileCommonOptions k
}
deriving stock instance (Typeable k, Data (InputFileType k)) => Data (NativeRustOptions k)
makeLenses ''NativeRustOptions
parseNativeRust :: (SingI k) => Parser (NativeRustOptions k)
parseNativeRust = do
_nativeRustCompileCommonOptions <- parseCompileCommonOptions
pure NativeRustOptions {..}
instance EntryPointOptions (NativeRustOptions k) where
applyOptions opts =
set entryPointTarget (Just TargetRust)
. applyOptions (opts ^. nativeRustCompileCommonOptions)

View File

@ -3,7 +3,9 @@ module Commands.Dev.DevCompile.Options where
import Commands.Dev.DevCompile.Asm.Options
import Commands.Dev.DevCompile.Casm.Options
import Commands.Dev.DevCompile.Core.Options
import Commands.Dev.DevCompile.NativeRust.Options
import Commands.Dev.DevCompile.Reg.Options
import Commands.Dev.DevCompile.Rust.Options
import Commands.Dev.DevCompile.Tree.Options
import CommonOptions
@ -13,6 +15,8 @@ data DevCompileCommand
| Reg (RegOptions 'InputMain)
| Tree (TreeOptions 'InputMain)
| Casm (CasmOptions 'InputMain)
| Rust (RustOptions 'InputMain)
| NativeRust (NativeRustOptions 'InputMain)
deriving stock (Data)
parseDevCompileCommand :: Parser DevCompileCommand
@ -23,7 +27,9 @@ parseDevCompileCommand =
commandReg,
commandTree,
commandCasm,
commandAsm
commandAsm,
commandRust,
commandNativeRust
]
)
@ -61,3 +67,17 @@ commandAsm =
info
(Asm <$> parseAsm)
(progDesc "Compile to Juvix ASM")
commandRust :: Mod CommandFields DevCompileCommand
commandRust =
command "rust" $
info
(Rust <$> parseRust)
(progDesc "Compile to Rust")
commandNativeRust :: Mod CommandFields DevCompileCommand
commandNativeRust =
command "native-rust" $
info
(NativeRust <$> parseNativeRust)
(progDesc "Compile to native executable through Rust")

View File

@ -0,0 +1,17 @@
module Commands.Dev.DevCompile.Rust where
import Commands.Base
import Commands.Dev.DevCompile.Rust.Options
import Commands.Extra.NewCompile
import Juvix.Compiler.Backend.Rust.Data.Result
runCommand ::
(Members '[App, EmbedIO, TaggedLock] r) =>
RustOptions 'InputMain ->
Sem r ()
runCommand opts = do
let inputFile = opts ^. rustCompileCommonOptions . compileInputFile
moutputFile = opts ^. rustCompileCommonOptions . compileOutputFile
outFile :: Path Abs File <- getOutputFile FileExtRust inputFile moutputFile
Result {..} <- runPipeline opts inputFile upToRust
writeFileEnsureLn outFile _resultRustCode

View File

@ -0,0 +1,28 @@
{-# LANGUAGE UndecidableInstances #-}
module Commands.Dev.DevCompile.Rust.Options
( module Commands.Dev.DevCompile.Rust.Options,
module Commands.Compile.CommonOptions,
)
where
import Commands.Compile.CommonOptions
import CommonOptions
data RustOptions (k :: InputKind) = RustOptions
{ _rustCompileCommonOptions :: CompileCommonOptions k
}
deriving stock instance (Typeable k, Data (InputFileType k)) => Data (RustOptions k)
makeLenses ''RustOptions
parseRust :: (SingI k) => Parser (RustOptions k)
parseRust = do
_rustCompileCommonOptions <- parseCompileCommonOptions
pure RustOptions {..}
instance EntryPointOptions (RustOptions k) where
applyOptions opts =
set entryPointTarget (Just TargetRust)
. applyOptions (opts ^. rustCompileCommonOptions)

View File

@ -0,0 +1,87 @@
module Commands.Extra.Rust where
import Commands.Base
import System.Process qualified as P
data RustArgs = RustArgs
{ _rustDebug :: Bool,
_rustInputFile :: Path Abs File,
_rustOutputFile :: Path Abs File,
_rustOptimizationLevel :: Maybe Int
}
makeLenses ''RustArgs
rustCompile ::
forall r.
(Members '[App, EmbedIO] r) =>
RustArgs ->
Sem r ()
rustCompile args = do
getRustcCliArgs args >>= runRustc
getRustcCliArgs :: (Members '[App, EmbedIO] r) => RustArgs -> Sem r [String]
getRustcCliArgs args = do
buildDir <- askBuildDir
return (nativeArgs buildDir args)
nativeArgs :: Path Abs Dir -> RustArgs -> [String]
nativeArgs buildDir args@RustArgs {..} =
commonArgs buildDir args ++ extraArgs
where
extraArgs :: [String]
extraArgs = run . execAccumList $ do
addOptimizationOption args
addArg (toFilePath _rustInputFile)
addOptimizationOption :: (Member (Accum String) r) => RustArgs -> Sem r ()
addOptimizationOption args = do
addArg "-C"
addArg $ "opt-level=" <> show (maybe defaultOptLevel (max 1) (args ^. rustOptimizationLevel))
where
defaultOptLevel :: Int
defaultOptLevel
| args ^. rustDebug = debugRustOptimizationLevel
| otherwise = defaultRustOptimizationLevel
debugRustOptimizationLevel :: Int
debugRustOptimizationLevel = 1
defaultRustOptimizationLevel :: Int
defaultRustOptimizationLevel = 3
addArg :: (Member (Accum String) r) => String -> Sem r ()
addArg = accum
commonArgs :: Path Abs Dir -> RustArgs -> [String]
commonArgs buildDir RustArgs {..} = run . execAccumList $ do
when _rustDebug $ do
addArg "-g"
addArg "-C"
addArg "debug-assertions=true"
addArg "-C"
addArg "overflow-checks=true"
addArg "-o"
addArg (toFilePath _rustOutputFile)
addArg ("-L")
addArg (toFilePath buildDir)
runRustc ::
forall r.
(Members '[App, EmbedIO] r) =>
[String] ->
Sem r ()
runRustc args = do
cp <- rustcBinPath
(exitCode, _, err) <- liftIO (P.readProcessWithExitCode cp args "")
case exitCode of
ExitSuccess -> return ()
_ -> exitFailMsg (pack err)
where
rustcBinPath :: Sem r String
rustcBinPath = do
p <- findExecutable $(mkRelFile "rustc")
maybe (exitFailMsg rustcNotFoundErr) (return . toFilePath) p
rustcNotFoundErr :: Text
rustcNotFoundErr = "Error: The rustc executable was not found. Please install the Rust compiler"

View File

@ -17,6 +17,7 @@ RUNTIME_CASM=$(count_ext '*.casm' runtime/casm)
RUNTIME=$((RUNTIME_C+RUNTIME_RUST+RUNTIME_VAMPIR+RUNTIME_JVT+RUNTIME_CASM))
BACKENDC=$(count src/Juvix/Compiler/Backend/C/)
BACKENDRUST=$(count src/Juvix/Compiler/Backend/Rust/)
CAIRO=$(count src/Juvix/Compiler/Backend/Cairo/)
GEB=$(count src/Juvix/Compiler/Backend/Geb/)
VAMPIR=$(count src/Juvix/Compiler/Backend/VampIR/)
@ -40,7 +41,7 @@ PRELUDE=$(count src/Juvix/Prelude/)
STORE=$(count src/Juvix/Compiler/Store/)
FRONT=$((CONCRETE + INTERNAL + BUILTINS + PIPELINE))
BACK=$((BACKENDC + GEB + VAMPIR + NOCK + REG + ASM + TREE + CORE + CASM + CAIRO))
BACK=$((BACKENDC + BACKENDRUST + GEB + VAMPIR + NOCK + REG + ASM + TREE + CORE + CASM + CAIRO))
OTHER=$((APP + STORE + HTML + EXTRA + DATA + PRELUDE))
TESTS=$(count test/)
@ -55,6 +56,7 @@ echo "Middle and back end: $BACK LOC"
echo " VampIR backend: $VAMPIR LOC"
echo " GEB backend: $GEB LOC"
echo " C backend: $BACKENDC LOC"
echo " Rust backend: $BACKENDRUST LOC"
echo " Cairo backend: $((CASM + CAIRO)) LOC"
echo " Nockma backend: $NOCK LOC"
echo " JuvixReg: $REG LOC"

View File

@ -38,6 +38,7 @@ extra-source-files:
- include/package-base/**/*.juvix
- runtime/c/include/**/*.h
- runtime/c/**/*.a
- runtime/rust/target/**/*.rlib
- runtime/tree/*.jvt
- runtime/vampir/*.pir
- runtime/casm/*.casm

View File

@ -10,7 +10,7 @@ juvix_c:
.PHONY: juvix_rust
juvix_rust:
cd rust && cargo build --release
cd rust && cargo build && cargo build --release
.PHONY: clean
clean:

View File

@ -29,11 +29,11 @@ impl Memory {
#[macro_export]
macro_rules! tapply {
($lab:lifetime, $program:ident, $mem:ident, $fid:ident, $args:ident, $cl0:expr, $cargs0:expr) => {
($lab:lifetime, $program:ident, $mem:ident, $fid:ident, $args:ident, $cl0:expr, $cargs0:expr) => {{
let mut cl = $cl0;
let mut cargs = $cargs0;
loop {
match $mem.apply( cl, &cargs) {
match $mem.apply(cl, &cargs) {
apply::AppResult::Call(fid1, args1) => {
$fid = fid1;
$args = args1;
@ -46,7 +46,7 @@ macro_rules! tapply {
}
}
}
};
}}
}
#[macro_export]

View File

@ -30,8 +30,8 @@ impl Memory {
p
}
// Assigns stored closure args plus the provided closure args (`cargs`) to the
// argument space (`args`). Returns the function id to call.
// Returns the function id to call and the full arguments for closure call:
// stored closure args plus the provided closure args (`cargs`).
pub fn call_closure(self: &Memory, cl: Word, cargs: &[Word]) -> (Word, Vec<Word>) {
let mut args = Vec::from(self.get_closure_args(cl));
args.extend(cargs);

View File

@ -12,6 +12,11 @@ pub type SmallInt = i32;
pub const INITIAL_STACK_CAPACITY: usize = 10 * 1024;
pub const INITIAL_MEMORY_CAPACITY: usize = 10 * 1024;
pub const BOOL_FALSE: Word = 0;
pub const BOOL_TRUE: Word = 1;
pub const OBJ_UNIT: Word = 0;
pub const OBJ_VOID: Word = 0;
pub fn word_to_usize(s: Word) -> usize {
s as usize
}

View File

@ -4,6 +4,6 @@ use super::defs::*;
// Check for equality. The implementation is incorrect for complex objects, but
// complex objects are not compared in Juvix with built-in equality for now.
pub fn juvix_equal(x: Word, y: Word) -> bool {
x == y
pub fn juvix_equal(x: Word, y: Word) -> Word {
bool_to_word(x == y)
}

View File

@ -49,7 +49,7 @@ mod tests {
continue;
}
FUN_ITFIB_GO => {
if juvix_equal(args[0], make_smallint(0)) {
if word_to_bool(juvix_equal(args[0], make_smallint(0))) {
break args[1];
} else {
args = vec![
@ -122,19 +122,19 @@ mod tests {
vec![id, id, id, id, id, id, id, id, id, id, id, make_smallint(1)]
);
let tmp1 = smallint_add(x, y);
break smallint_add(tmp1, z);
return smallint_add(tmp1, z);
}
FUN_S => {
let xz = apply!(program_sk, mem, args[0], vec![args[2]]);
let yz = apply!(program_sk, mem, args[1], vec![args[2]]);
tapply!('program, program_sk, mem, fid, args, xz, vec![yz]);
tapply!('program, program_sk, mem, fid, args, xz, vec![yz])
}
FUN_K => {
break args[0];
return args[0];
}
FUN_I => {
let k = mem.alloc_closure(FUN_K, &[], 2);
break mem.alloc_closure(FUN_S, &[k, k], 1);
return mem.alloc_closure(FUN_S, &[k, k], 1);
}
_ => panic!("unknown function id"),
}

View File

@ -12,6 +12,7 @@ data Target
| TargetAsm
| TargetReg
| TargetTree
| TargetRust
| TargetAnoma
| TargetCairo
deriving stock (Data, Eq, Show)
@ -110,6 +111,21 @@ getLimits tgt debug = case tgt of
_limitsBuiltinUIDsNum = 8,
_limitsSpecialisedApply = 3
}
TargetRust ->
Limits
{ _limitsMaxConstrs = fromIntegral (maxBound :: Int32),
_limitsMaxConstrArgs = fromIntegral (maxBound :: Int32),
_limitsMaxFunctionArgs = fromIntegral (maxBound :: Int32),
_limitsMaxLocalVars = 2048,
_limitsMaxClosureSize = fromIntegral (maxBound :: Int32),
_limitsClosureHeadSize = 3,
_limitsMaxStringSize = 0,
_limitsMaxStackDelta = 0,
_limitsMaxFunctionAlloc = 0,
_limitsDispatchStackSize = 0,
_limitsBuiltinUIDsNum = 0,
_limitsSpecialisedApply = 0
}
defaultLimits :: Limits
defaultLimits =

View File

@ -0,0 +1,9 @@
module Juvix.Compiler.Backend.Rust.Data.Result where
import Juvix.Prelude
newtype Result = Result
{ _resultRustCode :: Text
}
makeLenses ''Result

View File

@ -0,0 +1,135 @@
module Juvix.Compiler.Backend.Rust.Language where
import Juvix.Prelude
data Program = Program
{ _programFunctions :: [Function]
}
data Type
= Word
| VecOfWord
| Memory
data IsMut
= Mut
| NotMut
data FunctionArgument = FunctionArgument
{ _functionArgumentMutable :: IsMut,
_functionArgumentName :: Text,
_functionArgumentType :: Type
}
data Function = Function
{ _functionName :: Text,
_functionArguments :: [FunctionArgument],
_functionReturnType :: Maybe Type,
_functionBody :: [Statement],
_functionAttributes :: Maybe Text
}
data Statement
= StatementLet Let
| StatementConst ConstDecl
| StatementAssignment Assignment
| StatementIf If
| StatementMatch Match
| StatementLoop Loop
| StatementContinue
| StatementReturn Return
| StatementExpression Expression
data Let = Let
{ _letVariable :: Text,
_letType :: Maybe Type,
_letMutable :: IsMut,
_letInitializer :: Maybe Expression
}
data ConstDecl = ConstDecl
{ _constVariable :: Text,
_constType :: Type,
_constValue :: Expression
}
data Assignment = Assignment
{ _assignmentVariable :: Text,
_assignmentValue :: Expression
}
data If = If
{ _ifValue :: Expression,
_ifBranchTrue :: [Statement],
_ifBranchFalse :: [Statement]
}
data MatchBranch = MatchBranch
{ -- Nothing indicates the wildcard
_matchBranchPattern :: Maybe Expression,
_matchBranchBody :: [Statement]
}
data Match = Match
{ _matchValue :: Expression,
_matchBranches :: [MatchBranch]
}
data Loop = Loop
{ _loopLabel :: Maybe Text,
_loopBody :: [Statement]
}
data Return = Return
{ _returnValue :: Maybe Expression
}
data Expression
= ExprVar Var
| ExprCall Call
| ExprVec Vec
| ExprArray Array
| ExprLiteral Literal
| ExprBlock Block
| ExprVerbatim Text
data Var = Var
{ _varName :: Text
}
data Call = Call
{ _callFunction :: Text,
_callArgs :: [Expression]
}
data Vec = Vec
{ _vecArgs :: [Expression]
}
data Array = Array
{ _arrayArgs :: [Expression]
}
data Block = Block
{ _blockBody :: [Statement]
}
data Literal
= LitInteger Integer
| LitString Text
makeLenses ''FunctionArgument
makeLenses ''Function
makeLenses ''Let
makeLenses ''ConstDecl
makeLenses ''Assignment
makeLenses ''MatchBranch
makeLenses ''Match
makeLenses ''Loop
makeLenses ''Return
makeLenses ''Var
makeLenses ''Call
makeLenses ''Vec
makeLenses ''Array
makeLenses ''Block
makeLenses ''Program

View File

@ -0,0 +1,28 @@
module Juvix.Compiler.Backend.Rust.Pretty
( module Juvix.Compiler.Backend.Rust.Pretty,
module Juvix.Compiler.Backend.Rust.Pretty.Base,
module Juvix.Compiler.Backend.Rust.Pretty.Options,
module Juvix.Data.PPOutput,
)
where
import Juvix.Compiler.Backend.Rust.Pretty.Base
import Juvix.Compiler.Backend.Rust.Pretty.Options
import Juvix.Data.PPOutput
import Juvix.Prelude
import Prettyprinter.Render.Terminal qualified as Ansi
ppOutDefault :: (PrettyCode c) => c -> AnsiText
ppOutDefault = mkAnsiText . PPOutput . doc defaultOptions
ppOut :: (CanonicalProjection a Options, PrettyCode c) => a -> c -> AnsiText
ppOut o = mkAnsiText . PPOutput . doc (project o)
ppTrace' :: (CanonicalProjection a Options, PrettyCode c) => a -> c -> Text
ppTrace' opts = Ansi.renderStrict . reAnnotateS stylize . layoutPretty defaultLayoutOptions . doc (project opts)
ppTrace :: (PrettyCode c) => c -> Text
ppTrace = ppTrace' traceOptions
ppPrint :: (PrettyCode c) => c -> Text
ppPrint = show . ppOutDefault

View File

@ -0,0 +1,190 @@
module Juvix.Compiler.Backend.Rust.Pretty.Base where
import Juvix.Compiler.Backend.Rust.Language
import Juvix.Compiler.Backend.Rust.Pretty.Keywords
import Juvix.Compiler.Backend.Rust.Pretty.Options
import Juvix.Data.CodeAnn
import Juvix.Prelude
class PrettyCode c where
ppCode :: (Member (Reader Options) r) => c -> Sem r (Doc Ann)
doc :: (PrettyCode c) => Options -> c -> Doc Ann
doc opts x =
run $
runReader opts $
ppCode x
ampersand :: Doc Ann
ampersand = "&"
hashsym :: Doc Ann
hashsym = "#"
ppName :: NameKind -> Text -> Sem r (Doc Ann)
ppName k n = return $ annotate (AnnKind k) (pretty n)
ppMut :: IsMut -> Sem r (Maybe (Doc Ann))
ppMut = \case
Mut -> return $ Just kwMut
NotMut -> return Nothing
ppAttrs :: Maybe Text -> Sem r (Doc Ann)
ppAttrs = \case
Just attrs -> return $ hashsym <> brackets (pretty attrs) <> line
Nothing -> return mempty
ppBlock :: (Member (Reader Options) r) => [Statement] -> Sem r (Doc Ann)
ppBlock stmts = do
stmts' <- mapM ppCode stmts
let stmts'' = punctuate semi stmts'
return $ oneLineOrNextBraces (vsep stmts'')
instance PrettyCode Type where
ppCode = \case
Word -> return kwWord
VecOfWord -> return $ kwVector <> angles kwWord
Memory -> return $ ampersand <> kwMut <+> kwMemory
instance PrettyCode FunctionArgument where
ppCode FunctionArgument {..} = do
ty <- ppCode _functionArgumentType
n <- ppName KNameLocal _functionArgumentName
mut <- ppMut _functionArgumentMutable
return $ mut <?+> n <> colon <+> ty
instance PrettyCode Function where
ppCode Function {..} = do
attrs <- ppAttrs _functionAttributes
name <- ppName KNameFunction _functionName
args <- mapM ppCode _functionArguments
rty <- maybe (return Nothing) (ppCode >=> return . Just . ("->" <+>)) _functionReturnType
body <- ppBlock _functionBody
let args' = punctuate comma args
return $ attrs <> kwFn <+> name <> parens (hsep args') <+> rty <?+> body <> line
instance PrettyCode Statement where
ppCode = \case
StatementLet x -> ppCode x
StatementConst x -> ppCode x
StatementAssignment x -> ppCode x
StatementIf x -> ppCode x
StatementMatch x -> ppCode x
StatementLoop x -> ppCode x
StatementContinue -> return kwContinue
StatementReturn x -> ppCode x
StatementExpression x -> ppCode x
instance PrettyCode Let where
ppCode Let {..} = do
name <- ppName KNameLocal _letVariable
ty <- maybe (return Nothing) (ppCode >=> return . Just) _letType
mut <- ppMut _letMutable
ini <- maybe (return Nothing) (ppCode >=> return . Just) _letInitializer
let ini' = fmap ("=" <+>) ini
ty' = fmap (colon <+>) ty
return $ kwLet <+> mut <?+> (name <>? ty' <+?> ini')
instance PrettyCode ConstDecl where
ppCode ConstDecl {..} = do
name <- ppName KNameLocal _constVariable
ty <- ppCode _constType
val <- ppCode _constValue
return $ kwConst <+> name <> colon <+> ty <+> "=" <+> val
instance PrettyCode Assignment where
ppCode Assignment {..} = do
name <- ppName KNameLocal _assignmentVariable
val <- ppCode _assignmentValue
return $ name <+> "=" <+> val
instance PrettyCode If where
ppCode If {..} = do
val <- ppCode _ifValue
br1 <- ppBlock _ifBranchTrue
br2 <- ppBlock _ifBranchFalse
return $ kwIf <+> val <+> br1 <+> kwElse <+> br2
instance PrettyCode MatchBranch where
ppCode MatchBranch {..} = do
pat <- case _matchBranchPattern of
Just p -> ppCode p
Nothing -> return "_"
body <- ppBlock _matchBranchBody
return $ pat <+> "=>" <+> body
instance PrettyCode Match where
ppCode Match {..} = do
val <- ppCode _matchValue
brs <- mapM ppCode _matchBranches
return $ kwMatch <+> val <+> oneLineOrNextBraces (vsep brs)
instance PrettyCode Loop where
ppCode Loop {..} = do
let lab = fmap ((<> colon) . pretty) _loopLabel
body <- ppBlock _loopBody
return $ lab <?+> kwLoop <+> body
instance PrettyCode Return where
ppCode Return {..} = do
val <- maybe (return Nothing) (ppCode >=> return . Just) _returnValue
return $ kwReturn <+?> val
instance PrettyCode Expression where
ppCode = \case
ExprVar x -> ppCode x
ExprCall x -> ppCode x
ExprVec x -> ppCode x
ExprArray x -> ppCode x
ExprLiteral x -> ppCode x
ExprBlock x -> ppCode x
ExprVerbatim x -> return $ pretty x
instance PrettyCode Var where
ppCode Var {..} = ppName KNameLocal _varName
instance PrettyCode Call where
ppCode Call {..} = do
name <- ppName KNameFunction _callFunction
args <- mapM ppCode _callArgs
return $ name <> parens (hsep (punctuate comma args))
instance PrettyCode Vec where
ppCode Vec {..} = do
args <- mapM ppCode _vecArgs
return $ kwVec <> brackets (hsep (punctuate comma args))
instance PrettyCode Array where
ppCode Array {..} = do
args <- mapM ppCode _arrayArgs
return $ ampersand <> brackets (hsep (punctuate comma args))
instance PrettyCode Literal where
ppCode = \case
LitInteger i -> return $ annotate AnnLiteralInteger (pretty i)
LitString s -> return $ annotate AnnLiteralString (show s)
instance PrettyCode Block where
ppCode Block {..} = ppBlock _blockBody
instance PrettyCode Program where
ppCode Program {..} = do
funs <- mapM ppCode _programFunctions
return $ pretty prelude <> line <> vsep funs
where
prelude :: Text
prelude =
"extern crate juvix;\n\
\\n\
\#[allow(unused_imports)]\n\
\use juvix::defs::*;\n\
\#[allow(unused_imports)]\n\
\use juvix::apply;\n\
\#[allow(unused_imports)]\n\
\use juvix::tapply;\n\
\#[allow(unused_imports)]\n\
\use juvix::equality::*;\n\
\#[allow(unused_imports)]\n\
\use juvix::integer::*;\n\
\#[allow(unused_imports)]\n\
\use juvix::memory::*;\n"

View File

@ -0,0 +1,43 @@
module Juvix.Compiler.Backend.Rust.Pretty.Keywords where
import Juvix.Data.CodeAnn
import Juvix.Extra.Strings qualified as Str
kwFn :: Doc Ann
kwFn = keyword Str.rustFn
kwIf :: Doc Ann
kwIf = keyword Str.rustIf
kwElse :: Doc Ann
kwElse = keyword Str.rustElse
kwMatch :: Doc Ann
kwMatch = keyword Str.rustMatch
kwLoop :: Doc Ann
kwLoop = keyword Str.rustLoop
kwConst :: Doc Ann
kwConst = keyword Str.rustConst
kwMut :: Doc Ann
kwMut = keyword Str.rustMut
kwVec :: Doc Ann
kwVec = keyword Str.rustVec
kwVector :: Doc Ann
kwVector = keyword Str.rustVector
kwWord :: Doc Ann
kwWord = keyword Str.rustWord
kwMemory :: Doc Ann
kwMemory = keyword Str.rustMemory
kwContinue :: Doc Ann
kwContinue = keyword Str.rustContinue
kwReturn :: Doc Ann
kwReturn = keyword Str.rustReturn

View File

@ -0,0 +1,21 @@
module Juvix.Compiler.Backend.Rust.Pretty.Options where
import Juvix.Prelude
-- no fields for now, but make it easier to add options in the future I don't
-- remove this datatype entirely
data Options = Options
makeLenses ''Options
defaultOptions :: Options
defaultOptions = Options
traceOptions :: Options
traceOptions = defaultOptions
fromGenericOptions :: GenericOptions -> Options
fromGenericOptions _ = defaultOptions
instance CanonicalProjection GenericOptions Options where
project = fromGenericOptions

View File

@ -0,0 +1,396 @@
module Juvix.Compiler.Backend.Rust.Translation.FromReg
( module Juvix.Compiler.Backend.Rust.Translation.FromReg,
module Juvix.Compiler.Backend.Rust.Data.Result,
)
where
import Data.HashMap.Strict qualified as HashMap
import Juvix.Compiler.Backend
import Juvix.Compiler.Backend.Rust.Data.Result
import Juvix.Compiler.Backend.Rust.Data.Result as Rust
import Juvix.Compiler.Backend.Rust.Language as Rust
import Juvix.Compiler.Backend.Rust.Pretty as Rust
import Juvix.Compiler.Backend.Rust.Translation.FromReg.Base
import Juvix.Compiler.Reg.Data.InfoTable qualified as Reg
import Juvix.Compiler.Reg.Extra.Info qualified as Reg
import Juvix.Compiler.Reg.Language qualified as Reg
import Juvix.Prelude
fromReg :: Limits -> Reg.InfoTable -> Rust.Result
fromReg lims tab =
Rust.Result $ Rust.ppPrint $ Program [programFunction, mainFunction]
where
info :: Reg.ExtraInfo
info = Reg.computeExtraInfo lims tab
mainFunid :: Int
mainFunid = getFUID info $ fromJust $ tab ^. Reg.infoMainFunction
mainFunction :: Function
mainFunction =
Function
{ _functionName = "main",
_functionAttributes = Nothing,
_functionArguments = [],
_functionReturnType = Nothing,
_functionBody =
[ stmtLet NotMut "result" (mkCall "program" [ExprVerbatim "&mut Memory::new()", mkInteger mainFunid, mkVec []]),
StatementExpression (mkCall "println!" [mkString "{}", mkVar "result"]),
StatementReturn (Return Nothing)
]
}
programFunction :: Function
programFunction =
Function
{ _functionName = "program",
_functionAttributes = Just "allow(unused_mut, unused)",
_functionArguments =
[ FunctionArgument NotMut "mem" Memory,
FunctionArgument Mut "funid" Word,
FunctionArgument Mut "args" VecOfWord
],
_functionReturnType = Just Word,
_functionBody = programBody
}
programBody :: [Statement]
programBody = funConstDecls ++ juvixFunctions
funConstDecls :: [Statement]
funConstDecls = map mkFunConstDecl funs
where
mkFunConstDecl :: Reg.FunctionInfo -> Statement
mkFunConstDecl funInfo =
StatementConst $
ConstDecl
{ _constVariable = getFunctionIdent info (funInfo ^. Reg.functionSymbol),
_constType = Word,
_constValue = mkInteger (getFUID info (funInfo ^. Reg.functionSymbol))
}
juvixFunctions :: [Statement]
juvixFunctions =
[ StatementLoop $
Loop
{ _loopLabel = Just "'program",
_loopBody =
[ StatementMatch $
Match
{ _matchValue = mkVar "funid",
_matchBranches = map (fromRegFunction info) funs ++ [errBranch]
}
]
}
]
where
errBranch =
MatchBranch
{ _matchBranchPattern = Nothing,
_matchBranchBody = [StatementExpression $ mkCall "panic!" [mkString "unknown function"]]
}
funs :: [Reg.FunctionInfo]
funs = HashMap.elems (tab ^. Reg.infoFunctions)
fromRegFunction :: Reg.ExtraInfo -> Reg.FunctionInfo -> MatchBranch
fromRegFunction info funInfo =
MatchBranch
{ _matchBranchPattern = Just $ mkVar $ getFunctionIdent info (funInfo ^. Reg.functionSymbol),
_matchBranchBody = varDecls ++ fromRegCode info (funInfo ^. Reg.functionCode)
}
where
varsNum :: Int
varsNum = getLocalVarsNum info (funInfo ^. Reg.functionSymbol)
varDecls :: [Statement]
varDecls = map (\n -> stmtDecl Mut ("var_" <> show n) Word) [0 .. varsNum - 1]
fromRegCode :: Reg.ExtraInfo -> Reg.Code -> [Statement]
fromRegCode info = concatMap (fromRegInstr info)
fromRegInstr :: Reg.ExtraInfo -> Reg.Instruction -> [Statement]
fromRegInstr info = \case
Reg.Nop ->
[]
Reg.Binop x ->
[fromBinaryOp x]
Reg.Unop x ->
[fromUnaryOp x]
Reg.Cairo {} ->
unsupported "Cairo builtin"
Reg.Assign Reg.InstrAssign {..} ->
stmtsAssign (mkVarRef _instrAssignResult) (fromValue _instrAssignValue)
Reg.Trace {} ->
unsupported "trace"
Reg.Dump ->
unsupported "dump"
Reg.Failure {} ->
unsupported "fail"
Reg.Prealloc {} ->
[]
Reg.Alloc x ->
fromAlloc x
Reg.AllocClosure x ->
fromAllocClosure x
Reg.ExtendClosure x ->
fromExtendClosure x
Reg.TailCall x ->
fromTailCall x
Reg.Call x ->
fromCall x
Reg.TailCallClosures x ->
fromTailCallClosures x
Reg.CallClosures x ->
fromCallClosures x
Reg.Return x ->
fromReturn x
Reg.Branch x ->
fromBranch x
Reg.Case x ->
fromCase x
Reg.Block Reg.InstrBlock {..} ->
fromRegCode info _instrBlockCode
where
unsupported :: Text -> a
unsupported x = error ("unsupported: " <> x)
fromBinaryOp :: Reg.InstrBinop -> Statement
fromBinaryOp Reg.InstrBinop {..} =
stmtAssign
(mkVarRef _instrBinopResult)
( mkCall
(getBinaryOpName _instrBinopOpcode)
[fromValue _instrBinopArg1, fromValue _instrBinopArg2]
)
getBinaryOpName :: Reg.BinaryOp -> Text
getBinaryOpName = \case
Reg.OpIntAdd -> "smallint_add"
Reg.OpIntSub -> "smallint_sub"
Reg.OpIntMul -> "smallint_mul"
Reg.OpIntDiv -> "smallint_div"
Reg.OpIntMod -> "smallint_mod"
Reg.OpIntLt -> "smallint_lt"
Reg.OpIntLe -> "smallint_le"
Reg.OpEq -> "juvix_equal"
Reg.OpStrConcat -> unsupported "strings"
Reg.OpFieldAdd -> unsupported "field type"
Reg.OpFieldSub -> unsupported "field type"
Reg.OpFieldMul -> unsupported "field type"
Reg.OpFieldDiv -> unsupported "field type"
fromUnaryOp :: Reg.InstrUnop -> Statement
fromUnaryOp Reg.InstrUnop {..} = case _instrUnopOpcode of
Reg.OpShow -> unsupported "strings"
Reg.OpStrToInt -> unsupported "strings"
Reg.OpArgsNum ->
stmtAssign
(mkVarRef _instrUnopResult)
(mkCall "mem.get_closure_largs" [fromValue _instrUnopArg])
Reg.OpFieldToInt -> unsupported "field type"
Reg.OpIntToField -> unsupported "field type"
mkVarRef :: Reg.VarRef -> Text
mkVarRef Reg.VarRef {..} = case _varRefGroup of
Reg.VarGroupArgs -> "args[" <> show _varRefIndex <> "]"
Reg.VarGroupLocal -> "var_" <> show _varRefIndex
fromVarRef :: Reg.VarRef -> Expression
fromVarRef = mkVar . mkVarRef
fromValue :: Reg.Value -> Expression
fromValue = \case
Reg.ValConst c -> fromConst c
Reg.CRef Reg.ConstrField {..} ->
case _constrFieldMemRep of
Reg.MemRepConstr ->
mkCall
"mem.get_constr_arg"
[fromVarRef _constrFieldRef, mkInteger _constrFieldIndex]
Reg.MemRepTag ->
unsupported "MemRepTag"
Reg.MemRepTuple ->
unsupported "MemRepTuple"
Reg.MemRepUnit ->
unsupported "MemRepUnit"
Reg.MemRepUnpacked {} ->
fromVarRef _constrFieldRef
Reg.VRef x -> fromVarRef x
fromConst :: Reg.Constant -> Expression
fromConst = \case
Reg.ConstInt x -> mkCall "make_smallint" [mkInteger x]
Reg.ConstField {} -> impossible
Reg.ConstBool True -> mkVar "BOOL_TRUE"
Reg.ConstBool False -> mkVar "BOOL_FALSE"
Reg.ConstString {} -> unsupported "strings"
Reg.ConstUnit -> mkVar "OBJ_UNIT"
Reg.ConstVoid -> mkVar "OBJ_VOID"
fromAlloc :: Reg.InstrAlloc -> [Statement]
fromAlloc Reg.InstrAlloc {..} =
case _instrAllocMemRep of
Reg.MemRepConstr ->
stmtsAssign
(mkVarRef _instrAllocResult)
( mkCall
"mem.alloc_constr"
[ mkInteger (getUID info _instrAllocTag),
mkArray (map fromValue _instrAllocArgs)
]
)
Reg.MemRepTag ->
unsupported "MemRepTag"
Reg.MemRepTuple ->
unsupported "MemRepTuple"
Reg.MemRepUnit ->
unsupported "MemRepUnit"
Reg.MemRepUnpacked {} ->
unsupported "MemRepUnpacked"
fromAllocClosure :: Reg.InstrAllocClosure -> [Statement]
fromAllocClosure Reg.InstrAllocClosure {..} =
stmtsAssign
(mkVarRef _instrAllocClosureResult)
( mkCall
"mem.alloc_closure"
[ mkVar (getFunctionIdent info _instrAllocClosureSymbol),
mkArray (map fromValue _instrAllocClosureArgs),
mkInteger (_instrAllocClosureExpectedArgsNum - length _instrAllocClosureArgs)
]
)
fromExtendClosure :: Reg.InstrExtendClosure -> [Statement]
fromExtendClosure Reg.InstrExtendClosure {..} =
stmtsAssign
(mkVarRef _instrExtendClosureResult)
( mkCall
"mem.extend_closure"
[ fromVarRef _instrExtendClosureValue,
mkArray (map fromValue _instrExtendClosureArgs)
]
)
fromCall :: Reg.InstrCall -> [Statement]
fromCall Reg.InstrCall {..} =
case _instrCallType of
Reg.CallFun sym ->
stmtsAssign
(mkVarRef _instrCallResult)
( mkCall
"program"
[mkVar "mem", mkVar (getFunctionIdent info sym), mkVec (map fromValue _instrCallArgs)]
)
Reg.CallClosure vr ->
stmtsBlock
[ stmtLet Mut "cargs" (mkVec (map fromValue _instrCallArgs)),
stmtLet NotMut "(cfunid, args)" (mkCall "mem.call_closure" [fromVarRef vr, mkVar "&cargs"]),
stmtAssign
(mkVarRef _instrCallResult)
( mkCall
"program"
[mkVar "mem", mkVar "cfunid", mkVar "args"]
)
]
fromTailCall :: Reg.InstrTailCall -> [Statement]
fromTailCall Reg.InstrTailCall {..} =
case _instrTailCallType of
Reg.CallFun sym ->
[ stmtAssign "args" (mkVec (map fromValue _instrTailCallArgs)),
stmtAssign "funid" (mkVar (getFunctionIdent info sym)),
StatementContinue
]
Reg.CallClosure vr ->
[ stmtAssign
"funid"
( mkCall
"mem.call_closure"
[fromVarRef vr, mkArray ((map fromValue _instrTailCallArgs))]
),
StatementContinue
]
fromCallClosures :: Reg.InstrCallClosures -> [Statement]
fromCallClosures Reg.InstrCallClosures {..} =
stmtsAssign
(mkVarRef _instrCallClosuresResult)
( mkCall
"apply!"
[ mkVar "program",
mkVar "mem",
fromVarRef _instrCallClosuresValue,
mkVec (map fromValue _instrCallClosuresArgs)
]
)
fromTailCallClosures :: Reg.InstrTailCallClosures -> [Statement]
fromTailCallClosures Reg.InstrTailCallClosures {..} =
[ StatementExpression $
mkCall
"tapply!"
[ mkVar "'program",
mkVar "program",
mkVar "mem",
mkVar "funid",
mkVar "args",
fromVarRef _instrTailCallClosuresValue,
mkVec (map fromValue _instrTailCallClosuresArgs)
]
]
fromBranch :: Reg.InstrBranch -> [Statement]
fromBranch Reg.InstrBranch {..} =
stmtsIf (mkCall "word_to_bool" [fromValue _instrBranchValue]) br1 br2
where
br1 = fromRegCode info _instrBranchTrue
br2 = fromRegCode info _instrBranchFalse
fromCase :: Reg.InstrCase -> [Statement]
fromCase Reg.InstrCase {..} = do
case _instrCaseIndRep of
Reg.IndRepStandard ->
[ StatementMatch $
Match
{ _matchValue = mkCall "mem.get_constr_tag" [fromValue _instrCaseValue],
_matchBranches = brs'
}
]
Reg.IndRepEnum ->
error "unsupported constructor representation"
Reg.IndRepEnumRecord ->
error "unsupported constructor representation"
Reg.IndRepEnumMaybe {} ->
error "unsupported constructor representation"
Reg.IndRepRecord ->
error "unsupported constructor representation"
Reg.IndRepUnit ->
error "unsupported constructor representation"
Reg.IndRepNewtype {} ->
error "unsupported constructor representation"
Reg.IndRepMixed ->
error "unsupported constructor representation"
where
brs = map fromCaseBranch _instrCaseBranches
def = case _instrCaseDefault of
Nothing -> Nothing
Just c -> Just $ fromRegCode info c
brs' =
brs
++ [ MatchBranch
{ _matchBranchPattern = Nothing,
_matchBranchBody = fromMaybe [StatementExpression $ mkCall "panic!" [mkString "match failure"]] def
}
]
fromCaseBranch :: Reg.CaseBranch -> MatchBranch
fromCaseBranch Reg.CaseBranch {..} =
MatchBranch
{ _matchBranchPattern = Just $ mkInteger (getUID info _caseBranchTag),
_matchBranchBody = fromRegCode info _caseBranchCode
}
fromReturn :: Reg.InstrReturn -> [Statement]
fromReturn Reg.InstrReturn {..} =
[StatementReturn $ Return $ Just $ fromValue _instrReturnValue]

View File

@ -0,0 +1,100 @@
module Juvix.Compiler.Backend.Rust.Translation.FromReg.Base where
import Data.HashMap.Strict qualified as HashMap
import Data.Text qualified as T
import Juvix.Compiler.Backend.Rust.Language
import Juvix.Compiler.Reg.Data.InfoTable qualified as Reg
import Juvix.Compiler.Reg.Extra.Info qualified as Reg
import Juvix.Compiler.Reg.Language qualified as Reg
import Juvix.Prelude
mkRustIdent :: Text -> Text
mkRustIdent ident = T.filter isValidIdentChar ident
getFunctionName :: Reg.ExtraInfo -> Reg.Symbol -> Text
getFunctionName info sym = ((info ^. Reg.extraInfoTable . Reg.infoFunctions) HashMap.! sym) ^. Reg.functionName
getBuiltinUID :: Reg.BuiltinDataTag -> Int
getBuiltinUID = \case
Reg.TagFalse -> impossible
Reg.TagTrue -> impossible
Reg.TagReturn -> impossible
Reg.TagBind -> impossible
Reg.TagWrite -> impossible
Reg.TagReadLn -> impossible
getUID :: Reg.ExtraInfo -> Reg.Tag -> Int
getUID info tag = case tag of
Reg.BuiltinTag builtin -> getBuiltinUID builtin
Reg.UserTag {} -> fromJust $ HashMap.lookup tag (info ^. Reg.extraInfoCIDs)
getFUID :: Reg.ExtraInfo -> Reg.Symbol -> Int
getFUID info sym = fromJust $ HashMap.lookup sym (info ^. Reg.extraInfoFUIDs)
getStringId :: Reg.ExtraInfo -> Text -> Int
getStringId info txt = fromJust $ HashMap.lookup txt (info ^. Reg.extraInfoStringMap)
getMaxStackHeight :: Reg.ExtraInfo -> Reg.Symbol -> Int
getMaxStackHeight info sym = fromJust $ HashMap.lookup sym (info ^. Reg.extraInfoMaxStackHeight)
getLocalVarsNum :: Reg.ExtraInfo -> Reg.Symbol -> Int
getLocalVarsNum info sym = fromJust $ HashMap.lookup sym (info ^. Reg.extraInfoLocalVarsNum)
getFunctionIdent :: Reg.ExtraInfo -> Reg.Symbol -> Text
getFunctionIdent info sym = mkRustIdent $ "JUVIX_FUNCTION_" <> T.toUpper (getFunctionName info sym) <> "_" <> show (getFUID info sym)
stmtAssign :: Text -> Expression -> Statement
stmtAssign result value = StatementAssignment $ Assignment result value
stmtsAssign :: Text -> Expression -> [Statement]
stmtsAssign result value = [stmtAssign result value]
stmtsCall :: Text -> [Expression] -> [Statement]
stmtsCall fun args = [StatementExpression $ mkCall fun args]
stmtsBlock :: [Statement] -> [Statement]
stmtsBlock stmts = [StatementExpression (ExprBlock (Block stmts))]
stmtLet :: IsMut -> Text -> Expression -> Statement
stmtLet isMut result value =
StatementLet $
Let
{ _letMutable = isMut,
_letVariable = result,
_letType = Nothing,
_letInitializer = Just value
}
stmtDecl :: IsMut -> Text -> Type -> Statement
stmtDecl isMut var ty =
StatementLet $
Let
{ _letMutable = isMut,
_letVariable = var,
_letType = Just ty,
_letInitializer = Nothing
}
stmtIf :: Expression -> [Statement] -> [Statement] -> Statement
stmtIf v br1 br2 = StatementIf $ If v br1 br2
stmtsIf :: Expression -> [Statement] -> [Statement] -> [Statement]
stmtsIf v br1 br2 = [stmtIf v br1 br2]
mkCall :: Text -> [Expression] -> Expression
mkCall fun args = ExprCall $ Call fun args
mkInteger :: (Integral a) => a -> Expression
mkInteger i = ExprLiteral $ LitInteger (fromIntegral i)
mkString :: Text -> Expression
mkString s = ExprLiteral $ LitString s
mkVar :: Text -> Expression
mkVar = ExprVar . Var
mkArray :: [Expression] -> Expression
mkArray = ExprArray . Array
mkVec :: [Expression] -> Expression
mkVec = ExprVec . Vec

View File

@ -23,6 +23,7 @@ data TransformationId
| CombineInfoTables
| CheckGeb
| CheckExec
| CheckRust
| CheckVampIR
| CheckAnoma
| CheckCairo
@ -103,6 +104,7 @@ instance TransformationId' TransformationId where
CombineInfoTables -> strCombineInfoTables
CheckGeb -> strCheckGeb
CheckExec -> strCheckExec
CheckRust -> strCheckRust
CheckVampIR -> strCheckVampIR
CheckAnoma -> strCheckAnoma
CheckCairo -> strCheckCairo

View File

@ -77,6 +77,9 @@ strCheckGeb = "check-geb"
strCheckExec :: Text
strCheckExec = "check-exec"
strCheckRust :: Text
strCheckRust = "check-rust"
strCheckVampIR :: Text
strCheckVampIR = "check-vampir"

View File

@ -17,6 +17,7 @@ import Juvix.Compiler.Core.Transformation.Check.Anoma
import Juvix.Compiler.Core.Transformation.Check.Cairo
import Juvix.Compiler.Core.Transformation.Check.Exec
import Juvix.Compiler.Core.Transformation.Check.Geb
import Juvix.Compiler.Core.Transformation.Check.Rust
import Juvix.Compiler.Core.Transformation.Check.VampIR
import Juvix.Compiler.Core.Transformation.CombineInfoTables (combineInfoTables)
import Juvix.Compiler.Core.Transformation.ComputeTypeInfo
@ -81,6 +82,7 @@ applyTransformations ts tbl = foldM (flip appTrans) tbl ts
CombineInfoTables -> return . combineInfoTables
CheckGeb -> mapError (JuvixError @CoreError) . checkGeb
CheckExec -> mapError (JuvixError @CoreError) . checkExec
CheckRust -> mapError (JuvixError @CoreError) . checkRust
CheckVampIR -> mapError (JuvixError @CoreError) . checkVampIR
CheckAnoma -> mapError (JuvixError @CoreError) . checkAnoma
CheckCairo -> mapError (JuvixError @CoreError) . checkCairo

View File

@ -162,3 +162,36 @@ checkMainExists md =
_coreErrorNode = Nothing,
_coreErrorLoc = defaultLoc
}
checkMainTypeExec :: (Member (Error CoreError) r) => Module -> Sem r ()
checkMainTypeExec md =
case md ^. moduleInfoTable . infoMain of
Nothing ->
throw
CoreError
{ _coreErrorMsg = ppOutput "no `main` function",
_coreErrorNode = Nothing,
_coreErrorLoc = defaultLoc
}
Just sym ->
case ii ^. identifierType of
NPi {} ->
throw
CoreError
{ _coreErrorMsg = ppOutput "`main` cannot have a function type for this target",
_coreErrorNode = Nothing,
_coreErrorLoc = loc
}
ty
| isTypeConstr md ty ->
throw
CoreError
{ _coreErrorMsg = ppOutput "`main` cannot be a type for this target",
_coreErrorNode = Nothing,
_coreErrorLoc = loc
}
_ ->
return ()
where
ii = lookupIdentifierInfo md sym
loc = fromMaybe defaultLoc (ii ^. identifierLocation)

View File

@ -4,44 +4,10 @@ import Juvix.Compiler.Core.Error
import Juvix.Compiler.Core.Extra
import Juvix.Compiler.Core.Transformation.Base
import Juvix.Compiler.Core.Transformation.Check.Base
import Juvix.Data.PPOutput
checkExec :: forall r. (Member (Error CoreError) r) => Module -> Sem r Module
checkExec md = do
checkNoAxioms md
checkMainExists md
checkMainType
checkMainTypeExec md
mapAllNodesM (checkBuiltins' (builtinsCairo ++ builtinsAnoma) []) md
where
checkMainType :: Sem r ()
checkMainType =
case md ^. moduleInfoTable . infoMain of
Nothing ->
throw
CoreError
{ _coreErrorMsg = ppOutput "no `main` function",
_coreErrorNode = Nothing,
_coreErrorLoc = defaultLoc
}
Just sym ->
case ii ^. identifierType of
NPi {} ->
throw
CoreError
{ _coreErrorMsg = ppOutput "`main` cannot have a function type for this target",
_coreErrorNode = Nothing,
_coreErrorLoc = loc
}
ty
| isTypeConstr md ty ->
throw
CoreError
{ _coreErrorMsg = ppOutput "`main` cannot be a type for this target",
_coreErrorNode = Nothing,
_coreErrorLoc = loc
}
_ ->
return ()
where
ii = lookupIdentifierInfo md sym
loc = fromMaybe defaultLoc (ii ^. identifierLocation)

View File

@ -0,0 +1,14 @@
module Juvix.Compiler.Core.Transformation.Check.Rust where
import Juvix.Compiler.Core.Error
import Juvix.Compiler.Core.Extra
import Juvix.Compiler.Core.Transformation.Base
import Juvix.Compiler.Core.Transformation.Check.Base
checkRust :: forall r. (Member (Error CoreError) r) => Module -> Sem r Module
checkRust md = do
checkNoAxioms md
checkMainExists md
checkMainTypeExec md
mapAllNodesM checkNoIO md
mapAllNodesM (checkBuiltins False) md

View File

@ -16,6 +16,7 @@ import Juvix.Compiler.Backend qualified as Backend
import Juvix.Compiler.Backend.C qualified as C
import Juvix.Compiler.Backend.Cairo qualified as Cairo
import Juvix.Compiler.Backend.Geb qualified as Geb
import Juvix.Compiler.Backend.Rust.Translation.FromReg qualified as Rust
import Juvix.Compiler.Backend.VampIR.Translation qualified as VampIR
import Juvix.Compiler.Casm.Data.Builtins qualified as Casm
import Juvix.Compiler.Casm.Data.Result qualified as Casm
@ -158,6 +159,11 @@ upToAnoma ::
Sem r NockmaTree.AnomaResult
upToAnoma = upToStoredCore >>= \Core.CoreResult {..} -> storedCoreToAnoma _coreResultModule
upToRust ::
(Members '[HighlightBuilder, Reader Parser.ParserResult, Reader EntryPoint, Reader Store.ModuleTable, Files, NameIdGen, Error JuvixError, PathResolver] r) =>
Sem r Rust.Result
upToRust = upToStoredCore >>= \Core.CoreResult {..} -> storedCoreToRust _coreResultModule
upToCoreTypecheck ::
(Members '[HighlightBuilder, Reader Parser.ParserResult, Reader EntryPoint, Reader Store.ModuleTable, Files, NameIdGen, Error JuvixError, PathResolver] r) =>
Sem r Core.CoreResult
@ -191,6 +197,9 @@ storedCoreToReg = storedCoreToAsm >=> asmToReg
storedCoreToMiniC :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r C.MiniCResult
storedCoreToMiniC = storedCoreToAsm >=> asmToMiniC
storedCoreToRust :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r Rust.Result
storedCoreToRust = storedCoreToTree Core.CheckRust >=> treeToReg >=> regToRust
storedCoreToCasm :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r Casm.Result
storedCoreToCasm = local (set entryPointFieldSize cairoFieldSize) . storedCoreToTree Core.CheckCairo >=> treeToCasm
@ -283,6 +292,12 @@ regToMiniC tab = do
e <- ask
return $ C.fromReg (Backend.getLimits (getEntryPointTarget e) (e ^. entryPointDebug)) tab'
regToRust :: (Member (Reader EntryPoint) r) => Reg.InfoTable -> Sem r Rust.Result
regToRust tab = do
tab' <- Reg.toRust tab
e <- ask
return $ Rust.fromReg (Backend.getLimits (getEntryPointTarget e) (e ^. entryPointDebug)) tab'
regToCasm :: Reg.InfoTable -> Sem r Casm.Result
regToCasm = Reg.toCasm >=> return . Casm.fromReg

View File

@ -14,6 +14,7 @@ data TransformationId
data PipelineId
= PipelineCasm
| PipelineC
| PipelineRust
deriving stock (Data, Bounded, Enum)
type TransformationLikeId = TransformationLikeId' TransformationId PipelineId
@ -21,6 +22,9 @@ type TransformationLikeId = TransformationLikeId' TransformationId PipelineId
toCTransformations :: [TransformationId]
toCTransformations = [Cleanup]
toRustTransformations :: [TransformationId]
toRustTransformations = [Cleanup]
toCasmTransformations :: [TransformationId]
toCasmTransformations = [Cleanup, SSA]
@ -36,9 +40,11 @@ instance PipelineId' TransformationId PipelineId where
pipelineText :: PipelineId -> Text
pipelineText = \case
PipelineC -> strCPipeline
PipelineRust -> strRustPipeline
PipelineCasm -> strCasmPipeline
pipeline :: PipelineId -> [TransformationId]
pipeline = \case
PipelineC -> toCTransformations
PipelineRust -> toRustTransformations
PipelineCasm -> toCasmTransformations

View File

@ -5,6 +5,9 @@ import Juvix.Prelude
strCPipeline :: Text
strCPipeline = "pipeline-c"
strRustPipeline :: Text
strRustPipeline = "pipeline-rust"
strCasmPipeline :: Text
strCasmPipeline = "pipeline-casm"

View File

@ -14,6 +14,10 @@ import Juvix.Compiler.Reg.Translation.Blocks.FromReg qualified as Blocks
toC :: InfoTable -> Sem r InfoTable
toC = applyTransformations toCTransformations
-- | Perform transformations on JuvixReg necessary before the translation to Rust
toRust :: InfoTable -> Sem r InfoTable
toRust = applyTransformations toRustTransformations
-- | Perform transformations on JuvixReg necessary before the translation to
-- Cairo assembly
toCasm :: InfoTable -> Sem r Blocks.InfoTable

View File

@ -22,6 +22,7 @@ data FileExt
| FileExtPlonk
| FileExtHalo
| FileExtLisp
| FileExtRust
| FileExtC
| FileExtMarkdown
| FileExtHtml
@ -85,6 +86,9 @@ markdownFileExt = ".md"
cFileExt :: (IsString a) => a
cFileExt = ".c"
rustFileExt :: (IsString a) => a
rustFileExt = ".rs"
cssFileExt :: (IsString a) => a
cssFileExt = ".css"
@ -110,6 +114,7 @@ fileExtToIsString = \case
FileExtPlonk -> plonkFileExt
FileExtHalo -> haloFileExt
FileExtLisp -> lispFileExt
FileExtRust -> rustFileExt
FileExtC -> cFileExt
FileExtMarkdown -> markdownFileExt
FileExtHtml -> htmlFileExt
@ -135,6 +140,7 @@ toMetavar = \case
FileExtPlonk -> "PLONK_FILE"
FileExtHalo -> "HALO_FILE"
FileExtLisp -> "LISP_FILE"
FileExtRust -> "RUST_FILE"
FileExtC -> "C_FILE"
FileExtMarkdown -> "MARKDOWN_FILE"
FileExtHtml -> "HTML_FILE"
@ -194,6 +200,9 @@ isLispFile = (== Just lispFileExt) . fileExtension
isMarkdownFile :: Path b File -> Bool
isMarkdownFile = (== Just markdownFileExt) . fileExtension
isRustFile :: Path b File -> Bool
isRustFile = (== Just rustFileExt) . fileExtension
isCFile :: Path b File -> Bool
isCFile = (== Just cFileExt) . fileExtension
@ -221,6 +230,7 @@ toFileExt p
| isPlonkFile p = Just FileExtPlonk
| isHaloFile p = Just FileExtHalo
| isLispFile p = Just FileExtLisp
| isRustFile p = Just FileExtRust
| isCFile p = Just FileExtC
| isMarkdownFile p = Just FileExtMarkdown
| isHtmlFile p = Just FileExtHtml

View File

@ -1054,3 +1054,45 @@ cairoEcPoint = "ec_point"
cairoMkEcPoint :: (IsString s) => s
cairoMkEcPoint = "mkEcPoint"
rustFn :: (IsString s) => s
rustFn = "fn"
rustIf :: (IsString s) => s
rustIf = "if"
rustElse :: (IsString s) => s
rustElse = "else"
rustMatch :: (IsString s) => s
rustMatch = "match"
rustLoop :: (IsString s) => s
rustLoop = "loop"
rustLet :: (IsString s) => s
rustLet = "let"
rustConst :: (IsString s) => s
rustConst = "const"
rustMut :: (IsString s) => s
rustMut = "mut"
rustVec :: (IsString s) => s
rustVec = "vec!"
rustVector :: (IsString s) => s
rustVector = "Vec"
rustWord :: (IsString s) => s
rustWord = "Word"
rustMemory :: (IsString s) => s
rustMemory = "Memory"
rustContinue :: (IsString s) => s
rustContinue = "continue"
rustReturn :: (IsString s) => s
rustReturn = "return"

View File

@ -21,6 +21,7 @@ import Juvix.Data.Effect.TaggedLock
import Juvix.Extra.Paths hiding (rootBuildDir)
import Juvix.Prelude hiding (assert)
import Juvix.Prelude.Env
import System.Process qualified as P
import Test.Tasty
import Test.Tasty.HUnit hiding (assertFailure)
import Test.Tasty.HUnit qualified as HUnit
@ -114,3 +115,36 @@ testRunIOEitherTermination entry =
assertFailure :: (MonadIO m) => String -> m a
assertFailure = liftIO . HUnit.assertFailure
-- | The same as `P.readProcess` but instead of inheriting `stderr` redirects it
-- to the child's `stdout`.
readProcess :: FilePath -> [String] -> Text -> IO Text
readProcess = readProcessCwd' Nothing
readProcessCwd :: FilePath -> FilePath -> [String] -> Text -> IO Text
readProcessCwd cwd = readProcessCwd' (Just cwd)
readProcessCwd' :: Maybe FilePath -> FilePath -> [String] -> Text -> IO Text
readProcessCwd' mcwd cmd args stdinText =
withTempDir'
( \dirPath -> do
(_, hin) <- openTempFile dirPath "stdin"
(_, hout) <- openTempFile dirPath "stdout"
hPutStr hin stdinText
hSeek hin AbsoluteSeek 0
(_, _, _, ph) <-
P.createProcess_
"readProcess"
(P.proc cmd args)
{ P.std_in = P.UseHandle hin,
P.std_out = P.UseHandle hout,
P.std_err = P.UseHandle hout,
P.cwd = mcwd
}
P.waitForProcess ph
hSeek hout AbsoluteSeek 0
r <- hGetContents hout
hClose hin
hClose hout
return r
)

View File

@ -12,12 +12,11 @@ import Juvix.Compiler.Casm.Validate
import Juvix.Data.Field
import Juvix.Data.PPOutput
import Juvix.Parser.Error
import Runtime.Base qualified as R
casmRunVM' :: Path Abs Dir -> Path Abs File -> Maybe (Path Abs File) -> IO Text
casmRunVM' dirPath outputFile inputFile = do
let args = maybe [] (\f -> ["--program_input", toFilePath f]) inputFile
R.readProcessCwd (toFilePath dirPath) "run_cairo_vm.sh" (toFilePath outputFile : args) ""
readProcessCwd (toFilePath dirPath) "run_cairo_vm.sh" (toFilePath outputFile : args) ""
casmRunVM :: LabelInfo -> Code -> [Builtin] -> Maybe (Path Abs File) -> Path Abs File -> (String -> IO ()) -> Assertion
casmRunVM labi instrs blts inputFile expectedFile step = do

View File

@ -19,6 +19,7 @@ import Reg qualified
import Repl qualified
import Resolver qualified
import Runtime qualified
import Rust qualified
import Scope qualified
import Termination qualified
import Tree qualified
@ -38,6 +39,7 @@ slowTests =
Internal.allTests,
Compilation.allTests,
Examples.allTests,
Rust.allTests,
Casm.allTests,
VampIR.allTests,
Anoma.allTests,

View File

@ -23,39 +23,6 @@ clangCompile mkClangArgs inputFile outputFile execute step =
execute outputFile'
)
-- | The same as `P.readProcess` but instead of inheriting `stderr` redirects it
-- to the child's `stdout`.
readProcess :: FilePath -> [String] -> Text -> IO Text
readProcess = readProcessCwd' Nothing
readProcessCwd :: FilePath -> FilePath -> [String] -> Text -> IO Text
readProcessCwd cwd = readProcessCwd' (Just cwd)
readProcessCwd' :: Maybe FilePath -> FilePath -> [String] -> Text -> IO Text
readProcessCwd' mcwd cmd args stdinText =
withTempDir'
( \dirPath -> do
(_, hin) <- openTempFile dirPath "stdin"
(_, hout) <- openTempFile dirPath "stdout"
hPutStr hin stdinText
hSeek hin AbsoluteSeek 0
(_, _, _, ph) <-
P.createProcess_
"readProcess"
(P.proc cmd args)
{ P.std_in = P.UseHandle hin,
P.std_out = P.UseHandle hout,
P.std_err = P.UseHandle hout,
P.cwd = mcwd
}
P.waitForProcess ph
hSeek hout AbsoluteSeek 0
r <- hGetContents hout
hClose hin
hClose hout
return r
)
clangAssertion :: Int -> Path Abs File -> Path Abs File -> Text -> ((String -> IO ()) -> Assertion)
clangAssertion optLevel inputFile expectedFile stdinText step = do
step "Check clang and wasmer are on path"

7
test/Rust.hs Normal file
View File

@ -0,0 +1,7 @@
module Rust where
import Base
import Rust.Compilation qualified as Compilation
allTests :: TestTree
allTests = testGroup "Juvix to Rust tests" [Compilation.allTests]

7
test/Rust/Compilation.hs Normal file
View File

@ -0,0 +1,7 @@
module Rust.Compilation where
import Base
import Rust.Compilation.Positive qualified as P
allTests :: TestTree
allTests = testGroup "Juvix to native Rust compilation tests" [P.allTests, P.allTestsNoOptimize]

View File

@ -0,0 +1,81 @@
module Rust.Compilation.Base where
import Base
import Data.FileEmbed
import Juvix.Compiler.Backend.Rust.Data.Result
import Juvix.Compiler.Backend.Rust.Pretty
import Juvix.Compiler.Core qualified as Core
import System.Process qualified as P
compileAssertion ::
Path Abs Dir ->
Int ->
Path Abs File ->
Path Abs File ->
(String -> IO ()) ->
Assertion
compileAssertion root' optLevel mainFile expectedFile step = do
step "Translate to JuvixCore"
entryPoint <- testDefaultEntryPointIO root' mainFile
PipelineResult {..} <- snd <$> testRunIO entryPoint upToStoredCore
step "Translate to Rust"
case run $ runError @JuvixError $ runReader entryPoint $ storedCoreToRust (_pipelineResult ^. Core.coreResultModule) of
Left err -> assertFailure (prettyString (fromJuvixError @GenericError err))
Right Result {..} -> do
withTempDir'
( \dirPath -> do
let inputFile = dirPath <//> $(mkRelFile "Program.rs")
writeFileEnsureLn inputFile _resultRustCode
step "Check rustc is on path"
assertCmdExists $(mkRelFile "rustc")
expected <- readFile expectedFile
let executeNative :: Path Abs File -> IO Text
executeNative outputFile = readProcess (toFilePath outputFile) [] ""
step "Compile Rust to native code"
actualNative <- rustcCompile (nativeArgs optLevel) inputFile $(mkRelFile "Program") executeNative step
step "Compare expected and actual program output"
assertEqDiffText ("check: native output = " <> toFilePath expectedFile) actualNative expected
)
rustcCompile ::
(Path Abs File -> Path Abs File -> [String]) ->
Path Abs File ->
Path Rel File ->
(Path Abs File -> IO Text) ->
(String -> IO ()) ->
IO Text
rustcCompile mkRustcArgs inputFile outputFile execute step =
withTempDir'
( \dirPath -> do
let outputFile' = dirPath <//> outputFile
step "Rust compilation"
P.callProcess
"rustc"
(mkRustcArgs outputFile' inputFile)
step "Execution"
execute outputFile'
)
nativeArgs :: Int -> Path Abs File -> Path Abs File -> [String]
nativeArgs optLevel outputFile inputFile =
[ "-o",
toFilePath outputFile,
"-C",
"opt-level="
<> show optLevel,
"-L",
juvixLibraryDir,
toFilePath inputFile
]
where
juvixLibraryDir :: FilePath
juvixLibraryDir =
if
| optLevel > 0 ->
$(makeRelativeToProject "runtime/rust/target/release" >>= strToExp)
| otherwise ->
$(makeRelativeToProject "runtime/rust/target/debug" >>= strToExp)

View File

@ -0,0 +1,348 @@
module Rust.Compilation.Positive where
import Base
import Data.HashSet qualified as HashSet
import Rust.Compilation.Base
data PosTest = PosTest
{ _name :: String,
_dir :: Path Abs Dir,
_file :: Path Abs File,
_expectedFile :: Path Abs File
}
makeLenses ''PosTest
root :: Path Abs Dir
root = relToProject $(mkRelDir "tests/Rust/Compilation/positive/")
toTestDescr :: Int -> PosTest -> TestDescr
toTestDescr optLevel PosTest {..} =
let tRoot = _dir
file' = _file
expected' = _expectedFile
in TestDescr
{ _testName = _name,
_testRoot = tRoot,
_testAssertion = Steps $ compileAssertion _dir optLevel file' expected'
}
allTests :: TestTree
allTests =
testGroup
"Juvix to native Rust positive tests"
(map (mkTest . toTestDescr 3) tests)
allTestsNoOptimize :: TestTree
allTestsNoOptimize =
testGroup
"Juvix to native Rust positive tests (no optimization)"
(map (mkTest . toTestDescr 0) tests)
posTest :: String -> Path Rel Dir -> Path Rel File -> Path Rel File -> PosTest
posTest _name rdir rfile routfile =
let _dir = root <//> rdir
_file = _dir <//> rfile
_expectedFile = root <//> routfile
in PosTest {..}
isIgnored :: PosTest -> Bool
isIgnored t = HashSet.member (t ^. name) ignored
ignored :: HashSet String
ignored =
HashSet.fromList
[ -- strings not supported (Partial trait)
"Test026: Functional queues"
]
tests :: [PosTest]
tests =
filter
(not . isIgnored)
[ posTest
"Test001: Arithmetic operators"
$(mkRelDir ".")
$(mkRelFile "test001.juvix")
$(mkRelFile "out/test001.out"),
posTest
"Test002: Arithmetic operators inside lambdas"
$(mkRelDir ".")
$(mkRelFile "test002.juvix")
$(mkRelFile "out/test002.out"),
posTest
"Test003: Integer arithmetic"
$(mkRelDir ".")
$(mkRelFile "test003.juvix")
$(mkRelFile "out/test003.out"),
posTest
"Test005: Higher-order functions"
$(mkRelDir ".")
$(mkRelFile "test005.juvix")
$(mkRelFile "out/test005.out"),
posTest
"Test006: If-then-else and lazy boolean operators"
$(mkRelDir ".")
$(mkRelFile "test006.juvix")
$(mkRelFile "out/test006.out"),
posTest
"Test007: Pattern matching and lambda-case"
$(mkRelDir ".")
$(mkRelFile "test007.juvix")
$(mkRelFile "out/test007.out"),
posTest
"Test008: Recursion"
$(mkRelDir ".")
$(mkRelFile "test008.juvix")
$(mkRelFile "out/test008.out"),
posTest
"Test009: Tail recursion"
$(mkRelDir ".")
$(mkRelFile "test009.juvix")
$(mkRelFile "out/test009.out"),
posTest
"Test010: Let"
$(mkRelDir ".")
$(mkRelFile "test010.juvix")
$(mkRelFile "out/test010.out"),
posTest
"Test013: Functions returning functions with variable capture"
$(mkRelDir ".")
$(mkRelFile "test013.juvix")
$(mkRelFile "out/test013.out"),
posTest
"Test014: Arithmetic"
$(mkRelDir ".")
$(mkRelFile "test014.juvix")
$(mkRelFile "out/test014.out"),
posTest
"Test015: Local functions with free variables"
$(mkRelDir ".")
$(mkRelFile "test015.juvix")
$(mkRelFile "out/test015.out"),
posTest
"Test016: Recursion through higher-order functions"
$(mkRelDir ".")
$(mkRelFile "test016.juvix")
$(mkRelFile "out/test016.out"),
posTest
"Test017: Tail recursion through higher-order functions"
$(mkRelDir ".")
$(mkRelFile "test017.juvix")
$(mkRelFile "out/test017.out"),
posTest
"Test018: Higher-order functions and recursion"
$(mkRelDir ".")
$(mkRelFile "test018.juvix")
$(mkRelFile "out/test018.out"),
posTest
"Test019: Self-application"
$(mkRelDir ".")
$(mkRelFile "test019.juvix")
$(mkRelFile "out/test019.out"),
posTest
"Test020: Recursive functions: McCarthy's 91 function, subtraction by increments"
$(mkRelDir ".")
$(mkRelFile "test020.juvix")
$(mkRelFile "out/test020.out"),
posTest
"Test021: Fast exponentiation"
$(mkRelDir ".")
$(mkRelFile "test021.juvix")
$(mkRelFile "out/test021.out"),
posTest
"Test022: Lists"
$(mkRelDir ".")
$(mkRelFile "test022.juvix")
$(mkRelFile "out/test022.out"),
posTest
"Test023: Mutual recursion"
$(mkRelDir ".")
$(mkRelFile "test023.juvix")
$(mkRelFile "out/test023.out"),
posTest
"Test024: Nested binders with variable capture"
$(mkRelDir ".")
$(mkRelFile "test024.juvix")
$(mkRelFile "out/test024.out"),
posTest
"Test025: Euclid's algorithm"
$(mkRelDir ".")
$(mkRelFile "test025.juvix")
$(mkRelFile "out/test025.out"),
posTest
"Test026: Functional queues"
$(mkRelDir ".")
$(mkRelFile "test026.juvix")
$(mkRelFile "out/test026.out"),
posTest
"Test028: Streams without memoization"
$(mkRelDir ".")
$(mkRelFile "test028.juvix")
$(mkRelFile "out/test028.out"),
posTest
"Test029: Ackermann function"
$(mkRelDir ".")
$(mkRelFile "test029.juvix")
$(mkRelFile "out/test029.out"),
posTest
"Test030: Ackermann function (higher-order definition)"
$(mkRelDir ".")
$(mkRelFile "test030.juvix")
$(mkRelFile "out/test030.out"),
posTest
"Test032: Merge sort"
$(mkRelDir ".")
$(mkRelFile "test032.juvix")
$(mkRelFile "out/test032.out"),
posTest
"Test033: Eta-expansion of builtins and constructors"
$(mkRelDir ".")
$(mkRelFile "test033.juvix")
$(mkRelFile "out/test033.out"),
posTest
"Test034: Recursive let"
$(mkRelDir ".")
$(mkRelFile "test034.juvix")
$(mkRelFile "out/test034.out"),
posTest
"Test035: Pattern matching"
$(mkRelDir ".")
$(mkRelFile "test035.juvix")
$(mkRelFile "out/test035.out"),
posTest
"Test036: Eta-expansion"
$(mkRelDir ".")
$(mkRelFile "test036.juvix")
$(mkRelFile "out/test036.out"),
posTest
"Test037: Applications with lets and cases in function position"
$(mkRelDir ".")
$(mkRelFile "test037.juvix")
$(mkRelFile "out/test037.out"),
posTest
"Test038: Simple case expression"
$(mkRelDir ".")
$(mkRelFile "test038.juvix")
$(mkRelFile "out/test038.out"),
posTest
"Test039: Mutually recursive let expression"
$(mkRelDir ".")
$(mkRelFile "test039.juvix")
$(mkRelFile "out/test039.out"),
posTest
"Test040: Pattern matching nullary constructor"
$(mkRelDir ".")
$(mkRelFile "test040.juvix")
$(mkRelFile "out/test040.out"),
posTest
"Test045: Implicit builtin bool"
$(mkRelDir ".")
$(mkRelFile "test045.juvix")
$(mkRelFile "out/test045.out"),
posTest
"Test046: Polymorphic type arguments"
$(mkRelDir ".")
$(mkRelFile "test046.juvix")
$(mkRelFile "out/test046.out"),
posTest
"Test047: Local Modules"
$(mkRelDir ".")
$(mkRelFile "test047.juvix")
$(mkRelFile "out/test047.out"),
posTest
"Test050: Pattern matching with integers"
$(mkRelDir ".")
$(mkRelFile "test050.juvix")
$(mkRelFile "out/test050.out"),
posTest
"Test053: Inlining"
$(mkRelDir ".")
$(mkRelFile "test053.juvix")
$(mkRelFile "out/test053.out"),
posTest
"Test054: Iterators"
$(mkRelDir ".")
$(mkRelFile "test054.juvix")
$(mkRelFile "out/test054.out"),
posTest
"Test056: Argument specialization"
$(mkRelDir ".")
$(mkRelFile "test056.juvix")
$(mkRelFile "out/test056.out"),
posTest
"Test057: Case folding"
$(mkRelDir ".")
$(mkRelFile "test057.juvix")
$(mkRelFile "out/test057.out"),
posTest
"Test058: Ranges"
$(mkRelDir ".")
$(mkRelFile "test058.juvix")
$(mkRelFile "out/test058.out"),
posTest
"Test059: Builtin list"
$(mkRelDir ".")
$(mkRelFile "test059.juvix")
$(mkRelFile "out/test059.out"),
posTest
"Test060: Record update"
$(mkRelDir ".")
$(mkRelFile "test060.juvix")
$(mkRelFile "out/test060.out"),
posTest
"Test062: Overapplication"
$(mkRelDir ".")
$(mkRelFile "test062.juvix")
$(mkRelFile "out/test062.out"),
posTest
"Test064: Constant folding"
$(mkRelDir ".")
$(mkRelFile "test064.juvix")
$(mkRelFile "out/test064.out"),
posTest
"Test065: Arithmetic simplification"
$(mkRelDir ".")
$(mkRelFile "test065.juvix")
$(mkRelFile "out/test065.out"),
posTest
"Test066: Import function with a function call in default argument"
$(mkRelDir "test066")
$(mkRelFile "M.juvix")
$(mkRelFile "out/test066.out"),
posTest
"Test067: Dependent default values inserted during translation FromConcrete"
$(mkRelDir ".")
$(mkRelFile "test067.juvix")
$(mkRelFile "out/test067.out"),
posTest
"Test068: Dependent default values inserted in the arity checker"
$(mkRelDir ".")
$(mkRelFile "test068.juvix")
$(mkRelFile "out/test068.out"),
posTest
"Test069: Dependent default values for Ord trait"
$(mkRelDir ".")
$(mkRelFile "test069.juvix")
$(mkRelFile "out/test069.out"),
posTest
"Test070: Nested default values and named arguments"
$(mkRelDir ".")
$(mkRelFile "test070.juvix")
$(mkRelFile "out/test070.out"),
posTest
"Test071: Named application (Ord instance with default cmp)"
$(mkRelDir ".")
$(mkRelFile "test071.juvix")
$(mkRelFile "out/test071.out"),
posTest
"Test072: Monad transformers (ReaderT + StateT + Identity)"
$(mkRelDir "test072")
$(mkRelFile "ReaderT.juvix")
$(mkRelFile "out/test072.out"),
posTest
"Test073: Import and use a syntax alias"
$(mkRelDir "test073")
$(mkRelFile "test073.juvix")
$(mkRelFile "out/test073.out")
]

View File

@ -0,0 +1 @@
11

View File

@ -0,0 +1 @@
11

View File

@ -0,0 +1 @@
11

View File

@ -0,0 +1 @@
6

View File

@ -0,0 +1 @@
3

View File

@ -0,0 +1 @@
6

View File

@ -0,0 +1 @@
50005000

View File

@ -0,0 +1 @@
532635520

View File

@ -0,0 +1 @@
32

View File

@ -0,0 +1 @@
8

View File

@ -0,0 +1 @@
92

View File

@ -0,0 +1 @@
771

View File

@ -0,0 +1 @@
55

View File

@ -0,0 +1 @@
50005000

View File

@ -0,0 +1 @@
11

View File

@ -0,0 +1 @@
7

View File

@ -0,0 +1 @@
4876

View File

@ -0,0 +1 @@
80320

View File

@ -0,0 +1 @@
100010000

View File

@ -0,0 +1 @@
6386010

View File

@ -0,0 +1 @@
6688

View File

@ -0,0 +1 @@
87

View File

@ -0,0 +1 @@
5050

View File

@ -0,0 +1 @@
2040

View File

@ -0,0 +1 @@
78

View File

@ -0,0 +1 @@
2247

View File

@ -0,0 +1 @@
195

View File

@ -0,0 +1 @@
31

View File

@ -0,0 +1 @@
501527

View File

@ -0,0 +1 @@
34422

View File

@ -0,0 +1 @@
18

View File

@ -0,0 +1 @@
9

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
4

View File

@ -0,0 +1 @@
7

View File

@ -0,0 +1 @@
660

View File

@ -0,0 +1 @@
11

View File

@ -0,0 +1 @@
21

View File

@ -0,0 +1 @@
189

View File

@ -0,0 +1 @@
69

View File

@ -0,0 +1 @@
8

View File

@ -0,0 +1 @@
7550

View File

@ -0,0 +1 @@
11

View File

@ -0,0 +1 @@
42

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
37

View File

@ -0,0 +1 @@
42

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1 @@
30

View File

@ -0,0 +1 @@
30

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
1463

View File

@ -0,0 +1 @@
1528

View File

@ -0,0 +1 @@
10

Some files were not shown because too many files have changed in this diff Show More