From 650fad910ccc209dcf9ed46cadaa996d72290dfc Mon Sep 17 00:00:00 2001 From: Vladislav Zavialov Date: Mon, 4 Nov 2019 17:54:37 +0300 Subject: [PATCH] Factor out language specifications (#32) --- Makefile | 6 +- default.nix | 2 +- foundry.cabal | 58 +++- src/Foundry.hs | 148 --------- src/bin/Foundry.hs | 32 ++ src/bin/Hask.hs | 32 ++ src/{ => bin}/HaskellToSdam.hs | 54 +-- src/{ => bin}/MorteToSdam.hs | 36 +- {lib => src/driver}/Source.hs | 0 {lib => src/driver}/Source/Input.hs | 0 {lib => src/driver}/Source/Input/KeyCode.hs | 0 {lib => src/driver}/Source/NewGen.hs | 79 +---- {lib => src/driver}/Source/Phaser.hs | 0 .../haskell/Source/Language/Haskell.hs} | 312 ++++++++++-------- src/lang/morte/Source/Language/Morte.hs | 192 +++++++++++ src/layout/Source/Layout.hs | 85 +++++ 16 files changed, 615 insertions(+), 421 deletions(-) delete mode 100644 src/Foundry.hs create mode 100644 src/bin/Foundry.hs create mode 100644 src/bin/Hask.hs rename src/{ => bin}/HaskellToSdam.hs (84%) rename src/{ => bin}/MorteToSdam.hs (74%) rename {lib => src/driver}/Source.hs (100%) rename {lib => src/driver}/Source/Input.hs (100%) rename {lib => src/driver}/Source/Input/KeyCode.hs (100%) rename {lib => src/driver}/Source/NewGen.hs (96%) rename {lib => src/driver}/Source/Phaser.hs (100%) rename src/{Hask.hs => lang/haskell/Source/Language/Haskell.hs} (74%) create mode 100644 src/lang/morte/Source/Language/Morte.hs create mode 100644 src/layout/Source/Layout.hs diff --git a/Makefile b/Makefile index 872a5c9..648beaa 100644 --- a/Makefile +++ b/Makefile @@ -7,16 +7,16 @@ dev/build: cabal v2-build dev/watch: - watchman-make -p '*.cabal' 'lib/**/*.hs' 'src/**/*.hs' -t dev/build + watchman-make -p '*.cabal' 'src/**/*.hs' -t dev/build dev/run: cabal v2-run -- foundry "./expr.morte" fmt: - ormolu -c --mode inplace `find lib src -name "*.hs"` + ormolu -c --mode inplace `find src -name "*.hs"` tags: - fast-tags -R lib src + fast-tags -R src clean: cabal v2-clean diff --git a/default.nix b/default.nix index 923cffe..4c03b31 100644 --- a/default.nix +++ b/default.nix @@ -70,7 +70,7 @@ pkgs.stdenv.mkDerivation rec { src = ./.; buildCommand = '' mkdir -p $out/bin $out/home - cp -r $src/foundry.cabal $src/lib $src/src . + cp -r $src/foundry.cabal $src/src . HOME=$out/home \ cabal --offline --config-file /dev/null v2-install \ --builddir=$out/dist \ diff --git a/foundry.cabal b/foundry.cabal index 03beea2..092fa3e 100644 --- a/foundry.cabal +++ b/foundry.cabal @@ -3,6 +3,19 @@ name: foundry version: 0.1.0.0 build-type: Simple +library source-layout + exposed-modules: Source.Layout + build-depends: base >=4.7, unordered-containers, + hashable, text, + sdam + hs-source-dirs: src/layout + default-language: Haskell2010 + default-extensions: RankNTypes + ghc-options: -Wall -O2 + ghc-prof-options: -fprof-auto + -auto-all + -caf-all + library exposed-modules: Source @@ -32,8 +45,9 @@ library , slay-combinators , slay-cairo , sdam + , source-layout - hs-source-dirs: lib + hs-source-dirs: src/driver default-language: Haskell2010 @@ -42,13 +56,37 @@ library -auto-all -caf-all +library source-language-haskell + exposed-modules: Source.Language.Haskell + build-depends: base >=4.7, regex-applicative, + unordered-containers, + sdam, source-layout + hs-source-dirs: src/lang/haskell + default-language: Haskell2010 + ghc-options: -Wall -O2 + ghc-prof-options: -fprof-auto + -auto-all + -caf-all + +library source-language-morte + exposed-modules: Source.Language.Morte + build-depends: base >=4.7, regex-applicative, + unordered-containers, + sdam, source-layout + hs-source-dirs: src/lang/morte + default-language: Haskell2010 + ghc-options: -Wall -O2 + ghc-prof-options: -fprof-auto + -auto-all + -caf-all + executable foundry main-is: Foundry.hs build-depends: base, unordered-containers, megaparsec, regex-applicative, - sdam, foundry + sdam, source-language-morte, source-language-haskell, foundry - hs-source-dirs: src + hs-source-dirs: src/bin default-language: Haskell2010 @@ -62,14 +100,14 @@ executable hask main-is: Hask.hs build-depends: base, unordered-containers, megaparsec, regex-applicative, - sdam, foundry + sdam, source-language-haskell, foundry - hs-source-dirs: src + hs-source-dirs: src/bin default-language: Haskell2010 ghc-options: -Wall -threaded -O2 - -Wno-missing-signatures + ghc-prof-options: -fprof-auto -rtsopts -auto-all @@ -78,8 +116,8 @@ executable hask executable morte-to-sdam main-is: MorteToSdam.hs build-depends: base, text, unordered-containers, - morte, sdam - hs-source-dirs: src + morte, sdam, source-language-morte + hs-source-dirs: src/bin default-language: Haskell2010 ghc-options: -Wall -threaded -O2 @@ -87,7 +125,7 @@ executable haskell-to-sdam main-is: HaskellToSdam.hs build-depends: base, text, containers, unordered-containers, split, - ghc-lib-parser, sdam - hs-source-dirs: src + ghc-lib-parser, sdam, source-language-haskell + hs-source-dirs: src/bin default-language: Haskell2010 ghc-options: -Wall -threaded -O2 diff --git a/src/Foundry.hs b/src/Foundry.hs deleted file mode 100644 index 0ecd8e1..0000000 --- a/src/Foundry.hs +++ /dev/null @@ -1,148 +0,0 @@ -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedLists #-} -{-# LANGUAGE OverloadedStrings #-} - -module Main where - -import Control.Monad (void) -import Data.Char as Char -import qualified Data.HashMap.Strict as HashMap -import Data.HashMap.Strict (HashMap) -import Data.List as List -import Sdam.Parser (pValue, parse) -import Source -import Source.NewGen -import System.Environment (getArgs) -import System.Exit (die) -import Text.Megaparsec as Megaparsec -import Text.Regex.Applicative as RE - -main :: IO () -main = do - mParsedValue <- getArgs >>= \case - [filepath] -> do - content <- readFile filepath - case parse pValue filepath content of - Left e -> die (Megaparsec.errorBundlePretty e) - Right a -> return (Just a) - [] -> return Nothing - _ -> die "Usage: foundry FILE.sd" - runSource foundryPlugin mParsedValue - -foundryPlugin :: Plugin -foundryPlugin = - Plugin - { _pluginSchema = foundrySchema, - _pluginRecLayouts = foundryRecLayouts - } - -foundrySchema :: Schema -foundrySchema = - Schema - { schemaTypes = - [ "nat" ==> TyDefnStr, - "v" ==> TyDefnStr, -- variable - "iv" ==> TyDefnRec ["var", "index"], -- indexed variable - "lam" ==> TyDefnRec ["var", "ty", "body"], - "pi" ==> TyDefnRec ["var", "ty", "body"], - "a" ==> TyDefnRec ["fn", "arg"], -- function application - "star" ==> TyDefnRec [], - "box" ==> TyDefnRec [] - ], - schemaRoot = tExpr - } - where - tNat = - uT "nat" $ - TyInstStr (void re) - where - re = RE.some (RE.psym Char.isDigit) - tVar = - uT "v" $ - TyInstStr (void re) - where - re = re_alphavar <|> re_op - re_fst = - RE.psym $ \c -> - Char.isLetter c - || c == '_' - re_labelchar = - RE.psym $ \c -> - Char.isLetter c - || Char.isDigit c - || c == '_' - re_opchar = - RE.psym $ \c -> - c `List.elem` ("!#$%&*+./<=>?@^|-~" :: [Char]) - re_alphavar = - re_fst *> RE.many re_labelchar - re_op = - RE.some re_opchar - tIVar = - uT "iv" $ - TyInstRec - [ "var" ==> tVar, - "index" ==> tNat - ] - tLam = - uT "lam" $ - TyInstRec - [ "var" ==> tVar, - "ty" ==> tExpr, - "body" ==> tExpr - ] - tPi = - uT "pi" $ - TyInstRec - [ "var" ==> tVar, - "ty" ==> tExpr, - "body" ==> tExpr - ] - tApp = - uT "a" $ - TyInstRec - [ "fn" ==> tExpr, - "arg" ==> tExpr - ] - tStar = - uT "star" $ - TyInstRec [] - tBox = - uT "box" $ - TyInstRec [] - tExpr = - mconcat - [ tLam, - tPi, - tApp, - tStar, - tBox, - tVar, - tIVar - ] - -foundryRecLayouts :: HashMap TyName ALayoutFn -foundryRecLayouts = recLayouts - where - recLayouts = - [ "lam" ==> recLayoutLam, - "pi" ==> recLayoutPi, - "a" ==> recLayoutApp, - "star" ==> jumptag "★", - "box" ==> jumptag "□", - "iv" ==> recLayoutIVar - ] - precAll = precAllow (HashMap.keysSet recLayouts) - precAtoms = ["star", "box"] - prec ss = precAllow (ss <> precAtoms) - recLayoutApp = - field "fn" (prec ["a"]) "function" - <> field "arg" (prec []) "argument" - recLayoutLam = - jumptag "λ" <> field "var" noPrec "variable" <> ":" <> field "ty" precAll "type" - `vsep` field "body" precAll "body" - recLayoutPi = - jumptag "Π" <> field "var" noPrec "variable" <> ":" <> field "ty" precAll "type" - `vsep` field "body" precAll "body" - recLayoutIVar = - field "var" noPrec "variable" <> jumptag "@" <> field "index" noPrec "index" diff --git a/src/bin/Foundry.hs b/src/bin/Foundry.hs new file mode 100644 index 0000000..f5b0d17 --- /dev/null +++ b/src/bin/Foundry.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} + +module Main where + +import Sdam.Parser (pValue, parse) +import Source +import Source.Language.Morte +import Source.NewGen +import System.Environment (getArgs) +import System.Exit (die) +import Text.Megaparsec as Megaparsec + +main :: IO () +main = do + mParsedValue <- getArgs >>= \case + [filepath] -> do + content <- readFile filepath + case parse pValue filepath content of + Left e -> die (Megaparsec.errorBundlePretty e) + Right a -> return (Just a) + [] -> return Nothing + _ -> die "Usage: foundry FILE.sd" + runSource foundryPlugin mParsedValue + +foundryPlugin :: Plugin +foundryPlugin = + Plugin + { _pluginSchema = morteSchema, + _pluginRecLayouts = morteRecLayouts + } diff --git a/src/bin/Hask.hs b/src/bin/Hask.hs new file mode 100644 index 0000000..efdc360 --- /dev/null +++ b/src/bin/Hask.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} + +module Main where + +import Sdam.Parser (pValue, parse) +import Source +import Source.Language.Haskell +import Source.NewGen +import System.Environment (getArgs) +import System.Exit (die) +import Text.Megaparsec as Megaparsec + +main :: IO () +main = do + mParsedValue <- getArgs >>= \case + [filepath] -> do + content <- readFile filepath + case parse pValue filepath content of + Left e -> die (Megaparsec.errorBundlePretty e) + Right a -> return (Just a) + [] -> return Nothing + _ -> die "Usage: hask FILE.sd" + runSource haskPlugin mParsedValue + +haskPlugin :: Plugin +haskPlugin = + Plugin + { _pluginSchema = haskellSchema, + _pluginRecLayouts = haskellRecLayouts + } diff --git a/src/HaskellToSdam.hs b/src/bin/HaskellToSdam.hs similarity index 84% rename from src/HaskellToSdam.hs rename to src/bin/HaskellToSdam.hs index 993af51..53c4439 100644 --- a/src/HaskellToSdam.hs +++ b/src/bin/HaskellToSdam.hs @@ -1,7 +1,6 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedLists #-} -{-# LANGUAGE OverloadedStrings #-} module Main where @@ -22,6 +21,7 @@ import qualified Parser as GHC import qualified RdrName as GHC import Sdam.Core import Sdam.Printer +import Source.Language.Haskell import qualified SrcLoc as GHC import qualified StringBuffer as GHC import System.Environment (getArgs) @@ -41,18 +41,18 @@ main = do convertModule :: GHC.HsModule GhcPs -> RenderValue convertModule GHC.HsModule {GHC.hsmodName, GHC.hsmodExports, GHC.hsmodDecls} = mkRecValue - "module" - [ ( "name", + ty_module + [ ( fld_name, case hsmodName of - Nothing -> mkStrValue "v" "Main" + Nothing -> mkStrValue ty_v (Text.pack "Main") Just name -> convertModuleName (GHC.unLoc name) ), - ( "ex", + ( fld_ex, case hsmodExports of - Nothing -> mkRecValue "all" [] + Nothing -> mkRecValue ty_all [] Just ex -> convertExports (GHC.unLoc ex) ), - ("ds", mkSeqValue (convertDecl . GHC.unLoc) hsmodDecls) + (fld_ds, mkSeqValue (convertDecl . GHC.unLoc) hsmodDecls) ] convertModuleName :: GHC.ModuleName -> RenderValue @@ -61,12 +61,12 @@ convertModuleName modname = [] -> error "convertModuleName: empty list" s : ss -> toQVs s ss where - toQVs s [] = mkStrValue "v" (Text.pack s) + toQVs s [] = mkStrValue ty_v (Text.pack s) toQVs q (s : ss) = mkRecValue - "qv" - [ ("q", mkStrValue "v" (Text.pack q)), - ("v", toQVs s ss) + ty_qv + [ (fld_q, mkStrValue ty_v (Text.pack q)), + (fld_v, toQVs s ss) ] convertExports :: [GHC.LIE GhcPs] -> RenderValue @@ -94,14 +94,14 @@ convertFunBind _ _ = error "TODO: convertFunBind" convertMatch :: GHC.IdP GhcPs -> GHC.Match GhcPs (GHC.LHsExpr GhcPs) -> RenderValue convertMatch name GHC.Match {GHC.m_pats, GHC.m_grhss} = mkRecValue - "bind" - [ ("v", toApps name m_pats), - ("b", convertGRHSs m_grhss) + ty_bind + [ (fld_v, toApps name m_pats), + (fld_b, convertGRHSs m_grhss) ] where toApps n ps = foldl - (\f p -> mkRecValue "a" [("f", f), ("a", p)]) + (\f p -> mkRecValue ty_a [(fld_f, f), (fld_a, p)]) (convertName n) (map convertPat ps) convertMatch _ _ = error "TODO: convertMatch" @@ -121,35 +121,35 @@ convertGRHS _ = error "TODO: convertGRHS" convertTypeSig :: [GHC.IdP GhcPs] -> GHC.HsType GhcPs -> RenderValue convertTypeSig names ty = mkRecValue - "sig" - [ ("v", mkSeqValue convertName names), - ("t", convertType ty) + ty_sig + [ (fld_v, mkSeqValue convertName names), + (fld_t, convertType ty) ] convertName :: GHC.IdP GhcPs -> RenderValue convertName name = - mkStrValue "v" (Text.pack (GHC.occNameString (GHC.rdrNameOcc name))) + mkStrValue ty_v (Text.pack (GHC.occNameString (GHC.rdrNameOcc name))) convertType :: GHC.HsType GhcPs -> RenderValue convertType (GHC.HsTyVar _ _ name) = convertName (GHC.unLoc name) convertType (GHC.HsAppTy _ t1 t2) = mkRecValue - "a" - [ ("f", convertType (GHC.unLoc t1)), - ("a", convertType (GHC.unLoc t2)) + ty_a + [ (fld_f, convertType (GHC.unLoc t1)), + (fld_a, convertType (GHC.unLoc t2)) ] convertType (GHC.HsTupleTy _ GHC.HsBoxedOrConstraintTuple []) = -- TODO: Unit representation - mkStrValue "v" "Unit" + mkStrValue ty_v (Text.pack "Unit") convertType _ = error "TODO: convertType" convertExpr :: GHC.HsExpr GhcPs -> RenderValue convertExpr (GHC.HsVar _ name) = convertName (GHC.unLoc name) convertExpr (GHC.HsApp _ e1 e2) = mkRecValue - "a" - [ ("f", convertExpr (GHC.unLoc e1)), - ("a", convertExpr (GHC.unLoc e2)) + ty_a + [ (fld_f, convertExpr (GHC.unLoc e1)), + (fld_a, convertExpr (GHC.unLoc e2)) ] convertExpr (GHC.HsLit _ lit) = convertLit lit @@ -157,7 +157,7 @@ convertExpr _ = error "TODO: convertExpr" convertLit :: GHC.HsLit GhcPs -> RenderValue convertLit (GHC.HsString _ s) = - mkStrValue "str" (Text.pack (GHC.unpackFS s)) + mkStrValue ty_str (Text.pack (GHC.unpackFS s)) convertLit _ = error "TODO: convertLit" mkRecValue :: TyName -> [(FieldName, RenderValue)] -> RenderValue diff --git a/src/MorteToSdam.hs b/src/bin/MorteToSdam.hs similarity index 74% rename from src/MorteToSdam.hs rename to src/bin/MorteToSdam.hs index 4d4acf8..c5a6d3a 100644 --- a/src/MorteToSdam.hs +++ b/src/bin/MorteToSdam.hs @@ -1,6 +1,5 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLists #-} -{-# LANGUAGE OverloadedStrings #-} module Main where @@ -15,6 +14,7 @@ import qualified Morte.Import as M.I import qualified Morte.Parser as M.P import Sdam.Core import Sdam.Printer +import Source.Language.Morte import System.Environment (getArgs) import System.Exit (die) @@ -40,43 +40,43 @@ convertExpr = \case convertConst :: M.Const -> RenderValue convertConst = \case - M.Star -> mkRecValue "star" [] - M.Box -> mkRecValue "box" [] + M.Star -> mkRecValue ty_star [] + M.Box -> mkRecValue ty_box [] convertVar :: M.Var -> RenderValue convertVar = \case - M.V t 0 -> mkStrValue "v" (Text.Lazy.toStrict t) + M.V t 0 -> mkStrValue ty_v (Text.Lazy.toStrict t) M.V t n -> mkRecValue - "iv" - [ ("var", mkStrValue "v" (Text.Lazy.toStrict t)), - ("index", mkStrValue "nat" (Text.pack (show n))) + ty_iv + [ (fld_var, mkStrValue ty_v (Text.Lazy.toStrict t)), + (fld_index, mkStrValue ty_nat (Text.pack (show n))) ] convertLam :: Text.Lazy.Text -> M.Expr Void -> M.Expr Void -> RenderValue convertLam x _A b = mkRecValue - "lam" - [ ("var", mkStrValue "v" (Text.Lazy.toStrict x)), - ("ty", convertExpr _A), - ("body", convertExpr b) + ty_lam + [ (fld_var, mkStrValue ty_v (Text.Lazy.toStrict x)), + (fld_ty, convertExpr _A), + (fld_body, convertExpr b) ] convertPi :: Text.Lazy.Text -> M.Expr Void -> M.Expr Void -> RenderValue convertPi x _A _B = mkRecValue - "pi" - [ ("var", mkStrValue "v" (Text.Lazy.toStrict x)), - ("ty", convertExpr _A), - ("body", convertExpr _B) + ty_pi + [ (fld_var, mkStrValue ty_v (Text.Lazy.toStrict x)), + (fld_ty, convertExpr _A), + (fld_body, convertExpr _B) ] convertApp :: M.Expr Void -> M.Expr Void -> RenderValue convertApp f a = mkRecValue - "a" - [ ("fn", convertExpr f), - ("arg", convertExpr a) + ty_a + [ (fld_fn, convertExpr f), + (fld_arg, convertExpr a) ] mkRecValue :: TyName -> [(FieldName, RenderValue)] -> RenderValue diff --git a/lib/Source.hs b/src/driver/Source.hs similarity index 100% rename from lib/Source.hs rename to src/driver/Source.hs diff --git a/lib/Source/Input.hs b/src/driver/Source/Input.hs similarity index 100% rename from lib/Source/Input.hs rename to src/driver/Source/Input.hs diff --git a/lib/Source/Input/KeyCode.hs b/src/driver/Source/Input/KeyCode.hs similarity index 100% rename from lib/Source/Input/KeyCode.hs rename to src/driver/Source/Input/KeyCode.hs diff --git a/lib/Source/NewGen.hs b/src/driver/Source/NewGen.hs similarity index 96% rename from lib/Source/NewGen.hs rename to src/driver/Source/NewGen.hs index 981fe59..44d9011 100644 --- a/lib/Source/NewGen.hs +++ b/src/driver/Source/NewGen.hs @@ -107,9 +107,7 @@ import Data.Foldable as Foldable import Data.Function (on) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap -import Data.HashSet (HashSet) import qualified Data.HashSet as HashSet -import Data.Hashable (Hashable) import Data.List as List import Data.List.NonEmpty as NonEmpty import Data.Maybe @@ -134,6 +132,7 @@ import Slay.Combinators import Slay.Core import Source.Input import qualified Source.Input.KeyCode as KeyCode +import Source.Layout -------------------------------------------------------------------------------- -- Values @@ -250,19 +249,6 @@ rightOf (Extents vacantWidth _) collage = data WritingDirection = WritingDirectionLTR | WritingDirectionRTL -infixr 1 `vsep` - -class (IsString a, Semigroup a) => Layout a where - - vsep :: a -> a -> a - - field :: FieldName -> PrecPredicate -> Text -> a - - jumptag :: a -> a - -noPrec :: PrecPredicate -noPrec = PrecPredicate (const (PrecBorder True)) - newtype RecLayoutFn = RecLayoutFn { appRecLayoutFn :: @@ -319,25 +305,6 @@ withJumptag path = where pathJumptag offset = DList.singleton (Jumptag offset path) -newtype ALayoutFn = ALayoutFn (forall a. Layout a => a) - -instance IsString ALayoutFn where - fromString s = ALayoutFn (fromString s) - -instance Semigroup ALayoutFn where - ALayoutFn a <> ALayoutFn b = - ALayoutFn (a <> b) - -instance Layout ALayoutFn where - - ALayoutFn a `vsep` ALayoutFn b = - ALayoutFn (a `vsep` b) - - field fieldName precPredicate placeholder = - ALayoutFn (field fieldName precPredicate placeholder) - - jumptag (ALayoutFn a) = ALayoutFn (jumptag a) - newtype Find a b = Find (a -> Maybe b) instance Semigroup (Find a b) where @@ -451,50 +418,6 @@ ubuntuFont = Font "Ubuntu" 12 FontWeightNormal ubuntuMonoFont :: Font ubuntuMonoFont = Font "Ubuntu Mono" 12 FontWeightNormal --- | Is a precedence border needed? -newtype PrecBorder = PrecBorder Bool - -instance Semigroup PrecBorder where - PrecBorder a <> PrecBorder b = PrecBorder (a || b) - -instance Monoid PrecBorder where - mempty = PrecBorder False - --- | Layouts not enclosed by a precedence border. -newtype PrecUnenclosed = PrecUnenclosed (HashSet TyName) - -instance Semigroup PrecUnenclosed where - PrecUnenclosed a <> PrecUnenclosed b = - PrecUnenclosed (HashSet.union a b) - -instance Monoid PrecUnenclosed where - mempty = PrecUnenclosed HashSet.empty - -addUnenclosed :: TyName -> PrecUnenclosed -> PrecUnenclosed -addUnenclosed tyName (PrecUnenclosed s) = - PrecUnenclosed (HashSet.insert tyName s) - -guardUnenclosed :: PrecBorder -> PrecUnenclosed -> PrecUnenclosed -guardUnenclosed (PrecBorder True) = const mempty -guardUnenclosed (PrecBorder False) = id - -newtype PrecPredicate - = PrecPredicate {appPrecPredicate :: PrecUnenclosed -> PrecBorder} - -precAllow :: HashSet TyName -> PrecPredicate -precAllow allowed = - PrecPredicate $ \(PrecUnenclosed unenclosed) -> - PrecBorder $ - -- Need a border unless all of unenclosed layouts are allowed. - not (unenclosed `hashSet_isSubsetOf` allowed) - -precAllowAll :: PrecPredicate -precAllowAll = PrecPredicate (const (PrecBorder False)) - -hashSet_isSubsetOf :: (Eq a, Hashable a) => HashSet a -> HashSet a -> Bool -hashSet_isSubsetOf sub sup = - all (\k -> HashSet.member k sup) sub - lrtbMargin :: Margin -> LRTB Natural lrtbMargin (Margin l r t b) = lrtb l r t b diff --git a/lib/Source/Phaser.hs b/src/driver/Source/Phaser.hs similarity index 100% rename from lib/Source/Phaser.hs rename to src/driver/Source/Phaser.hs diff --git a/src/Hask.hs b/src/lang/haskell/Source/Language/Haskell.hs similarity index 74% rename from src/Hask.hs rename to src/lang/haskell/Source/Language/Haskell.hs index c2a65b9..ed35f38 100644 --- a/src/Hask.hs +++ b/src/lang/haskell/Source/Language/Haskell.hs @@ -1,100 +1,80 @@ -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MonoLocalBinds #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} -module Main where +module Source.Language.Haskell where import Control.Monad (void) import Data.Char as Char import Data.HashMap.Strict (HashMap) import Data.List as List -import Sdam.Parser (pValue, parse) -import Source -import Source.NewGen -import System.Environment (getArgs) -import System.Exit (die) -import Text.Megaparsec as Megaparsec +import Sdam.Core +import Source.Layout import Text.Regex.Applicative as RE -main :: IO () -main = do - mParsedValue <- getArgs >>= \case - [filepath] -> do - content <- readFile filepath - case parse pValue filepath content of - Left e -> die (Megaparsec.errorBundlePretty e) - Right a -> return (Just a) - [] -> return Nothing - _ -> die "Usage: hask FILE.sd" - runSource haskPlugin mParsedValue - -haskPlugin :: Plugin -haskPlugin = - Plugin - { _pluginSchema = haskSchema, - _pluginRecLayouts = haskRecLayouts - } - -ty_all = "all" - -ty_module = "module" - -ty_v = "v" - -ty_str = "str" - -ty_lam = "lam" - -ty_a = "a" - -ty_qv = "qv" - -ty_sig = "sig" - -ty_as_pat = "as-pat" - -ty_bind = "bind" - -ty_data = "data" - -ty_import = "import" - -ty_qualified = "qualified" +-------------------------------------------------------------------------------- +---- Layout +-------------------------------------------------------------------------------- -ty_as_mod = "as-mod" - -fld_name = "name" - -fld_ex = "ex" - -fld_ds = "ds" - -fld_v = "v" - -fld_b = "b" - -fld_f = "f" - -fld_a = "a" - -fld_q = "q" - -fld_t = "t" - -fld_alias = "alias" - -fld_p = "p" - -fld_alts = "alts" - -fld_module = "module" - -fld_e = "e" +haskellRecLayouts :: HashMap TyName ALayoutFn +haskellRecLayouts = recLayouts + where + recLayouts = + [ ty_all ==> recLayoutAll, + ty_lam ==> recLayoutLam, + ty_a ==> recLayoutApp, + ty_module ==> recLayoutMod, + ty_qv ==> recLayoutQVar, + ty_sig ==> recLayoutSig, + ty_as_pat ==> recLayoutAsPat, + ty_bind ==> recLayoutBind, + ty_data ==> recLayoutData, + ty_import ==> recLayoutImport, + ty_qualified ==> recLayoutQualified, + ty_as_mod ==> recLayoutAsMod + ] + recLayoutAll = jumptag "∗" + recLayoutQVar = + field fld_q noPrec "q" <> "." <> field fld_v precAllowAll "v" + recLayoutApp = + field fld_f (precAllow [ty_a]) "function" + <> field fld_a (precAllow [ty_v, ty_qv]) "argument" + recLayoutLam = + jumptag "λ" <> field fld_v precAllowAll "variable" + `vsep` field fld_b precAllowAll "body" + recLayoutMod = + jumptag "module" <> field fld_name (precAllow ["v", "qv"]) "name" + <> "exports" + <> field fld_ex precAllowAll "entities" + `vsep` field fld_ds precAllowAll "declarations" + recLayoutSig = + field fld_v noPrec "variable" <> jumptag "::" + <> field fld_t precAllowAll "type" + recLayoutAsPat = + field fld_alias noPrec "alias" <> jumptag "@" + <> field fld_p noPrec "pattern" + recLayoutBind = + field fld_v noPrec "variable" <> jumptag "=" + <> field fld_b precAllowAll "body" + recLayoutData = + jumptag "data" <> field fld_v noPrec "name" <> "=" + <> field fld_alts precAllowAll "alternatives" + recLayoutImport = + jumptag "from" <> field fld_module (precAllow [ty_v, ty_qv, ty_as_mod]) "module" + <> "import" + <> field fld_e precAllowAll "entities" + recLayoutQualified = + jumptag "qualified" <> field fld_entities (precAllow [ty_v]) "entities" + recLayoutAsMod = + field fld_module noPrec "module" <> jumptag "as" + <> field fld_alias noPrec "alias" -fld_entities = "entities" +-------------------------------------------------------------------------------- +---- Schema +-------------------------------------------------------------------------------- -haskSchema :: Schema -haskSchema = +haskellSchema :: Schema +haskellSchema = Schema { schemaTypes = [ ty_all ==> TyDefnRec [], @@ -272,55 +252,115 @@ haskSchema = tImport ] -haskRecLayouts :: HashMap TyName ALayoutFn -haskRecLayouts = recLayouts - where - recLayouts = - [ ty_all ==> recLayoutAll, - ty_lam ==> recLayoutLam, - ty_a ==> recLayoutApp, - ty_module ==> recLayoutMod, - ty_qv ==> recLayoutQVar, - ty_sig ==> recLayoutSig, - ty_as_pat ==> recLayoutAsPat, - ty_bind ==> recLayoutBind, - ty_data ==> recLayoutData, - ty_import ==> recLayoutImport, - ty_qualified ==> recLayoutQualified, - ty_as_mod ==> recLayoutAsMod - ] - recLayoutAll = jumptag "∗" - recLayoutQVar = - field fld_q noPrec "q" <> "." <> field fld_v precAllowAll "v" - recLayoutApp = - field fld_f (precAllow ["a"]) "function" - <> field fld_a (precAllow ["v", "qv"]) "argument" - recLayoutLam = - jumptag "λ" <> field fld_v precAllowAll "variable" - `vsep` field fld_b precAllowAll "body" - recLayoutMod = - jumptag "module" <> field fld_name (precAllow ["v", "qv"]) "name" - <> "exports" - <> field fld_ex precAllowAll "entities" - `vsep` field fld_ds precAllowAll "declarations" - recLayoutSig = - field fld_v noPrec "variable" <> jumptag "::" - <> field fld_t precAllowAll "type" - recLayoutAsPat = - field fld_alias noPrec "alias" <> jumptag "@" - <> field fld_p noPrec "pattern" - recLayoutBind = - field fld_v noPrec "variable" <> jumptag "=" - <> field fld_b precAllowAll "body" - recLayoutData = - jumptag "data" <> field fld_v noPrec "name" <> "=" - <> field fld_alts precAllowAll "alternatives" - recLayoutImport = - jumptag "from" <> field fld_module (precAllow ["v", "qv", "as-mod"]) "module" - <> "import" - <> field fld_e precAllowAll "entities" - recLayoutQualified = - jumptag "qualified" <> field fld_entities (precAllow ["v"]) "entities" - recLayoutAsMod = - field fld_module noPrec "module" <> jumptag "as" - <> field fld_alias noPrec "alias" +-------------------------------------------------------------------------------- +---- Helpers +-------------------------------------------------------------------------------- + +uT :: TyName -> TyInst -> TyUnion +uT = tyUnionSingleton + +uS :: TyUnion -> TyUnion +uS = tyUnionSequence + +uS' :: TyUnion -> TyUnion +uS' = tyUnionRecursiveSequence + +(==>) :: a -> b -> (a, b) +(==>) = (,) + +infix 0 ==> + +-------------------------------------------------------------------------------- +---- Type Names +-------------------------------------------------------------------------------- + +ty_all :: TyName +ty_all = "all" + +ty_module :: TyName +ty_module = "module" + +ty_v :: TyName +ty_v = "v" + +ty_str :: TyName +ty_str = "str" + +ty_lam :: TyName +ty_lam = "lam" + +ty_a :: TyName +ty_a = "a" + +ty_qv :: TyName +ty_qv = "qv" + +ty_sig :: TyName +ty_sig = "sig" + +ty_as_pat :: TyName +ty_as_pat = "as-pat" + +ty_bind :: TyName +ty_bind = "bind" + +ty_data :: TyName +ty_data = "data" + +ty_import :: TyName +ty_import = "import" + +ty_qualified :: TyName +ty_qualified = "qualified" + +ty_as_mod :: TyName +ty_as_mod = "as-mod" + +-------------------------------------------------------------------------------- +---- Field Names +-------------------------------------------------------------------------------- + +fld_name :: FieldName +fld_name = "name" + +fld_ex :: FieldName +fld_ex = "ex" + +fld_ds :: FieldName +fld_ds = "ds" + +fld_v :: FieldName +fld_v = "v" + +fld_b :: FieldName +fld_b = "b" + +fld_f :: FieldName +fld_f = "f" + +fld_a :: FieldName +fld_a = "a" + +fld_q :: FieldName +fld_q = "q" + +fld_t :: FieldName +fld_t = "t" + +fld_alias :: FieldName +fld_alias = "alias" + +fld_p :: FieldName +fld_p = "p" + +fld_alts :: FieldName +fld_alts = "alts" + +fld_module :: FieldName +fld_module = "module" + +fld_e :: FieldName +fld_e = "e" + +fld_entities :: FieldName +fld_entities = "entities" diff --git a/src/lang/morte/Source/Language/Morte.hs b/src/lang/morte/Source/Language/Morte.hs new file mode 100644 index 0000000..a2cf94b --- /dev/null +++ b/src/lang/morte/Source/Language/Morte.hs @@ -0,0 +1,192 @@ +{-# LANGUAGE MonoLocalBinds #-} +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} + +module Source.Language.Morte where + +import Control.Monad (void) +import Data.Char as Char +import Data.HashMap.Strict (HashMap) +import Data.HashMap.Strict as HashMap +import Data.List as List +import Sdam.Core +import Source.Layout +import Text.Regex.Applicative as RE + +-------------------------------------------------------------------------------- +---- Layout +-------------------------------------------------------------------------------- + +morteRecLayouts :: HashMap TyName ALayoutFn +morteRecLayouts = recLayouts + where + recLayouts = + [ ty_lam ==> recLayoutLam, + ty_pi ==> recLayoutPi, + ty_a ==> recLayoutApp, + ty_star ==> jumptag "★", + ty_box ==> jumptag "□", + ty_iv ==> recLayoutIVar + ] + precAll = precAllow (HashMap.keysSet recLayouts) + precAtoms = [ty_star, ty_box] + prec ss = precAllow (ss <> precAtoms) + recLayoutApp = + field fld_fn (prec [ty_a]) "function" + <> field fld_arg (prec []) "argument" + recLayoutLam = + jumptag "λ" <> field fld_var noPrec "variable" <> ":" <> field fld_ty precAll "type" + `vsep` field "body" precAll "body" + recLayoutPi = + jumptag "Π" <> field fld_var noPrec "variable" <> ":" <> field fld_ty precAll "type" + `vsep` field "body" precAll "body" + recLayoutIVar = + field fld_var noPrec "variable" <> jumptag "@" <> field fld_index noPrec "index" + +-------------------------------------------------------------------------------- +---- Schema +-------------------------------------------------------------------------------- + +morteSchema :: Schema +morteSchema = + Schema + { schemaTypes = + [ ty_nat ==> TyDefnStr, + ty_v ==> TyDefnStr, -- variable + ty_iv ==> TyDefnRec [fld_var, fld_index], -- indexed variable + ty_lam ==> TyDefnRec [fld_var, fld_ty, fld_body], + ty_pi ==> TyDefnRec [fld_var, fld_ty, fld_body], + ty_a ==> TyDefnRec [fld_fn, fld_arg], -- function application + ty_star ==> TyDefnRec [], + ty_box ==> TyDefnRec [] + ], + schemaRoot = tExpr + } + where + tNat = + tyUnionSingleton ty_nat $ + TyInstStr (void re) + where + re = RE.some (RE.psym Char.isDigit) + tVar = + tyUnionSingleton ty_v $ + TyInstStr (void re) + where + re = re_alphavar <|> re_op + re_fst = + RE.psym $ \c -> + Char.isLetter c + || c == '_' + re_labelchar = + RE.psym $ \c -> + Char.isLetter c + || Char.isDigit c + || c == '_' + re_opchar = + RE.psym $ \c -> + c `List.elem` ("!#$%&*+./<=>?@^|-~" :: [Char]) + re_alphavar = + re_fst *> RE.many re_labelchar + re_op = + RE.some re_opchar + tIVar = + tyUnionSingleton ty_iv $ + TyInstRec + [ fld_var ==> tVar, + fld_index ==> tNat + ] + tLam = + tyUnionSingleton ty_lam $ + TyInstRec + [ fld_var ==> tVar, + fld_ty ==> tExpr, + fld_body ==> tExpr + ] + tPi = + tyUnionSingleton ty_pi $ + TyInstRec + [ fld_var ==> tVar, + fld_ty ==> tExpr, + fld_body ==> tExpr + ] + tApp = + tyUnionSingleton ty_a $ + TyInstRec + [ fld_fn ==> tExpr, + fld_arg ==> tExpr + ] + tStar = + tyUnionSingleton ty_star $ + TyInstRec [] + tBox = + tyUnionSingleton ty_box $ + TyInstRec [] + tExpr = + mconcat + [ tLam, + tPi, + tApp, + tStar, + tBox, + tVar, + tIVar + ] + +-------------------------------------------------------------------------------- +---- Helpers +-------------------------------------------------------------------------------- + +(==>) :: a -> b -> (a, b) +(==>) = (,) + +infix 0 ==> + +-------------------------------------------------------------------------------- +---- Type Names +-------------------------------------------------------------------------------- + +ty_nat :: TyName +ty_nat = "nat" + +ty_v :: TyName +ty_v = "v" + +ty_iv :: TyName +ty_iv = "iv" + +ty_lam :: TyName +ty_lam = "lam" + +ty_pi :: TyName +ty_pi = "pi" + +ty_a :: TyName +ty_a = "a" + +ty_star :: TyName +ty_star = "star" + +ty_box :: TyName +ty_box = "box" + +-------------------------------------------------------------------------------- +---- Field Names +-------------------------------------------------------------------------------- + +fld_var :: FieldName +fld_var = "var" + +fld_index :: FieldName +fld_index = "index" + +fld_ty :: FieldName +fld_ty = "ty" + +fld_body :: FieldName +fld_body = "body" + +fld_fn :: FieldName +fld_fn = "fn" + +fld_arg :: FieldName +fld_arg = "arg" diff --git a/src/layout/Source/Layout.hs b/src/layout/Source/Layout.hs new file mode 100644 index 0000000..ad12a87 --- /dev/null +++ b/src/layout/Source/Layout.hs @@ -0,0 +1,85 @@ +-- | A DSL for layout definitions. +module Source.Layout where + +import Data.HashSet (HashSet) +import qualified Data.HashSet as HashSet +import Data.Hashable (Hashable) +import Data.String +import Data.Text (Text) +import Sdam.Core + +-- | Is a precedence border needed? +newtype PrecBorder = PrecBorder Bool + +instance Semigroup PrecBorder where + PrecBorder a <> PrecBorder b = PrecBorder (a || b) + +instance Monoid PrecBorder where + mempty = PrecBorder False + +-- | Layouts not enclosed by a precedence border. +newtype PrecUnenclosed = PrecUnenclosed (HashSet TyName) + +instance Semigroup PrecUnenclosed where + PrecUnenclosed a <> PrecUnenclosed b = + PrecUnenclosed (HashSet.union a b) + +instance Monoid PrecUnenclosed where + mempty = PrecUnenclosed HashSet.empty + +addUnenclosed :: TyName -> PrecUnenclosed -> PrecUnenclosed +addUnenclosed tyName (PrecUnenclosed s) = + PrecUnenclosed (HashSet.insert tyName s) + +guardUnenclosed :: PrecBorder -> PrecUnenclosed -> PrecUnenclosed +guardUnenclosed (PrecBorder True) = const mempty +guardUnenclosed (PrecBorder False) = id + +newtype PrecPredicate + = PrecPredicate {appPrecPredicate :: PrecUnenclosed -> PrecBorder} + +precAllow :: HashSet TyName -> PrecPredicate +precAllow allowed = + PrecPredicate $ \(PrecUnenclosed unenclosed) -> + PrecBorder $ + -- Need a border unless all of unenclosed layouts are allowed. + not (unenclosed `hashSet_isSubsetOf` allowed) + +precAllowAll :: PrecPredicate +precAllowAll = PrecPredicate (const (PrecBorder False)) + +hashSet_isSubsetOf :: (Eq a, Hashable a) => HashSet a -> HashSet a -> Bool +hashSet_isSubsetOf sub sup = + all (\k -> HashSet.member k sup) sub + +class (IsString a, Semigroup a) => Layout a where + + vsep :: a -> a -> a + + field :: FieldName -> PrecPredicate -> Text -> a + + jumptag :: a -> a + +infixr 1 `vsep` + +noPrec :: PrecPredicate +noPrec = PrecPredicate (const (PrecBorder True)) + +newtype ALayoutFn = ALayoutFn (forall a. Layout a => a) + +instance IsString ALayoutFn where + fromString s = ALayoutFn (fromString s) + +instance Semigroup ALayoutFn where + ALayoutFn a <> ALayoutFn b = + ALayoutFn (a <> b) + +instance Layout ALayoutFn where + + ALayoutFn a `vsep` ALayoutFn b = + ALayoutFn (a `vsep` b) + + field fieldName precPredicate placeholder = + ALayoutFn (field fieldName precPredicate placeholder) + + jumptag (ALayoutFn a) = ALayoutFn (jumptag a)