No description
Find a file
2023-04-06 14:20:27 +02:00
sample-programs Add implicit foralls for bidir, update and unify pipeline 2023-04-05 17:26:52 +02:00
src removed pretty printing of tvars 2023-04-06 14:20:27 +02:00
tests Fix test error message 2023-04-05 17:41:17 +02:00
.gitignore Separate make file actions 2023-04-03 12:24:22 +02:00
CHANGELOG.md cabal init and added formatting options 2023-01-17 11:42:40 +01:00
fourmolu.yaml Updated bug list & started working on more tests 2023-03-06 13:04:07 +01:00
Grammar.cf Add implicit foralls for bidir, update and unify pipeline 2023-04-05 17:26:52 +02:00
Grammar.pdf Add pdf of grammar 2023-04-03 12:11:21 +02:00
Justfile Added some debug options to the just file. 2023-03-28 09:48:27 +02:00
language.cabal Add implicit foralls for bidir, update and unify pipeline 2023-04-05 17:26:52 +02:00
LICENSE Initial commit 2023-01-17 11:37:08 +01:00
llvm.ll Strucute in place, MonomorpherIr module created 2023-03-01 13:50:01 +01:00
Makefile Separate make file actions 2023-04-03 12:24:22 +02:00
pipeline.txt Add implicit foralls for bidir, update and unify pipeline 2023-04-05 17:26:52 +02:00
README.md Added README section about Nix 2023-04-06 14:12:45 +02:00
shell.nix Add implicit foralls for bidir, update and unify pipeline 2023-04-05 17:26:52 +02:00
spec.txt Add bidirectional type checker, lambda lifter. 2023-03-27 16:07:11 +02:00
test_program.crf Reworked order of inference, added prettifier for tvars etc etc. 2023-04-02 00:04:33 +02:00

Build

First generate the parser using BNFC, this is done using the command bnfc -o src -d Grammar.cf

Churf can then be built using cabal install

Using the tool make the entire thing can be built by running make or using just, just build

Dependencies

If you have Nix installed, simply run nix-shell --pure shell.nix to get into an environment with the right versions of packages. Then run make and the compiler should build.

Compiling a program

Using the Hindley-Milner type checker: ./language -t hm example.crf

Using the bidirectional type checker: ./language -t bi example.crf

The program to compile has to have the file extension .crf

Syntax and quirks

See Grammar.pdf for the full syntax.

The syntactic requirements differ a bit using the different type checkers. The bidirectional type checker require explicit forall everywhere a type forall quantified type variable is declared. In the Hindley-Milner type checker all type variables are assumed to be forall quantified.

Currently for the code generator and monomorphizer to work correctly it is expected that the function main exist with either explicitly given type Int or inferrable.

Single line comments are written using -- Multi line comments are written using {- and -}

Braches and semicolons are optional.

Program

A program is a list of defs separated by semicolons, which in turn is either a bind, a signature, or a data types Program ::= [Def]

data Test () where 
    Test : Test ()
test : Int
test = 0

Bind

A bind is a name followed by a white space separated list of arguments, then an equal sign followed by an expression. Both name and arguments have to start with lower case letters

Bind ::= LIdent [LIdent] "=" Exp

example x y = x + y

Signature

A signature is a name followed by a colon and then the type The name has to start with a lowe case letter

Sig ::= LIdent ":" Type

const : a -> b -> a

Data type

A data type is declared as follows

Data ::= "data" Type "where" "{" [Inj] "}"

The words in quotes are necessary keywords The type can be any type for parsing, but only TData will type check.

The list of Inj is separated by white space. Using new lines is recommended for ones own sanity.

data Maybe (a) where
    Nothing : Maybe (a)
    Just    : a -> Maybe (a)

The parens are necessary for every data type to make the grammar unambiguous. Thus in data Bool () where ... the parens do not represent Unit

Inj

An inj is a constructor for the data type

It is declared like a signature, except the name has to start with a lower case letter. The return type of the constructor also has match the type of the data type to type check.

Inj ::= UIdent ":" Type

Type

A type can be either a type literal, type variable, function type, explicit forall quantified type or a type representing a data type A type literal have to start with an upper case letter, type variables have to start with a lower case letter, data types have to start with an upper case letter, a function type is two types separated by an arrow (arrows right associative), and foralls take one type variable followed by a type.

TLit ::= UIdent

TVar ::= LIdent

TData ::= UIdent "(" [Type] ")"

TFun ::= Type "->" Type

TAll ::= "forall" LIdent "." Type

exampleLit : Int
exampleVar : a
exampleData : Maybe (a)
exampleFun : Int -> a
exampleAll : forall a. forall b. a -> b

Expressions

There are a couple different expressions, probably best explained by their rules

Type annotated expression

EAnn ::= "(" Exp ":" Type ")"

Variable

EVar ::= LIdent

x

Constructor

EInj ::= UIdent

Just

Literal

ELit ::= Lit

0

Function application

EApp ::= Exp2 Exp3

f 0

Addition

EAdd ::= Exp1 "+" Exp2

3 + 5

Let expression

ELet ::= "let" Bind "in" Exp

let f x = x in f 0 

Abstraction, known as lambda or closure

EAbs ::= "\\" LIdent "." Exp

\x. x

Case expression consist of a list semicolon separated list of Branches

ECase ::= "case" Exp "of" "{" [Branch] "}"

case xs of
    Cons x xs => 1
    Nil => 0

Branch

A branch is a pattern followed by the fat arrow and then an expression

Branch ::= Pattern "=>" Exp

Pattern

A pattern can be either a variable, literal, a wildcard represented by _, an enum constructor (constructor with zero arguments) , or a constructor followed by a recursive list of patterns.

Variable match

PVar ::= LIdent

The x in the following example

x => 0

Literal match

PLit ::= Lit

The 1 in the following example

1 => 0

A wildcard match

PCatch ::= "_"

The underscore in the following example

_ => 0

A constructor without arguments

PEnum ::= UIdent

The Nothing in the following example

Nothing => 0

The recursive match on a constructor

PInj ::= UIdent [Pattern1]

The outer Just represents the UIdent and the rest is the recursive match

Just (Just 0) => 1

For simplicity sake a user does not need to consider these last two cases as different in parsing. We allow arbitrarily deep pattern matching.

Literal

We currently allow two different literals: Integer and Char