Index: uspace/app/bithenge/expression.c
===================================================================
--- uspace/app/bithenge/expression.c	(revision 03cad479d24a0461f4252c77bd91c3e03ea2f1a3)
+++ uspace/app/bithenge/expression.c	(revision 4056ad0489ea8411e60723494ce05bf411223d7d)
@@ -231,9 +231,7 @@
 
 error:
-	bithenge_node_dec_ref(in);
 	bithenge_scope_destroy(&inner);
 	return rc;
 }
-
 
 static int param_wrapper_prefix_length(bithenge_transform_t *base,
@@ -252,8 +250,8 @@
 
 error:
-	bithenge_blob_dec_ref(in);
 	bithenge_scope_destroy(&inner);
 	return rc;
 }
+
 static void param_wrapper_destroy(bithenge_transform_t *base)
 {
Index: uspace/app/bithenge/helenos/os.h
===================================================================
--- uspace/app/bithenge/helenos/os.h	(revision 03cad479d24a0461f4252c77bd91c3e03ea2f1a3)
+++ uspace/app/bithenge/helenos/os.h	(revision 4056ad0489ea8411e60723494ce05bf411223d7d)
@@ -33,4 +33,5 @@
 #include <byteorder.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <macros.h>
 #include <mem.h>
@@ -38,4 +39,7 @@
 #include <str.h>
 #include <str_error.h>
+
+typedef int64_t bithenge_int_t;
+#define BITHENGE_PRId PRId64
 
 typedef struct {
@@ -76,3 +80,8 @@
 }
 
+static inline int bithenge_parse_int(const char *start, bithenge_int_t *result)
+{
+	return str_uint64_t(start, NULL, 10, false, result);
+}
+
 #endif
Index: uspace/app/bithenge/linux/os.h
===================================================================
--- uspace/app/bithenge/linux/os.h	(revision 03cad479d24a0461f4252c77bd91c3e03ea2f1a3)
+++ uspace/app/bithenge/linux/os.h	(revision 4056ad0489ea8411e60723494ce05bf411223d7d)
@@ -32,7 +32,8 @@
 #include <endian.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <memory.h>
 #include <stdbool.h>
-#include <stdint.h>
+#include <stdlib.h>
 #include <string.h>
 #include <wchar.h>
@@ -44,6 +45,7 @@
 #define ELIMIT EINVAL
 
+typedef intmax_t bithenge_int_t;
+#define BITHENGE_PRId PRIdMAX
 typedef uint64_t aoff64_t;
-
 typedef const char *string_iterator_t;
 
@@ -130,3 +132,10 @@
 }
 
+static inline int bithenge_parse_int(const char *start, bithenge_int_t *result)
+{
+	errno = 0;
+	*result = strtoll(start, NULL, 10);
+	return errno;
+}
+
 #endif
Index: uspace/app/bithenge/script.c
===================================================================
--- uspace/app/bithenge/script.c	(revision 03cad479d24a0461f4252c77bd91c3e03ea2f1a3)
+++ uspace/app/bithenge/script.c	(revision 4056ad0489ea8411e60723494ce05bf411223d7d)
@@ -38,4 +38,5 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include "expression.h"
 #include "os.h"
 #include "script.h"
@@ -53,4 +54,5 @@
 	TOKEN_EOF,
 	TOKEN_IDENTIFIER,
+	TOKEN_INTEGER,
 	TOKEN_LEFT_ARROW,
 
@@ -95,4 +97,6 @@
 		 * NULL, it will be freed when the next token is read. */
 		char *token_string;
+		/** The value of a TOKEN_INTEGER token. */
+		bithenge_int_t token_int;
 	};
 } state_t;
@@ -202,4 +206,12 @@
 			state->token_string = value;
 		}
