Felix Programming Language

Felix is an advanced Algol like procedural programming language with a strong functional subsystem. It features ML style static typing, first class functions, pattern matching, garabge collection, polymorphism, and has built in support for high performance microthreading, regular expressions and context free parsing.

The system provides a scripting harness so the language can be used like other scripting languages such as Python and Perl, but underneath it generates native code to obtain high performance.

A key feature of the system is that it uses the C/C++ object model, and provides an advanced binding sublanguage to support integration with C/C++ at both the source and object levels, both for embedding C/C++ data types and functions into Felix, and for embedding Felix into existing C++ architectures.

The Felix compiler is written in Objective Caml, and generates ISO C++, which should compile on any platform.

Latest News

Example python extension builder

posted on December 30, 2009 - 02:03 PM PST by Erick Tryzelaar
filed under: FBuild

Holger on the fbuild mailing list was trying to use fbuild to create python c extensions, so I mocked up a simple builder for him. There's a little work looking up the include directory, and I'm hardcoding the gcc option "-undefined dynamic_lookup", but once done it's pretty simple:

import fbuild.builders.c
import fbuild.db
import fbuild.path

@fbuild.db.caches
def python_c_builder(ctx, python='python'):
    """Creates and returns a python C extension builder."""

    # If we didn't explicitly chose which version of python to use, search the
    # environment for python.
    python = fbuild.builders.find_program(ctx, [python])

    # Create a helper program that calls out to python to get the include and
    # lib directories.
    stdout, stderr = ctx.execute([python, '-c',
        'import distutils.sysconfig; '
        'print(distutils.sysconfig.get_python_inc())'],
        quieter=1)
    includepath = stdout.decode().strip()

    # Return a fully specified python c extension builder.
    return fbuild.builders.c.guess_shared(ctx,
        includes=[includepath],
        flags=['-undefined', 'dynamic_lookup'],
        lib_prefix='',
        lib_suffix='.so')

def build(ctx):
    for python in ('python', 'python2.5', 'python2.6', 'python3.1'):
        # Configure the python.
        shared = python_c_builder(ctx, python)

        # Copy the src file so we don't have collisions.
        fbuild.path.Path('build/%s' % python).makedirs()
        fbuild.path.Path('spam.c').copy('build/%s' % python)

        # Build the extension module.
        shared.build_lib('%s/spam' % python, ['build/%s/spam.c' % python])

        # Test if we can actually import the module.
        ctx.execute([python, '-c', 'import spam; spam.system("echo hello world!")'],
            cwd='build/%s' % python)

Which results in:

looking for program python : ok /opt/local/bin/python
determining platform       : {'bsd', 'darwin', 'macosx', 'posix'}
looking for program gcc    : ok /usr/bin/gcc
checking gcc               : ok
checking gcc with -g       : ok
checking gcc with -O2      : ok
checking gcc with -undefined dynamic_lookup -fPIC: ok
checking gcc with -undefined dynamic_lookup -dynamiclib: ok
checking gcc with -undefined dynamic_lookup: ok
checking if gcc -undefined dynamic_lookup -fPIC can make objects: ok
checking if gcc -undefined dynamic_lookup -fPIC can make libraries: ok
checking if gcc -undefined dynamic_lookup -fPIC can make exes: ok
checking if gcc -undefined dynamic_lookup -fPIC can link lib to exe: ok
 * gcc -undefined dynamic_lookup -fPIC  : build/python/spam.c -> build/python/spam.os
 * gcc -undefined dynamic_lookup -dynamiclib: build/python/spam.os -> build/python/spam.so
hello world!
looking for program python2.5           : ok /opt/local/bin/python2.5
checking gcc                            : ok
checking gcc with -g                    : ok
checking gcc with -O2                   : ok
checking gcc with -undefined dynamic_lookup -fPIC: ok
checking gcc with -undefined dynamic_lookup -dynamiclib: ok
checking gcc with -undefined dynamic_lookup: ok
checking if gcc -undefined dynamic_lookup -fPIC can make objects: ok
checking if gcc -undefined dynamic_lookup -fPIC can make libraries: ok
checking if gcc -undefined dynamic_lookup -fPIC can make exes: ok
checking if gcc -undefined dynamic_lookup -fPIC can link lib to exe: ok
 * gcc -undefined dynamic_lookup -fPIC  : build/python2.5/spam.c -> build/python2.5/spam.os
 * gcc -undefined dynamic_lookup -dynamiclib: build/python2.5/spam.os -> build/python2.5/spam.so
