Home

Awesome

cfl

Author: Anthony Burzillo


cfl (standing for C-based Functional Language) is a statically typed functional programming language.

Usage

To see the usage simply run ./cfl. If LLVM is already installed (can be installed by running brew install llvm or apt-get install llvm, etc.) then the usage will be

$ ./cfl
USAGE: cfl filename       :: compile the program
           -ast filename  :: print the AST of the program
           -type filename :: print the high level types of the program
           -deep filename :: print all the types of the program
           -eval filename :: evaluate the program
           -ll filename   :: print out the llvm code for the program
           -asm filename  :: print out native assembly code for the program
           -jit filename  :: evaluate the program using Just-In-Time compiling

Note that you can still run cfl without LLVM but you will not have LLVM related options like -jit or compilation.

Example

Obligatory hello world:

main = "hello world!"

Note that every cfl program must end with a main and global definitions are followed by a semicolon.

The quicksort algorithm can be written in cfl as

split x xs l r = case xs of
      []       -> (l, r)
    | (y : ys) -> let (l', r') = split x ys l r in if y <= x
        then (y : l', r')
        else (l', y : r');

quicksort x = case x of
      []       -> []
    | (y : ys) -> let (l, r) = split y ys [] []
        in quicksort l ++ y : quicksort r;

// random x is a globally defined function returning a random integer
// between 0 and x inclusive
random_list x l = if x == 0 then [] else random l : random_list (x - 1) l;

main = quicksort $ random_list 100 500

Running ./cfl -ast will output

split ::= function ~F0 -> (let rec split x = (function xs -> (function l -> (function r -> (case (xs) of [] -> ((l, r)) | (y : ys) -> ((function (l', r') -> (if (((y) < (x)) || ((y) == (x))) then (((y) : (l'), r')) else ((l', (y) : (r'))))) (((((split) (x)) (ys)) (l)) (r))))))) in ((split) (~F0)))
quicksort ::= function ~F1 -> (let rec quicksort x = (case (x) of [] -> ([]) | (y : ys) -> ((function (l, r) -> (((quicksort) (l)) ++ ((y) : ((quicksort) (r))))) (((((split) (y)) (ys)) ([])) ([])))) in ((quicksort) (~F1)))
random_list ::= function ~F2 -> (let rec random_list x = (function l -> (if ((x) == (0)) then ([]) else (((random) (l)) : (((random_list) ((x) + ((-1) * (1)))) (l))))) in ((random_list) (~F2)))
main ::= (quicksort) (((random_list) (100)) (500))

Running ./cfl -type will output

split => (Integer -> ([Integer] -> ([Integer] -> ([Integer] -> ([Integer], [Integer])))))
quicksort => ([Integer] -> [Integer])
random_list => (Integer -> (Integer -> [Integer]))
main => [Integer]

Running ./cfl -deep will output

split ~>> function (~F0 :: Integer) -> (let rec (split :: (Integer -> ([Integer] -> ([Integer] -> ([Integer] -> ([Integer], [Integer])))))) (x :: Integer) = (function (xs :: [Integer]) -> (function (l :: [Integer]) -> (function (r :: [Integer]) -> (case (xs :: [Integer]) of [] -> ((l :: [Integer], r :: [Integer]) :: ([Integer], [Integer])) | ((y :: Integer) : (ys :: [Integer])) -> ((function ((l' :: [Integer], r' :: [Integer]) :: ([Integer], [Integer])) -> (if (((y :: Integer) < (x :: Integer) :: Bool) || ((y :: Integer) == (x :: Integer) :: Bool) :: Bool) then (((y :: Integer) : (l' :: [Integer]) :: [Integer], r' :: [Integer]) :: ([Integer], [Integer])) else ((l' :: [Integer], (y :: Integer) : (r' :: [Integer]) :: [Integer]) :: ([Integer], [Integer])) :: ([Integer], [Integer])) :: (([Integer], [Integer]) -> ([Integer], [Integer]))) (((((split :: (Integer -> ([Integer] -> ([Integer] -> ([Integer] -> ([Integer], [Integer])))))) (x :: Integer) :: ([Integer] -> ([Integer] -> ([Integer] -> ([Integer], [Integer]))))) (ys :: [Integer]) :: ([Integer] -> ([Integer] -> ([Integer], [Integer])))) (l :: [Integer]) :: ([Integer] -> ([Integer], [Integer]))) (r :: [Integer]) :: ([Integer], [Integer])) :: ([Integer], [Integer])) :: ([Integer], [Integer])) :: ([Integer] -> ([Integer], [Integer]))) :: ([Integer] -> ([Integer] -> ([Integer], [Integer])))) :: ([Integer] -> ([Integer] -> ([Integer] -> ([Integer], [Integer]))))) in ((split :: (Integer -> ([Integer] -> ([Integer] -> ([Integer] -> ([Integer], [Integer])))))) (~F0 :: Integer) :: ([Integer] -> ([Integer] -> ([Integer] -> ([Integer], [Integer]))))) :: ([Integer] -> ([Integer] -> ([Integer] -> ([Integer], [Integer]))))) :: (Integer -> ([Integer] -> ([Integer] -> ([Integer] -> ([Integer], [Integer])))))
quicksort ~>> function (~F1 :: [Integer]) -> (let rec (quicksort :: ([Integer] -> [Integer])) (x :: [Integer]) = (case (x :: [Integer]) of [] -> ([] :: [Integer]) | ((y :: Integer) : (ys :: [Integer])) -> ((function ((l :: [Integer], r :: [Integer]) :: ([Integer], [Integer])) -> (((quicksort :: ([Integer] -> [Integer])) (l :: [Integer]) :: [Integer]) ++ ((y :: Integer) : ((quicksort :: ([Integer] -> [Integer])) (r :: [Integer]) :: [Integer]) :: [Integer]) :: [Integer]) :: (([Integer], [Integer]) -> [Integer])) (((((split :: (Integer -> ([Integer] -> ([Integer] -> ([Integer] -> ([Integer], [Integer])))))) (y :: Integer) :: ([Integer] -> ([Integer] -> ([Integer] -> ([Integer], [Integer]))))) (ys :: [Integer]) :: ([Integer] -> ([Integer] -> ([Integer], [Integer])))) ([] :: [Integer]) :: ([Integer] -> ([Integer], [Integer]))) ([] :: [Integer]) :: ([Integer], [Integer])) :: [Integer]) :: [Integer]) in ((quicksort :: ([Integer] -> [Integer])) (~F1 :: [Integer]) :: [Integer]) :: [Integer]) :: ([Integer] -> [Integer])
random_list ~>> function (~F2 :: Integer) -> (let rec (random_list :: (Integer -> (Integer -> [Integer]))) (x :: Integer) = (function (l :: Integer) -> (if ((x :: Integer) == (0 :: Integer) :: Bool) then ([] :: [Integer]) else (((random :: (Integer -> Integer)) (l :: Integer) :: Integer) : (((random_list :: (Integer -> (Integer -> [Integer]))) ((x :: Integer) + ((-1 :: Integer) * (1 :: Integer) :: Integer) :: Integer) :: (Integer -> [Integer])) (l :: Integer) :: [Integer]) :: [Integer]) :: [Integer]) :: (Integer -> [Integer])) in ((random_list :: (Integer -> (Integer -> [Integer]))) (~F2 :: Integer) :: (Integer -> [Integer])) :: (Integer -> [Integer])) :: (Integer -> (Integer -> [Integer]))
main ~>> (quicksort :: ([Integer] -> [Integer])) (((random_list :: (Integer -> (Integer -> [Integer]))) (100 :: Integer) :: (Integer -> [Integer])) (500 :: Integer) :: [Integer]) :: [Integer]

