Index: uspace/app/bithenge/expression.c
===================================================================
--- uspace/app/bithenge/expression.c	(revision 32eb01b859a99ad60bf61e20d715ef6f837bd618)
+++ uspace/app/bithenge/expression.c	(revision f85ca3f2b6b9dbdcf2a617bcc94811a77ad88b97)
@@ -58,4 +58,37 @@
 }
 
+static void expression_indestructible(bithenge_expression_t *self)
+{
+	assert(false);
+}
+
+static int current_node_evaluate(bithenge_expression_t *self,
+    bithenge_scope_t *scope, bithenge_node_t **out)
+{
+	*out = bithenge_scope_get_current_node(scope);
+	if (!*out)
+		return EINVAL;
+	return EOK;
+}
+
+static const bithenge_expression_ops_t current_node_ops = {
+	.evaluate = current_node_evaluate,
+	.destroy = expression_indestructible,
+};
+
+static bithenge_expression_t current_node_expression = {
+	&current_node_ops, 1
+};
+
+/** Create an expression that gets the current node being created.
+ * @param[out] out Holds the new expression.
+ * @return EOK on success or an error code from errno.h. */
+int bithenge_current_node_expression(bithenge_expression_t **out)
+{
+	bithenge_expression_inc_ref(&current_node_expression);
+	*out = &current_node_expression;
+	return EOK;
+}
+
 typedef struct {
 	bithenge_expression_t base;
@@ -176,4 +209,80 @@
 	*out = const_as_expression(self);
 	return EOK;
+}
+
+typedef struct {
+	bithenge_expression_t base;
+	bithenge_expression_t *expr;
+	bithenge_node_t *key;
+} member_expression_t;
+
+static member_expression_t *expression_as_member(bithenge_expression_t *base)
+{
+	return (member_expression_t *)base;
+}
+
+static bithenge_expression_t *member_as_expression(member_expression_t *expr)
+{
+	return &expr->base;
+}
+
+static int member_expression_evaluate(bithenge_expression_t *base, 
+    bithenge_scope_t *scope, bithenge_node_t **out)
+{
+	member_expression_t *self = expression_as_member(base);
+	bithenge_node_t *node;
+	int rc = bithenge_expression_evaluate(self->expr, scope, &node);
+	if (rc != EOK)
+		return rc;
+	bithenge_node_inc_ref(self->key);
+	rc = bithenge_node_get(node, self->key, out);
+	bithenge_node_dec_ref(node);
+	return rc;
+}
+
+static void member_expression_destroy(bithenge_expression_t *base)
+{
+	member_expression_t *self = expression_as_member(base);
+	bithenge_expression_dec_ref(self->expr);
+	bithenge_node_dec_ref(self->key);
+	free(self);
+}
+
+static const bithenge_expression_ops_t member_expression_ops = {
+	.evaluate = member_expression_evaluate,
+	.destroy = member_expression_destroy,
+};
+
+/** Create an expression that gets a member from a node. Takes references to
+ * @a expr and @a key.
+ * @param[out] out Holds the new expression.
+ * @param expr Calculates the node to get the member of.
+ * @param key The member to get.
+ * @return EOK on success or an error code from errno.h. */
+int bithenge_member_expression(bithenge_expression_t **out,
+    bithenge_expression_t *expr, bithenge_node_t *key)
+{
+	int rc;
+	member_expression_t *self = malloc(sizeof(*self));
+	if (!self) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = bithenge_init_expression(member_as_expression(self),
+	    &member_expression_ops);
+	if (rc != EOK)
+		goto error;
+
+	self->expr = expr;
+	self->key = key;
+	*out = member_as_expression(self);
+	return EOK;
+
+error:
+	bithenge_expression_dec_ref(expr);
+	bithenge_node_dec_ref(key);
+	free(self);
+	return rc;
 }
 
Index: uspace/app/bithenge/expression.h
===================================================================
--- uspace/app/bithenge/expression.h	(revision 32eb01b859a99ad60bf61e20d715ef6f837bd618)
+++ uspace/app/bithenge/expression.h	(revision f85ca3f2b6b9dbdcf2a617bcc94811a77ad88b97)
@@ -93,6 +93,9 @@
 int bithenge_init_expression(bithenge_expression_t *,
     const bithenge_expression_ops_t *);
+int bithenge_current_node_expression(bithenge_expression_t **);
 int bithenge_param_expression(bithenge_expression_t **, int);
 int bithenge_const_expression(bithenge_expression_t **, bithenge_node_t *);
+int bithenge_member_expression(bithenge_expression_t **,
+    bithenge_expression_t *, bithenge_node_t *);
 int bithenge_param_wrapper(bithenge_transform_t **, bithenge_transform_t *,
     bithenge_expression_t **);
Index: uspace/app/bithenge/script.c
===================================================================
--- uspace/app/bithenge/script.c	(revision 32eb01b859a99ad60bf61e20d715ef6f837bd618)
+++ uspace/app/bithenge/script.c	(revision f85ca3f2b6b9dbdcf2a617bcc94811a77ad88b97)
@@ -119,9 +119,9 @@
 }
 
