diff --git a/src/Juvix/Compiler/Backend/Markdown/Data/MkJuvixBlockOptions.hs b/src/Juvix/Compiler/Backend/Markdown/Data/MkJuvixBlockOptions.hs new file mode 100644 index 000000000..b4953f8ae --- /dev/null +++ b/src/Juvix/Compiler/Backend/Markdown/Data/MkJuvixBlockOptions.hs @@ -0,0 +1,66 @@ +module Juvix.Compiler.Backend.Markdown.Data.MkJuvixBlockOptions where + +import Juvix.Parser.Error.Base +import Juvix.Prelude.Base +import Juvix.Prelude.Parsing hiding (runParser) +import Juvix.Prelude.Path +import Text.Megaparsec qualified as P +import Text.Megaparsec.Char.Lexer qualified as L + +data MkJuvixBlockOptions + = MkJuvixBlockOptionsHide + | MkJuvixBlockOptionsShow + | MkJuvixBlockOptionsExtractModule JuvixBlockOptionsExtractModule + deriving stock (Eq, Show) + +newtype JuvixBlockOptionsExtractModule = JuvixBlockOptionsExtractModule + { _juvixBlockOptionsExtractModuleDrop :: Int + } + deriving stock (Eq, Show) + +makeLenses ''JuvixBlockOptionsExtractModule + +optionExtractModuleStatements :: (IsString s) => s +optionExtractModuleStatements = "extract-module-statements" + +optionHide :: (IsString s) => s +optionHide = "hide" + +parseJuvixBlockOptions :: Path Abs File -> Text -> Either MegaparsecError MkJuvixBlockOptions +parseJuvixBlockOptions p = mapLeft MegaparsecError . P.runParser parseOptions (toFilePath p) + +type Parser = P.Parsec Void Text + +spaceConsumer :: Parser () +spaceConsumer = L.space space1 empty empty + +lexeme :: Parser a -> Parser a +lexeme = L.lexeme spaceConsumer + +symbol :: Text -> Parser () +symbol = void . L.symbol spaceConsumer + +decimal :: Parser Int +decimal = lexeme L.decimal + +parseOptions :: Parser MkJuvixBlockOptions +parseOptions = do + spaceConsumer + opts <- + parseHide + <|> parseExtractModule + <|> parseShow + eof + return opts + +parseShow :: Parser MkJuvixBlockOptions +parseShow = return MkJuvixBlockOptionsShow + +parseHide :: Parser MkJuvixBlockOptions +parseHide = symbol optionHide $> MkJuvixBlockOptionsHide + +parseExtractModule :: Parser MkJuvixBlockOptions +parseExtractModule = do + symbol optionExtractModuleStatements + dropNum <- fromMaybe 0 <$> optional decimal + return (MkJuvixBlockOptionsExtractModule (JuvixBlockOptionsExtractModule dropNum)) diff --git a/src/Juvix/Compiler/Backend/Markdown/Data/Types.hs b/src/Juvix/Compiler/Backend/Markdown/Data/Types.hs index 6ed14f423..f9e3fc8b9 100644 --- a/src/Juvix/Compiler/Backend/Markdown/Data/Types.hs +++ b/src/Juvix/Compiler/Backend/Markdown/Data/Types.hs @@ -1,20 +1,20 @@ -module Juvix.Compiler.Backend.Markdown.Data.Types where +module Juvix.Compiler.Backend.Markdown.Data.Types + ( module Juvix.Compiler.Backend.Markdown.Data.Types, + module Juvix.Compiler.Backend.Markdown.Data.MkJuvixBlockOptions, + ) +where import Commonmark qualified as MK import Data.Text qualified as T +import Juvix.Compiler.Backend.Markdown.Data.MkJuvixBlockOptions import Juvix.Data.Loc import Juvix.Prelude hiding (Raw) import Juvix.Prelude.Pretty import Text.Show qualified as Show -newtype MkJuvixBlockOptions = MkJuvixBlockOptions - { _mkJuvixBlockOptionsHide :: Bool - } - deriving stock (Eq, Ord) - data JuvixCodeBlock = JuvixCodeBlock { _juvixCodeBlock :: Text, - _juvixCodeBlockOptions :: MkJuvixBlockOptions, + _juvixCodeBlockOptions :: Text, _juvixCodeBlockInterval :: Maybe Interval } deriving stock (Eq, Ord) @@ -26,33 +26,16 @@ data TextBlock = TextBlock deriving stock (Eq, Ord) makeLenses ''JuvixCodeBlock -makeLenses ''MkJuvixBlockOptions makeLenses ''TextBlock -defaultMkJuvixBlockOptions :: MkJuvixBlockOptions -defaultMkJuvixBlockOptions = - MkJuvixBlockOptions - { _mkJuvixBlockOptionsHide = False - } - instance Show TextBlock where show t = T.unpack (t ^. textBlock) -textJuvixBlockOptions :: MkJuvixBlockOptions -> Text -textJuvixBlockOptions opt = - T.intercalate " " $ - catMaybes - [ if opt ^. mkJuvixBlockOptionsHide then Just "hide" else Nothing - ] - -instance Show MkJuvixBlockOptions where - show opt = T.unpack (textJuvixBlockOptions opt) - textJuvixCodeBlock :: JuvixCodeBlock -> Text textJuvixCodeBlock cb = mconcat [ "```juvix", - textJuvixBlockOptions (cb ^. juvixCodeBlockOptions), + cb ^. juvixCodeBlockOptions, nl, cb ^. juvixCodeBlock, "```" @@ -83,19 +66,6 @@ instance Monoid TextBlock where } mappend = (<>) -instance Semigroup MkJuvixBlockOptions where - a <> b = - MkJuvixBlockOptions - { _mkJuvixBlockOptionsHide = a ^. mkJuvixBlockOptionsHide || b ^. mkJuvixBlockOptionsHide - } - -instance Monoid MkJuvixBlockOptions where - mempty = - MkJuvixBlockOptions - { _mkJuvixBlockOptionsHide = False - } - mappend = (<>) - instance Semigroup Mk where a <> MkNull = a MkNull <> a = a @@ -190,25 +160,20 @@ instance MK.IsInline TextBlock where toTextBlock t | otherwise = mempty -getJuvixBlockOptions :: Text -> MkJuvixBlockOptions -getJuvixBlockOptions = \case - "hide" -> mempty {_mkJuvixBlockOptionsHide = True} - _ -> mempty - nl' :: Mk nl' = toMK nl processCodeBlock :: Text -> Text -> Maybe Interval -> Mk processCodeBlock info t loc = - case T.splitOn " " (T.strip info) of - ("juvix" : opts) -> + case T.stripPrefix "juvix" (T.strip info) of + Just opts -> MkJuvixCodeBlock JuvixCodeBlock { _juvixCodeBlock = t, - _juvixCodeBlockOptions = foldMap getJuvixBlockOptions opts, + _juvixCodeBlockOptions = opts, _juvixCodeBlockInterval = loc } - _ -> + Nothing -> let b = "```" <> info <> t <> "```" in MkTextBlock TextBlock {_textBlock = b, _textBlockInterval = loc} diff --git a/src/Juvix/Compiler/Backend/Markdown/Error.hs b/src/Juvix/Compiler/Backend/Markdown/Error.hs index ad1ec48b4..a9cc15541 100644 --- a/src/Juvix/Compiler/Backend/Markdown/Error.hs +++ b/src/Juvix/Compiler/Backend/Markdown/Error.hs @@ -1,18 +1,24 @@ module Juvix.Compiler.Backend.Markdown.Error where +import Juvix.Compiler.Backend.Markdown.Data.Types import Juvix.Compiler.Concrete.Language import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.Scoping.Error.Pretty +import Juvix.Parser.Error.Base import Juvix.Prelude data MarkdownBackendError = ErrInternalNoMarkdownInfo NoMarkdownInfoError | ErrNoJuvixCodeBlocks NoJuvixCodeBlocksError + | ErrInvalidExtractModuleBlock InvalidExtractModuleBlockError + | ErrInvalidCodeBlockAttribtues InvalidCodeBlockAttributesError deriving stock (Show) instance ToGenericError MarkdownBackendError where genericError = \case ErrInternalNoMarkdownInfo e -> genericError e ErrNoJuvixCodeBlocks e -> genericError e + ErrInvalidExtractModuleBlock e -> genericError e + ErrInvalidCodeBlockAttribtues e -> genericError e newtype NoMarkdownInfoError = NoMarkdownInfoError { _noMarkdownInfoFilepath :: Path Abs File @@ -49,3 +55,35 @@ instance ToGenericError NoJuvixCodeBlocksError where where i :: Interval i = singletonInterval . mkInitialLoc $ _noJuvixCodeBlocksErrorFilepath + +data InvalidExtractModuleBlockError = InvalidExtractModuleBlockError + { _invalidExtractModuleBlockErrorInterval :: Maybe Interval, + _invalidExtractModuleBlockErrorPath :: Path Abs File + } + deriving stock (Show) + +instance ToGenericError InvalidExtractModuleBlockError where + genericError InvalidExtractModuleBlockError {..} = do + let msg :: Doc Ann + msg = "Juvix code blocks with attribute" <+> optionExtractModuleStatements <+> "must contain a single local module" + return + GenericError + { _genericErrorLoc = i, + _genericErrorMessage = prettyError msg, + _genericErrorIntervals = [i] + } + where + i :: Interval + i = + fromMaybe + (singletonInterval (mkInitialLoc (_invalidExtractModuleBlockErrorPath))) + _invalidExtractModuleBlockErrorInterval + +newtype InvalidCodeBlockAttributesError = InvalidCodeBlockAttributesError + { _invalidCodeBlockAttributesErrorMegaparsecError :: MegaparsecError + } + deriving stock (Show) + +instance ToGenericError InvalidCodeBlockAttributesError where + genericError InvalidCodeBlockAttributesError {..} = + genericError _invalidCodeBlockAttributesErrorMegaparsecError diff --git a/src/Juvix/Compiler/Backend/Markdown/Translation/FromTyped/Source.hs b/src/Juvix/Compiler/Backend/Markdown/Translation/FromTyped/Source.hs index 80fe7851d..7e652651d 100644 --- a/src/Juvix/Compiler/Backend/Markdown/Translation/FromTyped/Source.hs +++ b/src/Juvix/Compiler/Backend/Markdown/Translation/FromTyped/Source.hs @@ -86,7 +86,7 @@ fromJuvixMarkdown opts = do _processingStateStmtsSeparation = sepr, _processingStateStmts = indModuleFilter $ m ^. Concrete.moduleBody } - (_, r) <- runState st . runReader htmlOptions . runReader opts $ go + (_, r) <- runState st . runReader htmlOptions . runReader opts $ go fname return $ MK.toPlainText r Nothing -> throw @@ -104,34 +104,49 @@ go :: ( Members '[ Reader HtmlRender.HtmlOptions, Reader ProcessJuvixBlocksArgs, - State ProcessingState + State ProcessingState, + Error MarkdownBackendError ] r ) => + Path Abs File -> Sem r Mk -go = do +go fname = do stmts <- gets @ProcessingState (^. processingStateStmts) sepr <- gets @ProcessingState (^. processingStateStmtsSeparation) mk <- gets @ProcessingState (^. processingStateMk) - case (sepr, stmts) of - ([], _) -> return mk - ((n : ns), _) -> do + case sepr of + [] -> return mk + (n : ns) -> do case mk of MkNull -> return mk MkTextBlock _ -> return mk MkConcat l r -> do modify (set processingStateMk l) - lS <- go + lS <- go fname modify (set processingStateMk r) - MkConcat lS <$> go + MkConcat lS <$> go fname MkJuvixCodeBlock j -> do + opts <- case parseJuvixBlockOptions fname (j ^. juvixCodeBlockOptions) of + Left e -> + throw + ( ErrInvalidCodeBlockAttribtues + (InvalidCodeBlockAttributesError e) + ) + Right o -> return o + m <- asks @ProcessJuvixBlocksArgs (^. processJuvixBlocksArgsModule) isFirstBlock <- gets @ProcessingState (^. processingStateFirstBlock) - let stmts' = take n stmts + stmts' <- + let blockStmts = take n stmts + in case opts of + MkJuvixBlockOptionsExtractModule o -> + checkExtractModule j (o ^. juvixBlockOptionsExtractModuleDrop) blockStmts + _ -> return blockStmts htmlStatements :: [Html] <- mapM (\s -> goRender s <> pure htmlSemicolon) stmts' @@ -153,15 +168,14 @@ go = do Text.intercalate "\n\n" $ map (toStrict . Html.renderHtml) htmlStatements - let _processingStateMk = - if j ^. juvixCodeBlockOptions . mkJuvixBlockOptionsHide - then MkNull - else - MkTextBlock - TextBlock - { _textBlock = Text.replace "\n" "
" resHtml, - _textBlockInterval = j ^. juvixCodeBlockInterval - } + let _processingStateMk = case opts of + MkJuvixBlockOptionsHide -> MkNull + _ -> + MkTextBlock + TextBlock + { _textBlock = Text.replace "\n" "
" resHtml, + _textBlockInterval = j ^. juvixCodeBlockInterval + } let newState = ProcessingState { _processingStateFirstBlock = False, @@ -171,6 +185,20 @@ go = do } modify @ProcessingState $ const newState return _processingStateMk + where + checkExtractModule :: JuvixCodeBlock -> Int -> [Concrete.Statement 'Concrete.Scoped] -> Sem r [Concrete.Statement 'Concrete.Scoped] + checkExtractModule j dropNum xs = case xs of + [Concrete.StatementModule m] -> do + return (drop dropNum (indModuleFilter (m ^. Concrete.moduleBody))) + _ -> + throw + ( ErrInvalidExtractModuleBlock + ( InvalidExtractModuleBlockError + { _invalidExtractModuleBlockErrorPath = fname, + _invalidExtractModuleBlockErrorInterval = j ^. juvixCodeBlockInterval + } + ) + ) goRender :: (Concrete.PrettyPrint c, Members '[Reader HtmlRender.HtmlOptions, Reader ProcessJuvixBlocksArgs] r) => diff --git a/src/Juvix/Parser/Error.hs b/src/Juvix/Parser/Error.hs index bd2267020..8e2fa0f1f 100644 --- a/src/Juvix/Parser/Error.hs +++ b/src/Juvix/Parser/Error.hs @@ -1,4 +1,8 @@ -module Juvix.Parser.Error where +module Juvix.Parser.Error + ( module Juvix.Parser.Error, + module Juvix.Parser.Error.Base, + ) +where import Commonmark qualified as MK import Juvix.Compiler.Backend.Markdown.Error @@ -7,9 +11,9 @@ import Juvix.Compiler.Concrete.Pretty.Options (fromGenericOptions) import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.Scoping.Error.Pretty import Juvix.Compiler.Pipeline.Loader.PathResolver.Error import Juvix.Extra.Paths +import Juvix.Parser.Error.Base import Juvix.Prelude import Text.Megaparsec qualified as M -import Text.Megaparsec.Error (errorOffset) import Text.Parsec.Error qualified as P import Text.Parsec.Pos qualified as P @@ -33,43 +37,6 @@ instance ToGenericError ParserError where ErrDanglingJudoc e -> genericError e ErrMarkdownBackend e -> genericError e -instance Pretty MegaparsecError where - pretty (MegaparsecError b) = pretty (M.errorBundlePretty b) - -instance HasLoc MegaparsecError where - getLoc (MegaparsecError b) = singletonInterval (mkLoc offset sourcePos) - where - state :: M.PosState Text - state = M.bundlePosState b - offset = errorOffset (head (M.bundleErrors b)) - - sourcePos :: M.SourcePos - sourcePos = - (snd . head . fst) - (M.attachSourcePos errorOffset (M.bundleErrors b) state) - -newtype MegaparsecError = MegaparsecError - { _megaParsecError :: M.ParseErrorBundle Text Void - } - deriving stock (Show) - -instance ToGenericError MegaparsecError where - genericError e = - return - GenericError - { _genericErrorLoc = i, - _genericErrorMessage = mkAnsiText $ pretty @_ @AnsiStyle e, - _genericErrorIntervals = [i] - } - where - i = getLoc e - --- | Use only for debugging -fromMegaParsecError :: Either MegaparsecError a -> a -fromMegaParsecError = \case - Left e -> error (prettyText e) - Right a -> a - newtype CommonmarkError = CommonmarkError { _commonMarkError :: MK.ParseError } diff --git a/src/Juvix/Parser/Error/Base.hs b/src/Juvix/Parser/Error/Base.hs new file mode 100644 index 000000000..1232dc2a2 --- /dev/null +++ b/src/Juvix/Parser/Error/Base.hs @@ -0,0 +1,43 @@ +module Juvix.Parser.Error.Base where + +import Juvix.Prelude +import Juvix.Prelude.Pretty +import Text.Megaparsec qualified as M +import Text.Megaparsec.Error (errorOffset) + +instance Pretty MegaparsecError where + pretty (MegaparsecError b) = pretty (M.errorBundlePretty b) + +instance HasLoc MegaparsecError where + getLoc (MegaparsecError b) = singletonInterval (mkLoc offset sourcePos) + where + state :: M.PosState Text + state = M.bundlePosState b + offset = errorOffset (head (M.bundleErrors b)) + + sourcePos :: M.SourcePos + sourcePos = + (snd . head . fst) + (M.attachSourcePos errorOffset (M.bundleErrors b) state) + +newtype MegaparsecError = MegaparsecError + { _megaParsecError :: M.ParseErrorBundle Text Void + } + deriving stock (Show) + +instance ToGenericError MegaparsecError where + genericError e = + return + GenericError + { _genericErrorLoc = i, + _genericErrorMessage = mkAnsiText $ pretty @_ @AnsiStyle e, + _genericErrorIntervals = [i] + } + where + i = getLoc e + +-- | Use only for debugging +fromMegaParsecError :: Either MegaparsecError a -> a +fromMegaParsecError = \case + Left e -> error (prettyText e) + Right a -> a diff --git a/test/BackendMarkdown/Negative.hs b/test/BackendMarkdown/Negative.hs index 2617fb9d4..0cb4b9a47 100644 --- a/test/BackendMarkdown/Negative.hs +++ b/test/BackendMarkdown/Negative.hs @@ -2,6 +2,9 @@ module BackendMarkdown.Negative where import Base import Juvix.Compiler.Backend.Markdown.Error +import Juvix.Compiler.Backend.Markdown.Translation.FromTyped.Source +import Juvix.Compiler.Concrete qualified as Concrete +import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.Scoping qualified as Scoper import Juvix.Parser.Error type FailMsg = String @@ -10,7 +13,7 @@ data NegTest = NegTest { _name :: String, _relDir :: Path Rel Dir, _file :: Path Rel File, - _checkErr :: ParserError -> Maybe FailMsg + _checkErr :: JuvixError -> Maybe FailMsg } testDescr :: NegTest -> TestDescr @@ -22,12 +25,33 @@ testDescr NegTest {..} = _testRoot = tRoot, _testAssertion = Single $ do entryPoint <- testDefaultEntryPointIO tRoot file' - result <- testTaggedLockedToIO (runIOEither entryPoint upToParsing) - case mapLeft fromJuvixError result of - Left (Just err) -> whenJust (_checkErr err) assertFailure - Right _ -> assertFailure "Unexpected success." - Left Nothing -> assertFailure "Unexpected error." + result <- testTaggedLockedToIO (runIOEither entryPoint upToScoping) + case result of + Left err -> whenJust (_checkErr err) assertFailure + Right (_, pipelineRes) -> checkResult pipelineRes } + where + checkResult :: PipelineResult Scoper.ScoperResult -> IO () + checkResult PipelineResult {..} = do + let m = _pipelineResult ^. Scoper.resultModule + opts = + ProcessJuvixBlocksArgs + { _processJuvixBlocksArgsConcreteOpts = Concrete.defaultOptions, + _processJuvixBlocksArgsUrlPrefix = "", + _processJuvixBlocksArgsIdPrefix = "", + _processJuvixBlocksArgsNoPath = True, + _processJuvixBlocksArgsExt = ".html", + _processJuvixBlocksArgsStripPrefix = "", + _processJuvixBlocksArgsComments = Comments mempty, + _processJuvixBlocksArgsFolderStructure = False, + _processJuvixBlocksArgsModule = m, + _processJuvixBlocksArgsOutputDir = + root $(mkRelDir "markdown") + } + res = fromJuvixMarkdown' opts + case res of + Left err -> whenJust (_checkErr (JuvixError err)) assertFailure + Right _ -> assertFailure "Unexpected success" allTests :: TestTree allTests = @@ -38,8 +62,8 @@ allTests = root :: Path Abs Dir root = relToProject $(mkRelDir "tests/negative") -wrongError :: Maybe FailMsg -wrongError = Just "Incorrect error" +wrongError :: JuvixError -> Maybe FailMsg +wrongError e = Just ("unexpected error: " <> unpack (renderTextDefault e)) tests :: [NegTest] tests = @@ -47,14 +71,35 @@ tests = "Empty file" $(mkRelDir "Markdown") $(mkRelFile "Empty.juvix.md") - $ \case - ErrMarkdownBackend (ErrNoJuvixCodeBlocks _) -> Nothing - _ -> wrongError, + $ \e -> case fromJuvixError e of + Just (ErrMarkdownBackend (ErrNoJuvixCodeBlocks _)) -> Nothing + _ -> wrongError e, NegTest "No Juvix code blocks" $(mkRelDir "Markdown") $(mkRelFile "NoJuvixCodeBlocks.juvix.md") - $ \case - ErrMarkdownBackend (ErrNoJuvixCodeBlocks _) -> Nothing - _ -> wrongError + $ \e -> case fromJuvixError e of + Just (ErrMarkdownBackend (ErrNoJuvixCodeBlocks _)) -> Nothing + _ -> wrongError e, + NegTest + "extract-module-statements with no local module" + $(mkRelDir "Markdown") + $(mkRelFile "InvalidExtractModuleBlockNoModule.juvix.md") + $ \e -> case fromJuvixError e of + Just (ErrInvalidExtractModuleBlock _) -> Nothing + _ -> wrongError e, + NegTest + "extract-module-statements with no local module" + $(mkRelDir "Markdown") + $(mkRelFile "InvalidExtractModuleBlockNotJustModule.juvix.md") + $ \e -> case fromJuvixError e of + Just (ErrInvalidExtractModuleBlock _) -> Nothing + _ -> wrongError e, + NegTest + "code block with both hide and extract-module-statements" + $(mkRelDir "Markdown") + $(mkRelFile "InvalidCodeBlockExtraAttribute.juvix.md") + $ \e -> case fromJuvixError e of + Just (ErrInvalidCodeBlockAttribtues _) -> Nothing + _ -> wrongError e ] diff --git a/tests/negative/Markdown/InvalidCodeBlockExtraAttribute.juvix.md b/tests/negative/Markdown/InvalidCodeBlockExtraAttribute.juvix.md new file mode 100644 index 000000000..056f7a359 --- /dev/null +++ b/tests/negative/Markdown/InvalidCodeBlockExtraAttribute.juvix.md @@ -0,0 +1,9 @@ +# Title + +```juvix +module InvalidExtractModuleBlockExtraAttribute; +``` + +```juvix hide extract-module-statements +type T := t; +``` diff --git a/tests/negative/Markdown/InvalidCodeBlockUnknownAttribute.juvix.md b/tests/negative/Markdown/InvalidCodeBlockUnknownAttribute.juvix.md new file mode 100644 index 000000000..e091b3d25 --- /dev/null +++ b/tests/negative/Markdown/InvalidCodeBlockUnknownAttribute.juvix.md @@ -0,0 +1,9 @@ +# Title + +```juvix +module InvalidExtractModuleBlockUnknownAttribute; +``` + +```juvix invalid-attribute +type T := t; +``` diff --git a/tests/negative/Markdown/InvalidExtractModuleBlockNoModule.juvix.md b/tests/negative/Markdown/InvalidExtractModuleBlockNoModule.juvix.md new file mode 100644 index 000000000..bcb6b4142 --- /dev/null +++ b/tests/negative/Markdown/InvalidExtractModuleBlockNoModule.juvix.md @@ -0,0 +1,9 @@ +# Title + +```juvix +module InvalidExtractModuleBlockNoModule; +``` + +```juvix extract-module-statements +type T := t; +``` diff --git a/tests/negative/Markdown/InvalidExtractModuleBlockNotJustModule.juvix.md b/tests/negative/Markdown/InvalidExtractModuleBlockNotJustModule.juvix.md new file mode 100644 index 000000000..a54edf9da --- /dev/null +++ b/tests/negative/Markdown/InvalidExtractModuleBlockNotJustModule.juvix.md @@ -0,0 +1,12 @@ +# Title + +```juvix +module InvalidExtractModuleBlockNotJustModule; +``` + +```juvix extract-module-statements +module Inner; + type T1 := t1; +end; +type T2 := t2; +``` diff --git a/tests/positive/Markdown/Test.juvix.md b/tests/positive/Markdown/Test.juvix.md index d9d76cb7c..7146c7971 100644 --- a/tests/positive/Markdown/Test.juvix.md +++ b/tests/positive/Markdown/Test.juvix.md @@ -22,6 +22,23 @@ fib : Nat → Nat → Nat → Nat fibonacci (n : Nat) : Nat := fib n 0 1; ``` +The `extract-module-statements` attribute can be used to display only the statements contained in a module in the output. + +```juvix extract-module-statements +module Foo1; + type T := t; +end; +``` + +You can pass a number to the `extract-module-statements` attribute to drop that number of statements from the start of the module. + +```juvix extract-module-statements 1 +module Foo2; + type T := t; + a : T := t; +end; +``` + Commands like `typecheck` and `compile` can be used with Juvix Markdown files. ```juvix @@ -89,4 +106,4 @@ We also use other markup for documentation such as: | n zero := n | n (suc m) := suc (add n m); end; - ``` \ No newline at end of file + ``` diff --git a/tests/positive/Markdown/markdown/Test.md b/tests/positive/Markdown/markdown/Test.md index 0a45d0a79..50f1e7793 100644 --- a/tests/positive/Markdown/markdown/Test.md +++ b/tests/positive/Markdown/markdown/Test.md @@ -9,11 +9,19 @@ Certain blocks can be hidden from the output by adding the `hide` attribute, as -
fib : Nat  Nat  Nat  Nat
| zero x1 _ := x1
| (suc n) x1 x2 := fib n x2 (x1 + x2);

fibonacci (n : Nat) : Nat := fib n 0 1;
+
fib : Nat  Nat  Nat  Nat
| zero x1 _ := x1
| (suc n) x1 x2 := fib n x2 (x1 + x2);

fibonacci (n : Nat) : Nat := fib n 0 1;
+ +The `extract-module-statements` attribute can be used to display only the statements contained in a module in the output. + +
type T := t;
+ +You can pass a number to the `extract-module-statements` attribute to drop that number of statements from the start of the module. + +
a : T := t;
Commands like `typecheck` and `compile` can be used with Juvix Markdown files. -
main : IO := readLn (printNatLn  fibonacci  stringToNat);
+
main : IO := readLn (printNatLn  fibonacci  stringToNat);
Other code blocks are not touched, e.g: @@ -57,8 +65,8 @@ We also use other markup for documentation such as: Initial function arguments that match variables or wildcards in all clauses can be moved to the left of the colon in the function definition. For example, -
module move-to-left;
import Stdlib.Data.Nat open;

add
(n : Nat) : Nat -> Nat
| zero := n
| (suc m) := suc (add n m);
end;
+
module move-to-left;
import Stdlib.Data.Nat open;

add
(n : Nat) : Nat -> Nat
| zero := n
| (suc m) := suc (add n m);
end;
is equivalent to -
module example-add;
import Stdlib.Data.Nat open;

add
: Nat -> Nat -> Nat
| n zero := n
| n (suc m) := suc (add n m);
end;
+
module example-add;
import Stdlib.Data.Nat open;

add
: Nat -> Nat -> Nat
| n zero := n
| n (suc m) := suc (add n m);
end;