Home

Awesome

crisp-compiler

This isn't a production-grade compiler. It is merely a personal, educational haskell/llvm/compiler/static-vs-dynamic learning exercise. As such, I apologize in advance for atrocities such as mostly missing comments and/or lack of unit tests (only integration tests were written).

Do not try running this at home on your 32-bit machine! The compiler can be targeted towards 64-bit machines only. Moreover, it was only tested on a(n Arch) Linux environment.

Introduction

A compiler for Crisp (Lisp/Scheme subset) in Haskell, with an LLVM backend.

I initially wanted Crisp to be inspired by Scheme, and branch off it's own path. However, due to recent time constraints, and how bringing the compiler to it's current state took more time than I've anticipated, I settled on a subset of Scheme for now.

Example

Here's an example of implementing merge-sort in Crisp.

(define empty? (lambda(x) (= x '())))

(define list-length
  (lambda (lst)
    (if (empty? lst)
      0
      (+ 1 (list-length (cdr lst))))))

(define take
  (lambda (n lst)
    (if (= n 0)
      '()
      (cons (car lst)
            (take (- n 1) (cdr lst))))))

(define drop
  (lambda (n lst)
    (if (= n 0)
      lst
      (drop (- n 1) (cdr lst)))))

(define split
  (lambda (n lst)
    (cons (take n lst) (drop n lst))))

(define merge-sorted
  (lambda (lstA lstB)
    (cond ((empty? lstA)
            lstB)
          ((empty? lstB)
            lstA)
          ((< (car lstA) (car lstB))
            (cons (car lstA) (merge-sorted (cdr lstA) lstB)))
          (else
            (cons (car lstB) (merge-sorted lstA (cdr lstB)))))))

(define merge-sort
  (lambda (lst)
    (let ((len (list-length lst)))
      (if (< len 2)
        lst
        (let ((halves-pair (split (/ len 2) lst)))
          (merge-sorted (merge-sort (car halves-pair))
                        (merge-sort (cdr halves-pair))))))))

(merge-sort '(34 87 24 90 74 10 47))

Compile and execute:

$ crc -i merge-sort.crs -o merge-sort
Compiled successfully.
$ ./merge-sort
(10 24 34 47 74 87 90)

Here is what portions of the LLVM assembly for this module looks like: This is what merge-sort compiled to, not including it's generated inner-lambdas.

define i64 @initGlobals-lambda6(i64 %__env, i64 %lst) {
entry:
  %0 = alloca i64
  store i64 %__env, i64* %0
  %1 = alloca i64
  store i64 %lst, i64* %1
  %2 = inttoptr i64 %__env to <{}>*
  %3 = call i64 @memalign(i64 1, i64 16)
  %4 = inttoptr i64 %3 to <{ i64, i64 }>*
  %5 = call i64 @memalign(i64 1, i64 8)
  %6 = inttoptr i64 %5 to <{ i64 }>*
  %7 = load i64* %1
  %8 = call i64 @memalign(i64 1, i64 8)
  %9 = inttoptr i64 %8 to i64*
  store i64 %7, i64* %9
  %10 = getelementptr inbounds <{ i64 }>* %6, i32 0, i32 0
  store i64 %8, i64* %10
  %11 = getelementptr inbounds <{ i64, i64 }>* %4, i32 0, i32 0
  store i64 %5, i64* %11
  %12 = getelementptr inbounds <{ i64, i64 }>* %4, i32 0, i32 1
  store i64 ptrtoint (i64 (i64, i64)* @initGlobals-lambda6-lambda to i64), i64* %12
  %13 = inttoptr i64 %3 to <{ i64, i64 }>*
  %14 = getelementptr inbounds <{ i64, i64 }>* %13, i32 0, i32 0
  %15 = load i64* %14
  %16 = getelementptr inbounds <{ i64, i64 }>* %13, i32 0, i32 1
  %17 = load i64* %16
  %18 = inttoptr i64 %17 to i64 (i64, i64)*
  %19 = load i64* @list-length
  %20 = inttoptr i64 %19 to <{ i64, i64 }>*
  %21 = getelementptr inbounds <{ i64, i64 }>* %20, i32 0, i32 0
  %22 = load i64* %21
  %23 = getelementptr inbounds <{ i64, i64 }>* %20, i32 0, i32 1
  %24 = load i64* %23
  %25 = inttoptr i64 %24 to i64 (i64, i64)*
  %26 = load i64* %9
  %27 = call i64 %25(i64 %22, i64 %26)
  %28 = call i64 %18(i64 %15, i64 %27)
  ret i64 %28
}

Features

Implemented features, as of this writing:

Usage

The crc (crisp-compiler) is a command-line application. The options are:

-i FILEPATH  --input-filepath=FILEPATH   Path of input file. REQUIRED if not in repl-mode
-o FILEPATH  --output-filepath=FILEPATH  Path of output file. Default: ./a.out
-r           --repl-mode                 Interactive REPL mode. Default: false
-p           --print-llvm                Output resulting LLVM IR. Default: false
-h           --help                      Show help

Option details

The compiler has 2 modes of operation:

Requirements

Tests

(define empty? (lambda(x) (= x '())))

(define merge-sort (lambda (lstA lstB)
  (cond ((empty? lstA) lstB)
        ((empty? lstB) lstA)
        ((< (car lstA) (car lstB)) (cons (car lstA) (merge-sort (cdr lstA) lstB)))
        (else (cons (car lstB) (merge-sort lstA (cdr lstB))))
  )
))

(merge-sort '(2 6 9) '(3 5 10))
(2 3 5 6 9 10)

Install

###Option 1: Install package from hackage### Package not yet uploaded to Hackage.

###Option 2: clone from Github###

To install:

$ git clone https://github.com/talw/crisp-compiler.git
$ cd crisp-compiler
$ cabal sandbox init
$ cabal install

After which you should have a crc in .cabal-sandbox/bin/ where you 'cabal install'ed.