about summary refs log tree commit diff
path: root/doc/tutorial
diff options
context:
space:
mode:
authorMarijn Haverbeke <marijnh@gmail.com>2011-10-31 16:18:59 +0100
committerMarijn Haverbeke <marijnh@gmail.com>2011-10-31 16:21:39 +0100
commit0b4f0a4caa6e765226c5f9d5609289dccb1072ce (patch)
tree05bc8ec0e832959fa0b0fbd7c1f2a1a1e45dffb5 /doc/tutorial
parent80c926c5e253d6db299698cfd4932dbcb61cd1bd (diff)
Add a first stab at a tutorial
You build it with `cd doc/tutorial; node build.js`, and then point
your browser at doc/tutorial/web/index.html. Not remotely ready for
publicity yet.
Diffstat (limited to 'doc/tutorial')
-rw-r--r--doc/tutorial/args.md126
-rw-r--r--doc/tutorial/build.js82
-rw-r--r--doc/tutorial/control.md169
-rw-r--r--doc/tutorial/data.md291
-rw-r--r--doc/tutorial/func.md88
-rw-r--r--doc/tutorial/generic.md104
-rw-r--r--doc/tutorial/index.md1
-rw-r--r--doc/tutorial/intro.md48
-rw-r--r--doc/tutorial/lib/markdown.js1466
-rw-r--r--doc/tutorial/mod.md3
-rw-r--r--doc/tutorial/order9
-rw-r--r--doc/tutorial/setup.md42
-rw-r--r--doc/tutorial/syntax.md207
-rw-r--r--doc/tutorial/web/style.css36
14 files changed, 2672 insertions, 0 deletions
diff --git a/doc/tutorial/args.md b/doc/tutorial/args.md
new file mode 100644
index 00000000000..77a221cdcf4
--- /dev/null
+++ b/doc/tutorial/args.md
@@ -0,0 +1,126 @@
+# Argument passing
+
+Rust datatypes are not trivial to copy (the way, for example,
+JavaScript values can be copied by simply taking one or two machine
+words and plunking them somewhere else). Shared boxes require
+reference count updates, big records or tags require an arbitrary
+amount of data to be copied (plus updating the reference counts of
+shared boxes hanging off them), unique pointers require their origin
+to be de-initialized.
+
+For this reason, the way Rust passes arguments to functions is a bit
+more involved than it is in most languages. It performs some
+compile-time cleverness to get rid of most of the cost of copying
+arguments, and forces you to put in explicit copy operators in the
+places where it can not.
+
+## Safe references
+
+The foundation of Rust's argument-passing optimization is the fact
+that Rust tasks for single-threaded worlds, which share no data with
+other tasks, and that most data is immutable.
+
+Take the following program:
+
+    let x = get_really_big_record();
+    myfunc(x);
+
+We want to pass `x` to `myfunc` by pointer (which is easy), *and* we
+want to ensure that `x` stays intact for the duration of the call
+(which, in this example, is also easy). So we can just use the
+existing value as the argument, without copying.
+
+There are more involved cases. The call could look like this:
+
+    myfunc(x, {|| x = get_another_record(); });
+
+Now, if `myfunc` first calls its second argument and then accesses its
+first argument, it will see a different value from the one that was
+passed to it.
+
+The compiler will insert an implicit copy of `x` in such a case,
+*except* if `x` contains something mutable, in which case a copy would
+result in code that behaves differently (if you mutate the copy, `x`
+stays unchanged). That would be bad, so the compiler will disallow
+such code.
+
+When inserting an implicit copy for something big, the compiler will
+warn, so that you know that the code is not as efficient as it looks.
+
+There are even more tricky cases, in which the Rust compiler is forced
+to pessimistically assume a value will get mutated, even though it is
+not sure.
+
+    fn for_each(v: [mutable @int], iter: block(@int)) {
+       for elt in v { iter(elt); }
+    }
+
+For all this function knows, calling `iter` (which is a closure that
+might have access to the vector that's passed as `v`) could cause the
+elements in the vector to be mutated, with the effect that it can not
+guarantee that the boxes will live for the duration of the call. So it
+has to copy them. In this case, this will happen implicitly (bumping a
+reference count is considered cheap enough to not warn about it).
+
+## The copy operator
+
+If the `for_each` function given above were to take a vector of
+`{mutable a: int}` instead of `@int`, it would not be able to
+implicitly copy, since if the `iter` function changes a copy of a
+mutable record, the changes won't be visible in the record itself. If
+we *do* want to allow copies there, we have to explicitly allow it
+with the `copy` operator:
+
+    type mutrec = {mutable x: int};
+    fn for_each(v: [mutable mutrec], iter: block(mutrec)) {
+       for elt in v { iter(copy elt); }
+    }
+
+## Argument passing styles
+
+The fact that arguments are conceptually passed by safe reference does
+not mean all arguments are passed by pointer. Composite types like
+records and tags *are* passed by pointer, but others, like integers
+and pointers, are simply passed by value.
+
+It is possible, when defining a function, to specify a passing style
+for a parameter by prefixing the parameter name with a symbol. The
+most common special style is by-mutable-reference, written `&`:
+
+    fn vec_push(&v: [int], elt: int) {
+        v += [elt];
+    }
+
+This will make it possible for the function to mutate the parameter.
+Clearly, you are only allowed to pass things that can actually be
+mutated to such a function.
+
+Another style is by-move, which will cause the argument to become
+de-initialized on the caller side, and give ownership of it to the
+called function. This is written `-`.
+
+Finally, the default passing styles (by-value for non-structural
+types, by-reference for structural ones) are written `+` for by-value
+and `&&` for by(-immutable)-reference. It is sometimes necessary to
+override the defaults. We'll talk more about this when discussing
+[generics][gens].
+
+[gens]: FIXME
+
+## Other uses of safe references
+
+Safe references are not only used for argument passing. When you
+destructure on a value in an `alt` expression, or loop over a vector
+with `for`, variables bound to the inside of the given data structure
+will use safe references, not copies. This means such references have
+little overhead, but you'll occasionally have to copy them to ensure
+safety.
+
+    let my_rec = {a: 4, b: [1, 2, 3]};
+    alt my_rec {
+      {a, b} {
+        log b; // This is okay
+        my_rec = {a: a + 1, b: b + [a]};
+        log b; // Here reference b has become invalid
+      }
+    }
diff --git a/doc/tutorial/build.js b/doc/tutorial/build.js
new file mode 100644
index 00000000000..7318b2a0400
--- /dev/null
+++ b/doc/tutorial/build.js
@@ -0,0 +1,82 @@
+var fs = require("fs"), md = require("./lib/markdown");
+
+function markdown(str) { return md.toHTML(str, "Maruku"); }
+
+function fileDates(file, c) {
+  function takeTime(str) {
+    return Number(str.match(/^(\S+)\s/)[1]) * 1000;
+  }
+  require("child_process").exec("git rev-list --timestamp HEAD -- " + file, function(err, stdout) {
+    if (err != null) { console.log("Failed to run git rev-list"); return; }
+    var history = stdout.split("\n");
+    if (history.length && history[history.length-1] == "") history.pop();
+    var created = history.length ? takeTime(history[0]) : Date.now();
+    var modified = created;
+    if (history.length > 1) modified = takeTime(history[history.length-1]);
+    c(created, modified);
+  });
+}
+
+function head(title) {
+  return "<html><head><link rel='stylesheet' href='style.css' type='text/css'>" +
+    "<meta http-equiv='Content-Type' content='text/html; charset=utf-8'><title>" +
+    title + "</title></head><body>\n";
+}
+
+function foot(created, modified) {
+  var r = "<p class='head'>"
+  var crStr = formatTime(created), modStr = formatTime(modified);
+  if (created) r += "Created " + crStr;
+  if (crStr != modStr)
+    r += (created ? ", l" : "L") + "ast modified on " + modStr;
+  return r + "</p>";
+}
+
+function formatTime(tm) {
+  var d = new Date(tm);
+  var months = ["", "January", "February", "March", "April", "May", "June", "July", "August",
+                "September", "October", "November", "December"];
+  return months[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear();
+}
+
+var files = fs.readFileSync("order", "utf8").split("\n").filter(function(x) { return x; });
+var max_modified = 0;
+var sections = [];
+
+// Querying git for modified dates has to be done async in node it seems...
+var queried = 0;
+for (var i = 0; i < files.length; ++i)
+  (function(i) { // Make lexical i stable
+    fileDates(files[i], function(ctime, mtime) {
+      sections[i] = {
+        text: fs.readFileSync(files[i] + ".md", "utf8"),
+        ctime: ctime, mtime: mtime,
+        name: files[i],
+      };
+      max_modified = Math.max(mtime, max_modified);
+      if (++queried == files.length) buildTutorial();
+    });
+  })(i);
+
+function htmlName(i) { return sections[i].name + ".html"; }
+
+function buildTutorial() {
+  var index = head("Rust language tutorial") + "<div id='content'>" +
+    markdown(fs.readFileSync("index.md", "utf8")) + "<ol>";
+  for (var i = 0; i < sections.length; ++i) {
+    var s = sections[i];
+    var html = htmlName(i);
+    var title = s.text.match(/^# (.*)\n/)[1];
+    index += '<li><a href="' + html + '">' + title + "</a></li>";
+    
+    var nav = '<p class="head">Section ' + (i + 1) + ' of the Rust language tutorial.<br>';
+    if (i > 0) nav += '<a href="' + htmlName(i-1) + '">« Section ' + i + "</a> | ";
+    nav += '<a href="index.html">Index</a>';
+    if (i + 1 < sections.length) nav += ' | <a href="' + htmlName(i+1) + '">Section ' + (i + 2) + " »</a>";
+    nav += "</p>";
+    fs.writeFileSync("web/" + html, head(title) + nav + '<div id="content">' + markdown(s.text) + "</div>" +
+                     nav + foot(s.ctime, s.mtime) + "</body></html>");
+  }
+  index += "</ol></div>" + foot(null, max_modified) + "</body></html>";
+  fs.writeFileSync("web/index.html", index);
+}
diff --git a/doc/tutorial/control.md b/doc/tutorial/control.md
new file mode 100644
index 00000000000..9172cb7303f
--- /dev/null
+++ b/doc/tutorial/control.md
@@ -0,0 +1,169 @@
+# Control structures
+
+## Conditionals
+
+We've seen `if` pass by a few times already. To recap, braces are
+compulsory, an optional `else` clause can be appended, and multiple
+`if`/`else` constructs can be chained together:
+
+    if false {
+        std::io::println("that's odd");
+    } else if true {
+        std::io::println("right");
+    } else {
+        std::io::println("neither true nor false");
+    }
+
+The condition given to an `if` construct *must* be of type boolean (no
+implicit conversion happens). If the arms return a value, this value
+must be of the same type for every arm in which control reaches the
+end of the block:
+
+    fn signum(x: int) -> int {
+        if x < 0 { -1 }
+        else if x > 0 { 1 }
+        else { ret 0; }
+    }
+
+The `ret` (return) and its semicolon could have been left out without
+changing the meaning of this function, but it illustrates that you
+will not get a type error in this case, although the last arm doesn't
+have type `int`, because control doesn't reach the end of that arm
+(`ret` is jumping out of the function).
+
+## Pattern matching
+
+Rust's `alt` construct is a generalized, cleaned-up version of C's
+`switch` construct. You provide it with a value and a number of arms,
+each labelled with a pattern, and it will execute the arm that matches
+the value.
+
+    alt my_number {
+      0       { std::io::println("zero"); }
+      1 | 2   { std::io::println("one or two"); }
+      3 to 10 { std::io::println("three to ten"); }
+      _       { std::io::println("something else"); }
+    }
+
+There is no 'falling through' between arms, as in C—only one arm is
+executed, and it doesn't have to explicitly `break` out of the
+construct when it is finished.
+
+The part to the left of each arm is called the pattern. Literals are
+valid patterns, and will match only their own value. The pipe operator
+(`|`) can be used to assign multiple patterns to a single arm. Ranges
+of numeric literal patterns can be expressed with `to`. The underscore
+(`_`) is a wildcard pattern that matches everything.
+
+If the arm with the wildcard pattern was left off in the above
+example, running it on a number greater than ten (or negative) would
+cause a run-time failure. When no arm matches, `alt` constructs do not
+silently fall through—they blow up instead.
+
+A powerful application of pattern matching is *destructuring*, where
+you use the matching to get at the contents of data types. Remember
+that `(float, float)` is a tuple of two floats:
+
+    fn angle(vec: (float, float)) -> float {
+        alt vec {
+          (0f, y) when y < 0f { 1.5 * std::math::pi }
+          (0f, y) { 0.5 * std::math::pi }
+          (x, y) { std::math::atan(y / x) }
+        }
+    }
+
+A variable name in a pattern matches everything, *and* binds that name
+to the value of the matched thing inside of the arm block. Thus, `(0f,
+y)` matches any tuple whose first element is zero, and binds `y` to
+the second element. `(x, y)` matches any tuple, and binds both
+elements to a variable.
+
+Any `alt` arm can have a guard clause (written `when EXPR`), which is
+an expression of type `bool` that determines, after the pattern is
+found to match, whether the arm is taken or not. The variables bound
+by the pattern are available in this guard expression.
+
+## Destructuring let
+
+To a limited extent, it is possible to use destructuring patterns when
+declaring a variable with `let`. For example, you can say this to
+extract the fields from a tuple:
+
+    let (a, b) = get_tuple_of_two_ints();
+
+This will introduce two new variables, `a` and `b`, bound to the
+content of the tuple.
+
+You may only use irrevocable patterns in let bindings, though. Things
+like literals, which only match a specific value, are not allowed.
+
+## Loops
+
+`while` produces a loop that runs as long as its given condition
+(which must have type `bool`) evaluates to true. Inside a loop, the
+keyword `break` can be used to abort the loop, and `cont` can be used
+to abort the current iteration and continue with the next.
+
+    let x = 5;
+    while true {
+        x += x - 3;
+        if x % 5 == 0 { break; }
+        std::io::println(std::int::str(x));
+    }
+
+This code prints out a weird sequence of numbers and stops as soon as
+it finds one that can be divided by five.
+
+When iterating over a vector, use `for` instead.
+
+    for elt in ["red", "green", "blue"] {
+        std::io::println(elt);
+    }
+
+This will go over each element in the given vector (a three-element
+vector of strings, in this case), and repeatedly execute the body with
+`elt` bound to the current element. You may add an optional type
+declaration (`elt: str`) for the iteration variable if you want.
+
+For more involved iteration, such as going over the elements of a hash
+table, Rust uses higher-order functions. We'll come back to those in a
+moment.
+
+## Failure
+
+The `fail` keyword causes the current [task][tasks] to fail. You use
+it to indicate unexpected failure, much like you'd use `exit(1)` in a
+C program, except that in Rust, it is possible for other tasks to
+handle the failure, allowing the program to continue running.
+
+`fail` takes an optional argument, which must have type `str`. Trying
+to access a vector out of bounds, or running a pattern match with no
+matching clauses, both result in the equivalent of a `fail`.
+
+[tasks]: FIXME
+
+## Logging
+
+Rust has a built-in logging mechanism, using the `log` statement.
+Logging is polymorphic—any type of value can be logged, and the
+runtime will do its best to output a textual representation of the
+value.
+
+    log "hi";
+    log (1, [2.5, -1.8]);
+
+By default, you *will not* see the output of your log statements. The
+environment variable `RUST_LOG` controls which log statements actually
+get output. It can contain a comma-separated list of paths for modules
+that should be logged. For example, running `rustc` with
+`RUST_LOG=rustc::front::attr` will turn on logging in its attribute
+parser. If you compile a program `foo.rs`, you can set `RUST_LOG` to
+`foo` to enable its logging.
+
+Turned-off `log` statements impose minimal overhead on the code that
+contains them, so except in code that needs to be really, really fast,
+you should feel free to scatter around debug logging statements, and
+leave them in.
+
+For interactive debugging, you often want unconditional logging. For
+this, use `log_err` instead of `log` [FIXME better name].
diff --git a/doc/tutorial/data.md b/doc/tutorial/data.md
new file mode 100644
index 00000000000..25135787f4d
--- /dev/null
+++ b/doc/tutorial/data.md
@@ -0,0 +1,291 @@
+# Datatypes
+
+Rust datatypes are, by default, immutable. The core datatypes of Rust
+are structural records and 'tags' (tagged unions, algebraic data
+types).
+
+    type point = {x: float, y: float};
+    tag shape {
+        circle(point, float);
+        rectangle(point, point);
+    }
+    let my_shape = circle({x: 0.0, y: 0.0}, 10.0);
+
+## Records
+
+Rust record types are written `{field1: TYPE, field2: TYPE [,
+...]}`, and record literals are written in the same way, but with
+expressions instead of types. They are quite similar to C structs, and
+even laid out the same way in memory (so you can read from a Rust
+struct in C, and vice-versa).
+
+The dot operator is used to access record fields (`mypoint.x`).
+
+Fields that you want to mutate must be explicitly marked as such. For
+example...
+
+    type stack = {content: [int], mutable head: uint};
+
+With such a type, you can do `mystack.head += 1u`. When the `mutable`
+is omitted from the type, such an assignment would result in a type
+error.
+
+To 'update' an immutable record, you use functional record update
+syntax, by ending a record literal with the keyword `with`:
+
+    let newpoint = {x: 0f with oldpoint};
+
+This will create a new struct, copying all the fields from `oldpoint`
+into it, except for the ones that are explicitly set in the literal.
+
+Rust record types are *structural*. This means that `{x: float, y:
+float}` is not just a way to define a new type, but is the actual name
+of the type. Record types can be used without first defining them. If
+module A defines `type point = {x: float, y: float}`, and module B,
+without knowing anything about A, defines a function that returns an
+`{x: float, y: float}`, you can use that return value as a `point` in
+module A. (Remember that `type` defines an additional name for a type,
+not an actual new type.)
+
+## Record patterns
+
+Records can be destructured on in `alt` patterns. The basic syntax is
+`{fieldname: pattern, ...}`, but the pattern for a field can be
+omitted as a shorthand for simply binding the variable with the same
+name as the field.
+
+    alt mypoint {
+        {x: 0f, y: y_name} { /* Provide sub-patterns for fields */ }
+        {x, y}             { /* Simply bind the fields */ }
+    }
+
+When you are not interested in all the fields of a record, a record
+pattern may end with `, _` (as in `{field1, _}`) to indicate that
+you're ignoring all other fields.
+
+## Tags
+
+Tags [FIXME terminology] are datatypes that have several different
+representations. For example, the type shown earlier:
+
+    tag shape {
+        circle(point, float);
+        rectangle(point, point);
+    }
+
+A value of this type is either a circle¸ in which case it contains a
+point record and a float, or a rectangle, in which case it contains
+two point records. The run-time representation of such a value
+includes an identifier of the actual form that it holds, much like the
+'tagged union' pattern in C, but with better ergonomics.
+
+The above declaration will define a type `shape` that can be used to
+refer to such shapes, and two functions, `circle` and `rectangle`,
+which can be used to construct values of the type (taking arguments of
+the specified types). So `circle({x: 0f, y: 0f}, 10f)` is the way to
+create a new circle.
+
+Tag variants do not have to have parameters. This, for example, is
+equivalent to an `enum` in C:
+
+    tag direction {
+        north;
+        east;
+        south;
+        west;
+    };
+
+This will define `north`, `east`, `south`, and `west` as constants,
+all of which have type `direction`.
+
+There is a special case for tags with a single variant. These are used
+to define new types in such a way that the new name is not just a
+synonym for an existing type, but its own distinct type. If you say:
+
+    tag gizmo_id = int;
+
+That is a shorthand for this:
+
+    tag gizmo_id { gizmo_id(int); }
+
+Tag types like this can have their content extracted with the
+dereference (`*`) unary operator:
+
+    let my_gizmo_id = gizmo_id(10);
+    let id_int: int = *my_gizmo_id;
+
+## Tag patterns
+
+For tag types with multiple variants, destructuring is the only way to
+get at their contents. All variant constructors can be used as
+patterns, as in this definition of `area`:
+
+    fn area(sh: shape) -> float {
+        alt sh {
+            circle(_, size) { std::math::pi * size * size }
+            rectangle({x, y}, {x: x2, y: y2}) { (x2 - x) * (y2 - y) }
+        }
+    }
+
+For variants without arguments, you have to write `variantname.` (with
+a dot at the end) to match them in a pattern. This to prevent
+ambiguity between matching a variant name and binding a new variable.
+
+    fn point_from_direction(dir: direction) -> point {
+        alt dir {
+            north. { {x:  0f, y:  1f} }
+            east.  { {x:  1f, y:  0f} }
+            south. { {x:  0f, y: -1f} }
+            west.  { {x: -1f, y:  0f} }
+        }
+    }
+
+## Tuples
+
+Tuples in Rust behave exactly like records, except that their fields
+do not have names (and can thus not be accessed with dot notation).
+Tuples can have any arity except for 0 or 1 (though you may see nil,
+`()`, as the empty tuple if you like).
+
+    let mytup: (int, int, float) = (10, 20, 30.0);
+    alt mytup {
+      (a, b, c) { log a + b + (c as int); }
+    }
+
+## Pointers
+
+In contrast to a lot of modern languages, record and tag types in Rust
+are not represented as pointers to allocated memory. They are, like in
+C and C++, represented directly. This means that if you `let x = {x:
+1f, y: 1f};`, you are creating a record on the stack. If you then copy
+it into a data structure, the whole record is copied, not just a
+pointer.
+
+For small records like `point`, this is usually still more efficient
+than allocating memory and going through a pointer. But for big
+records, or records with mutable fields, it can be useful to have a
+single copy on the heap, and refer to that through a pointer.
+
+Rust supports several types of pointers. The simplest is the unsafe
+pointer, written `*TYPE`, which is a completely unchecked pointer
+type only used in unsafe code (and thus, in typical Rust code, very
+rarely). The safe pointer types are `@TYPE` for shared,
+reference-counted boxes, and `~TYPE`, for uniquely-owned pointers.
+
+All pointer types can be dereferenced with the `*` unary operator.
+
+### Shared boxes
+
+Shared boxes are pointers to heap-allocated, reference counted memory.
+A cycle collector ensures that circular references do not result in
+memory leaks.
+
+Creating a shared box is done by simply applying the binary `@`
+operator to an expression. The result of the expression will be boxed,
+resulting in a box of the right type. For example:
+
+    let x = @10; // New box, refcount of 1
+    let y = x; // Copy the pointer, increase refcount
+    // When x and y go out of scope, refcount goes to 0, box is freed
+
+NOTE: We may in the future switch to garbage collection, rather than
+reference counting, for shared boxes.
+
+Shared boxes never cross task boundaries.
+
+### Unique boxes
+
+In contrast to shared boxes, unique boxes are not reference counted.
+Instead, it is statically guaranteed that only a single owner of the
+box exists at any time.
+
+    let x = ~10;
+    let y <- x;
+
+This is where the 'move' (`<-`) operator comes in. It is similar to
+`=`, but it de-initializes its source. Thus, the unique box can move
+from `x` to `y`, without violating the constraint that it only has a
+single owner.
+
+NOTE: If you do `y = x` instead, the box will be copied. We should
+emit warning for this, or disallow it entirely, but do not currently
+do so.
+
+Unique boxes, when they do not contain any shared boxes, can be sent
+to other tasks. The sending task will give up ownership of the box,
+and won't be able to access it afterwards. The receiving task will
+become the sole owner of the box.
+
+### Mutability
+
+All pointer types have a mutable variant, written `@mutable TYPE` or
+`~mutable TYPE`. Given such a pointer, you can write to its contents
+by combining the dereference operator with a mutating action.
+
+    fn increase_contents(pt: @mutable int) {
+        *pt += 1;
+    }
+
+## Vectors
+
+Rust vectors are always heap-allocated and unique. A value of type
+`[TYPE]` is represented by a pointer to a section of heap memory
+containing any number of `TYPE` values.
+
+NOTE: This uniqueness is turning out to be quite awkward in practice,
+and might change.
+
+Vector literals are enclosed in square brackets. Dereferencing is done
+with square brackets (and zero-based):
+
+    let myvec = [true, false, true, false];
+    if myvec[1] { std::io::println("boom"); }
+
+By default, vectors are immutable—you can not replace their elements.
+The type written as `[mutable TYPE]` is a vector with mutable
+elements. Mutable vector literals are written `[mutable]` (empty) or
+`[mutable 1, 2, 3]` (with elements).
+
+Growing a vector in Rust is not as inefficient as it looks (the `+`
+operator means concatenation when applied to vector types):
+
+    let myvec = [], i = 0;
+    while i < 100 {
+        myvec += [i];
+        i += 1;
+    }
+
+Because a vector is unique, replacing it with a longer one (which is
+what `+= [i]` does) is indistinguishable from appending to it
+in-place. Vector representations are optimized to grow
+logarithmically, so the above code generates about the same amount of
+copying and reallocation as `push` implementations in most other
+languages.
+
+## Strings
+
+The `str` type in Rust is represented exactly the same way as a vector
+of bytes (`[u8]`), except that it is guaranteed to have a trailing
+null byte (for interoperability with C APIs).
+
+This sequence of bytes is interpreted as an UTF-8 encoded sequence of
+characters. This has the advantage that UTF-8 encoded I/O (which
+should really be the goal for modern systems) is very fast, and that
+strings have, for most intents and purposes, a nicely compact
+representation. It has the disadvantage that you only get
+constant-time access by byte, not by character.
+
+A lot of algorithms don't need constant-time indexed access (they
+iterate over all characters, which `std::str::chars` helps with), and
+for those that do, many don't need actual characters, and can operate
+on bytes. For algorithms that do really need to index by character,
+there's the option to convert your string to a character vector (using
+`std::str::to_chars`).
+
+Like vectors, strings are always unique. You can wrap them in a shared
+box to share them. Unlike vectors, there is no mutable variant of
+strings. They are always immutable.
+
+## Resources
+
+FIXME fill this in
diff --git a/doc/tutorial/func.md b/doc/tutorial/func.md
new file mode 100644
index 00000000000..a8bfacedb0f
--- /dev/null
+++ b/doc/tutorial/func.md
@@ -0,0 +1,88 @@
+# Functions
+
+Functions (like all other static declarations, such as `type`) can be
+declared both at the top level and inside other functions (or modules,
+which we'll come back to in moment).
+
+The `ret` keyword immediately returns from a function. It is
+optionally followed by an expression to return. In functions that
+return `()`, the returned expression can be left off. A function can
+also return a value by having its top level block produce an
+expression (by omitting the final semicolon).
+
+Some functions (such as the C function `exit`) never return normally.
+In Rust, these are annotated with return type `!`:
+
+    fn dead_end() -> ! { fail; }
+
+This helps the compiler avoid spurious error messages. For example,
+the following code would be a type error if `dead_end` would be
+expected to return.
+
+    let dir = if can_go_left() { left }
+              else if can_go_right() { right }
+              else { dead_end(); };
+
+## Closures
+
+FIXME Either move entirely to fn~/fn@ nomenclature, or fix compiler to
+accept lambda as a type
+
+Normal Rust functions (declared with `fn`) do not close over their
+environment. A `lambda` expression can be used to create a closure.
+
+    fn make_plus_function(x: int) -> fn@(int) -> int {
+        lambda(y: int) -> int { x + y }
+    }
+    let plus_two = make_plus_function(2);
+    assert plus_two(3) == 5;
+
+A `lambda` function *copies* its environment (in this case, the
+binding for `x`). It can not mutate the closed-over bindings, and will
+not see changes made to these variables after the `lambda` was
+evaluated. `lambda`s can be put in data structures and passed around
+without limitation.
+
+A different form of closure is the block. Blocks are written like they
+are in Ruby: `{|x| x + y}`, the formal parameters between pipes,
+followed by the function body. They are stack-allocated and properly
+close over their environment (they see updates to closed over
+variables, for example). But blocks can only be used in a limited set
+of circumstances. They can be passed to other functions, but not
+stored in data structures or returned.
+
+    fn map_int(f: block(int) -> int, vec: [int]) -> [int] {
+        let result = [];
+        for i in vec { result += [f(i)]; }
+        ret result;
+    }
+    map_int({|x| x + 1 }, [1, 2, 3]);
+
+A block with no arguments is written `{|| body(); }`—you can not leave
+off the pipes.
+
+## Iteration
+
+Functions taking blocks provide a good way to define non-trivial
+iteration constructs. For example, this one iterates over a vector
+of integers backwards:
+
+    fn for_rev(v: [int], act: block(int)) {
+        let i = std::vec::len(v);
+        while (i > 0u) {
+            i -= 1u;
+            act(v[i]);
+        }
+    }
+
+To run such an iteration, you could do this:
+
+    for_rev([1, 2, 3], {|n| log n; });
+
+But Rust allows a more pleasant syntax for this situation, with the
+loop block moved out of the parenthesis and the final semicolon
+omitted:
+
+    for_rev([1, 2, 3]) {|n|
+        log n;
+    }
diff --git a/doc/tutorial/generic.md b/doc/tutorial/generic.md
new file mode 100644
index 00000000000..4ab121bd7b3
--- /dev/null
+++ b/doc/tutorial/generic.md
@@ -0,0 +1,104 @@
+# Generics
+
+## Generic functions
+
+Throughout this tutorial, I've been defining functions like `map` and
+`for_rev` to take vectors of integers. It is 2011, and we no longer
+expect to be defining such functions again and again for every type
+they apply to. Thus, Rust allows functions and datatypes to have type
+parameters.
+
+    fn for_rev<T>(v: [T], act: block(T)) {
+        let i = std::vec::len(v);
+        while i > 0u {
+            i -= 1u;
+            act(v[i]);
+        }
+    }
+    
+    fn map<T, U>(f: block(T) -> U, v: [T]) -> [U] {
+        let acc = [];
+        for elt in v { acc += [f(elt)]; }
+        ret acc;
+    }
+
+When defined in this way, these functions can be applied to any type
+of vector, as long as the type of the block's argument and the type of
+the vector's content agree with each other.
+
+Inside a parameterized (generic) function, the names of the type
+parameters (capitalized by convention) stand for opaque types. You
+can't look inside them, but you can pass them around.
+
+## Generic datatypes
+
+Generic `type` and `tag` declarations follow the same pattern:
+
+    type circular_buf<T> = {start: uint,
+                            end: uint,
+                            buf: [mutable T]};
+    
+    tag option<T> { some(T); none; }
+
+You can then declare a function to take a `circular_buf<u8>` or return
+an `option<str>`, or even an `option<T>` if the function itself is
+generic.
+
+The `option` type given above exists in the standard library as
+`std::option::t`, and is the way Rust programs express the thing that
+in C would be a nullable pointer. The nice part is that you have to
+explicitly unpack an `option` type, so accidental null pointer
+dereferences become impossible.
+
+## Type-inference and generics
+
+Rust's type inferrer works very well with generics, but there are
+programs that just can't be typed.
+
+    let n = none;
+
+If you never do anything else with none, the compiler will not be able
+to assign a type to it. (The same goes for `[]`, in fact.) If you
+really want to have such a statement, you'll have to write it like
+this:
+
+    let n = none::<int>;
+
+Note that, in a value expression, `<` already has a meaning as a
+comparison operator, so you'll have to write `::<T>` to explicitly
+give a type to a name that denotes a generic value. Fortunately, this
+is rarely necessary.
+
+## Polymorphic built-ins
+
+There are two built-in operations that, perhaps surprisingly, act on
+values of any type. It was already mentioned earlier that `log` can
+take any type of value and output it as a string.
+
+More interesting is that Rust also defines an ordering for all
+datatypes, and allows you to meaningfully apply comparison operators
+(`<`, `>`, `<=`, `>=`, `==`, `!=`) to them. For structural types, the
+comparison happens left to right, so `"abc" < "bac"` (but note that
+`"bac" < "ác"`, because the ordering acts on UTF-8 sequences without
+any sophistication).
+
+## Generic functions and argument-passing
+
+If you try this program:
+
+    fn plus1(x: int) -> int { x + 1 }
+    map(plus1, [1, 2, 3]);
+
+You will get an error message about argument passing styles
+disagreeing. The reason is that generic types are always passed by
+pointer, so `map` expects a function that takes its argument by
+pointer. The `plus1` you defined, however, uses the default, efficient
+way to pass integers, which is by value. To get around this issue, you
+have to explicitly mark the arguments to a function that you want to
+pass to a generic higher-order function as being passed by pointer:
+
+    fn plus1(&&x: int) -> int { x + 1 }
+    map(plus1, [1, 2, 3]);
+
+NOTE: This is inconvenient, and we are hoping to get rid of this
+restriction in the future.
diff --git a/doc/tutorial/index.md b/doc/tutorial/index.md
new file mode 100644
index 00000000000..d8c9be56568
--- /dev/null
+++ b/doc/tutorial/index.md
@@ -0,0 +1 @@
+# Rust language tutorial
diff --git a/doc/tutorial/intro.md b/doc/tutorial/intro.md
new file mode 100644
index 00000000000..9976d7f14e6
--- /dev/null
+++ b/doc/tutorial/intro.md
@@ -0,0 +1,48 @@
+# Introduction
+
+## Scope
+
+This is a tutorial for the Rust programming language. It assumes the
+reader is familiar with the basic concepts of programming, and has
+programmed in one or more other languages before. The tutorial covers
+the whole language, though not with the depth and precision of the
+[language reference][1].
+
+FIXME: maybe also the stdlib?
+
+[1]: http://www.rust-lang.org/doc/rust.html
+
+## Disclaimer
+
+Rust is a language under development. The general flavor of the
+language has settled, but details will continue to change as it is
+further refined. Nothing in this tutorial is final, and though we try
+to keep it updated, it is possible that the text occasionally does not
+reflect the actual state of the language.
+
+## First Impressions
+
+Though syntax is something you get used to, an initial encounter with
+a language can be made easier if the notation looks familiar. Rust is
+a curly-brace language in the tradition of C, C++, and JavaScript.
+
+    fn fac(n: int) -> int {
+        let result = 1;
+        while n > 0 {
+            result *= n;
+            n -= 1;
+        }
+        ret result;
+    }
+
+Several differences from C stand out. Types do not come before, but
+after variable names (preceded by a colon). In local variables
+(introduced with `let`), they are optional, and will be inferred when
+left off. Constructs like `while` and `if` do not require parenthesis
+around the condition (though they allow them). Also, there's a
+tendency towards aggressive abbreviation in the keywords—`fn` for
+function, `ret` for return.
+
+You should, however, not conclude that Rust is simply an evolution of
+C. As will become clear in the rest of this tutorial, it goes into
+quite a different direction.
diff --git a/doc/tutorial/lib/markdown.js b/doc/tutorial/lib/markdown.js
new file mode 100644
index 00000000000..f19d0529953
--- /dev/null
+++ b/doc/tutorial/lib/markdown.js
@@ -0,0 +1,1466 @@
+// Released under MIT license
+// Copyright (c) 2009-2010 Dominic Baggott
+// Copyright (c) 2009-2010 Ash Berlin
+// Copyright (c) 2011 Christoph Dorn <christoph@christophdorn.com> (http://www.christophdorn.com)
+
+(function( expose ) {
+
+/**
+ *  class Markdown
+ *
+ *  Markdown processing in Javascript done right. We have very particular views
+ *  on what constitutes 'right' which include:
+ *
+ *  - produces well-formed HTML (this means that em and strong nesting is
+ *    important)
+ *
+ *  - has an intermediate representation to allow processing of parsed data (We
+ *    in fact have two, both as [JsonML]: a markdown tree and an HTML tree).
+ *
+ *  - is easily extensible to add new dialects without having to rewrite the
+ *    entire parsing mechanics
+ *
+ *  - has a good test suite
+ *
+ *  This implementation fulfills all of these (except that the test suite could
+ *  do with expanding to automatically run all the fixtures from other Markdown
+ *  implementations.)
+ *
+ *  ##### Intermediate Representation
+ *
+ *  *TODO* Talk about this :) Its JsonML, but document the node names we use.
+ *
+ *  [JsonML]: http://jsonml.org/ "JSON Markup Language"
+ **/
+var Markdown = expose.Markdown = function Markdown(dialect) {
+  switch (typeof dialect) {
+    case "undefined":
+      this.dialect = Markdown.dialects.Gruber;
+      break;
+    case "object":
+      this.dialect = dialect;
+      break;
+    default:
+      if (dialect in Markdown.dialects) {
+        this.dialect = Markdown.dialects[dialect];
+      }
+      else {
+        throw new Error("Unknown Markdown dialect '" + String(dialect) + "'");
+      }
+      break;
+  }
+  this.em_state = [];
+  this.strong_state = [];
+  this.debug_indent = "";
+}
+
+/**
+ *  parse( markdown, [dialect] ) -> JsonML
+ *  - markdown (String): markdown string to parse
+ *  - dialect (String | Dialect): the dialect to use, defaults to gruber
+ *
+ *  Parse `markdown` and return a markdown document as a Markdown.JsonML tree.
+ **/
+expose.parse = function( source, dialect ) {
+  // dialect will default if undefined
+  var md = new Markdown( dialect );
+  return md.toTree( source );
+}
+
+/**
+ *  toHTML( markdown, [dialect]  ) -> String
+ *  toHTML( md_tree ) -> String
+ *  - markdown (String): markdown string to parse
+ *  - md_tree (Markdown.JsonML): parsed markdown tree
+ *
+ *  Take markdown (either as a string or as a JsonML tree) and run it through
+ *  [[toHTMLTree]] then turn it into a well-formated HTML fragment.
+ **/
+expose.toHTML = function toHTML( source , dialect , options ) {
+  var input = expose.toHTMLTree( source , dialect , options );
+
+  return expose.renderJsonML( input );
+}
+
+/**
+ *  toHTMLTree( markdown, [dialect] ) -> JsonML
+ *  toHTMLTree( md_tree ) -> JsonML
+ *  - markdown (String): markdown string to parse
+ *  - dialect (String | Dialect): the dialect to use, defaults to gruber
+ *  - md_tree (Markdown.JsonML): parsed markdown tree
+ *
+ *  Turn markdown into HTML, represented as a JsonML tree. If a string is given
+ *  to this function, it is first parsed into a markdown tree by calling
+ *  [[parse]].
+ **/
+expose.toHTMLTree = function toHTMLTree( input, dialect , options ) {
+  // convert string input to an MD tree
+  if ( typeof input ==="string" ) input = this.parse( input, dialect );
+
+  // Now convert the MD tree to an HTML tree
+
+  // remove references from the tree
+  var attrs = extract_attr( input ),
+      refs = {};
+
+  if ( attrs && attrs.references ) {
+    refs = attrs.references;
+  }
+
+  var html = convert_tree_to_html( input, refs , options );
+  merge_text_nodes( html );
+  return html;
+}
+
+var mk_block = Markdown.mk_block = function(block, trail, line) {
+  // Be helpful for default case in tests.
+  if ( arguments.length == 1 ) trail = "\n\n";
+
+  var s = new String(block);
+  s.trailing = trail;
+  // To make it clear its not just a string
+  s.toSource = function() {
+    return "Markdown.mk_block( " +
+            uneval(block) +
+            ", " +
+            uneval(trail) +
+            ", " +
+            uneval(line) +
+            " )"
+  }
+
+  if (line != undefined)
+    s.lineNumber = line;
+
+  return s;
+}
+
+function count_lines( str ) {
+  var n = 0, i = -1;;
+  while ( ( i = str.indexOf('\n', i+1) ) != -1) n++;
+  return n;
+}
+
+// Internal - split source into rough blocks
+Markdown.prototype.split_blocks = function splitBlocks( input, startLine ) {
+  // [\s\S] matches _anything_ (newline or space)
+  var re = /([\s\S]+?)($|\n(?:\s*\n|$)+)/g,
+      blocks = [],
+      m;
+
+  var line_no = 1;
+
+  if ( ( m = /^(\s*\n)/.exec(input) ) != null ) {
+    // skip (but count) leading blank lines
+    line_no += count_lines( m[0] );
+    re.lastIndex = m[0].length;
+  }
+
+  while ( ( m = re.exec(input) ) != null ) {
+    blocks.push( mk_block( m[1], m[2], line_no ) );
+    line_no += count_lines( m[0] );
+  }
+
+  return blocks;
+}
+
+/**
+ *  Markdown#processBlock( block, next ) -> undefined | [ JsonML, ... ]
+ *  - block (String): the block to process
+ *  - next (Array): the following blocks
+ *
+ * Process `block` and return an array of JsonML nodes representing `block`.
+ *
+ * It does this by asking each block level function in the dialect to process
+ * the block until one can. Succesful handling is indicated by returning an
+ * array (with zero or more JsonML nodes), failure by a false value.
+ *
+ * Blocks handlers are responsible for calling [[Markdown#processInline]]
+ * themselves as appropriate.
+ *
+ * If the blocks were split incorrectly or adjacent blocks need collapsing you
+ * can adjust `next` in place using shift/splice etc.
+ *
+ * If any of this default behaviour is not right for the dialect, you can
+ * define a `__call__` method on the dialect that will get invoked to handle
+ * the block processing.
+ */
+Markdown.prototype.processBlock = function processBlock( block, next ) {
+  var cbs = this.dialect.block,
+      ord = cbs.__order__;
+
+  if ( "__call__" in cbs ) {
+    return cbs.__call__.call(this, block, next);
+  }
+
+  for ( var i = 0; i < ord.length; i++ ) {
+    //D:this.debug( "Testing", ord[i] );
+    var res = cbs[ ord[i] ].call( this, block, next );
+    if ( res ) {
+      //D:this.debug("  matched");
+      if ( !isArray(res) || ( res.length > 0 && !( isArray(res[0]) ) ) )
+        this.debug(ord[i], "didn't return a proper array");
+      //D:this.debug( "" );
+      return res;
+    }
+  }
+
+  // Uhoh! no match! Should we throw an error?
+  return [];
+}
+
+Markdown.prototype.processInline = function processInline( block ) {
+  return this.dialect.inline.__call__.call( this, String( block ) );
+}
+
+/**
+ *  Markdown#toTree( source ) -> JsonML
+ *  - source (String): markdown source to parse
+ *
+ *  Parse `source` into a JsonML tree representing the markdown document.
+ **/
+// custom_tree means set this.tree to `custom_tree` and restore old value on return
+Markdown.prototype.toTree = function toTree( source, custom_root ) {
+  var blocks = source instanceof Array
+             ? source
+             : this.split_blocks( source );
+
+  // Make tree a member variable so its easier to mess with in extensions
+  var old_tree = this.tree;
+  try {
+    this.tree = custom_root || this.tree || [ "markdown" ];
+
+    blocks:
+    while ( blocks.length ) {
+      var b = this.processBlock( blocks.shift(), blocks );
+
+      // Reference blocks and the like won't return any content
+      if ( !b.length ) continue blocks;
+
+      this.tree.push.apply( this.tree, b );
+    }
+    return this.tree;
+  }
+  finally {
+    if ( custom_root )
+      this.tree = old_tree;
+  }
+
+}
+
+// Noop by default
+Markdown.prototype.debug = function () {
+  var args = Array.prototype.slice.call( arguments);
+  args.unshift(this.debug_indent);
+  if (typeof print !== "undefined")
+      print.apply( print, args );
+  if (typeof console !== "undefined" && typeof console.log !== "undefined")
+      console.log.apply( null, args );
+}
+
+Markdown.prototype.loop_re_over_block = function( re, block, cb ) {
+  // Dont use /g regexps with this
+  var m,
+      b = block.valueOf();
+
+  while ( b.length && (m = re.exec(b) ) != null) {
+    b = b.substr( m[0].length );
+    cb.call(this, m);
+  }
+  return b;
+}
+
+/**
+ * Markdown.dialects
+ *
+ * Namespace of built-in dialects.
+ **/
+Markdown.dialects = {};
+
+/**
+ * Markdown.dialects.Gruber
+ *
+ * The default dialect that follows the rules set out by John Gruber's
+ * markdown.pl as closely as possible. Well actually we follow the behaviour of
+ * that script which in some places is not exactly what the syntax web page
+ * says.
+ **/
+Markdown.dialects.Gruber = {
+  block: {
+    atxHeader: function atxHeader( block, next ) {
+      var m = block.match( /^(#{1,6})\s*(.*?)\s*#*\s*(?:\n|$)/ );
+
+      if ( !m ) return undefined;
+
+      var header = [ "header", { level: m[ 1 ].length } ];
+      Array.prototype.push.apply(header, this.processInline(m[ 2 ]));
+
+      if ( m[0].length < block.length )
+        next.unshift( mk_block( block.substr( m[0].length ), block.trailing, block.lineNumber + 2 ) );
+
+      return [ header ];
+    },
+
+    setextHeader: function setextHeader( block, next ) {
+      var m = block.match( /^(.*)\n([-=])\2\2+(?:\n|$)/ );
+
+      if ( !m ) return undefined;
+
+      var level = ( m[ 2 ] === "=" ) ? 1 : 2;
+      var header = [ "header", { level : level }, m[ 1 ] ];
+
+      if ( m[0].length < block.length )
+        next.unshift( mk_block( block.substr( m[0].length ), block.trailing, block.lineNumber + 2 ) );
+
+      return [ header ];
+    },
+
+    code: function code( block, next ) {
+      // |    Foo
+      // |bar
+      // should be a code block followed by a paragraph. Fun
+      //
+      // There might also be adjacent code block to merge.
+
+      var ret = [],
+          re = /^(?: {0,3}\t| {4})(.*)\n?/,
+          lines;
+
+      // 4 spaces + content
+      var m = block.match( re );
+
+      if ( !m ) return undefined;
+
+      block_search:
+      do {
+        // Now pull out the rest of the lines
+        var b = this.loop_re_over_block(
+                  re, block.valueOf(), function( m ) { ret.push( m[1] ) } );
+
+        if (b.length) {
+          // Case alluded to in first comment. push it back on as a new block
+          next.unshift( mk_block(b, block.trailing) );
+          break block_search;
+        }
+        else if (next.length) {
+          // Check the next block - it might be code too
+          var m = next[0].match( re );
+
+          if ( !m ) break block_search;
+
+          // Pull how how many blanks lines follow - minus two to account for .join
+          ret.push ( block.trailing.replace(/[^\n]/g, '').substring(2) );
+
+          block = next.shift();
+        }
+        else
+          break block_search;
+      } while (true);
+
+      return [ [ "code_block", ret.join("\n") ] ];
+    },
+
+    horizRule: function horizRule( block, next ) {
+      // this needs to find any hr in the block to handle abutting blocks
+      var m = block.match( /^(?:([\s\S]*?)\n)?[ \t]*([-_*])(?:[ \t]*\2){2,}[ \t]*(?:\n([\s\S]*))?$/ );
+
+      if ( !m ) {
+        return undefined;
+      }
+
+      var jsonml = [ [ "hr" ] ];
+
+      // if there's a leading abutting block, process it
+      if ( m[ 1 ] ) {
+        jsonml.unshift.apply( jsonml, this.processBlock( m[ 1 ], [] ) );
+      }
+
+      // if there's a trailing abutting block, stick it into next
+      if ( m[ 3 ] ) {
+        next.unshift( mk_block( m[ 3 ] ) );
+      }
+
+      return jsonml;
+    },
+
+    // There are two types of lists. Tight and loose. Tight lists have no whitespace
+    // between the items (and result in text just in the <li>) and loose lists,
+    // which have an empty line between list items, resulting in (one or more)
+    // paragraphs inside the <li>.
+    //
+    // There are all sorts weird edge cases about the original markdown.pl's
+    // handling of lists:
+    //
+    // * Nested lists are supposed to be indented by four chars per level. But
+    //   if they aren't, you can get a nested list by indenting by less than
+    //   four so long as the indent doesn't match an indent of an existing list
+    //   item in the 'nest stack'.
+    //
+    // * The type of the list (bullet or number) is controlled just by the
+    //    first item at the indent. Subsequent changes are ignored unless they
+    //    are for nested lists
+    //
+    lists: (function( ) {
+      // Use a closure to hide a few variables.
+      var any_list = "[*+-]|\\d\\.",
+          bullet_list = /[*+-]/,
+          number_list = /\d+\./,
+          // Capture leading indent as it matters for determining nested lists.
+          is_list_re = new RegExp( "^( {0,3})(" + any_list + ")[ \t]+" ),
+          indent_re = "(?: {0,3}\\t| {4})";
+
+      // TODO: Cache this regexp for certain depths.
+      // Create a regexp suitable for matching an li for a given stack depth
+      function regex_for_depth( depth ) {
+
+        return new RegExp(
+          // m[1] = indent, m[2] = list_type
+          "(?:^(" + indent_re + "{0," + depth + "} {0,3})(" + any_list + ")\\s+)|" +
+          // m[3] = cont
+          "(^" + indent_re + "{0," + (depth-1) + "}[ ]{0,4})"
+        );
+      }
+      function expand_tab( input ) {
+        return input.replace( / {0,3}\t/g, "    " );
+      }
+
+      // Add inline content `inline` to `li`. inline comes from processInline
+      // so is an array of content
+      function add(li, loose, inline, nl) {
+        if (loose) {
+            li.push( [ "para" ].concat(inline) );
+          return;
+        }
+        // Hmmm, should this be any block level element or just paras?
+        var add_to = li[li.length -1] instanceof Array && li[li.length - 1][0] == "para"
+                   ? li[li.length -1]
+                   : li;
+
+        // If there is already some content in this list, add the new line in
+        if (nl && li.length > 1) inline.unshift(nl);
+
+        for (var i=0; i < inline.length; i++) {
+          var what = inline[i],
+              is_str = typeof what == "string";
+          if (is_str && add_to.length > 1 && typeof add_to[add_to.length-1] == "string" )
+          {
+            add_to[ add_to.length-1 ] += what;
+          }
+          else {
+            add_to.push( what );
+          }
+        }
+      }
+
+      // contained means have an indent greater than the current one. On
+      // *every* line in the block
+      function get_contained_blocks( depth, blocks ) {
+
+        var re = new RegExp( "^(" + indent_re + "{" + depth + "}.*?\\n?)*$" ),
+            replace = new RegExp("^" + indent_re + "{" + depth + "}", "gm"),
+            ret = [];
+
+        while ( blocks.length > 0 ) {
+          if ( re.exec( blocks[0] ) ) {
+            var b = blocks.shift(),
+                // Now remove that indent
+                x = b.replace( replace, "");
+
+            ret.push( mk_block( x, b.trailing, b.lineNumber ) );
+          }
+          break;
+        }
+        return ret;
+      }
+
+      // passed to stack.forEach to turn list items up the stack into paras
+      function paragraphify(s, i, stack) {
+        var list = s.list;
+        var last_li = list[list.length-1];
+
+        if (last_li[1] instanceof Array && last_li[1][0] == "para") {
+          return;
+        }
+        if (i+1 == stack.length) {
+          // Last stack frame
+          // Keep the same array, but replace the contents
+          last_li.push( ["para"].concat( last_li.splice(1) ) );
+        }
+        else {
+          var sublist = last_li.pop();
+          last_li.push( ["para"].concat( last_li.splice(1) ), sublist );
+        }
+      }
+
+      // The matcher function
+      return function( block, next ) {
+        var m = block.match( is_list_re );
+        if ( !m ) return undefined;
+
+        function make_list( m ) {
+          var list = bullet_list.exec( m[2] )
+                   ? ["bulletlist"]
+                   : ["numberlist"];
+
+          stack.push( { list: list, indent: m[1] } );
+          return list;
+        }
+
+
+        var stack = [], // Stack of lists for nesting.
+            list = make_list( m ),
+            last_li,
+            loose = false,
+            ret = [ stack[0].list ];
+
+        // Loop to search over block looking for inner block elements and loose lists
+        loose_search:
+        while( true ) {
+          // Split into lines preserving new lines at end of line
+          var lines = block.split( /(?=\n)/ );
+
+          // We have to grab all lines for a li and call processInline on them
+          // once as there are some inline things that can span lines.
+          var li_accumulate = "";
+
+          // Loop over the lines in this block looking for tight lists.
+          tight_search:
+          for (var line_no=0; line_no < lines.length; line_no++) {
+            var nl = "",
+                l = lines[line_no].replace(/^\n/, function(n) { nl = n; return "" });
+
+            // TODO: really should cache this
+            var line_re = regex_for_depth( stack.length );
+
+            m = l.match( line_re );
+            //print( "line:", uneval(l), "\nline match:", uneval(m) );
+
+            // We have a list item
+            if ( m[1] !== undefined ) {
+              // Process the previous list item, if any
+              if ( li_accumulate.length ) {
+                add( last_li, loose, this.processInline( li_accumulate ), nl );
+                // Loose mode will have been dealt with. Reset it
+                loose = false;
+                li_accumulate = "";
+              }
+
+              m[1] = expand_tab( m[1] );
+              var wanted_depth = Math.floor(m[1].length/4)+1;
+              //print( "want:", wanted_depth, "stack:", stack.length);
+              if ( wanted_depth > stack.length ) {
+                // Deep enough for a nested list outright
+                //print ( "new nested list" );
+                list = make_list( m );
+                last_li.push( list );
+                last_li = list[1] = [ "listitem" ];
+              }
+              else {
+                // We aren't deep enough to be strictly a new level. This is
+                // where Md.pl goes nuts. If the indent matches a level in the
+                // stack, put it there, else put it one deeper then the
+                // wanted_depth deserves.
+                var found = stack.some(function(s, i) {
+                  if ( s.indent != m[1] ) return false;
+                  list = s.list;     // Found the level we want
+                  stack.splice(i+1); // Remove the others
+                  //print("found");
+                  return true;       // And stop looping
+                });
+
+                if (!found) {
+                  //print("not found. l:", uneval(l));
+                  wanted_depth++;
+                  if (wanted_depth <= stack.length) {
+                    stack.splice(wanted_depth);
+                    //print("Desired depth now", wanted_depth, "stack:", stack.length);
+                    list = stack[wanted_depth-1].list;
+                    //print("list:", uneval(list) );
+                  }
+                  else {
+                    //print ("made new stack for messy indent");
+                    list = make_list(m);
+                    last_li.push(list);
+                  }
+                }
+
+                //print( uneval(list), "last", list === stack[stack.length-1].list );
+                last_li = [ "listitem" ];
+                list.push(last_li);
+              } // end depth of shenegains
+              nl = "";
+            }
+
+            // Add content
+            if (l.length > m[0].length) {
+              li_accumulate += nl + l.substr( m[0].length );
+            }
+          } // tight_search
+
+          if ( li_accumulate.length ) {
+            add( last_li, loose, this.processInline( li_accumulate ), nl );
+            // Loose mode will have been dealt with. Reset it
+            loose = false;
+            li_accumulate = "";
+          }
+
+          // Look at the next block - we might have a loose list. Or an extra
+          // paragraph for the current li
+          var contained = get_contained_blocks( stack.length, next );
+
+          // Deal with code blocks or properly nested lists
+          if (contained.length > 0) {
+            // Make sure all listitems up the stack are paragraphs
+            stack.forEach( paragraphify, this );
+
+            last_li.push.apply( last_li, this.toTree( contained, [] ) );
+          }
+
+          var next_block = next[0] && next[0].valueOf() || "";
+
+          if ( next_block.match(is_list_re) || next_block.match( /^ / ) ) {
+            block = next.shift();
+
+            // Check for an HR following a list: features/lists/hr_abutting
+            var hr = this.dialect.block.horizRule( block, next );
+
+            if (hr) {
+              ret.push.apply(ret, hr);
+              break;
+            }
+
+            // Make sure all listitems up the stack are paragraphs
+            stack.forEach( paragraphify , this );
+
+            loose = true;
+            continue loose_search;
+          }
+          break;
+        } // loose_search
+
+        return ret;
+      }
+    })(),
+
+    blockquote: function blockquote( block, next ) {
+      if ( !block.match( /^>/m ) )
+        return undefined;
+
+      var jsonml = [];
+
+      // separate out the leading abutting block, if any
+      if ( block[ 0 ] != ">" ) {
+        var lines = block.split( /\n/ ),
+            prev = [];
+
+        // keep shifting lines until you find a crotchet
+        while ( lines.length && lines[ 0 ][ 0 ] != ">" ) {
+            prev.push( lines.shift() );
+        }
+
+        // reassemble!
+        block = lines.join( "\n" );
+        jsonml.push.apply( jsonml, this.processBlock( prev.join( "\n" ), [] ) );
+      }
+
+      // if the next block is also a blockquote merge it in
+      while ( next.length && next[ 0 ][ 0 ] == ">" ) {
+        var b = next.shift();
+        block += block.trailing + b;
+        block.trailing = b.trailing;
+      }
+
+      // Strip off the leading "> " and re-process as a block.
+      var input = block.replace( /^> ?/gm, '' ),
+          old_tree = this.tree;
+      jsonml.push( this.toTree( input, [ "blockquote" ] ) );
+
+      return jsonml;
+    },
+
+    referenceDefn: function referenceDefn( block, next) {
+      var re = /^\s*\[(.*?)\]:\s*(\S+)(?:\s+(?:(['"])(.*?)\3|\((.*?)\)))?\n?/;
+      // interesting matches are [ , ref_id, url, , title, title ]
+
+      if ( !block.match(re) )
+        return undefined;
+
+      // make an attribute node if it doesn't exist
+      if ( !extract_attr( this.tree ) ) {
+        this.tree.splice( 1, 0, {} );
+      }
+
+      var attrs = extract_attr( this.tree );
+
+      // make a references hash if it doesn't exist
+      if ( attrs.references === undefined ) {
+        attrs.references = {};
+      }
+
+      var b = this.loop_re_over_block(re, block, function( m ) {
+
+        if ( m[2] && m[2][0] == '<' && m[2][m[2].length-1] == '>' )
+          m[2] = m[2].substring( 1, m[2].length - 1 );
+
+        var ref = attrs.references[ m[1].toLowerCase() ] = {
+          href: m[2]
+        };
+
+        if (m[4] !== undefined)
+          ref.title = m[4];
+        else if (m[5] !== undefined)
+          ref.title = m[5];
+
+      } );
+
+      if (b.length)
+        next.unshift( mk_block( b, block.trailing ) );
+
+      return [];
+    },
+
+    para: function para( block, next ) {
+      // everything's a para!
+      return [ ["para"].concat( this.processInline( block ) ) ];
+    }
+  }
+}
+
+Markdown.dialects.Gruber.inline = {
+    __call__: function inline( text, patterns ) {
+      // Hmmm - should this function be directly in Md#processInline, or
+      // conversely, should Md#processBlock be moved into block.__call__ too
+      var out = [ ],
+          m,
+          // Look for the next occurange of a special character/pattern
+          re = new RegExp( "([\\s\\S]*?)(" + (patterns.source || patterns) + ")", "g" ),
+          lastIndex = 0;
+
+      //D:var self = this;
+      //D:self.debug("processInline:", uneval(text) );
+      function add(x) {
+        //D:self.debug("  adding output", uneval(x));
+        if (typeof x == "string" && typeof out[out.length-1] == "string")
+          out[ out.length-1 ] += x;
+        else
+          out.push(x);
+      }
+
+      while ( ( m = re.exec(text) ) != null) {
+        if ( m[1] ) add( m[1] ); // Some un-interesting text matched
+        else        m[1] = { length: 0 }; // Or there was none, but make m[1].length == 0
+
+        var res;
+        if ( m[2] in this.dialect.inline ) {
+          res = this.dialect.inline[ m[2] ].call(
+                    this,
+                    text.substr( m.index + m[1].length ), m, out );
+        }
+        // Default for now to make dev easier. just slurp special and output it.
+        res = res || [ m[2].length, m[2] ];
+
+        var len = res.shift();
+        // Update how much input was consumed
+        re.lastIndex += ( len - m[2].length );
+
+        // Add children
+        res.forEach(add);
+
+        lastIndex = re.lastIndex;
+      }
+
+      // Add last 'boring' chunk
+      if ( text.length > lastIndex )
+        add( text.substr( lastIndex ) );
+
+      return out;
+    },
+
+    "\\": function escaped( text ) {
+      // [ length of input processed, node/children to add... ]
+      // Only esacape: \ ` * _ { } [ ] ( ) # * + - . !
+      if ( text.match( /^\\[\\`\*_{}\[\]()#\+.!\-]/ ) )
+        return [ 2, text[1] ];
+      else
+        // Not an esacpe
+        return [ 1, "\\" ];
+    },
+
+    "![": function image( text ) {
+      // ![Alt text](/path/to/img.jpg "Optional title")
+      //      1          2            3       4         <--- captures
+      var m = text.match( /^!\[(.*?)\][ \t]*\([ \t]*(\S*)(?:[ \t]+(["'])(.*?)\3)?[ \t]*\)/ );
+
+      if ( m ) {
+        if ( m[2] && m[2][0] == '<' && m[2][m[2].length-1] == '>' )
+          m[2] = m[2].substring( 1, m[2].length - 1 );
+
+        m[2] == this.dialect.inline.__call__.call( this, m[2], /\\/ )[0];
+
+        var attrs = { alt: m[1], href: m[2] || "" };
+        if ( m[4] !== undefined)
+          attrs.title = m[4];
+
+        return [ m[0].length, [ "img", attrs ] ];
+      }
+
+      // ![Alt text][id]
+      m = text.match( /^!\[(.*?)\][ \t]*\[(.*?)\]/ );
+
+      if ( m ) {
+        // We can't check if the reference is known here as it likely wont be
+        // found till after. Check it in md tree->hmtl tree conversion
+        return [ m[0].length, [ "img_ref", { alt: m[1], ref: m[2].toLowerCase(), text: m[0] } ] ];
+      }
+
+      // Just consume the '!['
+      return [ 2, "![" ];
+    },
+
+    "[": function link( text ) {
+      // [link text](/path/to/img.jpg "Optional title")
+      //      1          2            3       4         <--- captures
+      var m = text.match( /^\[([\s\S]*?)\][ \t]*\([ \t]*(\S+)(?:[ \t]+(["'])(.*?)\3)?[ \t]*\)/ );
+
+      if ( m ) {
+        if ( m[2] && m[2][0] == '<' && m[2][m[2].length-1] == '>' )
+          m[2] = m[2].substring( 1, m[2].length - 1 );
+
+        // Process escapes only
+        m[2] = this.dialect.inline.__call__.call( this, m[2], /\\/ )[0];
+
+        var attrs = { href: m[2] || "" };
+        if ( m[4] !== undefined)
+          attrs.title = m[4];
+
+        var link = [ "link", attrs ];
+        Array.prototype.push.apply( link, this.processInline( m[1] ) );
+        return [ m[0].length, link ];
+      }
+
+      // [Alt text][id]
+      // [Alt text] [id]
+      // [id]
+      m = text.match( /^\[([\s\S]*?)\](?: ?\[(.*?)\])?/ );
+
+      if ( m ) {
+        // [id] case, text == id
+        if ( m[2] === undefined || m[2] === "" ) m[2] = m[1];
+
+        attrs = { ref: m[ 2 ].toLowerCase(),  original: m[ 0 ] };
+        link = [ "link_ref", attrs ];
+        Array.prototype.push.apply( link, this.processInline( m[1] ) );
+
+        // We can't check if the reference is known here as it likely wont be
+        // found till after. Check it in md tree->hmtl tree conversion.
+        // Store the original so that conversion can revert if the ref isn't found.
+        return [
+          m[ 0 ].length,
+          link
+        ];
+      }
+
+      // Just consume the '['
+      return [ 1, "[" ];
+    },
+
+
+    "<": function autoLink( text ) {
+      var m;
+
+      if ( ( m = text.match( /^<(?:((https?|ftp|mailto):[^>]+)|(.*?@.*?\.[a-zA-Z]+))>/ ) ) != null ) {
+        if ( m[3] ) {
+          return [ m[0].length, [ "link", { href: "mailto:" + m[3] }, m[3] ] ];
+
+        }
+        else if ( m[2] == "mailto" ) {
+          return [ m[0].length, [ "link", { href: m[1] }, m[1].substr("mailto:".length ) ] ];
+        }
+        else
+          return [ m[0].length, [ "link", { href: m[1] }, m[1] ] ];
+      }
+
+      return [ 1, "<" ];
+    },
+
+    "`": function inlineCode( text ) {
+      // Inline code block. as many backticks as you like to start it
+      // Always skip over the opening ticks.
+      var m = text.match( /(`+)(([\s\S]*?)\1)/ );
+
+      if ( m && m[2] )
+        return [ m[1].length + m[2].length, [ "inlinecode", m[3] ] ];
+      else {
+        // TODO: No matching end code found - warn!
+        return [ 1, "`" ];
+      }
+    },
+
+    "  \n": function lineBreak( text ) {
+      return [ 3, [ "linebreak" ] ];
+    }
+
+}
+
+// Meta Helper/generator method for em and strong handling
+function strong_em( tag, md ) {
+
+  var state_slot = tag + "_state",
+      other_slot = tag == "strong" ? "em_state" : "strong_state";
+
+  function CloseTag(len) {
+    this.len_after = len;
+    this.name = "close_" + md;
+  }
+
+  return function ( text, orig_match ) {
+
+    if (this[state_slot][0] == md) {
+      // Most recent em is of this type
+      //D:this.debug("closing", md);
+      this[state_slot].shift();
+
+      // "Consume" everything to go back to the recrusion in the else-block below
+      return[ text.length, new CloseTag(text.length-md.length) ];
+    }
+    else {
+      // Store a clone of the em/strong states
+      var other = this[other_slot].slice(),
+          state = this[state_slot].slice();
+
+      this[state_slot].unshift(md);
+
+      //D:this.debug_indent += "  ";
+
+      // Recurse
+      var res = this.processInline( text.substr( md.length ) );
+      //D:this.debug_indent = this.debug_indent.substr(2);
+
+      var last = res[res.length - 1];
+
+      //D:this.debug("processInline from", tag + ": ", uneval( res ) );
+
+      var check = this[state_slot].shift();
+      if (last instanceof CloseTag) {
+        res.pop();
+        // We matched! Huzzah.
+        var consumed = text.length - last.len_after;
+        return [ consumed, [ tag ].concat(res) ];
+      }
+      else {
+        // Restore the state of the other kind. We might have mistakenly closed it.
+        this[other_slot] = other;
+        this[state_slot] = state;
+
+        // We can't reuse the processed result as it could have wrong parsing contexts in it.
+        return [ md.length, md ];
+      }
+    }
+  } // End returned function
+}
+
+Markdown.dialects.Gruber.inline["**"] = strong_em("strong", "**");
+Markdown.dialects.Gruber.inline["__"] = strong_em("strong", "__");
+Markdown.dialects.Gruber.inline["*"]  = strong_em("em", "*");
+Markdown.dialects.Gruber.inline["_"]  = strong_em("em", "_");
+
+
+// Build default order from insertion order.
+Markdown.buildBlockOrder = function(d) {
+  var ord = [];
+  for ( var i in d ) {
+    if ( i == "__order__" || i == "__call__" ) continue;
+    ord.push( i );
+  }
+  d.__order__ = ord;
+}
+
+// Build patterns for inline matcher
+Markdown.buildInlinePatterns = function(d) {
+  var patterns = [];
+
+  for ( var i in d ) {
+    if (i == "__call__") continue;
+    var l = i.replace( /([\\.*+?|()\[\]{}])/g, "\\$1" )
+             .replace( /\n/, "\\n" );
+    patterns.push( i.length == 1 ? l : "(?:" + l + ")" );
+  }
+
+  patterns = patterns.join("|");
+  //print("patterns:", uneval( patterns ) );
+
+  var fn = d.__call__;
+  d.__call__ = function(text, pattern) {
+    if (pattern != undefined)
+      return fn.call(this, text, pattern);
+    else
+      return fn.call(this, text, patterns);
+  }
+}
+
+// Helper function to make sub-classing a dialect easier
+Markdown.subclassDialect = function( d ) {
+  function Block() {};
+  Block.prototype = d.block;
+  function Inline() {};
+  Inline.prototype = d.inline;
+
+  return { block: new Block(), inline: new Inline() };
+}
+
+Markdown.buildBlockOrder ( Markdown.dialects.Gruber.block );
+Markdown.buildInlinePatterns( Markdown.dialects.Gruber.inline );
+
+Markdown.dialects.Maruku = Markdown.subclassDialect( Markdown.dialects.Gruber );
+
+Markdown.dialects.Maruku.block.document_meta = function document_meta( block, next ) {
+  // we're only interested in the first block
+  if ( block.lineNumber > 1 ) return undefined;
+
+  // document_meta blocks consist of one or more lines of `Key: Value\n`
+  if ( ! block.match( /^(?:\w+:.*\n)*\w+:.*$/ ) ) return undefined;
+
+  // make an attribute node if it doesn't exist
+  if ( !extract_attr( this.tree ) ) {
+    this.tree.splice( 1, 0, {} );
+  }
+
+  var pairs = block.split( /\n/ );
+  for ( p in pairs ) {
+    var m = pairs[ p ].match( /(\w+):\s*(.*)$/ ),
+        key = m[ 1 ].toLowerCase(),
+        value = m[ 2 ];
+
+    this.tree[ 1 ][ key ] = value;
+  }
+
+  // document_meta produces no content!
+  return [];
+}
+
+Markdown.dialects.Maruku.block.block_meta = function block_meta( block, next ) {
+  // check if the last line of the block is an meta hash
+  var m = block.match( /(^|\n) {0,3}\{:\s*((?:\\\}|[^\}])*)\s*\}$/ );
+  if ( !m ) return undefined;
+
+  // process the meta hash
+  var attr = process_meta_hash( m[ 2 ] );
+
+  // if we matched ^ then we need to apply meta to the previous block
+  if ( m[ 1 ] === "" ) {
+    var node = this.tree[ this.tree.length - 1 ],
+        hash = extract_attr( node );
+
+    // if the node is a string (rather than JsonML), bail
+    if ( typeof node === "string" ) return undefined;
+
+    // create the attribute hash if it doesn't exist
+    if ( !hash ) {
+      hash = {};
+      node.splice( 1, 0, hash );
+    }
+
+    // add the attributes in
+    for ( a in attr ) {
+      hash[ a ] = attr[ a ];
+    }
+
+    // return nothing so the meta hash is removed
+    return [];
+  }
+
+  // pull the meta hash off the block and process what's left
+  var b = block.replace( /\n.*$/, "" ),
+      result = this.processBlock( b, [] );
+
+  // get or make the attributes hash
+  var hash = extract_attr( result[ 0 ] );
+  if ( !hash ) {
+    hash = {};
+    result[ 0 ].splice( 1, 0, hash );
+  }
+
+  // attach the attributes to the block
+  for ( a in attr ) {
+    hash[ a ] = attr[ a ];
+  }
+
+  return result;
+}
+
+Markdown.dialects.Maruku.block.definition_list = function definition_list( block, next ) {
+  // one or more terms followed by one or more definitions, in a single block
+  var tight = /^((?:[^\s:].*\n)+):\s+([^]+)$/,
+      list = [ "dl" ];
+
+  // see if we're dealing with a tight or loose block
+  if ( ( m = block.match( tight ) ) ) {
+    // pull subsequent tight DL blocks out of `next`
+    var blocks = [ block ];
+    while ( next.length && tight.exec( next[ 0 ] ) ) {
+      blocks.push( next.shift() );
+    }
+
+    for ( var b = 0; b < blocks.length; ++b ) {
+      var m = blocks[ b ].match( tight ),
+          terms = m[ 1 ].replace( /\n$/, "" ).split( /\n/ ),
+          defns = m[ 2 ].split( /\n:\s+/ );
+
+      // print( uneval( m ) );
+
+      for ( var i = 0; i < terms.length; ++i ) {
+        list.push( [ "dt" ].concat(this.processInline(terms[i])));
+      }
+
+      for ( var i = 0; i < defns.length; ++i ) {
+        // run inline processing over the definition
+        list.push( [ "dd" ].concat( this.processInline( defns[ i ].replace( /(\n)\s+/, "$1" ) ) ) );
+      }
+    }
+  }
+  else {
+    return undefined;
+  }
+
+  return [ list ];
+}
+
+Markdown.dialects.Maruku.inline[ "{:" ] = function inline_meta( text, matches, out ) {
+  if ( !out.length ) {
+    return [ 2, "{:" ];
+  }
+
+  // get the preceeding element
+  var before = out[ out.length - 1 ];
+
+  if ( typeof before === "string" ) {
+    return [ 2, "{:" ];
+  }
+
+  // match a meta hash
+  var m = text.match( /^\{:\s*((?:\\\}|[^\}])*)\s*\}/ );
+
+  // no match, false alarm
+  if ( !m ) {
+    return [ 2, "{:" ];
+  }
+
+  // attach the attributes to the preceeding element
+  var meta = process_meta_hash( m[ 1 ] ),
+      attr = extract_attr( before );
+
+  if ( !attr ) {
+    attr = {};
+    before.splice( 1, 0, attr );
+  }
+
+  for ( var k in meta ) {
+    attr[ k ] = meta[ k ];
+  }
+
+  // cut out the string and replace it with nothing
+  return [ m[ 0 ].length, "" ];
+}
+
+Markdown.buildBlockOrder ( Markdown.dialects.Maruku.block );
+Markdown.buildInlinePatterns( Markdown.dialects.Maruku.inline );
+
+var isArray = expose.isArray = function(obj) {
+    return (obj instanceof Array || typeof obj === "array" || Array.isArray(obj));
+}
+
+function extract_attr( jsonml ) {
+  return isArray(jsonml)
+      && jsonml.length > 1
+      && typeof jsonml[ 1 ] === "object"
+      && !( isArray(jsonml[ 1 ]) )
+      ? jsonml[ 1 ]
+      : undefined;
+}
+
+function process_meta_hash( meta_string ) {
+  var meta = split_meta_hash( meta_string ),
+      attr = {};
+
+  for ( var i = 0; i < meta.length; ++i ) {
+    // id: #foo
+    if ( /^#/.test( meta[ i ] ) ) {
+      attr.id = meta[ i ].substring( 1 );
+    }
+    // class: .foo
+    else if ( /^\./.test( meta[ i ] ) ) {
+      // if class already exists, append the new one
+      if ( attr['class'] ) {
+        attr['class'] = attr['class'] + meta[ i ].replace( /./, " " );
+      }
+      else {
+        attr['class'] = meta[ i ].substring( 1 );
+      }
+    }
+    // attribute: foo=bar
+    else if ( /=/.test( meta[ i ] ) ) {
+      var s = meta[ i ].split( /=/ );
+      attr[ s[ 0 ] ] = s[ 1 ];
+    }
+  }
+
+  return attr;
+}
+
+function split_meta_hash( meta_string ) {
+  var meta = meta_string.split( "" ),
+      parts = [ "" ],
+      in_quotes = false;
+
+  while ( meta.length ) {
+    var letter = meta.shift();
+    switch ( letter ) {
+      case " " :
+        // if we're in a quoted section, keep it
+        if ( in_quotes ) {
+          parts[ parts.length - 1 ] += letter;
+        }
+        // otherwise make a new part
+        else {
+          parts.push( "" );
+        }
+        break;
+      case "'" :
+      case '"' :
+        // reverse the quotes and move straight on
+        in_quotes = !in_quotes;
+        break;
+      case "\\" :
+        // shift off the next letter to be used straight away.
+        // it was escaped so we'll keep it whatever it is
+        letter = meta.shift();
+      default :
+        parts[ parts.length - 1 ] += letter;
+        break;
+    }
+  }
+
+  return parts;
+}
+
+/**
+ *  renderJsonML( jsonml[, options] ) -> String
+ *  - jsonml (Array): JsonML array to render to XML
+ *  - options (Object): options
+ *
+ *  Converts the given JsonML into well-formed XML.
+ *
+ *  The options currently understood are:
+ *
+ *  - root (Boolean): wether or not the root node should be included in the
+ *    output, or just its children. The default `false` is to not include the
+ *    root itself.
+ */
+expose.renderJsonML = function( jsonml, options ) {
+  options = options || {};
+  // include the root element in the rendered output?
+  options.root = options.root || false;
+
+  var content = [];
+
+  if ( options.root ) {
+    content.push( render_tree( jsonml ) );
+  }
+  else {
+    jsonml.shift(); // get rid of the tag
+    if ( jsonml.length && typeof jsonml[ 0 ] === "object" && !( jsonml[ 0 ] instanceof Array ) ) {
+      jsonml.shift(); // get rid of the attributes
+    }
+
+    while ( jsonml.length ) {
+      content.push( render_tree( jsonml.shift() ) );
+    }
+  }
+
+  return content.join( "\n\n" );
+}
+
+function escapeHTML( text ) {
+  return text.replace( /&/g, "&amp;" )
+             .replace( /</g, "&lt;" )
+             .replace( />/g, "&gt;" )
+             .replace( /"/g, "&quot;" )
+             .replace( /'/g, "&#39;" );
+}
+
+function render_tree( jsonml ) {
+  // basic case
+  if ( typeof jsonml === "string" ) {
+    return escapeHTML( jsonml );
+  }
+
+  var tag = jsonml.shift(),
+      attributes = {},
+      content = [];
+
+  if ( jsonml.length && typeof jsonml[ 0 ] === "object" && !( jsonml[ 0 ] instanceof Array ) ) {
+    attributes = jsonml.shift();
+  }
+
+  while ( jsonml.length ) {
+    content.push( arguments.callee( jsonml.shift() ) );
+  }
+
+  var tag_attrs = "";
+  for ( var a in attributes ) {
+    tag_attrs += " " + a + '="' + escapeHTML( attributes[ a ] ) + '"';
+  }
+
+  // be careful about adding whitespace here for inline elements
+  return "<"+ tag + tag_attrs + ">" + content.join( "" ) + "</" + tag + ">";
+}
+
+function convert_tree_to_html( tree, references, options ) {
+  options = options || {};
+
+  // shallow clone
+  var jsonml = tree.slice( 0 );
+
+  if (typeof options.preprocessTreeNode === "function") {
+      jsonml = options.preprocessTreeNode(jsonml, references);
+  }
+
+  // Clone attributes if they exist
+  var attrs = extract_attr( jsonml );
+  if ( attrs ) {
+    jsonml[ 1 ] = {};
+    for ( var i in attrs ) {
+      jsonml[ 1 ][ i ] = attrs[ i ];
+    }
+    attrs = jsonml[ 1 ];
+  }
+
+  // basic case
+  if ( typeof jsonml === "string" ) {
+    return jsonml;
+  }
+
+  // convert this node
+  switch ( jsonml[ 0 ] ) {
+    case "header":
+      jsonml[ 0 ] = "h" + jsonml[ 1 ].level;
+      delete jsonml[ 1 ].level;
+      break;
+    case "bulletlist":
+      jsonml[ 0 ] = "ul";
+      break;
+    case "numberlist":
+      jsonml[ 0 ] = "ol";
+      break;
+    case "listitem":
+      jsonml[ 0 ] = "li";
+      break;
+    case "para":
+      jsonml[ 0 ] = "p";
+      break;
+    case "markdown":
+      jsonml[ 0 ] = "html";
+      if ( attrs ) delete attrs.references;
+      break;
+    case "code_block":
+      jsonml[ 0 ] = "pre";
+      var i = attrs ? 2 : 1;
+      var code = [ "code" ];
+      code.push.apply( code, jsonml.splice( i ) );
+      jsonml[ i ] = code;
+      break;
+    case "inlinecode":
+      jsonml[ 0 ] = "code";
+      break;
+    case "img":
+      jsonml[ 1 ].src = jsonml[ 1 ].href;
+      delete jsonml[ 1 ].href;
+      break;
+    case "linebreak":
+      jsonml[0] = "br";
+    break;
+    case "link":
+      jsonml[ 0 ] = "a";
+      break;
+    case "link_ref":
+      jsonml[ 0 ] = "a";
+
+      // grab this ref and clean up the attribute node
+      var ref = references[ attrs.ref ];
+
+      // if the reference exists, make the link
+      if ( ref ) {
+        delete attrs.ref;
+
+        // add in the href and title, if present
+        attrs.href = ref.href;
+        if ( ref.title ) {
+          attrs.title = ref.title;
+        }
+
+        // get rid of the unneeded original text
+        delete attrs.original;
+      }
+      // the reference doesn't exist, so revert to plain text
+      else {
+        return attrs.original;
+      }
+      break;
+  }
+
+  // convert all the children
+  var i = 1;
+
+  // deal with the attribute node, if it exists
+  if ( attrs ) {
+    // if there are keys, skip over it
+    for ( var key in jsonml[ 1 ] ) {
+      i = 2;
+    }
+    // if there aren't, remove it
+    if ( i === 1 ) {
+      jsonml.splice( i, 1 );
+    }
+  }
+
+  for ( ; i < jsonml.length; ++i ) {
+    jsonml[ i ] = arguments.callee( jsonml[ i ], references, options );
+  }
+
+  return jsonml;
+}
+
+
+// merges adjacent text nodes into a single node
+function merge_text_nodes( jsonml ) {
+  // skip the tag name and attribute hash
+  var i = extract_attr( jsonml ) ? 2 : 1;
+
+  while ( i < jsonml.length ) {
+    // if it's a string check the next item too
+    if ( typeof jsonml[ i ] === "string" ) {
+      if ( i + 1 < jsonml.length && typeof jsonml[ i + 1 ] === "string" ) {
+        // merge the second string into the first and remove it
+        jsonml[ i ] += jsonml.splice( i + 1, 1 )[ 0 ];
+      }
+      else {
+        ++i;
+      }
+    }
+    // if it's not a string recurse
+    else {
+      arguments.callee( jsonml[ i ] );
+      ++i;
+    }
+  }
+}
+
+} )( (function() {
+  if ( typeof exports === "undefined" ) {
+    window.markdown = {};
+    return window.markdown;
+  }
+  else {
+    return exports;
+  }
+} )() );
diff --git a/doc/tutorial/mod.md b/doc/tutorial/mod.md
new file mode 100644
index 00000000000..d7a0e6417f3
--- /dev/null
+++ b/doc/tutorial/mod.md
@@ -0,0 +1,3 @@
+# Modules and crates
+
+FIXME include name resolution details
diff --git a/doc/tutorial/order b/doc/tutorial/order
new file mode 100644
index 00000000000..54b7314a2dd
--- /dev/null
+++ b/doc/tutorial/order
@@ -0,0 +1,9 @@
+intro
+setup
+syntax
+control
+func
+data
+args
+generic
+mod
diff --git a/doc/tutorial/setup.md b/doc/tutorial/setup.md
new file mode 100644
index 00000000000..ffa3a976629
--- /dev/null
+++ b/doc/tutorial/setup.md
@@ -0,0 +1,42 @@
+# Getting started
+
+## Installation
+
+FIXME Fill this in when the installation package is finished.
+
+## Compiling your first program
+
+Rust program files are, by convention, given the extension `.rs`. Say
+we have a file `hello.rs` containing this program:
+
+    use std;
+    fn main() {
+        std::io::println("hello world!");
+    }
+
+If the Rust compiler was installed successfully, running `rustc
+hello.rs` will produce a binary called `hello` (or `hello.exe`).
+
+If you modify the program to make it invalid (for example, remove the
+`use std` line), and then compile it, you'll see an error message like
+this:
+
+    hello.rs:2:4: 2:20 error: unresolved modulename: std
+    hello.rs:2     std::io::println("hello world!");
+                   ^~~~~~~~~~~~~~~~
+
+The Rust compiler tries to provide useful information when it runs
+into an error.
+
+## Anatomy of a Rust program
+
+FIXME say something about libs, main, modules, use
+
+## Editing Rust code
+
+There are Vim highlighting and indentation scrips in the Rust source
+distribution under `src/etc/vim/`. An Emacs mode can be found at
+`[https://github.com/marijnh/rust-mode](https://github.com/marijnh/rust-mode)`.
+
+Other editors are not provided for yet. If you end up writing a Rust
+mode for your favorite editor, let us know so that we can link to it.
diff --git a/doc/tutorial/syntax.md b/doc/tutorial/syntax.md
new file mode 100644
index 00000000000..19e9c4aa8ef
--- /dev/null
+++ b/doc/tutorial/syntax.md
@@ -0,0 +1,207 @@
+# Syntax Basics
+
+FIXME: mention the module separator `::` somewhere
+
+## Braces
+
+Assuming you've programmed in any C-family language (C++, Java,
+JavaScript, C#, or PHP), Rust will feel familiar. The main surface
+difference to be aware of is that the bodies of `if` statements and of
+loops *have* to be wrapped in brackets. Single-statement, bracket-less
+bodies are not allowed.
+
+If the verbosity of that bothers you, consider the fact that this
+allows you to omit the parentheses around the condition in `if`,
+`while`, and similar constructs. This will save you two characters
+every time. As a bonus, you no longer have to spend any mental energy
+on deciding whether you need to add braces or not, or on adding them
+after the fact when a adding a statement to an `if` branch.
+
+Accounting for these differences, the surface syntax of Rust
+statements and expressions is C-like. Function calls are written
+`myfunc(arg1, arg2)`, operators have mostly the same name and
+precedence that they have in C, comments look the same, and constructs
+like `if` and `while` are available:
+
+    fn main() {
+        if 1 < 2 {
+            while false { call_a_function(10 * 4); }
+        } else if 4 < 3 || 3 < 4 {
+            // Comments are C++-style too
+        } else {
+            /* Multi-line comment syntax */
+        }
+    }
+
+## Expression syntax
+
+Though it isn't apparent in most everyday code, there is a fundamental
+difference between Rust's syntax and the predecessors in this family
+of languages. Almost everything in rust is an expression, even things
+that are statements in other languages. This allows for useless things
+like this (which passes nil—the void type—to a function):
+
+    a_function(while false {});
+
+But also useful things like this:
+
+    let x = if the_stars_align() { 4 }
+            else if something_else() { 3 }
+            else { 0 };
+
+This piece of code will bind the variable `x` to a value depending on
+the conditions. Note the condition bodies, which look like `{
+expression }`. The lack of a semicolon after the last statement in a
+braced block gives the whole block the value of that last expression.
+If the branches of the `if` had looked like `{ 4; }`, the above
+example would simply assign nil (void) to `x`. But without the
+semicolon, each branch has a different value, and `x` gets the value
+of the branch that was taken.
+
+This also works for function bodies. This function returns a boolean:
+
+    fn is_four(x: int) -> bool { x == 4 }
+
+If everything is an expression, you might conclude that you have to
+add a terminating semicolon after *every* statement, even ones that
+are not traditionally terminated with a semicolon in C (like `while`).
+That is not the case, though. Statements that end in a block only need
+a semicolon if that block contains a trailing expression. `while`
+loops do not allow trailing expressions, and `if` statements tend to
+only have a trailing expression when you want to use their value for
+something—in which case you'll have embedded it in a bigger statement,
+like the `let x = ...` example above.
+
+## Types
+
+The `-> bool` in the last example is the way a function's return type
+is written. For functions that do not return a meaningful value (these
+conceptually return nil in Rust), you can optionally say `-> ()` (`()`
+is how nil is written), but usually the return annotation is simply
+left off, as in the `fn main() { ... }` examples we've seen earlier.
+
+Every argument to a function must have its type declared (for example,
+`x: int`). Inside the function, type inference will be able to
+automatically deduce the type of most locals (generic functions, which
+we'll come back to later, will occasionally need additional
+annotation). Locals can be written either with or without a type
+annotation:
+
+    // The type of this vector will be inferred based on its use.
+    let x = [];
+    // Explicitly say this is a vector of integers.
+    let y: [int] = [];
+
+The basic types are written like this:
+
+`()`
+: Nil, the type that has only a single value.  
+
+`bool`
+: Boolean type..  
+
+`int`
+: A machine-pointer-sized integer.  
+
+`uint`
+: A machine-pointer-sized unsigned integer.  
+
+`i8`, `i16`, `i32`, `i64`
+: Signed integers with a specific size (in bits).  
+
+`u8`, `u16`, `u32`, `u64`
+: Unsigned integers with a specific size.  
+
+`f32`, `f64`
+: Floating-point types.  
+
+`float`
+: The largest floating-point type efficiently supported on the target machine.  
+
+`char`
+: A character is a 32-bit Unicode code point.  
+
+`str`
+: String type. A string contains a utf-8 encoded sequence of characters.
+
+These can be combined in composite types, which will be described in
+more detail later on (the `T`s here stand for any other type):
+
+`[T]`
+: Vector type.  
+
+`[mutable T]`
+: Mutable vector type.  
+
+`(T1, T2)`
+: Tuple type. Any arity above 1 is supported.  
+
+`{fname1: T1, fname2: T2}`
+: Record type.  
+
+`fn(arg1: T1, arg2: T2) -> T3`
+: Function type.  
+
+`@T`, `~T`, `*T`
+: Pointer types.  
+
+`obj { fn method1() }`
+: Object type.
+
+Types can be given names with `type` declarations:
+
+    type monster_size = uint;
+
+This will provide a synonym, `monster_size`, for unsigned integers. It
+will not actually create a new type—`monster_size` and `uint` can be
+used interchangeably, and using one where the other is expected is not
+a type error. Read about [single-variant tags][svt] in the next
+section if you need to create a type name that's not just a synonym.
+
+[svt]: FIXME
+
+## Literals
+
+Integers can be written in decimal (`144`), hexadecimal (`0x90`), and
+binary (`0b10010000`) base. Without suffix, an integer literal is
+considered to be of type `int`. Add a `u` (`144u`) to make it a `uint`
+instead. Literals of the fixed-size integer types can be created by
+the literal with the type name (`i8`, `u64`, etc).
+
+Note that, in Rust, no implicit conversion between integer types
+happens. If you are adding one to a variable of type `uint`, you must
+type `v += 1u`—saying `+= 1` will give you a type error.
+
+Floating point numbers are written `0.0`, `1e6`, or `2.1e-4`. Without
+suffix, the literal is assumed to be of type `float`. Suffixes `f32`
+and `f64` can be used to create literals of a specific type. The
+suffix `f` can be used to write `float` literals without a dot or
+exponent: `3f`.
+
+The nil literal is written just like the type: `()`. The keywords
+`true` and `false` produce the boolean literals.
+
+Character literals are written between single quotes, as in `'x'`. You
+may put non-ascii characters between single quotes (your source file
+should be encoded as utf-8 in that case). Rust understands a number of
+character escapes, using the backslash character:
+
+`\n`
+: A newline (unicode character 32).
+`\r`
+: A carriage return (13).
+`\t`
+: A tab character (9).
+`\\`, `\'`, `\"`
+: Simply escapes the following character.
+`\xHH`, `\uHHHH`, `\UHHHHHHHH`
+: Unicode escapes, where the `H` characters are the hexadecimal digits that form the character code.
+
+String literals allow the same escape sequences. They are written
+between double quotes (`"hello"`). Rust strings may contain newlines.
+When a newline is preceded by a backslash, it, and all white space
+following it, will not appear in the resulting string literal.
+
+## Operators
+
+FIXME recap C-style operators, ?:, explain `as`
diff --git a/doc/tutorial/web/style.css b/doc/tutorial/web/style.css
new file mode 100644
index 00000000000..a71180027f0
--- /dev/null
+++ b/doc/tutorial/web/style.css
@@ -0,0 +1,36 @@
+body {
+  padding: 1em;
+  margin: 0;
+  font-family: "Helvetica Neue", Helvetica, sans-serif;
+}
+
+#content {
+  padding: 1em 6em;
+  max-width: 50em;
+}
+
+h1 { font-size: 22pt; }
+h2 { font-size: 17pt; }
+h3 { font-size: 14pt; }
+
+code {
+  color: #033;
+}
+
+pre {
+  margin: 1.1em 12px;
+  border: 1px solid #CCCCCC;
+  padding: .4em;
+  font-size: 120%;
+}
+
+p.head {
+  font-size: 80%;
+  font-style: italic;
+  text-align: right;
+}
+
+a, a:visited, a:link {
+  text-decoration: none;
+  color: #00438a;
+}