Index: uspace/app/bithenge/expression.c
===================================================================
--- uspace/app/bithenge/expression.c	(revision 20ac1a4d9f8ba1e4a74ec7c3fe780a46f6c47167)
+++ uspace/app/bithenge/expression.c	(revision 78d3a00170996ffa6956bba73edd383691261969)
@@ -63,4 +63,93 @@
 }
 
+typedef struct {
+	bithenge_expression_t base;
+	bithenge_binary_op_t op;
+	bithenge_expression_t *a, *b;
+} binary_expression_t;
+
+static inline binary_expression_t *expression_as_binary(
+    bithenge_expression_t *base)
+{
+	return (binary_expression_t *)base;
+}
+
+static inline bithenge_expression_t *binary_as_expression(
+    binary_expression_t *self)
+{
+	return &self->base;
+}
+
+static int binary_expression_evaluate(bithenge_expression_t *base,
+    bithenge_scope_t *scope, bithenge_node_t **out)
+{
+	binary_expression_t *self = expression_as_binary(base);
+	bithenge_node_t *a, *b;
+	int rc = bithenge_expression_evaluate(self->a, scope, &a);
+	if (rc != EOK)
+		return rc;
+	rc = bithenge_expression_evaluate(self->b, scope, &b);
+	if (rc != EOK) {
+		bithenge_node_dec_ref(a);
+		return rc;
+	}
+	switch (self->op) {
+	case BITHENGE_EXPRESSION_EQUALS:
+		rc = bithenge_new_boolean_node(out, bithenge_node_equal(a, b));
+		break;
+	}
+	bithenge_node_dec_ref(a);
+	bithenge_node_dec_ref(b);
+	return rc;
+}
+
+static void binary_expression_destroy(bithenge_expression_t *base)
+{
+	binary_expression_t *self = expression_as_binary(base);
+	bithenge_expression_dec_ref(self->a);
+	bithenge_expression_dec_ref(self->b);
+	free(self);
+}
+
+static const bithenge_expression_ops_t binary_expression_ops = {
+	.evaluate = binary_expression_evaluate,
+	.destroy = binary_expression_destroy,
+};
+
+/** Create a binary expression. Takes ownership of @a a and @a b.
+ * @param[out] out Holds the new expression.
+ * @param op The operator to apply.
+ * @param a The first operand.
+ * @param b The second operand.
+ * @return EOK on success or an error code from errno.h. */
+int bithenge_binary_expression(bithenge_expression_t **out,
+    bithenge_binary_op_t op, bithenge_expression_t *a,
+    bithenge_expression_t *b)
+{
+	int rc;
+	binary_expression_t *self = malloc(sizeof(*self));
+	if (!self) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = bithenge_init_expression(binary_as_expression(self),
+	    &binary_expression_ops);
+	if (rc != EOK)
+		goto error;
+
+	self->op = op;
+	self->a = a;
+	self->b = b;
+	*out = binary_as_expression(self);
+	return EOK;
+
+error:
+	bithenge_expression_dec_ref(a);
+	bithenge_expression_dec_ref(b);
+	free(self);
+	return rc;
+}
+
 static int current_node_evaluate(bithenge_expression_t *self,
     bithenge_scope_t *scope, bithenge_node_t **out)
Index: uspace/app/bithenge/expression.h
===================================================================
--- uspace/app/bithenge/expression.h	(revision 20ac1a4d9f8ba1e4a74ec7c3fe780a46f6c47167)
+++ uspace/app/bithenge/expression.h	(revision 78d3a00170996ffa6956bba73edd383691261969)
@@ -91,6 +91,12 @@
 }
 
+typedef enum {
+	BITHENGE_EXPRESSION_EQUALS,
+} bithenge_binary_op_t;
+
 int bithenge_init_expression(bithenge_expression_t *,
     const bithenge_expression_ops_t *);
+int bithenge_binary_expression(bithenge_expression_t **, bithenge_binary_op_t,
+    bithenge_expression_t *, bithenge_expression_t *);
 int bithenge_current_node_expression(bithenge_expression_t **);
 int bithenge_param_expression(bithenge_expression_t **, int);
