Project

General

Profile

Actions

Feature #4276

closed

Allow use of quotes in symbol syntactic sugar for hashes

Added by tbenst (Tyler Benster) about 13 years ago. Updated over 9 years ago.

Status:
Closed
Target version:
[ruby-core:34453]

Description

Current syntactic sugar allows this:

hash = {Alabama: "AL"}

This feature request is to also allow symbols delimited by quotes (and thus able to contain a whitespace) to use an equivalent syntactic sugar:

hash2 = {"Rhode Island": "RI"}

Related issues 4 (0 open4 closed)

Related to Ruby master - Feature #4801: Shorthand Hash Syntax for StringsRejectednobu (Nobuyoshi Nakada)05/30/2011Actions
Related to Ruby master - Bug #10653: do-end block in ternary operator is syntax error Closedmatz (Yukihiro Matsumoto)Actions
Has duplicate Ruby master - Feature #4935: Quoted Label Form for 1.9 HashesClosedmatz (Yukihiro Matsumoto)06/27/2011Actions
Has duplicate Ruby master - Feature #9047: Alternate hash key syntax for symbolsClosedmatz (Yukihiro Matsumoto)10/24/2013Actions

Updated by nobu (Nobuyoshi Nakada) almost 13 years ago

Hi,

At Thu, 13 Jan 2011 10:17:23 +0900,
Tyler Benster wrote in [ruby-core:34453]:

Current syntactic sugar allows this:

hash = {Alabama: "AL"}

This feature request is to also allow symbols delimited by quotes (and thus able to contain a whitespace) to use an equivalent syntactic sugar:

hash2 = {"Rhode Island": "RI"}

I've forgotten to post the patch.

diff --git i/parse.y w/parse.y
index 06f96ce..0cacdd5 100644
--- i/parse.y
+++ w/parse.y
@@ -380,6 +380,8 @@ static NODE *ret_args_gen(struct parser_params*,NODE*);
 static NODE *arg_blk_pass(NODE*,NODE*);
 static NODE *new_yield_gen(struct parser_params*,NODE*);
 #define new_yield(node) new_yield_gen(parser, (node))
+static NODE *dsym_node_gen(struct parser_params*,NODE*);
+#define dsym_node(node) dsym_node_gen(parser, (node))
 
 static NODE *gettable_gen(struct parser_params*,ID);
 #define gettable(id) gettable_gen(parser,(id))
@@ -678,12 +680,12 @@ static void token_info_pop(struct parser_params*, const char *token);
 	keyword__FILE__
 	keyword__ENCODING__
 
-%token <id>   tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL
+%token <id>   tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL tLABEL_END
 %token <node> tINTEGER tFLOAT tSTRING_CONTENT tCHAR
 %token <node> tNTH_REF tBACK_REF
 %token <num>  tREGEXP_END
 
-%type <node> singleton strings string string1 xstring regexp
+%type <node> singleton strings string string1 xstring regexp string_body
 %type <node> string_contents xstring_contents regexp_contents string_content
 %type <node> words qwords word_list qword_list word
 %type <node> literal numeric dsym cpath
@@ -3841,12 +3843,18 @@ string		: tCHAR
 		    }
 		;
 
-string1		: tSTRING_BEG string_contents tSTRING_END
+string_body	: tSTRING_BEG string_contents
 		    {
-		    /*%%%*/
 			$$ = $2;
+		    }
+		;
+
+string1		: string_body tSTRING_END
+		    {
+		    /*%%%*/
+			$$ = $1;
 		    /*%
-			$$ = dispatch1(string_literal, $2);
+			$$ = dispatch1(string_literal, $1);
 		    %*/
 		    }
 		;
