int here_document(int term, int indent) { int c; string line = String.Empty; RNode list = null; int linesave = thread.line; newtok(); switch (term) { case '\'': goto case '`'; case '"': goto case '`'; case '`': while ((c = nextc()) != term) { tokadd(c); } if (term == '\'') term = '\0'; break; default: c = term; term = '"'; if (!is_identchar(c)) { ruby.warn("use of bare << to mean <<\"\" is deprecated"); break; } while (is_identchar(c)) { tokadd(c); c = nextc(); } pushback(c); break; } string lastline_save = lastline; int offset_save = pcur - pbeg; string eos = string.Copy(tok()); int len = eos.Length; string str = String.Empty; for (;;) { lastline = line = getline(); if (line == null) { thread.line = linesave; thread.CompileError("can't find string \"" + eos + "\" anywhere before EOF"); return 0; } thread.line++; string p = line; if (indent > 0) { while (p.Length > 0 && (p[0] == ' ' || p[0] == '\t')) { p = p.Substring(1); } } if (String.Compare(eos, 0, p, 0, len) == 0) { if (p[len] == '\n' || p[len] == '\r') break; if (len == line.Length) break; } pbeg = pcur = 0; pend = pcur + line.Length; retry: switch (parse_string(term, '\n', '\n')) { case Token.tSTRING: // fall down to the next case case Token.tXSTRING: { yylval = (string)yylval + "\n"; } if (list == null) { str += (string)yylval; } else { RNode.list_append(thread, list, new RNStr(thread, ruby, (string)yylval)); } break; case Token.tDSTRING: if (list == null) list = new RNDStr(thread, ruby, str); goto case Token.tDXSTRING; case Token.tDXSTRING: if (list == null) list = new RNDXStr(thread, ruby, str); RNode.list_append(thread, (RNode)yylval, new RNStr(thread, ruby, "\n")); RNStr val = new RNStr((RNStr)yylval); yylval = new RNArray(thread, val); ((RNode)yylval).next = ((RNode)yylval).head.next; RNode.list_concat(list, (RNode)yylval); break; case 0: thread.line = linesave; thread.CompileError("can't find string \"" + eos + "\" anywhere before EOF"); return 0; } if (pcur != pend) { goto retry; } } lastline = lastline_save; pbeg = 0; pend = lastline.Length; pcur = offset_save; lex_state = EXPR.END; heredoc_end = thread.line; thread.line = linesave; if (list != null) { list.SetLine(linesave+1); yylval = list; } switch (term) { case '\0': goto case '"'; case '\'': goto case '"'; case '"': if (list != null) return Token.tDSTRING; yylval = str; return Token.tSTRING; case '`': if (list != null) return Token.tDXSTRING; yylval = str; return Token.tXSTRING; } return 0; }
int parse_string(int func, int term, int paren) { int c; RNode list = null; int strstart; int nest = 0; if (func == '\'') { return parse_qstring(term, paren); } if (func == 0) { /* read 1 line for heredoc */ /* -1 for chomp */ yylval = lastline.Substring(pbeg, pend - pbeg - 1); pcur = pend; return Token.tSTRING; } strstart = thread.line; newtok(); while ((c = nextc()) != term || nest > 0) { if (c == -1) { thread.line = strstart; thread.CompileError("unterminated string meets end of file"); return 0; } /* if (ismbchar(c)) { int i, len = mbclen(c)-1; for (i = 0; i < len; i++) { tokadd(c); c = nextc(); } } */ else if (c == '#') { list = str_extend(list, term); if (list is RNEOF) { thread.line = strstart; thread.CompileError("unterminated string meets end of file"); return 0; } continue; } else if (c == '\\') { c = nextc(); if (c == '\n') continue; if (c == term) { tokadd(c); } else { pushback(c); if (func != '"') tokadd('\\'); tokadd(read_escape()); } continue; } if (paren != 0) { if (c == paren) nest++; if (c == term && nest-- == 0) break; } tokadd(c); } lex_state = EXPR.END; if (list != null) { list.SetLine(strstart); if (toklen() > 0) { RNode.list_append(thread, list, new RNStr(thread, ruby, tok())); } yylval = list; if (func == '`') { yylval = new RNDXStr(list); return Token.tDXSTRING; } else { return Token.tDSTRING; } } else { yylval = tok(); return (func == '`') ? Token.tXSTRING : Token.tSTRING; } }