1 Commits

Author SHA1 Message Date
crumbtoo
5ce11dfdd7 RlpExpr LetE Rec 2024-01-23 21:24:14 -07:00
20 changed files with 142 additions and 402 deletions

View File

@@ -1,17 +0,0 @@
# unreleased
* New tag syntax:
```hs
case x of
{ 1 -> something
; 2 -> another
}
```
is now written as
```hs
case x of
{ <1> -> something
; <2> -> another
}
```

View File

@@ -30,12 +30,12 @@ $ rlpc -ddump-opts t.hs
### Potential Features ### Potential Features
Listed in order of importance. Listed in order of importance.
- [x] ADTs - [ ] ADTs
- [x] First-class functions - [ ] First-class functions
- [ ] Higher-kinded types - [ ] Higher-kinded types
- [ ] Typeclasses - [ ] Typeclasses
- [x] Parametric polymorphism - [ ] Parametric polymorphism
- [x] Hindley-Milner type inference - [ ] Hindley-Milner type inference
- [ ] Newtype coercion - [ ] Newtype coercion
- [ ] Parallelism - [ ] Parallelism
@@ -66,59 +66,32 @@ Listed in order of importance.
- [ ] TCO - [ ] TCO
- [ ] DCE - [ ] DCE
- [ ] Frontend - [ ] Frontend
- [x] High-level language - [ ] High-level language
- [x] AST - [ ] AST
- [x] Lexer - [ ] Lexer
- [x] Parser - [ ] Parser
- [ ] Translation to the core language - [ ] Translation to the core language
- [ ] Constraint solver - [ ] Constraint solver
- [ ] `do`-notation - [ ] `do`-notation
- [x] CLI - [x] CLI
- [ ] Documentation - [ ] Documentation
- [x] State transition rules - [ ] State transition rules
- [ ] How does the evaluation model work? - [ ] How does the evaluation model work?
- [ ] The Hindley-Milner type system
- [ ] CLI usage - [ ] CLI usage
- [ ] Tail call optimisation - [ ] Tail call optimisation
- [ ] Parsing rlp - [x] Parsing rlp
- [ ] Tests - [ ] Tests
- [x] Generic example programs - [x] Generic example programs
- [ ] Parser - [ ] Parser
### ~~December Release Plan~~ ### December Release Plan
- [x] Tests - [ ] Tests
- [ ] Core lexer - [ ] Core lexer
- [ ] Core parser - [ ] Core parser
- [x] Evaluation model - [ ] Evaluation model
- [ ] Benchmarks - [ ] Benchmarks
- [x] Stable Core lexer - [ ] Stable Core lexer
- [x] Stable Core parser - [ ] Stable Core parser
- [x] Stable evaluation model - [ ] Stable evaluation model
- [x] Garbage Collection - [ ] Garbage Collection
- [ ] Stable documentation for the evaluation model - [ ] Stable documentation for the evaluation model
### January Release Plan
- [ ] Beta rl' to Core
- [ ] UX improvements
- [ ] Actual compiler errors -- no more unexceptional `error` calls
- [ ] Better CLI dump flags
- [ ] Annotate the AST with token positions for errors
### March Release Plan
- [ ] Tests
- [ ] rl' parser
- [ ] rl' lexer
### Indefinite Release Plan
This list is more concrete than the milestones, but likely further in the future
than the other release plans.
- [ ] Stable rl' to Core
- [ ] Core polish
- [ ] Better, stable parser
- [ ] Better, stable lexer
- [ ] Less hacky handling of named data
- [ ] Less hacky pragmas
- [ ] GM to LLVM

View File

@@ -112,3 +112,5 @@ The way around this is quite simple: simply offset the stack when w
:end-before: -- << [ref/compileC] :end-before: -- << [ref/compileC]
:caption: src/GM.hs :caption: src/GM.hs

View File