@@ -4199,26 +4207,7 @@ dsym		: tSYMBEG xstring_contents tSTRING_END
 		    {
 			lex_state = EXPR_END;
 		    /*%%%*/
-			if (!($$ = $2)) {
-			    $$ = NEW_LIT(ID2SYM(rb_intern("")));
-			}
-			else {
-			    VALUE lit;
-
-			    switch (nd_type($$)) {
-			      case NODE_DSTR:
-				nd_set_type($$, NODE_DSYM);
-				break;
-			      case NODE_STR:
-				lit = $$->nd_lit;
-				$$->nd_lit = ID2SYM(rb_intern_str(lit));
-				nd_set_type($$, NODE_LIT);
-				break;
-			      default:
-				$$ = NEW_NODE(NODE_DSYM, Qnil, 1, NEW_LIST($$));
-				break;
-			    }
-			}
+			$$ = dsym_node($2);
 		    /*%
 			$$ = dispatch1(dyna_symbol, $2);
 		    %*/
@@ -4761,6 +4750,15 @@ assoc		: arg_value tASSOC arg_value
 			$$ = dispatch2(assoc_new, $1, $2);
 		    %*/
 		    }
+		| string_body tLABEL_END arg_value
+		    {
+		    /*%%%*/
+			$$ = list_append(NEW_LIST(dsym_node($1)), $3);
+		    /*%
+			$$ = dispatch1(dyna_symbol, $1);
+			$$ = dispatch2(assoc_new, $$, $3);
+		    %*/
+		    }
 		;
 
 operation	: tIDENTIFIER
