diff --git a/plugins/excel/xlsx-utils.c b/plugins/excel/xlsx-utils.c
index f73fbd6..6c06384 100644
--- a/plugins/excel/xlsx-utils.c
+++ b/plugins/excel/xlsx-utils.c
@@ -34,6 +34,7 @@
 #include "gnm-format.h"
 #include <goffice/goffice.h>
 #include <glib-object.h>
+#include <string.h>
 
 typedef struct {
 	GnmConventions base;
@@ -63,10 +64,45 @@ xlsx_lookup_external_wb (GnmConventions const *convs,
 			 char const *name)
 {
 	XLSXExprConventions const *xconv = (XLSXExprConventions const *)convs;
-	g_print ("lookup '%s'\n", name);
+	if (0) g_printerr ("lookup '%s' %p\n", name, ref_wb);
+	if (strcmp (name, "0") == 0)
+		return ref_wb;
 	return g_hash_table_lookup (xconv->extern_wb_by_id, name);
 }
 
+static char const *
+xlsx_name_parser (char const *str,
+		  GnmConventions const *convs)
+{
+	gunichar uc;
+
+	if (*str == '[' && g_ascii_isdigit (str[1])) {
+		str += 2;
+		while (g_ascii_isdigit (str[0]))
+			str++;
+		if (str[0] != ']' || str[1] != '!')
+			return NULL;
+		str += 2;
+	}
+
+	uc = g_utf8_get_char (str);
+
+	if (!g_unichar_isalpha (uc) && uc != '_' && uc != '\\')
+		return NULL;
+
+	do {
+		str = g_utf8_next_char (str);
+		uc = g_utf8_get_char (str);
+	} while (g_unichar_isalnum (uc) ||
+		 uc == '_' ||
+		 uc == '?' ||
+		 uc == '\\' ||
+		 uc == '.');
+
+	return str;
+}
+
+
 static void
 xlsx_cellref_as_string (GnmConventionsOut *out,
 			GnmCellRef const *cell_ref,
@@ -108,12 +144,14 @@ Workbook *
 xlsx_conventions_add_extern_ref (GnmConventions *convs, char const *path)
 {
 	XLSXExprConventions *xconv = (XLSXExprConventions *)convs;
+	unsigned newid = g_hash_table_size (xconv->extern_wb_by_id) + 1;
 	Workbook *res = g_object_new (WORKBOOK_TYPE, NULL);
+
 	(void) go_doc_set_uri (GO_DOC (res), path);
 	g_hash_table_insert (xconv->extern_wb_by_id,
-		g_strdup_printf ("%d", g_hash_table_size (xconv->extern_wb_by_id) + 1),
-		res);
-	g_print ("add %d = '%s'\n", g_hash_table_size (xconv->extern_wb_by_id), path);
+			     g_strdup_printf ("%d", newid),
+			     res);
+	g_printerr ("add %d = '%s'\n", newid, path);
 	return res;
 }
 
@@ -127,6 +165,7 @@ xlsx_conventions_new (void)
 	convs->decimal_sep_dot		= TRUE;
 	convs->input.range_ref		= rangeref_parse;
 	convs->input.external_wb	= xlsx_lookup_external_wb;
+	//convs->input.name		= xlsx_name_parser;
 	convs->output.cell_ref		= xlsx_cellref_as_string;
 	convs->output.range_ref		= xlsx_rangeref_as_string;
 	convs->range_sep_colon		= TRUE;
diff --git a/src/parser.y b/src/parser.y
index 35b33b2..a4ee2af 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -581,6 +581,7 @@ int yyparse (void);
 %token		ARG_SEP ARRAY_COL_SEP ARRAY_ROW_SEP SHEET_SEP INVALID_TOKEN
 %type  <sheet>	sheetref
 %type  <wb>	workbookref
+%token <wb>     tok_WORKBOOKREF
 
 %left '<' '>' '=' tok_GTE tok_LTE tok_NE
 %left '&'
@@ -743,8 +744,12 @@ string_opt_quote : STRING
 		 | QUOTED_STRING
 		 ;
 
+opt_sheet_sep    : SHEET_SEP
+	         | ;
+
 /* only used for names */
-workbookref : '[' string_opt_quote ']'  {
+workbookref : tok_WORKBOOKREF opt_sheet_sep
+	    | '[' string_opt_quote ']'  {
 		char const *wb_name = value_peek_string ($2->constant.value);
 		Workbook *ref_wb = state->pos
 			? (state->pos->wb
@@ -753,12 +758,10 @@ workbookref : '[' string_opt_quote ']'  {
 			      ? state->pos->sheet->workbook
 			      : NULL))
 			: NULL;
-		Workbook *wb;
-		if (state->convs->input.external_wb)
-			wb = (*state->convs->input.external_wb) (state->convs, ref_wb, wb_name);
-		else
-			wb = gnm_app_workbook_get_by_name (wb_name,
-			 ref_wb ? go_doc_get_uri ((GODoc *)ref_wb) : NULL);
+		Workbook *wb =
+			state->convs->input.external_wb (state->convs,
+							 ref_wb,
+							 wb_name);
 
 		if (wb != NULL) {
 			unregister_allocation ($2); gnm_expr_free ($2);
@@ -1379,6 +1382,53 @@ yylex (void)
 			return eat_space (state, QUOTED_STRING);
 		}
 	}
+
+	case '[': {
+		const char *p = state->ptr;
+		GString *s = g_string_new (NULL);
+		Workbook *ref_wb = state->pos
+			? (state->pos->wb
+			   ? state->pos->wb
+			   : (state->pos->sheet
+			      ? state->pos->sheet->workbook
+			      : NULL))
+			: NULL;
+
+		while (g_unichar_isspace (g_utf8_get_char (p)))
+			p = g_utf8_next_char (p);
+
+		if (p[0] == '"' || p[0] == '\'') {
+			p = go_strunescape (s, p);
+		} else {
+			gunichar uc;
+			while (1) {
+				uc = g_utf8_get_char (p);
+				if (!uc || uc == ']' || g_unichar_isspace (uc))
+					break;
+				p = g_utf8_next_char (p);
+				g_string_append_unichar (s, uc);
+			}
+		}
+
+		while (g_unichar_isspace (g_utf8_get_char (p)))
+			p = g_utf8_next_char (p);
+
+		if (s->len == 0 || p[0] != ']') {
+			g_string_free (s, TRUE);
+			break;
+		}
+
+		yylval.wb = state->convs->input.external_wb (state->convs,
+							     ref_wb,
+							     s->str);
+		g_printerr ("XXX: %s %p\n", s->str, yylval.wb);
+		g_string_free (s, TRUE);
+		if (!yylval.wb)
+			break;
+
+		state->ptr = p + 1;
+		return tok_WORKBOOKREF;
+	}
 	}
 
 	if ((end = state->convs->input.name (start, state->convs))) {