@@ -2,21 +2,16 @@ Lexing, Parsing, and Layouts
============================ ============================
The C-style languages of my previous experiences have all had quite trivial The C-style languages of my previous experiences have all had quite trivial
lexical analysis stages: you ignore all whitespace and point out the symbols you lexical analysis stages, peaking in complexity when I streamed tokens lazily in
recognise. If you don't recognise something, check if it's a literal or an C. The task of tokenising a C-style language is very simple in description: you
identifier. Should it be neither, return an error. ignore all whitespace and point out what you recognise. If you don't recognise
something, check if it's a literal or an identifier. Should it be neither,
return an error.
In contrast, both lexing and parsing a Haskell-like language poses a number of On paper, both lexing and parsing a Haskell-like language seem to pose a few
greater challenges. Listed by ascending intimidation factor, some of the greater challenges. Listed by ascending intimidation factor, some of the
potential roadblocks on my mind before making an attempt were: potential roadblocks on my mind before making an attempt were:
* Context-sensitive keywords; Haskell allows for some words to be used as
identifiers in appropriate contexts, such as :code:`family`, :code:`role`,
:code:`as`. Reading a note_ found in `GHC's lexer`_, it appears that keywords
are only considered in bodies for which their use is relevant, e.g.
:code:`family` and :code:`role` in type declarations, :code:`as` after
:code:`case`; :code:`if`, :code:`then`, and :code:`else` in expressions, etc.
* Operators; Haskell has not only user-defined infix operators, but user-defined * Operators; Haskell has not only user-defined infix operators, but user-defined
precedence levels and associativities. I recall using an algorithm that looked precedence levels and associativities. I recall using an algorithm that looked
up infix, prefix, postfix, and even mixfix operators up in a global table to up infix, prefix, postfix, and even mixfix operators up in a global table to
@@ -24,9 +19,17 @@ potential roadblocks on my mind before making an attempt were:
stored in the table). I never modified the table at runtime, however this stored in the table). I never modified the table at runtime, however this
could be a very nice solution for Haskell. could be a very nice solution for Haskell.
* Context-sensitive keywords; Haskell allows for some words to be used as identifiers in
appropriate contexts, such as :code:`family`, :code:`role`, :code:`as`.
Reading a note_ found in `GHC's lexer`_,
it appears that keywords are only considered in bodies for which their use is
relevant, e.g. :code:`family` and :code:`role` in type declarations,
:code:`as` after :code:`case`; :code:`if`, :code:`then`, and :code:`else` in
expressions, etc.
* Whitespace sensitivity; While I was comfortable with the idea of a system * Whitespace sensitivity; While I was comfortable with the idea of a system
similar to Python's INDENT/DEDENT tokens, Haskell's layout system is based on similar to Python's INDENT/DEDENT tokens, Haskell seemed to use whitespace to
alignment and is very generous with line-folding. section code in a way that *felt* different.
.. _note: https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/coding-style#2-using-notes .. _note: https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/coding-style#2-using-notes
.. _GHC's lexer: https://gitlab.haskell.org/ghc/ghc/-/blob/master/compiler/GHC/Parser/Lexer.x#L1133 .. _GHC's lexer: https://gitlab.haskell.org/ghc/ghc/-/blob/master/compiler/GHC/Parser/Lexer.x#L1133
@@ -42,9 +45,9 @@ We will compare and contrast with Python's lexical analysis. Much to my dismay,
Python uses newlines and indentation to separate statements and resolve scope Python uses newlines and indentation to separate statements and resolve scope
instead of the traditional semicolons and braces found in C-style languages (we instead of the traditional semicolons and braces found in C-style languages (we
may generally refer to these C-style languages as *explicitly-sectioned*). may generally refer to these C-style languages as *explicitly-sectioned*).
Internally during tokenisation, when the Python lexer encounters a new line, the Internally during tokenisation, when the Python lexer begins a new line, they
indentation of the new line is compared with that of the previous and the compare the indentation of the new line with that of the previous and apply the
following rules are applied: following rules:
1. If the new line has greater indentation than the previous, insert an INDENT 1. If the new line has greater indentation than the previous, insert an INDENT
token and push the new line's indentation level onto the indentation stack token and push the new line's indentation level onto the indentation stack
@@ -57,37 +60,44 @@ following rules are applied:
3. If the indentation is equal, insert a NEWLINE token to terminate the previous 3. If the indentation is equal, insert a NEWLINE token to terminate the previous
line, and leave it at that! line, and leave it at that!
On the parser's end, the INDENT, DEDENT, and NEWLINE tokens are identical to Parsing Python with the INDENT, DEDENT, and NEWLINE tokens is identical to
braces and semicolons. In developing our *layout* rules, we will follow in the parsing a language with braces and semicolons. This is a solution pretty in line
pattern of translating the whitespace-sensitive source language to an explicitly with Python's philosophy of the "one correct answer" (TODO: this needs a
sectioned language. source). In developing our *layout* rules, we will follow in the pattern of
translating the whitespace-sensitive source language to an explicitly sectioned
language.
But What About Haskell? But What About Haskell?
*********************** ***********************
Parsing Haskell -- and thus rl' -- is only slightly more complex than Python, We saw that Python, the most notable example of an implicitly sectioned
but the design is certainly more sensitive. language, is pretty simple to lex. Why then am I so afraid of Haskell's layouts?
To be frank, I'm far less scared after asking myself this -- however there are
certainly some new complexities that Python needn't concern. Haskell has
implicit line *continuation*: forms written over multiple lines; indentation
styles often seen in Haskell are somewhat esoteric compared to Python's
"s/[{};]//".
.. code-block:: haskell .. code-block:: haskell
-- line folds -- line continuation
something = this is a something = this is a
single expression single expression
-- an extremely common style found in haskell -- an extremely common style found in haskell
data Some = Data data Python = Users
{ is :: Presented { are :: Crying
, in :: This , right :: About
, silly :: Style , now :: Sorry
} }
-- another style oddity -- another formatting oddity
-- note that this is not a single -- note that this is not a single
-- continued line! `look at`, -- continued line! `look at`,
-- `this odd`, and `alignment` are all -- `this`, and `alignment` are all
-- discrete items! -- separate expressions!
anotherThing = do look at anotherThing = do look at
this odd this
alignment alignment
But enough fear, lets actually think about implementation. Firstly, some But enough fear, lets actually think about implementation. Firstly, some
@@ -223,4 +233,3 @@ References
* `Haskell syntax reference * `Haskell syntax reference
<https://www.haskell.org/onlinereport/haskell2010/haskellch10.html>`_ <https://www.haskell.org/onlinereport/haskell2010/haskellch10.html>`_

View File

@@ -1,5 +0,0 @@
Type Inference in rl'
=====================
rl' implements type inference via the Hindley-Milner type system.

View File

