Compare commits
78 Commits
test-synta
...
frontend-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ce11dfdd7 | ||
|
|
3d45e12676 | ||
|
|
22b5b47795 | ||
|
|
cefdf6ffae | ||
|
|
e3b18c8915 | ||
|
|
692d22afb9 | ||
|
|
c146e1c450 | ||
|
|
5a659d22dd | ||
|
|
1a881399ab | ||
|
|
257d02da87 | ||
|
|
f47f325e34 | ||
|
|
f22d4238f5 | ||
|
|
4e1c9dd750 | ||
|
|
d6ac991105 | ||
|
|
d5663c1aad | ||
|
|
7e6bee3d4a | ||
|
|
5ec625e0fd | ||
|
|
9196e20e08 | ||
|
|
a1a50bd013 | ||
|
|
1c035d092a | ||
|
|
c0236dc079 | ||
|
|
9a4f24ec10 | ||
|
|
4f66e71b9a | ||
|
|
bdf74ac6c9 | ||
|
|
3dfadc17ec | ||
|
|
c92d8fac65 | ||
|
|
a38381f6ca | ||
|
|
6390ca80d8 | ||
|
|
17ddf3530c | ||
|
|
e597ecbfc6 | ||
|
|
2496589346 | ||
|
|
681a394312 | ||
|
|
aff1c6b4c6 | ||
|
|
bec376b7c7 | ||
|
|
eaa04c4a59 | ||
|
|
ea2fb4dcaa | ||
|
|
ab2cb59526 | ||
|
|
ec4902b2d4 | ||
|
|
1fc45b70b4 | ||
|
|
4b9a570c72 | ||
|
|
65b967689c | ||
|
|
ed60ec8b32 | ||
|
|
d0dbdbbd9b | ||
|
|
cae0939f0c | ||
|
|
3292998c42 | ||
|
|
84c1122995 | ||
|
|
97ce9b48ae | ||
|
|
936f24148f | ||
|
|
2a159232c7 | ||
|
|
4ee9785239 | ||
|
|
cbe4276061 | ||
|
|
c5c06fa6cb | ||
|
|
0f04e2decf | ||
|
|
6130a91668 | ||
|
|
c15f9b6546 | ||
|
|
bb6aca094c | ||
|
|
245b12a96e | ||
|
|
cb9ec43c14 | ||
|
|
8ad967fac0 | ||
|
|
55dbc9de70 | ||
|
|
05226373ee | ||
|
|
981c5d8a83 | ||
|
|
86cd1075ca | ||
|
|
1d43c1d304 | ||
|
|
4b44f57066 | ||
|
|
90a9594e8f | ||
|
|
074350768c | ||
|
|
37d9e6f219 | ||
|
|
cb7cdf7ed7 | ||
|
|
2f783d96e8 | ||
|
|
a71c099fe0 | ||
|
|
d1e64eb12d | ||
|
|
f31726b43d | ||
|
|
8aa9bb843f | ||
|
|
9a357a99b7 | ||
|
|
060d48f9e1 | ||
|
|
bf4abeb8b4 | ||
|
|
7ed565fc24 |
18
.ghci
Normal file
18
.ghci
Normal 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
25
Makefile_happysrcs
Normal 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 $@
|
||||
|
||||
@@ -32,6 +32,7 @@ html_theme = 'alabaster'
|
||||
imgmath_latex_preamble = r'''
|
||||
\usepackage{amsmath}
|
||||
\usepackage{tabularray}
|
||||
\usepackage{syntax}
|
||||
|
||||
\newcommand{\transrule}[2]
|
||||
{\begin{tblr}{|rrrlc|}
|
||||
|
||||
67
doc/src/references/rlp-grammar.rst
Normal file
67
doc/src/references/rlp-grammar.rst
Normal 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' ')'
|
||||
|
||||
67
rlp.cabal
67
rlp.cabal
@@ -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
|
||||
@@ -90,20 +101,12 @@ test-suite rlp-test
|
||||
hs-source-dirs: tst
|
||||
main-is: Main.hs
|
||||
build-depends: base ^>=4.18.0.0
|
||||
, unordered-containers
|
||||
, rlp
|
||||
, QuickCheck
|
||||
, hspec ==2.*
|
||||
, microlens
|
||||
, text
|
||||
, pretty
|
||||
, microlens-platform
|
||||
|
||||
other-modules: Arith
|
||||
, GMSpec
|
||||
, CoreSyntax
|
||||
, Core.HindleyMilnerSpec
|
||||
, Core.ParseSpec
|
||||
|
||||
build-tool-depends: hspec-discover:hspec-discover
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@ module Compiler.JustRun
|
||||
( justLexSrc
|
||||
, justParseSrc
|
||||
, justTypeCheckSrc
|
||||
, RlpcError
|
||||
, Program'
|
||||
)
|
||||
where
|
||||
----------------------------------------------------------------------------------
|
||||
@@ -28,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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
deriving (Show, Eq)
|
||||
data MsgEnvelope e = MsgEnvelope
|
||||
{ _msgSpan :: SrcSpan
|
||||
, _msgDiagnostic :: e
|
||||
, _msgSeverity :: Severity
|
||||
}
|
||||
deriving (Functor, Show)
|
||||
|
||||
class IsRlpcError a where
|
||||
liftRlpcErr :: a -> RlpcError
|
||||
newtype RlpcError = Text [Text]
|
||||
deriving Show
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
-- ]
|
||||
|
||||
--}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -103,8 +103,6 @@ rlp :-
|
||||
\n { skip }
|
||||
}
|
||||
|
||||
-- TODO: negative literals
|
||||
|
||||
<pragma>
|
||||
{
|
||||
"#-}" { constTok TokenRPragma `andBegin` 0 }
|
||||
@@ -169,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
|
||||
|
||||
@@ -203,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 }) ->
|
||||
|
||||
@@ -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 }
|
||||
@@ -77,7 +76,6 @@ Eof : eof { () }
|
||||
|
||||
StandaloneProgram :: { Program Name }
|
||||
StandaloneProgram : Program eof { $1 }
|
||||
| eof { mempty }
|
||||
|
||||
Program :: { Program Name }
|
||||
Program : ScTypeSig ';' Program { insTypeSig $1 $3 }
|
||||
@@ -100,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 }
|
||||
@@ -148,7 +148,6 @@ Alters : Alter ';' Alters { $1 : $3 }
|
||||
| Alter ';' { [$1] }
|
||||
| Alter { [$1] }
|
||||
|
||||
-- TODO: tags should be wrapped in <n> to allow matching against literals
|
||||
Alter :: { Alter Name }
|
||||
Alter : litint ParList '->' Expr { Alter (AltData $1) $2 $4 }
|
||||
|
||||
@@ -191,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
|
||||
@@ -232,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
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -58,9 +58,9 @@ data Expr b = Var Name
|
||||
| Let Rec [Binding b] (Expr b)
|
||||
| App (Expr b) (Expr b)
|
||||
| Lit Lit
|
||||
deriving (Show, Eq, Read, Lift)
|
||||
deriving (Show, Read, Lift)
|
||||
|
||||
-- deriving instance (Eq b) => Eq (Expr b)
|
||||
deriving instance (Eq b) => Eq (Expr b)
|
||||
|
||||
data Type = TyFun
|
||||
| TyVar Name
|
||||
@@ -86,14 +86,18 @@ pattern a :-> b = TyApp (TyApp TyFun a) b
|
||||
{-# COMPLETE Binding :: Binding #-}
|
||||
{-# COMPLETE (:=) :: Binding #-}
|
||||
data Binding b = Binding b (Expr b)
|
||||
deriving (Show, Read, Eq, Lift)
|
||||
deriving (Show, Read, Lift)
|
||||
|
||||
deriving instance (Eq b) => Eq (Binding b)
|
||||
|
||||
infixl 1 :=
|
||||
pattern (:=) :: b -> (Expr b) -> (Binding b)
|
||||
pattern k := v = Binding k v
|
||||
|
||||
data Alter b = Alter AltCon [b] (Expr b)
|
||||
deriving (Show, Read, Eq, Lift)
|
||||
deriving (Show, Read, Lift)
|
||||
|
||||
deriving instance (Eq b) => Eq (Alter b)
|
||||
|
||||
data Rec = Rec
|
||||
| NonRec
|
||||
@@ -111,7 +115,7 @@ type Name = T.Text
|
||||
type Tag = Int
|
||||
|
||||
data ScDef b = ScDef b [b] (Expr b)
|
||||
deriving (Show, Eq, Lift)
|
||||
deriving (Show, Lift)
|
||||
|
||||
unliftScDef :: ScDef b -> Expr b
|
||||
unliftScDef (ScDef _ as e) = Lam as e
|
||||
@@ -123,7 +127,7 @@ data Program b = Program
|
||||
{ _programScDefs :: [ScDef b]
|
||||
, _programTypeSigs :: H.HashMap b Type
|
||||
}
|
||||
deriving (Show, Eq, Lift)
|
||||
deriving (Show, Lift)
|
||||
|
||||
makeLenses ''Program
|
||||
pure []
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
349
src/Rlp/Lex.x
Normal 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
181
src/Rlp/Parse.y
Normal 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
100
src/Rlp/Parse/Associate.hs
Normal 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
242
src/Rlp/Parse/Types.hs
Normal 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
177
src/Rlp/Syntax.hs
Normal 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 Rec [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 r bs e) -> showsTernaryWith showsPrec showsPrec sp "LetEF" p r 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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
module Core.ParseSpec
|
||||
( spec
|
||||
)
|
||||
where
|
||||
----------------------------------------------------------------------------------
|
||||
import CoreSyntax
|
||||
import Core.Syntax
|
||||
import Compiler.JustRun
|
||||
import Compiler.RlpcError
|
||||
import Control.Monad ((<=<))
|
||||
import Data.Coerce
|
||||
import Data.Text qualified as T
|
||||
import Data.Functor.Classes (Eq1(..))
|
||||
import Test.Hspec
|
||||
import Test.QuickCheck
|
||||
----------------------------------------------------------------------------------
|
||||
|
||||
spec :: Spec
|
||||
spec = do
|
||||
it "should be a right-inverse to the unparser \
|
||||
\up to source code congruency" $
|
||||
withMaxSuccess 20 $ property $
|
||||
\p -> (unparse <=< parse) p ~== Right p
|
||||
|
||||
-- TODO: abitrary ASTs
|
||||
-- it "should be a right-inverse to the unparser\
|
||||
-- \up to source code congruency" $
|
||||
-- property $ \p -> (parse <=< unparse) p == Right p
|
||||
|
||||
(~==) :: (Eq1 f) => f ProgramSrc -> f ProgramSrc -> Bool
|
||||
(~==) = liftEq congruentSrc
|
||||
|
||||
infix 4 ~==
|
||||
|
||||
parse :: ProgramSrc -> Either RlpcError Program'
|
||||
parse (ProgramSrc s) = justParseSrc (T.unpack s)
|
||||
|
||||
unparse :: Program' -> Either RlpcError ProgramSrc
|
||||
unparse = Right . unparseCoreProg
|
||||
|
||||
@@ -1,303 +0,0 @@
|
||||
{-# LANGUAGE OverloadedStrings, LambdaCase, GeneralisedNewtypeDeriving #-}
|
||||
module CoreSyntax
|
||||
( ProgramSrc(..)
|
||||
, congruentSrc
|
||||
, unparseCoreProg
|
||||
)
|
||||
where
|
||||
----------------------------------------------------------------------------------
|
||||
import Core.Syntax
|
||||
import Compiler.JustRun (justParseSrc)
|
||||
import Control.Arrow ((>>>), (&&&))
|
||||
import Control.Monad
|
||||
import Data.List (intersperse)
|
||||
import Data.Coerce (coerce)
|
||||
import Data.Text (Text)
|
||||
import Data.Text qualified as T
|
||||
import Data.HashMap.Strict qualified as H
|
||||
import Test.QuickCheck
|
||||
import Text.PrettyPrint hiding ((<>))
|
||||
import Data.Functor ((<&>))
|
||||
import Data.Function ((&), on)
|
||||
import Data.String (IsString(..))
|
||||
import Lens.Micro.Platform
|
||||
import Lens.Micro.Platform.Internal (IsText(..))
|
||||
----------------------------------------------------------------------------------
|
||||
|
||||
newtype ProgramSrc = ProgramSrc Text
|
||||
deriving (Show, Read, Eq, Semigroup, Monoid, IsString)
|
||||
|
||||
instance Arbitrary ProgramSrc where
|
||||
arbitrary = sized genProg where
|
||||
|
||||
genProg :: Int -> Gen ProgramSrc
|
||||
genProg n = do
|
||||
-- in generating a program, we create a random list of sc names and
|
||||
-- assign them type signatures and definitions in random order.
|
||||
ns <- replicateM n genName
|
||||
-- generate a typesig and def for each name
|
||||
ns & each %~ (genTySig &&& genScDef)
|
||||
-- [(typesig, scdef)] -> [typesigs and scdefs]
|
||||
& uncurry (++) . unzip
|
||||
-- [Gen Text] -> Gen [Text]
|
||||
& sequenceA
|
||||
-- shuffle order of tysigs and scdefs
|
||||
>>= shuffle
|
||||
-- terminate each tysig and scdef with a semicolon with a blank
|
||||
-- line for legibility
|
||||
<&> intersperse ";\n\n"
|
||||
-- mconcat into a single body of text
|
||||
<&> mconcat
|
||||
-- she's done! put a bow on her! :D
|
||||
<&> ProgramSrc
|
||||
|
||||
genTySig :: Name -> Gen Text
|
||||
genTySig n = conseq [pure n, ws, pure "::", ws, genTy]
|
||||
|
||||
genScDef :: Name -> Gen Text
|
||||
genScDef n = conseq [pure n, ws, pure "=", ws, genExpr]
|
||||
|
||||
genExpr :: Gen Text
|
||||
genExpr = gen 4 0 where
|
||||
gen 0 _ = oneof
|
||||
[ genVar
|
||||
, genLit
|
||||
]
|
||||
gen n p = oneof
|
||||
[ gen 0 p
|
||||
, wrapParens <$> gen n' 0
|
||||
, genApp n p
|
||||
, genLet n p
|
||||
-- , genLam n p
|
||||
-- , genCase n p
|
||||
]
|
||||
where n' = next n
|
||||
|
||||
genVar = oneof
|
||||
[ genName
|
||||
, genCon
|
||||
, wrapParens <$> genSymName
|
||||
, wrapParens <$> genSymCon
|
||||
]
|
||||
|
||||
genCase n p = conseq [ pure "case", ws1, gen n' 0, ws1, pure "of"
|
||||
, pure "{", alts, pure "}"
|
||||
]
|
||||
<&> pprec 0 p
|
||||
where
|
||||
n' = next n
|
||||
alts = chooseSize (1,6) (listOf1 alt)
|
||||
<&> intersperse ";"
|
||||
<&> mconcat
|
||||
alt = conseq [ tag, ws, pure "->", ws1, gen n' 0 ]
|
||||
tag = T.pack . show <$> chooseInt (0,maxBound)
|
||||
|
||||
genLit = T.pack . show <$> chooseInt (0,maxBound)
|
||||
|
||||
genApp n p = chooseSize (2,10) (listOf1 (gen n' 1))
|
||||
<&> pprec 0 p . mconcat . intersperse " "
|
||||
where
|
||||
n' = next n
|
||||
|
||||
genLet n p = conseq [ letw, ws, pure "{", ws, binds
|
||||
, ws, pure "}", ws, pure "in"
|
||||
, ws1, gen n' 0
|
||||
]
|
||||
where
|
||||
letw = arbitrary <&> \case
|
||||
Rec -> "letrec"
|
||||
NonRec -> "let"
|
||||
binds = chooseSize (1,6) (listOf1 bind)
|
||||
<&> intersperse ";"
|
||||
<&> mconcat
|
||||
bind = conseq [var, ws, pure "=", ws, gen n' 0]
|
||||
var = oneof [genName, wrapParens <$> genSymName]
|
||||
n' = next n
|
||||
|
||||
genLam n p = conseq [l, ws, bs, ws, pure "->", ws, gen n' 0]
|
||||
<&> pprec 0 p
|
||||
where
|
||||
-- whitespace because reserved op shenanigans :3
|
||||
l = elements [" \\ ", "λ"]
|
||||
n' = next n
|
||||
bs = chooseSize (0,6) (listOf1 genName)
|
||||
<&> mconcat
|
||||
|
||||
next = (`div` 2)
|
||||
|
||||
genTy :: Gen Text
|
||||
genTy = gen 4 where
|
||||
gen 0 = genCon
|
||||
gen n = oneof
|
||||
[ gen 0
|
||||
-- function types
|
||||
, conseq [gen n', ws, pure "->", ws, gen n']
|
||||
-- TODO: type applications (remember precedence lol)
|
||||
]
|
||||
where n' = n `div` 2
|
||||
|
||||
instance Arbitrary Rec where
|
||||
arbitrary = elements [Rec,NonRec]
|
||||
|
||||
chooseSize :: (Int, Int) -> Gen a -> Gen a
|
||||
chooseSize (a,b) g = do
|
||||
n <- chooseInt (a,b)
|
||||
resize n g
|
||||
|
||||
-- | @pprec q p s@ wraps @s@ with parens when @p <= q@
|
||||
pprec :: (IsString a, Monoid a) => Int -> Int -> a -> a
|
||||
pprec maxp p
|
||||
| p <= maxp = id
|
||||
| otherwise = wrapParens
|
||||
|
||||
wrapParens :: (IsString a, Monoid a) => a -> a
|
||||
wrapParens t = "(" <> t <> ")"
|
||||
|
||||
conseq :: (Applicative f, Monoid m, Traversable t)
|
||||
=> t (f m)
|
||||
-> f m
|
||||
conseq tfm = sequenceA tfm <&> the_cool_kid's_concat
|
||||
-- me when `concat` is generalised in the container but specialised in the
|
||||
-- value, and `mconcat` is specialised in the container but generalised in
|
||||
-- the value. shoutout `foldMap id`
|
||||
where the_cool_kid's_concat = foldMap id
|
||||
|
||||
genName :: Gen Name
|
||||
genName = T.pack <$> liftA2 (:) small namechars where
|
||||
small = elements ['a'..'z']
|
||||
|
||||
genCon :: Gen Name
|
||||
genCon = T.pack <$> liftA2 (:) large namechars where
|
||||
large = elements ['A'..'Z']
|
||||
|
||||
genSymName :: Gen Name
|
||||
genSymName = T.pack <$> liftA2 (:) symbol symchars where
|
||||
symbol = elements nameSymbols
|
||||
|
||||
genSymCon :: Gen Name
|
||||
genSymCon = T.pack . (':' :) <$> symchars
|
||||
|
||||
namechars :: Gen String
|
||||
namechars = liftArbitrary namechar where
|
||||
namechar :: Gen Char
|
||||
namechar = elements $ ['a'..'z'] <> ['A'..'Z'] <> ['0'..'9'] <> "'"
|
||||
|
||||
nameSymbols :: [Char]
|
||||
nameSymbols = "!#$%&*+./<=>?@^|-~"
|
||||
|
||||
symchars :: Gen String
|
||||
symchars = liftArbitrary symchar where
|
||||
symchar = elements $ ':' : nameSymbols
|
||||
|
||||
txt :: (IsText t) => t -> Doc
|
||||
txt t = t ^. unpacked & text
|
||||
|
||||
ws :: (IsString a) => Gen a
|
||||
ws = elements [""," ", " "]
|
||||
|
||||
ws1 :: (IsString a) => Gen a
|
||||
ws1 = elements [" ", " "]
|
||||
|
||||
----------------------------------------------------------------------------------
|
||||
|
||||
-- | Two bodies of source code are considered congruent iff the parser produces
|
||||
-- identical ASTs for both.
|
||||
congruentSrc :: ProgramSrc -> ProgramSrc -> Bool
|
||||
congruentSrc = (==) `on` (justParseSrc . T.unpack . coerce)
|
||||
|
||||
----------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: unparseCoreProg :: Program -> [CoreToken]
|
||||
-- womp womp.
|
||||
|
||||
-- TODO: implement shrink
|
||||
|
||||
-- | @unparseCoreProg@ should be inverse to @parseCoreProg@ up to source code
|
||||
-- congruency, newtype coercion and errors handling.
|
||||
unparseCoreProg :: Program' -> ProgramSrc
|
||||
unparseCoreProg p = unparseTypeSigs (p ^. programTypeSigs)
|
||||
<> unparseScDefs (p ^. programScDefs)
|
||||
|
||||
unparseTypeSigs :: H.HashMap Name Type -> ProgramSrc
|
||||
unparseTypeSigs = H.foldrWithKey f mempty
|
||||
where f k v a = unparseTypeSig k v <> ";\n\n" <> a
|
||||
|
||||
unparseTypeSig :: Name -> Type -> ProgramSrc
|
||||
unparseTypeSig n t = unparseName n <> " :: " <> unparseType t
|
||||
|
||||
unparseName :: Name -> ProgramSrc
|
||||
unparseName n
|
||||
| T.head n `elem` (':' : nameSymbols) = coerce $ wrapParens n
|
||||
| otherwise = coerce n
|
||||
|
||||
unparseType :: Type -> ProgramSrc
|
||||
unparseType = go 0 where
|
||||
go :: Int -> Type -> ProgramSrc
|
||||
-- (:->) is a special case of TyApp, but we want the infix syntax
|
||||
go p (a :-> b) = a : assocFun b
|
||||
<&> go 1
|
||||
& coerce (T.intercalate " -> ")
|
||||
& pprec 0 p
|
||||
go p a@(TyApp f x) = assocApp a
|
||||
<&> go 1
|
||||
& coerce (T.intercalate " ")
|
||||
& pprec 1 p
|
||||
go _ TyFun = "(->)"
|
||||
go _ (TyCon a) = unparseName a
|
||||
go _ (TyVar a) = unparseName a
|
||||
|
||||
assocFun :: Type -> [Type]
|
||||
assocFun (a :-> b) = a : assocFun b
|
||||
assocFun x = [x]
|
||||
|
||||
assocApp :: Type -> [Type]
|
||||
assocApp (TyApp f x) = assocApp f ++ [x]
|
||||
assocApp x = [x]
|
||||
|
||||
unparseScDefs :: [ScDef'] -> ProgramSrc
|
||||
unparseScDefs = foldr f mempty where
|
||||
f sc a = unparseScDef sc <> ";\n\n" <> a
|
||||
|
||||
unparseScDef :: ScDef' -> ProgramSrc
|
||||
unparseScDef (ScDef n as e) = (unparseName <$> (n:as)) <> ["=", unparseExpr e]
|
||||
& coerce (T.intercalate " ")
|
||||
|
||||
unparseExpr :: Expr' -> ProgramSrc
|
||||
unparseExpr = go 0 where
|
||||
go :: Int -> Expr' -> ProgramSrc
|
||||
go _ (Var n) = unparseName n
|
||||
go _ (Con t a) = mconcat ["Pack{",srcShow t," ",srcShow a,"}"]
|
||||
go _ (Lit l) = unparseLit l
|
||||
go p a@(App _ _) = srci " " (go 1 <$> assocApp a)
|
||||
& pprec 0 p
|
||||
go p (Lam bs e) = "λ" <> srci " " (unparseName <$> bs)
|
||||
<> " -> " <> go 0 e
|
||||
& pprec 0 p
|
||||
go p (Let r bs e) = mconcat [lw," { ",bs'," } in ",go 0 e]
|
||||
& pprec 0 p
|
||||
where
|
||||
lw = case r of { NonRec -> "let"; Rec -> "letrec" }
|
||||
bs' = srci "; " $ unparseBinding <$> bs
|
||||
go p (Case e as) = mconcat ["case ",go 0 e," of {",as',"}"]
|
||||
& pprec 0 p
|
||||
where as' = srci "; " (unparseAlter <$> as)
|
||||
|
||||
assocApp (App f x) = assocApp f ++ [x]
|
||||
assocApp f = [f]
|
||||
|
||||
srci :: ProgramSrc -> [ProgramSrc] -> ProgramSrc
|
||||
srci = coerce T.intercalate
|
||||
|
||||
unparseBinding :: Binding' -> ProgramSrc
|
||||
unparseBinding (k := v) = mconcat [unparseName k, " = ", unparseExpr v]
|
||||
|
||||
unparseLit :: Lit -> ProgramSrc
|
||||
unparseLit (IntL n) = srcShow n
|
||||
|
||||
srcShow :: (Show a) => a -> ProgramSrc
|
||||
srcShow = coerce . T.pack . show
|
||||
|
||||
unparseAlter :: Alter' -> ProgramSrc
|
||||
unparseAlter (Alter (AltData t) as e) = srcShow t <> " " <> coerce (T.unwords as)
|
||||
<> " -> " <> unparseExpr e
|
||||
|
||||
@@ -21,7 +21,7 @@ spec = do
|
||||
resultOf [coreProg|id x = x; main = (id (-#)) 3 2;|] `shouldBe` Just (NNum 1)
|
||||
|
||||
it "should correctly evaluate arbitrary arithmetic" $ do
|
||||
withMaxSuccess 40 $ property $ \e ->
|
||||
property $ \e ->
|
||||
let arithRes = Just (evalArith e)
|
||||
coreRes = evalCore e
|
||||
in coreRes `shouldBe` arithRes
|
||||
|
||||
Reference in New Issue
Block a user