77 Commits

Author SHA1 Message Date
crumbtoo
3d45e12676 infer letrec expressions 2024-01-23 21:09:25 -07:00
crumbtoo
22b5b47795 letrec 2024-01-23 20:19:16 -07:00
crumbtoo
cefdf6ffae allow uppercase sc names in preperation for Rlp2Core 2024-01-22 12:45:42 -07:00
crumbtoo
e3b18c8915 errors! 2024-01-22 12:20:05 -07:00
crumbtoo
692d22afb9 msgenvelope 2024-01-22 10:26:33 -07:00
crumbtoo
c146e1c450 errorful parser
small
2024-01-22 10:14:30 -07:00
crumbtoo
5a659d22dd errorful parser 2024-01-22 09:55:58 -07:00
crumbtoo
1a881399ab when the "Test suite rlp-test: PASS" hits
i'm like atlas and the world is writing two lines of code
2024-01-21 14:02:28 -07:00
crumbtoo
257d02da87 RlpcError -> IsRlpcError 2024-01-21 11:53:41 -07:00
crumbtoo
f47f325e34 compiles (kill me)
man
2024-01-19 15:52:47 -07:00
crumbtoo
f22d4238f5 Merge branch 'dev' into frontend-parser 2024-01-17 10:28:59 -07:00
crumbtoo
4e1c9dd750 rename rlp 2024-01-17 10:19:48 -07:00
crumbtoo
d6ac991105 renamerlp 2024-01-17 10:19:16 -07:00
crumbtoo
d5663c1aad remove debug flags 2024-01-17 10:11:48 -07:00
crumbtoo
7e6bee3d4a infix exprs 2024-01-17 10:08:57 -07:00
crumbtoo
5ec625e0fd i really need to learn git proper 2024-01-15 15:20:04 -07:00
crumbtoo
9196e20e08 Merge branch 'frontend-parser' into happy-frontend 2024-01-15 15:18:29 -07:00
crumbtoo
a1a50bd013 now we're fucking GETTING SOMEWHERE 2024-01-15 14:58:26 -07:00
crumbtoo
1c035d092a works 2024-01-15 13:31:15 -07:00
crumbtoo
c0236dc079 oh my god 2024-01-15 11:11:43 -07:00
crumbtoo
9a4f24ec10 Merge commit '4f66e71' into happy-frontend 2024-01-15 11:06:37 -07:00
crumbtoo
4f66e71b9a FIX REAL 2024-01-15 11:05:10 -07:00
crumbtoo
bdf74ac6c9 cool 2024-01-15 10:35:11 -07:00
crumbtoo
3dfadc17ec fixy 2024-01-15 10:33:09 -07:00
crumbtoo
c92d8fac65 we're so back 2024-01-15 09:44:26 -07:00
crumbtoo
a38381f6ca version bounds 2024-01-15 07:53:40 -07:00
crumbtoo
6390ca80d8 see previous commit and scale back the part where i'm joking 2024-01-15 07:47:23 -07:00
crumbtoo
17ddf3530c kitten i'll be honest mommy's about to kill herself 2024-01-15 07:47:23 -07:00
crumbtoo
e597ecbfc6 okay layouts kinda 2024-01-15 07:47:23 -07:00
crumbtoo
2496589346 aagh 2024-01-15 07:47:23 -07:00
crumbtoo
681a394312 man this sucks 2024-01-15 07:47:23 -07:00
crumbtoo
aff1c6b4c6 decent starting point 2024-01-15 07:47:23 -07:00
crumbtoo
bec376b7c7 threaded lexer 2024-01-15 07:47:23 -07:00
crumbtoo
eaa04c4a59 its fine 2024-01-15 07:47:23 -07:00
crumbtoo
ea2fb4dcaa tysigs 2024-01-15 07:47:23 -07:00
crumbtoo
ab2cb59526 i did not realise my fs is case insensitive 2024-01-15 07:47:23 -07:00
crumbtoo
ec4902b2d4 layout
layouts

oh my layouts
2024-01-15 07:47:23 -07:00
crumbtoo
1fc45b70b4 replace uses of many+satisfy with takeWhileP 2024-01-15 07:47:23 -07:00
crumbtoo
4b9a570c72 finally in a decent state 2024-01-15 07:47:23 -07:00
crumbtoo
65b967689c decls fix 2024-01-15 07:47:23 -07:00
crumbtoo
ed60ec8b32 aaaaa 2024-01-15 07:47:23 -07:00
crumbtoo
d0dbdbbd9b cool 2024-01-15 07:47:23 -07:00
crumbtoo
cae0939f0c where 2024-01-15 07:47:23 -07:00
crumbtoo
3292998c42 expr fixups 2024-01-15 07:47:23 -07:00
crumbtoo
84c1122995 infix decl 2024-01-15 07:47:21 -07:00
crumbtoo
97ce9b48ae labels 2024-01-15 07:46:23 -07:00
crumbtoo
936f24148f works 2024-01-15 07:46:23 -07:00
crumbtoo
2a159232c7 fixation fufilled - back to work! 2024-01-15 07:46:23 -07:00
crumbtoo
4ee9785239 Show1 instances 2024-01-15 07:46:20 -07:00
crumbtoo
cbe4276061 goofy 2024-01-15 07:44:17 -07:00
crumbtoo
c5c06fa6cb something 2024-01-15 07:44:17 -07:00
crumbtoo
0f04e2decf application and lits
appl
2024-01-15 07:44:17 -07:00
crumbtoo
6130a91668 oh boy am i going to hate this code in 12 hours 2024-01-15 07:44:17 -07:00
crumbtoo
c15f9b6546 4:00 AM psychopath code 2024-01-15 07:44:17 -07:00
crumbtoo
bb6aca094c grammar reference 2024-01-15 07:44:17 -07:00
crumbtoo
245b12a96e add version bounds 2024-01-15 07:43:59 -07:00
crumbtoo
cb9ec43c14 tysigs 2024-01-10 15:11:26 -07:00
crumbtoo
8ad967fac0 i did not realise my fs is case insensitive 2024-01-10 14:33:03 -07:00
crumbtoo
55dbc9de70 layout
layouts

oh my layouts
2024-01-10 14:23:37 -07:00
crumbtoo
05226373ee replace uses of many+satisfy with takeWhileP 2024-01-10 11:33:27 -07:00
crumbtoo
981c5d8a83 finally in a decent state 2024-01-10 11:26:17 -07:00
crumbtoo
86cd1075ca decls fix 2024-01-10 11:03:06 -07:00
crumbtoo
1d43c1d304 aaaaa 2024-01-10 10:46:53 -07:00
crumbtoo
4b44f57066 cool 2024-01-09 22:57:14 -07:00
crumbtoo
90a9594e8f where 2024-01-09 14:24:51 -07:00
crumbtoo
074350768c expr fixups 2024-01-09 12:26:53 -07:00
crumbtoo
37d9e6f219 infix decl 2024-01-09 11:39:26 -07:00
crumbtoo
cb7cdf7ed7 labels 2024-01-08 20:14:18 -07:00
crumbtoo
2f783d96e8 works 2024-01-08 18:56:14 -07:00
crumbtoo
a71c099fe0 fixation fufilled - back to work! 2024-01-08 13:39:12 -07:00
crumbtoo
d1e64eb12d Show1 instances 2024-01-03 10:04:42 -07:00
crumbtoo
f31726b43d goofy 2024-01-02 08:43:34 -07:00
crumbtoo
8aa9bb843f something 2024-01-02 08:04:49 -07:00
crumbtoo
9a357a99b7 application and lits
appl
2024-01-02 07:04:27 -07:00
crumbtoo
060d48f9e1 oh boy am i going to hate this code in 12 hours 2024-01-02 06:26:48 -07:00
crumbtoo
bf4abeb8b4 4:00 AM psychopath code 2024-01-02 05:34:11 -07:00
crumbtoo
7ed565fc24 grammar reference 2024-01-02 02:33:31 -07:00
21 changed files with 1454 additions and 293 deletions

18
.ghci Normal file
View File

@@ -0,0 +1,18 @@
:set -XOverloadedStrings
:set -package process
:{
import System.Exit qualified
import System.Process qualified
_reload_and_make _ = do
p <- System.Process.spawnCommand "make -f Makefile_happysrcs"
r <- System.Process.waitForProcess p
case r of
System.Exit.ExitSuccess -> pure ":reload"
_ -> pure ""
:}
:def! r _reload_and_make

25
Makefile_happysrcs Normal file
View File

@@ -0,0 +1,25 @@
HAPPY = happy
HAPPY_OPTS = -a -g -c
ALEX = alex
ALEX_OPTS = -g
SRC = src
CABAL_BUILD = dist-newstyle/build/x86_64-osx/ghc-9.6.2/rlp-0.1.0.0/build
all: parsers lexers
parsers: $(CABAL_BUILD)/Rlp/Parse.hs $(CABAL_BUILD)/Core/Parse.hs
lexers: $(CABAL_BUILD)/Rlp/Lex.hs $(CABAL_BUILD)/Core/Lex.hs
$(CABAL_BUILD)/Rlp/Parse.hs: $(SRC)/Rlp/Parse.y
$(HAPPY) $(HAPPY_OPTS) $< -o $@
$(CABAL_BUILD)/Rlp/Lex.hs: $(SRC)/Rlp/Lex.x
$(ALEX) $(ALEX_OPTS) $< -o $@
$(CABAL_BUILD)/Core/Parse.hs: $(SRC)/Core/Parse.y
$(HAPPY) $(HAPPY_OPTS) $< -o $@
$(CABAL_BUILD)/Core/Lex.hs: $(SRC)/Core/Lex.x
$(ALEX) $(ALEX_OPTS) $< -o $@

View File