-/** Note that an error has occurred. */
+/** Note that an error has occurred if error is not EOK. */
 static void error_errno(state_t *state, int error)
 {
 	// Don't overwrite a previous error.
-	if (state->error == EOK) {
+	if (state->error == EOK && error != EOK) {
 		done_with_token(state);
 		state->token = TOKEN_ERROR;
@@ -220,6 +220,5 @@
 		int rc = bithenge_parse_int(state->buffer +
 		    state->old_buffer_pos, &state->token_int);
-		if (rc != EOK)
-			error_errno(state, rc);
+		error_errno(state, rc);
 	} else if (ch == '<') {
 		state->token = ch;
@@ -368,4 +367,35 @@
 
 		next_token(state);
+
+		return expr;
+	} else if (state->token == '.') {
+		next_token(state);
+
+		const char *id = expect_identifier(state);
+		bithenge_node_t *key = NULL;
+		bithenge_expression_t *expr = NULL;
+		int rc = bithenge_current_node_expression(&expr);
+		error_errno(state, rc);
+
+		if (state->error == EOK) {
+			rc = bithenge_new_string_node(&key, id, true);
+			id = NULL;
+			error_errno(state, rc);
+		}
+
+		if (state->error == EOK) {
+			rc = bithenge_member_expression(&expr, expr, key);
+			key = NULL;
+			if (rc != EOK)
+				expr = NULL;
+			error_errno(state, rc);
+		}
+
+		if (state->error != EOK) {
+			free((char *)id);
+			bithenge_node_dec_ref(key);
+			bithenge_expression_dec_ref(expr);
+			return NULL;
+		}
 
 		return expr;
@@ -588,5 +618,6 @@
 	done_with_token(state);
 	state->token = TOKEN_ERROR;
-	fclose(state->file);
+	if (state->file)
+		fclose(state->file);
 	transform_list_t *entry = state->transform_list;
 	while (entry) {
Index: uspace/app/bithenge/transform.c
===================================================================
--- uspace/app/bithenge/transform.c	(revision 32eb01b859a99ad60bf61e20d715ef6f837bd618)
+++ uspace/app/bithenge/transform.c	(revision f85ca3f2b6b9dbdcf2a617bcc94811a77ad88b97)
@@ -552,4 +552,6 @@
 	node->blob = bithenge_node_as_blob(in);
 	*out = struct_as_node(node);
+	bithenge_node_inc_ref(*out);
+	bithenge_scope_set_current_node(&node->scope, *out);
 	return EOK;
 }
Index: uspace/app/bithenge/transform.h
===================================================================
--- uspace/app/bithenge/transform.h	(revision 32eb01b859a99ad60bf61e20d715ef6f837bd618)
+++ uspace/app/bithenge/transform.h	(revision f85ca3f2b6b9dbdcf2a617bcc94811a77ad88b97)
@@ -52,6 +52,7 @@
 typedef struct {
 	/** @privatesection */
+	int num_params;
 	bithenge_node_t **params;
-	int num_params;
+	bithenge_node_t *current_node;
 } bithenge_scope_t;
 
@@ -74,6 +75,7 @@
 static inline void bithenge_scope_init(bithenge_scope_t *scope)
 {
+	scope->num_params = 0;
 	scope->params = NULL;
-	scope->num_params = 0;
+	scope->current_node = NULL;
 }
 
@@ -83,4 +85,5 @@
 static inline void bithenge_scope_destroy(bithenge_scope_t *scope)
 {
+	bithenge_node_dec_ref(scope->current_node);
 	for (int i = 0; i < scope->num_params; i++)
 		bithenge_node_dec_ref(scope->params[i]);
@@ -104,9 +107,35 @@
 	for (int i = 0; i < out->num_params; i++)
 		bithenge_node_inc_ref(out->params[i]);
-	return EOK;
+	out->current_node = scope->current_node;
+	if (out->current_node)
+		bithenge_node_inc_ref(out->current_node);
+	return EOK;
+}
+
+/** Set the current node being created. Takes a reference to @a node.
+ * @param scope The scope to set the current node in.
+ * @param node The current node being created.
+ * @return EOK on success or an error code from errno.h. */
+static inline void bithenge_scope_set_current_node(bithenge_scope_t *scope,
+    bithenge_node_t *node)
+{
+	bithenge_node_dec_ref(scope->current_node);
+	scope->current_node = node;
+}
+
+/** Get the current node being created, which may be NULL.
+ * @param scope The scope to get the current node from.
+ * @return The node being created, or NULL. */
+static inline bithenge_node_t *bithenge_scope_get_current_node(
+    bithenge_scope_t *scope)
+{
+	if (scope->current_node)
+		bithenge_node_inc_ref(scope->current_node);
+	return scope->current_node;
 }
 
 /** Allocate parameters. The parameters must then be set with @a
- * bithenge_scope_set_param.
+ * bithenge_scope_set_param. This must not be called on a scope that already
+ * has parameters.
  * @param scope The scope in which to allocate parameters.
  * @param num_params The number of parameters to allocate.
