1
1
mirror of https://github.com/anoma/juvix.git synced 2025-01-04 05:33:27 +03:00

Support compilation of Anoma transactions in nockma backend (#2693)

When we first implemented the Nockma backend we wrongly assumed that the
only entry point for Juvix compiled Nockma modules would be the main
function. Using this assumption we could add a setup step in the main
function that put the Anoma stdlib and compiled functions from the Juvix
module in a static place in the Nockma subject. References to the Anoma
stdlib and functions in the module could then be resolved statically.

However, one of the use cases for Juvix -> Nockma compilation is for
Anoma to run logic functions that are fields of a transaction. So the
user writes a Juvix program with main function that returns a
transaction. The result of the main function is passed to Anoma. When
Anoma calls the logic function on a field of the transaction, the setup
part of the main function is not run so the subject is not in the
required state. In fact, the logic function is not even callable by
Anoma because non-main functions in the Juvix module use a calling
convention that assumes the subject has a particular shape.

This PR solves the problem by making all functions in the Juvix module
use the Anoma calling convention. We make all compiled closures
(including, for example, the logic functions stored on resources in a
transaction) self contained, i.e they contain the functions library and
anoma standard library.

Modules that contain many closures produce large nockma output files
which slows down the evaluator. This will need to be fixed in the future
either with Nockma compression ([jam
serialization](https://developers.urbit.org/reference/hoon/stdlib/2p))
or otherwise. But it does not block the compilation and execution of
Anoma transactions.

Other fixes / additions:

* Extra tracing. You can now annotate output cells with a tag that will
be displayed in the output
* Unittests for listToTuple, appendRights helper functions
* Fixes for the nockma parser when parsing 'pretty nockma', specifically
stdlib calls, tags and functions_library atom.
* Adds `juvix dev nock run` command that can run a program output with
the `anoma` target.
* Remove the `nockma` target. As described above we always use the Anoma
calling convention so there's no need for a separate target for the
'juvix calling convention'
* Adds a `--profile` flag to `juvix dev nock run` which outputs a count
of Nockma ops used in the evaluation
* In tests we no longer serialise the compiled program to force full
evaluation of the compiled code. We added a negative test to check that
strings are not allowed in Nockma/Anoma programs,

it is output in a file `OUTPUT.profile` and has the following form:

```
quote : 15077
apply : 0
isCell : 0
suc : 0
= : 4517
if : 5086
seq : 5086
push : 0
call : 4896
replace : 1
hint : 8
scry : 0
trace : 0
```

---------

Co-authored-by: Jan Mas Rovira <janmasrovira@gmail.com>
This commit is contained in:
Paul Cadman 2024-03-22 22:03:38 +00:00 committed by GitHub
parent 2e006421a5
commit b981405e45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 750 additions and 352 deletions

View File

@ -27,7 +27,6 @@ runCommand opts@CompileOptions {..} = do
TargetTree -> Compile.runTreePipeline arg
TargetAsm -> Compile.runAsmPipeline arg
TargetReg -> Compile.runRegPipeline arg
TargetNockma -> Compile.runNockmaPipeline arg
TargetAnoma -> Compile.runAnomaPipeline arg
TargetCasm -> Compile.runCasmPipeline arg

View File

@ -70,7 +70,6 @@ runCommand opts = do
TargetNative64 -> return Backend.TargetCNative64
TargetReg -> return Backend.TargetReg
TargetCasm -> return Backend.TargetCairo
TargetNockma -> err "Nockma"
TargetAnoma -> err "Anoma"
TargetTree -> err "JuvixTree"
TargetGeb -> err "GEB"

View File

@ -21,7 +21,6 @@ runCommand opts = do
TargetAsm -> runAsmPipeline arg
TargetReg -> runRegPipeline arg
TargetTree -> runTreePipeline arg
TargetNockma -> runNockmaPipeline arg
TargetAnoma -> runAnomaPipeline arg
TargetCasm -> runCasmPipeline arg
where

View File

@ -2,6 +2,7 @@ module Commands.Dev.Core.Compile.Base where
import Commands.Base
import Commands.Dev.Core.Compile.Options
import Commands.Dev.Tree.Compile.Base (outputAnomaResult)
import Commands.Extra.Compile qualified as Compile
import Juvix.Compiler.Asm.Pretty qualified as Asm
import Juvix.Compiler.Backend qualified as Backend
@ -12,7 +13,6 @@ import Juvix.Compiler.Casm.Data.Result qualified as Casm
import Juvix.Compiler.Casm.Pretty qualified as Casm
import Juvix.Compiler.Core.Data.Module qualified as Core
import Juvix.Compiler.Core.Data.TransformationId qualified as Core
import Juvix.Compiler.Nockma.Pretty qualified as Nockma
import Juvix.Compiler.Reg.Pretty qualified as Reg
import Juvix.Compiler.Tree.Pretty qualified as Tree
import Juvix.Prelude.Pretty
@ -46,7 +46,6 @@ getEntry PipelineArg {..} = do
TargetAsm -> Backend.TargetAsm
TargetReg -> Backend.TargetReg
TargetTree -> Backend.TargetTree
TargetNockma -> Backend.TargetNockma
TargetAnoma -> Backend.TargetAnoma
TargetCasm -> Backend.TargetCairo
@ -147,19 +146,6 @@ runTreePipeline pa@PipelineArg {..} = do
let code = Tree.ppPrint tab' tab'
writeFileEnsureLn treeFile code
runNockmaPipeline :: (Members '[EmbedIO, App, TaggedLock] r) => PipelineArg -> Sem r ()
runNockmaPipeline pa@PipelineArg {..} = do
entryPoint <- getEntry pa
nockmaFile <- Compile.outputFile _pipelineArgOptions _pipelineArgFile
r <-
runReader entryPoint
. runError @JuvixError
. coreToNockma
$ _pipelineArgModule
tab' <- getRight r
let code = Nockma.ppSerialize tab'
writeFileEnsureLn nockmaFile code
runAnomaPipeline :: (Members '[EmbedIO, App, TaggedLock] r) => PipelineArg -> Sem r ()
runAnomaPipeline pa@PipelineArg {..} = do
entryPoint <- getEntry pa
@ -169,9 +155,8 @@ runAnomaPipeline pa@PipelineArg {..} = do
. runError @JuvixError
. coreToAnoma
$ _pipelineArgModule
tab' <- getRight r
let code = Nockma.ppSerialize tab'
writeFileEnsureLn nockmaFile code
res <- getRight r
outputAnomaResult nockmaFile res
runCasmPipeline :: (Members '[EmbedIO, App, TaggedLock] r) => PipelineArg -> Sem r ()
runCasmPipeline pa@PipelineArg {..} = do

View File

@ -5,9 +5,11 @@ import Commands.Dev.Nockma.Eval as Eval
import Commands.Dev.Nockma.Format as Format
import Commands.Dev.Nockma.Options
import Commands.Dev.Nockma.Repl as Repl
import Commands.Dev.Nockma.Run as Run
runCommand :: forall r. (Members '[EmbedIO, App] r) => NockmaCommand -> Sem r ()
runCommand = \case
NockmaRepl opts -> Repl.runCommand opts
NockmaEval opts -> Eval.runCommand opts
NockmaFormat opts -> Format.runCommand opts
NockmaRun opts -> Run.runCommand opts

View File

@ -3,7 +3,7 @@ module Commands.Dev.Nockma.Eval where
import Commands.Base hiding (Atom)
import Commands.Dev.Nockma.Eval.Options
import Juvix.Compiler.Nockma.EvalCompiled
import Juvix.Compiler.Nockma.Evaluator.Options
import Juvix.Compiler.Nockma.Evaluator
import Juvix.Compiler.Nockma.Pretty
import Juvix.Compiler.Nockma.Translation.FromSource qualified as Nockma
@ -14,11 +14,14 @@ runCommand opts = do
case parsedTerm of
Left err -> exitJuvixError (JuvixError err)
Right (TermCell c) -> do
res <-
runReader defaultEvalOptions
(counts, res) <-
runOpCounts
. runReader defaultEvalOptions
. runOutputSem @(Term Natural) (say . ppTrace)
$ evalCompiledNock' (c ^. cellLeft) (c ^. cellRight)
putStrLn (ppPrint res)
let statsFile = replaceExtension' ".profile" afile
writeFileEnsureLn statsFile (prettyText counts)
Right TermAtom {} -> exitFailMsg "Expected nockma input to be a cell"
where
file :: AppPath File

View File

@ -2,8 +2,9 @@ module Commands.Dev.Nockma.Eval.Options where
import CommonOptions
newtype NockmaEvalOptions = NockmaEvalOptions
{ _nockmaEvalFile :: AppPath File
data NockmaEvalOptions = NockmaEvalOptions
{ _nockmaEvalFile :: AppPath File,
_nockmaEvalProfile :: Bool
}
deriving stock (Data)
@ -12,4 +13,9 @@ makeLenses ''NockmaEvalOptions
parseNockmaEvalOptions :: Parser NockmaEvalOptions
parseNockmaEvalOptions = do
_nockmaEvalFile <- parseInputFile FileExtNockma
_nockmaEvalProfile <-
switch
( long "profile"
<> help "Report evaluator profiling statistics"
)
pure NockmaEvalOptions {..}

View File

@ -3,12 +3,14 @@ module Commands.Dev.Nockma.Options where
import Commands.Dev.Nockma.Eval.Options
import Commands.Dev.Nockma.Format.Options
import Commands.Dev.Nockma.Repl.Options
import Commands.Dev.Nockma.Run.Options
import CommonOptions
data NockmaCommand
= NockmaRepl NockmaReplOptions
| NockmaEval NockmaEvalOptions
| NockmaFormat NockmaFormatOptions
| NockmaRun NockmaRunOptions
deriving stock (Data)
parseNockmaCommand :: Parser NockmaCommand
@ -17,9 +19,19 @@ parseNockmaCommand =
mconcat
[ commandRepl,
commandFromAsm,
commandFormat
commandFormat,
commandRun
]
where
commandRun :: Mod CommandFields NockmaCommand
commandRun = command "run" runInfo
where
runInfo :: ParserInfo NockmaCommand
runInfo =
info
(NockmaRun <$> parseNockmaRunOptions)
(progDesc "Run an Anoma program. It should be used with artefacts obtained from compilation with the anoma target.")
commandFromAsm :: Mod CommandFields NockmaCommand
commandFromAsm = command "eval" fromAsmInfo
where

View File

@ -0,0 +1,37 @@
module Commands.Dev.Nockma.Run where
import Commands.Base hiding (Atom)
import Commands.Dev.Nockma.Run.Options
import Juvix.Compiler.Nockma.Anoma
import Juvix.Compiler.Nockma.EvalCompiled
import Juvix.Compiler.Nockma.Evaluator
import Juvix.Compiler.Nockma.Pretty
import Juvix.Compiler.Nockma.Translation.FromSource qualified as Nockma
import Juvix.Parser.Error
runCommand :: forall r. (Members '[EmbedIO, App] r) => NockmaRunOptions -> Sem r ()
runCommand opts = do
afile <- fromAppPathFile inputFile
argsFile <- mapM fromAppPathFile (opts ^. nockmaRunArgs)
parsedArgs <- mapM (Nockma.parseTermFile >=> checkParsed) argsFile
parsedTerm <- Nockma.parseTermFile afile >>= checkParsed
case parsedTerm of
t@(TermCell {}) -> do
let formula = anomaCallTuple parsedArgs
(counts, res) <-
runOpCounts
. runReader defaultEvalOptions
. runOutputSem @(Term Natural) (say . ppTrace)
$ evalCompiledNock' t formula
putStrLn (ppPrint res)
let statsFile = replaceExtension' ".profile" afile
writeFileEnsureLn statsFile (prettyText counts)
TermAtom {} -> exitFailMsg "Expected nockma input to be a cell"
where
inputFile :: AppPath File
inputFile = opts ^. nockmaRunFile
checkParsed :: Either MegaparsecError (Term Natural) -> Sem r (Term Natural)
checkParsed = \case
Left err -> exitJuvixError (JuvixError err)
Right tm -> return tm

View File

@ -0,0 +1,32 @@
module Commands.Dev.Nockma.Run.Options where
import CommonOptions
data NockmaRunOptions = NockmaRunOptions
{ _nockmaRunFile :: AppPath File,
_nockmaRunProfile :: Bool,
_nockmaRunArgs :: Maybe (AppPath File)
}
deriving stock (Data)
makeLenses ''NockmaRunOptions
parseNockmaRunOptions :: Parser NockmaRunOptions
parseNockmaRunOptions = do
_nockmaRunFile <- parseInputFile FileExtNockma
_nockmaRunArgs <- optional $ do
_pathPath <-
option
somePreFileOpt
( long "args"
<> metavar "ARGS_FILE"
<> help "Path to file containing args"
<> action "file"
)
pure AppPath {_pathIsInput = True, ..}
_nockmaRunProfile <-
switch
( long "profile"
<> help "Report evaluator profiling statistics"
)
pure NockmaRunOptions {..}

View File

@ -59,7 +59,6 @@ runCommand opts = do
TargetNative64 -> return Backend.TargetCNative64
TargetCasm -> return Backend.TargetCairo
TargetReg -> err "JuvixReg"
TargetNockma -> err "Nockma"
TargetAnoma -> err "Anoma"
TargetTree -> err "JuvixTree"
TargetGeb -> err "GEB"

View File

@ -20,7 +20,6 @@ runCommand opts = do
TargetAsm -> runAsmPipeline arg
TargetReg -> runRegPipeline arg
TargetTree -> return ()
TargetNockma -> runNockmaPipeline arg
TargetAnoma -> runAnomaPipeline arg
TargetCasm -> runCasmPipeline arg
where

View File

@ -9,6 +9,7 @@ import Juvix.Compiler.Backend.C qualified as C
import Juvix.Compiler.Casm.Data.Result qualified as Casm
import Juvix.Compiler.Casm.Pretty qualified as Casm
import Juvix.Compiler.Nockma.Pretty qualified as Nockma
import Juvix.Compiler.Nockma.Translation.FromTree qualified as Nockma
import Juvix.Compiler.Reg.Pretty qualified as Reg
import Juvix.Compiler.Tree.Data.InfoTable qualified as Tree
import Juvix.Prelude.Pretty
@ -41,7 +42,6 @@ getEntry PipelineArg {..} = do
TargetAsm -> Backend.TargetAsm
TargetReg -> Backend.TargetReg
TargetTree -> Backend.TargetTree
TargetNockma -> Backend.TargetNockma
TargetAnoma -> Backend.TargetAnoma
TargetCasm -> Backend.TargetCairo
@ -104,19 +104,6 @@ runRegPipeline pa@PipelineArg {..} = do
let code = Reg.ppPrint tab' tab'
writeFileEnsureLn regFile code
runNockmaPipeline :: (Members '[EmbedIO, App, TaggedLock] r) => PipelineArg -> Sem r ()
runNockmaPipeline pa@PipelineArg {..} = do
entryPoint <- getEntry pa
nockmaFile <- Compile.outputFile _pipelineArgOptions _pipelineArgFile
r <-
runReader entryPoint
. runError @JuvixError
. treeToNockma
$ _pipelineArgTable
tab' <- getRight r
let code = Nockma.ppSerialize tab'
writeFileEnsureLn nockmaFile code
runAnomaPipeline :: (Members '[EmbedIO, App, TaggedLock] r) => PipelineArg -> Sem r ()
runAnomaPipeline pa@PipelineArg {..} = do
entryPoint <- getEntry pa
@ -126,9 +113,15 @@ runAnomaPipeline pa@PipelineArg {..} = do
. runError @JuvixError
. treeToAnoma
$ _pipelineArgTable
tab' <- getRight r
let code = Nockma.ppSerialize tab'
res <- getRight r
outputAnomaResult nockmaFile res
outputAnomaResult :: (Members '[EmbedIO, App] r) => Path Abs File -> Nockma.AnomaResult -> Sem r ()
outputAnomaResult nockmaFile Nockma.AnomaResult {..} = do
let code = Nockma.ppSerialize _anomaClosure
prettyNockmaFile = replaceExtensions' [".pretty", ".nockma"] nockmaFile
writeFileEnsureLn nockmaFile code
writeFileEnsureLn prettyNockmaFile (Nockma.ppPrint _anomaClosure)
runCasmPipeline :: (Members '[EmbedIO, App, TaggedLock] r) => PipelineArg -> Sem r ()
runCasmPipeline pa@PipelineArg {..} = do

View File

@ -14,8 +14,8 @@ treeSupportedTargets =
TargetNative64,
TargetAsm,
TargetReg,
TargetNockma,
TargetCasm
TargetCasm,
TargetAnoma
]
parseTreeCompileOptions :: Parser CompileOptions

View File

@ -35,7 +35,6 @@ runCompile inputFile o = do
TargetAsm -> return (Right ())
TargetReg -> return (Right ())
TargetTree -> return (Right ())
TargetNockma -> return (Right ())
TargetAnoma -> return (Right ())
TargetCasm -> return (Right ())
@ -55,7 +54,6 @@ prepareRuntime buildDir o = do
TargetAsm -> return ()
TargetReg -> return ()
TargetTree -> return ()
TargetNockma -> return ()
TargetAnoma -> return ()
TargetCasm -> return ()
where
@ -119,8 +117,6 @@ outputFile opts inputFile =
replaceExtension' juvixRegFileExt baseOutputFile
TargetTree ->
replaceExtension' juvixTreeFileExt baseOutputFile
TargetNockma ->
replaceExtension' nockmaFileExt baseOutputFile
TargetAnoma ->
replaceExtension' nockmaFileExt baseOutputFile
TargetCasm ->

View File

@ -13,7 +13,6 @@ data CompileTarget
| TargetAsm
| TargetReg
| TargetTree
| TargetNockma
| TargetAnoma
| TargetCasm
deriving stock (Eq, Data, Bounded, Enum)
@ -28,7 +27,6 @@ instance Show CompileTarget where
TargetAsm -> "asm"
TargetReg -> "reg"
TargetTree -> "tree"
TargetNockma -> "nockma"
TargetAnoma -> "anoma"
TargetCasm -> "casm"

View File

@ -12,7 +12,6 @@ data Target
| TargetAsm
| TargetReg
| TargetTree
| TargetNockma
| TargetAnoma
| TargetCairo
deriving stock (Data, Eq, Show)
@ -94,8 +93,6 @@ getLimits tgt debug = case tgt of
}
TargetTree ->
defaultLimits
TargetNockma ->
defaultLimits
TargetAnoma ->
defaultLimits
TargetCairo ->

View File

@ -0,0 +1,24 @@
module Juvix.Compiler.Nockma.Anoma where
import Juvix.Compiler.Nockma.Language
import Juvix.Compiler.Nockma.Translation.FromTree
import Juvix.Prelude
-- | Call a function at the head of the subject using the Anoma calling convention
anomaCall :: [Term Natural] -> Term Natural
anomaCall args = anomaCallTuple (foldTerms <$> nonEmpty args)
anomaCallTuple :: Maybe (Term Natural) -> Term Natural
anomaCallTuple = \case
Just args -> helper (Just (opReplace "anomaCall-args" (closurePath ArgsTuple) args))
Nothing -> helper Nothing
where
helper replaceArgs =
opCall
"anomaCall"
(closurePath WrapperCode)
(repArgs (OpAddress # emptyPath))
where
repArgs x = case replaceArgs of
Nothing -> x
Just r -> r x

View File

@ -5,13 +5,13 @@ import Juvix.Compiler.Nockma.Language
import Juvix.Compiler.Nockma.Pretty (ppTrace)
import Juvix.Prelude
evalCompiledNock' :: (Members '[Reader EvalOptions, Output (Term Natural)] r) => Term Natural -> Term Natural -> Sem r (Term Natural)
evalCompiledNock' :: (Members '[State OpCounts, Reader EvalOptions, Output (Term Natural)] r) => Term Natural -> Term Natural -> Sem r (Term Natural)
evalCompiledNock' stack mainTerm = do
evalT <-
runError @(ErrNockNatural Natural)
. runError @(NockEvalError Natural)
. runReader @(Storage Natural) emptyStorage
$ eval stack mainTerm
$ evalProfile stack mainTerm
case evalT of
Left e -> error (show e)
Right ev -> case ev of

View File

@ -12,6 +12,37 @@ import Juvix.Compiler.Nockma.Evaluator.Options
import Juvix.Compiler.Nockma.Evaluator.Storage
import Juvix.Compiler.Nockma.Language
import Juvix.Prelude hiding (Atom, Path)
import Juvix.Prelude.Pretty
newtype OpCounts = OpCounts
{ _opCountsMap :: HashMap NockOp Int
}
makeLenses ''OpCounts
initOpCounts :: OpCounts
initOpCounts = OpCounts mempty
ignoreOpCounts :: Sem (State OpCounts ': r) a -> Sem r a
ignoreOpCounts = evalState initOpCounts
countOp :: (Members '[State OpCounts] r) => NockOp -> Sem r ()
countOp op =
modify
( over
(opCountsMap . at op)
( \case
Nothing -> Just 1
Just n -> Just (n + 1)
)
)
runOpCounts :: Sem (State OpCounts ': r) a -> Sem r (OpCounts, a)
runOpCounts = runState initOpCounts
instance Pretty OpCounts where
pretty :: OpCounts -> Doc a
pretty (OpCounts m) = vsepHard [pretty op <+> ":" <+> pretty (fromMaybe 0 (m ^. at op)) | op <- allElements]
asAtom :: (Members '[Reader EvalCtx, Error (NockEvalError a)] r) => Term a -> Sem r (Atom a)
asAtom = \case
@ -55,16 +86,16 @@ subTerm term pos =
Nothing -> throwInvalidPath term pos
Just t -> return t
setSubTerm :: forall a r. (Members '[Error (NockEvalError a)] r) => Term a -> Path -> Term a -> Sem r (Term a)
setSubTerm :: forall a r. (Members '[Error (NockEvalError a), Reader EvalCtx] r) => Term a -> Path -> Term a -> Sem r (Term a)
setSubTerm term pos repTerm =
let (old, new) = setAndRemember (subTermT' pos) repTerm term
in if
| isNothing (getFirst old) -> throw @(NockEvalError a) (error "")
| isNothing (getFirst old) -> throwInvalidPath term pos
| otherwise -> return new
parseCell ::
forall r a.
(Members '[Error (NockEvalError a), Error (ErrNockNatural a)] r, NockNatural a) =>
(Members '[Reader EvalCtx, Error (NockEvalError a), Error (ErrNockNatural a)] r, NockNatural a) =>
Cell a ->
Sem r (ParsedCell a)
parseCell c = case c ^. cellLeft of
@ -83,10 +114,13 @@ parseCell c = case c ^. cellLeft of
parseOperatorCell :: Atom a -> Term a -> Sem r (OperatorCell a)
parseOperatorCell a t = do
op <- nockOp a
op <- catch @(ErrNockNatural a) (nockOp a) $ \e ->
let atm :: Atom a = errGetAtom e
in throwInvalidNockOp atm
return
OperatorCell
{ _operatorCellOp = op,
_operatorCellTag = c ^. cellTag,
_operatorCellTerm = t
}
@ -132,7 +166,15 @@ eval ::
Term a ->
Term a ->
Sem s (Term a)
eval inistack initerm =
eval initstack initterm = ignoreOpCounts (evalProfile initstack initterm)
evalProfile ::
forall s a.
(Hashable a, Integral a, Members '[Reader (Storage a), State OpCounts, Reader EvalOptions, Output (Term a), Error (NockEvalError a), Error (ErrNockNatural a)] s, NockNatural a) =>
Term a ->
Term a ->
Sem s (Term a)
evalProfile inistack initerm =
topEvalCtx $
recEval inistack initerm
where
@ -200,26 +242,29 @@ eval inistack initerm =
return (TermCell (Cell l' r'))
goOperatorCell :: OperatorCell a -> Sem r (Term a)
goOperatorCell c = case c ^. operatorCellOp of
OpAddress -> goOpAddress
OpQuote -> return goOpQuote
OpApply -> goOpApply
OpIsCell -> goOpIsCell
OpInc -> goOpInc
OpEq -> goOpEq
OpIf -> goOpIf
OpSequence -> goOpSequence
OpPush -> goOpPush
OpCall -> goOpCall
OpReplace -> goOpReplace
OpHint -> goOpHint
OpScry -> goOpScry
OpTrace -> goOpTrace
goOperatorCell c = do
countOp (c ^. operatorCellOp)
case c ^. operatorCellOp of
OpAddress -> goOpAddress
OpQuote -> return goOpQuote
OpApply -> goOpApply
OpIsCell -> goOpIsCell
OpInc -> goOpInc
OpEq -> goOpEq
OpIf -> goOpIf
OpSequence -> goOpSequence
OpPush -> goOpPush
OpCall -> goOpCall
OpReplace -> goOpReplace
OpHint -> goOpHint
OpScry -> goOpScry
OpTrace -> goOpTrace
where
crumb crumbTag =
EvalCrumbOperator $
CrumbOperator
{ _crumbOperatorOp = c ^. operatorCellOp,
_crumbOperatorCellTag = c ^. operatorCellTag,
_crumbOperatorTag = crumbTag,
_crumbOperatorLoc = loc
}

View File

@ -54,6 +54,7 @@ data CrumbAutoCons = CrumbAutoCons
data CrumbOperator = CrumbOperator
{ _crumbOperatorOp :: NockOp,
_crumbOperatorTag :: CrumbTag,
_crumbOperatorCellTag :: Maybe Tag,
_crumbOperatorLoc :: Maybe Interval
}
@ -91,7 +92,8 @@ instance PrettyCode CrumbOperator where
tag <- ppCode _crumbOperatorTag
loc <- ppLoc _crumbOperatorLoc
op <- ppCode _crumbOperatorOp
return (loc <+> tag <+> "for" <+> op)
celltag <- mapM ppCode _crumbOperatorCellTag
return (loc <+> tag <+> "for" <+> op <+?> celltag)
instance PrettyCode CrumbAutoCons where
ppCode CrumbAutoCons {..} = do

View File

@ -14,6 +14,7 @@ import Juvix.Prelude hiding (Atom, Path)
data NockEvalError a
= ErrInvalidPath (InvalidPath a)
| ErrInvalidNockOp (InvalidNockOp a)
| ErrExpectedAtom (ExpectedAtom a)
| ErrExpectedCell (ExpectedCell a)
| -- TODO perhaps this should be a repl error type
@ -50,8 +51,23 @@ data KeyNotInStorage a = KeyNotInStorage
_keyNotInStorageStorage :: Storage a
}
data InvalidNockOp a = InvalidNockOp
{ _invalidNockOpCtx :: EvalCtx,
_invalidNockOp :: Atom a
}
data NoStack = NoStack
throwInvalidNockOp :: (Members '[Error (NockEvalError a), Reader EvalCtx] r) => Atom a -> Sem r x
throwInvalidNockOp a = do
ctx <- ask
throw $
ErrInvalidNockOp
InvalidNockOp
{ _invalidNockOpCtx = ctx,
_invalidNockOp = a
}
throwInvalidPath :: (Members '[Error (NockEvalError a), Reader EvalCtx] r) => Term a -> Path -> Sem r x
throwInvalidPath tm p = do
ctx <- ask
@ -110,6 +126,12 @@ instance (PrettyCode a, NockNatural a) => PrettyCode (ExpectedAtom a) where
let atm = annotate AnnImportant "atom"
return (ctx <> "Expected an" <+> atm <+> "but got:" <> line <> cell)
instance (PrettyCode a, NockNatural a) => PrettyCode (InvalidNockOp a) where
ppCode InvalidNockOp {..} = do
atm <- ppCode _invalidNockOp
ctx <- ppCtx _invalidNockOpCtx
return (ctx <> "Invalid nockOp or path: " <+> atm)
instance (PrettyCode a, NockNatural a) => PrettyCode (ExpectedCell a) where
ppCode ExpectedCell {..} = do
atm <- ppCode _expectedCellAtom
@ -133,6 +155,7 @@ instance (PrettyCode a, NockNatural a) => PrettyCode (KeyNotInStorage a) where
instance (PrettyCode a, NockNatural a) => PrettyCode (NockEvalError a) where
ppCode = \case
ErrInvalidPath e -> ppCode e
ErrInvalidNockOp e -> ppCode e
ErrExpectedAtom e -> ppCode e
ErrExpectedCell e -> ppCode e
ErrNoStack e -> ppCode e

View File

@ -59,8 +59,16 @@ data StdlibCall a = StdlibCall
instance (Hashable a) => Hashable (StdlibCall a)
newtype Tag = Tag
{ _unTag :: Text
}
deriving stock (Show, Eq, Lift, Generic)
instance Hashable Tag
data CellInfo a = CellInfo
{ _cellInfoLoc :: Irrelevant (Maybe Interval),
_cellInfoTag :: Maybe Tag,
_cellInfoCall :: Maybe (StdlibCall a)
}
deriving stock (Show, Eq, Lift, Generic)
@ -78,6 +86,7 @@ instance (Hashable a) => Hashable (Cell a)
data AtomInfo = AtomInfo
{ _atomInfoHint :: Maybe AtomHint,
_atomInfoTag :: Maybe Tag,
_atomInfoLoc :: Irrelevant (Maybe Interval)
}
deriving stock (Show, Eq, Lift, Generic)
@ -98,6 +107,7 @@ data AtomHint
| AtomHintBool
| AtomHintNil
| AtomHintVoid
| AtomHintFunctionsPlaceholder
deriving stock (Show, Eq, Lift, Generic)
instance Hashable AtomHint
@ -157,6 +167,7 @@ data StdlibCallCell a = StdlibCallCell
data OperatorCell a = OperatorCell
{ _operatorCellOp :: NockOp,
_operatorCellTag :: Maybe Tag,
_operatorCellTerm :: Term a
}
@ -179,6 +190,7 @@ encodedPathAppendRightN n (EncodedPath p) = EncodedPath (f p)
f x = (2 ^ n) * (x + 1) - 1
makeLenses ''Cell
makeLenses ''Tag
makeLenses ''StdlibCallCell
makeLenses ''StdlibCall
makeLenses ''Atom
@ -201,9 +213,15 @@ termLoc f = \case
cellLoc :: Lens' (Cell a) (Maybe Interval)
cellLoc = cellInfo . cellInfoLoc . unIrrelevant
cellTag :: Lens' (Cell a) (Maybe Tag)
cellTag = cellInfo . cellInfoTag
cellCall :: Lens' (Cell a) (Maybe (StdlibCall a))
cellCall = cellInfo . cellInfoCall
atomTag :: Lens' (Atom a) (Maybe Tag)
atomTag = atomInfo . atomInfoTag
atomLoc :: Lens' (Atom a) (Maybe Interval)
atomLoc = atomInfo . atomInfoLoc . unIrrelevant
@ -242,9 +260,14 @@ class (NockmaEq a) => NockNatural a where
errInvalidOp :: Atom a -> ErrNockNatural a
errInvalidPath :: Atom a -> ErrNockNatural a
errGetAtom :: ErrNockNatural a -> Atom a
nockOp :: (Member (Error (ErrNockNatural a)) r) => Atom a -> Sem r NockOp
nockOp atm = do
case atm ^. atomHint of
Just h
| h /= AtomHintOp -> throw (errInvalidOp atm)
_ -> return ()
n <- nockNatural atm
failWithError (errInvalidOp atm) (parseOp n)
@ -264,8 +287,11 @@ nockBool = \case
True -> nockTrue
False -> nockFalse
nockNil' :: Term Natural
nockNil' = TermAtom nockNil
nockNilTagged :: Text -> Term Natural
nockNilTagged txt = TermAtom (set atomTag (Just (Tag txt)) nockNil)
nockNilUntagged :: Term Natural
nockNilUntagged = TermAtom nockNil
data NockNaturalNaturalError
= NaturalInvalidPath (Atom Natural)
@ -289,6 +315,9 @@ instance NockNatural Natural where
nockTrue = Atom 0 (atomHintInfo AtomHintBool)
nockFalse = Atom 1 (atomHintInfo AtomHintBool)
nockNil = Atom 0 (atomHintInfo AtomHintNil)
errGetAtom = \case
NaturalInvalidPath a -> a
NaturalInvalidOp a -> a
nockSucc = over atom succ
nockVoid = Atom 0 (atomHintInfo AtomHintVoid)
errInvalidOp atm = NaturalInvalidOp atm
@ -331,6 +360,16 @@ instance IsNock Path where
instance IsNock EncodedPath where
toNock = toNock . decodePath'
infixr 1 @.
(@.) :: Text -> Cell Natural -> Cell Natural
tag @. c = set cellTag (Just (Tag tag)) c
infixr 1 @
(@) :: Text -> Cell Natural -> Term Natural
tag @ c = TermCell (set cellTag (Just (Tag tag)) c)
infixr 5 #.
(#.) :: (IsNock x, IsNock y) => x -> y -> Cell Natural
@ -351,6 +390,18 @@ infixl 1 >>#
(>>#) :: (IsNock x, IsNock y) => x -> y -> Term Natural
a >># b = TermCell (a >>#. b)
opCall :: Text -> Path -> Term Natural -> Term Natural
opCall txt p t = txt @ (OpCall #. (p # t))
opReplace :: Text -> Path -> Term Natural -> Term Natural -> Term Natural
opReplace txt p t1 t2 = txt @ OpReplace #. ((p #. t1) #. t2)
opAddress :: Text -> Path -> Term Natural
opAddress txt p = txt @ OpAddress #. p
opQuote :: (IsNock x) => Text -> x -> Term Natural
opQuote txt p = txt @ OpQuote #. p
{-# COMPLETE Cell #-}
pattern Cell :: Term a -> Term a -> Cell a
@ -374,6 +425,7 @@ emptyCellInfo :: CellInfo a
emptyCellInfo =
CellInfo
{ _cellInfoCall = Nothing,
_cellInfoTag = Nothing,
_cellInfoLoc = Irrelevant Nothing
}
@ -381,6 +433,7 @@ emptyAtomInfo :: AtomInfo
emptyAtomInfo =
AtomInfo
{ _atomInfoHint = Nothing,
_atomInfoTag = Nothing,
_atomInfoLoc = Irrelevant Nothing
}

View File

@ -28,7 +28,10 @@ ppSerialize :: (PrettyCode c) => c -> Text
ppSerialize = ppPrintOpts serializeOptions
ppPrint :: (PrettyCode c) => c -> Text
ppPrint = ppPrintOpts defaultOptions
ppPrint = toPlainText . ppOut defaultOptions
ppTest :: (PrettyCode c) => c -> Text
ppTest = toPlainText . ppOut testOptions
ppPrintOpts :: (PrettyCode c) => Options -> c -> Text
ppPrintOpts opts = renderStrict . toTextStream . ppOut opts

View File

@ -24,20 +24,25 @@ runPrettyCode :: (PrettyCode c) => Options -> c -> Doc Ann
runPrettyCode opts = run . runReader opts . ppCode
instance (PrettyCode a, NockNatural a) => PrettyCode (Atom a) where
ppCode atm = runFailDefaultM (annotate (AnnKind KNameFunction) <$> ppCode (atm ^. atom))
. failFromError @(ErrNockNatural a)
$ do
whenM (asks (^. optIgnoreHints)) fail
h' <- failMaybe (atm ^. atomHint)
case h' of
AtomHintOp -> nockOp atm >>= ppCode
AtomHintPath -> nockPath atm >>= ppCode
AtomHintBool
| nockmaEq atm nockTrue -> return (annotate (AnnKind KNameInductive) "true")
| nockmaEq atm nockFalse -> return (annotate (AnnKind KNameAxiom) "false")
| otherwise -> fail
AtomHintNil -> return (annotate (AnnKind KNameConstructor) "nil")
AtomHintVoid -> return (annotate (AnnKind KNameAxiom) "void")
ppCode atm = do
t <- runFail $ do
failWhenM (asks (^. optIgnoreTags))
failMaybe (atm ^. atomTag) >>= ppCode
let def = fmap (t <?+>) (annotate (AnnKind KNameFunction) <$> ppCode (atm ^. atom))
fmap (t <?+>) . runFailDefaultM def . failFromError @(ErrNockNatural a) $
do
whenM (asks (^. optIgnoreHints)) fail
h' <- failMaybe (atm ^. atomHint)
case h' of
AtomHintOp -> nockOp atm >>= ppCode
AtomHintPath -> nockPath atm >>= ppCode
AtomHintBool
| nockmaEq atm nockTrue -> return (annotate (AnnKind KNameInductive) Str.true_)
| nockmaEq atm nockFalse -> return (annotate (AnnKind KNameAxiom) Str.false_)
| otherwise -> fail
AtomHintNil -> return (annotate (AnnKind KNameConstructor) Str.nil)
AtomHintVoid -> return (annotate (AnnKind KNameAxiom) Str.void)
AtomHintFunctionsPlaceholder -> return (annotate (AnnKind KNameAxiom) Str.functionsPlaceholder)
instance PrettyCode Interval where
ppCode = return . pretty
@ -69,9 +74,15 @@ instance (PrettyCode a, NockNatural a) => PrettyCode (StdlibCall a) where
args <- ppCode (c ^. stdlibCallArgs)
return (Str.stdlibTag <> fun <+> Str.argsTag <> args)
instance PrettyCode Tag where
ppCode (Tag txt) = return (annotate AnnKeyword Str.tagTag <> pretty txt)
instance (PrettyCode a, NockNatural a) => PrettyCode (Cell a) where
ppCode c = do
m <- asks (^. optPrettyMode)
label <- runFail $ do
failWhenM (asks (^. optIgnoreTags))
failMaybe (c ^. cellTag) >>= ppCode
stdlibCall <- runFail $ do
failWhenM (asks (^. optIgnoreHints))
failMaybe (c ^. cellCall) >>= ppCode
@ -81,7 +92,7 @@ instance (PrettyCode a, NockNatural a) => PrettyCode (Cell a) where
r' <- ppCode (c ^. cellRight)
return (l' <+> r')
MinimizeDelimiters -> sep <$> mapM ppCode (unfoldCell c)
let inside = stdlibCall <?+> components
let inside = label <?+> stdlibCall <?+> components
return (oneLineOrNextBrackets inside)
unfoldCell :: Cell a -> NonEmpty (Term a)

View File

@ -11,26 +11,38 @@ defaultOptions :: Options
defaultOptions =
Options
{ _optPrettyMode = MinimizeDelimiters,
_optIgnoreHints = False
_optIgnoreHints = False,
_optIgnoreTags = False
}
data Options = Options
{ _optPrettyMode :: PrettyMode,
_optIgnoreHints :: Bool
_optIgnoreHints :: Bool,
_optIgnoreTags :: Bool
}
serializeOptions :: Options
serializeOptions =
Options
{ _optPrettyMode = MinimizeDelimiters,
_optIgnoreHints = True
_optIgnoreHints = True,
_optIgnoreTags = True
}
testOptions :: Options
testOptions =
Options
{ _optPrettyMode = MinimizeDelimiters,
_optIgnoreHints = False,
_optIgnoreTags = True
}
traceOptions :: Options
traceOptions =
Options
{ _optPrettyMode = MinimizeDelimiters,
_optIgnoreHints = False
_optIgnoreHints = False,
_optIgnoreTags = False
}
makeLenses ''Options

View File

@ -7,7 +7,7 @@ import Juvix.Compiler.Nockma.Language
import Juvix.Extra.Paths
import Juvix.Extra.Strings qualified as Str
import Juvix.Parser.Error
import Juvix.Parser.Lexer (onlyInterval, withLoc)
import Juvix.Parser.Lexer (isWhiteSpace, onlyInterval, withLoc)
import Juvix.Prelude hiding (Atom, Path, many, some)
import Juvix.Prelude qualified as Prelude
import Juvix.Prelude.Parsing hiding (runParser)
@ -75,22 +75,24 @@ dottedNatural = lexeme $ do
digit :: Parser Char
digit = satisfy isDigit
atomOp :: Parser (Atom Natural)
atomOp = do
atomOp :: Maybe Tag -> Parser (Atom Natural)
atomOp mtag = do
WithLoc loc op' <- withLoc (choice [symbol opName $> op | (opName, op) <- HashMap.toList atomOps])
let info =
AtomInfo
{ _atomInfoHint = Just AtomHintOp,
_atomInfoTag = mtag,
_atomInfoLoc = Irrelevant (Just loc)
}
return (Atom (serializeNockOp op') info)
atomPath :: Parser (Atom Natural)
atomPath = do
atomPath :: Maybe Tag -> Parser (Atom Natural)
atomPath mtag = do
WithLoc loc path <- withLoc pPath
let info =
AtomInfo
{ _atomInfoHint = Just AtomHintPath,
_atomInfoTag = mtag,
_atomInfoLoc = Irrelevant (Just loc)
}
return (Atom (serializePath path) info)
@ -105,12 +107,13 @@ pPath =
symbol "S" $> []
<|> NonEmpty.toList <$> some direction
atomNat :: Parser (Atom Natural)
atomNat = do
atomNat :: Maybe Tag -> Parser (Atom Natural)
atomNat mtag = do
WithLoc loc n <- withLoc dottedNatural
let info =
AtomInfo
{ _atomInfoHint = Nothing,
_atomInfoTag = mtag,
_atomInfoLoc = Irrelevant (Just loc)
}
return (Atom n info)
@ -133,21 +136,32 @@ atomNil = symbol Str.nil $> nockNil
atomVoid :: Parser (Atom Natural)
atomVoid = symbol Str.void $> nockVoid
atomFunctionsPlaceholder :: Parser (Atom Natural)
atomFunctionsPlaceholder = symbol Str.functionsPlaceholder $> nockNil
patom :: Parser (Atom Natural)
patom =
atomOp
<|> atomNat
<|> atomPath
patom = do
mtag <- optional pTag
atomOp mtag
<|> atomNat mtag
<|> atomPath mtag
<|> atomBool
<|> atomNil
<|> atomVoid
<|> atomFunctionsPlaceholder
iden :: Parser Text
iden = lexeme (takeWhile1P (Just "<iden>") isAlphaNum)
iden = lexeme (takeWhile1P (Just "<iden>") (isAscii .&&. not . isWhiteSpace))
pTag :: Parser Tag
pTag = do
void (chunk Str.tagTag)
Tag <$> iden
cell :: Parser (Cell Natural)
cell = do
lloc <- onlyInterval lsbracket
lbl <- optional pTag
c <- optional stdlibCall
firstTerm <- term
restTerms <- some term
@ -156,6 +170,7 @@ cell = do
info =
CellInfo
{ _cellInfoCall = c,
_cellInfoTag = lbl,
_cellInfoLoc = Irrelevant (Just (lloc <> rloc))
}
return (set cellInfo info r)

View File

@ -1,23 +1,34 @@
module Juvix.Compiler.Nockma.Translation.FromTree
( runCompilerWithAnoma,
runCompilerWithJuvix,
fromEntryPoint,
( fromEntryPoint,
fromTreeTable,
ProgramCallingConvention (..),
AnomaResult (..),
anomaClosure,
compilerFunctionName,
AnomaCallablePathId (..),
CompilerOptions (..),
CompilerFunction (..),
FunctionCtx (..),
FunctionId (..),
closurePath,
foldTermsOrNil,
add,
dec,
mul,
sub,
pow2,
nockNatLiteral,
nockIntegralLiteral,
callStdlib,
appendRights,
foldTerms,
pathToArg,
makeList,
listToTuple,
appendToTuple,
append,
opAddress',
replaceSubterm',
runCompilerWith,
)
where
@ -31,9 +42,9 @@ import Juvix.Compiler.Tree.Language qualified as Tree
import Juvix.Compiler.Tree.Language.Rep
import Juvix.Prelude hiding (Atom, Path)
data ProgramCallingConvention
= ProgramCallingConventionJuvix
| ProgramCallingConventionAnoma
newtype AnomaResult = AnomaResult
{ _anomaClosure :: Term Natural
}
nockmaMemRep :: MemRep -> NockmaMemRep
nockmaMemRep = \case
@ -86,6 +97,10 @@ data FunctionInfo = FunctionInfo
_functionInfoArity :: Natural
}
data FunctionCtx = FunctionCtx
{ _functionCtxArity :: Natural
}
data CompilerCtx = CompilerCtx
{ _compilerFunctionInfos :: HashMap FunctionId FunctionInfo,
_compilerConstructorInfos :: ConstructorInfos,
@ -102,14 +117,22 @@ type ConstructorInfos = HashMap Tree.Tag ConstructorInfo
data CompilerFunction = CompilerFunction
{ _compilerFunctionName :: FunctionId,
_compilerFunctionArity :: Natural,
_compilerFunction :: Sem '[Reader CompilerCtx] (Term Natural)
_compilerFunction :: Sem '[Reader CompilerCtx, Reader FunctionCtx] (Term Natural)
}
data StackId
= Args
-- The Code and Args constructors must be first and second respectively. This is
-- because the stack must have the structure of a Nock function,
-- i.e [code args env]
data AnomaCallablePathId
= WrapperCode
| ArgsTuple
| FunctionsLibrary
| RawCode
| TempStack
| StandardLibrary
| FunctionsLibrary
| ClosureTotalArgsNum
| ClosureArgsNum
| ClosureArgs
deriving stock (Enum, Bounded, Eq, Show)
-- | A closure has the following structure:
@ -120,13 +143,6 @@ data StackId
-- 3. argsNum is the number of arguments that have been applied to the closure.
-- 4. args is the list of args that have been applied.
-- The length of the list should be argsNum.
data ClosurePathId
= ClosureCode
| ClosureTotalArgsNum
| ClosureArgsNum
| ClosureArgs
deriving stock (Bounded, Enum)
pathFromEnum :: (Enum a) => a -> Path
pathFromEnum = indexStack . fromIntegral . fromEnum
@ -138,14 +154,7 @@ data ConstructorPathId
constructorPath :: ConstructorPathId -> Path
constructorPath = pathFromEnum
data FunctionPathId
= FunctionCode
functionPath :: FunctionPathId -> Path
functionPath = \case
FunctionCode -> []
stackPath :: StackId -> Path
stackPath :: AnomaCallablePathId -> Path
stackPath s = indexStack (fromIntegral (fromEnum s))
indexTuple :: Natural -> Natural -> Path
@ -160,29 +169,40 @@ indexTuple len idx
indexStack :: Natural -> Path
indexStack idx = replicate idx R ++ [L]
indexInStack :: StackId -> Natural -> Path
indexInStack :: AnomaCallablePathId -> Natural -> Path
indexInStack s idx = stackPath s ++ indexStack idx
pathToArg :: Natural -> Path
pathToArg = indexInStack Args
makeLenses ''CompilerOptions
makeLenses ''AnomaResult
makeLenses ''CompilerFunction
makeLenses ''CompilerCtx
makeLenses ''FunctionCtx
makeLenses ''ConstructorInfo
makeLenses ''FunctionInfo
runCompilerFunction :: CompilerCtx -> CompilerFunction -> Term Natural
runCompilerFunction ctx fun =
run
. runReader (FunctionCtx (fun ^. compilerFunctionArity))
. runReader ctx
$ fun ^. compilerFunction
pathToArg :: (Members '[Reader FunctionCtx] r) => Natural -> Sem r Path
pathToArg n = do
ari <- asks (^. functionCtxArity)
return (stackPath ArgsTuple <> indexTuple ari n)
termFromParts :: (Bounded p, Enum p) => (p -> Term Natural) -> Term Natural
termFromParts f = remakeList [f pi | pi <- allElements]
makeClosure :: (ClosurePathId -> Term Natural) -> Term Natural
makeClosure :: (AnomaCallablePathId -> Term Natural) -> Term Natural
makeClosure = termFromParts
makeConstructor :: (ConstructorPathId -> Term Natural) -> Term Natural
makeConstructor = termFromParts
makeFunction :: (FunctionPathId -> Term Natural) -> Term Natural
makeFunction f = f FunctionCode
foldTermsOrNil :: [Term Natural] -> Term Natural
foldTermsOrNil = maybe (OpQuote # nockNilTagged "foldTermsOrNil") foldTerms . nonEmpty
foldTerms :: NonEmpty (Term Natural) -> Term Natural
foldTerms = foldr1 (#)
@ -209,14 +229,14 @@ supportsListNockmaRep tab ci = case allConstructors tab ci of
_ -> Nothing
-- | Use `Tree.toNockma` before calling this function
fromTreeTable :: (Members '[Error JuvixError, Reader CompilerOptions] r) => ProgramCallingConvention -> Tree.InfoTable -> Sem r (Cell Natural)
fromTreeTable cc t = case t ^. Tree.infoMainFunction of
fromTreeTable :: (Members '[Error JuvixError, Reader CompilerOptions] r) => Tree.InfoTable -> Sem r AnomaResult
fromTreeTable t = case t ^. Tree.infoMainFunction of
Just mainFun -> do
opts <- ask
return (fromTree opts mainFun t)
Nothing -> throw @JuvixError (error "TODO missing main")
where
fromTree :: CompilerOptions -> Tree.Symbol -> Tree.InfoTable -> Cell Natural
fromTree :: CompilerOptions -> Tree.Symbol -> Tree.InfoTable -> AnomaResult
fromTree opts mainSym tab@Tree.InfoTable {..} =
let funs = map compileFunction allFunctions
mkConstructorInfo :: Tree.ConstructorInfo -> ConstructorInfo
@ -236,7 +256,7 @@ fromTreeTable cc t = case t ^. Tree.infoMainFunction of
getInductiveInfo :: Symbol -> Tree.InductiveInfo
getInductiveInfo s = _infoInductives ^?! at s . _Just
in runCompilerWith cc opts constrs funs mainFun
in runCompilerWith opts constrs funs mainFun
where
mainFun :: CompilerFunction
mainFun = compileFunction (_infoFunctions ^?! at mainSym . _Just)
@ -266,7 +286,22 @@ fromTreeTable cc t = case t ^. Tree.infoMainFunction of
fromOffsetRef :: Tree.OffsetRef -> Natural
fromOffsetRef = fromIntegral . (^. Tree.offsetRefOffset)
compile :: forall r. (Members '[Reader CompilerCtx] r) => Tree.Node -> Sem r (Term Natural)
anomaCallableClosureWrapper :: Term Natural
anomaCallableClosureWrapper =
let closureArgsNum :: Term Natural = getClosureFieldInSubject ClosureArgsNum
closureTotalArgsNum :: Term Natural = getClosureFieldInSubject ClosureTotalArgsNum
appendAndReplaceArgsTuple =
replaceArgsWithTerm $
appendToTuple
(getClosureFieldInSubject ClosureArgs)
closureArgsNum
(getClosureFieldInSubject ArgsTuple)
(sub closureTotalArgsNum closureArgsNum)
closureArgsIsEmpty = isZero closureArgsNum
adjustArgs = OpIf # closureArgsIsEmpty # (opAddress "wrapperSubject" emptyPath) # appendAndReplaceArgsTuple
in opCall "closureWrapper" (closurePath RawCode) adjustArgs
compile :: forall r. (Members '[Reader FunctionCtx, Reader CompilerCtx] r) => Tree.Node -> Sem r (Term Natural)
compile = \case
Tree.Binop b -> goBinop b
Tree.Unop b -> goUnop b
@ -290,27 +325,32 @@ compile = \case
goMemRef :: Tree.MemRef -> Sem r (Term Natural)
goMemRef = \case
Tree.DRef d -> return (goDirectRef d)
Tree.DRef d -> goDirectRef d
Tree.ConstrRef Tree.Field {..} -> do
info <- getConstructorInfo _fieldTag
let memrep = info ^. constructorInfoMemRep
argIx = fromIntegral _fieldOffset
arity = info ^. constructorInfoArity
path = case memrep of
NockmaMemRepConstr ->
directRefPath _fieldRef
++ constructorPath ConstructorArgs
++ indexStack argIx
NockmaMemRepTuple ->
directRefPath _fieldRef
++ indexTuple arity argIx
NockmaMemRepList constr -> case constr of
NockmaMemRepListConstrNil -> impossible
NockmaMemRepListConstrCons -> directRefPath _fieldRef ++ indexTuple 2 argIx
return (OpAddress # path)
path :: Sem r Path
path = do
fr <- directRefPath _fieldRef
return $ case memrep of
NockmaMemRepConstr ->
fr
++ constructorPath ConstructorArgs
++ indexStack argIx
NockmaMemRepTuple ->
fr
++ indexTuple arity argIx
NockmaMemRepList constr -> case constr of
NockmaMemRepListConstrNil -> impossible
NockmaMemRepListConstrCons -> fr ++ indexTuple 2 argIx
(opAddress "constrRef") <$> path
where
goDirectRef :: Tree.DirectRef -> Term Natural
goDirectRef dr = OpAddress # directRefPath dr
goDirectRef :: Tree.DirectRef -> Sem r (Term Natural)
goDirectRef dr = do
p <- directRefPath dr
return (opAddress "directRef" p)
goConstant :: Tree.Constant -> Term Natural
goConstant = \case
@ -373,7 +413,7 @@ compile = \case
Tree.OpFieldToInt -> fieldErr
goAnomaGet :: Term Natural -> Sem r (Term Natural)
goAnomaGet arg = return (OpScry # (OpQuote # nockNil') # arg)
goAnomaGet arg = return (OpScry # (OpQuote # nockNilTagged "OpScry-typehint") # arg)
goTrace :: Term Natural -> Sem r (Term Natural)
goTrace arg = do
@ -389,7 +429,7 @@ compile = \case
arg2 <- compile _nodeBinopArg2
case _nodeBinopOpcode of
Tree.PrimBinop op -> goPrimBinop op [arg1, arg2]
Tree.OpSeq -> return (OpHint # (nockNil' # arg1) # arg2)
Tree.OpSeq -> return (OpHint # (nockNilTagged "OpSeq-OpHint-annotation" # arg1) # arg2)
where
goPrimBinop :: Tree.BinaryOp -> [Term Natural] -> Sem r (Term Natural)
goPrimBinop op args = case op of
@ -414,7 +454,12 @@ compile = \case
farity <- getFunctionArity fun
args <- mapM compile _nodeAllocClosureArgs
return . makeClosure $ \case
ClosureCode -> OpAddress # fpath
WrapperCode -> OpQuote # anomaCallableClosureWrapper
ArgsTuple -> OpQuote # argsTuplePlaceholder "goAllocClosure"
RawCode -> opAddress "allocClosureFunPath" (fpath <> closurePath RawCode)
TempStack -> remakeList []
StandardLibrary -> OpQuote # stdlib
FunctionsLibrary -> OpQuote # functionsLibraryPlaceHolder
ClosureTotalArgsNum -> nockNatLiteral farity
ClosureArgsNum -> nockIntegralLiteral (length args)
ClosureArgs -> remakeList args
@ -428,32 +473,64 @@ compile = \case
case _nodeCallType of
Tree.CallFun fun -> callFunWithArgs (UserFunction fun) newargs
Tree.CallClosure f -> do
f' <- compile f
let argsNum = getClosureField ClosureArgsNum f'
oldArgs = getClosureField ClosureArgs f'
fcode = getClosureField ClosureCode f'
posOfArgsNil = appendRights emptyPath argsNum
allArgs = replaceSubterm' oldArgs posOfArgsNil (remakeList newargs)
return (OpApply # replaceArgsWithTerm allArgs # fcode)
closure <- compile f
let argsNum = getClosureField ClosureArgsNum closure
oldArgs = getClosureField ClosureArgs closure
allArgs = appendToTuple oldArgs argsNum (foldTermsOrNil newargs) (nockIntegralLiteral (length newargs))
newSubject = replaceSubject $ \case
WrapperCode -> Just (getClosureField RawCode closure) -- We Want RawCode because we already have all args.
ArgsTuple -> Just allArgs
RawCode -> Just (OpQuote # nockNilTagged "callClosure-RawCode")
TempStack -> Just (OpQuote # nockNilTagged "callClosure-TempStack")
FunctionsLibrary -> Nothing
StandardLibrary -> Nothing
ClosureArgs -> Nothing
ClosureTotalArgsNum -> Nothing
ClosureArgsNum -> Nothing
return $ (opCall "callClosure" (closurePath WrapperCode) newSubject)
isZero :: Term Natural -> Term Natural
isZero a = OpEq # a # nockNatLiteral 0
opAddress' :: Term Natural -> Term Natural
opAddress' x = evaluated $ (opQuote "opAddress'" OpAddress) # x
-- [a [b [c 0]]] -> [a [b c]]
-- len = quote 3
-- TODO lst is being evaluated three times!
listToTuple :: Term Natural -> Term Natural -> Term Natural
listToTuple lst len =
-- posOfLast uses stdlib so when it is evaulated the stdlib must be in the
-- subject lst must also be evaluated against the standard subject. We achieve
-- this by evaluating `lst #. posOfLastOffset` in `t1`. The address that
-- posOfLastOffset now points to must be shifted by [L] to make it relative to
-- `lst`.
let posOfLastOffset = appendRights [L] (dec len)
posOfLast = appendRights emptyPath (dec len)
t1 = (lst #. posOfLastOffset) >># (opAddress' (OpAddress # [R])) >># (opAddress "listToTupleLast" [L])
in OpIf # isZero len # lst # (replaceSubterm' lst posOfLast t1)
argsTuplePlaceholder :: Text -> Term Natural
argsTuplePlaceholder txt = nockNilTagged ("argsTuplePlaceholder-" <> txt)
appendRights :: Path -> Term Natural -> Term Natural
appendRights path n = dec (mul (pow2 n) (OpInc # OpQuote # path))
pushTemp :: Term Natural -> Term Natural
pushTemp toBePushed =
remakeList
[ let p = OpAddress # stackPath s
in if
| TempStack == s -> toBePushed # p
| otherwise -> p
| s <- allElements
]
withTemp :: Term Natural -> Term Natural -> Term Natural
withTemp toBePushed body =
OpSequence # pushTemp toBePushed # body
OpSequence # pushTemp # body
where
pushTemp :: Term Natural
pushTemp =
remakeList
[ let p = opAddress "pushTemp" (stackPath s)
in if
| TempStack == s -> toBePushed # p
| otherwise -> p
| s <- allElements
]
testEq :: (Members '[Reader CompilerCtx] r) => Tree.Node -> Tree.Node -> Sem r (Term Natural)
testEq :: (Members '[Reader FunctionCtx, Reader CompilerCtx] r) => Tree.Node -> Tree.Node -> Sem r (Term Natural)
testEq a b = do
a' <- compile a
b' <- compile b
@ -465,8 +542,20 @@ nockNatLiteral = nockIntegralLiteral
nockIntegralLiteral :: (Integral a) => a -> Term Natural
nockIntegralLiteral = (OpQuote #) . toNock @Natural . fromIntegral
-- | xs must be a list.
-- ys is a (possibly empty) tuple.
-- the result is a tuple.
appendToTuple :: Term Natural -> Term Natural -> Term Natural -> Term Natural -> Term Natural
appendToTuple xs lenXs ys lenYs =
OpIf # isZero lenYs # listToTuple xs lenXs # append xs lenXs ys
append :: Term Natural -> Term Natural -> Term Natural -> Term Natural
append xs lenXs ys =
let posOfXsNil = appendRights emptyPath lenXs
in replaceSubterm' xs posOfXsNil ys
extendClosure ::
(Members '[Reader CompilerCtx] r) =>
(Members '[Reader FunctionCtx, Reader CompilerCtx] r) =>
Tree.NodeExtendClosure ->
Sem r (Term Natural)
extendClosure Tree.NodeExtendClosure {..} = do
@ -474,15 +563,18 @@ extendClosure Tree.NodeExtendClosure {..} = do
closure <- compile _nodeExtendClosureFun
let argsNum = getClosureField ClosureArgsNum closure
oldArgs = getClosureField ClosureArgs closure
fcode = getClosureField ClosureCode closure
posOfArgsNil = appendRights emptyPath argsNum
allArgs = replaceSubterm' oldArgs posOfArgsNil (remakeList args)
allArgs = append oldArgs argsNum (remakeList args)
newArgsNum = add argsNum (nockIntegralLiteral (length _nodeExtendClosureArgs))
return . makeClosure $ \case
ClosureCode -> fcode
WrapperCode -> getClosureField WrapperCode closure
RawCode -> getClosureField RawCode closure
ClosureTotalArgsNum -> getClosureField ClosureTotalArgsNum closure
ClosureArgsNum -> newArgsNum
ClosureArgs -> allArgs
ArgsTuple -> getClosureField ArgsTuple closure
FunctionsLibrary -> getClosureField FunctionsLibrary closure
TempStack -> getClosureField TempStack closure
StandardLibrary -> getClosureField StandardLibrary closure
-- Calling convention for Anoma stdlib
--
@ -494,21 +586,21 @@ extendClosure Tree.NodeExtendClosure {..} = do
-- [replace [RL :: edit at axis RL
-- [seq [@ R] :: evaluate the a formula in the original context without f on it
-- a]] :: the formula giving a goes here
-- @ L] :: this whole replace is editing what's at axis L, i.e. what was
-- @ L] :: this whole replace is editing what's at axis L, i.e. what was pushed
-- ]
-- ]
callStdlib :: StdlibFunction -> [Term Natural] -> Term Natural
callStdlib fun args =
let fPath = stdlibPath fun
getFunCode = OpAddress # stackPath StandardLibrary >># fPath
getFunCode = opAddress "callStdlibFunCode" (stackPath StandardLibrary) >># fPath
adjustArgs = case nonEmpty args of
Just args' -> OpReplace # ([R, L] # ((OpAddress # [R]) >># foldTerms args')) # (OpAddress # [L])
Nothing -> OpAddress # [L]
callFn = OpCall # [L] # adjustArgs
Just args' -> opReplace "callStdlib-args" (closurePath ArgsTuple) ((opAddress "stdlibR" [R]) >># foldTerms args') (opAddress "stdlibL" [L])
Nothing -> opAddress "adjustArgsNothing" [L]
callFn = opCall "callStdlib" (closurePath WrapperCode) adjustArgs
callCell = set cellCall (Just meta) (OpPush #. (getFunCode # callFn))
meta =
StdlibCall
{ _stdlibCallArgs = maybe nockNil' foldTerms (nonEmpty args),
{ _stdlibCallArgs = foldTermsOrNil args,
_stdlibCallFunction = fun
}
in TermCell callCell
@ -519,13 +611,15 @@ constUnit = constVoid
constVoid :: Term Natural
constVoid = TermAtom nockVoid
directRefPath :: Tree.DirectRef -> Path
directRefPath :: forall r. (Members '[Reader FunctionCtx] r) => Tree.DirectRef -> Sem r Path
directRefPath = \case
Tree.ArgRef a -> pathToArg (fromOffsetRef a)
Tree.TempRef Tree.RefTemp {..} ->
tempRefPath
(fromIntegral (fromJust _refTempTempHeight))
(fromOffsetRef _refTempOffsetRef)
return
( tempRefPath
(fromIntegral (fromJust _refTempTempHeight))
(fromOffsetRef _refTempOffsetRef)
)
tempRefPath :: Natural -> Natural -> Path
tempRefPath tempHeight off = indexInStack TempStack (tempHeight - off - 1)
@ -572,43 +666,14 @@ fieldErr = unsupported "the field type"
sub :: Term Natural -> Term Natural -> Term Natural
sub a b = callStdlib StdlibSub [a, b]
makeList :: [Term Natural] -> Term Natural
makeList ts = foldTerms (ts `prependList` pure (TermAtom nockNil))
makeList :: (Foldable f) => f (Term Natural) -> Term Natural
makeList ts = foldTerms (toList ts `prependList` pure (nockNilTagged "makeList"))
remakeList :: (Foldable l) => l (Term Natural) -> Term Natural
remakeList ts = foldTerms (toList ts `prependList` pure (OpQuote # nockNil'))
remakeList ts = foldTerms (toList ts `prependList` pure (OpQuote # nockNilTagged "remakeList"))
-- | Initialize the stack. The resulting term is intended to be evaulated
-- against a subject that contains function arguments.
initStackWithArgs :: [Term Natural] -> [Term Natural] -> Term Natural
initStackWithArgs defs getArgs = remakeList (initSubStack <$> allElements)
where
initSubStack :: StackId -> Term Natural
initSubStack = \case
Args -> remakeList getArgs
TempStack -> OpQuote # nockNil'
StandardLibrary -> OpQuote # stdlib
FunctionsLibrary -> OpQuote # makeList defs
-- | Initialize the stack. Populate the FunctionsLibrary with the passed terms.
initStack :: [Term Natural] -> Term Natural
initStack defs = makeList (initSubStack <$> allElements)
where
initSubStack :: StackId -> Term Natural
initSubStack = \case
Args -> nockNil'
TempStack -> nockNil'
StandardLibrary -> stdlib
FunctionsLibrary -> makeList defs
runCompilerWithAnoma :: CompilerOptions -> ConstructorInfos -> [CompilerFunction] -> CompilerFunction -> Cell Natural
runCompilerWithAnoma = runCompilerWith ProgramCallingConventionAnoma
runCompilerWithJuvix :: CompilerOptions -> ConstructorInfos -> [CompilerFunction] -> CompilerFunction -> Cell Natural
runCompilerWithJuvix = runCompilerWith ProgramCallingConventionJuvix
runCompilerWith :: ProgramCallingConvention -> CompilerOptions -> ConstructorInfos -> [CompilerFunction] -> CompilerFunction -> Cell Natural
runCompilerWith callingConvention opts constrs libFuns mainFun = run . runReader compilerCtx $ mkEntryPoint
runCompilerWith :: CompilerOptions -> ConstructorInfos -> [CompilerFunction] -> CompilerFunction -> AnomaResult
runCompilerWith opts constrs libFuns mainFun = makeAnomaFun
where
allFuns :: NonEmpty CompilerFunction
allFuns = mainFun :| libFuns ++ (builtinFunction <$> allElements)
@ -623,14 +688,26 @@ runCompilerWith callingConvention opts constrs libFuns mainFun = run . runReader
compiledFuns :: NonEmpty (Term Natural)
compiledFuns =
makeFunction'
<$> ( run . runReader compilerCtx . (^. compilerFunction)
<$> allFuns
makeLibraryFunction
<$> ( runCompilerFunction compilerCtx <$> allFuns
)
makeFunction' :: Term Natural -> Term Natural
makeFunction' c = makeFunction $ \case
FunctionCode -> c
exportEnv :: Term Natural
exportEnv = makeList compiledFuns
makeLibraryFunction :: Term Natural -> Term Natural
makeLibraryFunction c = makeClosure $ \p ->
let nockNilHere = nockNilTagged ("makeLibraryFunction-" <> show p)
in case p of
WrapperCode -> c
ArgsTuple -> argsTuplePlaceholder "libraryFunction"
FunctionsLibrary -> functionsLibraryPlaceHolder
RawCode -> c
TempStack -> nockNilHere
StandardLibrary -> stdlib
ClosureTotalArgsNum -> nockNilHere
ClosureArgsNum -> nockNilHere
ClosureArgs -> nockNilHere
functionInfos :: HashMap FunctionId FunctionInfo
functionInfos = hashMap (run (runInputNaturals (toList <$> userFunctions)))
@ -646,34 +723,39 @@ runCompilerWith callingConvention opts constrs libFuns mainFun = run . runReader
}
)
makeAnomaFun :: (Members '[Reader CompilerCtx] r) => Sem r (Cell Natural)
makeAnomaFun = do
entryTerm <- callFun (mainFun ^. compilerFunctionName)
makeAnomaFun :: AnomaResult
makeAnomaFun =
let mainClosure :: Term Natural
mainClosure = head compiledFuns
in AnomaResult
{ _anomaClosure = substEnv mainClosure
}
where
-- Replaces all instances of functionsLibraryPlaceHolder by the actual
-- functions library. Note that the functions library will have
-- functionsLibraryPlaceHolders, but this is not an issue because they
-- are not directly accessible from anoma so they'll never be entrypoints.
substEnv :: Term Natural -> Term Natural
substEnv = \case
TermAtom a
| a ^. atomHint == Just AtomHintFunctionsPlaceholder -> exportEnv
| otherwise -> TermAtom a
TermCell (Cell' l r i) ->
-- note that we do not need to recurse into terms inside the CellInfo because those terms will never be an entry point from anoma
TermCell (Cell' (substEnv l) (substEnv r) i)
let mainArity :: Natural
mainArity = mainFun ^. compilerFunctionArity
args :: [Term Natural]
args = [OpAddress # [R, L] ++ indexTuple mainArity (pred i) | i <- [1 .. mainArity]]
wrapperCode :: Term Natural
wrapperCode = OpApply # (initStackWithArgs (toList compiledFuns) args) # (OpQuote # entryTerm)
argsPlaceholder :: Term Natural
argsPlaceholder = nockNil'
env :: Term Natural
env = nockNil'
return (wrapperCode #. (argsPlaceholder # env))
makeJuvixFun :: (Members '[Reader CompilerCtx] r) => Sem r (Cell Natural)
makeJuvixFun = do
entryTerm <- callFunWithArgs (mainFun ^. compilerFunctionName) []
return (initStack (toList compiledFuns) #. entryTerm)
mkEntryPoint = case callingConvention of
ProgramCallingConventionAnoma -> makeAnomaFun
ProgramCallingConventionJuvix -> makeJuvixFun
functionsLibraryPlaceHolder :: Term Natural
functionsLibraryPlaceHolder =
TermAtom
Atom
{ _atomInfo =
AtomInfo
{ _atomInfoLoc = Irrelevant Nothing,
_atomInfoTag = Nothing,
_atomInfoHint = Just AtomHintFunctionsPlaceholder
},
_atom = 0 :: Natural
}
builtinFunction :: BuiltinFunctionId -> CompilerFunction
builtinFunction = \case
@ -684,15 +766,19 @@ builtinFunction = \case
_compilerFunction = return crash
}
-- | Call a function. Arguments to the function are assumed to be in the Args stack
closurePath :: AnomaCallablePathId -> Path
closurePath = stackPath
-- | Call a function. Arguments to the function are assumed to be in the ArgsTuple stack
-- TODO what about temporary stack?
callFun ::
(Members '[Reader CompilerCtx] r) =>
FunctionId ->
Sem r (Term Natural)
callFun fun = do
fpath <- getFunctionPath fun
let p' = fpath ++ functionPath FunctionCode
return (OpCall # p' # (OpAddress # emptyPath))
let p' = fpath ++ closurePath WrapperCode
return (opCall "callFun" p' (opAddress "callFunSubject" emptyPath))
-- | Call a function with the passed arguments
callFunWithArgs ::
@ -702,29 +788,29 @@ callFunWithArgs ::
Sem r (Term Natural)
callFunWithArgs fun args = (replaceArgs args >>#) <$> callFun fun
replaceArgsWithTerm :: Term Natural -> Term Natural
replaceArgsWithTerm term =
replaceSubject :: (AnomaCallablePathId -> Maybe (Term Natural)) -> Term Natural
replaceSubject f =
remakeList
[ if
| Args == s -> term
| otherwise -> OpAddress # stackPath s
[ case f s of
Nothing -> opAddress "replaceSubject" (closurePath s)
Just t' -> t'
| s <- allElements
]
replaceArgsWithTerm :: Term Natural -> Term Natural
replaceArgsWithTerm term =
replaceSubject $ \case
ArgsTuple -> Just term
_ -> Nothing
replaceArgs :: [Term Natural] -> Term Natural
replaceArgs args =
remakeList
[ if
| Args == s -> remakeList args
| otherwise -> OpAddress # stackPath s
| s <- allElements
]
replaceArgs = replaceArgsWithTerm . foldTermsOrNil
getFunctionPath :: (Members '[Reader CompilerCtx] r) => FunctionId -> Sem r Path
getFunctionPath funName = asks (^?! compilerFunctionInfos . at funName . _Just . functionInfoPath)
evaluated :: Term Natural -> Term Natural
evaluated t = OpApply # (OpAddress # emptyPath) # t
evaluated t = OpApply # (opAddress "evaluated" emptyPath) # t
-- | obj[eval(relPath)] := newVal
-- relPath is relative to obj
@ -832,20 +918,26 @@ getFunctionArity s = asks (^?! compilerFunctionInfos . at s . _Just . functionIn
getConstructorInfo :: (Members '[Reader CompilerCtx] r) => Tree.Tag -> Sem r ConstructorInfo
getConstructorInfo tag = asks (^?! compilerConstructorInfos . at tag . _Just)
getClosureField :: ClosurePathId -> Term Natural -> Term Natural
getClosureField :: AnomaCallablePathId -> Term Natural -> Term Natural
getClosureField = getField
getClosureFieldInSubject :: AnomaCallablePathId -> Term Natural
getClosureFieldInSubject = getFieldInSubject
getConstructorField :: ConstructorPathId -> Term Natural -> Term Natural
getConstructorField = getField
getField :: (Enum field) => field -> Term Natural -> Term Natural
getField field t = t >># (OpAddress # pathFromEnum field)
getField field t = t >># getFieldInSubject field
getFieldInSubject :: (Enum field) => field -> Term Natural
getFieldInSubject field = opAddress "getFieldInSubject" (pathFromEnum field)
getConstructorMemRep :: (Members '[Reader CompilerCtx] r) => Tree.Tag -> Sem r NockmaMemRep
getConstructorMemRep tag = (^. constructorInfoMemRep) <$> getConstructorInfo tag
crash :: Term Natural
crash = (OpAddress # OpAddress # OpAddress)
crash = ("crash" @ OpAddress #. OpAddress # OpAddress)
mul :: Term Natural -> Term Natural -> Term Natural
mul a b = callStdlib StdlibMul [a, b]

View File

@ -27,7 +27,6 @@ import Juvix.Compiler.Core.Transformation
import Juvix.Compiler.Core.Translation.Stripped.FromCore qualified as Stripped
import Juvix.Compiler.Internal qualified as Internal
import Juvix.Compiler.Internal.Translation.FromInternal.Analysis.Termination.Checker
import Juvix.Compiler.Nockma.Language qualified as Nockma
import Juvix.Compiler.Nockma.Translation.FromTree qualified as NockmaTree
import Juvix.Compiler.Pipeline.Artifacts
import Juvix.Compiler.Pipeline.EntryPoint
@ -130,8 +129,8 @@ upToGeb spec =
upToAnoma ::
(Members '[HighlightBuilder, Reader Parser.ParserResult, Reader EntryPoint, Reader Store.ModuleTable, Files, NameIdGen, Error JuvixError, GitClone, PathResolver] r) =>
Sem r (Nockma.Term Natural)
upToAnoma = upToStoredCore >>= \Core.CoreResult {..} -> Nockma.TermCell <$> storedCoreToAnoma _coreResultModule
Sem r NockmaTree.AnomaResult
upToAnoma = upToStoredCore >>= \Core.CoreResult {..} -> storedCoreToAnoma _coreResultModule
upToCoreTypecheck ::
(Members '[HighlightBuilder, Reader Parser.ParserResult, Reader EntryPoint, Reader Store.ModuleTable, Files, NameIdGen, Error JuvixError, GitClone, PathResolver] r) =>
@ -150,7 +149,7 @@ storedCoreToTree checkId md = do
>=> return . Tree.fromCore . Stripped.fromCore fsize . Core.computeCombinedInfoTable
$ md
storedCoreToAnoma :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r (Nockma.Cell Natural)
storedCoreToAnoma :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r NockmaTree.AnomaResult
storedCoreToAnoma = storedCoreToTree Core.CheckAnoma >=> treeToAnoma
storedCoreToAsm :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r Asm.InfoTable
@ -190,10 +189,7 @@ coreToReg = Core.toStored >=> storedCoreToReg
coreToCasm :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r Casm.Result
coreToCasm = Core.toStored >=> storedCoreToCasm
coreToNockma :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r (Nockma.Cell Natural)
coreToNockma = coreToTree Core.CheckAnoma >=> treeToNockma
coreToAnoma :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r (Nockma.Cell Natural)
coreToAnoma :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r NockmaTree.AnomaResult
coreToAnoma = coreToTree Core.CheckAnoma >=> treeToAnoma
coreToMiniC :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r C.MiniCResult
@ -221,11 +217,8 @@ treeToCairoAsm = Tree.toCairoAsm >=> return . Asm.fromTree
treeToReg :: (Members '[Error JuvixError, Reader EntryPoint] r) => Tree.InfoTable -> Sem r Reg.InfoTable
treeToReg = treeToAsm >=> asmToReg
treeToNockma :: (Members '[Error JuvixError, Reader EntryPoint] r) => Tree.InfoTable -> Sem r (Nockma.Cell Natural)
treeToNockma = Tree.toNockma >=> mapReader NockmaTree.fromEntryPoint . NockmaTree.fromTreeTable NockmaTree.ProgramCallingConventionJuvix
treeToAnoma :: (Members '[Error JuvixError, Reader EntryPoint] r) => Tree.InfoTable -> Sem r (Nockma.Cell Natural)
treeToAnoma = Tree.toNockma >=> mapReader NockmaTree.fromEntryPoint . NockmaTree.fromTreeTable NockmaTree.ProgramCallingConventionAnoma
treeToAnoma :: (Members '[Error JuvixError, Reader EntryPoint] r) => Tree.InfoTable -> Sem r (NockmaTree.AnomaResult)
treeToAnoma = Tree.toNockma >=> mapReader NockmaTree.fromEntryPoint . NockmaTree.fromTreeTable
treeToMiniC :: (Members '[Error JuvixError, Reader EntryPoint] r) => Tree.InfoTable -> Sem r C.MiniCResult
treeToMiniC = treeToAsm >=> asmToMiniC
@ -251,11 +244,8 @@ regToMiniC tab = do
regToCasm :: Reg.InfoTable -> Sem r Casm.Result
regToCasm = Reg.toCasm >=> return . Casm.fromReg
treeToNockma' :: (Members '[Error JuvixError, Reader NockmaTree.CompilerOptions] r) => Tree.InfoTable -> Sem r (Nockma.Cell Natural)
treeToNockma' = Tree.toNockma >=> NockmaTree.fromTreeTable NockmaTree.ProgramCallingConventionJuvix
treeToAnoma' :: (Members '[Error JuvixError, Reader NockmaTree.CompilerOptions] r) => Tree.InfoTable -> Sem r (Nockma.Cell Natural)
treeToAnoma' = Tree.toNockma >=> NockmaTree.fromTreeTable NockmaTree.ProgramCallingConventionAnoma
treeToAnoma' :: (Members '[Error JuvixError, Reader NockmaTree.CompilerOptions] r) => Tree.InfoTable -> Sem r NockmaTree.AnomaResult
treeToAnoma' = Tree.toNockma >=> NockmaTree.fromTreeTable
asmToMiniC' :: (Members '[Error JuvixError, Reader Asm.Options] r) => Asm.InfoTable -> Sem r C.MiniCResult
asmToMiniC' = mapError (JuvixError @Asm.AsmError) . Asm.toReg' >=> regToMiniC' . Reg.fromAsm

View File

@ -689,6 +689,9 @@ instrAdd = "add"
argsTag :: (IsString s) => s
argsTag = "args@"
tagTag :: (IsString s) => s
tagTag = "tag@"
stdlibTag :: (IsString s) => s
stdlibTag = "stdlib@"
@ -982,3 +985,6 @@ package = "package"
version :: (IsString s) => s
version = "version"
functionsPlaceholder :: (IsString s) => s
functionsPlaceholder = "functions_placeholder"

View File

@ -1,7 +1,8 @@
module Anoma.Compilation where
import Anoma.Compilation.Negative qualified as N
import Anoma.Compilation.Positive qualified as P
import Base
allTests :: TestTree
allTests = testGroup "Compilation to Anoma" [P.allTests]
allTests = testGroup "Compilation to Anoma" [P.allTests, N.allTests]

View File

@ -0,0 +1,47 @@
module Anoma.Compilation.Negative where
import Base
import Juvix.Compiler.Backend (Target (TargetAnoma))
import Juvix.Compiler.Core.Error
import Juvix.Prelude qualified as Prelude
root :: Prelude.Path Abs Dir
root = relToProject $(mkRelDir "tests/Anoma/Compilation/negative")
type CheckError = JuvixError -> IO ()
mkAnomaNegativeTest :: Text -> Prelude.Path Rel Dir -> Prelude.Path Rel File -> CheckError -> TestTree
mkAnomaNegativeTest testName' relRoot mainFile testCheck =
testCase (unpack testName') mkTestIO
where
mkTestIO :: IO ()
mkTestIO = do
merr <- withRootCopy compileMain
case merr of
Nothing -> assertFailure "expected compilation to fail"
Just err -> testCheck err
withRootCopy :: (Prelude.Path Abs Dir -> IO a) -> IO a
withRootCopy action = withSystemTempDir "test" $ \tmpRootDir -> do
copyDirRecur root tmpRootDir
action tmpRootDir
compileMain :: Prelude.Path Abs Dir -> IO (Maybe JuvixError)
compileMain rootCopyDir = do
let testRootDir = rootCopyDir <//> relRoot
entryPoint <-
set entryPointTarget TargetAnoma
<$> testDefaultEntryPointIO testRootDir (testRootDir <//> mainFile)
either Just (const Nothing) <$> testRunIOEither entryPoint upToAnoma
checkCoreError :: CheckError
checkCoreError e =
unless
(isJust (fromJuvixError @CoreError e))
(assertFailure ("Expected core error got: " <> unpack (renderTextDefault e)))
allTests :: TestTree
allTests =
testGroup
"Anoma negative tests"
[mkAnomaNegativeTest "Use of Strings" $(mkRelDir ".") $(mkRelFile "String.juvix") checkCoreError]

View File

@ -3,13 +3,12 @@ module Anoma.Compilation.Positive where
import Base
import Data.HashMap.Strict qualified as HashMap
import Juvix.Compiler.Backend (Target (TargetAnoma))
import Juvix.Compiler.Nockma.Anoma
import Juvix.Compiler.Nockma.Evaluator
import Juvix.Compiler.Nockma.Language
import Juvix.Compiler.Nockma.Pretty
import Juvix.Compiler.Nockma.Translation.FromSource.QQ
import Juvix.Compiler.Nockma.Translation.FromTree
import Juvix.Prelude qualified as Prelude
import Nockma.Base
import Nockma.Eval.Positive
root :: Prelude.Path Abs Dir
@ -21,12 +20,9 @@ mkAnomaCallTest' enableDebug _testProgramStorage _testName relRoot mainFile args
where
mkTestIO :: IO Test
mkTestIO = do
_testProgramSubject <- withRootCopy $ \tmpDir -> do
compiledMain <- compileMain tmpDir
-- Write out the nockma function to force full evaluation of the compiler
writeFileEnsureLn (tmpDir <//> $(mkRelFile "test.nockma")) (ppSerialize compiledMain)
return compiledMain
anomaRes <- withRootCopy compileMain
let _testProgramFormula = anomaCall args
_testProgramSubject = anomaRes ^. anomaClosure
_testEvalOptions = defaultEvalOptions
_testAssertEvalError :: Maybe (NockEvalError Natural -> Assertion) = Nothing
return Test {..}
@ -36,7 +32,7 @@ mkAnomaCallTest' enableDebug _testProgramStorage _testName relRoot mainFile args
copyDirRecur root tmpRootDir
action tmpRootDir
compileMain :: Prelude.Path Abs Dir -> IO (Term Natural)
compileMain :: Prelude.Path Abs Dir -> IO AnomaResult
compileMain rootCopyDir = do
let testRootDir = rootCopyDir <//> relRoot
entryPoint <-

View File

@ -1,11 +0,0 @@
module Nockma.Base where
import Base
import Juvix.Compiler.Nockma.Language
import Juvix.Compiler.Nockma.Translation.FromTree
-- | Call a function at the head of the subject using the Anoma calling convention
anomaCall :: [Term Natural] -> Term Natural
anomaCall args = case nonEmpty args of
Just args' -> OpCall # [L] # OpReplace # ([R, L] # foldTerms args') # (OpAddress # emptyPath)
Nothing -> OpCall # [L] # (OpAddress # emptyPath)

View File

@ -1,6 +1,7 @@
module Nockma.Compile.Tree.Positive where
import Base
import Juvix.Compiler.Nockma.Anoma
import Juvix.Compiler.Nockma.EvalCompiled
import Juvix.Compiler.Nockma.Evaluator qualified as NockmaEval
import Juvix.Compiler.Nockma.Language
@ -13,20 +14,20 @@ import Tree.Eval.Positive qualified as Tree
runNockmaAssertion :: Handle -> Symbol -> InfoTable -> IO ()
runNockmaAssertion hout _main tab = do
Nockma.Cell nockSubject nockMain <-
anomaRes :: AnomaResult <-
runM
. runErrorIO' @JuvixError
. runReader opts
$ treeToNockma' tab
$ treeToAnoma' tab
res <-
runM
. runOutputSem @(Term Natural)
(hPutStrLn hout . Nockma.ppPrint)
(hPutStrLn hout . Nockma.ppTest)
. runReader NockmaEval.defaultEvalOptions
. evalCompiledNock' nockSubject
$ nockMain
. NockmaEval.ignoreOpCounts
$ evalCompiledNock' (anomaRes ^. anomaClosure) (anomaCall [])
let ret = getReturn res
whenJust ret (hPutStrLn hout . Nockma.ppPrint)
whenJust ret (hPutStrLn hout . Nockma.ppTest)
where
opts :: CompilerOptions
opts =

View File

@ -3,12 +3,12 @@ module Nockma.Eval.Positive where
import Base hiding (Path, testName)
import Data.HashMap.Strict qualified as HashMap
import Juvix.Compiler.Core.Language.Base (defaultSymbol)
import Juvix.Compiler.Nockma.Anoma
import Juvix.Compiler.Nockma.Evaluator
import Juvix.Compiler.Nockma.Language
import Juvix.Compiler.Nockma.Pretty
import Juvix.Compiler.Nockma.Translation.FromSource.QQ
import Juvix.Compiler.Nockma.Translation.FromTree
import Nockma.Base
type Check = Sem '[Reader [Term Natural], Reader (Term Natural), EmbedIO]
@ -87,21 +87,7 @@ eqTraces expected = do
compilerTest :: Text -> Term Natural -> Check () -> Bool -> Test
compilerTest n mainFun _testCheck _evalInterceptStdlibCalls =
let f =
CompilerFunction
{ _compilerFunctionName = UserFunction (defaultSymbol 0),
_compilerFunctionArity = 0,
_compilerFunction = return mainFun
}
_testName :: Text
| _evalInterceptStdlibCalls = n <> " - intercept stdlib"
| otherwise = n
opts = CompilerOptions {_compilerOptionsEnableTrace = False}
Cell _testProgramSubject _testProgramFormula = runCompilerWithJuvix opts mempty [] f
_testEvalOptions = EvalOptions {..}
_testProgramStorage :: Storage Natural = emptyStorage
_testAssertEvalError :: Maybe (NockEvalError Natural -> Assertion) = Nothing
in Test {..}
anomaTest n mainFun [] _testCheck _evalInterceptStdlibCalls
withAssertErrKeyNotInStorage :: Test -> Test
withAssertErrKeyNotInStorage Test {..} =
@ -127,7 +113,8 @@ anomaTest n mainFun args _testCheck _evalInterceptStdlibCalls =
opts = CompilerOptions {_compilerOptionsEnableTrace = False}
_testProgramSubject = TermCell (runCompilerWithAnoma opts mempty [] f)
res :: AnomaResult = runCompilerWith opts mempty [] f
_testProgramSubject = res ^. anomaClosure
_testProgramFormula = anomaCall args
_testProgramStorage :: Storage Natural = emptyStorage
@ -146,7 +133,15 @@ anomaCallingConventionTests =
[True, False]
<**> [ anomaTest "stdlib add" (add (nockNatLiteral 1) (nockNatLiteral 2)) [] (eqNock [nock| 3 |]),
anomaTest "stdlib add with arg" (add (nockNatLiteral 1) (nockNatLiteral 2)) [nockNatLiteral 1] (eqNock [nock| 3 |]),
anomaTest "stdlib sub args" (sub (OpAddress # pathToArg 0) (OpAddress # pathToArg 1)) [nockNatLiteral 3, nockNatLiteral 1] (eqNock [nock| 2 |])
let args = [nockNatLiteral 3, nockNatLiteral 1]
fx =
FunctionCtx
{ _functionCtxArity = fromIntegral (length args)
}
in run . runReader fx $ do
p0 <- pathToArg 0
p1 <- pathToArg 1
return (anomaTest "stdlib sub args" (sub (OpAddress # p0) (OpAddress # p1)) args (eqNock [nock| 2 |]))
]
juvixCallingConventionTests :: [Test]
@ -163,7 +158,39 @@ juvixCallingConventionTests =
compilerTest "stdlib pow2" (pow2 (nockNatLiteral 3)) (eqNock [nock| 8 |]),
compilerTest "stdlib nested" (dec (dec (nockNatLiteral 20))) (eqNock [nock| 18 |]),
compilerTest "append rights - empty" (appendRights emptyPath (nockNatLiteral 3)) (eqNock (toNock [R, R, R])),
compilerTest "append rights" (appendRights [L, L] (nockNatLiteral 3)) (eqNock (toNock [L, L, R, R, R]))
compilerTest "append rights" (appendRights [L, L] (nockNatLiteral 3)) (eqNock (toNock [L, L, R, R, R])),
compilerTest "opAddress" ((OpQuote # (foldTerms (toNock @Natural <$> (5 :| [6, 1])))) >># opAddress' (OpQuote # [R, R])) (eqNock (toNock @Natural 1)),
compilerTest "foldTermsOrNil (empty)" (foldTermsOrNil []) (eqNock (nockNilTagged "expected-result")),
let l :: NonEmpty Natural = 1 :| [2]
l' :: NonEmpty (Term Natural) = nockNatLiteral <$> l
in compilerTest "foldTermsOrNil (non-empty)" (foldTermsOrNil (toList l')) (eqNock (foldTerms (toNock @Natural <$> l))),
let l :: NonEmpty (Term Natural) = toNock <$> nonEmpty' [1 :: Natural .. 3]
in compilerTest "list to tuple" (listToTuple (OpQuote # makeList (toList l)) (nockIntegralLiteral (length l))) $
eqNock (foldTerms l),
let l :: Term Natural = OpQuote # foldTerms (toNock @Natural <$> (1 :| [2, 3]))
in compilerTest "replaceSubterm'" (replaceSubterm' l (OpQuote # toNock [R]) (OpQuote # (toNock @Natural 999))) (eqNock (toNock @Natural 1 # toNock @Natural 999)),
let lst :: [Term Natural] = toNock @Natural <$> [1, 2, 3]
len = nockIntegralLiteral (length lst)
l :: Term Natural = OpQuote # makeList lst
in compilerTest "append" (append l len l) (eqNock (makeList (lst ++ lst))),
let l :: [Natural] = [1, 2]
r :: NonEmpty Natural = 3 :| [4]
res :: Term Natural = foldTerms (toNock <$> prependList l r)
lenL :: Term Natural = nockIntegralLiteral (length l)
lenR :: Term Natural = nockIntegralLiteral (length r)
lstL = OpQuote # makeList (map toNock l)
tupR = OpQuote # foldTerms (toNock <$> r)
in compilerTest "appendToTuple (left non-empty, right non-empty)" (appendToTuple lstL lenL tupR lenR) (eqNock res),
let l :: NonEmpty Natural = 1 :| [2]
res :: Term Natural = foldTerms (toNock <$> l)
lenL :: Term Natural = nockIntegralLiteral (length l)
lstL = OpQuote # makeList (toNock <$> (toList l))
in compilerTest "appendToTuple (left non-empty, right empty)" (appendToTuple lstL lenL (OpQuote # nockNilTagged "appendToTuple") (nockNatLiteral 0)) (eqNock res),
let r :: NonEmpty Natural = 3 :| [4]
res :: Term Natural = foldTerms (toNock <$> r)
lenR :: Term Natural = nockIntegralLiteral (length r)
tupR = OpQuote # foldTerms (toNock <$> r)
in compilerTest "appendToTuple (left empty, right-nonempty)" (appendToTuple (OpQuote # nockNilTagged "test-appendtotuple") (nockNatLiteral 0) tupR lenR) (eqNock res)
]
unitTests :: [Test]

View File

@ -0,0 +1,5 @@
module String;
import Stdlib.Prelude open;
main : String := "boom";