@@ -5334,6 +5332,7 @@ rb_parser_compile_file(volatile VALUE vparser, const char *f, VALUE file, int st
 #define STR_FUNC_QWORDS 0x08
 #define STR_FUNC_SYMBOL 0x10
 #define STR_FUNC_INDENT 0x20
+#define STR_FUNC_LABEL  0x40
 
 enum string_type {
     str_squote = (0),
@@ -5925,6 +5924,8 @@ parser_tokadd_string(struct parser_params *parser,
 
 #define NEW_STRTERM(func, term, paren) \
 	rb_node_newnode(NODE_STRTERM, (func), (term) | ((paren) << (CHAR_BIT * 2)), 0)
+#define IS_LABEL_SUFFIX(n) (peek_n(':',(n)) && !peek_n(':', (n)+1))
+#define MAYBE_LABEL() (IS_LABEL_POSSIBLE() ? STR_FUNC_LABEL : 0)
 
 static int
 parser_parse_string(struct parser_params *parser, NODE *quote)
@@ -5946,6 +5947,10 @@ parser_parse_string(struct parser_params *parser, NODE *quote)
 	    quote->nd_func = -1;
 	    return ' ';
 	}
+	if ((func & STR_FUNC_LABEL) && IS_LABEL_SUFFIX(0)) {
+	    lex_state = EXPR_BEG;
+	    return tLABEL_END;
+	}
 	if (!(func & STR_FUNC_REGEXP)) return tSTRING_END;
         set_yylval_num(regx_options());
 	return tREGEXP_END;
@@ -6533,7 +6538,6 @@ parser_prepare(struct parser_params *parser)
 #define IS_BEG() (lex_state == EXPR_BEG || lex_state == EXPR_MID || lex_state == EXPR_VALUE || lex_state == EXPR_CLASS)
 #define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c))
 #define IS_LABEL_POSSIBLE() ((lex_state == EXPR_BEG && !cmd_state) || IS_ARG())
-#define IS_LABEL_SUFFIX(n) (peek_n(':',(n)) && !peek_n(':', (n)+1))
 
 #ifndef RIPPER
 #define ambiguous_operator(op, syn) ( \
@@ -6849,7 +6853,7 @@ parser_yylex(struct parser_params *parser)
 	return '>';
 
       case '"':
-	lex_strterm = NEW_STRTERM(str_dquote, '"', 0);
+	lex_strterm = NEW_STRTERM(str_dquote | MAYBE_LABEL(), '"', 0);
 	return tSTRING_BEG;
 
       case '`':
@@ -6868,7 +6872,7 @@ parser_yylex(struct parser_params *parser)
 	return tXSTRING_BEG;
 
       case '\'':
-	lex_strterm = NEW_STRTERM(str_squote, '\'', 0);
+	lex_strterm = NEW_STRTERM(str_squote | MAYBE_LABEL(), '\'', 0);
 	return tSTRING_BEG;
 
       case '?':
@@ -8987,6 +8991,32 @@ new_args_gen(struct parser_params *parser, NODE *m, NODE *o, ID r, NODE *p, ID b
     ruby_sourceline = saved_line;
     return node;
 }
+
+static NODE*
+dsym_node_gen(struct parser_params *parser, NODE *node)
+{
+    if (!node) {
+	node = NEW_LIT(ID2SYM(rb_intern("")));
+    }
+    else {
+	VALUE lit;
+
+	switch (nd_type(node)) {
+	  case NODE_DSTR:
+	    nd_set_type(node, NODE_DSYM);
+	    break;
+	  case NODE_STR:
+	    lit = node->nd_lit;
+	    node->nd_lit = ID2SYM(rb_intern_str(lit));
+	    nd_set_type(node, NODE_LIT);
+	    break;
+	  default:
+	    node = NEW_NODE(NODE_DSYM, Qnil, 1, NEW_LIST(node));
+	    break;
+	}
+    }
+    return node;
+}
 #endif /* !RIPPER */
 
 static void

Nobu Nakada

Updated by nahi (Hiroshi Nakamura) about 12 years ago

  • Description updated (diff)
  • Category set to core
  • Status changed from Open to Assigned
  • Assignee set to matz (Yukihiro Matsumoto)

Updated by mame (Yusuke Endoh) over 11 years ago

  • Target version set to 2.6

Updated by bitsweat (Jeremy Daer) over 11 years ago

This would improve a lot of my code that's punctuated with { foo: bar, :'hoge' => piyo }. Switching syntax is a mental and visual interruption.

I hope Ruby 2 supports this quoted-symbol syntax from the first day it's released, so everyone can rely on it. Please consider this patch for preview2!

Updated by drbrain (Eric Hodel) over 11 years ago

Sorry, this feature is too late for 2.0.0

Updated by nobu (Nobuyoshi Nakada) about 11 years ago

One of the reason I didn't introduce this was, a few bundled scripts had compatibility issue when once I tried.

Updated by nobu (Nobuyoshi Nakada) about 10 years ago

  • Has duplicate Feature #9047: Alternate hash key syntax for symbols added

Updated by nobu (Nobuyoshi Nakada) about 10 years ago

  • Description updated (diff)

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 9 years ago

Great, from the test it seems to allow interpolation. Could you please confirm I understood it correctly?

Updated by matz (Yukihiro Matsumoto) over 9 years ago

  • Status changed from Assigned to Feedback

I am not against the idea, but I want to make sure that key will be symbol, since some may expect

{"foo bar": 12}

to be a hash with string key, especially who is familiar with JSON.

Matz.

Updated by nobu (Nobuyoshi Nakada) over 9 years ago

Rodrigo Rosenfeld Rosas wrote:

Great, from the test it seems to allow interpolation. Could you please confirm I understood it correctly?

Yes, it is allowed inside double quotes, but not single quotes.
Other terminators (e.g., %(...)) cause syntax errors.

Updated by Ajedi32 (Andrew M) over 9 years ago

Yukihiro Matsumoto wrote:

I am not against the idea, but I want to make sure that key will be symbol,

Yes, I believe that was the intention. E.g.

# Currently, Hashes have syntactic sugar which allows:
{:key => "Value"} == {key: "Value"} #=> true

# This feature request is to allow:
{:"some key" => "Value"} == {"some key": "Value"} #=> true

Updated by nobu (Nobuyoshi Nakada) over 9 years ago

OK, I commit the previous patch.
Yell if you don't like it.

Updated by nobu (Nobuyoshi Nakada) over 9 years ago

  • Status changed from Feedback to Closed
  • % Done changed from 0 to 100

Applied in changeset r47649.


parse.y: quoted ID key

Updated by nobu (Nobuyoshi Nakada) over 9 years ago

Just a note: last evening at RubyKaigi 2014, talked to matz about this issue, and got his approval to introduce this to see if anyone rants.

Updated by etienne (Étienne Barrié) over 9 years ago

Hi, any reason it doesn't work with inner hashes or arrays?

{ foo: {} } # => {:foo=>{}}
{ 'foo': {} }
# SyntaxError: (irb):2: syntax error, unexpected '{'
{ 'foo': [] }
SyntaxError: (irb):1: syntax error, unexpected '['

Updated by nobu (Nobuyoshi Nakada) about 9 years ago

  • Related to Bug #10653: do-end block in ternary operator is syntax error added
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0