Index: uspace/app/bithenge/script.c
===================================================================
--- uspace/app/bithenge/script.c	(revision 20ac1a4d9f8ba1e4a74ec7c3fe780a46f6c47167)
+++ uspace/app/bithenge/script.c	(revision 78d3a00170996ffa6956bba73edd383691261969)
@@ -62,4 +62,5 @@
 	TOKEN_IF,
 	TOKEN_STRUCT,
+	TOKEN_SWITCH,
 	TOKEN_TRANSFORM,
 	TOKEN_TRUE,
@@ -219,4 +220,7 @@
 		} else if (!str_cmp(value, "struct")) {
 			state->token = TOKEN_STRUCT;
+			free(value);
+		} else if (!str_cmp(value, "switch")) {
+			state->token = TOKEN_SWITCH;
 			free(value);
 		} else if (!str_cmp(value, "transform")) {
@@ -548,4 +552,91 @@
 }
 
+static bithenge_transform_t *parse_switch(state_t *state, bool in_struct)
+{
+	expect(state, TOKEN_SWITCH);
+	expect(state, '(');
+	bithenge_expression_t *ref_expr = parse_expression(state);
+	expect(state, ')');
+	expect(state, '{');
+	int num = 0;
+	bithenge_expression_t **exprs = NULL;
+	bithenge_transform_t **xforms = NULL;
+	while (state->error == EOK && state->token != '}') {
+		bithenge_expression_t *expr;
+		if (state->token == TOKEN_ELSE) {
+			next_token(state);
+			bithenge_node_t *node;
+			int rc = bithenge_new_boolean_node(&node, true);
+			if (rc != EOK) {
+				error_errno(state, rc);
+				break;
+			}
+			rc = bithenge_const_expression(&expr, node);
+			if (rc != EOK) {
+				error_errno(state, rc);
+				break;
+			}
+		} else {
+			expr = parse_expression(state);
+			if (state->error == EOK) {
+				bithenge_expression_inc_ref(ref_expr);
+				int rc = bithenge_binary_expression(&expr,
+				    BITHENGE_EXPRESSION_EQUALS, ref_expr,
+				    expr);
+				if (rc != EOK) {
+					error_errno(state, rc);
+					break;
+				}
+			}
+		}
+
+		expect(state, ':');
+		bithenge_transform_t *xform;
+		if (in_struct) {
+			expect(state, '{');
+			xform = parse_struct(state);
+			expect(state, '}');
+		} else
+			xform = parse_transform(state);
+		expect(state, ';');
+
+		exprs = state_realloc(state, exprs,
+		    sizeof(*exprs) * (num + 1));
+		xforms = state_realloc(state, xforms,
+		    sizeof(*xforms) * (num + 1));
+		if (state->error != EOK) {
+			bithenge_expression_dec_ref(expr);
+			bithenge_transform_dec_ref(xform);
+			break;
+		}
+
+		exprs[num] = expr;
+		xforms[num] = xform;
+		num++;
+	}
+	bithenge_expression_dec_ref(ref_expr);
+
+	bithenge_transform_t *switch_xform = &bithenge_invalid_transform;
+	bithenge_transform_inc_ref(switch_xform);
+	while (state->error == EOK && num >= 1) {
+		num--;
+		int rc = bithenge_if_transform(&switch_xform, exprs[num],
+		    xforms[num], switch_xform);
+		if (rc != EOK)
+			error_errno(state, rc);
+	}
+
+	while (num >= 1) {
+		num--;
+		bithenge_expression_dec_ref(exprs[num]);
+		bithenge_transform_dec_ref(xforms[num]);
+	}
+	free(exprs);
+	free(xforms);
+
+	expect(state, '}');
+	return switch_xform;
+}
+
 /* The TOKEN_STRUCT and '{' must already have been skipped. */
 static bithenge_transform_t *parse_struct(state_t *state)