@@ -1,17 +0,0 @@
rl' Inference Rules
===================
.. rubric::
[Var]
.. math::
\frac{x : \tau \in \Gamma}
{\Gamma \vdash x : \tau}
.. rubric::
[App]
.. math::
\frac{\Gamma \vdash f : \alpha \to \beta \qquad \Gamma \vdash x : \alpha}
{\Gamma \vdash f x : \beta}

View File

@@ -1,6 +1,6 @@
fac n = case (==#) n 0 of fac n = case (==#) n 0 of
{ <1> -> 1 { 1 -> 1
; <0> -> (*#) n (fac ((-#) n 1)) ; 0 -> (*#) n (fac ((-#) n 1))
}; };
main = fac 3; main = fac 3;

View File

@@ -2,8 +2,8 @@ nil = Pack{0 0};
cons x y = Pack{1 2} x y; cons x y = Pack{1 2} x y;
list = cons 1 (cons 2 (cons 3 nil)); list = cons 1 (cons 2 (cons 3 nil));
sum l = case l of sum l = case l of
{ <0> -> 0 { 0 -> 0
; <1> x xs -> (+#) x (sum xs) ; 1 x xs -> (+#) x (sum xs)
}; };
main = sum list; main = sum list;

View File

@@ -1,105 +0,0 @@
Programming Language Checklist
by Colin McMillen, Jason Reed, and Elly Fong-Jones, 2011-10-10.
You appear to be advocating a new:
[x] functional [ ] imperative [ ] object-oriented [ ] procedural [ ] stack-based
[ ] "multi-paradigm" [x] lazy [ ] eager [x] statically-typed [ ] dynamically-typed
[x] pure [ ] impure [ ] non-hygienic [ ] visual [x] beginner-friendly
[ ] non-programmer-friendly [ ] completely incomprehensible
programming language. Your language will not work. Here is why it will not work.
You appear to believe that:
[ ] Syntax is what makes programming difficult
[x] Garbage collection is free [x] Computers have infinite memory
[x] Nobody really needs:
[x] concurrency [x] a REPL [x] debugger support [x] IDE support [x] I/O
[x] to interact with code not written in your language
[ ] The entire world speaks 7-bit ASCII
[ ] Scaling up to large software projects will be easy
[ ] Convincing programmers to adopt a new language will be easy
[ ] Convincing programmers to adopt a language-specific IDE will be easy
[ ] Programmers love writing lots of boilerplate
[ ] Specifying behaviors as "undefined" means that programmers won't rely on them
[ ] "Spooky action at a distance" makes programming more fun
Unfortunately, your language (has/lacks):
[x] comprehensible syntax [ ] semicolons [x] significant whitespace [ ] macros
[ ] implicit type conversion [ ] explicit casting [x] type inference
[ ] goto [ ] exceptions [x] closures [x] tail recursion [ ] coroutines
[ ] reflection [ ] subtyping [ ] multiple inheritance [x] operator overloading
[x] algebraic datatypes [x] recursive types [x] polymorphic types
[ ] covariant array typing [x] monads [ ] dependent types
[x] infix operators [x] nested comments [ ] multi-line strings [ ] regexes
[ ] call-by-value [x] call-by-name [ ] call-by-reference [ ] call-cc
The following philosophical objections apply:
[ ] Programmers should not need to understand category theory to write "Hello, World!"
[ ] Programmers should not develop RSI from writing "Hello, World!"
[ ] The most significant program written in your language is its own compiler
[x] The most significant program written in your language isn't even its own compiler
[x] No language spec
[x] "The implementation is the spec"
[ ] The implementation is closed-source [ ] covered by patents [ ] not owned by you
[ ] Your type system is unsound [ ] Your language cannot be unambiguously parsed
[ ] a proof of same is attached
[ ] invoking this proof crashes the compiler
[x] The name of your language makes it impossible to find on Google
[x] Interpreted languages will never be as fast as C
[ ] Compiled languages will never be "extensible"
[ ] Writing a compiler that understands English is AI-complete
[ ] Your language relies on an optimization which has never been shown possible
[ ] There are less than 100 programmers on Earth smart enough to use your language
[ ] ____________________________ takes exponential time
[ ] ____________________________ is known to be undecidable
Your implementation has the following flaws:
[ ] CPUs do not work that way
[ ] RAM does not work that way
[ ] VMs do not work that way
[ ] Compilers do not work that way
[ ] Compilers cannot work that way
[ ] Shift-reduce conflicts in parsing seem to be resolved using rand()
[ ] You require the compiler to be present at runtime
[ ] You require the language runtime to be present at compile-time
[ ] Your compiler errors are completely inscrutable
[ ] Dangerous behavior is only a warning
[ ] The compiler crashes if you look at it funny
[x] The VM crashes if you look at it funny
[x] You don't seem to understand basic optimization techniques
[x] You don't seem to understand basic systems programming
[ ] You don't seem to understand pointers
[ ] You don't seem to understand functions
Additionally, your marketing has the following problems:
[x] Unsupported claims of increased productivity
[x] Unsupported claims of greater "ease of use"
[ ] Obviously rigged benchmarks
[ ] Graphics, simulation, or crypto benchmarks where your code just calls
handwritten assembly through your FFI
[ ] String-processing benchmarks where you just call PCRE
[ ] Matrix-math benchmarks where you just call BLAS
[x] Noone really believes that your language is faster than:
[x] assembly [x] C [x] FORTRAN [x] Java [x] Ruby [ ] Prolog
[ ] Rejection of orthodox programming-language theory without justification
[x] Rejection of orthodox systems programming without justification
[ ] Rejection of orthodox algorithmic theory without justification
[ ] Rejection of basic computer science without justification
Taking the wider ecosystem into account, I would like to note that:
[x] Your complex sample code would be one line in: examples/
[ ] We already have an unsafe imperative language
[ ] We already have a safe imperative OO language
[x] We already have a safe statically-typed eager functional language
[ ] You have reinvented Lisp but worse
[ ] You have reinvented Javascript but worse
[ ] You have reinvented Java but worse
[ ] You have reinvented C++ but worse
[ ] You have reinvented PHP but worse
[ ] You have reinvented PHP better, but that's still no justification
[ ] You have reinvented Brainfuck but non-ironically
In conclusion, this is what I think of you:
[ ] You have some interesting ideas, but this won't fly.
[x] This is a bad language, and you should feel bad for inventing it.
[ ] Programming in this language is an adequate punishment for inventing it.

View File

@@ -4,7 +4,12 @@ Description : Core examples (may eventually be unit tests)
-} -}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module Core.Examples where module Core.Examples
( fac3
, sumList
, constDivZero
, idCase
) where
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
import Core.Syntax import Core.Syntax
import Core.TH import Core.TH
@@ -142,8 +147,8 @@ simple1 = [coreProg|
caseBool1 :: Program' caseBool1 :: Program'
caseBool1 = [coreProg| caseBool1 = [coreProg|
_if c x y = case c of _if c x y = case c of
{ <1> -> x { 1 -> x
; <0> -> y ; 0 -> y
}; };
false = Pack{0 0}; false = Pack{0 0};
@@ -155,8 +160,8 @@ caseBool1 = [coreProg|
fac3 :: Program' fac3 :: Program'
fac3 = [coreProg| fac3 = [coreProg|
fac n = case (==#) n 0 of fac n = case (==#) n 0 of
{ <1> -> 1 { 1 -> 1
; <0> -> (*#) n (fac ((-#) n 1)) ; 0 -> (*#) n (fac ((-#) n 1))
}; };
main = fac 3; main = fac 3;
@@ -170,8 +175,8 @@ sumList = [coreProg|
cons x y = Pack{1 2} x y; cons x y = Pack{1 2} x y;
list = cons 1 (cons 2 (cons 3 nil)); list = cons 1 (cons 2 (cons 3 nil));
sum l = case l of sum l = case l of
{ <0> -> 0 { 0 -> 0
; <1> x xs -> (+#) x (sum xs) ; 1 x xs -> (+#) x (sum xs)
}; };
main = sum list; main = sum list;
|] |]
@@ -187,36 +192,10 @@ idCase = [coreProg|
id x = x; id x = x;
main = id (case Pack{1 0} of main = id (case Pack{1 0} of
{ <1> -> (+#) 2 3 { 1 -> (+#) 2 3
}) })
|] |]
-- NOTE: the GM primitive (==#) returns an untyped constructor with tag 1 for
-- true, and 0 for false. See: GM.boxBool
namedBoolCase :: Program'
namedBoolCase = [coreProg|
{-# PackData True 1 0 #-}
{-# PackData False 0 0 #-}
main = case (==#) 1 1 of
{ True -> 123
; False -> 456
}
|]
namedConsCase :: Program'
namedConsCase = [coreProg|
{-# PackData Nil 0 0 #-}
{-# PackData Cons 1 2 #-}
Nil = Pack{0 0};
Cons = Pack{1 2};
foldr f z l = case l of
{ Nil -> z
; Cons x xs -> f x (foldr f z xs)
};
list = Cons 1 (Cons 2 (Cons 3 Nil));
main = foldr (+#) 0 list
|]
-- corePrelude :: Module Name -- corePrelude :: Module Name
-- corePrelude = Module (Just ("Prelude", [])) $ -- corePrelude = Module (Just ("Prelude", [])) $
-- -- non-primitive defs -- -- non-primitive defs

View File

@@ -55,14 +55,11 @@ instance IsRlpcError TypeError where
liftRlpcError = \case liftRlpcError = \case
-- todo: use anti-parser instead of show -- todo: use anti-parser instead of show
TyErrCouldNotUnify t u -> Text TyErrCouldNotUnify t u -> Text
[ T.pack $ printf "Could not match type `%s` with `%s`." [ T.pack $ printf "Could not match type `%s' with `%s'."
(show t) (show u) (show t) (show u)
, "Expected: " <> tshow t , "Expected: " <> tshow t
, "Got: " <> tshow u , "Got: " <> tshow u
] ]
TyErrUntypedVariable n -> Text
[ "Untyped (likely undefined) variable `" <> n <> "`"
]
TyErrRecursiveType t x -> Text TyErrRecursiveType t x -> Text
[ T.pack $ printf "recursive type error lol" [ T.pack $ printf "recursive type error lol"
] ]
@@ -160,12 +157,7 @@ gather = \g e -> runStateT (go g e) ([],0) <&> \ (t,(cs,_)) -> (t,cs) where
Let Rec bs e -> do Let Rec bs e -> do
g' <- buildLetrecContext g bs g' <- buildLetrecContext g bs
go g' e go g' e
Lam bs e -> case bs of
[x] -> do
tx <- uniqueVar
let g' = (x,tx) : g
te <- go g' e
pure (tx :-> te)
-- TODO lambda, case -- TODO lambda, case
buildLetrecContext :: Context' -> [Binding'] buildLetrecContext :: Context' -> [Binding']

View File

@@ -65,8 +65,6 @@ $white_no_nl = $white # $nl
@decimal = $digit+ @decimal = $digit+
@alttag = "<" $digit+ ">"
rlp :- rlp :-
<0> <0>
@@ -94,8 +92,6 @@ rlp :-
"=" { constTok TokenEquals } "=" { constTok TokenEquals }
"->" { constTok TokenArrow } "->" { constTok TokenArrow }
@alttag { lexWith ( TokenAltTag . read @Int . T.unpack
. T.drop 1 . T.init ) }
@varname { lexWith TokenVarName } @varname { lexWith TokenVarName }
@conname { lexWith TokenConName } @conname { lexWith TokenConName }
@varsym { lexWith TokenVarSym } @varsym { lexWith TokenVarSym }
@@ -139,7 +135,6 @@ data CoreToken = TokenLet
| TokenConName Name | TokenConName Name
| TokenVarSym Name | TokenVarSym Name
| TokenConSym Name | TokenConSym Name
| TokenAltTag Tag
| TokenEquals | TokenEquals
| TokenLParen | TokenLParen
| TokenRParen | TokenRParen

View File

@@ -3,7 +3,7 @@
Module : Core.Parse Module : Core.Parse
Description : Parser for the Core language Description : Parser for the Core language
-} -}
{-# LANGUAGE OverloadedStrings, ViewPatterns #-} {-# LANGUAGE OverloadedStrings #-}
module Core.Parse module Core.Parse
( parseCore ( parseCore
, parseCoreExpr , parseCoreExpr
@@ -23,9 +23,7 @@ import Compiler.RLPC
import Lens.Micro import Lens.Micro
import Data.Default.Class (def) import Data.Default.Class (def)
import Data.Hashable (Hashable) import Data.Hashable (Hashable)
import Data.List.Extra
import Data.Text.IO qualified as TIO import Data.Text.IO qualified as TIO
import Data.Text (Text)
import Data.Text qualified as T import Data.Text qualified as T
import Data.HashMap.Strict qualified as H import Data.HashMap.Strict qualified as H
} }
@@ -51,7 +49,6 @@ import Data.HashMap.Strict qualified as H
varsym { Located _ _ _ (TokenVarSym $$) } varsym { Located _ _ _ (TokenVarSym $$) }
conname { Located _ _ _ (TokenConName $$) } conname { Located _ _ _ (TokenConName $$) }
consym { Located _ _ _ (TokenConSym $$) } consym { Located _ _ _ (TokenConSym $$) }
alttag { Located _ _ _ (TokenAltTag $$) }
word { Located _ _ _ (TokenWord $$) } word { Located _ _ _ (TokenWord $$) }
'λ' { Located _ _ _ TokenLambda } 'λ' { Located _ _ _ TokenLambda }
'->' { Located _ _ _ TokenArrow } '->' { Located _ _ _ TokenArrow }
@@ -85,15 +82,6 @@ Program : ScTypeSig ';' Program { insTypeSig $1 $3 }
| ScTypeSig OptSemi { singletonTypeSig $1 } | ScTypeSig OptSemi { singletonTypeSig $1 }
| ScDef ';' Program { insScDef $1 $3 } | ScDef ';' Program { insScDef $1 $3 }
| ScDef OptSemi { singletonScDef $1 } | ScDef OptSemi { singletonScDef $1 }
| TLPragma Program {% doTLPragma $1 $2 }
| TLPragma {% doTLPragma $1 mempty }
TLPragma :: { Pragma }
: '{-#' Words '#-}' { Pragma $2 }
Words :: { [Text] }
: Words word { $1 `snoc` $2 }
| word { [$1] }
OptSemi :: { () } OptSemi :: { () }
OptSemi : ';' { () } OptSemi : ';' { () }
@@ -106,6 +94,7 @@ ScDefs :: { [ScDef Name] }
ScDefs : ScDef ';' ScDefs { $1 : $3 } ScDefs : ScDef ';' ScDefs { $1 : $3 }
| ScDef ';' { [$1] } | ScDef ';' { [$1] }
| ScDef { [$1] } | ScDef { [$1] }
| {- epsilon -} { [] }
ScDef :: { ScDef Name } ScDef :: { ScDef Name }
ScDef : Var ParList '=' Expr { ScDef $1 $2 $4 } ScDef : Var ParList '=' Expr { ScDef $1 $2 $4 }
@@ -160,15 +149,22 @@ Alters : Alter ';' Alters { $1 : $3 }
| Alter { [$1] } | Alter { [$1] }
Alter :: { Alter Name } Alter :: { Alter Name }
Alter : alttag ParList '->' Expr { Alter (AltTag $1) $2 $4 } Alter : litint ParList '->' Expr { Alter (AltData $1) $2 $4 }
| Con ParList '->' Expr { Alter (AltData $1) $2 $4 }
Expr1 :: { Expr Name } Expr1 :: { Expr Name }
Expr1 : litint { Lit $ IntL $1 } Expr1 : litint { Lit $ IntL $1 }
| Id { Var $1 } | Id { Var $1 }
| PackCon { $1 } | PackCon { $1 }
| ExprPragma { $1 }
| '(' Expr ')' { $2 } | '(' Expr ')' { $2 }
ExprPragma :: { Expr Name }
ExprPragma : '{-#' Words '#-}' {% exprPragma $2 }
Words :: { [String] }
Words : word Words { T.unpack $1 : $2 }
| word { [T.unpack $1] }
PackCon :: { Expr Name } PackCon :: { Expr Name }
PackCon : pack '{' litint litint '}' { Con $3 $4 } PackCon : pack '{' litint litint '}' { Con $3 $4 }
@@ -233,17 +229,5 @@ happyBind m k = m >>= k
happyPure :: a -> RLPC a happyPure :: a -> RLPC a
happyPure a = pure a happyPure a = pure a
doTLPragma :: Pragma -> Program' -> RLPC Program'
-- TODO: warn unrecognised pragma
doTLPragma (Pragma []) p = pure p
doTLPragma (Pragma pr) p = case pr of
-- TODO: warn on overwrite
["PackData", n, readt -> t, readt -> a] ->
pure $ p & programDataTags . at n ?~ (t,a)
readt :: (Read a) => Text -> a
readt = read . T.unpack
} }

View File

@@ -5,14 +5,8 @@ Description : Core ASTs and the like
{-# LANGUAGE PatternSynonyms, OverloadedStrings #-} {-# LANGUAGE PatternSynonyms, OverloadedStrings #-}
{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DerivingStrategies, DerivingVia #-}
-- for recursion-schemes
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable
, TemplateHaskell, TypeFamilies #-}
module Core.Syntax module Core.Syntax
( Expr(..) ( Expr(..)
, ExprF(..)
, ExprF'(..)
, Type(..) , Type(..)
, pattern TyInt , pattern TyInt
, Lit(..) , Lit(..)
@@ -30,11 +24,9 @@ module Core.Syntax
, Module(..) , Module(..)
, Program(..) , Program(..)
, Program' , Program'
, Pragma(..)
, unliftScDef , unliftScDef
, programScDefs , programScDefs
, programTypeSigs , programTypeSigs
, programDataTags
, Expr' , Expr'
, ScDef' , ScDef'
, Alter' , Alter'
@@ -48,15 +40,11 @@ import Data.Coerce
import Data.Pretty import Data.Pretty
import Data.List (intersperse) import Data.List (intersperse)
import Data.Function ((&)) import Data.Function ((&))
import Data.Functor.Foldable
import Data.Functor.Foldable.TH (makeBaseFunctor)
import Data.String import Data.String
import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as H import Data.HashMap.Strict qualified as H
import Data.Hashable import Data.Hashable
import Data.Text qualified as T import Data.Text qualified as T
import Data.Char import Data.Char
import GHC.Generics
-- 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.TH (makeLenses)
@@ -111,14 +99,11 @@ data Alter b = Alter AltCon [b] (Expr b)
deriving instance (Eq b) => Eq (Alter b) deriving instance (Eq b) => Eq (Alter b)
newtype Pragma = Pragma [T.Text]
data Rec = Rec data Rec = Rec
| NonRec | NonRec
deriving (Show, Read, Eq, Lift) deriving (Show, Read, Eq, Lift)
data AltCon = AltData Name data AltCon = AltData Tag
| AltTag Tag
| AltLit Lit | AltLit Lit
| Default | Default
deriving (Show, Read, Eq, Lift) deriving (Show, Read, Eq, Lift)
@@ -140,20 +125,13 @@ data Module b = Module (Maybe (Name, [Name])) (Program b)
data Program b = Program data Program b = Program
{ _programScDefs :: [ScDef b] { _programScDefs :: [ScDef b]
, _programTypeSigs :: HashMap b Type , _programTypeSigs :: H.HashMap b Type
-- map constructors to their tag and arity
, _programDataTags :: HashMap b (Tag, Int)
} }
deriving (Show, Lift, Generic) deriving (Show, Lift)
deriving (Semigroup, Monoid)
via Generically (Program b)
makeLenses ''Program makeLenses ''Program
makeBaseFunctor ''Expr
pure [] pure []
type ExprF' = ExprF Name
type Program' = Program Name type Program' = Program Name
type Expr' = Expr Name type Expr' = Expr Name
type ScDef' = ScDef Name type ScDef' = ScDef Name
@@ -170,6 +148,12 @@ instance IsString Type where
| otherwise = TyVar . fromString $ s | otherwise = TyVar . fromString $ s
where (c:_) = s where (c:_) = s
instance (Hashable b) => Semigroup (Program b) where
(<>) = undefined
instance (Hashable b) => Monoid (Program b) where
mempty = Program mempty mempty
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
class HasRHS s t a b | s -> a, t -> b, s b -> t, t a -> s where class HasRHS s t a b | s -> a, t -> b, s b -> t, t a -> s where
@@ -203,8 +187,3 @@ instance HasLHS (ScDef b) (ScDef b) (b, [b]) (b, [b]) where
(\ (ScDef n as _) -> (n,as)) (\ (ScDef n as _) -> (n,as))
(\ (ScDef _ _ e) (n',as') -> (ScDef n' as' e)) (\ (ScDef _ _ e) (n',as') -> (ScDef n' as' e))
instance HasLHS (Binding b) (Binding b) b b where
_lhs = lens
(\ (k := _) -> k)
(\ (_ := e) k' -> k' := e)

View File

@@ -1,10 +1,16 @@
-- for recursion schemes
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
-- for recursion schemes
{-# LANGUAGE TemplateHaskell, TypeFamilies #-}
module Core.Utils module Core.Utils
( programRhss ( bindersOf
, programGlobals , rhssOf
, isAtomic , isAtomic
-- , insertModule -- , insertModule
, extractProgram , extractProgram
, freeVariables , freeVariables
, ExprF(..)
) )
where where
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
@@ -17,11 +23,13 @@ import Lens.Micro
import GHC.Exts (IsList(..)) import GHC.Exts (IsList(..))
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
programGlobals :: Traversal' (Program b) b bindersOf :: (IsList l, Item l ~ b) => [Binding b] -> l
programGlobals = programScDefs . each . _lhs . _1 bindersOf bs = fromList $ fmap f bs
where f (k := _) = k
programRhss :: Traversal' (Program b) (Expr b) rhssOf :: (IsList l, Item l ~ Expr b) => [Binding b] -> l
programRhss = programScDefs . each . _rhs rhssOf = fromList . fmap f
where f (_ := v) = v
isAtomic :: Expr b -> Bool isAtomic :: Expr b -> Bool
isAtomic (Var _) = True isAtomic (Var _) = True
@@ -39,6 +47,8 @@ extractProgram (Module _ p) = p
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
makeBaseFunctor ''Expr
freeVariables :: Expr' -> Set Name freeVariables :: Expr' -> Set Name
freeVariables = cata go freeVariables = cata go
where where
@@ -47,8 +57,8 @@ freeVariables = cata go
-- TODO: collect free vars in rhss of bs -- TODO: collect free vars in rhss of bs
go (LetF _ bs e) = (e `S.union` esFree) `S.difference` ns go (LetF _ bs e) = (e `S.union` esFree) `S.difference` ns
where where
es = bs ^.. each . _rhs :: [Expr'] es = rhssOf bs :: [Expr']
ns = S.fromList $ bs ^.. each . _lhs ns = bindersOf bs
-- TODO: this feels a little wrong. maybe a different scheme is -- TODO: this feels a little wrong. maybe a different scheme is
-- appropriate -- appropriate
esFree = foldMap id $ freeVariables <$> es esFree = foldMap id $ freeVariables <$> es

View File

@@ -1,4 +1,3 @@
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-}
module Core2Core module Core2Core
( core2core ( core2core
@@ -16,12 +15,11 @@ import Data.Set (Set)
import Data.Set qualified as S import Data.Set qualified as S
import Data.List import Data.List
import Control.Monad.Writer import Control.Monad.Writer
import Control.Monad.State.Lazy import Control.Monad.State
import Control.Arrow ((>>>)) import Control.Arrow ((>>>))
import Data.Text qualified as T import Data.Text qualified as T
import Data.HashMap.Strict (HashMap)
import Numeric (showHex) import Numeric (showHex)
import Lens.Micro.Platform import Lens.Micro
import Core.Syntax import Core.Syntax
import Core.Utils import Core.Utils
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
@@ -30,35 +28,22 @@ core2core :: Program' -> Program'
core2core p = undefined core2core p = undefined
gmPrep :: Program' -> Program' gmPrep :: Program' -> Program'
gmPrep p = p & appFloater (floatNonStrictCases globals) gmPrep p = p' & programScDefs %~ (<>caseScs)
& tagData
where where
rhss :: Applicative f => (Expr z -> f (Expr z)) -> Program z -> f (Program z)
rhss = programScDefs . each . _rhs
globals = p ^.. programScDefs . each . _lhs . _1 globals = p ^.. programScDefs . each . _lhs . _1
& S.fromList & S.fromList
tagData :: Program' -> Program' -- i kinda don't like that we're calling floatNonStrictCases twice tbh
tagData p = let ?dt = p ^. programDataTags p' = p & rhss %~ fst . runFloater . floatNonStrictCases globals
in p & programRhss %~ cata go where caseScs = (p ^.. rhss)
go :: (?dt :: HashMap Name (Tag, Int)) => ExprF' Expr' -> Expr' <&> snd . runFloater . floatNonStrictCases globals
go (CaseF e as) = Case e (tagAlts <$> as) & mconcat
go x = embed x
tagAlts :: (?dt :: HashMap Name (Tag, Int)) => Alter' -> Alter'
tagAlts (Alter (AltData c) bs e) = Alter (AltTag tag) bs e
where tag = case ?dt ^. at c of
Just (t,_) -> t
-- TODO: errorful
Nothing -> error $ "unknown constructor " <> show c
tagAlts x = x
-- | Auxilary type used in @floatNonSrictCases@ -- | Auxilary type used in @floatNonSrictCases@
type Floater = StateT [Name] (Writer [ScDef']) type Floater = StateT [Name] (Writer [ScDef'])
appFloater :: (Expr' -> Floater Expr') -> Program' -> Program'
appFloater fl p = p & traverseOf programRhss fl
& runFloater
& \ (me,floats) -> me & programScDefs %~ (<>floats)
runFloater :: Floater a -> (a, [ScDef']) runFloater :: Floater a -> (a, [ScDef'])
runFloater = flip evalStateT ns >>> runWriter runFloater = flip evalStateT ns >>> runWriter
where where

View File

@@ -661,8 +661,7 @@ buildInitialHeap (view programScDefs -> ss) = mapAccumL allocateSc mempty compil
compileC _ (Con t n) = [PushConstr t n] compileC _ (Con t n) = [PushConstr t n]
compileC _ (Case _ _) = compileC _ (Case _ _) =
error "GM compiler found a non-strict case expression, which should\ error "case expressions may not appear in non-strict contexts :/"
\ have been floated by Core2Core.gmPrep. This is a bug!"
compileC _ _ = error "yet to be implemented!" compileC _ _ = error "yet to be implemented!"
@@ -725,16 +724,12 @@ buildInitialHeap (view programScDefs -> ss) = mapAccumL allocateSc mempty compil
compileD g as = fmap (compileA g) as compileD g as = fmap (compileA g) as
compileA :: Env -> Alter' -> (Tag, Code) compileA :: Env -> Alter' -> (Tag, Code)
compileA g (Alter (AltTag t) as e) = (t, [Split n] <> c <> [Slide n]) compileA g (Alter (AltData t) as e) = (t, [Split n] <> c <> [Slide n])
where where
n = length as n = length as
binds = (NameKey <$> as) `zip` [0..] binds = (NameKey <$> as) `zip` [0..]
g' = binds ++ argOffset n g g' = binds ++ argOffset n g
c = compileE g' e c = compileE g' e
compileA _ (Alter _ as e) = error "GM.compileA found an untagged\
\ constructor, which should have\
\ been handled by Core2Core.gmPrep.\
\ This is a bug!"
inlineOp1 :: Env -> Instr -> Expr' -> Code inlineOp1 :: Env -> Instr -> Expr' -> Code
inlineOp1 g i a = compileE g a <> [i] inlineOp1 g i a = compileE g a <> [i]

View File

@@ -82,7 +82,7 @@ data Assoc = InfixL
data ConAlt = ConAlt ConId [Type] data ConAlt = ConAlt ConId [Type]
deriving Show deriving Show
data RlpExpr b = LetE [Bind b] (RlpExpr b) data RlpExpr b = LetE Rec [Bind b] (RlpExpr b)
| VarE VarId | VarE VarId
| ConE ConId | ConE ConId
| LamE [Pat b] (RlpExpr b) | LamE [Pat b] (RlpExpr b)
@@ -150,7 +150,7 @@ type RlpExprF' = RlpExprF Name
-- society if derivable Show1 -- society if derivable Show1
instance (Show b) => Show1 (RlpExprF b) where instance (Show b) => Show1 (RlpExprF b) where
liftShowsPrec sp _ p m = case m of liftShowsPrec sp _ p m = case m of
(LetEF bs e) -> showsBinaryWith showsPrec sp "LetEF" p bs e (LetEF r bs e) -> showsTernaryWith showsPrec showsPrec sp "LetEF" p r bs e
(VarEF n) -> showsUnaryWith showsPrec "VarEF" p n (VarEF n) -> showsUnaryWith showsPrec "VarEF" p n
(ConEF n) -> showsUnaryWith showsPrec "ConEF" p n (ConEF n) -> showsUnaryWith showsPrec "ConEF" p n
(LamEF bs e) -> showsBinaryWith showsPrec sp "LamEF" p bs e (LamEF bs e) -> showsBinaryWith showsPrec sp "LamEF" p bs e

View File

@@ -38,18 +38,6 @@ spec = do
let e = [coreExpr|3|] let e = [coreExpr|3|]
in check' [] (TyCon "Bool") e `shouldSatisfy` isLeft in check' [] (TyCon "Bool") e `shouldSatisfy` isLeft
it "should infer `fix ((+#) 1)` :: Int" $
let g = [ ("fix", ("a" :-> "a") :-> "a")
, ("+#", TyInt :-> TyInt :-> TyInt) ]
e = [coreExpr|fix ((+#) 1)|]
in infer' g e `shouldBe` Right TyInt
it "should infer mutually recursively defined lists" $
let g = [ ("cons", TyInt :-> TyCon "IntList" :-> TyCon "IntList") ]
e :: Expr'
e = [coreExpr|letrec { as = cons 1 bs; bs = cons 2 as } in as|]
in infer' g e `shouldBe` Right (TyCon "IntList")
infer' :: Context' -> Expr' -> Either [TypeError] Type infer' :: Context' -> Expr' -> Either [TypeError] Type
infer' g e = case runErrorful $ infer g e of infer' g e = case runErrorful $ infer g e of
(Just t, _) -> Right t (Just t, _) -> Right t

View File

@@ -27,22 +27,15 @@ spec = do
in coreRes `shouldBe` arithRes in coreRes `shouldBe` arithRes
describe "test programs" $ do describe "test programs" $ do
it "fac 3" $ it "fac 3" $ do
resultOf Ex.fac3 `shouldBe` Just (NNum 6) resultOf Ex.fac3 `shouldBe` Just (NNum 6)
it "sum [1,2,3]" $ it "sum [1,2,3]" $ do
resultOf Ex.sumList `shouldBe` Just (NNum 6) resultOf Ex.sumList `shouldBe` Just (NNum 6)
it "k 3 ((/#) 1 0)" $ it "k 3 ((/#) 1 0)" $ do
resultOf Ex.constDivZero `shouldBe` Just (NNum 3) resultOf Ex.constDivZero `shouldBe` Just (NNum 3)
it "id (case ... of { ... })" $ it "id (case ... of { ... })" $ do
resultOf Ex.idCase `shouldBe` Just (NNum 5) resultOf Ex.idCase `shouldBe` Just (NNum 5)
it "bool pattern matching with named constructors" $
resultOf Ex.namedBoolCase `shouldBe` Just (NNum 123)
it "list pattern matching with named constructors" $
resultOf Ex.namedConsCase `shouldBe` Just (NNum 6)