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

View File

@@ -26,21 +26,23 @@ import Data.Function ((&))
import GM
----------------------------------------------------------------------------------
justLexSrc :: String -> Either RlpcError [CoreToken]
justLexSrc :: String -> Either [MsgEnvelope RlpcError] [CoreToken]
justLexSrc s = lexCoreR (T.pack s)
& fmap (map $ \ (Located _ _ _ t) -> t)
& rlpcToEither
justParseSrc :: String -> Either RlpcError Program'
justParseSrc :: String -> Either [MsgEnvelope RlpcError] Program'
justParseSrc s = parse (T.pack s)
& rlpcToEither
where parse = lexCoreR >=> parseCoreProgR
justTypeCheckSrc :: String -> Either RlpcError Program'
justTypeCheckSrc :: String -> Either [MsgEnvelope RlpcError] Program'
justTypeCheckSrc s = typechk (T.pack s)
& rlpcToEither
where typechk = lexCoreR >=> parseCoreProgR >=> checkCoreProgR
rlpcToEither :: RLPC e a -> Either e a
rlpcToEither = evalRLPC def >>> fmap fst
rlpcToEither :: RLPC a -> Either [MsgEnvelope RlpcError] a
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(..)
, RLPCIO
, RLPCOptions(RLPCOptions)
, RlpcError(..)
, IsRlpcError(..)
, rlpc
, RlpcError(..)
, MsgEnvelope(..)
, addFatal
, addWound
, MonadErrorful
@@ -27,9 +27,6 @@ module Compiler.RLPC
, evalRLPCT
, evalRLPCIO
, evalRLPC
, addRlpcWound
, addRlpcFatal
, liftRlpcErrs
, rlpcLogFile
, rlpcDebugOpts
, rlpcEvaluator
@@ -40,6 +37,7 @@ module Compiler.RLPC
, flagDDumpOpts
, flagDDumpAST
, def
, liftErrorful
)
where
----------------------------------------------------------------------------------
@@ -51,6 +49,7 @@ import Control.Monad.Errorful
import Compiler.RlpcError
import Data.Functor.Identity
import Data.Default.Class
import Data.Foldable
import GHC.Generics (Generic)
import Data.Hashable (Hashable)
import Data.HashSet (HashSet)
@@ -58,48 +57,44 @@ import Data.HashSet qualified as S
import Data.Coerce
import Lens.Micro
import Lens.Micro.TH
import System.Exit
----------------------------------------------------------------------------------
-- TODO: fancy errors
newtype RLPCT e m a = RLPCT {
runRLPCT :: ReaderT RLPCOptions (ErrorfulT e m) a
newtype RLPCT m a = RLPCT {
runRLPCT :: ReaderT RLPCOptions (ErrorfulT (MsgEnvelope RlpcError) m) a
}
-- TODO: incorrect ussage of MonadReader. RLPC should have its own
-- environment access functions
deriving (Functor, Applicative, Monad, MonadReader RLPCOptions)
deriving (Functor, Applicative, Monad)
deriving instance (MonadIO m) => MonadIO (RLPCT e m)
type RLPC = RLPCT Identity
instance MonadTrans (RLPCT e) where
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
type RLPCIO = RLPCT IO
evalRLPC :: RLPCOptions
-> RLPC e a
-> Either e (a, [e])
evalRLPC o m = coerce $ evalRLPCT o m
-> RLPC a
-> (Maybe a, [MsgEnvelope RlpcError])
evalRLPC opt r = runRLPCT r
& flip runReaderT opt
& runErrorful
evalRLPCIO :: (Exception e)
evalRLPCT :: (Monad m)
=> RLPCOptions
-> RLPCIO e a
-> IO (a, [e])
evalRLPCIO o m = do
m' <- evalRLPCT o m
case m' of
-- TODO: errors
Left e -> throwIO e
Right a -> pure a
-> RLPCT m a
-> m (Maybe a, [MsgEnvelope RlpcError])
evalRLPCT = undefined
evalRLPCIO :: RLPCOptions -> RLPCIO a -> IO a
evalRLPCIO opt r = do
(ma,es) <- evalRLPCT opt r
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
{ _rlpcLogFile :: Maybe FilePath
@@ -113,32 +108,6 @@ data RLPCOptions = RLPCOptions
data Evaluator = EvaluatorGM | EvaluatorTI
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

View File

@@ -1,15 +1,70 @@
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
module Compiler.RlpcError
( RlpcError(..)
, IsRlpcError(..)
( IsRlpcError(..)
, MsgEnvelope(..)
, Severity(..)
, RlpcError(..)
, SrcSpan(..)
, msgSpan
, msgDiagnostic
, msgSeverity
, liftRlpcErrors
, errorMsg
)
where
----------------------------------------------------------------------------------
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
class IsRlpcError a where
liftRlpcErr :: a -> RlpcError
instance IsString RlpcError where
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 FunctionalDependencies #-}
{-# LANGUAGE TupleSections, PatternSynonyms #-}
{-# LANGUAGE UndecidableInstances #-}
module Control.Monad.Errorful
( ErrorfulT
, runErrorfulT
, Errorful
, runErrorful
, mapErrors
, mapErrorful
, MonadErrorful(..)
)
where
----------------------------------------------------------------------------------
import Control.Monad.State.Strict
import Control.Monad.Trans
import Data.Functor.Identity
import Data.Coerce
import Data.HashSet (HashSet)
import Data.HashSet qualified as H
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
pattern Errorful :: (Either e (a, [e])) -> Errorful e a
pattern Errorful :: (Maybe a, [e]) -> Errorful e 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)
class (Applicative m) => MonadErrorful e m | m -> e where
addWound :: e -> m ()
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
addWound e = ErrorfulT $ pure . Right $ ((), [e])
addFatal e = ErrorfulT $ pure . Left $ e
addWound e = ErrorfulT $ pure (Just (), [e])
addFatal e = ErrorfulT $ pure (Nothing, [e])
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
liftIO = lift . liftIO
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
pure a = ErrorfulT (pure . Right $ (a, []))
pure a = ErrorfulT . pure $ (Just a, [])
m <*> a = ErrorfulT (m' `apply` a')
where
m' = runErrorfulT m
a' = runErrorfulT a
-- TODO: strict concatenation
apply = liftA2 $ liftA2 (\ (f,e1) (x,e2) -> (f x, e1 ++ e2))
ErrorfulT m <*> ErrorfulT n = ErrorfulT $ m `apply` n where
apply :: m (Maybe (a -> b), [e]) -> m (Maybe a, [e]) -> m (Maybe b, [e])
apply = liftA2 $ \ (mf,e1) (ma,e2) -> (mf <*> ma, e1 <> e2)
instance (Monad m) => Monad (ErrorfulT e m) where
ErrorfulT m >>= k = ErrorfulT $ do
m' <- m
case m' of
Right (a,es) -> runErrorfulT (k a)
Left e -> pure (Left e)
(a,es) <- m
case a of
Just x -> runErrorfulT (k x)
Nothing -> pure (Nothing, es)
mapErrors :: (Monad m) => (e -> e') -> ErrorfulT e m a -> ErrorfulT e' m a
mapErrors f m = ErrorfulT $ do
x <- runErrorfulT m
case x of
Left e -> pure . Left $ f e
Right (a,es) -> pure . Right $ (a, f <$> es)
mapErrorful :: (Functor m) => (e -> e') -> ErrorfulT e m a -> ErrorfulT e' m a
mapErrorful f (ErrorfulT m) = ErrorfulT $
m & mapped . _2 . mapped %~ f
-- when microlens-pro drops we can write this as
-- 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
----------------------------------------------------------------------------------
-- fac3 = undefined
-- sumList = undefined
-- constDivZero = undefined
-- idCase = undefined
---
letrecExample :: Program'
letrecExample = [coreProg|
pair x y f = f x y;
@@ -216,3 +223,4 @@ idCase = [coreProg|
-- , ScDef "Cons" [] $ Con 2 2
-- ]
--}

View File

@@ -3,6 +3,7 @@ Module : Core.HindleyMilner
Description : Hindley-Milner type system
-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Core.HindleyMilner
( Context'
, infer
@@ -16,15 +17,17 @@ module Core.HindleyMilner
----------------------------------------------------------------------------------
import Lens.Micro
import Lens.Micro.Mtl
import Lens.Micro.Platform
import Data.Maybe (fromMaybe)
import Data.Text qualified as T
import Data.HashMap.Strict qualified as H
import Data.Foldable (traverse_)
import Compiler.RLPC
import Control.Monad (foldM, void)
import Control.Monad (foldM, void, forM)
import Control.Monad.Errorful (Errorful, addFatal)
import Control.Monad.State
import Control.Monad.Utils (mapAccumLM)
import Text.Printf
import Core.Syntax
----------------------------------------------------------------------------------
@@ -48,9 +51,20 @@ data TypeError
| TyErrMissingTypeSig Name
deriving (Show, Eq)
-- TODO:
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
-- throw any number of fatal or nonfatal errors. Run with @runErrorful@.
@@ -88,10 +102,10 @@ checkCoreProg p = scDefs
where scname = sc ^. _lhs._1
-- | @checkCoreProgR p@ returns @p@ if @p@ successfully typechecks.
checkCoreProgR :: Program' -> RLPC RlpcError Program'
checkCoreProgR p = do
liftRlpcErrs . rlpc . checkCoreProg $ p
pure p
checkCoreProgR :: Program' -> RLPC Program'
checkCoreProgR p = undefined
{-# WARNING checkCoreProgR "unimpl" #-}
-- | 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
g' <- buildLetContext g bs
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']
-> 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 _ _ 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)
-- | The main lexer driver.
lexCore :: Text -> RLPC SrcError [Located CoreToken]
lexCore :: Text -> RLPC [Located CoreToken]
lexCore s = case m of
Left e -> addFatal err
where err = SrcError
{ _errSpan = (0,0,0) -- TODO: location
, _errSeverity = Error
, _errDiagnostic = SrcErrLexical e
}
Left e -> error "core lex error"
Right ts -> pure ts
where
m = runAlex s lexStream
lexCoreR :: Text -> RLPC RlpcError [Located CoreToken]
lexCoreR = liftRlpcErrs . lexCore
lexCoreR :: Text -> RLPC [Located CoreToken]
lexCoreR = lexCore
-- | @lexCore@, but the tokens are stripped of location info. Useful for
-- debugging
lexCore' :: Text -> RLPC SrcError [CoreToken]
lexCore' :: Text -> RLPC [CoreToken]
lexCore' s = fmap f <$> lexCore s
where f (Located _ _ _ t) = t
@@ -201,11 +196,11 @@ data ParseError = ParErrLexical String
-- TODO:
instance IsRlpcError SrcError where
liftRlpcErr = RlpcErr . show
liftRlpcError = Text . pure . T.pack . show
-- TODO:
instance IsRlpcError ParseError where
liftRlpcErr = RlpcErr . show
liftRlpcError = Text . pure . T.pack . show
alexEOF :: Alex (Located CoreToken)
alexEOF = Alex $ \ st@(AlexState { alex_pos = AlexPn _ y x }) ->

View File

@@ -10,7 +10,6 @@ module Core.Parse
, parseCoreProg
, parseCoreProgR
, module Core.Lex -- temp convenience
, parseTmp
, SrcError
, Module
)
@@ -34,7 +33,7 @@ import Data.HashMap.Strict qualified as H
%name parseCoreProg StandaloneProgram
%tokentype { Located CoreToken }
%error { parseError }
%monad { RLPC SrcError }
%monad { RLPC } { happyBind } { happyPure }
%token
let { Located _ _ _ TokenLet }
@@ -99,6 +98,8 @@ ScDefs : ScDef ';' ScDefs { $1 : $3 }
ScDef :: { ScDef Name }
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 : Type1 { $1 }
@@ -189,34 +190,23 @@ Con : '(' consym ')' { $2 }
{
parseError :: [Located CoreToken] -> RLPC SrcError a
parseError (Located y x l _ : _) = addFatal err
where err = SrcError
{ _errSpan = (y,x,l)
, _errSeverity = Error
, _errDiagnostic = SrcErrParse
}
parseError :: [Located CoreToken] -> RLPC a
parseError (Located y x l t : _) =
error $ show y <> ":" <> show x
<> ": parse error at token `" <> show t <> "'"
parseTmp :: IO (Module Name)
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)
{-# WARNING parseError "unimpl" #-}
exprPragma :: [String] -> RLPC SrcError (Expr Name)
exprPragma ("AST" : e) = astPragma e
exprPragma _ = addFatal err
where err = SrcError
{ _errSpan = (0,0,0) -- TODO: span
, _errSeverity = Warning
, _errDiagnostic = SrcErrUnknownPragma "" -- TODO: missing pragma
}
exprPragma :: [String] -> RLPC (Expr Name)
exprPragma ("AST" : e) = undefined
exprPragma _ = undefined
astPragma :: [String] -> RLPC SrcError (Expr Name)
astPragma = pure . read . unwords
{-# WARNING exprPragma "unimpl" #-}
astPragma :: [String] -> RLPC (Expr Name)
astPragma _ = undefined
{-# WARNING astPragma "unimpl" #-}
insTypeSig :: (Hashable b) => (b, Type) -> Program b -> Program b
insTypeSig ts = programTypeSigs %~ uncurry H.insert ts
@@ -230,8 +220,14 @@ insScDef sc = programScDefs %~ (sc:)
singletonScDef :: (Hashable b) => ScDef b -> Program b
singletonScDef sc = insScDef sc mempty
parseCoreProgR :: [Located CoreToken] -> RLPC RlpcError Program'
parseCoreProgR = liftRlpcErrs . parseCoreProg
parseCoreProgR :: [Located CoreToken] -> RLPC Program'
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
, coreProg
, coreProgT
, core
)
where
----------------------------------------------------------------------------------
@@ -14,74 +13,38 @@ import Language.Haskell.TH
import Language.Haskell.TH.Syntax hiding (Module)
import Language.Haskell.TH.Quote
import Control.Monad ((>=>))
import Control.Monad.IO.Class
import Control.Arrow ((>>>))
import Compiler.RLPC
import Data.Default.Class (def)
import Data.Text (Text)
import Data.Text qualified as T
import Core.Parse
import Core.Lex
import Core.Syntax
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
{ 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"
}
coreProg = mkqq $ lexCoreR >=> parseCoreProgR
coreExpr :: QuasiQuoter
coreExpr = QuasiQuoter
{ 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"
}
coreExpr = mkqq $ lexCoreR >=> parseCoreExpr
-- | Type-checked @coreProg@
coreProgT :: QuasiQuoter
coreProgT = QuasiQuoter
{ quoteExp = qCoreProgT
coreProgT = mkqq $ lexCoreR >=> parseCoreProgR >=> checkCoreProgR
mkqq :: (Lift a) => (Text -> RLPC a) -> QuasiQuoter
mkqq p = QuasiQuoter
{ quoteExp = mkq p
, 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"
}
qCore :: String -> Q Exp
qCore s = case parse (T.pack s) of
Left e -> error (show e)
Right (m,ts) -> lift m
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)
mkq :: (Lift a) => (Text -> RLPC a) -> String -> Q Exp
mkq parse s = case evalRLPC def (parse $ T.pack s) of
(Just a, _) -> lift a
(Nothing, _) -> error "todo: aaahhbbhjhbdjhabsjh"

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|]
in check' [] (TyCon "Bool") e `shouldSatisfy` isLeft
infer' :: Context' -> Expr' -> Either TypeError Type
infer' g e = fmap fst . runErrorful $ infer g e
infer' :: Context' -> Expr' -> Either [TypeError] Type
infer' g e = case runErrorful $ infer g e of
(Just t, _) -> Right t
(Nothing, es) -> Left es
check' :: Context' -> Type -> Expr' -> Either TypeError ()
check' g t e = fmap fst . runErrorful $ check g t e
check' :: Context' -> Type -> Expr' -> Either [TypeError] ()
check' g t e = case runErrorful $ check g t e of
(Just t, _) -> Right ()
(Nothing, es) -> Left es