hello world!
looking for program python2.6           : ok /opt/local/bin/python2.6
 * gcc -undefined dynamic_lookup -fPIC  : build/python2.6/spam.c -> build/python2.6/spam.os
 * gcc -undefined dynamic_lookup -dynamiclib: build/python2.6/spam.os -> build/python2.6/spam.so
hello world!
looking for program python3.1           : ok /opt/local/bin/python3.1
checking gcc                            : ok
checking gcc with -g                    : ok
checking gcc with -O2                   : ok
checking gcc with -undefined dynamic_lookup -fPIC: ok
checking gcc with -undefined dynamic_lookup -dynamiclib: ok
checking gcc with -undefined dynamic_lookup: ok
checking if gcc -undefined dynamic_lookup -fPIC can make objects: ok
checking if gcc -undefined dynamic_lookup -fPIC can make libraries: ok
checking if gcc -undefined dynamic_lookup -fPIC can make exes: ok
checking if gcc -undefined dynamic_lookup -fPIC can link lib to exe: ok
 * gcc -undefined dynamic_lookup -fPIC  : build/python3.1/spam.c -> build/python3.1/spam.os
 * gcc -undefined dynamic_lookup -dynamiclib: build/python3.1/spam.os -> build/python3.1/spam.so
hello world!

I should be able to get a prototype builder out soon.

read comments

Why I find felix interesting

posted on September 28, 2009 - 12:53 PM PDT by Erick Tryzelaar

This is a repost from a reddit that's about why I find neat about felix. Maybe this will inspire someone to try it out some time :)


So felix has a couple nice things going for it. The main things I find interesting is the concurrency model, the dynamic syntax, the type system, c++ integration. That space is getting a bit busy, with Scala probably being the closest relative, but I think there's still plenty of room for exploration.

My personal favorite thing is our concurrency model. Similar to Erlang and Scala, we've got an actor-ish concurrency model with user space fibers that communicate through messages. We call them fthreads. It's a little lower level than those languages though, we only provide synchronous no-copy message passing. I'm planning on layering a mailbox layer on top of that to add asynchronous messaging though.

We've also got an async io library that's intended to work seamlessly with fthreads. The intention is that you should be able to write standard blocking batch-style code and let the compiler/runtime convert your code into event driven code. We've got a prototype web server that's built on top of this, but haven't gotten too far with it yet though.

Next up our dynamic syntax. Felix's grammar is essentially defined at runtime. We have a basic shim grammar that lets us load up a grammar specification at runtime to parse the rest of the language. This then gets compiled in memory to scheme, which then gets compiled to the ast for code generation. The neat thing with this is that it allows you to have scoped syntax extensions. This could allow for a lot of neat lisp-style macro metaprogramming, or even theoretically allow you to write, say, a python-compatible grammar and let felix handle the rest of the language.

We've got a pretty advanced type system. It's a static language with support for both ad-hoc polymorphism and haskell-style typeclasses. Since the grammar's dynamic, we don't really have hardcoded concepts of types like ints. They're specified in the standard library. So that means we can't really have the compiler know that 1+0 can be optimized down to 1. So we've got hooks in order to specify the reductions like this. Similarly, we tell the compiler axioms about types, like this type is symmetric, and that one is associative. This then can be outputted to the proof assistant generator Why in order to help prove things about your program, though I don't know too much about how that works.

Finally, the c++ integration. Our main backend is c++ source. This lets us directly support binding to c++ libraries without needing to write external shim bindings. For instance, we use this and typeclasses to directly expose a good portion of STL to felix. We'll see if I can figure out how to integrate this with my LLVM backend though. Maybe once Clang gets c++ working we could somehow use that to generate the right code for us.

read comments

Hello Felix!

posted on September 28, 2009 - 02:10 AM PDT by Erick Tryzelaar
filed under: Felix, LLVM

Basic pointer types and c strings are now working!

type char = "%i8";
typedef charp = &char;    
proc puts : charp = "puts";

puts c"Hello world!";

Generates:

