Skip to content

Commit

Permalink
Add quoting and continue macro expansion
Browse files Browse the repository at this point in the history
  • Loading branch information
Joshua Grosso committed Jan 9, 2018
1 parent e7b7a48 commit be28da9
Show file tree
Hide file tree
Showing 16 changed files with 138 additions and 126 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ cabal.sandbox.config
cabal.project.local
.HTF/
.ghc.environment.*

resources/autogenerated/*
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ But, there's nothing (that I'm aware of) for all three.

Hence, meet Lihsp! It's a Lisp-like language (with macros and all) that transpiles to Eta, fulfilling all the requirements listed above. :tada:

## TODO

- [ ] Cleanup
- [ ] Use qualified imports
- [ ] Standardize use of `($)`
- [ ] Rename `AST` to `Target` (?) and `Parse` to `Lisp` (?)

## Future Plans
- Finish the JVM backend
- Add a PureScript backend as well (or maybe first, instead of targeting the JVM initially) to alternatively take advantage of the JavaScript ecosystem.
Expand Down
7 changes: 4 additions & 3 deletions lihsp.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
--
-- see: https://github.com/sol/hpack
--
-- hash: 596fc166e921fb1d089e6c0008a31999fb69a79ec742eb8f8582f5c30b048f6a
-- hash: a29ef77f7e9aefca0e574bb8c6b5da0949c57ca81f4668850a02116c3b1a64d0

name: lihsp
version: 0.1.0.0
Expand All @@ -20,11 +20,11 @@ cabal-version: >= 1.10
extra-source-files:
README.md
scripts/build.sh
scripts/clean.sh

data-files:
resources/autogenerated/macros/Header.hs
resources/macros/Footer.hs
resources/macros/Header.hs
resources/macros/Imports.hs

source-repository head
type: git
Expand Down Expand Up @@ -55,6 +55,7 @@ library
Lihsp.Utils.Display
Lihsp.Utils.Recursion
Lihsp.Utils.Resources
Lihsp.Utils.String
other-modules:
Paths_lihsp
default-language: Haskell2010
Expand Down
2 changes: 1 addition & 1 deletion resources/macros/Footer.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
%%%MACRO_DEFINITION%%%

main :: IO ()
main = aUTOGENERATED_MACRO_FUNCTION %%%%ARGUMENTS%%% >>= print
main = %%%MACRO_NAME%%% %%%ARGUMENTS%%% >>= print
23 changes: 0 additions & 23 deletions resources/macros/Header.hs

This file was deleted.

1 change: 0 additions & 1 deletion resources/macros/Imports.hs

This file was deleted.

4 changes: 2 additions & 2 deletions scripts/build.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ set -eu
set -o pipefail

echo "Copying macro header..."
cat resources/macros/Imports.hs > resources/macros/Header.hs
cat src/Lihsp/Parse/AST.hs >> resources/macros/Header.hs
mkdir -p resources/autogenerated/macros
cp src/Lihsp/Parse/AST.hs resources/autogenerated/macros/Header.hs
echo "Macro header copied!"

stack build
8 changes: 8 additions & 0 deletions scripts/clean.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
set -eu
set -o pipefail

echo "Cleaning autogenerated resources..."
rm -rf resources/autogenerated
echo "Autogenerated resources cleaned!"

stack clean
24 changes: 3 additions & 21 deletions src/Lihsp/AST.hs
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,15 @@ data Literal
= LChar Char
| LInt Int
| LList [Expression]
| LSymbol Identifier
deriving (Eq)

instance Show Literal where
show :: Literal -> String
show (LInt int) = show int
show (LChar char) = surround SingleQuotes [char]
show (LList list) = surround SquareBrackets $ delimit Commas (map show list)
show (LSymbol symbol) = "Literal (LSymbol \"" <> symbol <> "\")"

data Statement
= SDataDeclaration DataDeclaration
Expand Down Expand Up @@ -238,7 +240,7 @@ instance Show MacroDefinition where
show :: MacroDefinition -> String
show macroDefinition =
delimit Newlines $
macroDefinition ^. name :
(macroDefinition ^. name <> " :: [Expression] -> [Expression]") :
map
(showFunctionDefinition $ macroDefinition ^. name)
(macroDefinition ^. definitions)
Expand Down Expand Up @@ -267,23 +269,3 @@ instance Show TypeSynonym where
show typeSynonym =
"type " <> show (typeSynonym ^. alias) <> " = " <>
show (typeSynonym ^. definition)

-- TODO Either replace with `MonoTraversable` or make `Expression` polymorphic
-- (in which case, use `Traversable`, recursion schemes, etc.). The latter
-- would be preferable.
-- TODO Remove the dependency on `Monad` (since the standard `traverse` only
-- requires an `Applicative` instance).
traverseExpression ::
(Monad m) => (Expression -> m Expression) -> Expression -> m Expression
traverseExpression f x =
case x of
EFunctionApplication functionApplication ->
let newArguments =
traverse (traverseExpression f) (functionApplication ^. arguments)
newFunction = traverseExpression f (functionApplication ^. function)
in f =<<
(EFunctionApplication <$>
(FunctionApplication <$> newFunction <*> newArguments))
EIdentifier _ -> f x
ELetBlock _ -> f x
ELiteral _ -> f x
2 changes: 1 addition & 1 deletion src/Lihsp/Eval.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ execInterpreter fileName = do

evalSource :: (MonadError Error m, MonadIO m) => String -> m String
evalSource source =
withTempFile "TempMaro.hs" $ \fileName handle -> do
withTempFile "TempEval.hs" $ \fileName handle -> do
liftIO $ hPutStr handle source
execInterpreter fileName
95 changes: 61 additions & 34 deletions src/Lihsp/Macros.hs
Original file line number Diff line number Diff line change
@@ -1,57 +1,84 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}

module Lihsp.Macros where

import Control.Lens.Operators ((.~))
import Control.Monad.Except (MonadError)
import Control.Lens.Operators ((^.))
import Control.Monad.Except (MonadError, throwError)
import Control.Monad.IO.Class (MonadIO, liftIO)

import Data.Function ((&))
import Data.Semigroup ((<>))
import Data.Text (pack, replace, unpack)

import Lihsp.AST (Expression, MacroDefinition, name, traverseExpression)
import Lihsp.AST (name)
import qualified Lihsp.AST as AST (MacroDefinition)
import Lihsp.Error (Error(MacroError))
import Lihsp.Eval (evalSource)
import qualified Lihsp.Parse as Parse (Expression)
import Lihsp.Parse (parseProgram)
import qualified Lihsp.Parse as Parse
( Expression(LiteralChar, LiteralInt, SExpression, Symbol)
, parseProgram
)
import Lihsp.Utils.Recursion (Recursive(bottomUpTraverse))
import Lihsp.Utils.Resources (readDataFile)
import Lihsp.Utils.String (replace)

generateMacroProgram ::
(MonadIO m) => MacroDefinition -> [Expression] -> m String
generateMacroProgram macroDefinition applicationArguments = do
fileHeader <- liftIO $ readDataFile "resources/macros/Header.hs"
fileFooter <- liftIO getFileFooter
return $ fileHeader <> show fileContents <> fileFooter
(MonadIO m) => AST.MacroDefinition -> [Parse.Expression] -> m String
generateMacroProgram macroDefinition applicationArguments =
(<>) <$> liftIO getFileHeader <*> liftIO getFileFooter
where
getFileHeader = readDataFile "autogenerated/macros/Header.hs"
getFileFooter =
let insertApplicationArguments =
let applicationArgumentsPlaceholder = "%%%ARGUMENTS%%%"
in unpack .
replace
in replace
applicationArgumentsPlaceholder
(pack $ show applicationArguments) .
pack
(show applicationArguments)
insertDefinitionBody =
let definitionBodyPlaceholder = "%%%MACRO_DEFINITION%%%"
in unpack .
replace definitionBodyPlaceholder (pack $ show macroDefinition) .
pack
in insertApplicationArguments . insertDefinitionBody <$>
readDataFile "resources/macros/Footer.hs"
in replace definitionBodyPlaceholder (show macroDefinition)
insertDefinitionName =
let definitionNamePlaceholder = "%%%MACRO_NAME%%%"
in replace definitionNamePlaceholder (macroDefinition ^. name)
in insertApplicationArguments .
insertDefinitionName . insertDefinitionBody <$>
readDataFile "macros/Footer.hs"

expandMacroCall ::
expandMacros ::
(MonadError Error m, MonadIO m)
=> MacroDefinition
-> [Expression]
-> m [Parse.Expression]
expandMacroCall macroDefinition args =
generateMacroProgram macroDefinition args >>= evalSource >>= parseProgram
=> [AST.MacroDefinition]
-> Parse.Expression
-> m Parse.Expression
expandMacros environment =
bottomUpTraverse $ \expression ->
case expression of
Parse.LiteralChar _ -> return expression
Parse.LiteralInt _ -> return expression
Parse.SExpression functionApplication ->
lookupMacroDefinition environment (head functionApplication) >>= \case
Just macroDefinition ->
Parse.SExpression <$>
expandMacroApplication macroDefinition (tail functionApplication)
Nothing -> return expression
Parse.Symbol _ -> return expression
where
expandMacroApplication macroDefinition args =
generateMacroProgram macroDefinition args >>= evalSource >>=
Parse.parseProgram

expandMacros :: Expression -> IO Expression
expandMacros =
traverseExpression $ \expression ->
case macroApplication expression of
Just (macroName, arguments) -> expandMacroCall
Nothing -> return expression
lookupMacroDefinition ::
(MonadError Error m)
=> [AST.MacroDefinition]
-> Parse.Expression
-> m (Maybe AST.MacroDefinition)
lookupMacroDefinition environment identifierExpression =
case identifierExpression of
Parse.LiteralChar _ -> return Nothing
Parse.LiteralInt _ -> return Nothing
Parse.SExpression _ -> return Nothing
Parse.Symbol identifier ->
case filter
(\macroDefinition -> macroDefinition ^. name == identifier)
environment of
[] -> return Nothing
[macroDefinition] -> return $ Just macroDefinition
_ -> throwError (MacroError "0012")
24 changes: 3 additions & 21 deletions src/Lihsp/Normalize.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@

module Lihsp.Normalize where

import Control.Lens.Operators ((%~), (^.))
import Control.Monad ((>=>))
import Control.Monad.Except (MonadError, throwError)

import Data.Function ((&))

import Lihsp.AST
( ArgumentList(ArgumentList)
, DataDeclaration(DataDeclaration)
Expand All @@ -21,7 +18,7 @@ import Lihsp.AST
, ImportList(ImportList)
, LanguagePragma(LanguagePragma)
, LetBlock(LetBlock)
, Literal(LChar, LInt, LList)
, Literal(LChar, LInt, LList, LSymbol)
, MacroDefinition(MacroDefinition)
, QualifiedImport(QualifiedImport)
, RestrictedImport(RestrictedImport)
Expand All @@ -32,23 +29,19 @@ import Lihsp.AST
, TypeDefinition(ProperType, TypeConstructor)
, TypeSynonym(TypeSynonym)
, TypeclassInstance(TypeclassInstance)
, arguments
, function
)

import Lihsp.Error (Error(NormalizeError))
import qualified Lihsp.Parse as Parse
( Expression(LiteralChar, LiteralInt, LiteralList, SExpression,
Symbol)
( Expression(LiteralChar, LiteralInt, SExpression, Symbol)
)

normalizeExpression :: (MonadError Error m) => Parse.Expression -> m Expression
normalizeExpression (Parse.LiteralChar char) = return $ ELiteral (LChar char)
normalizeExpression (Parse.LiteralInt int) = return $ ELiteral (LInt int)
normalizeExpression (Parse.LiteralList list) =
ELiteral . LList <$> traverse normalizeExpression list
normalizeExpression (Parse.SExpression items) =
case items of
[Parse.Symbol "quote", expression] -> return $ toLihspLiteral expression
[Parse.Symbol "let", Parse.SExpression bindings', body] ->
let bindings =
traverse
Expand Down Expand Up @@ -154,14 +147,3 @@ normalizeStatement _ = throwError $ NormalizeError "0008"

normalizeProgram :: (MonadError Error m) => [Parse.Expression] -> m [Statement]
normalizeProgram = traverse normalizeStatement

bottomUpTraverse :: (Applicative m) => (Expression -> m Expression) -> Expression -> m Expression
bottomUpTraverse f x =
case x of
EFunctionApplication functionApplication ->
f . EFunctionApplication <$>
(FunctionApplication <$> f (functionApplication ^. function) <*>
traverse f (functionApplication ^. arguments))
EIdentifier _ -> f x
ELetBlock _ -> f x
ELiteral _ -> f x
Loading

0 comments on commit be28da9

Please sign in to comment.