Skip to content

Commit

Permalink
parse.y: add heredoc <<~ syntax (Feature #9098)
Browse files Browse the repository at this point in the history
  • Loading branch information
bjmllr committed Apr 20, 2015
1 parent 325a50f commit 61a35ad
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 1 deletion.
52 changes: 51 additions & 1 deletion parse.y
Original file line number Diff line number Diff line change
Expand Up @@ -5177,6 +5177,7 @@ static int parser_tokadd_string(struct parser_params*,int,int,int,long*,rb_encod
static void parser_tokaddmbc(struct parser_params *parser, int c, rb_encoding *enc);
static int parser_parse_string(struct parser_params*,NODE*);
static int parser_here_document(struct parser_params*,NODE*);
static VALUE parser_heredoc_dedent(VALUE);


# define nextc() parser_nextc(parser)
Expand Down Expand Up @@ -5699,6 +5700,7 @@ rb_parser_compile_file_path(volatile VALUE vparser, VALUE fname, VALUE file, int
#define STR_FUNC_QWORDS 0x08
#define STR_FUNC_SYMBOL 0x10
#define STR_FUNC_INDENT 0x20
#define STR_FUNC_DEDENT 0x40

enum string_type {
str_squote = (0),
Expand All @@ -5725,6 +5727,10 @@ parser_str_new(const char *p, long n, rb_encoding *enc, int func, rb_encoding *e
}
}

if (func & STR_FUNC_DEDENT) {
return parser_heredoc_dedent(str);
}

return str;
}

Expand Down Expand Up @@ -6478,7 +6484,11 @@ parser_heredoc_identifier(struct parser_params *parser)
if (c == '-') {
c = nextc();
func = STR_FUNC_INDENT;
} else if (c == '~') {
c = nextc();
func = STR_FUNC_INDENT | STR_FUNC_DEDENT;
}

switch (c) {
case '\'':
func |= str_squote; goto quoted;
Expand All @@ -6502,7 +6512,9 @@ parser_heredoc_identifier(struct parser_params *parser)
default:
if (!parser_is_identchar()) {
pushback(c);
if (func & STR_FUNC_INDENT) {
if (func & STR_FUNC_DEDENT) {
pushback('~');
} else if (func & STR_FUNC_INDENT) {
pushback('-');
}
return 0;
Expand Down Expand Up @@ -6550,6 +6562,44 @@ parser_heredoc_restore(struct parser_params *parser, NODE *here)
ripper_flush(parser);
}

static VALUE
parser_heredoc_dedent(VALUE input)
{
char *str = RSTRING_PTR(input), *p, *out_p;
int len = RSTRING_LEN(input), indent = len, line_indent = 0, lines = 0;
char *end = &str[len];
VALUE output;

p = str;
while (p < end) {
lines++;
line_indent = 0;
while (p < end && (*p == ' ' || *p == '\t')) {
line_indent++;
p++;
}
if (p < end && line_indent < indent) indent = line_indent;
if (indent == 0) break;

while (p < end && *p != '\r' && *p != '\n') p++;
if (p < end && *p == '\r') p++;
if (p < end && *p == '\n') p++;
}

output = rb_str_new(0, len - (lines * indent));
out_p = RSTRING_PTR(output);

p = str;
while (p < end) {
p = p + indent;
while (p < end && *p != '\r' && *p != '\n') *out_p++ = *p++;
if (p < end && *p == '\r') *out_p++ = *p++;
if (p < end && *p == '\n') *out_p++ = *p++;
}

return output;
}

static int
parser_whole_match_p(struct parser_params *parser,
const char *eos, long len, int indent)
Expand Down
14 changes: 14 additions & 0 deletions test/ruby/test_syntax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,20 @@ def test_lineno_command_call_quote
assert_equal(expected, actual, "#{Bug7559}: ")
end

def test_dedented_heredoc_without_indentation
assert_equal(" y\nz\n", <<~eos)
y
z
eos
end

def test_dedented_heredoc_with_indentation
assert_equal(" a\nb\n", <<~eos)
a
b
eos
end

def test_lineno_after_heredoc
bug7559 = '[ruby-dev:46737]'
expected, _, actual = __LINE__, <<eom, __LINE__
Expand Down

0 comments on commit 61a35ad

Please sign in to comment.