@0 = internal global [13 x i8] c"Hello world!\00" ; <[13 x i8]*> [#uses=1]

declare void @puts(i8*)

define void @1() {
entry:
  call void @puts(i8* getelementptr inbounds ([13 x i8]* @0, i32 0, i32 0))
  ret void
}

And prints out:

Hello World!

read comments

Closures!

posted on September 27, 2009 - 05:40 PM PDT by Erick Tryzelaar

flxc now has automatic stack closures! Take that, C99! Check this out!

type tiny = "%i8";
type int = "%i32";
typedef bool = 2;
fun add : int*int -> int = "%add";
fun sub : int*int -> int = "%sub";
fun eq : int*int -> bool = "%eq";
fun lnot : bool -> bool = "%lnot";
proc exit : int = "exit";

noinline fun foo (x:int) = {
  val y = 6;
  return x + y;
}

noinline proc fake_exit (x:int) {
  exit x;
  return;
}

noinline fun bar (x:int) = {
  var y = 10;
  noinline proc baz () {
    y = 20;
    return;
  }
  baz ();
  return x + y;
}

noinline fun x (a:int, b:int, c:tiny) = {
  val x1 = a;
  val x2 = b;
  val x3 = c;
  noinline fun y (d:int, e:int, f:tiny) = {
    val y1 = x1;
    val y2 = x2;
    val y3 = f;
    noinline fun z (g:int, h:int, i:tiny) = {
      val z1 = x1;
      val z2 = x2;
      val z3 = i;
      return z1;
    }
    return z (y1,y2,y3);
  }
  return y (x1,x2,x3);
}

fake_exit $ (foo 2) + (bar 3) + (x (1,2,3t));

And the llvm source:

%0 = type { i32, i32 }
%1 = type { i32, i32, i32, i32, i8 }
%2 = type { i32, i32, i8 }

declare void @exit(i32)

define i32 @foo(i32 %x) {
entry:
  %foo.x = alloca i32                             ; <i32*> [#uses=2]
  store i32 %x, i32* %foo.x
  %0 = load i32* %foo.x                           ; <i32> [#uses=1]
  %1 = add i32 %0, 6                              ; <i32> [#uses=1]
  ret i32 %1
}

define void @fake_exit(i32 %x) {
entry:
  %fake_exit.x = alloca i32                       ; <i32*> [#uses=2]
  store i32 %x, i32* %fake_exit.x
  %0 = load i32* %fake_exit.x                     ; <i32> [#uses=1]
  call void @exit(i32 %0)
  ret void
}

define i32 @bar(i32 %x) {
entry:
  %bar-closure = alloca %0                        ; <%0*> [#uses=3]
  %bar.y = getelementptr %0* %bar-closure, i32 0, i32 0 ; <i32*> [#uses=2]
  %bar.x = getelementptr %0* %bar-closure, i32 0, i32 1 ; <i32*> [#uses=2]
  store i32 %x, i32* %bar.x
  store i32 10, i32* %bar.y
  call void @bar.baz(%0* %bar-closure)
  %0 = load i32* %bar.x                           ; <i32> [#uses=1]
  %1 = load i32* %bar.y                           ; <i32> [#uses=1]
  %2 = add i32 %0, %1                             ; <i32> [#uses=1]
  ret i32 %2
}

define void @bar.baz(%0* %bar-closure) {
entry:
  %bar.y = getelementptr %0* %bar-closure, i32 0, i32 0 ; <i32*> [#uses=1]
  %bar.x = getelementptr %0* %bar-closure, i32 0, i32 1 ; <i32*> [#uses=0]
  store i32 20, i32* %bar.y
  ret void
}

define i32 @x(i32 %a, i32 %b, i8 %c) {
entry:
  %x-closure = alloca %1                          ; <%1*> [#uses=6]
  %x.x1 = getelementptr %1* %x-closure, i32 0, i32 0 ; <i32*> [#uses=2]
  %x.x2 = getelementptr %1* %x-closure, i32 0, i32 1 ; <i32*> [#uses=2]
  %x.a = getelementptr %1* %x-closure, i32 0, i32 2 ; <i32*> [#uses=2]
  %x.b = getelementptr %1* %x-closure, i32 0, i32 3 ; <i32*> [#uses=2]
  %x.c = getelementptr %1* %x-closure, i32 0, i32 4 ; <i8*> [#uses=2]
  store i32 %a, i32* %x.a
  store i32 %b, i32* %x.b
  store i8 %c, i8* %x.c
  %0 = load i32* %x.a                             ; <i32> [#uses=1]
  store i32 %0, i32* %x.x1
  %1 = load i32* %x.b                             ; <i32> [#uses=1]
  store i32 %1, i32* %x.x2
  %2 = load i32* %x.x1                            ; <i32> [#uses=1]
  %3 = load i32* %x.x2                            ; <i32> [#uses=1]
  %4 = load i8* %x.c                              ; <i8> [#uses=1]
  %5 = call i32 @x.y(%1* %x-closure, i32 %2, i32 %3, i8 %4) ; <i32> [#uses=1]
  ret i32 %5
}

define i32 @x.y(%1* %x-closure, i32 %d, i32 %e, i8 %f) {
entry:
  %x.x1 = getelementptr %1* %x-closure, i32 0, i32 0 ; <i32*> [#uses=1]
  %x.x2 = getelementptr %1* %x-closure, i32 0, i32 1 ; <i32*> [#uses=1]
  %x.a = getelementptr %1* %x-closure, i32 0, i32 2 ; <i32*> [#uses=0]
  %x.b = getelementptr %1* %x-closure, i32 0, i32 3 ; <i32*> [#uses=0]
  %x.c = getelementptr %1* %x-closure, i32 0, i32 4 ; <i8*> [#uses=0]
  %x.y-closure = alloca %2                        ; <%2*> [#uses=4]
  %x.y.d = getelementptr %2* %x.y-closure, i32 0, i32 0 ; <i32*> [#uses=1]
  %x.y.e = getelementptr %2* %x.y-closure, i32 0, i32 1 ; <i32*> [#uses=1]
  %x.y.f = getelementptr %2* %x.y-closure, i32 0, i32 2 ; <i8*> [#uses=2]
  store i32 %d, i32* %x.y.d
  store i32 %e, i32* %x.y.e
  store i8 %f, i8* %x.y.f
  %0 = load i32* %x.x1                            ; <i32> [#uses=1]
  %1 = load i32* %x.x2                            ; <i32> [#uses=1]
  %2 = load i8* %x.y.f                            ; <i8> [#uses=1]
  %3 = call i32 @x.y.z(%1* %x-closure, %2* %x.y-closure, i32 %0, i32 %1, i8 %2) ; <i32> [#uses=1]
  ret i32 %3
}

define i32 @x.y.z(%1* %x-closure, %2* %x.y-closure, i32 %g, i32 %h, i8 %i) {
entry:
  %x.x1 = getelementptr %1* %x-closure, i32 0, i32 0 ; <i32*> [#uses=1]
  %x.x2 = getelementptr %1* %x-closure, i32 0, i32 1 ; <i32*> [#uses=0]
  %x.a = getelementptr %1* %x-closure, i32 0, i32 2 ; <i32*> [#uses=0]
  %x.b = getelementptr %1* %x-closure, i32 0, i32 3 ; <i32*> [#uses=0]
  %x.c = getelementptr %1* %x-closure, i32 0, i32 4 ; <i8*> [#uses=0]
  %x.y.d = getelementptr %2* %x.y-closure, i32 0, i32 0 ; <i32*> [#uses=0]
  %x.y.e = getelementptr %2* %x.y-closure, i32 0, i32 1 ; <i32*> [#uses=0]
  %x.y.f = getelementptr %2* %x.y-closure, i32 0, i32 2 ; <i8*> [#uses=0]
  %x.y.z-closure = alloca %2                      ; <%2*> [#uses=3]
  %x.y.z.g = getelementptr %2* %x.y.z-closure, i32 0, i32 0 ; <i32*> [#uses=1]
  %x.y.z.h = getelementptr %2* %x.y.z-closure, i32 0, i32 1 ; <i32*> [#uses=1]
  %x.y.z.i = getelementptr %2* %x.y.z-closure, i32 0, i32 2 ; <i8*> [#uses=1]
  store i32 %g, i32* %x.y.z.g
  store i32 %h, i32* %x.y.z.h
  store i8 %i, i8* %x.y.z.i
  %0 = load i32* %x.x1                            ; <i32> [#uses=1]
  ret i32 %0
}

define void @0() {
entry:
  %0 = call i32 @foo(i32 2)                       ; <i32> [#uses=1]
  %1 = call i32 @bar(i32 3)                       ; <i32> [#uses=1]
  %2 = add i32 %0, %1                             ; <i32> [#uses=1]
  %3 = call i32 @x(i32 1, i32 2, i8 3)            ; <i32> [#uses=1]
  %4 = add i32 %2, %3                             ; <i32> [#uses=1]
  call void @fake_exit(i32 %4)
  ret void
}

What to work on next? Polymorphism, or strings? I am a bit tired relying on exit to tell me if the code's working or not. What to do, what to do...

read comments