Compare commits
103 Commits
test-examp
...
rlp2core
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4c0c3a71a | ||
|
|
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 | ||
|
|
832767575c | ||
|
|
1dc695f640 | ||
|
|
b941347f82 | ||
|
|
35446533d7 | ||
|
|
e80acbcd28 | ||
|
|
cb5692248f | ||
|
|
1164b13a1e | ||
|
|
b6945a64eb | ||
|
|
526bf0734e | ||
|
|
c2960e4acc | ||
|
|
07be32c618 | ||
|
|
5c9bf40e40 | ||
|
|
fe90c9afb0 | ||
|
|
414312cf98 | ||
|
|
6f522d34ff | ||
|
|
d954734660 | ||
|
|
52b7723ea0 | ||
|
|
ac6f826141 | ||
|
|
e222dae6ac | ||
|
|
e9e1c075db | ||
|
|
0470912983 | ||
|
|
f7e850c61a | ||
|
|
78f88e085f | ||
|
|
20c936f317 | ||
|
|
136e3687b0 | ||
|
|
585130cfac | ||
|
|
bc3bd92c43 | ||
|
|
81a324111e | ||
|
|
1d5af748b3 | ||
|
|
ce0a135666 | ||
|
|
eaac7ad7a3 | ||
|
|
633882119b | ||
|
|
1f2d540c72 | ||
|
|
9c7c9c4730 | ||
|
|
f45c06cad5 |
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
|
||||||
|
|
||||||
19
Makefile_happysrcs
Normal file
19
Makefile_happysrcs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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
|
||||||
|
lexers: $(CABAL_BUILD)/Rlp/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 $@
|
||||||
|
|
||||||
11
README.md
11
README.md
@@ -12,14 +12,15 @@ $ cabal build # Build the rlpc compiler
|
|||||||
$ cabal install # Install rlpc to $PATH
|
$ cabal install # Install rlpc to $PATH
|
||||||
$ cabal haddock # Build the API docs w/ Haddock
|
$ cabal haddock # Build the API docs w/ Haddock
|
||||||
$ make -C doc html # Build the primary docs w/ Sphinx
|
$ make -C doc html # Build the primary docs w/ Sphinx
|
||||||
|
|
||||||
|
# run the test suite
|
||||||
|
$ cabal test --test-show-details=direct
|
||||||
```
|
```
|
||||||
|
|
||||||
### Use
|
### Use
|
||||||
```sh
|
```sh
|
||||||
# Compile and evaluate t.hs
|
# Compile and evaluate examples/factorial.hs, with evaluation info dumped to stderr
|
||||||
$ rlpc t.hs
|
$ rlpc -ddump-eval examples/factorial.hs
|
||||||
# Compile and evaluate t.hs, with evaluation info dumped to stderr
|
|
||||||
$ rlpc -ddump-eval t.hs
|
|
||||||
# Compile and evaluate t.hs, with evaluation info dumped to t.log
|
# Compile and evaluate t.hs, with evaluation info dumped to t.log
|
||||||
$ rlpc -ddump-eval -l t.log t.hs
|
$ rlpc -ddump-eval -l t.log t.hs
|
||||||
# Print the raw structure describing the compiler options and die
|
# Print the raw structure describing the compiler options and die
|
||||||
@@ -80,7 +81,7 @@ Listed in order of importance.
|
|||||||
- [ ] Tail call optimisation
|
- [ ] Tail call optimisation
|
||||||
- [x] Parsing rlp
|
- [x] Parsing rlp
|
||||||
- [ ] Tests
|
- [ ] Tests
|
||||||
- [ ] Generic example programs
|
- [x] Generic example programs
|
||||||
- [ ] Parser
|
- [ ] Parser
|
||||||
|
|
||||||
### December Release Plan
|
### December Release Plan
|
||||||
|
|||||||
11
app/Main.hs
11
app/Main.hs
@@ -7,6 +7,9 @@ import Options.Applicative hiding (ParseError)
|
|||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Data.HashSet qualified as S
|
import Data.HashSet qualified as S
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Text qualified as T
|
||||||
|
import Data.Text.IO qualified as TIO
|
||||||
import System.IO
|
import System.IO
|
||||||
import System.Exit (exitSuccess)
|
import System.Exit (exitSuccess)
|
||||||
import Core
|
import Core
|
||||||
@@ -102,7 +105,7 @@ dshowFlags = whenFlag flagDDumpOpts do
|
|||||||
ddumpAST :: RLPCIO CompilerError ()
|
ddumpAST :: RLPCIO CompilerError ()
|
||||||
ddumpAST = whenFlag flagDDumpAST $ forFiles_ \o f -> do
|
ddumpAST = whenFlag flagDDumpAST $ forFiles_ \o f -> do
|
||||||
liftIO $ withFile f ReadMode $ \h -> do
|
liftIO $ withFile f ReadMode $ \h -> do
|
||||||
s <- hGetContents h
|
s <- TIO.hGetContents h
|
||||||
case parseProg o s of
|
case parseProg o s of
|
||||||
Right (a,_) -> hPutStrLn stderr $ show a
|
Right (a,_) -> hPutStrLn stderr $ show a
|
||||||
Left e -> error "todo errors lol"
|
Left e -> error "todo errors lol"
|
||||||
@@ -110,10 +113,10 @@ ddumpAST = whenFlag flagDDumpAST $ forFiles_ \o f -> do
|
|||||||
ddumpEval :: RLPCIO CompilerError ()
|
ddumpEval :: RLPCIO CompilerError ()
|
||||||
ddumpEval = whenFlag flagDDumpEval do
|
ddumpEval = whenFlag flagDDumpEval do
|
||||||
fs <- view rlpcInputFiles
|
fs <- view rlpcInputFiles
|
||||||
forM_ fs $ \f -> liftIO (readFile f) >>= doProg
|
forM_ fs $ \f -> liftIO (TIO.readFile f) >>= doProg
|
||||||
|
|
||||||
where
|
where
|
||||||
doProg :: String -> RLPCIO CompilerError ()
|
doProg :: Text -> RLPCIO CompilerError ()
|
||||||
doProg s = ask >>= \o -> case parseProg o s of
|
doProg s = ask >>= \o -> case parseProg o s of
|
||||||
-- TODO: error handling
|
-- TODO: error handling
|
||||||
Left e -> addFatal . CompilerError $ show e
|
Left e -> addFatal . CompilerError $ show e
|
||||||
@@ -133,7 +136,7 @@ ddumpEval = whenFlag flagDDumpEval do
|
|||||||
where v f p h = f p h *> pure ()
|
where v f p h = f p h *> pure ()
|
||||||
|
|
||||||
parseProg :: RLPCOptions
|
parseProg :: RLPCOptions
|
||||||
-> String
|
-> Text
|
||||||
-> Either SrcError (Program', [SrcError])
|
-> Either SrcError (Program', [SrcError])
|
||||||
parseProg o = evalRLPC o . (lexCore >=> parseCoreProg)
|
parseProg o = evalRLPC o . (lexCore >=> parseCoreProg)
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
The *G-Machine*
|
The *G-Machine*
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
The G-Machine (graph machine) is the current heart of rlpc, until we potentially
|
||||||
|
move onto a STG (spineless tagless graph machine) or a TIM (three-instruction
|
||||||
|
machine). rl' source code is desugared into Core; a dumbed-down subset of rl',
|
||||||
|
and then compiled to G-Machine code, which is then finally translated to the
|
||||||
|
desired target.
|
||||||
|
|
||||||
**********
|
**********
|
||||||
Motivation
|
Motivation
|
||||||
**********
|
**********
|
||||||
|
|
||||||
Our initial model, the *Template Instantiator* (TI) was a very
|
Our initial model, the *Template Instantiator* (TI) was a very straightforward
|
||||||
straightforward solution to compilation, but its core design has a major
|
solution to compilation, but its core design has a major Achilles' heel, being
|
||||||
Achilles' heel, being that Compilation is interleaved with evaluation -- The
|
that compilation is interleaved with evaluation -- The heap nodes for
|
||||||
heap nodes for supercombinators hold uninstantiated expressions, i.e. raw ASTs
|
supercombinators hold uninstantiated expressions, i.e. raw ASTs straight from
|
||||||
straight from the parser. When a supercombinator is found on the stack during
|
the parser. When a supercombinator is found on the stack during evaluation, the
|
||||||
evaluation, the template expression is instantiated (compiled) on the spot.
|
template expression is instantiated (compiled) on the spot. This makes
|
||||||
|
translation to an assembly difficult, undermining the point of an intermediate
|
||||||
|
language.
|
||||||
|
|
||||||
.. math::
|
.. math::
|
||||||
\transrule
|
\transrule
|
||||||
@@ -31,7 +39,7 @@ evaluation, the template expression is instantiated (compiled) on the spot.
|
|||||||
\text{where } h' = \mathtt{instantiateU} \; e \; a_n \; h \; g
|
\text{where } h' = \mathtt{instantiateU} \; e \; a_n \; h \; g
|
||||||
}
|
}
|
||||||
|
|
||||||
The process of instantiating a supercombinator goes something like this
|
The process of instantiating a supercombinator goes something like this:
|
||||||
|
|
||||||
1. Augment the environment with bindings to the arguments.
|
1. Augment the environment with bindings to the arguments.
|
||||||
|
|
||||||
@@ -52,13 +60,17 @@ The process of instantiating a supercombinator goes something like this
|
|||||||
Instantiating the supercombinator's body in this way is the root of our
|
Instantiating the supercombinator's body in this way is the root of our
|
||||||
Achilles' heel. Traversing a tree structure is a very non-linear task unfit for
|
Achilles' heel. Traversing a tree structure is a very non-linear task unfit for
|
||||||
an assembly target. The goal of our new G-Machine is to compile a *linear
|
an assembly target. The goal of our new G-Machine is to compile a *linear
|
||||||
sequence of instructions* which instantiate the expression at execution.
|
sequence of instructions* which, **when executed**, build up a graph
|
||||||
|
representing the code.
|
||||||
|
|
||||||
**************************
|
**************************
|
||||||
Trees and Vines, in Theory
|
Trees and Vines, in Theory
|
||||||
**************************
|
**************************
|
||||||
|
|
||||||
WIP.
|
Rather than instantiating an expression at runtime -- traversing the AST and
|
||||||
|
building a graph -- we want to compile all expressions at compile-time,
|
||||||
|
generating a linear sequence of instructions which may be executed to build the
|
||||||
|
graph.
|
||||||
|
|
||||||
**************************
|
**************************
|
||||||
Evaluation: Slurping Vines
|
Evaluation: Slurping Vines
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
The *Template Instantiator*
|
|
||||||
====================================
|
|
||||||
|
|
||||||
WIP. This will hopefully be expanded into a thorough walkthrough of the state
|
|
||||||
machine.
|
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ author = 'madeleine sydney slaga'
|
|||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||||
|
|
||||||
extensions = ['sphinx.ext.imgmath']
|
extensions = ['sphinx.ext.imgmath', 'sphinx.ext.graphviz']
|
||||||
|
|
||||||
# templates_path = ['_templates']
|
# templates_path = ['_templates']
|
||||||
exclude_patterns = []
|
exclude_patterns = []
|
||||||
@@ -32,6 +32,7 @@ html_theme = 'alabaster'
|
|||||||
imgmath_latex_preamble = r'''
|
imgmath_latex_preamble = r'''
|
||||||
\usepackage{amsmath}
|
\usepackage{amsmath}
|
||||||
\usepackage{tabularray}
|
\usepackage{tabularray}
|
||||||
|
\usepackage{syntax}
|
||||||
|
|
||||||
\newcommand{\transrule}[2]
|
\newcommand{\transrule}[2]
|
||||||
{\begin{tblr}{|rrrlc|}
|
{\begin{tblr}{|rrrlc|}
|
||||||
|
|||||||
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' ')'
|
||||||
|
|
||||||
3
examples/constDivZero.hs
Normal file
3
examples/constDivZero.hs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
k x y = x;
|
||||||
|
main = k 3 ((/#) 1 0);
|
||||||
|
|
||||||
7
examples/factorial.hs
Normal file
7
examples/factorial.hs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fac n = case (==#) n 0 of
|
||||||
|
{ 1 -> 1
|
||||||
|
; 0 -> (*#) n (fac ((-#) n 1))
|
||||||
|
};
|
||||||
|
|
||||||
|
main = fac 3;
|
||||||
|
|
||||||
9
examples/sumList.hs
Normal file
9
examples/sumList.hs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
nil = Pack{0 0};
|
||||||
|
cons x y = Pack{1 2} x y;
|
||||||
|
list = cons 1 (cons 2 (cons 3 nil));
|
||||||
|
sum l = case l of
|
||||||
|
{ 0 -> 0
|
||||||
|
; 1 x xs -> (+#) x (sum xs)
|
||||||
|
};
|
||||||
|
main = sum list;
|
||||||
|
|
||||||
61
rlp.cabal
61
rlp.cabal
@@ -12,6 +12,7 @@ category: Language
|
|||||||
build-type: Simple
|
build-type: Simple
|
||||||
extra-doc-files: README.md
|
extra-doc-files: README.md
|
||||||
-- extra-source-files:
|
-- extra-source-files:
|
||||||
|
tested-with: GHC==9.6.2
|
||||||
|
|
||||||
common warnings
|
common warnings
|
||||||
-- ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-unused-top-binds
|
-- ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-unused-top-binds
|
||||||
@@ -22,37 +23,54 @@ library
|
|||||||
, TI
|
, TI
|
||||||
, GM
|
, GM
|
||||||
, Compiler.RLPC
|
, Compiler.RLPC
|
||||||
|
, Compiler.RlpcError
|
||||||
|
, Compiler.JustRun
|
||||||
, Core.Syntax
|
, Core.Syntax
|
||||||
, Core.Examples
|
, Core.Examples
|
||||||
, Core.Utils
|
, Core.Utils
|
||||||
, Core.TH
|
, Core.TH
|
||||||
|
, Core.HindleyMilner
|
||||||
|
, Control.Monad.Errorful
|
||||||
|
, Rlp.Syntax
|
||||||
|
-- , Rlp.Parse.Decls
|
||||||
|
, Rlp.Parse
|
||||||
|
, Rlp.Parse.Associate
|
||||||
|
, Rlp.Lex
|
||||||
|
, Rlp.Parse.Types
|
||||||
|
, Rlp.TH
|
||||||
|
|
||||||
other-modules: Data.Heap
|
other-modules: Data.Heap
|
||||||
, Data.Pretty
|
, Data.Pretty
|
||||||
, Core.Parse
|
, Core.Parse
|
||||||
, Core.Lex
|
, Core.Lex
|
||||||
, Control.Monad.Errorful
|
|
||||||
, Core2Core
|
, Core2Core
|
||||||
, RLP.Syntax
|
, Rlp2Core
|
||||||
|
, Control.Monad.Utils
|
||||||
|
|
||||||
build-tool-depends: happy:happy, alex:alex
|
build-tool-depends: happy:happy, alex:alex
|
||||||
|
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: base ^>=4.18.0.0
|
build-depends: base ^>=4.18.0.0
|
||||||
, containers
|
|
||||||
, microlens
|
|
||||||
, microlens-th
|
|
||||||
, mtl
|
|
||||||
, template-haskell
|
|
||||||
-- required for happy
|
-- required for happy
|
||||||
, array
|
, array >= 0.5.5 && < 0.6
|
||||||
, data-default-class
|
, containers >= 0.6.7 && < 0.7
|
||||||
, unordered-containers
|
, template-haskell >= 2.20.0 && < 2.21
|
||||||
, hashable
|
, pretty >= 1.1.3 && < 1.2
|
||||||
, pretty
|
, data-default >= 0.7.1 && < 0.8
|
||||||
, recursion-schemes
|
, data-default-class >= 0.1.2 && < 0.2
|
||||||
, megaparsec
|
, hashable >= 1.4.3 && < 1.5
|
||||||
, text
|
, 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
|
hs-source-dirs: src
|
||||||
default-language: GHC2021
|
default-language: GHC2021
|
||||||
@@ -64,11 +82,12 @@ executable rlpc
|
|||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: base ^>=4.18.0.0
|
build-depends: base ^>=4.18.0.0
|
||||||
, rlp
|
, rlp
|
||||||
, optparse-applicative
|
, optparse-applicative >= 0.18.1 && < 0.19
|
||||||
, microlens
|
, microlens >= 0.4.13 && < 0.5
|
||||||
, microlens-mtl
|
, microlens-mtl >= 0.2.0 && < 0.3
|
||||||
, mtl
|
, mtl >= 2.3.1 && < 2.4
|
||||||
, unordered-containers
|
, unordered-containers >= 0.2.20 && < 0.3
|
||||||
|
, text >= 2.0.2 && < 2.1
|
||||||
|
|
||||||
hs-source-dirs: app
|
hs-source-dirs: app
|
||||||
default-language: GHC2021
|
default-language: GHC2021
|
||||||
@@ -84,7 +103,9 @@ test-suite rlp-test
|
|||||||
, rlp
|
, rlp
|
||||||
, QuickCheck
|
, QuickCheck
|
||||||
, hspec ==2.*
|
, hspec ==2.*
|
||||||
|
, microlens
|
||||||
other-modules: Arith
|
other-modules: Arith
|
||||||
, GMSpec
|
, GMSpec
|
||||||
|
, Core.HindleyMilnerSpec
|
||||||
build-tool-depends: hspec-discover:hspec-discover
|
build-tool-depends: hspec-discover:hspec-discover
|
||||||
|
|
||||||
|
|||||||
46
src/Compiler/JustRun.hs
Normal file
46
src/Compiler/JustRun.hs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{-|
|
||||||
|
Module : Compiler.JustRun
|
||||||
|
Description : No-BS, high-level wrappers for major pipeline pieces.
|
||||||
|
|
||||||
|
A collection of wrapper functions to demo processes such as lexing, parsing,
|
||||||
|
type-checking, and evaluation. This module intends to export "no-BS" functions
|
||||||
|
that use Prelude types such as @Either@ and @String@ rather than more complex
|
||||||
|
types such as @RLPC@ or @Text@.
|
||||||
|
-}
|
||||||
|
module Compiler.JustRun
|
||||||
|
( justLexSrc
|
||||||
|
, justParseSrc
|
||||||
|
, justTypeCheckSrc
|
||||||
|
)
|
||||||
|
where
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
import Core.Lex
|
||||||
|
import Core.Parse
|
||||||
|
import Core.HindleyMilner
|
||||||
|
import Core.Syntax (Program')
|
||||||
|
import Compiler.RLPC
|
||||||
|
import Control.Arrow ((>>>))
|
||||||
|
import Control.Monad ((>=>))
|
||||||
|
import Data.Text qualified as T
|
||||||
|
import Data.Function ((&))
|
||||||
|
import GM
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
justLexSrc :: String -> Either RlpcError [CoreToken]
|
||||||
|
justLexSrc s = lexCoreR (T.pack s)
|
||||||
|
& fmap (map $ \ (Located _ _ _ t) -> t)
|
||||||
|
& rlpcToEither
|
||||||
|
|
||||||
|
justParseSrc :: String -> Either RlpcError Program'
|
||||||
|
justParseSrc s = parse (T.pack s)
|
||||||
|
& rlpcToEither
|
||||||
|
where parse = lexCoreR >=> parseCoreProgR
|
||||||
|
|
||||||
|
justTypeCheckSrc :: String -> Either 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
|
||||||
|
|
||||||
@@ -13,9 +13,12 @@ errors and the family of RLPC monads.
|
|||||||
{-# LANGUAGE DeriveGeneric, DerivingStrategies, DerivingVia #-}
|
{-# LANGUAGE DeriveGeneric, DerivingStrategies, DerivingVia #-}
|
||||||
module Compiler.RLPC
|
module Compiler.RLPC
|
||||||
( RLPC
|
( RLPC
|
||||||
, RLPCT
|
, RLPCT(..)
|
||||||
, RLPCIO
|
, RLPCIO
|
||||||
, RLPCOptions(RLPCOptions)
|
, RLPCOptions(RLPCOptions)
|
||||||
|
, RlpcError(..)
|
||||||
|
, IsRlpcError(..)
|
||||||
|
, rlpc
|
||||||
, addFatal
|
, addFatal
|
||||||
, addWound
|
, addWound
|
||||||
, MonadErrorful
|
, MonadErrorful
|
||||||
@@ -24,6 +27,9 @@ module Compiler.RLPC
|
|||||||
, evalRLPCT
|
, evalRLPCT
|
||||||
, evalRLPCIO
|
, evalRLPCIO
|
||||||
, evalRLPC
|
, evalRLPC
|
||||||
|
, addRlpcWound
|
||||||
|
, addRlpcFatal
|
||||||
|
, liftRlpcErrs
|
||||||
, rlpcLogFile
|
, rlpcLogFile
|
||||||
, rlpcDebugOpts
|
, rlpcDebugOpts
|
||||||
, rlpcEvaluator
|
, rlpcEvaluator
|
||||||
@@ -42,6 +48,7 @@ import Control.Exception
|
|||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Control.Monad.State (MonadState(state))
|
import Control.Monad.State (MonadState(state))
|
||||||
import Control.Monad.Errorful
|
import Control.Monad.Errorful
|
||||||
|
import Compiler.RlpcError
|
||||||
import Data.Functor.Identity
|
import Data.Functor.Identity
|
||||||
import Data.Default.Class
|
import Data.Default.Class
|
||||||
import GHC.Generics (Generic)
|
import GHC.Generics (Generic)
|
||||||
@@ -94,7 +101,6 @@ evalRLPCIO o m = do
|
|||||||
Left e -> throwIO e
|
Left e -> throwIO e
|
||||||
Right a -> pure a
|
Right a -> pure a
|
||||||
|
|
||||||
|
|
||||||
data RLPCOptions = RLPCOptions
|
data RLPCOptions = RLPCOptions
|
||||||
{ _rlpcLogFile :: Maybe FilePath
|
{ _rlpcLogFile :: Maybe FilePath
|
||||||
, _rlpcDebugOpts :: DebugOpts
|
, _rlpcDebugOpts :: DebugOpts
|
||||||
@@ -115,13 +121,24 @@ data Severity = Error
|
|||||||
-- temporary until we have a new doc building system
|
-- temporary until we have a new doc building system
|
||||||
type ErrorDoc = String
|
type ErrorDoc = String
|
||||||
|
|
||||||
class Diagnostic e where
|
|
||||||
errorDoc :: e -> ErrorDoc
|
|
||||||
|
|
||||||
instance (Monad m) => MonadErrorful e (RLPCT e m) where
|
instance (Monad m) => MonadErrorful e (RLPCT e m) where
|
||||||
addWound = RLPCT . lift . addWound
|
addWound = RLPCT . lift . addWound
|
||||||
addFatal = RLPCT . lift . addFatal
|
addFatal = RLPCT . lift . addFatal
|
||||||
|
|
||||||
|
liftRlpcErrs :: (IsRlpcError e, Monad m)
|
||||||
|
=> RLPCT e m a -> RLPCT RlpcError m a
|
||||||
|
liftRlpcErrs m = RLPCT . ReaderT $ \r ->
|
||||||
|
mapErrors liftRlpcErr $ runRLPCT >>> (`runReaderT` r) $ m
|
||||||
|
|
||||||
|
addRlpcWound :: (IsRlpcError e, Monad m) => e -> RLPCT RlpcError m ()
|
||||||
|
addRlpcWound = addWound . liftRlpcErr
|
||||||
|
|
||||||
|
addRlpcFatal :: (IsRlpcError e, Monad m) => e -> RLPCT RlpcError m ()
|
||||||
|
addRlpcFatal = addWound . liftRlpcErr
|
||||||
|
|
||||||
|
rlpc :: (Monad m) => ErrorfulT e m a -> RLPCT e m a
|
||||||
|
rlpc = RLPCT . ReaderT . const
|
||||||
|
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
instance Default RLPCOptions where
|
instance Default RLPCOptions where
|
||||||
|
|||||||
15
src/Compiler/RlpcError.hs
Normal file
15
src/Compiler/RlpcError.hs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
module Compiler.RlpcError
|
||||||
|
( RlpcError(..)
|
||||||
|
, IsRlpcError(..)
|
||||||
|
)
|
||||||
|
where
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
import Control.Monad.Errorful
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
data RlpcError = RlpcErr String -- temp
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
class IsRlpcError a where
|
||||||
|
liftRlpcErr :: a -> RlpcError
|
||||||
|
|
||||||
@@ -6,6 +6,7 @@ module Control.Monad.Errorful
|
|||||||
, runErrorfulT
|
, runErrorfulT
|
||||||
, Errorful
|
, Errorful
|
||||||
, runErrorful
|
, runErrorful
|
||||||
|
, mapErrors
|
||||||
, MonadErrorful(..)
|
, MonadErrorful(..)
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
@@ -63,3 +64,10 @@ instance (Monad m) => Monad (ErrorfulT e m) where
|
|||||||
Right (a,es) -> runErrorfulT (k a)
|
Right (a,es) -> runErrorfulT (k a)
|
||||||
Left e -> pure (Left e)
|
Left e -> pure (Left e)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|||||||
21
src/Control/Monad/Utils.hs
Normal file
21
src/Control/Monad/Utils.hs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
module Control.Monad.Utils
|
||||||
|
( mapAccumLM
|
||||||
|
)
|
||||||
|
where
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
import Data.Tuple (swap)
|
||||||
|
import Control.Monad.State
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Monadic variant of @mapAccumL@
|
||||||
|
|
||||||
|
mapAccumLM :: forall m t s a b. (Monad m, Traversable t)
|
||||||
|
=> (s -> a -> m (s, b))
|
||||||
|
-> s
|
||||||
|
-> t a
|
||||||
|
-> m (s, t b)
|
||||||
|
mapAccumLM k s t = swap <$> runStateT (traverse k' t) s
|
||||||
|
where
|
||||||
|
k' :: a -> StateT s m b
|
||||||
|
k' a = StateT $ fmap swap <$> flip k a
|
||||||
|
|
||||||
@@ -8,14 +8,13 @@ module Core.Examples
|
|||||||
( fac3
|
( fac3
|
||||||
, sumList
|
, sumList
|
||||||
, constDivZero
|
, constDivZero
|
||||||
|
, idCase
|
||||||
) where
|
) where
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
import Core.Syntax
|
import Core.Syntax
|
||||||
import Core.TH
|
import Core.TH
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO: my shitty lexer isn't inserting semicolons
|
|
||||||
|
|
||||||
letrecExample :: Program'
|
letrecExample :: Program'
|
||||||
letrecExample = [coreProg|
|
letrecExample = [coreProg|
|
||||||
pair x y f = f x y;
|
pair x y f = f x y;
|
||||||
@@ -181,30 +180,39 @@ constDivZero = [coreProg|
|
|||||||
main = k 3 ((/#) 1 0);
|
main = k 3 ((/#) 1 0);
|
||||||
|]
|
|]
|
||||||
|
|
||||||
corePrelude :: Module Name
|
idCase :: Program'
|
||||||
corePrelude = Module (Just ("Prelude", [])) $
|
idCase = [coreProg|
|
||||||
-- non-primitive defs
|
|
||||||
[coreProg|
|
|
||||||
id x = x;
|
id x = x;
|
||||||
k x y = x;
|
|
||||||
k1 x y = y;
|
main = id (case Pack{1 0} of
|
||||||
s f g x = f x (g x);
|
{ 1 -> (+#) 2 3
|
||||||
compose f g x = f (g x);
|
})
|
||||||
twice f x = f (f x);
|
|]
|
||||||
fst p = casePair# p k;
|
|
||||||
snd p = casePair# p k1;
|
-- corePrelude :: Module Name
|
||||||
head l = caseList# l abort# k;
|
-- corePrelude = Module (Just ("Prelude", [])) $
|
||||||
tail l = caseList# l abort# k1;
|
-- -- non-primitive defs
|
||||||
_length_cc x xs = (+#) 1 (length xs);
|
-- [coreProg|
|
||||||
length l = caseList# l 0 length_cc;
|
-- id x = x;
|
||||||
|]
|
-- k x y = x;
|
||||||
<>
|
-- k1 x y = y;
|
||||||
-- primitive constructors need some specialised wiring:
|
-- s f g x = f x (g x);
|
||||||
Program
|
-- compose f g x = f (g x);
|
||||||
[ ScDef "False" [] $ Con 0 0
|
-- twice f x = f (f x);
|
||||||
, ScDef "True" [] $ Con 1 0
|
-- fst p = casePair# p k;
|
||||||
, ScDef "MkPair" [] $ Con 0 2
|
-- snd p = casePair# p k1;
|
||||||
, ScDef "Nil" [] $ Con 1 0
|
-- head l = caseList# l abort# k;
|
||||||
, ScDef "Cons" [] $ Con 2 2
|
-- tail l = caseList# l abort# k1;
|
||||||
]
|
-- _length_cc x xs = (+#) 1 (length xs);
|
||||||
|
-- length l = caseList# l 0 length_cc;
|
||||||
|
-- |]
|
||||||
|
-- <>
|
||||||
|
-- -- primitive constructors need some specialised wiring:
|
||||||
|
-- Program
|
||||||
|
-- [ ScDef "False" [] $ Con 0 0
|
||||||
|
-- , ScDef "True" [] $ Con 1 0
|
||||||
|
-- , ScDef "MkPair" [] $ Con 0 2
|
||||||
|
-- , ScDef "Nil" [] $ Con 1 0
|
||||||
|
-- , ScDef "Cons" [] $ Con 2 2
|
||||||
|
-- ]
|
||||||
|
|
||||||
|
|||||||
220
src/Core/HindleyMilner.hs
Normal file
220
src/Core/HindleyMilner.hs
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
{-|
|
||||||
|
Module : Core.HindleyMilner
|
||||||
|
Description : Hindley-Milner type system
|
||||||
|
-}
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
module Core.HindleyMilner
|
||||||
|
( Context'
|
||||||
|
, infer
|
||||||
|
, check
|
||||||
|
, checkCoreProg
|
||||||
|
, checkCoreProgR
|
||||||
|
, TypeError(..)
|
||||||
|
, HMError
|
||||||
|
)
|
||||||
|
where
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
import Lens.Micro
|
||||||
|
import Lens.Micro.Mtl
|
||||||
|
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.Errorful (Errorful, addFatal)
|
||||||
|
import Control.Monad.State
|
||||||
|
import Control.Monad.Utils (mapAccumLM)
|
||||||
|
import Core.Syntax
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Annotated typing context -- I have a feeling we're going to want this in the
|
||||||
|
-- future.
|
||||||
|
type Context b = [(b, Type)]
|
||||||
|
|
||||||
|
-- | Unannotated typing context, AKA our beloved Γ.
|
||||||
|
type Context' = Context Name
|
||||||
|
|
||||||
|
-- TODO: Errorful monad?
|
||||||
|
|
||||||
|
-- | Type error enum.
|
||||||
|
data TypeError
|
||||||
|
-- | Two types could not be unified
|
||||||
|
= TyErrCouldNotUnify Type Type
|
||||||
|
-- | @x@ could not be unified with @t@ because @x@ occurs in @t@
|
||||||
|
| TyErrRecursiveType Name Type
|
||||||
|
-- | Untyped, potentially undefined variable
|
||||||
|
| TyErrUntypedVariable Name
|
||||||
|
| TyErrMissingTypeSig Name
|
||||||
|
deriving (Show, Eq)
|
||||||
|
|
||||||
|
-- TODO:
|
||||||
|
instance IsRlpcError TypeError where
|
||||||
|
liftRlpcErr = RlpcErr . show
|
||||||
|
|
||||||
|
-- | Synonym for @Errorful [TypeError]@. This means an @HMError@ action may
|
||||||
|
-- throw any number of fatal or nonfatal errors. Run with @runErrorful@.
|
||||||
|
type HMError = Errorful TypeError
|
||||||
|
|
||||||
|
-- TODO: better errors. Errorful-esque, with cummulative errors instead of
|
||||||
|
-- instantly dying.
|
||||||
|
|
||||||
|
-- | Assert that an expression unifies with a given type
|
||||||
|
--
|
||||||
|
-- >>> let e = [coreProg|3|]
|
||||||
|
-- >>> check [] (TyCon "Bool") e
|
||||||
|
-- Left (TyErrCouldNotUnify (TyCon "Bool") (TyCon "Int#"))
|
||||||
|
-- >>> check [] (TyCon "Int#") e
|
||||||
|
-- Right ()
|
||||||
|
|
||||||
|
check :: Context' -> Type -> Expr' -> HMError ()
|
||||||
|
check g t1 e = do
|
||||||
|
t2 <- infer g e
|
||||||
|
void $ unify [(t1,t2)]
|
||||||
|
|
||||||
|
-- | Typecheck program. I plan to allow for *some* inference in the future, but
|
||||||
|
-- in the mean time all top-level binders must have a type annotation.
|
||||||
|
checkCoreProg :: Program' -> HMError ()
|
||||||
|
checkCoreProg p = scDefs
|
||||||
|
& traverse_ k
|
||||||
|
where
|
||||||
|
scDefs = p ^. programScDefs
|
||||||
|
g = gatherTypeSigs p
|
||||||
|
|
||||||
|
k :: ScDef' -> HMError ()
|
||||||
|
k sc = case lookup scname g of
|
||||||
|
Just t -> check g t (sc ^. _rhs)
|
||||||
|
Nothing -> addFatal $ TyErrMissingTypeSig scname
|
||||||
|
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
|
||||||
|
|
||||||
|
-- | Infer the type of an expression under some context.
|
||||||
|
--
|
||||||
|
-- >>> let g1 = [("id", TyVar "a" :-> TyVar "a")]
|
||||||
|
-- >>> let g2 = [("id", (TyVar "a" :-> TyVar "a") :-> TyVar "a" :-> TyVar "a")]
|
||||||
|
-- >>> infer g1 [coreExpr|id 3|]
|
||||||
|
-- Right TyInt
|
||||||
|
-- >>> infer g2 [coreExpr|id 3|]
|
||||||
|
-- Left (TyErrCouldNotUnify (TyVar "a" :-> TyVar "a") TyInt)
|
||||||
|
|
||||||
|
infer :: Context' -> Expr' -> HMError Type
|
||||||
|
infer g e = do
|
||||||
|
(t,cs) <- gather g e
|
||||||
|
-- apply all unified constraints
|
||||||
|
foldr (uncurry subst) t <$> unify cs
|
||||||
|
|
||||||
|
-- | A @Constraint@ between two types describes the requirement that the pair
|
||||||
|
-- must unify
|
||||||
|
type Constraint = (Type, Type)
|
||||||
|
|
||||||
|
-- | Type of an expression under some context, and gather the constraints
|
||||||
|
-- necessary to unify. Note that this is not the same as @infer@, as the
|
||||||
|
-- expression will likely be given a fresh type variable along with a
|
||||||
|
-- constraint, rather than the solved type.
|
||||||
|
--
|
||||||
|
-- For example, if the context says "@id@ has type a -> a," in an application of
|
||||||
|
-- @id 3@, the whole application is assigned type @$a0@ and the constraint that
|
||||||
|
-- @id@ must unify with type @Int -> $a0@ is generated.
|
||||||
|
--
|
||||||
|
-- >>> gather [("id", TyVar "a" :-> TyVar "a")] [coreExpr|id 3|]
|
||||||
|
-- (TyVar "$a0",[(TyVar "a" :-> TyVar "a",TyInt :-> TyVar "$a0")])
|
||||||
|
|
||||||
|
gather :: Context' -> Expr' -> HMError (Type, [Constraint])
|
||||||
|
gather = \g e -> runStateT (go g e) ([],0) <&> \ (t,(cs,_)) -> (t,cs) where
|
||||||
|
go :: Context' -> Expr' -> StateT ([Constraint], Int) HMError Type
|
||||||
|
go g = \case
|
||||||
|
Lit (IntL _) -> pure TyInt
|
||||||
|
Var k -> lift $ maybe e pure $ lookup k g
|
||||||
|
where e = addFatal $ TyErrUntypedVariable k
|
||||||
|
App f x -> do
|
||||||
|
tf <- go g f
|
||||||
|
tx <- go g x
|
||||||
|
tfx <- uniqueVar
|
||||||
|
addConstraint tf (tx :-> tfx)
|
||||||
|
pure tfx
|
||||||
|
Let NonRec bs e -> do
|
||||||
|
g' <- buildLetContext g bs
|
||||||
|
go g' e
|
||||||
|
-- TODO letrec, lambda, case
|
||||||
|
|
||||||
|
buildLetContext :: Context' -> [Binding']
|
||||||
|
-> StateT ([Constraint], Int) HMError Context'
|
||||||
|
buildLetContext = foldM k where
|
||||||
|
k :: Context' -> Binding' -> StateT ([Constraint], Int) HMError Context'
|
||||||
|
k g (x := y) = do
|
||||||
|
ty <- go g y
|
||||||
|
pure ((x,ty) : g)
|
||||||
|
|
||||||
|
uniqueVar :: StateT ([Constraint], Int) HMError Type
|
||||||
|
uniqueVar = do
|
||||||
|
n <- use _2
|
||||||
|
_2 %= succ
|
||||||
|
pure (TyVar . T.pack $ '$' : 'a' : show n)
|
||||||
|
|
||||||
|
addConstraint :: Type -> Type -> StateT ([Constraint], Int) HMError ()
|
||||||
|
addConstraint t u = _1 %= ((t, u):)
|
||||||
|
|
||||||
|
-- | Unify a list of constraints, meaning that pairs between types are turned
|
||||||
|
-- into pairs of type variables and types. A useful thought model is to think of
|
||||||
|
-- it as solving an equation such that the unknown variable is the left-hand
|
||||||
|
-- side.
|
||||||
|
|
||||||
|
unify :: [Constraint] -> HMError Context'
|
||||||
|
unify = go mempty where
|
||||||
|
go :: Context' -> [Constraint] -> HMError Context'
|
||||||
|
|
||||||
|
-- nothing left! return accumulated context
|
||||||
|
go g [] = pure g
|
||||||
|
|
||||||
|
go g (c:cs) = case c of
|
||||||
|
-- primitives may of course unify with themselves
|
||||||
|
(TyInt, TyInt) -> go g cs
|
||||||
|
|
||||||
|
-- `x` unifies with `x`
|
||||||
|
(TyVar t, TyVar u) | t == u -> go g cs
|
||||||
|
|
||||||
|
-- a type variable `x` unifies with an arbitrary type `t` if `t` does
|
||||||
|
-- not reference `x`
|
||||||
|
(TyVar x, t) -> unifyTV g x t cs
|
||||||
|
(t, TyVar x) -> unifyTV g x t cs
|
||||||
|
|
||||||
|
-- two functions may be unified if their domain and codomain unify
|
||||||
|
(a :-> b, x :-> y) -> go g $ (a,x) : (b,y) : cs
|
||||||
|
|
||||||
|
-- anything else is a failure :(
|
||||||
|
(t,u) -> addFatal $ TyErrCouldNotUnify t u
|
||||||
|
|
||||||
|
unifyTV :: Context' -> Name -> Type -> [Constraint] -> HMError Context'
|
||||||
|
unifyTV g x t cs | occurs t = addFatal $ TyErrRecursiveType x t
|
||||||
|
| otherwise = go g' substed
|
||||||
|
where
|
||||||
|
g' = (x,t) : g
|
||||||
|
substed = cs & each . both %~ subst x t
|
||||||
|
|
||||||
|
occurs (a :-> b) = occurs a || occurs b
|
||||||
|
occurs (TyVar y)
|
||||||
|
| x == y = True
|
||||||
|
occurs _ = False
|
||||||
|
|
||||||
|
gatherTypeSigs :: Program b -> Context b
|
||||||
|
gatherTypeSigs p = p ^. programTypeSigs
|
||||||
|
& H.toList
|
||||||
|
|
||||||
|
-- | The expression @subst x t e@ substitutes all occurences of @x@ in @e@ with
|
||||||
|
-- @t@
|
||||||
|
--
|
||||||
|
-- >>> subst "a" (TyCon "Int") (TyVar "a")
|
||||||
|
-- TyCon "Int"
|
||||||
|
-- >>> subst "a" (TyCon "Int") (TyVar "a" :-> TyVar "a")
|
||||||
|
-- TyCon "Int" :-> TyCon "Int"
|
||||||
|
|
||||||
|
subst :: Name -> Type -> Type -> Type
|
||||||
|
subst x t (TyVar y) | x == y = t
|
||||||
|
subst x t (a :-> b) = subst x t a :-> subst x t b
|
||||||
|
subst _ _ e = e
|
||||||
|
|
||||||
@@ -3,8 +3,10 @@
|
|||||||
Module : Core.Lex
|
Module : Core.Lex
|
||||||
Description : Lexical analysis for the core language
|
Description : Lexical analysis for the core language
|
||||||
-}
|
-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
module Core.Lex
|
module Core.Lex
|
||||||
( lexCore
|
( lexCore
|
||||||
|
, lexCoreR
|
||||||
, lexCore'
|
, lexCore'
|
||||||
, CoreToken(..)
|
, CoreToken(..)
|
||||||
, SrcError(..)
|
, SrcError(..)
|
||||||
@@ -15,13 +17,17 @@ module Core.Lex
|
|||||||
where
|
where
|
||||||
import Data.Char (chr)
|
import Data.Char (chr)
|
||||||
import Debug.Trace
|
import Debug.Trace
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Text qualified as T
|
||||||
|
import Data.String (IsString(..))
|
||||||
import Core.Syntax
|
import Core.Syntax
|
||||||
import Compiler.RLPC
|
import Compiler.RLPC
|
||||||
|
import Compiler.RlpcError
|
||||||
import Lens.Micro
|
import Lens.Micro
|
||||||
import Lens.Micro.TH
|
import Lens.Micro.TH
|
||||||
}
|
}
|
||||||
|
|
||||||
%wrapper "monad"
|
%wrapper "monad-strict-text"
|
||||||
|
|
||||||
$whitechar = [ \t\n\r\f\v]
|
$whitechar = [ \t\n\r\f\v]
|
||||||
$special = [\(\)\,\;\[\]\{\}]
|
$special = [\(\)\,\;\[\]\{\}]
|
||||||
@@ -68,6 +74,7 @@ rlp :-
|
|||||||
"{" { constTok TokenLBrace }
|
"{" { constTok TokenLBrace }
|
||||||
"}" { constTok TokenRBrace }
|
"}" { constTok TokenRBrace }
|
||||||
";" { constTok TokenSemicolon }
|
";" { constTok TokenSemicolon }
|
||||||
|
"::" { constTok TokenHasType }
|
||||||
"@" { constTok TokenTypeApp }
|
"@" { constTok TokenTypeApp }
|
||||||
"{-#" { constTok TokenLPragma `andBegin` pragma }
|
"{-#" { constTok TokenLPragma `andBegin` pragma }
|
||||||
|
|
||||||
@@ -80,7 +87,7 @@ rlp :-
|
|||||||
"where" { constTok TokenWhere }
|
"where" { constTok TokenWhere }
|
||||||
"Pack" { constTok TokenPack } -- temp
|
"Pack" { constTok TokenPack } -- temp
|
||||||
|
|
||||||
"\\" { constTok TokenLambda }
|
"\" { constTok TokenLambda }
|
||||||
"λ" { constTok TokenLambda }
|
"λ" { constTok TokenLambda }
|
||||||
"=" { constTok TokenEquals }
|
"=" { constTok TokenEquals }
|
||||||
"->" { constTok TokenArrow }
|
"->" { constTok TokenArrow }
|
||||||
@@ -90,7 +97,7 @@ rlp :-
|
|||||||
@varsym { lexWith TokenVarSym }
|
@varsym { lexWith TokenVarSym }
|
||||||
@consym { lexWith TokenConSym }
|
@consym { lexWith TokenConSym }
|
||||||
|
|
||||||
@decimal { lexWith (TokenLitInt . read @Int) }
|
@decimal { lexWith (TokenLitInt . read @Int . T.unpack) }
|
||||||
|
|
||||||
$white { skip }
|
$white { skip }
|
||||||
\n { skip }
|
\n { skip }
|
||||||
@@ -134,10 +141,11 @@ data CoreToken = TokenLet
|
|||||||
| TokenLBrace
|
| TokenLBrace
|
||||||
| TokenRBrace
|
| TokenRBrace
|
||||||
| TokenSemicolon
|
| TokenSemicolon
|
||||||
|
| TokenHasType
|
||||||
| TokenTypeApp
|
| TokenTypeApp
|
||||||
| TokenLPragma
|
| TokenLPragma
|
||||||
| TokenRPragma
|
| TokenRPragma
|
||||||
| TokenWord String
|
| TokenWord Text
|
||||||
| TokenEOF
|
| TokenEOF
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
@@ -155,11 +163,11 @@ data SrcErrorType = SrcErrLexical String
|
|||||||
|
|
||||||
type Lexer = AlexInput -> Int -> Alex (Located CoreToken)
|
type Lexer = AlexInput -> Int -> Alex (Located CoreToken)
|
||||||
|
|
||||||
lexWith :: (String -> CoreToken) -> Lexer
|
lexWith :: (Text -> CoreToken) -> Lexer
|
||||||
lexWith f (AlexPn _ y x,_,_,s) l = pure $ Located y x l (f $ take l s)
|
lexWith f (AlexPn _ y x,_,_,s) l = pure $ Located y x l (f $ T.take l s)
|
||||||
|
|
||||||
-- | The main lexer driver.
|
-- | The main lexer driver.
|
||||||
lexCore :: String -> RLPC SrcError [Located CoreToken]
|
lexCore :: Text -> RLPC SrcError [Located CoreToken]
|
||||||
lexCore s = case m of
|
lexCore s = case m of
|
||||||
Left e -> addFatal err
|
Left e -> addFatal err
|
||||||
where err = SrcError
|
where err = SrcError
|
||||||
@@ -171,9 +179,12 @@ lexCore s = case m of
|
|||||||
where
|
where
|
||||||
m = runAlex s lexStream
|
m = runAlex s lexStream
|
||||||
|
|
||||||
|
lexCoreR :: Text -> RLPC RlpcError [Located CoreToken]
|
||||||
|
lexCoreR = liftRlpcErrs . lexCore
|
||||||
|
|
||||||
-- | @lexCore@, but the tokens are stripped of location info. Useful for
|
-- | @lexCore@, but the tokens are stripped of location info. Useful for
|
||||||
-- debugging
|
-- debugging
|
||||||
lexCore' :: String -> RLPC SrcError [CoreToken]
|
lexCore' :: Text -> RLPC SrcError [CoreToken]
|
||||||
lexCore' s = fmap f <$> lexCore s
|
lexCore' s = fmap f <$> lexCore s
|
||||||
where f (Located _ _ _ t) = t
|
where f (Located _ _ _ t) = t
|
||||||
|
|
||||||
@@ -188,6 +199,14 @@ data ParseError = ParErrLexical String
|
|||||||
| ParErrParse
|
| ParErrParse
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
-- TODO:
|
||||||
|
instance IsRlpcError SrcError where
|
||||||
|
liftRlpcErr = RlpcErr . show
|
||||||
|
|
||||||
|
-- TODO:
|
||||||
|
instance IsRlpcError ParseError where
|
||||||
|
liftRlpcErr = RlpcErr . show
|
||||||
|
|
||||||
alexEOF :: Alex (Located CoreToken)
|
alexEOF :: Alex (Located CoreToken)
|
||||||
alexEOF = Alex $ \ st@(AlexState { alex_pos = AlexPn _ y x }) ->
|
alexEOF = Alex $ \ st@(AlexState { alex_pos = AlexPn _ y x }) ->
|
||||||
Right (st, Located y x 0 TokenEOF)
|
Right (st, Located y x 0 TokenEOF)
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
Module : Core.Parse
|
Module : Core.Parse
|
||||||
Description : Parser for the Core language
|
Description : Parser for the Core language
|
||||||
-}
|
-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
module Core.Parse
|
module Core.Parse
|
||||||
( parseCore
|
( parseCore
|
||||||
, parseCoreExpr
|
, parseCoreExpr
|
||||||
, parseCoreProg
|
, parseCoreProg
|
||||||
|
, parseCoreProgR
|
||||||
, module Core.Lex -- temp convenience
|
, module Core.Lex -- temp convenience
|
||||||
, parseTmp
|
, parseTmp
|
||||||
, SrcError
|
, SrcError
|
||||||
@@ -19,7 +21,12 @@ import Data.Foldable (foldl')
|
|||||||
import Core.Syntax
|
import Core.Syntax
|
||||||
import Core.Lex
|
import Core.Lex
|
||||||
import Compiler.RLPC
|
import Compiler.RLPC
|
||||||
|
import Lens.Micro
|
||||||
import Data.Default.Class (def)
|
import Data.Default.Class (def)
|
||||||
|
import Data.Hashable (Hashable)
|
||||||
|
import Data.Text.IO qualified as TIO
|
||||||
|
import Data.Text qualified as T
|
||||||
|
import Data.HashMap.Strict qualified as H
|
||||||
}
|
}
|
||||||
|
|
||||||
%name parseCore Module
|
%name parseCore Module
|
||||||
@@ -55,6 +62,7 @@ import Data.Default.Class (def)
|
|||||||
'{-#' { Located _ _ _ TokenLPragma }
|
'{-#' { Located _ _ _ TokenLPragma }
|
||||||
'#-}' { Located _ _ _ TokenRPragma }
|
'#-}' { Located _ _ _ TokenRPragma }
|
||||||
';' { Located _ _ _ TokenSemicolon }
|
';' { Located _ _ _ TokenSemicolon }
|
||||||
|
'::' { Located _ _ _ TokenHasType }
|
||||||
eof { Located _ _ _ TokenEOF }
|
eof { Located _ _ _ TokenEOF }
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@@ -71,7 +79,17 @@ StandaloneProgram :: { Program Name }
|
|||||||
StandaloneProgram : Program eof { $1 }
|
StandaloneProgram : Program eof { $1 }
|
||||||
|
|
||||||
Program :: { Program Name }
|
Program :: { Program Name }
|
||||||
Program : ScDefs { Program $1 }
|
Program : ScTypeSig ';' Program { insTypeSig $1 $3 }
|
||||||
|
| ScTypeSig OptSemi { singletonTypeSig $1 }
|
||||||
|
| ScDef ';' Program { insScDef $1 $3 }
|
||||||
|
| ScDef OptSemi { singletonScDef $1 }
|
||||||
|
|
||||||
|
OptSemi :: { () }
|
||||||
|
OptSemi : ';' { () }
|
||||||
|
| {- epsilon -} { () }
|
||||||
|
|
||||||
|
ScTypeSig :: { (Name, Type) }
|
||||||
|
ScTypeSig : Var '::' Type { ($1,$3) }
|
||||||
|
|
||||||
ScDefs :: { [ScDef Name] }
|
ScDefs :: { [ScDef Name] }
|
||||||
ScDefs : ScDef ';' ScDefs { $1 : $3 }
|
ScDefs : ScDef ';' ScDefs { $1 : $3 }
|
||||||
@@ -82,6 +100,16 @@ ScDefs : ScDef ';' ScDefs { $1 : $3 }
|
|||||||
ScDef :: { ScDef Name }
|
ScDef :: { ScDef Name }
|
||||||
ScDef : Var ParList '=' Expr { ScDef $1 $2 $4 }
|
ScDef : Var ParList '=' Expr { ScDef $1 $2 $4 }
|
||||||
|
|
||||||
|
Type :: { Type }
|
||||||
|
Type : Type1 { $1 }
|
||||||
|
|
||||||
|
Type1 :: { Type }
|
||||||
|
Type1 : '(' Type ')' { $2 }
|
||||||
|
| Type1 '->' Type { $1 :-> $3 }
|
||||||
|
-- do we want to allow symbolic names for tyvars and tycons?
|
||||||
|
| varname { TyVar $1 }
|
||||||
|
| conname { TyCon $1 }
|
||||||
|
|
||||||
ParList :: { [Name] }
|
ParList :: { [Name] }
|
||||||
ParList : Var ParList { $1 : $2 }
|
ParList : Var ParList { $1 : $2 }
|
||||||
| {- epsilon -} { [] }
|
| {- epsilon -} { [] }
|
||||||
@@ -123,7 +151,7 @@ Alter :: { Alter Name }
|
|||||||
Alter : litint ParList '->' Expr { Alter (AltData $1) $2 $4 }
|
Alter : litint ParList '->' Expr { Alter (AltData $1) $2 $4 }
|
||||||
|
|
||||||
Expr1 :: { Expr Name }
|
Expr1 :: { Expr Name }
|
||||||
Expr1 : litint { LitE $ IntL $1 }
|
Expr1 : litint { Lit $ IntL $1 }
|
||||||
| Id { Var $1 }
|
| Id { Var $1 }
|
||||||
| PackCon { $1 }
|
| PackCon { $1 }
|
||||||
| ExprPragma { $1 }
|
| ExprPragma { $1 }
|
||||||
@@ -133,8 +161,8 @@ ExprPragma :: { Expr Name }
|
|||||||
ExprPragma : '{-#' Words '#-}' {% exprPragma $2 }
|
ExprPragma : '{-#' Words '#-}' {% exprPragma $2 }
|
||||||
|
|
||||||
Words :: { [String] }
|
Words :: { [String] }
|
||||||
Words : word Words { $1 : $2 }
|
Words : word Words { T.unpack $1 : $2 }
|
||||||
| word { [$1] }
|
| word { [T.unpack $1] }
|
||||||
|
|
||||||
PackCon :: { Expr Name }
|
PackCon :: { Expr Name }
|
||||||
PackCon : pack '{' litint litint '}' { Con $3 $4 }
|
PackCon : pack '{' litint litint '}' { Con $3 $4 }
|
||||||
@@ -171,7 +199,7 @@ parseError (Located y x l _ : _) = addFatal err
|
|||||||
|
|
||||||
parseTmp :: IO (Module Name)
|
parseTmp :: IO (Module Name)
|
||||||
parseTmp = do
|
parseTmp = do
|
||||||
s <- readFile "/tmp/t.hs"
|
s <- TIO.readFile "/tmp/t.hs"
|
||||||
case parse s of
|
case parse s of
|
||||||
Left e -> error (show e)
|
Left e -> error (show e)
|
||||||
Right (ts,_) -> pure ts
|
Right (ts,_) -> pure ts
|
||||||
@@ -190,5 +218,20 @@ exprPragma _ = addFatal err
|
|||||||
astPragma :: [String] -> RLPC SrcError (Expr Name)
|
astPragma :: [String] -> RLPC SrcError (Expr Name)
|
||||||
astPragma = pure . read . unwords
|
astPragma = pure . read . unwords
|
||||||
|
|
||||||
|
insTypeSig :: (Hashable b) => (b, Type) -> Program b -> Program b
|
||||||
|
insTypeSig ts = programTypeSigs %~ uncurry H.insert ts
|
||||||
|
|
||||||
|
singletonTypeSig :: (Hashable b) => (b, Type) -> Program b
|
||||||
|
singletonTypeSig ts = insTypeSig ts mempty
|
||||||
|
|
||||||
|
insScDef :: (Hashable b) => ScDef b -> Program b -> Program b
|
||||||
|
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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,15 @@ Description : Core ASTs and the like
|
|||||||
-}
|
-}
|
||||||
{-# LANGUAGE PatternSynonyms, OverloadedStrings #-}
|
{-# LANGUAGE PatternSynonyms, OverloadedStrings #-}
|
||||||
{-# LANGUAGE FunctionalDependencies #-}
|
{-# LANGUAGE FunctionalDependencies #-}
|
||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
module Core.Syntax
|
module Core.Syntax
|
||||||
( Expr(..)
|
( Expr(..)
|
||||||
, Type(..)
|
, Type(..)
|
||||||
, Literal(..)
|
, pattern TyInt
|
||||||
|
, Lit(..)
|
||||||
, pattern (:$)
|
, pattern (:$)
|
||||||
|
, pattern (:@)
|
||||||
|
, pattern (:->)
|
||||||
, Binding(..)
|
, Binding(..)
|
||||||
, AltCon(..)
|
, AltCon(..)
|
||||||
, pattern (:=)
|
, pattern (:=)
|
||||||
@@ -20,7 +24,9 @@ module Core.Syntax
|
|||||||
, Module(..)
|
, Module(..)
|
||||||
, Program(..)
|
, Program(..)
|
||||||
, Program'
|
, Program'
|
||||||
|
, unliftScDef
|
||||||
, programScDefs
|
, programScDefs
|
||||||
|
, programTypeSigs
|
||||||
, Expr'
|
, Expr'
|
||||||
, ScDef'
|
, ScDef'
|
||||||
, Alter'
|
, Alter'
|
||||||
@@ -32,40 +38,51 @@ module Core.Syntax
|
|||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
import Data.Coerce
|
import Data.Coerce
|
||||||
import Data.Pretty
|
import Data.Pretty
|
||||||
import GHC.Generics
|
|
||||||
import Data.List (intersperse)
|
import Data.List (intersperse)
|
||||||
import Data.Function ((&))
|
import Data.Function ((&))
|
||||||
import Data.String
|
import Data.String
|
||||||
|
import Data.HashMap.Strict qualified as H
|
||||||
|
import Data.Hashable
|
||||||
|
import Data.Text qualified as T
|
||||||
|
import Data.Char
|
||||||
-- Lift instances for the Core quasiquoters
|
-- Lift instances for the Core quasiquoters
|
||||||
import Language.Haskell.TH.Syntax (Lift)
|
import Language.Haskell.TH.Syntax (Lift)
|
||||||
|
import Lens.Micro.TH (makeLenses)
|
||||||
import Lens.Micro
|
import Lens.Micro
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
data Expr b = Var Name
|
data Expr b = Var Name
|
||||||
| Con Tag Int -- Con Tag Arity
|
| Con Tag Int -- ^ Con Tag Arity
|
||||||
| Case (Expr b) [Alter b]
|
| Case (Expr b) [Alter b]
|
||||||
| Lam [b] (Expr b)
|
| Lam [b] (Expr b)
|
||||||
| Let Rec [Binding b] (Expr b)
|
| Let Rec [Binding b] (Expr b)
|
||||||
| App (Expr b) (Expr b)
|
| App (Expr b) (Expr b)
|
||||||
| LitE Literal
|
| Lit Lit
|
||||||
| Type Type
|
|
||||||
deriving (Show, Read, Lift)
|
deriving (Show, Read, Lift)
|
||||||
|
|
||||||
deriving instance (Eq b) => Eq (Expr b)
|
deriving instance (Eq b) => Eq (Expr b)
|
||||||
|
|
||||||
data Type = TyInt
|
data Type = TyFun
|
||||||
| TyFun
|
|
||||||
| TyVar Name
|
| TyVar Name
|
||||||
| TyApp Type Type
|
| TyApp Type Type
|
||||||
| TyConApp TyCon [Type]
|
| TyCon Name
|
||||||
deriving (Show, Read, Lift, Eq)
|
deriving (Show, Read, Lift, Eq)
|
||||||
|
|
||||||
type TyCon = Name
|
pattern TyInt :: Type
|
||||||
|
pattern TyInt = TyCon "Int#"
|
||||||
|
|
||||||
infixl 2 :$
|
infixl 2 :$
|
||||||
pattern (:$) :: Expr b -> Expr b -> Expr b
|
pattern (:$) :: Expr b -> Expr b -> Expr b
|
||||||
pattern f :$ x = App f x
|
pattern f :$ x = App f x
|
||||||
|
|
||||||
|
infixl 2 :@
|
||||||
|
pattern (:@) :: Type -> Type -> Type
|
||||||
|
pattern f :@ x = TyApp f x
|
||||||
|
|
||||||
|
infixr 1 :->
|
||||||
|
pattern (:->) :: Type -> Type -> Type
|
||||||
|
pattern a :-> b = TyApp (TyApp TyFun a) b
|
||||||
|
|
||||||
{-# COMPLETE Binding :: Binding #-}
|
{-# COMPLETE Binding :: Binding #-}
|
||||||
{-# COMPLETE (:=) :: Binding #-}
|
{-# COMPLETE (:=) :: Binding #-}
|
||||||
data Binding b = Binding b (Expr b)
|
data Binding b = Binding b (Expr b)
|
||||||
@@ -87,27 +104,33 @@ data Rec = Rec
|
|||||||
deriving (Show, Read, Eq, Lift)
|
deriving (Show, Read, Eq, Lift)
|
||||||
|
|
||||||
data AltCon = AltData Tag
|
data AltCon = AltData Tag
|
||||||
| AltLiteral Literal
|
| AltLit Lit
|
||||||
| Default
|
| Default
|
||||||
deriving (Show, Read, Eq, Lift)
|
deriving (Show, Read, Eq, Lift)
|
||||||
|
|
||||||
data Literal = IntL Int
|
data Lit = IntL Int
|
||||||
deriving (Show, Read, Eq, Lift)
|
deriving (Show, Read, Eq, Lift)
|
||||||
|
|
||||||
type Name = String
|
type Name = T.Text
|
||||||
type Tag = Int
|
type Tag = Int
|
||||||
|
|
||||||
data ScDef b = ScDef b [b] (Expr b)
|
data ScDef b = ScDef b [b] (Expr b)
|
||||||
deriving (Show, Lift)
|
deriving (Show, Lift)
|
||||||
|
|
||||||
|
unliftScDef :: ScDef b -> Expr b
|
||||||
|
unliftScDef (ScDef _ as e) = Lam as e
|
||||||
|
|
||||||
data Module b = Module (Maybe (Name, [Name])) (Program b)
|
data Module b = Module (Maybe (Name, [Name])) (Program b)
|
||||||
deriving (Show, Lift)
|
deriving (Show, Lift)
|
||||||
|
|
||||||
newtype Program b = Program [ScDef b]
|
data Program b = Program
|
||||||
|
{ _programScDefs :: [ScDef b]
|
||||||
|
, _programTypeSigs :: H.HashMap b Type
|
||||||
|
}
|
||||||
deriving (Show, Lift)
|
deriving (Show, Lift)
|
||||||
|
|
||||||
programScDefs :: Lens' (Program b) [ScDef b]
|
makeLenses ''Program
|
||||||
programScDefs = lens coerce (const coerce)
|
pure []
|
||||||
|
|
||||||
type Program' = Program Name
|
type Program' = Program Name
|
||||||
type Expr' = Expr Name
|
type Expr' = Expr Name
|
||||||
@@ -116,13 +139,20 @@ type Alter' = Alter Name
|
|||||||
type Binding' = Binding Name
|
type Binding' = Binding Name
|
||||||
|
|
||||||
instance IsString (Expr b) where
|
instance IsString (Expr b) where
|
||||||
fromString = Var
|
fromString = Var . fromString
|
||||||
|
|
||||||
instance Semigroup (Program b) where
|
instance IsString Type where
|
||||||
(<>) = coerce $ (<>) @[ScDef b]
|
fromString "" = error "IsString Type string may not be empty"
|
||||||
|
fromString s
|
||||||
|
| isUpper c = TyCon . fromString $ s
|
||||||
|
| otherwise = TyVar . fromString $ s
|
||||||
|
where (c:_) = s
|
||||||
|
|
||||||
instance Monoid (Program b) where
|
instance (Hashable b) => Semigroup (Program b) where
|
||||||
mempty = Program []
|
(<>) = undefined
|
||||||
|
|
||||||
|
instance (Hashable b) => Monoid (Program b) where
|
||||||
|
mempty = Program mempty mempty
|
||||||
|
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ Description : Core quasiquoters
|
|||||||
module Core.TH
|
module Core.TH
|
||||||
( coreExpr
|
( coreExpr
|
||||||
, coreProg
|
, coreProg
|
||||||
|
, coreProgT
|
||||||
, core
|
, core
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
@@ -15,10 +16,14 @@ import Language.Haskell.TH.Quote
|
|||||||
import Control.Monad ((>=>))
|
import Control.Monad ((>=>))
|
||||||
import Compiler.RLPC
|
import Compiler.RLPC
|
||||||
import Data.Default.Class (def)
|
import Data.Default.Class (def)
|
||||||
|
import Data.Text qualified as T
|
||||||
import Core.Parse
|
import Core.Parse
|
||||||
import Core.Lex
|
import Core.Lex
|
||||||
|
import Core.HindleyMilner (checkCoreProgR)
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: write in terms of a String -> QuasiQuoter
|
||||||
|
|
||||||
core :: QuasiQuoter
|
core :: QuasiQuoter
|
||||||
core = QuasiQuoter
|
core = QuasiQuoter
|
||||||
{ quoteExp = qCore
|
{ quoteExp = qCore
|
||||||
@@ -43,24 +48,40 @@ coreExpr = QuasiQuoter
|
|||||||
, quoteDec = error "core quasiquotes may only be used in expressions"
|
, quoteDec = error "core quasiquotes may only be used in expressions"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | Type-checked @coreProg@
|
||||||
|
coreProgT :: QuasiQuoter
|
||||||
|
coreProgT = QuasiQuoter
|
||||||
|
{ quoteExp = qCoreProgT
|
||||||
|
, 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 :: String -> Q Exp
|
||||||
qCore s = case parse s of
|
qCore s = case parse (T.pack s) of
|
||||||
Left e -> error (show e)
|
Left e -> error (show e)
|
||||||
Right (m,ts) -> lift m
|
Right (m,ts) -> lift m
|
||||||
where
|
where
|
||||||
parse = evalRLPC def . (lexCore >=> parseCore)
|
parse = evalRLPC def . (lexCore >=> parseCore)
|
||||||
|
|
||||||
qCoreExpr :: String -> Q Exp
|
qCoreExpr :: String -> Q Exp
|
||||||
qCoreExpr s = case parseExpr s of
|
qCoreExpr s = case parseExpr (T.pack s) of
|
||||||
Left e -> error (show e)
|
Left e -> error (show e)
|
||||||
Right (m,ts) -> lift m
|
Right (m,ts) -> lift m
|
||||||
where
|
where
|
||||||
parseExpr = evalRLPC def . (lexCore >=> parseCoreExpr)
|
parseExpr = evalRLPC def . (lexCore >=> parseCoreExpr)
|
||||||
|
|
||||||
qCoreProg :: String -> Q Exp
|
qCoreProg :: String -> Q Exp
|
||||||
qCoreProg s = case parseProg s of
|
qCoreProg s = case parse (T.pack s) of
|
||||||
Left e -> error (show e)
|
Left e -> error (show e)
|
||||||
Right (m,ts) -> lift m
|
Right (m,ts) -> lift m
|
||||||
where
|
where
|
||||||
parseProg = evalRLPC def . (lexCore >=> parseCoreProg)
|
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)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ module Core.Utils
|
|||||||
( bindersOf
|
( bindersOf
|
||||||
, rhssOf
|
, rhssOf
|
||||||
, isAtomic
|
, isAtomic
|
||||||
, insertModule
|
-- , insertModule
|
||||||
, extractProgram
|
, extractProgram
|
||||||
, freeVariables
|
, freeVariables
|
||||||
, ExprF(..)
|
, ExprF(..)
|
||||||
@@ -19,6 +19,7 @@ import Data.Functor.Foldable
|
|||||||
import Data.Set (Set)
|
import Data.Set (Set)
|
||||||
import Data.Set qualified as S
|
import Data.Set qualified as S
|
||||||
import Core.Syntax
|
import Core.Syntax
|
||||||
|
import Lens.Micro
|
||||||
import GHC.Exts (IsList(..))
|
import GHC.Exts (IsList(..))
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -32,14 +33,14 @@ rhssOf = fromList . fmap f
|
|||||||
|
|
||||||
isAtomic :: Expr b -> Bool
|
isAtomic :: Expr b -> Bool
|
||||||
isAtomic (Var _) = True
|
isAtomic (Var _) = True
|
||||||
isAtomic (LitE _) = True
|
isAtomic (Lit _) = True
|
||||||
isAtomic _ = False
|
isAtomic _ = False
|
||||||
|
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO: export list awareness
|
-- TODO: export list awareness
|
||||||
insertModule :: Module b -> Program b -> Program b
|
-- insertModule :: Module b -> Program b -> Program b
|
||||||
insertModule (Module _ m) p = p <> m
|
-- insertModule (Module _ p) = programScDefs %~ (<>m)
|
||||||
|
|
||||||
extractProgram :: Module b -> Program b
|
extractProgram :: Module b -> Program b
|
||||||
extractProgram (Module _ p) = p
|
extractProgram (Module _ p) = p
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import Data.List
|
|||||||
import Control.Monad.Writer
|
import Control.Monad.Writer
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import Control.Arrow ((>>>))
|
import Control.Arrow ((>>>))
|
||||||
|
import Data.Text qualified as T
|
||||||
import Numeric (showHex)
|
import Numeric (showHex)
|
||||||
import Lens.Micro
|
import Lens.Micro
|
||||||
import Core.Syntax
|
import Core.Syntax
|
||||||
@@ -27,7 +28,7 @@ core2core :: Program' -> Program'
|
|||||||
core2core p = undefined
|
core2core p = undefined
|
||||||
|
|
||||||
gmPrep :: Program' -> Program'
|
gmPrep :: Program' -> Program'
|
||||||
gmPrep p = p' <> Program caseScs
|
gmPrep p = p' & programScDefs %~ (<>caseScs)
|
||||||
where
|
where
|
||||||
rhss :: Applicative f => (Expr z -> f (Expr z)) -> Program z -> f (Program z)
|
rhss :: Applicative f => (Expr z -> f (Expr z)) -> Program z -> f (Program z)
|
||||||
rhss = programScDefs . each . _rhs
|
rhss = programScDefs . each . _rhs
|
||||||
@@ -46,8 +47,7 @@ type Floater = StateT [Name] (Writer [ScDef'])
|
|||||||
runFloater :: Floater a -> (a, [ScDef'])
|
runFloater :: Floater a -> (a, [ScDef'])
|
||||||
runFloater = flip evalStateT ns >>> runWriter
|
runFloater = flip evalStateT ns >>> runWriter
|
||||||
where
|
where
|
||||||
-- TODO: safer, uncapturable names
|
ns = [ T.pack $ "$nonstrict_case_" ++ showHex n "" | n <- [0..] ]
|
||||||
ns = [ "nonstrict_case_" ++ showHex n "" | n <- [0..] ]
|
|
||||||
|
|
||||||
-- TODO: formally define a "strict context" and reference that here
|
-- TODO: formally define a "strict context" and reference that here
|
||||||
-- the returned ScDefs are guaranteed to be free of non-strict cases.
|
-- the returned ScDefs are guaranteed to be free of non-strict cases.
|
||||||
@@ -56,7 +56,7 @@ floatNonStrictCases g = goE
|
|||||||
where
|
where
|
||||||
goE :: Expr' -> Floater Expr'
|
goE :: Expr' -> Floater Expr'
|
||||||
goE (Var k) = pure (Var k)
|
goE (Var k) = pure (Var k)
|
||||||
goE (LitE l) = pure (LitE l)
|
goE (Lit l) = pure (Lit l)
|
||||||
goE (Case e as) = pure (Case e as)
|
goE (Case e as) = pure (Case e as)
|
||||||
goE (Let Rec bs e) = Let Rec <$> bs' <*> goE e
|
goE (Let Rec bs e) = Let Rec <$> bs' <*> goE e
|
||||||
where bs' = travBs goE bs
|
where bs' = travBs goE bs
|
||||||
@@ -78,7 +78,7 @@ floatNonStrictCases g = goE
|
|||||||
goC (f :$ x) = (:$) <$> goC f <*> goC x
|
goC (f :$ x) = (:$) <$> goC f <*> goC x
|
||||||
goC (Let r bs e) = Let r <$> bs' <*> goE e
|
goC (Let r bs e) = Let r <$> bs' <*> goE e
|
||||||
where bs' = travBs goC bs
|
where bs' = travBs goC bs
|
||||||
goC (LitE l) = pure (LitE l)
|
goC (Lit l) = pure (Lit l)
|
||||||
goC (Var k) = pure (Var k)
|
goC (Var k) = pure (Var k)
|
||||||
goC (Con t as) = pure (Con t as)
|
goC (Con t as) = pure (Con t as)
|
||||||
|
|
||||||
|
|||||||
34
src/GM.hs
34
src/GM.hs
@@ -22,7 +22,10 @@ import Data.Maybe (fromMaybe, mapMaybe)
|
|||||||
import Data.Monoid (Endo(..))
|
import Data.Monoid (Endo(..))
|
||||||
import Data.Tuple (swap)
|
import Data.Tuple (swap)
|
||||||
import Lens.Micro
|
import Lens.Micro
|
||||||
|
import Lens.Micro.Extras (view)
|
||||||
import Lens.Micro.TH
|
import Lens.Micro.TH
|
||||||
|
import Lens.Micro.Platform (packed, unpacked)
|
||||||
|
import Lens.Micro.Platform.Internal (IsText(..))
|
||||||
import Text.Printf
|
import Text.Printf
|
||||||
import Text.PrettyPrint hiding ((<>))
|
import Text.PrettyPrint hiding ((<>))
|
||||||
import Text.PrettyPrint.HughesPJ (maybeParens)
|
import Text.PrettyPrint.HughesPJ (maybeParens)
|
||||||
@@ -281,7 +284,7 @@ step st = case head (st ^. gmCode) of
|
|||||||
m = st ^. gmEnv
|
m = st ^. gmEnv
|
||||||
s = st ^. gmStack
|
s = st ^. gmStack
|
||||||
h = st ^. gmHeap
|
h = st ^. gmHeap
|
||||||
n' = show n
|
n' = show n ^. packed
|
||||||
|
|
||||||
-- Core Rule 2. (no sharing)
|
-- Core Rule 2. (no sharing)
|
||||||
-- pushIntI :: Int -> GmState
|
-- pushIntI :: Int -> GmState
|
||||||
@@ -582,7 +585,7 @@ compiledPrims =
|
|||||||
binop k i = (k, 2, [Push 1, Eval, Push 1, Eval, i, Update 2, Pop 2, Unwind])
|
binop k i = (k, 2, [Push 1, Eval, Push 1, Eval, i, Update 2, Pop 2, Unwind])
|
||||||
|
|
||||||
buildInitialHeap :: Program' -> (GmHeap, Env)
|
buildInitialHeap :: Program' -> (GmHeap, Env)
|
||||||
buildInitialHeap (Program ss) = mapAccumL allocateSc mempty compiledScs
|
buildInitialHeap (view programScDefs -> ss) = mapAccumL allocateSc mempty compiledScs
|
||||||
where
|
where
|
||||||
compiledScs = fmap compileSc ss <> compiledPrims
|
compiledScs = fmap compileSc ss <> compiledPrims
|
||||||
|
|
||||||
@@ -612,12 +615,13 @@ buildInitialHeap (Program ss) = mapAccumL allocateSc mempty compiledScs
|
|||||||
| k `elem` domain = [Push n]
|
| k `elem` domain = [Push n]
|
||||||
| otherwise = [PushGlobal k]
|
| otherwise = [PushGlobal k]
|
||||||
where
|
where
|
||||||
n = fromMaybe (error $ "undeclared var: " <> k) $ lookupN k g
|
n = fromMaybe err $ lookupN k g
|
||||||
|
err = error $ "undeclared var: " <> (k ^. unpacked)
|
||||||
domain = f `mapMaybe` g
|
domain = f `mapMaybe` g
|
||||||
f (NameKey n, _) = Just n
|
f (NameKey n, _) = Just n
|
||||||
f _ = Nothing
|
f _ = Nothing
|
||||||
|
|
||||||
compileC _ (LitE l) = compileCL l
|
compileC _ (Lit l) = compileCL l
|
||||||
|
|
||||||
-- >> [ref/compileC]
|
-- >> [ref/compileC]
|
||||||
compileC g (App f x) = compileC g x
|
compileC g (App f x) = compileC g x
|
||||||
@@ -661,16 +665,16 @@ buildInitialHeap (Program ss) = mapAccumL allocateSc mempty compiledScs
|
|||||||
|
|
||||||
compileC _ _ = error "yet to be implemented!"
|
compileC _ _ = error "yet to be implemented!"
|
||||||
|
|
||||||
compileCL :: Literal -> Code
|
compileCL :: Lit -> Code
|
||||||
compileCL (IntL n) = [PushInt n]
|
compileCL (IntL n) = [PushInt n]
|
||||||
|
|
||||||
compileEL :: Literal -> Code
|
compileEL :: Lit -> Code
|
||||||
compileEL (IntL n) = [PushInt n]
|
compileEL (IntL n) = [PushInt n]
|
||||||
|
|
||||||
-- compile an expression in a strict context such that a pointer to the
|
-- compile an expression in a strict context such that a pointer to the
|
||||||
-- expression is left on top of the stack in WHNF
|
-- expression is left on top of the stack in WHNF
|
||||||
compileE :: Env -> Expr' -> Code
|
compileE :: Env -> Expr' -> Code
|
||||||
compileE _ (LitE l) = compileEL l
|
compileE _ (Lit l) = compileEL l
|
||||||
compileE g (Let NonRec bs e) =
|
compileE g (Let NonRec bs e) =
|
||||||
-- we use compileE instead of compileC
|
-- we use compileE instead of compileC
|
||||||
mconcat binders <> compileE g' e <> [Slide d]
|
mconcat binders <> compileE g' e <> [Slide d]
|
||||||
@@ -738,8 +742,8 @@ buildInitialHeap (Program ss) = mapAccumL allocateSc mempty compiledScs
|
|||||||
argOffset :: Int -> Env -> Env
|
argOffset :: Int -> Env -> Env
|
||||||
argOffset n = each . _2 %~ (+n)
|
argOffset n = each . _2 %~ (+n)
|
||||||
|
|
||||||
idPack :: Tag -> Int -> String
|
showCon :: (IsText a) => Tag -> Int -> a
|
||||||
idPack t n = printf "Pack{%d %d}" t n
|
showCon t n = printf "Pack{%d %d}" t n ^. packed
|
||||||
|
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -855,12 +859,12 @@ showNodeAt = showNodeAtP 0
|
|||||||
showNodeAtP :: Int -> GmState -> Addr -> Doc
|
showNodeAtP :: Int -> GmState -> Addr -> Doc
|
||||||
showNodeAtP p st a = case hLookup a h of
|
showNodeAtP p st a = case hLookup a h of
|
||||||
Just (NNum n) -> int n <> "#"
|
Just (NNum n) -> int n <> "#"
|
||||||
Just (NGlobal _ _) -> text name
|
Just (NGlobal _ _) -> textt name
|
||||||
where
|
where
|
||||||
g = st ^. gmEnv
|
g = st ^. gmEnv
|
||||||
name = case lookup a (swap <$> g) of
|
name = case lookup a (swap <$> g) of
|
||||||
Just (NameKey n) -> n
|
Just (NameKey n) -> n
|
||||||
Just (ConstrKey t n) -> idPack t n
|
Just (ConstrKey t n) -> showCon t n
|
||||||
_ -> errTxtInvalidAddress
|
_ -> errTxtInvalidAddress
|
||||||
-- TODO: left-associativity
|
-- TODO: left-associativity
|
||||||
Just (NAp f x) -> pprec $ showNodeAtP (p+1) st f
|
Just (NAp f x) -> pprec $ showNodeAtP (p+1) st f
|
||||||
@@ -877,7 +881,7 @@ showNodeAtP p st a = case hLookup a h of
|
|||||||
pprec = maybeParens (p > 0)
|
pprec = maybeParens (p > 0)
|
||||||
|
|
||||||
showSc :: GmState -> (Name, Addr) -> Doc
|
showSc :: GmState -> (Name, Addr) -> Doc
|
||||||
showSc st (k,a) = "Supercomb " <> qquotes (text k) <> colon
|
showSc st (k,a) = "Supercomb " <> qquotes (textt k) <> colon
|
||||||
$$ code
|
$$ code
|
||||||
where
|
where
|
||||||
code = case hLookup a (st ^. gmHeap) of
|
code = case hLookup a (st ^. gmHeap) of
|
||||||
@@ -900,6 +904,9 @@ showInstr (CaseJump alts) = "CaseJump" $$ nest pprTabstop alternatives
|
|||||||
alternatives = foldr (\a acc -> showAlt a $$ acc) mempty alts
|
alternatives = foldr (\a acc -> showAlt a $$ acc) mempty alts
|
||||||
showInstr i = text $ show i
|
showInstr i = text $ show i
|
||||||
|
|
||||||
|
textt :: (IsText a) => a -> Doc
|
||||||
|
textt t = t ^. unpacked & text
|
||||||
|
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
lookupN :: Name -> Env -> Maybe Addr
|
lookupN :: Name -> Env -> Maybe Addr
|
||||||
@@ -975,7 +982,8 @@ resultOf p = do
|
|||||||
h = st ^. gmHeap
|
h = st ^. gmHeap
|
||||||
|
|
||||||
resultOfExpr :: Expr' -> Maybe Node
|
resultOfExpr :: Expr' -> Maybe Node
|
||||||
resultOfExpr e = resultOf $ Program
|
resultOfExpr e = resultOf $
|
||||||
|
mempty & programScDefs .~
|
||||||
[ ScDef "main" [] e
|
[ ScDef "main" [] e
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -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')
|
|
||||||
347
src/Rlp/Lex.x
Normal file
347
src/Rlp/Lex.x
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
{
|
||||||
|
{-# LANGUAGE ViewPatterns, LambdaCase #-}
|
||||||
|
{-# LANGUAGE GeneralisedNewtypeDeriving #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
module Rlp.Lex
|
||||||
|
( P(..)
|
||||||
|
, RlpToken(..)
|
||||||
|
, Located(..)
|
||||||
|
, lexToken
|
||||||
|
, lexDebug
|
||||||
|
, lexCont
|
||||||
|
, execP
|
||||||
|
, execP'
|
||||||
|
)
|
||||||
|
where
|
||||||
|
import Codec.Binary.UTF8.String (encodeChar)
|
||||||
|
import Control.Monad
|
||||||
|
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
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
execP :: P a -> ParseState -> Maybe a
|
||||||
|
execP p st = runP p st & snd
|
||||||
|
|
||||||
|
execP' :: P a -> Text -> Maybe a
|
||||||
|
execP' p s = execP p st where
|
||||||
|
st = initParseState s
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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 = execP' lexStream s
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
184
src/Rlp/Parse.y
Normal file
184
src/Rlp/Parse.y
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
{
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
module Rlp.Parse
|
||||||
|
( parseRlpProg
|
||||||
|
, execP
|
||||||
|
, execP'
|
||||||
|
)
|
||||||
|
where
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
%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 _ TokenHasType }
|
||||||
|
'=' { 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 }
|
||||||
|
| TySigDecl { $1 }
|
||||||
|
| DataDecl { $1 }
|
||||||
|
| InfixDecl { $1 }
|
||||||
|
|
||||||
|
-- TODO: multiple vars
|
||||||
|
|
||||||
|
TySigDecl :: { PartialDecl' }
|
||||||
|
: Var '::' Type { TySigD [$1] $3 }
|
||||||
|
|
||||||
|
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 = error . show
|
||||||
|
|
||||||
|
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 -> error "(TODO: non-fatal) duplicate inix decls"
|
||||||
|
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))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
163
src/Rlp/Parse/Types.hs
Normal file
163
src/Rlp/Parse/Types.hs
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE ImplicitParams, ViewPatterns, PatternSynonyms #-}
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
module Rlp.Parse.Types where
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
import Core.Syntax (Name)
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.State.Class
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
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, Maybe a) }
|
||||||
|
deriving (Functor)
|
||||||
|
|
||||||
|
instance Applicative P where
|
||||||
|
pure a = P $ \st -> (st,Just a)
|
||||||
|
liftA2 = liftM2
|
||||||
|
|
||||||
|
instance Monad P where
|
||||||
|
p >>= k = P $ \st ->
|
||||||
|
let (st',a) = runP p st
|
||||||
|
in case a of
|
||||||
|
Just x -> runP (k x) st'
|
||||||
|
Nothing -> (st', Nothing)
|
||||||
|
|
||||||
|
instance MonadState ParseState P where
|
||||||
|
state f = P $ \st ->
|
||||||
|
let (a,st') = f st
|
||||||
|
in (st', Just a)
|
||||||
|
|
||||||
|
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
|
||||||
|
deriving (Eq, Ord, Show)
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
178
src/Rlp/Syntax.hs
Normal file
178
src/Rlp/Syntax.hs
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
-- 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 Language.Haskell.TH.Syntax (Lift)
|
||||||
|
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, Lift)
|
||||||
|
|
||||||
|
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, Lift)
|
||||||
|
|
||||||
|
type Decl' e = Decl e Name
|
||||||
|
|
||||||
|
data Assoc = InfixL
|
||||||
|
| InfixR
|
||||||
|
| Infix
|
||||||
|
deriving (Show, Lift)
|
||||||
|
|
||||||
|
data ConAlt = ConAlt ConId [Type]
|
||||||
|
deriving (Show, Lift)
|
||||||
|
|
||||||
|
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, Lift)
|
||||||
|
|
||||||
|
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, Lift)
|
||||||
|
|
||||||
|
data Bind b = PatB (Pat b) (RlpExpr b)
|
||||||
|
| FunB VarId [Pat b] (RlpExpr b)
|
||||||
|
deriving (Show, Lift)
|
||||||
|
|
||||||
|
data VarId = NameVar Text
|
||||||
|
| SymVar Text
|
||||||
|
deriving (Show, Lift)
|
||||||
|
|
||||||
|
instance IsString VarId where
|
||||||
|
-- TODO: use symvar if it's an operator
|
||||||
|
fromString = NameVar . T.pack
|
||||||
|
|
||||||
|
data ConId = NameCon Text
|
||||||
|
| SymCon Text
|
||||||
|
deriving (Show, Lift)
|
||||||
|
|
||||||
|
data Pat b = VarP VarId
|
||||||
|
| LitP (Lit b)
|
||||||
|
| ConP ConId [Pat b]
|
||||||
|
deriving (Show, Lift)
|
||||||
|
|
||||||
|
type Pat' = Pat Name
|
||||||
|
|
||||||
|
data Lit b = IntL Int
|
||||||
|
| CharL Char
|
||||||
|
| ListL [RlpExpr b]
|
||||||
|
deriving (Show, Lift)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
30
src/Rlp/TH.hs
Normal file
30
src/Rlp/TH.hs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
module Rlp.TH
|
||||||
|
( rlpProg
|
||||||
|
)
|
||||||
|
where
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
import Language.Haskell.TH
|
||||||
|
import Language.Haskell.TH.Syntax hiding (Module)
|
||||||
|
import Language.Haskell.TH.Quote
|
||||||
|
import Control.Monad ((>=>))
|
||||||
|
import Compiler.RLPC
|
||||||
|
import Data.Default.Class (def)
|
||||||
|
import Data.Text qualified as T
|
||||||
|
import Rlp.Parse
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
rlpProg :: QuasiQuoter
|
||||||
|
rlpProg = QuasiQuoter
|
||||||
|
{ quoteExp = qRlpProg
|
||||||
|
, quotePat = error "rlp quasiquotes may only be used in expressions"
|
||||||
|
, quoteType = error "rlp quasiquotes may only be used in expressions"
|
||||||
|
, quoteDec = error "rlp quasiquotes may only be used in expressions"
|
||||||
|
}
|
||||||
|
|
||||||
|
qRlpProg :: String -> Q Exp
|
||||||
|
qRlpProg s = case parse (T.pack s) of
|
||||||
|
Nothing -> error "error lol iddfk"
|
||||||
|
Just a -> lift a
|
||||||
|
where
|
||||||
|
parse = execP' parseRlpProg
|
||||||
|
|
||||||
44
src/Rlp2Core.hs
Normal file
44
src/Rlp2Core.hs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
module Rlp2Core
|
||||||
|
( rlp2core
|
||||||
|
)
|
||||||
|
where
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
import Core.Syntax as Core
|
||||||
|
import Rlp.Syntax as Rlp
|
||||||
|
import Data.Foldable
|
||||||
|
import Data.HashMap.Strict qualified as H
|
||||||
|
import Control.Monad.State
|
||||||
|
import Lens.Micro.Platform
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
rlp2core :: RlpProgram' -> Program'
|
||||||
|
rlp2core (RlpProgram ds) = execState (decl2core `traverse_` ds) init
|
||||||
|
where
|
||||||
|
init = Program
|
||||||
|
{ _programScDefs = mempty
|
||||||
|
, _programTypeSigs = mempty
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenCoreProg b = State (Program b)
|
||||||
|
|
||||||
|
type GenCoreProg' = GenCoreProg Name
|
||||||
|
|
||||||
|
emitTypeSig :: Name -> Type -> GenCoreProg' ()
|
||||||
|
emitTypeSig b t = do
|
||||||
|
let tl :: Lens' Program' (Maybe Type)
|
||||||
|
tl = programTypeSigs . at b
|
||||||
|
tl <~ (use tl >>= \case
|
||||||
|
-- TODO: non-fatal error
|
||||||
|
Just o -> error "(TODO: non-fatal) duplicate type sigs"
|
||||||
|
Nothing -> pure (Just t)
|
||||||
|
)
|
||||||
|
|
||||||
|
decl2core :: Decl' RlpExpr -> GenCoreProg' ()
|
||||||
|
|
||||||
|
decl2core (DataD n as cs) = undefined
|
||||||
|
|
||||||
|
decl2core (TySigD vs t) = mkSig `traverse_` vs where
|
||||||
|
mkSig :: VarId -> GenCoreProg' ()
|
||||||
|
mkSig (NameVar n) = emitTypeSig n t
|
||||||
|
|
||||||
@@ -6,6 +6,7 @@ module Arith
|
|||||||
) where
|
) where
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
import Data.Functor.Classes (eq1)
|
import Data.Functor.Classes (eq1)
|
||||||
|
import Lens.Micro
|
||||||
import Core.Syntax
|
import Core.Syntax
|
||||||
import GM
|
import GM
|
||||||
import Test.QuickCheck
|
import Test.QuickCheck
|
||||||
@@ -48,7 +49,7 @@ instance Arbitrary ArithExpr where
|
|||||||
-- i don't feel like dealing with division at the moment
|
-- i don't feel like dealing with division at the moment
|
||||||
[ IntA <$> int
|
[ IntA <$> int
|
||||||
, NegateA <$> arbitrary
|
, NegateA <$> arbitrary
|
||||||
-- , IdA <$> arbitrary
|
, IdA <$> arbitrary
|
||||||
, b (:+)
|
, b (:+)
|
||||||
, b (:-)
|
, b (:-)
|
||||||
, b (:*)
|
, b (:*)
|
||||||
@@ -70,13 +71,13 @@ instance Arbitrary ArithExpr where
|
|||||||
-- coreResult = evalCore (toCore e)
|
-- coreResult = evalCore (toCore e)
|
||||||
|
|
||||||
toCore :: ArithExpr -> Program'
|
toCore :: ArithExpr -> Program'
|
||||||
toCore expr = Program
|
toCore expr = mempty & programScDefs .~
|
||||||
[ ScDef "id" ["x"] $ Var "x"
|
[ ScDef "id" ["x"] $ Var "x"
|
||||||
, ScDef "main" [] $ go expr
|
, ScDef "main" [] $ go expr
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
go :: ArithExpr -> Expr'
|
go :: ArithExpr -> Expr'
|
||||||
go (IntA n) = LitE (IntL n)
|
go (IntA n) = Lit (IntL n)
|
||||||
go (NegateA e) = "negate#" :$ go e
|
go (NegateA e) = "negate#" :$ go e
|
||||||
go (IdA e) = "id" :$ go e
|
go (IdA e) = "id" :$ go e
|
||||||
go (a :+ b) = f "+#" a b
|
go (a :+ b) = f "+#" a b
|
||||||
|
|||||||
46
tst/Core/HindleyMilnerSpec.hs
Normal file
46
tst/Core/HindleyMilnerSpec.hs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{-# LANGUAGE QuasiQuotes, OverloadedStrings #-}
|
||||||
|
module Core.HindleyMilnerSpec
|
||||||
|
( spec
|
||||||
|
)
|
||||||
|
where
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
import Core.Syntax
|
||||||
|
import Core.TH (coreExpr)
|
||||||
|
import Core.HindleyMilner
|
||||||
|
import Control.Monad.Errorful
|
||||||
|
import Data.Either (isLeft)
|
||||||
|
import Test.Hspec
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: more tests. preferrably property-based. lol.
|
||||||
|
spec :: Spec
|
||||||
|
spec = do
|
||||||
|
it "should infer `id 3` :: Int" $
|
||||||
|
let g = [ ("id", "a" :-> "a") ]
|
||||||
|
in infer' g [coreExpr|id 3|] `shouldBe` Right TyInt
|
||||||
|
|
||||||
|
it "should not infer `id 3` when `id` is specialised to `a -> a`" $
|
||||||
|
let g = [ ("id", ("a" :-> "a") :-> "a" :-> "a") ]
|
||||||
|
in infer' g [coreExpr|id 3|] `shouldSatisfy` isLeft
|
||||||
|
|
||||||
|
-- TODO: property-based tests for let
|
||||||
|
it "should infer `let x = 3 in id x` :: Int" $
|
||||||
|
let g = [ ("id", "a" :-> "a") ]
|
||||||
|
e = [coreExpr|let {x = 3} in id x|]
|
||||||
|
in infer' g e `shouldBe` Right TyInt
|
||||||
|
|
||||||
|
it "should infer `let x = 3; y = 2 in (+#) x y` :: Int" $
|
||||||
|
let g = [ ("+#", TyInt :-> TyInt :-> TyInt) ]
|
||||||
|
e = [coreExpr|let {x=3;y=2} in (+#) x y|]
|
||||||
|
in infer' g e `shouldBe` Right TyInt
|
||||||
|
|
||||||
|
it "should find `3 :: Bool` contradictory" $
|
||||||
|
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
|
||||||
|
|
||||||
|
check' :: Context' -> Type -> Expr' -> Either TypeError ()
|
||||||
|
check' g t e = fmap fst . runErrorful $ check g t e
|
||||||
|
|
||||||
@@ -36,3 +36,6 @@ spec = do
|
|||||||
it "k 3 ((/#) 1 0)" $ do
|
it "k 3 ((/#) 1 0)" $ do
|
||||||
resultOf Ex.constDivZero `shouldBe` Just (NNum 3)
|
resultOf Ex.constDivZero `shouldBe` Just (NNum 3)
|
||||||
|
|
||||||
|
it "id (case ... of { ... })" $ do
|
||||||
|
resultOf Ex.idCase `shouldBe` Just (NNum 5)
|
||||||
|
|
||||||
|
|||||||
0
tst/Rlp/Parse/DeclsSpec.hs
Normal file
0
tst/Rlp/Parse/DeclsSpec.hs
Normal file
Reference in New Issue
Block a user