@@ -558,4 +649,7 @@
 		if (state->token == TOKEN_IF) {
 			subxforms[num].transform = parse_if(state, true);
+			subxforms[num].name = NULL;
+		} else if (state->token == TOKEN_SWITCH) {
+			subxforms[num].transform = parse_switch(state, true);
 			subxforms[num].name = NULL;
 		} else {
@@ -609,4 +703,6 @@
 		expect(state, '}');
 		return xform;
+	} else if (state->token == TOKEN_SWITCH) {
+		return parse_switch(state, false);
 	} else {
 		syntax_error(state, "unexpected (transform expected)");
Index: uspace/app/bithenge/transform.c
===================================================================
--- uspace/app/bithenge/transform.c	(revision 20ac1a4d9f8ba1e4a74ec7c3fe780a46f6c47167)
+++ uspace/app/bithenge/transform.c	(revision 78d3a00170996ffa6956bba73edd383691261969)
@@ -183,4 +183,20 @@
 bithenge_transform_t bithenge_ascii_transform = {
 	&ascii_ops, 1, 0
+};
+
+static int invalid_apply(bithenge_transform_t *self, bithenge_scope_t *scope,
+    bithenge_node_t *in, bithenge_node_t **out)
+{
+	return EINVAL;
+}
+
+static const bithenge_transform_ops_t invalid_ops = {
+	.apply = invalid_apply,
+	.destroy = transform_indestructible,
+};
+
+/** A transform that always raises an error. */
+bithenge_transform_t bithenge_invalid_transform = {
+	&invalid_ops, 1, 0
 };
 
Index: uspace/app/bithenge/transform.h
===================================================================
--- uspace/app/bithenge/transform.h	(revision 20ac1a4d9f8ba1e4a74ec7c3fe780a46f6c47167)
+++ uspace/app/bithenge/transform.h	(revision 78d3a00170996ffa6956bba73edd383691261969)
@@ -256,4 +256,5 @@
 extern bithenge_transform_t bithenge_ascii_transform;
 extern bithenge_transform_t bithenge_known_length_transform;
+extern bithenge_transform_t bithenge_invalid_transform;
 extern bithenge_transform_t bithenge_uint8_transform;
 extern bithenge_transform_t bithenge_uint16le_transform;
Index: uspace/app/bithenge/tree.c
===================================================================
--- uspace/app/bithenge/tree.c	(revision 20ac1a4d9f8ba1e4a74ec7c3fe780a46f6c47167)
+++ uspace/app/bithenge/tree.c	(revision 78d3a00170996ffa6956bba73edd383691261969)
@@ -325,5 +325,5 @@
 
 /** Check whether the contents of two nodes are equal. Does not yet work for
- * internal nodes.
+ * internal nodes. Takes ownership of nothing.
  * @memberof bithenge_node_t
  * @param a, b Nodes to compare.
Index: uspace/dist/src/bithenge/test.bh
===================================================================
--- uspace/dist/src/bithenge/test.bh	(revision 20ac1a4d9f8ba1e4a74ec7c3fe780a46f6c47167)
+++ uspace/dist/src/bithenge/test.bh	(revision 78d3a00170996ffa6956bba73edd383691261969)
@@ -8,10 +8,29 @@
 };
 
-transform item(first_len, second_len) = struct {
-	.id <- uint32le <- known_length(4); # the known_length is unnecessary
+transform u32(little_endian) =
+	if (little_endian) {
+		uint32le
+	} else {
+		uint32be
+	};
+
+transform item(little_endian, len) = struct {
+	.type <- u32(little_endian);
 	.name <- pascal_string;
-	.first <- known_length(first_len);
-	.second <- known_length(second_len);
+	switch (.type) {
+		3: {
+			.val <- u32(little_endian);
+		};
+		14: {
+			.text <- ascii <- known_length(len);
+		};
+		else: {
+			.unknown <- known_length(len);
+		};
+	}
 };
 
-transform main() = item(3, 0);
+transform main() = struct {
+	.first_item <- item(true, 3);
+	.second_item <- item(false, 4);
+};
