Hi Chet,

I wonder if you can allow bash to have another syntax to allow simpler
declaration and/or definition of associative arrays.  The changes
needed to have it done seem simple enough, and the only conflict it
makes is a scalar `var={...` assignment, which in my opinion is better
quoted to make it more readable and less questionable from other
syntaxes like brace expansion.  I believe most people intuitively
quotes it, and assignments that start with `{` is fairly rare.

I made a patch for this which I include as an attachment.  I also
uploaded the source tree to
https://github.com/konsolebox/bash/tree/assoc_array_brace_assign.

Besides having an easy way to define associative arrays, this feature
would also allow associative arrays to be used as "return variables".
The caller wouldn't have to initialize the associative array unless it
wants to have it declared local for itself, and the callee that
returns the data can allow itself to be a complete and independent
function.

g() { a={[x]=y}; }
f() { local a={}; g; declare -p a; }
a={[a]=b}; f; declare -p a

Output:

declare -A a=([x]="y" )
declare -A a=([a]="b" )

-- 
konsolebox
diff --git a/parse.y b/parse.y
index 0eb6413..f5c9a66 100644
--- a/parse.y
+++ b/parse.y
@@ -172,7 +172,7 @@ static int read_token __P((int));
 static char *parse_matched_pair __P((int, int, int, int *, int));
 static char *parse_comsub __P((int, int, int, int *, int));
 #if defined (ARRAY_VARS)
-static char *parse_compound_assignment __P((int *));
+static char *parse_compound_assignment __P((int *, int));
 #endif
 #if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND)
 static int parse_dparen __P((int));
@@ -3274,6 +3274,12 @@ itrace("shell_getc: bash_input.location.string = `%s'", bash_input.location.stri
   if (parser_state & PST_REGEXP)
     goto tokword;
 
+  if MBTEST(parser_state & PST_ASSIGNASSOC && character == '}')
+    {
+      parser_state &= ~PST_ASSIGNOK;
+      return (character);
+    }
+
   /* Shell meta-characters. */
   if MBTEST(shellmeta (character) && ((parser_state & PST_DBLPAREN) == 0))
     {
@@ -5151,25 +5157,27 @@ read_token_word (character)
       else if MBTEST(character == '=' && token_index > 0 && (assignment_acceptable (last_read_token) || (parser_state & PST_ASSIGNOK)) && token_is_assignment (token, token_index))
 	{
 	  peek_char = shell_getc (1);
-	  if MBTEST(peek_char == '(')		/* ) */
+	  if MBTEST(peek_char == '(' || peek_char == '{')	/* ) */
 	    {
-	      ttok = parse_compound_assignment (&ttoklen);
+	      int assoc;
+	      assoc = peek_char == '{';
+	      ttok = parse_compound_assignment (&ttoklen, assoc);
 
 	      RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 4,
 				      token_buffer_size,
 				      TOKEN_DEFAULT_GROW_SIZE);
 
 	      token[token_index++] = '=';
-	      token[token_index++] = '(';
+	      token[token_index++] = peek_char;
 	      if (ttok)
 		{
 		  strcpy (token + token_index, ttok);
 		  token_index += ttoklen;
 		}
-	      token[token_index++] = ')';
+	      token[token_index++] = assoc ? '}' : ')';
 	      FREE (ttok);
 	      all_digit_token = 0;
-	      compound_assignment = 1;
+	      compound_assignment = assoc ? 2 : 1;
 #if 1
 	      goto next_character;
 #else
@@ -5181,6 +5189,12 @@ read_token_word (character)
 	}
 #endif
 
+      if MBTEST(parser_state & PST_ASSIGNASSOC && character == '}')
+	{
+	  shell_ungetc (character);
+	  goto got_token;
+	}
+
       /* When not parsing a multi-character word construct, shell meta-
 	 characters break words. */
       if MBTEST(shellbreak (character))
@@ -5274,8 +5288,13 @@ got_token:
     the_word->flags |= W_HASDOLLAR;
   if (quoted)
     the_word->flags |= W_QUOTED;		/*(*/
-  if (compound_assignment && token[token_index-1] == ')')
+  if (compound_assignment == 1 && token[token_index-1] == ')')
     the_word->flags |= W_COMPASSIGN;
+  if (compound_assignment == 2 && token[token_index-1] == '}')
+    {
+      the_word->flags |= W_COMPASSIGN;
+      the_word->flags |= W_ASSIGNASSOC;
+    }
   /* A word is an assignment if it appears at the beginning of a
      simple command, or after another assignment word.  This is
      context-dependent, so it cannot be handled in the grammar. */
@@ -6443,8 +6462,9 @@ parse_string_to_word_list (s, flags, whom)
 }
 
 static char *
-parse_compound_assignment (retlenp)
+parse_compound_assignment (retlenp, assoc)
      int *retlenp;
+     int assoc;
 {
   WORD_LIST *wl, *rl;
   int tok, orig_line_number, orig_token_size, orig_last_token, assignok;
@@ -6465,7 +6485,13 @@ parse_compound_assignment (retlenp)
   wl = (WORD_LIST *)NULL;	/* ( */
   parser_state |= PST_COMPASSIGN;
 
-  while ((tok = read_token (READ)) != ')')
+  if (assoc)
+    parser_state |= PST_ASSIGNASSOC;
+
+  int matcher;
+  matcher = assoc ? '}' : ')';
+
+  while ((tok = read_token (READ)) != matcher)
     {
       if (tok == '\n')			/* Allow newlines in compound assignments */
 	{
@@ -6476,8 +6502,10 @@ parse_compound_assignment (retlenp)
       if (tok != WORD && tok != ASSIGNMENT_WORD)
 	{
 	  current_token = tok;	/* for error reporting */
-	  if (tok == yacc_EOF)	/* ( */
-	    parser_error (orig_line_number, _("unexpected EOF while looking for matching `)'"));
+	  if (tok == yacc_EOF)
+	    parser_error (orig_line_number,
+		_("unexpected EOF while looking for matching `%c'"),
+		matcher);
 	  else
 	    yyerror(NULL);	/* does the right thing */
 	  if (wl)
@@ -6492,7 +6520,7 @@ parse_compound_assignment (retlenp)
   token = saved_token;
   token_buffer_size = orig_token_size;
 
-  parser_state &= ~PST_COMPASSIGN;
+  parser_state &= ~(PST_COMPASSIGN|PST_ASSIGNASSOC);
 
   if (wl == &parse_string_error)
     {
diff --git a/parser.h b/parser.h
index 88c799d..178930e 100644
--- a/parser.h
+++ b/parser.h
@@ -46,6 +46,7 @@
 #define PST_HEREDOC	0x020000	/* reading body of here-document */
 #define PST_REPARSE	0x040000	/* re-parsing in parse_string_to_word_list */
 #define PST_REDIRLIST	0x080000	/* parsing a list of redirections preceding a simple command name */
+#define PST_ASSIGNASSOC 0x100000
 
 
 /* Definition of the delimiter stack.  Needed by parse.y and bashhist.c. */
diff --git a/subst.c b/subst.c
index dbb38fa..778b205 100644
--- a/subst.c
+++ b/subst.c
@@ -1292,7 +1292,7 @@ extract_array_assignment_list (string, sindex)
   char *ret;
 
   slen = strlen (string);
-  if (string[slen - 1] == RPAREN)
+  if (string[slen - 1] == RPAREN || string[slen - 1] == RBRACE)
    {
       ret = substring (string, *sindex, slen - 1);
       *sindex = slen - 1;

Reply via email to