+	} else if (isdigit(ch)) {
+		while (isdigit(state->buffer[state->buffer_pos]))
+			state->buffer_pos++;
+		state->token = TOKEN_INTEGER;
+		int rc = bithenge_parse_int(state->buffer +
+		    state->old_buffer_pos, &state->token_int);
+		if (rc != EOK)
+			error_errno(state, rc);
 	} else if (ch == '<') {
 		state->token = ch;
@@ -307,4 +319,80 @@
 
 static bithenge_transform_t *parse_transform(state_t *state);
+
+static bithenge_expression_t *parse_expression(state_t *state)
+{
+	if (state->token == TOKEN_INTEGER) {
+		bithenge_int_t val = state->token_int;
+		next_token(state);
+		bithenge_node_t *node;
+		int rc = bithenge_new_integer_node(&node, val);
+		if (rc != EOK) {
+			error_errno(state, rc);
+			return NULL;
+		}
+
+		bithenge_expression_t *expr;
+		rc = bithenge_const_expression(&expr, node);
+		if (rc != EOK) {
+			error_errno(state, rc);
+			return NULL;
+		}
+
+		return expr;
+	} else {
+		syntax_error(state, "expression expected");
+		return NULL;
+	}
+}
+
+// state->token must be TOKEN_IDENTIFIER when this is called
+static bithenge_transform_t *parse_invocation(state_t *state)
+{
+	bithenge_transform_t *result = get_named_transform(state,
+	    state->token_string);
+	if (!result)
+		syntax_error(state, "transform not found");
+	next_token(state);
+
+	bithenge_expression_t **params = NULL;
+	int num_params = 0;
+	if (state->token == '(') {
+		next_token(state);
+		while (state->error == EOK && state->token != ')') {
+			if (num_params)
+				expect(state, ',');
+			params = state_realloc(state, params,
+			    (num_params + 1)*sizeof(*params));
+			if (state->error != EOK)
+				break;
+			params[num_params] = parse_expression(state);
+			num_params++;
+		}
+		expect(state, ')');
+	}
+
+	/* TODO: show correct error position */
+	if (state->error == EOK
+	    && bithenge_transform_num_params(result) != num_params)
+		syntax_error(state, "incorrect number of parameters before");
+
+	if (state->error != EOK) {
+		while (num_params--)
+			bithenge_expression_dec_ref(params[num_params]);
+		free(params);
+		bithenge_transform_dec_ref(result);
+		return NULL;
+	}
+
+	if (num_params) {
+		int rc = bithenge_param_wrapper(&result, result, params);
+		if (rc != EOK) {
+			error_errno(state, rc);
+			result = NULL;
+		}
+	}
+
+	return result;
+}
 
 static bithenge_transform_t *parse_struct(state_t *state)
@@ -358,10 +446,5 @@
 {
 	if (state->token == TOKEN_IDENTIFIER) {
-		bithenge_transform_t *result = get_named_transform(state,
-		    state->token_string);
-		if (!result)
-			syntax_error(state, "transform not found");
-		next_token(state);
-		return result;
+		return parse_invocation(state);
 	} else if (state->token == TOKEN_STRUCT) {
 		return parse_struct(state);
Index: uspace/app/bithenge/transform.c
===================================================================
--- uspace/app/bithenge/transform.c	(revision 03cad479d24a0461f4252c77bd91c3e03ea2f1a3)
+++ uspace/app/bithenge/transform.c	(revision 4056ad0489ea8411e60723494ce05bf411223d7d)
@@ -47,5 +47,6 @@
  * transform will get its own context with parameters, probably provided by a
  * param_wrapper. If this is zero, the existing outer context will be used with
- * whatever parameters it has.
+ * whatever parameters it has, so they can be passed to any param_wrappers
+ * within.
  * @return EOK or an error code from errno.h. */
 int bithenge_init_transform(bithenge_transform_t *self,
@@ -64,4 +65,81 @@
 {
 	assert(false);
+}
+
+typedef struct {
+	bithenge_transform_t base;
+	bithenge_transform_t *transform;
+} param_transform_t;
+
+static inline param_transform_t *transform_as_param(
+    bithenge_transform_t *base)
+{
+	return (param_transform_t *)base;
+}
+
+static inline bithenge_transform_t *param_as_transform(
+    param_transform_t *self)
+{
+	return &self->base;
+}
+
+static int param_transform_apply(bithenge_transform_t *base,
+    bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
+{
+	param_transform_t *self = transform_as_param(base);
+	return bithenge_transform_apply(self->transform, scope, in, out);
+}
+
+static int param_transform_prefix_length(bithenge_transform_t *base,
+    bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
+{
+	param_transform_t *self = transform_as_param(base);
+	return bithenge_transform_prefix_length(self->transform, scope, in,
+	    out);
+}
+
+static void param_transform_destroy(bithenge_transform_t *base)
+{
+	param_transform_t *self = transform_as_param(base);
+	bithenge_transform_dec_ref(self->transform);
+	free(self);
+}
+
+static const bithenge_transform_ops_t param_transform_ops = {
+	.apply = param_transform_apply,
+	.prefix_length = param_transform_prefix_length,
+	.destroy = param_transform_destroy,
+};
+
+/** Create a wrapper transform with a different number of parameters. Takes a
+ * reference to @a transform, which it will use for all operations.
+ * @param[out] out Holds the created transform.
+ * @param transform The transform to wrap.
+ * @param num_params The number of parameters to require.
+ * @return EOK on success or an error code from errno.h. */
+int bithenge_new_param_transform(bithenge_transform_t **out,
+    bithenge_transform_t *transform, int num_params)
+{
+	assert(transform);
+	assert(bithenge_transform_num_params(transform) == 0);
+	assert(num_params != 0);
+
+	int rc;
+	param_transform_t *self = malloc(sizeof(*self));
+	if (!self) {
+		rc = ENOMEM;
+		goto error;
+	}
+	rc = bithenge_init_transform(param_as_transform(self),
+	    &param_transform_ops, num_params);
+	if (rc != EOK)
+		goto error;
+	self->transform = transform;
+	*out = param_as_transform(self);
+	return EOK;
+error:
+	bithenge_transform_dec_ref(transform);
+	free(self);
+	return rc;
 }
 
@@ -105,4 +183,61 @@
 bithenge_transform_t bithenge_ascii_transform = {
 	&ascii_ops, 1, 0
+};
+
+static int known_length_apply(bithenge_transform_t *self,
+    bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
+{
+	bithenge_node_t *length_node;
+	int rc = bithenge_scope_get_param(scope, 0, &length_node);
+	if (rc != EOK)
+		return rc;
+	if (bithenge_node_type(length_node) != BITHENGE_NODE_INTEGER) {
+		bithenge_node_dec_ref(length_node);
+		return EINVAL;
+	}
+	bithenge_int_t length = bithenge_integer_node_value(length_node);
+	bithenge_node_dec_ref(length_node);
+
+	if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
+		return EINVAL;
+	aoff64_t size;
+	rc = bithenge_blob_size(bithenge_node_as_blob(in), &size);
+	if (rc != EOK)
+		return rc;
+	if (length != (bithenge_int_t)size)
+		return EINVAL;
+
+	bithenge_node_inc_ref(in);
+	*out = in;
+	return EOK;
+}
+
+static int known_length_prefix_length(bithenge_transform_t *self,
+    bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
+{
+	bithenge_node_t *length_node;
+	int rc = bithenge_scope_get_param(scope, 0, &length_node);
+	if (rc != EOK)
+		return rc;
+	if (bithenge_node_type(length_node) != BITHENGE_NODE_INTEGER) {
+		bithenge_node_dec_ref(length_node);
+		return EINVAL;
+	}
+	bithenge_int_t length = bithenge_integer_node_value(length_node);
+	bithenge_node_dec_ref(length_node);
+
+	*out = (aoff64_t)length;
+	return EOK;
+}
+
+static const bithenge_transform_ops_t known_length_ops = {
+	.apply = known_length_apply,
+	.prefix_length = known_length_prefix_length,
+	.destroy = transform_indestructible,
+};
+
+/** Pass through a blob, but require its length to equal the first argument. */
+bithenge_transform_t bithenge_known_length_transform = {
+	&known_length_ops, 1, 1
 };
 
@@ -232,4 +367,5 @@
 static bithenge_named_transform_t primitive_transforms[] = {
 	{"ascii", &bithenge_ascii_transform},
+	{"known_length", &bithenge_known_length_transform},
 	{"uint8", &bithenge_uint8_transform},
 	{"uint16le", &bithenge_uint16le_transform},
Index: uspace/app/bithenge/transform.h
===================================================================
--- uspace/app/bithenge/transform.h	(revision 03cad479d24a0461f4252c77bd91c3e03ea2f1a3)
+++ uspace/app/bithenge/transform.h	(revision 4056ad0489ea8411e60723494ce05bf411223d7d)
@@ -207,4 +207,5 @@
 
 extern bithenge_transform_t bithenge_ascii_transform;
+extern bithenge_transform_t bithenge_known_length_transform;
 extern bithenge_transform_t bithenge_uint8_transform;
 extern bithenge_transform_t bithenge_uint16le_transform;
@@ -217,8 +218,10 @@
 extern bithenge_named_transform_t *bithenge_primitive_transforms;
 
-int bithenge_init_transform(bithenge_transform_t *self,
-    const bithenge_transform_ops_t *ops, int num_params);
-int bithenge_new_struct(bithenge_transform_t **out,
-    bithenge_named_transform_t *subtransforms);
+int bithenge_init_transform(bithenge_transform_t *,
+    const bithenge_transform_ops_t *, int);
+int bithenge_new_param_transform(bithenge_transform_t **,
+    bithenge_transform_t *, int);
+int bithenge_new_struct(bithenge_transform_t **,
+    bithenge_named_transform_t *);
 int bithenge_new_composed_transform(bithenge_transform_t **,
     bithenge_transform_t **, size_t);
Index: uspace/app/bithenge/tree.h
===================================================================
--- uspace/app/bithenge/tree.h	(revision 03cad479d24a0461f4252c77bd91c3e03ea2f1a3)
+++ uspace/app/bithenge/tree.h	(revision 4056ad0489ea8411e60723494ce05bf411223d7d)
@@ -39,15 +39,6 @@
 
 #include <assert.h>
-#include <inttypes.h>
 #include <sys/types.h>
 #include "os.h"
-
-#ifdef INTMAX_MAX
-typedef intmax_t bithenge_int_t;
-#define BITHENGE_PRId PRIdMAX
-#else
-typedef int64_t bithenge_int_t;
-#define BITHENGE_PRId PRId64
-#endif
 
 /** Indicates the type of a tree node. */