Running ./cfl -eval or ./cfl -jit, or running ./cfl program.cfl and then ./program will output something like

[0, 1, 3, 8, 14, 15, 16, 17, 25, 32, 37, 38, 40, 46, 61, 61, 64, 77, 80, 106, 108, 110, 111, 113, 121, 127, 134, 134, 137, 144, 149, 151, 158, 160, 165, 177, 179, 180, 188, 192, 196, 200, 201, 202, 212, 216, 218, 220, 223, 230, 236, 239, 239, 244, 251, 253, 265, 269, 281, 294, 301, 304, 308, 315, 323, 332, 334, 356, 359, 372, 373, 374, 375, 375, 383, 385, 387, 392, 393, 397, 398, 399, 402, 412, 420, 425, 431, 433, 438, 440, 441, 445, 452, 454, 459, 474, 482, 488, 489, 495]

Expressions

Basic types

The basic types of cfl are bools (true or false), integers (0, 1, -1, ...), and characters ('q').

Boolean Operations

cfl allows conjunction (&&), disjunction (||), and negation (!) expressions.

Integer Operations

The operations of addition (+), subtraction (-), multiplication (*), division (/), and modulus (%) evaluate to integer values. On the other hand, the comparison operators of equality (==), inequality (!=), less-than (<), less-than-or-equal (<=), greater-than (>), and greater-than-or-equal (>=) all evaluate to boolean values.

Functional Operations

cfl allows the creation of anonymous functions via function x -> e where e is some expression, and x is the argument to the function. We can create multiple argument anonymous functions via function x -> function y -> e where x is the first argument and y is the second, etc.

To apply a function f simply use f e where e is the expression for the first argument, or f e1 e2 for the first argument e1 and second argument e2. Similary one can use f $ x to apply the entire right side of the $ to f.

Finally, one can compose two functions f . g.

g a = if a then 5 else 4;
f b = b == 5;
main = f . g $ true

If Expressions

Use if e1 then e2 else e3.

Let Expressions

cfl allows let expressions to define constants and functions (possibly recursive) via

let x = 4 in x + 54

and

let f x y = if x = 0 then 0 else y + f (x - 1) y in f 3 4

Tuple Expressions

Tuples of arbitrary size can be created via (true, false, [0], 1, [false]).

Anything but a function name can be matched to a tuple in let statements, for instance

let (x, y) = (1, 2) in x + y
let f (x, y) = x + y in f 0 1

are both valid.

List Expressions

You can create empty lists [] or lists with values [1, 2, 3, 4]. You can push values onto a list via

5 : [4, 5, 6]

You can also concatenate lists

[1, 2] ++ [3, 4]

Finally, we can match on lists via a case expression via

case e1 of [] -> e2 | (x : xs) -> e3

String Expressions

Strings are implemented as lists of characters, therefore characters can be pushed onto strings and two strings can be concatenated. To create a string one can simply write the quotation enclosed string, i.e. "hello world!".