~jojo/Carth

58f475edcf63ad30f0aafd711da977e4ba317bf2 — JoJo 2 years ago 922160a
Literate programming: Compile .org by untangling carth src blocks
5 files changed, 151 insertions(+), 1 deletions(-)

M README.org
M REFERENCE.org
M app/Main.hs
A examples/literate.org
A src/Literate.hs
M README.org => README.org +1 -0
@@ 9,6 9,7 @@ Purely functional programming with lisp-syntax. Less infix, more parens!
   - Static, Hindley-Milner typechecking à la ML
   - Currying
   - Compiled with LLVM-based backend
   - [[file:REFERENCE.org::#Literate-Carth][Native support for literate programming in Org-mode]]

** Examples
   Not much is working yet, but here's an example of something simple

M REFERENCE.org => REFERENCE.org +82 -0
@@ 57,3 57,85 @@ complete, but it may not be.
* TODO Compile time evaluation

* TODO Runtime
* TODO Literate Carth
  :PROPERTIES:
  :CUSTOM_ID: Literate-Carth
  :END:
  Carth has native support for literate programming with Org
  mode. Either use Emacs with Babel in Org-mode for an interactive
  session, or interpret/compile the file with ~carth~ just like a
  normal ~.carth~ file!

** Example
   Consider a file ~cool.org~ with the following content:

   #+BEGIN_SRC org

   ,#+TITLE: Literate Programming Rules!

   Literate programming is just really cool!

   ~carth~ will assume ~tangle~ = ~yes~ by default, but setting it
   explicitly won't hurt.

   ,#+BEGIN_SRC carth :tangle yes
   (define (main _)
     (printInt (id 1337)))
   ,#+END_SRC

   ,* The ~id~ function
     ~id~ is the identity function. It returns its argument unchanged.

     ,#+BEGIN_SRC carth
     (define (id x) x)
     ,#+END_SRC

   ,* How not to use ~id~
     Here is an example of how not to use ~id~. Note that this won't
     compile. We show this in a SRC block to get syntax highlighting etc,
     but as ~tangle~ is ~no~, this source block will be ignored by carth.

     ,#+BEGIN_SRC carth :tangle no
     (printInt id)
     ,#+END_SRC

   #+END_SRC

   When e.g. interpreting this file with ~carth i cool.org~, the Carth
   source will untangled from the rest of the document. Line numbers
   are preserved. The result of the untangling stage will be the
   following:

   #+BEGIN_SRC carth









   (define (main _)
     (printInt (id 1337)))






   (define (id x) x)











   #+END_SRC

   And for completeness, the result of interpreting that will be ~1337~.

M app/Main.hs => app/Main.hs +10 -1
@@ 4,9 4,11 @@ module Main where

import Data.Functor
import System.Exit
import System.FilePath
import qualified LLVM.AST

import Misc
import Literate
import qualified Ast
import Check
import Config


@@ 38,8 40,15 @@ compileFile f cfg =

parse' :: FilePath -> String -> IO Ast.Program
parse' f src = do
    src' <- if takeExtension f == ".org"
        then do
            putStrLn "Untangling org..."
            let s = untangleOrg src
            writeFile "out.untangled" s
            pure s
        else pure src
    putStrLn "Parsing..."
    case parse f src of
    case parse f src' of
        Left e -> putStrLn ("Parse error:\n" ++ show e) >> exitFailure
        Right p -> writeFile "out.parsed" (pretty p) $> p


A examples/literate.org => examples/literate.org +27 -0
@@ 0,0 1,27 @@
#+TITLE: Literate Programming Rules!

Literate programming is just really cool!

~carth~ will assume ~tangle~ = ~yes~ by default, but setting it
explicitly won't hurt.

#+BEGIN_SRC carth :tangle yes
(define (main _)
  (printInt (id 1337)))
#+END_SRC

* The ~id~ function
  ~id~ is the identity function. It returns its argument unchanged.

  #+BEGIN_SRC carth
  (define (id x) x)
  #+END_SRC

* How not to use ~id~
  Here is an example of how not to use ~id~. Note that this won't
  compile. We show this in a SRC block to get syntax highlighting etc,
  but as ~tangle~ is ~no~, this source block will be ignored by carth.

  #+BEGIN_SRC carth :tangle no
  (printInt id)
  #+END_SRC

A src/Literate.hs => src/Literate.hs +31 -0
@@ 0,0 1,31 @@
{-# LANGUAGE LambdaCase #-}

module Literate (untangleOrg) where

import Data.Char
import Data.List

untangleOrg :: String -> String
untangleOrg s = unlines (untangleOrg' False (lines s))
  where
    untangleOrg' inSrc = \case
        [] -> []
        x : xs -> if inSrc
            then if endSrc x
                then "" : untangleOrg' False xs
                else x : untangleOrg' True xs
            else "" : untangleOrg' (beginSrc x) xs

beginSrc :: String -> Bool
beginSrc l =
    let ws = words l
    in
        (length ws >= 2)
        && (map toLower (ws !! 0) == "#+begin_src")
        && (ws !! 1 == "carth")
        && case elemIndex ":tangle" ws of
               Just i -> length ws >= i + 2 && ws !! (i + 1) == "yes"
               Nothing -> True

endSrc :: String -> Bool
endSrc = (\ws -> length ws > 0 && map toLower (ws !! 0) == "#+end_src") . words