packages feed

rzk-0.1.0: src/Language/Rzk/Syntax.hs

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Language.Rzk.Syntax (
  module Language.Rzk.Syntax.Abs,

  parseModule,
  parseModuleRzk,
  parseModuleFile,
  parseTerm,
  printTree,
  tryExtractMarkdownCodeBlocks,
  extractMarkdownCodeBlocks,
) where

import qualified Data.List as List
import Data.Char (isSpace)

import Language.Rzk.Syntax.Abs
import Language.Rzk.Syntax.Print (printTree)

import Language.Rzk.Syntax.Lex (tokens)
import Language.Rzk.Syntax.Par (pModule, pTerm)
import Language.Rzk.Syntax.Layout (resolveLayout)

parseModule :: String -> Either String Module
parseModule = pModule . resolveLayout True . tokens . tryExtractMarkdownCodeBlocks "rzk"

parseModuleRzk :: String -> Either String Module
parseModuleRzk = pModule . resolveLayout True . tokens

parseModuleFile :: FilePath -> IO (Either String Module)
parseModuleFile path = do
  parseModule <$> readFile path

parseTerm :: String -> Either String Term
parseTerm = pTerm . tokens

tryExtractMarkdownCodeBlocks :: String -> String -> String
tryExtractMarkdownCodeBlocks alias input
  | ("```" <> alias <> "\n") `List.isInfixOf` input = extractMarkdownCodeBlocks alias input
  | otherwise = input

-- | Extract rzk code from a Markdown file
--
-- >>> putStrLn $ detectMarkdownCodeBlocks "\n```rzk\n#lang rzk-1\n```\nasd asd\n```rzk\n#def x : U\n  := U\n``` asda"
-- #lang rzk-1
-- #def x : U
--   := U
extractMarkdownCodeBlocks :: String -> String -> String
extractMarkdownCodeBlocks alias = unlines . blankNonCode True . map trim . lines
  where
    blankNonCode _toBlank [] = []
    blankNonCode True (line : lines_)
      | line == "```" <> alias  = "" : blankNonCode False lines_
      | otherwise               = "" : blankNonCode True  lines_
    blankNonCode False (line : lines_)
      | line == "```"           = "" : blankNonCode True lines_
      | otherwise               = line : blankNonCode False lines_

    trim = List.dropWhileEnd isSpace