@@ -32,6 +32,7 @@ html_theme = 'alabaster'
imgmath_latex_preamble = r''' imgmath_latex_preamble = r'''
\usepackage{amsmath} \usepackage{amsmath}
\usepackage{tabularray} \usepackage{tabularray}
\usepackage{syntax}
\newcommand{\transrule}[2] \newcommand{\transrule}[2]
{\begin{tblr}{|rrrlc|} {\begin{tblr}{|rrrlc|}

View File

@@ -0,0 +1,67 @@
The Complete Syntax of rl'
==========================
WIP.
Provided is the complete syntax of rl' in (pseudo) EBNF. {A} represents zero or
more A's, [A] means optional A, and terminals are wrapped in 'single-quotes'.
.. math
:nowrap:
\setlength{\grammarparsep}{20pt plus 1pt minus 1pt}
\setlength{\grammarindent}{12em}
\begin{grammar}
<Decl> ::= <InfixDecl>
\alt <DataDecl>
\alt <TypeSig>
\alt <FunDef>
<InfixDecl> ::= <InfixWord> `litint' <Name>
<InfixWord> ::= `infix'
\alt `infixl'
\alt `infixr'
<DataDecl> ::= `data' `conname' {}
\end{grammar}
.. code-block:: bnf
Decl ::= InfixDecl
| DataDecl
| TypeSig
| FunDef
InfixDecl ::= InfixWord 'litint' Operator
InfixWord ::= 'infix'
| 'infixl'
| 'infixr'
DataDecl ::= 'data' 'conname' {'name'} '=' Data
DataCons ::= 'conname' {Type1} ['|' DataCons]
TypeSig ::= Var '::' Type
FunDef ::= Var {Pat1} '=' Expr
Type ::= Type1 {Type1}
-- note that (->) is right-associative,
-- and extends as far as possible
| Type '->' Type
Type1 ::= '(' Type ')'
| 'conname'
Pat ::= 'conname' Pat1 {Pat1}
| Pat 'consym' Pat
Pat1 ::= Literal
| 'conname'
| '(' Pat ')'
Literal ::= 'litint'
Var ::= 'varname'
| '(' 'varsym' ')'
Con ::= 'conname'
| '(' 'consym' ')'

View File

@@ -12,6 +12,7 @@ category: Language
build-type: Simple build-type: Simple
extra-doc-files: README.md extra-doc-files: README.md
-- extra-source-files: -- extra-source-files:
tested-with: GHC==9.6.2
common warnings common warnings
-- ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-unused-top-binds -- ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-unused-top-binds
@@ -30,6 +31,12 @@ library
, Core.TH , Core.TH
, Core.HindleyMilner , Core.HindleyMilner
, Control.Monad.Errorful , Control.Monad.Errorful
, Rlp.Syntax
-- , Rlp.Parse.Decls
, Rlp.Parse
, Rlp.Parse.Associate
, Rlp.Lex
, Rlp.Parse.Types
other-modules: Data.Heap other-modules: Data.Heap
, Data.Pretty , Data.Pretty
@@ -37,34 +44,38 @@ library
, Core.Lex , Core.Lex
, Core2Core , Core2Core
, Control.Monad.Utils , Control.Monad.Utils
, RLP.Syntax
build-tool-depends: happy:happy, alex:alex build-tool-depends: happy:happy, alex:alex
-- other-extensions: -- other-extensions:
build-depends: base ^>=4.18.0.0 build-depends: base ^>=4.18.0.0
, containers
, microlens
, microlens-mtl
, microlens-th
, microlens-platform
, mtl
, template-haskell
-- required for happy -- required for happy
, array , array >= 0.5.5 && < 0.6
, data-default-class , containers >= 0.6.7 && < 0.7
, unordered-containers , template-haskell >= 2.20.0 && < 2.21
, hashable , pretty >= 1.1.3 && < 1.2
, pretty , data-default >= 0.7.1 && < 0.8
-- TODO: either learn recursion-schemes, or stop depending , data-default-class >= 0.1.2 && < 0.2
-- on it. , hashable >= 1.4.3 && < 1.5
, recursion-schemes , mtl >= 2.3.1 && < 2.4
, megaparsec , text >= 2.0.2 && < 2.1
, text , megaparsec >= 9.6.1 && < 9.7
, microlens >= 0.4.13 && < 0.5
, microlens-mtl >= 0.2.0 && < 0.3
, microlens-platform >= 0.4.3 && < 0.5
, microlens-th >= 0.4.3 && < 0.5
, unordered-containers >= 0.2.20 && < 0.3
, recursion-schemes >= 5.2.2 && < 5.3
, data-fix >= 0.3.2 && < 0.4
, utf8-string >= 1.0.2 && < 1.1
, extra >= 1.7.0 && < 2
hs-source-dirs: src hs-source-dirs: src
default-language: GHC2021 default-language: GHC2021
default-extensions:
OverloadedStrings
executable rlpc executable rlpc
import: warnings import: warnings
main-is: Main.hs main-is: Main.hs
@@ -72,12 +83,12 @@ executable rlpc
-- other-extensions: -- other-extensions:
build-depends: base ^>=4.18.0.0 build-depends: base ^>=4.18.0.0
, rlp , rlp
, optparse-applicative , optparse-applicative >= 0.18.1 && < 0.19
, microlens , microlens >= 0.4.13 && < 0.5
, microlens-mtl , microlens-mtl >= 0.2.0 && < 0.3
, mtl , mtl >= 2.3.1 && < 2.4
, unordered-containers , unordered-containers >= 0.2.20 && < 0.3
, text , text >= 2.0.2 && < 2.1
hs-source-dirs: app hs-source-dirs: app
default-language: GHC2021 default-language: GHC2021

View File

@@ -26,21 +26,23 @@ import Data.Function ((&))
import GM import GM
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
justLexSrc :: String -> Either RlpcError [CoreToken] justLexSrc :: String -> Either [MsgEnvelope RlpcError] [CoreToken]
justLexSrc s = lexCoreR (T.pack s) justLexSrc s = lexCoreR (T.pack s)
& fmap (map $ \ (Located _ _ _ t) -> t) & fmap (map $ \ (Located _ _ _ t) -> t)
& rlpcToEither & rlpcToEither
justParseSrc :: String -> Either RlpcError Program' justParseSrc :: String -> Either [MsgEnvelope RlpcError] Program'
justParseSrc s = parse (T.pack s) justParseSrc s = parse (T.pack s)
& rlpcToEither & rlpcToEither
where parse = lexCoreR >=> parseCoreProgR where parse = lexCoreR >=> parseCoreProgR
justTypeCheckSrc :: String -> Either RlpcError Program' justTypeCheckSrc :: String -> Either [MsgEnvelope RlpcError] Program'
justTypeCheckSrc s = typechk (T.pack s) justTypeCheckSrc s = typechk (T.pack s)
& rlpcToEither & rlpcToEither
where typechk = lexCoreR >=> parseCoreProgR >=> checkCoreProgR where typechk = lexCoreR >=> parseCoreProgR >=> checkCoreProgR
rlpcToEither :: RLPC e a -> Either e a rlpcToEither :: RLPC a -> Either [MsgEnvelope RlpcError] a
rlpcToEither = evalRLPC def >>> fmap fst rlpcToEither r = case evalRLPC def r of
(Just a, _) -> Right a
(Nothing, es) -> Left es

View File

@@ -16,9 +16,9 @@ module Compiler.RLPC
, RLPCT(..) , RLPCT(..)
, RLPCIO , RLPCIO
, RLPCOptions(RLPCOptions) , RLPCOptions(RLPCOptions)
, RlpcError(..)
, IsRlpcError(..) , IsRlpcError(..)
, rlpc , RlpcError(..)
, MsgEnvelope(..)
, addFatal , addFatal
, addWound , addWound
, MonadErrorful , MonadErrorful
@@ -27,9 +27,6 @@ module Compiler.RLPC
, evalRLPCT , evalRLPCT
, evalRLPCIO , evalRLPCIO
, evalRLPC , evalRLPC
, addRlpcWound
, addRlpcFatal
, liftRlpcErrs
, rlpcLogFile , rlpcLogFile
, rlpcDebugOpts , rlpcDebugOpts
, rlpcEvaluator , rlpcEvaluator
@@ -40,6 +37,7 @@ module Compiler.RLPC
, flagDDumpOpts , flagDDumpOpts
, flagDDumpAST , flagDDumpAST
, def , def
, liftErrorful
) )
where where
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
@@ -51,6 +49,7 @@ import Control.Monad.Errorful
import Compiler.RlpcError import Compiler.RlpcError
import Data.Functor.Identity import Data.Functor.Identity
import Data.Default.Class import Data.Default.Class
import Data.Foldable
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Data.Hashable (Hashable) import Data.Hashable (Hashable)
import Data.HashSet (HashSet) import Data.HashSet (HashSet)
@@ -58,48 +57,44 @@ import Data.HashSet qualified as S
import Data.Coerce import Data.Coerce
import Lens.Micro import Lens.Micro
import Lens.Micro.TH import Lens.Micro.TH
import System.Exit
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
-- TODO: fancy errors newtype RLPCT m a = RLPCT {
newtype RLPCT e m a = RLPCT { runRLPCT :: ReaderT RLPCOptions (ErrorfulT (MsgEnvelope RlpcError) m) a
runRLPCT :: ReaderT RLPCOptions (ErrorfulT e m) a
} }
-- TODO: incorrect ussage of MonadReader. RLPC should have its own deriving (Functor, Applicative, Monad)
-- environment access functions
deriving (Functor, Applicative, Monad, MonadReader RLPCOptions)
deriving instance (MonadIO m) => MonadIO (RLPCT e m) type RLPC = RLPCT Identity
instance MonadTrans (RLPCT e) where type RLPCIO = RLPCT IO
lift = RLPCT . lift . lift
instance (MonadState s m) => MonadState s (RLPCT e m) where
state = lift . state
type RLPC e = RLPCT e Identity
type RLPCIO e = RLPCT e IO
evalRLPCT :: RLPCOptions
-> RLPCT e m a
-> m (Either e (a, [e]))
evalRLPCT o = runRLPCT >>> flip runReaderT o >>> runErrorfulT
evalRLPC :: RLPCOptions evalRLPC :: RLPCOptions
-> RLPC e a -> RLPC a
-> Either e (a, [e]) -> (Maybe a, [MsgEnvelope RlpcError])
evalRLPC o m = coerce $ evalRLPCT o m evalRLPC opt r = runRLPCT r
& flip runReaderT opt
& runErrorful
evalRLPCIO :: (Exception e) evalRLPCT :: (Monad m)
=> RLPCOptions => RLPCOptions
-> RLPCIO e a -> RLPCT m a
-> IO (a, [e]) -> m (Maybe a, [MsgEnvelope RlpcError])
evalRLPCIO o m = do evalRLPCT = undefined
m' <- evalRLPCT o m
case m' of evalRLPCIO :: RLPCOptions -> RLPCIO a -> IO a
-- TODO: errors evalRLPCIO opt r = do
Left e -> throwIO e (ma,es) <- evalRLPCT opt r
Right a -> pure a putRlpcErrs es
case ma of
Just x -> pure x
Nothing -> die "Failed, no code compiled."
putRlpcErrs :: [MsgEnvelope RlpcError] -> IO ()
putRlpcErrs = traverse_ print
liftErrorful :: (Monad m, IsRlpcError e) => ErrorfulT (MsgEnvelope e) m a -> RLPCT m a
liftErrorful e = RLPCT $ lift (fmap liftRlpcError `mapErrorful` e)
data RLPCOptions = RLPCOptions data RLPCOptions = RLPCOptions
{ _rlpcLogFile :: Maybe FilePath { _rlpcLogFile :: Maybe FilePath
@@ -113,32 +108,6 @@ data RLPCOptions = RLPCOptions
data Evaluator = EvaluatorGM | EvaluatorTI data Evaluator = EvaluatorGM | EvaluatorTI
deriving Show deriving Show
data Severity = Error
| Warning
| Debug
deriving Show
-- temporary until we have a new doc building system
type ErrorDoc = String
instance (Monad m) => MonadErrorful e (RLPCT e m) where
addWound = RLPCT . lift . addWound
addFatal = RLPCT . lift . addFatal
liftRlpcErrs :: (IsRlpcError e, Monad m)
=> RLPCT e m a -> RLPCT RlpcError m a
liftRlpcErrs m = RLPCT . ReaderT $ \r ->
mapErrors liftRlpcErr $ runRLPCT >>> (`runReaderT` r) $ m
addRlpcWound :: (IsRlpcError e, Monad m) => e -> RLPCT RlpcError m ()
addRlpcWound = addWound . liftRlpcErr
addRlpcFatal :: (IsRlpcError e, Monad m) => e -> RLPCT RlpcError m ()
addRlpcFatal = addWound . liftRlpcErr
rlpc :: (Monad m) => ErrorfulT e m a -> RLPCT e m a
rlpc = RLPCT . ReaderT . const
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
instance Default RLPCOptions where instance Default RLPCOptions where

View File

@@ -1,15 +1,70 @@
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
module Compiler.RlpcError module Compiler.RlpcError
( RlpcError(..) ( IsRlpcError(..)
, IsRlpcError(..) , MsgEnvelope(..)
, Severity(..)
, RlpcError(..)
, SrcSpan(..)
, msgSpan
, msgDiagnostic
, msgSeverity
, liftRlpcErrors
, errorMsg
) )
where where
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
import Control.Monad.Errorful import Control.Monad.Errorful
import Data.Text (Text)
import Data.Text qualified as T
import GHC.Exts (IsString(..))
import Lens.Micro.Platform
import Lens.Micro.Platform.Internal
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
data RlpcError = RlpcErr String -- temp data MsgEnvelope e = MsgEnvelope
{ _msgSpan :: SrcSpan
, _msgDiagnostic :: e
, _msgSeverity :: Severity
}
deriving (Functor, Show)
newtype RlpcError = Text [Text]
deriving Show deriving Show
class IsRlpcError a where instance IsString RlpcError where
liftRlpcErr :: a -> RlpcError fromString = Text . pure . T.pack
class IsRlpcError e where
liftRlpcError :: e -> RlpcError
instance IsRlpcError RlpcError where
liftRlpcError = id
data Severity = SevWarning
| SevError
deriving Show
data SrcSpan = SrcSpan
!Int -- ^ Line
!Int -- ^ Column
!Int -- ^ Length
deriving Show
makeLenses ''MsgEnvelope
liftRlpcErrors :: (Functor m, IsRlpcError e)
=> ErrorfulT e m a
-> ErrorfulT RlpcError m a
liftRlpcErrors = mapErrorful liftRlpcError
instance (IsRlpcError e) => IsRlpcError (MsgEnvelope e) where
liftRlpcError msg = msg ^. msgDiagnostic & liftRlpcError
errorMsg :: SrcSpan -> e -> MsgEnvelope e
errorMsg s e = MsgEnvelope
{ _msgSpan = s
, _msgDiagnostic = e
, _msgSeverity = SevError
}

View File

@@ -1,73 +1,79 @@
{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TupleSections, PatternSynonyms #-} {-# LANGUAGE TupleSections, PatternSynonyms #-}
{-# LANGUAGE UndecidableInstances #-}
module Control.Monad.Errorful module Control.Monad.Errorful
( ErrorfulT ( ErrorfulT
, runErrorfulT , runErrorfulT
, Errorful , Errorful
, runErrorful , runErrorful
, mapErrors , mapErrorful
, MonadErrorful(..) , MonadErrorful(..)
) )
where where
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
import Control.Monad.State.Strict
import Control.Monad.Trans import Control.Monad.Trans
import Data.Functor.Identity import Data.Functor.Identity
import Data.Coerce import Data.Coerce
import Data.HashSet (HashSet)
import Data.HashSet qualified as H
import Lens.Micro import Lens.Micro
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
newtype ErrorfulT e m a = ErrorfulT { runErrorfulT :: m (Either e (a, [e])) } newtype ErrorfulT e m a = ErrorfulT { runErrorfulT :: m (Maybe a, [e]) }
type Errorful e = ErrorfulT e Identity type Errorful e = ErrorfulT e Identity
pattern Errorful :: (Either e (a, [e])) -> Errorful e a pattern Errorful :: (Maybe a, [e]) -> Errorful e a
pattern Errorful a = ErrorfulT (Identity a) pattern Errorful a = ErrorfulT (Identity a)
runErrorful :: Errorful e a -> Either e (a, [e]) runErrorful :: Errorful e a -> (Maybe a, [e])
runErrorful m = coerce (runErrorfulT m) runErrorful m = coerce (runErrorfulT m)
class (Applicative m) => MonadErrorful e m | m -> e where class (Applicative m) => MonadErrorful e m | m -> e where
addWound :: e -> m () addWound :: e -> m ()
addFatal :: e -> m a addFatal :: e -> m a
-- not sure if i want to add this yet...
-- catchWound :: m a -> (e -> m a) -> m a
instance (Applicative m) => MonadErrorful e (ErrorfulT e m) where instance (Applicative m) => MonadErrorful e (ErrorfulT e m) where
addWound e = ErrorfulT $ pure . Right $ ((), [e]) addWound e = ErrorfulT $ pure (Just (), [e])
addFatal e = ErrorfulT $ pure . Left $ e addFatal e = ErrorfulT $ pure (Nothing, [e])
instance MonadTrans (ErrorfulT e) where instance MonadTrans (ErrorfulT e) where
lift m = ErrorfulT (Right . (,[]) <$> m) lift m = ErrorfulT ((\x -> (Just x,[])) <$> m)
instance (MonadIO m) => MonadIO (ErrorfulT e m) where instance (MonadIO m) => MonadIO (ErrorfulT e m) where
liftIO = lift . liftIO liftIO = lift . liftIO
instance (Functor m) => Functor (ErrorfulT e m) where instance (Functor m) => Functor (ErrorfulT e m) where
fmap f (ErrorfulT m) = ErrorfulT $ fmap (_1 %~ f) <$> m fmap f (ErrorfulT m) = ErrorfulT (m & mapped . _1 . _Just %~ f)
instance (Applicative m) => Applicative (ErrorfulT e m) where instance (Applicative m) => Applicative (ErrorfulT e m) where
pure a = ErrorfulT (pure . Right $ (a, [])) pure a = ErrorfulT . pure $ (Just a, [])
m <*> a = ErrorfulT (m' `apply` a') ErrorfulT m <*> ErrorfulT n = ErrorfulT $ m `apply` n where
where apply :: m (Maybe (a -> b), [e]) -> m (Maybe a, [e]) -> m (Maybe b, [e])
m' = runErrorfulT m apply = liftA2 $ \ (mf,e1) (ma,e2) -> (mf <*> ma, e1 <> e2)
a' = runErrorfulT a
-- TODO: strict concatenation
apply = liftA2 $ liftA2 (\ (f,e1) (x,e2) -> (f x, e1 ++ e2))
instance (Monad m) => Monad (ErrorfulT e m) where instance (Monad m) => Monad (ErrorfulT e m) where
ErrorfulT m >>= k = ErrorfulT $ do ErrorfulT m >>= k = ErrorfulT $ do
m' <- m (a,es) <- m
case m' of case a of
Right (a,es) -> runErrorfulT (k a) Just x -> runErrorfulT (k x)
Left e -> pure (Left e) Nothing -> pure (Nothing, es)
mapErrors :: (Monad m) => (e -> e') -> ErrorfulT e m a -> ErrorfulT e' m a mapErrorful :: (Functor m) => (e -> e') -> ErrorfulT e m a -> ErrorfulT e' m a
mapErrors f m = ErrorfulT $ do mapErrorful f (ErrorfulT m) = ErrorfulT $
x <- runErrorfulT m m & mapped . _2 . mapped %~ f
case x of
Left e -> pure . Left $ f e -- when microlens-pro drops we can write this as
Right (a,es) -> pure . Right $ (a, f <$> es) -- mapErrorful f = coerced . mapped . _2 . mappd %~ f
-- lol
--------------------------------------------------------------------------------
-- daily dose of n^2 instances
instance (Monad m, MonadErrorful e m) => MonadErrorful e (StateT s m) where
addWound = undefined
addFatal = undefined

View File

@@ -15,6 +15,13 @@ import Core.Syntax
import Core.TH import Core.TH
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
-- fac3 = undefined
-- sumList = undefined
-- constDivZero = undefined
-- idCase = undefined
---
letrecExample :: Program' letrecExample :: Program'
letrecExample = [coreProg| letrecExample = [coreProg|
pair x y f = f x y; pair x y f = f x y;
@@ -216,3 +223,4 @@ idCase = [coreProg|
-- , ScDef "Cons" [] $ Con 2 2 -- , ScDef "Cons" [] $ Con 2 2
-- ] -- ]
--}

View File

@@ -3,6 +3,7 @@ Module : Core.HindleyMilner
Description : Hindley-Milner type system Description : Hindley-Milner type system
-} -}
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Core.HindleyMilner module Core.HindleyMilner
( Context' ( Context'
, infer , infer
@@ -16,15 +17,17 @@ module Core.HindleyMilner
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
import Lens.Micro import Lens.Micro
import Lens.Micro.Mtl import Lens.Micro.Mtl
import Lens.Micro.Platform
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Text qualified as T import Data.Text qualified as T
import Data.HashMap.Strict qualified as H import Data.HashMap.Strict qualified as H
import Data.Foldable (traverse_) import Data.Foldable (traverse_)
import Compiler.RLPC import Compiler.RLPC
import Control.Monad (foldM, void) import Control.Monad (foldM, void, forM)
import Control.Monad.Errorful (Errorful, addFatal) import Control.Monad.Errorful (Errorful, addFatal)
import Control.Monad.State import Control.Monad.State
import Control.Monad.Utils (mapAccumLM) import Control.Monad.Utils (mapAccumLM)
import Text.Printf
import Core.Syntax import Core.Syntax
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
@@ -48,9 +51,20 @@ data TypeError
| TyErrMissingTypeSig Name | TyErrMissingTypeSig Name
deriving (Show, Eq) deriving (Show, Eq)
-- TODO:
instance IsRlpcError TypeError where instance IsRlpcError TypeError where
liftRlpcErr = RlpcErr . show liftRlpcError = \case
-- todo: use anti-parser instead of show
TyErrCouldNotUnify t u -> Text
[ T.pack $ printf "Could not match type `%s' with `%s'."
(show t) (show u)
, "Expected: " <> tshow t
, "Got: " <> tshow u
]
TyErrRecursiveType t x -> Text
[ T.pack $ printf "recursive type error lol"
]
where tshow = T.pack . show
-- | Synonym for @Errorful [TypeError]@. This means an @HMError@ action may -- | Synonym for @Errorful [TypeError]@. This means an @HMError@ action may
-- throw any number of fatal or nonfatal errors. Run with @runErrorful@. -- throw any number of fatal or nonfatal errors. Run with @runErrorful@.
@@ -88,10 +102,10 @@ checkCoreProg p = scDefs
where scname = sc ^. _lhs._1 where scname = sc ^. _lhs._1
-- | @checkCoreProgR p@ returns @p@ if @p@ successfully typechecks. -- | @checkCoreProgR p@ returns @p@ if @p@ successfully typechecks.
checkCoreProgR :: Program' -> RLPC RlpcError Program' checkCoreProgR :: Program' -> RLPC Program'
checkCoreProgR p = do checkCoreProgR p = undefined
liftRlpcErrs . rlpc . checkCoreProg $ p
pure p {-# WARNING checkCoreProgR "unimpl" #-}
-- | Infer the type of an expression under some context. -- | Infer the type of an expression under some context.
-- --
@@ -140,7 +154,27 @@ gather = \g e -> runStateT (go g e) ([],0) <&> \ (t,(cs,_)) -> (t,cs) where
Let NonRec bs e -> do Let NonRec bs e -> do
g' <- buildLetContext g bs g' <- buildLetContext g bs
go g' e go g' e
-- TODO letrec, lambda, case Let Rec bs e -> do
g' <- buildLetrecContext g bs
go g' e
-- TODO lambda, case
buildLetrecContext :: Context' -> [Binding']
-> StateT ([Constraint], Int) HMError Context'
buildLetrecContext g bs = do
let f ag (k := _) = do
n <- uniqueVar
pure ((k,n) : ag)
rg <- foldM f g bs
let k ag (k := v) = do
t <- go rg v
pure ((k,t) : ag)
foldM k g bs
-- | augment a context with the inferred types of each binder. the returned
-- context is linearly accumulated, meaning that the context used to infer each binder
-- will include the inferred types of all previous binder
buildLetContext :: Context' -> [Binding'] buildLetContext :: Context' -> [Binding']
-> StateT ([Constraint], Int) HMError Context' -> StateT ([Constraint], Int) HMError Context'
@@ -218,3 +252,20 @@ subst x t (TyVar y) | x == y = t
subst x t (a :-> b) = subst x t a :-> subst x t b subst x t (a :-> b) = subst x t a :-> subst x t b
subst _ _ e = e subst _ _ e = e
--------------------------------------------------------------------------------
demoContext :: Context'
demoContext =
[ ("fix", (TyVar "a" :-> TyVar "a") :-> TyVar "a")
, ("add", TyInt :-> TyInt :-> TyInt)
, ("==", TyInt :-> TyInt :-> TyCon "Bool")
, ("True", TyCon "Bool")
, ("False", TyCon "Bool")
]
pprintType :: Type -> String
pprintType (s :-> t) = "(" <> pprintType s <> " -> " <> pprintType t <> ")"
pprintType TyFun = "(->)"
pprintType (TyVar x) = x ^. unpacked
pprintType (TyCon t) = t ^. unpacked

View File

@@ -167,24 +167,19 @@ lexWith :: (Text -> CoreToken) -> Lexer
lexWith f (AlexPn _ y x,_,_,s) l = pure $ Located y x l (f $ T.take l s) lexWith f (AlexPn _ y x,_,_,s) l = pure $ Located y x l (f $ T.take l s)
-- | The main lexer driver. -- | The main lexer driver.
lexCore :: Text -> RLPC SrcError [Located CoreToken] lexCore :: Text -> RLPC [Located CoreToken]
lexCore s = case m of lexCore s = case m of
Left e -> addFatal err Left e -> error "core lex error"
where err = SrcError
{ _errSpan = (0,0,0) -- TODO: location
, _errSeverity = Error
, _errDiagnostic = SrcErrLexical e
}
Right ts -> pure ts Right ts -> pure ts
where where
m = runAlex s lexStream m = runAlex s lexStream
lexCoreR :: Text -> RLPC RlpcError [Located CoreToken] lexCoreR :: Text -> RLPC [Located CoreToken]
lexCoreR = liftRlpcErrs . lexCore lexCoreR = lexCore
-- | @lexCore@, but the tokens are stripped of location info. Useful for -- | @lexCore@, but the tokens are stripped of location info. Useful for
-- debugging -- debugging
lexCore' :: Text -> RLPC SrcError [CoreToken] lexCore' :: Text -> RLPC [CoreToken]
lexCore' s = fmap f <$> lexCore s lexCore' s = fmap f <$> lexCore s
where f (Located _ _ _ t) = t where f (Located _ _ _ t) = t
@@ -201,11 +196,11 @@ data ParseError = ParErrLexical String
-- TODO: -- TODO:
instance IsRlpcError SrcError where instance IsRlpcError SrcError where
liftRlpcErr = RlpcErr . show liftRlpcError = Text . pure . T.pack . show
-- TODO: -- TODO:
instance IsRlpcError ParseError where instance IsRlpcError ParseError where
liftRlpcErr = RlpcErr . show liftRlpcError = Text . pure . T.pack . show
alexEOF :: Alex (Located CoreToken) alexEOF :: Alex (Located CoreToken)
alexEOF = Alex $ \ st@(AlexState { alex_pos = AlexPn _ y x }) -> alexEOF = Alex $ \ st@(AlexState { alex_pos = AlexPn _ y x }) ->

View File

@@ -10,7 +10,6 @@ module Core.Parse
, parseCoreProg , parseCoreProg
, parseCoreProgR , parseCoreProgR
, module Core.Lex -- temp convenience , module Core.Lex -- temp convenience
, parseTmp
, SrcError , SrcError
, Module , Module
) )
@@ -34,7 +33,7 @@ import Data.HashMap.Strict qualified as H
%name parseCoreProg StandaloneProgram %name parseCoreProg StandaloneProgram
%tokentype { Located CoreToken } %tokentype { Located CoreToken }
%error { parseError } %error { parseError }
%monad { RLPC SrcError } %monad { RLPC } { happyBind } { happyPure }
%token %token
let { Located _ _ _ TokenLet } let { Located _ _ _ TokenLet }
@@ -99,6 +98,8 @@ ScDefs : ScDef ';' ScDefs { $1 : $3 }
ScDef :: { ScDef Name } ScDef :: { ScDef Name }
ScDef : Var ParList '=' Expr { ScDef $1 $2 $4 } ScDef : Var ParList '=' Expr { ScDef $1 $2 $4 }
-- hack to allow constructors to be compiled into scs
| Con ParList '=' Expr { ScDef $1 $2 $4 }
Type :: { Type } Type :: { Type }
Type : Type1 { $1 } Type : Type1 { $1 }
@@ -189,34 +190,23 @@ Con : '(' consym ')' { $2 }
{ {
parseError :: [Located CoreToken] -> RLPC SrcError a parseError :: [Located CoreToken] -> RLPC a
parseError (Located y x l _ : _) = addFatal err parseError (Located y x l t : _) =
where err = SrcError error $ show y <> ":" <> show x
{ _errSpan = (y,x,l) <> ": parse error at token `" <> show t <> "'"
, _errSeverity = Error
, _errDiagnostic = SrcErrParse
}
parseTmp :: IO (Module Name) {-# WARNING parseError "unimpl" #-}
parseTmp = do
s <- TIO.readFile "/tmp/t.hs"
case parse s of
Left e -> error (show e)
Right (ts,_) -> pure ts
where
parse = evalRLPC def . (lexCore >=> parseCore)
exprPragma :: [String] -> RLPC SrcError (Expr Name) exprPragma :: [String] -> RLPC (Expr Name)
exprPragma ("AST" : e) = astPragma e exprPragma ("AST" : e) = undefined
exprPragma _ = addFatal err exprPragma _ = undefined
where err = SrcError
{ _errSpan = (0,0,0) -- TODO: span
, _errSeverity = Warning
, _errDiagnostic = SrcErrUnknownPragma "" -- TODO: missing pragma
}
astPragma :: [String] -> RLPC SrcError (Expr Name) {-# WARNING exprPragma "unimpl" #-}
astPragma = pure . read . unwords
astPragma :: [String] -> RLPC (Expr Name)
astPragma _ = undefined
{-# WARNING astPragma "unimpl" #-}
insTypeSig :: (Hashable b) => (b, Type) -> Program b -> Program b insTypeSig :: (Hashable b) => (b, Type) -> Program b -> Program b
insTypeSig ts = programTypeSigs %~ uncurry H.insert ts insTypeSig ts = programTypeSigs %~ uncurry H.insert ts
@@ -230,8 +220,14 @@ insScDef sc = programScDefs %~ (sc:)
singletonScDef :: (Hashable b) => ScDef b -> Program b singletonScDef :: (Hashable b) => ScDef b -> Program b
singletonScDef sc = insScDef sc mempty singletonScDef sc = insScDef sc mempty
parseCoreProgR :: [Located CoreToken] -> RLPC RlpcError Program' parseCoreProgR :: [Located CoreToken] -> RLPC Program'
parseCoreProgR = liftRlpcErrs . parseCoreProg parseCoreProgR = parseCoreProg
happyBind :: RLPC a -> (a -> RLPC b) -> RLPC b
happyBind m k = m >>= k
happyPure :: a -> RLPC a
happyPure a = pure a
} }

View File

@@ -6,7 +6,6 @@ module Core.TH
( coreExpr ( coreExpr
, coreProg , coreProg
, coreProgT , coreProgT
, core
) )
where where
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
@@ -14,74 +13,38 @@ import Language.Haskell.TH
import Language.Haskell.TH.Syntax hiding (Module) import Language.Haskell.TH.Syntax hiding (Module)
import Language.Haskell.TH.Quote import Language.Haskell.TH.Quote
import Control.Monad ((>=>)) import Control.Monad ((>=>))
import Control.Monad.IO.Class
import Control.Arrow ((>>>))
import Compiler.RLPC import Compiler.RLPC
import Data.Default.Class (def) import Data.Default.Class (def)
import Data.Text (Text)
import Data.Text qualified as T import Data.Text qualified as T
import Core.Parse import Core.Parse
import Core.Lex import Core.Lex
import Core.Syntax
import Core.HindleyMilner (checkCoreProgR) import Core.HindleyMilner (checkCoreProgR)
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
-- TODO: write in terms of a String -> QuasiQuoter
core :: QuasiQuoter
core = QuasiQuoter
{ quoteExp = qCore
, quotePat = error "core quasiquotes may only be used in expressions"
, quoteType = error "core quasiquotes may only be used in expressions"
, quoteDec = error "core quasiquotes may only be used in expressions"
}
coreProg :: QuasiQuoter coreProg :: QuasiQuoter
coreProg = QuasiQuoter coreProg = mkqq $ lexCoreR >=> parseCoreProgR
{ quoteExp = qCoreProg
, quotePat = error "core quasiquotes may only be used in expressions"
, quoteType = error "core quasiquotes may only be used in expressions"
, quoteDec = error "core quasiquotes may only be used in expressions"
}
coreExpr :: QuasiQuoter coreExpr :: QuasiQuoter
coreExpr = QuasiQuoter coreExpr = mkqq $ lexCoreR >=> parseCoreExpr
{ quoteExp = qCoreExpr
, quotePat = error "core quasiquotes may only be used in expressions"
, quoteType = error "core quasiquotes may only be used in expressions"
, quoteDec = error "core quasiquotes may only be used in expressions"
}
-- | Type-checked @coreProg@ -- | Type-checked @coreProg@
coreProgT :: QuasiQuoter coreProgT :: QuasiQuoter
coreProgT = QuasiQuoter coreProgT = mkqq $ lexCoreR >=> parseCoreProgR >=> checkCoreProgR
{ quoteExp = qCoreProgT
mkqq :: (Lift a) => (Text -> RLPC a) -> QuasiQuoter
mkqq p = QuasiQuoter
{ quoteExp = mkq p
, quotePat = error "core quasiquotes may only be used in expressions" , quotePat = error "core quasiquotes may only be used in expressions"
, quoteType = error "core quasiquotes may only be used in expressions" , quoteType = error "core quasiquotes may only be used in expressions"
, quoteDec = error "core quasiquotes may only be used in expressions" , quoteDec = error "core quasiquotes may only be used in expressions"
} }
qCore :: String -> Q Exp mkq :: (Lift a) => (Text -> RLPC a) -> String -> Q Exp
qCore s = case parse (T.pack s) of mkq parse s = case evalRLPC def (parse $ T.pack s) of
Left e -> error (show e) (Just a, _) -> lift a
Right (m,ts) -> lift m (Nothing, _) -> error "todo: aaahhbbhjhbdjhabsjh"
where
parse = evalRLPC def . (lexCore >=> parseCore)
qCoreExpr :: String -> Q Exp
qCoreExpr s = case parseExpr (T.pack s) of
Left e -> error (show e)
Right (m,ts) -> lift m
where
parseExpr = evalRLPC def . (lexCore >=> parseCoreExpr)
qCoreProg :: String -> Q Exp
qCoreProg s = case parse (T.pack s) of
Left e -> error (show e)
Right (m,ts) -> lift m
where
parse = evalRLPC def . (lexCoreR >=> parseCoreProgR)
qCoreProgT :: String -> Q Exp
qCoreProgT s = case parse (T.pack s) of
Left e -> error (show e)
Right (m,_) -> lift m
where
parse = evalRLPC def . (lexCoreR >=> parseCoreProgR >=> checkCoreProgR)

View File

@@ -1,59 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
module RLP.Syntax
( RlpExpr
)
where
----------------------------------------------------------------------------------
import Data.Text (Text)
import Lens.Micro
import Core (HasRHS(..), HasLHS(..))
----------------------------------------------------------------------------------
newtype RlpProgram b = RlpProgram [Decl b]
data Decl b = InfixD InfixAssoc Int VarId
| FunD VarId [Pat b] (RlpExpr b)
| DataD ConId [ConId] [ConAlt]
data ConAlt = ConAlt ConId [ConId]
data InfixAssoc = Assoc | AssocL | AssocR
data RlpExpr b = LetE [Bind b] (RlpExpr b)
| VarE VarId
| ConE ConId
| LamE [Pat b] (RlpExpr b)
| CaseE (RlpExpr b) [Alt b]
| IfE (RlpExpr b) (RlpExpr b) (RlpExpr b)
| AppE (RlpExpr b) (RlpExpr b)
| LitE (Lit b)
-- do we want guards?
data Alt b = AltA (Pat b) (RlpExpr b)
data Bind b = PatB (Pat b) (RlpExpr b)
| FunB VarId [Pat b] (RlpExpr b)
data VarId = NameVar Text
| SymVar Text
data ConId = NameCon Text
| SymCon Text
data Pat b = VarP VarId
| LitP (Lit b)
| ConP ConId [Pat b]
data Lit b = IntL Int
| CharL Char
| ListL [RlpExpr b]
-- instance HasLHS Alt Alt Pat Pat where
-- _lhs = lens
-- (\ (AltA p _) -> p)
-- (\ (AltA _ e) p' -> AltA p' e)
-- instance HasRHS Alt Alt RlpExpr RlpExpr where
-- _rhs = lens
-- (\ (AltA _ e) -> e)
-- (\ (AltA p _) e' -> AltA p e')

349
src/Rlp/Lex.x Normal file
View File

@@ -0,0 +1,349 @@
{
{-# LANGUAGE ViewPatterns, LambdaCase #-}
{-# LANGUAGE GeneralisedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
module Rlp.Lex
( P(..)
, RlpToken(..)
, Located(..)
, lexToken
, lexStream
, lexDebug
, lexCont
)
where
import Codec.Binary.UTF8.String (encodeChar)
import Control.Monad
import Control.Monad.Errorful
import Core.Syntax (Name)
import Data.Functor.Identity
import Data.Char (digitToInt)
import Data.Monoid (First)
import Data.Maybe
import Data.Text (Text)
import Data.Text qualified as T
import Data.Word
import Data.Default
import Lens.Micro.Mtl
import Lens.Micro
import Debug.Trace
import Rlp.Parse.Types
}
$whitechar = [ \t\n\r\f\v]
$nl = [\n\r]
$white_no_nl = $white # $nl
$lower = [a-z \_]
$upper = [A-Z]
$alpha = [$lower $upper]
$digit = 0-9
$special = [\(\)\,\;\[\]\{\}]
$namechar = [$alpha $digit \' \#]
$asciisym = [\!\#\$\%\&\*\+\.\/\<\=\>\?\@\\\^\|\-\~\:]
@decimal = $digit+
@varname = $lower $namechar*
@conname = $upper $namechar*
@consym = \: $asciisym*
@varsym = $asciisym+
@reservedname =
case|data|do|import|in|let|letrec|module|of|where
|infixr|infixl|infix
@reservedop =
"=" | \\ | "->" | "|"
rlp :-
-- everywhere: skip whitespace
$white_no_nl+ ;
-- everywhere: skip comments
-- TODO: don't treat operators like (-->) as comments
"--".* ;
-- we are indentation-sensitive! do not skip NLs!. upon encountering a newline,
-- we check indentation and potentially insert extra tokens. search this file
-- for the definition of `doBol`
<0> \n { beginPush bol }
-- scan various identifiers and reserved words. order is important here!
<0>
{
@reservedname { tokenWith lexReservedName }
@conname { tokenWith TokenConName }
@varname { tokenWith TokenVarName }
@reservedop { tokenWith lexReservedOp }
@consym { tokenWith TokenConSym }
@varsym { tokenWith TokenVarSym }
}
-- literals -- currently this is just unsigned integer literals
<0>
{
@decimal { tokenWith (TokenLitInt . readInt) }
}
-- control characters
<0>
{
"(" { constToken TokenLParen }
")" { constToken TokenRParen }
"{" { explicitLBrace }
"}" { explicitRBrace }
";" { constToken TokenSemicolon }
}
-- consume all whitespace leaving us at the beginning of the next non-empty
-- line. we then compare the indentation of that line to the enclosing layout
-- context and proceed accordingly
<bol>
{
$whitechar ;
\n ;
() { doBol }
}
<layout_top>
{
\n ;
"{" { explicitLBrace `thenDo` popLexState }
() { doLayout }
}
{
lexReservedName :: Text -> RlpToken
lexReservedName = \case
"data" -> TokenData
"case" -> TokenCase
"of" -> TokenOf
"let" -> TokenLet
"in" -> TokenIn
"infix" -> TokenInfix
"infixl" -> TokenInfixL
"infixr" -> TokenInfixR
lexReservedOp :: Text -> RlpToken
lexReservedOp = \case
"=" -> TokenEquals
"::" -> TokenHasType
"|" -> TokenPipe
-- | @andBegin@, with the subtle difference that the start code is set
-- /after/ the action
thenBegin :: LexerAction a -> Int -> LexerAction a
thenBegin act c inp l = do
a <- act inp l
psLexState . _head .= c
pure a
andBegin :: LexerAction a -> Int -> LexerAction a
andBegin act c inp l = do
psLexState . _head .= c
act inp l
beginPush :: Int -> LexerAction (Located RlpToken)
beginPush n _ _ = pushLexState n >> lexToken
alexGetByte :: AlexInput -> Maybe (Word8, AlexInput)
alexGetByte inp = case inp ^. aiBytes of
[] -> do
(c,t) <- T.uncons (inp ^. aiSource)
let (b:bs) = encodeChar c
-- tail the source
inp' = inp & aiSource .~ t
-- record the excess bytes for successive calls
& aiBytes .~ bs
-- report the previous char
& aiPrevChar .~ c
-- update the position
& aiPos %~ \ (ln,col) ->
if c == '\n'
then (ln+1,1)
else (ln,col+1)
pure (b, inp')
_ -> Just (head bs, inp')
where
(bs, inp') = inp & aiBytes <<%~ drop 1
getInput :: P AlexInput
getInput = use psInput
getLexState :: P Int
getLexState = use (psLexState . singular _head)
alexInputPrevChar :: AlexInput -> Char
alexInputPrevChar = view aiPrevChar
pushLexState :: Int -> P ()
pushLexState n = psLexState %= (n:)
readInt :: Text -> Int
readInt = T.foldr f 0 where
f c n = digitToInt c + 10*n
constToken :: RlpToken -> LexerAction (Located RlpToken)
constToken t inp l = do
pos <- use (psInput . aiPos)
pure (Located (pos,l) t)
tokenWith :: (Text -> RlpToken) -> LexerAction (Located RlpToken)
tokenWith tf inp l = do
pos <- getPos
let t = tf (T.take l $ inp ^. aiSource)
pure (Located (pos,l) t)
getPos :: P Position
getPos = use (psInput . aiPos)
alexEOF :: P (Located RlpToken)
alexEOF = do
inp <- getInput
pure (Located undefined TokenEOF)
initParseState :: Text -> ParseState
initParseState s = ParseState
{ _psLayoutStack = []
-- IMPORTANT: the initial state is `bol` to begin the top-level layout,
-- which then returns to state 0 which continues the normal lexing process.
, _psLexState = [layout_top,0]
, _psInput = initAlexInput s
, _psOpTable = mempty
}
initAlexInput :: Text -> AlexInput
initAlexInput s = AlexInput
{ _aiPrevChar = '\0'
, _aiSource = s
, _aiBytes = []
, _aiPos = (1,1)
}
runP' :: P a -> Text -> (ParseState, [MsgEnvelope RlpParseError], Maybe a)
runP' p s = runP p st where
st = initParseState s
lexToken :: P (Located RlpToken)
lexToken = do
inp <- getInput
c <- getLexState
st <- use id
-- traceM $ "st: " <> show st
case alexScan inp c of
AlexEOF -> pure $ Located (inp ^. aiPos, 0) TokenEOF
AlexSkip inp' l -> do
psInput .= inp'
lexToken
AlexToken inp' l act -> do
psInput .= inp'
act inp l
AlexError inp' -> addFatalHere 1 RlpParErrLexical
lexCont :: (Located RlpToken -> P a) -> P a
lexCont = (lexToken >>=)
lexStream :: P [RlpToken]
lexStream = do
t <- lexToken
case t of
Located _ TokenEOF -> pure [TokenEOF]
Located _ t -> (t:) <$> lexStream
lexDebug :: (Located RlpToken -> P a) -> P a
lexDebug k = do
t <- lexToken
traceM $ "token: " <> show t
k t
lexTest :: Text -> Maybe [RlpToken]
lexTest s = runP' lexStream s ^. _3
indentLevel :: P Int
indentLevel = do
pos <- use (psInput . aiPos)
pure (pos ^. _2)
insertToken :: RlpToken -> P (Located RlpToken)
insertToken t = do
pos <- use (psInput . aiPos)
pure (Located (pos, 0) t)
popLayout :: P Layout
popLayout = do
-- traceM "pop layout"
ctx <- preuse (psLayoutStack . _head)
psLayoutStack %= (drop 1)
case ctx of
Just l -> pure l
Nothing -> error "uhh"
pushLayout :: Layout -> P ()
pushLayout l = do
-- traceM "push layout"
psLayoutStack %= (l:)
popLexState :: P ()
popLexState = do
psLexState %= tail
insertSemicolon, insertLBrace, insertRBrace :: P (Located RlpToken)
insertSemicolon = {- traceM "inserting semi" >> -} insertToken TokenSemicolonV
insertLBrace = {- traceM "inserting lbrace" >> -} insertToken TokenLBraceV
insertRBrace = {- traceM "inserting rbrace" >> -} insertToken TokenRBraceV
cmpLayout :: P Ordering
cmpLayout = do
i <- indentLevel
ctx <- preuse (psLayoutStack . _head)
case ctx of
Just (Implicit n) -> pure (i `compare` n)
_ -> pure GT
doBol :: LexerAction (Located RlpToken)
doBol inp l = do
off <- cmpLayout
i <- indentLevel
traceM $ "i: " <> show i
-- important that we pop the lex state lest we find our lexer diverging
popLexState
case off of
-- the line is aligned with the previous. it therefore belongs to the
-- same list
EQ -> insertSemicolon
-- the line is indented further than the previous, so we assume it is a
-- line continuation. ignore it and move on!
GT -> lexToken
-- the line is indented less than the previous, pop the layout stack and
-- insert a closing brace.
LT -> popLayout >> insertRBrace
thenDo :: LexerAction a -> P b -> LexerAction a
thenDo act p inp l = act inp l <* p
explicitLBrace :: LexerAction (Located RlpToken)
explicitLBrace inp l = do
pushLayout Explicit
constToken TokenLBrace inp l
explicitRBrace :: LexerAction (Located RlpToken)
explicitRBrace inp l = do
popLayout
constToken TokenRBrace inp l
doLayout :: LexerAction (Located RlpToken)
doLayout _ _ = do
i <- indentLevel
pushLayout (Implicit i)
popLexState
insertLBrace
}

181
src/Rlp/Parse.y Normal file
View File

@@ -0,0 +1,181 @@
{
{-# LANGUAGE LambdaCase #-}
module Rlp.Parse
( parseRlpProg
)
where
import Compiler.RlpcError
import Rlp.Lex
import Rlp.Syntax
import Rlp.Parse.Types
import Rlp.Parse.Associate
import Lens.Micro
import Lens.Micro.Mtl
import Lens.Micro.Platform ()
import Data.List.Extra
import Data.Fix
import Data.Functor.Const
import Data.Text qualified as T
}
%name parseRlpProg StandaloneProgram
%monad { P }
%lexer { lexCont } { Located _ TokenEOF }
%error { parseError }
%tokentype { Located RlpToken }
%token
varname { Located _ (TokenVarName $$) }
conname { Located _ (TokenConName $$) }
consym { Located _ (TokenConSym $$) }
varsym { Located _ (TokenVarSym $$) }
data { Located _ TokenData }
litint { Located _ (TokenLitInt $$) }
'=' { Located _ TokenEquals }
'|' { Located _ TokenPipe }
';' { Located _ TokenSemicolon }
'(' { Located _ TokenLParen }
')' { Located _ TokenRParen }
'->' { Located _ TokenArrow }
vsemi { Located _ TokenSemicolonV }
'{' { Located _ TokenLBrace }
'}' { Located _ TokenRBrace }
vlbrace { Located _ TokenLBraceV }
vrbrace { Located _ TokenRBraceV }
infixl { Located _ TokenInfixL }
infixr { Located _ TokenInfixR }
infix { Located _ TokenInfix }
%right '->'
%%
StandaloneProgram :: { RlpProgram' }
StandaloneProgram : '{' Decls '}' {% mkProgram $2 }
| VL DeclsV VR {% mkProgram $2 }
VL :: { () }
VL : vlbrace { () }
VR :: { () }
VR : vrbrace { () }
| error { () }
Decls :: { [PartialDecl'] }
Decls : Decl ';' Decls { $1 : $3 }
| Decl ';' { [$1] }
| Decl { [$1] }
DeclsV :: { [PartialDecl'] }
DeclsV : Decl VS Decls { $1 : $3 }
| Decl VS { [$1] }
| Decl { [$1] }
VS :: { Located RlpToken }
VS : ';' { $1 }
| vsemi { $1 }
Decl :: { PartialDecl' }
: FunDecl { $1 }
| DataDecl { $1 }
| InfixDecl { $1 }
InfixDecl :: { PartialDecl' }
: InfixWord litint InfixOp {% mkInfixD $1 $2 $3 }
InfixWord :: { Assoc }
: infixl { InfixL }
| infixr { InfixR }
| infix { Infix }
DataDecl :: { PartialDecl' }
: data Con TyParams '=' DataCons { DataD $2 $3 $5 }
TyParams :: { [Name] }
: {- epsilon -} { [] }
| TyParams varname { $1 `snoc` $2 }
DataCons :: { [ConAlt] }
: DataCons '|' DataCon { $1 `snoc` $3 }
| DataCon { [$1] }
DataCon :: { ConAlt }
: Con Type1s { ConAlt $1 $2 }
Type1s :: { [Type] }
: {- epsilon -} { [] }
| Type1s Type1 { $1 `snoc` $2 }
Type1 :: { Type }
: '(' Type ')' { $2 }
| conname { TyCon $1 }
| varname { TyVar $1 }
Type :: { Type }
: Type '->' Type { $1 :-> $3 }
| Type1 { $1 }
FunDecl :: { PartialDecl' }
FunDecl : Var Params '=' Expr { FunD $1 $2 (Const $4) Nothing }
Params :: { [Pat'] }
Params : {- epsilon -} { [] }
| Params Pat1 { $1 `snoc` $2 }
Pat1 :: { Pat' }
: Var { VarP $1 }
| Lit { LitP $1 }
Expr :: { PartialExpr' }
: Expr1 varsym Expr { Fix $ B $2 (unFix $1) (unFix $3) }
| Expr1 { $1 }
Expr1 :: { PartialExpr' }
: '(' Expr ')' { wrapFix . Par . unwrapFix $ $2 }
| Lit { Fix . E $ LitEF $1 }
| Var { Fix . E $ VarEF $1 }
-- TODO: happy prefers left-associativity. doing such would require adjusting
-- the code in Rlp.Parse.Associate to expect left-associative input rather than
-- right.
InfixExpr :: { PartialExpr' }
: Expr1 varsym Expr { Fix $ B $2 (unFix $1) (unFix $3) }
InfixOp :: { Name }
: consym { $1 }
| varsym { $1 }
Lit :: { Lit' }
Lit : litint { IntL $1 }
Var :: { VarId }
Var : varname { NameVar $1 }
Con :: { ConId }
: conname { NameCon $1 }
{
mkProgram :: [PartialDecl'] -> P RlpProgram'
mkProgram ds = do
pt <- use psOpTable
pure $ RlpProgram (associate pt <$> ds)
parseError :: Located RlpToken -> P a
parseError (Located ((l,c),s) t) = addFatal $
errorMsg (SrcSpan l c s) RlpParErrUnexpectedToken
mkInfixD :: Assoc -> Int -> Name -> P PartialDecl'
mkInfixD a p n = do
let opl :: Lens' ParseState (Maybe OpInfo)
opl = psOpTable . at n
opl <~ (use opl >>= \case
Just o -> addWoundHere l e >> pure (Just o) where
e = RlpParErrDuplicateInfixD n
l = T.length n
Nothing -> pure (Just (a,p))
)
pure $ InfixD a p n
}

100
src/Rlp/Parse/Associate.hs Normal file
View File

@@ -0,0 +1,100 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms, ViewPatterns, ImplicitParams #-}
module Rlp.Parse.Associate
( associate
)
where
--------------------------------------------------------------------------------
import Data.HashMap.Strict qualified as H
import Data.Functor.Foldable
import Data.Functor.Const
import Lens.Micro
import Rlp.Parse.Types
import Rlp.Syntax
--------------------------------------------------------------------------------
associate :: OpTable -> PartialDecl' -> Decl' RlpExpr
associate pt (FunD n as b w) = FunD n as b' w
where b' = let ?pt = pt in completeExpr (getConst b)
associate pt (TySigD ns t) = TySigD ns t
associate pt (DataD n as cs) = DataD n as cs
associate pt (InfixD a p n) = InfixD a p n
completeExpr :: (?pt :: OpTable) => PartialExpr' -> RlpExpr'
completeExpr = cata completePartial
completePartial :: (?pt :: OpTable) => PartialE -> RlpExpr'
completePartial (E e) = completeRlpExpr e
completePartial p@(B o l r) = completeB (build p)
completePartial (Par e) = completePartial e
completeRlpExpr :: (?pt :: OpTable) => RlpExprF' RlpExpr' -> RlpExpr'
completeRlpExpr = embed
completeB :: (?pt :: OpTable) => PartialE -> RlpExpr'
completeB p = case build p of
B o l r -> (o' `AppE` l') `AppE` r'
where
-- TODO: how do we know it's symbolic?
o' = VarE (SymVar o)
l' = completeB l
r' = completeB r
Par e -> completeB e
E e -> completeRlpExpr e
build :: (?pt :: OpTable) => PartialE -> PartialE
build e = go id e (rightmost e) where
rightmost :: PartialE -> PartialE
rightmost (B _ _ r) = rightmost r
rightmost p@(E _) = p
rightmost p@(Par _) = p
go :: (?pt :: OpTable)
=> (PartialE -> PartialE)
-> PartialE -> PartialE -> PartialE
go f p@(WithInfo o _ r) = case r of
E _ -> mkHole o (f . f')
Par _ -> mkHole o (f . f')
B _ _ _ -> go (mkHole o (f . f')) r
where f' r' = p & pR .~ r'
go f _ = id
mkHole :: (?pt :: OpTable)
=> OpInfo
-> (PartialE -> PartialE)
-> PartialE
-> PartialE
mkHole _ hole p@(Par _) = hole p
mkHole _ hole p@(E _) = hole p
mkHole (a,d) hole p@(WithInfo (a',d') _ _)
| d' < d = above
| d' > d = below
| d == d' = case (a,a') of
-- left-associative operators of equal precedence are
-- associated left
(InfixL,InfixL) -> above
-- right-associative operators are handled similarly
(InfixR,InfixR) -> below
-- non-associative operators of equal precedence, or equal
-- precedence operators of different associativities are
-- invalid
(_, _) -> error "invalid expression"
where
above = p & pL %~ hole
below = hole p
examplePrecTable :: OpTable
examplePrecTable = H.fromList
[ ("+", (InfixL,6))
, ("*", (InfixL,7))
, ("^", (InfixR,8))
, (".", (InfixR,7))
, ("~", (Infix, 9))
, ("=", (Infix, 4))
, ("&&", (Infix, 3))
, ("||", (Infix, 2))
, ("$", (InfixR,0))
, ("&", (InfixL,0))
]

242
src/Rlp/Parse/Types.hs Normal file
View File

@@ -0,0 +1,242 @@
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ImplicitParams, ViewPatterns, PatternSynonyms #-}
{-# LANGUAGE LambdaCase #-}
module Rlp.Parse.Types
( LexerAction
, MsgEnvelope(..)
, RlpcError(..)
, AlexInput(..)
, Position(..)
, RlpToken(..)
, P(..)
, ParseState(..)
, psLayoutStack
, psLexState
, psInput
, psOpTable
, Layout(..)
, Located(..)
, OpTable
, OpInfo
, RlpParseError(..)
, PartialDecl'
, Partial(..)
, pL, pR
, PartialE
, pattern WithInfo
, opInfoOrDef
, PartialExpr'
, aiPrevChar
, aiSource
, aiBytes
, aiPos
, addFatal
, addWound
, addFatalHere
, addWoundHere
)
where
--------------------------------------------------------------------------------
import Core.Syntax (Name)
import Control.Monad
import Control.Monad.State.Strict
import Control.Monad.Errorful
import Compiler.RlpcError
import Data.Text (Text)
import Data.Maybe
import Data.Fix
import Data.Functor.Foldable
import Data.Functor.Const
import Data.Functor.Classes
import Data.HashMap.Strict qualified as H
import Data.Word (Word8)
import Lens.Micro.TH
import Lens.Micro
import Rlp.Syntax
--------------------------------------------------------------------------------
type LexerAction a = AlexInput -> Int -> P a
data AlexInput = AlexInput
{ _aiPrevChar :: Char
, _aiSource :: Text
, _aiBytes :: [Word8]
, _aiPos :: Position
}
deriving Show
type Position =
( Int -- line
, Int -- column
)
posLine :: Lens' Position Int
posLine = _1
posColumn :: Lens' Position Int
posColumn = _2
data RlpToken
-- literals
= TokenLitInt Int
-- identifiers
| TokenVarName Name
| TokenConName Name
| TokenVarSym Name
| TokenConSym Name
-- reserved words
| TokenData
| TokenCase
| TokenOf
| TokenLet
| TokenIn
| TokenInfixL
| TokenInfixR
| TokenInfix
-- reserved ops
| TokenArrow
| TokenPipe
| TokenHasType
| TokenLambda
| TokenEquals
-- control symbols
| TokenSemicolon
| TokenLBrace
| TokenRBrace
| TokenLParen
| TokenRParen
-- 'virtual' control symbols, inserted by the lexer without any correlation
-- to a specific symbol
| TokenSemicolonV
| TokenLBraceV
| TokenRBraceV
| TokenEOF
deriving (Show)
newtype P a = P {
runP :: ParseState
-> (ParseState, [MsgEnvelope RlpParseError], Maybe a)
}
deriving (Functor)
instance Applicative P where
pure a = P $ \st -> (st, [], pure a)
liftA2 = liftM2
instance Monad P where
p >>= k = P $ \st ->
let (st',es,ma) = runP p st
in case ma of
Just a -> runP (k a) st'
& _2 %~ (es<>)
Nothing -> (st',es,Nothing)
{-# INLINE (>>=) #-}
instance MonadState ParseState P where
state f = P $ \st ->
let (a,st') = f st
in (st', [], Just a)
instance MonadErrorful (MsgEnvelope RlpParseError) P where
addWound e = P $ \st -> (st, [e], Just ())
addFatal e = P $ \st -> (st, [e], Nothing)
data ParseState = ParseState
{ _psLayoutStack :: [Layout]
, _psLexState :: [Int]
, _psInput :: AlexInput
, _psOpTable :: OpTable
}
deriving Show
data Layout = Explicit
| Implicit Int
deriving (Show, Eq)
data Located a = Located (Position, Int) a
deriving (Show)
type OpTable = H.HashMap Name OpInfo
type OpInfo = (Assoc, Int)
-- data WithLocation a = WithLocation [String] a
data RlpParseError = RlpParErrOutOfBoundsPrecedence Int
| RlpParErrDuplicateInfixD Name
| RlpParErrLexical
| RlpParErrUnexpectedToken
deriving (Eq, Ord, Show)
instance IsRlpcError RlpParseError where
----------------------------------------------------------------------------------
-- absolute psycho shit (partial ASTs)
type PartialDecl' = Decl (Const PartialExpr') Name
data Partial a = E (RlpExprF Name a)
| B Name (Partial a) (Partial a)
| Par (Partial a)
deriving (Show, Functor)
pL :: Traversal' (Partial a) (Partial a)
pL k (B o l r) = (\l' -> B o l' r) <$> k l
pL _ x = pure x
pR :: Traversal' (Partial a) (Partial a)
pR k (B o l r) = (\r' -> B o l r') <$> k r
pR _ x = pure x
type PartialE = Partial RlpExpr'
-- i love you haskell
pattern WithInfo :: (?pt :: OpTable) => OpInfo -> PartialE -> PartialE -> PartialE
pattern WithInfo p l r <- B (opInfoOrDef -> p) l r
opInfoOrDef :: (?pt :: OpTable) => Name -> OpInfo
opInfoOrDef c = fromMaybe (InfixL,9) $ H.lookup c ?pt
-- required to satisfy constraint on Fix's show instance
instance Show1 Partial where
liftShowsPrec :: forall a. (Int -> a -> ShowS)
-> ([a] -> ShowS)
-> Int -> Partial a -> ShowS
liftShowsPrec sp sl p m = case m of
(E e) -> showsUnaryWith lshow "E" p e
(B f a b) -> showsTernaryWith showsPrec lshow lshow "B" p f a b
(Par e) -> showsUnaryWith lshow "Par" p e
where
lshow :: forall f. (Show1 f) => Int -> f a -> ShowS
lshow = liftShowsPrec sp sl
type PartialExpr' = Fix Partial
makeLenses ''AlexInput
makeLenses ''ParseState
addWoundHere :: Int -> RlpParseError -> P ()
addWoundHere l e = P $ \st ->
let e' = MsgEnvelope
{ _msgSpan = let pos = psInput . aiPos
in SrcSpan (st ^. pos . posLine)
(st ^. pos . posColumn)
l
, _msgDiagnostic = e
, _msgSeverity = SevError
}
in (st, [e'], Just ())
addFatalHere :: Int -> RlpParseError -> P a
addFatalHere l e = P $ \st ->
let e' = MsgEnvelope
{ _msgSpan = let pos = psInput . aiPos
in SrcSpan (st ^. pos . posLine)
(st ^. pos . posColumn)
l
, _msgDiagnostic = e
, _msgSeverity = SevError
}
in (st, [e'], Nothing)

177
src/Rlp/Syntax.hs Normal file
View File

@@ -0,0 +1,177 @@
-- recursion-schemes
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
-- recursion-schemes
{-# LANGUAGE TemplateHaskell, TypeFamilies #-}
{-# LANGUAGE OverloadedStrings, PatternSynonyms #-}
module Rlp.Syntax
( RlpModule(..)
, RlpProgram(..)
, RlpProgram'
, rlpmodName
, rlpmodProgram
, RlpExpr(..)
, RlpExpr'
, RlpExprF(..)
, RlpExprF'
, Decl(..)
, Decl'
, Bind(..)
, Where
, Where'
, ConAlt(..)
, Type(..)
, pattern (:->)
, Assoc(..)
, VarId(..)
, ConId(..)
, Pat(..)
, Pat'
, Lit(..)
, Lit'
, Name
-- TODO: ugh move this somewhere else later
, showsTernaryWith
-- * Convenience re-exports
, Text
)
where
----------------------------------------------------------------------------------
import Data.Text (Text)
import Data.Text qualified as T
import Data.String (IsString(..))
import Data.Functor.Foldable.TH (makeBaseFunctor)
import Data.Functor.Classes
import Lens.Micro
import Lens.Micro.TH
import Core.Syntax hiding (Lit)
import Core (HasRHS(..), HasLHS(..))
----------------------------------------------------------------------------------
data RlpModule b = RlpModule
{ _rlpmodName :: Text
, _rlpmodProgram :: RlpProgram b
}
newtype RlpProgram b = RlpProgram [Decl RlpExpr b]
deriving Show
type RlpProgram' = RlpProgram Name
-- | The @e@ parameter is used for partial results. When parsing an input, we
-- first parse all top-level declarations in order to extract infix[lr]
-- declarations. This process yields a @[Decl (Const Text) Name]@, where @Const
-- Text@ stores the remaining unparsed function bodies. Once infixities are
-- accounted for, we may complete the parsing task and get a proper @[Decl
-- RlpExpr Name]@.
data Decl e b = FunD VarId [Pat b] (e b) (Maybe (Where b))
| TySigD [VarId] Type
| DataD ConId [Name] [ConAlt]
| InfixD Assoc Int Name
deriving Show
type Decl' e = Decl e Name
data Assoc = InfixL
| InfixR
| Infix
deriving Show
data ConAlt = ConAlt ConId [Type]
deriving Show
data RlpExpr b = LetE [Bind b] (RlpExpr b)
| VarE VarId
| ConE ConId
| LamE [Pat b] (RlpExpr b)
| CaseE (RlpExpr b) [(Alt b, Where b)]
| IfE (RlpExpr b) (RlpExpr b) (RlpExpr b)
| AppE (RlpExpr b) (RlpExpr b)
| LitE (Lit b)
deriving Show
type RlpExpr' = RlpExpr Name
type Where b = [Bind b]
type Where' = [Bind Name]
-- do we want guards?
data Alt b = AltA (Pat b) (RlpExpr b)
deriving Show
data Bind b = PatB (Pat b) (RlpExpr b)
| FunB VarId [Pat b] (RlpExpr b)
deriving Show
data VarId = NameVar Text
| SymVar Text
deriving Show
instance IsString VarId where
-- TODO: use symvar if it's an operator
fromString = NameVar . T.pack
data ConId = NameCon Text
| SymCon Text
deriving Show
data Pat b = VarP VarId
| LitP (Lit b)
| ConP ConId [Pat b]
deriving Show
type Pat' = Pat Name
data Lit b = IntL Int
| CharL Char
| ListL [RlpExpr b]
deriving Show
type Lit' = Lit Name
-- instance HasLHS Alt Alt Pat Pat where
-- _lhs = lens
-- (\ (AltA p _) -> p)
-- (\ (AltA _ e) p' -> AltA p' e)
-- instance HasRHS Alt Alt RlpExpr RlpExpr where
-- _rhs = lens
-- (\ (AltA _ e) -> e)
-- (\ (AltA p _) e' -> AltA p e')
makeBaseFunctor ''RlpExpr
deriving instance (Show b, Show a) => Show (RlpExprF b a)
type RlpExprF' = RlpExprF Name
-- society if derivable Show1
instance (Show b) => Show1 (RlpExprF b) where
liftShowsPrec sp _ p m = case m of
(LetEF bs e) -> showsBinaryWith showsPrec sp "LetEF" p bs e
(VarEF n) -> showsUnaryWith showsPrec "VarEF" p n
(ConEF n) -> showsUnaryWith showsPrec "ConEF" p n
(LamEF bs e) -> showsBinaryWith showsPrec sp "LamEF" p bs e
(CaseEF e as) -> showsBinaryWith sp showsPrec "CaseEF" p e as
(IfEF a b c) -> showsTernaryWith sp sp sp "IfEF" p a b c
(AppEF f x) -> showsBinaryWith sp sp "AppEF" p f x
(LitEF l) -> showsUnaryWith showsPrec "LitEF" p l
showsTernaryWith :: (Int -> x -> ShowS)
-> (Int -> y -> ShowS)
-> (Int -> z -> ShowS)
-> String -> Int
-> x -> y -> z
-> ShowS
showsTernaryWith sa sb sc name p a b c = showParen (p > 10)
$ showString name
. showChar ' ' . sa 11 a
. showChar ' ' . sb 11 b
. showChar ' ' . sc 11 c
--------------------------------------------------------------------------------
makeLenses ''RlpModule

View File

@@ -38,9 +38,13 @@ spec = do
let e = [coreExpr|3|] let e = [coreExpr|3|]
in check' [] (TyCon "Bool") e `shouldSatisfy` isLeft in check' [] (TyCon "Bool") e `shouldSatisfy` isLeft
infer' :: Context' -> Expr' -> Either TypeError Type infer' :: Context' -> Expr' -> Either [TypeError] Type
infer' g e = fmap fst . runErrorful $ infer g e infer' g e = case runErrorful $ infer g e of
(Just t, _) -> Right t
(Nothing, es) -> Left es
check' :: Context' -> Type -> Expr' -> Either TypeError () check' :: Context' -> Type -> Expr' -> Either [TypeError] ()
check' g t e = fmap fst . runErrorful $ check g t e check' g t e = case runErrorful $ check g t e of
(Just t, _) -> Right ()
(Nothing, es) -> Left es