Unfancy JavaScript with Manual Memory Management
CoffeeScript Other
Pull request Compare This branch is 89 commits ahead, 1238 commits behind jashkenas:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
bin Moved src/*.coffee under src/coffee-script/ so that lib can be added … Jul 5, 2011
documentation See issue #2620 Apr 13, 2012
examples
extras lib May 13, 2012
lib lib May 13, 2012
src Small fixes galore May 13, 2012
test Small fixes galore May 13, 2012
.gitignore Add uglify-js and jison as development dependencies Aug 5, 2011
.npmignore fixes issue #1490 ... jsl.conf is incorrectly npmignored. Jul 7, 2011
CNAME trying with just the .org Nov 1, 2011
Cakefile
LICENSE CoffeeScript 1.3.0 Apr 10, 2012
README README: corrected compilation instruction; added execution instruction Aug 9, 2011
README.md
Rakefile Updating rake task to pull version number and date. Nov 22, 2010
index.html CoffeeScript 1.3.1 (quick bugfix for compound assignment to a global … Apr 10, 2012
package.json CoffeeScript 1.3.1 (quick bugfix for compound assignment to a global … Apr 10, 2012

README.md

heap.coffee

heap.coffee is a CoffeeScript dialect that has an optional C-like type system with manual memory management which compiles to use an explicit typed array as its "heap". This lets you write memory-efficient and GC pause-free code less painfully.

A word of warning: this is not a very good type system, or even does very much. It's basically a glorified preprocessor to help compute offsets for struct fields.

Original CoffeeScript

# A linked list type.
type node = struct
  val  :: int
  next :: *node

# Allocate 2 new nodes with new.
n, m :: *node
n = m = new node

# Dot-notation now also dereference struct pointers.
n.val = 0
n.next = m
m.val = 1
m.next = null

# Free heap-allocated nodes with delete.
delete n
delete m

Compiled JavaScript

// Generated by CoffeeScript 1.3.1
(function() {
  var free, m, malloc, n, _ref,
    _HEAP = require('heap/heap'),
    _I32 = _HEAP.I32,
    _U32 = _HEAP.U32;

  _ref = require('heap/malloc'), malloc = _ref.malloc, free = _ref.free;

  // type node = struct { [0] val :: i32, [4] next :: *node }

  // n :: *node
  // m :: *node
  n = m = malloc(8);

  _I32[n] = 0;
  _U32[n + 1] = m;
  _I32[m] = 1;
  _U32[m + 1] = 0;

  free(n);
  free(m);
}).call(this);

Components

To use heap.coffee, there are two components: the compiler and the runtime. The compiler is hooked into CoffeeScript internals and should just work. The runtime are found in `src/heap' and should be built using:

$ bin/cake build:heap

This builds heap.js, which is the module to be required to initialize the heap, and malloc.js, which is an implementation of K&R's naive malloc and free implementation.

Syntax

Type synonyms alias one type name to another. These may only appear at the toplevel.

type size_t = uint

Type declarations tell the compiler what variable is what type. Our type system is simple and C-like, so you must tell it the types of everything, it doesn't really do any inference.

# Type a simple identifier.
i :: int
# Type a destructuring array.
[ i, j ] :: [ int, int ]
# Type a destructuring object.
{ foo, bar } :: { foo: int, bar: int }

Allocate using new:

n :: *node
n = new node

Free using delete:

delete n

Dereferencing in expressions and taking the address of variables are just like in C:

v  = *n
np = &n

Use dot where you would use arrow in C. Dereferencing of the struct pointer is automatic.

n = n.next
n = (*n).next # semantically equivalent to above

Use := to assign to the memory location of a pointer type. Note that the C-like syntax *p = e is valid, but really means *(p = e).

i :: *int
i = new int
i := 42

Pointer arithmetic work as it does in C.

Types

heap.coffee includes any, {i,u}{8,16,32} and double, and the following aliases.

  • byte = u8
  • short = i16
  • int = i32
  • uint = u32
  • num = double

Declaring a variable to be of type any is like not declaring its type.

Struct types are declared using type synonyms and share syntax with creating objects, except each property-value pair is a type declaration:

type point = struct
  x :: int
  y :: int

Pointer types are prefixed by *:

type intp = *int

Function types are composed using ->. Since functions are still JavaScript-native, pointers cannot be taken of functions.

type binop = (int, int) -> int

Normally, the user has to declare the types of parameters manually. There is a special sugar in place for when a variable is given a function type declaration and its value is found to be a syntactic function. When this holds, the compiler automatically inserts the declarations for the parameters.

f :: (int, int) -> int
f = (x, y) -> x * y

# The above is the same as:
f :: (int, int) -> int
f = (x, y) :: (int, int) ->
  x * y

Destructuring array or object types are allowed to more easily type destructuring assignments.

{ x, y } :: { x: int, y: int }
{ x, y } = { x: 0, y: 1 }

# The above is the same as
x :: int
y :: int
{ x, y } = { x: 0, y: 1 }

Note, however, that property accesses in general are untyped. Only statically determinable objects (literals) and statically determinable properties (dot notation or a constant index) are typed.

This is particularly helpful for typing parameters:

f :: ([int, int]) -> int
f = ([i, j]) -> i * j

If the left-hand side of an assignment is typed, the right-hand side must also be typed, and the two types must be unifiable.

n :: *node
n = new node

i :: int
i = n  # error: incompatible types `int' and `*node'
i = {} # error: cannot assign untyped to typed